From 0558244645611f314f47e0fa427f7323ce253eaf Mon Sep 17 00:00:00 2001
From: Daniel Friesel <daniel.friesel@uos.de>
Date: Mon, 7 Sep 2020 12:57:04 +0200
Subject: remove external libraries from main branch

---
 src/lib/binn.cc                | 3372 ---------------------
 src/lib/capnp-c/capn-malloc.cc |  424 ---
 src/lib/capnp-c/capn-stream.cc |  217 --
 src/lib/capnp-c/capn.cc        | 1117 -------
 src/lib/mpack/mpack.cc         | 6440 ----------------------------------------
 src/lib/mpmalloc.cc            |   42 -
 src/lib/nanopb/pb_common.cc    |   97 -
 src/lib/nanopb/pb_decode.cc    | 1528 ----------
 src/lib/nanopb/pb_encode.cc    |  893 ------
 src/lib/ubjson/ubjr.c          |  525 ----
 src/lib/ubjson/ubjrw.c         |  169 --
 src/lib/ubjson/ubjw.c          |  626 ----
 src/lib/xdr.cc                 |  188 --
 src/lib/xdr16.cc               |  216 --
 14 files changed, 15854 deletions(-)
 delete mode 100644 src/lib/binn.cc
 delete mode 100644 src/lib/capnp-c/capn-malloc.cc
 delete mode 100644 src/lib/capnp-c/capn-stream.cc
 delete mode 100644 src/lib/capnp-c/capn.cc
 delete mode 100644 src/lib/mpack/mpack.cc
 delete mode 100644 src/lib/mpmalloc.cc
 delete mode 100644 src/lib/nanopb/pb_common.cc
 delete mode 100644 src/lib/nanopb/pb_decode.cc
 delete mode 100644 src/lib/nanopb/pb_encode.cc
 delete mode 100644 src/lib/ubjson/ubjr.c
 delete mode 100644 src/lib/ubjson/ubjrw.c
 delete mode 100644 src/lib/ubjson/ubjw.c
 delete mode 100644 src/lib/xdr.cc
 delete mode 100644 src/lib/xdr16.cc

(limited to 'src/lib')

diff --git a/src/lib/binn.cc b/src/lib/binn.cc
deleted file mode 100644
index b7e4839..0000000
--- a/src/lib/binn.cc
+++ /dev/null
@@ -1,3372 +0,0 @@
-#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
deleted file mode 100644
index 79f08c8..0000000
--- a/src/lib/capnp-c/capn-malloc.cc
+++ /dev/null
@@ -1,424 +0,0 @@
-/* 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
deleted file mode 100644
index 135d1b2..0000000
--- a/src/lib/capnp-c/capn-stream.cc
+++ /dev/null
@@ -1,217 +0,0 @@
-/* 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
deleted file mode 100644
index bbd7be3..0000000
--- a/src/lib/capnp-c/capn.cc
+++ /dev/null
@@ -1,1117 +0,0 @@
-/* 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
deleted file mode 100644
index 67e54e8..0000000
--- a/src/lib/mpack/mpack.cc
+++ /dev/null
@@ -1,6440 +0,0 @@
-/**
- * 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
deleted file mode 100644
index 2483a3a..0000000
--- a/src/lib/mpmalloc.cc
+++ /dev/null
@@ -1,42 +0,0 @@
-#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
deleted file mode 100644
index 4fb7186..0000000
--- a/src/lib/nanopb/pb_common.cc
+++ /dev/null
@@ -1,97 +0,0 @@
-/* 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
deleted file mode 100644
index d08259c..0000000
--- a/src/lib/nanopb/pb_decode.cc
+++ /dev/null
@@ -1,1528 +0,0 @@
-/* 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
deleted file mode 100644
index f37ceec..0000000
--- a/src/lib/nanopb/pb_encode.cc
+++ /dev/null
@@ -1,893 +0,0 @@
-/* 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
deleted file mode 100644
index 7bb2b2f..0000000
--- a/src/lib/ubjson/ubjr.c
+++ /dev/null
@@ -1,525 +0,0 @@
-#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
deleted file mode 100644
index ee43d70..0000000
--- a/src/lib/ubjson/ubjrw.c
+++ /dev/null
@@ -1,169 +0,0 @@
-#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
deleted file mode 100644
index 9fce397..0000000
--- a/src/lib/ubjson/ubjw.c
+++ /dev/null
@@ -1,626 +0,0 @@
-#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
deleted file mode 100644
index b44471f..0000000
--- a/src/lib/xdr.cc
+++ /dev/null
@@ -1,188 +0,0 @@
-#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
deleted file mode 100644
index b1fb7ca..0000000
--- a/src/lib/xdr16.cc
+++ /dev/null
@@ -1,216 +0,0 @@
-#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);
-	}
-}
-- 
cgit v1.2.3