Added a LIST type to the blob descriptor.
authorMichael Zucchi <michael@swordfish.com.au>
Mon, 29 Apr 2019 22:34:12 +0000 (08:04 +0930)
committerMichael Zucchi <michael@swordfish.com.au>
Mon, 29 Apr 2019 22:34:12 +0000 (08:04 +0930)
Made ez-blob-print public.

Makefile
ez-blob-print.c [new file with mode: 0644]
ez-blob.c
ez-blob.h
test-blob.c

index 3d0b6ce..0a828bf 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,13 +1,14 @@
 
 CPPFLAGS=-I.
-CFLAGS=-O2 -fPIC -Wall -mtune=native
+CFLAGS=-Og -g -fPIC -Wall -mtune=native
 test_LDLIBS=-lpthread -lrt
 ARFLAGS=rvsUc
-VERSION=2.0
+VERSION=2.1.99
 
 SRCS=                                          \
  ez-bitset.c                                   \
  ez-blob.c                                     \
+ ez-blob-print.c                               \
  ez-port.c                                     \
  ez-set.c                                      \
  ez-tree.c
@@ -41,7 +42,7 @@ test-%: test-%.o
        $(CC) $(CFLAGS) -o $@ $< libeze.a $(test_LDLIBS)
 
 test-bitset: libeze.a(ez-bitset.o)
-test-blob: libeze.a(ez-blob.o)
+test-blob: libeze.a(ez-blob.o) libeze.a(ez-blob-print.o)
 test-port: libeze.a(ez-port.o)
 test-set: libeze.a(ez-set.o)
 test-tree: libeze.a(ez-tree.o)
diff --git a/ez-blob-print.c b/ez-blob-print.c
new file mode 100644 (file)
index 0000000..8fa94dc
--- /dev/null
@@ -0,0 +1,121 @@
+/* ez-blob.h: Serialising description and serialiser.
+
+   Copyright (C) 2019 Michael Zucchi
+
+   This program is free software: you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public License
+   as published by the Free Software Foundation, either version 3 of
+   the License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this program. If not, see
+   <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <assert.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include "ez-blob.h"
+#include "ez-list.h"
+
+static void dumphex(const char *data, size_t size, const char *prefix) {
+       for (int i=0;i<size;i+=16) {
+               fputs(prefix, stdout);
+               printf(" %04x:", i);
+               for (int j=0;j<16;j++) {
+                       if (i+j<size)
+                               printf(" %02x", data[i+j] & 0xff);
+                       else
+                               fputs("   ", stdout);
+               }
+               fputs("  ", stdout);
+               for (int j=0;j<16;j++) {
+                       if (i+j<size)
+                               putchar(isprint(data[i+j]) ? data[i+j] : '.');
+                       else
+                               putchar(' ');
+               }
+               putchar('\n');
+       }
+}
+
+void ez_blob_print(const ez_blob_desc *d, const void *a, int depth) {
+       int e = 1;
+       char x[depth+1];
+
+       memset(x, ' ', depth);
+       x[depth] = 0;
+       
+       for (;e && (d->bd_type != EZ_BLOB_END);d++) {
+               const void *u = a + d->bd_offset;
+
+               printf("%s [type=$%02x, offset=$%04x]", x, d->bd_type, d->bd_offset);
+               
+               switch (d->bd_type) {
+               case EZ_BLOB_INT8:
+                       printf(" .%d = %d\n", d->bd_id, *(uint8_t *)u);
+                       break;
+               case EZ_BLOB_INT16:
+                       printf(" .%d = %d\n", d->bd_id, *(uint16_t *)u);
+                       break;
+               case EZ_BLOB_INT32:
+                       printf(" .%d = %d\n", d->bd_id, *(uint32_t *)u);
+                       break;
+               case EZ_BLOB_INT64:
+                       printf(" .%d = %ld\n", d->bd_id, *(uint64_t *)u);
+                       break;
+               case EZ_BLOB_FLOAT32:
+                       printf(" .%d = %f\n", d->bd_id, *(float *)u);
+                       break;
+               case EZ_BLOB_FLOAT64:
+                       printf(" .%d = %f\n", d->bd_id, *(double *)u);
+                       break;
+               case EZ_BLOB_STRING:
+                       printf(" .%d %p = `%s'\n", d->bd_id, *((char **)u), *((char **)u));
+                       break;
+               case EZ_BLOB_BLOB: {
+                       const struct ez_blob *ua = u;
+                       
+                       printf(" .%d %p [%zd] = {\n", d->bd_id, ua->data, ua->size);
+                       dumphex(ua->data, ua->size, x);
+                       printf("%s  }\n", x);
+                       break; }
+               case EZ_BLOB_POINTER: {
+                       void *up = *((void **)u);
+                       if (up) {
+                               printf(" .%d %p = {\n", d->bd_id, up);
+                               ez_blob_print(d->bd_table, up, depth+4);
+                               printf("%s  }\n", x);
+                       } else {
+                               printf(" .%d %p\n", d->bd_id, up);
+                       }
+                       break; }
+               case EZ_BLOB_LIST: {
+                       ez_list *l = (void *)u;
+                       
+                       printf(" .%d %p [] = {\n", d->bd_id, l);
+                       for (ez_node *w = ez_list_head(l), *n = ez_node_next(w);
+                            n;
+                            w = n, n=ez_node_next(n)) {
+                               printf("%s{\n", x);
+                               ez_blob_print(d->bd_table, w, depth+4);
+                               printf("%s},\n", x);
+                       }
+                       printf("%s  }\n", x);
+                       break; }
+               default:
+                       printf("\n");
+               }
+       }
+}
+
index c6a914e..be6ddc0 100644 (file)
--- a/ez-blob.c
+++ b/ez-blob.c
@@ -24,6 +24,7 @@
 #include <assert.h>
 
 #include "ez-blob.h"
+#include "ez-list.h"
 
 /*
  This implements a basic 'high performance' serialisation based on the
@@ -70,8 +71,8 @@ size_t ez_blob_size(const ez_blob_desc *d, const void *p) {
                                char *s = ((char **)v)[0];
                                size += s ? 4 + strlen(s) : 4;
                                break; }
-                       case EZ_BLOB_ARRAY:
-                               size += 4 + ((const struct ez_blob_array *)v)->size;
+                       case EZ_BLOB_BLOB:
+                               size += 4 + ((const struct ez_blob *)v)->size;
                                break;
                        case EZ_BLOB_STRUCT: {
                                const void *sp = v;
@@ -81,6 +82,16 @@ size_t ez_blob_size(const ez_blob_desc *d, const void *p) {
                                const void *s = ((const void * const *)v)[0];
                                size += s ? 4 + ez_blob_size(d->bd_table, s) : 4;
                                break; }
+                       case EZ_BLOB_LIST: {
+                               ez_list *l = (ez_list *)v;
+
+                               size += 4;
+                               for (ez_node *w = ez_list_head(l), *n = ez_node_succ(w);
+                                    n;
+                                    w = n, n=ez_node_succ(n)) {
+                                       size += 4 + ez_blob_size(d->bd_table, w);
+                               }
+                               break; }
                        default:
                                break;
                        }
@@ -98,8 +109,8 @@ void ez_blob_free_raw(const ez_blob_desc *d, void *p) {
                case EZ_BLOB_STRING:
                        free(((char **)v)[0]);
                        break;
-               case EZ_BLOB_ARRAY:
-                       free(((struct ez_blob_array *)v)->data);
+               case EZ_BLOB_BLOB:
+                       free(((struct ez_blob *)v)->data);
                        break;
                case EZ_BLOB_STRUCT:
                        ez_blob_free_raw(d->bd_table, v);
@@ -107,6 +118,14 @@ void ez_blob_free_raw(const ez_blob_desc *d, void *p) {
                case EZ_BLOB_POINTER:
                        ez_blob_free(d->bd_table, ((void **)v)[0]);
                        break;
+               case EZ_BLOB_LIST: {
+                       ez_list *l = (ez_list *)v;
+                       for (ez_node *w = ez_list_head(l), *n = ez_node_succ(w);
+                            n;
+                            w = n, n=ez_node_succ(n)) {
+                               ez_blob_free(d->bd_table, w);
+                       }
+                       break; }
                default:
                        break;
                }
@@ -120,8 +139,9 @@ void ez_blob_free(const ez_blob_desc *d, void *p) {
        }
 }
 
-void *ez_blob_decode_raw(const ez_blob_desc *d, const char *b, size_t size, void *p) {
-       const char *be = b + size;
+void *ez_blob_decode_raw(const ez_blob_desc *d, const ez_blob *blob, void *p) {
+       const char *b = blob->data;
+       const char *be = b + blob->size;
 
        memset(p, 0, d->bd_offset);
        
@@ -135,14 +155,52 @@ void *ez_blob_decode_raw(const ez_blob_desc *d, const char *b, size_t size, void
                                goto fail;
                        memcpy(v, b, psize);
                        b += psize;
+               } else if (d->bd_type == EZ_BLOB_LIST) {
+                       uint32_t count;
+                       ez_list *l = v;
+                       
+                       if (b + 4 > be)
+                               goto fail;
+                       memcpy(&count, b, 4);
+                       b += 4;
+
+                       ez_list_init(l);
+                       for (int i=0;i<count;i++) {
+                               uint32_t ss;
+                               ez_blob sub;
+                               void *s;
+                               
+                               if (b + 4 > be)
+                                       goto fail;
+                               memcpy(&ss, b, 4);
+                               b += 4;
+
+                               sub.size = ss;
+                               sub.data = (void *)b;
+                               
+                               if (b + ss > be)
+                                       goto fail;
+                               if ((s = calloc(d->bd_table->bd_offset, 1)) == NULL)
+                                       goto fail;
+                               if (ez_blob_decode_raw(d->bd_table, &sub, s) == NULL) {
+                                       free(s);
+                                       goto fail;
+                               }
+                               ez_list_addtail(l, s);
+                               b+=ss;
+                       }
                } else if (d->bd_type <= EZ_BLOB_LAST) {
                        uint32_t ss;
+                       ez_blob sub;
 
                        if (b + 4 > be)
                                goto fail;
                        memcpy(&ss, b, 4);
                        b += 4;
 
+                       sub.size = ss;
+                       sub.data = (char *)b;
+
                        switch (d->bd_type) {
                        case EZ_BLOB_STRING: {
                                char *s = NULL;
@@ -158,8 +216,8 @@ void *ez_blob_decode_raw(const ez_blob_desc *d, const char *b, size_t size, void
                                }
                                ((char **)v)[0] = s;
                                break; }
-                       case EZ_BLOB_ARRAY: {
-                               struct ez_blob_array *a = v;
+                       case EZ_BLOB_BLOB: {
+                               struct ez_blob *a = v;
                                
                                if (b + ss > be)
                                        goto fail;
@@ -173,7 +231,7 @@ void *ez_blob_decode_raw(const ez_blob_desc *d, const char *b, size_t size, void
                                if (b + ss > be)
                                        goto fail;
                                
-                               if (ez_blob_decode_raw(d->bd_table, b, ss, v) == NULL)
+                               if (ez_blob_decode_raw(d->bd_table, &sub, v) == NULL)
                                        goto fail;
                                b += ss;
                                break;
@@ -185,7 +243,7 @@ void *ez_blob_decode_raw(const ez_blob_desc *d, const char *b, size_t size, void
                                                goto fail;
                                        if ((s = calloc(d->bd_table->bd_offset, 1)) == NULL)
                                                goto fail;
-                                       if (ez_blob_decode_raw(d->bd_table, b, ss, s) == NULL) {
+                                       if (ez_blob_decode_raw(d->bd_table, &sub, s) == NULL) {
                                                free(s);
                                                goto fail;
                                        }
@@ -207,10 +265,10 @@ void *ez_blob_decode_raw(const ez_blob_desc *d, const char *b, size_t size, void
        return NULL;
 }
 
-void *ez_blob_decode(const ez_blob_desc *d, const char *b, size_t size) {
+void *ez_blob_decode(const ez_blob_desc *d, const ez_blob *blob) {
        void *p = calloc(d->bd_offset, 1);
 
-       if (ez_blob_decode_raw(d, b, size, p) == NULL) {
+       if (ez_blob_decode_raw(d, blob, p) == NULL) {
                free(p);
                return NULL;
        }
@@ -218,8 +276,10 @@ void *ez_blob_decode(const ez_blob_desc *d, const char *b, size_t size) {
        return p;
 }
 
-void ez_blob_encode_raw(const ez_blob_desc *d, const void *p, char *b, size_t size) {
-       char *be = b+size;
+void ez_blob_encode_raw(const ez_blob_desc *d, const void *p, ez_blob *blob) {
+       char *b = blob->data;
+       char *be = b+blob->size;
+       
        for (;(d->bd_type != EZ_BLOB_END);d++) {
                const void *v = (p + d->bd_offset);
 
@@ -229,8 +289,29 @@ void ez_blob_encode_raw(const ez_blob_desc *d, const void *p, char *b, size_t si
                        assert(b+psize <= be);
                        memcpy(b, v, psize);
                        b += psize;
+               } else if (d->bd_type == EZ_BLOB_LIST) {
+                       ez_list *l = (void *)v;
+                       uint32_t ss = ez_list_size(l);
+
+                       memcpy(b, &ss, 4);
+                       b += 4;
+                       for (ez_node *w = ez_list_head(l), *n = ez_node_succ(w);
+                            n;
+                            w = n, n=ez_node_succ(n)) {
+                               ez_blob sub;
+                               
+                               ss = ez_blob_size(d->bd_table, w);
+                               
+                               assert(b+4+ss <= be);
+                               memcpy(b, &ss, 4);
+                               sub.data = b+4;
+                               sub.size = ss;
+                               ez_blob_encode_raw(d->bd_table, w, &sub);
+                               b += 4 + ss;
+                       }
                } else if (d->bd_type <= EZ_BLOB_LAST) {
                        uint32_t ss;
+                       struct ez_blob sub;
                        
                        switch (d->bd_type) {
                        case EZ_BLOB_STRING: {
@@ -248,8 +329,8 @@ void ez_blob_encode_raw(const ez_blob_desc *d, const void *p, char *b, size_t si
                                        b += 4;
                                }
                                break; }
-                       case EZ_BLOB_ARRAY: {
-                               const struct ez_blob_array *a = v;
+                       case EZ_BLOB_BLOB: {
+                               const struct ez_blob *a = v;
 
                                ss = a->size;
                                assert(b+4+ss <= be);
@@ -262,7 +343,9 @@ void ez_blob_encode_raw(const ez_blob_desc *d, const void *p, char *b, size_t si
                              
                                assert(b+4+ss <= be);
                                memcpy(b, &ss, 4);
-                               ez_blob_encode_raw(d->bd_table, v, b+4, ss);
+                               sub.data = b+4;
+                               sub.size = ss;
+                               ez_blob_encode_raw(d->bd_table, v, &sub);
                                b += 4 + ss;
                                break;
                        case EZ_BLOB_POINTER: {
@@ -272,7 +355,9 @@ void ez_blob_encode_raw(const ez_blob_desc *d, const void *p, char *b, size_t si
                                        ss = ez_blob_size(d->bd_table, s);
                                        assert(b+4+ss <= be);
                                        memcpy(b, &ss, 4);
-                                       ez_blob_encode_raw(d->bd_table, s, b+4, ss);
+                                       sub.data = b+4;
+                                       sub.size = ss;
+                                       ez_blob_encode_raw(d->bd_table, s, &sub);
                                        b += 4 + ss;
                                } else {
                                        ss = ~0;
@@ -289,20 +374,15 @@ void ez_blob_encode_raw(const ez_blob_desc *d, const void *p, char *b, size_t si
        assert(b == be);
 }
 
-/**
- * Encode blob to new buffer.
- *
- * @param p object to serialise.
- * @param sizep size of encoded blob return pointer.
- * @return a newly allocated blob of the correct size.
- */
-char *ez_blob_encode(const ez_blob_desc *d, const void *p, size_t *sizep) {
-       size_t size = ez_blob_size(d, p);
-       char *b = malloc(size);
-
-       ez_blob_encode_raw(d, p, b, size);
-
-       *sizep = size;
-       return b;
+int ez_blob_encode(const ez_blob_desc *d, const void *p, ez_blob *blob) {
+       blob->size = ez_blob_size(d, p);
+       blob->data = malloc(blob->size);
+
+       if (blob->data) {
+               ez_blob_encode_raw(d, p, blob);
+               return 0;
+       }
+       
+       return -1;
 }
 
index e162f0b..9dfad2f 100644 (file)
--- a/ez-blob.h
+++ b/ez-blob.h
@@ -84,18 +84,22 @@ typedef enum ez_blob_desc_type {
        EZ_BLOB_FLOAT32,
        EZ_BLOB_FLOAT64,
        EZ_BLOB_STRING,                 // normall c strring
-       EZ_BLOB_ARRAY,                  // embedded ez_blob_array for arbitrary bytes.
+       EZ_BLOB_BLOB,                   // embedded ez_blob for arbitrary bytes.
        EZ_BLOB_STRUCT,                 // bd_table points to another descriptor list, object embedded
        EZ_BLOB_POINTER,                // bd_table points to another descriptor list, object pointer
-       EZ_BLOB_LAST = EZ_BLOB_POINTER,
+       EZ_BLOB_LIST,                   // ez_list nodes.
+       EZ_BLOB_LAST = EZ_BLOB_LIST,
        EZ_BLOB_PK = 16,                // MUST always be first element in descriptor and occur only once
        EZ_BLOB_END
 } ez_blob_desc_type;
 
-typedef struct ez_blob_array {
-       unsigned int size;
+/** 
+ * This is compatible with lmdb MDB_val
+ */
+typedef struct ez_blob {
+       size_t size;
        void *data;
-} ez_blob_array;
+} ez_blob;
 
 #define EZ_BLOB_START(s) { EZ_BLOB_PK, 0, sizeof(s) }
 #define EZ_BLOB_INT8(s, id, f) { EZ_BLOB_INT8, id, offsetof(s, f) }
@@ -105,9 +109,10 @@ typedef struct ez_blob_array {
 #define EZ_BLOB_FLOAT32(s, id, f) { EZ_BLOB_FLOAT32, id, offsetof(s, f) }
 #define EZ_BLOB_FLOAT64(s, id, f) { EZ_BLOB_FLOAT64, id, offsetof(s, f) }
 #define EZ_BLOB_STRING(s, id, f) { EZ_BLOB_STRING, id, offsetof(s, f) }
-#define EZ_BLOB_ARRAY(s, id, f) { EZ_BLOB_ARRAY, id, offsetof(s, f) }
+#define EZ_BLOB_BLOB(s, id, f) { EZ_BLOB_BLOB, id, offsetof(s, f) }
 #define EZ_BLOB_STRUCT(s, id, f, other) {  EZ_BLOB_STRUCT, id, offsetof(s, f), .u.table = other }
 #define EZ_BLOB_POINTER(s, id, f, other) { EZ_BLOB_POINTER, id, offsetof(s, f), .u.table = other }
+#define EZ_BLOB_LIST(s, id, f, other) { EZ_BLOB_LIST, id, offsetof(s, f), .u.table = other }
 #define EZ_BLOB_END(s) { EZ_BLOB_END }
 
 /**
@@ -124,20 +129,19 @@ size_t ez_blob_size(const ez_blob_desc *d, const void *p);
  * @param d descriptor table matching the struct.
  * @param p structure.
  * @param sizep return pointer for blob size, must not be null.
- * @return the blob, it must be freed with free() when complete.
+ * @param blob return, the data pointer must be freed with free() when complete.
+ * @return non-zero on failure.
  */
-char *ez_blob_encode(const ez_blob_desc *d, const void *p, size_t *sizep);
+int ez_blob_encode(const ez_blob_desc *d, const void *p, ez_blob *blob);
 
 /**
  * Marshal a struct to a blob directly
  *
  * @param d descriptor table matching the struct.
  * @param p structure.
- * @param b target memory, must be as large as size
- * @param size size of target memory, must be exactly ez_blob_size().
- * @return the blob, it must be freed with free() when complete.
+ * @param blob available target memory, size must be exactly ez_blob_size().
  */
-void ez_blob_encode_raw(const ez_blob_desc *d, const void *p, char *b, size_t size);
+void ez_blob_encode_raw(const ez_blob_desc *d, const void *p, ez_blob *blob);
 
 /**
  * Unmarshall a blob into a struct.
@@ -147,7 +151,7 @@ void ez_blob_encode_raw(const ez_blob_desc *d, const void *p, char *b, size_t si
  * @param size blob size.
  * @return the decoded struct.  May be freed using ez_blob_free().
  */
-void *ez_blob_decode(const ez_blob_desc *d, const char *blob, size_t size);
+void *ez_blob_decode(const ez_blob_desc *d, const ez_blob *blob);
 
 /**
  * Decode to pre-allocated buffer.
@@ -156,7 +160,7 @@ void *ez_blob_decode(const ez_blob_desc *d, const char *blob, size_t size);
  * @param size blob size
  * @return p is returned on success.  On failure NULL is returned and any internal pointers are freed.
  */
-void *ez_blob_decode_raw(const ez_blob_desc *d, const char *b, size_t size, void *p);
+void *ez_blob_decode_raw(const ez_blob_desc *d, const ez_blob *blob, void *p);
 
 /**
  * Free struct unmarshalled by ez_blob_decode().
@@ -174,4 +178,9 @@ void ez_blob_free(const ez_blob_desc *d, void *p);
  */
 void ez_blob_free_raw(const ez_blob_desc *d, void *p);
 
+/**
+ * Debug function to dump blob contents.
+ */
+void ez_blob_print(const ez_blob_desc *d, const void *a, int depth);
+
 #endif
index 1024cb9..bce25d4 100644 (file)
@@ -8,6 +8,7 @@
 #include <assert.h>
 
 #include "ez-blob.h"
+#include "ez-list.h"
 
 struct simple {
        char b;
@@ -30,13 +31,13 @@ static const ez_blob_desc simple_DESC[] = {
 struct arrays {
        uint32_t count;
        char *string;
-       ez_blob_array array;
+       ez_blob array;
 };
 
 static const ez_blob_desc array_DESC[] = {
        EZ_BLOB_START(struct arrays),
        EZ_BLOB_STRING(struct arrays, 1, string),
-       EZ_BLOB_ARRAY(struct arrays, 2, array),
+       EZ_BLOB_BLOB(struct arrays, 2, array),
        EZ_BLOB_END(struct arrays)
 };
 
@@ -67,6 +68,31 @@ static const ez_blob_desc tree_DESC[] = {
        EZ_BLOB_END(struct tree)
 };
 
+struct listnode {
+       ez_node ln;
+       char *name;
+       ez_blob value;
+};
+
+static const ez_blob_desc listnode_DESC[] = {
+       EZ_BLOB_START(struct listnode),
+       EZ_BLOB_STRING(struct listnode, 1, name),
+       EZ_BLOB_BLOB(struct listnode, 2, value),
+       EZ_BLOB_END(struct listnode)
+};
+
+struct list {
+       char *name;
+       ez_list list;
+};
+
+static const ez_blob_desc list_DESC[] = {
+       EZ_BLOB_START(struct list),
+       EZ_BLOB_STRING(struct list, 1, name),
+       EZ_BLOB_LIST(struct list, 2, list, listnode_DESC),
+       EZ_BLOB_END(struct list)
+};
+
 static void dumphex(const char *data, size_t size, const char *prefix) {
        for (int i=0;i<size;i+=16) {
                fputs(prefix, stdout);
@@ -85,64 +111,8 @@ static void dumphex(const char *data, size_t size, const char *prefix) {
        }
 }
 
-static int blob_print(const ez_blob_desc *d, const void *a, int depth) {
-       int e = 1;
-       char x[depth+1];
-
-       memset(x, ' ', depth);
-       x[depth] = 0;
-       
-       for (;e && (d->bd_type != EZ_BLOB_END);d++) {
-               const void *u = a + d->bd_offset;
-
-               printf("%s [type=$%02x, offset=$%04x]", x, d->bd_type, d->bd_offset);
-               
-               switch (d->bd_type) {
-               case EZ_BLOB_INT8:
-                       printf(" .%d = %d\n", d->bd_id, *(uint8_t *)u);
-                       break;
-               case EZ_BLOB_INT16:
-                       printf(" .%d = %d\n", d->bd_id, *(uint16_t *)u);
-                       break;
-               case EZ_BLOB_INT32:
-                       printf(" .%d = %d\n", d->bd_id, *(uint32_t *)u);
-                       break;
-               case EZ_BLOB_INT64:
-                       printf(" .%d = %ld\n", d->bd_id, *(uint64_t *)u);
-                       break;
-               case EZ_BLOB_FLOAT32:
-                       printf(" .%d = %f\n", d->bd_id, *(float *)u);
-                       break;
-               case EZ_BLOB_FLOAT64:
-                       printf(" .%d = %f\n", d->bd_id, *(double *)u);
-                       break;
-               case EZ_BLOB_STRING:
-                       printf(" .%d %p = `%s'\n", d->bd_id, *((char **)u), *((char **)u));
-                       break;
-               case EZ_BLOB_ARRAY: {
-                       const struct ez_blob_array *ua = u;
-                       
-                       printf(" .%d %p [%d] = {\n", d->bd_id, ua->data, ua->size);
-                       dumphex(ua->data, ua->size, x);
-                       printf("%s  }\n", x);
-                       break; }
-               case EZ_BLOB_POINTER: {
-                       void *up = *((void **)u);
-                       if (up) {
-                               printf(" .%d %p = {\n", d->bd_id, up);
-                               blob_print(d->bd_table, up, depth+4);
-                               printf("%s  }\n", x);
-                       } else {
-                               printf(" .%d %p\n", d->bd_id, up);
-                       }
-                       break;
-               }
-               default:
-                       printf("\n");
-               }
-       }
-
-       return e;
+static void blob_print(const ez_blob_desc *d, const void *a, int depth) {
+       ez_blob_print(d, a, depth);
 }
 
 static int blob_equals(const ez_blob_desc *d, const void *a, const void *b) {
@@ -173,13 +143,27 @@ static int blob_equals(const ez_blob_desc *d, const void *a, const void *b) {
                case EZ_BLOB_STRING:
                        e &= strcmp((*(char **)u), (*(char **)v)) == 0;
                        break;
-               case EZ_BLOB_ARRAY: {
-                       const struct ez_blob_array *ua = u;
-                       const struct ez_blob_array *va = v;
+               case EZ_BLOB_BLOB: {
+                       const struct ez_blob *ua = u;
+                       const struct ez_blob *va = v;
 
                        e &= ua->size == va->size
                                && memcmp(ua->data, va->data, ua->size) == 0;
                        break; }
+               case EZ_BLOB_LIST: {
+                       ez_list *ul = (void *)u;
+                       ez_list *vl = (void *)v;
+
+                       e &= ez_list_size(ul) == ez_list_size(vl);
+                       for (ez_node *uw = ez_list_head(ul), *un = ez_node_succ(uw),
+                                    *vw = ez_list_head(vl), *vn = ez_node_succ(uw);
+                            un && vn;
+                            uw = un, un = ez_node_succ(un),
+                                    vw = vn, vn = ez_node_succ(vn)) {
+                               e &= blob_equals(d->bd_table, uw, vw);
+                       }
+                       
+                       break; }
                }
        }
 
@@ -187,38 +171,36 @@ static int blob_equals(const ez_blob_desc *d, const void *a, const void *b) {
 }
 
 static void test_basics(const ez_blob_desc *d, void *src) {
-       void *blob, *blob2;
+       ez_blob blob, blob2;
        void *t;
-       size_t size, size2;
        size_t dsize = d->bd_offset;
        char tmp[dsize + 64] __attribute__ ((aligned(64)));
-
+       
        printf("source\n");
        blob_print(d, src, 4);
        
-       blob = ez_blob_encode(d, src, &size);
+       ez_blob_encode(d, src, &blob);
+       dumphex(blob.data, blob.size, "serial: ");
 
        // check decode equals
-       t = ez_blob_decode(d, blob, size);
+       t = ez_blob_decode(d, &blob);
        assert(t != NULL);
        assert(blob_equals(d, t, src));
 
-       dumphex(blob, size, "serial: ");
-
        printf("decoded\n");
        blob_print(d, t, 4);
 
        // check encode-decoded equals source
-       blob2 = ez_blob_encode(d, t, &size2);
-       assert(size == size2);
-       assert(memcmp(blob, blob2, size) == 0);
-       free(blob2);
+       ez_blob_encode(d, t, &blob2);
+       assert(blob.size == blob2.size);
+       assert(memcmp(blob.data, blob2.data, blob2.size) == 0);
+       free(blob2.data);
        
        ez_blob_free(d, t);
 
        // check raw decode stays within bounds of struct
        memset(tmp, 0xbe, sizeof(tmp));
-       t = ez_blob_decode_raw(d, blob, size, &tmp[32]);
+       t = ez_blob_decode_raw(d, &blob, &tmp[32]);
        //assert(t != NULL);
        //assert(blob_equals(d, tmp+32, src));
        dumphex(tmp, sizeof(tmp), "object: ");
@@ -228,7 +210,7 @@ static void test_basics(const ez_blob_desc *d, void *src) {
                assert((tmp[i + 32 + dsize] & 0xff) == 0xbe);
        }
        ez_blob_free_raw(d, tmp+32);
-       free(blob);
+       free(blob.data);
 }
 
 static void test_simple(void) {
@@ -253,7 +235,7 @@ static void test_arrays(void) {
        test_basics(d, &src);
 }
 
-static void test_tree(void) {
+static void test_rawtree(void) {
        struct tree src[6] = {
                { &src[1], &src[3], "root" },
                { NULL, &src[2], "left" },
@@ -267,8 +249,30 @@ static void test_tree(void) {
        test_basics(d, &src);
 }
 
+static void test_list(void) {
+       struct list list = {
+               .name = "a list",
+               .list = EZ_INIT_LIST(list.list)
+       };
+       struct listnode nodes[3] = {
+               { .name = "node 0", .value.size = 5, .value.data = "data0" },
+               { .name = "node 1", .value.size = 5, .value.data = "data1" },
+               { .name = "node 2", .value.size = 5, .value.data = "data2" },
+       };
+
+       list.name = "a list";
+       //      ez_list_init(&list.
+       for (int i=0;i<3;i++)
+               ez_list_addtail(&list.list, &nodes[i]);
+       
+       const ez_blob_desc *d = list_DESC;
+
+       test_basics(d, &list);
+}
+
 int main(int argc, char **argv) {
        test_simple();
        test_arrays();
-       test_tree();
+       test_rawtree();
+       test_list();
 }