+// 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) {
+ 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 + newoffset, buf + 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("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;
+}
+
+// Get data from the buffer.
+static ssize_t buffer_get(struct buffer *buf, void *data, size_t len) {
+ len = buffer_copy(buf, data, 0, len);
+
+ if(buf->size - buf->offset < len) {
+ buf->offset -= buf->size;
+ }
+
+ buf->offset += len;
+ buf->used -= 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;
+ }
+
+ buf->offset += len;
+ buf->used -= len;
+
+ return len;
+}
+
+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_exit(struct buffer *buf) {
+ free(buf->data);
+ memset(buf, 0, sizeof(*buf));
+}
+
+static uint32_t buffer_free(const struct buffer *buf) {
+ return buf->maxsize - buf->used;
+}
+