Explorar el Código

Initial Commit

wuhanstudio hace 6 años
commit
a650d69029

+ 15 - 0
SConscript

@@ -0,0 +1,15 @@
+from building import *
+import rtconfig
+
+# get current directory
+cwd     = GetCurrentDir()
+
+# The set of source files associated with this SConscript file.
+src     = Glob('src/*.c')
+path    = [cwd + '/src']
+
+LOCAL_CCFLAGS = ''
+
+group = DefineGroup('capnp', src, depend = ['PKG_USING_CAPNP'], CPPPATH = path, LOCAL_CCFLAGS = LOCAL_CCFLAGS)
+
+Return('group')

BIN
examples/.DS_Store


+ 146 - 0
examples/capnp_encode_decode.c

@@ -0,0 +1,146 @@
+#include <rtthread.h>
+#include <stdio.h>
+#include <string.h>
+#include "capnp_c.h"
+#include "myschema.capnp.h"
+
+static capn_text chars_to_text(const char *chars) {
+  return (capn_text) {
+    .len = (int) strlen(chars),
+    .str = chars,
+    .seg = NULL,
+  };
+}
+
+void capnp_encode_decode_entry(void* parameters)
+{
+  uint8_t buf[4096];
+  ssize_t sz = 0;
+
+  const char *name = "Han Wu";
+  const char *email = "wuhanstudio@domain.com";
+  const char *school = "Huazhong Univ. of Sci. and Tech.";
+
+  printf("--- Encoding ---\n");
+  {
+    // Init memory
+    struct capn c;
+    capn_init_malloc(&c);
+
+    // Get capn object
+    capn_ptr cr = capn_root(&c);
+    struct capn_segment *cs = cr.seg;
+
+    // Set Person
+    struct Person p = {
+      .id = 17,
+      .name = chars_to_text(name),
+      .email = chars_to_text(email),
+    };
+
+    // Set employment
+    p.employment_which = Person_employment_school;
+    p.employment.school = chars_to_text(school);
+
+    // Set PhoneNumber lists
+    p.phones = new_Person_PhoneNumber_list(cs, 2);
+
+    struct Person_PhoneNumber pn0 = {
+      .number = chars_to_text("+8612345678900"),
+      .type = Person_PhoneNumber_Type_work,
+    };
+    set_Person_PhoneNumber(&pn0, p.phones, 0);
+
+    struct Person_PhoneNumber pn1 = {
+      .number = chars_to_text("+8613579246800"),
+      .type = Person_PhoneNumber_Type_home,
+    };
+    set_Person_PhoneNumber(&pn1, p.phones, 1);
+
+    // Write Person to Capnp
+    Person_ptr pp = new_Person(cs);
+    write_Person(&p, pp);
+    capn_setp(cr, 0, pp.p);
+
+    // Write to memory
+    sz = capn_write_mem(&c, buf, sizeof(buf), 0 /* packed */);
+
+    capn_free(&c);
+  }
+  printf("\n");
+
+  // printf("--- Write to File ---\n");
+  // {
+  //   // Write serialized object to file system.
+  //   FILE *f = fopen("./person.bin", "wb");
+  //   fwrite(buf, 1 /* size */, sz /* count */, f);
+  //   fclose(f);
+  // }
+  // printf("\n");
+
+  printf("--- Decoding ---\n");
+  {
+    struct capn rc;
+    int init_mem_ret = capn_init_mem(&rc, buf, sz, 0 /* packed */);
+
+    Person_ptr rroot;
+    struct Person rp;
+
+    // Read data out
+    rroot.p = capn_getp(capn_root(&rc), 0 /* off */, 1 /* resolve */);
+    read_Person(&rp, rroot);
+
+    printf("Person ID: %d\n", rp.id);
+    printf("Person Name: %s\n", rp.name.str);
+    printf("Person Email: %s\n", rp.email.str);
+
+    switch(rp.employment_which) 
+    {
+        case Person_employment_unemployed:
+            printf("Unemployed\n");
+        break;
+
+        case Person_employment_selfEmployed:
+            printf("Self Employed\n");
+        break;
+
+        case Person_employment_employer:
+            printf("Employer: %s\n", rp.employment.employer.str);
+        break;
+
+        case Person_employment_school:
+            printf("School: %s\n", rp.employment.school.str);
+        break;
+
+        default:
+        printf("Unknown Employment Status\n");
+        break;
+    }
+
+    struct Person_PhoneNumber rpn0;
+    get_Person_PhoneNumber(&rpn0, rp.phones, 0);
+    printf("Phone Number 0: %s\n", rpn0.number.str);
+
+    struct Person_PhoneNumber rpn1;
+    get_Person_PhoneNumber(&rpn1, rp.phones, 1);
+    printf("Phone Number 1: %s\n", rpn1.number.str);
+
+    capn_free(&rc);
+  }
+  printf("\n");
+}
+
+static void capnp_encode_decode(int argc,char *argv[])
+{
+    rt_thread_t thread = rt_thread_create("capnp", capnp_encode_decode_entry, RT_NULL, 8192, 25, 10);
+    if(thread != RT_NULL)
+    {
+        rt_thread_startup(thread);
+        rt_kprintf("[capnp] New thread capnp\n");
+    }
+    else
+    {
+        rt_kprintf("[capnp] Failed to create thread capnp\n");
+    }
+}
+MSH_CMD_EXPORT(capnp_encode_decode, capnp encode decode example)

+ 214 - 0
examples/myschema.capnp.c

@@ -0,0 +1,214 @@
+#include "myschema.capnp.h"
+/* AUTO GENERATED - DO NOT EDIT */
+#ifdef __GNUC__
+# define capnp_unused __attribute__((unused))
+# define capnp_use(x) (void) x;
+#else
+# define capnp_unused
+# define capnp_use(x)
+#endif
+
+static const capn_text capn_val0 = {0,"",0};
+
+Person_ptr new_Person(struct capn_segment *s) {
+	Person_ptr p;
+	p.p = capn_new_struct(s, 8, 4);
+	return p;
+}
+Person_list new_Person_list(struct capn_segment *s, int len) {
+	Person_list p;
+	p.p = capn_new_list(s, len, 8, 4);
+	return p;
+}
+void read_Person(struct Person *s capnp_unused, Person_ptr p) {
+	capn_resolve(&p.p);
+	capnp_use(s);
+	s->id = capn_read32(p.p, 0);
+	s->name = capn_get_text(p.p, 0, capn_val0);
+	s->email = capn_get_text(p.p, 1, capn_val0);
+	s->phones.p = capn_getp(p.p, 2, 0);
+	s->employment_which = (enum Person_employment_which)(int) capn_read16(p.p, 4);
+	switch (s->employment_which) {
+	case Person_employment_employer:
+	case Person_employment_school:
+		s->employment.school = capn_get_text(p.p, 3, capn_val0);
+		break;
+	default:
+		break;
+	}
+}
+void write_Person(const struct Person *s capnp_unused, Person_ptr p) {
+	capn_resolve(&p.p);
+	capnp_use(s);
+	capn_write32(p.p, 0, s->id);
+	capn_set_text(p.p, 0, s->name);
+	capn_set_text(p.p, 1, s->email);
+	capn_setp(p.p, 2, s->phones.p);
+	capn_write16(p.p, 4, s->employment_which);
+	switch (s->employment_which) {
+	case Person_employment_employer:
+	case Person_employment_school:
+		capn_set_text(p.p, 3, s->employment.school);
+		break;
+	default:
+		break;
+	}
+}
+void get_Person(struct Person *s, Person_list l, int i) {
+	Person_ptr p;
+	p.p = capn_getp(l.p, i, 0);
+	read_Person(s, p);
+}
+void set_Person(const struct Person *s, Person_list l, int i) {
+	Person_ptr p;
+	p.p = capn_getp(l.p, i, 0);
+	write_Person(s, p);
+}
+
+uint32_t Person_get_id(Person_ptr p)
+{
+	uint32_t id;
+	id = capn_read32(p.p, 0);
+	return id;
+}
+
+capn_text Person_get_name(Person_ptr p)
+{
+	capn_text name;
+	name = capn_get_text(p.p, 0, capn_val0);
+	return name;
+}
+
+capn_text Person_get_email(Person_ptr p)
+{
+	capn_text email;
+	email = capn_get_text(p.p, 1, capn_val0);
+	return email;
+}
+
+Person_PhoneNumber_list Person_get_phones(Person_ptr p)
+{
+	Person_PhoneNumber_list phones;
+	phones.p = capn_getp(p.p, 2, 0);
+	return phones;
+}
+
+void Person_set_id(Person_ptr p, uint32_t id)
+{
+	capn_write32(p.p, 0, id);
+}
+
+void Person_set_name(Person_ptr p, capn_text name)
+{
+	capn_set_text(p.p, 0, name);
+}
+
+void Person_set_email(Person_ptr p, capn_text email)
+{
+	capn_set_text(p.p, 1, email);
+}
+
+void Person_set_phones(Person_ptr p, Person_PhoneNumber_list phones)
+{
+	capn_setp(p.p, 2, phones.p);
+}
+
+Person_PhoneNumber_ptr new_Person_PhoneNumber(struct capn_segment *s) {
+	Person_PhoneNumber_ptr p;
+	p.p = capn_new_struct(s, 8, 1);
+	return p;
+}
+Person_PhoneNumber_list new_Person_PhoneNumber_list(struct capn_segment *s, int len) {
+	Person_PhoneNumber_list p;
+	p.p = capn_new_list(s, len, 8, 1);
+	return p;
+}
+void read_Person_PhoneNumber(struct Person_PhoneNumber *s capnp_unused, Person_PhoneNumber_ptr p) {
+	capn_resolve(&p.p);
+	capnp_use(s);
+	s->number = capn_get_text(p.p, 0, capn_val0);
+	s->type = (enum Person_PhoneNumber_Type)(int) capn_read16(p.p, 0);
+}
+void write_Person_PhoneNumber(const struct Person_PhoneNumber *s capnp_unused, Person_PhoneNumber_ptr p) {
+	capn_resolve(&p.p);
+	capnp_use(s);
+	capn_set_text(p.p, 0, s->number);
+	capn_write16(p.p, 0, (uint16_t) (s->type));
+}
+void get_Person_PhoneNumber(struct Person_PhoneNumber *s, Person_PhoneNumber_list l, int i) {
+	Person_PhoneNumber_ptr p;
+	p.p = capn_getp(l.p, i, 0);
+	read_Person_PhoneNumber(s, p);
+}
+void set_Person_PhoneNumber(const struct Person_PhoneNumber *s, Person_PhoneNumber_list l, int i) {
+	Person_PhoneNumber_ptr p;
+	p.p = capn_getp(l.p, i, 0);
+	write_Person_PhoneNumber(s, p);
+}
+
+capn_text Person_PhoneNumber_get_number(Person_PhoneNumber_ptr p)
+{
+	capn_text number;
+	number = capn_get_text(p.p, 0, capn_val0);
+	return number;
+}
+
+enum Person_PhoneNumber_Type Person_PhoneNumber_get_type(Person_PhoneNumber_ptr p)
+{
+	enum Person_PhoneNumber_Type type;
+	type = (enum Person_PhoneNumber_Type)(int) capn_read16(p.p, 0);
+	return type;
+}
+
+void Person_PhoneNumber_set_number(Person_PhoneNumber_ptr p, capn_text number)
+{
+	capn_set_text(p.p, 0, number);
+}
+
+void Person_PhoneNumber_set_type(Person_PhoneNumber_ptr p, enum Person_PhoneNumber_Type type)
+{
+	capn_write16(p.p, 0, (uint16_t) (type));
+}
+
+AddressBook_ptr new_AddressBook(struct capn_segment *s) {
+	AddressBook_ptr p;
+	p.p = capn_new_struct(s, 0, 1);
+	return p;
+}
+AddressBook_list new_AddressBook_list(struct capn_segment *s, int len) {
+	AddressBook_list p;
+	p.p = capn_new_list(s, len, 0, 1);
+	return p;
+}
+void read_AddressBook(struct AddressBook *s capnp_unused, AddressBook_ptr p) {
+	capn_resolve(&p.p);
+	capnp_use(s);
+	s->people.p = capn_getp(p.p, 0, 0);
+}
+void write_AddressBook(const struct AddressBook *s capnp_unused, AddressBook_ptr p) {
+	capn_resolve(&p.p);
+	capnp_use(s);
+	capn_setp(p.p, 0, s->people.p);
+}
+void get_AddressBook(struct AddressBook *s, AddressBook_list l, int i) {
+	AddressBook_ptr p;
+	p.p = capn_getp(l.p, i, 0);
+	read_AddressBook(s, p);
+}
+void set_AddressBook(const struct AddressBook *s, AddressBook_list l, int i) {
+	AddressBook_ptr p;
+	p.p = capn_getp(l.p, i, 0);
+	write_AddressBook(s, p);
+}
+
+Person_list AddressBook_get_people(AddressBook_ptr p)
+{
+	Person_list people;
+	people.p = capn_getp(p.p, 0, 0);
+	return people;
+}
+
+void AddressBook_set_people(AddressBook_ptr p, Person_list people)
+{
+	capn_setp(p.p, 0, people.p);
+}

+ 142 - 0
examples/myschema.capnp.h

@@ -0,0 +1,142 @@
+#ifndef CAPN_9EB32E19F86EE174
+#define CAPN_9EB32E19F86EE174
+/* AUTO GENERATED - DO NOT EDIT */
+#include <capnp_c.h>
+
+#if CAPN_VERSION != 1
+#error "version mismatch between capnp_c.h and generated code"
+#endif
+
+#ifndef capnp_nowarn
+# ifdef __GNUC__
+#  define capnp_nowarn __extension__
+# else
+#  define capnp_nowarn
+# endif
+#endif
+
+#include "c.capnp.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct Person;
+struct Person_PhoneNumber;
+struct AddressBook;
+
+typedef struct {capn_ptr p;} Person_ptr;
+typedef struct {capn_ptr p;} Person_PhoneNumber_ptr;
+typedef struct {capn_ptr p;} AddressBook_ptr;
+
+typedef struct {capn_ptr p;} Person_list;
+typedef struct {capn_ptr p;} Person_PhoneNumber_list;
+typedef struct {capn_ptr p;} AddressBook_list;
+
+enum Person_PhoneNumber_Type {
+	Person_PhoneNumber_Type_mobile = 0,
+	Person_PhoneNumber_Type_home = 1,
+	Person_PhoneNumber_Type_work = 2
+};
+enum Person_employment_which {
+	Person_employment_unemployed = 0,
+	Person_employment_employer = 1,
+	Person_employment_school = 2,
+	Person_employment_selfEmployed = 3
+};
+
+struct Person {
+	uint32_t id;
+	capn_text name;
+	capn_text email;
+	Person_PhoneNumber_list phones;
+	enum Person_employment_which employment_which;
+	capnp_nowarn union {
+		capn_text employer;
+		capn_text school;
+	} employment;
+};
+
+static const size_t Person_word_count = 1;
+
+static const size_t Person_pointer_count = 4;
+
+static const size_t Person_struct_bytes_count = 40;
+
+uint32_t Person_get_id(Person_ptr p);
+
+capn_text Person_get_name(Person_ptr p);
+
+capn_text Person_get_email(Person_ptr p);
+
+Person_PhoneNumber_list Person_get_phones(Person_ptr p);
+
+void Person_set_id(Person_ptr p, uint32_t id);
+
+void Person_set_name(Person_ptr p, capn_text name);
+
+void Person_set_email(Person_ptr p, capn_text email);
+
+void Person_set_phones(Person_ptr p, Person_PhoneNumber_list phones);
+
+struct Person_PhoneNumber {
+	capn_text number;
+	enum Person_PhoneNumber_Type type;
+};
+
+static const size_t Person_PhoneNumber_word_count = 1;
+
+static const size_t Person_PhoneNumber_pointer_count = 1;
+
+static const size_t Person_PhoneNumber_struct_bytes_count = 16;
+
+capn_text Person_PhoneNumber_get_number(Person_PhoneNumber_ptr p);
+
+enum Person_PhoneNumber_Type Person_PhoneNumber_get_type(Person_PhoneNumber_ptr p);
+
+void Person_PhoneNumber_set_number(Person_PhoneNumber_ptr p, capn_text number);
+
+void Person_PhoneNumber_set_type(Person_PhoneNumber_ptr p, enum Person_PhoneNumber_Type type);
+
+struct AddressBook {
+	Person_list people;
+};
+
+static const size_t AddressBook_word_count = 0;
+
+static const size_t AddressBook_pointer_count = 1;
+
+static const size_t AddressBook_struct_bytes_count = 8;
+
+Person_list AddressBook_get_people(AddressBook_ptr p);
+
+void AddressBook_set_people(AddressBook_ptr p, Person_list people);
+
+Person_ptr new_Person(struct capn_segment*);
+Person_PhoneNumber_ptr new_Person_PhoneNumber(struct capn_segment*);
+AddressBook_ptr new_AddressBook(struct capn_segment*);
+
+Person_list new_Person_list(struct capn_segment*, int len);
+Person_PhoneNumber_list new_Person_PhoneNumber_list(struct capn_segment*, int len);
+AddressBook_list new_AddressBook_list(struct capn_segment*, int len);
+
+void read_Person(struct Person*, Person_ptr);
+void read_Person_PhoneNumber(struct Person_PhoneNumber*, Person_PhoneNumber_ptr);
+void read_AddressBook(struct AddressBook*, AddressBook_ptr);
+
+void write_Person(const struct Person*, Person_ptr);
+void write_Person_PhoneNumber(const struct Person_PhoneNumber*, Person_PhoneNumber_ptr);
+void write_AddressBook(const struct AddressBook*, AddressBook_ptr);
+
+void get_Person(struct Person*, Person_list, int i);
+void get_Person_PhoneNumber(struct Person_PhoneNumber*, Person_PhoneNumber_list, int i);
+void get_AddressBook(struct AddressBook*, AddressBook_list, int i);
+
+void set_Person(const struct Person*, Person_list, int i);
+void set_Person_PhoneNumber(const struct Person_PhoneNumber*, Person_PhoneNumber_list, int i);
+void set_AddressBook(const struct AddressBook*, AddressBook_list, int i);
+
+#ifdef __cplusplus
+}
+#endif
+#endif

BIN
examples/proto/.DS_Store


+ 41 - 0
examples/proto/c.capnp

@@ -0,0 +1,41 @@
+# Copyright (c) 2016 NetDEF, Inc. and contributors
+# Licensed under the MIT License:
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+@0xc0183dd65ffef0f3;
+
+annotation nameinfix @0x85a8d86d736ba637 (file): Text;
+# add an infix (middle insert) for output file names
+#
+# "make" generally has implicit rules for compiling "foo.c" => "foo".  This
+# is very annoying with capnp since the rule will be "foo" => "foo.c", leading
+# to a loop.  $nameinfix (recommended parameter: "-gen") inserts its parameter
+# before the ".c", so the filename becomes "foo-gen.c"
+#
+# Alternatively, add this Makefile rule to disable compiling "foo.capnp.c" -> "foo.capnp":
+#   %.capnp: ;
+#
+#
+# ("foo" is really "foo.capnp", so it's foo.capnp-gen.c)
+
+annotation fieldgetset @0xf72bc690355d66de (file): Void;
+# generate getter & setter functions for accessing fields
+#
+# allows grabbing/putting values without de-/encoding the entire struct.

+ 35 - 0
examples/proto/myschema.capnp

@@ -0,0 +1,35 @@
+
+@0x9eb32e19f86ee174;
+
+using C = import "c.capnp";
+$C.fieldgetset;
+
+struct Person {
+  id @0 :UInt32;
+  name @1 :Text;
+  email @2 :Text;
+  phones @3 :List(PhoneNumber);
+
+  struct PhoneNumber {
+    number @0 :Text;
+    type @1 :Type;
+
+    enum Type {
+      mobile @0;
+      home @1;
+      work @2;
+    }
+  }
+
+  employment :union {
+    unemployed @4 :Void;
+    employer @5 :Text;
+    school @6 :Text;
+    selfEmployed @7 :Void;
+    # We assume that a person is only one of these.
+  }
+}
+
+struct AddressBook {
+  people @0 :List(Person);
+}

+ 2 - 0
src/c.capnp.c

@@ -0,0 +1,2 @@
+#include "c.capnp.h"
+/* AUTO GENERATED - DO NOT EDIT */

+ 27 - 0
src/c.capnp.h

@@ -0,0 +1,27 @@
+#ifndef CAPN_C0183DD65FFEF0F3
+#define CAPN_C0183DD65FFEF0F3
+/* AUTO GENERATED - DO NOT EDIT */
+#include "capnp_c.h"
+
+#if CAPN_VERSION != 1
+#error "version mismatch between capnp_c.h and generated code"
+#endif
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+
+
+
+
+
+
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif

+ 162 - 0
src/capn-list.inc

@@ -0,0 +1,162 @@
+/* capn-list.inc
+ *
+ * Copyright (C) 2013 James McKaskill
+ *
+ * This software may be modified and distributed under the terms
+ * of the MIT license.  See the LICENSE file for details.
+ */
+
+#define CAT2(A,B) A ## B
+#define CAT(A,B) CAT2(A, B)
+#define UINT_T CAT(CAT(uint, SZ), _t)
+#define LIST_T CAT(capn_list, SZ)
+#define FLIP CAT(capn_flip, SZ)
+
+UINT_T CAT(capn_get,SZ) (LIST_T l, int off) {
+	char *d;
+	capn_ptr p = l.p;
+	if (off >= p.len) {
+		return 0;
+	}
+
+	switch (p.type) {
+	case CAPN_LIST:
+		if (p.datasz < SZ/8)
+			return 0;
+		d = p.data + off * (p.datasz + 8*p.ptrs);
+		return FLIP(*(UINT_T*)d);
+
+	case CAPN_PTR_LIST:
+		d = struct_ptr(p.seg, p.data + 8*off, SZ/8);
+		if (d) {
+			return FLIP(*(UINT_T*)d);
+		} else {
+			return 0;
+		}
+
+	default:
+		return 0;
+	}
+}
+
+int CAT(capn_getv,SZ) (LIST_T l, int off, UINT_T *to, int sz) {
+	int i;
+	capn_ptr p;
+	capn_resolve(&l.p);
+	p = l.p;
+	if (off + sz > p.len) {
+		sz = p.len - off;
+	}
+
+	switch (p.type) {
+	case CAPN_LIST:
+		if (p.datasz == SZ/8 && !p.ptrs && (SZ == 8 || CAPN_LITTLE)) {
+			memcpy(to, p.data + off, sz * (SZ/8));
+			return sz;
+		} else if (p.datasz < SZ/8) {
+			return -1;
+		}
+
+		for (i = 0; i < sz; i++) {
+			char *d = p.data + (i + off) * (p.datasz + 8*p.ptrs);
+			to[i] = FLIP(*(UINT_T*)d);
+		}
+		return sz;
+
+	case CAPN_PTR_LIST:
+		for (i = 0; i < sz; i++) {
+			char *d = struct_ptr(p.seg, p.data + 8*(i+off), SZ/8);
+			if (d) {
+				to[i] = FLIP(*(UINT_T*)d);
+			} else {
+				return -1;
+			}
+		}
+		return sz;
+
+	default:
+		return -1;
+	}
+}
+
+int CAT(capn_set,SZ) (LIST_T l, int off, UINT_T v) {
+	char *d;
+	capn_ptr p = l.p;
+	if (off >= p.len) {
+		return -1;
+	}
+
+	switch (p.type) {
+	case CAPN_LIST:
+		if (p.datasz < SZ/8)
+			return -1;
+		d = p.data + off * (p.datasz + 8*p.ptrs);
+		*(UINT_T*) d = FLIP(v);
+		return 0;
+
+	case CAPN_PTR_LIST:
+		d = struct_ptr(p.seg, p.data + 8*off, SZ/8);
+		if (!d) {
+			return -1;
+		}
+		*(UINT_T*) d = FLIP(v);
+		return 0;
+
+	default:
+		return -1;
+	}
+}
+
+int CAT(capn_setv,SZ) (LIST_T l, int off, const UINT_T *from, int sz) {
+	int i;
+	capn_ptr p = l.p;
+	if (off + sz > p.len) {
+		sz = p.len - off;
+	}
+
+	switch (p.type) {
+	case CAPN_LIST:
+		if (p.datasz == SZ/8 && !p.ptrs && (SZ == 8 || CAPN_LITTLE)) {
+			memcpy(p.data + off, from, sz * (SZ/8));
+			return sz;
+		} else if (p.datasz < SZ/8) {
+			return -1;
+		}
+
+		for (i = 0; i < sz; i++) {
+			char *d = p.data + (i + off) * (p.datasz + 8*p.ptrs);
+			*(UINT_T*) d = FLIP(from[i]);
+		}
+		return sz;
+
+	case CAPN_PTR_LIST:
+		for (i = 0; i < sz; i++) {
+			char *d = struct_ptr(p.seg, p.data + 8*(i+off), SZ/8);
+			if (d) {
+				*(UINT_T*) d = FLIP(from[i]);
+			} else {
+				return -1;
+			}
+		}
+		return sz;
+
+	default:
+		return -1;
+	}
+}
+
+LIST_T CAT(capn_new_list,SZ) (struct capn_segment *seg, int sz) {
+	LIST_T l = {{CAPN_LIST}};
+	l.p.seg = seg;
+	l.p.len = sz;
+	l.p.datasz = SZ/8;
+	new_object(&l.p, sz*(SZ/8));
+	return l;
+}
+
+#undef CAT2
+#undef CAT
+#undef UINT_T
+#undef LIST_T
+#undef FLIP
+

+ 409 - 0
src/capn-malloc.c

@@ -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;
+}

+ 217 - 0
src/capn-stream.c

@@ -0,0 +1,217 @@
+/* vim: set sw=8 ts=8 sts=8 noet: */
+/* capn-stream.c
+ *
+ * Copyright (C) 2013 James McKaskill
+ *
+ * This software may be modified and distributed under the terms
+ * of the MIT license.  See the LICENSE file for details.
+ */
+
+#include "capnp_c.h"
+#include "capnp_priv.h"
+#include <string.h>
+
+#ifndef min
+static unsigned min(unsigned a, unsigned b) { return (a < b) ? a : b; }
+#endif
+
+int capn_deflate(struct capn_stream* s) {
+	if (s->avail_in % 8) {
+		return CAPN_MISALIGNED;
+	}
+
+	while (s->avail_in) {
+		int i;
+		size_t sz;
+		uint8_t hdr = 0;
+		uint8_t *p;
+
+		if (!s->avail_out)
+			return CAPN_NEED_MORE;
+
+		if (s->raw > 0) {
+			sz = min(s->raw, min(s->avail_in, s->avail_out));
+			memcpy(s->next_out, s->next_in, sz);
+			s->next_out += sz;
+			s->next_in += sz;
+			s->avail_out -= sz;
+			s->avail_in -= sz;
+			s->raw -= sz;
+			continue;
+		}
+
+		if (s->avail_in < 8)
+			return CAPN_NEED_MORE;
+
+		sz = 0;
+		for (i = 0; i < 8; i++) {
+			if (s->next_in[i]) {
+				sz ++;
+				hdr |= 1 << i;
+			}
+		}
+
+		switch (sz) {
+		case 0:
+			if (s->avail_out < 2)
+				return CAPN_NEED_MORE;
+
+			s->next_out[0] = 0;
+			for (sz = 1; sz < min(s->avail_in/8, 256); sz++) {
+				if (((uint64_t*) s->next_in)[sz] != 0) {
+					break;
+				}
+			}
+
+			s->next_out[1] = (uint8_t) (sz-1);
+			s->next_in += sz*8;
+			s->avail_in -= sz*8;
+			s->next_out += 2;
+			s->avail_out -= 2;
+			continue;
+
+		case 8:
+			if (s->avail_out < 10)
+				return CAPN_NEED_MORE;
+
+			s->next_out[0] = 0xFF;
+			memcpy(s->next_out+1, s->next_in, 8);
+			s->next_in += 8;
+			s->avail_in -= 8;
+
+			s->raw = min(s->avail_in, 256*8);
+			if ((p = (uint8_t*) memchr(s->next_in, 0, s->raw)) != NULL) {
+				s->raw = (p - s->next_in) & ~7;
+			}
+
+			s->next_out[9] = (uint8_t) (s->raw/8);
+			s->next_out += 10;
+			s->avail_out -= 10;
+			continue;
+
+		default:
+			if (s->avail_out < 1U + sz)
+				return CAPN_NEED_MORE;
+
+			*(s->next_out++) = hdr;
+			for (i = 0; i < 8; i++) {
+				if (s->next_in[i]) {
+					*(s->next_out++) = s->next_in[i];
+				}
+			}
+			s->avail_out -= sz + 1;
+			s->next_in += 8;
+			s->avail_in -= 8;
+			continue;
+		}
+	}
+
+	return 0;
+}
+
+int capn_inflate(struct capn_stream* s) {
+	while (s->avail_out) {
+		int i;
+		size_t sz;
+		uint8_t hdr;
+		uint8_t *wr;
+
+		if (s->avail_buf && s->avail_out >= s->avail_buf) {
+			memcpy(s->next_out, s->inflate_buf, s->avail_buf);
+			s->next_out += s->avail_buf;
+			s->avail_out -= s->avail_buf;
+			s->avail_buf = 0;
+			if (!s->avail_out)
+				return 0;
+		}
+		if (s->avail_buf && s->avail_out < s->avail_buf) {
+			memcpy(s->next_out, s->inflate_buf, s->avail_out);
+			memmove(s->inflate_buf, s->inflate_buf + s->avail_out,
+					s->avail_buf - s->avail_out);
+			s->avail_buf -= s->avail_out;
+			s->avail_out = 0;
+			return 0;
+		}
+
+		if (s->zeros > 0) {
+			sz = min(s->avail_out, s->zeros);
+			memset(s->next_out, 0, sz);
+			s->next_out += sz;
+			s->avail_out -= sz;
+			s->zeros -= sz;
+			continue;
+		}
+
+		if (s->raw > 0) {
+			if (s->avail_in == 0)
+				return CAPN_NEED_MORE;
+
+			sz = min(min(s->avail_out, s->raw), s->avail_in);
+			memcpy(s->next_out, s->next_in, sz);
+			s->next_in += sz;
+			s->next_out += sz;
+			s->avail_in -= sz;
+			s->avail_out -= sz;
+			s->raw -= sz;
+			continue;
+		}
+
+		if (s->avail_in == 0)
+			return 0;
+		else if (s->avail_in < 2)
+			return CAPN_NEED_MORE;
+
+		switch (s->next_in[0]) {
+		case 0xFF:
+			/* 0xFF is followed by 8 bytes raw, followed by
+			 * a byte with length in words to read raw */
+			if (s->avail_in < 10)
+				return CAPN_NEED_MORE;
+
+			memcpy(s->inflate_buf, s->next_in+1, 8);
+			s->avail_buf = 8;
+
+			s->raw = s->next_in[9] * 8;
+			s->next_in += 10;
+			s->avail_in -= 10;
+			continue;
+
+		case 0x00:
+			/* 0x00 is followed by a single byte indicating
+			 * the count of consecutive zero value words
+			 * minus 1 */
+			s->zeros = (s->next_in[1] + 1) * 8;
+			s->next_in += 2;
+			s->avail_in -= 2;
+			continue;
+
+		default:
+			hdr = s->next_in[0];
+			sz = 0;
+			for (i = 0; i < 8; i++) {
+				if (hdr & (1 << i))
+					sz++;
+			}
+			if (s->avail_in < 1U + sz)
+				return CAPN_NEED_MORE;
+
+			s->next_in += 1;
+
+			wr = s->inflate_buf;
+			for (i = 0; i < 8; i++) {
+				if (hdr & (1 << i)) {
+					*wr++ = *s->next_in++;
+				} else {
+					*wr++ = 0;
+				}
+			}
+
+			s->avail_buf = 8;
+			s->avail_in -= 1 + sz;
+			continue;
+		}
+	}
+
+	return 0;
+}
+

+ 1115 - 0
src/capn.c

@@ -0,0 +1,1115 @@
+/* vim: set sw=8 ts=8 sts=8 noet: */
+/* capn.c
+ *
+ * Copyright (C) 2013 James McKaskill
+ *
+ * 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 "-Wmissing-field-initializers"
+#endif
+
+#include "capnp_c.h"
+
+#include <stdlib.h>
+#include <string.h>
+#ifndef _MSC_VER
+#include <sys/param.h>
+#endif
+
+#define STRUCT_PTR 0
+#define LIST_PTR 1
+#define FAR_PTR 2
+#define DOUBLE_PTR 6
+
+#define VOID_LIST 0
+#define BIT_1_LIST 1
+#define BYTE_1_LIST 2
+#define BYTE_2_LIST 3
+#define BYTE_4_LIST 4
+#define BYTE_8_LIST 5
+#define PTR_LIST 6
+#define COMPOSITE_LIST 7
+
+#define U64(val) ((uint64_t) (val))
+#define I64(val) ((int64_t) (val))
+#define U32(val) ((uint32_t) (val))
+#define I32(val) ((int32_t) (val))
+#define U16(val) ((uint16_t) (val))
+#define I16(val) ((int16_t) (val))
+
+#ifndef min
+static int min(int a, int b) { return (a < b) ? a : b; }
+#endif
+
+#ifdef BYTE_ORDER
+#define CAPN_LITTLE (BYTE_ORDER == LITTLE_ENDIAN)
+#elif defined(__BYTE_ORDER)
+#define CAPN_LITTLE (__BYTE_ORDER == __LITTLE_ENDIAN)
+#else
+#define CAPN_LITTLE 0
+#endif
+
+struct capn_tree *capn_tree_insert(struct capn_tree *root, struct capn_tree *n) {
+	n->red = 1;
+	n->link[0] = n->link[1] = NULL;
+
+	for (;;) {
+		/* parent, uncle, grandparent, great grandparent link */
+		struct capn_tree *p, *u, *g, **gglink;
+		int dir;
+
+		/* Case 1: N is root */
+		p = n->parent;
+		if (!p) {
+			n->red = 0;
+			root = n;
+			break;
+		}
+
+		/* Case 2: p is black */
+		if (!p->red) {
+			break;
+		}
+
+		g = p->parent;
+		dir = (p == g->link[1]);
+
+		/* Case 3: P and U are red, switch g to red, but must
+		 * loop as G could be root or have a red parent
+		 *     g    to   G
+		 *    / \       / \
+		 *   P   U     p   u
+		 *  /         /
+		 * N         N
+		 */
+		u = g->link[!dir];
+		if (u != NULL && u->red) {
+			p->red = 0;
+			u->red = 0;
+			g->red = 1;
+			n = g;
+			continue;
+		}
+
+		if (!g->parent) {
+			gglink = &root;
+		} else if (g->parent->link[1] == g) {
+			gglink = &g->parent->link[1];
+		} else {
+			gglink = &g->parent->link[0];
+		}
+
+		if (dir != (n == p->link[1])) {
+			/* Case 4: rotate on P, then on g
+			 * here dir is /
+			 *     g    to   g   to   n
+			 *    / \       / \      / \
+			 *   P   u     N   u    P   G
+			 *  / \       / \      /|  / \
+			 * 1   N     P   3    1 2 3   u
+			 *    / \   / \
+			 *   2   3 1   2
+			 */
+			struct capn_tree *two = n->link[dir];
+			struct capn_tree *three = n->link[!dir];
+			p->link[!dir] = two;
+			g->link[dir] = three;
+			n->link[dir] = p;
+			n->link[!dir] = g;
+			*gglink = n;
+			n->parent = g->parent;
+			p->parent = n;
+			g->parent = n;
+			if (two)
+				two->parent = p;
+			if (three)
+				three->parent = g;
+			n->red = 0;
+			g->red = 1;
+		} else {
+			/* Case 5: rotate on g
+			 * here dir is /
+			 *       g   to   p
+			 *      / \      / \
+			 *     P   u    N   G
+			 *    / \      /|  / \
+			 *   N   3    1 2 3   u
+			 *  / \
+			 * 1   2
+			 */
+			struct capn_tree *three = p->link[!dir];
+			g->link[dir] = three;
+			p->link[!dir] = g;
+			*gglink = p;
+			p->parent = g->parent;
+			g->parent = p;
+			if (three)
+				three->parent = g;
+			g->red = 1;
+			p->red = 0;
+		}
+
+		break;
+	}
+
+	return root;
+}
+
+void capn_append_segment(struct capn *c, struct capn_segment *s) {
+	s->id = c->segnum++;
+	s->capn = c;
+	s->next = NULL;
+
+	if (c->lastseg) {
+		c->lastseg->next = s;
+		c->lastseg->hdr.link[1] = &s->hdr;
+		s->hdr.parent = &c->lastseg->hdr;
+	} else {
+		c->seglist = s;
+		s->hdr.parent = NULL;
+	}
+
+	c->lastseg = s;
+	c->segtree = capn_tree_insert(c->segtree, &s->hdr);
+}
+
+static char *new_data(struct capn *c, int sz, struct capn_segment **ps) {
+	struct capn_segment *s;
+
+	/* find a segment with sufficient data */
+	for (s = c->seglist; s != NULL; s = s->next) {
+		if (s->len + sz <= s->cap) {
+			goto end;
+		}
+	}
+
+	s = c->create ? c->create(c->user, c->segnum, sz) : NULL;
+	if (!s) {
+		*ps = NULL;
+		return NULL;
+	}
+
+	capn_append_segment(c, s);
+end:
+	*ps = s;
+	s->len += sz;
+	return s->data + s->len - sz;
+}
+
+static struct capn_segment *lookup_segment(struct capn* c, struct capn_segment *s, uint32_t id) {
+	struct capn_tree **x;
+	struct capn_segment *y = NULL;
+
+	if (s && s->id == id)
+		return s;
+	if (!c)
+		return NULL;
+
+	if (id < c->segnum) {
+		x = &c->segtree;
+		while (*x) {
+			y = (struct capn_segment*) *x;
+			if (id == y->id) {
+				return y;
+			} else if (id < y->id) {
+				x = &y->hdr.link[0];
+			} else {
+				x = &y->hdr.link[1];
+			}
+		}
+	} else {
+		/* Otherwise `x` may be uninitialized */
+		return NULL;
+	}
+
+	s = c->lookup ? c->lookup(c->user, id) : NULL;
+	if (!s)
+		return NULL;
+
+	if (id < c->segnum) {
+		s->id = id;
+		s->capn = c;
+		s->next = c->seglist;
+		c->seglist = s;
+		s->hdr.parent = &y->hdr;
+		*x = &s->hdr;
+		c->segtree = capn_tree_insert(c->segtree, &s->hdr);
+	} else {
+		c->segnum = id;
+		capn_append_segment(c, s);
+	}
+
+	return s;
+}
+
+static uint64_t lookup_double(struct capn_segment **s, char **d, uint64_t val) {
+	uint64_t far, tag;
+	size_t off = (U32(val) >> 3) * 8;
+	char *p;
+
+	if ((*s = lookup_segment((*s)->capn, *s, U32(val >> 32))) == NULL) {
+		return 0;
+	}
+
+	p = (*s)->data + off;
+	if (off + 16 > (*s)->len) {
+		return 0;
+	}
+
+	far = capn_flip64(*(uint64_t*) p);
+	tag = capn_flip64(*(uint64_t*) (p+8));
+
+	/* the far tag should not be another double, and the tag
+	 * should be struct/list and have no offset */
+	if ((far&7) != FAR_PTR || U32(tag) > LIST_PTR) {
+		return 0;
+	}
+
+	if ((*s = lookup_segment((*s)->capn, *s, U32(far >> 32))) == NULL) {
+		return 0;
+	}
+
+	/* -8 because far pointers reference from the start of
+	 * the segment, but offsets reference the end of the
+	 * pointer data. Here *d points to where an equivalent
+	 * ptr would be.
+	 */
+	*d = (*s)->data - 8;
+	return U64(U32(far) >> 3 << 2) | tag;
+}
+
+static uint64_t lookup_far(struct capn_segment **s, char **d, uint64_t val) {
+	size_t off = (U32(val) >> 3) * 8;
+
+	if ((*s = lookup_segment((*s)->capn, *s, U32(val >> 32))) == NULL) {
+		return 0;
+	}
+
+	if (off + 8 > (*s)->len) {
+		return 0;
+	}
+
+	*d = (*s)->data + off;
+	return capn_flip64(*(uint64_t*)*d);
+}
+
+static char *struct_ptr(struct capn_segment *s, char *d, int minsz) {
+	uint64_t val = capn_flip64(*(uint64_t*)d);
+	uint16_t datasz;
+
+	switch (val&7) {
+	case FAR_PTR:
+		val = lookup_far(&s, &d, val);
+		break;
+	case DOUBLE_PTR:
+		val = lookup_double(&s, &d, val);
+		break;
+	}
+
+	datasz = U16(val >> 32);
+	d += (I32(U32(val)) << 1) + 8;
+
+	if (val != 0 && (val&3) != STRUCT_PTR && datasz >= minsz && s->data <= d && d < s->data + s->len) {
+		return d;
+	}
+
+	return NULL;
+}
+
+static capn_ptr read_ptr(struct capn_segment *s, char *d) {
+	capn_ptr ret = {CAPN_NULL};
+	uint64_t val;
+	char *e = 0;
+
+	val = capn_flip64(*(uint64_t*) d);
+
+	switch (val&7) {
+	case FAR_PTR:
+		val = lookup_far(&s, &d, val);
+		ret.has_ptr_tag = (U32(val) >> 2) == 0;
+		break;
+	case DOUBLE_PTR:
+		val = lookup_double(&s, &d, val);
+		break;
+	}
+
+	d += (I32(U32(val)) >> 2) * 8 + 8;
+
+	if (d < s->data) {
+		goto err;
+	}
+
+	switch (val & 3) {
+	case STRUCT_PTR:
+		ret.type = val ? CAPN_STRUCT : CAPN_NULL;
+		goto struct_common;
+
+	struct_common:
+		ret.datasz = U32(U16(val >> 32)) * 8;
+		ret.ptrs = U32(U16(val >> 48));
+		e = d + ret.datasz + 8 * ret.ptrs;
+		break;
+
+	case LIST_PTR:
+		ret.type = CAPN_LIST;
+		ret.len = val >> 35;
+
+		switch ((val >> 32) & 7) {
+		case VOID_LIST:
+			e = d;
+			break;
+		case BIT_1_LIST:
+			ret.type = CAPN_BIT_LIST;
+			ret.datasz = (ret.len+7)/8;
+			e = d + ret.datasz;
+			break;
+		case BYTE_1_LIST:
+			ret.datasz = 1;
+			e = d + ret.len;
+			break;
+		case BYTE_2_LIST:
+			ret.datasz = 2;
+			e = d + ret.len * 2;
+			break;
+		case BYTE_4_LIST:
+			ret.datasz = 4;
+			e = d + ret.len * 4;
+			break;
+		case BYTE_8_LIST:
+			ret.datasz = 8;
+			e = d + ret.len * 8;
+			break;
+		case PTR_LIST:
+			ret.type = CAPN_PTR_LIST;
+			e = d + ret.len * 8;
+			break;
+		case COMPOSITE_LIST:
+			if ((size_t)((d+8) - s->data) > s->len) {
+				goto err;
+			}
+
+			val = capn_flip64(*(uint64_t*) d);
+
+			d += 8;
+			e = d + ret.len * 8;
+
+			ret.datasz = U32(U16(val >> 32)) * 8;
+			ret.ptrs = U32(U16(val >> 48));
+			ret.len = U32(val) >> 2;
+			ret.is_composite_list = 1;
+
+			if ((ret.datasz + 8*ret.ptrs) * ret.len != e - d) {
+				goto err;
+			}
+			break;
+		}
+		break;
+
+	default:
+		goto err;
+	}
+
+	if ((size_t)(e - s->data) > s->len)
+		goto err;
+
+	ret.data = d;
+	ret.seg = s;
+	return ret;
+err:
+	memset(&ret, 0, sizeof(ret));
+	return ret;
+}
+
+void capn_resolve(capn_ptr *p) {
+	if (p->type == CAPN_FAR_POINTER) {
+		*p = read_ptr(p->seg, p->data);
+	}
+}
+
+/* TODO: should this handle CAPN_BIT_LIST? */
+capn_ptr capn_getp(capn_ptr p, int off, int resolve) {
+	capn_ptr ret = {CAPN_FAR_POINTER};
+	ret.seg = p.seg;
+
+	capn_resolve(&p);
+
+	switch (p.type) {
+	case CAPN_LIST:
+		/* Return an inner pointer */
+		if (off < p.len) {
+			capn_ptr ret = {CAPN_STRUCT};
+			ret.is_list_member = 1;
+			ret.data = p.data + off * (p.datasz + 8*p.ptrs);
+			ret.seg = p.seg;
+			ret.datasz = p.datasz;
+			ret.ptrs = p.ptrs;
+			return ret;
+		} else {
+			goto err;
+		}
+
+	case CAPN_STRUCT:
+		if (off >= p.ptrs) {
+			goto err;
+		}
+		ret.data = p.data + p.datasz + 8*off;
+		break;
+
+	case CAPN_PTR_LIST:
+		if (off >= p.len) {
+			goto err;
+		}
+		ret.data = p.data + 8*off;
+		break;
+
+	default:
+		goto err;
+	}
+
+	if (resolve) {
+		ret = read_ptr(ret.seg, ret.data);
+	}
+
+	return ret;
+
+err:
+	memset(&p, 0, sizeof(p));
+	return p;
+}
+
+static void write_ptr_tag(char *d, capn_ptr p, int off) {
+	uint64_t val = U64(U32(I32(off/8) << 2));
+
+	switch (p.type) {
+	case CAPN_STRUCT:
+		val |= STRUCT_PTR | (U64(p.datasz/8) << 32) | (U64(p.ptrs) << 48);
+		break;
+
+	case CAPN_LIST:
+		if (p.is_composite_list) {
+			val |= LIST_PTR | (U64(COMPOSITE_LIST) << 32) | (U64(p.len * (p.datasz/8 + p.ptrs)) << 35);
+		} else {
+			val |= LIST_PTR | (U64(p.len) << 35);
+
+			switch (p.datasz) {
+			case 8:
+				val |= (U64(BYTE_8_LIST) << 32);
+				break;
+			case 4:
+				val |= (U64(BYTE_4_LIST) << 32);
+				break;
+			case 2:
+				val |= (U64(BYTE_2_LIST) << 32);
+				break;
+			case 1:
+				val |= (U64(BYTE_1_LIST) << 32);
+				break;
+			case 0:
+				val |= (U64(VOID_LIST) << 32);
+				break;
+			}
+		}
+		break;
+
+	case CAPN_BIT_LIST:
+		val |= LIST_PTR | (U64(BIT_1_LIST) << 32) | (U64(p.len) << 35);
+		break;
+
+	case CAPN_PTR_LIST:
+		val |= LIST_PTR | (U64(PTR_LIST) << 32) | (U64(p.len) << 35);
+		break;
+
+	default:
+		val = 0;
+		break;
+	}
+
+	*(uint64_t*) d = capn_flip64(val);
+}
+
+static void write_far_ptr(char *d, struct capn_segment *s, char *tgt) {
+	*(uint64_t*) d = capn_flip64(FAR_PTR | U64(tgt - s->data) | (U64(s->id) << 32));
+}
+
+static void write_double_far(char *d, struct capn_segment *s, char *tgt) {
+	*(uint64_t*) d = capn_flip64(DOUBLE_PTR | U64(tgt - s->data) | (U64(s->id) << 32));
+}
+
+#define NEED_TO_COPY 1
+
+static int write_ptr(struct capn_segment *s, char *d, capn_ptr p) {
+	/* note p.seg can be NULL if its a ptr to static data */
+	char *pdata = p.data - 8*p.is_composite_list;
+
+	if (p.type == CAPN_NULL || (p.type == CAPN_STRUCT && p.datasz == 0 && p.ptrs == 0)) {
+		write_ptr_tag(d, p, 0);
+		return 0;
+
+	} else if (!p.seg || p.seg->capn != s->capn || p.is_list_member) {
+		return NEED_TO_COPY;
+
+	} else if (p.seg == s) {
+		write_ptr_tag(d, p, pdata - d - 8);
+		return 0;
+
+	} else if (p.has_ptr_tag) {
+		/* By lucky chance, the data has a tag in front
+		 * of it. This happens when new_object had to move
+		 * the data to a new segment. */
+		write_far_ptr(d, p.seg, pdata-8);
+		return 0;
+
+	} else if (p.seg->len + 8 <= p.seg->cap) {
+		/* The target segment has enough room for tag */
+		char *t = p.seg->data + p.seg->len;
+		write_ptr_tag(t, p, pdata - t - 8);
+		write_far_ptr(d, p.seg, t);
+		p.seg->len += 8;
+		return 0;
+
+	} else {
+		/* have to allocate room for a double far
+		 * pointer */
+		char *t;
+
+		if (s->len + 16 <= s->cap) {
+			/* Try and allocate in the src segment
+			 * first. This should improve lookup on
+			 * read. */
+			t = s->data + s->len;
+			s->len += 16;
+		} else {
+			t = new_data(s->capn, 16, &s);
+			if (!t) return -1;
+		}
+
+		write_far_ptr(t, p.seg, pdata);
+		write_ptr_tag(t+8, p, 0);
+		write_double_far(d, s, t);
+		return 0;
+	}
+}
+
+struct copy {
+	struct capn_tree hdr;
+	struct capn_ptr to, from;
+	char *fbegin, *fend;
+};
+
+static capn_ptr new_clone(struct capn_segment *s, capn_ptr p) {
+	switch (p.type) {
+	case CAPN_STRUCT:
+		return capn_new_struct(s, p.datasz, p.ptrs);
+	case CAPN_PTR_LIST:
+		return capn_new_ptr_list(s, p.len);
+	case CAPN_BIT_LIST:
+		return capn_new_list1(s, p.len).p;
+	case CAPN_LIST:
+		return capn_new_list(s, p.len, p.datasz, p.ptrs);
+	default:
+		return p;
+	}
+}
+
+static int is_ptr_equal(const struct capn_ptr *a, const struct capn_ptr *b) {
+	return a->data == b->data
+		&& a->type == b->type
+		&& a->len == b->len
+		&& a->datasz == b->datasz
+		&& a->ptrs == b->ptrs;
+}
+
+static int data_size(struct capn_ptr p) {
+	switch (p.type) {
+	case CAPN_BIT_LIST:
+		return p.datasz;
+	case CAPN_PTR_LIST:
+		return p.len*8;
+	case CAPN_STRUCT:
+		return p.datasz + 8*p.ptrs;
+	case CAPN_LIST:
+		return p.len * (p.datasz + 8*p.ptrs) + 8*p.is_composite_list;
+	default:
+		return 0;
+	}
+}
+
+static int copy_ptr(struct capn_segment *seg, char *data, struct capn_ptr *t, struct capn_ptr *f, int *dep) {
+	struct capn *c = seg->capn;
+	struct copy *cp = NULL;
+	struct capn_tree **xcp;
+	char *fbegin = f->data - 8*f->is_composite_list;
+	char *fend = fbegin + data_size(*f);
+	int zero_sized = (fend == fbegin);
+
+	/* We always copy list members as it would otherwise be an
+	 * overlapped pointer (the data is owned by the enclosing list).
+	 * We do not bother with the overlapped lookup for zero sized
+	 * structures/lists as they never overlap. Nor do we add them to
+	 * the copy list as there is no data to be shared by multiple
+	 * pointers.
+	 */
+
+	xcp = &c->copy;
+	while (*xcp && !zero_sized) {
+		cp = (struct copy*) *xcp;
+		if (fend <= cp->fbegin) {
+			xcp = &cp->hdr.link[0];
+		} else if (cp->fend <= fbegin) {
+			xcp = &cp->hdr.link[1];
+		} else if (is_ptr_equal(f, &cp->from)) {
+			/* we already have a copy so just point to that */
+			return write_ptr(seg, data, cp->to);
+		} else {
+			/* pointer to overlapped data */
+			return -1;
+		}
+	}
+
+	/* no copy found - have to create a new copy */
+	*t = new_clone(seg, *f);
+
+	if (write_ptr(seg, data, *t))
+		return -1;
+
+	/* add the copy to the copy tree so we can look for overlapping
+	 * source pointers and handle recursive structures */
+	if (!zero_sized) {
+		struct copy *n;
+		struct capn_segment *cs = c->copylist;
+
+		/* need to allocate a struct copy */
+		if (!cs || cs->len + (int)sizeof(*n) > cs->cap) {
+			cs = c->create_local ? c->create_local(c->user, sizeof(*n)) : NULL;
+			if (!cs) {
+				/* can't allocate a copy structure */
+				return -1;
+			}
+			cs->next = c->copylist;
+			c->copylist = cs;
+		}
+
+		n = (struct copy*) (cs->data + cs->len);
+		cs->len += sizeof(*n);
+
+		n->from = *f;
+		n->to = *t;
+		n->fbegin = fbegin;
+		n->fend = fend;
+
+		*xcp = &n->hdr;
+		n->hdr.parent = &cp->hdr;
+
+		c->copy = capn_tree_insert(c->copy, &n->hdr);
+	}
+
+	/* minimize the number of types the main copy routine has to
+	 * deal with to just CAPN_LIST and CAPN_PTR_LIST. ptr list only
+	 * needs t->type, t->len, t->data, t->seg, f->data, f->seg to
+	 * be valid */
+	switch (t->type) {
+	case CAPN_STRUCT:
+		if (t->datasz) {
+			memcpy(t->data, f->data, t->datasz);
+			t->data += t->datasz;
+			f->data += t->datasz;
+		}
+		if (t->ptrs) {
+			t->type = CAPN_PTR_LIST;
+			t->len = t->ptrs;
+			(*dep)++;
+		}
+		return 0;
+
+	case CAPN_BIT_LIST:
+		memcpy(t->data, f->data, t->datasz);
+		return 0;
+
+	case CAPN_LIST:
+		if (!t->len) {
+			/* empty list - nothing to copy */
+		} else if (t->ptrs && t->datasz) {
+			(*dep)++;
+		} else if (t->datasz) {
+			memcpy(t->data, f->data, t->len * t->datasz);
+		} else if (t->ptrs) {
+			t->type = CAPN_PTR_LIST;
+			t->len *= t->ptrs;
+			(*dep)++;
+		}
+		return 0;
+
+	case CAPN_PTR_LIST:
+		if (t->len) {
+			(*dep)++;
+		}
+		return 0;
+
+	default:
+		return -1;
+	}
+}
+
+static void copy_list_member(capn_ptr* t, capn_ptr *f, int *dep) {
+	/* copy struct data */
+	int sz = min(t->datasz, f->datasz);
+	memcpy(t->data, f->data, sz);
+	memset(t->data + sz, 0, t->datasz - sz);
+	t->data += t->datasz;
+	f->data += f->datasz;
+
+	/* reset excess pointers */
+	sz = min(t->ptrs, f->ptrs);
+	memset(t->data + sz, 0, 8*(t->ptrs - sz));
+
+	/* create a pointer list for the main loop to copy */
+	if (t->ptrs) {
+		t->type = CAPN_PTR_LIST;
+		t->len = t->ptrs;
+		(*dep)++;
+	}
+}
+
+#define MAX_COPY_DEPTH 32
+
+/* TODO: handle CAPN_BIT_LIST and setting from an inner bit list member */
+int capn_setp(capn_ptr p, int off, capn_ptr tgt) {
+	struct capn_ptr to[MAX_COPY_DEPTH], from[MAX_COPY_DEPTH];
+	char *data;
+	int err, dep = 0;
+
+	capn_resolve(&p);
+
+	if (tgt.type == CAPN_FAR_POINTER && tgt.seg->capn == p.seg->capn) {
+		uint64_t val = capn_flip64(*(uint64_t*) tgt.data);
+		if ((val & 3) == FAR_PTR) {
+			*(uint64_t*) p.data = *(uint64_t*) tgt.data;
+			return 0;
+		}
+	}
+
+	capn_resolve(&tgt);
+
+	switch (p.type) {
+	case CAPN_LIST:
+		if (off >= p.len || tgt.type != CAPN_STRUCT)
+			return -1;
+
+		to[0] = p;
+		to[0].data += off * (p.datasz + 8*p.ptrs);
+		from[0] = tgt;
+		copy_list_member(to, from, &dep);
+		break;
+
+	case CAPN_PTR_LIST:
+		if (off >= p.len)
+			return -1;
+		data = p.data + 8*off;
+		goto copy_ptr;
+
+	case CAPN_STRUCT:
+		if (off >= p.ptrs)
+			return -1;
+		data = p.data + p.datasz + 8*off;
+		goto copy_ptr;
+
+	copy_ptr:
+		err = write_ptr(p.seg, data, tgt);
+		if (err != NEED_TO_COPY)
+			return err;
+
+		/* Depth first copy the source whilst using a pointer stack to
+		 * maintain the ptr to set and size left to copy at each level.
+		 * We also maintain a rbtree (capn->copy) of the copies indexed
+		 * by the source data. This way we can detect overlapped
+		 * pointers in the source (and bail) and recursive structures
+		 * (and point to the previous copy).
+		 */
+
+		from[0] = tgt;
+		if (copy_ptr(p.seg, data, to, from, &dep))
+			return -1;
+		break;
+
+	default:
+		return -1;
+	}
+
+	while (dep) {
+		struct capn_ptr *tc = &to[dep-1], *tn = &to[dep];
+		struct capn_ptr *fc = &from[dep-1], *fn = &from[dep];
+
+		if (dep+1 == MAX_COPY_DEPTH) {
+			return -1;
+		}
+
+		if (!tc->len) {
+			dep--;
+			continue;
+		}
+
+		if (tc->type == CAPN_LIST) {
+			*fn = capn_getp(*fc, 0, 1);
+			*tn = capn_getp(*tc, 0, 1);
+
+			copy_list_member(tn, fn, &dep);
+
+			fc->data += fc->datasz + 8*fc->ptrs;
+			tc->data += tc->datasz + 8*tc->ptrs;
+			tc->len--;
+
+		} else { /* CAPN_PTR_LIST */
+			*fn = read_ptr(fc->seg, fc->data);
+
+			if (fn->type && copy_ptr(tc->seg, tc->data, tn, fn, &dep))
+				return -1;
+
+			fc->data += 8;
+			tc->data += 8;
+			tc->len--;
+		}
+	}
+
+	return 0;
+}
+
+/* TODO: handle CAPN_LIST, CAPN_PTR_LIST for bit lists */
+
+int capn_get1(capn_list1 l, int off) {
+	return l.p.type == CAPN_BIT_LIST
+		&& off < l.p.len
+		&& (l.p.data[off/8] & (1 << (off%8))) != 0;
+}
+
+int capn_set1(capn_list1 l, int off, int val) {
+	if (l.p.type != CAPN_BIT_LIST || off >= l.p.len)
+		return -1;
+	if (val) {
+		l.p.data[off/8] |= 1 << (off%8);
+	} else {
+		l.p.data[off/8] &= ~(1 << (off%8));
+	}
+	return 0;
+}
+
+int capn_getv1(capn_list1 l, int off, uint8_t *data, int sz) {
+	/* Note we only support aligned reads */
+	int bsz;
+	capn_ptr p;
+	capn_resolve(&l.p);
+	p = l.p;
+	if (p.type != CAPN_BIT_LIST || (off & 7) != 0)
+		return -1;
+
+	bsz = (sz + 7) / 8;
+	off /= 8;
+
+	if (off + sz > p.datasz) {
+		memcpy(data, p.data + off, p.datasz - off);
+		return p.len - off*8;
+	} else {
+		memcpy(data, p.data + off, bsz);
+		return sz;
+	}
+}
+
+int capn_setv1(capn_list1 l, int off, const uint8_t *data, int sz) {
+	/* Note we only support aligned writes */
+	int bsz;
+	capn_ptr p = l.p;
+	if (p.type != CAPN_BIT_LIST || (off & 7) != 0)
+		return -1;
+
+	bsz = (sz + 7) / 8;
+	off /= 8;
+
+	if (off + sz > p.datasz) {
+		memcpy(p.data + off, data, p.datasz - off);
+		return p.len - off*8;
+	} else {
+		memcpy(p.data + off, data, bsz);
+		return sz;
+	}
+}
+
+/* pull out whether we add a tag or not as a define so the unit test can
+ * test double far pointers by not creating tags */
+#ifndef ADD_TAG
+#define ADD_TAG 1
+#endif
+
+static void new_object(capn_ptr *p, int bytes) {
+	struct capn_segment *s = p->seg;
+
+	if (!s) {
+		memset(p, 0, sizeof(*p));
+		return;
+	}
+
+	/* pointer needs to be initialised to get a valid offset on write */
+	if (!bytes) {
+		p->data = s->data + s->len;
+		return;
+	}
+
+	/* all allocations are 8 byte aligned */
+	bytes = (bytes + 7) & ~7;
+
+	if (s->len + bytes <= s->cap) {
+		p->data = s->data + s->len;
+		s->len += bytes;
+		return;
+	}
+
+	/* add a tag whenever we switch segments so that write_ptr can
+	 * use it */
+	p->data = new_data(s->capn, bytes + ADD_TAG*8, &p->seg);
+	if (!p->data) {
+		memset(p, 0, sizeof(*p));
+		return;
+	}
+
+	if (ADD_TAG) {
+		write_ptr_tag(p->data, *p, 0);
+		p->data += 8;
+		p->has_ptr_tag = 1;
+	}
+}
+
+capn_ptr capn_root(struct capn *c) {
+	capn_ptr r = {CAPN_PTR_LIST};
+	r.seg = lookup_segment(c, NULL, 0);
+	r.data = r.seg ? r.seg->data : new_data(c, 8, &r.seg);
+	r.len = 1;
+
+	if (!r.seg || r.seg->cap < 8) {
+		memset(&r, 0, sizeof(r));
+	} else if (r.seg->len < 8) {
+		r.seg->len = 8;
+	}
+
+	return r;
+}
+
+capn_ptr capn_new_struct(struct capn_segment *seg, int datasz, int ptrs) {
+	capn_ptr p = {CAPN_STRUCT};
+	p.seg = seg;
+	p.datasz = (datasz + 7) & ~7;
+	p.ptrs = ptrs;
+	new_object(&p, p.datasz + 8*p.ptrs);
+	return p;
+}
+
+capn_ptr capn_new_list(struct capn_segment *seg, int sz, int datasz, int ptrs) {
+	capn_ptr p = {CAPN_LIST};
+	p.seg = seg;
+	p.len = sz;
+
+	if (ptrs || datasz > 8) {
+		p.is_composite_list = 1;
+		p.datasz = (datasz + 7) & ~7;
+		p.ptrs = ptrs;
+		new_object(&p, p.len * (p.datasz + 8*p.ptrs) + 8);
+		if (p.data) {
+			uint64_t hdr = STRUCT_PTR | (U64(p.len) << 2) | (U64(p.datasz/8) << 32) | (U64(ptrs) << 48);
+			*(uint64_t*) p.data = capn_flip64(hdr);
+			p.data += 8;
+		}
+	} else if (datasz > 4) {
+		p.datasz = 8;
+		new_object(&p, p.len * 8);
+	} else if (datasz > 2) {
+		p.datasz = 4;
+		new_object(&p, p.len * 4);
+	} else {
+		p.datasz = datasz;
+		new_object(&p, p.len * datasz);
+	}
+
+	return p;
+}
+
+capn_list1 capn_new_list1(struct capn_segment *seg, int sz) {
+	capn_list1 l = {{CAPN_BIT_LIST}};
+	l.p.seg = seg;
+	l.p.datasz = (sz+7)/8;
+	l.p.len = sz;
+	new_object(&l.p, l.p.datasz);
+	return l;
+}
+
+capn_ptr capn_new_ptr_list(struct capn_segment *seg, int sz) {
+	capn_ptr p = {CAPN_PTR_LIST};
+	p.seg = seg;
+	p.len = sz;
+	p.ptrs = 0;
+	p.datasz = 0;
+	new_object(&p, sz*8);
+	return p;
+}
+
+capn_ptr capn_new_string(struct capn_segment *seg, const char *str, ssize_t sz) {
+	capn_ptr p = {CAPN_LIST};
+	p.seg = seg;
+	p.len = ((sz >= 0) ? (size_t)sz : strlen(str)) + 1;
+	p.datasz = 1;
+	new_object(&p, p.len);
+	if (p.data) {
+		memcpy(p.data, str, p.len - 1);
+		p.data[p.len - 1] = '\0';
+	}
+	return p;
+}
+
+capn_text capn_get_text(capn_ptr p, int off, capn_text def) {
+	capn_ptr m = capn_getp(p, off, 1);
+	capn_text ret = def;
+	if (m.type == CAPN_LIST && m.datasz == 1 && m.len && m.data[m.len - 1] == 0) {
+		ret.seg = m.seg;
+		ret.str = m.data;
+		ret.len = m.len - 1;
+	}
+	return ret;
+}
+
+int capn_set_text(capn_ptr p, int off, capn_text tgt) {
+	capn_ptr m = {CAPN_NULL};
+	if (tgt.seg) {
+		m.type = CAPN_LIST;
+		m.seg = tgt.seg;
+		m.data = (char*)tgt.str;
+		m.len = tgt.len + 1;
+		m.datasz = 1;
+	} else if (tgt.str) {
+		m = capn_new_string(p.seg, tgt.str, tgt.len);
+	}
+	return capn_setp(p, off, m);
+}
+
+capn_data capn_get_data(capn_ptr p, int off) {
+	capn_data ret;
+	ret.p = capn_getp(p, off, 1);
+	if (ret.p.type != CAPN_LIST || ret.p.datasz != 1) {
+		memset(&ret, 0, sizeof(ret));
+	}
+	return ret;
+}
+
+#define SZ 8
+#include "capn-list.inc"
+#undef SZ
+
+#define SZ 16
+#include "capn-list.inc"
+#undef SZ
+
+#define SZ 32
+#include "capn-list.inc"
+#undef SZ
+
+#define SZ 64
+#include "capn-list.inc"
+#undef SZ

+ 441 - 0
src/capnp_c.h

@@ -0,0 +1,441 @@
+/* vim: set sw=8 ts=8 sts=8 noet: */
+/* capnp_c.h
+ *
+ * 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.
+ */
+
+#ifndef CAPNP_C_H
+#define CAPNP_C_H
+
+#include <stdint.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#if defined(unix) && !defined(__APPLE__)
+#include <endian.h>
+#endif
+
+/* ssize_t is a POSIX type, not an ISO C one...
+ * Windows seems to only have SSIZE_T in BaseTsd.h
+ */
+#ifdef _MSC_VER
+typedef intmax_t ssize_t;
+#else
+#include <stddef.h>
+#endif
+
+// Cross-platform macro ALIGNED_(x) aligns a struct by `x` bytes.
+#ifdef _MSC_VER
+#define ALIGNED_(x) __declspec(align(x))
+#endif
+#ifdef __GNUC__
+#define ALIGNED_(x) __attribute__ ((aligned(x)))
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined(__cplusplus) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L)
+#define CAPN_INLINE static inline
+#else
+#define CAPN_INLINE static
+#endif
+
+#define CAPN_VERSION 1
+
+/* struct capn is a common structure shared between segments in the same
+ * session/context so that far pointers between segments will be created.
+ *
+ * lookup is used to lookup segments by id when derefencing a far pointer
+ *
+ * create is used to create or lookup an alternate segment that has at least
+ * sz available (ie returned seg->len + sz <= seg->cap)
+ *
+ * create_local is used to create a segment for the copy tree and should be
+ * allocated in the local memory space.
+ *
+ * Allocated segments must be zero initialized.
+ *
+ * create and lookup can be NULL if you don't need multiple segments and don't
+ * want to support copying
+ *
+ * seglist and copylist are linked lists which can be used to free up segments
+ * on cleanup, but should not be modified by the user.
+ *
+ * lookup, create, create_local, and user can be set by the user. Other values
+ * should be zero initialized.
+ */
+struct capn {
+	/* user settable */
+	struct capn_segment *(*lookup)(void* /*user*/, uint32_t /*id */);
+	struct capn_segment *(*create)(void* /*user*/, uint32_t /*id */, int /*sz*/);
+	struct capn_segment *(*create_local)(void* /*user*/, int /*sz*/);
+	void *user;
+	/* zero initialized, user should not modify */
+	uint32_t segnum;
+	struct capn_tree *copy;
+	struct capn_tree *segtree;
+	struct capn_segment *seglist, *lastseg;
+	struct capn_segment *copylist;
+};
+
+/* struct capn_tree is a rb tree header used internally for the segment id
+ * lookup and copy tree */
+struct capn_tree {
+	struct capn_tree *parent, *link[2];
+	unsigned int red : 1;
+};
+
+struct capn_tree *capn_tree_insert(struct capn_tree *root, struct capn_tree *n);
+
+/* struct capn_segment contains the information about a single segment.
+ *
+ * capn points to a struct capn that is shared between segments in the
+ * same session
+ *
+ * id specifies the segment id, used for far pointers
+ *
+ * data specifies the segment data. This should not move after creation.
+ *
+ * len specifies the current segment length. This is 0 for a blank
+ * segment.
+ *
+ * cap specifies the segment capacity.
+ *
+ * When creating new structures len will be incremented until it reaches cap,
+ * at which point a new segment will be requested via capn->create. The
+ * create callback can either create a new segment or expand an existing
+ * one by incrementing cap and returning the expanded segment.
+ *
+ * data, len, and cap must all be 8 byte aligned, hence the ALIGNED_(8) macro
+ * on the struct definition.
+ *
+ * data, len, cap, and user should all be set by the user. Other values
+ * should be zero initialized.
+ */
+
+struct ALIGNED_(8) capn_segment {
+	struct capn_tree hdr;
+	struct capn_segment *next;
+	struct capn *capn;
+	uint32_t id;
+	/* user settable */
+	char *data;
+	size_t len, cap;
+	void *user;
+};
+
+enum CAPN_TYPE {
+	CAPN_NULL = 0,
+	CAPN_STRUCT = 1,
+	CAPN_LIST = 2,
+	CAPN_PTR_LIST = 3,
+	CAPN_BIT_LIST = 4,
+	CAPN_FAR_POINTER = 5,
+};
+
+struct capn_ptr {
+	unsigned int type : 4;
+	unsigned int has_ptr_tag : 1;
+	unsigned int is_list_member : 1;
+	unsigned int is_composite_list : 1;
+	unsigned int datasz : 19;
+	unsigned int ptrs : 16;
+	int len;
+	char *data;
+	struct capn_segment *seg;
+};
+
+struct capn_text {
+	int len;
+	const char *str;
+	struct capn_segment *seg;
+};
+
+typedef struct capn_ptr capn_ptr;
+typedef struct capn_text capn_text;
+typedef struct {capn_ptr p;} capn_data;
+typedef struct {capn_ptr p;} capn_list1;
+typedef struct {capn_ptr p;} capn_list8;
+typedef struct {capn_ptr p;} capn_list16;
+typedef struct {capn_ptr p;} capn_list32;
+typedef struct {capn_ptr p;} capn_list64;
+
+struct capn_msg {
+	struct capn_segment *seg;
+	uint64_t iface;
+	uint16_t method;
+	capn_ptr args;
+};
+
+/* capn_append_segment appends a segment to a session */
+void capn_append_segment(struct capn*, struct capn_segment*);
+
+capn_ptr capn_root(struct capn *c);
+void capn_resolve(capn_ptr *p);
+
+#define capn_len(list) ((list).p.type == CAPN_FAR_POINTER ? (capn_resolve(&(list).p), (list).p.len) : (list).p.len)
+
+/* capn_getp|setp functions get/set ptrs in list/structs
+ * off is the list index or pointer index in a struct
+ * capn_setp will copy the data, create far pointers, etc if the target
+ * is in a different segment/context.
+ * Both of these will use/return inner pointers for composite lists.
+ */
+capn_ptr capn_getp(capn_ptr p, int off, int resolve);
+int capn_setp(capn_ptr p, int off, capn_ptr tgt);
+
+capn_text capn_get_text(capn_ptr p, int off, capn_text def);
+capn_data capn_get_data(capn_ptr p, int off);
+int capn_set_text(capn_ptr p, int off, capn_text tgt);
+/* there is no set_data -- use capn_new_list8 + capn_setv8 instead
+ * and set data.p = list.p */
+
+/* capn_get* functions get data from a list
+ * The length of the list is given by p->size
+ * off specifies how far into the list to start
+ * sz indicates the number of elements to get
+ * The function returns the number of elements read or -1 on an error.
+ * off must be byte aligned for capn_getv1
+ */
+int capn_get1(capn_list1 p, int off);
+uint8_t capn_get8(capn_list8 p, int off);
+uint16_t capn_get16(capn_list16 p, int off);
+uint32_t capn_get32(capn_list32 p, int off);
+uint64_t capn_get64(capn_list64 p, int off);
+int capn_getv1(capn_list1 p, int off, uint8_t *data, int sz);
+int capn_getv8(capn_list8 p, int off, uint8_t *data, int sz);
+int capn_getv16(capn_list16 p, int off, uint16_t *data, int sz);
+int capn_getv32(capn_list32 p, int off, uint32_t *data, int sz);
+int capn_getv64(capn_list64 p, int off, uint64_t *data, int sz);
+
+/* capn_set* functions set data in a list
+ * off specifies how far into the list to start
+ * sz indicates the number of elements to write
+ * The function returns the number of elemnts written or -1 on an error.
+ * off must be byte aligned for capn_setv1
+ */
+int capn_set1(capn_list1 p, int off, int v);
+int capn_set8(capn_list8 p, int off, uint8_t v);
+int capn_set16(capn_list16 p, int off, uint16_t v);
+int capn_set32(capn_list32 p, int off, uint32_t v);
+int capn_set64(capn_list64 p, int off, uint64_t v);
+int capn_setv1(capn_list1 p, int off, const uint8_t *data, int sz);
+int capn_setv8(capn_list8 p, int off, const uint8_t *data, int sz);
+int capn_setv16(capn_list16 p, int off, const uint16_t *data, int sz);
+int capn_setv32(capn_list32 p, int off, const uint32_t *data, int sz);
+int capn_setv64(capn_list64 p, int off, const uint64_t *data, int sz);
+
+/* capn_new_* functions create a new object
+ * datasz is in bytes, ptrs is # of pointers, sz is # of elements in the list
+ * On an error a CAPN_NULL pointer is returned
+ */
+capn_ptr capn_new_string(struct capn_segment *seg, const char *str, ssize_t sz);
+capn_ptr capn_new_struct(struct capn_segment *seg, int datasz, int ptrs);
+capn_ptr capn_new_interface(struct capn_segment *seg, int datasz, int ptrs);
+capn_ptr capn_new_ptr_list(struct capn_segment *seg, int sz);
+capn_ptr capn_new_list(struct capn_segment *seg, int sz, int datasz, int ptrs);
+capn_list1 capn_new_list1(struct capn_segment *seg, int sz);
+capn_list8 capn_new_list8(struct capn_segment *seg, int sz);
+capn_list16 capn_new_list16(struct capn_segment *seg, int sz);
+capn_list32 capn_new_list32(struct capn_segment *seg, int sz);
+capn_list64 capn_new_list64(struct capn_segment *seg, int sz);
+
+/* capn_read|write* functions read/write struct values
+ * off is the offset into the structure in bytes
+ * Rarely should these be called directly, instead use the generated code.
+ * Data must be xored with the default value
+ * These are inlined
+ */
+CAPN_INLINE uint8_t capn_read8(capn_ptr p, int off);
+CAPN_INLINE uint16_t capn_read16(capn_ptr p, int off);
+CAPN_INLINE uint32_t capn_read32(capn_ptr p, int off);
+CAPN_INLINE uint64_t capn_read64(capn_ptr p, int off);
+CAPN_INLINE int capn_write1(capn_ptr p, int off, int val);
+CAPN_INLINE int capn_write8(capn_ptr p, int off, uint8_t val);
+CAPN_INLINE int capn_write16(capn_ptr p, int off, uint16_t val);
+CAPN_INLINE int capn_write32(capn_ptr p, int off, uint32_t val);
+CAPN_INLINE int capn_write64(capn_ptr p, int off, uint64_t val);
+
+/* capn_init_malloc inits the capn struct with a create function which
+ * allocates segments on the heap using malloc
+ *
+ * capn_init_(fp|mem) inits by reading segments in from the file/memory buffer
+ * in serialized form (optionally packed). It will then setup the create
+ * function ala capn_init_malloc so that further segments can be created.
+ *
+ * capn_free frees all the segment headers and data created by the create
+ * function setup by capn_init_*
+ */
+void capn_init_malloc(struct capn *c);
+int capn_init_fp(struct capn *c, FILE *f, int packed);
+int capn_init_mem(struct capn *c, const uint8_t *p, size_t sz, int packed);
+
+/* capn_write_(fp|mem) writes segments to the file/memory buffer in
+ * serialized form and returns the number of bytes written.
+ */
+/* TODO */
+/*int capn_write_fp(struct capn *c, FILE *f, int packed);*/
+int capn_write_fd(struct capn *c, ssize_t (*write_fd)(int fd, const void *p, size_t count), int fd, int packed);
+int capn_write_mem(struct capn *c, uint8_t *p, size_t sz, int packed);
+
+void capn_free(struct capn *c);
+void capn_reset_copy(struct capn *c);
+
+/* Inline functions */
+
+
+CAPN_INLINE uint8_t capn_flip8(uint8_t v) {
+	return v;
+}
+CAPN_INLINE uint16_t capn_flip16(uint16_t v) {
+#if defined(__BYTE_ORDER) && (__BYTE_ORDER == __LITTLE_ENDIAN)
+	return v;
+#elif defined(__BYTE_ORDER) && (__BYTE_ORDER == __BIG_ENDIAN) && \
+      defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 8
+	return __builtin_bswap16(v);
+#else
+	union { uint16_t u; uint8_t v[2]; } s;
+	s.v[0] = (uint8_t)v;
+	s.v[1] = (uint8_t)(v>>8);
+	return s.u;
+#endif
+}
+CAPN_INLINE uint32_t capn_flip32(uint32_t v) {
+#if defined(__BYTE_ORDER) && (__BYTE_ORDER == __LITTLE_ENDIAN)
+	return v;
+#elif defined(__BYTE_ORDER) && (__BYTE_ORDER == __BIG_ENDIAN) && \
+      defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 8
+	return __builtin_bswap32(v);
+#else
+	union { uint32_t u; uint8_t v[4]; } s;
+	s.v[0] = (uint8_t)v;
+	s.v[1] = (uint8_t)(v>>8);
+	s.v[2] = (uint8_t)(v>>16);
+	s.v[3] = (uint8_t)(v>>24);
+	return s.u;
+#endif
+}
+CAPN_INLINE uint64_t capn_flip64(uint64_t v) {
+#if defined(__BYTE_ORDER) && (__BYTE_ORDER == __LITTLE_ENDIAN)
+	return v;
+#elif defined(__BYTE_ORDER) && (__BYTE_ORDER == __BIG_ENDIAN) && \
+      defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 8
+	return __builtin_bswap64(v);
+#else
+	union { uint64_t u; uint8_t v[8]; } s;
+	s.v[0] = (uint8_t)v;
+	s.v[1] = (uint8_t)(v>>8);
+	s.v[2] = (uint8_t)(v>>16);
+	s.v[3] = (uint8_t)(v>>24);
+	s.v[4] = (uint8_t)(v>>32);
+	s.v[5] = (uint8_t)(v>>40);
+	s.v[6] = (uint8_t)(v>>48);
+	s.v[7] = (uint8_t)(v>>56);
+	return s.u;
+#endif
+}
+
+CAPN_INLINE int capn_write1(capn_ptr p, int off, int val) {
+	if (off >= p.datasz*8) {
+		return -1;
+	} else if (val) {
+		uint8_t tmp = (uint8_t)(1 << (off & 7));
+		((uint8_t*) p.data)[off >> 3] |= tmp;
+		return 0;
+	} else {
+		uint8_t tmp = (uint8_t)(~(1 << (off & 7)));
+		((uint8_t*) p.data)[off >> 3] &= tmp;
+		return 0;
+	}
+}
+
+CAPN_INLINE uint8_t capn_read8(capn_ptr p, int off) {
+	return off+1 <= p.datasz ? capn_flip8(*(uint8_t*) (p.data+off)) : 0;
+}
+CAPN_INLINE int capn_write8(capn_ptr p, int off, uint8_t val) {
+	if (off+1 <= p.datasz) {
+		*(uint8_t*) (p.data+off) = capn_flip8(val);
+		return 0;
+	} else {
+		return -1;
+	}
+}
+
+CAPN_INLINE uint16_t capn_read16(capn_ptr p, int off) {
+	return off+2 <= p.datasz ? capn_flip16(*(uint16_t*) (p.data+off)) : 0;
+}
+CAPN_INLINE int capn_write16(capn_ptr p, int off, uint16_t val) {
+	if (off+2 <= p.datasz) {
+		*(uint16_t*) (p.data+off) = capn_flip16(val);
+		return 0;
+	} else {
+		return -1;
+	}
+}
+
+CAPN_INLINE uint32_t capn_read32(capn_ptr p, int off) {
+	return off+4 <= p.datasz ? capn_flip32(*(uint32_t*) (p.data+off)) : 0;
+}
+CAPN_INLINE int capn_write32(capn_ptr p, int off, uint32_t val) {
+	if (off+4 <= p.datasz) {
+		*(uint32_t*) (p.data+off) = capn_flip32(val);
+		return 0;
+	} else {
+		return -1;
+	}
+}
+
+CAPN_INLINE uint64_t capn_read64(capn_ptr p, int off) {
+	return off+8 <= p.datasz ? capn_flip64(*(uint64_t*) (p.data+off)) : 0;
+}
+CAPN_INLINE int capn_write64(capn_ptr p, int off, uint64_t val) {
+	if (off+8 <= p.datasz) {
+		*(uint64_t*) (p.data+off) = capn_flip64(val);
+		return 0;
+	} else {
+		return -1;
+	}
+}
+
+union capn_conv_f32 {
+	uint32_t u;
+	float f;
+};
+
+union capn_conv_f64 {
+	uint64_t u;
+	double f;
+};
+
+CAPN_INLINE float capn_to_f32(uint32_t v) {
+	union capn_conv_f32 u;
+	u.u = v;
+	return u.f;
+}
+CAPN_INLINE double capn_to_f64(uint64_t v) {
+	union capn_conv_f64 u;
+	u.u = v;
+	return u.f;
+}
+CAPN_INLINE uint32_t capn_from_f32(float v) {
+	union capn_conv_f32 u;
+	u.f = v;
+	return u.u;
+}
+CAPN_INLINE uint64_t capn_from_f64(double v) {
+	union capn_conv_f64 u;
+	u.f = v;
+	return u.u;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 62 - 0
src/capnp_priv.h

@@ -0,0 +1,62 @@
+/* vim: set sw=8 ts=8 sts=8 noet: */
+/* capnp_c.h
+ *
+ * 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.
+ */
+
+/*
+ * functions / structures in this header are private to the capnproto-c
+ * library;  applications should not call or use them.
+ */
+
+#ifndef CAPNP_PRIV_H
+#define CAPNP_PRIV_H
+
+#include "capnp_c.h"
+
+#if defined(__GNUC__) && __GNUC__ >= 4
+# define intern __attribute__((visibility ("internal")))
+#else
+# define intern /**/
+#endif
+
+/* capn_stream encapsulates the needed fields for capn_(deflate|inflate) in a
+ * similar manner to z_stream from zlib
+ *
+ * The user should set next_in, avail_in, next_out, avail_out to the
+ * available in/out buffers before calling capn_(deflate|inflate).
+ *
+ * Other fields should be zero initialized.
+ */
+struct capn_stream {
+	const uint8_t *next_in;
+	size_t avail_in;
+	uint8_t *next_out;
+	size_t avail_out;
+	unsigned zeros, raw;
+
+	uint8_t inflate_buf[8];
+	size_t avail_buf;
+};
+
+#define CAPN_MISALIGNED -1
+#define CAPN_NEED_MORE -2
+
+/* capn_deflate deflates a stream to the packed format
+ * capn_inflate inflates a stream from the packed format
+ *
+ * Returns:
+ * CAPN_MISALIGNED - if the unpacked data is not 8 byte aligned
+ * CAPN_NEED_MORE - more packed data/room is required (out for inflate, in for
+ * deflate)
+ * 0 - success, all output for inflate, all input for deflate processed
+ */
+intern int capn_deflate(struct capn_stream*);
+intern int capn_inflate(struct capn_stream*);
+
+
+#endif /* CAPNP_PRIV_H */