]> git.ipfire.org Git - thirdparty/lldpd.git/commitdiff
Add a new serialization engine.
authorVincent Bernat <bernat@luffy.cx>
Sat, 21 Jan 2012 13:42:42 +0000 (14:42 +0100)
committerVincent Bernat <bernat@luffy.cx>
Sat, 21 Jan 2012 18:59:56 +0000 (19:59 +0100)
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.

src/Makefile.am
src/lldpd.h
src/marshal.c [new file with mode: 0644]
src/marshal.h [new file with mode: 0644]
tests/Makefile.am
tests/check_marshal.c [new file with mode: 0644]

index 953c124dc1bf5cdb024683dd77e855bfb29d66e4..6931ef63957e471b3506d7c25101af6cd5a4db25 100644 (file)
@@ -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
index 60aef5e968673526ddbe3a988cd768d7f215e30e..646c5246cd53be4283905502e046d86cabad4557 100644 (file)
@@ -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 (file)
index 0000000..fa62abd
--- /dev/null
@@ -0,0 +1,249 @@
+/*
+ * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx>
+ *
+ * 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 (file)
index 0000000..1b988a6
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx>
+ *
+ * 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
index 07b7ca3e3586eab8cfd8457c3f7e6285f837d7a1..41c863eb8e32df1aad66bdfd366a38bfc0e398a9 100644 (file)
@@ -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 (file)
index 0000000..e00cc20
--- /dev/null
@@ -0,0 +1,611 @@
+#include <check.h>
+
+#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;
+}