Added a proprietary tagged (de)serialiaser.
Prepare for removal of previous serialiser.
CPPFLAGS=-I.
-CFLAGS=-Og -g -fPIC -Wall -mtune=native
+CFLAGS=-O0 -g -fPIC -Wall -mtune=native
test_LDLIBS=-lpthread -lrt
ARFLAGS=rvsUc
VERSION=2.1.99
SRCS= \
ez-bitset.c \
ez-blob.c \
+ ez-blob.c \
+ ez-blob-io.c \
ez-blob-print.c \
+ ez-blob-blob.c \
+ ez-blob-tagz.c \
+ ez-blob-xdrn.c \
ez-port.c \
ez-set.c \
ez-tree.c
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 \
ez-node.h \
ez-port.h \
$(CC) $(CFLAGS) -o $@ $< libeze.a $(test_LDLIBS)
test-bitset: libeze.a(ez-bitset.o)
-test-blob: libeze.a(ez-blob.o) libeze.a(ez-blob-print.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)
test-port: libeze.a(ez-port.o)
test-set: libeze.a(ez-set.o)
test-tree: libeze.a(ez-tree.o)
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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);
+
--- /dev/null
+/* ez-blob-io.c: Common i/o utilities for serialisers
+
+ 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 <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "ez-blob.h"
+#include "ez-blob-io.h"
+
+//#define ABORT() abort()
+#define ABORT()
+
+void *blobio_reserve(struct ez_blobio * __restrict io, size_t len) {
+ size_t size = io->size;
+ size_t to = io->index + len;
+ void *v;
+
+ if (io->error)
+ return NULL;
+
+ switch (io->mode) {
+ case BLOBIO_WRITE_ALLOC:
+ if (to > size) {
+ do {
+ size = size ? size * 2 : 256;
+ } while (to > size);
+
+ io->size = size;
+ io->data = realloc(io->data, size);
+
+ if (!io->data) {
+ io->error = 1;
+ ABORT();
+ return NULL;
+ }
+ }
+
+ v = io->index + io->data;
+ io->index = to;
+ return v;
+
+ case BLOBIO_WRITE_FIXED:
+ if (to > size) {
+ io->error = 1;
+ ABORT();
+ return NULL;
+ }
+ v = io->index + io->data;
+ io->index = to;
+ return v;
+
+ case BLOBIO_WRITE_SIZE:
+ io->index = to;
+ default:
+ return NULL;
+ }
+}
+
+void *blobio_take(struct ez_blobio * __restrict io, size_t len) {
+ size_t to = io->index + len;
+
+ if (to <= io->size) {
+ void *v = io->data + io->index;
+ io->index = to;
+ return v;
+ } else {
+ ABORT();
+ io->index = io->size;
+ io->error = 1;
+ return NULL;
+ }
+}
+
+void blobio_write_align(struct ez_blobio *io, unsigned int step) {
+ int skip = (step-1) & io->index;
+ uint8_t *v;
+
+ if (skip && (v = blobio_reserve(io, skip))) {
+ for (int i=0;i<skip;i++)
+ v[i] =0 ;
+ }
+}
+
+void blobio_read_align(struct ez_blobio *io, unsigned int step) {
+ blobio_take(io, (step-1) & io->index);
+}
+
+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');
+ }
+}
+
--- /dev/null
+/* ez-blob-io.h: Common i/o utilities for serialisers
+
+ 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/>.
+*/
+
+// Grow output as necessary
+#define BLOBIO_WRITE_ALLOC 0
+// Writing to pre-existing buffer
+#define BLOBIO_WRITE_FIXED 1
+// Calculating size only
+#define BLOBIO_WRITE_SIZE 2
+// Read mode
+#define BLOBIO_READ 3
+
+struct ez_blobio {
+ size_t index;
+ uint8_t *data;
+
+ // for read: upper limit
+ // for write: allocation size
+ size_t size;
+
+ // on read, non-zero means invalid
+ int error;
+ // io mode.
+ int mode;
+};
+
+/**
+ * Reserve write-space.
+ *
+ * For BLOBIO_WRITE_ALLOC mode this will fail if any allocation fails.
+ *
+ * For BLOBIO_WRITE_FIXED this will fail if there is a buffer
+ * overflow.
+ *
+ * For BLOBIO_WRITE_SIZE this will never fail.
+ *
+ * @param io
+ * @param len length required
+ * @return base of memory, or NULL on error.
+ */
+void *blobio_reserve(struct ez_blobio *io, size_t len);
+
+/**
+ * Take read-space.
+ *
+ * For BLOBIO_READ this will fail if there is a buffer underflow.
+ *
+ * @param io
+ * @param len Length required.
+ * @return base of memory or NULL on error.
+ */
+void *blobio_take(struct ez_blobio *io, size_t len);
+
+/**
+ * Align write output.
+ *
+ * Data is padded with 0 bytes.
+ *
+ * @param io
+ * @param step must be power of 2.
+ */
+void blobio_write_align(struct ez_blobio *io, unsigned int step);
+
+/**
+ * Align read input.
+ *
+ * @param io
+ * @param step must be power of 2.
+ */
+void blobio_read_align(struct ez_blobio *io, unsigned int step);
+
+/**
+ * Dump binary data in human-readable hex+ascii format.
+ * @todo rename ez_blob_dump(prefix, ez_blob)?
+ */
+void blobio_dumphex(const char *data, size_t size, const char *prefix);
+
+/*
+ The read and write functions read or write the type and silently
+ perform no-operation on error. The error can be checked via
+ io.error being non-zero.
+
+ @todo I don't think there's any point in making these inline if it just
+ calls an external function anyway.
+ */
+
+static __inline__ void blobio_write(struct ez_blobio *io, const void *data, size_t len) {
+ void *v = blobio_reserve(io, len);
+ if (v) memcpy(v, data, len);
+}
+
+static __inline__ void blobio_writeb(struct ez_blobio *io, uint8_t val) {
+ uint8_t *v = blobio_reserve(io, 1);
+ if (v) *v = val;
+}
+
+static __inline__ void blobio_write8(struct ez_blobio *io, uint8_t val) {
+ uint8_t *v = blobio_reserve(io, 1);
+ if (v) *v = val;
+}
+
+static __inline__ void blobio_write16(struct ez_blobio *io, uint16_t val) {
+ uint16_t *v = blobio_reserve(io, 2);
+ if (v) *v = val;
+}
+
+static __inline__ void blobio_write32(struct ez_blobio *io, uint32_t val) {
+ uint32_t *v = blobio_reserve(io, 4);
+ if (v) *v = val;
+}
+
+static __inline__ void blobio_write64(struct ez_blobio *io, uint64_t val) {
+ uint64_t *v = blobio_reserve(io, 8);
+ if (v) *v = val;
+}
+
+static __inline__ void blobio_writef(struct ez_blobio *io, float val) {
+ float *v = blobio_reserve(io, 4);
+ if (v) *v = val;
+}
+
+static __inline__ void blobio_writed(struct ez_blobio *io, double val) {
+ double *v = blobio_reserve(io, 8);
+ if (v) *v = val;
+}
+
+static __inline__ uint8_t blobio_readi8(struct ez_blobio *io) {
+ uint8_t *v = blobio_take(io, 1);
+ return v ? *v : 0;
+}
+
+static __inline__ uint16_t blobio_readi16(struct ez_blobio *io) {
+ uint16_t *v = blobio_take(io, 2);
+ return v ? *v : 0;
+}
+
+static __inline__ uint32_t blobio_readi32(struct ez_blobio *io) {
+ uint32_t *v = blobio_take(io, 4);
+ return v ? *v : 0;
+}
+
+static __inline__ uint64_t blobio_readi64(struct ez_blobio *io) {
+ uint64_t *v = blobio_take(io, 8);
+ return v ? *v : 0;
+}
+
+static __inline__ float blobio_readf(struct ez_blobio *io) {
+ float *v = blobio_take(io, 4);
+ return v ? *v : 0.0f;
+}
+
+static __inline__ double blobio_readd(struct ez_blobio *io) {
+ double *v = blobio_take(io, 8);
+ return v ? *v : 0.0;
+}
#include "ez-blob.h"
#include "ez-list.h"
-static void dumphex(const char *data, size_t size, const char *prefix) {
- for (int i=0;i<size;i+=16) {
- fputs(prefix, stdout);
- printf(" %04x:", i);
- for (int j=0;j<16;j++) {
- if (i+j<size)
- printf(" %02x", data[i+j] & 0xff);
- else
- fputs(" ", stdout);
- }
- fputs(" ", stdout);
- for (int j=0;j<16;j++) {
- if (i+j<size)
- putchar(isprint(data[i+j]) ? data[i+j] : '.');
- else
- putchar(' ');
- }
- putchar('\n');
- }
-}
+#include "ez-blob-io.h"
void ez_blob_print(const ez_blob_desc *d, const void *a, int depth) {
- int e = 1;
char x[depth+1];
+ int len = d->bd_length;
memset(x, ' ', depth);
x[depth] = 0;
-
- for (;e && (d->bd_type != EZ_BLOB_END);d++) {
+
+ 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);
- switch (d->bd_type) {
+ switch (d->bd_type & EZ_BLOB_TYPE) {
case EZ_BLOB_INT8:
printf(" .%d = %d\n", d->bd_id, *(uint8_t *)u);
break;
const struct ez_blob *ua = u;
printf(" .%d %p [%zd] = {\n", d->bd_id, ua->data, ua->size);
- dumphex(ua->data, ua->size, x);
+ blobio_dumphex(ua->data, ua->size, x);
printf("%s }\n", x);
- break; }
+ break;
+ }
case EZ_BLOB_POINTER: {
void *up = *((void **)u);
if (up) {
} else {
printf(" .%d %p\n", d->bd_id, up);
}
- break; }
+ break;
+ }
case EZ_BLOB_LIST: {
ez_list *l = (void *)u;
printf("%s},\n", x);
}
printf("%s }\n", x);
- break; }
+ break;
+ }
default:
printf("\n");
}
--- /dev/null
+/* ez-blob-tagz.c: A tagged blob 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/>.
+*/
+
+/*
+ Tagged blob encoding.
+
+ This is a streamable self-describing byte-oriented binary format.
+ It is a general purpose format and supports a super-set of the
+ ez_blob descriptor. It supports primitive and struct types and
+ sequences thereof and there is room for extension.
+
+ Each item beings with a descriptor byte, then followed by a tag id,
+ a possible count, and the payload.
+
+ xxxxttcc control byte
+
+ xxxx type code
+
+ 0 uint8 unsigned int, value zero-extended
+ 1 uint16
+ 2 uint32
+ 3 uint64
+ - reserved
+ 5 float16
+ 6 float32
+ 7 float64
+ - reserved
+ f struct
+
+ note that for int/float types, code&3 == log2 of element size in bytes
+
+ tt log2 of tag size in bytes
+
+ 0 1 byte
+ 1 2 byte
+ 2 4 byte
+ 3 reserved, no tag?
+
+ cc log2 of count size in bytes, used to indicate sequence length or non-sequence.
+
+ 0 1 byte
+ 1 2 byte
+ 2 4 byte
+ 3 none, single item follows
+
+ ff is struct-end code
+
+ A header is a control byte followed by an optional 1/2/4 byte-length tag,
+ followed by an optional 1/2/4 byte-length count.
+
+ A structure payload is a list of tagged fields until a struct-end
+ code. A structure sequence includes count of a tagged structure.
+
+ Integers can be stored in the smallest number of bytes, i.e. with
+ all leading $00 bytes removed.
+
+ Possible extension is signed types and ignoring leading $ff bytes.
+
+ez_blob requirements:
+
+ All fields must have an id.
+ The id for each field must be unique for any given struct.
+ The ids must be in incremental order.
+ Arrays must be stored at their natural size.
+
+final note:
+
+ This is a bit over-engineered.
+
+*/
+
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "ez-list.h"
+
+#include "ez-blob.h"
+#include "ez-blob-tagz.h"
+#include "ez-blob-io.h"
+
+//#define D(x) do { x; fflush(stdout); } while (0)
+#define D(x)
+
+#define EZT_INT8 0x00
+#define EZT_INT16 0x10
+#define EZT_INT32 0x20
+#define EZT_INT64 0x30
+
+#define EZT_FLOAT16 0x50
+#define EZT_FLOAT32 0x60
+#define EZT_FLOAT64 0x70
+#define EZT_STRUCT 0xf0
+
+#define EZT_TYPE 0xf0
+
+#define EZT_COUNTSHIFT 0
+#define EZT_TAGSHIFT 2
+#define EZT_DATASHIFT 4
+
+#define EZT_COUNTSIZE (0x3 << EZT_COUNTSHIFT)
+#define EZT_TAGSIZE (0x3 << EZT_TAGSHIFT)
+#define EZT_DATASIZE (0x3 << EZT_DATASHIFT)
+
+#define EZT_NOCOUNT (0x3 << EZT_COUNTSHIFT)
+
+#define EZT_END 0xff // well, end-struct
+
+static void blobio_writei(struct ez_blobio *io, int sc, uint64_t val) {
+ switch (sc) {
+ case 0:
+ blobio_write8(io, val);
+ break;
+ case 1:
+ blobio_write16(io, val);
+ break;
+ case 2:
+ blobio_write32(io, val);
+ break;
+ case 3:
+ blobio_write64(io, val);
+ break;
+ }
+}
+
+// log2 of significant bytes in value
+static int size_code(uint64_t size) {
+ if (size <= 0xff)
+ return 0x0;
+ else if (size <= 0xffff)
+ return 0x1;
+ else if (size <= 0xffffffff)
+ return 0x2;
+ else
+ return 0x3;
+}
+
+static int count_code(uint32_t size) {
+ if (size <= 0xff)
+ return 0x0;
+ else if (size <= 0xffff)
+ return 0x1;
+ else
+ return 0x2;
+}
+
+static void blobio_writet(struct ez_blobio *io, uint8_t type, uint32_t tag) {
+ int tc = size_code(tag);
+
+ blobio_writeb(io, type | (tc << EZT_TAGSHIFT));
+ blobio_writei(io, tc, tag);
+}
+
+static void tagz_encode_struct(struct ez_blobio *io, int id, const ez_blob_desc *desc, const void *p) {
+ blobio_writet(io, EZT_STRUCT | EZT_NOCOUNT, 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;
+ uint32_t count = 0;
+ const void *ptr = NULL;
+
+ switch (d->bd_type & EZ_BLOB_TYPE) {
+ case EZ_BLOB_INT8:
+ val = *(uint8_t *)v;
+ goto doint;
+ case EZ_BLOB_INT16:
+ val = *(uint16_t *)v;
+ goto doint;
+ case EZ_BLOB_INT32:
+ val = *(uint32_t *)v;
+ goto doint;
+ case EZ_BLOB_INT64:
+ val = *(uint64_t *)v;
+ doint:
+ if (val) {
+ int sc = size_code(val);
+
+ blobio_writet(io, EZT_INT8 | EZT_NOCOUNT | (sc << EZT_DATASHIFT), d->bd_id);
+ blobio_writei(io, sc, val);
+ }
+ break;
+ case EZ_BLOB_FLOAT32:
+ blobio_writet(io, EZT_FLOAT32 | EZT_NOCOUNT, d->bd_id);
+ blobio_writef(io, *(const float *)v);
+ break;
+ case EZ_BLOB_FLOAT64:
+ blobio_writet(io, EZT_FLOAT64 | EZT_NOCOUNT, d->bd_id);
+ blobio_writed(io, *(const double *)v);
+ break;
+ case EZ_BLOB_STRING:
+ ptr = *(const void **)v;
+ if (ptr) {
+ count = strlen(ptr);
+ goto doblob;
+ }
+ break;
+ case EZ_BLOB_BLOB:
+ count = ((ez_blob *)v)->size;
+ if (count) {
+ int cc;
+
+ ptr = ((ez_blob *)v)->data;
+ doblob:
+ cc = count_code(count);
+ blobio_writet(io, EZT_INT8 | (cc << EZT_COUNTSHIFT), d->bd_id);
+ blobio_writei(io, cc, count);
+ blobio_write(io, ptr, 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);
+ break;
+ case EZ_BLOB_LIST:
+ count = ez_list_size((ez_list *)v);
+ if (count) {
+ int cc = count_code(count);
+
+ blobio_writet(io, EZT_STRUCT | (cc << EZT_COUNTSHIFT), d->bd_id);
+ blobio_writei(io, cc, 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;
+ }
+
+ }
+
+ blobio_writeb(io, EZT_END);
+}
+
+size_t ez_tagz_size(const ez_blob_desc *d, const void *p) {
+ struct ez_blobio io = {
+ .mode = BLOBIO_WRITE_SIZE
+ };
+ tagz_encode_struct(&io, d->bd_id, d, p);
+
+ return io.index;
+}
+
+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,
+ .mode = BLOBIO_WRITE_FIXED
+ };
+
+ tagz_encode_struct(&io, d->bd_id, d, p);
+
+ return io.error;
+}
+
+int ez_tagz_encode(const ez_blob_desc *d, const void *p, ez_blob *blob) {
+ struct ez_blobio io = {
+ .mode = BLOBIO_WRITE_ALLOC
+ };
+
+ tagz_encode_struct(&io, d->bd_id, d, p);
+
+ if (!io.error) {
+ blob->size = io.index;
+ blob->data = io.data;
+ return 0;
+ }
+ free(blob->data);
+ blob->data = 0;
+ blob->size = 0;
+
+ return -1;
+}
+
+static int blobio_readb(struct ez_blobio *io) {
+ uint8_t *v = blobio_take(io, 1);
+ return v ? *v : EZT_END;
+}
+
+static uint64_t blobio_readi(struct ez_blobio *io, int sc) {
+ switch (sc) {
+ case 0:
+ return blobio_readi8(io);
+ case 1:
+ return blobio_readi16(io);
+ case 2:
+ return blobio_readi32(io);
+ case 3:
+ return blobio_readi64(io);
+ }
+ return 0;
+}
+
+// TODO: binary search, length is in first field
+// Alternative: if encode/decode is same order, then can just search forward from last spot
+static const ez_blob_desc *blob_desc_find(const ez_blob_desc *desc, int id) {
+ for (int i=0,dlen=desc->bd_length;i<dlen;i++) {
+ const ez_blob_desc *d = &desc[i+1];
+
+ if (d->bd_id == id)
+ return d;
+ }
+ return NULL;
+}
+
+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 void tagz_decode_fields(struct ez_blobio *io, const ez_blob_desc *desc, void *p) {
+ uint8_t h;
+
+ while ((h = blobio_readb(io)) != EZT_END) {
+ const ez_blob_desc *d;
+ uint32_t ftag;
+ uint32_t fcount = 0;
+ int sc = (h & EZT_DATASIZE) >> EZT_DATASHIFT; /* for primitives */
+
+ ftag = blobio_readi(io, (h & EZT_TAGSIZE) >> EZT_TAGSHIFT);
+ if ((h & EZT_COUNTSIZE) != EZT_NOCOUNT)
+ fcount = blobio_readi(io, (h & EZT_COUNTSIZE) >> EZT_COUNTSHIFT);
+
+ d = blob_desc_find(desc, ftag);
+
+ 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 (fcount) {
+ 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);
+ *(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);
+ ((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;
+ }
+ }
+ break;
+ case EZT_FLOAT32:
+ if (fcount) {
+ 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;
+ }
+ }
+ break;
+ case EZT_FLOAT64:
+ if (fcount) {
+ 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;
+ }
+ }
+ break;
+ case EZT_STRUCT:
+ if (fcount) {
+ switch (d->bd_type) {
+ case EZ_BLOB_LIST:
+ for (int i=0;i<fcount;i++) {
+ void *node = ez_blob_alloc(d->bd_table);
+
+ if (!node)
+ goto error;
+
+ tagz_decode_struct(io, d->bd_table, node);
+ ez_list_addtail((ez_list *)v, node);
+ }
+ 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;
+ }
+ } else {
+ // Just skip fields
+ switch (h & EZT_TYPE) {
+ case EZT_INT8:
+ case EZT_INT16:
+ case EZT_INT32:
+ case EZT_INT64:
+ case EZT_FLOAT32:
+ case EZT_FLOAT64:
+ if (fcount) {
+ blobio_take(io, fcount * (1<<sc));
+ } else {
+ blobio_take(io, (1<<sc));
+ }
+ break;
+ case EZT_STRUCT:
+ if (fcount) {
+ for (int i=0;i<fcount;i++) {
+ tagz_decode_struct(io, d->bd_table, NULL);
+ }
+ } else {
+ tagz_decode_fields(io, d->bd_table, NULL);
+ }
+ break;
+ default:
+ goto error;
+ }
+ }
+ }
+ 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;
+ //uint32_t scount = 1;
+
+ // must be a struct
+ // what about a struct list? hmm
+ if ((h & EZT_TYPE) != EZT_STRUCT) {
+ //io->error = 1;
+ return;
+ }
+
+ stag = blobio_readi(io, (h & EZT_TAGSIZE) >> EZT_TAGSHIFT);
+ stag = stag;
+ //if (stag != desc->bd_id) {
+ // ??
+ //}
+ //if (h & EZT_COUNTSIZE)
+ // count = blobio_readi(io, (h & EZT_COUNTSIZE) >> EZT_COUNTSHIFT);
+
+ // read fields
+ tagz_decode_fields(io, desc, p);
+}
+
+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,
+ .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;
+}
+
+void *ez_tagz_decode(const ez_blob_desc *desc, const ez_blob *blob) {
+ void *p = malloc(desc->bd_offset);
+
+ if (p && ez_tagz_decode_raw(desc, blob, p) != 0) {
+ free(p);
+ p = NULL;
+ }
+
+ return p;
+}
--- /dev/null
+/* ez-blob-tagz.h: A tagged blob 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/>.
+*/
+
+
+#ifndef _EZ_BLOB_TAGZ_H
+#define _EZ_BLOB_TAGZ_H
+
+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);
+int ez_tagz_encode(const ez_blob_desc *d, const void *p, ez_blob *blob);
+
+int ez_tagz_decode_raw(const ez_blob_desc *desc, const ez_blob *blob, void *p);
+void *ez_tagz_decode(const ez_blob_desc *desc, const ez_blob *blob);
+
+#endif
--- /dev/null
+/* ez-blob-xdrn.c: XDR-like serialiser using native ordering.
+
+ 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/>.
+*/
+
+/*
+ Implements an XDR serialiser.
+
+ 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.
+
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "ez-list.h"
+
+#include "ez-blob.h"
+#include "ez-blob-xdrn.h"
+#include "ez-blob-io.h"
+
+static size_t roundup(size_t v) __attribute__ ((always_inline));
+static __inline__ size_t roundup(size_t v) {
+ return (v+3) & ~3;
+}
+
+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:
+ blobio_write32(io, *(uint8_t *)v);
+ break;
+ case EZ_BLOB_INT16:
+ blobio_write32(io, *(uint16_t *)v);
+ break;
+ case EZ_BLOB_INT32:
+ blobio_write32(io, *(uint32_t *)v);
+ break;
+ case EZ_BLOB_INT64:
+ blobio_write64(io, *(uint32_t *)v);
+ break;
+ case EZ_BLOB_FLOAT32:
+ blobio_writef(io, *(float *)v);
+ break;
+ case EZ_BLOB_FLOAT64:
+ blobio_writed(io, *(double *)v);
+ break;
+ case EZ_BLOB_STRING | EZ_BLOB_NULLABLE:
+ if (!*(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);
+ 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:
+ xdrn_encode_raw(io, d->bd_table, v);
+ break;
+ case EZ_BLOB_POINTER | EZ_BLOB_NULLABLE:
+ if (!*(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:
+ 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)) {
+ xdrn_encode_raw(io, d->bd_table, w);
+ }
+ break;
+ default:
+ io->error = 1;
+ return;
+ }
+ }
+}
+
+size_t ez_xdrn_size(const ez_blob_desc *d, const void *p) {
+ struct ez_blobio io = {
+ .mode = BLOBIO_WRITE_SIZE
+ };
+ xdrn_encode_raw(&io, d, p);
+
+ return io.index;
+}
+
+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,
+ .mode = BLOBIO_WRITE_FIXED
+ };
+
+ xdrn_encode_raw(&io, d, p);
+
+ return io.error;
+}
+
+int ez_xdrn_encode(const ez_blob_desc *d, const void *p, ez_blob *blob) {
+ struct ez_blobio io = {
+ .mode = BLOBIO_WRITE_ALLOC
+ };
+
+ xdrn_encode_raw(&io, d, p);
+
+ if (!io.error) {
+ blob->size = io.index;
+ blob->data = io.data;
+ return 0;
+ }
+
+ free(blob->data);
+
+ return -1;
+}
+
+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:
+ *(uint8_t *)v = blobio_readi32(io);
+ break;
+ case EZ_BLOB_INT16:
+ *(uint16_t *)v = blobio_readi32(io);
+ break;
+ case EZ_BLOB_INT32:
+ *(uint32_t *)v = blobio_readi32(io);
+ break;
+ case EZ_BLOB_INT64:
+ *(uint64_t *)v = blobio_readi64(io);
+ break;
+ case EZ_BLOB_FLOAT32:
+ *(float *)v = blobio_readf(io);
+ break;
+ case EZ_BLOB_FLOAT64:
+ *(double *)v = blobio_readd(io);
+ break;
+ case EZ_BLOB_STRING | EZ_BLOB_NULLABLE:
+ 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);
+
+ if (!mem)
+ goto error;
+
+ memcpy(mem, src, size);
+ mem[size] = 0;
+ *(char **)v = mem;
+ blobio_read_align(io, 4);
+ }
+ 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);
+ }
+ break;
+ }
+ case EZ_BLOB_STRUCT:
+ xdrn_decode_raw(io, d->bd_table, v);
+ break;
+ case EZ_BLOB_POINTER | EZ_BLOB_NULLABLE:
+ 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: {
+ 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);
+ }
+ break;
+ }
+ default:
+ goto error;
+ }
+ }
+ 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,
+ .mode = BLOBIO_READ
+ };
+
+ xdrn_decode_raw(&io, desc, p);
+
+ if (io.error) {
+ ez_blob_free_raw(desc, p);
+ return -1;
+ }
+
+ return 0;
+}
+
+void *ez_xdrn_decode(const ez_blob_desc *desc, const ez_blob *blob) {
+ void *p = malloc(desc->bd_offset);
+
+ if (p && ez_xdrn_decode_raw(desc, blob, p) != 0) {
+ free(p);
+ p = NULL;
+ }
+
+ return p;
+}
--- /dev/null
+/* ez-blob-xdrn.c: XDR-like serialiser using native ordering.
+
+ 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/>.
+*/
+
+#ifndef _EZ_BLOB_XDRN_H
+#define _EZ_BLOB_XDRN_H
+
+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);
+int ez_xdrn_encode(const ez_blob_desc *d, const void *p, ez_blob *blob);
+
+int ez_xdrn_decode_raw(const ez_blob_desc *desc, const ez_blob *blob, void *p);
+void *ez_xdrn_decode(const ez_blob_desc *desc, const ez_blob *blob);
+
+#endif
-/* ez-blob.h: Serialising description and serialiser.
+/* ez-blob.h: Serialising description and utils.
Copyright (C) 2019 Michael Zucchi
#include "ez-blob.h"
#include "ez-list.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 *d, const void *p) {
- size_t size = 0;
-
- for (;(d->bd_type != EZ_BLOB_END);d++) {
- 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) {
- 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;
-}
-
-void ez_blob_free_raw(const ez_blob_desc *d, void *p) {
- for (;(d->bd_type != EZ_BLOB_END);d++) {
+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;
switch (d->bd_type) {
w = n, n=ez_node_succ(n)) {
ez_blob_free(d->bd_table, w);
}
- break; }
+ break;
+ }
default:
break;
}
}
}
-void *ez_blob_decode_raw(const ez_blob_desc *d, const ez_blob *blob, void *p) {
- const char *b = blob->data;
- const char *be = b + blob->size;
-
- memset(p, 0, d->bd_offset);
-
- for (;(d->bd_type != EZ_BLOB_END);d++) {
- 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) == NULL) {
- free(s);
- goto fail;
- }
- ez_list_addtail(l, s);
- b+=ss;
- }
- } else if (d->bd_type <= EZ_BLOB_LAST) {
- uint32_t ss;
- ez_blob sub;
-
- if (b + 4 > be)
- goto fail;
- memcpy(&ss, b, 4);
- b += 4;
+void ez_blob_init(const ez_blob_desc *desc, void *p) {
+ memset(p, 0, desc->bd_offset);
- sub.size = ss;
- sub.data = (char *)b;
+ for (int i=0,dlen = desc->bd_length;i<dlen;i++) {
+ const ez_blob_desc *d = &desc[i+1];
- switch (d->bd_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) == NULL)
- 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) == NULL) {
- free(s);
- goto fail;
- }
- b += ss;
- }
- ((void **)v)[0] = s;
- break; }
- default:
- break;
- }
- }
+ if (d->bd_type == EZ_BLOB_LIST)
+ ez_list_init((ez_list *)(p + d->bd_offset));
}
- if (b != be)
- goto fail;
-
- return p;
- fail:
- ez_blob_free_raw(d, p);
- return NULL;
}
-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) == NULL) {
- free(p);
- return NULL;
- }
-
+void *ez_blob_alloc(const ez_blob_desc *d) {
+ void *p = malloc(d->bd_offset);
+ if (p)
+ ez_blob_init(d, p);
return p;
}
-
-void ez_blob_encode_raw(const ez_blob_desc *d, const void *p, ez_blob *blob) {
- char *b = blob->data;
- char *be = b+blob->size;
-
- for (;(d->bd_type != EZ_BLOB_END);d++) {
- const void *v = (p + d->bd_offset);
-
- 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 if (d->bd_type <= EZ_BLOB_LAST) {
- uint32_t ss;
- struct ez_blob sub;
-
- switch (d->bd_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);
-}
-
-int ez_blob_encode(const ez_blob_desc *d, const void *p, ez_blob *blob) {
- blob->size = ez_blob_size(d, p);
- blob->data = malloc(blob->size);
-
- if (blob->data) {
- ez_blob_encode_raw(d, p, blob);
- return 0;
- }
-
- return -1;
-}
-
-/* ez-blob.h: Serialising description and serialiser.
+/* ez-blob.h: Serialising description.
Copyright (C) 2019 Michael Zucchi
#include <stddef.h>
/**
- * This is a C structure serialiser.
+ * This is a C structure annotation for implementing a serialiser.
*
* It is table driven and can describe most common C data structures,
* including nested and linked structures.
*
- * 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.
- */
-
-/**
- * An array of ez_blob_desc is passed to ez_blob_encode/decode. The first
- * entry must be of type EZ_BLOB_PK and it's 'offset' must be the size
- * of the structure. The final entry must be of type EZ_BLOB_END.
+ * 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.
*
- * As an example:
+ * As an example:
*
* typedef struct dbdisk {
* int id;
* char *type;
* } dbdisk;
*
- * Has a table such as this, using the init macros.
+ * Has a table such as this, using the init macros.
*
* static ez_blob_desc DISK_ENC[] = {
- * EZ_BLOB_START(dbdisk),
+ * EZ_BLOB_START(dbdisk, 0, 3),
* EZ_BLOB_STRING(dbdisk, 1, uuid),
* EZ_BLOB_STRING(dbdisk, 2, label),
* EZ_BLOB_STRING(dbdisk, 3, type),
- * EZ_BLOB_END(dbdisk)
* };
*
- * The 'id' field (of ez_blob_desc) is currently unused by the blob
- * serialiser but could be implemented for data versioning or partial
- * serialisation.
+ * The 'id' field and it's semantics are serialiser dependent.
*
*/
typedef struct ez_blob_desc {
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
} ez_blob_desc;
/**
EZ_BLOB_INT64,
EZ_BLOB_FLOAT32,
EZ_BLOB_FLOAT64,
- EZ_BLOB_STRING, // normall c strring
+ 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_desc_type;
void *data;
} ez_blob;
-#define EZ_BLOB_START(s) { EZ_BLOB_PK, 0, sizeof(s) }
+#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_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_END(s) { EZ_BLOB_END }
/**
- * Calculate the serialised blob size for a struct.
+ * Allocate a new structure to hold a blob.
*
- * @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
+ * The structure is initialised by ez_blob_init().
*
- * @param d descriptor table matching the struct.
- * @param p structure.
- * @param blob available target memory, size must be exactly ez_blob_size().
+ * @param d the descriptor table for the blob.
*/
-void ez_blob_encode_raw(const ez_blob_desc *d, const void *p, ez_blob *blob);
+void *ez_blob_alloc(const ez_blob_desc *d);
/**
- * Unmarshall a blob into a struct.
+ * Initialise a struct to the default state.
*
- * @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.
+ * This sets all values to zero and lists to empty.
*
- * @param b blob data
- * @param size blob size
- * @return p is returned on success. On failure NULL is returned and any internal pointers are freed.
+ * @param d descriptor table matching struct.
+ * @param p pointer to struct base.
*/
-void *ez_blob_decode_raw(const ez_blob_desc *d, const ez_blob *blob, void *p);
+void ez_blob_init(const ez_blob_desc *d, void *p);
/**
* Free struct unmarshalled by ez_blob_decode().
/**
* Free the contents of a structure, but don't free the container.
*
- * @todo should this zero the structure too?
+ * Use ez_blob_init() if you want to re-use the object.
+ *
* @param p structure to free. p will not be freed.
*/
void ez_blob_free_raw(const ez_blob_desc *d, void *p);
#include "ez-blob.h"
#include "ez-list.h"
+#include "ez-blob-blob.h"
+#include "ez-blob-tagz.h"
+#include "ez-blob-xdrn.h"
+
struct simple {
char b;
short s;
};
static const ez_blob_desc simple_DESC[] = {
- EZ_BLOB_START(struct simple),
+ EZ_BLOB_START(struct simple, 0, 5),
EZ_BLOB_INT8(struct simple, 1, b),
EZ_BLOB_INT16(struct simple, 2, s),
EZ_BLOB_INT32(struct simple, 3, a),
EZ_BLOB_FLOAT32(struct simple, 4, c),
EZ_BLOB_FLOAT64(struct simple, 5, d),
- EZ_BLOB_END(struct simple)
};
struct arrays {
};
static const ez_blob_desc array_DESC[] = {
- EZ_BLOB_START(struct arrays),
+ EZ_BLOB_START(struct arrays, 0, 2),
EZ_BLOB_STRING(struct arrays, 1, string),
EZ_BLOB_BLOB(struct arrays, 2, array),
- EZ_BLOB_END(struct arrays)
};
struct embedded {
};
static const ez_blob_desc embed_DESC[] = {
- EZ_BLOB_START(struct embedded),
+ EZ_BLOB_START(struct embedded, 0, 2),
EZ_BLOB_STRUCT(struct embedded, 1, s, simple_DESC),
EZ_BLOB_STRUCT(struct embedded, 2, a, array_DESC),
- EZ_BLOB_END(struct embedded)
};
struct tree {
};
static const ez_blob_desc tree_DESC[] = {
- EZ_BLOB_START(struct tree),
+ EZ_BLOB_START(struct tree, 0, 3),
EZ_BLOB_STRING(struct tree, 1, s),
- EZ_BLOB_POINTER(struct tree, 2, left, tree_DESC),
- EZ_BLOB_POINTER(struct tree, 3, right, tree_DESC),
- EZ_BLOB_END(struct tree)
+ EZ_BLOB_POINTER_NULL(struct tree, 2, left, tree_DESC),
+ EZ_BLOB_POINTER_NULL(struct tree, 3, right, tree_DESC),
};
struct listnode {
};
static const ez_blob_desc listnode_DESC[] = {
- EZ_BLOB_START(struct listnode),
+ EZ_BLOB_START(struct listnode, 0, 2),
EZ_BLOB_STRING(struct listnode, 1, name),
EZ_BLOB_BLOB(struct listnode, 2, value),
- EZ_BLOB_END(struct listnode)
};
struct list {
};
static const ez_blob_desc list_DESC[] = {
- EZ_BLOB_START(struct list),
+ EZ_BLOB_START(struct list, 0, 2),
EZ_BLOB_STRING(struct list, 1, name),
EZ_BLOB_LIST(struct list, 2, list, listnode_DESC),
- EZ_BLOB_END(struct list)
};
static void dumphex(const char *data, size_t size, const char *prefix) {
ez_blob_print(d, a, depth);
}
-static int blob_equals(const ez_blob_desc *d, const void *a, const void *b) {
+static int blob_equals(const ez_blob_desc *desc, const void *a, const void *b) {
int e = 1;
- for (;e && (d->bd_type != EZ_BLOB_END);d++) {
+ for (int i=0, dlen=desc->bd_length; e && i < dlen; i++) {
+ const ez_blob_desc *d = &desc[i+1];
const void *u = a + d->bd_offset;
const void *v = b + d->bd_offset;
case EZ_BLOB_FLOAT64:
e &= (*(double *)u) == (*(double *)v);
break;
+ case EZ_BLOB_STRING | EZ_BLOB_NULLABLE:
+ if (!*(char **)u || !*(char **)v) {
+ e &= (*(char **)u) == (*(char **)v);
+ break;
+ }
+ // falls through
case EZ_BLOB_STRING:
- e &= strcmp((*(char **)u), (*(char **)v)) == 0;
+ if (!*(char **)u || !*(char **)v) {
+ printf("strings can't be null\n");
+ e = 0;
+ } else {
+ e &= strcmp(*(char **)u, *(char **)v) == 0;
+ }
break;
case EZ_BLOB_BLOB: {
const struct ez_blob *ua = u;
e &= ua->size == va->size
&& memcmp(ua->data, va->data, ua->size) == 0;
- break; }
+ break;
+ }
+ case EZ_BLOB_POINTER | EZ_BLOB_NULLABLE:
+ 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);
+ break;
case EZ_BLOB_LIST: {
ez_list *ul = (void *)u;
ez_list *vl = (void *)v;
e &= blob_equals(d->bd_table, uw, vw);
}
- break; }
+ 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();
}
}
return e;
}
-static void test_basics(const ez_blob_desc *d, void *src) {
+struct test_funcs {
+ const char *name;
+ int (*blob_encode)(const ez_blob_desc *d, const void *p, ez_blob *blob);
+ void *(*blob_decode)(const ez_blob_desc *desc, const ez_blob *blob);
+ int (*blob_decode_raw)(const ez_blob_desc *desc, const ez_blob *blob, void *p);
+};
+
+static void test_basics(const ez_blob_desc *d, void *src, struct test_funcs *funcs) {
ez_blob blob, blob2;
void *t;
size_t dsize = d->bd_offset;
char tmp[dsize + 64] __attribute__ ((aligned(64)));
+ int res;
printf("source\n");
blob_print(d, src, 4);
- ez_blob_encode(d, src, &blob);
+ res = funcs->blob_encode(d, src, &blob);
+ assert(res == 0);
dumphex(blob.data, blob.size, "serial: ");
// check decode equals
- t = ez_blob_decode(d, &blob);
+ t = funcs->blob_decode(d, &blob);
assert(t != NULL);
assert(blob_equals(d, t, src));
blob_print(d, t, 4);
// check encode-decoded equals source
- ez_blob_encode(d, t, &blob2);
+ funcs->blob_encode(d, t, &blob2);
assert(blob.size == blob2.size);
assert(memcmp(blob.data, blob2.data, blob2.size) == 0);
free(blob2.data);
// check raw decode stays within bounds of struct
memset(tmp, 0xbe, sizeof(tmp));
- t = ez_blob_decode_raw(d, &blob, &tmp[32]);
+ funcs->blob_decode_raw(d, &blob, &tmp[32]);
//assert(t != NULL);
//assert(blob_equals(d, tmp+32, src));
dumphex(tmp, sizeof(tmp), "object: ");
free(blob.data);
}
-static void test_simple(void) {
+static void test_simple(struct test_funcs *funcs) {
struct simple src = {
'z', 0xff0f, 1, 3.14159f, 5644941221133
};
const ez_blob_desc *d = simple_DESC;
- test_basics(d, &src);
+ printf("test simple\n");
+ test_basics(d, &src, funcs);
}
-static void test_arrays(void) {
+static void test_arrays(struct test_funcs *funcs) {
char data[7] = { 1, 2, 3, 4, 5, 6, 7 };
struct arrays src = {
32,
};
const ez_blob_desc *d = array_DESC;
- test_basics(d, &src);
+ printf("test arrays\n");
+ test_basics(d, &src, funcs);
}
-static void test_rawtree(void) {
+static void test_rawtree(struct test_funcs *funcs) {
struct tree src[6] = {
{ &src[1], &src[3], "root" },
{ NULL, &src[2], "left" },
};
const ez_blob_desc *d = tree_DESC;
- test_basics(d, &src);
+ printf("test raw tree\n");
+ test_basics(d, &src, funcs);
}
-static void test_list(void) {
+static void test_list(struct test_funcs *funcs) {
struct list list = {
.name = "a list",
.list = EZ_INIT_LIST(list.list)
const ez_blob_desc *d = list_DESC;
- test_basics(d, &list);
+ printf("test list\n");
+ test_basics(d, &list, funcs);
}
+struct test_funcs backends[] = {
+ {
+ "blob legacy",
+ ez_blob_encode,
+ ez_blob_decode,
+ ez_blob_decode_raw
+ },
+ {
+ "tagz",
+ ez_tagz_encode,
+ ez_tagz_decode,
+ ez_tagz_decode_raw
+ },
+ {
+ "xdrn",
+ ez_xdrn_encode,
+ ez_xdrn_decode,
+ ez_xdrn_decode_raw
+ },
+};
+
int main(int argc, char **argv) {
- test_simple();
- test_arrays();
- test_rawtree();
- test_list();
+ for (int i=0;i<3;i++) {
+ struct test_funcs *f = &backends[i];
+ printf("* **********************************************************************\n");
+ printf("test: %s\n", f->name);
+ fflush(stdout);
+
+ test_simple(f);
+ test_arrays(f);
+ test_rawtree(f);
+ test_list(f);
+ }
}