From: Vincent Bernat Date: Sat, 21 Jan 2012 13:42:42 +0000 (+0100) Subject: Add a new serialization engine. X-Git-Tag: 0.6.0~69 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=db3235553830b700ab5c94e11e88b3ed92714dd6;p=thirdparty%2Flldpd.git Add a new serialization engine. The chosen approach is totally different compared to the existing packing mechanism. Instead of describing the struct with some string and trying to decode the structure like the compiler would do (with alignments problem), the structure is now described by its size and the list of pointers in it. Macros are provided to make this easy. Here is an example: struct struct_simpleentry { TAILQ_ENTRY(struct_simpleentry) s_entries; int g1; struct struct_simple *g2; struct struct_simple g3; }; MARSHAL_DECLARE_BEGIN(struct_simpleentry) MARSHAL_ADD_TQE(struct_simpleentry, s_entries) MARSHAL_ADD_POINTER(struct_simpleentry, struct_simple, g2) MARSHAL_ADD_SUBSTRUCT(struct_simpleentry, struct_simple, g3) MARSHAL_DECLARE_END(struct_simpleentry); This enables the use of pointers, with detection of circular references and therefore support of lists. While only pointers need to be described, it is also possible to add sub structure to avoid specifying (again) its list of pointers if it has already been specified. The use of this mechanism should simplify the client/server code. --- diff --git a/src/Makefile.am b/src/Makefile.am index 953c124d..6931ef63 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -2,7 +2,7 @@ sbin_PROGRAMS = lldpd lldpctl noinst_LTLIBRARIES = liblldpd.la libcommon.la ## Shared by all executables -libcommon_la_SOURCES = log.c ctl.c lldpd.h lldp.h cdp.h compat.h sonmp.h edp.h +libcommon_la_SOURCES = log.c ctl.c marshal.c marshal.h lldpd.h lldp.h cdp.h compat.h sonmp.h edp.h libcommon_la_LIBADD = @LTLIBOBJS@ ## Used for lldpd and tests diff --git a/src/lldpd.h b/src/lldpd.h index 60aef5e9..646c5246 100644 --- a/src/lldpd.h +++ b/src/lldpd.h @@ -53,6 +53,7 @@ #ifdef ENABLE_EDP # include "edp.h" #endif +#include "marshal.h" #define SYSFS_CLASS_NET "/sys/class/net/" #define SYSFS_CLASS_DMI "/sys/class/dmi/id/" diff --git a/src/marshal.c b/src/marshal.c new file mode 100644 index 00000000..fa62abd4 --- /dev/null +++ b/src/marshal.c @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2012 Vincent Bernat + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "lldpd.h" + +/* A serialized object */ +struct marshal_serialized { + void *orig; /* Original reference. Also enforce alignment. */ + size_t size; + unsigned char object[0]; +}; + +/* List of already seen pointers */ +struct ref { + TAILQ_ENTRY(ref) next; + void *pointer; +}; +TAILQ_HEAD(ref_l, ref); + +/* Serialize the given object. */ +size_t +_marshal_serialize(struct marshal_info *mi, void *unserialized, void **input, + int skip, void *_refs) +{ + /* Check if we have already serialized this one. */ + struct ref_l *refs = _refs; + if (!refs) { + refs = calloc(1, sizeof(struct ref_l)); + if (!refs) { + LLOG_WARNX("unable to allocate memory for list of references"); + return -1; + } + TAILQ_INIT(refs); + } + struct ref *cref; + TAILQ_FOREACH(cref, refs, next) { + if (unserialized == cref->pointer) + return 0; + } + + size_t len = sizeof(struct marshal_serialized) + (skip?0:mi->size); + struct marshal_serialized *serialized = calloc(1, len); + if (!serialized) { + LLOG_WARNX("unable to allocate memory to serialize structure %s", + mi->name); + len = -1; + goto marshal_error; + } + serialized->orig = unserialized; + + /* Append the new reference */ + if (!(cref = calloc(1, sizeof(struct ref)))) { + LLOG_WARNX("unable to allocate memory for list of references"); + free(serialized); + len = -1; + goto marshal_error; + } + cref->pointer = unserialized; + TAILQ_INSERT_TAIL(refs, cref, next); + + /* First, serialize the main structure */ + if (!skip) + memcpy(serialized->object, unserialized, mi->size); + + /* Then, serialize inner structures */ + struct marshal_subinfo *current; + for (current = mi->pointers; current->mi; current++) { + size_t sublen; + void *source; + void *target; + if (current->kind == pointer) { + source = *(void **)((unsigned char *)unserialized + current->offset); + if (source == NULL) continue; + } else + source = (void *)((unsigned char *)unserialized + current->offset); + sublen = _marshal_serialize(current->mi, + source, &target, + current->kind == substruct, refs); + if (sublen == -1) { + LLOG_WARNX("unable to serialize substructure %s for %s", + current->mi->name, mi->name); + free(serialized); + return -1; + } + if (sublen == 0) continue; /* This was already serialized */ + /* Append the result */ + unsigned char *new = realloc(serialized, len + sublen); + if (!new) { + LLOG_WARNX("unable to allocate more memory to serialize structure %s", + mi->name); + free(serialized); + free(target); + len = -1; + goto marshal_error; + } + memcpy(new + len, target, sublen); + free(target); + len += sublen; + serialized = (struct marshal_serialized *)new; + } + + serialized->size = len; + *input = serialized; +marshal_error: + if (refs && !_refs) { + struct ref *cref, *cref_next; + for (cref = TAILQ_FIRST(refs); + cref != NULL; + cref = cref_next) { + cref_next = TAILQ_NEXT(cref, next); + TAILQ_REMOVE(refs, cref, next); + free(cref); + } + free(refs); + } + return len; +} + +/* This structure is used to track memory allocation when serializing */ +struct gc { + TAILQ_ENTRY(gc) next; + void *pointer; + void *orig; /* Original reference (not valid anymore !) */ +}; +TAILQ_HEAD(gc_l, gc); + +static void* +marshal_alloc(struct gc_l *pointers, size_t len, void *orig) +{ + void *result = malloc(len); + if (!result) return NULL; + struct gc *gpointer = NULL; + if ((gpointer = (struct gc *)calloc(1, + sizeof(struct gc))) == NULL) { + free(result); + return NULL; + } + gpointer->pointer = result; + gpointer->orig = orig; + TAILQ_INSERT_TAIL(pointers, gpointer, next); + return result; +} +static void +marshal_free(struct gc_l *pointers, int gconly) +{ + struct gc *pointer, *pointer_next; + for (pointer = TAILQ_FIRST(pointers); + pointer != NULL; + pointer = pointer_next) { + pointer_next = TAILQ_NEXT(pointer, next); + TAILQ_REMOVE(pointers, pointer, next); + if (!gconly) + free(pointer->pointer); + free(pointer); + } +} + + +/* Unserialize the given object. */ +size_t +_marshal_unserialize(struct marshal_info *mi, void *buffer, size_t len, void **output, + void *_pointers, int skip) +{ + int total_len = sizeof(struct marshal_serialized) + (skip?0:mi->size); + struct marshal_serialized *serialized = buffer; + if (len < sizeof(struct marshal_serialized) || len < total_len) { + LLOG_WARNX("data to deserialize is too small for structure %s", + mi->name); + return 0; + } + + /* Initialize garbage collection */ + struct gc_l *pointers = _pointers; + if (!pointers) { + pointers = calloc(1, sizeof(struct gc_l)); + if (!pointers) { + LLOG_WARNX("unable to allocate memory for garbage collection"); + return 0; + } + TAILQ_INIT(pointers); + } + + /* First, the main structure */ + if (!skip) { + if ((*output = marshal_alloc(pointers, mi->size, serialized->orig)) == NULL) { + LLOG_WARNX("unable to allocate memory to unserialize structure %s", + mi->name); + total_len = 0; + goto unmarshal_error; + } + memcpy(*output, serialized->object, mi->size); + } + + /* Then, each substructure */ + struct marshal_subinfo *current; + for (current = mi->pointers; current->mi; current++) { + size_t sublen; + void *new = (unsigned char *)*output + current->offset; + if (current->kind == pointer) { + if (*(void **)new == NULL) continue; + + /* Did we already see this reference? */ + struct gc *pointer; + int already = 0; + TAILQ_FOREACH(pointer, pointers, next) + if (pointer->orig == *(void **)new) { + *(void **)((unsigned char *)*output + + current->offset) = pointer->pointer; + already = 1; + break; + } + if (already) continue; + } + /* Deserialize */ + sublen = _marshal_unserialize(current->mi, + (unsigned char *)buffer + total_len, len - total_len, &new, pointers, + current->kind == substruct); + if (sublen == 0) { + LLOG_WARNX("unable to serialize substructure %s for %s", + current->mi->name, mi->name); + total_len = 0; + goto unmarshal_error; + } + /* Link the result */ + if (current->kind == pointer) + *(void **)((unsigned char *)*output + current->offset) = new; + total_len += sublen; + } + +unmarshal_error: + if (pointers && !_pointers) { + marshal_free(pointers, (total_len > 0)); + free(pointers); + } + return total_len; +} diff --git a/src/marshal.h b/src/marshal.h new file mode 100644 index 00000000..1b988a6c --- /dev/null +++ b/src/marshal.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2012 Vincent Bernat + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _MARSHAL_H +#define _MARSHAL_H + +struct marshal_info; +enum marshal_subinfo_kind { + pointer, + substruct, +}; +#define MARSHAL_INFO_POINTER 1 +#define MARSHAL_INFO_SUB 2 +struct marshal_subinfo { + size_t offset; /* Offset compared to parent structure */ + enum marshal_subinfo_kind kind; /* Kind of substructure */ + struct marshal_info *mi; +}; +struct marshal_info { + char *name; /* Name of structure */ + size_t size; /* Size of the structure */ + struct marshal_subinfo pointers[]; /* Pointer to other structures */ +}; + +/* Declare a new marshal_info struct named after the type we want to + marshal. The marshalled type has to be a structure. */ +#define MARSHAL_DECLARE_BEGIN(type) struct marshal_info marshal_info_##type = \ + { \ + .name = #type, \ + .size = sizeof(struct type), \ + .pointers = { +#define MARSHAL_ADD(_kind, type, subtype, member) \ + { .offset = offsetof(struct type, member), \ + .kind = _kind, \ + .mi = &marshal_info_##subtype }, +#define MARSHAL_ADD_POINTER(...) MARSHAL_ADD(pointer, ##__VA_ARGS__) +#define MARSHAL_ADD_SUBSTRUCT(...) MARSHAL_ADD(substruct, ##__VA_ARGS__) +#define MARSHAL_ADD_TQE(type, field) \ + MARSHAL_ADD_POINTER(type, type, field.tqe_next) \ + MARSHAL_ADD_POINTER(type, type, field.tqe_prev) +#define MARSHAL_ADD_TQH(type, subtype) \ + MARSHAL_ADD_POINTER(type, subtype, tqh_first) \ + MARSHAL_ADD_POINTER(type, subtype, tqh_last) +#define MARSHAL_ADD_SUBTQ(type, subtype,field) \ + MARSHAL_ADD_POINTER(type, subtype, field.tqh_first) \ + MARSHAL_ADD_POINTER(type, subtype, field.tqh_last) +#define MARSHAL_DECLARE_END(type) \ + { .mi = NULL } } } +/* Shortcuts */ +#define MARSHAL_DECLARE(type) \ + MARSHAL_DECLARE_BEGIN(type) \ + MARSHAL_DECLARE_END(type) +#define MARSHAL_DECLARE_TQ(type, subtype) \ + MARSHAL_DECLARE_BEGIN(type) \ + MARSHAL_ADD_TQH(type, subtype) \ + MARSHAL_DECLARE_END(type) + +/* Serialization */ +size_t _marshal_serialize(struct marshal_info *, void *, void **, int, void *); +#define marshal_serialize(type, o, output) _marshal_serialize(&marshal_info_##type, o, output, 0, NULL) + +/* Unserialization */ +size_t _marshal_unserialize(struct marshal_info *, void *, size_t, void **, void*, int); +#define marshal_unserialize(type, o, l, input) \ + _marshal_unserialize(&marshal_info_##type, o, l, (void **)input, NULL, 0) + +#endif diff --git a/tests/Makefile.am b/tests/Makefile.am index 07b7ca3e..41c863eb 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,10 +1,13 @@ -if HAVE_CHECK +TESTS = check_pack check_marshal check_lldp check_cdp check_sonmp check_edp check_ifaddrs -check_PROGRAMS = check_pack check_lldp check_cdp check_sonmp check_edp check_ifaddrs +if HAVE_CHECK check_pack_SOURCES = check_pack.c \ $(top_builddir)/src/lldpd.h +check_marshal_SOURCES = check_marshal.c \ + $(top_builddir)/src/lldpd.h + check_lldp_SOURCES = check_lldp.c \ $(top_builddir)/src/lldpd.h \ common.h common.c @@ -29,7 +32,7 @@ AM_CFLAGS = @CHECK_CFLAGS@ LDADD = $(top_builddir)/src/liblldpd.la @CHECK_LIBS@ if USE_SNMP -check_PROGRAMS += check_snmp +TESTS += check_snmp check_snmp_SOURCES = check_snmp.c \ $(top_builddir)/src/lldpd.h \ $(top_builddir)/src/agent.h @@ -37,6 +40,8 @@ check_snmp_SOURCES = check_snmp.c \ LDADD += @NETSNMP_LIBS@ endif +check_PROGRAMS = $(TESTS) + endif MOSTLYCLEANFILES = lldp_send_*.pcap cdp_send_*.pcap sonmp_send_*.pcap edp_send_*.pcap \ diff --git a/tests/check_marshal.c b/tests/check_marshal.c new file mode 100644 index 00000000..e00cc207 --- /dev/null +++ b/tests/check_marshal.c @@ -0,0 +1,611 @@ +#include + +#include "../src/lldpd.h" + +/* This suite can be run in valgrind for memory leaks: + CK_FORK=no valgrind -v --leak-check=yes ./tests/check_marshal +*/ + +struct struct_simple { + int a1; + long a2; + char a3; + time_t a4; + char a5[7]; +}; +MARSHAL_DECLARE(struct_simple); + +START_TEST(test_simple_structure) { + struct struct_simple source = { + .a1 = 78452, + .a2 = 48751424, + .a3 = 'h', + .a4 = 784254, + .a5 = { 'A', 'B', 'C', 'D', 'E', 'F', 'G'}, + }; + struct struct_simple *destination; + void *buffer; + size_t len, len2; + + len = marshal_serialize(struct_simple, &source, &buffer); + fail_unless(len > 0, "Unable to serialize"); + memset(&source, 0, sizeof(struct struct_simple)); + len2 = marshal_unserialize(struct_simple, buffer, len, &destination); + fail_unless(len2 > 0, "Unable to deserialize"); + free(buffer); + ck_assert_int_eq(len, len2); + ck_assert_int_eq(destination->a1, 78452); + ck_assert_int_eq(destination->a2, 48751424); + ck_assert_int_eq(destination->a3, 'h'); + ck_assert_int_eq(destination->a4, 784254); + ck_assert_int_eq(destination->a5[0], 'A'); + ck_assert_int_eq(destination->a5[1], 'B'); + ck_assert_int_eq(destination->a5[2], 'C'); + ck_assert_int_eq(destination->a5[3], 'D'); + ck_assert_int_eq(destination->a5[4], 'E'); + ck_assert_int_eq(destination->a5[5], 'F'); + ck_assert_int_eq(destination->a5[6], 'G'); + free(destination); +} +END_TEST + +struct struct_sub { + int e1; + struct struct_simple e2; + char e3; +}; +MARSHAL_DECLARE_BEGIN(struct_sub) +MARSHAL_ADD_SUBSTRUCT(struct_sub, struct_simple, e2) +MARSHAL_DECLARE_END(struct_sub); + +START_TEST(test_substruct_structure) { + struct struct_sub source = { + .e1 = -5122, + .e2 = { + .a1 = 78452, + .a2 = 48751424, + .a3 = 'h', + .a4 = 784254, + .a5 = { 'A', 'B', 'C', 'D', 'E', 'F', 'G'}, + }, + .e3 = 'a', + }; + + struct struct_sub *destination; + void *buffer; + size_t len, len2; + + len = marshal_serialize(struct_sub, &source, &buffer); + fail_unless(len > 0, "Unable to serialize"); + memset(&source, 0, sizeof(struct struct_sub)); + len2 = marshal_unserialize(struct_sub, buffer, len, &destination); + fail_unless(len2 > 0, "Unable to deserialize"); + free(buffer); + ck_assert_int_eq(len, len2); + ck_assert_int_eq(destination->e1, -5122); + ck_assert_int_eq(destination->e2.a1, 78452); + ck_assert_int_eq(destination->e2.a2, 48751424); + ck_assert_int_eq(destination->e2.a3, 'h'); + ck_assert_int_eq(destination->e2.a4, 784254); + ck_assert_int_eq(destination->e2.a5[0], 'A'); + ck_assert_int_eq(destination->e2.a5[1], 'B'); + ck_assert_int_eq(destination->e2.a5[2], 'C'); + ck_assert_int_eq(destination->e2.a5[3], 'D'); + ck_assert_int_eq(destination->e2.a5[4], 'E'); + ck_assert_int_eq(destination->e2.a5[5], 'F'); + ck_assert_int_eq(destination->e2.a5[6], 'G'); + ck_assert_int_eq(destination->e3, 'a'); + free(destination); +} +END_TEST + +struct struct_onepointer { + int b1; + long b2; + char b3; + struct struct_simple *b4; + int b5; +}; +MARSHAL_DECLARE_BEGIN(struct_onepointer) +MARSHAL_ADD_POINTER(struct_onepointer, struct_simple, b4) +MARSHAL_DECLARE_END(struct_onepointer); + +START_TEST(test_pointer_structure) { + struct struct_simple source_simple = { + .a1 = 78452, + .a2 = 48751424, + .a3 = 'h', + .a4 = 784254, + .a5 = { 'A', 'B', 'C', 'D', 'E', 'F', 'G'}, + }; + struct struct_onepointer source = { + .b1 = 18, + .b2 = 15454, + .b3 = 'o', + .b4 = &source_simple, + .b5 = 333333, + }; + + struct struct_onepointer *destination; + void *buffer; + size_t len, len2; + + len = marshal_serialize(struct_onepointer, &source, &buffer); + fail_unless(len > 0, "Unable to serialize"); + memset(&source_simple, 0, sizeof(struct struct_simple)); + memset(&source, 0, sizeof(struct struct_onepointer)); + len2 = marshal_unserialize(struct_onepointer, buffer, len, &destination); + fail_unless(len2 > 0, "Unable to deserialize"); + free(buffer); + ck_assert_int_eq(len, len2); + ck_assert_int_eq(destination->b1, 18); + ck_assert_int_eq(destination->b2, 15454); + ck_assert_int_eq(destination->b3, 'o'); + ck_assert_int_eq(destination->b4->a1, 78452); + ck_assert_int_eq(destination->b4->a2, 48751424); + ck_assert_int_eq(destination->b4->a3, 'h'); + ck_assert_int_eq(destination->b4->a4, 784254); + ck_assert_int_eq(destination->b4->a5[0], 'A'); + ck_assert_int_eq(destination->b4->a5[1], 'B'); + ck_assert_int_eq(destination->b4->a5[2], 'C'); + ck_assert_int_eq(destination->b4->a5[3], 'D'); + ck_assert_int_eq(destination->b4->a5[4], 'E'); + ck_assert_int_eq(destination->b4->a5[5], 'F'); + ck_assert_int_eq(destination->b4->a5[6], 'G'); + ck_assert_int_eq(destination->b5, 333333); + free(destination->b4); free(destination); +} +END_TEST + +struct struct_nestedpointers { + int c1; + long c2; + struct struct_simple *c3; + struct struct_onepointer *c4; + int c5; +}; +MARSHAL_DECLARE_BEGIN(struct_nestedpointers) +MARSHAL_ADD_POINTER(struct_nestedpointers, struct_simple, c3) +MARSHAL_ADD_POINTER(struct_nestedpointers, struct_onepointer, c4) +MARSHAL_DECLARE_END(struct_nestedpointers); + +START_TEST(test_several_pointers_structure) { + struct struct_simple source_simple1 = { + .a1 = 78452, + .a2 = 48751424, + .a3 = 'h', + .a4 = 784254, + .a5 = { 'A', 'B', 'C', 'D', 'E', 'F', 'G'}, + }; + struct struct_simple source_simple2 = { + .a1 = 451, + .a2 = 451424, + .a3 = 'o', + .a4 = 74, + .a5 = { 'a', 'b', 'c', 'd', 'e', 'f', 'g'}, + }; + struct struct_onepointer source_onepointer = { + .b1 = 18, + .b2 = 15454, + .b3 = 'o', + .b4 = &source_simple1, + .b5 = 333333, + }; + struct struct_nestedpointers source = { + .c1 = 4542, + .c2 = 5665454, + .c3 = &source_simple2, + .c4 = &source_onepointer, + .c5 = -545424, + }; + + struct struct_nestedpointers *destination; + void *buffer; + size_t len, len2; + + len = marshal_serialize(struct_nestedpointers, &source, &buffer); + fail_unless(len > 0, "Unable to serialize"); + memset(&source_simple1, 0, sizeof(struct struct_simple)); + memset(&source_simple2, 0, sizeof(struct struct_simple)); + memset(&source_onepointer, 0, sizeof(struct struct_onepointer)); + memset(&source, 0, sizeof(struct struct_nestedpointers)); + len2 = marshal_unserialize(struct_nestedpointers, buffer, len, &destination); + fail_unless(len2 > 0, "Unable to deserialize"); + free(buffer); + ck_assert_int_eq(len, len2); + ck_assert_int_eq(destination->c1, 4542); + ck_assert_int_eq(destination->c2, 5665454); + ck_assert_int_eq(destination->c3->a1, 451); + ck_assert_int_eq(destination->c3->a2, 451424); + ck_assert_int_eq(destination->c3->a3, 'o'); + ck_assert_int_eq(destination->c3->a4, 74); + ck_assert_int_eq(destination->c3->a5[3], 'd'); + ck_assert_int_eq(destination->c3->a5[4], 'e'); + ck_assert_int_eq(destination->c3->a5[6], 'g'); + ck_assert_int_eq(destination->c4->b1, 18); + ck_assert_int_eq(destination->c4->b2, 15454); + ck_assert_int_eq(destination->c4->b3, 'o'); + ck_assert_int_eq(destination->c4->b4->a1, 78452); + ck_assert_int_eq(destination->c4->b4->a2, 48751424); + ck_assert_int_eq(destination->c4->b4->a3, 'h'); + ck_assert_int_eq(destination->c4->b4->a4, 784254); + ck_assert_int_eq(destination->c4->b4->a5[0], 'A'); + ck_assert_int_eq(destination->c4->b4->a5[1], 'B'); + ck_assert_int_eq(destination->c4->b4->a5[2], 'C'); + ck_assert_int_eq(destination->c4->b4->a5[3], 'D'); + ck_assert_int_eq(destination->c4->b4->a5[4], 'E'); + ck_assert_int_eq(destination->c4->b4->a5[5], 'F'); + ck_assert_int_eq(destination->c4->b4->a5[6], 'G'); + ck_assert_int_eq(destination->c4->b5, 333333); + free(destination->c3); free(destination->c4->b4); + free(destination->c4); free(destination); +} +END_TEST + +START_TEST(test_null_pointers) { + struct struct_simple source_simple2 = { + .a1 = 451, + .a2 = 451424, + .a3 = 'o', + .a4 = 74, + .a5 = { 'a', 'b', 'c', 'd', 'e', 'f', 'g'}, + }; + struct struct_nestedpointers source = { + .c1 = 4542, + .c2 = 5665454, + .c3 = &source_simple2, + .c4 = NULL, + .c5 = -545424, + }; + + struct struct_nestedpointers *destination; + void *buffer; + size_t len, len2; + + len = marshal_serialize(struct_nestedpointers, &source, &buffer); + fail_unless(len > 0, "Unable to serialize"); + memset(&source_simple2, 0, sizeof(struct struct_simple)); + memset(&source, 0, sizeof(struct struct_nestedpointers)); + len2 = marshal_unserialize(struct_nestedpointers, buffer, len, &destination); + fail_unless(len2 > 0, "Unable to deserialize"); + free(buffer); + ck_assert_int_eq(len, len2); + ck_assert_int_eq(destination->c1, 4542); + ck_assert_int_eq(destination->c2, 5665454); + ck_assert_int_eq(destination->c3->a1, 451); + ck_assert_int_eq(destination->c3->a2, 451424); + ck_assert_int_eq(destination->c3->a3, 'o'); + ck_assert_int_eq(destination->c3->a4, 74); + ck_assert_int_eq(destination->c3->a5[3], 'd'); + ck_assert_int_eq(destination->c3->a5[4], 'e'); + ck_assert_int_eq(destination->c3->a5[6], 'g'); + ck_assert_int_eq(destination->c4, NULL); + free(destination->c3); free(destination); +} +END_TEST + +struct struct_multipleref { + int f1; + struct struct_simple* f2; + struct struct_simple* f3; + struct struct_nestedpointers* f4; +}; +MARSHAL_DECLARE_BEGIN(struct_multipleref) +MARSHAL_ADD_POINTER(struct_multipleref, struct_simple, f2) +MARSHAL_ADD_POINTER(struct_multipleref, struct_simple, f3) +MARSHAL_ADD_POINTER(struct_multipleref, struct_nestedpointers, f4) +MARSHAL_DECLARE_END(struct_multipleref); + +START_TEST(test_multiple_references) { + struct struct_simple source_simple = { + .a1 = 451, + .a2 = 451424, + .a3 = 'o', + .a4 = 74, + .a5 = { 'a', 'b', 'c', 'd', 'e', 'f', 'g'}, + }; + struct struct_nestedpointers source_nested = { + .c3 = &source_simple, + .c4 = NULL, + }; + struct struct_multipleref source = { + .f1 = 15, + .f2 = &source_simple, + .f3 = &source_simple, + .f4 = &source_nested, + }; + + struct struct_multipleref *destination; + void *buffer = NULL; + size_t len, len2; + + len = marshal_serialize(struct_multipleref, &source, &buffer); + fail_unless(buffer != NULL, "Buffer is empty"); + fail_unless(len > 0, "Unable to serialize"); + memset(&source_simple, 0, sizeof(struct struct_simple)); + memset(&source_nested, 0, sizeof(struct struct_nestedpointers)); + memset(&source, 0, sizeof(struct struct_multipleref)); + len2 = marshal_unserialize(struct_multipleref, buffer, len, &destination); + fail_unless(len2 > 0, "Unable to deserialize"); + free(buffer); + ck_assert_int_eq(len, len2); + ck_assert_int_eq(destination->f1, 15); + ck_assert_int_eq(destination->f2, destination->f3); + ck_assert_int_eq(destination->f2, destination->f4->c3); + ck_assert_int_eq(destination->f2->a1, 451); + ck_assert_int_eq(destination->f2->a2, 451424); + ck_assert_int_eq(destination->f2->a3, 'o'); + ck_assert_int_eq(destination->f2->a4, 74); + ck_assert_int_eq(destination->f4->c4, NULL); + free(destination->f2); free(destination->f4); free(destination); +} +END_TEST + +struct struct_circularref { + int g1; + struct struct_circularref* g2; +}; +MARSHAL_DECLARE_BEGIN(struct_circularref) +MARSHAL_ADD_POINTER(struct_circularref, struct_circularref, g2) +MARSHAL_DECLARE_END(struct_circularref); + +START_TEST(test_circular_references) { + struct struct_circularref source = { + .g1 = 42, + .g2 = &source, + }; + + struct struct_circularref *destination; + void *buffer = NULL; + size_t len, len2; + + len = marshal_serialize(struct_circularref, &source, &buffer); + fail_unless(len > 0, "Unable to serialize"); + memset(&source, 0, sizeof(struct struct_circularref)); + len2 = marshal_unserialize(struct_circularref, buffer, len, &destination); + fail_unless(len2 > 0, "Unable to deserialize"); + free(buffer); + ck_assert_int_eq(len, len2); + ck_assert_int_eq(destination->g1, 42); + ck_assert_int_eq(destination->g2->g1, 42); + ck_assert_int_eq(destination->g2, destination->g2->g2); + free(destination); +} +END_TEST + +START_TEST(test_too_small_unmarshal) { + struct struct_simple source_simple1; + struct struct_onepointer source_onepointer = { + .b4 = &source_simple1, + }; + struct struct_nestedpointers source = { + .c3 = &source_simple1, + .c4 = &source_onepointer, + }; + + struct struct_nestedpointers *destination; + void *buffer; + size_t len, len2; + + len = marshal_serialize(struct_nestedpointers, &source, &buffer); + fail_unless(len > 0, "Unable to serialize"); + memset(&source_simple1, 0, sizeof(struct struct_simple)); + memset(&source_onepointer, 0, sizeof(struct struct_onepointer)); + memset(&source, 0, sizeof(struct struct_nestedpointers)); + int i, j; + /* Loop 30 times to ease debugging leaks with valgrind */ + for (j = 0; j < 30; j++) { + for (i = 0; i < len; i++) { + len2 = marshal_unserialize(struct_nestedpointers, buffer, 1, &destination); + fail_unless(len2 == 0, + "Should not be able to deserialize, too small (%d<%d)", + i, len); + } + } + len2 = marshal_unserialize(struct_nestedpointers, buffer, len + 5, &destination); + fail_unless(len2 == len, "Deserialized too much"); + free(destination->c3); + free(destination->c4); free(destination); free(buffer); +} +END_TEST + +struct struct_simpleentry { + TAILQ_ENTRY(struct_simpleentry) s_entries; + int g1; + struct struct_simple *g2; +}; +MARSHAL_DECLARE_BEGIN(struct_simpleentry) +MARSHAL_ADD_TQE(struct_simpleentry, s_entries) +MARSHAL_ADD_POINTER(struct_simpleentry, struct_simple, g2) +MARSHAL_DECLARE_END(struct_simpleentry); + +TAILQ_HEAD(list_simple, struct_simpleentry); +MARSHAL_DECLARE_TQ(list_simple, struct_simpleentry); + +START_TEST(test_simple_list) { + struct struct_simple source_simple = { + .a1 = 451, + .a2 = 451424, + .a3 = 'o', + .a4 = 74, + .a5 = { 'a', 'b', 'c', 'd', 'e', 'f', 'g'}, + }; + struct list_simple source; + struct struct_simpleentry entry1 = { + .g1 = 47, + .g2 = &source_simple, + }; + struct struct_simpleentry entry2 = { + .g1 = 49, + .g2 = &source_simple, + }; + struct struct_simpleentry entry3 = { + .g1 = 4700, + .g2 = NULL, + }; + struct struct_simpleentry entry4 = { + .g1 = -47, + .g2 = &source_simple, + }; + TAILQ_INIT(&source); + TAILQ_INSERT_TAIL(&source, &entry1, s_entries); + TAILQ_INSERT_TAIL(&source, &entry2, s_entries); + TAILQ_INSERT_TAIL(&source, &entry3, s_entries); + TAILQ_INSERT_TAIL(&source, &entry4, s_entries); + + struct list_simple *destination; + void *buffer; + size_t len, len2; + len = marshal_serialize(list_simple, &source, &buffer); + fail_unless(len > 0, "Unable to serialize"); + memset(&source, 0, sizeof(struct list_simple)); + memset(&entry1, 0, sizeof(struct struct_simpleentry)); + memset(&entry2, 0, sizeof(struct struct_simpleentry)); + memset(&entry3, 0, sizeof(struct struct_simpleentry)); + memset(&entry4, 0, sizeof(struct struct_simpleentry)); + len2 = marshal_unserialize(list_simple, buffer, len, &destination); + fail_unless(len2 > 0, "Unable to deserialize"); + free(buffer); + + struct struct_simpleentry *e1, *e2; + struct struct_simple *s; + e1 = TAILQ_FIRST(destination); + ck_assert_int_eq(e1->g1, 47); + s = e1->g2; + e2 = TAILQ_NEXT(e1, s_entries); + free(e1); + ck_assert_int_eq(e2->g1, 49); + ck_assert_int_eq(e2->g2, s); + e1 = TAILQ_NEXT(e2, s_entries); + free(e2); + ck_assert_int_eq(e1->g1, 4700); + ck_assert_int_eq(e1->g2, NULL); + e2 = TAILQ_NEXT(e1, s_entries); + free(e1); + ck_assert_int_eq(e2->g1, -47); + ck_assert_int_eq(e2->g2, s); + e1 = TAILQ_NEXT(e2, s_entries); + free(e2); + ck_assert_int_eq(e1, NULL); + free(s); + free(destination); +} +END_TEST + +struct struct_withlist { + int i1; + TAILQ_HEAD(, struct_simpleentry) i2; + int i3; +}; +MARSHAL_DECLARE_BEGIN(struct_withlist) +MARSHAL_ADD_SUBTQ(struct_withlist, struct_simpleentry, i2) +MARSHAL_DECLARE_END(struct_withlist); + +START_TEST(test_embedded_list) { + struct struct_withlist source = { + .i1 = 45424, + .i3 = 4542, + }; + struct struct_simple source_simple = { + .a1 = 451, + .a2 = 451424, + .a3 = 'o', + .a4 = 74, + .a5 = { 'a', 'b', 'c', 'd', 'e', 'f', 'g'}, + }; + struct struct_simpleentry entry1 = { + .g1 = 47, + .g2 = &source_simple, + }; + struct struct_simpleentry entry2 = { + .g1 = 49, + .g2 = &source_simple, + }; + struct struct_simpleentry entry3 = { + .g1 = 4700, + .g2 = NULL, + }; + struct struct_simpleentry entry4 = { + .g1 = -47, + .g2 = &source_simple, + }; + TAILQ_INIT(&source.i2); + TAILQ_INSERT_TAIL(&source.i2, &entry1, s_entries); + TAILQ_INSERT_TAIL(&source.i2, &entry2, s_entries); + TAILQ_INSERT_TAIL(&source.i2, &entry3, s_entries); + TAILQ_INSERT_TAIL(&source.i2, &entry4, s_entries); + + struct struct_withlist *destination; + void *buffer; + size_t len, len2; + len = marshal_serialize(struct_withlist, &source, &buffer); + fail_unless(len > 0, "Unable to serialize"); + memset(&source, 0, sizeof(struct list_simple)); + memset(&entry1, 0, sizeof(struct struct_simpleentry)); + memset(&entry2, 0, sizeof(struct struct_simpleentry)); + memset(&entry3, 0, sizeof(struct struct_simpleentry)); + memset(&entry4, 0, sizeof(struct struct_simpleentry)); + len2 = marshal_unserialize(struct_withlist, buffer, len, &destination); + fail_unless(len2 > 0, "Unable to deserialize"); + free(buffer); + + ck_assert_int_eq(destination->i1, 45424); + ck_assert_int_eq(destination->i3, 4542); + struct struct_simpleentry *e1, *e2; + struct struct_simple *s; + e1 = TAILQ_FIRST(&destination->i2); + ck_assert_int_eq(e1->g1, 47); + ck_assert_int_eq(e1->g2->a4, 74); + s = e1->g2; + e2 = TAILQ_NEXT(e1, s_entries); + free(e1); + ck_assert_int_eq(e2->g1, 49); + ck_assert_int_eq(e2->g2, s); + e1 = TAILQ_NEXT(e2, s_entries); + free(e2); + ck_assert_int_eq(e1->g1, 4700); + ck_assert_int_eq(e1->g2, NULL); + e2 = TAILQ_NEXT(e1, s_entries); + free(e1); + ck_assert_int_eq(e2->g1, -47); + ck_assert_int_eq(e2->g2, s); + e1 = TAILQ_NEXT(e2, s_entries); + free(e2); + ck_assert_int_eq(e1, NULL); + free(s); + free(destination); +} +END_TEST + +Suite * +marshal_suite(void) +{ + Suite *s = suite_create("Marshalling"); + + TCase *tc_marshal = tcase_create("Marshalling"); + tcase_add_test(tc_marshal, test_simple_structure); + tcase_add_test(tc_marshal, test_substruct_structure); + tcase_add_test(tc_marshal, test_pointer_structure); + tcase_add_test(tc_marshal, test_several_pointers_structure); + tcase_add_test(tc_marshal, test_null_pointers); + tcase_add_test(tc_marshal, test_multiple_references); + tcase_add_test(tc_marshal, test_circular_references); + tcase_add_test(tc_marshal, test_too_small_unmarshal); + tcase_add_test(tc_marshal, test_simple_list); + tcase_add_test(tc_marshal, test_embedded_list); + suite_add_tcase(s, tc_marshal); + + return s; +} + +int +main() +{ + int number_failed; + Suite *s = marshal_suite(); + SRunner *sr = srunner_create(s); + srunner_run_all(sr, CK_ENV); + number_failed = srunner_ntests_failed(sr); + srunner_free(sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +}