Redid the way struct fields are described to allow for combinatorial variations.
authorNot Zed <notzed@gmail.com>
Thu, 2 May 2019 08:30:49 +0000 (18:00 +0930)
committerNot Zed <notzed@gmail.com>
Thu, 2 May 2019 08:30:49 +0000 (18:00 +0930)
Added arrays of other primitives beyond bytes.
Namespace cleanup.
Exposed data-dump function.
Delete old serialiser.
Implement all changes in xdrn/tagz formats.

Makefile
ez-blob-blob.c [deleted file]
ez-blob-blob.h [deleted file]
ez-blob-dump.c [new file with mode: 0644]
ez-blob-io.c
ez-blob-print.c
ez-blob-tagz.c
ez-blob-xdrn.c
ez-blob.c
ez-blob.h
test-blob.c

index 5a3fc12..f16320f 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
 
 CPPFLAGS=-I.
-CFLAGS=-O0 -g -fPIC -Wall -mtune=native
+CFLAGS=-O2 -g -fPIC -Wall -mtune=native
 test_LDLIBS=-lpthread -lrt
 ARFLAGS=rvsUc
 VERSION=2.1.99
@@ -11,7 +11,7 @@ SRCS=                                         \
  ez-blob.c                                     \
  ez-blob-io.c                                  \
  ez-blob-print.c                               \
- ez-blob-blob.c                                        \
+ ez-blob-dump.c                                        \
  ez-blob-tagz.c                                        \
  ez-blob-xdrn.c                                        \
  ez-port.c                                     \
@@ -22,7 +22,6 @@ HEADERS =                                     \
  ez-bitset.h                                   \
  ez-blob.h                                     \
  ez-blobio.h                                   \
- ez-blob-blob.h                                        \
  ez-blob-tagz.h                                        \
  ez-blob-xdrn.h                                        \
  ez-list.h                                     \
@@ -52,7 +51,7 @@ test-%: test-%.o
 
 test-bitset: libeze.a(ez-bitset.o)
 test-blob: libeze.a(ez-blob.o) libeze.a(ez-blob-print.o) libeze.a(ez-blob-io.o) \
-       libeze.a(ez-blob-xdrn.o) libeze.a(ez-blob-tagz.o) libeze.a(ez-blob-blob.o)
+       libeze.a(ez-blob-xdrn.o) libeze.a(ez-blob-tagz.o) libeze.a(ez-blob-dump.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-blob.c b/ez-blob-blob.c
deleted file mode 100644 (file)
index ecc61a3..0000000
+++ /dev/null
@@ -1,367 +0,0 @@
-/* ez-blob-blob.c: Prototype 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 <string.h>
-#include <stdlib.h>
-#include <assert.h>
-
-#include "ez-list.h"
-
-#include "ez-blob.h"
-#include "ez-blob-blob.h"
-
-/*
- This implements a basic 'high performance' serialisation based on the
- descriptors.
-
- Fields are encoded in the same order as the descriptor with no
- labelling.  Therefore the same descriptor must be used on both ends.
- Fields may be at arbitrary alignments.
-
- primitives
-  encoded in machine order at native size
-
- strings
-  4-byte length followed by characters.  No nul termination.  A length
-  of ~0 indicates a null string pointer.
-
- arrays
-  4-byte length followed by data.  ** currently null pointer arrays with zero length are converted to a zero-length array.
-
- struct
-  4-byte length followed by blob-encoded data.
-
- pointer
-  4-byte length followed by blob-encoded data.  A length of ~0
-  indicates a null pointer.
-
-*/
-
-static const int primitive_size[EZ_BLOB_FLOAT64+1] = {
-       1, 2, 4, 8,
-       4, 8
-};
-
-size_t ez_blob_size(const ez_blob_desc *desc, const void *p) {
-       size_t size = 0;
-
-       for (int i=0,dlen = desc->bd_length;i<dlen;i++) {
-               const ez_blob_desc *d = &desc[i+1];
-
-               if (d->bd_type <= EZ_BLOB_FLOAT64) {
-                       size += primitive_size[d->bd_type];
-               } else {
-                       const void *v = p + d->bd_offset;
-                       switch (d->bd_type & EZ_BLOB_TYPE) {
-                       case EZ_BLOB_STRING: {
-                               char *s = ((char **)v)[0];
-                               size += s ? 4 + strlen(s) : 4;
-                               break;
-                       }
-                       case EZ_BLOB_BLOB:
-                               size += 4 + ((const struct ez_blob *)v)->size;
-                               break;
-                       case EZ_BLOB_STRUCT: {
-                               const void *sp = v;
-                               size += 4 + ez_blob_size(d->bd_table, sp);
-                               break;
-                       }
-                       case EZ_BLOB_POINTER: {
-                               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;
-                       }
-               }
-       }
-
-       return size;
-}
-
-int ez_blob_decode_raw(const ez_blob_desc *desc, const ez_blob *blob, void *p) {
-       const char *b = blob->data;
-       const char *be = b + blob->size;
-
-       ez_blob_init(desc, p);
-       
-       for (int i=0,dlen = desc->bd_length;i<dlen;i++) {
-               const ez_blob_desc *d = &desc[i+1];
-               void *v = (p + d->bd_offset);
-
-               if (d->bd_type <= EZ_BLOB_FLOAT64) {
-                       int psize = primitive_size[d->bd_type];
-
-                       if (b + psize > be)
-                               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) != 0) {
-                                       free(s);
-                                       goto fail;
-                               }
-                               ez_list_addtail(l, s);
-                               b+=ss;
-                       }
-               } else {
-                       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 & EZ_BLOB_TYPE) {
-                       case EZ_BLOB_STRING: {
-                               char *s = NULL;
-
-                               if (ss != ~0) {
-                                       if (b + ss > be)
-                                               goto fail;
-                                       if ((s = malloc((size_t)ss + 1)) == NULL)
-                                               goto fail;
-                                       memcpy(s, b, ss);
-                                       s[ss] = 0;
-                                       b += ss;
-                               }
-                               ((char **)v)[0] = s;
-                               break;
-                       }
-                       case EZ_BLOB_BLOB: {
-                               struct ez_blob *a = v;
-                               
-                               if (b + ss > be)
-                                       goto fail;
-                               a->size = ss;
-                               if ((a->data = malloc(ss)) == NULL)
-                                       goto fail;
-                               memcpy(a->data, b, ss);
-                               b += ss;
-                               break;
-                       }
-                       case EZ_BLOB_STRUCT:
-                               if (b + ss > be)
-                                       goto fail;
-
-                               if (ez_blob_decode_raw(d->bd_table, &sub, v) != 0)
-                                       goto fail;
-                               b += ss;
-                               break;
-                       case EZ_BLOB_POINTER: {
-                               void *s = NULL;
-
-                               if (ss != ~0) {
-                                       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) != 0) {
-                                               free(s);
-                                               goto fail;
-                                       }
-                                       b += ss;
-                               }
-                               ((void **)v)[0] = s;
-                               break;
-                       }
-                       default:
-                               break;
-                       }
-               }
-       }
-       if (b != be)
-               goto fail;
-
-       return 0;
- fail:
-       ez_blob_free_raw(desc, p);
-       return 1;
-}
-
-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, blob, p) != 0) {
-               free(p);
-               return NULL;
-       }
-       
-       return p;
-}
-
-int ez_blob_encode_raw(const ez_blob_desc *desc, const void *p, ez_blob *blob) {
-       char *b = blob->data;
-       char *be = b+blob->size;
-       
-       for (int i=0,dlen = desc->bd_length;i<dlen;i++) {
-               const ez_blob_desc *d = &desc[i+1];
-               const void *v = (p + d->bd_offset);
-
-               if (d->bd_type <= EZ_BLOB_FLOAT64) {
-                       int psize = primitive_size[d->bd_type];
-                       
-                       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 {
-                       uint32_t ss;
-                       struct ez_blob sub;
-                       
-                       switch (d->bd_type & EZ_BLOB_TYPE) {
-                       case EZ_BLOB_STRING: {
-                               char *s = ((char **)v)[0];
-                               if (s) {
-                                       ss = strlen(s);
-                                       assert(b+4+ss <= be);
-                                       memcpy(b, &ss, 4);
-                                       memcpy(b+4, s, ss);
-                                       b += 4 + ss;
-                               } else {
-                                       ss = ~0;
-                                       assert(b+4 <= be);
-                                       memcpy(b, &ss, 4);
-                                       b += 4;
-                               }
-                               break;
-                       }
-                       case EZ_BLOB_BLOB: {
-                               const struct ez_blob *a = v;
-
-                               ss = a->size;
-                               assert(b+4+ss <= be);
-                               memcpy(b, &ss, 4);
-                               memcpy(b+4, a->data, ss);
-                               b += 4 + ss;
-                               break;
-                       }
-                       case EZ_BLOB_STRUCT:
-                               ss = ez_blob_size(d->bd_table, v);
-                             
-                               assert(b+4+ss <= be);
-                               memcpy(b, &ss, 4);
-                               sub.data = b+4;
-                               sub.size = ss;
-                               ez_blob_encode_raw(d->bd_table, v, &sub);
-                               b += 4 + ss;
-                               break;
-                       case EZ_BLOB_POINTER: {
-                               const void *s = ((const void **)v)[0];
-
-                               if (s) {
-                                       ss = ez_blob_size(d->bd_table, s);
-                                       assert(b+4+ss <= be);
-                                       memcpy(b, &ss, 4);
-                                       sub.data = b+4;
-                                       sub.size = ss;
-                                       ez_blob_encode_raw(d->bd_table, s, &sub);
-                                       b += 4 + ss;
-                               } else {
-                                       ss = ~0;
-                                       assert(b+4 <= be);
-                                       memcpy(b, &ss, 4);
-                                       b += 4;
-                               }
-                               break;
-                       }
-                       default:
-                               break;
-                       }
-               }
-       }
-       assert(b == be);
-       return 0;
-}
-
-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) {
-               if (ez_blob_encode_raw(d, p, blob) == 0)
-                       return 0;
-               free(blob->data);
-               blob->data = NULL;
-       }
-       blob->size = 0;
-       
-       return -1;
-}
diff --git a/ez-blob-blob.h b/ez-blob-blob.h
deleted file mode 100644 (file)
index 53c9b8c..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-/* ez-blob-blob.h: Prototype 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/>.
-*/
-
-/*
- * The implemented serialisation mechanism only supports
- * forward-linked structures and will not perform well when
- * marshalling deeply nested structures but other serialisation
- * mechanisms are possible.
- *
- * It's historical and is to be removed.
- */
-
-/**
- * Calculate the serialised blob size for a struct.
- *
- * @param d descriptor table matching struct.
- * @param p struct.
- */
-size_t ez_blob_size(const ez_blob_desc *d, const void *p);
-
-/**
- * Marshal a struct to a blob.
- * 
- * @param d descriptor table matching the struct.
- * @param p structure.
- * @param sizep return pointer for blob size, must not be null.
- * @param blob return, the data pointer must be freed with free() when complete.
- * @return non-zero on failure.
- */
-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 blob available target memory, size must be exactly ez_blob_size().
- * @return non-zero on failure.  On failure blob.data is invalid.
- */
-int ez_blob_encode_raw(const ez_blob_desc *d, const void *p, ez_blob *blob);
-
-/**
- * Unmarshall a blob into a struct.
- *
- * @param d descriptor table matching blob.
- * @param blob raw blob.
- * @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 ez_blob *blob);
-
-/**
- * Decode to pre-allocated buffer.
- *
- * @param b blob data
- * @param size blob size
- * @return non-zero on failure.  Any allocated pointers are freed.
- */
-int ez_blob_decode_raw(const ez_blob_desc *d, const ez_blob *blob, void *p);
-
diff --git a/ez-blob-dump.c b/ez-blob-dump.c
new file mode 100644 (file)
index 0000000..db46661
--- /dev/null
@@ -0,0 +1,48 @@
+/* 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 <stdio.h>
+#include <stdint.h>
+#include <ctype.h>
+
+#include "ez-blob.h"
+
+void ez_blob_dump(const ez_blob *blob, const char *prefix) {
+       const char *data = blob->eb_data;
+       size_t size = blob->eb_size;
+       
+       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');
+       }
+}
index c943ced..51b59ea 100644 (file)
@@ -21,7 +21,7 @@
 #include <stdint.h>
 #include <string.h>
 #include <stdlib.h>
-#include <ctype.h>
+#include <errno.h>
 
 #include "ez-blob.h"
 #include "ez-blob-io.h"
@@ -50,7 +50,7 @@ void *blobio_reserve(struct ez_blobio * __restrict io, size_t len) {
                        alloc = realloc(io->data, size);
 
                        if (!alloc) {
-                               io->error = 1;
+                               io->error = ENOMEM;
                                printf("realloc failed\n");
                                ABORT();
                                return NULL;
@@ -65,7 +65,7 @@ void *blobio_reserve(struct ez_blobio * __restrict io, size_t len) {
 
        case BLOBIO_WRITE_FIXED:
                if (to > size) {
-                       io->error = 1;
+                       io->error = EOVERFLOW;
                        ABORT();
                        return NULL;
                }
@@ -75,7 +75,9 @@ void *blobio_reserve(struct ez_blobio * __restrict io, size_t len) {
 
        case BLOBIO_WRITE_SIZE:
                io->index = to;
+               return NULL;
        default:
+               io->error = EINVAL;
                return NULL;
        }
 }
@@ -90,7 +92,7 @@ void *blobio_take(struct ez_blobio * __restrict io, size_t len) {
        } else {
                ABORT();
                io->index = io->size;
-               io->error = 1;
+               io->error = EOVERFLOW;
                return NULL;
        }
 }
@@ -110,25 +112,3 @@ void blobio_read_align(struct ez_blobio *io, unsigned int step) {
 
        blobio_take(io, skip);
 }
-
-void blobio_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');
-       }
-}
-
index 6cea3bd..4f61bc3 100644 (file)
@@ -23,7 +23,6 @@
 
 #include <assert.h>
 #include <stdio.h>
-#include <ctype.h>
 
 #include "ez-blob.h"
 #include "ez-list.h"
@@ -40,9 +39,9 @@ void ez_blob_print(const ez_blob_desc *d, const void *a, int depth) {
        for (int i=0;i<=len;i++,d++) {
                const void *u = a + d->bd_offset;
 
-               printf("%s [type=$%02x, offset=$%04x]", x, d->bd_type, d->bd_offset);
+               printf("%s [type=$%04x, offset=$%04x]", x, d->bd_type, d->bd_offset);
                
-               switch (d->bd_type & EZ_BLOB_TYPE) {
+               switch (d->bd_type) {
                case EZ_BLOB_INT8:
                        printf(" .%d = %02x\n", d->bd_id, *(uint8_t *)u);
                        break;
@@ -61,44 +60,64 @@ void ez_blob_print(const ez_blob_desc *d, const void *a, int depth) {
                case EZ_BLOB_FLOAT64:
                        printf(" .%d = %f\n", d->bd_id, *(double *)u);
                        break;
-               case EZ_BLOB_STRING:
+                       
+               case EZ_BLOB_STRUCT | EZ_BLOB_SINGLEP | EZ_BLOB_ISNULLABLE:
+               case EZ_BLOB_STRUCT | EZ_BLOB_SINGLEP:
+                       u = *(void **)u;
+               case EZ_BLOB_STRUCT | EZ_BLOB_SINGLE:
+                       if (u) {
+                               printf(" .%d %p = {\n", d->bd_id, u);
+                               ez_blob_print(d->bd_table, u, depth+4);
+                               printf("%s  }\n", x);
+                       } else {
+                               printf(" .%d %p\n", d->bd_id, u);
+                       }
+                       break;
+
+               case EZ_BLOB_CSTRING | EZ_BLOB_INT8 | EZ_BLOB_ISNULLABLE:
+               case EZ_BLOB_CSTRING | EZ_BLOB_INT8:
                        printf(" .%d %p = `%s'\n", d->bd_id, *((char **)u), *((char **)u));
                        break;
-               case EZ_BLOB_BLOB: {
+
+               case EZ_BLOB_VECTOR | EZ_BLOB_INT8:
+               case EZ_BLOB_VECTOR | EZ_BLOB_INT16:
+               case EZ_BLOB_VECTOR | EZ_BLOB_INT32:
+               case EZ_BLOB_VECTOR | EZ_BLOB_INT64:
+               case EZ_BLOB_VECTOR | EZ_BLOB_FLOAT32:
+               case EZ_BLOB_VECTOR | EZ_BLOB_FLOAT64: {
                        const struct ez_blob *ua = u;
+                       unsigned int elsize = 1 << (d->bd_type & EZ_BLOB_SIZE);
+                       ez_blob blob = { .eb_size = ua->eb_size * elsize, .eb_data = ua->eb_data };
                        
-                       printf(" .%d %p [%zd] = {\n", d->bd_id, ua->data, ua->size);
-                       blobio_dumphex(ua->data, ua->size, x);
+                       printf(" .%d %p [%zd x %d] = {\n", d->bd_id, ua->eb_data, ua->eb_size, elsize);
+                       ez_blob_dump(&blob, 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);
+
+               case EZ_BLOB_STRUCT | EZ_BLOB_LISTP | EZ_BLOB_ISNULLABLE:
+               case EZ_BLOB_STRUCT | EZ_BLOB_LISTP:
+                       u = *(const void **)u;
+               case EZ_BLOB_STRUCT | EZ_BLOB_LIST:
+                       if (u) {
+                               printf(" .%d %p [] = {\n", d->bd_id, u);
+                               for (ez_node *w = ez_list_head((ez_list *)u), *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);
                        } else {
-                               printf(" .%d %p\n", d->bd_id, up);
+                               printf(" .%d %p\n", d->bd_id, u);
                        }
                        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);
+
+               case EZ_BLOB_MAGIC:
+                       printf(" OBJECT id=%d length=%d\n", d->bd_id, d->bd_length);
                        break;
-               }
                default:
-                       printf("\n");
+                       printf(" .%d ?? UNKNOWN ??\n", d->bd_id);
+                       break;
                }
        }
 }
index 1c6b7f1..467eba7 100644 (file)
@@ -85,10 +85,31 @@ final note:
 
 */
 
+/*
+  Support matrix:
+
+                    single  singlep vector  vectorp list    listp   cstring
+                             ?null 1                         ?null   ?null
+
+      int8             x               x 2                             x 3
+      int16            x               x 2
+      int32            x               x 2
+      int64            x               x 2
+      float            x               x
+      double           x               x
+      struct           x       x                       x       x
+
+  1. All pointers are treated as nullable.  The annotation is ignored.
+  2. All bytes of integers are written with no prefix removal.
+  3. ??Strings are written containing the trailing 0 byte??
+
+*/
+
 #include <stdint.h>
 #include <string.h>
 #include <stdlib.h>
 #include <stdio.h>
+#include <errno.h>
 
 #include "ez-list.h"
 
@@ -99,6 +120,7 @@ final note:
 //#define D(x) do { x; fflush(stdout); } while (0)
 #define D(x)
 
+// These are EZ_* types << 4
 #define EZT_INT8    0x00
 #define EZT_INT16   0x10
 #define EZT_INT32   0x20
@@ -149,7 +171,7 @@ static int size_code(uint64_t size) {
        else if (size <= 0xffffffff)
                return 0x2;
        else
-               return 0x3;     
+               return 0x3;
 }
 
 static int tag_code(uint32_t size) {
@@ -181,81 +203,95 @@ static void blobio_writetc(struct ez_blobio *io, uint8_t type, uint32_t tag, uin
 
 static void tagz_encode_struct(struct ez_blobio *io, int id, const ez_blob_desc *desc, const void *p) {
        blobio_writet(io, EZT_STRUCT, id);
-       
+
        for (int i=0,dlen=desc->bd_length;i<dlen;i++) {
                const ez_blob_desc *d = &desc[i+1];
                const void *v = (p + d->bd_offset);
                uint64_t val = 0;
                uint64_t count = 0;
                const void *ptr = NULL;
-               
-               switch (d->bd_type & EZ_BLOB_TYPE) {
-               case EZ_BLOB_INT8:
+
+               switch (d->bd_type & (EZ_BLOB_TYPE | EZ_BLOB_STORAGE)) {
+               case EZ_BLOB_SINGLE | EZ_BLOB_INT8:
                        val = *(uint8_t *)v;
                        goto doint;
-               case EZ_BLOB_INT16:
+               case EZ_BLOB_SINGLE | EZ_BLOB_INT16:
                        val = *(uint16_t *)v;
                        goto doint;
-               case EZ_BLOB_INT32:
+               case EZ_BLOB_SINGLE | EZ_BLOB_INT32:
                        val = *(uint32_t *)v;
                        goto doint;
-               case EZ_BLOB_INT64:
+               case EZ_BLOB_SINGLE | EZ_BLOB_INT64:
                        val = *(uint64_t *)v;
                doint:
                        if (val) {
                                int sc = size_code(val);
-                               
+
                                blobio_writet(io, EZT_INT8 | (sc << EZT_DATASHIFT), d->bd_id);
                                blobio_writei(io, sc, val);
                        }
                        break;
-               case EZ_BLOB_FLOAT32:
+               case EZ_BLOB_SINGLE | EZ_BLOB_FLOAT32:
                        blobio_writet(io, EZT_FLOAT32, d->bd_id);
                        blobio_writef(io, *(const float *)v);
                        break;
-               case EZ_BLOB_FLOAT64:
+               case EZ_BLOB_SINGLE | EZ_BLOB_FLOAT64:
                        blobio_writet(io, EZT_FLOAT64, d->bd_id);
                        blobio_writed(io, *(const double *)v);
                        break;
-               case EZ_BLOB_STRING:
-                       // NULL strings are the default, so implement same but allow for empty strings
-                       ptr = *(const void **)v;
-                       if (ptr) {
-                               count = strlen(ptr);
-                               goto doblob;
+
+               case EZ_BLOB_VECTOR | EZ_BLOB_INT8:
+               case EZ_BLOB_VECTOR | EZ_BLOB_INT16:
+               case EZ_BLOB_VECTOR | EZ_BLOB_INT32:
+               case EZ_BLOB_VECTOR | EZ_BLOB_INT64:
+               case EZ_BLOB_VECTOR | EZ_BLOB_FLOAT32:
+               case EZ_BLOB_VECTOR | EZ_BLOB_FLOAT64:
+                       count = ((ez_blob *)v)->eb_size;
+                       if (count) {
+                               int sc = (d->bd_type & EZ_BLOB_SIZE);
+                               int tc = (d->bd_type & EZ_BLOB_TYPE) << 4;
+
+                               ptr = ((ez_blob *)v)->eb_data;
+                               blobio_writetc(io, tc, d->bd_id, count);
+                               blobio_write(io, ptr, count << sc);
                        }
                        break;
-               case EZ_BLOB_BLOB:
-                       // empty blobs are the default, so implement same, empty are not encoded
-                       count = ((ez_blob *)v)->size;
-                       if (count) {
-                               ptr = ((ez_blob *)v)->data;
-                       doblob:
+
+               case EZ_BLOB_CSTRING | EZ_BLOB_INT8:
+                       // NULL strings are the default, so implement same but allow for empty strings
+                       v = *(const void **)v;
+                       if (v) {
+                               count = strlen(v);
                                blobio_writetc(io, EZT_INT8, d->bd_id, count);
-                               blobio_write(io, ptr, count);
+                               blobio_write(io, v, count);
                        }
                        break;
-               case EZ_BLOB_STRUCT:
-                       tagz_encode_struct(io, d->bd_id, d->bd_table, v);
-                       break;
-               case EZ_BLOB_POINTER:
-                       ptr = *(const void **)v;
-                       if (ptr)
-                               tagz_encode_struct(io, d->bd_id, d->bd_table, ptr);
+
+               case EZ_BLOB_STRUCT | EZ_BLOB_SINGLEP:
+                       v = *(const void **)v;
+               case EZ_BLOB_STRUCT | EZ_BLOB_SINGLE:
+                       if (v)
+                               tagz_encode_struct(io, d->bd_id, d->bd_table, v);
                        break;
-               case EZ_BLOB_LIST:
-                       count = ez_list_size((ez_list *)v);
-                       if (count) {
-                               blobio_writetc(io, EZT_STRUCT, d->bd_id, count);
-                       
-                               for (ez_node *w = ez_list_head((ez_list *)v), *n = ez_node_succ(w);
-                                    n;
-                                    w = n, n=ez_node_succ(n)) {
-                                       // which id to use?
-                                       tagz_encode_struct(io, d->bd_table[0].bd_id, d->bd_table, w);
+
+               case EZ_BLOB_STRUCT | EZ_BLOB_LISTP:
+                       v = *(const void **)v;
+               case EZ_BLOB_STRUCT | EZ_BLOB_LIST:
+                       if (v) {
+                               count = ez_list_size((ez_list *)v);
+                               if (count) {
+                                       blobio_writetc(io, EZT_STRUCT, d->bd_id, count);
+
+                                       for (ez_node *w = ez_list_head((ez_list *)v), *n = ez_node_succ(w); n; w = n, n=ez_node_succ(n)) {
+                                               // which id to use?
+                                               tagz_encode_struct(io, d->bd_table[0].bd_id, d->bd_table, w);
+                                       }
                                }
                        }
                        break;
+               default:
+                       io->error = ENOTSUP;
+                       return;
                }
 
        }
@@ -274,8 +310,8 @@ size_t ez_tagz_size(const ez_blob_desc *d, const void *p) {
 
 int ez_tagz_encode_raw(const ez_blob_desc *d, const void *p, ez_blob *blob) {
        struct ez_blobio io = {
-               .data = blob->data,
-               .size = blob->size,
+               .data = blob->eb_data,
+               .size = blob->eb_size,
                .mode = BLOBIO_WRITE_FIXED
        };
 
@@ -292,15 +328,16 @@ int ez_tagz_encode(const ez_blob_desc *d, const void *p, ez_blob *blob) {
        tagz_encode_struct(&io, d->bd_id, d, p);
 
        if (!io.error) {
-               blob->size = io.index;
-               blob->data = io.data;
+               blob->eb_size = io.index;
+               blob->eb_data = io.data;
                return 0;
        }
-       free(io.data);
-       blob->data = 0;
-       blob->size = 0;
        
-       return -1;
+       free(io.data);  
+       blob->eb_data = NULL;
+       blob->eb_size = 0;
+
+       return io.error;
 }
 
 static int blobio_readb(struct ez_blobio *io) {
@@ -325,6 +362,10 @@ static uint64_t blobio_readi(struct ez_blobio *io, int sc) {
 static void tagz_decode_struct(struct ez_blobio *io, const ez_blob_desc *desc, void *p);
 static void tagz_decode_fields(struct ez_blobio *io, const ez_blob_desc *desc, void *p);
 
+static unsigned int tagz_blob_type(int h) {
+       return (h >> 4) & EZ_BLOB_TYPE;
+}
+
 static void tagz_decode_fields(struct ez_blobio *io, const ez_blob_desc *desc, void *p) {
        uint8_t h;
        const ez_blob_desc *dscan = desc ? desc + 1 : desc;
@@ -355,131 +396,132 @@ static void tagz_decode_fields(struct ez_blobio *io, const ez_blob_desc *desc, v
                if (p && d) {
                        void *v = p + d->bd_offset;
 
-                       switch (h & EZT_TYPE) {
-                       case EZT_INT8:
-                       case EZT_INT16:
-                       case EZT_INT32:
-                       case EZT_INT64:
-                               if (use_count) {
-                                       char *mem;
-                                       size_t len = fcount * (1<<sc);
-                                       void *data = blobio_take(io, len);
-
-                                       if (!data)
-                                               goto error;
-
-                                       switch (d->bd_type & EZ_BLOB_TYPE) {
-                                       case EZ_BLOB_STRING:
-                                               mem = malloc(len+1);
-                                               if (!mem)
-                                                       goto error;
-                                               *(char **)v = mem;
-                                               memcpy(mem, data, len);
-                                               mem[len] = 0;
-                                               //*(char **)v = data;
-                                               D(printf("string %d '%s'\n", d->bd_id, mem));
-                                               break;
-                                       case EZ_BLOB_BLOB:
-                                               mem = malloc(len);
-                                               if (!mem)
-                                                       goto error;
-                                               ((ez_blob *)v)->data = mem;
-                                               ((ez_blob *)v)->size = len;
-                                               memcpy(mem, data, len);
-                                               //((ez_blob *)v)->data = data;
-                                               break;
-                                       }
-                               } else {
-                                       switch (d->bd_type) {
-                                       case EZ_BLOB_INT8:
-                                               *(uint8_t *)v = blobio_readi(io, sc);
-                                               break;
-                                       case EZ_BLOB_INT16:
-                                               *(uint16_t *)v = blobio_readi(io, sc);
-                                               break;
-                                       case EZ_BLOB_INT32:
-                                               *(uint32_t *)v = blobio_readi(io, sc);
-                                               break;
-                                       case EZ_BLOB_INT64:
-                                               *(uint64_t *)v = blobio_readi(io, sc);
-                                               break;
-                                       default:
-                                               goto error;
-                                       }
+                       unsigned int ttype = tagz_blob_type(h);
+                       unsigned int btype = d->bd_type & EZ_BLOB_TYPE;
+
+                       if (use_count) {
+                               // Type must match exactly
+                               if (ttype != btype) {
+                                       io->error = EILSEQ;
+                                       return;
                                }
-                               break;
-                       case EZT_FLOAT32:
-                               if (use_count) {
-                                       blobio_take(io, fcount * 4);
-                               } else {
-                                       switch (d->bd_type) {
-                                       case EZ_BLOB_FLOAT32:
-                                               *(float *)v = blobio_readf(io);
-                                               break;
-                                       case EZ_BLOB_FLOAT64:
-                                               *(double *)v = blobio_readf(io);
-                                               break;
-                                       default:
-                                               goto error;
+                               
+                               switch (d->bd_type & (EZ_BLOB_STORAGE | EZ_BLOB_TYPE)) {
+                                       /* typed vectors */
+                               case EZ_BLOB_VECTOR | EZ_BLOB_INT8:
+                               case EZ_BLOB_VECTOR | EZ_BLOB_INT16:
+                               case EZ_BLOB_VECTOR | EZ_BLOB_INT32:
+                               case EZ_BLOB_VECTOR | EZ_BLOB_INT64:
+                               case EZ_BLOB_VECTOR | EZ_BLOB_FLOAT32:
+                               case EZ_BLOB_VECTOR | EZ_BLOB_FLOAT64: {
+                                       size_t size = fcount << (d->bd_type & EZ_BLOB_SIZE);
+                                       void *src = blobio_take(io, size);
+
+                                       if (src) {
+                                               void *mem = malloc(size);
+
+                                               if (mem) {
+                                                       memcpy(mem, src, size);
+                                                       ((ez_blob *)v)->eb_data = mem;
+                                                       ((ez_blob *)v)->eb_size = fcount;
+                                                       D(printf("vector %d [%d]'\n", d->bd_id, fcount));
+                                               } else {
+                                                       io->error = ENOMEM;
+                                                       return;
+                                               }
                                        }
+                                       break;
                                }
-                               break;
-                       case EZT_FLOAT64:
-                               if (use_count) {
-                                       blobio_take(io, fcount * 8);
-                               } else {
-                                       switch (d->bd_type) {
-                                       case EZ_BLOB_FLOAT32:
-                                               *(float *)v = blobio_readd(io);
-                                               break;
-                                       case EZ_BLOB_FLOAT64:
-                                               *(double *)v = blobio_readd(io);
-                                               break;
-                                       default:
-                                               goto error;
+
+                                       /* C string */
+                               case EZ_BLOB_CSTRING | EZ_BLOB_INT8: {
+                                       size_t size = fcount;
+                                       void *src = blobio_take(io, size);
+                                       if (src) {
+                                               char *mem = malloc(size+1);
+                                               
+                                               if (mem) {
+                                                       memcpy(mem, src, size);
+                                                       mem[size] = 0;
+                                                       *(char **)v = mem;
+                                               } else {
+                                                       io->error = ENOMEM;
+                                                       return;
+                                               }
                                        }
+                                       break;
                                }
-                               break;
-                       case EZT_STRUCT:
-                               if (use_count) {
-                                       switch (d->bd_type) {
-                                       case EZ_BLOB_LIST:
-                                               for (int i=0; !io->error && i < fcount; i++) {
-                                                       void *node = ez_blob_alloc(d->bd_table);
-
-                                                       if (!node)
-                                                               goto error;
-
+                                       
+                                       /* List */
+                               case EZ_BLOB_STRUCT | EZ_BLOB_LISTP:
+                                       *(void **)v = malloc(sizeof(ez_list));
+                                       v = *(void **)v;
+                                       if (!v) {
+                                               io->error = ENOMEM;
+                                               return;
+                                       }
+                                       ez_list_init((ez_list *)v);
+                               case EZ_BLOB_STRUCT | EZ_BLOB_LIST:
+                                       for (int i=0; !io->error && i < fcount; i++) {
+                                               void *node = ez_blob_alloc(d->bd_table);
+                                               
+                                               if (node) {
                                                        tagz_decode_struct(io, d->bd_table, node);
                                                        ez_list_addtail((ez_list *)v, node);
+                                               } else {
+                                                       io->error = ENOMEM;
+                                                       return;
                                                }
-                                               break;
-                                       default:
-                                               goto error;
                                        }
-                               } else {
-                                       switch (d->bd_type & EZ_BLOB_TYPE) {
-                                       case EZ_BLOB_STRUCT:
-                                               D(printf("struct %d\n", d->bd_id));
-                                               //ez_blob_init(d->bd_table, v);
-                                               tagz_decode_fields(io, d->bd_table, v);
-                                               break;
-                                       case EZ_BLOB_POINTER:
-                                               D(printf("pointer %d\n", d->bd_id));
-                                               *(void **)v = ez_blob_alloc(d->bd_table);
-
-                                               if (!*(void **)v)
-                                                       goto error;
-                                               
-                                               tagz_decode_fields(io, d->bd_table, *(void **)v);
-                                               break;
-                                       default:
-                                               goto error;
+                                       break;
+                               default:
+                                       io->error = EILSEQ;
+                                       return;
+                               }
+                       } else {
+                               // Type can either match or the target is a larger or same sized integer
+                               if (ttype != btype
+                                   && (ttype > EZ_BLOB_INT64 || btype < ttype)) {
+                                       io->error = EILSEQ;
+                                       return;
+                               }
+
+                               switch (d->bd_type & (EZ_BLOB_STORAGE | EZ_BLOB_TYPE)) {
+                               
+                               case EZ_BLOB_SINGLE | EZ_BLOB_INT8:
+                                       *(uint8_t *)v = blobio_readi(io, sc);
+                                       break;
+                               case EZ_BLOB_SINGLE | EZ_BLOB_INT16:
+                                       *(uint16_t *)v = blobio_readi(io, sc);
+                                       break;
+                               case EZ_BLOB_SINGLE | EZ_BLOB_INT32:
+                                       *(uint32_t *)v = blobio_readi(io, sc);
+                                       break;
+                               case EZ_BLOB_SINGLE | EZ_BLOB_INT64:
+                                       *(uint64_t *)v = blobio_readi(io, sc);
+                                       break;
+                               case EZ_BLOB_SINGLE | EZ_BLOB_FLOAT32:
+                                       *(float *)v = blobio_readf(io);
+                                       break;
+                               case EZ_BLOB_SINGLE | EZ_BLOB_FLOAT64:
+                                       *(double *)v = blobio_readd(io);
+                                       break;
+
+                               case EZ_BLOB_STRUCT | EZ_BLOB_SINGLEP:
+                                       *(void **)v = ez_blob_alloc(d->bd_table);
+                                       v = *(void **)v;
+                                       if (!v) {
+                                               io->error = ENOMEM;
+                                               return;
                                        }
+                               case EZ_BLOB_STRUCT | EZ_BLOB_SINGLE:
+                                       tagz_decode_fields(io, d->bd_table, v);
+                                       break;
+
+                               default:
+                                       io->error = EILSEQ;
+                                       return;
                                }
-                               break;
-                       default:
-                               goto error;
                        }
                } else {
                        // Just skip fields
@@ -506,55 +548,48 @@ static void tagz_decode_fields(struct ez_blobio *io, const ez_blob_desc *desc, v
                                }
                                break;
                        default:
-                               goto error;
+                               io->error = EILSEQ;
+                               return;
                        }
                }
        }
-       return;
- error:
-       io->index = io->size;
-       io->error = 1;
 }
 
 static void tagz_decode_struct(struct ez_blobio *io, const ez_blob_desc *desc, void *p) {
        uint8_t h = blobio_readb(io);
        uint32_t stag;
        int tc = (h >> EZT_TAGSHIFT) & 3;
-       int cc = (h >> EZT_COUNTSHIFT) & 3; 
+       int cc = (h >> EZT_COUNTSHIFT) & 3;
 
        // must be a struct with tc=11 and cc!=11
-       if ((h & EZT_TYPE) != EZT_STRUCT)
-               goto error;
-       if (tc != 3 || cc == 3)
-               goto error;
+       if ((h & EZT_TYPE) != EZT_STRUCT
+               || tc != 3 || cc == 3) {
+               io->error = EILSEQ;
+               return;
+       }
 
        // unused, but could check against desc->bd_id
        stag = blobio_readi(io, cc);
        stag = stag;
 
        tagz_decode_fields(io, desc, p);
-
-       return;
- error:
-       io->error = 1;
 }
 
 int ez_tagz_decode_raw(const ez_blob_desc *desc, const ez_blob *blob, void *p) {
        struct ez_blobio io = {
-               .size = blob->size,
-               .data = blob->data,
+               .size = blob->eb_size,
+               .data = blob->eb_data,
                .mode = BLOBIO_READ
        };
 
        ez_blob_init(desc, p);
        tagz_decode_struct(&io, desc, p);
 
-       if (io.error) {
-               ez_blob_free_raw(desc, p);
-               return -1;
-       }
-
-       return 0;
+       if (!io.error)
+               return 0;
+       
+       ez_blob_free_raw(desc, p);
+       return io.error;
 }
 
 void *ez_tagz_decode(const ez_blob_desc *desc, const ez_blob *blob) {
@@ -578,7 +613,7 @@ static void tagz_dump_struct(struct ez_blobio *io, int depth) {
        uint8_t h = blobio_readb(io);
        uint32_t stag;
        int tc = (h >> EZT_TAGSHIFT) & 3;
-       int cc = (h >> EZT_COUNTSHIFT) & 3; 
+       int cc = (h >> EZT_COUNTSHIFT) & 3;
 
        char s[depth+1];
        memset(s, ' ', depth);
@@ -601,17 +636,17 @@ static void tagz_dump_struct(struct ez_blobio *io, int depth) {
        stag = blobio_readi(io, cc);
 
        printf("%s[%02x] %d = {\n", s, h, stag);
-       
+
        tagz_dump_fields(io, depth+4);
 }
 
 static void tagz_dump_fields(struct ez_blobio *io, int depth) {
        int h;
-       
+
        char s[depth+1];
        memset(s, ' ', depth);
        s[depth] = 0;
-               
+
        // read fields
        while ((h = blobio_readb(io)) != EZT_END) {
                uint32_t ftag;
@@ -633,8 +668,9 @@ static void tagz_dump_fields(struct ez_blobio *io, int depth) {
                        if (use_count) {
                                void *v = blobio_take(io, fcount * (1<<sc));
                                if (v) {
+                                       ez_blob blob = { .eb_size = fcount * (1<<sc), .eb_data = v };
                                        printf("%s[%02x] integer-%-2d %d [%ld] = {\n", s, h, (1<<sc)*8, ftag, fcount);
-                                       blobio_dumphex(v, fcount * (1<<sc), s);
+                                       ez_blob_dump(&blob, s);
                                        printf("%s}\n", s);
                                }
                        } else {
@@ -686,8 +722,8 @@ static void tagz_dump_fields(struct ez_blobio *io, int depth) {
  */
 void ez_tagz_dump(ez_blob *blob) {
        struct ez_blobio io = {
-               .data = blob->data,
-               .size = blob->size,
+               .data = blob->eb_data,
+               .size = blob->eb_size,
                .mode = BLOBIO_READ
        };
 
index 28a94d2..057ff49 100644 (file)
 */
 
 /*
-  Implements an XDR serialiser.
+  Implements a XDR serialiser using native byte ordering.
 
   Each end must have identical descriptor tables.
 
-  STRING or POINTER files which may be NULL must be marked as
-  EZ_BLOB_NULLABLE.  They are implemented as optional fields.
+  Support matrix:
+
+                    single  singlep vector  vectorp list    listp   cstring
+                             ?null 1                         ?null   ?null
+
+      int8             x               x 2                             x 3
+      int16            x               x 2
+      int32            x               x
+      int64            x               x
+      float            x               x
+      double           x               x
+      struct           x       x                       x       x
+
+  1. Nullable fields are optional and must be properly annotated.
+     They are written as Optional-Data.
+  2. These are written as Variable-Length Opaque Data rather than
+     Variable-Length Array.  The size is the byte size not
+     the element count.
+  3. These are 8-bit strings (e.g. UTF-8), not necessarily ASCII as
+     specified by String.
 
 */
 
@@ -31,6 +49,8 @@
 #include <string.h>
 #include <stdint.h>
 
+#include <errno.h>
+
 #include "ez-list.h"
 
 #include "ez-blob.h"
@@ -42,76 +62,176 @@ static __inline__ size_t roundup(size_t v) {
        return (v+3) & ~3;
 }
 
+/*
+  Arrays of byte and short are encoded as 'opquate data' for
+  compactness, the count is the number of bytes.
+
+  Arrays of int32/int64/float/double are written as arrays,
+  the count is the number of elements.
+*/
+
+static void xdrio_writev(struct ez_blobio *io, const ez_blob *data, size_t elshift) {
+       size_t size = data->eb_size << elshift;
+       int32_t count = elshift <= 1 ? size : data->eb_size;
+       void *v;
+
+       blobio_write32(io, count);
+       v = blobio_reserve(io, size);
+       if (v) {
+               memcpy(v, data->eb_data, size);
+               blobio_write_align(io, 4);
+       }
+}
+
+static void xdrio_readv(struct ez_blobio *io, ez_blob *data, size_t elshift) {
+       int32_t count = blobio_readi32(io);
+       size_t size = elshift <= 1 ? count : count << elshift;
+       void *src = blobio_take(io, size);
+
+       if (src) {
+               void *mem = malloc(size);
+
+               if (mem) {
+                       memcpy(mem, src, size);
+                       data->eb_data = mem;
+                       data->eb_size = size >> elshift;
+                       blobio_read_align(io, 4);
+               } else {
+                       io->error = ENOMEM;
+               }
+       }
+}
+
+static void xdrio_writes(struct ez_blobio *io, const char *v) {
+       if (v) {
+               int32_t size = strlen(v);
+
+               blobio_write32(io, size);
+               blobio_write(io, v, size);
+               blobio_write_align(io, 4);
+       } else {
+               io->error = EINVAL;
+       }
+}
+
+static void xdrio_reads(struct ez_blobio *io, char **v) {
+       uint32_t size = blobio_readi32(io);
+       void *src = blobio_take(io, size);
+
+       if (src) {
+               char *mem = malloc(size+1);
+
+               if (mem) {
+                       memcpy(mem, src, size);
+                       mem[size] = 0;
+                       *v = mem;
+                       blobio_read_align(io, 4);
+               } else {
+                       io->error = ENOMEM;
+               }
+       }
+}
+
 static void xdrn_encode_raw(struct ez_blobio *io, const ez_blob_desc *desc, const void *p) {
        for (int i=0,dlen=desc->bd_length;i<dlen;i++) {
                const ez_blob_desc *d = &desc[i+1];
                const void *v = (p + d->bd_offset);
 
                switch (d->bd_type) {
-               case EZ_BLOB_INT8:
+                       /*
+                        * Primitive x 1
+                        */
+               case EZ_BLOB_SINGLE | EZ_BLOB_INT8:
                        blobio_write32(io, *(uint8_t *)v);
                        break;
-               case EZ_BLOB_INT16:
+               case EZ_BLOB_SINGLE | EZ_BLOB_INT16:
                        blobio_write32(io, *(uint16_t *)v);
                        break;
-               case EZ_BLOB_INT32:
+               case EZ_BLOB_SINGLE | EZ_BLOB_INT32:
                        blobio_write32(io, *(uint32_t *)v);
                        break;
-               case EZ_BLOB_INT64:
+               case EZ_BLOB_SINGLE | EZ_BLOB_INT64:
                        blobio_write64(io, *(uint64_t *)v);
                        break;
-               case EZ_BLOB_FLOAT32:
+               case EZ_BLOB_SINGLE | EZ_BLOB_FLOAT32:
                        blobio_writef(io, *(float *)v);
                        break;
-               case EZ_BLOB_FLOAT64:
+               case EZ_BLOB_SINGLE | EZ_BLOB_FLOAT64:
                        blobio_writed(io, *(double *)v);
                        break;
-               case EZ_BLOB_STRING | EZ_BLOB_NULLABLE:
-                       if (!*(char **)v) {
+
+                       /*
+                        * Primitive x array
+                        */
+               case EZ_BLOB_VECTOR | EZ_BLOB_INT8:
+               case EZ_BLOB_VECTOR | EZ_BLOB_INT16:
+               case EZ_BLOB_VECTOR | EZ_BLOB_INT32:
+               case EZ_BLOB_VECTOR | EZ_BLOB_INT64:
+               case EZ_BLOB_VECTOR | EZ_BLOB_FLOAT32:
+               case EZ_BLOB_VECTOR | EZ_BLOB_FLOAT64:
+                       xdrio_writev(io, (const ez_blob *)v, (d->bd_type & EZ_BLOB_SIZE));
+                       break;
+
+                       /*
+                        * C string byte
+                        */
+               case EZ_BLOB_CSTRING | EZ_BLOB_INT8 | EZ_BLOB_ISNULLABLE:
+                       if (!*(const char **)v) {
                                blobio_write32(io, 0);
                                break;
                        }
                        blobio_write32(io, 1);
                        // falls through
-               case EZ_BLOB_STRING: {
-                       int32_t size = strlen(*(char **)v);
-
-                       blobio_write32(io, size);
-                       blobio_write(io, *(char **)v, size);
-                       blobio_write_align(io, 4);
+               case EZ_BLOB_CSTRING | EZ_BLOB_INT8:
+                       xdrio_writes(io, *(const char **)v);
                        break;
-               }
-               case EZ_BLOB_BLOB: {
-                       uint32_t size = ((ez_blob *)v)->size;
 
-                       blobio_write32(io, size);
-                       blobio_write(io, ((ez_blob *)v)->data, size);
-                       blobio_write_align(io, 4);
-                       break;
-               }
-               case EZ_BLOB_STRUCT:
+                       /*
+                        * Structure x 1
+                        */
+               case EZ_BLOB_STRUCT | EZ_BLOB_SINGLEP | EZ_BLOB_ISNULLABLE:
+                       if (!*(const void **)v) {
+                               blobio_write32(io, 0);
+                               break;
+                       }
+                       blobio_write32(io, 1);
+                       // falls through
+               case EZ_BLOB_STRUCT | EZ_BLOB_SINGLEP:
+                       v = *(const void **)v;
+                       if (!v) {
+                               io->error = EINVAL;
+                               return;
+                       }
+                       // falls through
+               case EZ_BLOB_STRUCT | EZ_BLOB_SINGLE:
                        xdrn_encode_raw(io, d->bd_table, v);
                        break;
-               case EZ_BLOB_POINTER | EZ_BLOB_NULLABLE:
-                       if (!*(void **)v) {
+
+                       /*
+                        * Structure x list
+                        */
+               case EZ_BLOB_STRUCT | EZ_BLOB_LISTP | EZ_BLOB_ISNULLABLE:
+                       if (!*(const void **)v) {
                                blobio_write32(io, 0);
                                break;
                        }
                        blobio_write32(io, 1);
                        // falls through
-               case EZ_BLOB_POINTER:
-                       xdrn_encode_raw(io, d->bd_table, *(void **)v);
-                       break;
-               case EZ_BLOB_LIST:
+               case EZ_BLOB_STRUCT | EZ_BLOB_LISTP:
+                       v = *(const void **)v;
+                       if (!v) {
+                               io->error = EINVAL;
+                               return;
+                       }
+                       // falls through
+               case EZ_BLOB_STRUCT | EZ_BLOB_LIST:
                        blobio_write32(io, ez_list_size((ez_list *)v));
-                       for (ez_node *w = ez_list_head((ez_list *)v), *n = ez_node_succ(w);
-                            n;
-                            w = n, n=ez_node_succ(n)) {
+                       for (ez_node *w = ez_list_head((ez_list *)v), *n = ez_node_succ(w); n; w = n, n=ez_node_succ(n)) {
                                xdrn_encode_raw(io, d->bd_table, w);
                        }
                        break;
                default:
-                       io->error = 1;
+                       io->error = ENOTSUP;
                        return;
                }
        }
@@ -128,8 +248,8 @@ size_t ez_xdrn_size(const ez_blob_desc *d, const void *p) {
 
 int ez_xdrn_encode_raw(const ez_blob_desc *d, const void *p, ez_blob *blob) {
        struct ez_blobio io = {
-               .data = blob->data,
-               .size = blob->size,
+               .data = blob->eb_data,
+               .size = blob->eb_size,
                .mode = BLOBIO_WRITE_FIXED
        };
 
@@ -146,135 +266,142 @@ int ez_xdrn_encode(const ez_blob_desc *d, const void *p, ez_blob *blob) {
        xdrn_encode_raw(&io, d, p);
 
        if (!io.error) {
-               blob->size = io.index;
-               blob->data = io.data;
+               blob->eb_size = io.index;
+               blob->eb_data = io.data;
                return 0;
        }
 
-       free(blob->data);
-       
-       return -1;
+       free(io.data);
+       blob->eb_data = NULL;
+       blob->eb_size = 0;
+
+       return io.error;
 }
 
 static void xdrn_decode_raw(struct ez_blobio *io, const ez_blob_desc *desc, void *p) {
-       ez_blob_init(desc, p);
-       
        for (int i=0,dlen=desc->bd_length;i<dlen;i++) {
                const ez_blob_desc *d = &desc[i+1];
                void *v = (p + d->bd_offset);
 
                switch (d->bd_type) {
-               case EZ_BLOB_INT8:
+                       /*
+                        * Primitive x 1
+                        */
+               case EZ_BLOB_SINGLE | EZ_BLOB_INT8:
                        *(uint8_t *)v = blobio_readi32(io);
                        break;
-               case EZ_BLOB_INT16:
+               case EZ_BLOB_SINGLE | EZ_BLOB_INT16:
                        *(uint16_t *)v = blobio_readi32(io);
                        break;
-               case EZ_BLOB_INT32:
+               case EZ_BLOB_SINGLE | EZ_BLOB_INT32:
                        *(uint32_t *)v = blobio_readi32(io);
                        break;
-               case EZ_BLOB_INT64:
+               case EZ_BLOB_SINGLE | EZ_BLOB_INT64:
                        *(uint64_t *)v = blobio_readi64(io);
                        break;
-               case EZ_BLOB_FLOAT32:
+               case EZ_BLOB_SINGLE | EZ_BLOB_FLOAT32:
                        *(float *)v = blobio_readf(io);
                        break;
-               case EZ_BLOB_FLOAT64:
+               case EZ_BLOB_SINGLE | EZ_BLOB_FLOAT64:
                        *(double *)v = blobio_readd(io);
                        break;
-               case EZ_BLOB_STRING | EZ_BLOB_NULLABLE:
+
+                       /*
+                        * Primitive x array
+                        */
+               case EZ_BLOB_VECTOR | EZ_BLOB_INT8:
+               case EZ_BLOB_VECTOR | EZ_BLOB_INT16:
+               case EZ_BLOB_VECTOR | EZ_BLOB_INT32:
+               case EZ_BLOB_VECTOR | EZ_BLOB_INT64:
+               case EZ_BLOB_VECTOR | EZ_BLOB_FLOAT32:
+               case EZ_BLOB_VECTOR | EZ_BLOB_FLOAT64:
+                       xdrio_readv(io, (ez_blob *)v, (d->bd_type & EZ_BLOB_SIZE));
+                       break;
+
+                       /*
+                        * C string byte
+                        */
+               case EZ_BLOB_CSTRING | EZ_BLOB_INT8 | EZ_BLOB_ISNULLABLE:
                        if (blobio_readi32(io) == 0)
                                break;
                        // falls through
-               case EZ_BLOB_STRING: {
-                       uint32_t size = blobio_readi32(io);
-                       void *src = blobio_take(io, size);
-
-                       if (src) {
-                               char *mem = malloc(size+1);
-
-                               if (!mem)
-                                       goto error;
-
-                               memcpy(mem, src, size);
-                               mem[size] = 0;
-                               *(char **)v = mem;
-                               blobio_read_align(io, 4);
-                       }
+               case EZ_BLOB_CSTRING | EZ_BLOB_INT8:
+                       xdrio_reads(io, (char **)v);
                        break;
-               }
-               case EZ_BLOB_BLOB: {
-                       uint32_t size = blobio_readi32(io);
-                       void *src = blobio_take(io, size);
 
-                       if (src) {
-                               char *mem = malloc(size);
-
-                               if (!mem)
-                                       goto error;
-
-                               memcpy(mem, src, size);
-                               ((ez_blob *)v)->data = mem;
-                               ((ez_blob *)v)->size = size;
-                               blobio_read_align(io, 4);
+                       /*
+                        * Structure x 1
+                        */
+               case EZ_BLOB_STRUCT | EZ_BLOB_SINGLEP | EZ_BLOB_ISNULLABLE:
+                       if (blobio_readi32(io) == 0)
+                               break;
+                       // falls through
+               case EZ_BLOB_STRUCT | EZ_BLOB_SINGLEP:
+                       *(void **)v = ez_blob_alloc(d->bd_table);
+                       v = *(void **)v;
+                       if (!v) {
+                               io->error = ENOMEM;
+                               return;
                        }
-                       break;
-               }
-               case EZ_BLOB_STRUCT:
+                       // falls through
+               case EZ_BLOB_STRUCT | EZ_BLOB_SINGLE:
                        xdrn_decode_raw(io, d->bd_table, v);
                        break;
-               case EZ_BLOB_POINTER | EZ_BLOB_NULLABLE:
+
+                       /*
+                        * Structure x list
+                        */
+               case EZ_BLOB_STRUCT | EZ_BLOB_LISTP | EZ_BLOB_ISNULLABLE:
                        if (blobio_readi32(io) == 0)
                                break;
-               case EZ_BLOB_POINTER: {
-                       void *mem = malloc(d->bd_table->bd_offset);
-
-                       if (!mem)
-                               goto error;
-
-                       *(void **)v = mem;
-                       xdrn_decode_raw(io, d->bd_table, mem);
-                       break;
-               }
-               case EZ_BLOB_LIST: {
+                       // falls through
+               case EZ_BLOB_STRUCT | EZ_BLOB_LISTP:
+                       *(ez_list **)v = malloc(sizeof(ez_list));
+                       v = *(void **)v;
+                       if (!v) {
+                               io->error = ENOMEM;
+                               return;
+                       }
+                       ez_list_init((ez_list *)v);
+                       // falls through
+               case EZ_BLOB_STRUCT | EZ_BLOB_LIST: {
                        uint32_t count = blobio_readi32(io);
 
                        for (int j=0;j<count;j++) {
-                               void *node = malloc(d->bd_table->bd_offset);
-
-                               if (!node)
-                                       goto error;
-
-                               xdrn_decode_raw(io, d->bd_table, node);
-                               ez_list_addtail((ez_list *)v, node);
+                               void *node = ez_blob_alloc(d->bd_table);
+
+                               if (node) {
+                                       xdrn_decode_raw(io, d->bd_table, node);
+                                       ez_list_addtail((ez_list *)v, node);
+                               } else {
+                                       io->error = ENOMEM;
+                                       return;
+                               }
                        }
                        break;
                }
                default:
-                       goto error;
+                       io->error = EILSEQ;
+                       return;
                }
        }
-       return;
- error:
-       io->index = io->size;
-       io->error = 1;
 }
 
 int ez_xdrn_decode_raw(const ez_blob_desc *desc, const ez_blob *blob, void *p) {
        struct ez_blobio io = {
-               .size = blob->size,
-               .data = blob->data,
+               .size = blob->eb_size,
+               .data = blob->eb_data,
                .mode = BLOBIO_READ
        };
 
+       ez_blob_init(desc, p);
        xdrn_decode_raw(&io, desc, p);
 
-       if (io.error) {
-               ez_blob_free_raw(desc, p);
-               return -1;
-       }
-
-       return 0;
+       if (!io.error)
+               return 0;
+       
+       ez_blob_free_raw(desc, p);
+       return io.error;
 }
 
 void *ez_xdrn_decode(const ez_blob_desc *desc, const ez_blob *blob) {
index 66b7856..683ac4e 100644 (file)
--- a/ez-blob.c
+++ b/ez-blob.c
@@ -30,29 +30,52 @@ void ez_blob_free_raw(const ez_blob_desc *desc, void *p) {
        for (int i=0, dlen = desc->bd_length; i < dlen; i++) {
                const ez_blob_desc *d = &desc[i+1];
                void *v = p + d->bd_offset;
+               int st = d->bd_type & EZ_BLOB_STORAGE;
+               int dt = d->bd_type & EZ_BLOB_TYPE;
                
-               switch (d->bd_type & EZ_BLOB_TYPE) {
-               case EZ_BLOB_STRING:
-                       free(((char **)v)[0]);
+               switch (st) {
+               case EZ_BLOB_SINGLE:
+                       if (dt == EZ_BLOB_STRUCT)
+                               ez_blob_free_raw(d->bd_table, v);
+                       break;
+               case EZ_BLOB_SINGLEP:
+                       v = *(void **)v;
+                       if (v) {
+                               if (dt == EZ_BLOB_STRUCT)
+                                       ez_blob_free_raw(d->bd_table, v);
+                               free(v);
+                       }
                        break;
-               case EZ_BLOB_BLOB:
-                       free(((struct ez_blob *)v)->data);
+               case EZ_BLOB_CSTRING:
+                       free(((char **)v)[0]);
                        break;
-               case EZ_BLOB_STRUCT:
-                       ez_blob_free_raw(d->bd_table, v);
+               case EZ_BLOB_VECTOR:
+                       if (dt == EZ_BLOB_STRUCT) {
+                               // FIXME: free elements
+                       }
+                       free(((struct ez_blob *)v)->eb_data);
                        break;
-               case EZ_BLOB_POINTER:
-                       ez_blob_free(d->bd_table, ((void **)v)[0]);
+               case EZ_BLOB_VECTORP:
+                       v = *(void **)v;
+                       if (v) {
+                               if (dt == EZ_BLOB_STRUCT) {
+                                       // FIXME: free elements
+                               }
+                               free(((struct ez_blob *)v)->eb_data);
+                       }
                        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)) {
+               case EZ_BLOB_LIST:
+                       for (ez_node *w = ez_list_head((ez_list *)v), *n = ez_node_succ(w); n; w = n, n=ez_node_succ(n))
                                ez_blob_free(d->bd_table, w);
+                       break;
+               case EZ_BLOB_LISTP:
+                       v = *(void **)v;
+                       if (v) {
+                               for (ez_node *w = ez_list_head((ez_list *)v), *n = ez_node_succ(w); n; w = n, n=ez_node_succ(n))
+                                       ez_blob_free(d->bd_table, w);
+                               free(v);
                        }
                        break;
-               }
                default:
                        break;
                }
@@ -72,11 +95,18 @@ void ez_blob_init(const ez_blob_desc *desc, void *p) {
        for (int i=0, dlen = desc->bd_length; i < dlen; i++) {
                const ez_blob_desc *d = &desc[i+1];
                void *v = p + d->bd_offset;
+               int st = d->bd_type & EZ_BLOB_STORAGE;
+               int dt = d->bd_type & EZ_BLOB_TYPE;
 
-               if (d->bd_type == EZ_BLOB_LIST)
+               switch (st) {
+               case EZ_BLOB_SINGLE:
+                       if (dt == EZ_BLOB_STRUCT)
+                               ez_blob_init(d->bd_table, v);
+                       break;
+               case EZ_BLOB_LIST:
                        ez_list_init((ez_list *)v);
-               else if (d->bd_type == EZ_BLOB_STRUCT)
-                       ez_blob_init(d->bd_table, v);
+                       break;
+               }
        }
 }
 
index 6ee3f05..f316bd6 100644 (file)
--- a/ez-blob.h
+++ b/ez-blob.h
@@ -29,9 +29,8 @@
  * including nested and linked structures.
  *
  * A structure is described by an array of ez_blob_desc.  The first
- * entry must be of type EZ_BLOB_PK, it's 'offset' must be the size
- * of the structure, and .u.count must be the number of fields
- * following.
+ * entry must have bd_offset=sizeof(structure), and bd_length=the
+ * number of field annotats which follow.
  * 
  * As an example:
  * 
  */
 typedef struct ez_blob_desc {
        unsigned short bd_type;         // type of field
-       unsigned short bd_id;           // used to tag encoded entries (currently unused)
+       unsigned short bd_id;           // used to tag encoded entries
        unsigned int bd_offset;         // offset into structure, if type == EZ_BLOB_PK then this is sizeof(struct)
        union {
-               const struct ez_blob_desc *table;// embedded structure or pointer
-               unsigned int length;
-       } u;
-#define bd_table u.table
-#define bd_length u.length
+               const struct ez_blob_desc *bd_table;// embedded structure or pointer
+               unsigned int bd_length;
+       };
 } ez_blob_desc;
 
 /**
  * Field type
  */
 typedef enum ez_blob_desc_type {
-       EZ_BLOB_INT8,
+       /* Data type */
+       EZ_BLOB_INT8,                   // (type & 3) = log2(element size)
        EZ_BLOB_INT16,
        EZ_BLOB_INT32,
        EZ_BLOB_INT64,
-       EZ_BLOB_FLOAT32,
+       EZ_BLOB_FLOAT32 = 6,
        EZ_BLOB_FLOAT64,
-       EZ_BLOB_STRING,                 // normal c strring
-       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_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_NULLABLE = 0x8000,      // Pointer may be NULL for STRING, POINTER
-       EZ_BLOB_TYPE = 0xff,            // Type mask
-       EZ_BLOB_END
+       EZ_BLOB_STRUCT = 15,            // bd_table points to another descriptor list
+
+       /* Data Storage */
+       EZ_BLOB_SINGLE = 0x00,          // the value at bd_offset
+       EZ_BLOB_SINGLEP = 0x10,         // pointer to value at bd_offset
+       EZ_BLOB_CSTRING = 0x20,         // 0-terminated string pointer
+       
+       EZ_BLOB_VECTOR = 0x40,          // ez_blob at bd_offset
+       EZ_BLOB_VECTORP = 0x50,         // pointer to ez_blob at bd_offset
+       EZ_BLOB_LIST = 0x60,            // ez_list at bd_offset
+       EZ_BLOB_LISTP = 0x70,           // pointer to ez_list at bd_offset
+
+       /* Options */
+       EZ_BLOB_ISNULLABLE = (1<<8),
+
+       /* meta */
+       EZ_BLOB_SIZE = 0x03,            // size mask for int or float types
+       EZ_BLOB_TYPE = 0x0f,            // Type mask
+       EZ_BLOB_STORAGE = 0xf0,         // Storage mask
+
+       EZ_BLOB_MAGIC = 0x10f5
 } ez_blob_desc_type;
 
 /** 
  * This is compatible with lmdb MDB_val
  */
 typedef struct ez_blob {
-       size_t size;
-       void *data;
+       size_t eb_size;
+       union {
+               void *eb_data;
+               uint8_t eb_data8;
+               uint16_t eb_data16;
+               uint32_t eb_data32;
+               uint64_t eb_data64;
+               float *eb_float;
+               double *eb_double;
+       };
 } ez_blob;
 
-#define EZ_BLOB_START(s, id, len) { EZ_BLOB_PK, 0, sizeof(s), .u.length = len }
-#define EZ_BLOB_INT8(s, id, f) { EZ_BLOB_INT8, id, offsetof(s, f) }
-#define EZ_BLOB_INT16(s, id, f) { EZ_BLOB_INT16, id, offsetof(s, f) }
-#define EZ_BLOB_INT32(s, id, f) { EZ_BLOB_INT32, id, offsetof(s, f) }
-#define EZ_BLOB_INT64(s, id, f) { EZ_BLOB_INT64, id, offsetof(s, f) }
-#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_STRING_NULL(s, id, f) { EZ_BLOB_STRING | EZ_BLOB_NULLABLE, 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_POINTER_NULL(s, id, f, other) { EZ_BLOB_POINTER | EZ_BLOB_NULLABLE, 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_START(s, id, len) { EZ_BLOB_MAGIC, id, sizeof(s), .bd_length = len }
+
+#define EZ_BLOB_INT8(s, id, f) { EZ_BLOB_INT8 | EZ_BLOB_SINGLE, id, offsetof(s, f) }
+#define EZ_BLOB_INT16(s, id, f) { EZ_BLOB_INT16 | EZ_BLOB_SINGLE, id, offsetof(s, f) }
+#define EZ_BLOB_INT32(s, id, f) { EZ_BLOB_INT32 | EZ_BLOB_SINGLE, id, offsetof(s, f) }
+#define EZ_BLOB_INT64(s, id, f) { EZ_BLOB_INT64 | EZ_BLOB_SINGLE, id, offsetof(s, f) }
+#define EZ_BLOB_FLOAT32(s, id, f) { EZ_BLOB_FLOAT32 | EZ_BLOB_SINGLE, id, offsetof(s, f) }
+#define EZ_BLOB_FLOAT64(s, id, f) { EZ_BLOB_FLOAT64 | EZ_BLOB_SINGLE, id, offsetof(s, f) }
+
+#define EZ_BLOB_INT8V(s, id, f) { EZ_BLOB_INT8 | EZ_BLOB_VECTOR, id, offsetof(s, f) }
+#define EZ_BLOB_INT16V(s, id, f) { EZ_BLOB_INT16 | EZ_BLOB_VECTOR, id, offsetof(s, f) }
+#define EZ_BLOB_INT32V(s, id, f) { EZ_BLOB_INT32 | EZ_BLOB_VECTOR, id, offsetof(s, f) }
+#define EZ_BLOB_INT64V(s, id, f) { EZ_BLOB_INT64 | EZ_BLOB_VECTOR, id, offsetof(s, f) }
+#define EZ_BLOB_FLOAT32V(s, id, f) { EZ_BLOB_FLOAT32 | EZ_BLOB_VECTOR, id, offsetof(s, f) }
+#define EZ_BLOB_FLOAT64V(s, id, f) { EZ_BLOB_FLOAT64 | EZ_BLOB_VECTOR, id, offsetof(s, f) }
+
+#define EZ_BLOB_STRING(s, id, f) { EZ_BLOB_INT8 | EZ_BLOB_CSTRING, id, offsetof(s, f) }
+#define EZ_BLOB_STRINGN(s, id, f) { EZ_BLOB_INT8 | EZ_BLOB_CSTRING | EZ_BLOB_ISNULLABLE, id, offsetof(s, f) }
+
+#define EZ_BLOB_STRUCT(s, id, f, other) {  EZ_BLOB_STRUCT | EZ_BLOB_SINGLE, id, offsetof(s, f), .bd_table = other }
+#define EZ_BLOB_STRUCTP(s, id, f, other) {  EZ_BLOB_STRUCT | EZ_BLOB_SINGLEP, id, offsetof(s, f), .bd_table = other }
+#define EZ_BLOB_STRUCTPN(s, id, f, other) {  EZ_BLOB_STRUCT | EZ_BLOB_SINGLEP | EZ_BLOB_ISNULLABLE, id, offsetof(s, f), .bd_table = other }
+
+#define EZ_BLOB_LIST(s, id, f, other) { EZ_BLOB_STRUCT | EZ_BLOB_LIST, id, offsetof(s, f), .bd_table = other }
+#define EZ_BLOB_LISTPN(s, id, f, other) { EZ_BLOB_STRUCT | EZ_BLOB_LISTP | EZ_BLOB_ISNULLABLE, id, offsetof(s, f), .bd_table = other }
 
 /**
  * Allocate a new structure to hold a blob.
@@ -148,8 +176,13 @@ 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.
+ * Debug function to dump structured blob contents.
  */
 void ez_blob_print(const ez_blob_desc *d, const void *a, int depth);
 
+/**
+ * Debug/utility function to dump hexadeximal+ascii display.
+ */
+void ez_blob_dump(const ez_blob *blob, const char *prefix);
+
 #endif
index 67f93c9..8ccb92f 100644 (file)
@@ -3,14 +3,13 @@
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
-#include <ctype.h>
 
 #include <assert.h>
+#include <errno.h>
 
 #include "ez-blob.h"
 #include "ez-list.h"
 
-#include "ez-blob-blob.h"
 #include "ez-blob-tagz.h"
 #include "ez-blob-xdrn.h"
 
@@ -33,6 +32,9 @@
   With explicit knowledge of format, it should fail on corrupt meta-data.
  */
 
+static int doprint = 1;
+static int dolarge = 0;
+
 struct simple {
        char b;
        short s;
@@ -62,7 +64,7 @@ static const ez_blob_desc array_DESC[] = {
        EZ_BLOB_START(struct arrays, 0, 3),
        EZ_BLOB_INT32(struct arrays, 1, count),
        EZ_BLOB_STRING(struct arrays, 2, string),
-       EZ_BLOB_BLOB(struct arrays, 3, array),
+       EZ_BLOB_INT8V(struct arrays, 3, array),
 };
 
 struct embedded {
@@ -86,8 +88,8 @@ struct tree {
 static const ez_blob_desc tree_DESC[] = {
        EZ_BLOB_START(struct tree, 0, 3),
        EZ_BLOB_STRING(struct tree, 1, s),
-       EZ_BLOB_POINTER_NULL(struct tree, 2, left, tree_DESC),
-       EZ_BLOB_POINTER_NULL(struct tree, 3, right, tree_DESC),
+       EZ_BLOB_STRUCTPN(struct tree, 2, left, tree_DESC),
+       EZ_BLOB_STRUCTPN(struct tree, 3, right, tree_DESC),
 };
 
 struct listnode {
@@ -99,7 +101,7 @@ struct listnode {
 static const ez_blob_desc listnode_DESC[] = {
        EZ_BLOB_START(struct listnode, 0, 2),
        EZ_BLOB_STRING(struct listnode, 1, name),
-       EZ_BLOB_BLOB(struct listnode, 2, value),
+       EZ_BLOB_INT8V(struct listnode, 2, value),
 };
 
 struct list {
@@ -136,7 +138,8 @@ static const ez_blob_desc stringsize_DESC[] = {
 };
 
 static void dumphex(const char *data, size_t size, const char *prefix) {
-       blobio_dumphex(data, size, prefix);
+       ez_blob blob = { .eb_size = size, .eb_data = (void *)data };
+       ez_blob_dump(&blob, prefix);
 }
 
 static void blob_print(const ez_blob_desc *d, const void *a, int depth) {
@@ -169,13 +172,13 @@ static int blob_equals(const ez_blob_desc *desc, const void *a, const void *b) {
                case EZ_BLOB_FLOAT64:
                        e &= (*(double *)u) == (*(double *)v);
                        break;
-               case EZ_BLOB_STRING | EZ_BLOB_NULLABLE:
+
+               case EZ_BLOB_CSTRING | EZ_BLOB_INT8 | EZ_BLOB_ISNULLABLE:
                        if (!*(char **)u || !*(char **)v) {
                                e &= (*(char **)u) == (*(char **)v);
                                break;
                        }
-                       // falls through
-               case EZ_BLOB_STRING:
+               case EZ_BLOB_CSTRING | EZ_BLOB_INT8:
                        if (!*(char **)u || !*(char **)v) {
                                printf("strings can't be null\n");
                                e = 0;
@@ -183,46 +186,73 @@ static int blob_equals(const ez_blob_desc *desc, const void *a, const void *b) {
                                e &= strcmp(*(char **)u, *(char **)v) == 0;
                        }
                        break;
-               case EZ_BLOB_BLOB: {
+
+               case EZ_BLOB_VECTOR | EZ_BLOB_INT8:
+               case EZ_BLOB_VECTOR | EZ_BLOB_INT16:
+               case EZ_BLOB_VECTOR | EZ_BLOB_INT32:
+               case EZ_BLOB_VECTOR | EZ_BLOB_INT64:
+               case EZ_BLOB_VECTOR | EZ_BLOB_FLOAT32:
+               case EZ_BLOB_VECTOR | EZ_BLOB_FLOAT64: {
                        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;
+                       e &= ua->eb_size == va->eb_size
+                               && memcmp(ua->eb_data, va->eb_data, ua->eb_size) == 0;
                        break;
                }
-               case EZ_BLOB_STRUCT:
-                       e &= blob_equals(d->bd_table, u, v);
-                       break;
-               case EZ_BLOB_POINTER | EZ_BLOB_NULLABLE:
+                       
+               case EZ_BLOB_STRUCT | EZ_BLOB_SINGLEP | EZ_BLOB_ISNULLABLE:
                        if (!*(void **)u || !*(void **)v) {
                                e &= *(void **)u == *(void **)v;
                                break;
                        }
-                       // falls through
-               case EZ_BLOB_POINTER:
-                       e &= blob_equals(d->bd_table, *(void **)u, *(void **)v);
+               case EZ_BLOB_STRUCT | EZ_BLOB_SINGLEP:
+                       u = *(void **)u;
+                       v = *(void **)v;
+               case EZ_BLOB_STRUCT | EZ_BLOB_SINGLE:
+                       if (!u || !v) {
+                               printf("structs p can't be null\n");
+                               e = 0;
+                       } else {
+                               e &= blob_equals(d->bd_table, u, v);
+                       }
                        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);
+
+
+               case EZ_BLOB_STRUCT | EZ_BLOB_LISTP | EZ_BLOB_ISNULLABLE:
+                       if (!*(void **)u || !*(void **)v) {
+                               e &= *(void **)u == *(void **)v;
+                               break;
                        }
+               case EZ_BLOB_STRUCT | EZ_BLOB_LISTP:
+                       u = *(void **)u;
+                       v = *(void **)v;
+               case EZ_BLOB_STRUCT | EZ_BLOB_LIST:
+                       if (!u || !v) {
+                               printf("lists p can't be null\n");
+                               e = 0;
+                       } else {
+                               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);
+                                    e && 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;
-               }
+
                default:
                        printf("cmp unknown field type=%d id=%d\n", d->bd_type, d->bd_id);
                        e = 0;
                        break;
                }
+
                if (!e) {
                        printf("cmp failed in struct %d at field %d type %04x\n", desc->bd_id, d->bd_id, d->bd_type);
                        abort();
@@ -239,8 +269,6 @@ struct test_funcs {
        int (*blob_decode_raw)(const ez_blob_desc *desc, const ez_blob *blob, void *p);
 };
 
-static int doprint = 1;
-
 static void test_basics(const ez_blob_desc *d, void *src, struct test_funcs *funcs) {
        ez_blob blob, blob2;
        void *t;
@@ -254,14 +282,16 @@ static void test_basics(const ez_blob_desc *d, void *src, struct test_funcs *fun
        }
        
        res = funcs->blob_encode(d, src, &blob);
-       if (res != 0) {
+       if (res == ENOMEM) {
                printf(" encode failed: probably out of memory, skipped testing this\n");
                return;
        }
+       assert(res == 0);
+
        if (doprint)
-               dumphex(blob.data, blob.size, "serial: ");
+               dumphex(blob.eb_data, blob.eb_size, "serial: ");
        else
-               dumphex(blob.data, blob.size < 256 ? blob.size : 256, "header: ");
+               dumphex(blob.eb_data, blob.eb_size < 256 ? blob.eb_size : 256, "header: ");
                
        // check decode equals
        t = funcs->blob_decode(d, &blob);
@@ -276,9 +306,9 @@ static void test_basics(const ez_blob_desc *d, void *src, struct test_funcs *fun
        // check encode-decoded equals source
        res = funcs->blob_encode(d, t, &blob2);
        assert(res == 0);
-       assert(blob.size == blob2.size);
-       assert(memcmp(blob.data, blob2.data, blob2.size) == 0);
-       free(blob2.data);
+       assert(blob.eb_size == blob2.eb_size);
+       assert(memcmp(blob.eb_data, blob2.eb_data, blob2.eb_size) == 0);
+       free(blob2.eb_data);
 
        ez_blob_free(d, t);
 
@@ -295,7 +325,7 @@ static void test_basics(const ez_blob_desc *d, void *src, struct test_funcs *fun
                assert((tmp[i + 32 + dsize] & 0xff) == 0xbe);
        }
        ez_blob_free_raw(d, tmp+32);
-       free(blob.data);
+       free(blob.eb_data);
 }
 
 static void test_simple(struct test_funcs *funcs) {
@@ -313,8 +343,8 @@ static void test_arrays(struct test_funcs *funcs) {
        struct arrays src = {
                32,
                "Bob was here.",
-               .array.size = 7,
-               .array.data = data
+               .array.eb_size = 7,
+               .array.eb_data = data
        };
        const ez_blob_desc *d = array_DESC;
 
@@ -329,7 +359,7 @@ static void test_struct(struct test_funcs *funcs) {
                        -1, -1, -1, -1, 1337.4004, 6.022E23,
                },
                {
-                       55, "Jane was there.", .array.size = 10, .array.data = data
+                       55, "Jane was there.", .array.eb_size = 10, .array.eb_data = data
                }
        };
        const ez_blob_desc *d = embed_DESC;
@@ -359,9 +389,9 @@ static void test_list(struct test_funcs *funcs) {
                .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" },
+               { .name = "node 0", .value.eb_size = 5, .value.eb_data = "data0" },
+               { .name = "node 1", .value.eb_size = 5, .value.eb_data = "data1" },
+               { .name = "node 2", .value.eb_size = 5, .value.eb_data = "data2" },
        };
 
        list.name = "a list";
@@ -404,8 +434,9 @@ static void test_stringsize(struct test_funcs *funcs) {
                size_t size = sizes[i];
 
                // only tagz supports >32 bit counts
-               if (size >= 0x100000000ULL
-                   &&  strcmp(funcs->name, "tagz") != 0) {
+               if (!dolarge
+                   || (size >= 0x100000000ULL
+                       && strcmp(funcs->name, "tagz") != 0)) {
                        printf("skip string size %zd\n", size);
                        continue;
                }
@@ -430,19 +461,13 @@ static void test_stringsize(struct test_funcs *funcs) {
 // however they should not crash, cause leaks, or valgrind oddness.
 
 static void test_corrupt_bit(struct test_funcs *funcs) {
-       // this is definitely not robust
-       if (strcmp(funcs->name, "legacy") == 0) {
-               printf("Not supposed to be safe\n");
-               return;
-       }
-       
        char data[10] = { 1, 2, 3, 4, 5, 6, 7, 1, 2, 3 };
        struct embedded src = {
                {
                        -1, -1, -1, -1, 1337.4004, 6.022E23,
                },
                {
-                       55, "Jane was there.", .array.size = 10, .array.data = data
+                       55, "Jane was there.", .array.eb_size = 10, .array.eb_data = data
                }
        };
        const ez_blob_desc *d = embed_DESC;
@@ -458,8 +483,8 @@ static void test_corrupt_bit(struct test_funcs *funcs) {
        int good = 0, bad = 0;
        res = funcs->blob_encode(d, &src, &blob);
        assert(res == 0);
-       for (int i=0;i<blob.size;i++) {
-               uint8_t *data = blob.data;
+       for (int i=0;i<blob.eb_size;i++) {
+               uint8_t *data = blob.eb_data;
                for (int j=0;j<8;j++) {
                        data[i] ^= (1<<j);
                        dst = funcs->blob_decode(d, &blob);
@@ -473,24 +498,18 @@ static void test_corrupt_bit(struct test_funcs *funcs) {
                        }
                }
        }
-       free(blob.data);
+       free(blob.eb_data);
        printf(" info: %d failed, %d decoded - but not important\n", bad, good);
 }
 
 static void test_corrupt_byte(struct test_funcs *funcs) {
-       // this is definitely not robust
-       if (strcmp(funcs->name, "legacy") == 0) {
-               printf("Not supposed to be safe\n");
-               return;
-       }
-       
        char data[10] = { 1, 2, 3, 4, 5, 6, 7, 1, 2, 3 };
        struct embedded src = {
                {
                        -1, -1, -1, -1, 1337.4004, 6.022E23,
                },
                {
-                       55, "Jane was there.", .array.size = 10, .array.data = data
+                       55, "Jane was there.", .array.eb_size = 10, .array.eb_data = data
                }
        };
        const ez_blob_desc *d = embed_DESC;
@@ -501,8 +520,8 @@ static void test_corrupt_byte(struct test_funcs *funcs) {
        int good = 0, bad = 0;
        res = funcs->blob_encode(d, &src, &blob);
        assert(res == 0);
-       for (int i=0;i<blob.size;i++) {
-               uint8_t *data = blob.data;
+       for (int i=0;i<blob.eb_size;i++) {
+               uint8_t *data = blob.eb_data;
                uint8_t save = data[i];
 
                for (int j=0;j<256;j++) {
@@ -517,17 +536,11 @@ static void test_corrupt_byte(struct test_funcs *funcs) {
                }
                data[i] = save;
        }
-       free(blob.data);
+       free(blob.eb_data);
        printf(" info: %d failed, %d decoded - but not important\n", bad, good);
 }
 
 struct test_funcs backends[] = {
-       {
-               "legacy",
-               ez_blob_encode,
-               ez_blob_decode,
-               ez_blob_decode_raw
-       },
        {
                "tagz",
                ez_tagz_encode,
@@ -561,6 +574,8 @@ int main(int argc, char **argv) {
        int nfunc = sizeof(backends)/sizeof(backends[0]);
        int ntest = sizeof(tests)/sizeof(tests[0]);
 
+       dolarge = (argc >= 2);
+       
        for (int i=0;i<nfunc;i++) {
                struct test_funcs *f = &backends[i];