+static void set_state(struct utcp_connection *c, enum state state) {
+ c->state = state;
+
+ if(state == ESTABLISHED) {
+ timerclear(&c->conn_timeout);
+ }
+
+ debug("%p new state: %s\n", c->utcp, strstate[state]);
+}
+
+static bool fin_wanted(struct utcp_connection *c, uint32_t seq) {
+ if(seq != c->snd.last) {
+ return false;
+ }
+
+ switch(c->state) {
+ case FIN_WAIT_1:
+ case CLOSING:
+ case LAST_ACK:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static bool is_reliable(struct utcp_connection *c) {
+ return c->flags & UTCP_RELIABLE;
+}
+
+static int32_t seqdiff(uint32_t a, uint32_t b) {
+ return a - b;
+}
+
+// Buffer functions
+// TODO: convert to ringbuffers to avoid memmove() operations.
+
+// 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);
+
+ size_t required = offset + len;
+
+ if(required > buf->maxsize) {
+ if(offset >= buf->maxsize) {
+ return 0;
+ }
+
+ len = buf->maxsize - offset;
+ required = buf->maxsize;
+ }
+
+ if(required > buf->size) {
+ size_t newsize = buf->size;
+
+ if(!newsize) {
+ newsize = required;
+ } else {
+ do {
+ newsize *= 2;
+ } while(newsize < required);
+ }
+
+ if(newsize > buf->maxsize) {
+ newsize = buf->maxsize;
+ }
+
+ char *newdata = realloc(buf->data, newsize);
+
+ if(!newdata) {
+ return -1;
+ }
+
+ buf->data = newdata;
+ buf->size = newsize;
+ }
+
+ memcpy(buf->data + offset, 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);
+}
+
+// Get data from the buffer. data can be NULL.
+static ssize_t buffer_get(struct buffer *buf, void *data, size_t len) {
+ if(len > buf->used) {
+ len = buf->used;
+ }
+
+ if(data) {
+ memcpy(data, buf->data, len);
+ }
+
+ if(len < buf->used) {
+ memmove(buf->data, buf->data + len, buf->used - len);
+ }
+
+ buf->used -= len;
+ return 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) {
+ if(offset >= buf->used) {
+ return 0;
+ }
+
+ if(offset + len > buf->used) {
+ len = buf->used - offset;
+ }
+
+ memcpy(data, buf->data + offset, len);
+ return len;
+}
+
+static bool buffer_init(struct buffer *buf, uint32_t len, uint32_t maxlen) {
+ memset(buf, 0, sizeof(*buf));
+
+ if(len) {
+ buf->data = malloc(len);
+
+ if(!buf->data) {
+ return false;
+ }
+ }
+
+ buf->size = len;
+ buf->maxsize = maxlen;
+ return true;
+}
+
+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;