-// Buffer functions
-static bool buffer_wraps(struct buffer *buf) {
- return buf->size - buf->offset < buf->used;
-}
-
-static bool buffer_resize(struct buffer *buf, uint32_t newsize) {
- assert(!buf->external);
-
- if(!newsize) {
- free(buf->data);
- buf->data = NULL;
- buf->size = 0;
- buf->offset = 0;
- return true;
- }
-
- char *newdata = realloc(buf->data, newsize);
-
- if(!newdata) {
- return false;
- }
-
- buf->data = newdata;
-
- if(buffer_wraps(buf)) {
- // Shift the right part of the buffer until it hits the end of the new buffer.
- // Old situation:
- // [345......012]
- // New situation:
- // [345.........|........012]
- uint32_t tailsize = buf->size - buf->offset;
- uint32_t newoffset = newsize - tailsize;
- memmove(buf->data + newoffset, buf->data + buf->offset, tailsize);
- buf->offset = newoffset;
- }
-
- buf->size = newsize;
- return true;
-}
-
-// Store data into the buffer
-static ssize_t buffer_put_at(struct buffer *buf, size_t offset, const void *data, size_t len) {
- debug(NULL, "buffer_put_at %lu %lu %lu\n", (unsigned long)buf->used, (unsigned long)offset, (unsigned long)len);
-
- // Ensure we don't store more than maxsize bytes in total
- size_t required = offset + len;
-
- if(required > buf->maxsize) {
- if(offset >= buf->maxsize) {
- return 0;
- }
-
- len = buf->maxsize - offset;
- required = buf->maxsize;
- }
-
- // Check if we need to resize the buffer
- if(required > buf->size) {
- size_t newsize = buf->size;
-
- if(!newsize) {
- newsize = 4096;
- }
-
- do {
- newsize *= 2;
- } while(newsize < required);
-
- if(newsize > buf->maxsize) {
- newsize = buf->maxsize;
- }
-
- if(!buffer_resize(buf, newsize)) {
- return -1;
- }
- }
-
- uint32_t realoffset = buf->offset + offset;
-
- if(buf->size - buf->offset <= offset) {
- // The offset wrapped
- realoffset -= buf->size;
- }
-
- if(buf->size - realoffset < len) {
- // The new chunk of data must be wrapped
- memcpy(buf->data + realoffset, data, buf->size - realoffset);
- memcpy(buf->data, (char *)data + buf->size - realoffset, len - (buf->size - realoffset));
- } else {
- memcpy(buf->data + realoffset, data, len);
- }
-
- if(required > buf->used) {
- buf->used = required;
- }
-
- return len;
-}
-
-static ssize_t buffer_put(struct buffer *buf, const void *data, size_t len) {
- return buffer_put_at(buf, buf->used, data, len);
-}
-
-// Copy data from the buffer without removing it.
-static ssize_t buffer_copy(struct buffer *buf, void *data, size_t offset, size_t len) {
- // Ensure we don't copy more than is actually stored in the buffer
- if(offset >= buf->used) {
- return 0;
- }
-
- if(buf->used - offset < len) {
- len = buf->used - offset;
- }
-
- uint32_t realoffset = buf->offset + offset;
-
- if(buf->size - buf->offset <= offset) {
- // The offset wrapped
- realoffset -= buf->size;
- }
-
- if(buf->size - realoffset < len) {
- // The data is wrapped
- memcpy(data, buf->data + realoffset, buf->size - realoffset);
- memcpy((char *)data + buf->size - realoffset, buf->data, len - (buf->size - realoffset));
- } else {
- memcpy(data, buf->data + realoffset, len);
- }
-
- return len;
-}
-
-// Discard data from the buffer.
-static ssize_t buffer_discard(struct buffer *buf, size_t len) {
- if(buf->used < len) {
- len = buf->used;
- }
-
- if(buf->size - buf->offset <= len) {
- buf->offset -= buf->size;
- }
-
- if(buf->used == len) {
- buf->offset = 0;
- } else {
- buf->offset += len;
- }
-
- buf->used -= len;
-
- return len;
-}
-
-static void buffer_clear(struct buffer *buf) {
- buf->used = 0;
- buf->offset = 0;
-}
-
-static bool buffer_set_size(struct buffer *buf, uint32_t minsize, uint32_t maxsize) {
- if(maxsize < minsize) {
- maxsize = minsize;
- }
-
- buf->maxsize = maxsize;
-
- return buf->size >= minsize || buffer_resize(buf, minsize);
-}
-
-static void buffer_transfer(struct buffer *buf, char *newdata, size_t newsize) {
- if(buffer_wraps(buf)) {
- // Old situation:
- // [345......012]
- // New situation:
- // [012345......]
- uint32_t tailsize = buf->size - buf->offset;
- memcpy(newdata, buf->data + buf->offset, tailsize);
- memcpy(newdata + tailsize, buf->data, buf->used - tailsize);
- } else {
- // Old situation:
- // [....012345..]
- // New situation:
- // [012345......]
- memcpy(newdata, buf->data + buf->offset, buf->used);
- }
-
- buf->offset = 0;
- buf->size = newsize;
-}
-
-static void set_buffer_storage(struct buffer *buf, char *data, size_t size) {
- if(size > UINT32_MAX) {
- size = UINT32_MAX;
- }
-
- buf->maxsize = size;
-
- if(data) {
- if(buf->external) {
- // Don't allow resizing an external buffer
- abort();
- }
-
- if(size < buf->used) {
- // Ignore requests for an external buffer if we are already using more than it can store
- return;
- }
-
- // Transition from internal to external buffer
- buffer_transfer(buf, data, size);
- free(buf->data);
- buf->data = data;
- buf->external = true;
- } else if(buf->external) {
- // Transition from external to internal buf
- size_t minsize = buf->used <= DEFAULT_SNDBUFSIZE ? DEFAULT_SNDBUFSIZE : buf->used;
-
- if(minsize) {
- data = malloc(minsize);
-
- if(!data) {
- // Cannot handle this
- abort();
- }
-
- buffer_transfer(buf, data, minsize);
- buf->data = data;
- } else {
- buf->data = NULL;
- buf->size = 0;
- }
-
- buf->external = false;
- } else {
- // Don't do anything if the buffer wraps
- if(buffer_wraps(buf)) {
- return;
- }
-
- // Realloc internal storage
- size_t minsize = max(DEFAULT_SNDBUFSIZE, buf->offset + buf->used);
-
- if(minsize) {
- data = realloc(buf->data, minsize);
-
- if(data) {
- buf->data = data;
- buf->size = minsize;
- }
- } else {
- free(buf->data);
- buf->data = NULL;
- buf->size = 0;
- }
- }
-}
-
-static void buffer_exit(struct buffer *buf) {
- if(!buf->external) {
- free(buf->data);
- }
-
- memset(buf, 0, sizeof(*buf));
-}
-
-static uint32_t buffer_free(const struct buffer *buf) {
- return buf->maxsize > buf->used ? buf->maxsize - buf->used : 0;
-}
-