summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/app/prototest/Makefile.inc96
-rw-r--r--src/app/prototest/main.cc79
-rw-r--r--src/lib/binn.cc3372
-rw-r--r--src/lib/capnp-c/capn-malloc.cc424
-rw-r--r--src/lib/capnp-c/capn-stream.cc217
-rw-r--r--src/lib/capnp-c/capn.cc1117
-rw-r--r--src/lib/mpack/mpack.cc6440
-rw-r--r--src/lib/mpmalloc.cc42
-rw-r--r--src/lib/nanopb/pb_common.cc97
-rw-r--r--src/lib/nanopb/pb_decode.cc1528
-rw-r--r--src/lib/nanopb/pb_encode.cc893
-rw-r--r--src/lib/ubjson/ubjr.c525
-rw-r--r--src/lib/ubjson/ubjrw.c169
-rw-r--r--src/lib/ubjson/ubjw.c626
-rw-r--r--src/lib/xdr.cc188
-rw-r--r--src/lib/xdr16.cc216
16 files changed, 16029 insertions, 0 deletions
diff --git a/src/app/prototest/Makefile.inc b/src/app/prototest/Makefile.inc
new file mode 100644
index 0000000..c088f6b
--- /dev/null
+++ b/src/app/prototest/Makefile.inc
@@ -0,0 +1,96 @@
+ifeq (${aspectc}, 1)
+ CXX_FLAGS += --Xweaver -asrc/app/prototest/prototest.ah --Xcompiler
+endif
+
+ifeq (${prototest_bench_energy}, 1)
+ loop ?= 1
+ COMMON_FLAGS += -DPROTOTEST_BENCH_ENERGY
+endif
+
+ifeq (${prototest_bench_cycles}, 1)
+ loop ?= 0
+ arch_drivers += ,counter
+ COMMON_FLAGS += -DPROTOTEST_BENCH_CYCLES
+endif
+
+ifneq (${prototest_include_global}, )
+ COMMON_FLAGS += -DPROTOTEST_INCLUDE_GLOBAL=${prototest_include_global}
+endif
+
+ifneq (${prototest_include_local}, )
+ COMMON_FLAGS += -DPROTOTEST_INCLUDE_LOCAL=${prototest_include_local}
+endif
+
+ifeq (${prototest_arduinojson}, 1)
+ COMMON_FLAGS += -DPROTOTEST_ARDUINOJSON
+endif
+
+ifeq (${prototest_binn}, 1)
+ COMMON_FLAGS += -DPROTOTEST_BINN
+ CXX_TARGETS += src/lib/binn.cc
+endif
+
+ifeq (${prototest_capnproto_c}, 1)
+ COMMON_FLAGS += -DPROTOTEST_CAPNPROTO_C
+ CXX_TARGETS += src/app/prototest/capnp_c_bench.capnp.cc
+ CXX_TARGETS += src/lib/capnp-c/capn.cc
+ CXX_TARGETS += src/lib/capnp-c/capn-malloc.cc
+ CXX_TARGETS += src/lib/capnp-c/capn-stream.cc
+ INCLUDES += -Iinclude/lib/capnp-c
+endif
+
+ifeq (${prototest_manualjson}, 1)
+ COMMON_FLAGS += -DPROTOTEST_MANUALJSON
+endif
+
+ifeq (${prototest_modernjson}, 1)
+ COMMON_FLAGS += -DPROTOTEST_MODERNJSON
+ ostream = 1
+endif
+
+ifeq (${prototest_mpack}, 1)
+ COMMON_FLAGS += -DPROTOTEST_MPACK
+ CXX_TARGETS += src/lib/mpack/mpack.cc
+ INCLUDES += -Iinclude/lib/mpack
+endif
+
+ifeq (${prototest_nanopb}, 1)
+ COMMON_FLAGS += -DPROTOTEST_NANOPB
+ CXX_TARGETS += src/app/prototest/nanopbbench.pb.cc src/lib/nanopb/pb_common.cc
+ CXX_TARGETS += src/lib/nanopb/pb_decode.cc src/lib/nanopb/pb_encode.cc
+ INCLUDES += -Iinclude/lib/nanopb
+endif
+
+ifeq (${prototest_ubjson}, 1)
+ COMMON_FLAGS += -DPROTOTEST_UBJSON
+ C_TARGETS += src/lib/ubjson/ubjr.c src/lib/ubjson/ubjw.c
+ INCLUDES += -Iinclude/lib/ubjson
+endif
+
+ifeq (${prototest_xdr}, 1)
+ COMMON_FLAGS += -DPROTOTEST_XDR
+ CXX_TARGETS += src/lib/xdr.cc
+endif
+
+ifeq (${prototest_xdr16}, 1)
+ COMMON_FLAGS += -DPROTOTEST_XDR16
+ CXX_TARGETS += src/lib/xdr16.cc
+endif
+
+# Don't try to make .capnp from .capnp.c
+%.capnp: ;
+
+# Don't try to make .proto from .proto.c
+%.proto: ;
+
+%.pb.cc: %.proto
+ ${QUIET}protoc --plugin=protoc-gen-nanopb=${HOME}/var/ess/protocol-modeling/nanopb/generator/protoc-gen-nanopb --nanopb_out=. src/app/prototest/nanopbbench.proto
+ ${QUIET}mv src/app/prototest/nanopbbench.pb.c src/app/prototest/nanopbbench.pb.cc
+ ${QUIET}cp src/app/prototest/nanopbbench.pb.cc /tmp
+ ${QUIET}sed -i 's!src/app/prototest/!!' src/app/prototest/nanopbbench.pb.cc
+ ${QUIET}sed -i 's!\#include "src/app/prototest/nanopb.pb.h"!!' src/app/prototest/nanopbbench.pb.h
+
+%.capnp.cc: %.capnp
+ ${QUIET}capnp compile -oc $<
+ ${QUIET}mv $<.c $<.cc
+ ${QUIET}cp $<.cc /tmp
diff --git a/src/app/prototest/main.cc b/src/app/prototest/main.cc
new file mode 100644
index 0000000..7fc897e
--- /dev/null
+++ b/src/app/prototest/main.cc
@@ -0,0 +1,79 @@
+#include "arch.h"
+#include "driver/gpio.h"
+#include "driver/stdout.h"
+
+#ifdef PROTOTEST_BENCH_CYCLES
+#include "driver/counter.h"
+#endif
+
+#ifdef PROTOTEST_ARDUINOJSON
+#include "lib/ArduinoJson.h"
+#endif
+#ifdef PROTOTEST_BINN
+#include "lib/binn.h"
+#endif
+#ifdef PROTOTEST_CAPNPROTO_C
+#include <capnp_c.h>
+#include "capnp_c_bench.capnp.h"
+#endif
+#ifdef PROTOTEST_MANUALJSON
+#include "object/stdbuf.h"
+#endif
+#ifdef PROTOTEST_MODERNJSON
+#include "lib/modernjson/json.h"
+#endif
+#ifdef PROTOTEST_MPACK
+#include "mpack.h"
+#endif
+#ifdef PROTOTEST_NANOPB
+#include <pb.h>
+#include "nanopbbench.pb.h"
+#include <pb_encode.h>
+#include <pb_decode.h>
+#endif
+#ifdef PROTOTEST_UBJSON
+#include "ubj.h"
+#endif
+#ifdef PROTOTEST_XDR
+#include "lib/xdr.h"
+#endif
+#ifdef PROTOTEST_XDR16
+#include "lib/xdr16.h"
+#endif
+
+#include <stdint.h>
+
+#ifdef PROTOTEST_INCLUDE_GLOBAL
+#include "prototest_global.cc.inc"
+#endif
+
+void loop(void)
+{
+ //static uint16_t ts = 0;
+ //ts++;
+
+#ifdef PROTOTEST_INCLUDE_LOCAL
+#include "prototest_local.cc.inc"
+#endif
+
+}
+
+int main(void)
+{
+ arch.setup();
+ gpio.setup();
+ kout.setup();
+
+ //gpio.led_on(0);
+ kout << "Hello, World!" << endl;
+
+#if defined(PROTOTEST_BENCH_CYCLES) and !defined(PROTOTEST_ARCH_esp8266)
+ while (1) {
+ loop();
+ }
+#else
+ arch.idle_loop();
+#endif
+
+ return 0;
+}
diff --git a/src/lib/binn.cc b/src/lib/binn.cc
new file mode 100644
index 0000000..b7e4839
--- /dev/null
+++ b/src/lib/binn.cc
@@ -0,0 +1,3372 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <memory.h>
+#include "lib/binn.h"
+
+#ifdef MULTIPASS_TRACE_MALLOC
+#include "lib/mpmalloc.h"
+#else
+#define mpmalloc malloc
+#define mprealloc realloc
+#define mpfree free
+#endif
+
+#define UNUSED(x) (void)(x)
+#define round(dbl) dbl >= 0.0 ? (int)(dbl + 0.5) : ((dbl - (double)(int)dbl) <= -0.5 ? (int)dbl : (int)(dbl - 0.5))
+
+// magic number: 0x1F 0xb1 0x22 0x1F => 0x1FB1221F or 0x1F22B11F
+// because the BINN_STORAGE_NOBYTES (binary 000) may not have so many sub-types (BINN_STORAGE_HAS_MORE = 0x10)
+#define BINN_MAGIC 0x1F22B11F
+
+#define MAX_BINN_HEADER 9 // [1:type][4:size][4:count]
+#define MIN_BINN_SIZE 3 // [1:type][1:size][1:count]
+#define CHUNK_SIZE 256 // 1024
+
+#define BINN_STRUCT 1
+#define BINN_BUFFER 2
+
+void* (*malloc_fn)(size_t len) = 0;
+void* (*realloc_fn)(void *ptr, size_t len) = 0;
+void (*free_fn)(void *ptr) = 0;
+
+/***************************************************************************/
+
+#if defined(__alpha__) || defined(__hppa__) || defined(__mips__) || defined(__powerpc__) || defined(__sparc__)
+#define BINN_ONLY_ALIGNED_ACCESS
+#elif ( defined(__arm__) || defined(__aarch64__) ) && !defined(__ARM_FEATURE_UNALIGNED)
+#define BINN_ONLY_ALIGNED_ACCESS
+#endif
+
+#if defined(_WIN32)
+#define BIG_ENDIAN 0x1000
+#define LITTLE_ENDIAN 0x0001
+#define BYTE_ORDER LITTLE_ENDIAN
+#elif defined(__APPLE__)
+/* macros already defined */
+#elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__)
+#include <sys/endian.h>
+#elif defined(_AIX)
+#include <sys/machine.h>
+#else
+#include <endian.h>
+#endif
+
+#ifndef BYTE_ORDER
+#error "BYTE_ORDER not defined"
+#endif
+#ifndef BIG_ENDIAN
+#error "BIG_ENDIAN not defined"
+#endif
+#ifndef LITTLE_ENDIAN
+#error "LITTLE_ENDIAN not defined"
+#endif
+#if BIG_ENDIAN == LITTLE_ENDIAN
+#error "BIG_ENDIAN == LITTLE_ENDIAN"
+#endif
+#if BYTE_ORDER!=BIG_ENDIAN && BYTE_ORDER!=LITTLE_ENDIAN
+#error "BYTE_ORDER not supported"
+#endif
+
+typedef unsigned short int u16;
+typedef unsigned int u32;
+typedef unsigned long long int u64;
+
+BINN_PRIVATE void copy_be16(u16 *pdest, u16 *psource) {
+#if BYTE_ORDER == LITTLE_ENDIAN
+ unsigned char *source = (unsigned char *) psource;
+ unsigned char *dest = (unsigned char *) pdest;
+ dest[0] = source[1];
+ dest[1] = source[0];
+#else // if BYTE_ORDER == BIG_ENDIAN
+#ifdef BINN_ONLY_ALIGNED_ACCESS
+ if (psource % 2 == 0){ // address aligned to 16 bit
+ *pdest = *psource;
+ } else {
+ unsigned char *source = (unsigned char *) psource;
+ unsigned char *dest = (unsigned char *) pdest;
+ dest[0] = source[0]; // indexes are the same
+ dest[1] = source[1];
+ }
+#else
+ *pdest = *psource;
+#endif
+#endif
+}
+
+BINN_PRIVATE void copy_be32(u32 *pdest, u32 *psource) {
+#if BYTE_ORDER == LITTLE_ENDIAN
+ unsigned char *source = (unsigned char *) psource;
+ unsigned char *dest = (unsigned char *) pdest;
+ dest[0] = source[3];
+ dest[1] = source[2];
+ dest[2] = source[1];
+ dest[3] = source[0];
+#else // if BYTE_ORDER == BIG_ENDIAN
+#ifdef BINN_ONLY_ALIGNED_ACCESS
+ if (psource % 4 == 0){ // address aligned to 32 bit
+ *pdest = *psource;
+ } else {
+ unsigned char *source = (unsigned char *) psource;
+ unsigned char *dest = (unsigned char *) pdest;
+ dest[0] = source[0]; // indexes are the same
+ dest[1] = source[1];
+ dest[2] = source[2];
+ dest[3] = source[3];
+ }
+#else
+ *pdest = *psource;
+#endif
+#endif
+}
+
+BINN_PRIVATE void copy_be64(u64 *pdest, u64 *psource) {
+#if BYTE_ORDER == LITTLE_ENDIAN
+ unsigned char *source = (unsigned char *) psource;
+ unsigned char *dest = (unsigned char *) pdest;
+ int i;
+ for (i=0; i < 8; i++) {
+ dest[i] = source[7-i];
+ }
+#else // if BYTE_ORDER == BIG_ENDIAN
+#ifdef BINN_ONLY_ALIGNED_ACCESS
+ if (psource % 8 == 0){ // address aligned to 64 bit
+ *pdest = *psource;
+ } else {
+ unsigned char *source = (unsigned char *) psource;
+ unsigned char *dest = (unsigned char *) pdest;
+ int i;
+ for (i=0; i < 8; i++) {
+ dest[i] = source[i]; // indexes are the same
+ }
+ }
+#else
+ *pdest = *psource;
+#endif
+#endif
+}
+
+/***************************************************************************/
+
+#ifndef WIN32
+#define stricmp strcasecmp
+#define strnicmp strncasecmp
+#endif
+
+/***************************************************************************/
+
+void APIENTRY binn_set_alloc_functions(void* (*new_malloc)(size_t), void* (*new_realloc)(void*,size_t), void (*new_free)(void*)) {
+
+ malloc_fn = new_malloc;
+ realloc_fn = new_realloc;
+ free_fn = new_free;
+
+}
+
+/***************************************************************************/
+
+BINN_PRIVATE void check_alloc_functions() {
+
+ if (malloc_fn == 0) malloc_fn = &mpmalloc;
+ if (realloc_fn == 0) realloc_fn = &mprealloc;
+ if (free_fn == 0) free_fn = &mpfree;
+
+}
+
+/***************************************************************************/
+
+BINN_PRIVATE void * binn_malloc(int size) {
+ check_alloc_functions();
+ return malloc_fn(size);
+}
+
+/***************************************************************************/
+
+BINN_PRIVATE void * binn_memdup(void *src, int size) {
+ void *dest;
+
+ if (src == NULL || size <= 0) return NULL;
+ dest = binn_malloc(size);
+ if (dest == NULL) return NULL;
+ memcpy(dest, src, size);
+ return dest;
+
+}
+
+/***************************************************************************/
+
+BINN_PRIVATE size_t strlen2(char *str) {
+
+ if (str == NULL) return 0;
+ return strlen(str);
+
+}
+
+/***************************************************************************/
+
+int APIENTRY binn_create_type(int storage_type, int data_type_index) {
+ if (data_type_index < 0) return -1;
+ if ((storage_type < BINN_STORAGE_MIN) || (storage_type > BINN_STORAGE_MAX)) return -1;
+ if (data_type_index < 16)
+ return storage_type | data_type_index;
+ else if (data_type_index < 4096) {
+ storage_type |= BINN_STORAGE_HAS_MORE;
+ storage_type <<= 8;
+ data_type_index >>= 4;
+ return storage_type | data_type_index;
+ } else
+ return -1;
+}
+
+/***************************************************************************/
+
+BOOL APIENTRY binn_get_type_info(int long_type, int *pstorage_type, int *pextra_type) {
+ int storage_type, extra_type;
+ BOOL retval=TRUE;
+
+again:
+
+ if (long_type < 0) {
+ goto loc_invalid;
+ } else if (long_type <= 0xff) {
+ storage_type = long_type & BINN_STORAGE_MASK;
+ extra_type = long_type & BINN_TYPE_MASK;
+ } else if (long_type <= 0xffff) {
+ storage_type = long_type & BINN_STORAGE_MASK16;
+ storage_type >>= 8;
+ extra_type = long_type & BINN_TYPE_MASK16;
+ extra_type >>= 4;
+ } else if (long_type & BINN_STORAGE_VIRTUAL) {
+ //storage_type = BINN_STORAGE_VIRTUAL;
+ //extra_type = xxx;
+ long_type &= 0xffff;
+ goto again;
+ } else {
+loc_invalid:
+ storage_type = -1;
+ extra_type = -1;
+ retval = FALSE;
+ }
+
+ if (pstorage_type) *pstorage_type = storage_type;
+ if (pextra_type) *pextra_type = extra_type;
+
+ return retval;
+
+}
+
+/***************************************************************************/
+
+BOOL APIENTRY binn_create(binn *item, int type, int size, void *pointer) {
+ BOOL retval=FALSE;
+
+ switch (type) {
+ case BINN_LIST:
+ case BINN_MAP:
+ case BINN_OBJECT:
+ break;
+ default:
+ goto loc_exit;
+ }
+
+ if ((item == NULL) || (size < 0)) goto loc_exit;
+ if (size < MIN_BINN_SIZE) {
+ if (pointer) goto loc_exit;
+ else size = 0;
+ }
+
+ memset(item, 0, sizeof(binn));
+
+ if (pointer) {
+ item->pre_allocated = TRUE;
+ item->pbuf = pointer;
+ item->alloc_size = size;
+ } else {
+ item->pre_allocated = FALSE;
+ if (size == 0) size = CHUNK_SIZE;
+ pointer = binn_malloc(size);
+ if (pointer == 0) return INVALID_BINN;
+ item->pbuf = pointer;
+ item->alloc_size = size;
+ }
+
+ item->header = BINN_MAGIC;
+ //item->allocated = FALSE; -- already zeroed
+ item->writable = TRUE;
+ item->used_size = MAX_BINN_HEADER; // save space for the header
+ item->type = type;
+ //item->count = 0; -- already zeroed
+ item->dirty = TRUE; // the header is not written to the buffer
+
+ retval = TRUE;
+
+loc_exit:
+ return retval;
+
+}
+
+/***************************************************************************/
+
+binn * APIENTRY binn_new(int type, int size, void *pointer) {
+ binn *item;
+
+ item = (binn*) binn_malloc(sizeof(binn));
+
+ if (binn_create(item, type, size, pointer) == FALSE) {
+ free_fn(item);
+ return NULL;
+ }
+
+ item->allocated = TRUE;
+ return item;
+
+}
+
+/*************************************************************************************/
+
+BOOL APIENTRY binn_create_list(binn *list) {
+
+ return binn_create(list, BINN_LIST, 0, NULL);
+
+}
+
+/*************************************************************************************/
+
+BOOL APIENTRY binn_create_map(binn *map) {
+
+ return binn_create(map, BINN_MAP, 0, NULL);
+
+}
+
+/*************************************************************************************/
+
+BOOL APIENTRY binn_create_object(binn *object) {
+
+ return binn_create(object, BINN_OBJECT, 0, NULL);
+
+}
+
+/***************************************************************************/
+
+binn * APIENTRY binn_list() {
+ return binn_new(BINN_LIST, 0, 0);
+}
+
+/***************************************************************************/
+
+binn * APIENTRY binn_map() {
+ return binn_new(BINN_MAP, 0, 0);
+}
+
+/***************************************************************************/
+
+binn * APIENTRY binn_object() {
+ return binn_new(BINN_OBJECT, 0, 0);
+}
+
+/*************************************************************************************/
+
+BOOL APIENTRY binn_load(void *data, binn *value) {
+
+ if ((data == NULL) || (value == NULL)) return FALSE;
+ memset(value, 0, sizeof(binn));
+ value->header = BINN_MAGIC;
+ //value->allocated = FALSE; -- already zeroed
+ //value->writable = FALSE;
+
+ if (binn_is_valid(data, &value->type, &value->count, &value->size) == FALSE) return FALSE;
+ value->ptr = data;
+ return TRUE;
+
+}
+
+/*************************************************************************************/
+
+binn * APIENTRY binn_open(void *data) {
+ binn *item;
+
+ item = (binn*) binn_malloc(sizeof(binn));
+
+ if (binn_load(data, item) == FALSE) {
+ free_fn(item);
+ return NULL;
+ }
+
+ item->allocated = TRUE;
+ return item;
+
+}
+
+/***************************************************************************/
+
+BINN_PRIVATE int binn_get_ptr_type(void *ptr) {
+
+ if (ptr == NULL) return 0;
+
+ switch (*(unsigned int *)ptr) {
+ case BINN_MAGIC:
+ return BINN_STRUCT;
+ default:
+ return BINN_BUFFER;
+ }
+
+}
+
+/***************************************************************************/
+
+BOOL APIENTRY binn_is_struct(void *ptr) {
+
+ if (ptr == NULL) return FALSE;
+
+ if ((*(unsigned int *)ptr) == BINN_MAGIC) {
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+
+}
+
+/***************************************************************************/
+
+BINN_PRIVATE int CalcAllocation(int needed_size, int alloc_size) {
+ int calc_size;
+
+ calc_size = alloc_size;
+ while (calc_size < needed_size) {
+ calc_size <<= 1; // same as *= 2
+ //calc_size += CHUNK_SIZE; -- this is slower than the above line, because there are more reallocations
+ }
+ return calc_size;
+
+}
+
+/***************************************************************************/
+
+BINN_PRIVATE BOOL CheckAllocation(binn *item, int add_size) {
+ int alloc_size;
+ void *ptr;
+
+ if (item->used_size + add_size > item->alloc_size) {
+ if (item->pre_allocated) return FALSE;
+ alloc_size = CalcAllocation(item->used_size + add_size, item->alloc_size);
+ ptr = realloc_fn(item->pbuf, alloc_size);
+ if (ptr == NULL) return FALSE;
+ item->pbuf = ptr;
+ item->alloc_size = alloc_size;
+ }
+
+ return TRUE;
+
+}
+
+/***************************************************************************/
+
+#if BYTE_ORDER == BIG_ENDIAN
+
+BINN_PRIVATE int get_storage_size(int storage_type) {
+
+ switch (storage_type) {
+ case BINN_STORAGE_NOBYTES:
+ return 0;
+ case BINN_STORAGE_BYTE:
+ return 1;
+ case BINN_STORAGE_WORD:
+ return 2;
+ case BINN_STORAGE_DWORD:
+ return 4;
+ case BINN_STORAGE_QWORD:
+ return 8;
+ default:
+ return 0;
+ }
+
+}
+
+#endif
+
+/***************************************************************************/
+
+BINN_PRIVATE unsigned char * AdvanceDataPos(unsigned char *p, unsigned char *plimit) {
+ unsigned char byte;
+ int storage_type, DataSize;
+
+ if (p > plimit) return 0;
+
+ byte = *p; p++;
+ storage_type = byte & BINN_STORAGE_MASK;
+ if (byte & BINN_STORAGE_HAS_MORE) p++;
+
+ switch (storage_type) {
+ case BINN_STORAGE_NOBYTES:
+ //p += 0;
+ break;
+ case BINN_STORAGE_BYTE:
+ p ++;
+ break;
+ case BINN_STORAGE_WORD:
+ p += 2;
+ break;
+ case BINN_STORAGE_DWORD:
+ p += 4;
+ break;
+ case BINN_STORAGE_QWORD:
+ p += 8;
+ break;
+ case BINN_STORAGE_BLOB:
+ if (p + sizeof(int) - 1 > plimit) return 0;
+ copy_be32((u32*)&DataSize, (u32*)p);
+ p += 4 + DataSize;
+ break;
+ case BINN_STORAGE_CONTAINER:
+ if (p > plimit) return 0;
+ DataSize = *((unsigned char*)p);
+ if (DataSize & 0x80) {
+ if (p + sizeof(int) - 1 > plimit) return 0;
+ copy_be32((u32*)&DataSize, (u32*)p);
+ DataSize &= 0x7FFFFFFF;
+ }
+ DataSize--; // remove the type byte already added before
+ p += DataSize;
+ break;
+ case BINN_STORAGE_STRING:
+ if (p > plimit) return 0;
+ DataSize = *((unsigned char*)p);
+ if (DataSize & 0x80) {
+ if (p + sizeof(int) - 1 > plimit) return 0;
+ copy_be32((u32*)&DataSize, (u32*)p);
+ DataSize &= 0x7FFFFFFF;
+ p+=4;
+ } else {
+ p++;
+ }
+ p += DataSize;
+ p++; // null terminator.
+ break;
+ default:
+ return 0;
+ }
+
+ if (p > plimit) return 0;
+
+ return p;
+
+}
+
+/***************************************************************************/
+
+BINN_PRIVATE unsigned char * SearchForID(unsigned char *p, int header_size, int size, int numitems, int id) {
+ unsigned char *plimit, *base;
+ int i, int32;
+
+ base = p;
+ plimit = p + size - 1;
+ p += header_size;
+
+ // search for the ID in all the arguments.
+ for (i = 0; i < numitems; i++) {
+ copy_be32((u32*)&int32, (u32*)p);
+ p += 4;
+ if (p > plimit) break;
+ // Compare if the IDs are equal.
+ if (int32 == id) return p;
+ // xxx
+ p = AdvanceDataPos(p, plimit);
+ if ((p == 0) || (p < base)) break;
+ }
+
+ return NULL;
+
+}
+
+/***************************************************************************/
+
+BINN_PRIVATE unsigned char * SearchForKey(unsigned char *p, int header_size, int size, int numitems, char *key) {
+ unsigned char len, *plimit, *base;
+ int i, keylen;
+
+ base = p;
+ plimit = p + size - 1;
+ p += header_size;
+
+ keylen = strlen(key);
+
+ // search for the key in all the arguments.
+ for (i = 0; i < numitems; i++) {
+ len = *((unsigned char *)p);
+ p++;
+ if (p > plimit) break;
+ // Compare if the strings are equal.
+ if (len > 0) {
+ if (strnicmp((char*)p, key, len) == 0) { // note that there is no null terminator here
+ if (keylen == len) {
+ p += len;
+ return p;
+ }
+ }
+ p += len;
+ if (p > plimit) break;
+ } else if (len == keylen) { // in the case of empty string: ""
+ return p;
+ }
+ // xxx
+ p = AdvanceDataPos(p, plimit);
+ if ((p == 0) || (p < base)) break;
+ }
+
+ return NULL;
+
+}
+
+/***************************************************************************/
+
+BINN_PRIVATE BOOL AddValue(binn *item, int type, void *pvalue, int size);
+
+/***************************************************************************/
+
+BINN_PRIVATE BOOL binn_list_add_raw(binn *item, int type, void *pvalue, int size) {
+
+ if ((item == NULL) || (item->type != BINN_LIST) || (item->writable == FALSE)) return FALSE;
+
+ //if (CheckAllocation(item, 4) == FALSE) return FALSE; // 4 bytes used for data_store and data_format.
+
+ if (AddValue(item, type, pvalue, size) == FALSE) return FALSE;
+
+ item->count++;
+
+ return TRUE;
+
+}
+
+/***************************************************************************/
+
+BINN_PRIVATE BOOL binn_object_set_raw(binn *item, char *key, int type, void *pvalue, int size) {
+ unsigned char *p, len;
+ int int32;
+
+ if ((item == NULL) || (item->type != BINN_OBJECT) || (item->writable == FALSE)) return FALSE;
+
+ if (key == NULL) return FALSE;
+ int32 = strlen(key);
+ if (int32 > 255) return FALSE;
+
+ // is the key already in it?
+ p = SearchForKey((unsigned char*)item->pbuf, MAX_BINN_HEADER, item->used_size, item->count, key);
+ if (p) return FALSE;
+
+ // start adding it
+
+ if (CheckAllocation(item, 1 + int32) == FALSE) return FALSE; // bytes used for the key size and the key itself.
+
+ p = ((unsigned char *) item->pbuf) + item->used_size;
+ len = int32;
+ *p = len;
+ p++;
+ memcpy(p, key, int32);
+ int32++; // now contains the strlen + 1 byte for the len
+ item->used_size += int32;
+
+ if (AddValue(item, type, pvalue, size) == FALSE) {
+ item->used_size -= int32;
+ return FALSE;
+ }
+
+ item->count++;
+
+ return TRUE;
+
+}
+
+/***************************************************************************/
+
+BINN_PRIVATE BOOL binn_map_set_raw(binn *item, int id, int type, void *pvalue, int size) {
+ unsigned char *p;
+
+ if ((item == NULL) || (item->type != BINN_MAP) || (item->writable == FALSE)) return FALSE;
+
+ // is the ID already in it?
+ p = SearchForID((unsigned char*)item->pbuf, MAX_BINN_HEADER, item->used_size, item->count, id);
+ if (p) return FALSE;
+
+ // start adding it
+
+ if (CheckAllocation(item, 4) == FALSE) return FALSE; // 4 bytes used for the id.
+
+ p = ((unsigned char *) item->pbuf) + item->used_size;
+ copy_be32((u32*)p, (u32*)&id);
+ item->used_size += 4;
+
+ if (AddValue(item, type, pvalue, size) == FALSE) {
+ item->used_size -= 4;
+ return FALSE;
+ }
+
+ item->count++;
+
+ return TRUE;
+
+}
+
+/***************************************************************************/
+
+BINN_PRIVATE void * compress_int(int *pstorage_type, int *ptype, void *psource) {
+ int storage_type, storage_type2, type, type2=0;
+ int64 vint;
+ uint64 vuint;
+ char *pvalue;
+#if BYTE_ORDER == BIG_ENDIAN
+ int size1, size2;
+#endif
+
+ storage_type = *pstorage_type;
+ if (storage_type == BINN_STORAGE_BYTE) return psource;
+
+ type = *ptype;
+
+ switch (type) {
+ case BINN_INT64:
+ vint = *(int64*)psource;
+ goto loc_signed;
+ case BINN_INT32:
+ vint = *(int*)psource;
+ goto loc_signed;
+ case BINN_INT16:
+ vint = *(short*)psource;
+ goto loc_signed;
+ case BINN_UINT64:
+ vuint = *(uint64*)psource;
+ goto loc_positive;
+ case BINN_UINT32:
+ vuint = *(unsigned int*)psource;
+ goto loc_positive;
+ case BINN_UINT16:
+ vuint = *(unsigned short*)psource;
+ goto loc_positive;
+ }
+
+loc_signed:
+
+ if (vint >= 0) {
+ vuint = vint;
+ goto loc_positive;
+ }
+
+//loc_negative:
+
+ if (vint >= INT8_MIN) {
+ type2 = BINN_INT8;
+ } else
+ if (vint >= INT16_MIN) {
+ type2 = BINN_INT16;
+ } else
+ if (vint >= INT32_MIN) {
+ type2 = BINN_INT32;
+ }
+ goto loc_exit;
+
+loc_positive:
+
+ if (vuint <= UINT8_MAX) {
+ type2 = BINN_UINT8;
+ } else
+ if (vuint <= UINT16_MAX) {
+ type2 = BINN_UINT16;
+ } else
+ if (vuint <= UINT32_MAX) {
+ type2 = BINN_UINT32;
+ }
+
+loc_exit:
+
+ pvalue = (char *) psource;
+
+ if ((type2) && (type2 != type)) {
+ *ptype = type2;
+ storage_type2 = binn_get_write_storage(type2);
+ *pstorage_type = storage_type2;
+#if BYTE_ORDER == BIG_ENDIAN
+ size1 = get_storage_size(storage_type);
+ size2 = get_storage_size(storage_type2);
+ pvalue += (size1 - size2);
+#endif
+ }
+
+ return pvalue;
+
+}
+
+/***************************************************************************/
+
+BINN_PRIVATE int type_family(int type);
+
+BINN_PRIVATE BOOL AddValue(binn *item, int type, void *pvalue, int size) {
+ int int32, ArgSize, storage_type, extra_type;
+ unsigned char *p;
+
+ binn_get_type_info(type, &storage_type, &extra_type);
+
+ if (pvalue == NULL) {
+ switch (storage_type) {
+ case BINN_STORAGE_NOBYTES:
+ break;
+ case BINN_STORAGE_BLOB:
+ case BINN_STORAGE_STRING:
+ if (size == 0) break; // the 2 above are allowed to have 0 length
+ default:
+ return FALSE;
+ }
+ }
+
+ if ((type_family(type) == BINN_FAMILY_INT) && (item->disable_int_compression == FALSE))
+ pvalue = compress_int(&storage_type, &type, pvalue);
+
+ switch (storage_type) {
+ case BINN_STORAGE_NOBYTES:
+ size = 0;
+ ArgSize = size;
+ break;
+ case BINN_STORAGE_BYTE:
+ size = 1;
+ ArgSize = size;
+ break;
+ case BINN_STORAGE_WORD:
+ size = 2;
+ ArgSize = size;
+ break;
+ case BINN_STORAGE_DWORD:
+ size = 4;
+ ArgSize = size;
+ break;
+ case BINN_STORAGE_QWORD:
+ size = 8;
+ ArgSize = size;
+ break;
+ case BINN_STORAGE_BLOB:
+ if (size < 0) return FALSE;
+ //if (size == 0) ...
+ ArgSize = size + 4;
+ break;
+ case BINN_STORAGE_STRING:
+ if (size < 0) return FALSE;
+ if (size == 0) size = strlen2( (char *) pvalue);
+ ArgSize = size + 5; // at least this size
+ break;
+ case BINN_STORAGE_CONTAINER:
+ if (size <= 0) return FALSE;
+ ArgSize = size;
+ break;
+ default:
+ return FALSE;
+ }
+
+ ArgSize += 2; // at least 2 bytes used for data_type.
+ if (CheckAllocation(item, ArgSize) == FALSE) return FALSE;
+
+ // Gets the pointer to the next place in buffer
+ p = ((unsigned char *) item->pbuf) + item->used_size;
+
+ // If the data is not a container, store the data type
+ if (storage_type != BINN_STORAGE_CONTAINER) {
+ if (type > 255) {
+ u16 type16 = type;
+ copy_be16((u16*)p, (u16*)&type16);
+ p += 2;
+ item->used_size += 2;
+ } else {
+ *p = type;
+ p++;
+ item->used_size++;
+ }
+ }
+
+ switch (storage_type) {
+ case BINN_STORAGE_NOBYTES:
+ // Nothing to do.
+ break;
+ case BINN_STORAGE_BYTE:
+ *((char *) p) = *((char *) pvalue);
+ item->used_size += 1;
+ break;
+ case BINN_STORAGE_WORD:
+ copy_be16((u16*)p, (u16*)pvalue);
+ item->used_size += 2;
+ break;
+ case BINN_STORAGE_DWORD:
+ copy_be32((u32*)p, (u32*)pvalue);
+ item->used_size += 4;
+ break;
+ case BINN_STORAGE_QWORD:
+ copy_be64((u64*)p, (u64*)pvalue);
+ item->used_size += 8;
+ break;
+ case BINN_STORAGE_BLOB:
+ copy_be32((u32*)p, (u32*)&size);
+ p += 4;
+ memcpy(p, pvalue, size);
+ item->used_size += 4 + size;
+ break;
+ case BINN_STORAGE_STRING:
+ if (size > 127) {
+ int32 = size | 0x80000000;
+ copy_be32((u32*)p, (u32*)&int32);
+ p += 4;
+ item->used_size += 4;
+ } else {
+ *((unsigned char *) p) = size;
+ p++;
+ item->used_size++;
+ }
+ memcpy(p, pvalue, size);
+ p += size;
+ *((char *) p) = (char) 0;
+ size++; // null terminator
+ item->used_size += size;
+ break;
+ case BINN_STORAGE_CONTAINER:
+ memcpy(p, pvalue, size);
+ item->used_size += size;
+ break;
+ }
+
+ item->dirty = TRUE;
+
+ return TRUE;
+}
+
+/***************************************************************************/
+
+BINN_PRIVATE BOOL binn_save_header(binn *item) {
+ unsigned char byte, *p;
+ int int32, size;
+
+ if (item == NULL) return FALSE;
+
+#ifndef BINN_DISABLE_SMALL_HEADER
+
+ p = ((unsigned char *) item->pbuf) + MAX_BINN_HEADER;
+ size = item->used_size - MAX_BINN_HEADER + 3; // at least 3 bytes for the header
+
+ // write the count
+ if (item->count > 127) {
+ p -= 4;
+ size += 3;
+ int32 = item->count | 0x80000000;
+ copy_be32((u32*)p, (u32*)&int32);
+ } else {
+ p--;
+ *p = (unsigned char) item->count;
+ }
+
+ // write the size
+ if (size > 127) {
+ p -= 4;
+ size += 3;
+ int32 = size | 0x80000000;
+ copy_be32((u32*)p, (u32*)&int32);
+ } else {
+ p--;
+ *p = (unsigned char) size;
+ }
+
+ // write the type.
+ p--;
+ *p = (unsigned char) item->type;
+
+ // set the values
+ item->ptr = p;
+ item->size = size;
+
+ UNUSED(byte);
+
+#else
+
+ p = (unsigned char *) item->pbuf;
+
+ // write the type.
+ byte = item->type;
+ *p = byte; p++;
+ // write the size
+ int32 = item->used_size | 0x80000000;
+ copy_be32((u32*)p, (u32*)&int32);
+ p+=4;
+ // write the count
+ int32 = item->count | 0x80000000;
+ copy_be32((u32*)p, (u32*)&int32);
+
+ item->ptr = item->pbuf;
+ item->size = item->used_size;
+
+#endif
+
+ item->dirty = FALSE;
+
+ return TRUE;
+
+}
+
+/***************************************************************************/
+
+void APIENTRY binn_free(binn *item) {
+
+ if (item == NULL) return;
+
+ if ((item->writable) && (item->pre_allocated == FALSE)) {
+ free_fn(item->pbuf);
+ }
+
+ if (item->freefn) item->freefn(item->ptr);
+
+ if (item->allocated) {
+ free_fn(item);
+ } else {
+ memset(item, 0, sizeof(binn));
+ item->header = BINN_MAGIC;
+ }
+
+}
+
+/***************************************************************************/
+// free the binn structure but keeps the binn buffer allocated, returning a pointer to it. use the free function to release the buffer later
+void * APIENTRY binn_release(binn *item) {
+ void *data;
+
+ if (item == NULL) return NULL;
+
+ data = binn_ptr(item);
+
+ if (data > item->pbuf) {
+ memmove(item->pbuf, data, item->size);
+ data = item->pbuf;
+ }
+
+ if (item->allocated) {
+ free_fn(item);
+ } else {
+ memset(item, 0, sizeof(binn));
+ item->header = BINN_MAGIC;
+ }
+
+ return data;
+
+}
+
+/***************************************************************************/
+
+BINN_PRIVATE BOOL IsValidBinnHeader(void *pbuf, int *ptype, int *pcount, int *psize, int *pheadersize) {
+ unsigned char byte, *p, *plimit=0;
+ int int32, type, size, count;
+
+ if (pbuf == NULL) return FALSE;
+
+ p = (unsigned char *) pbuf;
+
+ if (psize && *psize > 0) {
+ plimit = p + *psize - 1;
+ }
+
+ // get the type
+ byte = *p; p++;
+ if ((byte & BINN_STORAGE_MASK) != BINN_STORAGE_CONTAINER) return FALSE;
+ if (byte & BINN_STORAGE_HAS_MORE) return FALSE;
+ type = byte;
+
+ switch (type) {
+ case BINN_LIST:
+ case BINN_MAP:
+ case BINN_OBJECT:
+ break;
+ default:
+ return FALSE;
+ }
+
+ // get the size
+ if (plimit && p > plimit) return FALSE;
+ int32 = *((unsigned char*)p);
+ if (int32 & 0x80) {
+ if (plimit && p + sizeof(int) - 1 > plimit) return FALSE;
+ copy_be32((u32*)&int32, (u32*)p);
+ int32 &= 0x7FFFFFFF;
+ p+=4;
+ } else {
+ p++;
+ }
+ size = int32;
+
+ // get the count
+ if (plimit && p > plimit) return FALSE;
+ int32 = *((unsigned char*)p);
+ if (int32 & 0x80) {
+ if (plimit && p + sizeof(int) - 1 > plimit) return FALSE;
+ copy_be32((u32*)&int32, (u32*)p);
+ int32 &= 0x7FFFFFFF;
+ p+=4;
+ } else {
+ p++;
+ }
+ count = int32;
+
+#if 0
+ // get the size
+ copy_be32((u32*)&size, (u32*)p);
+ size &= 0x7FFFFFFF;
+ p+=4;
+
+ // get the count
+ copy_be32((u32*)&count, (u32*)p);
+ count &= 0x7FFFFFFF;
+ p+=4;
+#endif
+
+ if ((size < MIN_BINN_SIZE) || (count < 0)) return FALSE;
+
+ // return the values
+ if (ptype) *ptype = type;
+ if (pcount) *pcount = count;
+ if (psize && *psize==0) *psize = size;
+ if (pheadersize) *pheadersize = (int) (p - (unsigned char*)pbuf);
+ return TRUE;
+}
+
+/***************************************************************************/
+
+BINN_PRIVATE int binn_buf_type(void *pbuf) {
+ int type;
+
+ if (!IsValidBinnHeader(pbuf, &type, NULL, NULL, NULL)) return INVALID_BINN;
+
+ return type;
+
+}
+
+/***************************************************************************/
+
+BINN_PRIVATE int binn_buf_count(void *pbuf) {
+ int nitems;
+
+ if (!IsValidBinnHeader(pbuf, NULL, &nitems, NULL, NULL)) return 0;
+
+ return nitems;
+
+}
+
+/***************************************************************************/
+
+BINN_PRIVATE int binn_buf_size(void *pbuf) {
+ int size=0;
+
+ if (!IsValidBinnHeader(pbuf, NULL, NULL, &size, NULL)) return 0;
+
+ return size;
+
+}
+
+/***************************************************************************/
+
+void * APIENTRY binn_ptr(void *ptr) {
+ binn *item;
+
+ switch (binn_get_ptr_type(ptr)) {
+ case BINN_STRUCT:
+ item = (binn*) ptr;
+ if (item->writable && item->dirty) {
+ binn_save_header(item);
+ }
+ return item->ptr;
+ case BINN_BUFFER:
+ return ptr;
+ default:
+ return NULL;
+ }
+
+}
+
+/***************************************************************************/
+
+int APIENTRY binn_size(void *ptr) {
+ binn *item;
+
+ switch (binn_get_ptr_type(ptr)) {
+ case BINN_STRUCT:
+ item = (binn*) ptr;
+ if (item->writable && item->dirty) {
+ binn_save_header(item);
+ }
+ return item->size;
+ case BINN_BUFFER:
+ return binn_buf_size(ptr);
+ default:
+ return 0;
+ }
+
+}
+
+/***************************************************************************/
+
+int APIENTRY binn_type(void *ptr) {
+ binn *item;
+
+ switch (binn_get_ptr_type(ptr)) {
+ case BINN_STRUCT:
+ item = (binn*) ptr;
+ return item->type;
+ case BINN_BUFFER:
+ return binn_buf_type(ptr);
+ default:
+ return -1;
+ }
+
+}
+
+/***************************************************************************/
+
+int APIENTRY binn_count(void *ptr) {
+ binn *item;
+
+ switch (binn_get_ptr_type(ptr)) {
+ case BINN_STRUCT:
+ item = (binn*) ptr;
+ return item->count;
+ case BINN_BUFFER:
+ return binn_buf_count(ptr);
+ default:
+ return -1;
+ }
+
+}
+
+/***************************************************************************/
+
+BOOL APIENTRY binn_is_valid_ex(void *ptr, int *ptype, int *pcount, int *psize) {
+ int i, type, count, size, header_size;
+ unsigned char *p, *plimit, *base, len;
+ void *pbuf;
+
+ pbuf = binn_ptr(ptr);
+ if (pbuf == NULL) return FALSE;
+
+ // is there an informed size?
+ if (psize && *psize > 0) {
+ size = *psize;
+ } else {
+ size = 0;
+ }
+
+ if (!IsValidBinnHeader(pbuf, &type, &count, &size, &header_size)) return FALSE;
+
+ // is there an informed size?
+ if (psize && *psize > 0) {
+ // is it the same as the one in the buffer?
+ if (size != *psize) return FALSE;
+ }
+ // is there an informed count?
+ if (pcount && *pcount > 0) {
+ // is it the same as the one in the buffer?
+ if (count != *pcount) return FALSE;
+ }
+ // is there an informed type?
+ if (ptype && *ptype != 0) {
+ // is it the same as the one in the buffer?
+ if (type != *ptype) return FALSE;
+ }
+
+ // it could compare the content size with the size informed on the header
+
+ p = (unsigned char *)pbuf;
+ base = p;
+ plimit = p + size;
+
+ p += header_size;
+
+ // process all the arguments.
+ for (i = 0; i < count; i++) {
+ switch (type) {
+ case BINN_OBJECT:
+ // gets the string size (argument name)
+ len = *p;
+ p++;
+ //if (len == 0) goto Invalid;
+ // increment the used space
+ p += len;
+ break;
+ case BINN_MAP:
+ // increment the used space
+ p += 4;
+ break;
+ //case BINN_LIST:
+ // break;
+ }
+ // xxx
+ p = AdvanceDataPos(p, plimit);
+ if ((p == 0) || (p < base)) goto Invalid;
+ }
+
+ if (ptype && *ptype==0) *ptype = type;
+ if (pcount && *pcount==0) *pcount = count;
+ if (psize && *psize==0) *psize = size;
+ return TRUE;
+
+Invalid:
+ return FALSE;
+
+}
+
+/***************************************************************************/
+
+BOOL APIENTRY binn_is_valid(void *ptr, int *ptype, int *pcount, int *psize) {
+
+ if (ptype) *ptype = 0;
+ if (pcount) *pcount = 0;
+ if (psize) *psize = 0;
+
+ return binn_is_valid_ex(ptr, ptype, pcount, psize);
+
+}
+
+/***************************************************************************/
+/*** INTERNAL FUNCTIONS ****************************************************/
+/***************************************************************************/
+
+BINN_PRIVATE BOOL GetValue(unsigned char *p, binn *value) {
+ unsigned char byte;
+ int data_type, storage_type; //, extra_type;
+ int DataSize;
+ void *p2;
+
+ if (value == NULL) return FALSE;
+ memset(value, 0, sizeof(binn));
+ value->header = BINN_MAGIC;
+ //value->allocated = FALSE; -- already zeroed
+ //value->writable = FALSE;
+
+ // saves for use with BINN_STORAGE_CONTAINER
+ p2 = p;
+
+ // read the data type
+ byte = *p; p++;
+ storage_type = byte & BINN_STORAGE_MASK;
+ if (byte & BINN_STORAGE_HAS_MORE) {
+ data_type = byte << 8;
+ byte = *p; p++;
+ data_type |= byte;
+ //extra_type = data_type & BINN_TYPE_MASK16;
+ } else {
+ data_type = byte;
+ //extra_type = byte & BINN_TYPE_MASK;
+ }
+
+ //value->storage_type = storage_type;
+ value->type = data_type;
+
+ switch (storage_type) {
+ case BINN_STORAGE_NOBYTES:
+ break;
+ case BINN_STORAGE_BYTE:
+ value->vuint8 = *((unsigned char *) p);
+ value->ptr = p; //value->ptr = &value->vuint8;
+ break;
+ case BINN_STORAGE_WORD:
+ copy_be16((u16*)&value->vint16, (u16*)p);
+ value->ptr = &value->vint16;
+ break;
+ case BINN_STORAGE_DWORD:
+ copy_be32((u32*)&value->vint32, (u32*)p);
+ value->ptr = &value->vint32;
+ break;
+ case BINN_STORAGE_QWORD:
+ copy_be64((u64*)&value->vint64, (u64*)p);
+ value->ptr = &value->vint64;
+ break;
+ case BINN_STORAGE_BLOB:
+ copy_be32((u32*)&value->size, (u32*)p);
+ p+=4;
+ value->ptr = p;
+ break;
+ case BINN_STORAGE_CONTAINER:
+ value->ptr = p2; // <-- it returns the pointer to the container, not the data
+ if (IsValidBinnHeader(p2, NULL, &value->count, &value->size, NULL) == FALSE) return FALSE;
+ break;
+ case BINN_STORAGE_STRING:
+ DataSize = *((unsigned char*)p);
+ if (DataSize & 0x80) {
+ copy_be32((u32*)&DataSize, (u32*)p);
+ DataSize &= 0x7FFFFFFF;
+ p+=4;
+ } else {
+ p++;
+ }
+ value->size = DataSize;
+ value->ptr = p;
+ break;
+ default:
+ return FALSE;
+ }
+
+ // convert the returned value, if needed
+
+ switch (value->type) {
+ case BINN_TRUE:
+ value->type = BINN_BOOL;
+ value->vbool = TRUE;
+ value->ptr = &value->vbool;
+ break;
+ case BINN_FALSE:
+ value->type = BINN_BOOL;
+ value->vbool = FALSE;
+ value->ptr = &value->vbool;
+ break;
+#ifdef BINN_EXTENDED
+ case BINN_SINGLE_STR:
+ value->type = BINN_SINGLE;
+ value->vfloat = (float) atof((const char*)value->ptr); // converts from string to double, and then to float
+ value->ptr = &value->vfloat;
+ break;
+ case BINN_DOUBLE_STR:
+ value->type = BINN_DOUBLE;
+ value->vdouble = atof((const char*)value->ptr); // converts from string to double
+ value->ptr = &value->vdouble;
+ break;
+#endif
+ /*
+ case BINN_DECIMAL:
+ case BINN_CURRENCYSTR:
+ case BINN_DATE:
+ case BINN_DATETIME:
+ case BINN_TIME:
+ */
+ }
+
+ return TRUE;
+
+}
+
+/***************************************************************************/
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+
+// on little-endian devices we store the value so we can return a pointer to integers.
+// it's valid only for single-threaded apps. multi-threaded apps must use the _get_ functions instead.
+
+binn local_value;
+
+BINN_PRIVATE void * store_value(binn *value) {
+
+ memcpy(&local_value, value, sizeof(binn));
+
+ switch (binn_get_read_storage(value->type)) {
+ case BINN_STORAGE_NOBYTES:
+ // return a valid pointer
+ case BINN_STORAGE_WORD:
+ case BINN_STORAGE_DWORD:
+ case BINN_STORAGE_QWORD:
+ return &local_value.vint32; // returns the pointer to the converted value, from big-endian to little-endian
+ }
+
+ return value->ptr; // returns from the on stack value to be thread-safe (for list, map, object, string and blob)
+
+}
+
+#endif
+
+/***************************************************************************/
+/*** READ FUNCTIONS ********************************************************/
+/***************************************************************************/
+
+BOOL APIENTRY binn_object_get_value(void *ptr, char *key, binn *value) {
+ int type, count, size=0, header_size;
+ unsigned char *p;
+
+ ptr = binn_ptr(ptr);
+ if ((ptr == 0) || (key == 0) || (value == 0)) return FALSE;
+
+ // check the header
+ if (IsValidBinnHeader(ptr, &type, &count, &size, &header_size) == FALSE) return FALSE;
+
+ if (type != BINN_OBJECT) return FALSE;
+ if (count == 0) return FALSE;
+
+ p = (unsigned char *) ptr;
+ p = SearchForKey(p, header_size, size, count, key);
+ if (p == FALSE) return FALSE;
+
+ return GetValue(p, value);
+
+}
+
+/***************************************************************************/
+
+BOOL APIENTRY binn_map_get_value(void* ptr, int id, binn *value) {
+ int type, count, size=0, header_size;
+ unsigned char *p;
+
+ ptr = binn_ptr(ptr);
+ if ((ptr == 0) || (value == 0)) return FALSE;
+
+ // check the header
+ if (IsValidBinnHeader(ptr, &type, &count, &size, &header_size) == FALSE) return FALSE;
+
+ if (type != BINN_MAP) return FALSE;
+ if (count == 0) return FALSE;
+
+ p = (unsigned char *) ptr;
+ p = SearchForID(p, header_size, size, count, id);
+ if (p == FALSE) return FALSE;
+
+ return GetValue(p, value);
+
+}
+
+/***************************************************************************/
+
+BOOL APIENTRY binn_list_get_value(void* ptr, int pos, binn *value) {
+ int i, type, count, size=0, header_size;
+ unsigned char *p, *plimit, *base;
+
+ ptr = binn_ptr(ptr);
+ if ((ptr == 0) || (value == 0)) return FALSE;
+
+ // check the header
+ if (IsValidBinnHeader(ptr, &type, &count, &size, &header_size) == FALSE) return FALSE;
+
+ if (type != BINN_LIST) return FALSE;
+ if (count == 0) return FALSE;
+ if ((pos <= 0) | (pos > count)) return FALSE;
+ pos--; // convert from base 1 to base 0
+
+ p = (unsigned char *) ptr;
+ base = p;
+ plimit = p + size;
+ p += header_size;
+
+ for (i = 0; i < pos; i++) {
+ p = AdvanceDataPos(p, plimit);
+ if ((p == 0) || (p < base)) return FALSE;
+ }
+
+ return GetValue(p, value);
+
+}
+
+/***************************************************************************/
+/*** READ PAIR BY POSITION *************************************************/
+/***************************************************************************/
+
+BINN_PRIVATE BOOL binn_read_pair(int expected_type, void *ptr, int pos, int *pid, char *pkey, binn *value) {
+ int type, count, size=0, header_size;
+ int i, int32, id, counter=0;
+ unsigned char *p, *plimit, *base, *key, len;
+
+ ptr = binn_ptr(ptr);
+
+ // check the header
+ if (IsValidBinnHeader(ptr, &type, &count, &size, &header_size) == FALSE) return FALSE;
+
+ if ((type != expected_type) || (count == 0) || (pos < 1) || (pos > count)) return FALSE;
+
+ p = (unsigned char *) ptr;
+ base = p;
+ plimit = p + size - 1;
+ p += header_size;
+
+ for (i = 0; i < count; i++) {
+ switch (type) {
+ case BINN_MAP:
+ copy_be32((u32*)&int32, (u32*)p);
+ p += 4;
+ if (p > plimit) return FALSE;
+ id = int32;
+ break;
+ case BINN_OBJECT:
+ len = *((unsigned char *)p); p++;
+ if (p > plimit) return FALSE;
+ key = p;
+ p += len;
+ if (p > plimit) return FALSE;
+ break;
+ }
+ counter++;
+ if (counter == pos) goto found;
+ //
+ p = AdvanceDataPos(p, plimit);
+ if ((p == 0) || (p < base)) return FALSE;
+ }
+
+ return FALSE;
+
+found:
+
+ switch (type) {
+ case BINN_MAP:
+ if (pid) *pid = id;
+ break;
+ case BINN_OBJECT:
+ if (pkey) {
+ memcpy(pkey, key, len);
+ pkey[len] = 0;
+ }
+ break;
+ }
+
+ return GetValue(p, value);
+
+}
+
+/***************************************************************************/
+
+BOOL APIENTRY binn_map_get_pair(void *ptr, int pos, int *pid, binn *value) {
+
+ return binn_read_pair(BINN_MAP, ptr, pos, pid, NULL, value);
+
+}
+
+/***************************************************************************/
+
+BOOL APIENTRY binn_object_get_pair(void *ptr, int pos, char *pkey, binn *value) {
+
+ return binn_read_pair(BINN_OBJECT, ptr, pos, NULL, pkey, value);
+
+}
+
+/***************************************************************************/
+
+binn * APIENTRY binn_map_pair(void *map, int pos, int *pid) {
+ binn *value;
+
+ value = (binn *) binn_malloc(sizeof(binn));
+
+ if (binn_read_pair(BINN_MAP, map, pos, pid, NULL, value) == FALSE) {
+ free_fn(value);
+ return NULL;
+ }
+
+ value->allocated = TRUE;
+ return value;
+
+}
+
+/***************************************************************************/
+
+binn * APIENTRY binn_object_pair(void *obj, int pos, char *pkey) {
+ binn *value;
+
+ value = (binn *) binn_malloc(sizeof(binn));
+
+ if (binn_read_pair(BINN_OBJECT, obj, pos, NULL, pkey, value) == FALSE) {
+ free_fn(value);
+ return NULL;
+ }
+
+ value->allocated = TRUE;
+ return value;
+
+}
+
+/***************************************************************************/
+/***************************************************************************/
+
+void * APIENTRY binn_map_read_pair(void *ptr, int pos, int *pid, int *ptype, int *psize) {
+ binn value;
+
+ if (binn_map_get_pair(ptr, pos, pid, &value) == FALSE) return NULL;
+ if (ptype) *ptype = value.type;
+ if (psize) *psize = value.size;
+#if BYTE_ORDER == LITTLE_ENDIAN
+ return store_value(&value);
+#else
+ return value.ptr;
+#endif
+
+}
+
+/***************************************************************************/
+
+void * APIENTRY binn_object_read_pair(void *ptr, int pos, char *pkey, int *ptype, int *psize) {
+ binn value;
+
+ if (binn_object_get_pair(ptr, pos, pkey, &value) == FALSE) return NULL;
+ if (ptype) *ptype = value.type;
+ if (psize) *psize = value.size;
+#if BYTE_ORDER == LITTLE_ENDIAN
+ return store_value(&value);
+#else
+ return value.ptr;
+#endif
+
+}
+
+/***************************************************************************/
+/*** SEQUENTIAL READ FUNCTIONS *********************************************/
+/***************************************************************************/
+
+BOOL APIENTRY binn_iter_init(binn_iter *iter, void *ptr, int expected_type) {
+ int type, count, size=0, header_size;
+
+ ptr = binn_ptr(ptr);
+ if ((ptr == 0) || (iter == 0)) return FALSE;
+ memset(iter, 0, sizeof(binn_iter));
+
+ // check the header
+ if (IsValidBinnHeader(ptr, &type, &count, &size, &header_size) == FALSE) return FALSE;
+
+ if (type != expected_type) return FALSE;
+ //if (count == 0) return FALSE; -- should not be used
+
+ iter->plimit = (unsigned char *)ptr + size - 1;
+ iter->pnext = (unsigned char *)ptr + header_size;
+ iter->count = count;
+ iter->current = 0;
+ iter->type = type;
+
+ return TRUE;
+}
+
+/***************************************************************************/
+
+BOOL APIENTRY binn_list_next(binn_iter *iter, binn *value) {
+ unsigned char *pnow;
+
+ if ((iter == 0) || (iter->pnext == 0) || (iter->pnext > iter->plimit) || (iter->current > iter->count) || (iter->type != BINN_LIST)) return FALSE;
+
+ iter->current++;
+ if (iter->current > iter->count) return FALSE;
+
+ pnow = iter->pnext;
+ iter->pnext = AdvanceDataPos(pnow, iter->plimit);
+ if (iter->pnext != 0 && iter->pnext < pnow) return FALSE;
+
+ return GetValue(pnow, value);
+
+}
+
+/***************************************************************************/
+
+BINN_PRIVATE BOOL binn_read_next_pair(int expected_type, binn_iter *iter, int *pid, char *pkey, binn *value) {
+ int int32, id;
+ unsigned char *p, *key;
+ unsigned short len;
+
+ if ((iter == 0) || (iter->pnext == 0) || (iter->pnext > iter->plimit) || (iter->current > iter->count) || (iter->type != expected_type)) return FALSE;
+
+ iter->current++;
+ if (iter->current > iter->count) return FALSE;
+
+ p = iter->pnext;
+
+ switch (expected_type) {
+ case BINN_MAP:
+ copy_be32((u32*)&int32, (u32*)p);
+ p += 4;
+ if (p > iter->plimit) return FALSE;
+ id = int32;
+ if (pid) *pid = id;
+ break;
+ case BINN_OBJECT:
+ len = *((unsigned char *)p); p++;
+ key = p;
+ p += len;
+ if (p > iter->plimit) return FALSE;
+ if (pkey) {
+ memcpy(pkey, key, len);
+ pkey[len] = 0;
+ }
+ break;
+ }
+
+ iter->pnext = AdvanceDataPos(p, iter->plimit);
+ if (iter->pnext != 0 && iter->pnext < p) return FALSE;
+
+ return GetValue(p, value);
+
+}
+
+/***************************************************************************/
+
+BOOL APIENTRY binn_map_next(binn_iter *iter, int *pid, binn *value) {
+
+ return binn_read_next_pair(BINN_MAP, iter, pid, NULL, value);
+
+}
+
+/***************************************************************************/
+
+BOOL APIENTRY binn_object_next(binn_iter *iter, char *pkey, binn *value) {
+
+ return binn_read_next_pair(BINN_OBJECT, iter, NULL, pkey, value);
+
+}
+
+/***************************************************************************/
+/***************************************************************************/
+
+binn * APIENTRY binn_list_next_value(binn_iter *iter) {
+ binn *value;
+
+ value = (binn *) binn_malloc(sizeof(binn));
+
+ if (binn_list_next(iter, value) == FALSE) {
+ free_fn(value);
+ return NULL;
+ }
+
+ value->allocated = TRUE;
+ return value;
+
+}
+
+/***************************************************************************/
+
+binn * APIENTRY binn_map_next_value(binn_iter *iter, int *pid) {
+ binn *value;
+
+ value = (binn *) binn_malloc(sizeof(binn));
+
+ if (binn_map_next(iter, pid, value) == FALSE) {
+ free_fn(value);
+ return NULL;
+ }
+
+ value->allocated = TRUE;
+ return value;
+
+}
+
+/***************************************************************************/
+
+binn * APIENTRY binn_object_next_value(binn_iter *iter, char *pkey) {
+ binn *value;
+
+ value = (binn *) binn_malloc(sizeof(binn));
+
+ if (binn_object_next(iter, pkey, value) == FALSE) {
+ free_fn(value);
+ return NULL;
+ }
+
+ value->allocated = TRUE;
+ return value;
+
+}
+
+/***************************************************************************/
+/***************************************************************************/
+
+void * APIENTRY binn_list_read_next(binn_iter *iter, int *ptype, int *psize) {
+ binn value;
+
+ if (binn_list_next(iter, &value) == FALSE) return NULL;
+ if (ptype) *ptype = value.type;
+ if (psize) *psize = value.size;
+#if BYTE_ORDER == LITTLE_ENDIAN
+ return store_value(&value);
+#else
+ return value.ptr;
+#endif
+
+}
+
+/***************************************************************************/
+
+void * APIENTRY binn_map_read_next(binn_iter *iter, int *pid, int *ptype, int *psize) {
+ binn value;
+
+ if (binn_map_next(iter, pid, &value) == FALSE) return NULL;
+ if (ptype) *ptype = value.type;
+ if (psize) *psize = value.size;
+#if BYTE_ORDER == LITTLE_ENDIAN
+ return store_value(&value);
+#else
+ return value.ptr;
+#endif
+
+}
+
+/***************************************************************************/
+
+void * APIENTRY binn_object_read_next(binn_iter *iter, char *pkey, int *ptype, int *psize) {
+ binn value;
+
+ if (binn_object_next(iter, pkey, &value) == FALSE) return NULL;
+ if (ptype) *ptype = value.type;
+ if (psize) *psize = value.size;
+#if BYTE_ORDER == LITTLE_ENDIAN
+ return store_value(&value);
+#else
+ return value.ptr;
+#endif
+
+}
+
+/*************************************************************************************/
+/****** EXTENDED INTERFACE ***********************************************************/
+/****** none of the functions above call the functions below *************************/
+/*************************************************************************************/
+
+int APIENTRY binn_get_write_storage(int type) {
+ int storage_type;
+
+ switch (type) {
+ case BINN_SINGLE_STR:
+ case BINN_DOUBLE_STR:
+ return BINN_STORAGE_STRING;
+
+ case BINN_BOOL:
+ return BINN_STORAGE_NOBYTES;
+
+ default:
+ binn_get_type_info(type, &storage_type, NULL);
+ return storage_type;
+ }
+
+}
+
+/*************************************************************************************/
+
+int APIENTRY binn_get_read_storage(int type) {
+ int storage_type;
+
+ switch (type) {
+#ifdef BINN_EXTENDED
+ case BINN_SINGLE_STR:
+ return BINN_STORAGE_DWORD;
+ case BINN_DOUBLE_STR:
+ return BINN_STORAGE_QWORD;
+#endif
+ case BINN_BOOL:
+ case BINN_TRUE:
+ case BINN_FALSE:
+ return BINN_STORAGE_DWORD;
+ default:
+ binn_get_type_info(type, &storage_type, NULL);
+ return storage_type;
+ }
+
+}
+
+/*************************************************************************************/
+
+BINN_PRIVATE BOOL GetWriteConvertedData(int *ptype, void **ppvalue, int *psize) {
+ int type;
+ float f1;
+ double d1;
+ char pstr[128];
+
+ UNUSED(pstr);
+ UNUSED(d1);
+ UNUSED(f1);
+
+ type = *ptype;
+
+ if (*ppvalue == NULL) {
+ switch (type) {
+ case BINN_NULL:
+ case BINN_TRUE:
+ case BINN_FALSE:
+ break;
+ case BINN_STRING:
+ case BINN_BLOB:
+ if (*psize == 0) break;
+ default:
+ return FALSE;
+ }
+ }
+
+ switch (type) {
+#ifdef BINN_EXTENDED
+ case BINN_SINGLE:
+ f1 = **(float**)ppvalue;
+ d1 = f1; // convert from float (32bits) to double (64bits)
+ type = BINN_SINGLE_STR;
+ goto conv_double;
+ case BINN_DOUBLE:
+ d1 = **(double**)ppvalue;
+ type = BINN_DOUBLE_STR;
+conv_double:
+ // the '%.17e' is more precise than the '%g'
+ snprintf(pstr, 127, "%.17e", d1);
+ *ppvalue = pstr;
+ *ptype = type;
+ break;
+#endif
+ case BINN_DECIMAL:
+ case BINN_CURRENCYSTR:
+ /*
+ if (binn_malloc_extptr(128) == NULL) return FALSE;
+ snprintf(sptr, 127, "%E", **ppvalue);
+ *ppvalue = sptr;
+ */
+ return TRUE; //! temporary
+ break;
+
+ case BINN_DATE:
+ case BINN_DATETIME:
+ case BINN_TIME:
+ return TRUE; //! temporary
+ break;
+
+ case BINN_BOOL:
+ if (**((BOOL**)ppvalue) == FALSE) {
+ type = BINN_FALSE;
+ } else {
+ type = BINN_TRUE;
+ }
+ *ptype = type;
+ break;
+
+ }
+
+ return TRUE;
+
+}
+
+/*************************************************************************************/
+
+BINN_PRIVATE int type_family(int type) {
+
+ switch (type) {
+ case BINN_LIST:
+ case BINN_MAP:
+ case BINN_OBJECT:
+ return BINN_FAMILY_BINN;
+
+ case BINN_INT8:
+ case BINN_INT16:
+ case BINN_INT32:
+ case BINN_INT64:
+ case BINN_UINT8:
+ case BINN_UINT16:
+ case BINN_UINT32:
+ case BINN_UINT64:
+ return BINN_FAMILY_INT;
+
+ case BINN_FLOAT32:
+ case BINN_FLOAT64:
+ //case BINN_SINGLE:
+ case BINN_SINGLE_STR:
+ //case BINN_DOUBLE:
+ case BINN_DOUBLE_STR:
+ return BINN_FAMILY_FLOAT;
+
+ case BINN_STRING:
+ case BINN_HTML:
+ case BINN_CSS:
+ case BINN_XML:
+ case BINN_JSON:
+ case BINN_JAVASCRIPT:
+ return BINN_FAMILY_STRING;
+
+ case BINN_BLOB:
+ case BINN_JPEG:
+ case BINN_GIF:
+ case BINN_PNG:
+ case BINN_BMP:
+ return BINN_FAMILY_BLOB;
+
+ case BINN_DECIMAL:
+ case BINN_CURRENCY:
+ case BINN_DATE:
+ case BINN_TIME:
+ case BINN_DATETIME:
+ return BINN_FAMILY_STRING;
+
+ case BINN_BOOL:
+ return BINN_FAMILY_BOOL;
+
+ case BINN_NULL:
+ return BINN_FAMILY_NULL;
+
+ default:
+ // if it wasn't found
+ return BINN_FAMILY_NONE;
+ }
+
+}
+
+/*************************************************************************************/
+
+BINN_PRIVATE int int_type(int type) {
+
+ switch (type) {
+ case BINN_INT8:
+ case BINN_INT16:
+ case BINN_INT32:
+ case BINN_INT64:
+ return BINN_SIGNED_INT;
+
+ case BINN_UINT8:
+ case BINN_UINT16:
+ case BINN_UINT32:
+ case BINN_UINT64:
+ return BINN_UNSIGNED_INT;
+
+ default:
+ return 0;
+ }
+
+}
+
+/*************************************************************************************/
+
+BINN_PRIVATE BOOL copy_raw_value(void *psource, void *pdest, int data_store) {
+
+ switch (data_store) {
+ case BINN_STORAGE_NOBYTES:
+ break;
+ case BINN_STORAGE_BYTE:
+ *((char *) pdest) = *(char *)psource;
+ break;
+ case BINN_STORAGE_WORD:
+ *((short *) pdest) = *(short *)psource;
+ break;
+ case BINN_STORAGE_DWORD:
+ *((int *) pdest) = *(int *)psource;
+ break;
+ case BINN_STORAGE_QWORD:
+ *((uint64 *) pdest) = *(uint64 *)psource;
+ break;
+ case BINN_STORAGE_BLOB:
+ case BINN_STORAGE_STRING:
+ case BINN_STORAGE_CONTAINER:
+ *((char **) pdest) = (char *)psource;
+ break;
+ default:
+ return FALSE;
+ }
+
+ return TRUE;
+
+}
+
+/*************************************************************************************/
+
+BINN_PRIVATE BOOL copy_int_value(void *psource, void *pdest, int source_type, int dest_type) {
+ uint64 vuint64; int64 vint64;
+
+ switch (source_type) {
+ case BINN_INT8:
+ vint64 = *(signed char *)psource;
+ break;
+ case BINN_INT16:
+ vint64 = *(short *)psource;
+ break;
+ case BINN_INT32:
+ vint64 = *(int *)psource;
+ break;
+ case BINN_INT64:
+ vint64 = *(int64 *)psource;
+ break;
+
+ case BINN_UINT8:
+ vuint64 = *(unsigned char *)psource;
+ break;
+ case BINN_UINT16:
+ vuint64 = *(unsigned short *)psource;
+ break;
+ case BINN_UINT32:
+ vuint64 = *(unsigned int *)psource;
+ break;
+ case BINN_UINT64:
+ vuint64 = *(uint64 *)psource;
+ break;
+
+ default:
+ return FALSE;
+ }
+
+
+ // copy from int64 to uint64, if possible
+
+ if ((int_type(source_type) == BINN_UNSIGNED_INT) && (int_type(dest_type) == BINN_SIGNED_INT)) {
+ if (vuint64 > INT64_MAX) return FALSE;
+ vint64 = vuint64;
+ } else if ((int_type(source_type) == BINN_SIGNED_INT) && (int_type(dest_type) == BINN_UNSIGNED_INT)) {
+ if (vint64 < 0) return FALSE;
+ vuint64 = vint64;
+ }
+
+
+ switch (dest_type) {
+ case BINN_INT8:
+ if ((vint64 < INT8_MIN) || (vint64 > INT8_MAX)) return FALSE;
+ *(signed char *)pdest = (signed char) vint64;
+ break;
+ case BINN_INT16:
+ if ((vint64 < INT16_MIN) || (vint64 > INT16_MAX)) return FALSE;
+ *(short *)pdest = (short) vint64;
+ break;
+ case BINN_INT32:
+ if ((vint64 < INT32_MIN) || (vint64 > INT32_MAX)) return FALSE;
+ *(int *)pdest = (int) vint64;
+ break;
+ case BINN_INT64:
+ *(int64 *)pdest = vint64;
+ break;
+
+ case BINN_UINT8:
+ if (vuint64 > UINT8_MAX) return FALSE;
+ *(unsigned char *)pdest = (unsigned char) vuint64;
+ break;
+ case BINN_UINT16:
+ if (vuint64 > UINT16_MAX) return FALSE;
+ *(unsigned short *)pdest = (unsigned short) vuint64;
+ break;
+ case BINN_UINT32:
+ if (vuint64 > UINT32_MAX) return FALSE;
+ *(unsigned int *)pdest = (unsigned int) vuint64;
+ break;
+ case BINN_UINT64:
+ *(uint64 *)pdest = vuint64;
+ break;
+
+ default:
+ return FALSE;
+ }
+
+ return TRUE;
+
+}
+
+/*************************************************************************************/
+
+BINN_PRIVATE BOOL copy_float_value(void *psource, void *pdest, int source_type, int dest_type) {
+
+ switch (source_type) {
+ case BINN_FLOAT32:
+ *(double *)pdest = *(float *)psource;
+ break;
+ case BINN_FLOAT64:
+ *(float *)pdest = (float) *(double *)psource;
+ break;
+ default:
+ return FALSE;
+ }
+
+ return TRUE;
+
+}
+
+/*************************************************************************************/
+
+BINN_PRIVATE void zero_value(void *pvalue, int type) {
+ //int size=0;
+
+ switch (binn_get_read_storage(type)) {
+ case BINN_STORAGE_NOBYTES:
+ break;
+ case BINN_STORAGE_BYTE:
+ *((char *) pvalue) = 0;
+ //size=1;
+ break;
+ case BINN_STORAGE_WORD:
+ *((short *) pvalue) = 0;
+ //size=2;
+ break;
+ case BINN_STORAGE_DWORD:
+ *((int *) pvalue) = 0;
+ //size=4;
+ break;
+ case BINN_STORAGE_QWORD:
+ *((uint64 *) pvalue) = 0;
+ //size=8;
+ break;
+ case BINN_STORAGE_BLOB:
+ case BINN_STORAGE_STRING:
+ case BINN_STORAGE_CONTAINER:
+ *(char **)pvalue = NULL;
+ break;
+ }
+
+ //if (size>0) memset(pvalue, 0, size);
+
+}
+
+/*************************************************************************************/
+
+BINN_PRIVATE BOOL copy_value(void *psource, void *pdest, int source_type, int dest_type, int data_store) {
+
+ if (type_family(source_type) != type_family(dest_type)) return FALSE;
+
+ if ((type_family(source_type) == BINN_FAMILY_INT) && (source_type != dest_type)) {
+ return copy_int_value(psource, pdest, source_type, dest_type);
+ } else if ((type_family(source_type) == BINN_FAMILY_FLOAT) && (source_type != dest_type)) {
+ return copy_float_value(psource, pdest, source_type, dest_type);
+ } else {
+ return copy_raw_value(psource, pdest, data_store);
+ }
+
+}
+
+/*************************************************************************************/
+/*** WRITE FUNCTIONS *****************************************************************/
+/*************************************************************************************/
+
+BOOL APIENTRY binn_list_add(binn *list, int type, void *pvalue, int size) {
+
+ if (GetWriteConvertedData(&type, &pvalue, &size) == FALSE) return FALSE;
+
+ return binn_list_add_raw(list, type, pvalue, size);
+
+}
+
+/*************************************************************************************/
+
+BOOL APIENTRY binn_map_set(binn *map, int id, int type, void *pvalue, int size) {
+
+ if (GetWriteConvertedData(&type, &pvalue, &size) == FALSE) return FALSE;
+
+ return binn_map_set_raw(map, id, type, pvalue, size);
+
+}
+
+/*************************************************************************************/
+
+BOOL APIENTRY binn_object_set(binn *obj, char *key, int type, void *pvalue, int size) {
+
+ if (GetWriteConvertedData(&type, &pvalue, &size) == FALSE) return FALSE;
+
+ return binn_object_set_raw(obj, key, type, pvalue, size);
+
+}
+
+/*************************************************************************************/
+
+// this function is used by the wrappers
+BOOL APIENTRY binn_add_value(binn *item, int binn_type, int id, char *name, int type, void *pvalue, int size) {
+
+ switch (binn_type) {
+ case BINN_LIST:
+ return binn_list_add(item, type, pvalue, size);
+ case BINN_MAP:
+ return binn_map_set(item, id, type, pvalue, size);
+ case BINN_OBJECT:
+ return binn_object_set(item, name, type, pvalue, size);
+ default:
+ return FALSE;
+ }
+
+}
+
+/*************************************************************************************/
+/*************************************************************************************/
+
+BOOL APIENTRY binn_list_add_new(binn *list, binn *value) {
+ BOOL retval;
+
+ retval = binn_list_add_value(list, value);
+ if (value) free_fn(value);
+ return retval;
+
+}
+
+/*************************************************************************************/
+
+BOOL APIENTRY binn_map_set_new(binn *map, int id, binn *value) {
+ BOOL retval;
+
+ retval = binn_map_set_value(map, id, value);
+ if (value) free_fn(value);
+ return retval;
+
+}
+
+/*************************************************************************************/
+
+BOOL APIENTRY binn_object_set_new(binn *obj, char *key, binn *value) {
+ BOOL retval;
+
+ retval = binn_object_set_value(obj, key, value);
+ if (value) free_fn(value);
+ return retval;
+
+}
+
+/*************************************************************************************/
+/*** READ FUNCTIONS ******************************************************************/
+/*************************************************************************************/
+
+binn * APIENTRY binn_list_value(void *ptr, int pos) {
+ binn *value;
+
+ value = (binn *) binn_malloc(sizeof(binn));
+
+ if (binn_list_get_value(ptr, pos, value) == FALSE) {
+ free_fn(value);
+ return NULL;
+ }
+
+ value->allocated = TRUE;
+ return value;
+
+}
+
+/*************************************************************************************/
+
+binn * APIENTRY binn_map_value(void *ptr, int id) {
+ binn *value;
+
+ value = (binn *) binn_malloc(sizeof(binn));
+
+ if (binn_map_get_value(ptr, id, value) == FALSE) {
+ free_fn(value);
+ return NULL;
+ }
+
+ value->allocated = TRUE;
+ return value;
+
+}
+
+/*************************************************************************************/
+
+binn * APIENTRY binn_object_value(void *ptr, char *key) {
+ binn *value;
+
+ value = (binn *) binn_malloc(sizeof(binn));
+
+ if (binn_object_get_value(ptr, key, value) == FALSE) {
+ free_fn(value);
+ return NULL;
+ }
+
+ value->allocated = TRUE;
+ return value;
+
+}
+
+/***************************************************************************/
+/***************************************************************************/
+
+void * APIENTRY binn_list_read(void *list, int pos, int *ptype, int *psize) {
+ binn value;
+
+ if (binn_list_get_value(list, pos, &value) == FALSE) return NULL;
+ if (ptype) *ptype = value.type;
+ if (psize) *psize = value.size;
+#if BYTE_ORDER == LITTLE_ENDIAN
+ return store_value(&value);
+#else
+ return value.ptr;
+#endif
+
+}
+
+/***************************************************************************/
+
+void * APIENTRY binn_map_read(void *map, int id, int *ptype, int *psize) {
+ binn value;
+
+ if (binn_map_get_value(map, id, &value) == FALSE) return NULL;
+ if (ptype) *ptype = value.type;
+ if (psize) *psize = value.size;
+#if BYTE_ORDER == LITTLE_ENDIAN
+ return store_value(&value);
+#else
+ return value.ptr;
+#endif
+
+}
+
+/***************************************************************************/
+
+void * APIENTRY binn_object_read(void *obj, char *key, int *ptype, int *psize) {
+ binn value;
+
+ if (binn_object_get_value(obj, key, &value) == FALSE) return NULL;
+ if (ptype) *ptype = value.type;
+ if (psize) *psize = value.size;
+#if BYTE_ORDER == LITTLE_ENDIAN
+ return store_value(&value);
+#else
+ return value.ptr;
+#endif
+
+}
+
+/***************************************************************************/
+/***************************************************************************/
+
+BOOL APIENTRY binn_list_get(void *ptr, int pos, int type, void *pvalue, int *psize) {
+ binn value;
+ int storage_type;
+
+ storage_type = binn_get_read_storage(type);
+ if ((storage_type != BINN_STORAGE_NOBYTES) && (pvalue == NULL)) return FALSE;
+
+ zero_value(pvalue, type);
+
+ if (binn_list_get_value(ptr, pos, &value) == FALSE) return FALSE;
+
+ if (copy_value(value.ptr, pvalue, value.type, type, storage_type) == FALSE) return FALSE;
+
+ if (psize) *psize = value.size;
+
+ return TRUE;
+
+}
+
+/***************************************************************************/
+
+BOOL APIENTRY binn_map_get(void *ptr, int id, int type, void *pvalue, int *psize) {
+ binn value;
+ int storage_type;
+
+ storage_type = binn_get_read_storage(type);
+ if ((storage_type != BINN_STORAGE_NOBYTES) && (pvalue == NULL)) return FALSE;
+
+ zero_value(pvalue, type);
+
+ if (binn_map_get_value(ptr, id, &value) == FALSE) return FALSE;
+
+ if (copy_value(value.ptr, pvalue, value.type, type, storage_type) == FALSE) return FALSE;
+
+ if (psize) *psize = value.size;
+
+ return TRUE;
+
+}
+
+/***************************************************************************/
+
+// if (binn_object_get(obj, "multiplier", BINN_INT32, &multiplier, NULL) == FALSE) xxx;
+
+BOOL APIENTRY binn_object_get(void *ptr, char *key, int type, void *pvalue, int *psize) {
+ binn value;
+ int storage_type;
+
+ storage_type = binn_get_read_storage(type);
+ if ((storage_type != BINN_STORAGE_NOBYTES) && (pvalue == NULL)) return FALSE;
+
+ zero_value(pvalue, type);
+
+ if (binn_object_get_value(ptr, key, &value) == FALSE) return FALSE;
+
+ if (copy_value(value.ptr, pvalue, value.type, type, storage_type) == FALSE) return FALSE;
+
+ if (psize) *psize = value.size;
+
+ return TRUE;
+
+}
+
+/***************************************************************************/
+/***************************************************************************/
+
+// these functions below may not be implemented as inline functions, because
+// they use a lot of space, even for the variable. so they will be exported.
+
+// but what about using as static?
+// is there any problem with wrappers? can these wrappers implement these functions using the header?
+// if as static, will they be present even on modules that don't use the functions?
+
+signed char APIENTRY binn_list_int8(void *list, int pos) {
+ signed char value;
+
+ binn_list_get(list, pos, BINN_INT8, &value, NULL);
+
+ return value;
+}
+
+short APIENTRY binn_list_int16(void *list, int pos) {
+ short value;
+
+ binn_list_get(list, pos, BINN_INT16, &value, NULL);
+
+ return value;
+}
+
+int APIENTRY binn_list_int32(void *list, int pos) {
+ int value;
+
+ binn_list_get(list, pos, BINN_INT32, &value, NULL);
+
+ return value;
+}
+
+int64 APIENTRY binn_list_int64(void *list, int pos) {
+ int64 value;
+
+ binn_list_get(list, pos, BINN_INT64, &value, NULL);
+
+ return value;
+}
+
+unsigned char APIENTRY binn_list_uint8(void *list, int pos) {
+ unsigned char value;
+
+ binn_list_get(list, pos, BINN_UINT8, &value, NULL);
+
+ return value;
+}
+
+unsigned short APIENTRY binn_list_uint16(void *list, int pos) {
+ unsigned short value;
+
+ binn_list_get(list, pos, BINN_UINT16, &value, NULL);
+
+ return value;
+}
+
+unsigned int APIENTRY binn_list_uint32(void *list, int pos) {
+ unsigned int value;
+
+ binn_list_get(list, pos, BINN_UINT32, &value, NULL);
+
+ return value;
+}
+
+uint64 APIENTRY binn_list_uint64(void *list, int pos) {
+ uint64 value;
+
+ binn_list_get(list, pos, BINN_UINT64, &value, NULL);
+
+ return value;
+}
+
+float APIENTRY binn_list_float(void *list, int pos) {
+ float value;
+
+ binn_list_get(list, pos, BINN_FLOAT32, &value, NULL);
+
+ return value;
+}
+
+double APIENTRY binn_list_double(void *list, int pos) {
+ double value;
+
+ binn_list_get(list, pos, BINN_FLOAT64, &value, NULL);
+
+ return value;
+}
+
+BOOL APIENTRY binn_list_bool(void *list, int pos) {
+ BOOL value;
+
+ binn_list_get(list, pos, BINN_BOOL, &value, NULL);
+
+ return value;
+}
+
+BOOL APIENTRY binn_list_null(void *list, int pos) {
+
+ return binn_list_get(list, pos, BINN_NULL, NULL, NULL);
+
+}
+
+char * APIENTRY binn_list_str(void *list, int pos) {
+ char *value;
+
+ binn_list_get(list, pos, BINN_STRING, &value, NULL);
+
+ return value;
+}
+
+void * APIENTRY binn_list_blob(void *list, int pos, int *psize) {
+ void *value;
+
+ binn_list_get(list, pos, BINN_BLOB, &value, psize);
+
+ return value;
+}
+
+void * APIENTRY binn_list_list(void *list, int pos) {
+ void *value;
+
+ binn_list_get(list, pos, BINN_LIST, &value, NULL);
+
+ return value;
+}
+
+void * APIENTRY binn_list_map(void *list, int pos) {
+ void *value;
+
+ binn_list_get(list, pos, BINN_MAP, &value, NULL);
+
+ return value;
+}
+
+void * APIENTRY binn_list_object(void *list, int pos) {
+ void *value;
+
+ binn_list_get(list, pos, BINN_OBJECT, &value, NULL);
+
+ return value;
+}
+
+/***************************************************************************/
+
+signed char APIENTRY binn_map_int8(void *map, int id) {
+ signed char value;
+
+ binn_map_get(map, id, BINN_INT8, &value, NULL);
+
+ return value;
+}
+
+short APIENTRY binn_map_int16(void *map, int id) {
+ short value;
+
+ binn_map_get(map, id, BINN_INT16, &value, NULL);
+
+ return value;
+}
+
+int APIENTRY binn_map_int32(void *map, int id) {
+ int value;
+
+ binn_map_get(map, id, BINN_INT32, &value, NULL);
+
+ return value;
+}
+
+int64 APIENTRY binn_map_int64(void *map, int id) {
+ int64 value;
+
+ binn_map_get(map, id, BINN_INT64, &value, NULL);
+
+ return value;
+}
+
+unsigned char APIENTRY binn_map_uint8(void *map, int id) {
+ unsigned char value;
+
+ binn_map_get(map, id, BINN_UINT8, &value, NULL);
+
+ return value;
+}
+
+unsigned short APIENTRY binn_map_uint16(void *map, int id) {
+ unsigned short value;
+
+ binn_map_get(map, id, BINN_UINT16, &value, NULL);
+
+ return value;
+}
+
+unsigned int APIENTRY binn_map_uint32(void *map, int id) {
+ unsigned int value;
+
+ binn_map_get(map, id, BINN_UINT32, &value, NULL);
+
+ return value;
+}
+
+uint64 APIENTRY binn_map_uint64(void *map, int id) {
+ uint64 value;
+
+ binn_map_get(map, id, BINN_UINT64, &value, NULL);
+
+ return value;
+}
+
+float APIENTRY binn_map_float(void *map, int id) {
+ float value;
+
+ binn_map_get(map, id, BINN_FLOAT32, &value, NULL);
+
+ return value;
+}
+
+double APIENTRY binn_map_double(void *map, int id) {
+ double value;
+
+ binn_map_get(map, id, BINN_FLOAT64, &value, NULL);
+
+ return value;
+}
+
+BOOL APIENTRY binn_map_bool(void *map, int id) {
+ BOOL value;
+
+ binn_map_get(map, id, BINN_BOOL, &value, NULL);
+
+ return value;
+}
+
+BOOL APIENTRY binn_map_null(void *map, int id) {
+
+ return binn_map_get(map, id, BINN_NULL, NULL, NULL);
+
+}
+
+char * APIENTRY binn_map_str(void *map, int id) {
+ char *value;
+
+ binn_map_get(map, id, BINN_STRING, &value, NULL);
+
+ return value;
+}
+
+void * APIENTRY binn_map_blob(void *map, int id, int *psize) {
+ void *value;
+
+ binn_map_get(map, id, BINN_BLOB, &value, psize);
+
+ return value;
+}
+
+void * APIENTRY binn_map_list(void *map, int id) {
+ void *value;
+
+ binn_map_get(map, id, BINN_LIST, &value, NULL);
+
+ return value;
+}
+
+void * APIENTRY binn_map_map(void *map, int id) {
+ void *value;
+
+ binn_map_get(map, id, BINN_MAP, &value, NULL);
+
+ return value;
+}
+
+void * APIENTRY binn_map_object(void *map, int id) {
+ void *value;
+
+ binn_map_get(map, id, BINN_OBJECT, &value, NULL);
+
+ return value;
+}
+
+/***************************************************************************/
+
+signed char APIENTRY binn_object_int8(void *obj, char *key) {
+ signed char value;
+
+ binn_object_get(obj, key, BINN_INT8, &value, NULL);
+
+ return value;
+}
+
+short APIENTRY binn_object_int16(void *obj, char *key) {
+ short value;
+
+ binn_object_get(obj, key, BINN_INT16, &value, NULL);
+
+ return value;
+}
+
+int APIENTRY binn_object_int32(void *obj, char *key) {
+ int value;
+
+ binn_object_get(obj, key, BINN_INT32, &value, NULL);
+
+ return value;
+}
+
+int64 APIENTRY binn_object_int64(void *obj, char *key) {
+ int64 value;
+
+ binn_object_get(obj, key, BINN_INT64, &value, NULL);
+
+ return value;
+}
+
+unsigned char APIENTRY binn_object_uint8(void *obj, char *key) {
+ unsigned char value;
+
+ binn_object_get(obj, key, BINN_UINT8, &value, NULL);
+
+ return value;
+}
+
+unsigned short APIENTRY binn_object_uint16(void *obj, char *key) {
+ unsigned short value;
+
+ binn_object_get(obj, key, BINN_UINT16, &value, NULL);
+
+ return value;
+}
+
+unsigned int APIENTRY binn_object_uint32(void *obj, char *key) {
+ unsigned int value;
+
+ binn_object_get(obj, key, BINN_UINT32, &value, NULL);
+
+ return value;
+}
+
+uint64 APIENTRY binn_object_uint64(void *obj, char *key) {
+ uint64 value;
+
+ binn_object_get(obj, key, BINN_UINT64, &value, NULL);
+
+ return value;
+}
+
+float APIENTRY binn_object_float(void *obj, char *key) {
+ float value;
+
+ binn_object_get(obj, key, BINN_FLOAT32, &value, NULL);
+
+ return value;
+}
+
+double APIENTRY binn_object_double(void *obj, char *key) {
+ double value;
+
+ binn_object_get(obj, key, BINN_FLOAT64, &value, NULL);
+
+ return value;
+}
+
+BOOL APIENTRY binn_object_bool(void *obj, char *key) {
+ BOOL value;
+
+ binn_object_get(obj, key, BINN_BOOL, &value, NULL);
+
+ return value;
+}
+
+BOOL APIENTRY binn_object_null(void *obj, char *key) {
+
+ return binn_object_get(obj, key, BINN_NULL, NULL, NULL);
+
+}
+
+char * APIENTRY binn_object_str(void *obj, char *key) {
+ char *value;
+
+ binn_object_get(obj, key, BINN_STRING, &value, NULL);
+
+ return value;
+}
+
+void * APIENTRY binn_object_blob(void *obj, char *key, int *psize) {
+ void *value;
+
+ binn_object_get(obj, key, BINN_BLOB, &value, psize);
+
+ return value;
+}
+
+void * APIENTRY binn_object_list(void *obj, char *key) {
+ void *value;
+
+ binn_object_get(obj, key, BINN_LIST, &value, NULL);
+
+ return value;
+}
+
+void * APIENTRY binn_object_map(void *obj, char *key) {
+ void *value;
+
+ binn_object_get(obj, key, BINN_MAP, &value, NULL);
+
+ return value;
+}
+
+void * APIENTRY binn_object_object(void *obj, char *key) {
+ void *value;
+
+ binn_object_get(obj, key, BINN_OBJECT, &value, NULL);
+
+ return value;
+}
+
+/*************************************************************************************/
+/*************************************************************************************/
+
+BINN_PRIVATE binn * binn_alloc_item() {
+ binn *item;
+ item = (binn *) binn_malloc(sizeof(binn));
+ if (item) {
+ memset(item, 0, sizeof(binn));
+ item->header = BINN_MAGIC;
+ item->allocated = TRUE;
+ //item->writable = FALSE; -- already zeroed
+ }
+ return item;
+}
+
+/*************************************************************************************/
+
+binn * APIENTRY binn_value(int type, void *pvalue, int size, binn_mem_free freefn) {
+ int storage_type;
+ binn *item = binn_alloc_item();
+ if (item) {
+ item->type = type;
+ binn_get_type_info(type, &storage_type, NULL);
+ switch (storage_type) {
+ case BINN_STORAGE_NOBYTES:
+ break;
+ case BINN_STORAGE_STRING:
+ if (size == 0) size = strlen((char*)pvalue) + 1;
+ case BINN_STORAGE_BLOB:
+ case BINN_STORAGE_CONTAINER:
+ if (freefn == BINN_TRANSIENT) {
+ item->ptr = binn_memdup(pvalue, size);
+ if (item->ptr == NULL) {
+ free_fn(item);
+ return NULL;
+ }
+ item->freefn = free_fn;
+ if (storage_type == BINN_STORAGE_STRING) size--;
+ } else {
+ item->ptr = pvalue;
+ item->freefn = freefn;
+ }
+ item->size = size;
+ break;
+ default:
+ item->ptr = &item->vint32;
+ copy_raw_value(pvalue, item->ptr, storage_type);
+ }
+ }
+ return item;
+}
+
+/*************************************************************************************/
+
+BOOL APIENTRY binn_set_string(binn *item, char *str, binn_mem_free pfree) {
+
+ if (item == NULL || str == NULL) return FALSE;
+
+ if (pfree == BINN_TRANSIENT) {
+ item->ptr = binn_memdup(str, strlen(str) + 1);
+ if (item->ptr == NULL) return FALSE;
+ item->freefn = free_fn;
+ } else {
+ item->ptr = str;
+ item->freefn = pfree;
+ }
+
+ item->type = BINN_STRING;
+ return TRUE;
+
+}
+
+/*************************************************************************************/
+
+BOOL APIENTRY binn_set_blob(binn *item, void *ptr, int size, binn_mem_free pfree) {
+
+ if (item == NULL || ptr == NULL) return FALSE;
+
+ if (pfree == BINN_TRANSIENT) {
+ item->ptr = binn_memdup(ptr, size);
+ if (item->ptr == NULL) return FALSE;
+ item->freefn = free_fn;
+ } else {
+ item->ptr = ptr;
+ item->freefn = pfree;
+ }
+
+ item->type = BINN_BLOB;
+ item->size = size;
+ return TRUE;
+
+}
+
+/*************************************************************************************/
+/*** READ CONVERTED VALUE ************************************************************/
+/*************************************************************************************/
+
+#ifdef _MSC_VER
+#define atoi64 _atoi64
+#else
+int64 atoi64(char *str) {
+ int64 retval;
+ int is_negative=0;
+
+ if (*str == '-') {
+ is_negative = 1;
+ str++;
+ }
+ retval = 0;
+ for (; *str; str++) {
+ retval = 10 * retval + (*str - '0');
+ }
+ if (is_negative) retval *= -1;
+ return retval;
+}
+#endif
+
+/*****************************************************************************/
+
+BINN_PRIVATE BOOL is_integer(char *p) {
+ BOOL retval;
+
+ if (p == NULL) return FALSE;
+ if (*p == '-') p++;
+ if (*p == 0) return FALSE;
+
+ retval = TRUE;
+
+ for (; *p; p++) {
+ if ( (*p < '0') || (*p > '9') ) {
+ retval = FALSE;
+ }
+ }
+
+ return retval;
+}
+
+/*****************************************************************************/
+
+BINN_PRIVATE BOOL is_float(char *p) {
+ BOOL retval, number_found=FALSE;
+
+ if (p == NULL) return FALSE;
+ if (*p == '-') p++;
+ if (*p == 0) return FALSE;
+
+ retval = TRUE;
+
+ for (; *p; p++) {
+ if ((*p == '.') || (*p == ',')) {
+ if (!number_found) retval = FALSE;
+ } else if ( (*p >= '0') && (*p <= '9') ) {
+ number_found = TRUE;
+ } else {
+ return FALSE;
+ }
+ }
+
+ return retval;
+}
+
+/*************************************************************************************/
+
+BINN_PRIVATE BOOL is_bool_str(char *str, BOOL *pbool) {
+ int64 vint;
+ double vdouble;
+
+ if (str == NULL || pbool == NULL) return FALSE;
+
+ if (stricmp(str, "true") == 0) goto loc_true;
+ if (stricmp(str, "yes") == 0) goto loc_true;
+ if (stricmp(str, "on") == 0) goto loc_true;
+ //if (stricmp(str, "1") == 0) goto loc_true;
+
+ if (stricmp(str, "false") == 0) goto loc_false;
+ if (stricmp(str, "no") == 0) goto loc_false;
+ if (stricmp(str, "off") == 0) goto loc_false;
+ //if (stricmp(str, "0") == 0) goto loc_false;
+
+ if (is_integer(str)) {
+ vint = atoi64(str);
+ *pbool = (vint != 0) ? TRUE : FALSE;
+ return TRUE;
+ } else if (is_float(str)) {
+ vdouble = atof(str);
+ *pbool = (vdouble != 0) ? TRUE : FALSE;
+ return TRUE;
+ }
+
+ return FALSE;
+
+loc_true:
+ *pbool = TRUE;
+ return TRUE;
+
+loc_false:
+ *pbool = FALSE;
+ return TRUE;
+
+}
+
+/*************************************************************************************/
+
+BOOL APIENTRY binn_get_int32(binn *value, int *pint) {
+
+ if (value == NULL || pint == NULL) return FALSE;
+
+ if (type_family(value->type) == BINN_FAMILY_INT) {
+ return copy_int_value(value->ptr, pint, value->type, BINN_INT32);
+ }
+
+ switch (value->type) {
+ case BINN_FLOAT:
+ if ((value->vfloat < INT32_MIN) || (value->vfloat > INT32_MAX)) return FALSE;
+ *pint = round(value->vfloat);
+ break;
+ case BINN_DOUBLE:
+ if ((value->vdouble < INT32_MIN) || (value->vdouble > INT32_MAX)) return FALSE;
+ *pint = round(value->vdouble);
+ break;
+ case BINN_STRING:
+ if (is_integer((char*)value->ptr))
+ *pint = atoi((char*)value->ptr);
+ else if (is_float((char*)value->ptr))
+ *pint = round(atof((char*)value->ptr));
+ else
+ return FALSE;
+ break;
+ case BINN_BOOL:
+ *pint = value->vbool;
+ break;
+ default:
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*************************************************************************************/
+
+BOOL APIENTRY binn_get_int64(binn *value, int64 *pint) {
+
+ if (value == NULL || pint == NULL) return FALSE;
+
+ if (type_family(value->type) == BINN_FAMILY_INT) {
+ return copy_int_value(value->ptr, pint, value->type, BINN_INT64);
+ }
+
+ switch (value->type) {
+ case BINN_FLOAT:
+ if ((value->vfloat < INT64_MIN) || (value->vfloat > INT64_MAX)) return FALSE;
+ *pint = round(value->vfloat);
+ break;
+ case BINN_DOUBLE:
+ if ((value->vdouble < INT64_MIN) || (value->vdouble > INT64_MAX)) return FALSE;
+ *pint = round(value->vdouble);
+ break;
+ case BINN_STRING:
+ if (is_integer((char*)value->ptr))
+ *pint = atoi64((char*)value->ptr);
+ else if (is_float((char*)value->ptr))
+ *pint = round(atof((char*)value->ptr));
+ else
+ return FALSE;
+ break;
+ case BINN_BOOL:
+ *pint = value->vbool;
+ break;
+ default:
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*************************************************************************************/
+
+BOOL APIENTRY binn_get_double(binn *value, double *pfloat) {
+ int64 vint;
+
+ if (value == NULL || pfloat == NULL) return FALSE;
+
+ if (type_family(value->type) == BINN_FAMILY_INT) {
+ if (copy_int_value(value->ptr, &vint, value->type, BINN_INT64) == FALSE) return FALSE;
+ *pfloat = (double) vint;
+ return TRUE;
+ }
+
+ switch (value->type) {
+ case BINN_FLOAT:
+ *pfloat = value->vfloat;
+ break;
+ case BINN_DOUBLE:
+ *pfloat = value->vdouble;
+ break;
+ case BINN_STRING:
+ if (is_integer((char*)value->ptr))
+ *pfloat = (double) atoi64((char*)value->ptr);
+ else if (is_float((char*)value->ptr))
+ *pfloat = atof((char*)value->ptr);
+ else
+ return FALSE;
+ break;
+ case BINN_BOOL:
+ *pfloat = value->vbool;
+ break;
+ default:
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*************************************************************************************/
+
+BOOL APIENTRY binn_get_bool(binn *value, BOOL *pbool) {
+ int64 vint;
+
+ if (value == NULL || pbool == NULL) return FALSE;
+
+ if (type_family(value->type) == BINN_FAMILY_INT) {
+ if (copy_int_value(value->ptr, &vint, value->type, BINN_INT64) == FALSE) return FALSE;
+ *pbool = (vint != 0) ? TRUE : FALSE;
+ return TRUE;
+ }
+
+ switch (value->type) {
+ case BINN_BOOL:
+ *pbool = value->vbool;
+ break;
+ case BINN_FLOAT:
+ *pbool = (value->vfloat != 0) ? TRUE : FALSE;
+ break;
+ case BINN_DOUBLE:
+ *pbool = (value->vdouble != 0) ? TRUE : FALSE;
+ break;
+ case BINN_STRING:
+ return is_bool_str((char*)value->ptr, pbool);
+ default:
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*************************************************************************************/
+
+char * APIENTRY binn_get_str(binn *value) {
+ int64 vint;
+ char buf[128];
+
+ if (value == NULL) return NULL;
+
+ if (type_family(value->type) == BINN_FAMILY_INT) {
+ if (copy_int_value(value->ptr, &vint, value->type, BINN_INT64) == FALSE) return NULL;
+ sprintf(buf, "%" INT64_FORMAT, vint);
+ goto loc_convert_value;
+ }
+
+ switch (value->type) {
+ case BINN_FLOAT:
+ value->vdouble = value->vfloat;
+ case BINN_DOUBLE:
+ sprintf(buf, "%g", value->vdouble);
+ goto loc_convert_value;
+ case BINN_STRING:
+ return (char*) value->ptr;
+ case BINN_BOOL:
+ if (value->vbool)
+ strcpy(buf, "true");
+ else
+ strcpy(buf, "false");
+ goto loc_convert_value;
+ }
+
+ return NULL;
+
+loc_convert_value:
+
+ //value->vint64 = 0;
+ value->ptr = strdup(buf);
+ if (value->ptr == NULL) return NULL;
+ value->freefn = free;
+ value->type = BINN_STRING;
+ return (char*) value->ptr;
+
+}
+
+/*************************************************************************************/
+/*** GENERAL FUNCTIONS ***************************************************************/
+/*************************************************************************************/
+
+BOOL APIENTRY binn_is_container(binn *item) {
+
+ if (item == NULL) return FALSE;
+
+ switch (item->type) {
+ case BINN_LIST:
+ case BINN_MAP:
+ case BINN_OBJECT:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+
+}
+
+/*************************************************************************************/
diff --git a/src/lib/capnp-c/capn-malloc.cc b/src/lib/capnp-c/capn-malloc.cc
new file mode 100644
index 0000000..79f08c8
--- /dev/null
+++ b/src/lib/capnp-c/capn-malloc.cc
@@ -0,0 +1,424 @@
+/* vim: set sw=8 ts=8 sts=8 noet: */
+/* capn-malloc.c
+ *
+ * Copyright (C) 2013 James McKaskill
+ * Copyright (C) 2014 Steve Dee
+ *
+ * This software may be modified and distributed under the terms
+ * of the MIT license. See the LICENSE file for details.
+ */
+
+#ifdef __GNUC__
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+#endif
+
+#include "capnp_c.h"
+#include "capnp_priv.h"
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <errno.h>
+
+#ifdef MULTIPASS_TRACE_MALLOC
+#include "lib/mpmalloc.h"
+#else
+#define mpcalloc calloc
+#define mpfree free
+#endif
+
+/*
+ * 8 byte alignment is required for struct capn_segment.
+ * This struct check_segment_alignment verifies this at compile time.
+ *
+ * Unless capn_segment is defined with 8 byte alignment, check_segment_alignment
+ * fails to compile in x86 mode (or on another CPU with 32-bit pointers),
+ * as (sizeof(struct capn_segment)&7) -> (44 & 7) evaluates to 4.
+ * It compiles in x64 mode (or on another CPU with 64-bit pointers),
+ * as (sizeof(struct capn_segment)&7) -> (80 & 7) evaluates to 0.
+ */
+struct check_segment_alignment {
+ unsigned int foo : (sizeof(struct capn_segment)&7) ? -1 : 1;
+};
+
+static struct capn_segment *create(void *u, uint32_t id, int sz) {
+ struct capn_segment *s;
+ sz += sizeof(*s);
+#if 0
+ if (sz < 1024) {
+ sz = 1024;
+ } else {
+ sz = (sz + 1023) & ~1023;
+ }
+#else
+ if (sz < 4096) {
+ sz = 4096;
+ } else {
+ sz = (sz + 4095) & ~4095;
+ }
+#endif
+ s = (struct capn_segment*) mpcalloc(1, sz);
+ s->data = (char*) (s+1);
+ s->cap = sz - sizeof(*s);
+ s->user = s;
+ return s;
+}
+
+static struct capn_segment *create_local(void *u, int sz) {
+ return create(u, 0, sz);
+}
+
+void capn_init_malloc(struct capn *c) {
+ memset(c, 0, sizeof(*c));
+ c->create = &create;
+ c->create_local = &create_local;
+}
+
+void capn_free(struct capn *c) {
+ struct capn_segment *s = c->seglist;
+ while (s != NULL) {
+ struct capn_segment *n = s->next;
+ mpfree(s->user);
+ s = n;
+ }
+ capn_reset_copy(c);
+}
+
+void capn_reset_copy(struct capn *c) {
+ struct capn_segment *s = c->copylist;
+ while (s != NULL) {
+ struct capn_segment *n = s->next;
+ mpfree(s->user);
+ s = n;
+ }
+ c->copy = NULL;
+ c->copylist = NULL;
+}
+
+#define ZBUF_SZ 4096
+
+static int read_fp(void *p, size_t sz, FILE *f, struct capn_stream *z, uint8_t* zbuf, int packed) {
+ if (f && packed) {
+ z->next_out = (uint8_t*) p;
+ z->avail_out = sz;
+
+ while (z->avail_out && capn_inflate(z) == CAPN_NEED_MORE) {
+ int r;
+ memmove(zbuf, z->next_in, z->avail_in);
+ r = fread(zbuf+z->avail_in, 1, ZBUF_SZ - z->avail_in, f);
+ if (r <= 0)
+ return -1;
+ z->avail_in += r;
+ }
+ return 0;
+
+ } else if (f && !packed) {
+ return fread(p, sz, 1, f) != 1;
+
+ } else if (packed) {
+ z->next_out = (uint8_t*) p;
+ z->avail_out = sz;
+ return capn_inflate(z) != 0;
+
+ } else {
+ if (z->avail_in < sz)
+ return -1;
+ memcpy(p, z->next_in, sz);
+ z->next_in += sz;
+ z->avail_in -= sz;
+ return 0;
+ }
+}
+
+static int init_fp(struct capn *c, FILE *f, struct capn_stream *z, int packed) {
+ /*
+ * Initialize 'c' from the contents of 'f', assuming the message has been
+ * serialized with the standard framing format. From https://capnproto.org/encoding.html:
+ *
+ * When transmitting over a stream, the following should be sent. All integers are unsigned and little-endian.
+ * (4 bytes) The number of segments, minus one (since there is always at least one segment).
+ * (N * 4 bytes) The size of each segment, in words.
+ * (0 or 4 bytes) Padding up to the next word boundary.
+ * The content of each segment, in order.
+ */
+
+ struct capn_segment *s = NULL;
+ uint32_t i, segnum, total = 0;
+ uint32_t hdr[1024];
+ uint8_t zbuf[ZBUF_SZ];
+ char *data = NULL;
+
+ capn_init_malloc(c);
+
+ /* Read the first four bytes to know how many headers we have */
+ if (read_fp(&segnum, 4, f, z, zbuf, packed))
+ goto err;
+
+ segnum = capn_flip32(segnum);
+ if (segnum > 1023)
+ goto err;
+ segnum++; /* The wire encoding was zero-based */
+
+ /* Read the header list */
+ if (read_fp(hdr, 8 * (segnum/2) + 4, f, z, zbuf, packed))
+ goto err;
+
+ for (i = 0; i < segnum; i++) {
+ uint32_t n = capn_flip32(hdr[i]);
+ if (n > INT_MAX/8 || n > UINT32_MAX/8 || UINT32_MAX - total < n*8)
+ goto err;
+ hdr[i] = n*8;
+ total += hdr[i];
+ }
+
+ /* Allocate space for the data and the capn_segment structs */
+ s = (struct capn_segment*) mpcalloc(1, total + (sizeof(*s) * segnum));
+ if (!s)
+ goto err;
+
+ /* Now read the data and setup the capn_segment structs */
+ data = (char*) (s+segnum);
+ if (read_fp(data, total, f, z, zbuf, packed))
+ goto err;
+
+ for (i = 0; i < segnum; i++) {
+ s[i].len = s[i].cap = hdr[i];
+ s[i].data = data;
+ data += s[i].len;
+ capn_append_segment(c, &s[i]);
+ }
+
+ /* Set the entire region to be freed on the last segment */
+ s[segnum-1].user = s;
+
+ return 0;
+
+err:
+ memset(c, 0, sizeof(*c));
+ mpfree(s);
+ return -1;
+}
+
+int capn_init_fp(struct capn *c, FILE *f, int packed) {
+ struct capn_stream z;
+ memset(&z, 0, sizeof(z));
+ return init_fp(c, f, &z, packed);
+}
+
+int capn_init_mem(struct capn *c, const uint8_t *p, size_t sz, int packed) {
+ struct capn_stream z;
+ memset(&z, 0, sizeof(z));
+ z.next_in = p;
+ z.avail_in = sz;
+ return init_fp(c, NULL, &z, packed);
+}
+
+static void header_calc(struct capn *c, uint32_t *headerlen, size_t *headersz)
+{
+ /* segnum == 1:
+ * [segnum][segsiz]
+ * segnum == 2:
+ * [segnum][segsiz][segsiz][zeroes]
+ * segnum == 3:
+ * [segnum][segsiz][segsiz][segsiz]
+ * segnum == 4:
+ * [segnum][segsiz][segsiz][segsiz][segsiz][zeroes]
+ */
+ *headerlen = ((2 + c->segnum) / 2) * 2;
+ *headersz = 4 * *headerlen;
+}
+
+static int header_render(struct capn *c, struct capn_segment *seg, uint32_t *header, uint32_t headerlen, size_t *datasz)
+{
+ size_t i;
+
+ header[0] = capn_flip32(c->segnum - 1);
+ header[headerlen-1] = 0; /* Zero out the spare position in the header sizes */
+ for (i = 0; i < c->segnum; i++, seg = seg->next) {
+ if (0 == seg)
+ return -1;
+ *datasz += seg->len;
+ header[1 + i] = capn_flip32(seg->len / 8);
+ }
+ if (0 != seg)
+ return -1;
+
+ return 0;
+}
+
+static int capn_write_mem_packed(struct capn *c, uint8_t *p, size_t sz)
+{
+ struct capn_segment *seg;
+ struct capn_ptr root;
+ uint32_t headerlen;
+ size_t headersz, datasz = 0;
+ uint32_t *header;
+ struct capn_stream z;
+ int ret;
+
+ root = capn_root(c);
+ header_calc(c, &headerlen, &headersz);
+ header = (uint32_t*) (p + headersz + 2); /* must reserve two bytes for worst case expansion */
+
+ if (sz < headersz*2 + 2) /* We must have space for temporary writing of header to deflate */
+ return -1;
+
+ ret = header_render(c, root.seg, header, headerlen, &datasz);
+ if (ret != 0)
+ return -1;
+
+ memset(&z, 0, sizeof(z));
+ z.next_in = (uint8_t *)header;
+ z.avail_in = headersz;
+ z.next_out = p;
+ z.avail_out = sz;
+
+ // pack the headers
+ ret = capn_deflate(&z);
+ if (ret != 0 || z.avail_in != 0)
+ return -1;
+
+ for (seg = root.seg; seg; seg = seg->next) {
+ z.next_in = (uint8_t *)seg->data;
+ z.avail_in = seg->len;
+ ret = capn_deflate(&z);
+ if (ret != 0 || z.avail_in != 0)
+ return -1;
+ }
+
+ return sz - z.avail_out;
+}
+
+int
+capn_write_mem(struct capn *c, uint8_t *p, size_t sz, int packed)
+{
+ struct capn_segment *seg;
+ struct capn_ptr root;
+ uint32_t headerlen;
+ size_t headersz, datasz = 0;
+ uint32_t *header;
+ int ret;
+
+ if (c->segnum == 0)
+ return -1;
+
+ if (packed)
+ return capn_write_mem_packed(c, p, sz);
+
+ root = capn_root(c);
+ header_calc(c, &headerlen, &headersz);
+ header = (uint32_t*) p;
+
+ if (sz < headersz)
+ return -1;
+
+ ret = header_render(c, root.seg, header, headerlen, &datasz);
+ if (ret != 0)
+ return -1;
+
+ if (sz < headersz + datasz)
+ return -1;
+
+ p += headersz;
+
+ for (seg = root.seg; seg; seg = seg->next) {
+ memcpy(p, seg->data, seg->len);
+ p += seg->len;
+ }
+
+ return headersz+datasz;
+}
+
+static int _write_fd(ssize_t (*write_fd)(int fd, const void *p, size_t count), int fd, void *p, size_t count)
+{
+ ssize_t ret;
+ size_t sent = 0;
+
+ while (sent < count) {
+ ret = write_fd(fd, ((uint8_t*)p)+sent, count-sent);
+ if (ret < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ continue;
+ else
+ return -1;
+ }
+ sent += ret;
+ }
+
+ return 0;
+}
+
+int capn_write_fd(struct capn *c, ssize_t (*write_fd)(int fd, const void *p, size_t count), int fd, int packed)
+{
+ unsigned char buf[4096];
+ struct capn_segment *seg;
+ struct capn_ptr root;
+ uint32_t headerlen;
+ size_t headersz, datasz = 0;
+ int ret;
+ struct capn_stream z;
+ unsigned char *p;
+
+ if (c->segnum == 0)
+ return -1;
+
+ root = capn_root(c);
+ header_calc(c, &headerlen, &headersz);
+
+ if (sizeof(buf) < headersz)
+ return -1;
+
+ ret = header_render(c, root.seg, (uint32_t*)buf, headerlen, &datasz);
+ if (ret != 0)
+ return -1;
+
+ if (packed) {
+ const int headerrem = sizeof(buf) - headersz;
+ const int maxpack = headersz + 2;
+ if (headerrem < maxpack)
+ return -1;
+
+ memset(&z, 0, sizeof(z));
+ z.next_in = buf;
+ z.avail_in = headersz;
+ z.next_out = buf + headersz;
+ z.avail_out = headerrem;
+ ret = capn_deflate(&z);
+ if (ret != 0)
+ return -1;
+
+ p = buf + headersz;
+ headersz = headerrem - z.avail_out;
+ } else {
+ p = buf;
+ }
+
+ ret = _write_fd(write_fd, fd, p, headersz);
+ if (ret < 0)
+ return -1;
+
+ datasz = headersz;
+ for (seg = root.seg; seg; seg = seg->next) {
+ size_t bufsz;
+ if (packed) {
+ memset(&z, 0, sizeof(z));
+ z.next_in = (uint8_t*)seg->data;
+ z.avail_in = seg->len;
+ z.next_out = buf;
+ z.avail_out = sizeof(buf);
+ ret = capn_deflate(&z);
+ if (ret != 0)
+ return -1;
+ p = buf;
+ bufsz = sizeof(buf) - z.avail_out;
+ } else {
+ p = (uint8_t*)seg->data;
+ bufsz = seg->len;
+ }
+ ret = _write_fd(write_fd, fd, p, bufsz);
+ if (ret < 0)
+ return -1;
+ datasz += bufsz;
+ }
+
+ return datasz;
+}
diff --git a/src/lib/capnp-c/capn-stream.cc b/src/lib/capnp-c/capn-stream.cc
new file mode 100644
index 0000000..135d1b2
--- /dev/null
+++ b/src/lib/capnp-c/capn-stream.cc
@@ -0,0 +1,217 @@
+/* vim: set sw=8 ts=8 sts=8 noet: */
+/* capn-stream.c
+ *
+ * Copyright (C) 2013 James McKaskill
+ *
+ * This software may be modified and distributed under the terms
+ * of the MIT license. See the LICENSE file for details.
+ */
+
+#include "capnp_c.h"
+#include "capnp_priv.h"
+#include <string.h>
+
+#ifndef min
+static unsigned min(unsigned a, unsigned b) { return (a < b) ? a : b; }
+#endif
+
+int capn_deflate(struct capn_stream* s) {
+ if (s->avail_in % 8) {
+ return CAPN_MISALIGNED;
+ }
+
+ while (s->avail_in) {
+ int i;
+ size_t sz;
+ uint8_t hdr = 0;
+ uint8_t *p;
+
+ if (!s->avail_out)
+ return CAPN_NEED_MORE;
+
+ if (s->raw > 0) {
+ sz = min(s->raw, min(s->avail_in, s->avail_out));
+ memcpy(s->next_out, s->next_in, sz);
+ s->next_out += sz;
+ s->next_in += sz;
+ s->avail_out -= sz;
+ s->avail_in -= sz;
+ s->raw -= sz;
+ continue;
+ }
+
+ if (s->avail_in < 8)
+ return CAPN_NEED_MORE;
+
+ sz = 0;
+ for (i = 0; i < 8; i++) {
+ if (s->next_in[i]) {
+ sz ++;
+ hdr |= 1 << i;
+ }
+ }
+
+ switch (sz) {
+ case 0:
+ if (s->avail_out < 2)
+ return CAPN_NEED_MORE;
+
+ s->next_out[0] = 0;
+ for (sz = 1; sz < min(s->avail_in/8, 256); sz++) {
+ if (((uint64_t*) s->next_in)[sz] != 0) {
+ break;
+ }
+ }
+
+ s->next_out[1] = (uint8_t) (sz-1);
+ s->next_in += sz*8;
+ s->avail_in -= sz*8;
+ s->next_out += 2;
+ s->avail_out -= 2;
+ continue;
+
+ case 8:
+ if (s->avail_out < 10)
+ return CAPN_NEED_MORE;
+
+ s->next_out[0] = 0xFF;
+ memcpy(s->next_out+1, s->next_in, 8);
+ s->next_in += 8;
+ s->avail_in -= 8;
+
+ s->raw = min(s->avail_in, 256*8);
+ if ((p = (uint8_t*) memchr(s->next_in, 0, s->raw)) != NULL) {
+ s->raw = (p - s->next_in) & ~7;
+ }
+
+ s->next_out[9] = (uint8_t) (s->raw/8);
+ s->next_out += 10;
+ s->avail_out -= 10;
+ continue;
+
+ default:
+ if (s->avail_out < 1U + sz)
+ return CAPN_NEED_MORE;
+
+ *(s->next_out++) = hdr;
+ for (i = 0; i < 8; i++) {
+ if (s->next_in[i]) {
+ *(s->next_out++) = s->next_in[i];
+ }
+ }
+ s->avail_out -= sz + 1;
+ s->next_in += 8;
+ s->avail_in -= 8;
+ continue;
+ }
+ }
+
+ return 0;
+}
+
+int capn_inflate(struct capn_stream* s) {
+ while (s->avail_out) {
+ int i;
+ size_t sz;
+ uint8_t hdr;
+ uint8_t *wr;
+
+ if (s->avail_buf && s->avail_out >= s->avail_buf) {
+ memcpy(s->next_out, s->inflate_buf, s->avail_buf);
+ s->next_out += s->avail_buf;
+ s->avail_out -= s->avail_buf;
+ s->avail_buf = 0;
+ if (!s->avail_out)
+ return 0;
+ }
+ if (s->avail_buf && s->avail_out < s->avail_buf) {
+ memcpy(s->next_out, s->inflate_buf, s->avail_out);
+ memmove(s->inflate_buf, s->inflate_buf + s->avail_out,
+ s->avail_buf - s->avail_out);
+ s->avail_buf -= s->avail_out;
+ s->avail_out = 0;
+ return 0;
+ }
+
+ if (s->zeros > 0) {
+ sz = min(s->avail_out, s->zeros);
+ memset(s->next_out, 0, sz);
+ s->next_out += sz;
+ s->avail_out -= sz;
+ s->zeros -= sz;
+ continue;
+ }
+
+ if (s->raw > 0) {
+ if (s->avail_in == 0)
+ return CAPN_NEED_MORE;
+
+ sz = min(min(s->avail_out, s->raw), s->avail_in);
+ memcpy(s->next_out, s->next_in, sz);
+ s->next_in += sz;
+ s->next_out += sz;
+ s->avail_in -= sz;
+ s->avail_out -= sz;
+ s->raw -= sz;
+ continue;
+ }
+
+ if (s->avail_in == 0)
+ return 0;
+ else if (s->avail_in < 2)
+ return CAPN_NEED_MORE;
+
+ switch (s->next_in[0]) {
+ case 0xFF:
+ /* 0xFF is followed by 8 bytes raw, followed by
+ * a byte with length in words to read raw */
+ if (s->avail_in < 10)
+ return CAPN_NEED_MORE;
+
+ memcpy(s->inflate_buf, s->next_in+1, 8);
+ s->avail_buf = 8;
+
+ s->raw = s->next_in[9] * 8;
+ s->next_in += 10;
+ s->avail_in -= 10;
+ continue;
+
+ case 0x00:
+ /* 0x00 is followed by a single byte indicating
+ * the count of consecutive zero value words
+ * minus 1 */
+ s->zeros = (s->next_in[1] + 1) * 8;
+ s->next_in += 2;
+ s->avail_in -= 2;
+ continue;
+
+ default:
+ hdr = s->next_in[0];
+ sz = 0;
+ for (i = 0; i < 8; i++) {
+ if (hdr & (1 << i))
+ sz++;
+ }
+ if (s->avail_in < 1U + sz)
+ return CAPN_NEED_MORE;
+
+ s->next_in += 1;
+
+ wr = s->inflate_buf;
+ for (i = 0; i < 8; i++) {
+ if (hdr & (1 << i)) {
+ *wr++ = *s->next_in++;
+ } else {
+ *wr++ = 0;
+ }
+ }
+
+ s->avail_buf = 8;
+ s->avail_in -= 1 + sz;
+ continue;
+ }
+ }
+
+ return 0;
+}
+
diff --git a/src/lib/capnp-c/capn.cc b/src/lib/capnp-c/capn.cc
new file mode 100644
index 0000000..bbd7be3
--- /dev/null
+++ b/src/lib/capnp-c/capn.cc
@@ -0,0 +1,1117 @@
+/* vim: set sw=8 ts=8 sts=8 noet: */
+/* capn.c
+ *
+ * Copyright (C) 2013 James McKaskill
+ *
+ * This software may be modified and distributed under the terms
+ * of the MIT license. See the LICENSE file for details.
+ */
+
+#ifdef __GNUC__
+#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
+#endif
+
+#include "capnp_c.h"
+
+#include <stdlib.h>
+#include <string.h>
+#ifndef _MSC_VER
+#ifndef MULTIPASS_ARCH_arduino_nano
+#include <sys/param.h>
+#endif
+#endif
+
+#define STRUCT_PTR 0
+#define LIST_PTR 1
+#define FAR_PTR 2
+#define DOUBLE_PTR 6
+
+#define VOID_LIST 0
+#define BIT_1_LIST 1
+#define BYTE_1_LIST 2
+#define BYTE_2_LIST 3
+#define BYTE_4_LIST 4
+#define BYTE_8_LIST 5
+#define PTR_LIST 6
+#define COMPOSITE_LIST 7
+
+#define U64(val) ((uint64_t) (val))
+#define I64(val) ((int64_t) (val))
+#define U32(val) ((uint32_t) (val))
+#define I32(val) ((int32_t) (val))
+#define U16(val) ((uint16_t) (val))
+#define I16(val) ((int16_t) (val))
+
+#ifndef min
+static int min(int a, int b) { return (a < b) ? a : b; }
+#endif
+
+#ifdef BYTE_ORDER
+#define CAPN_LITTLE (BYTE_ORDER == LITTLE_ENDIAN)
+#elif defined(__BYTE_ORDER)
+#define CAPN_LITTLE (__BYTE_ORDER == __LITTLE_ENDIAN)
+#else
+#define CAPN_LITTLE 0
+#endif
+
+struct capn_tree *capn_tree_insert(struct capn_tree *root, struct capn_tree *n) {
+ n->red = 1;
+ n->link[0] = n->link[1] = NULL;
+
+ for (;;) {
+ /* parent, uncle, grandparent, great grandparent link */
+ struct capn_tree *p, *u, *g, **gglink;
+ int dir;
+
+ /* Case 1: N is root */
+ p = n->parent;
+ if (!p) {
+ n->red = 0;
+ root = n;
+ break;
+ }
+
+ /* Case 2: p is black */
+ if (!p->red) {
+ break;
+ }
+
+ g = p->parent;
+ dir = (p == g->link[1]);
+
+ /* Case 3: P and U are red, switch g to red, but must
+ * loop as G could be root or have a red parent
+ * g to G
+ * / \ / \
+ * P U p u
+ * / /
+ * N N
+ */
+ u = g->link[!dir];
+ if (u != NULL && u->red) {
+ p->red = 0;
+ u->red = 0;
+ g->red = 1;
+ n = g;
+ continue;
+ }
+
+ if (!g->parent) {
+ gglink = &root;
+ } else if (g->parent->link[1] == g) {
+ gglink = &g->parent->link[1];
+ } else {
+ gglink = &g->parent->link[0];
+ }
+
+ if (dir != (n == p->link[1])) {
+ /* Case 4: rotate on P, then on g
+ * here dir is /
+ * g to g to n
+ * / \ / \ / \
+ * P u N u P G
+ * / \ / \ /| / \
+ * 1 N P 3 1 2 3 u
+ * / \ / \
+ * 2 3 1 2
+ */
+ struct capn_tree *two = n->link[dir];
+ struct capn_tree *three = n->link[!dir];
+ p->link[!dir] = two;
+ g->link[dir] = three;
+ n->link[dir] = p;
+ n->link[!dir] = g;
+ *gglink = n;
+ n->parent = g->parent;
+ p->parent = n;
+ g->parent = n;
+ if (two)
+ two->parent = p;
+ if (three)
+ three->parent = g;
+ n->red = 0;
+ g->red = 1;
+ } else {
+ /* Case 5: rotate on g
+ * here dir is /
+ * g to p
+ * / \ / \
+ * P u N G
+ * / \ /| / \
+ * N 3 1 2 3 u
+ * / \
+ * 1 2
+ */
+ struct capn_tree *three = p->link[!dir];
+ g->link[dir] = three;
+ p->link[!dir] = g;
+ *gglink = p;
+ p->parent = g->parent;
+ g->parent = p;
+ if (three)
+ three->parent = g;
+ g->red = 1;
+ p->red = 0;
+ }
+
+ break;
+ }
+
+ return root;
+}
+
+void capn_append_segment(struct capn *c, struct capn_segment *s) {
+ s->id = c->segnum++;
+ s->capn = c;
+ s->next = NULL;
+
+ if (c->lastseg) {
+ c->lastseg->next = s;
+ c->lastseg->hdr.link[1] = &s->hdr;
+ s->hdr.parent = &c->lastseg->hdr;
+ } else {
+ c->seglist = s;
+ s->hdr.parent = NULL;
+ }
+
+ c->lastseg = s;
+ c->segtree = capn_tree_insert(c->segtree, &s->hdr);
+}
+
+static char *new_data(struct capn *c, int sz, struct capn_segment **ps) {
+ struct capn_segment *s;
+
+ /* find a segment with sufficient data */
+ for (s = c->seglist; s != NULL; s = s->next) {
+ if (s->len + sz <= s->cap) {
+ goto end;
+ }
+ }
+
+ s = c->create ? c->create(c->user, c->segnum, sz) : NULL;
+ if (!s) {
+ *ps = NULL;
+ return NULL;
+ }
+
+ capn_append_segment(c, s);
+end:
+ *ps = s;
+ s->len += sz;
+ return s->data + s->len - sz;
+}
+
+static struct capn_segment *lookup_segment(struct capn* c, struct capn_segment *s, uint32_t id) {
+ struct capn_tree **x;
+ struct capn_segment *y = NULL;
+
+ if (s && s->id == id)
+ return s;
+ if (!c)
+ return NULL;
+
+ if (id < c->segnum) {
+ x = &c->segtree;
+ while (*x) {
+ y = (struct capn_segment*) *x;
+ if (id == y->id) {
+ return y;
+ } else if (id < y->id) {
+ x = &y->hdr.link[0];
+ } else {
+ x = &y->hdr.link[1];
+ }
+ }
+ } else {
+ /* Otherwise `x` may be uninitialized */
+ return NULL;
+ }
+
+ s = c->lookup ? c->lookup(c->user, id) : NULL;
+ if (!s)
+ return NULL;
+
+ if (id < c->segnum) {
+ s->id = id;
+ s->capn = c;
+ s->next = c->seglist;
+ c->seglist = s;
+ s->hdr.parent = &y->hdr;
+ *x = &s->hdr;
+ c->segtree = capn_tree_insert(c->segtree, &s->hdr);
+ } else {
+ c->segnum = id;
+ capn_append_segment(c, s);
+ }
+
+ return s;
+}
+
+static uint64_t lookup_double(struct capn_segment **s, char **d, uint64_t val) {
+ uint64_t far, tag;
+ size_t off = (U32(val) >> 3) * 8;
+ char *p;
+
+ if ((*s = lookup_segment((*s)->capn, *s, U32(val >> 32))) == NULL) {
+ return 0;
+ }
+
+ p = (*s)->data + off;
+ if (off + 16 > (*s)->len) {
+ return 0;
+ }
+
+ far = capn_flip64(*(uint64_t*) p);
+ tag = capn_flip64(*(uint64_t*) (p+8));
+
+ /* the far tag should not be another double, and the tag
+ * should be struct/list and have no offset */
+ if ((far&7) != FAR_PTR || U32(tag) > LIST_PTR) {
+ return 0;
+ }
+
+ if ((*s = lookup_segment((*s)->capn, *s, U32(far >> 32))) == NULL) {
+ return 0;
+ }
+
+ /* -8 because far pointers reference from the start of
+ * the segment, but offsets reference the end of the
+ * pointer data. Here *d points to where an equivalent
+ * ptr would be.
+ */
+ *d = (*s)->data - 8;
+ return U64(U32(far) >> 3 << 2) | tag;
+}
+
+static uint64_t lookup_far(struct capn_segment **s, char **d, uint64_t val) {
+ size_t off = (U32(val) >> 3) * 8;
+
+ if ((*s = lookup_segment((*s)->capn, *s, U32(val >> 32))) == NULL) {
+ return 0;
+ }
+
+ if (off + 8 > (*s)->len) {
+ return 0;
+ }
+
+ *d = (*s)->data + off;
+ return capn_flip64(*(uint64_t*)*d);
+}
+
+static char *struct_ptr(struct capn_segment *s, char *d, int minsz) {
+ uint64_t val = capn_flip64(*(uint64_t*)d);
+ uint16_t datasz;
+
+ switch (val&7) {
+ case FAR_PTR:
+ val = lookup_far(&s, &d, val);
+ break;
+ case DOUBLE_PTR:
+ val = lookup_double(&s, &d, val);
+ break;
+ }
+
+ datasz = U16(val >> 32);
+ d += (I32(U32(val)) << 1) + 8;
+
+ if (val != 0 && (val&3) != STRUCT_PTR && datasz >= minsz && s->data <= d && d < s->data + s->len) {
+ return d;
+ }
+
+ return NULL;
+}
+
+static capn_ptr read_ptr(struct capn_segment *s, char *d) {
+ capn_ptr ret = {CAPN_NULL};
+ uint64_t val;
+ char *e = 0;
+
+ val = capn_flip64(*(uint64_t*) d);
+
+ switch (val&7) {
+ case FAR_PTR:
+ val = lookup_far(&s, &d, val);
+ ret.has_ptr_tag = (U32(val) >> 2) == 0;
+ break;
+ case DOUBLE_PTR:
+ val = lookup_double(&s, &d, val);
+ break;
+ }
+
+ d += (I32(U32(val)) >> 2) * 8 + 8;
+
+ if (d < s->data) {
+ goto err;
+ }
+
+ switch (val & 3) {
+ case STRUCT_PTR:
+ ret.type = val ? CAPN_STRUCT : CAPN_NULL;
+ goto struct_common;
+
+ struct_common:
+ ret.datasz = U32(U16(val >> 32)) * 8;
+ ret.ptrs = U32(U16(val >> 48));
+ e = d + ret.datasz + 8 * ret.ptrs;
+ break;
+
+ case LIST_PTR:
+ ret.type = CAPN_LIST;
+ ret.len = val >> 35;
+
+ switch ((val >> 32) & 7) {
+ case VOID_LIST:
+ e = d;
+ break;
+ case BIT_1_LIST:
+ ret.type = CAPN_BIT_LIST;
+ ret.datasz = (ret.len+7)/8;
+ e = d + ret.datasz;
+ break;
+ case BYTE_1_LIST:
+ ret.datasz = 1;
+ e = d + ret.len;
+ break;
+ case BYTE_2_LIST:
+ ret.datasz = 2;
+ e = d + ret.len * 2;
+ break;
+ case BYTE_4_LIST:
+ ret.datasz = 4;
+ e = d + ret.len * 4;
+ break;
+ case BYTE_8_LIST:
+ ret.datasz = 8;
+ e = d + ret.len * 8;
+ break;
+ case PTR_LIST:
+ ret.type = CAPN_PTR_LIST;
+ e = d + ret.len * 8;
+ break;
+ case COMPOSITE_LIST:
+ if ((size_t)((d+8) - s->data) > s->len) {
+ goto err;
+ }
+
+ val = capn_flip64(*(uint64_t*) d);
+
+ d += 8;
+ e = d + ret.len * 8;
+
+ ret.datasz = U32(U16(val >> 32)) * 8;
+ ret.ptrs = U32(U16(val >> 48));
+ ret.len = U32(val) >> 2;
+ ret.is_composite_list = 1;
+
+ if ((ret.datasz + 8*ret.ptrs) * ret.len != e - d) {
+ goto err;
+ }
+ break;
+ }
+ break;
+
+ default:
+ goto err;
+ }
+
+ if ((size_t)(e - s->data) > s->len)
+ goto err;
+
+ ret.data = d;
+ ret.seg = s;
+ return ret;
+err:
+ memset(&ret, 0, sizeof(ret));
+ return ret;
+}
+
+void capn_resolve(capn_ptr *p) {
+ if (p->type == CAPN_FAR_POINTER) {
+ *p = read_ptr(p->seg, p->data);
+ }
+}
+
+/* TODO: should this handle CAPN_BIT_LIST? */
+capn_ptr capn_getp(capn_ptr p, int off, int resolve) {
+ capn_ptr ret = {CAPN_FAR_POINTER};
+ ret.seg = p.seg;
+
+ capn_resolve(&p);
+
+ switch (p.type) {
+ case CAPN_LIST:
+ /* Return an inner pointer */
+ if (off < p.len) {
+ capn_ptr ret = {CAPN_STRUCT};
+ ret.is_list_member = 1;
+ ret.data = p.data + off * (p.datasz + 8*p.ptrs);
+ ret.seg = p.seg;
+ ret.datasz = p.datasz;
+ ret.ptrs = p.ptrs;
+ return ret;
+ } else {
+ goto err;
+ }
+
+ case CAPN_STRUCT:
+ if (off >= p.ptrs) {
+ goto err;
+ }
+ ret.data = p.data + p.datasz + 8*off;
+ break;
+
+ case CAPN_PTR_LIST:
+ if (off >= p.len) {
+ goto err;
+ }
+ ret.data = p.data + 8*off;
+ break;
+
+ default:
+ goto err;
+ }
+
+ if (resolve) {
+ ret = read_ptr(ret.seg, ret.data);
+ }
+
+ return ret;
+
+err:
+ memset(&p, 0, sizeof(p));
+ return p;
+}
+
+static void write_ptr_tag(char *d, capn_ptr p, int off) {
+ uint64_t val = U64(U32(I32(off/8) << 2));
+
+ switch (p.type) {
+ case CAPN_STRUCT:
+ val |= STRUCT_PTR | (U64(p.datasz/8) << 32) | (U64(p.ptrs) << 48);
+ break;
+
+ case CAPN_LIST:
+ if (p.is_composite_list) {
+ val |= LIST_PTR | (U64(COMPOSITE_LIST) << 32) | (U64(p.len * (p.datasz/8 + p.ptrs)) << 35);
+ } else {
+ val |= LIST_PTR | (U64(p.len) << 35);
+
+ switch (p.datasz) {
+ case 8:
+ val |= (U64(BYTE_8_LIST) << 32);
+ break;
+ case 4:
+ val |= (U64(BYTE_4_LIST) << 32);
+ break;
+ case 2:
+ val |= (U64(BYTE_2_LIST) << 32);
+ break;
+ case 1:
+ val |= (U64(BYTE_1_LIST) << 32);
+ break;
+ case 0:
+ val |= (U64(VOID_LIST) << 32);
+ break;
+ }
+ }
+ break;
+
+ case CAPN_BIT_LIST:
+ val |= LIST_PTR | (U64(BIT_1_LIST) << 32) | (U64(p.len) << 35);
+ break;
+
+ case CAPN_PTR_LIST:
+ val |= LIST_PTR | (U64(PTR_LIST) << 32) | (U64(p.len) << 35);
+ break;
+
+ default:
+ val = 0;
+ break;
+ }
+
+ *(uint64_t*) d = capn_flip64(val);
+}
+
+static void write_far_ptr(char *d, struct capn_segment *s, char *tgt) {
+ *(uint64_t*) d = capn_flip64(FAR_PTR | U64(tgt - s->data) | (U64(s->id) << 32));
+}
+
+static void write_double_far(char *d, struct capn_segment *s, char *tgt) {
+ *(uint64_t*) d = capn_flip64(DOUBLE_PTR | U64(tgt - s->data) | (U64(s->id) << 32));
+}
+
+#define NEED_TO_COPY 1
+
+static int write_ptr(struct capn_segment *s, char *d, capn_ptr p) {
+ /* note p.seg can be NULL if its a ptr to static data */
+ char *pdata = p.data - 8*p.is_composite_list;
+
+ if (p.type == CAPN_NULL || (p.type == CAPN_STRUCT && p.datasz == 0 && p.ptrs == 0)) {
+ write_ptr_tag(d, p, 0);
+ return 0;
+
+ } else if (!p.seg || p.seg->capn != s->capn || p.is_list_member) {
+ return NEED_TO_COPY;
+
+ } else if (p.seg == s) {
+ write_ptr_tag(d, p, pdata - d - 8);
+ return 0;
+
+ } else if (p.has_ptr_tag) {
+ /* By lucky chance, the data has a tag in front
+ * of it. This happens when new_object had to move
+ * the data to a new segment. */
+ write_far_ptr(d, p.seg, pdata-8);
+ return 0;
+
+ } else if (p.seg->len + 8 <= p.seg->cap) {
+ /* The target segment has enough room for tag */
+ char *t = p.seg->data + p.seg->len;
+ write_ptr_tag(t, p, pdata - t - 8);
+ write_far_ptr(d, p.seg, t);
+ p.seg->len += 8;
+ return 0;
+
+ } else {
+ /* have to allocate room for a double far
+ * pointer */
+ char *t;
+
+ if (s->len + 16 <= s->cap) {
+ /* Try and allocate in the src segment
+ * first. This should improve lookup on
+ * read. */
+ t = s->data + s->len;
+ s->len += 16;
+ } else {
+ t = new_data(s->capn, 16, &s);
+ if (!t) return -1;
+ }
+
+ write_far_ptr(t, p.seg, pdata);
+ write_ptr_tag(t+8, p, 0);
+ write_double_far(d, s, t);
+ return 0;
+ }
+}
+
+struct copy {
+ struct capn_tree hdr;
+ struct capn_ptr to, from;
+ char *fbegin, *fend;
+};
+
+static capn_ptr new_clone(struct capn_segment *s, capn_ptr p) {
+ switch (p.type) {
+ case CAPN_STRUCT:
+ return capn_new_struct(s, p.datasz, p.ptrs);
+ case CAPN_PTR_LIST:
+ return capn_new_ptr_list(s, p.len);
+ case CAPN_BIT_LIST:
+ return capn_new_list1(s, p.len).p;
+ case CAPN_LIST:
+ return capn_new_list(s, p.len, p.datasz, p.ptrs);
+ default:
+ return p;
+ }
+}
+
+static int is_ptr_equal(const struct capn_ptr *a, const struct capn_ptr *b) {
+ return a->data == b->data
+ && a->type == b->type
+ && a->len == b->len
+ && a->datasz == b->datasz
+ && a->ptrs == b->ptrs;
+}
+
+static int data_size(struct capn_ptr p) {
+ switch (p.type) {
+ case CAPN_BIT_LIST:
+ return p.datasz;
+ case CAPN_PTR_LIST:
+ return p.len*8;
+ case CAPN_STRUCT:
+ return p.datasz + 8*p.ptrs;
+ case CAPN_LIST:
+ return p.len * (p.datasz + 8*p.ptrs) + 8*p.is_composite_list;
+ default:
+ return 0;
+ }
+}
+
+static int copy_ptr(struct capn_segment *seg, char *data, struct capn_ptr *t, struct capn_ptr *f, int *dep) {
+ struct capn *c = seg->capn;
+ struct copy *cp = NULL;
+ struct capn_tree **xcp;
+ char *fbegin = f->data - 8*f->is_composite_list;
+ char *fend = fbegin + data_size(*f);
+ int zero_sized = (fend == fbegin);
+
+ /* We always copy list members as it would otherwise be an
+ * overlapped pointer (the data is owned by the enclosing list).
+ * We do not bother with the overlapped lookup for zero sized
+ * structures/lists as they never overlap. Nor do we add them to
+ * the copy list as there is no data to be shared by multiple
+ * pointers.
+ */
+
+ xcp = &c->copy;
+ while (*xcp && !zero_sized) {
+ cp = (struct copy*) *xcp;
+ if (fend <= cp->fbegin) {
+ xcp = &cp->hdr.link[0];
+ } else if (cp->fend <= fbegin) {
+ xcp = &cp->hdr.link[1];
+ } else if (is_ptr_equal(f, &cp->from)) {
+ /* we already have a copy so just point to that */
+ return write_ptr(seg, data, cp->to);
+ } else {
+ /* pointer to overlapped data */
+ return -1;
+ }
+ }
+
+ /* no copy found - have to create a new copy */
+ *t = new_clone(seg, *f);
+
+ if (write_ptr(seg, data, *t))
+ return -1;
+
+ /* add the copy to the copy tree so we can look for overlapping
+ * source pointers and handle recursive structures */
+ if (!zero_sized) {
+ struct copy *n;
+ struct capn_segment *cs = c->copylist;
+
+ /* need to allocate a struct copy */
+ if (!cs || cs->len + (int)sizeof(*n) > cs->cap) {
+ cs = c->create_local ? c->create_local(c->user, sizeof(*n)) : NULL;
+ if (!cs) {
+ /* can't allocate a copy structure */
+ return -1;
+ }
+ cs->next = c->copylist;
+ c->copylist = cs;
+ }
+
+ n = (struct copy*) (cs->data + cs->len);
+ cs->len += sizeof(*n);
+
+ n->from = *f;
+ n->to = *t;
+ n->fbegin = fbegin;
+ n->fend = fend;
+
+ *xcp = &n->hdr;
+ n->hdr.parent = &cp->hdr;
+
+ c->copy = capn_tree_insert(c->copy, &n->hdr);
+ }
+
+ /* minimize the number of types the main copy routine has to
+ * deal with to just CAPN_LIST and CAPN_PTR_LIST. ptr list only
+ * needs t->type, t->len, t->data, t->seg, f->data, f->seg to
+ * be valid */
+ switch (t->type) {
+ case CAPN_STRUCT:
+ if (t->datasz) {
+ memcpy(t->data, f->data, t->datasz);
+ t->data += t->datasz;
+ f->data += t->datasz;
+ }
+ if (t->ptrs) {
+ t->type = CAPN_PTR_LIST;
+ t->len = t->ptrs;
+ (*dep)++;
+ }
+ return 0;
+
+ case CAPN_BIT_LIST:
+ memcpy(t->data, f->data, t->datasz);
+ return 0;
+
+ case CAPN_LIST:
+ if (!t->len) {
+ /* empty list - nothing to copy */
+ } else if (t->ptrs && t->datasz) {
+ (*dep)++;
+ } else if (t->datasz) {
+ memcpy(t->data, f->data, t->len * t->datasz);
+ } else if (t->ptrs) {
+ t->type = CAPN_PTR_LIST;
+ t->len *= t->ptrs;
+ (*dep)++;
+ }
+ return 0;
+
+ case CAPN_PTR_LIST:
+ if (t->len) {
+ (*dep)++;
+ }
+ return 0;
+
+ default:
+ return -1;
+ }
+}
+
+static void copy_list_member(capn_ptr* t, capn_ptr *f, int *dep) {
+ /* copy struct data */
+ int sz = min(t->datasz, f->datasz);
+ memcpy(t->data, f->data, sz);
+ memset(t->data + sz, 0, t->datasz - sz);
+ t->data += t->datasz;
+ f->data += f->datasz;
+
+ /* reset excess pointers */
+ sz = min(t->ptrs, f->ptrs);
+ memset(t->data + sz, 0, 8*(t->ptrs - sz));
+
+ /* create a pointer list for the main loop to copy */
+ if (t->ptrs) {
+ t->type = CAPN_PTR_LIST;
+ t->len = t->ptrs;
+ (*dep)++;
+ }
+}
+
+#define MAX_COPY_DEPTH 32
+
+/* TODO: handle CAPN_BIT_LIST and setting from an inner bit list member */
+int capn_setp(capn_ptr p, int off, capn_ptr tgt) {
+ struct capn_ptr to[MAX_COPY_DEPTH], from[MAX_COPY_DEPTH];
+ char *data;
+ int err, dep = 0;
+
+ capn_resolve(&p);
+
+ if (tgt.type == CAPN_FAR_POINTER && tgt.seg->capn == p.seg->capn) {
+ uint64_t val = capn_flip64(*(uint64_t*) tgt.data);
+ if ((val & 3) == FAR_PTR) {
+ *(uint64_t*) p.data = *(uint64_t*) tgt.data;
+ return 0;
+ }
+ }
+
+ capn_resolve(&tgt);
+
+ switch (p.type) {
+ case CAPN_LIST:
+ if (off >= p.len || tgt.type != CAPN_STRUCT)
+ return -1;
+
+ to[0] = p;
+ to[0].data += off * (p.datasz + 8*p.ptrs);
+ from[0] = tgt;
+ copy_list_member(to, from, &dep);
+ break;
+
+ case CAPN_PTR_LIST:
+ if (off >= p.len)
+ return -1;
+ data = p.data + 8*off;
+ goto copy_ptr;
+
+ case CAPN_STRUCT:
+ if (off >= p.ptrs)
+ return -1;
+ data = p.data + p.datasz + 8*off;
+ goto copy_ptr;
+
+ copy_ptr:
+ err = write_ptr(p.seg, data, tgt);
+ if (err != NEED_TO_COPY)
+ return err;
+
+ /* Depth first copy the source whilst using a pointer stack to
+ * maintain the ptr to set and size left to copy at each level.
+ * We also maintain a rbtree (capn->copy) of the copies indexed
+ * by the source data. This way we can detect overlapped
+ * pointers in the source (and bail) and recursive structures
+ * (and point to the previous copy).
+ */
+
+ from[0] = tgt;
+ if (copy_ptr(p.seg, data, to, from, &dep))
+ return -1;
+ break;
+
+ default:
+ return -1;
+ }
+
+ while (dep) {
+ struct capn_ptr *tc = &to[dep-1], *tn = &to[dep];
+ struct capn_ptr *fc = &from[dep-1], *fn = &from[dep];
+
+ if (dep+1 == MAX_COPY_DEPTH) {
+ return -1;
+ }
+
+ if (!tc->len) {
+ dep--;
+ continue;
+ }
+
+ if (tc->type == CAPN_LIST) {
+ *fn = capn_getp(*fc, 0, 1);
+ *tn = capn_getp(*tc, 0, 1);
+
+ copy_list_member(tn, fn, &dep);
+
+ fc->data += fc->datasz + 8*fc->ptrs;
+ tc->data += tc->datasz + 8*tc->ptrs;
+ tc->len--;
+
+ } else { /* CAPN_PTR_LIST */
+ *fn = read_ptr(fc->seg, fc->data);
+
+ if (fn->type && copy_ptr(tc->seg, tc->data, tn, fn, &dep))
+ return -1;
+
+ fc->data += 8;
+ tc->data += 8;
+ tc->len--;
+ }
+ }
+
+ return 0;
+}
+
+/* TODO: handle CAPN_LIST, CAPN_PTR_LIST for bit lists */
+
+int capn_get1(capn_list1 l, int off) {
+ return l.p.type == CAPN_BIT_LIST
+ && off < l.p.len
+ && (l.p.data[off/8] & (1 << (off%8))) != 0;
+}
+
+int capn_set1(capn_list1 l, int off, int val) {
+ if (l.p.type != CAPN_BIT_LIST || off >= l.p.len)
+ return -1;
+ if (val) {
+ l.p.data[off/8] |= 1 << (off%8);
+ } else {
+ l.p.data[off/8] &= ~(1 << (off%8));
+ }
+ return 0;
+}
+
+int capn_getv1(capn_list1 l, int off, uint8_t *data, int sz) {
+ /* Note we only support aligned reads */
+ int bsz;
+ capn_ptr p;
+ capn_resolve(&l.p);
+ p = l.p;
+ if (p.type != CAPN_BIT_LIST || (off & 7) != 0)
+ return -1;
+
+ bsz = (sz + 7) / 8;
+ off /= 8;
+
+ if (off + sz > p.datasz) {
+ memcpy(data, p.data + off, p.datasz - off);
+ return p.len - off*8;
+ } else {
+ memcpy(data, p.data + off, bsz);
+ return sz;
+ }
+}
+
+int capn_setv1(capn_list1 l, int off, const uint8_t *data, int sz) {
+ /* Note we only support aligned writes */
+ int bsz;
+ capn_ptr p = l.p;
+ if (p.type != CAPN_BIT_LIST || (off & 7) != 0)
+ return -1;
+
+ bsz = (sz + 7) / 8;
+ off /= 8;
+
+ if (off + sz > p.datasz) {
+ memcpy(p.data + off, data, p.datasz - off);
+ return p.len - off*8;
+ } else {
+ memcpy(p.data + off, data, bsz);
+ return sz;
+ }
+}
+
+/* pull out whether we add a tag or not as a define so the unit test can
+ * test double far pointers by not creating tags */
+#ifndef ADD_TAG
+#define ADD_TAG 1
+#endif
+
+static void new_object(capn_ptr *p, int bytes) {
+ struct capn_segment *s = p->seg;
+
+ if (!s) {
+ memset(p, 0, sizeof(*p));
+ return;
+ }
+
+ /* pointer needs to be initialised to get a valid offset on write */
+ if (!bytes) {
+ p->data = s->data + s->len;
+ return;
+ }
+
+ /* all allocations are 8 byte aligned */
+ bytes = (bytes + 7) & ~7;
+
+ if (s->len + bytes <= s->cap) {
+ p->data = s->data + s->len;
+ s->len += bytes;
+ return;
+ }
+
+ /* add a tag whenever we switch segments so that write_ptr can
+ * use it */
+ p->data = new_data(s->capn, bytes + ADD_TAG*8, &p->seg);
+ if (!p->data) {
+ memset(p, 0, sizeof(*p));
+ return;
+ }
+
+ if (ADD_TAG) {
+ write_ptr_tag(p->data, *p, 0);
+ p->data += 8;
+ p->has_ptr_tag = 1;
+ }
+}
+
+capn_ptr capn_root(struct capn *c) {
+ capn_ptr r = {CAPN_PTR_LIST};
+ r.seg = lookup_segment(c, NULL, 0);
+ r.data = r.seg ? r.seg->data : new_data(c, 8, &r.seg);
+ r.len = 1;
+
+ if (!r.seg || r.seg->cap < 8) {
+ memset(&r, 0, sizeof(r));
+ } else if (r.seg->len < 8) {
+ r.seg->len = 8;
+ }
+
+ return r;
+}
+
+capn_ptr capn_new_struct(struct capn_segment *seg, int datasz, int ptrs) {
+ capn_ptr p = {CAPN_STRUCT};
+ p.seg = seg;
+ p.datasz = (datasz + 7) & ~7;
+ p.ptrs = ptrs;
+ new_object(&p, p.datasz + 8*p.ptrs);
+ return p;
+}
+
+capn_ptr capn_new_list(struct capn_segment *seg, int sz, int datasz, int ptrs) {
+ capn_ptr p = {CAPN_LIST};
+ p.seg = seg;
+ p.len = sz;
+
+ if (ptrs || datasz > 8) {
+ p.is_composite_list = 1;
+ p.datasz = (datasz + 7) & ~7;
+ p.ptrs = ptrs;
+ new_object(&p, p.len * (p.datasz + 8*p.ptrs) + 8);
+ if (p.data) {
+ uint64_t hdr = STRUCT_PTR | (U64(p.len) << 2) | (U64(p.datasz/8) << 32) | (U64(ptrs) << 48);
+ *(uint64_t*) p.data = capn_flip64(hdr);
+ p.data += 8;
+ }
+ } else if (datasz > 4) {
+ p.datasz = 8;
+ new_object(&p, p.len * 8);
+ } else if (datasz > 2) {
+ p.datasz = 4;
+ new_object(&p, p.len * 4);
+ } else {
+ p.datasz = datasz;
+ new_object(&p, p.len * datasz);
+ }
+
+ return p;
+}
+
+capn_list1 capn_new_list1(struct capn_segment *seg, int sz) {
+ capn_list1 l = {{CAPN_BIT_LIST}};
+ l.p.seg = seg;
+ l.p.datasz = (sz+7)/8;
+ l.p.len = sz;
+ new_object(&l.p, l.p.datasz);
+ return l;
+}
+
+capn_ptr capn_new_ptr_list(struct capn_segment *seg, int sz) {
+ capn_ptr p = {CAPN_PTR_LIST};
+ p.seg = seg;
+ p.len = sz;
+ p.ptrs = 0;
+ p.datasz = 0;
+ new_object(&p, sz*8);
+ return p;
+}
+
+capn_ptr capn_new_string(struct capn_segment *seg, const char *str, ssize_t sz) {
+ capn_ptr p = {CAPN_LIST};
+ p.seg = seg;
+ p.len = ((sz >= 0) ? (size_t)sz : strlen(str)) + 1;
+ p.datasz = 1;
+ new_object(&p, p.len);
+ if (p.data) {
+ memcpy(p.data, str, p.len - 1);
+ p.data[p.len - 1] = '\0';
+ }
+ return p;
+}
+
+capn_text capn_get_text(capn_ptr p, int off, capn_text def) {
+ capn_ptr m = capn_getp(p, off, 1);
+ capn_text ret = def;
+ if (m.type == CAPN_LIST && m.datasz == 1 && m.len && m.data[m.len - 1] == 0) {
+ ret.seg = m.seg;
+ ret.str = m.data;
+ ret.len = m.len - 1;
+ }
+ return ret;
+}
+
+int capn_set_text(capn_ptr p, int off, capn_text tgt) {
+ capn_ptr m = {CAPN_NULL};
+ if (tgt.seg) {
+ m.type = CAPN_LIST;
+ m.seg = tgt.seg;
+ m.data = (char*)tgt.str;
+ m.len = tgt.len + 1;
+ m.datasz = 1;
+ } else if (tgt.str) {
+ m = capn_new_string(p.seg, tgt.str, tgt.len);
+ }
+ return capn_setp(p, off, m);
+}
+
+capn_data capn_get_data(capn_ptr p, int off) {
+ capn_data ret;
+ ret.p = capn_getp(p, off, 1);
+ if (ret.p.type != CAPN_LIST || ret.p.datasz != 1) {
+ memset(&ret, 0, sizeof(ret));
+ }
+ return ret;
+}
+
+#define SZ 8
+#include "capn-list.inc"
+#undef SZ
+
+#define SZ 16
+#include "capn-list.inc"
+#undef SZ
+
+#define SZ 32
+#include "capn-list.inc"
+#undef SZ
+
+#define SZ 64
+#include "capn-list.inc"
+#undef SZ
diff --git a/src/lib/mpack/mpack.cc b/src/lib/mpack/mpack.cc
new file mode 100644
index 0000000..67e54e8
--- /dev/null
+++ b/src/lib/mpack/mpack.cc
@@ -0,0 +1,6440 @@
+/**
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2015-2018 Nicholas Fraser
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+/*
+ * This is the MPack 1.0 amalgamation package.
+ *
+ * http://github.com/ludocode/mpack
+ */
+
+#define MPACK_INTERNAL 1
+#define MPACK_EMIT_INLINE_DEFS 1
+
+#include "mpack.h"
+
+
+/* mpack/mpack-platform.c.c */
+
+
+// We define MPACK_EMIT_INLINE_DEFS and include mpack.h to emit
+// standalone definitions of all (non-static) inline functions in MPack.
+
+#define MPACK_INTERNAL 1
+#define MPACK_EMIT_INLINE_DEFS 1
+
+/* #include "mpack-platform.h" */
+/* #include "mpack.h" */
+
+
+#if MPACK_DEBUG && MPACK_STDIO
+#include <stdarg.h>
+#endif
+
+
+
+#if MPACK_DEBUG
+
+#if MPACK_STDIO
+void mpack_assert_fail_format(const char* format, ...) {
+ char buffer[512];
+ va_list args;
+ va_start(args, format);
+ vsnprintf(buffer, sizeof(buffer), format, args);
+ va_end(args);
+ buffer[sizeof(buffer) - 1] = 0;
+ mpack_assert_fail_wrapper(buffer);
+}
+
+void mpack_break_hit_format(const char* format, ...) {
+ char buffer[512];
+ va_list args;
+ va_start(args, format);
+ vsnprintf(buffer, sizeof(buffer), format, args);
+ va_end(args);
+ buffer[sizeof(buffer) - 1] = 0;
+ mpack_break_hit(buffer);
+}
+#endif
+
+#if !MPACK_CUSTOM_ASSERT
+void mpack_assert_fail(const char* message) {
+ MPACK_UNUSED(message);
+
+ #if MPACK_STDIO
+ fprintf(stderr, "%s\n", message);
+ #endif
+}
+#endif
+
+// We split the assert failure from the wrapper so that a
+// custom assert function can return.
+void mpack_assert_fail_wrapper(const char* message) {
+
+ #ifdef MPACK_GCOV
+ // gcov marks even __builtin_unreachable() as an uncovered line. this
+ // silences it.
+ (mpack_assert_fail(message), __builtin_unreachable());
+
+ #else
+ mpack_assert_fail(message);
+
+ // mpack_assert_fail() is not supposed to return. in case it does, we
+ // abort.
+
+ #if !MPACK_NO_BUILTINS
+ #if defined(__GNUC__) || defined(__clang__)
+ __builtin_trap();
+ #elif defined(WIN32)
+ __debugbreak();
+ #endif
+ #endif
+
+ #if (defined(__GNUC__) || defined(__clang__)) && !MPACK_NO_BUILTINS
+ __builtin_abort();
+ #elif MPACK_STDLIB
+ abort();
+ #endif
+
+ MPACK_UNREACHABLE;
+ #endif
+}
+
+#if !MPACK_CUSTOM_BREAK
+
+// If we have a custom assert handler, break wraps it by default.
+// This allows users of MPack to only implement mpack_assert_fail() without
+// having to worry about the difference between assert and break.
+//
+// MPACK_CUSTOM_BREAK is available to define a separate break handler
+// (which is needed by the unit test suite), but this is not offered in
+// mpack-config.h for simplicity.
+
+#if MPACK_CUSTOM_ASSERT
+void mpack_break_hit(const char* message) {
+ mpack_assert_fail_wrapper(message);
+}
+#else
+void mpack_break_hit(const char* message) {
+ MPACK_UNUSED(message);
+
+ #if MPACK_STDIO
+ fprintf(stderr, "%s\n", message);
+ #endif
+
+ #if defined(__GNUC__) || defined(__clang__) && !MPACK_NO_BUILTINS
+ __builtin_trap();
+ #elif defined(WIN32) && !MPACK_NO_BUILTINS
+ __debugbreak();
+ #elif MPACK_STDLIB
+ abort();
+ #endif
+}
+#endif
+
+#endif
+
+#endif
+
+
+
+// The below are adapted from the C wikibook:
+// https://en.wikibooks.org/wiki/C_Programming/Strings
+
+#ifndef mpack_memcmp
+int mpack_memcmp(const void* s1, const void* s2, size_t n) {
+ const unsigned char *us1 = (const unsigned char *) s1;
+ const unsigned char *us2 = (const unsigned char *) s2;
+ while (n-- != 0) {
+ if (*us1 != *us2)
+ return (*us1 < *us2) ? -1 : +1;
+ us1++;
+ us2++;
+ }
+ return 0;
+}
+#endif
+
+#ifndef mpack_memcpy
+void* mpack_memcpy(void* MPACK_RESTRICT s1, const void* MPACK_RESTRICT s2, size_t n) {
+ char* MPACK_RESTRICT dst = (char *)s1;
+ const char* MPACK_RESTRICT src = (const char *)s2;
+ while (n-- != 0)
+ *dst++ = *src++;
+ return s1;
+}
+#endif
+
+#ifndef mpack_memmove
+void* mpack_memmove(void* s1, const void* s2, size_t n) {
+ char *p1 = (char *)s1;
+ const char *p2 = (const char *)s2;
+ if (p2 < p1 && p1 < p2 + n) {
+ p2 += n;
+ p1 += n;
+ while (n-- != 0)
+ *--p1 = *--p2;
+ } else
+ while (n-- != 0)
+ *p1++ = *p2++;
+ return s1;
+}
+#endif
+
+#ifndef mpack_memset
+void* mpack_memset(void* s, int c, size_t n) {
+ unsigned char *us = (unsigned char *)s;
+ unsigned char uc = (unsigned char)c;
+ while (n-- != 0)
+ *us++ = uc;
+ return s;
+}
+#endif
+
+#ifndef mpack_strlen
+size_t mpack_strlen(const char* s) {
+ const char* p = s;
+ while (*p != '\0')
+ p++;
+ return (size_t)(p - s);
+}
+#endif
+
+
+
+#if defined(MPACK_MALLOC) && !defined(MPACK_REALLOC)
+void* mpack_realloc(void* old_ptr, size_t used_size, size_t new_size) {
+ if (new_size == 0) {
+ if (old_ptr)
+ MPACK_FREE(old_ptr);
+ return NULL;
+ }
+
+ void* new_ptr = MPACK_MALLOC(new_size);
+ if (new_ptr == NULL)
+ return NULL;
+
+ mpack_memcpy(new_ptr, old_ptr, used_size);
+ MPACK_FREE(old_ptr);
+ return new_ptr;
+}
+#endif
+
+/* mpack/mpack-common.c.c */
+
+#define MPACK_INTERNAL 1
+
+/* #include "mpack-common.h" */
+
+#if MPACK_DEBUG && MPACK_STDIO
+#include <stdarg.h>
+#endif
+
+const char* mpack_error_to_string(mpack_error_t error) {
+ #if MPACK_STRINGS
+ switch (error) {
+ #define MPACK_ERROR_STRING_CASE(e) case e: return #e
+ MPACK_ERROR_STRING_CASE(mpack_ok);
+ MPACK_ERROR_STRING_CASE(mpack_error_io);
+ MPACK_ERROR_STRING_CASE(mpack_error_invalid);
+ MPACK_ERROR_STRING_CASE(mpack_error_unsupported);
+ MPACK_ERROR_STRING_CASE(mpack_error_type);
+ MPACK_ERROR_STRING_CASE(mpack_error_too_big);
+ MPACK_ERROR_STRING_CASE(mpack_error_memory);
+ MPACK_ERROR_STRING_CASE(mpack_error_bug);
+ MPACK_ERROR_STRING_CASE(mpack_error_data);
+ MPACK_ERROR_STRING_CASE(mpack_error_eof);
+ #undef MPACK_ERROR_STRING_CASE
+ }
+ mpack_assert(0, "unrecognized error %i", (int)error);
+ return "(unknown mpack_error_t)";
+ #else
+ MPACK_UNUSED(error);
+ return "";
+ #endif
+}
+
+const char* mpack_type_to_string(mpack_type_t type) {
+ #if MPACK_STRINGS
+ switch (type) {
+ #define MPACK_TYPE_STRING_CASE(e) case e: return #e
+ MPACK_TYPE_STRING_CASE(mpack_type_missing);
+ MPACK_TYPE_STRING_CASE(mpack_type_nil);
+ MPACK_TYPE_STRING_CASE(mpack_type_bool);
+ MPACK_TYPE_STRING_CASE(mpack_type_float);
+ MPACK_TYPE_STRING_CASE(mpack_type_double);
+ MPACK_TYPE_STRING_CASE(mpack_type_int);
+ MPACK_TYPE_STRING_CASE(mpack_type_uint);
+ MPACK_TYPE_STRING_CASE(mpack_type_str);
+ MPACK_TYPE_STRING_CASE(mpack_type_bin);
+ MPACK_TYPE_STRING_CASE(mpack_type_array);
+ MPACK_TYPE_STRING_CASE(mpack_type_map);
+ #if MPACK_EXTENSIONS
+ MPACK_TYPE_STRING_CASE(mpack_type_ext);
+ #endif
+ #undef MPACK_TYPE_STRING_CASE
+ }
+ mpack_assert(0, "unrecognized type %i", (int)type);
+ return "(unknown mpack_type_t)";
+ #else
+ MPACK_UNUSED(type);
+ return "";
+ #endif
+}
+
+int mpack_tag_cmp(mpack_tag_t left, mpack_tag_t right) {
+
+ // positive numbers may be stored as int; convert to uint
+ if (left.type == mpack_type_int && left.v.i >= 0) {
+ left.type = mpack_type_uint;
+ left.v.u = (uint64_t)left.v.i;
+ }
+ if (right.type == mpack_type_int && right.v.i >= 0) {
+ right.type = mpack_type_uint;
+ right.v.u = (uint64_t)right.v.i;
+ }
+
+ if (left.type != right.type)
+ return ((int)left.type < (int)right.type) ? -1 : 1;
+
+ switch (left.type) {
+ case mpack_type_missing: // fallthrough
+ case mpack_type_nil:
+ return 0;
+
+ case mpack_type_bool:
+ return (int)left.v.b - (int)right.v.b;
+
+ case mpack_type_int:
+ if (left.v.i == right.v.i)
+ return 0;
+ return (left.v.i < right.v.i) ? -1 : 1;
+
+ case mpack_type_uint:
+ if (left.v.u == right.v.u)
+ return 0;
+ return (left.v.u < right.v.u) ? -1 : 1;
+
+ case mpack_type_array:
+ case mpack_type_map:
+ if (left.v.n == right.v.n)
+ return 0;
+ return (left.v.n < right.v.n) ? -1 : 1;
+
+ case mpack_type_str:
+ case mpack_type_bin:
+ if (left.v.l == right.v.l)
+ return 0;
+ return (left.v.l < right.v.l) ? -1 : 1;
+
+ #if MPACK_EXTENSIONS
+ case mpack_type_ext:
+ if (left.exttype == right.exttype) {
+ if (left.v.l == right.v.l)
+ return 0;
+ return (left.v.l < right.v.l) ? -1 : 1;
+ }
+ return (int)left.exttype - (int)right.exttype;
+ #endif
+
+ // floats should not normally be compared for equality. we compare
+ // with memcmp() to silence compiler warnings, but this will return
+ // equal if both are NaNs with the same representation (though we may
+ // want this, for instance if you are for some bizarre reason using
+ // floats as map keys.) i'm not sure what the right thing to
+ // do is here. check for NaN first? always return false if the type
+ // is float? use operator== and pragmas to silence compiler warning?
+ // please send me your suggestions.
+ // note also that we don't convert floats to doubles, so when this is
+ // used for ordering purposes, all floats are ordered before all
+ // doubles.
+ case mpack_type_float:
+ return mpack_memcmp(&left.v.f, &right.v.f, sizeof(left.v.f));
+ case mpack_type_double:
+ return mpack_memcmp(&left.v.d, &right.v.d, sizeof(left.v.d));
+ }
+
+ mpack_assert(0, "unrecognized type %i", (int)left.type);
+ return false;
+}
+
+#if MPACK_DEBUG && MPACK_STDIO
+static char mpack_hex_char(uint8_t hex_value) {
+ return (hex_value < 10) ? (char)('0' + hex_value) : (char)('a' + (hex_value - 10));
+}
+
+static void mpack_tag_debug_complete_bin_ext(mpack_tag_t tag, size_t string_length, char* buffer, size_t buffer_size,
+ const char* prefix, size_t prefix_size)
+{
+ // If at any point in this function we run out of space in the buffer, we
+ // bail out. The outer tag print wrapper will make sure we have a
+ // null-terminator.
+
+ if (string_length == 0 || string_length >= buffer_size)
+ return;
+ buffer += string_length;
+ buffer_size -= string_length;
+
+ size_t total = mpack_tag_bytes(&tag);
+ if (total == 0) {
+ strncpy(buffer, ">", buffer_size);
+ return;
+ }
+
+ strncpy(buffer, ": ", buffer_size);
+ if (buffer_size < 2)
+ return;
+ buffer += 2;
+ buffer_size -= 2;
+
+ size_t hex_bytes = 0;
+ for (size_t i = 0; i < MPACK_PRINT_BYTE_COUNT && i < prefix_size && buffer_size > 2; ++i) {
+ uint8_t byte = (uint8_t)prefix[i];
+ buffer[0] = mpack_hex_char((uint8_t)(byte >> 4));
+ buffer[1] = mpack_hex_char((uint8_t)(byte & 0xfu));
+ buffer += 2;
+ buffer_size -= 2;
+ ++hex_bytes;
+ }
+
+ if (buffer_size != 0)
+ mpack_snprintf(buffer, buffer_size, "%s>", (total > hex_bytes) ? "..." : "");
+}
+
+static void mpack_tag_debug_pseudo_json_bin(mpack_tag_t tag, char* buffer, size_t buffer_size,
+ const char* prefix, size_t prefix_size)
+{
+ mpack_assert(mpack_tag_type(&tag) == mpack_type_bin);
+ size_t length = (size_t)mpack_snprintf(buffer, buffer_size, "<binary data of length %u", tag.v.l);
+ mpack_tag_debug_complete_bin_ext(tag, length, buffer, buffer_size, prefix, prefix_size);
+}
+
+#if MPACK_EXTENSIONS
+static void mpack_tag_debug_pseudo_json_ext(mpack_tag_t tag, char* buffer, size_t buffer_size,
+ const char* prefix, size_t prefix_size)
+{
+ mpack_assert(mpack_tag_type(&tag) == mpack_type_ext);
+ size_t length = (size_t)mpack_snprintf(buffer, buffer_size, "<ext data of type %i and length %u",
+ mpack_tag_ext_exttype(&tag), mpack_tag_ext_length(&tag));
+ mpack_tag_debug_complete_bin_ext(tag, length, buffer, buffer_size, prefix, prefix_size);
+}
+#endif
+
+static void mpack_tag_debug_pseudo_json_impl(mpack_tag_t tag, char* buffer, size_t buffer_size,
+ const char* prefix, size_t prefix_size)
+{
+ switch (tag.type) {
+ case mpack_type_missing:
+ mpack_snprintf(buffer, buffer_size, "<missing!>");
+ return;
+ case mpack_type_nil:
+ mpack_snprintf(buffer, buffer_size, "null");
+ return;
+ case mpack_type_bool:
+ mpack_snprintf(buffer, buffer_size, tag.v.b ? "true" : "false");
+ return;
+ case mpack_type_int:
+ mpack_snprintf(buffer, buffer_size, "%" PRIi64, tag.v.i);
+ return;
+ case mpack_type_uint:
+ mpack_snprintf(buffer, buffer_size, "%" PRIu64, tag.v.u);
+ return;
+ case mpack_type_float:
+ mpack_snprintf(buffer, buffer_size, "%f", tag.v.f);
+ return;
+ case mpack_type_double:
+ mpack_snprintf(buffer, buffer_size, "%f", tag.v.d);
+ return;
+
+ case mpack_type_str:
+ mpack_snprintf(buffer, buffer_size, "<string of %u bytes>", tag.v.l);
+ return;
+ case mpack_type_bin:
+ mpack_tag_debug_pseudo_json_bin(tag, buffer, buffer_size, prefix, prefix_size);
+ return;
+ #if MPACK_EXTENSIONS
+ case mpack_type_ext:
+ mpack_tag_debug_pseudo_json_ext(tag, buffer, buffer_size, prefix, prefix_size);
+ return;
+ #endif
+
+ case mpack_type_array:
+ mpack_snprintf(buffer, buffer_size, "<array of %u elements>", tag.v.n);
+ return;
+ case mpack_type_map:
+ mpack_snprintf(buffer, buffer_size, "<map of %u key-value pairs>", tag.v.n);
+ return;
+ }
+
+ mpack_snprintf(buffer, buffer_size, "<unknown!>");
+}
+
+void mpack_tag_debug_pseudo_json(mpack_tag_t tag, char* buffer, size_t buffer_size,
+ const char* prefix, size_t prefix_size)
+{
+ mpack_assert(buffer_size > 0, "buffer size cannot be zero!");
+ buffer[0] = 0;
+
+ mpack_tag_debug_pseudo_json_impl(tag, buffer, buffer_size, prefix, prefix_size);
+
+ // We always null-terminate the buffer manually just in case the snprintf()
+ // function doesn't null-terminate when the string doesn't fit.
+ buffer[buffer_size - 1] = 0;
+}
+
+static void mpack_tag_debug_describe_impl(mpack_tag_t tag, char* buffer, size_t buffer_size) {
+ switch (tag.type) {
+ case mpack_type_missing:
+ mpack_snprintf(buffer, buffer_size, "missing");
+ return;
+ case mpack_type_nil:
+ mpack_snprintf(buffer, buffer_size, "nil");
+ return;
+ case mpack_type_bool:
+ mpack_snprintf(buffer, buffer_size, tag.v.b ? "true" : "false");
+ return;
+ case mpack_type_int:
+ mpack_snprintf(buffer, buffer_size, "int %" PRIi64, tag.v.i);
+ return;
+ case mpack_type_uint:
+ mpack_snprintf(buffer, buffer_size, "uint %" PRIu64, tag.v.u);
+ return;
+ case mpack_type_float:
+ mpack_snprintf(buffer, buffer_size, "float %f", tag.v.f);
+ return;
+ case mpack_type_double:
+ mpack_snprintf(buffer, buffer_size, "double %f", tag.v.d);
+ return;
+ case mpack_type_str:
+ mpack_snprintf(buffer, buffer_size, "str of %u bytes", tag.v.l);
+ return;
+ case mpack_type_bin:
+ mpack_snprintf(buffer, buffer_size, "bin of %u bytes", tag.v.l);
+ return;
+ #if MPACK_EXTENSIONS
+ case mpack_type_ext:
+ mpack_snprintf(buffer, buffer_size, "ext of type %i, %u bytes",
+ mpack_tag_ext_exttype(&tag), mpack_tag_ext_length(&tag));
+ return;
+ #endif
+ case mpack_type_array:
+ mpack_snprintf(buffer, buffer_size, "array of %u elements", tag.v.n);
+ return;
+ case mpack_type_map:
+ mpack_snprintf(buffer, buffer_size, "map of %u key-value pairs", tag.v.n);
+ return;
+ }
+
+ mpack_snprintf(buffer, buffer_size, "unknown!");
+}
+
+void mpack_tag_debug_describe(mpack_tag_t tag, char* buffer, size_t buffer_size) {
+ mpack_assert(buffer_size > 0, "buffer size cannot be zero!");
+ buffer[0] = 0;
+
+ mpack_tag_debug_describe_impl(tag, buffer, buffer_size);
+
+ // We always null-terminate the buffer manually just in case the snprintf()
+ // function doesn't null-terminate when the string doesn't fit.
+ buffer[buffer_size - 1] = 0;
+}
+#endif
+
+
+
+#if MPACK_READ_TRACKING || MPACK_WRITE_TRACKING
+
+#ifndef MPACK_TRACKING_INITIAL_CAPACITY
+// seems like a reasonable number. we grow by doubling, and it only
+// needs to be as long as the maximum depth of the message.
+#define MPACK_TRACKING_INITIAL_CAPACITY 8
+#endif
+
+mpack_error_t mpack_track_init(mpack_track_t* track) {
+ track->count = 0;
+ track->capacity = MPACK_TRACKING_INITIAL_CAPACITY;
+ track->elements = (mpack_track_element_t*)MPACK_MALLOC(sizeof(mpack_track_element_t) * track->capacity);
+ if (track->elements == NULL)
+ return mpack_error_memory;
+ return mpack_ok;
+}
+
+mpack_error_t mpack_track_grow(mpack_track_t* track) {
+ mpack_assert(track->elements, "null track elements!");
+ mpack_assert(track->count == track->capacity, "incorrect growing?");
+
+ size_t new_capacity = track->capacity * 2;
+
+ mpack_track_element_t* new_elements = (mpack_track_element_t*)mpack_realloc(track->elements,
+ sizeof(mpack_track_element_t) * track->count, sizeof(mpack_track_element_t) * new_capacity);
+ if (new_elements == NULL)
+ return mpack_error_memory;
+
+ track->elements = new_elements;
+ track->capacity = new_capacity;
+ return mpack_ok;
+}
+
+mpack_error_t mpack_track_push(mpack_track_t* track, mpack_type_t type, uint64_t count) {
+ mpack_assert(track->elements, "null track elements!");
+ mpack_log("track pushing %s count %i\n", mpack_type_to_string(type), (int)count);
+
+ // maps have twice the number of elements (key/value pairs)
+ if (type == mpack_type_map)
+ count *= 2;
+
+ // grow if needed
+ if (track->count == track->capacity) {
+ mpack_error_t error = mpack_track_grow(track);
+ if (error != mpack_ok)
+ return error;
+ }
+
+ // insert new track
+ track->elements[track->count].type = type;
+ track->elements[track->count].left = count;
+ ++track->count;
+ return mpack_ok;
+}
+
+mpack_error_t mpack_track_pop(mpack_track_t* track, mpack_type_t type) {
+ mpack_assert(track->elements, "null track elements!");
+ mpack_log("track popping %s\n", mpack_type_to_string(type));
+
+ if (track->count == 0) {
+ mpack_break("attempting to close a %s but nothing was opened!", mpack_type_to_string(type));
+ return mpack_error_bug;
+ }
+
+ mpack_track_element_t* element = &track->elements[track->count - 1];
+
+ if (element->type != type) {
+ mpack_break("attempting to close a %s but the open element is a %s!",
+ mpack_type_to_string(type), mpack_type_to_string(element->type));
+ return mpack_error_bug;
+ }
+
+ if (element->left != 0) {
+ mpack_break("attempting to close a %s but there are %" PRIu64 " %s left",
+ mpack_type_to_string(type), element->left,
+ (type == mpack_type_map || type == mpack_type_array) ? "elements" : "bytes");
+ return mpack_error_bug;
+ }
+
+ --track->count;
+ return mpack_ok;
+}
+
+mpack_error_t mpack_track_peek_element(mpack_track_t* track, bool read) {
+ MPACK_UNUSED(read);
+ mpack_assert(track->elements, "null track elements!");
+
+ // if there are no open elements, that's fine, we can read/write elements at will
+ if (track->count == 0)
+ return mpack_ok;
+
+ mpack_track_element_t* element = &track->elements[track->count - 1];
+
+ if (element->type != mpack_type_map && element->type != mpack_type_array) {
+ mpack_break("elements cannot be %s within an %s", read ? "read" : "written",
+ mpack_type_to_string(element->type));
+ return mpack_error_bug;
+ }
+
+ if (element->left == 0) {
+ mpack_break("too many elements %s for %s", read ? "read" : "written",
+ mpack_type_to_string(element->type));
+ return mpack_error_bug;
+ }
+
+ return mpack_ok;
+}
+
+mpack_error_t mpack_track_element(mpack_track_t* track, bool read) {
+ mpack_error_t error = mpack_track_peek_element(track, read);
+ if (track->count > 0 && error == mpack_ok)
+ --track->elements[track->count - 1].left;
+ return error;
+}
+
+mpack_error_t mpack_track_bytes(mpack_track_t* track, bool read, uint64_t count) {
+ MPACK_UNUSED(read);
+ mpack_assert(track->elements, "null track elements!");
+
+ if (track->count == 0) {
+ mpack_break("bytes cannot be %s with no open bin, str or ext", read ? "read" : "written");
+ return mpack_error_bug;
+ }
+
+ mpack_track_element_t* element = &track->elements[track->count - 1];
+
+ if (element->type == mpack_type_map || element->type == mpack_type_array) {
+ mpack_break("bytes cannot be %s within an %s", read ? "read" : "written",
+ mpack_type_to_string(element->type));
+ return mpack_error_bug;
+ }
+
+ if (element->left < count) {
+ mpack_break("too many bytes %s for %s", read ? "read" : "written",
+ mpack_type_to_string(element->type));
+ return mpack_error_bug;
+ }
+
+ element->left -= count;
+ return mpack_ok;
+}
+
+mpack_error_t mpack_track_str_bytes_all(mpack_track_t* track, bool read, uint64_t count) {
+ mpack_error_t error = mpack_track_bytes(track, read, count);
+ if (error != mpack_ok)
+ return error;
+
+ mpack_track_element_t* element = &track->elements[track->count - 1];
+
+ if (element->type != mpack_type_str) {
+ mpack_break("the open type must be a string, not a %s", mpack_type_to_string(element->type));
+ return mpack_error_bug;
+ }
+
+ if (element->left != 0) {
+ mpack_break("not all bytes were read; the wrong byte count was requested for a string read.");
+ return mpack_error_bug;
+ }
+
+ return mpack_ok;
+}
+
+mpack_error_t mpack_track_check_empty(mpack_track_t* track) {
+ if (track->count != 0) {
+ mpack_break("unclosed %s", mpack_type_to_string(track->elements[0].type));
+ return mpack_error_bug;
+ }
+ return mpack_ok;
+}
+
+mpack_error_t mpack_track_destroy(mpack_track_t* track, bool cancel) {
+ mpack_error_t error = cancel ? mpack_ok : mpack_track_check_empty(track);
+ if (track->elements) {
+ MPACK_FREE(track->elements);
+ track->elements = NULL;
+ }
+ return error;
+}
+#endif
+
+
+
+static bool mpack_utf8_check_impl(const uint8_t* str, size_t count, bool allow_null) {
+ while (count > 0) {
+ uint8_t lead = str[0];
+
+ // NUL
+ if (!allow_null && lead == '\0') // we don't allow NUL bytes in MPack C-strings
+ return false;
+
+ // ASCII
+ if (lead <= 0x7F) {
+ ++str;
+ --count;
+
+ // 2-byte sequence
+ } else if ((lead & 0xE0) == 0xC0) {
+ if (count < 2) // truncated sequence
+ return false;
+
+ uint8_t cont = str[1];
+ if ((cont & 0xC0) != 0x80) // not a continuation byte
+ return false;
+
+ str += 2;
+ count -= 2;
+
+ uint32_t z = ((uint32_t)(lead & ~0xE0) << 6) |
+ (uint32_t)(cont & ~0xC0);
+
+ if (z < 0x80) // overlong sequence
+ return false;
+
+ // 3-byte sequence
+ } else if ((lead & 0xF0) == 0xE0) {
+ if (count < 3) // truncated sequence
+ return false;
+
+ uint8_t cont1 = str[1];
+ if ((cont1 & 0xC0) != 0x80) // not a continuation byte
+ return false;
+ uint8_t cont2 = str[2];
+ if ((cont2 & 0xC0) != 0x80) // not a continuation byte
+ return false;
+
+ str += 3;
+ count -= 3;
+
+ uint32_t z = ((uint32_t)(lead & ~0xF0) << 12) |
+ ((uint32_t)(cont1 & ~0xC0) << 6) |
+ (uint32_t)(cont2 & ~0xC0);
+
+ if (z < 0x800) // overlong sequence
+ return false;
+ if (z >= 0xD800 && z <= 0xDFFF) // surrogate
+ return false;
+
+ // 4-byte sequence
+ } else if ((lead & 0xF8) == 0xF0) {
+ if (count < 4) // truncated sequence
+ return false;
+
+ uint8_t cont1 = str[1];
+ if ((cont1 & 0xC0) != 0x80) // not a continuation byte
+ return false;
+ uint8_t cont2 = str[2];
+ if ((cont2 & 0xC0) != 0x80) // not a continuation byte
+ return false;
+ uint8_t cont3 = str[3];
+ if ((cont3 & 0xC0) != 0x80) // not a continuation byte
+ return false;
+
+ str += 4;
+ count -= 4;
+
+ uint32_t z = ((uint32_t)(lead & ~0xF8) << 18) |
+ ((uint32_t)(cont1 & ~0xC0) << 12) |
+ ((uint32_t)(cont2 & ~0xC0) << 6) |
+ (uint32_t)(cont3 & ~0xC0);
+
+ if (z < 0x10000) // overlong sequence
+ return false;
+ if (z > 0x10FFFF) // codepoint limit
+ return false;
+
+ } else {
+ return false; // continuation byte without a lead, or lead for a 5-byte sequence or longer
+ }
+ }
+ return true;
+}
+
+bool mpack_utf8_check(const char* str, size_t bytes) {
+ return mpack_utf8_check_impl((const uint8_t*)str, bytes, true);
+}
+
+bool mpack_utf8_check_no_null(const char* str, size_t bytes) {
+ return mpack_utf8_check_impl((const uint8_t*)str, bytes, false);
+}
+
+bool mpack_str_check_no_null(const char* str, size_t bytes) {
+ for (size_t i = 0; i < bytes; ++i)
+ if (str[i] == '\0')
+ return false;
+ return true;
+}
+
+#if MPACK_DEBUG && MPACK_STDIO
+void mpack_print_append(mpack_print_t* print, const char* data, size_t count) {
+
+ // copy whatever fits into the buffer
+ size_t copy = print->size - print->count;
+ if (copy > count)
+ copy = count;
+ mpack_memcpy(print->buffer + print->count, data, copy);
+ print->count += copy;
+ data += copy;
+ count -= copy;
+
+ // if we don't need to flush or can't flush there's nothing else to do
+ if (count == 0 || print->callback == NULL)
+ return;
+
+ // flush the buffer
+ print->callback(print->context, print->buffer, print->count);
+
+ if (count > print->size / 2) {
+ // flush the rest of the data
+ print->count = 0;
+ print->callback(print->context, data, count);
+ } else {
+ // copy the rest of the data into the buffer
+ mpack_memcpy(print->buffer, data, count);
+ print->count = count;
+ }
+
+}
+
+void mpack_print_flush(mpack_print_t* print) {
+ if (print->count > 0 && print->callback != NULL) {
+ print->callback(print->context, print->buffer, print->count);
+ print->count = 0;
+ }
+}
+
+void mpack_print_file_callback(void* context, const char* data, size_t count) {
+ FILE* file = (FILE*)context;
+ fwrite(data, 1, count, file);
+}
+#endif
+
+/* mpack/mpack-writer.c.c */
+
+#define MPACK_INTERNAL 1
+
+/* #include "mpack-writer.h" */
+
+#if MPACK_WRITER
+
+#if MPACK_WRITE_TRACKING
+static void mpack_writer_flag_if_error(mpack_writer_t* writer, mpack_error_t error) {
+ if (error != mpack_ok)
+ mpack_writer_flag_error(writer, error);
+}
+
+void mpack_writer_track_push(mpack_writer_t* writer, mpack_type_t type, uint64_t count) {
+ if (writer->error == mpack_ok)
+ mpack_writer_flag_if_error(writer, mpack_track_push(&writer->track, type, count));
+}
+
+void mpack_writer_track_pop(mpack_writer_t* writer, mpack_type_t type) {
+ if (writer->error == mpack_ok)
+ mpack_writer_flag_if_error(writer, mpack_track_pop(&writer->track, type));
+}
+
+void mpack_writer_track_element(mpack_writer_t* writer) {
+ if (writer->error == mpack_ok)
+ mpack_writer_flag_if_error(writer, mpack_track_element(&writer->track, false));
+}
+
+void mpack_writer_track_bytes(mpack_writer_t* writer, size_t count) {
+ if (writer->error == mpack_ok)
+ mpack_writer_flag_if_error(writer, mpack_track_bytes(&writer->track, false, count));
+}
+#endif
+
+static void mpack_writer_clear(mpack_writer_t* writer) {
+ #if MPACK_COMPATIBILITY
+ writer->version = mpack_version_current;
+ #endif
+ writer->flush = NULL;
+ writer->error_fn = NULL;
+ writer->teardown = NULL;
+ writer->context = NULL;
+
+ writer->buffer = NULL;
+ writer->current = NULL;
+ writer->end = NULL;
+ writer->error = mpack_ok;
+
+ #if MPACK_WRITE_TRACKING
+ mpack_memset(&writer->track, 0, sizeof(writer->track));
+ #endif
+}
+
+void mpack_writer_init(mpack_writer_t* writer, char* buffer, size_t size) {
+ mpack_assert(buffer != NULL, "cannot initialize writer with empty buffer");
+ mpack_writer_clear(writer);
+ writer->buffer = buffer;
+ writer->current = buffer;
+ writer->end = writer->buffer + size;
+
+ #if MPACK_WRITE_TRACKING
+ mpack_writer_flag_if_error(writer, mpack_track_init(&writer->track));
+ #endif
+
+ mpack_log("===========================\n");
+ mpack_log("initializing writer with buffer size %i\n", (int)size);
+}
+
+void mpack_writer_init_error(mpack_writer_t* writer, mpack_error_t error) {
+ mpack_writer_clear(writer);
+ writer->error = error;
+
+ mpack_log("===========================\n");
+ mpack_log("initializing writer in error state %i\n", (int)error);
+}
+
+void mpack_writer_set_flush(mpack_writer_t* writer, mpack_writer_flush_t flush) {
+ MPACK_STATIC_ASSERT(MPACK_WRITER_MINIMUM_BUFFER_SIZE >= MPACK_MAXIMUM_TAG_SIZE,
+ "minimum buffer size must fit any tag!");
+ MPACK_STATIC_ASSERT(31 + MPACK_TAG_SIZE_FIXSTR >= MPACK_WRITER_MINIMUM_BUFFER_SIZE,
+ "minimum buffer size must fit the largest possible fixstr!");
+
+ if (mpack_writer_buffer_size(writer) < MPACK_WRITER_MINIMUM_BUFFER_SIZE) {
+ mpack_break("buffer size is %i, but minimum buffer size for flush is %i",
+ (int)mpack_writer_buffer_size(writer), MPACK_WRITER_MINIMUM_BUFFER_SIZE);
+ mpack_writer_flag_error(writer, mpack_error_bug);
+ return;
+ }
+
+ writer->flush = flush;
+}
+
+#ifdef MPACK_MALLOC
+typedef struct mpack_growable_writer_t {
+ char** target_data;
+ size_t* target_size;
+} mpack_growable_writer_t;
+
+static char* mpack_writer_get_reserved(mpack_writer_t* writer) {
+ // This is in a separate function in order to avoid false strict aliasing
+ // warnings. We aren't actually violating strict aliasing (the reserved
+ // space is only ever dereferenced as an mpack_growable_writer_t.)
+ return (char*)writer->reserved;
+}
+
+static void mpack_growable_writer_flush(mpack_writer_t* writer, const char* data, size_t count) {
+
+ // This is an intrusive flush function which modifies the writer's buffer
+ // in response to a flush instead of emptying it in order to add more
+ // capacity for data. This removes the need to copy data from a fixed buffer
+ // into a growable one, improving performance.
+ //
+ // There are three ways flush can be called:
+ // - flushing the buffer during writing (used is zero, count is all data, data is buffer)
+ // - flushing extra data during writing (used is all flushed data, count is extra data, data is not buffer)
+ // - flushing during teardown (used and count are both all flushed data, data is buffer)
+ //
+ // In the first two cases, we grow the buffer by at least double, enough
+ // to ensure that new data will fit. We ignore the teardown flush.
+
+ if (data == writer->buffer) {
+
+ // teardown, do nothing
+ if (mpack_writer_buffer_used(writer) == count)
+ return;
+
+ // otherwise leave the data in the buffer and just grow
+ writer->current = writer->buffer + count;
+ count = 0;
+ }
+
+ size_t used = mpack_writer_buffer_used(writer);
+ size_t size = mpack_writer_buffer_size(writer);
+
+ mpack_log("flush size %i used %i data %p buffer %p\n",
+ (int)count, (int)used, data, writer->buffer);
+
+ mpack_assert(data == writer->buffer || used + count > size,
+ "extra flush for %i but there is %i space left in the buffer! (%i/%i)",
+ (int)count, (int)mpack_writer_buffer_left(writer), (int)used, (int)size);
+
+ // grow to fit the data
+ // TODO: this really needs to correctly test for overflow
+ size_t new_size = size * 2;
+ while (new_size < used + count)
+ new_size *= 2;
+
+ mpack_log("flush growing buffer size from %i to %i\n", (int)size, (int)new_size);
+
+ // grow the buffer
+ char* new_buffer = (char*)mpack_realloc(writer->buffer, used, new_size);
+ if (new_buffer == NULL) {
+ mpack_writer_flag_error(writer, mpack_error_memory);
+ return;
+ }
+ writer->current = new_buffer + used;
+ writer->buffer = new_buffer;
+ writer->end = writer->buffer + new_size;
+
+ // append the extra data
+ if (count > 0) {
+ mpack_memcpy(writer->current, data, count);
+ writer->current += count;
+ }
+
+ mpack_log("new buffer %p, used %i\n", new_buffer, (int)mpack_writer_buffer_used(writer));
+}
+
+static void mpack_growable_writer_teardown(mpack_writer_t* writer) {
+ mpack_growable_writer_t* growable_writer = (mpack_growable_writer_t*)mpack_writer_get_reserved(writer);
+
+ if (mpack_writer_error(writer) == mpack_ok) {
+
+ // shrink the buffer to an appropriate size if the data is
+ // much smaller than the buffer
+ if (mpack_writer_buffer_used(writer) < mpack_writer_buffer_size(writer) / 2) {
+ size_t used = mpack_writer_buffer_used(writer);
+
+ // We always return a non-null pointer that must be freed, even if
+ // nothing was written. malloc() and realloc() do not necessarily
+ // do this so we enforce it ourselves.
+ size_t size = (used != 0) ? used : 1;
+
+ char* buffer = (char*)mpack_realloc(writer->buffer, used, size);
+ if (!buffer) {
+ MPACK_FREE(writer->buffer);
+ mpack_writer_flag_error(writer, mpack_error_memory);
+ return;
+ }
+ writer->buffer = buffer;
+ writer->end = (writer->current = writer->buffer + used);
+ }
+
+ *growable_writer->target_data = writer->buffer;
+ *growable_writer->target_size = mpack_writer_buffer_used(writer);
+ writer->buffer = NULL;
+
+ } else if (writer->buffer) {
+ MPACK_FREE(writer->buffer);
+ writer->buffer = NULL;
+ }
+
+ writer->context = NULL;
+}
+
+void mpack_writer_init_growable(mpack_writer_t* writer, char** target_data, size_t* target_size) {
+ mpack_assert(target_data != NULL, "cannot initialize writer without a destination for the data");
+ mpack_assert(target_size != NULL, "cannot initialize writer without a destination for the size");
+
+ *target_data = NULL;
+ *target_size = 0;
+
+ MPACK_STATIC_ASSERT(sizeof(mpack_growable_writer_t) <= sizeof(writer->reserved),
+ "not enough reserved space for growable writer!");
+ mpack_growable_writer_t* growable_writer = (mpack_growable_writer_t*)mpack_writer_get_reserved(writer);
+
+ growable_writer->target_data = target_data;
+ growable_writer->target_size = target_size;
+
+ size_t capacity = MPACK_BUFFER_SIZE;
+ char* buffer = (char*)MPACK_MALLOC(capacity);
+ if (buffer == NULL) {
+ mpack_writer_init_error(writer, mpack_error_memory);
+ return;
+ }
+
+ mpack_writer_init(writer, buffer, capacity);
+ mpack_writer_set_flush(writer, mpack_growable_writer_flush);
+ mpack_writer_set_teardown(writer, mpack_growable_writer_teardown);
+}
+#endif
+
+#if MPACK_STDIO
+static void mpack_file_writer_flush(mpack_writer_t* writer, const char* buffer, size_t count) {
+ FILE* file = (FILE*)writer->context;
+ size_t written = fwrite((const void*)buffer, 1, count, file);
+ if (written != count)
+ mpack_writer_flag_error(writer, mpack_error_io);
+}
+
+static void mpack_file_writer_teardown(mpack_writer_t* writer) {
+ MPACK_FREE(writer->buffer);
+ writer->buffer = NULL;
+ writer->context = NULL;
+}
+
+static void mpack_file_writer_teardown_close(mpack_writer_t* writer) {
+ FILE* file = (FILE*)writer->context;
+
+ if (file) {
+ int ret = fclose(file);
+ if (ret != 0)
+ mpack_writer_flag_error(writer, mpack_error_io);
+ }
+
+ mpack_file_writer_teardown(writer);
+}
+
+void mpack_writer_init_stdfile(mpack_writer_t* writer, FILE* file, bool close_when_done) {
+ mpack_assert(file != NULL, "file is NULL");
+
+ size_t capacity = MPACK_BUFFER_SIZE;
+ char* buffer = (char*)MPACK_MALLOC(capacity);
+ if (buffer == NULL) {
+ mpack_writer_init_error(writer, mpack_error_memory);
+ if (close_when_done) {
+ fclose(file);
+ }
+ return;
+ }
+
+ mpack_writer_init(writer, buffer, capacity);
+ mpack_writer_set_context(writer, file);
+ mpack_writer_set_flush(writer, mpack_file_writer_flush);
+ mpack_writer_set_teardown(writer, close_when_done ?
+ mpack_file_writer_teardown_close :
+ mpack_file_writer_teardown);
+}
+
+void mpack_writer_init_filename(mpack_writer_t* writer, const char* filename) {
+ mpack_assert(filename != NULL, "filename is NULL");
+
+ FILE* file = fopen(filename, "wb");
+ if (file == NULL) {
+ mpack_writer_init_error(writer, mpack_error_io);
+ return;
+ }
+
+ mpack_writer_init_stdfile(writer, file, true);
+}
+#endif
+
+void mpack_writer_flag_error(mpack_writer_t* writer, mpack_error_t error) {
+ mpack_log("writer %p setting error %i: %s\n", writer, (int)error, mpack_error_to_string(error));
+
+ if (writer->error == mpack_ok) {
+ writer->error = error;
+ if (writer->error_fn)
+ writer->error_fn(writer, writer->error);
+ }
+}
+
+MPACK_STATIC_INLINE void mpack_writer_flush_unchecked(mpack_writer_t* writer) {
+ // This is a bit ugly; we reset used before calling flush so that
+ // a flush function can distinguish between flushing the buffer
+ // versus flushing external data. see mpack_growable_writer_flush()
+ size_t used = mpack_writer_buffer_used(writer);
+ writer->current = writer->buffer;
+ writer->flush(writer, writer->buffer, used);
+}
+
+void mpack_writer_flush_message(mpack_writer_t* writer) {
+ if (writer->error != mpack_ok)
+ return;
+
+ #if MPACK_WRITE_TRACKING
+ mpack_writer_flag_if_error(writer, mpack_track_check_empty(&writer->track));
+ if (writer->error != mpack_ok)
+ return;
+ #endif
+
+ if (writer->flush == NULL) {
+ mpack_break("cannot call mpack_writer_flush_message() without a flush function!");
+ mpack_writer_flag_error(writer, mpack_error_bug);
+ return;
+ }
+
+ if (mpack_writer_buffer_used(writer) > 0)
+ mpack_writer_flush_unchecked(writer);
+}
+
+// Ensures there are at least count bytes free in the buffer. This
+// will flag an error if the flush function fails to make enough
+// room in the buffer.
+MPACK_NOINLINE static bool mpack_writer_ensure(mpack_writer_t* writer, size_t count) {
+ mpack_assert(count != 0, "cannot ensure zero bytes!");
+ mpack_assert(count <= MPACK_WRITER_MINIMUM_BUFFER_SIZE,
+ "cannot ensure %i bytes, this is more than the minimum buffer size %i!",
+ (int)count, (int)MPACK_WRITER_MINIMUM_BUFFER_SIZE);
+ mpack_assert(count > mpack_writer_buffer_left(writer),
+ "request to ensure %i bytes but there are already %i left in the buffer!",
+ (int)count, (int)mpack_writer_buffer_left(writer));
+
+ mpack_log("ensuring %i bytes, %i left\n", (int)count, (int)mpack_writer_buffer_left(writer));
+
+ if (mpack_writer_error(writer) != mpack_ok)
+ return false;
+
+ if (writer->flush == NULL) {
+ mpack_writer_flag_error(writer, mpack_error_too_big);
+ return false;
+ }
+
+ mpack_writer_flush_unchecked(writer);
+ if (mpack_writer_error(writer) != mpack_ok)
+ return false;
+
+ if (mpack_writer_buffer_left(writer) >= count)
+ return true;
+
+ mpack_writer_flag_error(writer, mpack_error_io);
+ return false;
+}
+
+// Writes encoded bytes to the buffer when we already know the data
+// does not fit in the buffer (i.e. it straddles the edge of the
+// buffer.) If there is a flush function, it is guaranteed to be
+// called; otherwise mpack_error_too_big is raised.
+MPACK_NOINLINE static void mpack_write_native_straddle(mpack_writer_t* writer, const char* p, size_t count) {
+ mpack_assert(count == 0 || p != NULL, "data pointer for %i bytes is NULL", (int)count);
+
+ if (mpack_writer_error(writer) != mpack_ok)
+ return;
+ mpack_log("big write for %i bytes from %p, %i space left in buffer\n",
+ (int)count, p, (int)mpack_writer_buffer_left(writer));
+ mpack_assert(count > mpack_writer_buffer_left(writer),
+ "big write requested for %i bytes, but there is %i available "
+ "space in buffer. should have called mpack_write_native() instead",
+ (int)count, (int)(mpack_writer_buffer_left(writer)));
+
+ // we'll need a flush function
+ if (!writer->flush) {
+ mpack_writer_flag_error(writer, mpack_error_too_big);
+ return;
+ }
+
+ // flush the buffer
+ mpack_writer_flush_unchecked(writer);
+ if (mpack_writer_error(writer) != mpack_ok)
+ return;
+
+ // note that an intrusive flush function (such as mpack_growable_writer_flush())
+ // may have changed size and/or reset used to a non-zero value. we treat both as
+ // though they may have changed, and there may still be data in the buffer.
+
+ // flush the extra data directly if it doesn't fit in the buffer
+ if (count > mpack_writer_buffer_left(writer)) {
+ writer->flush(writer, p, count);
+ if (mpack_writer_error(writer) != mpack_ok)
+ return;
+ } else {
+ mpack_memcpy(writer->current, p, count);
+ writer->current += count;
+ }
+}
+
+// Writes encoded bytes to the buffer, flushing if necessary.
+MPACK_STATIC_INLINE void mpack_write_native(mpack_writer_t* writer, const char* p, size_t count) {
+ mpack_assert(count == 0 || p != NULL, "data pointer for %i bytes is NULL", (int)count);
+
+ if (mpack_writer_buffer_left(writer) < count) {
+ mpack_write_native_straddle(writer, p, count);
+ } else {
+ mpack_memcpy(writer->current, p, count);
+ writer->current += count;
+ }
+}
+
+mpack_error_t mpack_writer_destroy(mpack_writer_t* writer) {
+
+ // clean up tracking, asserting if we're not already in an error state
+ #if MPACK_WRITE_TRACKING
+ mpack_track_destroy(&writer->track, writer->error != mpack_ok);
+ #endif
+
+ // flush any outstanding data
+ if (mpack_writer_error(writer) == mpack_ok && mpack_writer_buffer_used(writer) != 0 && writer->flush != NULL) {
+ writer->flush(writer, writer->buffer, mpack_writer_buffer_used(writer));
+ writer->flush = NULL;
+ }
+
+ if (writer->teardown) {
+ writer->teardown(writer);
+ writer->teardown = NULL;
+ }
+
+ return writer->error;
+}
+
+void mpack_write_tag(mpack_writer_t* writer, mpack_tag_t value) {
+ switch (value.type) {
+ case mpack_type_missing:
+ mpack_break("cannot write a missing value!");
+ mpack_writer_flag_error(writer, mpack_error_bug);
+ return;
+
+ case mpack_type_nil: mpack_write_nil (writer); return;
+ case mpack_type_bool: mpack_write_bool (writer, value.v.b); return;
+ case mpack_type_float: mpack_write_float (writer, value.v.f); return;
+ case mpack_type_double: mpack_write_double(writer, value.v.d); return;
+ case mpack_type_int: mpack_write_int (writer, value.v.i); return;
+ case mpack_type_uint: mpack_write_uint (writer, value.v.u); return;
+
+ case mpack_type_str: mpack_start_str(writer, value.v.l); return;
+ case mpack_type_bin: mpack_start_bin(writer, value.v.l); return;
+
+ #if MPACK_EXTENSIONS
+ case mpack_type_ext:
+ mpack_start_ext(writer, mpack_tag_ext_exttype(&value), mpack_tag_ext_length(&value));
+ return;
+ #endif
+
+ case mpack_type_array: mpack_start_array(writer, value.v.n); return;
+ case mpack_type_map: mpack_start_map(writer, value.v.n); return;
+ }
+
+ mpack_break("unrecognized type %i", (int)value.type);
+ mpack_writer_flag_error(writer, mpack_error_bug);
+}
+
+MPACK_STATIC_INLINE void mpack_write_byte_element(mpack_writer_t* writer, char value) {
+ mpack_writer_track_element(writer);
+ if (MPACK_LIKELY(mpack_writer_buffer_left(writer) >= 1) || mpack_writer_ensure(writer, 1))
+ *(writer->current++) = value;
+}
+
+void mpack_write_nil(mpack_writer_t* writer) {
+ mpack_write_byte_element(writer, (char)0xc0);
+}
+
+void mpack_write_bool(mpack_writer_t* writer, bool value) {
+ mpack_write_byte_element(writer, (char)(0xc2 | (value ? 1 : 0)));
+}
+
+void mpack_write_true(mpack_writer_t* writer) {
+ mpack_write_byte_element(writer, (char)0xc3);
+}
+
+void mpack_write_false(mpack_writer_t* writer) {
+ mpack_write_byte_element(writer, (char)0xc2);
+}
+
+void mpack_write_object_bytes(mpack_writer_t* writer, const char* data, size_t bytes) {
+ mpack_writer_track_element(writer);
+ mpack_write_native(writer, data, bytes);
+}
+
+/*
+ * Encode functions
+ */
+
+MPACK_STATIC_INLINE void mpack_encode_fixuint(char* p, uint8_t value) {
+ mpack_assert(value <= 127);
+ mpack_store_u8(p, value);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_u8(char* p, uint8_t value) {
+ mpack_assert(value > 127);
+ mpack_store_u8(p, 0xcc);
+ mpack_store_u8(p + 1, value);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_u16(char* p, uint16_t value) {
+ mpack_assert(value > UINT8_MAX);
+ mpack_store_u8(p, 0xcd);
+ mpack_store_u16(p + 1, value);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_u32(char* p, uint32_t value) {
+ mpack_assert(value > UINT16_MAX);
+ mpack_store_u8(p, 0xce);
+ mpack_store_u32(p + 1, value);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_u64(char* p, uint64_t value) {
+ mpack_assert(value > UINT32_MAX);
+ mpack_store_u8(p, 0xcf);
+ mpack_store_u64(p + 1, value);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_fixint(char* p, int8_t value) {
+ // this can encode positive or negative fixints
+ mpack_assert(value >= -32);
+ mpack_store_i8(p, value);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_i8(char* p, int8_t value) {
+ mpack_assert(value < -32);
+ mpack_store_u8(p, 0xd0);
+ mpack_store_i8(p + 1, value);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_i16(char* p, int16_t value) {
+ mpack_assert(value < INT8_MIN);
+ mpack_store_u8(p, 0xd1);
+ mpack_store_i16(p + 1, value);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_i32(char* p, int32_t value) {
+ mpack_assert(value < INT16_MIN);
+ mpack_store_u8(p, 0xd2);
+ mpack_store_i32(p + 1, value);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_i64(char* p, int64_t value) {
+ mpack_assert(value < INT32_MIN);
+ mpack_store_u8(p, 0xd3);
+ mpack_store_i64(p + 1, value);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_float(char* p, float value) {
+ mpack_store_u8(p, 0xca);
+ mpack_store_float(p + 1, value);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_double(char* p, double value) {
+ mpack_store_u8(p, 0xcb);
+ mpack_store_double(p + 1, value);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_fixarray(char* p, uint8_t count) {
+ mpack_assert(count <= 15);
+ mpack_store_u8(p, (uint8_t)(0x90 | count));
+}
+
+MPACK_STATIC_INLINE void mpack_encode_array16(char* p, uint16_t count) {
+ mpack_assert(count > 15);
+ mpack_store_u8(p, 0xdc);
+ mpack_store_u16(p + 1, count);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_array32(char* p, uint32_t count) {
+ mpack_assert(count > UINT16_MAX);
+ mpack_store_u8(p, 0xdd);
+ mpack_store_u32(p + 1, count);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_fixmap(char* p, uint8_t count) {
+ mpack_assert(count <= 15);
+ mpack_store_u8(p, (uint8_t)(0x80 | count));
+}
+
+MPACK_STATIC_INLINE void mpack_encode_map16(char* p, uint16_t count) {
+ mpack_assert(count > 15);
+ mpack_store_u8(p, 0xde);
+ mpack_store_u16(p + 1, count);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_map32(char* p, uint32_t count) {
+ mpack_assert(count > UINT16_MAX);
+ mpack_store_u8(p, 0xdf);
+ mpack_store_u32(p + 1, count);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_fixstr(char* p, uint8_t count) {
+ mpack_assert(count <= 31);
+ mpack_store_u8(p, (uint8_t)(0xa0 | count));
+}
+
+MPACK_STATIC_INLINE void mpack_encode_str8(char* p, uint8_t count) {
+ mpack_assert(count > 31);
+ mpack_store_u8(p, 0xd9);
+ mpack_store_u8(p + 1, count);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_str16(char* p, uint16_t count) {
+ // we might be encoding a raw in compatibility mode, so we
+ // allow count to be in the range [32, UINT8_MAX].
+ mpack_assert(count > 31);
+ mpack_store_u8(p, 0xda);
+ mpack_store_u16(p + 1, count);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_str32(char* p, uint32_t count) {
+ mpack_assert(count > UINT16_MAX);
+ mpack_store_u8(p, 0xdb);
+ mpack_store_u32(p + 1, count);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_bin8(char* p, uint8_t count) {
+ mpack_store_u8(p, 0xc4);
+ mpack_store_u8(p + 1, count);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_bin16(char* p, uint16_t count) {
+ mpack_assert(count > UINT8_MAX);
+ mpack_store_u8(p, 0xc5);
+ mpack_store_u16(p + 1, count);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_bin32(char* p, uint32_t count) {
+ mpack_assert(count > UINT16_MAX);
+ mpack_store_u8(p, 0xc6);
+ mpack_store_u32(p + 1, count);
+}
+
+#if MPACK_EXTENSIONS
+MPACK_STATIC_INLINE void mpack_encode_fixext1(char* p, int8_t exttype) {
+ mpack_store_u8(p, 0xd4);
+ mpack_store_i8(p + 1, exttype);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_fixext2(char* p, int8_t exttype) {
+ mpack_store_u8(p, 0xd5);
+ mpack_store_i8(p + 1, exttype);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_fixext4(char* p, int8_t exttype) {
+ mpack_store_u8(p, 0xd6);
+ mpack_store_i8(p + 1, exttype);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_fixext8(char* p, int8_t exttype) {
+ mpack_store_u8(p, 0xd7);
+ mpack_store_i8(p + 1, exttype);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_fixext16(char* p, int8_t exttype) {
+ mpack_store_u8(p, 0xd8);
+ mpack_store_i8(p + 1, exttype);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_ext8(char* p, int8_t exttype, uint8_t count) {
+ mpack_assert(count != 1 && count != 2 && count != 4 && count != 8 && count != 16);
+ mpack_store_u8(p, 0xc7);
+ mpack_store_u8(p + 1, count);
+ mpack_store_i8(p + 2, exttype);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_ext16(char* p, int8_t exttype, uint16_t count) {
+ mpack_assert(count > UINT8_MAX);
+ mpack_store_u8(p, 0xc8);
+ mpack_store_u16(p + 1, count);
+ mpack_store_i8(p + 3, exttype);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_ext32(char* p, int8_t exttype, uint32_t count) {
+ mpack_assert(count > UINT16_MAX);
+ mpack_store_u8(p, 0xc9);
+ mpack_store_u32(p + 1, count);
+ mpack_store_i8(p + 5, exttype);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_timestamp_4(char* p, uint32_t seconds) {
+ mpack_encode_fixext4(p, MPACK_EXTTYPE_TIMESTAMP);
+ mpack_store_u32(p + MPACK_TAG_SIZE_FIXEXT4, seconds);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_timestamp_8(char* p, int64_t seconds, uint32_t nanoseconds) {
+ mpack_assert(nanoseconds <= MPACK_TIMESTAMP_NANOSECONDS_MAX);
+ mpack_encode_fixext8(p, MPACK_EXTTYPE_TIMESTAMP);
+ uint64_t encoded = ((uint64_t)nanoseconds << 34) | (uint64_t)seconds;
+ mpack_store_u64(p + MPACK_TAG_SIZE_FIXEXT8, encoded);
+}
+
+MPACK_STATIC_INLINE void mpack_encode_timestamp_12(char* p, int64_t seconds, uint32_t nanoseconds) {
+ mpack_assert(nanoseconds <= MPACK_TIMESTAMP_NANOSECONDS_MAX);
+ mpack_encode_ext8(p, MPACK_EXTTYPE_TIMESTAMP, 12);
+ mpack_store_u32(p + MPACK_TAG_SIZE_EXT8, nanoseconds);
+ mpack_store_i64(p + MPACK_TAG_SIZE_EXT8 + 4, seconds);
+}
+#endif
+
+
+
+/*
+ * Write functions
+ */
+
+// This is a macro wrapper to the encode functions to encode
+// directly into the buffer. If mpack_writer_ensure() fails
+// it will flag an error so we don't have to do anything.
+#define MPACK_WRITE_ENCODED(encode_fn, size, ...) do { \
+ if (MPACK_LIKELY(mpack_writer_buffer_left(writer) >= size) || mpack_writer_ensure(writer, size)) { \
+ MPACK_EXPAND(encode_fn(writer->current, __VA_ARGS__)); \
+ writer->current += size; \
+ } \
+} while (0)
+
+void mpack_write_u8(mpack_writer_t* writer, uint8_t value) {
+ #if MPACK_OPTIMIZE_FOR_SIZE
+ mpack_write_u64(writer, value);
+ #else
+ mpack_writer_track_element(writer);
+ if (value <= 127) {
+ MPACK_WRITE_ENCODED(mpack_encode_fixuint, MPACK_TAG_SIZE_FIXUINT, value);
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_u8, MPACK_TAG_SIZE_U8, value);
+ }
+ #endif
+}
+
+void mpack_write_u16(mpack_writer_t* writer, uint16_t value) {
+ #if MPACK_OPTIMIZE_FOR_SIZE
+ mpack_write_u64(writer, value);
+ #else
+ mpack_writer_track_element(writer);
+ if (value <= 127) {
+ MPACK_WRITE_ENCODED(mpack_encode_fixuint, MPACK_TAG_SIZE_FIXUINT, (uint8_t)value);
+ } else if (value <= UINT8_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_u8, MPACK_TAG_SIZE_U8, (uint8_t)value);
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_u16, MPACK_TAG_SIZE_U16, value);
+ }
+ #endif
+}
+
+void mpack_write_u32(mpack_writer_t* writer, uint32_t value) {
+ #if MPACK_OPTIMIZE_FOR_SIZE
+ mpack_write_u64(writer, value);
+ #else
+ mpack_writer_track_element(writer);
+ if (value <= 127) {
+ MPACK_WRITE_ENCODED(mpack_encode_fixuint, MPACK_TAG_SIZE_FIXUINT, (uint8_t)value);
+ } else if (value <= UINT8_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_u8, MPACK_TAG_SIZE_U8, (uint8_t)value);
+ } else if (value <= UINT16_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_u16, MPACK_TAG_SIZE_U16, (uint16_t)value);
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_u32, MPACK_TAG_SIZE_U32, value);
+ }
+ #endif
+}
+
+void mpack_write_u64(mpack_writer_t* writer, uint64_t value) {
+ mpack_writer_track_element(writer);
+
+ if (value <= 127) {
+ MPACK_WRITE_ENCODED(mpack_encode_fixuint, MPACK_TAG_SIZE_FIXUINT, (uint8_t)value);
+ } else if (value <= UINT8_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_u8, MPACK_TAG_SIZE_U8, (uint8_t)value);
+ } else if (value <= UINT16_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_u16, MPACK_TAG_SIZE_U16, (uint16_t)value);
+ } else if (value <= UINT32_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_u32, MPACK_TAG_SIZE_U32, (uint32_t)value);
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_u64, MPACK_TAG_SIZE_U64, value);
+ }
+}
+
+void mpack_write_i8(mpack_writer_t* writer, int8_t value) {
+ #if MPACK_OPTIMIZE_FOR_SIZE
+ mpack_write_i64(writer, value);
+ #else
+ mpack_writer_track_element(writer);
+ if (value >= -32) {
+ // we encode positive and negative fixints together
+ MPACK_WRITE_ENCODED(mpack_encode_fixint, MPACK_TAG_SIZE_FIXINT, (int8_t)value);
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_i8, MPACK_TAG_SIZE_I8, (int8_t)value);
+ }
+ #endif
+}
+
+void mpack_write_i16(mpack_writer_t* writer, int16_t value) {
+ #if MPACK_OPTIMIZE_FOR_SIZE
+ mpack_write_i64(writer, value);
+ #else
+ mpack_writer_track_element(writer);
+ if (value >= -32) {
+ if (value <= 127) {
+ // we encode positive and negative fixints together
+ MPACK_WRITE_ENCODED(mpack_encode_fixint, MPACK_TAG_SIZE_FIXINT, (int8_t)value);
+ } else if (value <= UINT8_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_u8, MPACK_TAG_SIZE_U8, (uint8_t)value);
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_u16, MPACK_TAG_SIZE_U16, (uint16_t)value);
+ }
+ } else if (value >= INT8_MIN) {
+ MPACK_WRITE_ENCODED(mpack_encode_i8, MPACK_TAG_SIZE_I8, (int8_t)value);
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_i16, MPACK_TAG_SIZE_I16, (int16_t)value);
+ }
+ #endif
+}
+
+void mpack_write_i32(mpack_writer_t* writer, int32_t value) {
+ #if MPACK_OPTIMIZE_FOR_SIZE
+ mpack_write_i64(writer, value);
+ #else
+ mpack_writer_track_element(writer);
+ if (value >= -32) {
+ if (value <= 127) {
+ // we encode positive and negative fixints together
+ MPACK_WRITE_ENCODED(mpack_encode_fixint, MPACK_TAG_SIZE_FIXINT, (int8_t)value);
+ } else if (value <= UINT8_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_u8, MPACK_TAG_SIZE_U8, (uint8_t)value);
+ } else if (value <= UINT16_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_u16, MPACK_TAG_SIZE_U16, (uint16_t)value);
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_u32, MPACK_TAG_SIZE_U32, (uint32_t)value);
+ }
+ } else if (value >= INT8_MIN) {
+ MPACK_WRITE_ENCODED(mpack_encode_i8, MPACK_TAG_SIZE_I8, (int8_t)value);
+ } else if (value >= INT16_MIN) {
+ MPACK_WRITE_ENCODED(mpack_encode_i16, MPACK_TAG_SIZE_I16, (int16_t)value);
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_i32, MPACK_TAG_SIZE_I32, value);
+ }
+ #endif
+}
+
+void mpack_write_i64(mpack_writer_t* writer, int64_t value) {
+ #if MPACK_OPTIMIZE_FOR_SIZE
+ if (value > 127) {
+ // for non-fix positive ints we call the u64 writer to save space
+ mpack_write_u64(writer, (uint64_t)value);
+ return;
+ }
+ #endif
+
+ mpack_writer_track_element(writer);
+ if (value >= -32) {
+ #if MPACK_OPTIMIZE_FOR_SIZE
+ MPACK_WRITE_ENCODED(mpack_encode_fixint, MPACK_TAG_SIZE_FIXINT, (int8_t)value);
+ #else
+ if (value <= 127) {
+ MPACK_WRITE_ENCODED(mpack_encode_fixint, MPACK_TAG_SIZE_FIXINT, (int8_t)value);
+ } else if (value <= UINT8_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_u8, MPACK_TAG_SIZE_U8, (uint8_t)value);
+ } else if (value <= UINT16_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_u16, MPACK_TAG_SIZE_U16, (uint16_t)value);
+ } else if (value <= UINT32_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_u32, MPACK_TAG_SIZE_U32, (uint32_t)value);
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_u64, MPACK_TAG_SIZE_U64, (uint64_t)value);
+ }
+ #endif
+ } else if (value >= INT8_MIN) {
+ MPACK_WRITE_ENCODED(mpack_encode_i8, MPACK_TAG_SIZE_I8, (int8_t)value);
+ } else if (value >= INT16_MIN) {
+ MPACK_WRITE_ENCODED(mpack_encode_i16, MPACK_TAG_SIZE_I16, (int16_t)value);
+ } else if (value >= INT32_MIN) {
+ MPACK_WRITE_ENCODED(mpack_encode_i32, MPACK_TAG_SIZE_I32, (int32_t)value);
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_i64, MPACK_TAG_SIZE_I64, value);
+ }
+}
+
+void mpack_write_float(mpack_writer_t* writer, float value) {
+ mpack_writer_track_element(writer);
+ MPACK_WRITE_ENCODED(mpack_encode_float, MPACK_TAG_SIZE_FLOAT, value);
+}
+
+void mpack_write_double(mpack_writer_t* writer, double value) {
+ mpack_writer_track_element(writer);
+ MPACK_WRITE_ENCODED(mpack_encode_double, MPACK_TAG_SIZE_DOUBLE, value);
+}
+
+#if MPACK_EXTENSIONS
+void mpack_write_timestamp(mpack_writer_t* writer, int64_t seconds, uint32_t nanoseconds) {
+ #if MPACK_COMPATIBILITY
+ if (writer->version <= mpack_version_v4) {
+ mpack_break("Timestamps require spec version v5 or later. This writer is in v%i mode.", (int)writer->version);
+ mpack_writer_flag_error(writer, mpack_error_bug);
+ return;
+ }
+ #endif
+
+ if (nanoseconds > MPACK_TIMESTAMP_NANOSECONDS_MAX) {
+ mpack_break("timestamp nanoseconds out of bounds: %u", nanoseconds);
+ mpack_writer_flag_error(writer, mpack_error_bug);
+ return;
+ }
+
+ mpack_writer_track_element(writer);
+
+ if (seconds < 0 || seconds >= (INT64_C(1) << 34)) {
+ MPACK_WRITE_ENCODED(mpack_encode_timestamp_12, MPACK_EXT_SIZE_TIMESTAMP12, seconds, nanoseconds);
+ } else if (seconds > UINT32_MAX || nanoseconds > 0) {
+ MPACK_WRITE_ENCODED(mpack_encode_timestamp_8, MPACK_EXT_SIZE_TIMESTAMP8, seconds, nanoseconds);
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_timestamp_4, MPACK_EXT_SIZE_TIMESTAMP4, (uint32_t)seconds);
+ }
+}
+#endif
+
+void mpack_start_array(mpack_writer_t* writer, uint32_t count) {
+ mpack_writer_track_element(writer);
+
+ if (count <= 15) {
+ MPACK_WRITE_ENCODED(mpack_encode_fixarray, MPACK_TAG_SIZE_FIXARRAY, (uint8_t)count);
+ } else if (count <= UINT16_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_array16, MPACK_TAG_SIZE_ARRAY16, (uint16_t)count);
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_array32, MPACK_TAG_SIZE_ARRAY32, (uint32_t)count);
+ }
+
+ mpack_writer_track_push(writer, mpack_type_array, count);
+}
+
+void mpack_start_map(mpack_writer_t* writer, uint32_t count) {
+ mpack_writer_track_element(writer);
+
+ if (count <= 15) {
+ MPACK_WRITE_ENCODED(mpack_encode_fixmap, MPACK_TAG_SIZE_FIXMAP, (uint8_t)count);
+ } else if (count <= UINT16_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_map16, MPACK_TAG_SIZE_MAP16, (uint16_t)count);
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_map32, MPACK_TAG_SIZE_MAP32, (uint32_t)count);
+ }
+
+ mpack_writer_track_push(writer, mpack_type_map, count);
+}
+
+static void mpack_start_str_notrack(mpack_writer_t* writer, uint32_t count) {
+ if (count <= 31) {
+ MPACK_WRITE_ENCODED(mpack_encode_fixstr, MPACK_TAG_SIZE_FIXSTR, (uint8_t)count);
+
+ // str8 is only supported in v5 or later.
+ } else if (count <= UINT8_MAX
+ #if MPACK_COMPATIBILITY
+ && writer->version >= mpack_version_v5
+ #endif
+ ) {
+ MPACK_WRITE_ENCODED(mpack_encode_str8, MPACK_TAG_SIZE_STR8, (uint8_t)count);
+
+ } else if (count <= UINT16_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_str16, MPACK_TAG_SIZE_STR16, (uint16_t)count);
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_str32, MPACK_TAG_SIZE_STR32, (uint32_t)count);
+ }
+}
+
+static void mpack_start_bin_notrack(mpack_writer_t* writer, uint32_t count) {
+ #if MPACK_COMPATIBILITY
+ // In the v4 spec, there was only the raw type for any kind of
+ // variable-length data. In v4 mode, we support the bin functions,
+ // but we produce an old-style raw.
+ if (writer->version <= mpack_version_v4) {
+ mpack_start_str_notrack(writer, count);
+ return;
+ }
+ #endif
+
+ if (count <= UINT8_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_bin8, MPACK_TAG_SIZE_BIN8, (uint8_t)count);
+ } else if (count <= UINT16_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_bin16, MPACK_TAG_SIZE_BIN16, (uint16_t)count);
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_bin32, MPACK_TAG_SIZE_BIN32, (uint32_t)count);
+ }
+}
+
+void mpack_start_str(mpack_writer_t* writer, uint32_t count) {
+ mpack_writer_track_element(writer);
+ mpack_start_str_notrack(writer, count);
+ mpack_writer_track_push(writer, mpack_type_str, count);
+}
+
+void mpack_start_bin(mpack_writer_t* writer, uint32_t count) {
+ mpack_writer_track_element(writer);
+ mpack_start_bin_notrack(writer, count);
+ mpack_writer_track_push(writer, mpack_type_bin, count);
+}
+
+#if MPACK_EXTENSIONS
+void mpack_start_ext(mpack_writer_t* writer, int8_t exttype, uint32_t count) {
+ #if MPACK_COMPATIBILITY
+ if (writer->version <= mpack_version_v4) {
+ mpack_break("Ext types require spec version v5 or later. This writer is in v%i mode.", (int)writer->version);
+ mpack_writer_flag_error(writer, mpack_error_bug);
+ return;
+ }
+ #endif
+
+ mpack_writer_track_element(writer);
+
+ if (count == 1) {
+ MPACK_WRITE_ENCODED(mpack_encode_fixext1, MPACK_TAG_SIZE_FIXEXT1, exttype);
+ } else if (count == 2) {
+ MPACK_WRITE_ENCODED(mpack_encode_fixext2, MPACK_TAG_SIZE_FIXEXT2, exttype);
+ } else if (count == 4) {
+ MPACK_WRITE_ENCODED(mpack_encode_fixext4, MPACK_TAG_SIZE_FIXEXT4, exttype);
+ } else if (count == 8) {
+ MPACK_WRITE_ENCODED(mpack_encode_fixext8, MPACK_TAG_SIZE_FIXEXT8, exttype);
+ } else if (count == 16) {
+ MPACK_WRITE_ENCODED(mpack_encode_fixext16, MPACK_TAG_SIZE_FIXEXT16, exttype);
+ } else if (count <= UINT8_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_ext8, MPACK_TAG_SIZE_EXT8, exttype, (uint8_t)count);
+ } else if (count <= UINT16_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_ext16, MPACK_TAG_SIZE_EXT16, exttype, (uint16_t)count);
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_ext32, MPACK_TAG_SIZE_EXT32, exttype, (uint32_t)count);
+ }
+
+ mpack_writer_track_push(writer, mpack_type_ext, count);
+}
+#endif
+
+
+
+/*
+ * Compound helpers and other functions
+ */
+
+void mpack_write_str(mpack_writer_t* writer, const char* data, uint32_t count) {
+ mpack_assert(data != NULL, "data for string of length %i is NULL", (int)count);
+
+ #if MPACK_OPTIMIZE_FOR_SIZE
+ mpack_writer_track_element(writer);
+ mpack_start_str_notrack(writer, count);
+ mpack_write_native(writer, data, count);
+ #else
+
+ mpack_writer_track_element(writer);
+
+ if (count <= 31) {
+ // The minimum buffer size when using a flush function is guaranteed to
+ // fit the largest possible fixstr.
+ size_t size = count + MPACK_TAG_SIZE_FIXSTR;
+ if (MPACK_LIKELY(mpack_writer_buffer_left(writer) >= size) || mpack_writer_ensure(writer, size)) {
+ char* MPACK_RESTRICT p = writer->current;
+ mpack_encode_fixstr(p, (uint8_t)count);
+ mpack_memcpy(p + MPACK_TAG_SIZE_FIXSTR, data, count);
+ writer->current += count + MPACK_TAG_SIZE_FIXSTR;
+ }
+ return;
+ }
+
+ if (count <= UINT8_MAX
+ #if MPACK_COMPATIBILITY
+ && writer->version >= mpack_version_v5
+ #endif
+ ) {
+ if (count + MPACK_TAG_SIZE_STR8 <= mpack_writer_buffer_left(writer)) {
+ char* MPACK_RESTRICT p = writer->current;
+ mpack_encode_str8(p, (uint8_t)count);
+ mpack_memcpy(p + MPACK_TAG_SIZE_STR8, data, count);
+ writer->current += count + MPACK_TAG_SIZE_STR8;
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_str8, MPACK_TAG_SIZE_STR8, (uint8_t)count);
+ mpack_write_native(writer, data, count);
+ }
+ return;
+ }
+
+ // str16 and str32 are likely to be a significant fraction of the buffer
+ // size, so we don't bother with a combined space check in order to
+ // minimize code size.
+ if (count <= UINT16_MAX) {
+ MPACK_WRITE_ENCODED(mpack_encode_str16, MPACK_TAG_SIZE_STR16, (uint16_t)count);
+ mpack_write_native(writer, data, count);
+ } else {
+ MPACK_WRITE_ENCODED(mpack_encode_str32, MPACK_TAG_SIZE_STR32, (uint32_t)count);
+ mpack_write_native(writer, data, count);
+ }
+
+ #endif
+}
+
+void mpack_write_bin(mpack_writer_t* writer, const char* data, uint32_t count) {
+ mpack_assert(data != NULL, "data pointer for bin of %i bytes is NULL", (int)count);
+ mpack_start_bin(writer, count);
+ mpack_write_bytes(writer, data, count);
+ mpack_finish_bin(writer);
+}
+
+#if MPACK_EXTENSIONS
+void mpack_write_ext(mpack_writer_t* writer, int8_t exttype, const char* data, uint32_t count) {
+ mpack_assert(data != NULL, "data pointer for ext of type %i and %i bytes is NULL", exttype, (int)count);
+ mpack_start_ext(writer, exttype, count);
+ mpack_write_bytes(writer, data, count);
+ mpack_finish_ext(writer);
+}
+#endif
+
+void mpack_write_bytes(mpack_writer_t* writer, const char* data, size_t count) {
+ mpack_assert(data != NULL, "data pointer for %i bytes is NULL", (int)count);
+ mpack_writer_track_bytes(writer, count);
+ mpack_write_native(writer, data, count);
+}
+
+void mpack_write_cstr(mpack_writer_t* writer, const char* cstr) {
+ mpack_assert(cstr != NULL, "cstr pointer is NULL");
+ size_t length = mpack_strlen(cstr);
+ if (length > UINT32_MAX)
+ mpack_writer_flag_error(writer, mpack_error_invalid);
+ mpack_write_str(writer, cstr, (uint32_t)length);
+}
+
+void mpack_write_cstr_or_nil(mpack_writer_t* writer, const char* cstr) {
+ if (cstr)
+ mpack_write_cstr(writer, cstr);
+ else
+ mpack_write_nil(writer);
+}
+
+void mpack_write_utf8(mpack_writer_t* writer, const char* str, uint32_t length) {
+ mpack_assert(str != NULL, "data for string of length %i is NULL", (int)length);
+ if (!mpack_utf8_check(str, length)) {
+ mpack_writer_flag_error(writer, mpack_error_invalid);
+ return;
+ }
+ mpack_write_str(writer, str, length);
+}
+
+void mpack_write_utf8_cstr(mpack_writer_t* writer, const char* cstr) {
+ mpack_assert(cstr != NULL, "cstr pointer is NULL");
+ size_t length = mpack_strlen(cstr);
+ if (length > UINT32_MAX) {
+ mpack_writer_flag_error(writer, mpack_error_invalid);
+ return;
+ }
+ mpack_write_utf8(writer, cstr, (uint32_t)length);
+}
+
+void mpack_write_utf8_cstr_or_nil(mpack_writer_t* writer, const char* cstr) {
+ if (cstr)
+ mpack_write_utf8_cstr(writer, cstr);
+ else
+ mpack_write_nil(writer);
+}
+
+#endif
+
+
+/* mpack/mpack-reader.c.c */
+
+#define MPACK_INTERNAL 1
+
+/* #include "mpack-reader.h" */
+
+#if MPACK_READER
+
+static void mpack_reader_skip_using_fill(mpack_reader_t* reader, size_t count);
+
+void mpack_reader_init(mpack_reader_t* reader, char* buffer, size_t size, size_t count) {
+ mpack_assert(buffer != NULL, "buffer is NULL");
+
+ mpack_memset(reader, 0, sizeof(*reader));
+ reader->buffer = buffer;
+ reader->size = size;
+ reader->data = buffer;
+ reader->end = buffer + count;
+
+ #if MPACK_READ_TRACKING
+ mpack_reader_flag_if_error(reader, mpack_track_init(&reader->track));
+ #endif
+
+ mpack_log("===========================\n");
+ mpack_log("initializing reader with buffer size %i\n", (int)size);
+}
+
+void mpack_reader_init_error(mpack_reader_t* reader, mpack_error_t error) {
+ mpack_memset(reader, 0, sizeof(*reader));
+ reader->error = error;
+
+ mpack_log("===========================\n");
+ mpack_log("initializing reader error state %i\n", (int)error);
+}
+
+void mpack_reader_init_data(mpack_reader_t* reader, const char* data, size_t count) {
+ mpack_assert(data != NULL, "data is NULL");
+
+ mpack_memset(reader, 0, sizeof(*reader));
+ reader->data = data;
+ reader->end = data + count;
+
+ #if MPACK_READ_TRACKING
+ mpack_reader_flag_if_error(reader, mpack_track_init(&reader->track));
+ #endif
+
+ mpack_log("===========================\n");
+ mpack_log("initializing reader with data size %i\n", (int)count);
+}
+
+void mpack_reader_set_fill(mpack_reader_t* reader, mpack_reader_fill_t fill) {
+ MPACK_STATIC_ASSERT(MPACK_READER_MINIMUM_BUFFER_SIZE >= MPACK_MAXIMUM_TAG_SIZE,
+ "minimum buffer size must fit any tag!");
+
+ if (reader->size == 0) {
+ mpack_break("cannot use fill function without a writeable buffer!");
+ mpack_reader_flag_error(reader, mpack_error_bug);
+ return;
+ }
+
+ if (reader->size < MPACK_READER_MINIMUM_BUFFER_SIZE) {
+ mpack_break("buffer size is %i, but minimum buffer size for fill is %i",
+ (int)reader->size, MPACK_READER_MINIMUM_BUFFER_SIZE);
+ mpack_reader_flag_error(reader, mpack_error_bug);
+ return;
+ }
+
+ reader->fill = fill;
+}
+
+void mpack_reader_set_skip(mpack_reader_t* reader, mpack_reader_skip_t skip) {
+ mpack_assert(reader->size != 0, "cannot use skip function without a writeable buffer!");
+ reader->skip = skip;
+}
+
+#if MPACK_STDIO
+static size_t mpack_file_reader_fill(mpack_reader_t* reader, char* buffer, size_t count) {
+ if (feof((FILE *)reader->context)) {
+ mpack_reader_flag_error(reader, mpack_error_eof);
+ return 0;
+ }
+ return fread((void*)buffer, 1, count, (FILE*)reader->context);
+}
+
+static void mpack_file_reader_skip(mpack_reader_t* reader, size_t count) {
+ if (mpack_reader_error(reader) != mpack_ok)
+ return;
+ FILE* file = (FILE*)reader->context;
+
+ // We call ftell() to test whether the stream is seekable
+ // without causing a file error.
+ if (ftell(file) >= 0) {
+ mpack_log("seeking forward %i bytes\n", (int)count);
+ if (fseek(file, (long int)count, SEEK_CUR) == 0)
+ return;
+ mpack_log("fseek() didn't return zero!\n");
+ if (ferror(file)) {
+ mpack_reader_flag_error(reader, mpack_error_io);
+ return;
+ }
+ }
+
+ // If the stream is not seekable, fall back to the fill function.
+ mpack_reader_skip_using_fill(reader, count);
+}
+
+static void mpack_file_reader_teardown(mpack_reader_t* reader) {
+ MPACK_FREE(reader->buffer);
+ reader->buffer = NULL;
+ reader->context = NULL;
+ reader->size = 0;
+ reader->fill = NULL;
+ reader->skip = NULL;
+ reader->teardown = NULL;
+}
+
+static void mpack_file_reader_teardown_close(mpack_reader_t* reader) {
+ FILE* file = (FILE*)reader->context;
+
+ if (file) {
+ int ret = fclose(file);
+ if (ret != 0)
+ mpack_reader_flag_error(reader, mpack_error_io);
+ }
+
+ mpack_file_reader_teardown(reader);
+}
+
+void mpack_reader_init_stdfile(mpack_reader_t* reader, FILE* file, bool close_when_done) {
+ mpack_assert(file != NULL, "file is NULL");
+
+ size_t capacity = MPACK_BUFFER_SIZE;
+ char* buffer = (char*)MPACK_MALLOC(capacity);
+ if (buffer == NULL) {
+ mpack_reader_init_error(reader, mpack_error_memory);
+ if (close_when_done) {
+ fclose(file);
+ }
+ return;
+ }
+
+ mpack_reader_init(reader, buffer, capacity, 0);
+ mpack_reader_set_context(reader, file);
+ mpack_reader_set_fill(reader, mpack_file_reader_fill);
+ mpack_reader_set_skip(reader, mpack_file_reader_skip);
+ mpack_reader_set_teardown(reader, close_when_done ?
+ mpack_file_reader_teardown_close :
+ mpack_file_reader_teardown);
+}
+
+void mpack_reader_init_filename(mpack_reader_t* reader, const char* filename) {
+ mpack_assert(filename != NULL, "filename is NULL");
+
+ FILE* file = fopen(filename, "rb");
+ if (file == NULL) {
+ mpack_reader_init_error(reader, mpack_error_io);
+ return;
+ }
+
+ mpack_reader_init_stdfile(reader, file, true);
+}
+#endif
+
+mpack_error_t mpack_reader_destroy(mpack_reader_t* reader) {
+
+ // clean up tracking, asserting if we're not already in an error state
+ #if MPACK_READ_TRACKING
+ mpack_reader_flag_if_error(reader, mpack_track_destroy(&reader->track, mpack_reader_error(reader) != mpack_ok));
+ #endif
+
+ if (reader->teardown)
+ reader->teardown(reader);
+ reader->teardown = NULL;
+
+ return reader->error;
+}
+
+size_t mpack_reader_remaining(mpack_reader_t* reader, const char** data) {
+ if (mpack_reader_error(reader) != mpack_ok)
+ return 0;
+
+ #if MPACK_READ_TRACKING
+ if (mpack_reader_flag_if_error(reader, mpack_track_check_empty(&reader->track)) != mpack_ok)
+ return 0;
+ #endif
+
+ if (data)
+ *data = reader->data;
+ return (size_t)(reader->end - reader->data);
+}
+
+void mpack_reader_flag_error(mpack_reader_t* reader, mpack_error_t error) {
+ mpack_log("reader %p setting error %i: %s\n", reader, (int)error, mpack_error_to_string(error));
+
+ if (reader->error == mpack_ok) {
+ reader->error = error;
+ reader->end = reader->data;
+ if (reader->error_fn)
+ reader->error_fn(reader, error);
+ }
+}
+
+// Loops on the fill function, reading between the minimum and
+// maximum number of bytes and flagging an error if it fails.
+MPACK_NOINLINE static size_t mpack_fill_range(mpack_reader_t* reader, char* p, size_t min_bytes, size_t max_bytes) {
+ mpack_assert(reader->fill != NULL, "mpack_fill_range() called with no fill function?");
+ mpack_assert(min_bytes > 0, "cannot fill zero bytes!");
+ mpack_assert(max_bytes >= min_bytes, "min_bytes %i cannot be larger than max_bytes %i!",
+ (int)min_bytes, (int)max_bytes);
+
+ size_t count = 0;
+ while (count < min_bytes) {
+ size_t read = reader->fill(reader, p + count, max_bytes - count);
+
+ // Reader fill functions can flag an error or return 0 on failure. We
+ // also guard against functions that -1 just in case.
+ if (mpack_reader_error(reader) != mpack_ok)
+ return 0;
+ if (read == 0 || read == ((size_t)(-1))) {
+ mpack_reader_flag_error(reader, mpack_error_io);
+ return 0;
+ }
+
+ count += read;
+ }
+ return count;
+}
+
+MPACK_NOINLINE bool mpack_reader_ensure_straddle(mpack_reader_t* reader, size_t count) {
+ mpack_assert(count != 0, "cannot ensure zero bytes!");
+ mpack_assert(reader->error == mpack_ok, "reader cannot be in an error state!");
+
+ mpack_assert(count > (size_t)(reader->end - reader->data),
+ "straddling ensure requested for %i bytes, but there are %i bytes "
+ "left in buffer. call mpack_reader_ensure() instead",
+ (int)count, (int)(reader->end - reader->data));
+
+ // we'll need a fill function to get more data. if there's no
+ // fill function, the buffer should contain an entire MessagePack
+ // object, so we raise mpack_error_invalid instead of mpack_error_io
+ // on truncated data.
+ if (reader->fill == NULL) {
+ mpack_reader_flag_error(reader, mpack_error_invalid);
+ return false;
+ }
+
+ // we need enough space in the buffer. if the buffer is not
+ // big enough, we return mpack_error_too_big (since this is
+ // for an in-place read larger than the buffer size.)
+ if (count > reader->size) {
+ mpack_reader_flag_error(reader, mpack_error_too_big);
+ return false;
+ }
+
+ // move the existing data to the start of the buffer
+ size_t left = (size_t)(reader->end - reader->data);
+ mpack_memmove(reader->buffer, reader->data, left);
+ reader->end -= reader->data - reader->buffer;
+ reader->data = reader->buffer;
+
+ // read at least the necessary number of bytes, accepting up to the
+ // buffer size
+ size_t read = mpack_fill_range(reader, reader->buffer + left,
+ count - left, reader->size - left);
+ if (mpack_reader_error(reader) != mpack_ok)
+ return false;
+ reader->end += read;
+ return true;
+}
+
+// Reads count bytes into p. Used when there are not enough bytes
+// left in the buffer to satisfy a read.
+MPACK_NOINLINE void mpack_read_native_straddle(mpack_reader_t* reader, char* p, size_t count) {
+ mpack_assert(count == 0 || p != NULL, "data pointer for %i bytes is NULL", (int)count);
+
+ if (mpack_reader_error(reader) != mpack_ok) {
+ mpack_memset(p, 0, count);
+ return;
+ }
+
+ size_t left = (size_t)(reader->end - reader->data);
+ mpack_log("big read for %i bytes into %p, %i left in buffer, buffer size %i\n",
+ (int)count, p, (int)left, (int)reader->size);
+
+ if (count <= left) {
+ mpack_assert(0,
+ "big read requested for %i bytes, but there are %i bytes "
+ "left in buffer. call mpack_read_native() instead",
+ (int)count, (int)left);
+ mpack_reader_flag_error(reader, mpack_error_bug);
+ mpack_memset(p, 0, count);
+ return;
+ }
+
+ // we'll need a fill function to get more data. if there's no
+ // fill function, the buffer should contain an entire MessagePack
+ // object, so we raise mpack_error_invalid instead of mpack_error_io
+ // on truncated data.
+ if (reader->fill == NULL) {
+ mpack_reader_flag_error(reader, mpack_error_invalid);
+ mpack_memset(p, 0, count);
+ return;
+ }
+
+ if (reader->size == 0) {
+ // somewhat debatable what error should be returned here. when
+ // initializing a reader with an in-memory buffer it's not
+ // necessarily a bug if the data is blank; it might just have
+ // been truncated to zero. for this reason we return the same
+ // error as if the data was truncated.
+ mpack_reader_flag_error(reader, mpack_error_io);
+ mpack_memset(p, 0, count);
+ return;
+ }
+
+ // flush what's left of the buffer
+ if (left > 0) {
+ mpack_log("flushing %i bytes remaining in buffer\n", (int)left);
+ mpack_memcpy(p, reader->data, left);
+ count -= left;
+ p += left;
+ reader->data += left;
+ }
+
+ // if the remaining data needed is some small fraction of the
+ // buffer size, we'll try to fill the buffer as much as possible
+ // and copy the needed data out.
+ if (count <= reader->size / MPACK_READER_SMALL_FRACTION_DENOMINATOR) {
+ size_t read = mpack_fill_range(reader, reader->buffer, count, reader->size);
+ if (mpack_reader_error(reader) != mpack_ok)
+ return;
+ mpack_memcpy(p, reader->buffer, count);
+ reader->data = reader->buffer + count;
+ reader->end = reader->buffer + read;
+
+ // otherwise we read the remaining data directly into the target.
+ } else {
+ mpack_log("reading %i additional bytes\n", (int)count);
+ mpack_fill_range(reader, p, count, count);
+ }
+}
+
+MPACK_NOINLINE static void mpack_skip_bytes_straddle(mpack_reader_t* reader, size_t count) {
+
+ // we'll need at least a fill function to skip more data. if there's
+ // no fill function, the buffer should contain an entire MessagePack
+ // object, so we raise mpack_error_invalid instead of mpack_error_io
+ // on truncated data. (see mpack_read_native_straddle())
+ if (reader->fill == NULL) {
+ mpack_log("reader has no fill function!\n");
+ mpack_reader_flag_error(reader, mpack_error_invalid);
+ return;
+ }
+
+ // discard whatever's left in the buffer
+ size_t left = (size_t)(reader->end - reader->data);
+ mpack_log("discarding %i bytes still in buffer\n", (int)left);
+ count -= left;
+ reader->data = reader->end;
+
+ // use the skip function if we've got one, and if we're trying
+ // to skip a lot of data. if we only need to skip some tiny
+ // fraction of the buffer size, it's probably better to just
+ // fill the buffer and skip from it instead of trying to seek.
+ if (reader->skip && count > reader->size / 16) {
+ mpack_log("calling skip function for %i bytes\n", (int)count);
+ reader->skip(reader, count);
+ return;
+ }
+
+ mpack_reader_skip_using_fill(reader, count);
+}
+
+void mpack_skip_bytes(mpack_reader_t* reader, size_t count) {
+ if (mpack_reader_error(reader) != mpack_ok)
+ return;
+ mpack_log("skip requested for %i bytes\n", (int)count);
+ mpack_reader_track_bytes(reader, count);
+
+ // check if we have enough in the buffer already
+ size_t left = (size_t)(reader->end - reader->data);
+ if (left >= count) {
+ mpack_log("skipping %i bytes still in buffer\n", (int)count);
+ reader->data += count;
+ return;
+ }
+
+ mpack_skip_bytes_straddle(reader, count);
+}
+
+MPACK_NOINLINE static void mpack_reader_skip_using_fill(mpack_reader_t* reader, size_t count) {
+ mpack_assert(reader->fill != NULL, "missing fill function!");
+ mpack_assert(reader->data == reader->end, "there are bytes left in the buffer!");
+ mpack_assert(reader->error == mpack_ok, "should not have called this in an error state (%i)", reader->error);
+ mpack_log("skip using fill for %i bytes\n", (int)count);
+
+ // fill and discard multiples of the buffer size
+ while (count > reader->size) {
+ mpack_log("filling and discarding buffer of %i bytes\n", (int)reader->size);
+ if (mpack_fill_range(reader, reader->buffer, reader->size, reader->size) < reader->size) {
+ mpack_reader_flag_error(reader, mpack_error_io);
+ return;
+ }
+ count -= reader->size;
+ }
+
+ // fill the buffer as much as possible
+ reader->data = reader->buffer;
+ size_t read = mpack_fill_range(reader, reader->buffer, count, reader->size);
+ if (read < count) {
+ mpack_reader_flag_error(reader, mpack_error_io);
+ return;
+ }
+ reader->end = reader->data + read;
+ mpack_log("filled %i bytes into buffer; discarding %i bytes\n", (int)read, (int)count);
+ reader->data += count;
+}
+
+void mpack_read_bytes(mpack_reader_t* reader, char* p, size_t count) {
+ mpack_assert(p != NULL, "destination for read of %i bytes is NULL", (int)count);
+ mpack_reader_track_bytes(reader, count);
+ mpack_read_native(reader, p, count);
+}
+
+void mpack_read_utf8(mpack_reader_t* reader, char* p, size_t byte_count) {
+ mpack_assert(p != NULL, "destination for read of %i bytes is NULL", (int)byte_count);
+ mpack_reader_track_str_bytes_all(reader, byte_count);
+ mpack_read_native(reader, p, byte_count);
+
+ if (mpack_reader_error(reader) == mpack_ok && !mpack_utf8_check(p, byte_count))
+ mpack_reader_flag_error(reader, mpack_error_type);
+}
+
+static void mpack_read_cstr_unchecked(mpack_reader_t* reader, char* buf, size_t buffer_size, size_t byte_count) {
+ mpack_assert(buf != NULL, "destination for read of %i bytes is NULL", (int)byte_count);
+ mpack_assert(buffer_size >= 1, "buffer size is zero; you must have room for at least a null-terminator");
+
+ if (mpack_reader_error(reader)) {
+ buf[0] = 0;
+ return;
+ }
+
+ if (byte_count > buffer_size - 1) {
+ mpack_reader_flag_error(reader, mpack_error_too_big);
+ buf[0] = 0;
+ return;
+ }
+
+ mpack_reader_track_str_bytes_all(reader, byte_count);
+ mpack_read_native(reader, buf, byte_count);
+ buf[byte_count] = 0;
+}
+
+void mpack_read_cstr(mpack_reader_t* reader, char* buf, size_t buffer_size, size_t byte_count) {
+ mpack_read_cstr_unchecked(reader, buf, buffer_size, byte_count);
+
+ // check for null bytes
+ if (mpack_reader_error(reader) == mpack_ok && !mpack_str_check_no_null(buf, byte_count)) {
+ buf[0] = 0;
+ mpack_reader_flag_error(reader, mpack_error_type);
+ }
+}
+
+void mpack_read_utf8_cstr(mpack_reader_t* reader, char* buf, size_t buffer_size, size_t byte_count) {
+ mpack_read_cstr_unchecked(reader, buf, buffer_size, byte_count);
+
+ // check encoding
+ if (mpack_reader_error(reader) == mpack_ok && !mpack_utf8_check_no_null(buf, byte_count)) {
+ buf[0] = 0;
+ mpack_reader_flag_error(reader, mpack_error_type);
+ }
+}
+
+#ifdef MPACK_MALLOC
+// Reads native bytes with error callback disabled. This allows MPack reader functions
+// to hold an allocated buffer and read native data into it without leaking it in
+// case of a non-local jump (longjmp, throw) out of an error handler.
+static void mpack_read_native_noerrorfn(mpack_reader_t* reader, char* p, size_t count) {
+ mpack_assert(reader->error == mpack_ok, "cannot call if an error is already flagged!");
+ mpack_reader_error_t error_fn = reader->error_fn;
+ reader->error_fn = NULL;
+ mpack_read_native(reader, p, count);
+ reader->error_fn = error_fn;
+}
+
+char* mpack_read_bytes_alloc_impl(mpack_reader_t* reader, size_t count, bool null_terminated) {
+
+ // track the bytes first in case it jumps
+ mpack_reader_track_bytes(reader, count);
+ if (mpack_reader_error(reader) != mpack_ok)
+ return NULL;
+
+ // cannot allocate zero bytes. this is not an error.
+ if (count == 0 && null_terminated == false)
+ return NULL;
+
+ // allocate data
+ char* data = (char*)MPACK_MALLOC(count + (null_terminated ? 1 : 0)); // TODO: can this overflow?
+ if (data == NULL) {
+ mpack_reader_flag_error(reader, mpack_error_memory);
+ return NULL;
+ }
+
+ // read with error callback disabled so we don't leak our buffer
+ mpack_read_native_noerrorfn(reader, data, count);
+
+ // report flagged errors
+ if (mpack_reader_error(reader) != mpack_ok) {
+ MPACK_FREE(data);
+ if (reader->error_fn)
+ reader->error_fn(reader, mpack_reader_error(reader));
+ return NULL;
+ }
+
+ if (null_terminated)
+ data[count] = '\0';
+ return data;
+}
+#endif
+
+// read inplace without tracking (since there are different
+// tracking modes for different inplace readers)
+static const char* mpack_read_bytes_inplace_notrack(mpack_reader_t* reader, size_t count) {
+ if (mpack_reader_error(reader) != mpack_ok)
+ return NULL;
+
+ // if we have enough bytes already in the buffer, we can return it directly.
+ if ((size_t)(reader->end - reader->data) >= count) {
+ const char* bytes = reader->data;
+ reader->data += count;
+ return bytes;
+ }
+
+ if (!mpack_reader_ensure(reader, count))
+ return NULL;
+
+ const char* bytes = reader->data;
+ reader->data += count;
+ return bytes;
+}
+
+const char* mpack_read_bytes_inplace(mpack_reader_t* reader, size_t count) {
+ mpack_reader_track_bytes(reader, count);
+ return mpack_read_bytes_inplace_notrack(reader, count);
+}
+
+const char* mpack_read_utf8_inplace(mpack_reader_t* reader, size_t count) {
+ mpack_reader_track_str_bytes_all(reader, count);
+ const char* str = mpack_read_bytes_inplace_notrack(reader, count);
+
+ if (mpack_reader_error(reader) == mpack_ok && !mpack_utf8_check(str, count)) {
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return NULL;
+ }
+
+ return str;
+}
+
+static size_t mpack_parse_tag(mpack_reader_t* reader, mpack_tag_t* tag) {
+ mpack_assert(reader->error == mpack_ok, "reader cannot be in an error state!");
+
+ if (!mpack_reader_ensure(reader, 1))
+ return 0;
+ uint8_t type = mpack_load_u8(reader->data);
+
+ // unfortunately, by far the fastest way to parse a tag is to switch
+ // on the first byte, and to explicitly list every possible byte. so for
+ // infix types, the list of cases is quite large.
+ //
+ // in size-optimized builds, we switch on the top four bits first to
+ // handle most infix types with a smaller jump table to save space.
+
+ #if MPACK_OPTIMIZE_FOR_SIZE
+ switch (type >> 4) {
+
+ // positive fixnum
+ case 0x0: case 0x1: case 0x2: case 0x3:
+ case 0x4: case 0x5: case 0x6: case 0x7:
+ *tag = mpack_tag_make_uint(type);
+ return 1;
+
+ // negative fixnum
+ case 0xe: case 0xf:
+ *tag = mpack_tag_make_int((int8_t)type);
+ return 1;
+
+ // fixmap
+ case 0x8:
+ *tag = mpack_tag_make_map(type & ~0xf0u);
+ return 1;
+
+ // fixarray
+ case 0x9:
+ *tag = mpack_tag_make_array(type & ~0xf0u);
+ return 1;
+
+ // fixstr
+ case 0xa: case 0xb:
+ *tag = mpack_tag_make_str(type & ~0xe0u);
+ return 1;
+
+ // not one of the common infix types
+ default:
+ break;
+
+ }
+ #endif
+
+ // handle individual type tags
+ switch (type) {
+
+ #if !MPACK_OPTIMIZE_FOR_SIZE
+ // positive fixnum
+ case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07:
+ case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x0c: case 0x0d: case 0x0e: case 0x0f:
+ case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17:
+ case 0x18: case 0x19: case 0x1a: case 0x1b: case 0x1c: case 0x1d: case 0x1e: case 0x1f:
+ case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x26: case 0x27:
+ case 0x28: case 0x29: case 0x2a: case 0x2b: case 0x2c: case 0x2d: case 0x2e: case 0x2f:
+ case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37:
+ case 0x38: case 0x39: case 0x3a: case 0x3b: case 0x3c: case 0x3d: case 0x3e: case 0x3f:
+ case 0x40: case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: case 0x46: case 0x47:
+ case 0x48: case 0x49: case 0x4a: case 0x4b: case 0x4c: case 0x4d: case 0x4e: case 0x4f:
+ case 0x50: case 0x51: case 0x52: case 0x53: case 0x54: case 0x55: case 0x56: case 0x57:
+ case 0x58: case 0x59: case 0x5a: case 0x5b: case 0x5c: case 0x5d: case 0x5e: case 0x5f:
+ case 0x60: case 0x61: case 0x62: case 0x63: case 0x64: case 0x65: case 0x66: case 0x67:
+ case 0x68: case 0x69: case 0x6a: case 0x6b: case 0x6c: case 0x6d: case 0x6e: case 0x6f:
+ case 0x70: case 0x71: case 0x72: case 0x73: case 0x74: case 0x75: case 0x76: case 0x77:
+ case 0x78: case 0x79: case 0x7a: case 0x7b: case 0x7c: case 0x7d: case 0x7e: case 0x7f:
+ *tag = mpack_tag_make_uint(type);
+ return 1;
+
+ // negative fixnum
+ case 0xe0: case 0xe1: case 0xe2: case 0xe3: case 0xe4: case 0xe5: case 0xe6: case 0xe7:
+ case 0xe8: case 0xe9: case 0xea: case 0xeb: case 0xec: case 0xed: case 0xee: case 0xef:
+ case 0xf0: case 0xf1: case 0xf2: case 0xf3: case 0xf4: case 0xf5: case 0xf6: case 0xf7:
+ case 0xf8: case 0xf9: case 0xfa: case 0xfb: case 0xfc: case 0xfd: case 0xfe: case 0xff:
+ *tag = mpack_tag_make_int((int8_t)type);
+ return 1;
+
+ // fixmap
+ case 0x80: case 0x81: case 0x82: case 0x83: case 0x84: case 0x85: case 0x86: case 0x87:
+ case 0x88: case 0x89: case 0x8a: case 0x8b: case 0x8c: case 0x8d: case 0x8e: case 0x8f:
+ *tag = mpack_tag_make_map(type & ~0xf0u);
+ return 1;
+
+ // fixarray
+ case 0x90: case 0x91: case 0x92: case 0x93: case 0x94: case 0x95: case 0x96: case 0x97:
+ case 0x98: case 0x99: case 0x9a: case 0x9b: case 0x9c: case 0x9d: case 0x9e: case 0x9f:
+ *tag = mpack_tag_make_array(type & ~0xf0u);
+ return 1;
+
+ // fixstr
+ case 0xa0: case 0xa1: case 0xa2: case 0xa3: case 0xa4: case 0xa5: case 0xa6: case 0xa7:
+ case 0xa8: case 0xa9: case 0xaa: case 0xab: case 0xac: case 0xad: case 0xae: case 0xaf:
+ case 0xb0: case 0xb1: case 0xb2: case 0xb3: case 0xb4: case 0xb5: case 0xb6: case 0xb7:
+ case 0xb8: case 0xb9: case 0xba: case 0xbb: case 0xbc: case 0xbd: case 0xbe: case 0xbf:
+ *tag = mpack_tag_make_str(type & ~0xe0u);
+ return 1;
+ #endif
+
+ // nil
+ case 0xc0:
+ *tag = mpack_tag_make_nil();
+ return 1;
+
+ // bool
+ case 0xc2: case 0xc3:
+ *tag = mpack_tag_make_bool((bool)(type & 1));
+ return 1;
+
+ // bin8
+ case 0xc4:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_BIN8))
+ return 0;
+ *tag = mpack_tag_make_bin(mpack_load_u8(reader->data + 1));
+ return MPACK_TAG_SIZE_BIN8;
+
+ // bin16
+ case 0xc5:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_BIN16))
+ return 0;
+ *tag = mpack_tag_make_bin(mpack_load_u16(reader->data + 1));
+ return MPACK_TAG_SIZE_BIN16;
+
+ // bin32
+ case 0xc6:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_BIN32))
+ return 0;
+ *tag = mpack_tag_make_bin(mpack_load_u32(reader->data + 1));
+ return MPACK_TAG_SIZE_BIN32;
+
+ #if MPACK_EXTENSIONS
+ // ext8
+ case 0xc7:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_EXT8))
+ return 0;
+ *tag = mpack_tag_make_ext(mpack_load_i8(reader->data + 2), mpack_load_u8(reader->data + 1));
+ return MPACK_TAG_SIZE_EXT8;
+
+ // ext16
+ case 0xc8:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_EXT16))
+ return 0;
+ *tag = mpack_tag_make_ext(mpack_load_i8(reader->data + 3), mpack_load_u16(reader->data + 1));
+ return MPACK_TAG_SIZE_EXT16;
+
+ // ext32
+ case 0xc9:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_EXT32))
+ return 0;
+ *tag = mpack_tag_make_ext(mpack_load_i8(reader->data + 5), mpack_load_u32(reader->data + 1));
+ return MPACK_TAG_SIZE_EXT32;
+ #endif
+
+ // float
+ case 0xca:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_FLOAT))
+ return 0;
+ *tag = mpack_tag_make_float(mpack_load_float(reader->data + 1));
+ return MPACK_TAG_SIZE_FLOAT;
+
+ // double
+ case 0xcb:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_DOUBLE))
+ return 0;
+ *tag = mpack_tag_make_double(mpack_load_double(reader->data + 1));
+ return MPACK_TAG_SIZE_DOUBLE;
+
+ // uint8
+ case 0xcc:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_U8))
+ return 0;
+ *tag = mpack_tag_make_uint(mpack_load_u8(reader->data + 1));
+ return MPACK_TAG_SIZE_U8;
+
+ // uint16
+ case 0xcd:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_U16))
+ return 0;
+ *tag = mpack_tag_make_uint(mpack_load_u16(reader->data + 1));
+ return MPACK_TAG_SIZE_U16;
+
+ // uint32
+ case 0xce:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_U32))
+ return 0;
+ *tag = mpack_tag_make_uint(mpack_load_u32(reader->data + 1));
+ return MPACK_TAG_SIZE_U32;
+
+ // uint64
+ case 0xcf:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_U64))
+ return 0;
+ *tag = mpack_tag_make_uint(mpack_load_u64(reader->data + 1));
+ return MPACK_TAG_SIZE_U64;
+
+ // int8
+ case 0xd0:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_I8))
+ return 0;
+ *tag = mpack_tag_make_int(mpack_load_i8(reader->data + 1));
+ return MPACK_TAG_SIZE_I8;
+
+ // int16
+ case 0xd1:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_I16))
+ return 0;
+ *tag = mpack_tag_make_int(mpack_load_i16(reader->data + 1));
+ return MPACK_TAG_SIZE_I16;
+
+ // int32
+ case 0xd2:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_I32))
+ return 0;
+ *tag = mpack_tag_make_int(mpack_load_i32(reader->data + 1));
+ return MPACK_TAG_SIZE_I32;
+
+ // int64
+ case 0xd3:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_I64))
+ return 0;
+ *tag = mpack_tag_make_int(mpack_load_i64(reader->data + 1));
+ return MPACK_TAG_SIZE_I64;
+
+ #if MPACK_EXTENSIONS
+ // fixext1
+ case 0xd4:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_FIXEXT1))
+ return 0;
+ *tag = mpack_tag_make_ext(mpack_load_i8(reader->data + 1), 1);
+ return MPACK_TAG_SIZE_FIXEXT1;
+
+ // fixext2
+ case 0xd5:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_FIXEXT2))
+ return 0;
+ *tag = mpack_tag_make_ext(mpack_load_i8(reader->data + 1), 2);
+ return MPACK_TAG_SIZE_FIXEXT2;
+
+ // fixext4
+ case 0xd6:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_FIXEXT4))
+ return 0;
+ *tag = mpack_tag_make_ext(mpack_load_i8(reader->data + 1), 4);
+ return 2;
+
+ // fixext8
+ case 0xd7:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_FIXEXT8))
+ return 0;
+ *tag = mpack_tag_make_ext(mpack_load_i8(reader->data + 1), 8);
+ return MPACK_TAG_SIZE_FIXEXT8;
+
+ // fixext16
+ case 0xd8:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_FIXEXT16))
+ return 0;
+ *tag = mpack_tag_make_ext(mpack_load_i8(reader->data + 1), 16);
+ return MPACK_TAG_SIZE_FIXEXT16;
+ #endif
+
+ // str8
+ case 0xd9:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_STR8))
+ return 0;
+ *tag = mpack_tag_make_str(mpack_load_u8(reader->data + 1));
+ return MPACK_TAG_SIZE_STR8;
+
+ // str16
+ case 0xda:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_STR16))
+ return 0;
+ *tag = mpack_tag_make_str(mpack_load_u16(reader->data + 1));
+ return MPACK_TAG_SIZE_STR16;
+
+ // str32
+ case 0xdb:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_STR32))
+ return 0;
+ *tag = mpack_tag_make_str(mpack_load_u32(reader->data + 1));
+ return MPACK_TAG_SIZE_STR32;
+
+ // array16
+ case 0xdc:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_ARRAY16))
+ return 0;
+ *tag = mpack_tag_make_array(mpack_load_u16(reader->data + 1));
+ return MPACK_TAG_SIZE_ARRAY16;
+
+ // array32
+ case 0xdd:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_ARRAY32))
+ return 0;
+ *tag = mpack_tag_make_array(mpack_load_u32(reader->data + 1));
+ return MPACK_TAG_SIZE_ARRAY32;
+
+ // map16
+ case 0xde:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_MAP16))
+ return 0;
+ *tag = mpack_tag_make_map(mpack_load_u16(reader->data + 1));
+ return MPACK_TAG_SIZE_MAP16;
+
+ // map32
+ case 0xdf:
+ if (!mpack_reader_ensure(reader, MPACK_TAG_SIZE_MAP32))
+ return 0;
+ *tag = mpack_tag_make_map(mpack_load_u32(reader->data + 1));
+ return MPACK_TAG_SIZE_MAP32;
+
+ // reserved
+ case 0xc1:
+ mpack_reader_flag_error(reader, mpack_error_invalid);
+ return 0;
+
+ #if !MPACK_EXTENSIONS
+ // ext
+ case 0xc7: // fallthrough
+ case 0xc8: // fallthrough
+ case 0xc9: // fallthrough
+ // fixext
+ case 0xd4: // fallthrough
+ case 0xd5: // fallthrough
+ case 0xd6: // fallthrough
+ case 0xd7: // fallthrough
+ case 0xd8:
+ mpack_reader_flag_error(reader, mpack_error_unsupported);
+ return 0;
+ #endif
+
+ #if MPACK_OPTIMIZE_FOR_SIZE
+ // any other bytes should have been handled by the infix switch
+ default:
+ break;
+ #endif
+ }
+
+ mpack_assert(0, "unreachable");
+ return 0;
+}
+
+mpack_tag_t mpack_read_tag(mpack_reader_t* reader) {
+ mpack_log("reading tag\n");
+
+ // make sure we can read a tag
+ if (mpack_reader_error(reader) != mpack_ok)
+ return mpack_tag_nil();
+ if (mpack_reader_track_element(reader) != mpack_ok)
+ return mpack_tag_nil();
+
+ mpack_tag_t tag = MPACK_TAG_ZERO;
+ size_t count = mpack_parse_tag(reader, &tag);
+ if (count == 0)
+ return mpack_tag_nil();
+
+ #if MPACK_READ_TRACKING
+ mpack_error_t track_error = mpack_ok;
+
+ switch (tag.type) {
+ case mpack_type_map:
+ case mpack_type_array:
+ track_error = mpack_track_push(&reader->track, tag.type, tag.v.n);
+ break;
+ #if MPACK_EXTENSIONS
+ case mpack_type_ext:
+ #endif
+ case mpack_type_str:
+ case mpack_type_bin:
+ track_error = mpack_track_push(&reader->track, tag.type, tag.v.l);
+ break;
+ default:
+ break;
+ }
+
+ if (track_error != mpack_ok) {
+ mpack_reader_flag_error(reader, track_error);
+ return mpack_tag_nil();
+ }
+ #endif
+
+ reader->data += count;
+ return tag;
+}
+
+mpack_tag_t mpack_peek_tag(mpack_reader_t* reader) {
+ mpack_log("peeking tag\n");
+
+ // make sure we can peek a tag
+ if (mpack_reader_error(reader) != mpack_ok)
+ return mpack_tag_nil();
+ if (mpack_reader_track_peek_element(reader) != mpack_ok)
+ return mpack_tag_nil();
+
+ mpack_tag_t tag = MPACK_TAG_ZERO;
+ if (mpack_parse_tag(reader, &tag) == 0)
+ return mpack_tag_nil();
+ return tag;
+}
+
+void mpack_discard(mpack_reader_t* reader) {
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (mpack_reader_error(reader))
+ return;
+ switch (var.type) {
+ case mpack_type_str:
+ mpack_skip_bytes(reader, var.v.l);
+ mpack_done_str(reader);
+ break;
+ case mpack_type_bin:
+ mpack_skip_bytes(reader, var.v.l);
+ mpack_done_bin(reader);
+ break;
+ #if MPACK_EXTENSIONS
+ case mpack_type_ext:
+ mpack_skip_bytes(reader, var.v.l);
+ mpack_done_ext(reader);
+ break;
+ #endif
+ case mpack_type_array: {
+ for (; var.v.n > 0; --var.v.n) {
+ mpack_discard(reader);
+ if (mpack_reader_error(reader))
+ break;
+ }
+ mpack_done_array(reader);
+ break;
+ }
+ case mpack_type_map: {
+ for (; var.v.n > 0; --var.v.n) {
+ mpack_discard(reader);
+ mpack_discard(reader);
+ if (mpack_reader_error(reader))
+ break;
+ }
+ mpack_done_map(reader);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+#if MPACK_EXTENSIONS
+mpack_timestamp_t mpack_read_timestamp(mpack_reader_t* reader, size_t size) {
+ mpack_timestamp_t timestamp = {0, 0};
+
+ if (size != 4 && size != 8 && size != 12) {
+ mpack_reader_flag_error(reader, mpack_error_invalid);
+ return timestamp;
+ }
+
+ char buf[12];
+ mpack_read_bytes(reader, buf, size);
+ mpack_done_ext(reader);
+ if (mpack_reader_error(reader) != mpack_ok)
+ return timestamp;
+
+ switch (size) {
+ case 4:
+ timestamp.seconds = (int64_t)(uint64_t)mpack_load_u32(buf);
+ break;
+
+ case 8: {
+ uint64_t packed = mpack_load_u64(buf);
+ timestamp.seconds = (int64_t)(packed & ((UINT64_C(1) << 34) - 1));
+ timestamp.nanoseconds = (uint32_t)(packed >> 34);
+ break;
+ }
+
+ case 12:
+ timestamp.nanoseconds = mpack_load_u32(buf);
+ timestamp.seconds = mpack_load_i64(buf + 4);
+ break;
+
+ default:
+ mpack_assert(false, "unreachable");
+ break;
+ }
+
+ if (timestamp.nanoseconds > MPACK_TIMESTAMP_NANOSECONDS_MAX) {
+ mpack_reader_flag_error(reader, mpack_error_invalid);
+ mpack_timestamp_t zero = {0, 0};
+ return zero;
+ }
+
+ return timestamp;
+}
+#endif
+
+#if MPACK_READ_TRACKING
+void mpack_done_type(mpack_reader_t* reader, mpack_type_t type) {
+ if (mpack_reader_error(reader) == mpack_ok)
+ mpack_reader_flag_if_error(reader, mpack_track_pop(&reader->track, type));
+}
+#endif
+
+#if MPACK_DEBUG && MPACK_STDIO
+static size_t mpack_print_read_prefix(mpack_reader_t* reader, size_t length, char* buffer, size_t buffer_size) {
+ if (length == 0)
+ return 0;
+
+ size_t read = (length < buffer_size) ? length : buffer_size;
+ mpack_read_bytes(reader, buffer, read);
+ if (mpack_reader_error(reader) != mpack_ok)
+ return 0;
+
+ mpack_skip_bytes(reader, length - read);
+ return read;
+}
+
+static void mpack_print_element(mpack_reader_t* reader, mpack_print_t* print, size_t depth) {
+ mpack_tag_t val = mpack_read_tag(reader);
+ if (mpack_reader_error(reader) != mpack_ok)
+ return;
+
+ // We read some bytes from bin and ext so we can print its prefix in hex.
+ char buffer[MPACK_PRINT_BYTE_COUNT];
+ size_t count = 0;
+
+ switch (val.type) {
+ case mpack_type_str:
+ mpack_print_append_cstr(print, "\"");
+ for (size_t i = 0; i < val.v.l; ++i) {
+ char c;
+ mpack_read_bytes(reader, &c, 1);
+ if (mpack_reader_error(reader) != mpack_ok)
+ return;
+ switch (c) {
+ case '\n': mpack_print_append_cstr(print, "\\n"); break;
+ case '\\': mpack_print_append_cstr(print, "\\\\"); break;
+ case '"': mpack_print_append_cstr(print, "\\\""); break;
+ default: mpack_print_append(print, &c, 1); break;
+ }
+ }
+ mpack_print_append_cstr(print, "\"");
+ mpack_done_str(reader);
+ return;
+
+ case mpack_type_array:
+ mpack_print_append_cstr(print, "[\n");
+ for (size_t i = 0; i < val.v.n; ++i) {
+ for (size_t j = 0; j < depth + 1; ++j)
+ mpack_print_append_cstr(print, " ");
+ mpack_print_element(reader, print, depth + 1);
+ if (mpack_reader_error(reader) != mpack_ok)
+ return;
+ if (i != val.v.n - 1)
+ mpack_print_append_cstr(print, ",");
+ mpack_print_append_cstr(print, "\n");
+ }
+ for (size_t i = 0; i < depth; ++i)
+ mpack_print_append_cstr(print, " ");
+ mpack_print_append_cstr(print, "]");
+ mpack_done_array(reader);
+ return;
+
+ case mpack_type_map:
+ mpack_print_append_cstr(print, "{\n");
+ for (size_t i = 0; i < val.v.n; ++i) {
+ for (size_t j = 0; j < depth + 1; ++j)
+ mpack_print_append_cstr(print, " ");
+ mpack_print_element(reader, print, depth + 1);
+ if (mpack_reader_error(reader) != mpack_ok)
+ return;
+ mpack_print_append_cstr(print, ": ");
+ mpack_print_element(reader, print, depth + 1);
+ if (mpack_reader_error(reader) != mpack_ok)
+ return;
+ if (i != val.v.n - 1)
+ mpack_print_append_cstr(print, ",");
+ mpack_print_append_cstr(print, "\n");
+ }
+ for (size_t i = 0; i < depth; ++i)
+ mpack_print_append_cstr(print, " ");
+ mpack_print_append_cstr(print, "}");
+ mpack_done_map(reader);
+ return;
+
+ // The above cases return so as not to print a pseudo-json value. The
+ // below cases break and print pseudo-json.
+
+ case mpack_type_bin:
+ count = mpack_print_read_prefix(reader, mpack_tag_bin_length(&val), buffer, sizeof(buffer));
+ mpack_done_bin(reader);
+ break;
+
+ #if MPACK_EXTENSIONS
+ case mpack_type_ext:
+ count = mpack_print_read_prefix(reader, mpack_tag_ext_length(&val), buffer, sizeof(buffer));
+ mpack_done_ext(reader);
+ break;
+ #endif
+
+ default:
+ break;
+ }
+
+ char buf[256];
+ mpack_tag_debug_pseudo_json(val, buf, sizeof(buf), buffer, count);
+ mpack_print_append_cstr(print, buf);
+}
+
+static void mpack_print_and_destroy(mpack_reader_t* reader, mpack_print_t* print, size_t depth) {
+ for (size_t i = 0; i < depth; ++i)
+ mpack_print_append_cstr(print, " ");
+ mpack_print_element(reader, print, depth);
+
+ size_t remaining = mpack_reader_remaining(reader, NULL);
+
+ char buf[256];
+ if (mpack_reader_destroy(reader) != mpack_ok) {
+ mpack_snprintf(buf, sizeof(buf), "\n<mpack parsing error %s>", mpack_error_to_string(mpack_reader_error(reader)));
+ buf[sizeof(buf) - 1] = '\0';
+ mpack_print_append_cstr(print, buf);
+ } else if (remaining > 0) {
+ mpack_snprintf(buf, sizeof(buf), "\n<%i extra bytes at end of message>", (int)remaining);
+ buf[sizeof(buf) - 1] = '\0';
+ mpack_print_append_cstr(print, buf);
+ }
+}
+
+static void mpack_print_data(const char* data, size_t len, mpack_print_t* print, size_t depth) {
+ mpack_reader_t reader;
+ mpack_reader_init_data(&reader, data, len);
+ mpack_print_and_destroy(&reader, print, depth);
+}
+
+void mpack_print_data_to_buffer(const char* data, size_t data_size, char* buffer, size_t buffer_size) {
+ if (buffer_size == 0) {
+ mpack_assert(false, "buffer size is zero!");
+ return;
+ }
+
+ mpack_print_t print;
+ mpack_memset(&print, 0, sizeof(print));
+ print.buffer = buffer;
+ print.size = buffer_size;
+ mpack_print_data(data, data_size, &print, 0);
+ mpack_print_append(&print, "", 1); // null-terminator
+ mpack_print_flush(&print);
+
+ // we always make sure there's a null-terminator at the end of the buffer
+ // in case we ran out of space.
+ print.buffer[print.size - 1] = '\0';
+}
+
+void mpack_print_data_to_callback(const char* data, size_t size, mpack_print_callback_t callback, void* context) {
+ char buffer[1024];
+ mpack_print_t print;
+ mpack_memset(&print, 0, sizeof(print));
+ print.buffer = buffer;
+ print.size = sizeof(buffer);
+ print.callback = callback;
+ print.context = context;
+ mpack_print_data(data, size, &print, 0);
+ mpack_print_flush(&print);
+}
+
+void mpack_print_data_to_file(const char* data, size_t len, FILE* file) {
+ mpack_assert(data != NULL, "data is NULL");
+ mpack_assert(file != NULL, "file is NULL");
+
+ char buffer[1024];
+ mpack_print_t print;
+ mpack_memset(&print, 0, sizeof(print));
+ print.buffer = buffer;
+ print.size = sizeof(buffer);
+ print.callback = &mpack_print_file_callback;
+ print.context = file;
+
+ mpack_print_data(data, len, &print, 2);
+ mpack_print_append_cstr(&print, "\n");
+ mpack_print_flush(&print);
+}
+
+void mpack_print_stdfile_to_callback(FILE* file, mpack_print_callback_t callback, void* context) {
+ char buffer[1024];
+ mpack_print_t print;
+ mpack_memset(&print, 0, sizeof(print));
+ print.buffer = buffer;
+ print.size = sizeof(buffer);
+ print.callback = callback;
+ print.context = context;
+
+ mpack_reader_t reader;
+ mpack_reader_init_stdfile(&reader, file, false);
+ mpack_print_and_destroy(&reader, &print, 0);
+ mpack_print_flush(&print);
+}
+#endif
+
+#endif
+
+/* mpack/mpack-expect.c.c */
+
+#define MPACK_INTERNAL 1
+
+/* #include "mpack-expect.h" */
+
+#if MPACK_EXPECT
+
+
+// Helpers
+
+MPACK_STATIC_INLINE uint8_t mpack_expect_native_u8(mpack_reader_t* reader) {
+ if (mpack_reader_error(reader) != mpack_ok)
+ return 0;
+ uint8_t type;
+ if (!mpack_reader_ensure(reader, sizeof(type)))
+ return 0;
+ type = mpack_load_u8(reader->data);
+ reader->data += sizeof(type);
+ return type;
+}
+
+#if !MPACK_OPTIMIZE_FOR_SIZE
+MPACK_STATIC_INLINE uint16_t mpack_expect_native_u16(mpack_reader_t* reader) {
+ if (mpack_reader_error(reader) != mpack_ok)
+ return 0;
+ uint16_t type;
+ if (!mpack_reader_ensure(reader, sizeof(type)))
+ return 0;
+ type = mpack_load_u16(reader->data);
+ reader->data += sizeof(type);
+ return type;
+}
+
+MPACK_STATIC_INLINE uint32_t mpack_expect_native_u32(mpack_reader_t* reader) {
+ if (mpack_reader_error(reader) != mpack_ok)
+ return 0;
+ uint32_t type;
+ if (!mpack_reader_ensure(reader, sizeof(type)))
+ return 0;
+ type = mpack_load_u32(reader->data);
+ reader->data += sizeof(type);
+ return type;
+}
+#endif
+
+MPACK_STATIC_INLINE uint8_t mpack_expect_type_byte(mpack_reader_t* reader) {
+ mpack_reader_track_element(reader);
+ return mpack_expect_native_u8(reader);
+}
+
+
+// Basic Number Functions
+
+uint8_t mpack_expect_u8(mpack_reader_t* reader) {
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_uint) {
+ if (var.v.u <= UINT8_MAX)
+ return (uint8_t)var.v.u;
+ } else if (var.type == mpack_type_int) {
+ if (var.v.i >= 0 && var.v.i <= UINT8_MAX)
+ return (uint8_t)var.v.i;
+ }
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0;
+}
+
+uint16_t mpack_expect_u16(mpack_reader_t* reader) {
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_uint) {
+ if (var.v.u <= UINT16_MAX)
+ return (uint16_t)var.v.u;
+ } else if (var.type == mpack_type_int) {
+ if (var.v.i >= 0 && var.v.i <= UINT16_MAX)
+ return (uint16_t)var.v.i;
+ }
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0;
+}
+
+uint32_t mpack_expect_u32(mpack_reader_t* reader) {
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_uint) {
+ if (var.v.u <= UINT32_MAX)
+ return (uint32_t)var.v.u;
+ } else if (var.type == mpack_type_int) {
+ if (var.v.i >= 0 && var.v.i <= UINT32_MAX)
+ return (uint32_t)var.v.i;
+ }
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0;
+}
+
+uint64_t mpack_expect_u64(mpack_reader_t* reader) {
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_uint) {
+ return var.v.u;
+ } else if (var.type == mpack_type_int) {
+ if (var.v.i >= 0)
+ return (uint64_t)var.v.i;
+ }
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0;
+}
+
+int8_t mpack_expect_i8(mpack_reader_t* reader) {
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_uint) {
+ if (var.v.u <= INT8_MAX)
+ return (int8_t)var.v.u;
+ } else if (var.type == mpack_type_int) {
+ if (var.v.i >= INT8_MIN && var.v.i <= INT8_MAX)
+ return (int8_t)var.v.i;
+ }
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0;
+}
+
+int16_t mpack_expect_i16(mpack_reader_t* reader) {
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_uint) {
+ if (var.v.u <= INT16_MAX)
+ return (int16_t)var.v.u;
+ } else if (var.type == mpack_type_int) {
+ if (var.v.i >= INT16_MIN && var.v.i <= INT16_MAX)
+ return (int16_t)var.v.i;
+ }
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0;
+}
+
+int32_t mpack_expect_i32(mpack_reader_t* reader) {
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_uint) {
+ if (var.v.u <= INT32_MAX)
+ return (int32_t)var.v.u;
+ } else if (var.type == mpack_type_int) {
+ if (var.v.i >= INT32_MIN && var.v.i <= INT32_MAX)
+ return (int32_t)var.v.i;
+ }
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0;
+}
+
+int64_t mpack_expect_i64(mpack_reader_t* reader) {
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_uint) {
+ if (var.v.u <= INT64_MAX)
+ return (int64_t)var.v.u;
+ } else if (var.type == mpack_type_int) {
+ return var.v.i;
+ }
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0;
+}
+
+float mpack_expect_float(mpack_reader_t* reader) {
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_uint)
+ return (float)var.v.u;
+ else if (var.type == mpack_type_int)
+ return (float)var.v.i;
+ else if (var.type == mpack_type_float)
+ return var.v.f;
+ else if (var.type == mpack_type_double)
+ return (float)var.v.d;
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0.0f;
+}
+
+double mpack_expect_double(mpack_reader_t* reader) {
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_uint)
+ return (double)var.v.u;
+ else if (var.type == mpack_type_int)
+ return (double)var.v.i;
+ else if (var.type == mpack_type_float)
+ return (double)var.v.f;
+ else if (var.type == mpack_type_double)
+ return var.v.d;
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0.0;
+}
+
+float mpack_expect_float_strict(mpack_reader_t* reader) {
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_float)
+ return var.v.f;
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0.0f;
+}
+
+double mpack_expect_double_strict(mpack_reader_t* reader) {
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_float)
+ return (double)var.v.f;
+ else if (var.type == mpack_type_double)
+ return var.v.d;
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0.0;
+}
+
+
+// Ranged Number Functions
+//
+// All ranged functions are identical other than the type, so we
+// define their content with a macro. The prototypes are still written
+// out in full to support ctags/IDE tools.
+
+#define MPACK_EXPECT_RANGE_IMPL(name, type_t) \
+ \
+ /* make sure the range is sensible */ \
+ mpack_assert(min_value <= max_value, \
+ "min_value %i must be less than or equal to max_value %i", \
+ min_value, max_value); \
+ \
+ /* read the value */ \
+ type_t val = mpack_expect_##name(reader); \
+ if (mpack_reader_error(reader) != mpack_ok) \
+ return min_value; \
+ \
+ /* make sure it fits */ \
+ if (val < min_value || val > max_value) { \
+ mpack_reader_flag_error(reader, mpack_error_type); \
+ return min_value; \
+ } \
+ \
+ return val;
+
+uint8_t mpack_expect_u8_range(mpack_reader_t* reader, uint8_t min_value, uint8_t max_value) {MPACK_EXPECT_RANGE_IMPL(u8, uint8_t)}
+uint16_t mpack_expect_u16_range(mpack_reader_t* reader, uint16_t min_value, uint16_t max_value) {MPACK_EXPECT_RANGE_IMPL(u16, uint16_t)}
+uint32_t mpack_expect_u32_range(mpack_reader_t* reader, uint32_t min_value, uint32_t max_value) {MPACK_EXPECT_RANGE_IMPL(u32, uint32_t)}
+uint64_t mpack_expect_u64_range(mpack_reader_t* reader, uint64_t min_value, uint64_t max_value) {MPACK_EXPECT_RANGE_IMPL(u64, uint64_t)}
+
+int8_t mpack_expect_i8_range(mpack_reader_t* reader, int8_t min_value, int8_t max_value) {MPACK_EXPECT_RANGE_IMPL(i8, int8_t)}
+int16_t mpack_expect_i16_range(mpack_reader_t* reader, int16_t min_value, int16_t max_value) {MPACK_EXPECT_RANGE_IMPL(i16, int16_t)}
+int32_t mpack_expect_i32_range(mpack_reader_t* reader, int32_t min_value, int32_t max_value) {MPACK_EXPECT_RANGE_IMPL(i32, int32_t)}
+int64_t mpack_expect_i64_range(mpack_reader_t* reader, int64_t min_value, int64_t max_value) {MPACK_EXPECT_RANGE_IMPL(i64, int64_t)}
+
+float mpack_expect_float_range(mpack_reader_t* reader, float min_value, float max_value) {MPACK_EXPECT_RANGE_IMPL(float, float)}
+double mpack_expect_double_range(mpack_reader_t* reader, double min_value, double max_value) {MPACK_EXPECT_RANGE_IMPL(double, double)}
+
+uint32_t mpack_expect_map_range(mpack_reader_t* reader, uint32_t min_value, uint32_t max_value) {MPACK_EXPECT_RANGE_IMPL(map, uint32_t)}
+uint32_t mpack_expect_array_range(mpack_reader_t* reader, uint32_t min_value, uint32_t max_value) {MPACK_EXPECT_RANGE_IMPL(array, uint32_t)}
+
+
+// Matching Number Functions
+
+void mpack_expect_uint_match(mpack_reader_t* reader, uint64_t value) {
+ if (mpack_expect_u64(reader) != value)
+ mpack_reader_flag_error(reader, mpack_error_type);
+}
+
+void mpack_expect_int_match(mpack_reader_t* reader, int64_t value) {
+ if (mpack_expect_i64(reader) != value)
+ mpack_reader_flag_error(reader, mpack_error_type);
+}
+
+
+// Other Basic Types
+
+void mpack_expect_nil(mpack_reader_t* reader) {
+ if (mpack_expect_type_byte(reader) != 0xc0)
+ mpack_reader_flag_error(reader, mpack_error_type);
+}
+
+bool mpack_expect_bool(mpack_reader_t* reader) {
+ uint8_t type = mpack_expect_type_byte(reader);
+ if ((type & ~1) != 0xc2)
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return (bool)(type & 1);
+}
+
+void mpack_expect_true(mpack_reader_t* reader) {
+ if (mpack_expect_bool(reader) != true)
+ mpack_reader_flag_error(reader, mpack_error_type);
+}
+
+void mpack_expect_false(mpack_reader_t* reader) {
+ if (mpack_expect_bool(reader) != false)
+ mpack_reader_flag_error(reader, mpack_error_type);
+}
+
+#if MPACK_EXTENSIONS
+mpack_timestamp_t mpack_expect_timestamp(mpack_reader_t* reader) {
+ mpack_timestamp_t zero = {0, 0};
+
+ mpack_tag_t tag = mpack_read_tag(reader);
+ if (tag.type != mpack_type_ext) {
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return zero;
+ }
+ if (mpack_tag_ext_exttype(&tag) != MPACK_EXTTYPE_TIMESTAMP) {
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return zero;
+ }
+
+ return mpack_read_timestamp(reader, mpack_tag_ext_length(&tag));
+}
+
+int64_t mpack_expect_timestamp_truncate(mpack_reader_t* reader) {
+ return mpack_expect_timestamp(reader).seconds;
+}
+#endif
+
+
+// Compound Types
+
+uint32_t mpack_expect_map(mpack_reader_t* reader) {
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_map)
+ return var.v.n;
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0;
+}
+
+void mpack_expect_map_match(mpack_reader_t* reader, uint32_t count) {
+ if (mpack_expect_map(reader) != count)
+ mpack_reader_flag_error(reader, mpack_error_type);
+}
+
+bool mpack_expect_map_or_nil(mpack_reader_t* reader, uint32_t* count) {
+ mpack_assert(count != NULL, "count cannot be NULL");
+
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_nil) {
+ *count = 0;
+ return false;
+ }
+ if (var.type == mpack_type_map) {
+ *count = var.v.n;
+ return true;
+ }
+ mpack_reader_flag_error(reader, mpack_error_type);
+ *count = 0;
+ return false;
+}
+
+bool mpack_expect_map_max_or_nil(mpack_reader_t* reader, uint32_t max_count, uint32_t* count) {
+ mpack_assert(count != NULL, "count cannot be NULL");
+
+ bool has_map = mpack_expect_map_or_nil(reader, count);
+ if (has_map && *count > max_count) {
+ *count = 0;
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return false;
+ }
+ return has_map;
+}
+
+uint32_t mpack_expect_array(mpack_reader_t* reader) {
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_array)
+ return var.v.n;
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0;
+}
+
+void mpack_expect_array_match(mpack_reader_t* reader, uint32_t count) {
+ if (mpack_expect_array(reader) != count)
+ mpack_reader_flag_error(reader, mpack_error_type);
+}
+
+bool mpack_expect_array_or_nil(mpack_reader_t* reader, uint32_t* count) {
+ mpack_assert(count != NULL, "count cannot be NULL");
+
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_nil) {
+ *count = 0;
+ return false;
+ }
+ if (var.type == mpack_type_array) {
+ *count = var.v.n;
+ return true;
+ }
+ mpack_reader_flag_error(reader, mpack_error_type);
+ *count = 0;
+ return false;
+}
+
+bool mpack_expect_array_max_or_nil(mpack_reader_t* reader, uint32_t max_count, uint32_t* count) {
+ mpack_assert(count != NULL, "count cannot be NULL");
+
+ bool has_array = mpack_expect_array_or_nil(reader, count);
+ if (has_array && *count > max_count) {
+ *count = 0;
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return false;
+ }
+ return has_array;
+}
+
+#ifdef MPACK_MALLOC
+void* mpack_expect_array_alloc_impl(mpack_reader_t* reader, size_t element_size, uint32_t max_count, uint32_t* out_count, bool allow_nil) {
+ mpack_assert(out_count != NULL, "out_count cannot be NULL");
+ *out_count = 0;
+
+ uint32_t count;
+ bool has_array = true;
+ if (allow_nil)
+ has_array = mpack_expect_array_max_or_nil(reader, max_count, &count);
+ else
+ count = mpack_expect_array_max(reader, max_count);
+ if (mpack_reader_error(reader))
+ return NULL;
+
+ // size 0 is not an error; we return NULL for no elements.
+ if (count == 0) {
+ // we call mpack_done_array() automatically ONLY if we are using
+ // the _or_nil variant. this is the only way to allow nil and empty
+ // to work the same way.
+ if (allow_nil && has_array)
+ mpack_done_array(reader);
+ return NULL;
+ }
+
+ void* p = MPACK_MALLOC(element_size * count);
+ if (p == NULL) {
+ mpack_reader_flag_error(reader, mpack_error_memory);
+ return NULL;
+ }
+
+ *out_count = count;
+ return p;
+}
+#endif
+
+
+// Str, Bin and Ext Functions
+
+uint32_t mpack_expect_str(mpack_reader_t* reader) {
+ #if MPACK_OPTIMIZE_FOR_SIZE
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_str)
+ return var.v.l;
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0;
+ #else
+ uint8_t type = mpack_expect_type_byte(reader);
+ uint32_t count;
+
+ if ((type >> 5) == 5) {
+ count = type & (uint8_t)~0xe0;
+ } else if (type == 0xd9) {
+ count = mpack_expect_native_u8(reader);
+ } else if (type == 0xda) {
+ count = mpack_expect_native_u16(reader);
+ } else if (type == 0xdb) {
+ count = mpack_expect_native_u32(reader);
+ } else {
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0;
+ }
+
+ #if MPACK_READ_TRACKING
+ mpack_reader_flag_if_error(reader, mpack_track_push(&reader->track, mpack_type_str, count));
+ #endif
+ return count;
+ #endif
+}
+
+size_t mpack_expect_str_buf(mpack_reader_t* reader, char* buf, size_t bufsize) {
+ mpack_assert(buf != NULL, "buf cannot be NULL");
+
+ size_t length = mpack_expect_str(reader);
+ if (mpack_reader_error(reader))
+ return 0;
+
+ if (length > bufsize) {
+ mpack_reader_flag_error(reader, mpack_error_too_big);
+ return 0;
+ }
+
+ mpack_read_bytes(reader, buf, length);
+ if (mpack_reader_error(reader))
+ return 0;
+
+ mpack_done_str(reader);
+ return length;
+}
+
+size_t mpack_expect_utf8(mpack_reader_t* reader, char* buf, size_t size) {
+ mpack_assert(buf != NULL, "buf cannot be NULL");
+
+ size_t length = mpack_expect_str_buf(reader, buf, size);
+
+ if (!mpack_utf8_check(buf, length)) {
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0;
+ }
+
+ return length;
+}
+
+uint32_t mpack_expect_bin(mpack_reader_t* reader) {
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_bin)
+ return var.v.l;
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0;
+}
+
+size_t mpack_expect_bin_buf(mpack_reader_t* reader, char* buf, size_t bufsize) {
+ mpack_assert(buf != NULL, "buf cannot be NULL");
+
+ size_t binsize = mpack_expect_bin(reader);
+ if (mpack_reader_error(reader))
+ return 0;
+ if (binsize > bufsize) {
+ mpack_reader_flag_error(reader, mpack_error_too_big);
+ return 0;
+ }
+ mpack_read_bytes(reader, buf, binsize);
+ if (mpack_reader_error(reader))
+ return 0;
+ mpack_done_bin(reader);
+ return binsize;
+}
+
+#if MPACK_EXTENSIONS
+uint32_t mpack_expect_ext(mpack_reader_t* reader, int8_t* type) {
+ mpack_tag_t var = mpack_read_tag(reader);
+ if (var.type == mpack_type_ext) {
+ *type = mpack_tag_ext_exttype(&var);
+ return mpack_tag_ext_length(&var);
+ }
+ *type = 0;
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return 0;
+}
+
+size_t mpack_expect_ext_buf(mpack_reader_t* reader, int8_t* type, char* buf, size_t bufsize) {
+ mpack_assert(buf != NULL, "buf cannot be NULL");
+
+ size_t extsize = mpack_expect_ext(reader, type);
+ if (mpack_reader_error(reader))
+ return 0;
+ if (extsize > bufsize) {
+ *type = 0;
+ mpack_reader_flag_error(reader, mpack_error_too_big);
+ return 0;
+ }
+ mpack_read_bytes(reader, buf, extsize);
+ if (mpack_reader_error(reader)) {
+ *type = 0;
+ return 0;
+ }
+ mpack_done_ext(reader);
+ return extsize;
+}
+#endif
+
+void mpack_expect_cstr(mpack_reader_t* reader, char* buf, size_t bufsize) {
+ uint32_t length = mpack_expect_str(reader);
+ mpack_read_cstr(reader, buf, bufsize, length);
+ mpack_done_str(reader);
+}
+
+void mpack_expect_utf8_cstr(mpack_reader_t* reader, char* buf, size_t bufsize) {
+ uint32_t length = mpack_expect_str(reader);
+ mpack_read_utf8_cstr(reader, buf, bufsize, length);
+ mpack_done_str(reader);
+}
+
+#ifdef MPACK_MALLOC
+static char* mpack_expect_cstr_alloc_unchecked(mpack_reader_t* reader, size_t maxsize, size_t* out_length) {
+ mpack_assert(out_length != NULL, "out_length cannot be NULL");
+ *out_length = 0;
+
+ // make sure argument makes sense
+ if (maxsize < 1) {
+ mpack_break("maxsize is zero; you must have room for at least a null-terminator");
+ mpack_reader_flag_error(reader, mpack_error_bug);
+ return NULL;
+ }
+
+ if (maxsize > UINT32_MAX)
+ maxsize = UINT32_MAX;
+
+ size_t length = mpack_expect_str_max(reader, (uint32_t)maxsize - 1);
+ char* str = mpack_read_bytes_alloc_impl(reader, length, true);
+ mpack_done_str(reader);
+
+ if (str)
+ *out_length = length;
+ return str;
+}
+
+char* mpack_expect_cstr_alloc(mpack_reader_t* reader, size_t maxsize) {
+ size_t length;
+ char* str = mpack_expect_cstr_alloc_unchecked(reader, maxsize, &length);
+
+ if (str && !mpack_str_check_no_null(str, length)) {
+ MPACK_FREE(str);
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return NULL;
+ }
+
+ return str;
+}
+
+char* mpack_expect_utf8_cstr_alloc(mpack_reader_t* reader, size_t maxsize) {
+ size_t length;
+ char* str = mpack_expect_cstr_alloc_unchecked(reader, maxsize, &length);
+
+ if (str && !mpack_utf8_check_no_null(str, length)) {
+ MPACK_FREE(str);
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return NULL;
+ }
+
+ return str;
+}
+#endif
+
+void mpack_expect_str_match(mpack_reader_t* reader, const char* str, size_t len) {
+ mpack_assert(str != NULL, "str cannot be NULL");
+
+ // expect a str the correct length
+ if (len > UINT32_MAX)
+ mpack_reader_flag_error(reader, mpack_error_type);
+ mpack_expect_str_length(reader, (uint32_t)len);
+ if (mpack_reader_error(reader))
+ return;
+ mpack_reader_track_bytes(reader, len);
+
+ // check each byte one by one (matched strings are likely to be very small)
+ for (; len > 0; --len) {
+ if (mpack_expect_native_u8(reader) != *str++) {
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return;
+ }
+ }
+
+ mpack_done_str(reader);
+}
+
+void mpack_expect_tag(mpack_reader_t* reader, mpack_tag_t expected) {
+ mpack_tag_t actual = mpack_read_tag(reader);
+ if (!mpack_tag_equal(actual, expected))
+ mpack_reader_flag_error(reader, mpack_error_type);
+}
+
+#ifdef MPACK_MALLOC
+char* mpack_expect_bin_alloc(mpack_reader_t* reader, size_t maxsize, size_t* size) {
+ mpack_assert(size != NULL, "size cannot be NULL");
+ *size = 0;
+
+ if (maxsize > UINT32_MAX)
+ maxsize = UINT32_MAX;
+
+ size_t length = mpack_expect_bin_max(reader, (uint32_t)maxsize);
+ if (mpack_reader_error(reader))
+ return NULL;
+
+ char* data = mpack_read_bytes_alloc(reader, length);
+ mpack_done_bin(reader);
+
+ if (data)
+ *size = length;
+ return data;
+}
+#endif
+
+#if MPACK_EXTENSIONS && defined(MPACK_MALLOC)
+char* mpack_expect_ext_alloc(mpack_reader_t* reader, int8_t* type, size_t maxsize, size_t* size) {
+ mpack_assert(size != NULL, "size cannot be NULL");
+ *size = 0;
+
+ if (maxsize > UINT32_MAX)
+ maxsize = UINT32_MAX;
+
+ size_t length = mpack_expect_ext_max(reader, type, (uint32_t)maxsize);
+ if (mpack_reader_error(reader))
+ return NULL;
+
+ char* data = mpack_read_bytes_alloc(reader, length);
+ mpack_done_ext(reader);
+
+ if (data) {
+ *size = length;
+ } else {
+ *type = 0;
+ }
+ return data;
+}
+#endif
+
+size_t mpack_expect_enum(mpack_reader_t* reader, const char* strings[], size_t count) {
+
+ // read the string in-place
+ size_t keylen = mpack_expect_str(reader);
+ const char* key = mpack_read_bytes_inplace(reader, keylen);
+ mpack_done_str(reader);
+ if (mpack_reader_error(reader) != mpack_ok)
+ return count;
+
+ // find what key it matches
+ for (size_t i = 0; i < count; ++i) {
+ const char* other = strings[i];
+ size_t otherlen = mpack_strlen(other);
+ if (keylen == otherlen && mpack_memcmp(key, other, keylen) == 0)
+ return i;
+ }
+
+ // no matches
+ mpack_reader_flag_error(reader, mpack_error_type);
+ return count;
+}
+
+size_t mpack_expect_enum_optional(mpack_reader_t* reader, const char* strings[], size_t count) {
+ if (mpack_reader_error(reader) != mpack_ok)
+ return count;
+
+ mpack_assert(count != 0, "count cannot be zero; no strings are valid!");
+ mpack_assert(strings != NULL, "strings cannot be NULL");
+
+ // the key is only recognized if it is a string
+ if (mpack_peek_tag(reader).type != mpack_type_str) {
+ mpack_discard(reader);
+ return count;
+ }
+
+ // read the string in-place
+ size_t keylen = mpack_expect_str(reader);
+ const char* key = mpack_read_bytes_inplace(reader, keylen);
+ mpack_done_str(reader);
+ if (mpack_reader_error(reader) != mpack_ok)
+ return count;
+
+ // find what key it matches
+ for (size_t i = 0; i < count; ++i) {
+ const char* other = strings[i];
+ size_t otherlen = mpack_strlen(other);
+ if (keylen == otherlen && mpack_memcmp(key, other, keylen) == 0)
+ return i;
+ }
+
+ // no matches
+ return count;
+}
+
+size_t mpack_expect_key_uint(mpack_reader_t* reader, bool found[], size_t count) {
+ if (mpack_reader_error(reader) != mpack_ok)
+ return count;
+
+ if (count == 0) {
+ mpack_break("count cannot be zero; no keys are valid!");
+ mpack_reader_flag_error(reader, mpack_error_bug);
+ return count;
+ }
+ mpack_assert(found != NULL, "found cannot be NULL");
+
+ // the key is only recognized if it is an unsigned int
+ if (mpack_peek_tag(reader).type != mpack_type_uint) {
+ mpack_discard(reader);
+ return count;
+ }
+
+ // read the key
+ uint64_t value = mpack_expect_u64(reader);
+ if (mpack_reader_error(reader) != mpack_ok)
+ return count;
+
+ // unrecognized keys are fine, we just return count
+ if (value >= count)
+ return count;
+
+ // check if this key is a duplicate
+ if (found[value]) {
+ mpack_reader_flag_error(reader, mpack_error_invalid);
+ return count;
+ }
+
+ found[value] = true;
+ return (size_t)value;
+}
+
+size_t mpack_expect_key_cstr(mpack_reader_t* reader, const char* keys[], bool found[], size_t count) {
+ size_t i = mpack_expect_enum_optional(reader, keys, count);
+
+ // unrecognized keys are fine, we just return count
+ if (i == count)
+ return count;
+
+ // check if this key is a duplicate
+ mpack_assert(found != NULL, "found cannot be NULL");
+ if (found[i]) {
+ mpack_reader_flag_error(reader, mpack_error_invalid);
+ return count;
+ }
+
+ found[i] = true;
+ return i;
+}
+
+#endif
+
+
+/* mpack/mpack-node.c.c */
+
+#define MPACK_INTERNAL 1
+
+/* #include "mpack-node.h" */
+
+#if MPACK_NODE
+
+MPACK_STATIC_INLINE const char* mpack_node_data_unchecked(mpack_node_t node) {
+ mpack_assert(mpack_node_error(node) == mpack_ok, "tree is in an error state!");
+
+ mpack_type_t type = node.data->type;
+ MPACK_UNUSED(type);
+ #if MPACK_EXTENSIONS
+ mpack_assert(type == mpack_type_str || type == mpack_type_bin || type == mpack_type_ext,
+ "node of type %i (%s) is not a data type!", type, mpack_type_to_string(type));
+ #else
+ mpack_assert(type == mpack_type_str || type == mpack_type_bin,
+ "node of type %i (%s) is not a data type!", type, mpack_type_to_string(type));
+ #endif
+
+ return node.tree->data + node.data->value.offset;
+}
+
+#if MPACK_EXTENSIONS
+MPACK_STATIC_INLINE int8_t mpack_node_exttype_unchecked(mpack_node_t node) {
+ mpack_assert(mpack_node_error(node) == mpack_ok, "tree is in an error state!");
+
+ mpack_type_t type = node.data->type;
+ MPACK_UNUSED(type);
+ mpack_assert(type == mpack_type_ext, "node of type %i (%s) is not an ext type!",
+ type, mpack_type_to_string(type));
+
+ // the exttype of an ext node is stored in the byte preceding the data
+ return mpack_load_i8(mpack_node_data_unchecked(node) - 1);
+}
+#endif
+
+
+
+/*
+ * Tree Parsing
+ */
+
+#ifdef MPACK_MALLOC
+
+// fix up the alloc size to make sure it exactly fits the
+// maximum number of nodes it can contain (the allocator will
+// waste it back anyway, but we round it down just in case)
+
+#define MPACK_NODES_PER_PAGE \
+ ((MPACK_NODE_PAGE_SIZE - sizeof(mpack_tree_page_t)) / sizeof(mpack_node_data_t) + 1)
+
+#define MPACK_PAGE_ALLOC_SIZE \
+ (sizeof(mpack_tree_page_t) + sizeof(mpack_node_data_t) * (MPACK_NODES_PER_PAGE - 1))
+
+#endif
+
+#ifdef MPACK_MALLOC
+/*
+ * Fills the tree until we have at least enough bytes for the current node.
+ */
+static bool mpack_tree_reserve_fill(mpack_tree_t* tree) {
+ mpack_assert(tree->parser.state == mpack_tree_parse_state_in_progress);
+
+ size_t bytes = tree->parser.current_node_reserved;
+ mpack_assert(bytes > tree->parser.possible_nodes_left,
+ "there are already enough bytes! call mpack_tree_ensure() instead.");
+ mpack_log("filling to reserve %i bytes\n", (int)bytes);
+
+ // if the necessary bytes would put us over the maximum tree
+ // size, fail right away.
+ // TODO: check for overflow?
+ if (tree->data_length + bytes > tree->max_size) {
+ mpack_tree_flag_error(tree, mpack_error_too_big);
+ return false;
+ }
+
+ // we'll need a read function to fetch more data. if there's
+ // no read function, the data should contain an entire message
+ // (or messages), so we flag it as invalid.
+ if (tree->read_fn == NULL) {
+ mpack_log("tree has no read function!\n");
+ mpack_tree_flag_error(tree, mpack_error_invalid);
+ return false;
+ }
+
+ // expand the buffer if needed
+ if (tree->data_length + bytes > tree->buffer_capacity) {
+
+ // TODO: check for overflow?
+ size_t new_capacity = (tree->buffer_capacity == 0) ? MPACK_BUFFER_SIZE : tree->buffer_capacity;
+ while (new_capacity < tree->data_length + bytes)
+ new_capacity *= 2;
+ if (new_capacity > tree->max_size)
+ new_capacity = tree->max_size;
+
+ mpack_log("expanding buffer from %i to %i\n", (int)tree->buffer_capacity, (int)new_capacity);
+
+ char* new_buffer;
+ if (tree->buffer == NULL)
+ new_buffer = (char*)MPACK_MALLOC(new_capacity);
+ else
+ new_buffer = (char*)mpack_realloc(tree->buffer, tree->data_length, new_capacity);
+
+ if (new_buffer == NULL) {
+ mpack_tree_flag_error(tree, mpack_error_memory);
+ return false;
+ }
+
+ tree->data = new_buffer;
+ tree->buffer = new_buffer;
+ tree->buffer_capacity = new_capacity;
+ }
+
+ // request as much data as possible, looping until we have
+ // all the data we need
+ do {
+ size_t read = tree->read_fn(tree, tree->buffer + tree->data_length, tree->buffer_capacity - tree->data_length);
+
+ // If the fill function encounters an error, it should flag an error on
+ // the tree.
+ if (mpack_tree_error(tree) != mpack_ok)
+ return false;
+
+ // We guard against fill functions that return -1 just in case.
+ if (read == (size_t)(-1)) {
+ mpack_tree_flag_error(tree, mpack_error_io);
+ return false;
+ }
+
+ // If the fill function returns 0, the data is not available yet. We
+ // return false to stop parsing the current node.
+ if (read == 0) {
+ mpack_log("not enough data.\n");
+ return false;
+ }
+
+ mpack_log("read %u more bytes\n", (uint32_t)read);
+ tree->data_length += read;
+ tree->parser.possible_nodes_left += read;
+ } while (tree->parser.possible_nodes_left < bytes);
+
+ return true;
+}
+#endif
+
+/*
+ * Ensures there are enough additional bytes in the tree for the current node
+ * (including reserved bytes for the children of this node, and in addition to
+ * the reserved bytes for children of previous compound nodes), reading more
+ * data if needed.
+ *
+ * extra_bytes is the number of additional bytes to reserve for the current
+ * node beyond the type byte (since one byte is already reserved for each node
+ * by its parent array or map.)
+ *
+ * This may reallocate the tree, which means the tree->data pointer may change!
+ *
+ * Returns false if not enough bytes could be read.
+ */
+MPACK_STATIC_INLINE bool mpack_tree_reserve_bytes(mpack_tree_t* tree, size_t extra_bytes) {
+ mpack_assert(tree->parser.state == mpack_tree_parse_state_in_progress);
+
+ // We guard against overflow here. A compound type could declare more than
+ // UINT32_MAX contents which overflows SIZE_MAX on 32-bit platforms. We
+ // flag mpack_error_invalid instead of mpack_error_too_big since it's far
+ // more likely that the message is corrupt than that the data is valid but
+ // not parseable on this architecture (see test_read_node_possible() in
+ // test-node.c .)
+ if ((uint64_t)tree->parser.current_node_reserved + (uint64_t)extra_bytes > SIZE_MAX) {
+ mpack_tree_flag_error(tree, mpack_error_invalid);
+ return false;
+ }
+
+ tree->parser.current_node_reserved += extra_bytes;
+
+ // Note that possible_nodes_left already accounts for reserved bytes for
+ // children of previous compound nodes. So even if there are hundreds of
+ // bytes left in the buffer, we might need to read anyway.
+ if (tree->parser.current_node_reserved <= tree->parser.possible_nodes_left)
+ return true;
+
+ #ifdef MPACK_MALLOC
+ return mpack_tree_reserve_fill(tree);
+ #else
+ return false;
+ #endif
+}
+
+MPACK_STATIC_INLINE size_t mpack_tree_parser_stack_capacity(mpack_tree_t* tree) {
+ #ifdef MPACK_MALLOC
+ return tree->parser.stack_capacity;
+ #else
+ return sizeof(tree->parser.stack) / sizeof(tree->parser.stack[0]);
+ #endif
+}
+
+static bool mpack_tree_push_stack(mpack_tree_t* tree, mpack_node_data_t* first_child, size_t total) {
+ mpack_tree_parser_t* parser = &tree->parser;
+ mpack_assert(parser->state == mpack_tree_parse_state_in_progress);
+
+ // No need to push empty containers
+ if (total == 0)
+ return true;
+
+ // Make sure we have enough room in the stack
+ if (parser->level + 1 == mpack_tree_parser_stack_capacity(tree)) {
+ #ifdef MPACK_MALLOC
+ size_t new_capacity = parser->stack_capacity * 2;
+ mpack_log("growing parse stack to capacity %i\n", (int)new_capacity);
+
+ // Replace the stack-allocated parsing stack
+ if (!parser->stack_owned) {
+ mpack_level_t* new_stack = (mpack_level_t*)MPACK_MALLOC(sizeof(mpack_level_t) * new_capacity);
+ if (!new_stack) {
+ mpack_tree_flag_error(tree, mpack_error_memory);
+ return false;
+ }
+ mpack_memcpy(new_stack, parser->stack, sizeof(mpack_level_t) * parser->stack_capacity);
+ parser->stack = new_stack;
+ parser->stack_owned = true;
+
+ // Realloc the allocated parsing stack
+ } else {
+ mpack_level_t* new_stack = (mpack_level_t*)mpack_realloc(parser->stack,
+ sizeof(mpack_level_t) * parser->stack_capacity, sizeof(mpack_level_t) * new_capacity);
+ if (!new_stack) {
+ mpack_tree_flag_error(tree, mpack_error_memory);
+ return false;
+ }
+ parser->stack = new_stack;
+ }
+ parser->stack_capacity = new_capacity;
+ #else
+ mpack_tree_flag_error(tree, mpack_error_too_big);
+ return false;
+ #endif
+ }
+
+ // Push the contents of this node onto the parsing stack
+ ++parser->level;
+ parser->stack[parser->level].child = first_child;
+ parser->stack[parser->level].left = total;
+ return true;
+}
+
+static bool mpack_tree_parse_children(mpack_tree_t* tree, mpack_node_data_t* node) {
+ mpack_tree_parser_t* parser = &tree->parser;
+ mpack_assert(parser->state == mpack_tree_parse_state_in_progress);
+
+ mpack_type_t type = node->type;
+ size_t total = node->len;
+
+ // Calculate total elements to read
+ if (type == mpack_type_map) {
+ if ((uint64_t)total * 2 > SIZE_MAX) {
+ mpack_tree_flag_error(tree, mpack_error_too_big);
+ return false;
+ }
+ total *= 2;
+ }
+
+ // Make sure we are under our total node limit (TODO can this overflow?)
+ tree->node_count += total;
+ if (tree->node_count > tree->max_nodes) {
+ mpack_tree_flag_error(tree, mpack_error_too_big);
+ return false;
+ }
+
+ // Each node is at least one byte. Count these bytes now to make
+ // sure there is enough data left.
+ if (!mpack_tree_reserve_bytes(tree, total))
+ return false;
+
+ // If there are enough nodes left in the current page, no need to grow
+ if (total <= parser->nodes_left) {
+ node->value.children = parser->nodes;
+ parser->nodes += total;
+ parser->nodes_left -= total;
+
+ } else {
+
+ #ifdef MPACK_MALLOC
+
+ // We can't grow if we're using a fixed pool (i.e. we didn't start with a page)
+ if (!tree->next) {
+ mpack_tree_flag_error(tree, mpack_error_too_big);
+ return false;
+ }
+
+ // Otherwise we need to grow, and the node's children need to be contiguous.
+ // This is a heuristic to decide whether we should waste the remaining space
+ // in the current page and start a new one, or give the children their
+ // own page. With a fraction of 1/8, this causes at most 12% additional
+ // waste. Note that reducing this too much causes less cache coherence and
+ // more malloc() overhead due to smaller allocations, so there's a tradeoff
+ // here. This heuristic could use some improvement, especially with custom
+ // page sizes.
+
+ mpack_tree_page_t* page;
+
+ if (total > MPACK_NODES_PER_PAGE || parser->nodes_left > MPACK_NODES_PER_PAGE / 8) {
+ // TODO: this should check for overflow
+ page = (mpack_tree_page_t*)MPACK_MALLOC(
+ sizeof(mpack_tree_page_t) + sizeof(mpack_node_data_t) * (total - 1));
+ if (page == NULL) {
+ mpack_tree_flag_error(tree, mpack_error_memory);
+ return false;
+ }
+ mpack_log("allocated seperate page %p for %i children, %i left in page of %i total\n",
+ page, (int)total, (int)parser->nodes_left, (int)MPACK_NODES_PER_PAGE);
+
+ node->value.children = page->nodes;
+
+ } else {
+ page = (mpack_tree_page_t*)MPACK_MALLOC(MPACK_PAGE_ALLOC_SIZE);
+ if (page == NULL) {
+ mpack_tree_flag_error(tree, mpack_error_memory);
+ return false;
+ }
+ mpack_log("allocated new page %p for %i children, wasting %i in page of %i total\n",
+ page, (int)total, (int)parser->nodes_left, (int)MPACK_NODES_PER_PAGE);
+
+ node->value.children = page->nodes;
+ parser->nodes = page->nodes + total;
+ parser->nodes_left = MPACK_NODES_PER_PAGE - total;
+ }
+
+ page->next = tree->next;
+ tree->next = page;
+
+ #else
+ // We can't grow if we don't have an allocator
+ mpack_tree_flag_error(tree, mpack_error_too_big);
+ return false;
+ #endif
+ }
+
+ return mpack_tree_push_stack(tree, node->value.children, total);
+}
+
+static bool mpack_tree_parse_bytes(mpack_tree_t* tree, mpack_node_data_t* node) {
+ node->value.offset = tree->size + tree->parser.current_node_reserved + 1;
+ return mpack_tree_reserve_bytes(tree, node->len);
+}
+
+#if MPACK_EXTENSIONS
+static bool mpack_tree_parse_ext(mpack_tree_t* tree, mpack_node_data_t* node) {
+ // reserve space for exttype
+ tree->parser.current_node_reserved += sizeof(int8_t);
+ node->type = mpack_type_ext;
+ return mpack_tree_parse_bytes(tree, node);
+}
+#endif
+
+static bool mpack_tree_parse_node_contents(mpack_tree_t* tree, mpack_node_data_t* node) {
+ mpack_assert(tree->parser.state == mpack_tree_parse_state_in_progress);
+ mpack_assert(node != NULL, "null node?");
+
+ // read the type. we've already accounted for this byte in
+ // possible_nodes_left, so we already know it is in bounds, and we don't
+ // need to reserve it for this node.
+ mpack_assert(tree->data_length > tree->size);
+ uint8_t type = mpack_load_u8(tree->data + tree->size);
+ mpack_log("node type %x\n", type);
+ tree->parser.current_node_reserved = 0;
+
+ // as with mpack_read_tag(), the fastest way to parse a node is to switch
+ // on the first byte, and to explicitly list every possible byte. we switch
+ // on the first four bits in size-optimized builds.
+
+ #if MPACK_OPTIMIZE_FOR_SIZE
+ switch (type >> 4) {
+
+ // positive fixnum
+ case 0x0: case 0x1: case 0x2: case 0x3:
+ case 0x4: case 0x5: case 0x6: case 0x7:
+ node->type = mpack_type_uint;
+ node->value.u = type;
+ return true;
+
+ // negative fixnum
+ case 0xe: case 0xf:
+ node->type = mpack_type_int;
+ node->value.i = (int8_t)type;
+ return true;
+
+ // fixmap
+ case 0x8:
+ node->type = mpack_type_map;
+ node->len = (uint32_t)(type & ~0xf0);
+ return mpack_tree_parse_children(tree, node);
+
+ // fixarray
+ case 0x9:
+ node->type = mpack_type_array;
+ node->len = (uint32_t)(type & ~0xf0);
+ return mpack_tree_parse_children(tree, node);
+
+ // fixstr
+ case 0xa: case 0xb:
+ node->type = mpack_type_str;
+ node->len = (uint32_t)(type & ~0xe0);
+ return mpack_tree_parse_bytes(tree, node);
+
+ // not one of the common infix types
+ default:
+ break;
+ }
+ #endif
+
+ switch (type) {
+
+ #if !MPACK_OPTIMIZE_FOR_SIZE
+ // positive fixnum
+ case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07:
+ case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x0c: case 0x0d: case 0x0e: case 0x0f:
+ case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17:
+ case 0x18: case 0x19: case 0x1a: case 0x1b: case 0x1c: case 0x1d: case 0x1e: case 0x1f:
+ case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x26: case 0x27:
+ case 0x28: case 0x29: case 0x2a: case 0x2b: case 0x2c: case 0x2d: case 0x2e: case 0x2f:
+ case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37:
+ case 0x38: case 0x39: case 0x3a: case 0x3b: case 0x3c: case 0x3d: case 0x3e: case 0x3f:
+ case 0x40: case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: case 0x46: case 0x47:
+ case 0x48: case 0x49: case 0x4a: case 0x4b: case 0x4c: case 0x4d: case 0x4e: case 0x4f:
+ case 0x50: case 0x51: case 0x52: case 0x53: case 0x54: case 0x55: case 0x56: case 0x57:
+ case 0x58: case 0x59: case 0x5a: case 0x5b: case 0x5c: case 0x5d: case 0x5e: case 0x5f:
+ case 0x60: case 0x61: case 0x62: case 0x63: case 0x64: case 0x65: case 0x66: case 0x67:
+ case 0x68: case 0x69: case 0x6a: case 0x6b: case 0x6c: case 0x6d: case 0x6e: case 0x6f:
+ case 0x70: case 0x71: case 0x72: case 0x73: case 0x74: case 0x75: case 0x76: case 0x77:
+ case 0x78: case 0x79: case 0x7a: case 0x7b: case 0x7c: case 0x7d: case 0x7e: case 0x7f:
+ node->type = mpack_type_uint;
+ node->value.u = type;
+ return true;
+
+ // negative fixnum
+ case 0xe0: case 0xe1: case 0xe2: case 0xe3: case 0xe4: case 0xe5: case 0xe6: case 0xe7:
+ case 0xe8: case 0xe9: case 0xea: case 0xeb: case 0xec: case 0xed: case 0xee: case 0xef:
+ case 0xf0: case 0xf1: case 0xf2: case 0xf3: case 0xf4: case 0xf5: case 0xf6: case 0xf7:
+ case 0xf8: case 0xf9: case 0xfa: case 0xfb: case 0xfc: case 0xfd: case 0xfe: case 0xff:
+ node->type = mpack_type_int;
+ node->value.i = (int8_t)type;
+ return true;
+
+ // fixmap
+ case 0x80: case 0x81: case 0x82: case 0x83: case 0x84: case 0x85: case 0x86: case 0x87:
+ case 0x88: case 0x89: case 0x8a: case 0x8b: case 0x8c: case 0x8d: case 0x8e: case 0x8f:
+ node->type = mpack_type_map;
+ node->len = (uint32_t)(type & ~0xf0);
+ return mpack_tree_parse_children(tree, node);
+
+ // fixarray
+ case 0x90: case 0x91: case 0x92: case 0x93: case 0x94: case 0x95: case 0x96: case 0x97:
+ case 0x98: case 0x99: case 0x9a: case 0x9b: case 0x9c: case 0x9d: case 0x9e: case 0x9f:
+ node->type = mpack_type_array;
+ node->len = (uint32_t)(type & ~0xf0);
+ return mpack_tree_parse_children(tree, node);
+
+ // fixstr
+ case 0xa0: case 0xa1: case 0xa2: case 0xa3: case 0xa4: case 0xa5: case 0xa6: case 0xa7:
+ case 0xa8: case 0xa9: case 0xaa: case 0xab: case 0xac: case 0xad: case 0xae: case 0xaf:
+ case 0xb0: case 0xb1: case 0xb2: case 0xb3: case 0xb4: case 0xb5: case 0xb6: case 0xb7:
+ case 0xb8: case 0xb9: case 0xba: case 0xbb: case 0xbc: case 0xbd: case 0xbe: case 0xbf:
+ node->type = mpack_type_str;
+ node->len = (uint32_t)(type & ~0xe0);
+ return mpack_tree_parse_bytes(tree, node);
+ #endif
+
+ // nil
+ case 0xc0:
+ node->type = mpack_type_nil;
+ return true;
+
+ // bool
+ case 0xc2: case 0xc3:
+ node->type = mpack_type_bool;
+ node->value.b = type & 1;
+ return true;
+
+ // bin8
+ case 0xc4:
+ node->type = mpack_type_bin;
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint8_t)))
+ return false;
+ node->len = mpack_load_u8(tree->data + tree->size + 1);
+ return mpack_tree_parse_bytes(tree, node);
+
+ // bin16
+ case 0xc5:
+ node->type = mpack_type_bin;
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint16_t)))
+ return false;
+ node->len = mpack_load_u16(tree->data + tree->size + 1);
+ return mpack_tree_parse_bytes(tree, node);
+
+ // bin32
+ case 0xc6:
+ node->type = mpack_type_bin;
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint32_t)))
+ return false;
+ node->len = mpack_load_u32(tree->data + tree->size + 1);
+ return mpack_tree_parse_bytes(tree, node);
+
+ #if MPACK_EXTENSIONS
+ // ext8
+ case 0xc7:
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint8_t)))
+ return false;
+ node->len = mpack_load_u8(tree->data + tree->size + 1);
+ return mpack_tree_parse_ext(tree, node);
+
+ // ext16
+ case 0xc8:
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint16_t)))
+ return false;
+ node->len = mpack_load_u16(tree->data + tree->size + 1);
+ return mpack_tree_parse_ext(tree, node);
+
+ // ext32
+ case 0xc9:
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint32_t)))
+ return false;
+ node->len = mpack_load_u32(tree->data + tree->size + 1);
+ return mpack_tree_parse_ext(tree, node);
+ #endif
+
+ // float
+ case 0xca:
+ if (!mpack_tree_reserve_bytes(tree, sizeof(float)))
+ return false;
+ node->value.f = mpack_load_float(tree->data + tree->size + 1);
+ node->type = mpack_type_float;
+ return true;
+
+ // double
+ case 0xcb:
+ if (!mpack_tree_reserve_bytes(tree, sizeof(double)))
+ return false;
+ node->value.d = mpack_load_double(tree->data + tree->size + 1);
+ node->type = mpack_type_double;
+ return true;
+
+ // uint8
+ case 0xcc:
+ node->type = mpack_type_uint;
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint8_t)))
+ return false;
+ node->value.u = mpack_load_u8(tree->data + tree->size + 1);
+ return true;
+
+ // uint16
+ case 0xcd:
+ node->type = mpack_type_uint;
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint16_t)))
+ return false;
+ node->value.u = mpack_load_u16(tree->data + tree->size + 1);
+ return true;
+
+ // uint32
+ case 0xce:
+ node->type = mpack_type_uint;
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint32_t)))
+ return false;
+ node->value.u = mpack_load_u32(tree->data + tree->size + 1);
+ return true;
+
+ // uint64
+ case 0xcf:
+ node->type = mpack_type_uint;
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint64_t)))
+ return false;
+ node->value.u = mpack_load_u64(tree->data + tree->size + 1);
+ return true;
+
+ // int8
+ case 0xd0:
+ node->type = mpack_type_int;
+ if (!mpack_tree_reserve_bytes(tree, sizeof(int8_t)))
+ return false;
+ node->value.i = mpack_load_i8(tree->data + tree->size + 1);
+ return true;
+
+ // int16
+ case 0xd1:
+ node->type = mpack_type_int;
+ if (!mpack_tree_reserve_bytes(tree, sizeof(int16_t)))
+ return false;
+ node->value.i = mpack_load_i16(tree->data + tree->size + 1);
+ return true;
+
+ // int32
+ case 0xd2:
+ node->type = mpack_type_int;
+ if (!mpack_tree_reserve_bytes(tree, sizeof(int32_t)))
+ return false;
+ node->value.i = mpack_load_i32(tree->data + tree->size + 1);
+ return true;
+
+ // int64
+ case 0xd3:
+ node->type = mpack_type_int;
+ if (!mpack_tree_reserve_bytes(tree, sizeof(int64_t)))
+ return false;
+ node->value.i = mpack_load_i64(tree->data + tree->size + 1);
+ return true;
+
+ #if MPACK_EXTENSIONS
+ // fixext1
+ case 0xd4:
+ node->len = 1;
+ return mpack_tree_parse_ext(tree, node);
+
+ // fixext2
+ case 0xd5:
+ node->len = 2;
+ return mpack_tree_parse_ext(tree, node);
+
+ // fixext4
+ case 0xd6:
+ node->len = 4;
+ return mpack_tree_parse_ext(tree, node);
+
+ // fixext8
+ case 0xd7:
+ node->len = 8;
+ return mpack_tree_parse_ext(tree, node);
+
+ // fixext16
+ case 0xd8:
+ node->len = 16;
+ return mpack_tree_parse_ext(tree, node);
+ #endif
+
+ // str8
+ case 0xd9:
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint8_t)))
+ return false;
+ node->len = mpack_load_u8(tree->data + tree->size + 1);
+ node->type = mpack_type_str;
+ return mpack_tree_parse_bytes(tree, node);
+
+ // str16
+ case 0xda:
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint16_t)))
+ return false;
+ node->len = mpack_load_u16(tree->data + tree->size + 1);
+ node->type = mpack_type_str;
+ return mpack_tree_parse_bytes(tree, node);
+
+ // str32
+ case 0xdb:
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint32_t)))
+ return false;
+ node->len = mpack_load_u32(tree->data + tree->size + 1);
+ node->type = mpack_type_str;
+ return mpack_tree_parse_bytes(tree, node);
+
+ // array16
+ case 0xdc:
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint16_t)))
+ return false;
+ node->len = mpack_load_u16(tree->data + tree->size + 1);
+ node->type = mpack_type_array;
+ return mpack_tree_parse_children(tree, node);
+
+ // array32
+ case 0xdd:
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint32_t)))
+ return false;
+ node->len = mpack_load_u32(tree->data + tree->size + 1);
+ node->type = mpack_type_array;
+ return mpack_tree_parse_children(tree, node);
+
+ // map16
+ case 0xde:
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint16_t)))
+ return false;
+ node->len = mpack_load_u16(tree->data + tree->size + 1);
+ node->type = mpack_type_map;
+ return mpack_tree_parse_children(tree, node);
+
+ // map32
+ case 0xdf:
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint32_t)))
+ return false;
+ node->len = mpack_load_u32(tree->data + tree->size + 1);
+ node->type = mpack_type_map;
+ return mpack_tree_parse_children(tree, node);
+
+ // reserved
+ case 0xc1:
+ mpack_tree_flag_error(tree, mpack_error_invalid);
+ return false;
+
+ #if !MPACK_EXTENSIONS
+ // ext
+ case 0xc7: // fallthrough
+ case 0xc8: // fallthrough
+ case 0xc9: // fallthrough
+ // fixext
+ case 0xd4: // fallthrough
+ case 0xd5: // fallthrough
+ case 0xd6: // fallthrough
+ case 0xd7: // fallthrough
+ case 0xd8:
+ mpack_tree_flag_error(tree, mpack_error_unsupported);
+ return false;
+ #endif
+
+ #if MPACK_OPTIMIZE_FOR_SIZE
+ // any other bytes should have been handled by the infix switch
+ default:
+ break;
+ #endif
+ }
+
+ mpack_assert(0, "unreachable");
+ return false;
+}
+
+static bool mpack_tree_parse_node(mpack_tree_t* tree, mpack_node_data_t* node) {
+ mpack_log("parsing a node at position %i in level %i\n",
+ (int)tree->size, (int)tree->parser.level);
+
+ if (!mpack_tree_parse_node_contents(tree, node)) {
+ mpack_log("node parsing returned false\n");
+ return false;
+ }
+
+ tree->parser.possible_nodes_left -= tree->parser.current_node_reserved;
+
+ // The reserve for the current node does not include the initial byte
+ // previously reserved as part of its parent.
+ size_t node_size = tree->parser.current_node_reserved + 1;
+
+ // If the parsed type is a map or array, the reserve includes one byte for
+ // each child. We want to subtract these out of possible_nodes_left, but
+ // not out of the current size of the tree.
+ if (node->type == mpack_type_array)
+ node_size -= node->len;
+ else if (node->type == mpack_type_map)
+ node_size -= node->len * 2;
+ tree->size += node_size;
+
+ mpack_log("parsed a node of type %s of %i bytes and "
+ "%i additional bytes reserved for children.\n",
+ mpack_type_to_string(node->type), (int)node_size,
+ (int)tree->parser.current_node_reserved + 1 - (int)node_size);
+
+ return true;
+}
+
+/*
+ * We read nodes in a loop instead of recursively for maximum performance. The
+ * stack holds the amount of children left to read in each level of the tree.
+ * Parsing can pause and resume when more data becomes available.
+ */
+static bool mpack_tree_continue_parsing(mpack_tree_t* tree) {
+ if (mpack_tree_error(tree) != mpack_ok)
+ return false;
+
+ mpack_tree_parser_t* parser = &tree->parser;
+ mpack_assert(parser->state == mpack_tree_parse_state_in_progress);
+ mpack_log("parsing tree elements, %i bytes in buffer\n", (int)tree->data_length);
+
+ // we loop parsing nodes until the parse stack is empty. we break
+ // by returning out of the function.
+ while (true) {
+ mpack_node_data_t* node = parser->stack[parser->level].child;
+ size_t level = parser->level;
+ if (!mpack_tree_parse_node(tree, node))
+ return false;
+ --parser->stack[level].left;
+ ++parser->stack[level].child;
+
+ mpack_assert(mpack_tree_error(tree) == mpack_ok,
+ "mpack_tree_parse_node() should have returned false due to error!");
+
+ // pop empty stack levels, exiting the outer loop when the stack is empty.
+ // (we could tail-optimize containers by pre-emptively popping empty
+ // stack levels before reading the new element, this way we wouldn't
+ // have to loop. but we eventually want to use the parse stack to give
+ // better error messages that contain the location of the error, so
+ // it needs to be complete.)
+ while (parser->stack[parser->level].left == 0) {
+ if (parser->level == 0)
+ return true;
+ --parser->level;
+ }
+ }
+}
+
+static void mpack_tree_cleanup(mpack_tree_t* tree) {
+ MPACK_UNUSED(tree);
+
+ #ifdef MPACK_MALLOC
+ if (tree->parser.stack_owned) {
+ MPACK_FREE(tree->parser.stack);
+ tree->parser.stack = NULL;
+ tree->parser.stack_owned = false;
+ }
+
+ mpack_tree_page_t* page = tree->next;
+ while (page != NULL) {
+ mpack_tree_page_t* next = page->next;
+ mpack_log("freeing page %p\n", page);
+ MPACK_FREE(page);
+ page = next;
+ }
+ tree->next = NULL;
+ #endif
+}
+
+static bool mpack_tree_parse_start(mpack_tree_t* tree) {
+ if (mpack_tree_error(tree) != mpack_ok)
+ return false;
+
+ mpack_tree_parser_t* parser = &tree->parser;
+ mpack_assert(parser->state != mpack_tree_parse_state_in_progress,
+ "previous parsing was not finished!");
+
+ if (parser->state == mpack_tree_parse_state_parsed)
+ mpack_tree_cleanup(tree);
+
+ mpack_log("starting parse\n");
+ tree->parser.state = mpack_tree_parse_state_in_progress;
+ tree->parser.current_node_reserved = 0;
+
+ // check if we previously parsed a tree
+ if (tree->size > 0) {
+ #ifdef MPACK_MALLOC
+ // if we're buffered, move the remaining data back to the
+ // start of the buffer
+ // TODO: This is not ideal performance-wise. We should only move data
+ // when we need to call the fill function.
+ // TODO: We could consider shrinking the buffer here, especially if we
+ // determine that the fill function is providing less than a quarter of
+ // the buffer size or if messages take up less than a quarter of the
+ // buffer size. Maybe this should be configurable.
+ if (tree->buffer != NULL) {
+ mpack_memmove(tree->buffer, tree->buffer + tree->size, tree->data_length - tree->size);
+ }
+ else
+ #endif
+ // otherwise advance past the parsed data
+ {
+ tree->data += tree->size;
+ }
+ tree->data_length -= tree->size;
+ tree->size = 0;
+ tree->node_count = 0;
+ }
+
+ // make sure we have at least one byte available before allocating anything
+ parser->possible_nodes_left = tree->data_length;
+ if (!mpack_tree_reserve_bytes(tree, sizeof(uint8_t))) {
+ tree->parser.state = mpack_tree_parse_state_not_started;
+ return false;
+ }
+ mpack_log("parsing tree at %p starting with byte %x\n", tree->data, (uint8_t)tree->data[0]);
+ parser->possible_nodes_left -= 1;
+ tree->node_count = 1;
+
+ #ifdef MPACK_MALLOC
+ parser->stack = parser->stack_local;
+ parser->stack_owned = false;
+ parser->stack_capacity = sizeof(parser->stack_local) / sizeof(*parser->stack_local);
+
+ if (tree->pool == NULL) {
+
+ // allocate first page
+ mpack_tree_page_t* page = (mpack_tree_page_t*)MPACK_MALLOC(MPACK_PAGE_ALLOC_SIZE);
+ mpack_log("allocated initial page %p of size %i count %i\n",
+ page, (int)MPACK_PAGE_ALLOC_SIZE, (int)MPACK_NODES_PER_PAGE);
+ if (page == NULL) {
+ tree->error = mpack_error_memory;
+ return false;
+ }
+ page->next = NULL;
+ tree->next = page;
+
+ parser->nodes = page->nodes;
+ parser->nodes_left = MPACK_NODES_PER_PAGE;
+ }
+ else
+ #endif
+ {
+ // otherwise use the provided pool
+ mpack_assert(tree->pool != NULL, "no pool provided?");
+ parser->nodes = tree->pool;
+ parser->nodes_left = tree->pool_count;
+ }
+
+ tree->root = parser->nodes;
+ ++parser->nodes;
+ --parser->nodes_left;
+
+ parser->level = 0;
+ parser->stack[0].child = tree->root;
+ parser->stack[0].left = 1;
+
+ return true;
+}
+
+void mpack_tree_parse(mpack_tree_t* tree) {
+ if (mpack_tree_error(tree) != mpack_ok)
+ return;
+
+ if (tree->parser.state != mpack_tree_parse_state_in_progress) {
+ if (!mpack_tree_parse_start(tree)) {
+ mpack_tree_flag_error(tree, (tree->read_fn == NULL) ?
+ mpack_error_invalid : mpack_error_io);
+ return;
+ }
+ }
+
+ if (!mpack_tree_continue_parsing(tree)) {
+ if (mpack_tree_error(tree) != mpack_ok)
+ return;
+
+ // We're parsing synchronously on a blocking fill function. If we
+ // didn't completely finish parsing the tree, it's an error.
+ mpack_log("tree parsing incomplete. flagging error.\n");
+ mpack_tree_flag_error(tree, (tree->read_fn == NULL) ?
+ mpack_error_invalid : mpack_error_io);
+ return;
+ }
+
+ mpack_assert(mpack_tree_error(tree) == mpack_ok);
+ mpack_assert(tree->parser.level == 0);
+ tree->parser.state = mpack_tree_parse_state_parsed;
+ mpack_log("parsed tree of %i bytes, %i bytes left\n", (int)tree->size, (int)tree->parser.possible_nodes_left);
+ mpack_log("%i nodes in final page\n", (int)tree->parser.nodes_left);
+}
+
+bool mpack_tree_try_parse(mpack_tree_t* tree) {
+ if (mpack_tree_error(tree) != mpack_ok)
+ return false;
+
+ if (tree->parser.state != mpack_tree_parse_state_in_progress)
+ if (!mpack_tree_parse_start(tree))
+ return false;
+
+ if (!mpack_tree_continue_parsing(tree))
+ return false;
+
+ mpack_assert(mpack_tree_error(tree) == mpack_ok);
+ mpack_assert(tree->parser.level == 0);
+ tree->parser.state = mpack_tree_parse_state_parsed;
+ return true;
+}
+
+
+
+/*
+ * Tree functions
+ */
+
+mpack_node_t mpack_tree_root(mpack_tree_t* tree) {
+ if (mpack_tree_error(tree) != mpack_ok)
+ return mpack_tree_nil_node(tree);
+
+ // We check that a tree was parsed successfully and assert if not. You must
+ // call mpack_tree_parse() (or mpack_tree_try_parse() with a success
+ // result) in order to access the root node.
+ if (tree->parser.state != mpack_tree_parse_state_parsed) {
+ mpack_break("Tree has not been parsed! "
+ "Did you call mpack_tree_parse() or mpack_tree_try_parse()?");
+ mpack_tree_flag_error(tree, mpack_error_bug);
+ return mpack_tree_nil_node(tree);
+ }
+
+ return mpack_node(tree, tree->root);
+}
+
+static void mpack_tree_init_clear(mpack_tree_t* tree) {
+ mpack_memset(tree, 0, sizeof(*tree));
+ tree->nil_node.type = mpack_type_nil;
+ tree->missing_node.type = mpack_type_missing;
+ tree->max_size = SIZE_MAX;
+ tree->max_nodes = SIZE_MAX;
+}
+
+#ifdef MPACK_MALLOC
+void mpack_tree_init_data(mpack_tree_t* tree, const char* data, size_t length) {
+ mpack_tree_init_clear(tree);
+
+ MPACK_STATIC_ASSERT(MPACK_NODE_PAGE_SIZE >= sizeof(mpack_tree_page_t),
+ "MPACK_NODE_PAGE_SIZE is too small");
+
+ MPACK_STATIC_ASSERT(MPACK_PAGE_ALLOC_SIZE <= MPACK_NODE_PAGE_SIZE,
+ "incorrect page rounding?");
+
+ tree->data = data;
+ tree->data_length = length;
+ tree->pool = NULL;
+ tree->pool_count = 0;
+ tree->next = NULL;
+
+ mpack_log("===========================\n");
+ mpack_log("initializing tree with data of size %i\n", (int)length);
+}
+#endif
+
+void mpack_tree_init_pool(mpack_tree_t* tree, const char* data, size_t length,
+ mpack_node_data_t* node_pool, size_t node_pool_count)
+{
+ mpack_tree_init_clear(tree);
+ #ifdef MPACK_MALLOC
+ tree->next = NULL;
+ #endif
+
+ if (node_pool_count == 0) {
+ mpack_break("initial page has no nodes!");
+ mpack_tree_flag_error(tree, mpack_error_bug);
+ return;
+ }
+
+ tree->data = data;
+ tree->data_length = length;
+ tree->pool = node_pool;
+ tree->pool_count = node_pool_count;
+
+ mpack_log("===========================\n");
+ mpack_log("initializing tree with data of size %i and pool of count %i\n",
+ (int)length, (int)node_pool_count);
+}
+
+void mpack_tree_init_error(mpack_tree_t* tree, mpack_error_t error) {
+ mpack_tree_init_clear(tree);
+ tree->error = error;
+
+ mpack_log("===========================\n");
+ mpack_log("initializing tree error state %i\n", (int)error);
+}
+
+#ifdef MPACK_MALLOC
+void mpack_tree_init_stream(mpack_tree_t* tree, mpack_tree_read_t read_fn, void* context,
+ size_t max_message_size, size_t max_message_nodes) {
+ mpack_tree_init_clear(tree);
+
+ tree->read_fn = read_fn;
+ tree->context = context;
+
+ mpack_tree_set_limits(tree, max_message_size, max_message_nodes);
+ tree->max_size = max_message_size;
+ tree->max_nodes = max_message_nodes;
+
+ mpack_log("===========================\n");
+ mpack_log("initializing tree with stream, max size %i max nodes %i\n",
+ (int)max_message_size, (int)max_message_nodes);
+}
+#endif
+
+void mpack_tree_set_limits(mpack_tree_t* tree, size_t max_message_size, size_t max_message_nodes) {
+ mpack_assert(max_message_size > 0);
+ mpack_assert(max_message_nodes > 0);
+ tree->max_size = max_message_size;
+ tree->max_nodes = max_message_nodes;
+}
+
+#if MPACK_STDIO
+typedef struct mpack_file_tree_t {
+ char* data;
+ size_t size;
+ char buffer[MPACK_BUFFER_SIZE];
+} mpack_file_tree_t;
+
+static void mpack_file_tree_teardown(mpack_tree_t* tree) {
+ mpack_file_tree_t* file_tree = (mpack_file_tree_t*)tree->context;
+ MPACK_FREE(file_tree->data);
+ MPACK_FREE(file_tree);
+}
+
+static bool mpack_file_tree_read(mpack_tree_t* tree, mpack_file_tree_t* file_tree, FILE* file, size_t max_bytes) {
+
+ // get the file size
+ errno = 0;
+ int error = 0;
+ fseek(file, 0, SEEK_END);
+ error |= errno;
+ long size = ftell(file);
+ error |= errno;
+ fseek(file, 0, SEEK_SET);
+ error |= errno;
+
+ // check for errors
+ if (error != 0 || size < 0) {
+ mpack_tree_init_error(tree, mpack_error_io);
+ return false;
+ }
+ if (size == 0) {
+ mpack_tree_init_error(tree, mpack_error_invalid);
+ return false;
+ }
+
+ // make sure the size is less than max_bytes
+ // (this mess exists to safely convert between long and size_t regardless of their widths)
+ if (max_bytes != 0 && (((uint64_t)LONG_MAX > (uint64_t)SIZE_MAX && size > (long)SIZE_MAX) || (size_t)size > max_bytes)) {
+ mpack_tree_init_error(tree, mpack_error_too_big);
+ return false;
+ }
+
+ // allocate data
+ file_tree->data = (char*)MPACK_MALLOC((size_t)size);
+ if (file_tree->data == NULL) {
+ mpack_tree_init_error(tree, mpack_error_memory);
+ return false;
+ }
+
+ // read the file
+ long total = 0;
+ while (total < size) {
+ size_t read = fread(file_tree->data + total, 1, (size_t)(size - total), file);
+ if (read <= 0) {
+ mpack_tree_init_error(tree, mpack_error_io);
+ MPACK_FREE(file_tree->data);
+ return false;
+ }
+ total += (long)read;
+ }
+
+ file_tree->size = (size_t)size;
+ return true;
+}
+
+static bool mpack_tree_file_check_max_bytes(mpack_tree_t* tree, size_t max_bytes) {
+
+ // the C STDIO family of file functions use long (e.g. ftell)
+ if (max_bytes > LONG_MAX) {
+ mpack_break("max_bytes of %" PRIu64 " is invalid, maximum is LONG_MAX", (uint64_t)max_bytes);
+ mpack_tree_init_error(tree, mpack_error_bug);
+ return false;
+ }
+
+ return true;
+}
+
+static void mpack_tree_init_stdfile_noclose(mpack_tree_t* tree, FILE* stdfile, size_t max_bytes) {
+
+ // allocate file tree
+ mpack_file_tree_t* file_tree = (mpack_file_tree_t*) MPACK_MALLOC(sizeof(mpack_file_tree_t));
+ if (file_tree == NULL) {
+ mpack_tree_init_error(tree, mpack_error_memory);
+ return;
+ }
+
+ // read all data
+ if (!mpack_file_tree_read(tree, file_tree, stdfile, max_bytes)) {
+ MPACK_FREE(file_tree);
+ return;
+ }
+
+ mpack_tree_init_data(tree, file_tree->data, file_tree->size);
+ mpack_tree_set_context(tree, file_tree);
+ mpack_tree_set_teardown(tree, mpack_file_tree_teardown);
+}
+
+void mpack_tree_init_stdfile(mpack_tree_t* tree, FILE* stdfile, size_t max_bytes, bool close_when_done) {
+ if (!mpack_tree_file_check_max_bytes(tree, max_bytes))
+ return;
+
+ mpack_tree_init_stdfile_noclose(tree, stdfile, max_bytes);
+
+ if (close_when_done)
+ fclose(stdfile);
+}
+
+void mpack_tree_init_filename(mpack_tree_t* tree, const char* filename, size_t max_bytes) {
+ if (!mpack_tree_file_check_max_bytes(tree, max_bytes))
+ return;
+
+ // open the file
+ FILE* file = fopen(filename, "rb");
+ if (!file) {
+ mpack_tree_init_error(tree, mpack_error_io);
+ return;
+ }
+
+ mpack_tree_init_stdfile(tree, file, max_bytes, true);
+}
+#endif
+
+mpack_error_t mpack_tree_destroy(mpack_tree_t* tree) {
+ mpack_tree_cleanup(tree);
+
+ #ifdef MPACK_MALLOC
+ if (tree->buffer)
+ MPACK_FREE(tree->buffer);
+ #endif
+
+ if (tree->teardown)
+ tree->teardown(tree);
+ tree->teardown = NULL;
+
+ return tree->error;
+}
+
+void mpack_tree_flag_error(mpack_tree_t* tree, mpack_error_t error) {
+ if (tree->error == mpack_ok) {
+ mpack_log("tree %p setting error %i: %s\n", tree, (int)error, mpack_error_to_string(error));
+ tree->error = error;
+ if (tree->error_fn)
+ tree->error_fn(tree, error);
+ }
+
+}
+
+
+
+/*
+ * Node misc functions
+ */
+
+void mpack_node_flag_error(mpack_node_t node, mpack_error_t error) {
+ mpack_tree_flag_error(node.tree, error);
+}
+
+mpack_tag_t mpack_node_tag(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return mpack_tag_nil();
+
+ mpack_tag_t tag = MPACK_TAG_ZERO;
+
+ tag.type = node.data->type;
+ switch (node.data->type) {
+ case mpack_type_missing:
+ // If a node is missing, I don't know if it makes sense to ask for
+ // a tag for it. We'll return a missing tag to match the missing
+ // node I guess, but attempting to use the tag for anything (like
+ // writing it for example) will flag mpack_error_bug.
+ break;
+ case mpack_type_nil: break;
+ case mpack_type_bool: tag.v.b = node.data->value.b; break;
+ case mpack_type_float: tag.v.f = node.data->value.f; break;
+ case mpack_type_double: tag.v.d = node.data->value.d; break;
+ case mpack_type_int: tag.v.i = node.data->value.i; break;
+ case mpack_type_uint: tag.v.u = node.data->value.u; break;
+
+ case mpack_type_str: tag.v.l = node.data->len; break;
+ case mpack_type_bin: tag.v.l = node.data->len; break;
+
+ #if MPACK_EXTENSIONS
+ case mpack_type_ext:
+ tag.v.l = node.data->len;
+ tag.exttype = mpack_node_exttype_unchecked(node);
+ break;
+ #endif
+
+ case mpack_type_array: tag.v.n = node.data->len; break;
+ case mpack_type_map: tag.v.n = node.data->len; break;
+
+ default:
+ mpack_assert(0, "unrecognized type %i", (int)node.data->type);
+ break;
+ }
+ return tag;
+}
+
+#if MPACK_DEBUG && MPACK_STDIO
+static void mpack_node_print_element(mpack_node_t node, mpack_print_t* print, size_t depth) {
+ mpack_node_data_t* data = node.data;
+ switch (data->type) {
+ case mpack_type_str:
+ {
+ mpack_print_append_cstr(print, "\"");
+ const char* bytes = mpack_node_data_unchecked(node);
+ for (size_t i = 0; i < data->len; ++i) {
+ char c = bytes[i];
+ switch (c) {
+ case '\n': mpack_print_append_cstr(print, "\\n"); break;
+ case '\\': mpack_print_append_cstr(print, "\\\\"); break;
+ case '"': mpack_print_append_cstr(print, "\\\""); break;
+ default: mpack_print_append(print, &c, 1); break;
+ }
+ }
+ mpack_print_append_cstr(print, "\"");
+ }
+ break;
+
+ case mpack_type_array:
+ mpack_print_append_cstr(print, "[\n");
+ for (size_t i = 0; i < data->len; ++i) {
+ for (size_t j = 0; j < depth + 1; ++j)
+ mpack_print_append_cstr(print, " ");
+ mpack_node_print_element(mpack_node_array_at(node, i), print, depth + 1);
+ if (i != data->len - 1)
+ mpack_print_append_cstr(print, ",");
+ mpack_print_append_cstr(print, "\n");
+ }
+ for (size_t i = 0; i < depth; ++i)
+ mpack_print_append_cstr(print, " ");
+ mpack_print_append_cstr(print, "]");
+ break;
+
+ case mpack_type_map:
+ mpack_print_append_cstr(print, "{\n");
+ for (size_t i = 0; i < data->len; ++i) {
+ for (size_t j = 0; j < depth + 1; ++j)
+ mpack_print_append_cstr(print, " ");
+ mpack_node_print_element(mpack_node_map_key_at(node, i), print, depth + 1);
+ mpack_print_append_cstr(print, ": ");
+ mpack_node_print_element(mpack_node_map_value_at(node, i), print, depth + 1);
+ if (i != data->len - 1)
+ mpack_print_append_cstr(print, ",");
+ mpack_print_append_cstr(print, "\n");
+ }
+ for (size_t i = 0; i < depth; ++i)
+ mpack_print_append_cstr(print, " ");
+ mpack_print_append_cstr(print, "}");
+ break;
+
+ default:
+ {
+ const char* prefix = NULL;
+ size_t prefix_length = 0;
+ if (mpack_node_type(node) == mpack_type_bin
+ #if MPACK_EXTENSIONS
+ || mpack_node_type(node) == mpack_type_ext
+ #endif
+ ) {
+ prefix = mpack_node_data(node);
+ prefix_length = mpack_node_data_len(node);
+ }
+
+ char buf[256];
+ mpack_tag_t tag = mpack_node_tag(node);
+ mpack_tag_debug_pseudo_json(tag, buf, sizeof(buf), prefix, prefix_length);
+ mpack_print_append_cstr(print, buf);
+ }
+ break;
+ }
+}
+
+void mpack_node_print_to_buffer(mpack_node_t node, char* buffer, size_t buffer_size) {
+ if (buffer_size == 0) {
+ mpack_assert(false, "buffer size is zero!");
+ return;
+ }
+
+ mpack_print_t print;
+ mpack_memset(&print, 0, sizeof(print));
+ print.buffer = buffer;
+ print.size = buffer_size;
+ mpack_node_print_element(node, &print, 0);
+ mpack_print_append(&print, "", 1); // null-terminator
+ mpack_print_flush(&print);
+
+ // we always make sure there's a null-terminator at the end of the buffer
+ // in case we ran out of space.
+ print.buffer[print.size - 1] = '\0';
+}
+
+void mpack_node_print_to_callback(mpack_node_t node, mpack_print_callback_t callback, void* context) {
+ char buffer[1024];
+ mpack_print_t print;
+ mpack_memset(&print, 0, sizeof(print));
+ print.buffer = buffer;
+ print.size = sizeof(buffer);
+ print.callback = callback;
+ print.context = context;
+ mpack_node_print_element(node, &print, 0);
+ mpack_print_flush(&print);
+}
+
+void mpack_node_print_to_file(mpack_node_t node, FILE* file) {
+ mpack_assert(file != NULL, "file is NULL");
+
+ char buffer[1024];
+ mpack_print_t print;
+ mpack_memset(&print, 0, sizeof(print));
+ print.buffer = buffer;
+ print.size = sizeof(buffer);
+ print.callback = &mpack_print_file_callback;
+ print.context = file;
+
+ size_t depth = 2;
+ for (size_t i = 0; i < depth; ++i)
+ mpack_print_append_cstr(&print, " ");
+ mpack_node_print_element(node, &print, depth);
+ mpack_print_append_cstr(&print, "\n");
+ mpack_print_flush(&print);
+}
+#endif
+
+
+
+/*
+ * Node Value Functions
+ */
+
+#if MPACK_EXTENSIONS
+mpack_timestamp_t mpack_node_timestamp(mpack_node_t node) {
+ mpack_timestamp_t timestamp = {0, 0};
+
+ // we'll let mpack_node_exttype() do most checks
+ if (mpack_node_exttype(node) != MPACK_EXTTYPE_TIMESTAMP) {
+ mpack_log("exttype %i\n", mpack_node_exttype(node));
+ mpack_node_flag_error(node, mpack_error_type);
+ return timestamp;
+ }
+
+ const char* p = mpack_node_data_unchecked(node);
+
+ switch (node.data->len) {
+ case 4:
+ timestamp.nanoseconds = 0;
+ timestamp.seconds = mpack_load_u32(p);
+ break;
+
+ case 8: {
+ uint64_t value = mpack_load_u64(p);
+ timestamp.nanoseconds = (uint32_t)(value >> 34);
+ timestamp.seconds = value & ((UINT64_C(1) << 34) - 1);
+ break;
+ }
+
+ case 12:
+ timestamp.nanoseconds = mpack_load_u32(p);
+ timestamp.seconds = mpack_load_i64(p + 4);
+ break;
+
+ default:
+ mpack_tree_flag_error(node.tree, mpack_error_invalid);
+ return timestamp;
+ }
+
+ if (timestamp.nanoseconds > MPACK_TIMESTAMP_NANOSECONDS_MAX) {
+ mpack_tree_flag_error(node.tree, mpack_error_invalid);
+ mpack_timestamp_t zero = {0, 0};
+ return zero;
+ }
+
+ return timestamp;
+}
+
+int64_t mpack_node_timestamp_seconds(mpack_node_t node) {
+ return mpack_node_timestamp(node).seconds;
+}
+
+uint32_t mpack_node_timestamp_nanoseconds(mpack_node_t node) {
+ return mpack_node_timestamp(node).nanoseconds;
+}
+#endif
+
+
+
+/*
+ * Node Data Functions
+ */
+
+void mpack_node_check_utf8(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return;
+ mpack_node_data_t* data = node.data;
+ if (data->type != mpack_type_str || !mpack_utf8_check(mpack_node_data_unchecked(node), data->len))
+ mpack_node_flag_error(node, mpack_error_type);
+}
+
+void mpack_node_check_utf8_cstr(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return;
+ mpack_node_data_t* data = node.data;
+ if (data->type != mpack_type_str || !mpack_utf8_check_no_null(mpack_node_data_unchecked(node), data->len))
+ mpack_node_flag_error(node, mpack_error_type);
+}
+
+size_t mpack_node_copy_data(mpack_node_t node, char* buffer, size_t bufsize) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0;
+
+ mpack_assert(bufsize == 0 || buffer != NULL, "buffer is NULL for maximum of %i bytes", (int)bufsize);
+
+ mpack_type_t type = node.data->type;
+ if (type != mpack_type_str && type != mpack_type_bin
+ #if MPACK_EXTENSIONS
+ && type != mpack_type_ext
+ #endif
+ ) {
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+ }
+
+ if (node.data->len > bufsize) {
+ mpack_node_flag_error(node, mpack_error_too_big);
+ return 0;
+ }
+
+ mpack_memcpy(buffer, mpack_node_data_unchecked(node), node.data->len);
+ return (size_t)node.data->len;
+}
+
+size_t mpack_node_copy_utf8(mpack_node_t node, char* buffer, size_t bufsize) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0;
+
+ mpack_assert(bufsize == 0 || buffer != NULL, "buffer is NULL for maximum of %i bytes", (int)bufsize);
+
+ mpack_type_t type = node.data->type;
+ if (type != mpack_type_str) {
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+ }
+
+ if (node.data->len > bufsize) {
+ mpack_node_flag_error(node, mpack_error_too_big);
+ return 0;
+ }
+
+ if (!mpack_utf8_check(mpack_node_data_unchecked(node), node.data->len)) {
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+ }
+
+ mpack_memcpy(buffer, mpack_node_data_unchecked(node), node.data->len);
+ return (size_t)node.data->len;
+}
+
+void mpack_node_copy_cstr(mpack_node_t node, char* buffer, size_t bufsize) {
+
+ // we can't break here because the error isn't recoverable; we
+ // have to add a null-terminator.
+ mpack_assert(buffer != NULL, "buffer is NULL");
+ mpack_assert(bufsize >= 1, "buffer size is zero; you must have room for at least a null-terminator");
+
+ if (mpack_node_error(node) != mpack_ok) {
+ buffer[0] = '\0';
+ return;
+ }
+
+ if (node.data->type != mpack_type_str) {
+ buffer[0] = '\0';
+ mpack_node_flag_error(node, mpack_error_type);
+ return;
+ }
+
+ if (node.data->len > bufsize - 1) {
+ buffer[0] = '\0';
+ mpack_node_flag_error(node, mpack_error_too_big);
+ return;
+ }
+
+ if (!mpack_str_check_no_null(mpack_node_data_unchecked(node), node.data->len)) {
+ buffer[0] = '\0';
+ mpack_node_flag_error(node, mpack_error_type);
+ return;
+ }
+
+ mpack_memcpy(buffer, mpack_node_data_unchecked(node), node.data->len);
+ buffer[node.data->len] = '\0';
+}
+
+void mpack_node_copy_utf8_cstr(mpack_node_t node, char* buffer, size_t bufsize) {
+
+ // we can't break here because the error isn't recoverable; we
+ // have to add a null-terminator.
+ mpack_assert(buffer != NULL, "buffer is NULL");
+ mpack_assert(bufsize >= 1, "buffer size is zero; you must have room for at least a null-terminator");
+
+ if (mpack_node_error(node) != mpack_ok) {
+ buffer[0] = '\0';
+ return;
+ }
+
+ if (node.data->type != mpack_type_str) {
+ buffer[0] = '\0';
+ mpack_node_flag_error(node, mpack_error_type);
+ return;
+ }
+
+ if (node.data->len > bufsize - 1) {
+ buffer[0] = '\0';
+ mpack_node_flag_error(node, mpack_error_too_big);
+ return;
+ }
+
+ if (!mpack_utf8_check_no_null(mpack_node_data_unchecked(node), node.data->len)) {
+ buffer[0] = '\0';
+ mpack_node_flag_error(node, mpack_error_type);
+ return;
+ }
+
+ mpack_memcpy(buffer, mpack_node_data_unchecked(node), node.data->len);
+ buffer[node.data->len] = '\0';
+}
+
+#ifdef MPACK_MALLOC
+char* mpack_node_data_alloc(mpack_node_t node, size_t maxlen) {
+ if (mpack_node_error(node) != mpack_ok)
+ return NULL;
+
+ // make sure this is a valid data type
+ mpack_type_t type = node.data->type;
+ if (type != mpack_type_str && type != mpack_type_bin
+ #if MPACK_EXTENSIONS
+ && type != mpack_type_ext
+ #endif
+ ) {
+ mpack_node_flag_error(node, mpack_error_type);
+ return NULL;
+ }
+
+ if (node.data->len > maxlen) {
+ mpack_node_flag_error(node, mpack_error_too_big);
+ return NULL;
+ }
+
+ char* ret = (char*) MPACK_MALLOC((size_t)node.data->len);
+ if (ret == NULL) {
+ mpack_node_flag_error(node, mpack_error_memory);
+ return NULL;
+ }
+
+ mpack_memcpy(ret, mpack_node_data_unchecked(node), node.data->len);
+ return ret;
+}
+
+char* mpack_node_cstr_alloc(mpack_node_t node, size_t maxlen) {
+ if (mpack_node_error(node) != mpack_ok)
+ return NULL;
+
+ // make sure maxlen makes sense
+ if (maxlen < 1) {
+ mpack_break("maxlen is zero; you must have room for at least a null-terminator");
+ mpack_node_flag_error(node, mpack_error_bug);
+ return NULL;
+ }
+
+ if (node.data->type != mpack_type_str) {
+ mpack_node_flag_error(node, mpack_error_type);
+ return NULL;
+ }
+
+ if (node.data->len > maxlen - 1) {
+ mpack_node_flag_error(node, mpack_error_too_big);
+ return NULL;
+ }
+
+ if (!mpack_str_check_no_null(mpack_node_data_unchecked(node), node.data->len)) {
+ mpack_node_flag_error(node, mpack_error_type);
+ return NULL;
+ }
+
+ char* ret = (char*) MPACK_MALLOC((size_t)(node.data->len + 1));
+ if (ret == NULL) {
+ mpack_node_flag_error(node, mpack_error_memory);
+ return NULL;
+ }
+
+ mpack_memcpy(ret, mpack_node_data_unchecked(node), node.data->len);
+ ret[node.data->len] = '\0';
+ return ret;
+}
+
+char* mpack_node_utf8_cstr_alloc(mpack_node_t node, size_t maxlen) {
+ if (mpack_node_error(node) != mpack_ok)
+ return NULL;
+
+ // make sure maxlen makes sense
+ if (maxlen < 1) {
+ mpack_break("maxlen is zero; you must have room for at least a null-terminator");
+ mpack_node_flag_error(node, mpack_error_bug);
+ return NULL;
+ }
+
+ if (node.data->type != mpack_type_str) {
+ mpack_node_flag_error(node, mpack_error_type);
+ return NULL;
+ }
+
+ if (node.data->len > maxlen - 1) {
+ mpack_node_flag_error(node, mpack_error_too_big);
+ return NULL;
+ }
+
+ if (!mpack_utf8_check_no_null(mpack_node_data_unchecked(node), node.data->len)) {
+ mpack_node_flag_error(node, mpack_error_type);
+ return NULL;
+ }
+
+ char* ret = (char*) MPACK_MALLOC((size_t)(node.data->len + 1));
+ if (ret == NULL) {
+ mpack_node_flag_error(node, mpack_error_memory);
+ return NULL;
+ }
+
+ mpack_memcpy(ret, mpack_node_data_unchecked(node), node.data->len);
+ ret[node.data->len] = '\0';
+ return ret;
+}
+#endif
+
+
+/*
+ * Compound Node Functions
+ */
+
+static mpack_node_data_t* mpack_node_map_int_impl(mpack_node_t node, int64_t num) {
+ if (mpack_node_error(node) != mpack_ok)
+ return NULL;
+
+ if (node.data->type != mpack_type_map) {
+ mpack_node_flag_error(node, mpack_error_type);
+ return NULL;
+ }
+
+ mpack_node_data_t* found = NULL;
+
+ for (size_t i = 0; i < node.data->len; ++i) {
+ mpack_node_data_t* key = mpack_node_child(node, i * 2);
+
+ if ((key->type == mpack_type_int && key->value.i == num) ||
+ (key->type == mpack_type_uint && num >= 0 && key->value.u == (uint64_t)num))
+ {
+ if (found) {
+ mpack_node_flag_error(node, mpack_error_data);
+ return NULL;
+ }
+ found = mpack_node_child(node, i * 2 + 1);
+ }
+ }
+
+ if (found)
+ return found;
+
+ return NULL;
+}
+
+static mpack_node_data_t* mpack_node_map_uint_impl(mpack_node_t node, uint64_t num) {
+ if (mpack_node_error(node) != mpack_ok)
+ return NULL;
+
+ if (node.data->type != mpack_type_map) {
+ mpack_node_flag_error(node, mpack_error_type);
+ return NULL;
+ }
+
+ mpack_node_data_t* found = NULL;
+
+ for (size_t i = 0; i < node.data->len; ++i) {
+ mpack_node_data_t* key = mpack_node_child(node, i * 2);
+
+ if ((key->type == mpack_type_uint && key->value.u == num) ||
+ (key->type == mpack_type_int && key->value.i >= 0 && (uint64_t)key->value.i == num))
+ {
+ if (found) {
+ mpack_node_flag_error(node, mpack_error_data);
+ return NULL;
+ }
+ found = mpack_node_child(node, i * 2 + 1);
+ }
+ }
+
+ if (found)
+ return found;
+
+ return NULL;
+}
+
+static mpack_node_data_t* mpack_node_map_str_impl(mpack_node_t node, const char* str, size_t length) {
+ if (mpack_node_error(node) != mpack_ok)
+ return NULL;
+
+ mpack_assert(length == 0 || str != NULL, "str of length %i is NULL", (int)length);
+
+ if (node.data->type != mpack_type_map) {
+ mpack_node_flag_error(node, mpack_error_type);
+ return NULL;
+ }
+
+ mpack_tree_t* tree = node.tree;
+ mpack_node_data_t* found = NULL;
+
+ for (size_t i = 0; i < node.data->len; ++i) {
+ mpack_node_data_t* key = mpack_node_child(node, i * 2);
+
+ if (key->type == mpack_type_str && key->len == length &&
+ mpack_memcmp(str, mpack_node_data_unchecked(mpack_node(tree, key)), length) == 0) {
+ if (found) {
+ mpack_node_flag_error(node, mpack_error_data);
+ return NULL;
+ }
+ found = mpack_node_child(node, i * 2 + 1);
+ }
+ }
+
+ if (found)
+ return found;
+
+ return NULL;
+}
+
+static mpack_node_t mpack_node_wrap_lookup(mpack_tree_t* tree, mpack_node_data_t* data) {
+ if (!data) {
+ if (tree->error == mpack_ok)
+ mpack_tree_flag_error(tree, mpack_error_data);
+ return mpack_tree_nil_node(tree);
+ }
+ return mpack_node(tree, data);
+}
+
+static mpack_node_t mpack_node_wrap_lookup_optional(mpack_tree_t* tree, mpack_node_data_t* data) {
+ if (!data) {
+ if (tree->error == mpack_ok)
+ return mpack_tree_missing_node(tree);
+ return mpack_tree_nil_node(tree);
+ }
+ return mpack_node(tree, data);
+}
+
+mpack_node_t mpack_node_map_int(mpack_node_t node, int64_t num) {
+ return mpack_node_wrap_lookup(node.tree, mpack_node_map_int_impl(node, num));
+}
+
+mpack_node_t mpack_node_map_int_optional(mpack_node_t node, int64_t num) {
+ return mpack_node_wrap_lookup_optional(node.tree, mpack_node_map_int_impl(node, num));
+}
+
+mpack_node_t mpack_node_map_uint(mpack_node_t node, uint64_t num) {
+ return mpack_node_wrap_lookup(node.tree, mpack_node_map_uint_impl(node, num));
+}
+
+mpack_node_t mpack_node_map_uint_optional(mpack_node_t node, uint64_t num) {
+ return mpack_node_wrap_lookup_optional(node.tree, mpack_node_map_uint_impl(node, num));
+}
+
+mpack_node_t mpack_node_map_str(mpack_node_t node, const char* str, size_t length) {
+ return mpack_node_wrap_lookup(node.tree, mpack_node_map_str_impl(node, str, length));
+}
+
+mpack_node_t mpack_node_map_str_optional(mpack_node_t node, const char* str, size_t length) {
+ return mpack_node_wrap_lookup_optional(node.tree, mpack_node_map_str_impl(node, str, length));
+}
+
+mpack_node_t mpack_node_map_cstr(mpack_node_t node, const char* cstr) {
+ mpack_assert(cstr != NULL, "cstr is NULL");
+ return mpack_node_map_str(node, cstr, mpack_strlen(cstr));
+}
+
+mpack_node_t mpack_node_map_cstr_optional(mpack_node_t node, const char* cstr) {
+ mpack_assert(cstr != NULL, "cstr is NULL");
+ return mpack_node_map_str_optional(node, cstr, mpack_strlen(cstr));
+}
+
+bool mpack_node_map_contains_int(mpack_node_t node, int64_t num) {
+ return mpack_node_map_int_impl(node, num) != NULL;
+}
+
+bool mpack_node_map_contains_uint(mpack_node_t node, uint64_t num) {
+ return mpack_node_map_uint_impl(node, num) != NULL;
+}
+
+bool mpack_node_map_contains_str(mpack_node_t node, const char* str, size_t length) {
+ return mpack_node_map_str_impl(node, str, length) != NULL;
+}
+
+bool mpack_node_map_contains_cstr(mpack_node_t node, const char* cstr) {
+ mpack_assert(cstr != NULL, "cstr is NULL");
+ return mpack_node_map_contains_str(node, cstr, mpack_strlen(cstr));
+}
+
+size_t mpack_node_enum_optional(mpack_node_t node, const char* strings[], size_t count) {
+ if (mpack_node_error(node) != mpack_ok)
+ return count;
+
+ // the value is only recognized if it is a string
+ if (mpack_node_type(node) != mpack_type_str)
+ return count;
+
+ // fetch the string
+ const char* key = mpack_node_str(node);
+ size_t keylen = mpack_node_strlen(node);
+ mpack_assert(mpack_node_error(node) == mpack_ok, "these should not fail");
+
+ // find what key it matches
+ for (size_t i = 0; i < count; ++i) {
+ const char* other = strings[i];
+ size_t otherlen = mpack_strlen(other);
+ if (keylen == otherlen && mpack_memcmp(key, other, keylen) == 0)
+ return i;
+ }
+
+ // no matches
+ return count;
+}
+
+size_t mpack_node_enum(mpack_node_t node, const char* strings[], size_t count) {
+ size_t value = mpack_node_enum_optional(node, strings, count);
+ if (value == count)
+ mpack_node_flag_error(node, mpack_error_type);
+ return value;
+}
+
+mpack_type_t mpack_node_type(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return mpack_type_nil;
+ return node.data->type;
+}
+
+bool mpack_node_is_nil(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok) {
+ // All nodes are treated as nil nodes when we are in error.
+ return true;
+ }
+ return node.data->type == mpack_type_nil;
+}
+
+bool mpack_node_is_missing(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok) {
+ // errors still return nil nodes, not missing nodes.
+ return false;
+ }
+ return node.data->type == mpack_type_missing;
+}
+
+void mpack_node_nil(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return;
+ if (node.data->type != mpack_type_nil)
+ mpack_node_flag_error(node, mpack_error_type);
+}
+
+void mpack_node_missing(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return;
+ if (node.data->type != mpack_type_missing)
+ mpack_node_flag_error(node, mpack_error_type);
+}
+
+bool mpack_node_bool(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return false;
+
+ if (node.data->type == mpack_type_bool)
+ return node.data->value.b;
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return false;
+}
+
+void mpack_node_true(mpack_node_t node) {
+ if (mpack_node_bool(node) != true)
+ mpack_node_flag_error(node, mpack_error_type);
+}
+
+void mpack_node_false(mpack_node_t node) {
+ if (mpack_node_bool(node) != false)
+ mpack_node_flag_error(node, mpack_error_type);
+}
+
+uint8_t mpack_node_u8(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0;
+
+ if (node.data->type == mpack_type_uint) {
+ if (node.data->value.u <= UINT8_MAX)
+ return (uint8_t)node.data->value.u;
+ } else if (node.data->type == mpack_type_int) {
+ if (node.data->value.i >= 0 && node.data->value.i <= UINT8_MAX)
+ return (uint8_t)node.data->value.i;
+ }
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+}
+
+int8_t mpack_node_i8(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0;
+
+ if (node.data->type == mpack_type_uint) {
+ if (node.data->value.u <= INT8_MAX)
+ return (int8_t)node.data->value.u;
+ } else if (node.data->type == mpack_type_int) {
+ if (node.data->value.i >= INT8_MIN && node.data->value.i <= INT8_MAX)
+ return (int8_t)node.data->value.i;
+ }
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+}
+
+uint16_t mpack_node_u16(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0;
+
+ if (node.data->type == mpack_type_uint) {
+ if (node.data->value.u <= UINT16_MAX)
+ return (uint16_t)node.data->value.u;
+ } else if (node.data->type == mpack_type_int) {
+ if (node.data->value.i >= 0 && node.data->value.i <= UINT16_MAX)
+ return (uint16_t)node.data->value.i;
+ }
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+}
+
+int16_t mpack_node_i16(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0;
+
+ if (node.data->type == mpack_type_uint) {
+ if (node.data->value.u <= INT16_MAX)
+ return (int16_t)node.data->value.u;
+ } else if (node.data->type == mpack_type_int) {
+ if (node.data->value.i >= INT16_MIN && node.data->value.i <= INT16_MAX)
+ return (int16_t)node.data->value.i;
+ }
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+}
+
+uint32_t mpack_node_u32(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0;
+
+ if (node.data->type == mpack_type_uint) {
+ if (node.data->value.u <= UINT32_MAX)
+ return (uint32_t)node.data->value.u;
+ } else if (node.data->type == mpack_type_int) {
+ if (node.data->value.i >= 0 && node.data->value.i <= UINT32_MAX)
+ return (uint32_t)node.data->value.i;
+ }
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+}
+
+int32_t mpack_node_i32(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0;
+
+ if (node.data->type == mpack_type_uint) {
+ if (node.data->value.u <= INT32_MAX)
+ return (int32_t)node.data->value.u;
+ } else if (node.data->type == mpack_type_int) {
+ if (node.data->value.i >= INT32_MIN && node.data->value.i <= INT32_MAX)
+ return (int32_t)node.data->value.i;
+ }
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+}
+
+uint64_t mpack_node_u64(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0;
+
+ if (node.data->type == mpack_type_uint) {
+ return node.data->value.u;
+ } else if (node.data->type == mpack_type_int) {
+ if (node.data->value.i >= 0)
+ return (uint64_t)node.data->value.i;
+ }
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+}
+
+int64_t mpack_node_i64(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0;
+
+ if (node.data->type == mpack_type_uint) {
+ if (node.data->value.u <= (uint64_t)INT64_MAX)
+ return (int64_t)node.data->value.u;
+ } else if (node.data->type == mpack_type_int) {
+ return node.data->value.i;
+ }
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+}
+
+unsigned int mpack_node_uint(mpack_node_t node) {
+
+ // This should be true at compile-time, so this just wraps the 32-bit function.
+ if (sizeof(unsigned int) == 4)
+ return (unsigned int)mpack_node_u32(node);
+
+ // Otherwise we use u64 and check the range.
+ uint64_t val = mpack_node_u64(node);
+ if (val <= UINT_MAX)
+ return (unsigned int)val;
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+}
+
+int mpack_node_int(mpack_node_t node) {
+
+ // This should be true at compile-time, so this just wraps the 32-bit function.
+ if (sizeof(int) == 4)
+ return (int)mpack_node_i32(node);
+
+ // Otherwise we use i64 and check the range.
+ int64_t val = mpack_node_i64(node);
+ if (val >= INT_MIN && val <= INT_MAX)
+ return (int)val;
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+}
+
+float mpack_node_float(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0.0f;
+
+ if (node.data->type == mpack_type_uint)
+ return (float)node.data->value.u;
+ else if (node.data->type == mpack_type_int)
+ return (float)node.data->value.i;
+ else if (node.data->type == mpack_type_float)
+ return node.data->value.f;
+ else if (node.data->type == mpack_type_double)
+ return (float)node.data->value.d;
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0.0f;
+}
+
+double mpack_node_double(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0.0;
+
+ if (node.data->type == mpack_type_uint)
+ return (double)node.data->value.u;
+ else if (node.data->type == mpack_type_int)
+ return (double)node.data->value.i;
+ else if (node.data->type == mpack_type_float)
+ return (double)node.data->value.f;
+ else if (node.data->type == mpack_type_double)
+ return node.data->value.d;
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0.0;
+}
+
+float mpack_node_float_strict(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0.0f;
+
+ if (node.data->type == mpack_type_float)
+ return node.data->value.f;
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0.0f;
+}
+
+double mpack_node_double_strict(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0.0;
+
+ if (node.data->type == mpack_type_float)
+ return (double)node.data->value.f;
+ else if (node.data->type == mpack_type_double)
+ return node.data->value.d;
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0.0;
+}
+
+#if MPACK_EXTENSIONS
+int8_t mpack_node_exttype(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0;
+
+ if (node.data->type == mpack_type_ext)
+ return mpack_node_exttype_unchecked(node);
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+}
+#endif
+
+uint32_t mpack_node_data_len(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0;
+
+ mpack_type_t type = node.data->type;
+ if (type == mpack_type_str || type == mpack_type_bin
+ #if MPACK_EXTENSIONS
+ || type == mpack_type_ext
+ #endif
+ )
+ return (uint32_t)node.data->len;
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+}
+
+size_t mpack_node_strlen(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0;
+
+ if (node.data->type == mpack_type_str)
+ return (size_t)node.data->len;
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+}
+
+const char* mpack_node_str(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return NULL;
+
+ mpack_type_t type = node.data->type;
+ if (type == mpack_type_str)
+ return mpack_node_data_unchecked(node);
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return NULL;
+}
+
+const char* mpack_node_data(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return NULL;
+
+ mpack_type_t type = node.data->type;
+ if (type == mpack_type_str || type == mpack_type_bin
+ #if MPACK_EXTENSIONS
+ || type == mpack_type_ext
+ #endif
+ )
+ return mpack_node_data_unchecked(node);
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return NULL;
+}
+
+const char* mpack_node_bin_data(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return NULL;
+
+ if (node.data->type == mpack_type_bin)
+ return mpack_node_data_unchecked(node);
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return NULL;
+}
+
+size_t mpack_node_bin_size(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0;
+
+ if (node.data->type == mpack_type_bin)
+ return (size_t)node.data->len;
+
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+}
+
+size_t mpack_node_array_length(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0;
+
+ if (node.data->type != mpack_type_array) {
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+ }
+
+ return (size_t)node.data->len;
+}
+
+mpack_node_t mpack_node_array_at(mpack_node_t node, size_t index) {
+ if (mpack_node_error(node) != mpack_ok)
+ return mpack_tree_nil_node(node.tree);
+
+ if (node.data->type != mpack_type_array) {
+ mpack_node_flag_error(node, mpack_error_type);
+ return mpack_tree_nil_node(node.tree);
+ }
+
+ if (index >= node.data->len) {
+ mpack_node_flag_error(node, mpack_error_data);
+ return mpack_tree_nil_node(node.tree);
+ }
+
+ return mpack_node(node.tree, mpack_node_child(node, index));
+}
+
+size_t mpack_node_map_count(mpack_node_t node) {
+ if (mpack_node_error(node) != mpack_ok)
+ return 0;
+
+ if (node.data->type != mpack_type_map) {
+ mpack_node_flag_error(node, mpack_error_type);
+ return 0;
+ }
+
+ return node.data->len;
+}
+
+// internal node map lookup
+static mpack_node_t mpack_node_map_at(mpack_node_t node, size_t index, size_t offset) {
+ if (mpack_node_error(node) != mpack_ok)
+ return mpack_tree_nil_node(node.tree);
+
+ if (node.data->type != mpack_type_map) {
+ mpack_node_flag_error(node, mpack_error_type);
+ return mpack_tree_nil_node(node.tree);
+ }
+
+ if (index >= node.data->len) {
+ mpack_node_flag_error(node, mpack_error_data);
+ return mpack_tree_nil_node(node.tree);
+ }
+
+ return mpack_node(node.tree, mpack_node_child(node, index * 2 + offset));
+}
+
+mpack_node_t mpack_node_map_key_at(mpack_node_t node, size_t index) {
+ return mpack_node_map_at(node, index, 0);
+}
+
+mpack_node_t mpack_node_map_value_at(mpack_node_t node, size_t index) {
+ return mpack_node_map_at(node, index, 1);
+}
+
+#endif
diff --git a/src/lib/mpmalloc.cc b/src/lib/mpmalloc.cc
new file mode 100644
index 0000000..2483a3a
--- /dev/null
+++ b/src/lib/mpmalloc.cc
@@ -0,0 +1,42 @@
+#include <stdlib.h>
+#include "driver/stdout.h"
+#include "lib/mpmalloc.h"
+
+void* mpcalloc(size_t nmemb, size_t size)
+{
+ void* ret = calloc(nmemb, size);
+#ifdef ADDR_20BIT
+ kout << "calloc:" << dec << (uint32_t)nmemb << "x" << (uint32_t)size << "@" << ret << endl;
+#else
+ kout << "calloc:" << dec << nmemb << "x" << size << "@" << ret << endl;
+#endif
+ return ret;
+}
+
+void* mpmalloc(size_t size)
+{
+ void* ret = malloc(size);
+#ifdef ADDR_20BIT
+ kout << "malloc:" << dec << (uint32_t)size << "@" << ret << endl;
+#else
+ kout << "malloc:" << dec << size << "@" << ret << endl;
+#endif
+ return ret;
+}
+
+void* mprealloc(void* addr, size_t size)
+{
+ void* ret = realloc(addr, size);
+#ifdef ADDR_20BIT
+ kout << "realloc:" << addr << ":" << dec << (uint32_t)size << "@" << ret << endl;
+#else
+ kout << "realloc:" << addr << ":" << dec << size << "@" << ret << endl;
+#endif
+ return ret;
+}
+
+void mpfree(void* addr)
+{
+ kout << "free:" << addr << endl;
+ free(addr);
+}
diff --git a/src/lib/nanopb/pb_common.cc b/src/lib/nanopb/pb_common.cc
new file mode 100644
index 0000000..4fb7186
--- /dev/null
+++ b/src/lib/nanopb/pb_common.cc
@@ -0,0 +1,97 @@
+/* pb_common.c: Common support functions for pb_encode.c and pb_decode.c.
+ *
+ * 2014 Petteri Aimonen <jpa@kapsi.fi>
+ */
+
+#include "pb_common.h"
+
+bool pb_field_iter_begin(pb_field_iter_t *iter, const pb_field_t *fields, void *dest_struct)
+{
+ iter->start = fields;
+ iter->pos = fields;
+ iter->required_field_index = 0;
+ iter->dest_struct = dest_struct;
+ iter->pData = (char*)dest_struct + iter->pos->data_offset;
+ iter->pSize = (char*)iter->pData + iter->pos->size_offset;
+
+ return (iter->pos->tag != 0);
+}
+
+bool pb_field_iter_next(pb_field_iter_t *iter)
+{
+ const pb_field_t *prev_field = iter->pos;
+
+ if (prev_field->tag == 0)
+ {
+ /* Handle empty message types, where the first field is already the terminator.
+ * In other cases, the iter->pos never points to the terminator. */
+ return false;
+ }
+
+ iter->pos++;
+
+ if (iter->pos->tag == 0)
+ {
+ /* Wrapped back to beginning, reinitialize */
+ (void)pb_field_iter_begin(iter, iter->start, iter->dest_struct);
+ return false;
+ }
+ else
+ {
+ /* Increment the pointers based on previous field size */
+ size_t prev_size = prev_field->data_size;
+
+ if (PB_HTYPE(prev_field->type) == PB_HTYPE_ONEOF &&
+ PB_HTYPE(iter->pos->type) == PB_HTYPE_ONEOF &&
+ iter->pos->data_offset == PB_SIZE_MAX)
+ {
+ /* Don't advance pointers inside unions */
+ return true;
+ }
+ else if (PB_ATYPE(prev_field->type) == PB_ATYPE_STATIC &&
+ PB_HTYPE(prev_field->type) == PB_HTYPE_REPEATED)
+ {
+ /* In static arrays, the data_size tells the size of a single entry and
+ * array_size is the number of entries */
+ prev_size *= prev_field->array_size;
+ }
+ else if (PB_ATYPE(prev_field->type) == PB_ATYPE_POINTER)
+ {
+ /* Pointer fields always have a constant size in the main structure.
+ * The data_size only applies to the dynamically allocated area. */
+ prev_size = sizeof(void*);
+ }
+
+ if (PB_HTYPE(prev_field->type) == PB_HTYPE_REQUIRED)
+ {
+ /* Count the required fields, in order to check their presence in the
+ * decoder. */
+ iter->required_field_index++;
+ }
+
+ iter->pData = (char*)iter->pData + prev_size + iter->pos->data_offset;
+ iter->pSize = (char*)iter->pData + iter->pos->size_offset;
+ return true;
+ }
+}
+
+bool pb_field_iter_find(pb_field_iter_t *iter, uint32_t tag)
+{
+ const pb_field_t *start = iter->pos;
+
+ do {
+ if (iter->pos->tag == tag &&
+ PB_LTYPE(iter->pos->type) != PB_LTYPE_EXTENSION)
+ {
+ /* Found the wanted field */
+ return true;
+ }
+
+ (void)pb_field_iter_next(iter);
+ } while (iter->pos != start);
+
+ /* Searched all the way back to start, and found nothing. */
+ return false;
+}
+
+
diff --git a/src/lib/nanopb/pb_decode.cc b/src/lib/nanopb/pb_decode.cc
new file mode 100644
index 0000000..d08259c
--- /dev/null
+++ b/src/lib/nanopb/pb_decode.cc
@@ -0,0 +1,1528 @@
+/* pb_decode.c -- decode a protobuf using minimal resources
+ *
+ * 2011 Petteri Aimonen <jpa@kapsi.fi>
+ */
+
+/* Use the GCC warn_unused_result attribute to check that all return values
+ * are propagated correctly. On other compilers and gcc before 3.4.0 just
+ * ignore the annotation.
+ */
+#if !defined(__GNUC__) || ( __GNUC__ < 3) || (__GNUC__ == 3 && __GNUC_MINOR__ < 4)
+ #define checkreturn
+#else
+ #define checkreturn __attribute__((warn_unused_result))
+#endif
+
+#include "pb.h"
+#include "pb_decode.h"
+#include "pb_common.h"
+
+/**************************************
+ * Declarations internal to this file *
+ **************************************/
+
+typedef bool (*pb_decoder_t)(pb_istream_t *stream, const pb_field_t *field, void *dest) checkreturn;
+
+static bool checkreturn buf_read(pb_istream_t *stream, pb_byte_t *buf, size_t count);
+static bool checkreturn read_raw_value(pb_istream_t *stream, pb_wire_type_t wire_type, pb_byte_t *buf, size_t *size);
+static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *iter);
+static bool checkreturn decode_callback_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *iter);
+static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *iter);
+static void iter_from_extension(pb_field_iter_t *iter, pb_extension_t *extension);
+static bool checkreturn default_extension_decoder(pb_istream_t *stream, pb_extension_t *extension, uint32_t tag, pb_wire_type_t wire_type);
+static bool checkreturn decode_extension(pb_istream_t *stream, uint32_t tag, pb_wire_type_t wire_type, pb_field_iter_t *iter);
+static bool checkreturn find_extension_field(pb_field_iter_t *iter);
+static void pb_field_set_to_default(pb_field_iter_t *iter);
+static void pb_message_set_to_defaults(const pb_field_t fields[], void *dest_struct);
+static bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_t *field, void *dest);
+static bool checkreturn pb_decode_varint32_eof(pb_istream_t *stream, uint32_t *dest, bool *eof);
+static bool checkreturn pb_dec_uvarint(pb_istream_t *stream, const pb_field_t *field, void *dest);
+static bool checkreturn pb_dec_svarint(pb_istream_t *stream, const pb_field_t *field, void *dest);
+static bool checkreturn pb_dec_fixed32(pb_istream_t *stream, const pb_field_t *field, void *dest);
+static bool checkreturn pb_dec_fixed64(pb_istream_t *stream, const pb_field_t *field, void *dest);
+static bool checkreturn pb_dec_bytes(pb_istream_t *stream, const pb_field_t *field, void *dest);
+static bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_t *field, void *dest);
+static bool checkreturn pb_dec_submessage(pb_istream_t *stream, const pb_field_t *field, void *dest);
+static bool checkreturn pb_dec_fixed_length_bytes(pb_istream_t *stream, const pb_field_t *field, void *dest);
+static bool checkreturn pb_skip_varint(pb_istream_t *stream);
+static bool checkreturn pb_skip_string(pb_istream_t *stream);
+
+#ifdef PB_ENABLE_MALLOC
+static bool checkreturn allocate_field(pb_istream_t *stream, void *pData, size_t data_size, size_t array_size);
+static bool checkreturn pb_release_union_field(pb_istream_t *stream, pb_field_iter_t *iter);
+static void pb_release_single_field(const pb_field_iter_t *iter);
+#endif
+
+#ifdef PB_WITHOUT_64BIT
+#define pb_int64_t int32_t
+#define pb_uint64_t uint32_t
+#else
+#define pb_int64_t int64_t
+#define pb_uint64_t uint64_t
+#endif
+
+/* --- Function pointers to field decoders ---
+ * Order in the array must match pb_action_t LTYPE numbering.
+ */
+static const pb_decoder_t PB_DECODERS[PB_LTYPES_COUNT] = {
+ &pb_dec_varint,
+ &pb_dec_uvarint,
+ &pb_dec_svarint,
+ &pb_dec_fixed32,
+ &pb_dec_fixed64,
+
+ &pb_dec_bytes,
+ &pb_dec_string,
+ &pb_dec_submessage,
+ NULL, /* extensions */
+ &pb_dec_fixed_length_bytes
+};
+
+/*******************************
+ * pb_istream_t implementation *
+ *******************************/
+
+static bool checkreturn buf_read(pb_istream_t *stream, pb_byte_t *buf, size_t count)
+{
+ size_t i;
+ const pb_byte_t *source = (const pb_byte_t*)stream->state;
+ stream->state = (pb_byte_t*)stream->state + count;
+
+ if (buf != NULL)
+ {
+ for (i = 0; i < count; i++)
+ buf[i] = source[i];
+ }
+
+ return true;
+}
+
+bool checkreturn pb_read(pb_istream_t *stream, pb_byte_t *buf, size_t count)
+{
+#ifndef PB_BUFFER_ONLY
+ if (buf == NULL && stream->callback != buf_read)
+ {
+ /* Skip input bytes */
+ pb_byte_t tmp[16];
+ while (count > 16)
+ {
+ if (!pb_read(stream, tmp, 16))
+ return false;
+
+ count -= 16;
+ }
+
+ return pb_read(stream, tmp, count);
+ }
+#endif
+
+ if (stream->bytes_left < count)
+ PB_RETURN_ERROR(stream, "end-of-stream");
+
+#ifndef PB_BUFFER_ONLY
+ if (!stream->callback(stream, buf, count))
+ PB_RETURN_ERROR(stream, "io error");
+#else
+ if (!buf_read(stream, buf, count))
+ return false;
+#endif
+
+ stream->bytes_left -= count;
+ return true;
+}
+
+/* Read a single byte from input stream. buf may not be NULL.
+ * This is an optimization for the varint decoding. */
+static bool checkreturn pb_readbyte(pb_istream_t *stream, pb_byte_t *buf)
+{
+ if (stream->bytes_left == 0)
+ PB_RETURN_ERROR(stream, "end-of-stream");
+
+#ifndef PB_BUFFER_ONLY
+ if (!stream->callback(stream, buf, 1))
+ PB_RETURN_ERROR(stream, "io error");
+#else
+ *buf = *(const pb_byte_t*)stream->state;
+ stream->state = (pb_byte_t*)stream->state + 1;
+#endif
+
+ stream->bytes_left--;
+
+ return true;
+}
+
+pb_istream_t pb_istream_from_buffer(const pb_byte_t *buf, size_t bufsize)
+{
+ pb_istream_t stream;
+ /* Cast away the const from buf without a compiler error. We are
+ * careful to use it only in a const manner in the callbacks.
+ */
+ union {
+ void *state;
+ const void *c_state;
+ } state;
+#ifdef PB_BUFFER_ONLY
+ stream.callback = NULL;
+#else
+ stream.callback = &buf_read;
+#endif
+ state.c_state = buf;
+ stream.state = state.state;
+ stream.bytes_left = bufsize;
+#ifndef PB_NO_ERRMSG
+ stream.errmsg = NULL;
+#endif
+ return stream;
+}
+
+/********************
+ * Helper functions *
+ ********************/
+
+static bool checkreturn pb_decode_varint32_eof(pb_istream_t *stream, uint32_t *dest, bool *eof)
+{
+ pb_byte_t byte;
+ uint32_t result;
+
+ if (!pb_readbyte(stream, &byte))
+ {
+ if (stream->bytes_left == 0)
+ {
+ if (eof)
+ {
+ *eof = true;
+ }
+ }
+
+ return false;
+ }
+
+ if ((byte & 0x80) == 0)
+ {
+ /* Quick case, 1 byte value */
+ result = byte;
+ }
+ else
+ {
+ /* Multibyte case */
+ uint_fast8_t bitpos = 7;
+ result = byte & 0x7F;
+
+ do
+ {
+ if (!pb_readbyte(stream, &byte))
+ return false;
+
+ if (bitpos >= 32)
+ {
+ /* Note: The varint could have trailing 0x80 bytes, or 0xFF for negative. */
+ uint8_t sign_extension = (bitpos < 63) ? 0xFF : 0x01;
+
+ if ((byte & 0x7F) != 0x00 && ((result >> 31) == 0 || byte != sign_extension))
+ {
+ PB_RETURN_ERROR(stream, "varint overflow");
+ }
+ }
+ else
+ {
+ result |= (uint32_t)(byte & 0x7F) << bitpos;
+ }
+ bitpos = (uint_fast8_t)(bitpos + 7);
+ } while (byte & 0x80);
+
+ if (bitpos == 35 && (byte & 0x70) != 0)
+ {
+ /* The last byte was at bitpos=28, so only bottom 4 bits fit. */
+ PB_RETURN_ERROR(stream, "varint overflow");
+ }
+ }
+
+ *dest = result;
+ return true;
+}
+
+bool checkreturn pb_decode_varint32(pb_istream_t *stream, uint32_t *dest)
+{
+ return pb_decode_varint32_eof(stream, dest, NULL);
+}
+
+#ifndef PB_WITHOUT_64BIT
+bool checkreturn pb_decode_varint(pb_istream_t *stream, uint64_t *dest)
+{
+ pb_byte_t byte;
+ uint_fast8_t bitpos = 0;
+ uint64_t result = 0;
+
+ do
+ {
+ if (bitpos >= 64)
+ PB_RETURN_ERROR(stream, "varint overflow");
+
+ if (!pb_readbyte(stream, &byte))
+ return false;
+
+ result |= (uint64_t)(byte & 0x7F) << bitpos;
+ bitpos = (uint_fast8_t)(bitpos + 7);
+ } while (byte & 0x80);
+
+ *dest = result;
+ return true;
+}
+#endif
+
+bool checkreturn pb_skip_varint(pb_istream_t *stream)
+{
+ pb_byte_t byte;
+ do
+ {
+ if (!pb_read(stream, &byte, 1))
+ return false;
+ } while (byte & 0x80);
+ return true;
+}
+
+bool checkreturn pb_skip_string(pb_istream_t *stream)
+{
+ uint32_t length;
+ if (!pb_decode_varint32(stream, &length))
+ return false;
+
+ return pb_read(stream, NULL, length);
+}
+
+bool checkreturn pb_decode_tag(pb_istream_t *stream, pb_wire_type_t *wire_type, uint32_t *tag, bool *eof)
+{
+ uint32_t temp;
+ *eof = false;
+ *wire_type = (pb_wire_type_t) 0;
+ *tag = 0;
+
+ if (!pb_decode_varint32_eof(stream, &temp, eof))
+ {
+ return false;
+ }
+
+ if (temp == 0)
+ {
+ *eof = true; /* Special feature: allow 0-terminated messages. */
+ return false;
+ }
+
+ *tag = temp >> 3;
+ *wire_type = (pb_wire_type_t)(temp & 7);
+ return true;
+}
+
+bool checkreturn pb_skip_field(pb_istream_t *stream, pb_wire_type_t wire_type)
+{
+ switch (wire_type)
+ {
+ case PB_WT_VARINT: return pb_skip_varint(stream);
+ case PB_WT_64BIT: return pb_read(stream, NULL, 8);
+ case PB_WT_STRING: return pb_skip_string(stream);
+ case PB_WT_32BIT: return pb_read(stream, NULL, 4);
+ default: PB_RETURN_ERROR(stream, "invalid wire_type");
+ }
+}
+
+/* Read a raw value to buffer, for the purpose of passing it to callback as
+ * a substream. Size is maximum size on call, and actual size on return.
+ */
+static bool checkreturn read_raw_value(pb_istream_t *stream, pb_wire_type_t wire_type, pb_byte_t *buf, size_t *size)
+{
+ size_t max_size = *size;
+ switch (wire_type)
+ {
+ case PB_WT_VARINT:
+ *size = 0;
+ do
+ {
+ (*size)++;
+ if (*size > max_size) return false;
+ if (!pb_read(stream, buf, 1)) return false;
+ } while (*buf++ & 0x80);
+ return true;
+
+ case PB_WT_64BIT:
+ *size = 8;
+ return pb_read(stream, buf, 8);
+
+ case PB_WT_32BIT:
+ *size = 4;
+ return pb_read(stream, buf, 4);
+
+ case PB_WT_STRING:
+ /* Calling read_raw_value with a PB_WT_STRING is an error.
+ * Explicitly handle this case and fallthrough to default to avoid
+ * compiler warnings.
+ */
+
+ default: PB_RETURN_ERROR(stream, "invalid wire_type");
+ }
+}
+
+/* Decode string length from stream and return a substream with limited length.
+ * Remember to close the substream using pb_close_string_substream().
+ */
+bool checkreturn pb_make_string_substream(pb_istream_t *stream, pb_istream_t *substream)
+{
+ uint32_t size;
+ if (!pb_decode_varint32(stream, &size))
+ return false;
+
+ *substream = *stream;
+ if (substream->bytes_left < size)
+ PB_RETURN_ERROR(stream, "parent stream too short");
+
+ substream->bytes_left = size;
+ stream->bytes_left -= size;
+ return true;
+}
+
+bool checkreturn pb_close_string_substream(pb_istream_t *stream, pb_istream_t *substream)
+{
+ if (substream->bytes_left) {
+ if (!pb_read(substream, NULL, substream->bytes_left))
+ return false;
+ }
+
+ stream->state = substream->state;
+
+#ifndef PB_NO_ERRMSG
+ stream->errmsg = substream->errmsg;
+#endif
+ return true;
+}
+
+/*************************
+ * Decode a single field *
+ *************************/
+
+static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *iter)
+{
+ pb_type_t type;
+ pb_decoder_t func;
+
+ type = iter->pos->type;
+ func = PB_DECODERS[PB_LTYPE(type)];
+
+ switch (PB_HTYPE(type))
+ {
+ case PB_HTYPE_REQUIRED:
+ return func(stream, iter->pos, iter->pData);
+
+ case PB_HTYPE_OPTIONAL:
+ if (iter->pSize != iter->pData)
+ *(bool*)iter->pSize = true;
+ return func(stream, iter->pos, iter->pData);
+
+ case PB_HTYPE_REPEATED:
+ if (wire_type == PB_WT_STRING
+ && PB_LTYPE(type) <= PB_LTYPE_LAST_PACKABLE)
+ {
+ /* Packed array */
+ bool status = true;
+ pb_size_t *size = (pb_size_t*)iter->pSize;
+
+ pb_istream_t substream;
+ if (!pb_make_string_substream(stream, &substream))
+ return false;
+
+ while (substream.bytes_left > 0 && *size < iter->pos->array_size)
+ {
+ void *pItem = (char*)iter->pData + iter->pos->data_size * (*size);
+ if (!func(&substream, iter->pos, pItem))
+ {
+ status = false;
+ break;
+ }
+ (*size)++;
+ }
+
+ if (substream.bytes_left != 0)
+ PB_RETURN_ERROR(stream, "array overflow");
+ if (!pb_close_string_substream(stream, &substream))
+ return false;
+
+ return status;
+ }
+ else
+ {
+ /* Repeated field */
+ pb_size_t *size = (pb_size_t*)iter->pSize;
+ char *pItem = (char*)iter->pData + iter->pos->data_size * (*size);
+
+ if ((*size)++ >= iter->pos->array_size)
+ PB_RETURN_ERROR(stream, "array overflow");
+
+ return func(stream, iter->pos, pItem);
+ }
+
+ case PB_HTYPE_ONEOF:
+ *(pb_size_t*)iter->pSize = iter->pos->tag;
+ if (PB_LTYPE(type) == PB_LTYPE_SUBMESSAGE)
+ {
+ /* We memset to zero so that any callbacks are set to NULL.
+ * Then set any default values. */
+ memset(iter->pData, 0, iter->pos->data_size);
+ pb_message_set_to_defaults((const pb_field_t*)iter->pos->ptr, iter->pData);
+ }
+ return func(stream, iter->pos, iter->pData);
+
+ default:
+ PB_RETURN_ERROR(stream, "invalid field type");
+ }
+}
+
+#ifdef PB_ENABLE_MALLOC
+/* Allocate storage for the field and store the pointer at iter->pData.
+ * array_size is the number of entries to reserve in an array.
+ * Zero size is not allowed, use pb_free() for releasing.
+ */
+static bool checkreturn allocate_field(pb_istream_t *stream, void *pData, size_t data_size, size_t array_size)
+{
+ void *ptr = *(void**)pData;
+
+ if (data_size == 0 || array_size == 0)
+ PB_RETURN_ERROR(stream, "invalid size");
+
+ /* Check for multiplication overflows.
+ * This code avoids the costly division if the sizes are small enough.
+ * Multiplication is safe as long as only half of bits are set
+ * in either multiplicand.
+ */
+ {
+ const size_t check_limit = (size_t)1 << (sizeof(size_t) * 4);
+ if (data_size >= check_limit || array_size >= check_limit)
+ {
+ const size_t size_max = (size_t)-1;
+ if (size_max / array_size < data_size)
+ {
+ PB_RETURN_ERROR(stream, "size too large");
+ }
+ }
+ }
+
+ /* Allocate new or expand previous allocation */
+ /* Note: on failure the old pointer will remain in the structure,
+ * the message must be freed by caller also on error return. */
+ ptr = pb_realloc(ptr, array_size * data_size);
+ if (ptr == NULL)
+ PB_RETURN_ERROR(stream, "realloc failed");
+
+ *(void**)pData = ptr;
+ return true;
+}
+
+/* Clear a newly allocated item in case it contains a pointer, or is a submessage. */
+static void initialize_pointer_field(void *pItem, pb_field_iter_t *iter)
+{
+ if (PB_LTYPE(iter->pos->type) == PB_LTYPE_STRING ||
+ PB_LTYPE(iter->pos->type) == PB_LTYPE_BYTES)
+ {
+ *(void**)pItem = NULL;
+ }
+ else if (PB_LTYPE(iter->pos->type) == PB_LTYPE_SUBMESSAGE)
+ {
+ /* We memset to zero so that any callbacks are set to NULL.
+ * Then set any default values. */
+ memset(pItem, 0, iter->pos->data_size);
+ pb_message_set_to_defaults((const pb_field_t *) iter->pos->ptr, pItem);
+ }
+}
+#endif
+
+static bool checkreturn decode_pointer_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *iter)
+{
+#ifndef PB_ENABLE_MALLOC
+ PB_UNUSED(wire_type);
+ PB_UNUSED(iter);
+ PB_RETURN_ERROR(stream, "no malloc support");
+#else
+ pb_type_t type;
+ pb_decoder_t func;
+
+ type = iter->pos->type;
+ func = PB_DECODERS[PB_LTYPE(type)];
+
+ switch (PB_HTYPE(type))
+ {
+ case PB_HTYPE_REQUIRED:
+ case PB_HTYPE_OPTIONAL:
+ case PB_HTYPE_ONEOF:
+ if (PB_LTYPE(type) == PB_LTYPE_SUBMESSAGE &&
+ *(void**)iter->pData != NULL)
+ {
+ /* Duplicate field, have to release the old allocation first. */
+ pb_release_single_field(iter);
+ }
+
+ if (PB_HTYPE(type) == PB_HTYPE_ONEOF)
+ {
+ *(pb_size_t*)iter->pSize = iter->pos->tag;
+ }
+
+ if (PB_LTYPE(type) == PB_LTYPE_STRING ||
+ PB_LTYPE(type) == PB_LTYPE_BYTES)
+ {
+ return func(stream, iter->pos, iter->pData);
+ }
+ else
+ {
+ if (!allocate_field(stream, iter->pData, iter->pos->data_size, 1))
+ return false;
+
+ initialize_pointer_field(*(void**)iter->pData, iter);
+ return func(stream, iter->pos, *(void**)iter->pData);
+ }
+
+ case PB_HTYPE_REPEATED:
+ if (wire_type == PB_WT_STRING
+ && PB_LTYPE(type) <= PB_LTYPE_LAST_PACKABLE)
+ {
+ /* Packed array, multiple items come in at once. */
+ bool status = true;
+ pb_size_t *size = (pb_size_t*)iter->pSize;
+ size_t allocated_size = *size;
+ void *pItem;
+ pb_istream_t substream;
+
+ if (!pb_make_string_substream(stream, &substream))
+ return false;
+
+ while (substream.bytes_left)
+ {
+ if ((size_t)*size + 1 > allocated_size)
+ {
+ /* Allocate more storage. This tries to guess the
+ * number of remaining entries. Round the division
+ * upwards. */
+ allocated_size += (substream.bytes_left - 1) / iter->pos->data_size + 1;
+
+ if (!allocate_field(&substream, iter->pData, iter->pos->data_size, allocated_size))
+ {
+ status = false;
+ break;
+ }
+ }
+
+ /* Decode the array entry */
+ pItem = *(char**)iter->pData + iter->pos->data_size * (*size);
+ initialize_pointer_field(pItem, iter);
+ if (!func(&substream, iter->pos, pItem))
+ {
+ status = false;
+ break;
+ }
+
+ if (*size == PB_SIZE_MAX)
+ {
+#ifndef PB_NO_ERRMSG
+ stream->errmsg = "too many array entries";
+#endif
+ status = false;
+ break;
+ }
+
+ (*size)++;
+ }
+ if (!pb_close_string_substream(stream, &substream))
+ return false;
+
+ return status;
+ }
+ else
+ {
+ /* Normal repeated field, i.e. only one item at a time. */
+ pb_size_t *size = (pb_size_t*)iter->pSize;
+ void *pItem;
+
+ if (*size == PB_SIZE_MAX)
+ PB_RETURN_ERROR(stream, "too many array entries");
+
+ (*size)++;
+ if (!allocate_field(stream, iter->pData, iter->pos->data_size, *size))
+ return false;
+
+ pItem = *(char**)iter->pData + iter->pos->data_size * (*size - 1);
+ initialize_pointer_field(pItem, iter);
+ return func(stream, iter->pos, pItem);
+ }
+
+ default:
+ PB_RETURN_ERROR(stream, "invalid field type");
+ }
+#endif
+}
+
+static bool checkreturn decode_callback_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *iter)
+{
+ pb_callback_t *pCallback = (pb_callback_t*)iter->pData;
+#ifdef PB_OLD_CALLBACK_STYLE
+ void *arg;
+#else
+ void **arg;
+#endif
+
+ if (pCallback == NULL || pCallback->funcs.decode == NULL)
+ return pb_skip_field(stream, wire_type);
+
+#ifdef PB_OLD_CALLBACK_STYLE
+ arg = pCallback->arg;
+#else
+ arg = &(pCallback->arg);
+#endif
+
+ if (wire_type == PB_WT_STRING)
+ {
+ pb_istream_t substream;
+
+ if (!pb_make_string_substream(stream, &substream))
+ return false;
+
+ do
+ {
+ if (!pCallback->funcs.decode(&substream, iter->pos, arg))
+ PB_RETURN_ERROR(stream, "callback failed");
+ } while (substream.bytes_left);
+
+ if (!pb_close_string_substream(stream, &substream))
+ return false;
+
+ return true;
+ }
+ else
+ {
+ /* Copy the single scalar value to stack.
+ * This is required so that we can limit the stream length,
+ * which in turn allows to use same callback for packed and
+ * not-packed fields. */
+ pb_istream_t substream;
+ pb_byte_t buffer[10];
+ size_t size = sizeof(buffer);
+
+ if (!read_raw_value(stream, wire_type, buffer, &size))
+ return false;
+ substream = pb_istream_from_buffer(buffer, size);
+
+ return pCallback->funcs.decode(&substream, iter->pos, arg);
+ }
+}
+
+static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *iter)
+{
+#ifdef PB_ENABLE_MALLOC
+ /* When decoding an oneof field, check if there is old data that must be
+ * released first. */
+ if (PB_HTYPE(iter->pos->type) == PB_HTYPE_ONEOF)
+ {
+ if (!pb_release_union_field(stream, iter))
+ return false;
+ }
+#endif
+
+ switch (PB_ATYPE(iter->pos->type))
+ {
+ case PB_ATYPE_STATIC:
+ return decode_static_field(stream, wire_type, iter);
+
+ case PB_ATYPE_POINTER:
+ return decode_pointer_field(stream, wire_type, iter);
+
+ case PB_ATYPE_CALLBACK:
+ return decode_callback_field(stream, wire_type, iter);
+
+ default:
+ PB_RETURN_ERROR(stream, "invalid field type");
+ }
+}
+
+static void iter_from_extension(pb_field_iter_t *iter, pb_extension_t *extension)
+{
+ /* Fake a field iterator for the extension field.
+ * It is not actually safe to advance this iterator, but decode_field
+ * will not even try to. */
+ const pb_field_t *field = (const pb_field_t*)extension->type->arg;
+ (void)pb_field_iter_begin(iter, field, extension->dest);
+ iter->pData = extension->dest;
+ iter->pSize = &extension->found;
+
+ if (PB_ATYPE(field->type) == PB_ATYPE_POINTER)
+ {
+ /* For pointer extensions, the pointer is stored directly
+ * in the extension structure. This avoids having an extra
+ * indirection. */
+ iter->pData = &extension->dest;
+ }
+}
+
+/* Default handler for extension fields. Expects a pb_field_t structure
+ * in extension->type->arg. */
+static bool checkreturn default_extension_decoder(pb_istream_t *stream,
+ pb_extension_t *extension, uint32_t tag, pb_wire_type_t wire_type)
+{
+ const pb_field_t *field = (const pb_field_t*)extension->type->arg;
+ pb_field_iter_t iter;
+
+ if (field->tag != tag)
+ return true;
+
+ iter_from_extension(&iter, extension);
+ extension->found = true;
+ return decode_field(stream, wire_type, &iter);
+}
+
+/* Try to decode an unknown field as an extension field. Tries each extension
+ * decoder in turn, until one of them handles the field or loop ends. */
+static bool checkreturn decode_extension(pb_istream_t *stream,
+ uint32_t tag, pb_wire_type_t wire_type, pb_field_iter_t *iter)
+{
+ pb_extension_t *extension = *(pb_extension_t* const *)iter->pData;
+ size_t pos = stream->bytes_left;
+
+ while (extension != NULL && pos == stream->bytes_left)
+ {
+ bool status;
+ if (extension->type->decode)
+ status = extension->type->decode(stream, extension, tag, wire_type);
+ else
+ status = default_extension_decoder(stream, extension, tag, wire_type);
+
+ if (!status)
+ return false;
+
+ extension = extension->next;
+ }
+
+ return true;
+}
+
+/* Step through the iterator until an extension field is found or until all
+ * entries have been checked. There can be only one extension field per
+ * message. Returns false if no extension field is found. */
+static bool checkreturn find_extension_field(pb_field_iter_t *iter)
+{
+ const pb_field_t *start = iter->pos;
+
+ do {
+ if (PB_LTYPE(iter->pos->type) == PB_LTYPE_EXTENSION)
+ return true;
+ (void)pb_field_iter_next(iter);
+ } while (iter->pos != start);
+
+ return false;
+}
+
+/* Initialize message fields to default values, recursively */
+static void pb_field_set_to_default(pb_field_iter_t *iter)
+{
+ pb_type_t type;
+ type = iter->pos->type;
+
+ if (PB_LTYPE(type) == PB_LTYPE_EXTENSION)
+ {
+ pb_extension_t *ext = *(pb_extension_t* const *)iter->pData;
+ while (ext != NULL)
+ {
+ pb_field_iter_t ext_iter;
+ ext->found = false;
+ iter_from_extension(&ext_iter, ext);
+ pb_field_set_to_default(&ext_iter);
+ ext = ext->next;
+ }
+ }
+ else if (PB_ATYPE(type) == PB_ATYPE_STATIC)
+ {
+ bool init_data = true;
+ if (PB_HTYPE(type) == PB_HTYPE_OPTIONAL && iter->pSize != iter->pData)
+ {
+ /* Set has_field to false. Still initialize the optional field
+ * itself also. */
+ *(bool*)iter->pSize = false;
+ }
+ else if (PB_HTYPE(type) == PB_HTYPE_REPEATED ||
+ PB_HTYPE(type) == PB_HTYPE_ONEOF)
+ {
+ /* REPEATED: Set array count to 0, no need to initialize contents.
+ ONEOF: Set which_field to 0. */
+ *(pb_size_t*)iter->pSize = 0;
+ init_data = false;
+ }
+
+ if (init_data)
+ {
+ if (PB_LTYPE(iter->pos->type) == PB_LTYPE_SUBMESSAGE)
+ {
+ /* Initialize submessage to defaults */
+ pb_message_set_to_defaults((const pb_field_t *) iter->pos->ptr, iter->pData);
+ }
+ else if (iter->pos->ptr != NULL)
+ {
+ /* Initialize to default value */
+ memcpy(iter->pData, iter->pos->ptr, iter->pos->data_size);
+ }
+ else
+ {
+ /* Initialize to zeros */
+ memset(iter->pData, 0, iter->pos->data_size);
+ }
+ }
+ }
+ else if (PB_ATYPE(type) == PB_ATYPE_POINTER)
+ {
+ /* Initialize the pointer to NULL. */
+ *(void**)iter->pData = NULL;
+
+ /* Initialize array count to 0. */
+ if (PB_HTYPE(type) == PB_HTYPE_REPEATED ||
+ PB_HTYPE(type) == PB_HTYPE_ONEOF)
+ {
+ *(pb_size_t*)iter->pSize = 0;
+ }
+ }
+ else if (PB_ATYPE(type) == PB_ATYPE_CALLBACK)
+ {
+ /* Don't overwrite callback */
+ }
+}
+
+static void pb_message_set_to_defaults(const pb_field_t fields[], void *dest_struct)
+{
+ pb_field_iter_t iter;
+
+ if (!pb_field_iter_begin(&iter, fields, dest_struct))
+ return; /* Empty message type */
+
+ do
+ {
+ pb_field_set_to_default(&iter);
+ } while (pb_field_iter_next(&iter));
+}
+
+/*********************
+ * Decode all fields *
+ *********************/
+
+bool checkreturn pb_decode_noinit(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct)
+{
+ uint32_t fields_seen[(PB_MAX_REQUIRED_FIELDS + 31) / 32] = {0, 0};
+ const uint32_t allbits = ~(uint32_t)0;
+ uint32_t extension_range_start = 0;
+ pb_field_iter_t iter;
+
+ /* 'fixed_count_field' and 'fixed_count_size' track position of a repeated fixed
+ * count field. This can only handle _one_ repeated fixed count field that
+ * is unpacked and unordered among other (non repeated fixed count) fields.
+ */
+ const pb_field_t *fixed_count_field = NULL;
+ pb_size_t fixed_count_size = 0;
+
+ /* Return value ignored, as empty message types will be correctly handled by
+ * pb_field_iter_find() anyway. */
+ (void)pb_field_iter_begin(&iter, fields, dest_struct);
+
+ while (stream->bytes_left)
+ {
+ uint32_t tag;
+ pb_wire_type_t wire_type;
+ bool eof;
+
+ if (!pb_decode_tag(stream, &wire_type, &tag, &eof))
+ {
+ if (eof)
+ break;
+ else
+ return false;
+ }
+
+ if (!pb_field_iter_find(&iter, tag))
+ {
+ /* No match found, check if it matches an extension. */
+ if (tag >= extension_range_start)
+ {
+ if (!find_extension_field(&iter))
+ extension_range_start = (uint32_t)-1;
+ else
+ extension_range_start = iter.pos->tag;
+
+ if (tag >= extension_range_start)
+ {
+ size_t pos = stream->bytes_left;
+
+ if (!decode_extension(stream, tag, wire_type, &iter))
+ return false;
+
+ if (pos != stream->bytes_left)
+ {
+ /* The field was handled */
+ continue;
+ }
+ }
+ }
+
+ /* No match found, skip data */
+ if (!pb_skip_field(stream, wire_type))
+ return false;
+ continue;
+ }
+
+ /* If a repeated fixed count field was found, get size from
+ * 'fixed_count_field' as there is no counter contained in the struct.
+ */
+ if (PB_HTYPE(iter.pos->type) == PB_HTYPE_REPEATED
+ && iter.pSize == iter.pData)
+ {
+ if (fixed_count_field != iter.pos) {
+ /* If the new fixed count field does not match the previous one,
+ * check that the previous one is NULL or that it finished
+ * receiving all the expected data.
+ */
+ if (fixed_count_field != NULL &&
+ fixed_count_size != fixed_count_field->array_size)
+ {
+ PB_RETURN_ERROR(stream, "wrong size for fixed count field");
+ }
+
+ fixed_count_field = iter.pos;
+ fixed_count_size = 0;
+ }
+
+ iter.pSize = &fixed_count_size;
+ }
+
+ if (PB_HTYPE(iter.pos->type) == PB_HTYPE_REQUIRED
+ && iter.required_field_index < PB_MAX_REQUIRED_FIELDS)
+ {
+ uint32_t tmp = ((uint32_t)1 << (iter.required_field_index & 31));
+ fields_seen[iter.required_field_index >> 5] |= tmp;
+ }
+
+ if (!decode_field(stream, wire_type, &iter))
+ return false;
+ }
+
+ /* Check that all elements of the last decoded fixed count field were present. */
+ if (fixed_count_field != NULL &&
+ fixed_count_size != fixed_count_field->array_size)
+ {
+ PB_RETURN_ERROR(stream, "wrong size for fixed count field");
+ }
+
+ /* Check that all required fields were present. */
+ {
+ /* First figure out the number of required fields by
+ * seeking to the end of the field array. Usually we
+ * are already close to end after decoding.
+ */
+ unsigned req_field_count;
+ pb_type_t last_type;
+ unsigned i;
+ do {
+ req_field_count = iter.required_field_index;
+ last_type = iter.pos->type;
+ } while (pb_field_iter_next(&iter));
+
+ /* Fixup if last field was also required. */
+ if (PB_HTYPE(last_type) == PB_HTYPE_REQUIRED && iter.pos->tag != 0)
+ req_field_count++;
+
+ if (req_field_count > PB_MAX_REQUIRED_FIELDS)
+ req_field_count = PB_MAX_REQUIRED_FIELDS;
+
+ if (req_field_count > 0)
+ {
+ /* Check the whole words */
+ for (i = 0; i < (req_field_count >> 5); i++)
+ {
+ if (fields_seen[i] != allbits)
+ PB_RETURN_ERROR(stream, "missing required field");
+ }
+
+ /* Check the remaining bits (if any) */
+ if ((req_field_count & 31) != 0)
+ {
+ if (fields_seen[req_field_count >> 5] !=
+ (allbits >> (32 - (req_field_count & 31))))
+ {
+ PB_RETURN_ERROR(stream, "missing required field");
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+bool checkreturn pb_decode(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct)
+{
+ bool status;
+ pb_message_set_to_defaults(fields, dest_struct);
+ status = pb_decode_noinit(stream, fields, dest_struct);
+
+#ifdef PB_ENABLE_MALLOC
+ if (!status)
+ pb_release(fields, dest_struct);
+#endif
+
+ return status;
+}
+
+bool pb_decode_delimited_noinit(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct)
+{
+ pb_istream_t substream;
+ bool status;
+
+ if (!pb_make_string_substream(stream, &substream))
+ return false;
+
+ status = pb_decode_noinit(&substream, fields, dest_struct);
+
+ if (!pb_close_string_substream(stream, &substream))
+ return false;
+ return status;
+}
+
+bool pb_decode_delimited(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct)
+{
+ pb_istream_t substream;
+ bool status;
+
+ if (!pb_make_string_substream(stream, &substream))
+ return false;
+
+ status = pb_decode(&substream, fields, dest_struct);
+
+ if (!pb_close_string_substream(stream, &substream))
+ return false;
+ return status;
+}
+
+bool pb_decode_nullterminated(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct)
+{
+ /* This behaviour will be separated in nanopb-0.4.0, see issue #278. */
+ return pb_decode(stream, fields, dest_struct);
+}
+
+#ifdef PB_ENABLE_MALLOC
+/* Given an oneof field, if there has already been a field inside this oneof,
+ * release it before overwriting with a different one. */
+static bool pb_release_union_field(pb_istream_t *stream, pb_field_iter_t *iter)
+{
+ pb_size_t old_tag = *(pb_size_t*)iter->pSize; /* Previous which_ value */
+ pb_size_t new_tag = iter->pos->tag; /* New which_ value */
+
+ if (old_tag == 0)
+ return true; /* Ok, no old data in union */
+
+ if (old_tag == new_tag)
+ return true; /* Ok, old data is of same type => merge */
+
+ /* Release old data. The find can fail if the message struct contains
+ * invalid data. */
+ if (!pb_field_iter_find(iter, old_tag))
+ PB_RETURN_ERROR(stream, "invalid union tag");
+
+ pb_release_single_field(iter);
+
+ /* Restore iterator to where it should be.
+ * This shouldn't fail unless the pb_field_t structure is corrupted. */
+ if (!pb_field_iter_find(iter, new_tag))
+ PB_RETURN_ERROR(stream, "iterator error");
+
+ return true;
+}
+
+static void pb_release_single_field(const pb_field_iter_t *iter)
+{
+ pb_type_t type;
+ type = iter->pos->type;
+
+ if (PB_HTYPE(type) == PB_HTYPE_ONEOF)
+ {
+ if (*(pb_size_t*)iter->pSize != iter->pos->tag)
+ return; /* This is not the current field in the union */
+ }
+
+ /* Release anything contained inside an extension or submsg.
+ * This has to be done even if the submsg itself is statically
+ * allocated. */
+ if (PB_LTYPE(type) == PB_LTYPE_EXTENSION)
+ {
+ /* Release fields from all extensions in the linked list */
+ pb_extension_t *ext = *(pb_extension_t**)iter->pData;
+ while (ext != NULL)
+ {
+ pb_field_iter_t ext_iter;
+ iter_from_extension(&ext_iter, ext);
+ pb_release_single_field(&ext_iter);
+ ext = ext->next;
+ }
+ }
+ else if (PB_LTYPE(type) == PB_LTYPE_SUBMESSAGE && PB_ATYPE(type) != PB_ATYPE_CALLBACK)
+ {
+ /* Release fields in submessage or submsg array */
+ void *pItem = iter->pData;
+ pb_size_t count = 1;
+
+ if (PB_ATYPE(type) == PB_ATYPE_POINTER)
+ {
+ pItem = *(void**)iter->pData;
+ }
+
+ if (PB_HTYPE(type) == PB_HTYPE_REPEATED)
+ {
+ if (PB_ATYPE(type) == PB_ATYPE_STATIC && iter->pSize == iter->pData) {
+ /* No _count field so use size of the array */
+ count = iter->pos->array_size;
+ } else {
+ count = *(pb_size_t*)iter->pSize;
+ }
+
+ if (PB_ATYPE(type) == PB_ATYPE_STATIC && count > iter->pos->array_size)
+ {
+ /* Protect against corrupted _count fields */
+ count = iter->pos->array_size;
+ }
+ }
+
+ if (pItem)
+ {
+ while (count--)
+ {
+ pb_release((const pb_field_t*)iter->pos->ptr, pItem);
+ pItem = (char*)pItem + iter->pos->data_size;
+ }
+ }
+ }
+
+ if (PB_ATYPE(type) == PB_ATYPE_POINTER)
+ {
+ if (PB_HTYPE(type) == PB_HTYPE_REPEATED &&
+ (PB_LTYPE(type) == PB_LTYPE_STRING ||
+ PB_LTYPE(type) == PB_LTYPE_BYTES))
+ {
+ /* Release entries in repeated string or bytes array */
+ void **pItem = *(void***)iter->pData;
+ pb_size_t count = *(pb_size_t*)iter->pSize;
+ while (count--)
+ {
+ pb_free(*pItem);
+ *pItem++ = NULL;
+ }
+ }
+
+ if (PB_HTYPE(type) == PB_HTYPE_REPEATED)
+ {
+ /* We are going to release the array, so set the size to 0 */
+ *(pb_size_t*)iter->pSize = 0;
+ }
+
+ /* Release main item */
+ pb_free(*(void**)iter->pData);
+ *(void**)iter->pData = NULL;
+ }
+}
+
+void pb_release(const pb_field_t fields[], void *dest_struct)
+{
+ pb_field_iter_t iter;
+
+ if (!dest_struct)
+ return; /* Ignore NULL pointers, similar to free() */
+
+ if (!pb_field_iter_begin(&iter, fields, dest_struct))
+ return; /* Empty message type */
+
+ do
+ {
+ pb_release_single_field(&iter);
+ } while (pb_field_iter_next(&iter));
+}
+#endif
+
+/* Field decoders */
+
+bool pb_decode_svarint(pb_istream_t *stream, pb_int64_t *dest)
+{
+ pb_uint64_t value;
+ if (!pb_decode_varint(stream, &value))
+ return false;
+
+ if (value & 1)
+ *dest = (pb_int64_t)(~(value >> 1));
+ else
+ *dest = (pb_int64_t)(value >> 1);
+
+ return true;
+}
+
+bool pb_decode_fixed32(pb_istream_t *stream, void *dest)
+{
+ union {
+ uint32_t fixed32;
+ pb_byte_t bytes[4];
+ } u;
+
+ if (!pb_read(stream, u.bytes, 4))
+ return false;
+
+#if defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN && CHAR_BIT == 8
+ /* fast path - if we know that we're on little endian, assign directly */
+ *(uint32_t*)dest = u.fixed32;
+#else
+ *(uint32_t*)dest = ((uint32_t)u.bytes[0] << 0) |
+ ((uint32_t)u.bytes[1] << 8) |
+ ((uint32_t)u.bytes[2] << 16) |
+ ((uint32_t)u.bytes[3] << 24);
+#endif
+ return true;
+}
+
+#ifndef PB_WITHOUT_64BIT
+bool pb_decode_fixed64(pb_istream_t *stream, void *dest)
+{
+ union {
+ uint64_t fixed64;
+ pb_byte_t bytes[8];
+ } u;
+
+ if (!pb_read(stream, u.bytes, 8))
+ return false;
+
+#if defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN && CHAR_BIT == 8
+ /* fast path - if we know that we're on little endian, assign directly */
+ *(uint64_t*)dest = u.fixed64;
+#else
+ *(uint64_t*)dest = ((uint64_t)u.bytes[0] << 0) |
+ ((uint64_t)u.bytes[1] << 8) |
+ ((uint64_t)u.bytes[2] << 16) |
+ ((uint64_t)u.bytes[3] << 24) |
+ ((uint64_t)u.bytes[4] << 32) |
+ ((uint64_t)u.bytes[5] << 40) |
+ ((uint64_t)u.bytes[6] << 48) |
+ ((uint64_t)u.bytes[7] << 56);
+#endif
+ return true;
+}
+#endif
+
+static bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_t *field, void *dest)
+{
+ pb_uint64_t value;
+ pb_int64_t svalue;
+ pb_int64_t clamped;
+ if (!pb_decode_varint(stream, &value))
+ return false;
+
+ /* See issue 97: Google's C++ protobuf allows negative varint values to
+ * be cast as int32_t, instead of the int64_t that should be used when
+ * encoding. Previous nanopb versions had a bug in encoding. In order to
+ * not break decoding of such messages, we cast <=32 bit fields to
+ * int32_t first to get the sign correct.
+ */
+ if (field->data_size == sizeof(pb_int64_t))
+ svalue = (pb_int64_t)value;
+ else
+ svalue = (int32_t)value;
+
+ /* Cast to the proper field size, while checking for overflows */
+ if (field->data_size == sizeof(pb_int64_t))
+ clamped = *(pb_int64_t*)dest = svalue;
+ else if (field->data_size == sizeof(int32_t))
+ clamped = *(int32_t*)dest = (int32_t)svalue;
+ else if (field->data_size == sizeof(int_least16_t))
+ clamped = *(int_least16_t*)dest = (int_least16_t)svalue;
+ else if (field->data_size == sizeof(int_least8_t))
+ clamped = *(int_least8_t*)dest = (int_least8_t)svalue;
+ else
+ PB_RETURN_ERROR(stream, "invalid data_size");
+
+ if (clamped != svalue)
+ PB_RETURN_ERROR(stream, "integer too large");
+
+ return true;
+}
+
+static bool checkreturn pb_dec_uvarint(pb_istream_t *stream, const pb_field_t *field, void *dest)
+{
+ pb_uint64_t value, clamped;
+ if (!pb_decode_varint(stream, &value))
+ return false;
+
+ /* Cast to the proper field size, while checking for overflows */
+ if (field->data_size == sizeof(pb_uint64_t))
+ clamped = *(pb_uint64_t*)dest = value;
+ else if (field->data_size == sizeof(uint32_t))
+ clamped = *(uint32_t*)dest = (uint32_t)value;
+ else if (field->data_size == sizeof(uint_least16_t))
+ clamped = *(uint_least16_t*)dest = (uint_least16_t)value;
+ else if (field->data_size == sizeof(uint_least8_t))
+ clamped = *(uint_least8_t*)dest = (uint_least8_t)value;
+ else
+ PB_RETURN_ERROR(stream, "invalid data_size");
+
+ if (clamped != value)
+ PB_RETURN_ERROR(stream, "integer too large");
+
+ return true;
+}
+
+static bool checkreturn pb_dec_svarint(pb_istream_t *stream, const pb_field_t *field, void *dest)
+{
+ pb_int64_t value, clamped;
+ if (!pb_decode_svarint(stream, &value))
+ return false;
+
+ /* Cast to the proper field size, while checking for overflows */
+ if (field->data_size == sizeof(pb_int64_t))
+ clamped = *(pb_int64_t*)dest = value;
+ else if (field->data_size == sizeof(int32_t))
+ clamped = *(int32_t*)dest = (int32_t)value;
+ else if (field->data_size == sizeof(int_least16_t))
+ clamped = *(int_least16_t*)dest = (int_least16_t)value;
+ else if (field->data_size == sizeof(int_least8_t))
+ clamped = *(int_least8_t*)dest = (int_least8_t)value;
+ else
+ PB_RETURN_ERROR(stream, "invalid data_size");
+
+ if (clamped != value)
+ PB_RETURN_ERROR(stream, "integer too large");
+
+ return true;
+}
+
+static bool checkreturn pb_dec_fixed32(pb_istream_t *stream, const pb_field_t *field, void *dest)
+{
+ PB_UNUSED(field);
+ return pb_decode_fixed32(stream, dest);
+}
+
+static bool checkreturn pb_dec_fixed64(pb_istream_t *stream, const pb_field_t *field, void *dest)
+{
+ PB_UNUSED(field);
+#ifndef PB_WITHOUT_64BIT
+ return pb_decode_fixed64(stream, dest);
+#else
+ PB_UNUSED(dest);
+ PB_RETURN_ERROR(stream, "no 64bit support");
+#endif
+}
+
+static bool checkreturn pb_dec_bytes(pb_istream_t *stream, const pb_field_t *field, void *dest)
+{
+ uint32_t size;
+ size_t alloc_size;
+ pb_bytes_array_t *bdest;
+
+ if (!pb_decode_varint32(stream, &size))
+ return false;
+
+ if (size > PB_SIZE_MAX)
+ PB_RETURN_ERROR(stream, "bytes overflow");
+
+ alloc_size = PB_BYTES_ARRAY_T_ALLOCSIZE(size);
+ if (size > alloc_size)
+ PB_RETURN_ERROR(stream, "size too large");
+
+ if (PB_ATYPE(field->type) == PB_ATYPE_POINTER)
+ {
+#ifndef PB_ENABLE_MALLOC
+ PB_RETURN_ERROR(stream, "no malloc support");
+#else
+ if (!allocate_field(stream, dest, alloc_size, 1))
+ return false;
+ bdest = *(pb_bytes_array_t**)dest;
+#endif
+ }
+ else
+ {
+ if (alloc_size > field->data_size)
+ PB_RETURN_ERROR(stream, "bytes overflow");
+ bdest = (pb_bytes_array_t*)dest;
+ }
+
+ bdest->size = (pb_size_t)size;
+ return pb_read(stream, bdest->bytes, size);
+}
+
+static bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_t *field, void *dest)
+{
+ uint32_t size;
+ size_t alloc_size;
+ bool status;
+ if (!pb_decode_varint32(stream, &size))
+ return false;
+
+ /* Space for null terminator */
+ alloc_size = size + 1;
+
+ if (alloc_size < size)
+ PB_RETURN_ERROR(stream, "size too large");
+
+ if (PB_ATYPE(field->type) == PB_ATYPE_POINTER)
+ {
+#ifndef PB_ENABLE_MALLOC
+ PB_RETURN_ERROR(stream, "no malloc support");
+#else
+ if (!allocate_field(stream, dest, alloc_size, 1))
+ return false;
+ dest = *(void**)dest;
+#endif
+ }
+ else
+ {
+ if (alloc_size > field->data_size)
+ PB_RETURN_ERROR(stream, "string overflow");
+ }
+
+ status = pb_read(stream, (pb_byte_t*)dest, size);
+ *((pb_byte_t*)dest + size) = 0;
+ return status;
+}
+
+static bool checkreturn pb_dec_submessage(pb_istream_t *stream, const pb_field_t *field, void *dest)
+{
+ bool status;
+ pb_istream_t substream;
+ const pb_field_t* submsg_fields = (const pb_field_t*)field->ptr;
+
+ if (!pb_make_string_substream(stream, &substream))
+ return false;
+
+ if (field->ptr == NULL)
+ PB_RETURN_ERROR(stream, "invalid field descriptor");
+
+ /* New array entries need to be initialized, while required and optional
+ * submessages have already been initialized in the top-level pb_decode. */
+ if (PB_HTYPE(field->type) == PB_HTYPE_REPEATED)
+ status = pb_decode(&substream, submsg_fields, dest);
+ else
+ status = pb_decode_noinit(&substream, submsg_fields, dest);
+
+ if (!pb_close_string_substream(stream, &substream))
+ return false;
+ return status;
+}
+
+static bool checkreturn pb_dec_fixed_length_bytes(pb_istream_t *stream, const pb_field_t *field, void *dest)
+{
+ uint32_t size;
+
+ if (!pb_decode_varint32(stream, &size))
+ return false;
+
+ if (size > PB_SIZE_MAX)
+ PB_RETURN_ERROR(stream, "bytes overflow");
+
+ if (size == 0)
+ {
+ /* As a special case, treat empty bytes string as all zeros for fixed_length_bytes. */
+ memset(dest, 0, field->data_size);
+ return true;
+ }
+
+ if (size != field->data_size)
+ PB_RETURN_ERROR(stream, "incorrect fixed length bytes size");
+
+ return pb_read(stream, (pb_byte_t*)dest, field->data_size);
+}
diff --git a/src/lib/nanopb/pb_encode.cc b/src/lib/nanopb/pb_encode.cc
new file mode 100644
index 0000000..f37ceec
--- /dev/null
+++ b/src/lib/nanopb/pb_encode.cc
@@ -0,0 +1,893 @@
+/* pb_encode.c -- encode a protobuf using minimal resources
+ *
+ * 2011 Petteri Aimonen <jpa@kapsi.fi>
+ */
+
+#include "pb.h"
+#include "pb_encode.h"
+#include "pb_common.h"
+
+/* Use the GCC warn_unused_result attribute to check that all return values
+ * are propagated correctly. On other compilers and gcc before 3.4.0 just
+ * ignore the annotation.
+ */
+#if !defined(__GNUC__) || ( __GNUC__ < 3) || (__GNUC__ == 3 && __GNUC_MINOR__ < 4)
+ #define checkreturn
+#else
+ #define checkreturn __attribute__((warn_unused_result))
+#endif
+
+/**************************************
+ * Declarations internal to this file *
+ **************************************/
+typedef bool (*pb_encoder_t)(pb_ostream_t *stream, const pb_field_t *field, const void *src) checkreturn;
+
+static bool checkreturn buf_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count);
+static bool checkreturn encode_array(pb_ostream_t *stream, const pb_field_t *field, const void *pData, size_t count, pb_encoder_t func);
+static bool checkreturn encode_field(pb_ostream_t *stream, const pb_field_t *field, const void *pData);
+static bool checkreturn default_extension_encoder(pb_ostream_t *stream, const pb_extension_t *extension);
+static bool checkreturn encode_extension_field(pb_ostream_t *stream, const pb_field_t *field, const void *pData);
+static void *pb_const_cast(const void *p);
+static bool checkreturn pb_encode_varint_32(pb_ostream_t *stream, uint32_t low, uint32_t high);
+static bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_t *field, const void *src);
+static bool checkreturn pb_enc_uvarint(pb_ostream_t *stream, const pb_field_t *field, const void *src);
+static bool checkreturn pb_enc_svarint(pb_ostream_t *stream, const pb_field_t *field, const void *src);
+static bool checkreturn pb_enc_fixed32(pb_ostream_t *stream, const pb_field_t *field, const void *src);
+static bool checkreturn pb_enc_fixed64(pb_ostream_t *stream, const pb_field_t *field, const void *src);
+static bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_t *field, const void *src);
+static bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_t *field, const void *src);
+static bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_t *field, const void *src);
+static bool checkreturn pb_enc_fixed_length_bytes(pb_ostream_t *stream, const pb_field_t *field, const void *src);
+
+#ifdef PB_WITHOUT_64BIT
+#define pb_int64_t int32_t
+#define pb_uint64_t uint32_t
+#else
+#define pb_int64_t int64_t
+#define pb_uint64_t uint64_t
+#endif
+
+/* --- Function pointers to field encoders ---
+ * Order in the array must match pb_action_t LTYPE numbering.
+ */
+static const pb_encoder_t PB_ENCODERS[PB_LTYPES_COUNT] = {
+ &pb_enc_varint,
+ &pb_enc_uvarint,
+ &pb_enc_svarint,
+ &pb_enc_fixed32,
+ &pb_enc_fixed64,
+
+ &pb_enc_bytes,
+ &pb_enc_string,
+ &pb_enc_submessage,
+ NULL, /* extensions */
+ &pb_enc_fixed_length_bytes
+};
+
+/*******************************
+ * pb_ostream_t implementation *
+ *******************************/
+
+static bool checkreturn buf_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count)
+{
+ size_t i;
+ pb_byte_t *dest = (pb_byte_t*)stream->state;
+ stream->state = dest + count;
+
+ for (i = 0; i < count; i++)
+ dest[i] = buf[i];
+
+ return true;
+}
+
+pb_ostream_t pb_ostream_from_buffer(pb_byte_t *buf, size_t bufsize)
+{
+ pb_ostream_t stream;
+#ifdef PB_BUFFER_ONLY
+ stream.callback = (void*)1; /* Just a marker value */
+#else
+ stream.callback = &buf_write;
+#endif
+ stream.state = buf;
+ stream.max_size = bufsize;
+ stream.bytes_written = 0;
+#ifndef PB_NO_ERRMSG
+ stream.errmsg = NULL;
+#endif
+ return stream;
+}
+
+bool checkreturn pb_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count)
+{
+ if (stream->callback != NULL)
+ {
+ if (stream->bytes_written + count > stream->max_size)
+ PB_RETURN_ERROR(stream, "stream full");
+
+#ifdef PB_BUFFER_ONLY
+ if (!buf_write(stream, buf, count))
+ PB_RETURN_ERROR(stream, "io error");
+#else
+ if (!stream->callback(stream, buf, count))
+ PB_RETURN_ERROR(stream, "io error");
+#endif
+ }
+
+ stream->bytes_written += count;
+ return true;
+}
+
+/*************************
+ * Encode a single field *
+ *************************/
+
+/* Encode a static array. Handles the size calculations and possible packing. */
+static bool checkreturn encode_array(pb_ostream_t *stream, const pb_field_t *field,
+ const void *pData, size_t count, pb_encoder_t func)
+{
+ size_t i;
+ const void *p;
+ size_t size;
+
+ if (count == 0)
+ return true;
+
+ if (PB_ATYPE(field->type) != PB_ATYPE_POINTER && count > field->array_size)
+ PB_RETURN_ERROR(stream, "array max size exceeded");
+
+ /* We always pack arrays if the datatype allows it. */
+ if (PB_LTYPE(field->type) <= PB_LTYPE_LAST_PACKABLE)
+ {
+ if (!pb_encode_tag(stream, PB_WT_STRING, field->tag))
+ return false;
+
+ /* Determine the total size of packed array. */
+ if (PB_LTYPE(field->type) == PB_LTYPE_FIXED32)
+ {
+ size = 4 * count;
+ }
+ else if (PB_LTYPE(field->type) == PB_LTYPE_FIXED64)
+ {
+ size = 8 * count;
+ }
+ else
+ {
+ pb_ostream_t sizestream = PB_OSTREAM_SIZING;
+ p = pData;
+ for (i = 0; i < count; i++)
+ {
+ if (!func(&sizestream, field, p))
+ return false;
+ p = (const char*)p + field->data_size;
+ }
+ size = sizestream.bytes_written;
+ }
+
+ if (!pb_encode_varint(stream, (pb_uint64_t)size))
+ return false;
+
+ if (stream->callback == NULL)
+ return pb_write(stream, NULL, size); /* Just sizing.. */
+
+ /* Write the data */
+ p = pData;
+ for (i = 0; i < count; i++)
+ {
+ if (!func(stream, field, p))
+ return false;
+ p = (const char*)p + field->data_size;
+ }
+ }
+ else
+ {
+ p = pData;
+ for (i = 0; i < count; i++)
+ {
+ if (!pb_encode_tag_for_field(stream, field))
+ return false;
+
+ /* Normally the data is stored directly in the array entries, but
+ * for pointer-type string and bytes fields, the array entries are
+ * actually pointers themselves also. So we have to dereference once
+ * more to get to the actual data. */
+ if (PB_ATYPE(field->type) == PB_ATYPE_POINTER &&
+ (PB_LTYPE(field->type) == PB_LTYPE_STRING ||
+ PB_LTYPE(field->type) == PB_LTYPE_BYTES))
+ {
+ if (!func(stream, field, *(const void* const*)p))
+ return false;
+ }
+ else
+ {
+ if (!func(stream, field, p))
+ return false;
+ }
+ p = (const char*)p + field->data_size;
+ }
+ }
+
+ return true;
+}
+
+/* In proto3, all fields are optional and are only encoded if their value is "non-zero".
+ * This function implements the check for the zero value. */
+static bool pb_check_proto3_default_value(const pb_field_t *field, const void *pData)
+{
+ pb_type_t type = field->type;
+ const void *pSize = (const char*)pData + field->size_offset;
+
+ if (PB_HTYPE(type) == PB_HTYPE_REQUIRED)
+ {
+ /* Required proto2 fields inside proto3 submessage, pretty rare case */
+ return false;
+ }
+ else if (PB_HTYPE(type) == PB_HTYPE_REPEATED)
+ {
+ /* Repeated fields inside proto3 submessage: present if count != 0 */
+ return *(const pb_size_t*)pSize == 0;
+ }
+ else if (PB_HTYPE(type) == PB_HTYPE_ONEOF)
+ {
+ /* Oneof fields */
+ return *(const pb_size_t*)pSize == 0;
+ }
+ else if (PB_HTYPE(type) == PB_HTYPE_OPTIONAL && field->size_offset != 0)
+ {
+ /* Proto2 optional fields inside proto3 submessage */
+ return *(const bool*)pSize == false;
+ }
+
+ /* Rest is proto3 singular fields */
+
+ if (PB_ATYPE(type) == PB_ATYPE_STATIC)
+ {
+ if (PB_LTYPE(type) == PB_LTYPE_BYTES)
+ {
+ const pb_bytes_array_t *bytes = (const pb_bytes_array_t*)pData;
+ return bytes->size == 0;
+ }
+ else if (PB_LTYPE(type) == PB_LTYPE_STRING)
+ {
+ return *(const char*)pData == '\0';
+ }
+ else if (PB_LTYPE(type) == PB_LTYPE_FIXED_LENGTH_BYTES)
+ {
+ /* Fixed length bytes is only empty if its length is fixed
+ * as 0. Which would be pretty strange, but we can check
+ * it anyway. */
+ return field->data_size == 0;
+ }
+ else if (PB_LTYPE(type) == PB_LTYPE_SUBMESSAGE)
+ {
+ /* Check all fields in the submessage to find if any of them
+ * are non-zero. The comparison cannot be done byte-per-byte
+ * because the C struct may contain padding bytes that must
+ * be skipped.
+ */
+ pb_field_iter_t iter;
+ if (pb_field_iter_begin(&iter, (const pb_field_t*)field->ptr, pb_const_cast(pData)))
+ {
+ do
+ {
+ if (!pb_check_proto3_default_value(iter.pos, iter.pData))
+ {
+ return false;
+ }
+ } while (pb_field_iter_next(&iter));
+ }
+ return true;
+ }
+ }
+
+ {
+ /* Catch-all branch that does byte-per-byte comparison for zero value.
+ *
+ * This is for all pointer fields, and for static PB_LTYPE_VARINT,
+ * UVARINT, SVARINT, FIXED32, FIXED64, EXTENSION fields, and also
+ * callback fields. These all have integer or pointer value which
+ * can be compared with 0.
+ */
+ pb_size_t i;
+ const char *p = (const char*)pData;
+ for (i = 0; i < field->data_size; i++)
+ {
+ if (p[i] != 0)
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
+
+/* Encode a field with static or pointer allocation, i.e. one whose data
+ * is available to the encoder directly. */
+static bool checkreturn encode_basic_field(pb_ostream_t *stream,
+ const pb_field_t *field, const void *pData)
+{
+ pb_encoder_t func;
+ bool implicit_has;
+ const void *pSize = &implicit_has;
+
+ func = PB_ENCODERS[PB_LTYPE(field->type)];
+
+ if (field->size_offset)
+ {
+ /* Static optional, repeated or oneof field */
+ pSize = (const char*)pData + field->size_offset;
+ }
+ else if (PB_HTYPE(field->type) == PB_HTYPE_OPTIONAL)
+ {
+ /* Proto3 style field, optional but without explicit has_ field. */
+ implicit_has = !pb_check_proto3_default_value(field, pData);
+ }
+ else
+ {
+ /* Required field, always present */
+ implicit_has = true;
+ }
+
+ if (PB_ATYPE(field->type) == PB_ATYPE_POINTER)
+ {
+ /* pData is a pointer to the field, which contains pointer to
+ * the data. If the 2nd pointer is NULL, it is interpreted as if
+ * the has_field was false.
+ */
+ pData = *(const void* const*)pData;
+ implicit_has = (pData != NULL);
+ }
+
+ switch (PB_HTYPE(field->type))
+ {
+ case PB_HTYPE_REQUIRED:
+ if (!pData)
+ PB_RETURN_ERROR(stream, "missing required field");
+ if (!pb_encode_tag_for_field(stream, field))
+ return false;
+ if (!func(stream, field, pData))
+ return false;
+ break;
+
+ case PB_HTYPE_OPTIONAL:
+ if (*(const bool*)pSize)
+ {
+ if (!pb_encode_tag_for_field(stream, field))
+ return false;
+
+ if (!func(stream, field, pData))
+ return false;
+ }
+ break;
+
+ case PB_HTYPE_REPEATED: {
+ pb_size_t count;
+ if (field->size_offset != 0) {
+ count = *(const pb_size_t*)pSize;
+ } else {
+ count = field->array_size;
+ }
+ if (!encode_array(stream, field, pData, count, func))
+ return false;
+ break;
+ }
+
+ case PB_HTYPE_ONEOF:
+ if (*(const pb_size_t*)pSize == field->tag)
+ {
+ if (!pb_encode_tag_for_field(stream, field))
+ return false;
+
+ if (!func(stream, field, pData))
+ return false;
+ }
+ break;
+
+ default:
+ PB_RETURN_ERROR(stream, "invalid field type");
+ }
+
+ return true;
+}
+
+/* Encode a field with callback semantics. This means that a user function is
+ * called to provide and encode the actual data. */
+static bool checkreturn encode_callback_field(pb_ostream_t *stream,
+ const pb_field_t *field, const void *pData)
+{
+ const pb_callback_t *callback = (const pb_callback_t*)pData;
+
+#ifdef PB_OLD_CALLBACK_STYLE
+ const void *arg = callback->arg;
+#else
+ void * const *arg = &(callback->arg);
+#endif
+
+ if (callback->funcs.encode != NULL)
+ {
+ if (!callback->funcs.encode(stream, field, arg))
+ PB_RETURN_ERROR(stream, "callback error");
+ }
+ return true;
+}
+
+/* Encode a single field of any callback or static type. */
+static bool checkreturn encode_field(pb_ostream_t *stream,
+ const pb_field_t *field, const void *pData)
+{
+ switch (PB_ATYPE(field->type))
+ {
+ case PB_ATYPE_STATIC:
+ case PB_ATYPE_POINTER:
+ return encode_basic_field(stream, field, pData);
+
+ case PB_ATYPE_CALLBACK:
+ return encode_callback_field(stream, field, pData);
+
+ default:
+ PB_RETURN_ERROR(stream, "invalid field type");
+ }
+}
+
+/* Default handler for extension fields. Expects to have a pb_field_t
+ * pointer in the extension->type->arg field. */
+static bool checkreturn default_extension_encoder(pb_ostream_t *stream,
+ const pb_extension_t *extension)
+{
+ const pb_field_t *field = (const pb_field_t*)extension->type->arg;
+
+ if (PB_ATYPE(field->type) == PB_ATYPE_POINTER)
+ {
+ /* For pointer extensions, the pointer is stored directly
+ * in the extension structure. This avoids having an extra
+ * indirection. */
+ return encode_field(stream, field, &extension->dest);
+ }
+ else
+ {
+ return encode_field(stream, field, extension->dest);
+ }
+}
+
+/* Walk through all the registered extensions and give them a chance
+ * to encode themselves. */
+static bool checkreturn encode_extension_field(pb_ostream_t *stream,
+ const pb_field_t *field, const void *pData)
+{
+ const pb_extension_t *extension = *(const pb_extension_t* const *)pData;
+ PB_UNUSED(field);
+
+ while (extension)
+ {
+ bool status;
+ if (extension->type->encode)
+ status = extension->type->encode(stream, extension);
+ else
+ status = default_extension_encoder(stream, extension);
+
+ if (!status)
+ return false;
+
+ extension = extension->next;
+ }
+
+ return true;
+}
+
+/*********************
+ * Encode all fields *
+ *********************/
+
+static void *pb_const_cast(const void *p)
+{
+ /* Note: this casts away const, in order to use the common field iterator
+ * logic for both encoding and decoding. */
+ union {
+ void *p1;
+ const void *p2;
+ } t;
+ t.p2 = p;
+ return t.p1;
+}
+
+bool checkreturn pb_encode(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct)
+{
+ pb_field_iter_t iter;
+ if (!pb_field_iter_begin(&iter, fields, pb_const_cast(src_struct)))
+ return true; /* Empty message type */
+
+ do {
+ if (PB_LTYPE(iter.pos->type) == PB_LTYPE_EXTENSION)
+ {
+ /* Special case for the extension field placeholder */
+ if (!encode_extension_field(stream, iter.pos, iter.pData))
+ return false;
+ }
+ else
+ {
+ /* Regular field */
+ if (!encode_field(stream, iter.pos, iter.pData))
+ return false;
+ }
+ } while (pb_field_iter_next(&iter));
+
+ return true;
+}
+
+bool pb_encode_delimited(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct)
+{
+ return pb_encode_submessage(stream, fields, src_struct);
+}
+
+bool pb_encode_nullterminated(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct)
+{
+ const pb_byte_t zero = 0;
+
+ if (!pb_encode(stream, fields, src_struct))
+ return false;
+
+ return pb_write(stream, &zero, 1);
+}
+
+bool pb_get_encoded_size(size_t *size, const pb_field_t fields[], const void *src_struct)
+{
+ pb_ostream_t stream = PB_OSTREAM_SIZING;
+
+ if (!pb_encode(&stream, fields, src_struct))
+ return false;
+
+ *size = stream.bytes_written;
+ return true;
+}
+
+/********************
+ * Helper functions *
+ ********************/
+
+/* This function avoids 64-bit shifts as they are quite slow on many platforms. */
+static bool checkreturn pb_encode_varint_32(pb_ostream_t *stream, uint32_t low, uint32_t high)
+{
+ size_t i = 0;
+ pb_byte_t buffer[10];
+ pb_byte_t byte = (pb_byte_t)(low & 0x7F);
+ low >>= 7;
+
+ while (i < 4 && (low != 0 || high != 0))
+ {
+ byte |= 0x80;
+ buffer[i++] = byte;
+ byte = (pb_byte_t)(low & 0x7F);
+ low >>= 7;
+ }
+
+ if (high)
+ {
+ byte = (pb_byte_t)(byte | ((high & 0x07) << 4));
+ high >>= 3;
+
+ while (high)
+ {
+ byte |= 0x80;
+ buffer[i++] = byte;
+ byte = (pb_byte_t)(high & 0x7F);
+ high >>= 7;
+ }
+ }
+
+ buffer[i++] = byte;
+
+ return pb_write(stream, buffer, i);
+}
+
+bool checkreturn pb_encode_varint(pb_ostream_t *stream, pb_uint64_t value)
+{
+ if (value <= 0x7F)
+ {
+ /* Fast path: single byte */
+ pb_byte_t byte = (pb_byte_t)value;
+ return pb_write(stream, &byte, 1);
+ }
+ else
+ {
+#ifdef PB_WITHOUT_64BIT
+ return pb_encode_varint_32(stream, value, 0);
+#else
+ return pb_encode_varint_32(stream, (uint32_t)value, (uint32_t)(value >> 32));
+#endif
+ }
+}
+
+bool checkreturn pb_encode_svarint(pb_ostream_t *stream, pb_int64_t value)
+{
+ pb_uint64_t zigzagged;
+ if (value < 0)
+ zigzagged = ~((pb_uint64_t)value << 1);
+ else
+ zigzagged = (pb_uint64_t)value << 1;
+
+ return pb_encode_varint(stream, zigzagged);
+}
+
+bool checkreturn pb_encode_fixed32(pb_ostream_t *stream, const void *value)
+{
+ uint32_t val = *(const uint32_t*)value;
+ pb_byte_t bytes[4];
+ bytes[0] = (pb_byte_t)(val & 0xFF);
+ bytes[1] = (pb_byte_t)((val >> 8) & 0xFF);
+ bytes[2] = (pb_byte_t)((val >> 16) & 0xFF);
+ bytes[3] = (pb_byte_t)((val >> 24) & 0xFF);
+ return pb_write(stream, bytes, 4);
+}
+
+#ifndef PB_WITHOUT_64BIT
+bool checkreturn pb_encode_fixed64(pb_ostream_t *stream, const void *value)
+{
+ uint64_t val = *(const uint64_t*)value;
+ pb_byte_t bytes[8];
+ bytes[0] = (pb_byte_t)(val & 0xFF);
+ bytes[1] = (pb_byte_t)((val >> 8) & 0xFF);
+ bytes[2] = (pb_byte_t)((val >> 16) & 0xFF);
+ bytes[3] = (pb_byte_t)((val >> 24) & 0xFF);
+ bytes[4] = (pb_byte_t)((val >> 32) & 0xFF);
+ bytes[5] = (pb_byte_t)((val >> 40) & 0xFF);
+ bytes[6] = (pb_byte_t)((val >> 48) & 0xFF);
+ bytes[7] = (pb_byte_t)((val >> 56) & 0xFF);
+ return pb_write(stream, bytes, 8);
+}
+#endif
+
+bool checkreturn pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, uint32_t field_number)
+{
+ pb_uint64_t tag = ((pb_uint64_t)field_number << 3) | wiretype;
+ return pb_encode_varint(stream, tag);
+}
+
+bool checkreturn pb_encode_tag_for_field(pb_ostream_t *stream, const pb_field_t *field)
+{
+ pb_wire_type_t wiretype;
+ switch (PB_LTYPE(field->type))
+ {
+ case PB_LTYPE_VARINT:
+ case PB_LTYPE_UVARINT:
+ case PB_LTYPE_SVARINT:
+ wiretype = PB_WT_VARINT;
+ break;
+
+ case PB_LTYPE_FIXED32:
+ wiretype = PB_WT_32BIT;
+ break;
+
+ case PB_LTYPE_FIXED64:
+ wiretype = PB_WT_64BIT;
+ break;
+
+ case PB_LTYPE_BYTES:
+ case PB_LTYPE_STRING:
+ case PB_LTYPE_SUBMESSAGE:
+ case PB_LTYPE_FIXED_LENGTH_BYTES:
+ wiretype = PB_WT_STRING;
+ break;
+
+ default:
+ PB_RETURN_ERROR(stream, "invalid field type");
+ }
+
+ return pb_encode_tag(stream, wiretype, field->tag);
+}
+
+bool checkreturn pb_encode_string(pb_ostream_t *stream, const pb_byte_t *buffer, size_t size)
+{
+ if (!pb_encode_varint(stream, (pb_uint64_t)size))
+ return false;
+
+ return pb_write(stream, buffer, size);
+}
+
+bool checkreturn pb_encode_submessage(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct)
+{
+ /* First calculate the message size using a non-writing substream. */
+ pb_ostream_t substream = PB_OSTREAM_SIZING;
+ size_t size;
+ bool status;
+
+ if (!pb_encode(&substream, fields, src_struct))
+ {
+#ifndef PB_NO_ERRMSG
+ stream->errmsg = substream.errmsg;
+#endif
+ return false;
+ }
+
+ size = substream.bytes_written;
+
+ if (!pb_encode_varint(stream, (pb_uint64_t)size))
+ return false;
+
+ if (stream->callback == NULL)
+ return pb_write(stream, NULL, size); /* Just sizing */
+
+ if (stream->bytes_written + size > stream->max_size)
+ PB_RETURN_ERROR(stream, "stream full");
+
+ /* Use a substream to verify that a callback doesn't write more than
+ * what it did the first time. */
+ substream.callback = stream->callback;
+ substream.state = stream->state;
+ substream.max_size = size;
+ substream.bytes_written = 0;
+#ifndef PB_NO_ERRMSG
+ substream.errmsg = NULL;
+#endif
+
+ status = pb_encode(&substream, fields, src_struct);
+
+ stream->bytes_written += substream.bytes_written;
+ stream->state = substream.state;
+#ifndef PB_NO_ERRMSG
+ stream->errmsg = substream.errmsg;
+#endif
+
+ if (substream.bytes_written != size)
+ PB_RETURN_ERROR(stream, "submsg size changed");
+
+ return status;
+}
+
+/* Field encoders */
+
+static bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_t *field, const void *src)
+{
+ pb_int64_t value = 0;
+
+ if (field->data_size == sizeof(int_least8_t))
+ value = *(const int_least8_t*)src;
+ else if (field->data_size == sizeof(int_least16_t))
+ value = *(const int_least16_t*)src;
+ else if (field->data_size == sizeof(int32_t))
+ value = *(const int32_t*)src;
+ else if (field->data_size == sizeof(pb_int64_t))
+ value = *(const pb_int64_t*)src;
+ else
+ PB_RETURN_ERROR(stream, "invalid data_size");
+
+#ifdef PB_WITHOUT_64BIT
+ if (value < 0)
+ return pb_encode_varint_32(stream, (uint32_t)value, (uint32_t)-1);
+ else
+#endif
+ return pb_encode_varint(stream, (pb_uint64_t)value);
+}
+
+static bool checkreturn pb_enc_uvarint(pb_ostream_t *stream, const pb_field_t *field, const void *src)
+{
+ pb_uint64_t value = 0;
+
+ if (field->data_size == sizeof(uint_least8_t))
+ value = *(const uint_least8_t*)src;
+ else if (field->data_size == sizeof(uint_least16_t))
+ value = *(const uint_least16_t*)src;
+ else if (field->data_size == sizeof(uint32_t))
+ value = *(const uint32_t*)src;
+ else if (field->data_size == sizeof(pb_uint64_t))
+ value = *(const pb_uint64_t*)src;
+ else
+ PB_RETURN_ERROR(stream, "invalid data_size");
+
+ return pb_encode_varint(stream, value);
+}
+
+static bool checkreturn pb_enc_svarint(pb_ostream_t *stream, const pb_field_t *field, const void *src)
+{
+ pb_int64_t value = 0;
+
+ if (field->data_size == sizeof(int_least8_t))
+ value = *(const int_least8_t*)src;
+ else if (field->data_size == sizeof(int_least16_t))
+ value = *(const int_least16_t*)src;
+ else if (field->data_size == sizeof(int32_t))
+ value = *(const int32_t*)src;
+ else if (field->data_size == sizeof(pb_int64_t))
+ value = *(const pb_int64_t*)src;
+ else
+ PB_RETURN_ERROR(stream, "invalid data_size");
+
+ return pb_encode_svarint(stream, value);
+}
+
+static bool checkreturn pb_enc_fixed64(pb_ostream_t *stream, const pb_field_t *field, const void *src)
+{
+ PB_UNUSED(field);
+#ifndef PB_WITHOUT_64BIT
+ return pb_encode_fixed64(stream, src);
+#else
+ PB_UNUSED(src);
+ PB_RETURN_ERROR(stream, "no 64bit support");
+#endif
+}
+
+static bool checkreturn pb_enc_fixed32(pb_ostream_t *stream, const pb_field_t *field, const void *src)
+{
+ PB_UNUSED(field);
+ return pb_encode_fixed32(stream, src);
+}
+
+static bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_t *field, const void *src)
+{
+ const pb_bytes_array_t *bytes = NULL;
+
+ bytes = (const pb_bytes_array_t*)src;
+
+ if (src == NULL)
+ {
+ /* Treat null pointer as an empty bytes field */
+ return pb_encode_string(stream, NULL, 0);
+ }
+
+ if (PB_ATYPE(field->type) == PB_ATYPE_STATIC &&
+ PB_BYTES_ARRAY_T_ALLOCSIZE(bytes->size) > field->data_size)
+ {
+ PB_RETURN_ERROR(stream, "bytes size exceeded");
+ }
+
+ return pb_encode_string(stream, bytes->bytes, bytes->size);
+}
+
+static bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_t *field, const void *src)
+{
+ size_t size = 0;
+ size_t max_size = field->data_size;
+ const char *p = (const char*)src;
+
+ if (PB_ATYPE(field->type) == PB_ATYPE_POINTER)
+ {
+ max_size = (size_t)-1;
+ }
+ else
+ {
+ /* pb_dec_string() assumes string fields end with a null
+ * terminator when the type isn't PB_ATYPE_POINTER, so we
+ * shouldn't allow more than max-1 bytes to be written to
+ * allow space for the null terminator.
+ */
+ if (max_size == 0)
+ PB_RETURN_ERROR(stream, "zero-length string");
+
+ max_size -= 1;
+ }
+
+
+ if (src == NULL)
+ {
+ size = 0; /* Treat null pointer as an empty string */
+ }
+ else
+ {
+ /* strnlen() is not always available, so just use a loop */
+ while (size < max_size && *p != '\0')
+ {
+ size++;
+ p++;
+ }
+
+ if (*p != '\0')
+ {
+ PB_RETURN_ERROR(stream, "unterminated string");
+ }
+ }
+
+ return pb_encode_string(stream, (const pb_byte_t*)src, size);
+}
+
+static bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_t *field, const void *src)
+{
+ if (field->ptr == NULL)
+ PB_RETURN_ERROR(stream, "invalid field descriptor");
+
+ return pb_encode_submessage(stream, (const pb_field_t*)field->ptr, src);
+}
+
+static bool checkreturn pb_enc_fixed_length_bytes(pb_ostream_t *stream, const pb_field_t *field, const void *src)
+{
+ return pb_encode_string(stream, (const pb_byte_t*)src, field->data_size);
+}
+
diff --git a/src/lib/ubjson/ubjr.c b/src/lib/ubjson/ubjr.c
new file mode 100644
index 0000000..7bb2b2f
--- /dev/null
+++ b/src/lib/ubjson/ubjr.c
@@ -0,0 +1,525 @@
+#include "ubj.h"
+#include "ubj_internal.h"
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef MULTIPASS_TRACE_MALLOC
+#include "lib/mpmalloc.h"
+#else
+#define mpmalloc malloc
+#define mprealloc realloc
+#define mpfree free
+#endif
+
+#if _MSC_VER
+#define inline __inline
+#endif
+
+typedef struct ubjr_context_t_s
+{
+ size_t (*read_cb )(void* data, size_t size, size_t count, void* userdata);
+ int (*peek_cb )(void* userdata);
+ int (*close_cb)(void* userdata);
+ void (*error_cb)(const char* error_msg);
+
+ void* userdata;
+
+// struct _ubjr_container_t container_stack[CONTAINER_STACK_MAX];
+// struct _ubjr_container_t* head;
+
+ uint8_t ignore_container_flags;
+
+ uint16_t last_error_code;
+
+ size_t total_read;
+} ubjr_context_t;
+
+ubjr_context_t* ubjr_open_callback(void* userdata,
+ size_t(*read_cb)(void* data, size_t size, size_t count, void* userdata),
+ int(*peek_cb)(void* userdata),
+ int(*close_cb)(void* userdata),
+ void(*error_cb)(const char* error_msg)
+ )
+{
+ ubjr_context_t* ctx = (ubjr_context_t*)mpmalloc(sizeof(ubjr_context_t));
+ ctx->userdata = userdata;
+ ctx->read_cb = read_cb;
+ ctx->peek_cb = peek_cb;
+ ctx->close_cb = close_cb;
+ ctx->error_cb = error_cb;
+
+
+/* ctx->head = ctx->container_stack;
+ ctx->head->flags = 0;
+ ctx->head->type = UBJ_MIXED;
+ ctx->head->elements_remaining = 0;
+
+ ctx->ignore_container_flags = 0;*/
+
+ ctx->last_error_code = 0;
+
+ ctx->total_read = 0;
+ return ctx;
+}
+
+size_t ubjr_close_context(ubjr_context_t* ctx)
+{
+ size_t n = ctx->total_read;
+ mpfree(ctx->userdata);
+ mpfree(ctx);
+ return n;
+}
+
+static inline uint8_t priv_ubjr_context_getc(ubjr_context_t* ctx)
+{
+ uint8_t a;
+ ctx->total_read += 1;
+ ctx->read_cb(&a, 1, 1, ctx->userdata);
+ return a;
+}
+
+static int fpeek(void* fp)
+{
+ int c;
+ c = fgetc(fp);
+ ungetc(c, fp);
+
+ return c;
+}
+
+ubjr_context_t* ubjr_open_file(FILE* fd)
+{
+ return ubjr_open_callback(fd, (void*)fread,(void*)fpeek,(void*)fclose, NULL);
+}
+
+struct mem_r_fd
+{
+ const uint8_t *begin, *current, *end;
+};
+static int memclose(void* mfd)
+{
+ //mpfree(mfd);
+ return 0;
+}
+static size_t memread(void* data, size_t size, size_t count, struct mem_r_fd* fp)
+{
+ size_t n = size*count;
+ size_t lim = fp->end - fp->current;
+ if (lim < n)
+ {
+ n = lim;
+ }
+ memcpy(data, fp->current, n);
+ fp->current += n;
+ return n;
+}
+static int mempeek(struct mem_r_fd* mfd)
+{
+ return *mfd->current;
+}
+
+ubjr_context_t* ubjr_open_memory(const uint8_t* be, const uint8_t* en)
+{
+ struct mem_r_fd* mfd = (struct mem_r_fd*)mpmalloc(sizeof(struct mem_r_fd));
+ mfd->current = be;
+ mfd->begin = be;
+ mfd->end = en;
+ return ubjr_open_callback(mfd, (void*)memread, (void*)mempeek,(void*)memclose, NULL);
+}
+
+static inline int priv_ubjr_context_peek(ubjr_context_t* ctx)
+{
+ return ctx->peek_cb(ctx->userdata);
+}
+static inline size_t priv_ubjr_context_read(ubjr_context_t* ctx,uint8_t* dst,size_t n)
+{
+ size_t nr=ctx->read_cb(dst,n,1,ctx->userdata);
+ ctx->total_read+=nr;
+ return nr;
+}
+
+typedef struct priv_ubjr_sorted_key_t_s
+{
+ ubjr_string_t key;
+ const uint8_t* value;
+
+} priv_ubjr_sorted_key_t;
+
+static int _obj_key_cmp(const void* av, const void* bv)
+{
+ const priv_ubjr_sorted_key_t *a, *b;
+ a = (const priv_ubjr_sorted_key_t*)av;
+ b = (const priv_ubjr_sorted_key_t*)bv;
+ return strcmp(a->key,b->key);
+}
+
+static inline UBJ_TYPE priv_ubjr_type_from_char(uint8_t c)
+{
+ int i = 0; //TODO: Benchmark this and see if it should be a switch statement where the compiler implements fastest switch e.g. binary search (17 cases might be binary search fast)
+ for (i = 0; i < UBJ_NUM_TYPES && UBJI_TYPEC_convert[i] != c; i++);
+ return (UBJ_TYPE)i;
+}
+
+size_t ubjr_local_type_size(UBJ_TYPE typ)
+{
+ return UBJR_TYPE_localsize[typ];
+}
+
+
+static inline priv_ubjr_sorted_key_t* priv_ubjr_object_build_sorted_keys(ubjr_object_t* obj)
+{
+ priv_ubjr_sorted_key_t* sorted_keysmem = mpmalloc(obj->size*sizeof(priv_ubjr_sorted_key_t));
+ size_t i;
+ for (i = 0; i < obj->size; i++)
+ {
+ sorted_keysmem[i].key = obj->keys[i];
+ sorted_keysmem[i].value = (const uint8_t*)obj->values + i*UBJR_TYPE_localsize[obj->type];
+ }
+ qsort(sorted_keysmem, obj->size, sizeof(priv_ubjr_sorted_key_t), _obj_key_cmp);
+ return sorted_keysmem;
+}
+
+static inline uint8_t priv_ubjr_read_1b(ubjr_context_t* ctx)
+{
+ return priv_ubjr_context_getc(ctx);
+}
+static inline uint16_t priv_ubjr_read_2b(ubjr_context_t* ctx)
+{
+ return (uint16_t)priv_ubjr_read_1b(ctx) << 8 | (uint16_t)priv_ubjr_read_1b(ctx);
+}
+static inline uint32_t priv_ubjr_read_4b(ubjr_context_t* ctx)
+{
+ return (uint32_t)priv_ubjr_read_2b(ctx) << 16 | (uint32_t)priv_ubjr_read_2b(ctx);
+}
+static inline uint64_t priv_ubjr_read_8b(ubjr_context_t* ctx)
+{
+ return (uint64_t)priv_ubjr_read_4b(ctx) << 32 | (uint64_t)priv_ubjr_read_4b(ctx);
+}
+
+static inline int64_t priv_ubjw_read_integer(ubjr_context_t* ctx)
+{
+ ubjr_dynamic_t d = ubjr_read_dynamic(ctx);
+ if (d.type >= UBJ_INT8 && d.type <= UBJ_INT64)
+ return d.integer;
+ return 0;//error
+}
+
+static inline ubjr_object_t priv_ubjr_read_raw_object(ubjr_context_t* ctx);
+static inline ubjr_array_t priv_ubjr_read_raw_array(ubjr_context_t* ctx);
+static inline void priv_ubjr_read_to_ptr(ubjr_context_t* ctx, uint8_t* dst, UBJ_TYPE typ)
+{
+ int64_t n = 1;
+ char *tstr;
+ switch (typ)
+ {
+ case UBJ_MIXED:
+ {
+ *(ubjr_dynamic_t*)dst = ubjr_read_dynamic(ctx);
+ break;
+ }
+ case UBJ_STRING:
+ case UBJ_HIGH_PRECISION:
+ {
+ n = priv_ubjw_read_integer(ctx);
+ }
+ case UBJ_CHAR:
+ {
+ tstr = mpmalloc(n + 1);
+ priv_ubjr_context_read(ctx, tstr, n);
+ tstr[n] = 0;
+ *(ubjr_string_t*)dst = tstr;
+ break;
+ }
+ case UBJ_INT8:
+ case UBJ_UINT8:
+ {
+ *dst = priv_ubjr_read_1b(ctx);
+ break;
+ }
+ case UBJ_INT16:
+ {
+ *(uint16_t*)dst = priv_ubjr_read_2b(ctx);
+ break;
+ }
+ case UBJ_INT32:
+ case UBJ_FLOAT32:
+ {
+ *(uint32_t*)dst = priv_ubjr_read_4b(ctx);
+ break;
+ }
+ case UBJ_INT64:
+ case UBJ_FLOAT64:
+ {
+ *(uint64_t*)dst = priv_ubjr_read_8b(ctx);
+ break;
+ }
+ case UBJ_ARRAY:
+ {
+ *(ubjr_array_t*)dst = priv_ubjr_read_raw_array(ctx);
+ break;
+ }
+ case UBJ_OBJECT:
+ {
+ *(ubjr_object_t*)dst = priv_ubjr_read_raw_object(ctx);
+ break;
+ }
+ };
+}
+
+ubjr_dynamic_t ubjr_object_lookup(ubjr_object_t* obj, const char* key)
+{
+ if (obj->metatable == NULL)
+ {
+ //memcpy(obj->sorted_keys,obj->keys)
+ obj->metatable = priv_ubjr_object_build_sorted_keys(obj);
+ }
+ void* result=bsearch(key, obj->metatable,obj->size, sizeof(priv_ubjr_sorted_key_t),_obj_key_cmp);
+ if (result == NULL)
+ {
+ ubjr_dynamic_t nulldyn;
+ nulldyn.type = UBJ_NULLTYPE;
+ return nulldyn;
+ }
+ const priv_ubjr_sorted_key_t* result_key = (const priv_ubjr_sorted_key_t*)result;
+ return priv_ubjr_pointer_to_dynamic(obj->type,result_key->value);
+}
+
+size_t ubjr_ndarray_index(const ubjr_array_t* arr, const size_t* indices)
+{
+ //multi-dimensional array to linear array lookup
+ size_t cstride = 1;
+ size_t cdex = 0;
+ uint8_t i;
+ uint8_t nd = arr->num_dims;
+ const size_t* dims = arr->dims;
+ for (i = 0; i<nd; i++)
+ {
+ cdex += cstride*indices[i];
+ cstride *= dims[i];
+ }
+ return cdex;
+}
+
+
+
+ubjr_dynamic_t ubjr_read_dynamic(ubjr_context_t* ctx)
+{
+ ubjr_dynamic_t scratch; //scratch memory
+ UBJ_TYPE newtyp = priv_ubjr_type_from_char(priv_ubjr_context_getc(ctx));
+ priv_ubjr_read_to_ptr(ctx, (uint8_t*)&scratch, newtyp);
+ return priv_ubjr_pointer_to_dynamic(newtyp, &scratch);
+}
+
+static inline void priv_read_container_params(ubjr_context_t* ctx, UBJ_TYPE* typout, size_t* sizeout)
+{
+ int nextchar = priv_ubjr_context_peek(ctx);
+ if (nextchar == '$')
+ {
+ priv_ubjr_context_getc(ctx);
+ *typout = priv_ubjr_type_from_char(priv_ubjr_context_getc(ctx));
+ nextchar = priv_ubjr_context_peek(ctx);
+ }
+ else
+ {
+ *typout = UBJ_MIXED;
+ }
+
+ if (nextchar == '#')
+ {
+ priv_ubjr_context_getc(ctx);
+ *sizeout = priv_ubjw_read_integer(ctx);
+ }
+ else
+ {
+ *sizeout = 0;
+ }
+}
+//TODO: This can be reused for object
+
+static inline ubjr_array_t priv_ubjr_read_raw_array(ubjr_context_t* ctx)
+{
+ ubjr_array_t myarray;
+ priv_read_container_params(ctx,&myarray.type,&myarray.size);
+ myarray.num_dims = 1;
+ myarray.dims = NULL;
+ if (myarray.type != UBJ_MIXED && myarray.size==0) //params detected this is a typed array but no size was detected..possibly an N-D array?
+ {
+ if (priv_ubjr_context_peek(ctx) == '@')
+ {
+ uint8_t dselect;
+ priv_ubjr_context_getc(ctx);//skip over the '@' marker
+ myarray.num_dims = priv_ubjr_context_getc(ctx);//since max is 8, no type indicator needed...always a int7 type
+ myarray.dims = mpmalloc(sizeof(size_t)*myarray.num_dims);
+ myarray.size = 1;
+ for (dselect = 0; dselect < myarray.num_dims; dselect++)
+ {
+ size_t d = priv_ubjw_read_integer(ctx);
+ myarray.dims[dselect] = d;
+ myarray.size *= d;
+ }
+ }
+ }
+
+ size_t ls = UBJR_TYPE_localsize[myarray.type];
+ if (myarray.size == 0)
+ {
+ myarray.originally_sized = 0;
+ size_t arrpot = 0;
+ myarray.values=mpmalloc(1*ls+1); //the +1 is for memory for the 0-size elements
+ for (myarray.size = 0; priv_ubjr_context_peek(ctx) != ']'; myarray.size++)
+ {
+ if (myarray.size >= (1ULL << arrpot))
+ {
+ arrpot ++;
+ myarray.values = mprealloc(myarray.values, (1ULL << arrpot)*ls+1);
+ }
+ priv_ubjr_read_to_ptr(ctx,(uint8_t*)myarray.values + ls*myarray.size,myarray.type);
+ }
+ priv_ubjr_context_getc(ctx); // read the closing ']'
+ }
+ else
+ {
+ myarray.originally_sized = 1;
+ size_t i;
+ myarray.values = mpmalloc(ls*myarray.size+1);
+ size_t sz = UBJI_TYPE_size[myarray.type];
+
+ if (sz >= 0 && myarray.type != UBJ_STRING && myarray.type != UBJ_HIGH_PRECISION && myarray.type != UBJ_CHAR && myarray.type != UBJ_MIXED) //constant size,fastread
+ {
+ priv_ubjr_context_read(ctx, myarray.values, sz*myarray.size);
+ buf_endian_swap(myarray.values, sz, myarray.size); //do nothing for 0-sized buffers
+ }
+ else
+ {
+ for (i = 0; i < myarray.size; i++)
+ {
+ priv_ubjr_read_to_ptr(ctx, (uint8_t*)myarray.values + ls*i, myarray.type);
+ }
+ }
+ }
+ if (myarray.dims == NULL)
+ {
+ myarray.dims = mpmalloc(sizeof(size_t));
+ myarray.dims[0] = myarray.size;
+ }
+ return myarray;
+}
+
+static inline ubjr_object_t priv_ubjr_read_raw_object(ubjr_context_t* ctx)
+{
+ ubjr_object_t myobject;
+ myobject.metatable = NULL;
+ priv_read_container_params(ctx, &myobject.type, &myobject.size);
+
+ size_t ls = UBJR_TYPE_localsize[myobject.type];
+ if (myobject.size == 0)
+ {
+ myobject.originally_sized = 0;
+ size_t arrpot = 0;
+ myobject.values = mpmalloc(1 * ls + 1); //the +1 is for memory for the 0-size elements
+ myobject.keys = mpmalloc(1 * sizeof(ubjr_string_t));
+ for (myobject.size = 0; priv_ubjr_context_peek(ctx) != '}'; myobject.size++)
+ {
+ if (myobject.size >= (1ULL << arrpot))
+ {
+ arrpot++;
+ myobject.values = mprealloc(myobject.values, (1ULL << arrpot)*ls + 1);
+ myobject.keys = mprealloc((uint8_t*)myobject.keys, (1ULL << arrpot)*sizeof(ubjr_string_t));
+ }
+ priv_ubjr_read_to_ptr(ctx, (uint8_t*)(myobject.keys + myobject.size), UBJ_STRING);
+ priv_ubjr_read_to_ptr(ctx, (uint8_t*)myobject.values + ls*myobject.size, myobject.type);
+ }
+ priv_ubjr_context_getc(ctx); // read the closing '}'
+ }
+ else
+ {
+ size_t i;
+ myobject.originally_sized = 1;
+ myobject.values = mpmalloc(ls*myobject.size + 1);
+ myobject.keys = mpmalloc(myobject.size * sizeof(ubjr_string_t));
+
+ for (i = 0; i < myobject.size; i++)
+ {
+ priv_ubjr_read_to_ptr(ctx, (uint8_t*)(myobject.keys + i), UBJ_STRING);
+ priv_ubjr_read_to_ptr(ctx, (uint8_t*)myobject.values + ls*i, myobject.type);
+ }
+ }
+ return myobject;
+}
+static inline void priv_ubjr_cleanup_pointer(UBJ_TYPE typ,void* value);
+static inline priv_ubjr_cleanup_container(UBJ_TYPE type,size_t size,void* values)
+{
+ if(type == UBJ_MIXED || type == UBJ_ARRAY || type == UBJ_OBJECT || type == UBJ_STRING)
+ {
+ size_t ls=UBJR_TYPE_localsize[type];
+ uint8_t *viter,*vend;
+ viter=values;
+ vend=viter+ls*size;
+ for(;viter != vend;viter+=ls)
+ {
+ priv_ubjr_cleanup_pointer(type,(void*)viter);
+ }
+ }
+ mpfree(values);
+}
+static inline void priv_ubjr_cleanup_pointer(UBJ_TYPE typ,void* value)
+{
+ switch(typ)
+ {
+ case UBJ_MIXED:
+ {
+ ubjr_dynamic_t* dyn=(ubjr_dynamic_t*)value;
+ switch(dyn->type)
+ {
+ case UBJ_STRING:
+ priv_ubjr_cleanup_pointer(UBJ_STRING,&dyn->string);
+ break;
+ case UBJ_ARRAY:
+ priv_ubjr_cleanup_pointer(UBJ_ARRAY,&dyn->container_array);
+ break;
+ case UBJ_OBJECT:
+ priv_ubjr_cleanup_pointer(UBJ_OBJECT,&dyn->container_object);
+ break;
+ };
+ break;
+ }
+ case UBJ_STRING:
+ {
+ ubjr_string_t* st=(ubjr_string_t*)value;
+ mpfree((void*)*st);
+ break;
+ }
+ case UBJ_ARRAY:
+ {
+ ubjr_array_t* arr=(ubjr_array_t*)value;
+ priv_ubjr_cleanup_container(arr->type,arr->size,arr->values);
+ mpfree(arr->dims);
+ break;
+ }
+ case UBJ_OBJECT:
+ {
+ ubjr_object_t* obj=(ubjr_object_t*)value;
+ priv_ubjr_cleanup_container(obj->type,obj->size,obj->values);
+ priv_ubjr_cleanup_container(UBJ_STRING,obj->size,obj->keys);
+ if(obj->metatable)
+ {
+ mpfree(obj->metatable);
+ }
+ break;
+ }
+ };
+}
+
+void ubjr_cleanup_dynamic(ubjr_dynamic_t* dyn)
+{
+ priv_ubjr_cleanup_pointer(UBJ_MIXED,dyn);
+}
+void ubjr_cleanup_array(ubjr_array_t* arr)
+{
+ priv_ubjr_cleanup_pointer(UBJ_ARRAY,arr);
+}
+void ubjr_cleanup_object(ubjr_object_t* obj)
+{
+ priv_ubjr_cleanup_pointer(UBJ_OBJECT,obj);
+}
+
diff --git a/src/lib/ubjson/ubjrw.c b/src/lib/ubjson/ubjrw.c
new file mode 100644
index 0000000..ee43d70
--- /dev/null
+++ b/src/lib/ubjson/ubjrw.c
@@ -0,0 +1,169 @@
+#include "ubj_internal.h"
+
+static uint32_t compute_typemask(ubjr_dynamic_t* vals, size_t sz)
+{
+ uint32_t typemask = 0;
+ size_t i;
+ for (i = 0; i < sz; i++)
+ {
+ typemask |= 1UL << vals[i].type;
+ }
+ return typemask;
+}
+
+static inline UBJ_TYPE typemask2type(uint32_t v)
+{
+ unsigned int r = 0; // r will be lg(v)
+
+ while (v >>= 1) // unroll for more speed...
+ {
+ r++;
+ }
+ return (UBJ_TYPE)r;
+}
+static UBJ_TYPE compute_best_integer_type(ubjr_dynamic_t* vals, size_t sz)
+{
+ uint32_t typemask = 0;
+ size_t i;
+ for (i = 0; i < sz; i++)
+ {
+ typemask |= 1UL << ubjw_min_integer_type(vals[i].integer);
+ }
+ return typemask2type(typemask);
+}
+static uint32_t compute_best_string_type(ubjr_dynamic_t* vals, size_t sz)
+{
+ size_t i;
+ for (i = 0; i < sz; i++)
+ {
+ if (strlen(vals[i].string) > 1)
+ {
+ return UBJ_STRING;
+ }
+ }
+ return UBJ_CHAR;
+}
+static UBJ_TYPE optimize_type(UBJ_TYPE typein,ubjr_dynamic_t* vals, size_t sz)
+{
+ static const uint32_t intmask = (1 << UBJ_INT8) | (1 << UBJ_UINT8) | (1 << UBJ_INT16) | (1 << UBJ_INT32) | (1 << UBJ_INT64);
+ static const uint32_t stringmask = (1 << UBJ_STRING) | (1 << UBJ_CHAR);
+ if (typein != UBJ_MIXED)
+ return typein;
+ //integer optimization can be done here...
+ uint32_t tm = compute_typemask(vals, sz);
+ if ((tm & intmask) == tm) //if all values are integers
+ {
+ return compute_best_integer_type(vals,sz); //calculate the optimum type given the data
+ }
+ else if ((tm & stringmask) == tm)
+ {
+ return compute_best_string_type(vals,sz);
+ }
+ else if(tm && !(tm & (tm- 1))) //if only one bit is set in typemask
+ {
+ return typemask2type(tm); //figure out which bit is set.
+ }
+ else
+ {
+ return UBJ_MIXED;
+ }
+}
+
+void ubjrw_write_dynamic(ubjw_context_t* ctx, ubjr_dynamic_t dobj,uint8_t optimize)
+{
+ UBJ_TYPE ctyp,otyp;
+ size_t csize;
+ uint8_t* cvalues;
+ switch (dobj.type)
+ {
+ case UBJ_MIXED:
+ return;///error, can't be mixed
+ case UBJ_NULLTYPE:
+ ubjw_write_null(ctx);
+ return;
+ case UBJ_NOOP:
+ ubjw_write_noop(ctx);
+ return;
+ case UBJ_BOOL_FALSE:
+ ubjw_write_bool(ctx, 0);
+ return;
+ case UBJ_BOOL_TRUE:
+ ubjw_write_bool(ctx, 1);
+ return;
+ case UBJ_CHAR:
+ ubjw_write_char(ctx, *dobj.string);//first character of string
+ return;
+ case UBJ_STRING:
+ ubjw_write_string(ctx, dobj.string);
+ return;
+ case UBJ_HIGH_PRECISION:
+ ubjw_write_high_precision(ctx, dobj.string);
+ return;
+ case UBJ_INT8:
+ ubjw_write_int8(ctx, (int8_t)dobj.integer);
+ return;
+ case UBJ_UINT8:
+ ubjw_write_uint8(ctx, (uint8_t)dobj.integer);
+ return;
+ case UBJ_INT16:
+ ubjw_write_int16(ctx, (int16_t)dobj.integer);
+ return;
+ case UBJ_INT32:
+ ubjw_write_int32(ctx, (int32_t)dobj.integer);
+ return;
+ case UBJ_INT64:
+ ubjw_write_int64(ctx, dobj.integer);
+ return;
+ case UBJ_FLOAT32:
+ ubjw_write_float32(ctx, (float)dobj.real);
+ return;
+ case UBJ_FLOAT64:
+ ubjw_write_float64(ctx, dobj.real);
+ return;
+ case UBJ_ARRAY:
+ if ((dobj.container_array.originally_sized || optimize) //if we optimize an unsized array to a sized one or the original is sized
+ && dobj.container_array.type != UBJ_MIXED
+ && dobj.container_array.type != UBJ_OBJECT
+ && dobj.container_array.type != UBJ_ARRAY)
+ {
+ ubjw_write_buffer(ctx, dobj.container_array.values, dobj.container_array.type, dobj.container_array.size);
+ return;
+ }
+ else
+ {
+ ctyp = dobj.container_array.type;
+ csize = dobj.container_array.size;
+ cvalues = dobj.container_array.values;
+ otyp = optimize ? optimize_type(ctyp,(ubjr_dynamic_t*)cvalues,csize) : ctyp;
+ ubjw_begin_array(ctx, otyp, (dobj.container_array.originally_sized || optimize) ? csize : 0);
+ break;
+ }
+ case UBJ_OBJECT:
+ {
+ ctyp = dobj.container_object.type;
+ csize = dobj.container_object.size;
+ cvalues = dobj.container_object.values;
+ otyp = optimize ? optimize_type(ctyp, (ubjr_dynamic_t*)cvalues, csize) : ctyp;
+ ubjw_begin_object(ctx, otyp, (dobj.container_object.originally_sized || optimize) ? csize : 0);
+ break;
+ }
+ };
+ {
+ size_t i;
+ ubjr_dynamic_t scratch;
+ size_t ls = UBJR_TYPE_localsize[ctyp];
+
+ for (i = 0; i < csize; i++)
+ {
+ if (dobj.type == UBJ_OBJECT)
+ {
+ ubjw_write_key(ctx, dobj.container_object.keys[i]);
+ }
+ scratch = priv_ubjr_pointer_to_dynamic(ctyp, cvalues + ls*i);
+ scratch.type = (otyp == UBJ_MIXED ? scratch.type : otyp);
+ ubjrw_write_dynamic(ctx, scratch,optimize);
+ }
+ ubjw_end(ctx);
+ }
+
+}
diff --git a/src/lib/ubjson/ubjw.c b/src/lib/ubjson/ubjw.c
new file mode 100644
index 0000000..9fce397
--- /dev/null
+++ b/src/lib/ubjson/ubjw.c
@@ -0,0 +1,626 @@
+#include "ubj.h"
+#include "ubj_internal.h"
+
+#ifdef MULTIPASS_TRACE_MALLOC
+#include "lib/mpmalloc.h"
+#else
+#define mpmalloc malloc
+#define mpfree free
+#endif
+
+#define CONTAINER_IS_SIZED 0x1
+#define CONTAINER_IS_TYPED 0x2
+#define CONTAINER_IS_UBJ_ARRAY 0x4
+#define CONTAINER_IS_UBJ_OBJECT 0x8
+
+#define CONTAINER_EXPECTS_KEY 0x10
+
+#define CONTAINER_STACK_MAX 16
+#define BUFFER_OUT_SIZE 1024
+
+#define MAX_DIMS 8
+
+
+struct priv_ubjw_container_t
+{
+ uint8_t flags;
+ UBJ_TYPE type;
+ size_t elements_remaining;
+};
+
+struct ubjw_context_t_s
+{
+ size_t(*write_cb)(const void* data, size_t size, size_t count, void* userdata);
+ int(*close_cb)(void* userdata);
+ void (*error_cb)(const char* error_msg);
+
+ void* userdata;
+
+ struct priv_ubjw_container_t container_stack[CONTAINER_STACK_MAX];
+ struct priv_ubjw_container_t* head;
+
+ uint8_t ignore_container_flags;
+
+ uint16_t last_error_code;
+
+ size_t total_written;
+};
+
+
+
+ubjw_context_t* ubjw_open_callback(void* userdata,
+ size_t(*write_cb)(const void* data, size_t size, size_t count, void* userdata),
+ int(*close_cb)(void* userdata),
+ void (*error_cb)(const char* error_msg)
+ )
+{
+ ubjw_context_t* ctx = (ubjw_context_t*)mpmalloc(sizeof(ubjw_context_t));
+ ctx->userdata = userdata;
+ ctx->write_cb = write_cb;
+ ctx->close_cb = close_cb;
+ ctx->error_cb = error_cb;
+
+ ctx->head = ctx->container_stack;
+ ctx->head->flags = 0;
+ ctx->head->type = UBJ_MIXED;
+ ctx->head->elements_remaining = 0;
+ //ctx->head->num_dims=1;
+
+ ctx->ignore_container_flags = 0;
+
+ ctx->last_error_code = 0;
+
+ ctx->total_written = 0;
+ return ctx;
+}
+ubjw_context_t* ubjw_open_file(FILE* fd)
+{
+ return ubjw_open_callback(fd, (void*)fwrite,(void*)fclose,NULL);
+}
+
+struct mem_w_fd
+{
+ uint8_t *begin,*current, *end;
+};
+
+static int memclose(void* mfd)
+{
+ mpfree(mfd);
+ return 0;
+}
+static size_t memwrite(const void* data, size_t size, size_t count, struct mem_w_fd* fp)
+{
+ size_t n = size*count;
+ size_t lim = fp->end - fp->current;
+ if (lim < n)
+ {
+ n = lim;
+ }
+ memcpy(fp->current, data, n);
+ fp->current += n;
+ return n;
+}
+
+ubjw_context_t* ubjw_open_memory(uint8_t* be, uint8_t* en)
+{
+ struct mem_w_fd* mfd = (struct mem_w_fd*)mpmalloc(sizeof(struct mem_w_fd));
+ mfd->current = be;
+ mfd->begin = be;
+ mfd->end = en;
+ return ubjw_open_callback(mfd, (void*)memwrite, (void*)memclose,NULL);
+}
+
+static inline void priv_ubjw_context_append(ubjw_context_t* ctx, uint8_t a)
+{
+ ctx->total_written += 1;
+ ctx->write_cb(&a, 1, 1, ctx->userdata);
+}
+
+static inline void priv_disassembly_begin(ubjw_context_t* ctx)
+{
+#ifdef UBJW_DISASSEMBLY_MODE
+ priv_ubjw_context_append(ctx, (uint8_t)'[');
+#endif
+}
+static inline void priv_disassembly_end(ubjw_context_t* ctx)
+{
+#ifdef UBJW_DISASSEMBLY_MODE
+ priv_ubjw_context_append(ctx, (uint8_t)']');
+#endif
+}
+static inline void priv_disassembly_indent(ubjw_context_t* ctx)
+{
+#ifdef UBJW_DISASSEMBLY_MODE
+ int n = ctx->head - ctx->container_stack;
+ int i;
+ priv_ubjw_context_append(ctx, (uint8_t)'\n');
+ for (i = 0; i < n; i++)
+ {
+ priv_ubjw_context_append(ctx, (uint8_t)'\t');
+ }
+#endif
+}
+
+static inline void priv_ubjw_context_finish_container(ubjw_context_t* ctx, struct priv_ubjw_container_t* head)
+{
+ if (head->flags & CONTAINER_IS_SIZED)
+ {
+ if (head->elements_remaining > 0)
+ {
+ //error not all elements written
+ }
+ }
+ else
+ {
+ priv_disassembly_begin(ctx);
+ if (head->flags & CONTAINER_IS_UBJ_ARRAY)
+ {
+ priv_ubjw_context_append(ctx, (uint8_t)']');
+ }
+ else if (head->flags & CONTAINER_IS_UBJ_OBJECT)
+ {
+ priv_ubjw_context_append(ctx, (uint8_t)'}');
+ }
+ priv_disassembly_end(ctx);
+ }
+}
+
+static inline priv_ubjw_container_stack_push(ubjw_context_t* ctx, const struct priv_ubjw_container_t* cnt)
+{
+ size_t height = ctx->head-ctx->container_stack+1;
+ if(height < CONTAINER_STACK_MAX)
+ {
+ *(++(ctx->head))=*cnt;
+ }
+ else
+ {
+ //todo::error
+ }
+}
+static inline struct priv_ubjw_container_t priv_ubjw_container_stack_pop(ubjw_context_t* ctx)
+{
+ return *ctx->head--;
+}
+
+size_t ubjw_close_context(ubjw_context_t* ctx)
+{
+ while (ctx->head > ctx->container_stack)
+ {
+ struct priv_ubjw_container_t cnt = priv_ubjw_container_stack_pop(ctx);
+ priv_ubjw_context_finish_container(ctx, &cnt);
+ };
+ size_t n = ctx->total_written;
+ if (ctx->close_cb)
+ ctx->close_cb(ctx->userdata);
+ mpfree(ctx);
+ return n;
+}
+
+
+static inline size_t priv_ubjw_context_write(ubjw_context_t* ctx, const uint8_t* data, size_t sz)
+{
+ ctx->total_written += sz;
+ return ctx->write_cb(data, 1, sz, ctx->userdata);
+}
+
+static inline void priv_ubjw_tag_public(ubjw_context_t* ctx, UBJ_TYPE tid)
+{
+ struct priv_ubjw_container_t* ch = ctx->head;
+ if (!ctx->ignore_container_flags)
+ {
+
+ /*if (
+ (!(ch->flags & (CONTAINER_IS_UBJ_ARRAY | CONTAINER_IS_UBJ_OBJECT))) &&
+ (tid != UBJ_ARRAY && tid !=UBJ_OBJECT))
+ {
+ //error, only array and object can be first written
+ }*/
+
+ if (ch->flags & CONTAINER_IS_UBJ_OBJECT)
+ {
+ if (ch->flags & CONTAINER_EXPECTS_KEY)
+ {
+ //error,a key expected
+ return;
+ }
+ ch->flags |= CONTAINER_EXPECTS_KEY; //set key expected next time in this context
+ }
+ else
+ {
+ priv_disassembly_indent(ctx);
+ }
+
+ if (ch->flags & CONTAINER_IS_SIZED)
+ {
+ ch->elements_remaining--; //todo: error if elements remaining is 0;
+ }
+
+ if ((ch->flags & CONTAINER_IS_TYPED) && ch->type == tid)
+ {
+ return;
+ }
+ }
+ priv_disassembly_begin(ctx);
+ priv_ubjw_context_append(ctx, UBJI_TYPEC_convert[tid]);
+ priv_disassembly_end(ctx);
+}
+
+static inline void priv_ubjw_write_raw_string(ubjw_context_t* ctx, const char* out)//TODO: possibly use a safe string
+{
+ size_t n = strlen(out);
+ ctx->ignore_container_flags = 1;
+ ubjw_write_integer(ctx, (int64_t)n);
+ ctx->ignore_container_flags = 0;
+ priv_disassembly_begin(ctx);
+ priv_ubjw_context_write(ctx, (const uint8_t*)out, n);
+ priv_disassembly_end(ctx);
+}
+void ubjw_write_string(ubjw_context_t* ctx, const char* out)
+{
+ priv_ubjw_tag_public(ctx,UBJ_STRING);
+ priv_ubjw_write_raw_string(ctx, out);
+}
+
+static inline void priv_ubjw_write_raw_char(ubjw_context_t* ctx, char out)
+{
+ priv_disassembly_begin(ctx);
+ priv_ubjw_context_append(ctx, (uint8_t)out);
+ priv_disassembly_end(ctx);
+}
+void ubjw_write_char(ubjw_context_t* ctx, char out)
+{
+ priv_ubjw_tag_public(ctx,UBJ_CHAR);
+ priv_ubjw_write_raw_char(ctx, out);
+}
+
+#ifndef min
+static inline size_t min(size_t x,size_t y)
+{
+ return x < y ? x : y;
+}
+#endif
+
+#ifdef UBJW_DISASSEMBLY_MODE
+#include <stdarg.h>
+#define DISASSEMBLY_PRINT_BUFFER_SIZE 1024
+
+static inline priv_disassembly_print(ubjw_context_t* ctx, const char* format,...)
+{
+ char buffer[DISASSEMBLY_PRINT_BUFFER_SIZE];
+ va_list args;
+ va_start(args, format);
+ int n=vsnprintf(buffer, DISASSEMBLY_PRINT_BUFFER_SIZE, format, args);
+ n = min(n, DISASSEMBLY_PRINT_BUFFER_SIZE);
+ priv_ubjw_context_write(ctx, buffer,n);
+ va_end(args);
+}
+#endif
+
+static inline void priv_ubjw_write_raw_uint8(ubjw_context_t* ctx, uint8_t out)
+{
+ priv_disassembly_begin(ctx);
+#ifndef UBJW_DISASSEMBLY_MODE
+ priv_ubjw_context_append(ctx, out);
+#else
+ priv_disassembly_print(ctx, "%hhu", out);
+#endif
+ priv_disassembly_end(ctx);
+}
+void ubjw_write_uint8(ubjw_context_t* ctx, uint8_t out)
+{
+ priv_ubjw_tag_public(ctx,UBJ_UINT8);
+ priv_ubjw_write_raw_uint8(ctx, out);
+}
+
+static inline void priv_ubjw_write_raw_int8(ubjw_context_t* ctx, int8_t out)
+{
+ priv_disassembly_begin(ctx);
+#ifndef UBJW_DISASSEMBLY_MODE
+ priv_ubjw_context_append(ctx, *(uint8_t*)&out);
+#else
+ priv_disassembly_print(ctx, "%hhd", out);
+#endif
+ priv_disassembly_end(ctx);
+}
+void ubjw_write_int8(ubjw_context_t* ctx, int8_t out)
+{
+ priv_ubjw_tag_public(ctx,UBJ_INT8);
+ priv_ubjw_write_raw_int8(ctx, out);
+}
+
+static inline void priv_ubjw_write_raw_int16(ubjw_context_t* ctx, int16_t out)
+{
+ priv_disassembly_begin(ctx);
+#ifndef UBJW_DISASSEMBLY_MODE
+ uint8_t buf[2];
+ _to_bigendian16(buf, *(uint16_t*)&out);
+ priv_ubjw_context_write(ctx, buf, 2);
+#else
+ priv_disassembly_print(ctx, "%hd", out);
+#endif
+ priv_disassembly_end(ctx);
+}
+void ubjw_write_int16(ubjw_context_t* ctx, int16_t out)
+{
+ priv_ubjw_tag_public(ctx,UBJ_INT16);
+ priv_ubjw_write_raw_int16(ctx, out);
+}
+static inline void priv_ubjw_write_raw_int32(ubjw_context_t* ctx, int32_t out)
+{
+ priv_disassembly_begin(ctx);
+#ifndef UBJW_DISASSEMBLY_MODE
+ uint8_t buf[4];
+ _to_bigendian32(buf, *(uint32_t*)&out);
+ priv_ubjw_context_write(ctx, buf, 4);
+#else
+ priv_disassembly_print(ctx, "%ld", out);
+#endif
+ priv_disassembly_end(ctx);
+}
+void ubjw_write_int32(ubjw_context_t* ctx, int32_t out)
+{
+ priv_ubjw_tag_public(ctx,UBJ_INT32);
+ priv_ubjw_write_raw_int32(ctx, out);
+}
+static inline void priv_ubjw_write_raw_int64(ubjw_context_t* ctx, int64_t out)
+{
+ priv_disassembly_begin(ctx);
+#ifndef UBJW_DISASSEMBLY_MODE
+ uint8_t buf[8];
+ _to_bigendian64(buf, *(uint64_t*)&out);
+ priv_ubjw_context_write(ctx, buf, 8);
+#else
+ priv_disassembly_print(ctx, "%lld", out);
+#endif
+ priv_disassembly_end(ctx);
+}
+void ubjw_write_int64(ubjw_context_t* ctx, int64_t out)
+{
+ priv_ubjw_tag_public(ctx,UBJ_INT64);
+ priv_ubjw_write_raw_int64(ctx, out);
+}
+
+void ubjw_write_high_precision(ubjw_context_t* ctx, const char* hp)
+{
+ priv_ubjw_tag_public(ctx,UBJ_HIGH_PRECISION);
+ priv_ubjw_write_raw_string(ctx, hp);
+}
+UBJ_TYPE ubjw_min_integer_type(int64_t in)
+{
+ uint64_t mc = llabs(in);
+ if (mc < 0x80)
+ {
+ return UBJ_INT8;
+ }
+ else if (in > 0 && mc < 0x100)
+ {
+ return UBJ_UINT8;
+ }
+ else if (mc < 0x8000)
+ {
+ return UBJ_INT16;
+ }
+ else if (mc < 0x80000000)
+ {
+ return UBJ_INT32;
+ }
+ else
+ {
+ return UBJ_INT64;
+ }
+}
+
+void ubjw_write_integer(ubjw_context_t* ctx, int64_t out)
+{
+ switch (ubjw_min_integer_type(out))
+ {
+ case UBJ_INT8:
+ ubjw_write_int8(ctx, (int8_t)out);
+ break;
+ case UBJ_UINT8:
+ ubjw_write_uint8(ctx, (uint8_t)out);
+ break;
+ case UBJ_INT16:
+ ubjw_write_int16(ctx, (int16_t)out);
+ break;
+ case UBJ_INT32:
+ ubjw_write_int32(ctx, (int32_t)out);
+ break;
+ default:
+ ubjw_write_int64(ctx, out);
+ break;
+ };
+}
+
+static inline void priv_ubjw_write_raw_float32(ubjw_context_t* ctx, float out)
+{
+ priv_disassembly_begin(ctx);
+#ifndef UBJW_DISASSEMBLY_MODE
+ uint32_t fout = *(uint32_t*)&out;
+ uint8_t outbuf[4];
+ _to_bigendian32(outbuf, fout);
+ priv_ubjw_context_write(ctx, outbuf, 4);
+#else
+ priv_disassembly_print(ctx, "%g", out);
+#endif
+ priv_disassembly_end(ctx);
+
+}
+void ubjw_write_float32(ubjw_context_t* ctx, float out)
+{
+ priv_ubjw_tag_public(ctx,UBJ_FLOAT32);
+ priv_ubjw_write_raw_float32(ctx, out);
+}
+static inline void priv_ubjw_write_raw_float64(ubjw_context_t* ctx, double out)
+{
+ priv_disassembly_begin(ctx);
+#ifndef UBJW_DISASSEMBLY_MODE
+ uint64_t fout = *(uint64_t*)&out;
+ uint8_t outbuf[8];
+ _to_bigendian64(outbuf, fout);
+ priv_ubjw_context_write(ctx, outbuf, 8);
+#else
+ priv_disassembly_print(ctx, "%g", out);
+#endif
+ priv_disassembly_end(ctx);
+}
+void ubjw_write_float64(ubjw_context_t* ctx, double out)
+{
+ priv_ubjw_tag_public(ctx,UBJ_FLOAT64);
+ priv_ubjw_write_raw_float64(ctx, out);
+}
+
+void ubjw_write_floating_point(ubjw_context_t* ctx, double out)
+{
+ //this may not be possible to implement correctly...for now we just write it as a float64'
+ ubjw_write_float64(ctx,out);
+}
+
+void ubjw_write_noop(ubjw_context_t* ctx)
+{
+ priv_ubjw_tag_public(ctx,UBJ_NOOP);
+}
+void ubjw_write_null(ubjw_context_t* ctx)
+{
+ priv_ubjw_tag_public(ctx,UBJ_NULLTYPE);
+}
+void ubjw_write_bool(ubjw_context_t* ctx, uint8_t out)
+{
+ priv_ubjw_tag_public(ctx,(out ? UBJ_BOOL_TRUE : UBJ_BOOL_FALSE));
+}
+
+void priv_ubjw_begin_container(struct priv_ubjw_container_t* cnt, ubjw_context_t* ctx, UBJ_TYPE typ, size_t count)
+{
+ cnt->flags=0;
+ cnt->elements_remaining = count;
+ cnt->type = typ;
+
+ if (typ != UBJ_MIXED)
+ {
+ if (count == 0)
+ {
+ //error and return;
+ }
+ priv_disassembly_begin(ctx);
+ priv_ubjw_context_append(ctx, '$');
+ priv_disassembly_end(ctx);
+
+ priv_disassembly_begin(ctx);
+ priv_ubjw_context_append(ctx, UBJI_TYPEC_convert[typ]);
+ priv_disassembly_end(ctx);
+
+ cnt->flags |= CONTAINER_IS_TYPED;
+ }
+ if (count != 0)
+ {
+ priv_disassembly_begin(ctx);
+ priv_ubjw_context_append(ctx, '#');
+ priv_disassembly_end(ctx);
+
+ ctx->ignore_container_flags = 1;
+ ubjw_write_integer(ctx, (int64_t)count);
+ ctx->ignore_container_flags = 0;
+
+ cnt->flags |= CONTAINER_IS_SIZED;
+ cnt->elements_remaining = count;
+ }
+}
+void ubjw_begin_array(ubjw_context_t* ctx, UBJ_TYPE type, size_t count)
+{
+ priv_ubjw_tag_public(ctx, UBJ_ARRAY); //todo: should this happen before any erro potential?
+ struct priv_ubjw_container_t ch;
+ priv_ubjw_begin_container(&ch, ctx, type, count);
+ ch.flags |= CONTAINER_IS_UBJ_ARRAY;
+ priv_ubjw_container_stack_push(ctx, &ch);
+}
+void ubjw_begin_ndarray(ubjw_context_t* dst, UBJ_TYPE type, const size_t* dims, uint8_t ndims);
+void ubjw_write_ndbuffer(ubjw_context_t* dst, const uint8_t* data, UBJ_TYPE type, const size_t* dims, uint8_t ndims);
+
+void ubjw_begin_object(ubjw_context_t* ctx, UBJ_TYPE type, size_t count)
+{
+ priv_ubjw_tag_public(ctx, UBJ_OBJECT);
+ struct priv_ubjw_container_t ch;
+ priv_ubjw_begin_container(&ch, ctx, type, count);
+ ch.flags |= CONTAINER_EXPECTS_KEY | CONTAINER_IS_UBJ_OBJECT;
+ priv_ubjw_container_stack_push(ctx, &ch);
+}
+void ubjw_write_key(ubjw_context_t* ctx, const char* key)
+{
+ if (ctx->head->flags & CONTAINER_EXPECTS_KEY && ctx->head->flags & CONTAINER_IS_UBJ_OBJECT)
+ {
+ priv_disassembly_indent(ctx);
+ priv_ubjw_write_raw_string(ctx, key);
+ ctx->head->flags ^= CONTAINER_EXPECTS_KEY; //turn off container
+ }
+ else
+ {
+ //error unexpected key
+ }
+}
+void ubjw_end(ubjw_context_t* ctx)
+{
+ struct priv_ubjw_container_t ch = priv_ubjw_container_stack_pop(ctx);
+ if ((ch.flags & CONTAINER_IS_UBJ_OBJECT) && !(ch.flags & CONTAINER_EXPECTS_KEY))
+ {
+ //error expected value
+ }
+ priv_disassembly_indent(ctx);
+ priv_ubjw_context_finish_container(ctx, &ch);
+}
+
+
+// Not used by benchmark.py -> high BUFFER_OUT_SIZE does not matter
+static inline void priv_ubjw_write_byteswap(ubjw_context_t* ctx, const uint8_t* data, int sz, size_t count)
+{
+ uint8_t buf[BUFFER_OUT_SIZE];
+
+ size_t i;
+ size_t nbytes = sz*count;
+ for (i = 0; i < nbytes; i+=BUFFER_OUT_SIZE)
+ {
+ size_t npass = min(nbytes - i, BUFFER_OUT_SIZE);
+ memcpy(buf, data + i, npass);
+ buf_endian_swap(buf, sz, npass/sz);
+ priv_ubjw_context_write(ctx, buf, npass);
+ }
+}
+void ubjw_write_buffer(ubjw_context_t* ctx, const uint8_t* data, UBJ_TYPE type, size_t count)
+{
+ int typesz = UBJI_TYPE_size[type];
+ if (typesz < 0)
+ {
+ //error cannot write an STC buffer of this type.
+ }
+ ubjw_begin_array(ctx, type, count);
+ if (type == UBJ_STRING || type == UBJ_HIGH_PRECISION)
+ {
+ const char** databufs = (const char**)data;
+ size_t i;
+ for (i = 0; i < count; i++)
+ {
+ priv_ubjw_write_raw_string(ctx, databufs[i]);
+ }
+ }
+#ifndef UBJW_DISASSEMBLY_MODE
+ else if (typesz == 1 || _is_bigendian())
+ {
+ size_t n = typesz*count;
+ priv_ubjw_context_write(ctx, data, typesz*count);
+ }
+ else if (typesz > 1) //and not big-endian
+ {
+ priv_ubjw_write_byteswap(ctx, data,typesz,count);
+ }
+#else
+ else
+ {
+ size_t i;
+ for (i = 0; i < count; i++)
+ {
+ ubjr_dynamic_t dyn = priv_ubjr_pointer_to_dynamic(type, data + i*typesz);
+ ubjrw_write_dynamic(ctx, dyn, 0);
+ }
+ }
+#endif
+ ubjw_end(ctx);
+}
diff --git a/src/lib/xdr.cc b/src/lib/xdr.cc
new file mode 100644
index 0000000..b44471f
--- /dev/null
+++ b/src/lib/xdr.cc
@@ -0,0 +1,188 @@
+#include "lib/xdr.h"
+
+void XDRWriter::put(uint32_t number)
+{
+ *buffer++ = ((number >> 24) & 0xffU);
+ *buffer++ = ((number >> 16) & 0xffU);
+ *buffer++ = ((number >> 8) & 0xffU);
+ *buffer++ = (number & 0xffU);
+ pos += 4;
+}
+
+void XDRWriter::put(int32_t number)
+{
+ *buffer++ = ((number >> 24) & 0xffU);
+ *buffer++ = ((number >> 16) & 0xffU);
+ *buffer++ = ((number >> 8) & 0xffU);
+ *buffer++ = (number & 0xffU);
+ pos += 4;
+}
+
+void XDRWriter::put(uint64_t number)
+{
+ *buffer++ = ((number >> 56) & 0xffU);
+ *buffer++ = ((number >> 48) & 0xffU);
+ *buffer++ = ((number >> 40) & 0xffU);
+ *buffer++ = ((number >> 32) & 0xffU);
+ *buffer++ = ((number >> 24) & 0xffU);
+ *buffer++ = ((number >> 16) & 0xffU);
+ *buffer++ = ((number >> 8) & 0xffU);
+ *buffer++ = (number & 0xffU);
+ pos += 8;
+}
+
+void XDRWriter::put(int64_t number)
+{
+ *buffer++ = ((number >> 56) & 0xffU);
+ *buffer++ = ((number >> 48) & 0xffU);
+ *buffer++ = ((number >> 40) & 0xffU);
+ *buffer++ = ((number >> 32) & 0xffU);
+ *buffer++ = ((number >> 24) & 0xffU);
+ *buffer++ = ((number >> 16) & 0xffU);
+ *buffer++ = ((number >> 8) & 0xffU);
+ *buffer++ = (number & 0xffU);
+ pos += 8;
+}
+
+void XDRWriter::put(float number)
+{
+ union {
+ uint32_t i;
+ float f;
+ } v;
+ // Setting one member of a struct and then reading another is undefined
+ // behaviour, but works as intended in nearly any (embedded) compiler
+ v.f = number;
+ put(v.i);
+}
+
+void XDRWriter::put(double number)
+{
+ union {
+ uint64_t i;
+ double d;
+ } v;
+ // Setting one member of a struct and then reading another is undefined
+ // behaviour, but works as intended in nearly any (embedded) compiler
+ v.d = number;
+ put(v.i);
+}
+
+void XDRWriter::put(char const *data)
+{
+ if (!is_fixed_length)
+ {
+ put(next_array_len);
+ }
+ uint32_t i;
+ for (i = 0; i < next_array_len; i++)
+ {
+ *buffer++ = data[i];
+ }
+ while ((i++) % 4 != 0)
+ {
+ *buffer++ = 0;
+ }
+ pos += i - 1;
+}
+
+template <uint32_t TSize>
+void XDRWriter::put(char const (&data)[TSize])
+{
+ if (!is_fixed_length)
+ {
+ put(TSize);
+ }
+ uint32_t i;
+ for (i = 0; i < TSize; i++)
+ {
+ *buffer++ = data[i];
+ }
+ while ((i++) % 4 != 0)
+ {
+ *buffer++ = 0;
+ }
+ pos += i - 1;
+}
+
+uint32_t XDRReader::get_uint32()
+{
+ uint32_t ret = (((uint32_t)data[pos] & 0xffU) << 24) | (((uint32_t)data[pos + 1] & 0xffU) << 16) | (((uint32_t)data[pos + 2] & 0xffU) << 8) | ((uint32_t)data[pos + 3] & 0xffU);
+ pos += 4;
+ return ret;
+}
+
+int32_t XDRReader::get_int32()
+{
+ int32_t ret = (((int32_t)data[pos] & 0xff) << 24) | (((int32_t)data[pos + 1] & 0xff) << 16) | (((int32_t)data[pos + 2] & 0xff) << 8) | ((int32_t)data[pos + 3] & 0xff);
+ pos += 4;
+ return ret;
+}
+
+uint64_t XDRReader::get_uint64()
+{
+ uint64_t ret = (((uint64_t)data[pos] & 0xffU) << 56) | (((uint64_t)data[pos + 1] & 0xffU) << 48) | (((uint64_t)data[pos + 2] & 0xffU) << 40) | (((uint64_t)data[pos + 3] & 0xffU) << 32) | (((uint64_t)data[pos + 4] & 0xffU) << 24) | (((uint64_t)data[pos + 5] & 0xffU) << 16) | (((uint64_t)data[pos + 6] & 0xffU) << 8) | ((uint64_t)data[pos + 7] & 0xffU);
+ return ret;
+}
+
+int64_t XDRReader::get_int64()
+{
+ int64_t ret = (((int64_t)data[pos] & 0xff) << 56) | (((int64_t)data[pos + 1] & 0xff) << 48) | (((int64_t)data[pos + 2] & 0xff) << 40) | (((int64_t)data[pos + 3] & 0xff) << 32) | (((int64_t)data[pos + 4] & 0xff) << 24) | (((int64_t)data[pos + 5] & 0xff) << 16) | (((int64_t)data[pos + 6] & 0xff) << 8) | ((int64_t)data[pos + 7] & 0xff);
+ return ret;
+}
+
+float XDRReader::get_float()
+{
+ union {
+ uint32_t i;
+ float f;
+ } v;
+ // Setting one member of a struct and then reading another is undefined
+ // behaviour, but works as intended in nearly any (embedded) compiler
+ v.i = get_uint32();
+ return v.f;
+}
+
+double XDRReader::get_double()
+{
+ union {
+ uint64_t i;
+ double d;
+ } v;
+ // Setting one member of a struct and then reading another is undefined
+ // behaviour, but works as intended in nearly any (embedded) compiler
+ v.i = get_uint64();
+ return v.d;
+}
+
+uint32_t XDRReader::get_opaque_length()
+{
+ return get_uint32();
+}
+
+char *XDRReader::get_opaque(uint32_t length)
+{
+ char *ret = data + pos;
+ pos += length;
+ if (length % 4)
+ {
+ pos += 4 - (length % 4);
+ }
+ return ret;
+}
+
+void XDRReader::get_string(char *target)
+{
+ uint16_t length = get_opaque_length();
+ uint16_t i;
+ for (i = 0; i < length; i++)
+ {
+ target[i] = data[pos + i];
+ }
+ target[i] = 0;
+ pos += length;
+ if (length % 4)
+ {
+ pos += 4 - (length % 4);
+ }
+}
diff --git a/src/lib/xdr16.cc b/src/lib/xdr16.cc
new file mode 100644
index 0000000..b1fb7ca
--- /dev/null
+++ b/src/lib/xdr16.cc
@@ -0,0 +1,216 @@
+#include "lib/xdr16.h"
+
+void XDRWriter::put(uint16_t number)
+{
+ *buffer++ = ((number >> 8) & 0xffU);
+ *buffer++ = (number & 0xffU);
+ pos += 2;
+}
+
+void XDRWriter::put(int16_t number)
+{
+ *buffer++ = ((number >> 8) & 0xffU);
+ *buffer++ = (number & 0xffU);
+ pos += 2;
+}
+
+void XDRWriter::put(uint32_t number)
+{
+ *buffer++ = ((number >> 24) & 0xffU);
+ *buffer++ = ((number >> 16) & 0xffU);
+ *buffer++ = ((number >> 8) & 0xffU);
+ *buffer++ = (number & 0xffU);
+ pos += 4;
+}
+
+void XDRWriter::put(int32_t number)
+{
+ *buffer++ = ((number >> 24) & 0xffU);
+ *buffer++ = ((number >> 16) & 0xffU);
+ *buffer++ = ((number >> 8) & 0xffU);
+ *buffer++ = (number & 0xffU);
+ pos += 4;
+}
+
+void XDRWriter::put(uint64_t number)
+{
+ *buffer++ = ((number >> 56) & 0xffU);
+ *buffer++ = ((number >> 48) & 0xffU);
+ *buffer++ = ((number >> 40) & 0xffU);
+ *buffer++ = ((number >> 32) & 0xffU);
+ *buffer++ = ((number >> 24) & 0xffU);
+ *buffer++ = ((number >> 16) & 0xffU);
+ *buffer++ = ((number >> 8) & 0xffU);
+ *buffer++ = (number & 0xffU);
+ pos += 8;
+}
+
+void XDRWriter::put(int64_t number)
+{
+ *buffer++ = ((number >> 56) & 0xffU);
+ *buffer++ = ((number >> 48) & 0xffU);
+ *buffer++ = ((number >> 40) & 0xffU);
+ *buffer++ = ((number >> 32) & 0xffU);
+ *buffer++ = ((number >> 24) & 0xffU);
+ *buffer++ = ((number >> 16) & 0xffU);
+ *buffer++ = ((number >> 8) & 0xffU);
+ *buffer++ = (number & 0xffU);
+ pos += 8;
+}
+
+void XDRWriter::put(float number)
+{
+ union {
+ uint32_t i;
+ float f;
+ } v;
+ // Setting one member of a struct and then reading another is undefined
+ // behaviour, but works as intended in nearly any (embedded) compiler
+ v.f = number;
+ put(v.i);
+}
+
+void XDRWriter::put(double number)
+{
+ union {
+ uint64_t i;
+ double d;
+ } v;
+ // Setting one member of a struct and then reading another is undefined
+ // behaviour, but works as intended in nearly any (embedded) compiler
+ v.d = number;
+ put(v.i);
+}
+
+void XDRWriter::put(char const *data)
+{
+ if (!is_fixed_length)
+ {
+ put(next_array_len);
+ }
+ uint16_t i;
+ for (i = 0; i < next_array_len; i++)
+ {
+ *buffer++ = data[i];
+ }
+ while ((i++) % 2 != 0)
+ {
+ *buffer++ = 0;
+ }
+ pos += i - 1;
+}
+
+template <uint16_t TSize>
+void XDRWriter::put(char const (&data)[TSize])
+{
+ if (!is_fixed_length)
+ {
+ put(TSize);
+ }
+ uint16_t i;
+ for (i = 0; i < TSize; i++)
+ {
+ *buffer++ = data[i];
+ }
+ while ((i++) % 2 != 0)
+ {
+ *buffer++ = 0;
+ }
+ pos += i - 1;
+}
+
+uint16_t XDRReader::get_uint16()
+{
+ uint16_t ret = (((uint16_t)data[pos] & 0xffU) << 8) | ((uint16_t)data[pos + 1] & 0xffU);
+ pos += 2;
+ return ret;
+}
+
+int16_t XDRReader::get_int16()
+{
+ int16_t ret = (((int16_t)data[pos] & 0xff) << 8) | ((int16_t)data[pos + 1] & 0xff);
+ pos += 2;
+ return ret;
+}
+
+uint32_t XDRReader::get_uint32()
+{
+ uint32_t ret = (((uint32_t)data[pos] & 0xffU) << 24) | (((uint32_t)data[pos + 1] & 0xffU) << 16) | (((uint32_t)data[pos + 2] & 0xffU) << 8) | ((uint32_t)data[pos + 3] & 0xffU);
+ pos += 4;
+ return ret;
+}
+
+int32_t XDRReader::get_int32()
+{
+ int32_t ret = (((int32_t)data[pos] & 0xff) << 24) | (((int32_t)data[pos + 1] & 0xff) << 16) | (((int32_t)data[pos + 2] & 0xff) << 8) | ((int32_t)data[pos + 3] & 0xff);
+ pos += 4;
+ return ret;
+}
+
+uint64_t XDRReader::get_uint64()
+{
+ uint64_t ret = (((uint64_t)data[pos] & 0xffU) << 56) | (((uint64_t)data[pos + 1] & 0xffU) << 48) | (((uint64_t)data[pos + 2] & 0xffU) << 40) | (((uint64_t)data[pos + 3] & 0xffU) << 32) | (((uint64_t)data[pos + 4] & 0xffU) << 24) | (((uint64_t)data[pos + 5] & 0xffU) << 16) | (((uint64_t)data[pos + 6] & 0xffU) << 8) | ((uint64_t)data[pos + 7] & 0xffU);
+ return ret;
+}
+
+int64_t XDRReader::get_int64()
+{
+ int64_t ret = (((int64_t)data[pos] & 0xff) << 56) | (((int64_t)data[pos + 1] & 0xff) << 48) | (((int64_t)data[pos + 2] & 0xff) << 40) | (((int64_t)data[pos + 3] & 0xff) << 32) | (((int64_t)data[pos + 4] & 0xff) << 24) | (((int64_t)data[pos + 5] & 0xff) << 16) | (((int64_t)data[pos + 6] & 0xff) << 8) | ((int64_t)data[pos + 7] & 0xff);
+ return ret;
+}
+
+float XDRReader::get_float()
+{
+ union {
+ uint32_t i;
+ float f;
+ } v;
+ // Setting one member of a struct and then reading another is undefined
+ // behaviour, but works as intended in nearly any (embedded) compiler
+ v.i = get_uint32();
+ return v.f;
+}
+
+double XDRReader::get_double()
+{
+ union {
+ uint64_t i;
+ double d;
+ } v;
+ // Setting one member of a struct and then reading another is undefined
+ // behaviour, but works as intended in nearly any (embedded) compiler
+ v.i = get_uint64();
+ return v.d;
+}
+
+uint32_t XDRReader::get_opaque_length()
+{
+ return get_uint16();
+}
+
+char *XDRReader::get_opaque(uint32_t length)
+{
+ char *ret = data + pos;
+ pos += length;
+ if (length % 2)
+ {
+ pos += 2 - (length % 2);
+ }
+ return ret;
+}
+
+void XDRReader::get_string(char *target)
+{
+ uint16_t length = get_opaque_length();
+ uint16_t i;
+ for (i = 0; i < length; i++)
+ {
+ target[i] = data[pos + i];
+ }
+ target[i] = 0;
+ pos += length;
+ if (length % 2)
+ {
+ pos += 2 - (length % 2);
+ }
+}