diff --git a/.gitignore b/.gitignore index 925b4b4..d1e0061 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /*.o /*.so +/capn-test diff --git a/Makefile b/Makefile index e1dcb97..12e5b1c 100644 --- a/Makefile +++ b/Makefile @@ -1,12 +1,23 @@ -.PHONY: all clean +.PHONY: all clean test -all: capn.so +CFLAGS=-g -Wall -Werror -fPIC -I. -Wno-unused-function + +all: capn.so test clean: - rm -f *.o *.so + rm -f *.o *.so capnpc-c compiler/*.o %.o: %.c *.h *.inc - $(CC) -Wall -Werror -g -O2 -c $< -o $@ + $(CC) $(CFLAGS) -c $< -o $@ capn.so: capn-malloc.o capn-stream.o capn.o - $(CC) -shared -Wall -Werror -fPIC -g -O2 $^ -o $@ + $(CC) -shared $(CFLAGS) $^ -o $@ + +test: capn-test + ./capn-test + +%-test.o: %-test.cpp *.h *.c *.inc + $(CXX) `gtest-config --cppflags --cxxflags` -o $@ -c $< + +capn-test: capn-test.o + $(CXX) `gtest-config --ldflags --libs` -o $@ $^ diff --git a/capn-list.inc b/capn-list.inc index ce6f2ea..8d7f15a 100644 --- a/capn-list.inc +++ b/capn-list.inc @@ -4,9 +4,34 @@ #define UINT_T CAT(CAT(uint, SZ), _t) #define FLIP CAT(capn_flip, SZ) -int CAT(capn_read,SZ) (const struct CAT(capn_list,SZ) *list, int off, UINT_T *to, int sz) { +UINT_T CAT(capn_get,SZ) (const struct capn_ptr *p, int off) { + char *d; + if (off >= p->size) { + return 0; + } + + switch (p->type) { + case CAPN_LIST: + if (p->datasz < SZ/8) + return 0; + d = p->data + off * (p->datasz + p->ptrsz); + 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) (const struct capn_ptr *p, int off, UINT_T *to, int sz) { int i; - const struct capn_ptr *p = &list->p; if (off + sz > p->size) { sz = p->size - off; @@ -29,7 +54,7 @@ int CAT(capn_read,SZ) (const struct CAT(capn_list,SZ) *list, int off, UINT_T *to case CAPN_PTR_LIST: for (i = 0; i < sz; i++) { - char *d = struct_ptr(p->seg, p->data + 8*(i+off)); + char *d = struct_ptr(p->seg, p->data + 8*(i+off), SZ/8); if (d) { to[i] = FLIP(*(UINT_T*)d); } else { @@ -43,9 +68,35 @@ int CAT(capn_read,SZ) (const struct CAT(capn_list,SZ) *list, int off, UINT_T *to } } -int CAT(capn_write,SZ) (struct CAT(capn_list,SZ) *list, int off, const UINT_T *from, int sz) { +int CAT(capn_set,SZ) (struct capn_ptr *p, int off, UINT_T v) { + char *d; + if (off >= p->size) { + return -1; + } + + switch (p->type) { + case CAPN_LIST: + if (p->datasz < SZ/8) + return -1; + d = p->data + off * (p->datasz + p->ptrsz); + *(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) (struct capn_ptr *p, int off, const UINT_T *from, int sz) { int i; - struct capn_ptr *p = &list->p; if (off + sz > p->size) { sz = p->size - off; @@ -68,7 +119,7 @@ int CAT(capn_write,SZ) (struct CAT(capn_list,SZ) *list, int off, const UINT_T *f case CAPN_PTR_LIST: for (i = 0; i < sz; i++) { - char *d = struct_ptr(p->seg, p->data + 8*(i+off)); + char *d = struct_ptr(p->seg, p->data + 8*(i+off), SZ/8); if (d) { *(UINT_T*) d = FLIP(from[i]); } else { diff --git a/capn-malloc.c b/capn-malloc.c index db97a66..9355b02 100644 --- a/capn-malloc.c +++ b/capn-malloc.c @@ -11,8 +11,7 @@ static struct capn_segment *create(void *u, uint32_t id, int sz) { sz = (sz + 4095) & ~4095; } s = (struct capn_segment*) calloc(1, sizeof(*s)); - s->data = calloc(1, sz); - s->len = 0; + s->data = (char*) calloc(1, sz); s->cap = sz; return s; } @@ -22,7 +21,7 @@ void capn_init_malloc(struct capn *c) { c->create = &create; } -void capn_free_all(struct capn *c) { +void capn_free_malloc(struct capn *c) { struct capn_segment *s = c->seglist; while (s != NULL) { struct capn_segment *n = s->next; diff --git a/capn-test.cpp b/capn-test.cpp new file mode 100644 index 0000000..5fb54d3 --- /dev/null +++ b/capn-test.cpp @@ -0,0 +1,419 @@ +#include + +static int g_AddTag = 1; +#define ADD_TAG g_AddTag + +#include "capn.c" +#include "capn-malloc.c" + +typedef uint64_t word; + +template +union AlignedData { + // Useful for declaring static constant data blobs as an array of bytes, but forcing those + // bytes to be word-aligned. + + uint8_t bytes[wordCount * sizeof(word)]; + word words[wordCount]; +}; + +class Session { +public: + Session() {capn_init_malloc(&capn);} + ~Session() {capn_free_malloc(&capn);} + struct capn capn; +}; + + + + + +TEST(WireFormat, SimpleRawDataStruct) { + AlignedData<2> data = {{ + // Struct ref, offset = 1, dataSize = 1, referenceCount = 0 + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + // Content for the data segment. + 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef + }}; + + struct capn_segment seg; + memset(&seg, 0, sizeof(seg)); + seg.data = (char*) data.bytes; + seg.len = seg.cap = sizeof(data.bytes); + + struct capn ctx; + memset(&ctx, 0, sizeof(ctx)); + capn_append_segment(&ctx, &seg); + + EXPECT_EQ(&seg, ctx.seglist); + EXPECT_EQ(&seg, ctx.lastseg); + EXPECT_EQ(&seg.hdr, ctx.segtree); + EXPECT_EQ(1, ctx.segnum); + EXPECT_EQ(0, seg.id); + + struct capn_ptr root = capn_root(&ctx); + EXPECT_EQ(CAPN_PTR_LIST, root.type); + EXPECT_EQ(1, root.size); + + struct capn_ptr ptr = capn_getp(&root, 0); + EXPECT_EQ(CAPN_STRUCT, ptr.type); + EXPECT_EQ(8, ptr.datasz); + EXPECT_EQ(0, ptr.ptrsz); + + EXPECT_EQ(UINT64_C(0xefcdab8967452301), capn_read64(&ptr, 0)); + EXPECT_EQ(UINT64_C(0), capn_read64(&ptr, 8)); + EXPECT_EQ(UINT32_C(0x67452301), capn_read32(&ptr, 0)); + EXPECT_EQ(UINT32_C(0xefcdab89), capn_read32(&ptr, 4)); + EXPECT_EQ(UINT32_C(0), capn_read32(&ptr, 8)); + EXPECT_EQ(UINT16_C(0x2301), capn_read16(&ptr, 0)); + EXPECT_EQ(UINT16_C(0x6745), capn_read16(&ptr, 2)); + EXPECT_EQ(UINT16_C(0xab89), capn_read16(&ptr, 4)); + EXPECT_EQ(UINT16_C(0xefcd), capn_read16(&ptr, 6)); + EXPECT_EQ(UINT16_C(0), capn_read16(&ptr, 8)); +} + +static const AlignedData<2> SUBSTRUCT_DEFAULT = {{0,0,0,0,1,0,0,0, 0,0,0,0,0,0,0,0}}; +static const AlignedData<2> STRUCTLIST_ELEMENT_SUBSTRUCT_DEFAULT = + {{0,0,0,0,1,0,0,0, 0,0,0,0,0,0,0,0}}; + +static void setupStruct(struct capn *ctx) { + struct capn_ptr root = capn_root(ctx); + EXPECT_EQ(CAPN_PTR_LIST, root.type); + EXPECT_EQ(1, root.size); + + struct capn_ptr ptr = capn_new_struct(root.seg, 16, 4); + EXPECT_EQ(CAPN_STRUCT, ptr.type); + EXPECT_EQ(16, ptr.datasz); + EXPECT_EQ(32, ptr.ptrsz); + EXPECT_EQ(0, capn_setp(&root, 0, &ptr)); + + EXPECT_EQ(0, capn_write64(&ptr, 0, UINT64_C(0x1011121314151617))); + EXPECT_EQ(0, capn_write32(&ptr, 8, UINT32_C(0x20212223))); + EXPECT_EQ(0, capn_write16(&ptr, 12, UINT16_C(0x3031))); + EXPECT_EQ(0, capn_write8(&ptr, 14, 0x40)); + EXPECT_EQ(0, capn_write8(&ptr, 15, (1 << 6) | (1 << 5) | (1 << 4) | (1 << 2))); + + struct capn_ptr subStruct = capn_new_struct(ptr.seg, 8, 0); + EXPECT_EQ(CAPN_STRUCT, subStruct.type); + EXPECT_EQ(8, subStruct.datasz); + EXPECT_EQ(0, subStruct.ptrsz); + EXPECT_EQ(0, capn_write32(&subStruct, 0, 123)); + EXPECT_NE(0, capn_write32(&subStruct, 8, 124)); + EXPECT_EQ(0, capn_setp(&ptr, 0, &subStruct)); + + struct capn_ptr list = capn_new_list(ptr.seg, 3, 4, 0); + EXPECT_EQ(CAPN_LIST, list.type); + EXPECT_EQ(3, list.size); + EXPECT_EQ(4, list.datasz); + EXPECT_EQ(0, capn_set32(&list, 0, 200)); + EXPECT_EQ(0, capn_set32(&list, 1, 201)); + EXPECT_EQ(0, capn_set32(&list, 2, 202)); + EXPECT_NE(0, capn_set32(&list, 3, 203)); + EXPECT_NE(0, capn_set64(&list, 0, 405)); + EXPECT_EQ(0, capn_setp(&ptr, 1, &list)); + + list = capn_new_list(ptr.seg, 4, 4, 1); + EXPECT_EQ(CAPN_LIST, list.type); + EXPECT_EQ(4, list.size); + EXPECT_EQ(8, list.datasz); + EXPECT_EQ(8, list.ptrsz); + EXPECT_EQ(0, capn_setp(&ptr, 2, &list)); + for (int i = 0; i < 4; i++) { + struct capn_ptr element = capn_getp(&list, i); + EXPECT_EQ(CAPN_LIST_MEMBER, element.type); + EXPECT_EQ(8, element.datasz); + EXPECT_EQ(8, element.ptrsz); + EXPECT_EQ(0, capn_write32(&element, 0, 300+i)); + + struct capn_ptr subelement = capn_new_struct(element.seg, 8, 0); + EXPECT_EQ(CAPN_STRUCT, subelement.type); + EXPECT_EQ(8, subelement.datasz); + EXPECT_EQ(0, subelement.ptrsz); + EXPECT_EQ(0, capn_write32(&subelement, 0, 400+i)); + EXPECT_EQ(0, capn_setp(&element, 0, &subelement)); + } + + list = capn_new_ptr_list(ptr.seg, 5); + EXPECT_EQ(CAPN_PTR_LIST, list.type); + EXPECT_EQ(5, list.size); + EXPECT_EQ(0, capn_setp(&ptr, 3, &list)); + for (int i = 0; i < 5; i++) { + struct capn_ptr element = capn_new_list(list.seg, i+1, 2, 0); + EXPECT_EQ(CAPN_LIST, element.type); + EXPECT_EQ(i+1, element.size); + EXPECT_EQ(2, element.datasz); + EXPECT_EQ(0, element.ptrsz); + EXPECT_EQ(0, capn_setp(&list, i, &element)); + for (int j = 0; j <= i; j++) { + EXPECT_EQ(0, capn_set16(&element, j, 500+j)); + } + } +} + +static void checkStruct(struct capn *ctx) { + struct capn_ptr root = capn_root(ctx); + EXPECT_EQ(CAPN_PTR_LIST, root.type); + EXPECT_EQ(1, root.size); + + struct capn_ptr ptr = capn_getp(&root, 0); + EXPECT_EQ(CAPN_STRUCT, ptr.type); + EXPECT_EQ(16, ptr.datasz); + EXPECT_EQ(32, ptr.ptrsz); + EXPECT_EQ(UINT64_C(0x1011121314151617), capn_read64(&ptr, 0)); + EXPECT_EQ(UINT32_C(0x20212223), capn_read32(&ptr, 8)); + EXPECT_EQ(0x3031, capn_read16(&ptr, 12)); + EXPECT_EQ(0x40, capn_read8(&ptr, 14)); + EXPECT_EQ((1 << 6) | (1 << 5) | (1 << 4) | (1 << 2), capn_read8(&ptr, 15)); + + struct capn_ptr subStruct = capn_getp(&ptr, 0); + EXPECT_EQ(CAPN_STRUCT, subStruct.type); + EXPECT_EQ(8, subStruct.datasz); + EXPECT_EQ(0, subStruct.ptrsz); + EXPECT_EQ(123, capn_read32(&subStruct, 0)); + + struct capn_ptr list = capn_getp(&ptr, 1); + EXPECT_EQ(CAPN_LIST, list.type); + EXPECT_EQ(3, list.size); + EXPECT_EQ(4, list.datasz); + EXPECT_EQ(0, list.ptrsz); + EXPECT_EQ(200, capn_get32(&list, 0)); + EXPECT_EQ(201, capn_get32(&list, 1)); + EXPECT_EQ(202, capn_get32(&list, 2)); + EXPECT_EQ(0, capn_get32(&list, 3)); + EXPECT_EQ(0, capn_get64(&list, 0)); + EXPECT_EQ(201, capn_get8(&list, 1)); + EXPECT_EQ(202, capn_get16(&list, 2)); + + list = capn_getp(&ptr, 2); + EXPECT_EQ(CAPN_LIST, list.type); + EXPECT_EQ(4, list.size); + EXPECT_EQ(8, list.datasz); + EXPECT_EQ(8, list.ptrsz); + + for (int i = 0; i < 4; i++) { + struct capn_ptr element = capn_getp(&list, i); + EXPECT_EQ(CAPN_LIST_MEMBER, element.type); + EXPECT_EQ(8, element.datasz); + EXPECT_EQ(8, element.ptrsz); + EXPECT_EQ(300+i, capn_read32(&element,0)); + + struct capn_ptr subelement = capn_getp(&element, 0); + EXPECT_EQ(CAPN_STRUCT, subelement.type); + EXPECT_EQ(8, subelement.datasz); + EXPECT_EQ(0, subelement.ptrsz); + EXPECT_EQ(400+i, capn_read32(&subelement, 0)); + } + + list = capn_getp(&ptr, 3); + EXPECT_EQ(CAPN_PTR_LIST, list.type); + EXPECT_EQ(5, list.size); + for (int i = 0; i < 5; i++) { + struct capn_ptr element = capn_getp(&list, i); + EXPECT_EQ(CAPN_LIST, element.type); + EXPECT_EQ(i+1, element.size); + EXPECT_EQ(2, element.datasz); + EXPECT_EQ(0, element.ptrsz); + for (int j = 0; j <= i; j++) { + EXPECT_EQ(500+j, capn_get16(&element, j)); + } + } +} + +TEST(WireFormat, StructRoundTrip_OneSegment) { + Session ctx; + setupStruct(&ctx.capn); + + // word count: + // 1 root reference + // 6 root struct + // 1 sub message + // 2 3-element int32 list + // 13 struct list + // 1 tag + // 12 4x struct + // 1 data segment + // 1 reference segment + // 1 sub-struct + // 11 list list + // 5 references to sub-lists + // 6 sub-lists (4x 1 word, 1x 2 words) + // ----- + // 34 + ASSERT_EQ(1, ctx.capn.segnum); + EXPECT_EQ(34*8, ctx.capn.seglist->len); + + checkStruct(&ctx.capn); + + struct capn ctx2; + memset(&ctx2, 0, sizeof(ctx2)); + capn_append_segment(&ctx2, ctx.capn.seglist); + checkStruct(&ctx2); +} + +static struct capn_segment *CreateSmallSegment(void *u, uint32_t id, int sz) { + struct capn_segment *s = (struct capn_segment*) calloc(1, sizeof(*s)); + s->data = (char*) calloc(1, sz); + s->cap = sz; + return s; +} + +static void getSegments(struct capn *c, struct capn_segment **s, size_t num) { + ASSERT_EQ(num, c->segnum); + + s[0] = c->seglist; + for (int i = 1; i < num; i++) { + s[i] = s[i-1]->next; + } + + for (int i = 0; i < num; i++) { + EXPECT_EQ(s[i]->id, i); + } +} + +TEST(WireFormat, StructRoundTrip_OneSegmentPerAllocation) { + Session ctx; + ctx.capn.create = &CreateSmallSegment; + + setupStruct(&ctx.capn); + + struct capn_segment *segments[15]; + getSegments(&ctx.capn, segments, 15); + + // Check that each segment has the expected size. Recall that the first word of each segment will + // actually be a reference to the first thing allocated within that segment. + EXPECT_EQ( 8, segments[ 0]->len); // root ref + EXPECT_EQ(56, segments[ 1]->len); // root struct + EXPECT_EQ(16, segments[ 2]->len); // sub-struct + EXPECT_EQ(24, segments[ 3]->len); // 3-element int32 list + EXPECT_EQ(80, segments[ 4]->len); // struct list + EXPECT_EQ(16, segments[ 5]->len); // struct list substruct 1 + EXPECT_EQ(16, segments[ 6]->len); // struct list substruct 2 + EXPECT_EQ(16, segments[ 7]->len); // struct list substruct 3 + EXPECT_EQ(16, segments[ 8]->len); // struct list substruct 4 + EXPECT_EQ(48, segments[ 9]->len); // list list + EXPECT_EQ(16, segments[10]->len); // list list sublist 1 + EXPECT_EQ(16, segments[11]->len); // list list sublist 2 + EXPECT_EQ(16, segments[12]->len); // list list sublist 3 + EXPECT_EQ(16, segments[13]->len); // list list sublist 4 + EXPECT_EQ(24, segments[14]->len); // list list sublist 5 + + checkStruct(&ctx.capn); + + struct capn ctx2; + memset(&ctx2, 0, sizeof(ctx2)); + for (int i = 0; i < 15; i++) { + capn_append_segment(&ctx2, segments[i]); + } + + checkStruct(&ctx2); +} + +TEST(WireFormat, StructRoundTrip_OneSegmentPerAllocation_NoTag) { + Session ctx; + ctx.capn.create = &CreateSmallSegment; + + g_AddTag = 0; + setupStruct(&ctx.capn); + g_AddTag = 1; + + struct capn_segment *segments[29]; + getSegments(&ctx.capn, segments, 29); + + // Check that each segment has the expected size. Note that we have plenty + // of 16 byte double far ptrs. + EXPECT_EQ( 8, segments[ 0]->len); // root ref + EXPECT_EQ(48, segments[ 1]->len); // root struct + EXPECT_EQ(16, segments[ 2]->len); // root struct ptr + EXPECT_EQ( 8, segments[ 3]->len); // sub-struct + EXPECT_EQ(16, segments[ 4]->len); // sub-struct ptr + EXPECT_EQ(16, segments[ 5]->len); // 3-element int32 list + EXPECT_EQ(16, segments[ 6]->len); // 3-element int32 list ptr + EXPECT_EQ(72, segments[ 7]->len); // struct list + EXPECT_EQ(16, segments[ 8]->len); // struct list ptr + EXPECT_EQ( 8, segments[ 9]->len); // struct list substruct 1 + EXPECT_EQ(16, segments[10]->len); // struct list substruct 1 ptr + EXPECT_EQ( 8, segments[11]->len); // struct list substruct 2 + EXPECT_EQ(16, segments[12]->len); // struct list substruct 2 ptr + EXPECT_EQ( 8, segments[13]->len); // struct list substruct 3 + EXPECT_EQ(16, segments[14]->len); // struct list substruct 3 ptr + EXPECT_EQ( 8, segments[15]->len); // struct list substruct 4 + EXPECT_EQ(16, segments[16]->len); // struct list substruct 4 ptr + EXPECT_EQ(40, segments[17]->len); // list list + EXPECT_EQ(16, segments[18]->len); // list list ptr + EXPECT_EQ( 8, segments[19]->len); // list list sublist 1 + EXPECT_EQ(16, segments[20]->len); // list list sublist 1 ptr + EXPECT_EQ( 8, segments[21]->len); // list list sublist 2 + EXPECT_EQ(16, segments[22]->len); // list list sublist 2 ptr + EXPECT_EQ( 8, segments[23]->len); // list list sublist 3 + EXPECT_EQ(16, segments[24]->len); // list list sublist 3 ptr + EXPECT_EQ( 8, segments[25]->len); // list list sublist 4 + EXPECT_EQ(16, segments[26]->len); // list list sublist 4 ptr + EXPECT_EQ(16, segments[27]->len); // list list sublist 5 + EXPECT_EQ(16, segments[28]->len); // list list sublist 5 ptr + + checkStruct(&ctx.capn); + + struct capn ctx2; + memset(&ctx2, 0, sizeof(ctx2)); + for (int i = 0; i < 29; i++) { + capn_append_segment(&ctx2, segments[i]); + } + + checkStruct(&ctx2); +} + + +static struct capn_segment *CreateSegment64(void *u, uint32_t id, int sz) { + if (sz < 64) { + sz = 64; + } + struct capn_segment *s = (struct capn_segment*) calloc(1, sizeof(*s)); + s->data = (char*) calloc(1, sz); + s->cap = sz; + return s; +} + +TEST(WireFormat, StructRoundTrip_MultipleSegmentsWithMultipleAllocations) { + Session ctx; + ctx.capn.create = &CreateSegment64; + + setupStruct(&ctx.capn); + + // Verify that we made 6 segments. + ASSERT_EQ(6, ctx.capn.segnum); + + struct capn_segment *segments[6]; + segments[0] = ctx.capn.seglist; + for (int i = 1; i < 6; i++) { + segments[i] = segments[i-1]->next; + } + + for (int i = 0; i < 6; i++) { + EXPECT_EQ(segments[i]->id, i); + } + + // Check that each segment has the expected size. Recall that each object will be prefixed by an + // extra word if its parent is in a different segment. + EXPECT_EQ(64, segments[0]->len); // root ref + struct + sub + EXPECT_EQ(56, segments[1]->len); // 3-element int32 list (+tag) + struct list substructs 1+2 (+tag) + EXPECT_EQ(80, segments[2]->len); // struct list (+tag) + EXPECT_EQ(64, segments[3]->len); // struct list substructs 3+4 (+tag) + list list sublist 1,2 (+tag) + EXPECT_EQ(64, segments[4]->len); // list list (+tag) + sublist 3,4 + EXPECT_EQ(24, segments[5]->len); // list list sublist 5 + + checkStruct(&ctx.capn); + + struct capn ctx2; + memset(&ctx2, 0, sizeof(ctx2)); + for (int i = 0; i < 6; i++) { + capn_append_segment(&ctx2, segments[i]); + } + + checkStruct(&ctx2); +} + +int main(int argc, char *argv[]) { + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} + diff --git a/capn.c b/capn.c index fba5bb3..b65bc60 100644 --- a/capn.c +++ b/capn.c @@ -37,103 +37,132 @@ static int min(int a, int b) { return (a < b) ? a : b; } #define CAPN_LITTLE 0 #endif -/* from to - * G G - * / \ / \ - * P U N U - * / \ / \ - * 1 N P 3 - * / \ / \ - * 2 3 1 2 - */ -static void rotate_left(struct capn_tree **pp, struct capn_tree **pn) { - struct capn_tree *p = *pp, *n = *pn; - struct capn_tree *g = p->parent; - p->right = n->left; - n->left = p; - g->left = n; - n->parent = g; - p->parent = n; - *pn = p; - *pp = n; -} - -/* from to - * G G - * / \ / \ - * U P U N - * / \ / \ - * N 3 1 P - * / \ / \ - * 1 2 2 3 - */ -static void rotate_right(struct capn_tree **pp, struct capn_tree **pn) { - struct capn_tree *p = *pp, *n = *pn; - struct capn_tree *g = p->parent; - p->left = n->right; - n->right = p; - g->right = n; - n->parent = g; - p->parent = n; - *pn = p; - *pp = n; -} - -static void insert_rebalance(struct capn_tree **root, struct capn_tree *n) { +static struct capn_tree *insert_rebalance(struct capn_tree *root, struct capn_tree *n) { n->red = 1; - n->left = n->right = NULL; + n->link[0] = n->link[1] = NULL; for (;;) { - struct capn_tree *p, *u, *g; + /* 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) { - *root = n; n->red = 0; - return; + root = n; + break; + } + + /* Case 2: p is black */ + if (!p->red) { + break; } g = p->parent; - if (!g->red) { - return; + 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 (p == g->left) { - if ((u = g->right) != NULL && u->red) { - p->red = 0; - u->red = 0; - g->red = 1; - n = g; - continue; - } - if (n == p->right) { - rotate_left(&p, &n); - } - p->red = 0; - g->red = 1; - rotate_right(&g, &p); + if (!g->parent) { + gglink = &root; + } else if (g->parent->link[1] == g) { + gglink = &g->parent->link[1]; } else { - if ((u = g->left) != NULL && u->red) { - p->red = 0; - u->red = 0; - g->red = 1; - n = g; - continue; - } - if (n == p->left) { - rotate_right(&p, &n); - } - p->red = 0; - g->red = 1; - rotate_left(&g, &p); + 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; } -static void *new_data(struct capn_segment **ps, int sz) { - struct capn *c = (*ps)->capn; - struct capn_segment *s = *ps; +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 = insert_rebalance(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) { @@ -144,35 +173,17 @@ static void *new_data(struct capn_segment **ps, int sz) { s = c->create ? c->create(c->user, c->segnum, sz) : NULL; if (!s) { + *ps = NULL; return NULL; } - s->capn = c; - s->id = c->segnum++; - s->next = c->seglist; - c->seglist = s; - s->hdr.parent = c->lastseg; - c->lastseg->right = &s->hdr; - c->lastseg = &s->hdr; - insert_rebalance(&c->segtree, &s->hdr); - + capn_append_segment(c, s); end: *ps = s; s->len += sz; return s->data + s->len - sz; } -void capn_append_segment(struct capn *c, struct capn_segment *s) { - s->id = c->segnum++; - s->capn = c; - s->next = c->seglist; - c->seglist = s; - s->hdr.parent = c->lastseg; - c->lastseg->right = &s->hdr; - c->lastseg = &s->hdr; - insert_rebalance(&c->segtree, &s->hdr); -} - static struct capn_segment *lookup_segment(struct capn* c, struct capn_segment *s, uint32_t id) { struct capn_tree **x; struct capn_segment *y; @@ -182,16 +193,18 @@ static struct capn_segment *lookup_segment(struct capn* c, struct capn_segment * if (!c) return NULL; - x = &c->segtree; - y = NULL; - while (*x) { - y = (struct capn_segment*) *x; - if (id == y->id) { - return y; - } else if (id < y->id) { - x = &y->hdr.left; - } else { - x = &y->hdr.right; + if (id < c->segnum) { + x = &c->segtree; + y = NULL; + 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]; + } } } @@ -199,24 +212,24 @@ static struct capn_segment *lookup_segment(struct capn* c, struct capn_segment * if (!s) return NULL; - if (id >= c->segnum) { - c->lastseg = &s->hdr; - c->segnum = id+1; + 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 = insert_rebalance(c->segtree, &s->hdr); + } else { + c->segnum = id; + capn_append_segment(c, s); } - s->id = id; - s->capn = c; - s->next = c->seglist; - c->seglist = s; - s->hdr.parent = &y->hdr; - *x = &s->hdr; - insert_rebalance(&c->segtree, &s->hdr); - return s; } static uint64_t lookup_far(struct capn_segment **s, char **d, uint64_t val) { - uint32_t off = U32(val >> 3); + uint32_t off = (U32(val) >> 3) * 8; if ((*s = lookup_segment((*s)->capn, *s, U32(val >> 32))) == NULL) { return 0; @@ -226,7 +239,7 @@ static uint64_t lookup_far(struct capn_segment **s, char **d, uint64_t val) { /* Double far pointer */ uint64_t far, tag; char *p = (*s)->data + off; - if (off + 16 >= (*s)->len) { + if (off + 16 > (*s)->len) { return 0; } @@ -243,10 +256,15 @@ static uint64_t lookup_far(struct capn_segment **s, char **d, uint64_t val) { return 0; } - *d = (*s)->data; - return U64(U32(far >> 3) << 2) | tag; + /* -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; } else { - if (off + 8 >= (*s)->len) { + if (off + 8 > (*s)->len) { return 0; } @@ -255,7 +273,7 @@ static uint64_t lookup_far(struct capn_segment **s, char **d, uint64_t val) { } } -static char *struct_ptr(struct capn_segment *s, char *d) { +static char *struct_ptr(struct capn_segment *s, char *d, int minsz) { uint64_t val = capn_flip64(*(uint64_t*)d); uint16_t datasz; @@ -266,7 +284,7 @@ static char *struct_ptr(struct capn_segment *s, char *d) { datasz = U16(val >> 32); d += (I32(U32(val)) << 1) + 8; - if (val != 0 && (val&3) != STRUCT_PTR && !datasz && s->data <= d && d < s->data + s->len) { + if (val != 0 && (val&3) != STRUCT_PTR && datasz >= minsz && s->data <= d && d < s->data + s->len) { return d; } @@ -284,8 +302,7 @@ static struct capn_ptr read_ptr(struct capn_segment *s, char *d) { val = lookup_far(&s, &d, val); } - d += (I32(U32(val)) << 1) + 8; - ret.data = d; + d += (I32(U32(val)) >> 2) * 8 + 8; if ((val&3) > LIST_PTR || d < s->data) { goto err; @@ -344,7 +361,7 @@ static struct capn_ptr read_ptr(struct capn_segment *s, char *d) { ret.datasz = U32(U16(val >> 32)) * 8; ret.ptrsz = U32(U16(val >> 48)) * 8; - ret.size = U32(val >> 2); + ret.size = U32(val) >> 2; if ((ret.datasz + ret.ptrsz) * ret.size != e - d) { goto err; @@ -356,6 +373,7 @@ static struct capn_ptr read_ptr(struct capn_segment *s, char *d) { if (e - s->data > s->len) goto err; + ret.data = d; ret.seg = s; return ret; err: @@ -363,7 +381,7 @@ err: return ret; } -struct capn_ptr capn_read_ptr(const struct capn_ptr *p, int off) { +struct capn_ptr capn_getp(const struct capn_ptr *p, int off) { struct capn_ptr ret; switch (p->type) { @@ -404,11 +422,11 @@ err: } static uint64_t ptr_value(const struct capn_ptr *p, int off) { - uint64_t val = U64(U32(I32((off >> 3) << 2))); + uint64_t val = U64(U32(I32(off/8) << 2)); switch (p->type) { case CAPN_STRUCT: - val |= STRUCT_PTR | (U64(p->datasz) << 32) | (U64(p->ptrsz) << 48); + val |= STRUCT_PTR | (U64(p->datasz/8) << 32) | (U64(p->ptrsz/8) << 48); break; case CAPN_LIST: @@ -455,15 +473,22 @@ static void write_ptr_tag(char *d, const struct capn_ptr *p, int off) { *(uint64_t*) d = ptr_value(p, off); } -static int has_tag(const struct capn_ptr* p) { - char *d = p->data - 8; +static int has_tag(const struct capn_ptr* p, const char *pdata) { + const char *d = pdata - 8; return d >= p->seg->data && ptr_value(p, 0) == *(uint64_t*) d; } #define NEED_TO_COPY 1 -static int write_ptr(struct capn_segment *s, char *d, const struct capn_ptr *p) { +static int write_ptr_no_copy(struct capn_segment *s, char *d, const struct capn_ptr *p) { /* note p->seg can be NULL if its a ptr to static data */ + char *pdata = p->data; + + /* TODO: how to distinguish between 8 byte list and a composite + * list with ptrs == 0, data == 1? */ + if (p->type == CAPN_LIST && (p->ptrsz || p->datasz > 8)) { + pdata -= 8; + } if (!p || p->type == CAPN_NULL) { *(uint64_t*) d = 0; @@ -473,23 +498,23 @@ static int write_ptr(struct capn_segment *s, char *d, const struct capn_ptr *p) return NEED_TO_COPY; } else if (p->seg == s) { - write_ptr_tag(d, p, p->data - d - 8); + write_ptr_tag(d, p, pdata - d - 8); return 0; } else { /* if its in the same context we can create a far pointer */ - if (has_tag(p)) { + if (has_tag(p, pdata)) { /* 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, p->data); + 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, p->data - t - 8); + write_ptr_tag(t, p, pdata - t - 8); write_far_ptr(d, p->seg, t); p->seg->len += 8; return 0; @@ -506,11 +531,11 @@ static int write_ptr(struct capn_segment *s, char *d, const struct capn_ptr *p) t = s->data + s->len; s->len += 16; } else { - t = new_data(&s, 16); + t = new_data(s->capn, 16, &s); if (!t) return -1; } - write_far_ptr(t, p->seg, p->data); + write_far_ptr(t, p->seg, pdata); write_ptr_tag(t+8, p, 0); write_double_far(d, s, t); return 0; @@ -561,7 +586,7 @@ 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->size == b->size && a->datasz == b->datasz && a->ptrsz == b->ptrsz; } -static int write_copy(struct capn_segment *seg, char *data, struct capn_ptr *t, struct capn_ptr *f, int *dep) { +static int write_copy(struct capn_segment *seg, char *data, struct capn_ptr *t, struct capn_ptr *f, int *dep, int zeros) { struct capn *c = seg->capn; struct copy *cp = (struct copy*) c->copy; int sz = data_size(f); @@ -576,12 +601,12 @@ static int write_copy(struct capn_segment *seg, char *data, struct capn_ptr *t, while (c && sz) { if (f->data + sz <= cp->from.data) { - cp = (struct copy*) cp->hdr.left; + cp = (struct copy*) cp->hdr.link[0]; } else if (cp->from.data + cp->fsize <= f->data) { - cp = (struct copy*) cp->hdr.right; + cp = (struct copy*) 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->from); + return write_ptr_no_copy(seg, data, &cp->from); } else { /* pointer to overlapped data */ return -1; @@ -617,12 +642,12 @@ static int write_copy(struct capn_segment *seg, char *data, struct capn_ptr *t, n->fsize = sz; if (f->data < cp->from.data) { - cp->hdr.left = &n->hdr; + cp->hdr.link[0] = &n->hdr; } else { - cp->hdr.right = &n->hdr; + cp->hdr.link[1] = &n->hdr; } - insert_rebalance(&seg->capn->copy, &n->hdr); + seg->capn->copy = insert_rebalance(seg->capn->copy, &n->hdr); } /* minimize the number of types the main copy routine has to @@ -633,7 +658,7 @@ static int write_copy(struct capn_segment *seg, char *data, struct capn_ptr *t, case CAPN_STRUCT: case CAPN_LIST_MEMBER: if (t->datasz) { - memcpy(t->data, f->data, t->datasz); + memcpy(t->data, f->data, t->datasz - zeros); t->data += t->datasz; f->data += t->datasz; } @@ -675,7 +700,7 @@ static int write_copy(struct capn_segment *seg, char *data, struct capn_ptr *t, #define MAX_COPY_DEPTH 32 -int capn_write_ptr(struct capn_ptr *p, int off, const struct capn_ptr *tgt) { +int write_ptr(struct capn_ptr *p, int off, const struct capn_ptr *tgt, int zeros) { struct capn_ptr to[MAX_COPY_DEPTH], from[MAX_COPY_DEPTH]; char *data; int err, dep; @@ -736,7 +761,7 @@ int capn_write_ptr(struct capn_ptr *p, int off, const struct capn_ptr *tgt) { return -1; } - err = write_ptr(p->seg, data, tgt); + err = write_ptr_no_copy(p->seg, data, tgt); if (err != NEED_TO_COPY) return err; @@ -750,7 +775,7 @@ int capn_write_ptr(struct capn_ptr *p, int off, const struct capn_ptr *tgt) { dep = 0; from[0] = *tgt; - if (write_copy(p->seg, data, to, from, &dep)) + if (write_copy(p->seg, data, to, from, &dep, zeros)) return -1; copy_loop: @@ -775,7 +800,7 @@ copy_loop: fn->type = tn->type = CAPN_LIST_MEMBER; fn->size = tn->size = 0; - if (write_copy(tc->seg, tc->data, tn, fn, &dep)) + if (write_copy(tc->seg, tc->data, tn, fn, &dep, 0)) return -1; fc->data += tc->datasz + tc->ptrsz; @@ -787,7 +812,7 @@ copy_loop: default: *fn = read_ptr(fc->seg, fc->data); - if (write_copy(tc->seg, tc->data, tn, fn, &dep)) + if (write_copy(tc->seg, tc->data, tn, fn, &dep, 0)) return -1; fc->data += 8; @@ -800,10 +825,13 @@ copy_loop: return 0; } -int capn_read1(const struct capn_list1 *list, int off, uint8_t *data, int sz) { +int capn_setp(struct capn_ptr *p, int off, const struct capn_ptr *tgt) { + return write_ptr(p, off, tgt, 0); +} + +int capn_read1(const struct capn_ptr *p, int off, uint8_t *data, int sz) { /* Note we only support aligned reads */ int bsz; - const struct capn_ptr *p = &list->p; if (p->type != CAPN_BIT_LIST || (off & 7) != 0) return -1; @@ -819,10 +847,9 @@ int capn_read1(const struct capn_list1 *list, int off, uint8_t *data, int sz) { } } -int capn_write1(struct capn_list1 *list, int off, const uint8_t *data, int sz) { +int capn_write1(struct capn_ptr *p, int off, const uint8_t *data, int sz) { /* Note we only support aligned writes */ int bsz; - const struct capn_ptr *p = &list->p; if (p->type != CAPN_BIT_LIST || (off & 7) != 0) return -1; @@ -854,6 +881,12 @@ int capn_write1(struct capn_list1 *list, int off, const uint8_t *data, int sz) { #include "capn-list.inc" #undef SZ +/* pull out whether we add a tag or nor 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(struct capn_ptr *p, int bytes) { struct capn_segment *s = p->seg; @@ -868,26 +901,34 @@ static void new_object(struct capn_ptr *p, int bytes) { /* add a tag whenever we switch segments so that write_ptr can * use it */ - p->data = new_data(&s, bytes + 8); + p->data = new_data(s->capn, bytes + ADD_TAG*8, &p->seg); if (!p->data) { memset(p, 0, sizeof(*p)); return; } - write_ptr_tag(p->data, p, 0); - p->data += 8; + if (ADD_TAG) { + write_ptr_tag(p->data, p, 0); + p->data += 8; + } } struct capn_ptr capn_root(struct capn* c) { struct capn_ptr p; p.seg = lookup_segment(c, NULL, 0); - if (!p.seg) goto err; + + /* don't use new_object as we don't want the tag */ + if (!p.seg && new_data(c, 8, &p.seg) == NULL) + goto err; + + if (p.seg->len < 8) + goto err; + p.data = p.seg->data; p.size = 1; p.type = CAPN_PTR_LIST; p.datasz = 0; p.ptrsz = 0; - new_object(&p, 8); return p; err: memset(&p, 0, sizeof(p)); @@ -910,19 +951,28 @@ struct capn_ptr capn_new_list(struct capn_segment *seg, int sz, int datasz, int p.seg = seg; p.type = CAPN_LIST; p.size = sz; + p.ptrsz = 0; - if (ptrs || datasz > 4) { + if (ptrs || datasz > 8) { p.datasz = (datasz + 7) & ~7; p.ptrsz = ptrs*8; - } else if (datasz == 3) { + new_object(&p, p.size * (p.datasz + p.ptrsz) + 8); + if (p.data) { + uint64_t hdr = STRUCT_PTR | (U64(p.size) << 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.size * 8); + } else if (datasz > 2) { p.datasz = 4; - p.ptrsz = 0; + new_object(&p, p.size * 4); } else { p.datasz = datasz; - p.ptrsz = 0; + new_object(&p, p.size * datasz); } - new_object(&p, p.size * (p.datasz+p.ptrsz)); return p; } @@ -972,12 +1022,13 @@ char *capn_to_string(const struct capn_ptr *p, int *psz) { return p->data; } -struct capn_text capn_read_text(const struct capn_ptr *p, int off) { +struct capn_text capn_get_text(const struct capn_ptr *p, int off) { + struct capn_ptr m = capn_getp(p, off); struct capn_text ret; - if (p->type == CAPN_LIST && p->datasz == 1 && p->size && p->data[p->size - 1] == 0) { - ret.seg = p->seg; - ret.str = p->data; - ret.size = p->size - 1; + if (m.type == CAPN_LIST && m.datasz == 1 && m.size && m.data[m.size - 1] == 0) { + ret.seg = m.seg; + ret.str = m.data; + ret.size = m.size - 1; } else { ret.seg = NULL; ret.str = NULL; @@ -986,12 +1037,13 @@ struct capn_text capn_read_text(const struct capn_ptr *p, int off) { return ret; } -struct capn_data capn_read_data(const struct capn_ptr *p, int off) { +struct capn_data capn_get_data(const struct capn_ptr *p, int off) { + struct capn_ptr m = capn_getp(p, off); struct capn_data ret; - if (p->type == CAPN_LIST && p->datasz == 1) { - ret.seg = p->seg; - ret.data = (uint8_t*) p->data; - ret.size = p->size; + if (m.type == CAPN_LIST && m.datasz == 1) { + ret.seg = m.seg; + ret.data = (uint8_t*) m.data; + ret.size = m.size; } else { ret.seg = NULL; ret.data = NULL; @@ -1000,34 +1052,40 @@ struct capn_data capn_read_data(const struct capn_ptr *p, int off) { return ret; } -int capn_write_text(struct capn_ptr *p, int off, struct capn_text tgt) { +int capn_set_text(struct capn_ptr *p, int off, struct capn_text tgt) { struct capn_ptr m; if (tgt.str) { m.type = CAPN_LIST; - m.size = (tgt.size >= 0 ? tgt.size : strlen(tgt.str)) + 1; m.seg = tgt.seg; m.data = (char*)tgt.str; + m.size = (tgt.size >= 0 ? tgt.size : strlen(tgt.str)) + 1; m.datasz = 1; + m.ptrsz = 0; } else { m.type = CAPN_NULL; } - return capn_write_ptr(p, off, &m); + /* in the case that the size is specified we need to be careful + * that we don't read the extra byte as it may be not be null or + * may be in a different page and cause a segfault + */ + return write_ptr(p, off, &m, 1); } -int capn_write_data(struct capn_ptr *p, int off, struct capn_data tgt) { +int capn_set_data(struct capn_ptr *p, int off, struct capn_data tgt) { struct capn_ptr m; if (tgt.data) { m.type = CAPN_LIST; + m.seg = tgt.seg; m.data = (char*)tgt.data; m.size = tgt.size; m.datasz = 1; - m.seg = tgt.seg; + m.ptrsz = 0; } else { m.type = CAPN_NULL; } - return capn_write_ptr(p, off, &m); + return write_ptr(p, off, &m, 0); } int capn_marshal_iptr(const union capn_iptr *ip, struct capn_ptr *p, int off) { - return capn_write_ptr(p, off, &ip->c); + return write_ptr(p, off, &ip->c, 0); } diff --git a/capn.h b/capn.h index 857f74a..518509e 100644 --- a/capn.h +++ b/capn.h @@ -5,6 +5,10 @@ #include +#ifdef __cplusplus +extern "C" { +#endif + #define CAPN_SEGID_LOCAL 0xFFFFFFFF /* struct capn is a common structure shared between segments in the same @@ -35,13 +39,13 @@ struct capn { void *user; uint32_t segnum; struct capn_tree *copy; - struct capn_tree *segtree, *lastseg; - struct capn_segment *seglist; + struct capn_tree *segtree; + struct capn_segment *seglist, *lastseg; struct capn_segment *copylist; }; struct capn_tree { - struct capn_tree *left, *right, *parent; + struct capn_tree *parent, *link[2]; unsigned int red : 1; }; @@ -110,65 +114,69 @@ struct capn_ret_vt { void (*free)(void*); }; -struct capn_list1{struct capn_ptr p;}; -struct capn_list8{struct capn_ptr p;}; -struct capn_list16{struct capn_ptr p;}; -struct capn_list32{struct capn_ptr p;}; -struct capn_list64{struct capn_ptr p;}; - /* capn_append_segment appends a segment to a session */ void capn_append_segment(struct capn*, struct capn_segment*); -/* capn_root returns a fake pointer that can be used to read/write the session - * root object using capn_(read|write)_ptr at index 0. The root is the object +/* capn_root returns a fake pointer that can be used to get/set the session + * root object using capn_(get|set)_ptr at index 0. The root is the object * pointed to by a ptr at offset 0 in segment 0. This will allocate room for * the root if not already. */ struct capn_ptr capn_root(struct capn*); -/* capn_read|write_ptr functions read/write ptrs to list/structs +/* capn_getp|setp functions get/set ptrs in list/structs * off is the list index or pointer index in a struct - * capn_write_ptr will copy the data, create far pointers, etc if the target + * capn_set_ptr 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. */ -struct capn_ptr capn_read_ptr(const struct capn_ptr *p, int off); -int capn_write_ptr(struct capn_ptr *p, int off, const struct capn_ptr *tgt); +struct capn_ptr capn_getp(const struct capn_ptr *p, int off); +int capn_setp(struct capn_ptr *p, int off, const struct capn_ptr *tgt); /* capn_to_string returns a pointer to a string * Use this instead of accessing the data directly as these checks that the * string is null terminated, the list type, etc. * psz is filled out with the string length if non NULL */ -struct capn_text capn_read_text(const struct capn_ptr *p, int off); -struct capn_data capn_read_data(const struct capn_ptr *p, int off); -int capn_write_text(struct capn_ptr *p, int off, struct capn_text tgt); -int capn_write_data(struct capn_ptr *p, int off, struct capn_data tgt); +struct capn_text capn_get_text(const struct capn_ptr *p, int off); +struct capn_data capn_get_data(const struct capn_ptr *p, int off); +int capn_set_text(struct capn_ptr *p, int off, struct capn_text tgt); +int capn_set_data(struct capn_ptr *p, int off, struct capn_data tgt); -/* capn_read_* functions read data from a list +/* 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 read + * 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_read_1 + * off must be byte aligned for capn_get1v */ -int capn_read1(const struct capn_list1 *p, int off, uint8_t *data, int sz); -int capn_read8(const struct capn_list8 *p, int off, uint8_t *data, int sz); -int capn_read16(const struct capn_list16 *p, int off, uint16_t *data, int sz); -int capn_read32(const struct capn_list32 *p, int off, uint32_t *data, int sz); -int capn_read64(const struct capn_list64 *p, int off, uint64_t *data, int sz); +int capn_get1(const struct capn_ptr *p, int off); +uint8_t capn_get8(const struct capn_ptr *p, int off); +uint16_t capn_get16(const struct capn_ptr *p, int off); +uint32_t capn_get32(const struct capn_ptr *p, int off); +uint64_t capn_get64(const struct capn_ptr *p, int off); +int capn_getv1(const struct capn_ptr *p, int off, uint8_t *data, int sz); +int capn_getv8(const struct capn_ptr *p, int off, uint8_t *data, int sz); +int capn_getv16(const struct capn_ptr *p, int off, uint16_t *data, int sz); +int capn_getv32(const struct capn_ptr *p, int off, uint32_t *data, int sz); +int capn_getv64(const struct capn_ptr *p, int off, uint64_t *data, int sz); -/* capn_write_* function write data to a list +/* capn_set_* function 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_read_1 + * off must be byte aligned for capn_set1v */ -int capn_write1(struct capn_list1 *p, int off, const uint8_t *data, int sz); -int capn_write8(struct capn_list8 *p, int off, const uint8_t *data, int sz); -int capn_write16(struct capn_list16 *p, int off, const uint16_t *data, int sz); -int capn_write32(struct capn_list32 *p, int off, const uint32_t *data, int sz); -int capn_write64(struct capn_list64 *p, int off, const uint64_t *data, int sz); +int capn_set1(struct capn_ptr *p, int off, int v); +int capn_set8(struct capn_ptr *p, int off, uint8_t v); +int capn_set16(struct capn_ptr *p, int off, uint16_t v); +int capn_set32(struct capn_ptr *p, int off, uint32_t v); +int capn_set64(struct capn_ptr *p, int off, uint64_t v); +int capn_setv1(struct capn_ptr *p, int off, const uint8_t *data, int sz); +int capn_setv8(struct capn_ptr *p, int off, const uint8_t *data, int sz); +int capn_setv16(struct capn_ptr *p, int off, const uint16_t *data, int sz); +int capn_setv32(struct capn_ptr *p, int off, const uint32_t *data, int sz); +int capn_setv64(struct capn_ptr *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 @@ -182,25 +190,25 @@ struct capn_ptr capn_new_ptr_list(struct capn_segment *seg, int sz); struct capn_ptr capn_new_string(struct capn_segment *seg, const char *str, int sz); #if defined(__cplusplus) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) -#define CAPN_INLINE inline +#define CAPN_INLINE static inline #else #define CAPN_INLINE static #endif -/* capn_get|set_* functions get/set struct values +/* 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_get8(const struct capn_ptr *p, int off); -CAPN_INLINE uint16_t capn_get16(const struct capn_ptr *p, int off); -CAPN_INLINE uint32_t capn_get32(const struct capn_ptr *p, int off); -CAPN_INLINE uint64_t capn_get64(const struct capn_ptr *p, int off); -CAPN_INLINE int capn_set8(struct capn_ptr *p, int off, uint8_t val); -CAPN_INLINE int capn_set16(struct capn_ptr *p, int off, uint16_t val); -CAPN_INLINE int capn_set32(struct capn_ptr *p, int off, uint32_t val); -CAPN_INLINE int capn_set64(struct capn_ptr *p, int off, uint64_t val); +CAPN_INLINE uint8_t capn_read8(const struct capn_ptr *p, int off); +CAPN_INLINE uint16_t capn_read16(const struct capn_ptr *p, int off); +CAPN_INLINE uint32_t capn_read32(const struct capn_ptr *p, int off); +CAPN_INLINE uint64_t capn_read64(const struct capn_ptr *p, int off); +CAPN_INLINE int capn_write8(struct capn_ptr *p, int off, uint8_t val); +CAPN_INLINE int capn_write16(struct capn_ptr *p, int off, uint16_t val); +CAPN_INLINE int capn_write32(struct capn_ptr *p, int off, uint32_t val); +CAPN_INLINE int capn_write64(struct capn_ptr *p, int off, uint64_t val); /* capn_init_malloc inits the capn struct with a create function which @@ -210,7 +218,7 @@ CAPN_INLINE int capn_set64(struct capn_ptr *p, int off, uint64_t val); * function setup by capn_init_malloc */ void capn_init_malloc(struct capn *c); -void capn_free_all(struct capn *c); +void capn_free_malloc(struct capn *c); /* capn_stream encapsulates the needed fields for capn_(deflate|inflate) in a * similar manner to z_stream from zlib @@ -266,80 +274,84 @@ CAPN_INLINE uint64_t capn_flip64(uint64_t v) { } #undef T -CAPN_INLINE uint8_t capn_get8(const struct capn_ptr *p, int off) { - return off < p->datasz ? capn_flip8(*(uint8_t*) p->data) : 0; +CAPN_INLINE uint8_t capn_read8(const struct capn_ptr *p, int off) { + return off+1 <= p->datasz ? capn_flip8(*(uint8_t*) (p->data+off)) : 0; } -CAPN_INLINE int capn_set8(struct capn_ptr *p, int off, uint8_t val) { - if (off < p->datasz) { - *(uint8_t*) p->data = capn_flip8(val); +CAPN_INLINE int capn_write8(struct 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_get16(const struct capn_ptr *p, int off) { - return off < p->datasz ? capn_flip16(*(uint16_t*) p->data) : 0; +CAPN_INLINE uint16_t capn_read16(const struct capn_ptr *p, int off) { + return off+2 <= p->datasz ? capn_flip16(*(uint16_t*) (p->data+off)) : 0; } -CAPN_INLINE int capn_set16(struct capn_ptr *p, int off, uint16_t val) { - if (off < p->datasz) { - *(uint16_t*) p->data = capn_flip16(val); +CAPN_INLINE int capn_write16(struct 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_get32(const struct capn_ptr *p, int off) { - return off < p->datasz ? capn_flip32(*(uint32_t*) p->data) : 0; +CAPN_INLINE uint32_t capn_read32(const struct capn_ptr *p, int off) { + return off+4 <= p->datasz ? capn_flip32(*(uint32_t*) (p->data+off)) : 0; } -CAPN_INLINE int capn_set32(struct capn_ptr *p, int off, uint32_t val) { - if (off < p->datasz) { - *(uint32_t*) p->data = capn_flip32(val); +CAPN_INLINE int capn_write32(struct 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_get64(const struct capn_ptr *p, int off) { - return off < p->datasz ? capn_flip64(*(uint64_t*) p->data) : 0; +CAPN_INLINE uint64_t capn_read64(const struct capn_ptr *p, int off) { + return off+8 <= p->datasz ? capn_flip64(*(uint64_t*) (p->data+off)) : 0; } -CAPN_INLINE int capn_set64(struct capn_ptr *p, int off, uint64_t val) { - if (off < p->datasz) { - *(uint64_t*) p->data = capn_flip64(val); +CAPN_INLINE int capn_write64(struct 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; } } -CAPN_INLINE float capn_get_float(const struct capn_ptr *p, int off, float def) { +CAPN_INLINE float capn_read_float(const struct capn_ptr *p, int off, float def) { union { float f; uint32_t u;} u; u.f = def; - u.u ^= capn_get32(p, off); + u.u ^= capn_read32(p, off); return u.f; } -CAPN_INLINE int capn_set_float(struct capn_ptr *p, int off, float f, float def) { +CAPN_INLINE int capn_write_float(struct capn_ptr *p, int off, float f, float def) { union { float f; uint32_t u;} u; union { float f; uint32_t u;} d; u.f = f; d.f = def; - return capn_set32(p, off, u.u ^ d.u); + return capn_write32(p, off, u.u ^ d.u); } -CAPN_INLINE double capn_get_double(const struct capn_ptr *p, int off, double def) { +CAPN_INLINE double capn_read_double(const struct capn_ptr *p, int off, double def) { union { double f; uint64_t u;} u; u.f = def; - u.u ^= capn_get64(p, off); + u.u ^= capn_read64(p, off); return u.f; } -CAPN_INLINE int capn_set_double(struct capn_ptr *p, int off, double f, double def) { +CAPN_INLINE int capn_write_double(struct capn_ptr *p, int off, double f, double def) { union { double f; uint64_t u;} u; union { double f; uint64_t u;} d; d.f = f; u.f = f; - return capn_set64(p, off, u.u ^ d.u); + return capn_write64(p, off, u.u ^ d.u); } +#ifdef __cplusplus +} +#endif + #endif