summaryrefslogtreecommitdiff
path: root/src/lib/capnp-c/capn-malloc.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/capnp-c/capn-malloc.cc')
-rw-r--r--src/lib/capnp-c/capn-malloc.cc409
1 files changed, 409 insertions, 0 deletions
diff --git a/src/lib/capnp-c/capn-malloc.cc b/src/lib/capnp-c/capn-malloc.cc
new file mode 100644
index 0000000..7308061
--- /dev/null
+++ b/src/lib/capnp-c/capn-malloc.cc
@@ -0,0 +1,409 @@
+/* vim: set sw=8 ts=8 sts=8 noet: */
+/* capn-malloc.c
+ *
+ * Copyright (C) 2013 James McKaskill
+ * Copyright (C) 2014 Steve Dee
+ *
+ * This software may be modified and distributed under the terms
+ * of the MIT license. See the LICENSE file for details.
+ */
+
+#ifdef __GNUC__
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+#endif
+
+#include "capnp_c.h"
+#include "capnp_priv.h"
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <errno.h>
+
+/*
+ * 8 byte alignment is required for struct capn_segment.
+ * This struct check_segment_alignment verifies this at compile time.
+ *
+ * Unless capn_segment is defined with 8 byte alignment, check_segment_alignment
+ * fails to compile in x86 mode (or on another CPU with 32-bit pointers),
+ * as (sizeof(struct capn_segment)&7) -> (44 & 7) evaluates to 4.
+ * It compiles in x64 mode (or on another CPU with 64-bit pointers),
+ * as (sizeof(struct capn_segment)&7) -> (80 & 7) evaluates to 0.
+ */
+struct check_segment_alignment {
+ unsigned int foo : (sizeof(struct capn_segment)&7) ? -1 : 1;
+};
+
+static struct capn_segment *create(void *u, uint32_t id, int sz) {
+ struct capn_segment *s;
+ sz += sizeof(*s);
+ if (sz < 4096) {
+ sz = 4096;
+ } else {
+ sz = (sz + 4095) & ~4095;
+ }
+ s = (struct capn_segment*) calloc(1, sz);
+ s->data = (char*) (s+1);
+ s->cap = sz - sizeof(*s);
+ s->user = s;
+ return s;
+}
+
+static struct capn_segment *create_local(void *u, int sz) {
+ return create(u, 0, sz);
+}
+
+void capn_init_malloc(struct capn *c) {
+ memset(c, 0, sizeof(*c));
+ c->create = &create;
+ c->create_local = &create_local;
+}
+
+void capn_free(struct capn *c) {
+ struct capn_segment *s = c->seglist;
+ while (s != NULL) {
+ struct capn_segment *n = s->next;
+ free(s->user);
+ s = n;
+ }
+ capn_reset_copy(c);
+}
+
+void capn_reset_copy(struct capn *c) {
+ struct capn_segment *s = c->copylist;
+ while (s != NULL) {
+ struct capn_segment *n = s->next;
+ free(s->user);
+ s = n;
+ }
+ c->copy = NULL;
+ c->copylist = NULL;
+}
+
+#define ZBUF_SZ 4096
+
+static int read_fp(void *p, size_t sz, FILE *f, struct capn_stream *z, uint8_t* zbuf, int packed) {
+ if (f && packed) {
+ z->next_out = (uint8_t*) p;
+ z->avail_out = sz;
+
+ while (z->avail_out && capn_inflate(z) == CAPN_NEED_MORE) {
+ int r;
+ memmove(zbuf, z->next_in, z->avail_in);
+ r = fread(zbuf+z->avail_in, 1, ZBUF_SZ - z->avail_in, f);
+ if (r <= 0)
+ return -1;
+ z->avail_in += r;
+ }
+ return 0;
+
+ } else if (f && !packed) {
+ return fread(p, sz, 1, f) != 1;
+
+ } else if (packed) {
+ z->next_out = (uint8_t*) p;
+ z->avail_out = sz;
+ return capn_inflate(z) != 0;
+
+ } else {
+ if (z->avail_in < sz)
+ return -1;
+ memcpy(p, z->next_in, sz);
+ z->next_in += sz;
+ z->avail_in -= sz;
+ return 0;
+ }
+}
+
+static int init_fp(struct capn *c, FILE *f, struct capn_stream *z, int packed) {
+ /*
+ * Initialize 'c' from the contents of 'f', assuming the message has been
+ * serialized with the standard framing format. From https://capnproto.org/encoding.html:
+ *
+ * When transmitting over a stream, the following should be sent. All integers are unsigned and little-endian.
+ * (4 bytes) The number of segments, minus one (since there is always at least one segment).
+ * (N * 4 bytes) The size of each segment, in words.
+ * (0 or 4 bytes) Padding up to the next word boundary.
+ * The content of each segment, in order.
+ */
+
+ struct capn_segment *s = NULL;
+ uint32_t i, segnum, total = 0;
+ uint32_t hdr[1024];
+ uint8_t zbuf[ZBUF_SZ];
+ char *data = NULL;
+
+ capn_init_malloc(c);
+
+ /* Read the first four bytes to know how many headers we have */
+ if (read_fp(&segnum, 4, f, z, zbuf, packed))
+ goto err;
+
+ segnum = capn_flip32(segnum);
+ if (segnum > 1023)
+ goto err;
+ segnum++; /* The wire encoding was zero-based */
+
+ /* Read the header list */
+ if (read_fp(hdr, 8 * (segnum/2) + 4, f, z, zbuf, packed))
+ goto err;
+
+ for (i = 0; i < segnum; i++) {
+ uint32_t n = capn_flip32(hdr[i]);
+ if (n > INT_MAX/8 || n > UINT32_MAX/8 || UINT32_MAX - total < n*8)
+ goto err;
+ hdr[i] = n*8;
+ total += hdr[i];
+ }
+
+ /* Allocate space for the data and the capn_segment structs */
+ s = (struct capn_segment*) calloc(1, total + (sizeof(*s) * segnum));
+ if (!s)
+ goto err;
+
+ /* Now read the data and setup the capn_segment structs */
+ data = (char*) (s+segnum);
+ if (read_fp(data, total, f, z, zbuf, packed))
+ goto err;
+
+ for (i = 0; i < segnum; i++) {
+ s[i].len = s[i].cap = hdr[i];
+ s[i].data = data;
+ data += s[i].len;
+ capn_append_segment(c, &s[i]);
+ }
+
+ /* Set the entire region to be freed on the last segment */
+ s[segnum-1].user = s;
+
+ return 0;
+
+err:
+ memset(c, 0, sizeof(*c));
+ free(s);
+ return -1;
+}
+
+int capn_init_fp(struct capn *c, FILE *f, int packed) {
+ struct capn_stream z;
+ memset(&z, 0, sizeof(z));
+ return init_fp(c, f, &z, packed);
+}
+
+int capn_init_mem(struct capn *c, const uint8_t *p, size_t sz, int packed) {
+ struct capn_stream z;
+ memset(&z, 0, sizeof(z));
+ z.next_in = p;
+ z.avail_in = sz;
+ return init_fp(c, NULL, &z, packed);
+}
+
+static void header_calc(struct capn *c, uint32_t *headerlen, size_t *headersz)
+{
+ /* segnum == 1:
+ * [segnum][segsiz]
+ * segnum == 2:
+ * [segnum][segsiz][segsiz][zeroes]
+ * segnum == 3:
+ * [segnum][segsiz][segsiz][segsiz]
+ * segnum == 4:
+ * [segnum][segsiz][segsiz][segsiz][segsiz][zeroes]
+ */
+ *headerlen = ((2 + c->segnum) / 2) * 2;
+ *headersz = 4 * *headerlen;
+}
+
+static int header_render(struct capn *c, struct capn_segment *seg, uint32_t *header, uint32_t headerlen, size_t *datasz)
+{
+ size_t i;
+
+ header[0] = capn_flip32(c->segnum - 1);
+ header[headerlen-1] = 0; /* Zero out the spare position in the header sizes */
+ for (i = 0; i < c->segnum; i++, seg = seg->next) {
+ if (0 == seg)
+ return -1;
+ *datasz += seg->len;
+ header[1 + i] = capn_flip32(seg->len / 8);
+ }
+ if (0 != seg)
+ return -1;
+
+ return 0;
+}
+
+static int capn_write_mem_packed(struct capn *c, uint8_t *p, size_t sz)
+{
+ struct capn_segment *seg;
+ struct capn_ptr root;
+ uint32_t headerlen;
+ size_t headersz, datasz = 0;
+ uint32_t *header;
+ struct capn_stream z;
+ int ret;
+
+ root = capn_root(c);
+ header_calc(c, &headerlen, &headersz);
+ header = (uint32_t*) (p + headersz + 2); /* must reserve two bytes for worst case expansion */
+
+ if (sz < headersz*2 + 2) /* We must have space for temporary writing of header to deflate */
+ return -1;
+
+ ret = header_render(c, root.seg, header, headerlen, &datasz);
+ if (ret != 0)
+ return -1;
+
+ memset(&z, 0, sizeof(z));
+ z.next_in = (uint8_t *)header;
+ z.avail_in = headersz;
+ z.next_out = p;
+ z.avail_out = sz;
+
+ // pack the headers
+ ret = capn_deflate(&z);
+ if (ret != 0 || z.avail_in != 0)
+ return -1;
+
+ for (seg = root.seg; seg; seg = seg->next) {
+ z.next_in = (uint8_t *)seg->data;
+ z.avail_in = seg->len;
+ ret = capn_deflate(&z);
+ if (ret != 0 || z.avail_in != 0)
+ return -1;
+ }
+
+ return sz - z.avail_out;
+}
+
+int
+capn_write_mem(struct capn *c, uint8_t *p, size_t sz, int packed)
+{
+ struct capn_segment *seg;
+ struct capn_ptr root;
+ uint32_t headerlen;
+ size_t headersz, datasz = 0;
+ uint32_t *header;
+ int ret;
+
+ if (c->segnum == 0)
+ return -1;
+
+ if (packed)
+ return capn_write_mem_packed(c, p, sz);
+
+ root = capn_root(c);
+ header_calc(c, &headerlen, &headersz);
+ header = (uint32_t*) p;
+
+ if (sz < headersz)
+ return -1;
+
+ ret = header_render(c, root.seg, header, headerlen, &datasz);
+ if (ret != 0)
+ return -1;
+
+ if (sz < headersz + datasz)
+ return -1;
+
+ p += headersz;
+
+ for (seg = root.seg; seg; seg = seg->next) {
+ memcpy(p, seg->data, seg->len);
+ p += seg->len;
+ }
+
+ return headersz+datasz;
+}
+
+static int _write_fd(ssize_t (*write_fd)(int fd, const void *p, size_t count), int fd, void *p, size_t count)
+{
+ ssize_t ret;
+ size_t sent = 0;
+
+ while (sent < count) {
+ ret = write_fd(fd, ((uint8_t*)p)+sent, count-sent);
+ if (ret < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ continue;
+ else
+ return -1;
+ }
+ sent += ret;
+ }
+
+ return 0;
+}
+
+int capn_write_fd(struct capn *c, ssize_t (*write_fd)(int fd, const void *p, size_t count), int fd, int packed)
+{
+ unsigned char buf[4096];
+ struct capn_segment *seg;
+ struct capn_ptr root;
+ uint32_t headerlen;
+ size_t headersz, datasz = 0;
+ int ret;
+ struct capn_stream z;
+ unsigned char *p;
+
+ if (c->segnum == 0)
+ return -1;
+
+ root = capn_root(c);
+ header_calc(c, &headerlen, &headersz);
+
+ if (sizeof(buf) < headersz)
+ return -1;
+
+ ret = header_render(c, root.seg, (uint32_t*)buf, headerlen, &datasz);
+ if (ret != 0)
+ return -1;
+
+ if (packed) {
+ const int headerrem = sizeof(buf) - headersz;
+ const int maxpack = headersz + 2;
+ if (headerrem < maxpack)
+ return -1;
+
+ memset(&z, 0, sizeof(z));
+ z.next_in = buf;
+ z.avail_in = headersz;
+ z.next_out = buf + headersz;
+ z.avail_out = headerrem;
+ ret = capn_deflate(&z);
+ if (ret != 0)
+ return -1;
+
+ p = buf + headersz;
+ headersz = headerrem - z.avail_out;
+ } else {
+ p = buf;
+ }
+
+ ret = _write_fd(write_fd, fd, p, headersz);
+ if (ret < 0)
+ return -1;
+
+ datasz = headersz;
+ for (seg = root.seg; seg; seg = seg->next) {
+ size_t bufsz;
+ if (packed) {
+ memset(&z, 0, sizeof(z));
+ z.next_in = (uint8_t*)seg->data;
+ z.avail_in = seg->len;
+ z.next_out = buf;
+ z.avail_out = sizeof(buf);
+ ret = capn_deflate(&z);
+ if (ret != 0)
+ return -1;
+ p = buf;
+ bufsz = sizeof(buf) - z.avail_out;
+ } else {
+ p = (uint8_t*)seg->data;
+ bufsz = seg->len;
+ }
+ ret = _write_fd(write_fd, fd, p, bufsz);
+ if (ret < 0)
+ return -1;
+ datasz += bufsz;
+ }
+
+ return datasz;
+}