/*
* buffer.c:
* Circular buffers.
*
* Copyright (c) 2002 Chris Lightfoot. All rights reserved.
* Email: chris@ex-parrot.com; WWW: http://www.ex-parrot.com/~chris/
*
*/
static const char rcsid[] = "$Id: buffer.c,v 1.7 2003/11/06 01:19:27 chris Exp $";
#include <sys/types.h>
#include <assert.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include "buffer.h"
#include "util.h"
/* buffer_new LEN
* Create a new buffer initially holding LEN bytes. */
buffer buffer_new(const size_t len) {
buffer B;
alloc_struct(_buffer, B);
B->buf = xmalloc(B->len = len);
return B;
}
/* buffer_delete BUFFER
* Destroy BUFFER, deallocating memory used. */
void buffer_delete(buffer B) {
assert(B);
xfree(B->buf);
xfree(B);
}
/* buffer_make_contiguous BUFFER
* Makes the available data in BUFFER contiguous, so that it can be returned
* by a single call to buffer_get_consume_ptr. */
void buffer_make_contiguous(buffer B) {
size_t a;
char *newbuf;
assert(B);
a = buffer_available(B);
if (!a || (size_t)B->get + a <= B->len)
/* Nothing to do. */
return;
/*
* v (get + a) % len
* ,---------------------------------------------.
* |XXXXXXXXXXXXXXXXX| |XXXXXXXXXXX|
* | (get + a) % len | | len - get |
* |XXXXXXXXXXXXXXXXX| |XXXXXXXXXXX|
* `---------------------------------------------'
* ^ 0 ^ get
*/
newbuf = xmalloc(B->len);
memcpy(newbuf, B->buf + B->get, B->len - B->get);
memcpy(newbuf + B->len - B->get, B->buf, (B->get + a) % B->len);
xfree(B->buf);
B->buf = newbuf;
B->get = 0;
B->put = a;
}
/* buffer_get_consume_ptr BUFFER SLEN
* Consume some available data in BUFFER, returning a pointer to the data and
* recording the number of bytes consumed in SLEN. This may be less than the
* total number of bytes available, in which case a further call to
* buffer_consume will yield the remainder. The pointer returned may be
* invalidated by a call to buffer_expand or buffer_push_data. The data
* returned are not null-terminated. If no data are available, NULL is
* returned. */
char *buffer_get_consume_ptr(buffer B, size_t *slen) {
size_t a;
char *p;
assert(B);
if (!(a = buffer_available(B))) {
*slen = 0;
return NULL;
}
if ((size_t)B->get + a > B->len)
a = B->len - B->get;
p = B->buf + B->get;
*slen = a;
return p;
}
/* buffer_consume_bytes BUFFER NUM
* Indicate that NUM bytes have been consumed from a location in BUFFER
* returned by buffer_get_consume_ptr. */
void buffer_consume_bytes(buffer B, const size_t num) {
assert(B);
B->get = (B->get + num) % B->len;
}
/* buffer_consume_all BUFFER STR SLEN
* Consume all available data in BUFFER, recording the number of bytes
* consumed in SLEN. If STR is not NULL it must point to a buffer of length at
* least *SLEN allocated with malloc(3); this buffer will be used as is if the
* returned string is small enough, or reallocated with realloc(3) otherwise.
* The returned string is null-terminated. Returns NULL if no data are
* available. */
char *buffer_consume_all(buffer B, char *str, size_t *slen) {
size_t a, i;
assert(B);
if (!(a = buffer_available(B))) {
*slen = 0;
return NULL;
}
if (!str || *slen < a + 1)
str = xrealloc(str, a + 1);
*slen = a + 1;
for (i = 0; i < a; ++i)
str[i] = B->buf[(B->get + i) % B->len];
str[i] = 0;
B->get = B->put;
return str;
}
/* buffer_consume_to_mark BUFFER MARK MLEN STR SLEN
* Consume data from BUFFER up to and including MARK of length MLEN, returning
* a pointer to a string allocated with malloc(3) or NULL if the mark was not
* found. The number of bytes consumed is recorded in SLEN. If STR is not
* NULL, it must point to a buffer of length at least *SLEN allocated with
* malloc(3); this buffer will be used as is if the returned string is small
* enough, or reallocated with realloc(3) otherwise. The returned string is
* null-terminated.
*
* This uses a Boyer-Moore search, but we can't just reuse memstr because we
* may have to search across the end of the buffer. */
char *buffer_consume_to_mark(buffer B, const char *mark, const size_t mlen, char *str, size_t *slen) {
size_t skip[256], a;
int k;
assert(B);
assert(mlen > 0 && mlen <= (size_t)INT_MAX);
if ((a = buffer_available(B)) < mlen) return NULL;
assert(a <= (size_t)INT_MAX);
/* Oh dear. Should special-case the mlen == 1 case, since it's the only
* one we use.... */
for (k = 0; k < 256; ++k) skip[k] = mlen;
for (k = 0; k < (int)mlen - 1; ++k) skip[(unsigned char)mark[k]] = mlen - k - 1;
for (k = (int)mlen - 1; k < (int)a; k += skip[(unsigned char)mark[k]]) {
int i, j;
for (j = (int)mlen - 1, i = k; j >= 0 && B->buf[(B->get + i) % B->len] == mark[j]; j--) i--;
if (j == -1) {
/* Have found the mark at location i + 1. */
i += 1 + mlen; /* account for mark and terminating null */
if (!str || *slen < (size_t)i + 1)
str = xrealloc(str, (size_t)i + 1);
*slen = (size_t)i + 1;
for (j = 0; j < i; ++j)
str[j] = B->buf[(B->get + j) % B->len];
str[j] = 0;
B->get = (B->get + i) % B->len;
return str;
}
}
return NULL;
}
/* buffer_expand BUFFER NUM
* Ensure that BUFFER can store at least an additional NUM bytes. */
void buffer_expand(buffer B, const size_t num) {
size_t a;
assert(B);
if (B->len <= (a = buffer_available(B)) + num) { /* NB <= NOT < */
size_t i;
char *newbuf;
size_t newlen;
for (newlen = B->len * 2; newlen <= a + num; newlen *= 2);
newbuf = xmalloc(newlen);
for (i = 0; i < a; ++i)
newbuf[i] = B->buf[(B->get + i) % B->len];
xfree(B->buf);
B->buf = newbuf;
B->len = newlen;
B->get = 0;
B->put = (off_t)a;
}
}
/* buffer_push_data BUFFER DATA DLEN
* Add DLEN bytes of DATA to BUFFER, expanding the buffer if necessary. */
void buffer_push_data(buffer B, const char *data, const size_t dlen) {
size_t i;
assert(B);
buffer_expand(B, dlen);
for (i = 0; i < dlen; ++i)
B->buf[(B->put + i) % B->len] = data[i];
B->put = (B->put + dlen) % B->len;
}
/* buffer_get_push_ptr BUFFER LEN
* Return a pointer to part of BUFFER to which new data can be written. On
* return, LEN indicates how many contiguous bytes may be written. */
char *buffer_get_push_ptr(buffer B, size_t *len) {
assert(B);
*len = B->len - B->put;
return B->buf + B->put;
}
/* buffer_push_bytes BUFFER NUM
* Indicate that NUM bytes have been added to the buffer by writing them at
* a location returned by buffer_get_push_ptr. */
void buffer_push_bytes(buffer B, const size_t num) {
B->put = (B->put + num) % B->len;
}
syntax highlighted by Code2HTML, v. 0.9.1