diff --git a/3party/tpl/tpl.c b/3party/tpl/tpl.c new file mode 100644 index 0000000..84035fb --- /dev/null +++ b/3party/tpl/tpl.c @@ -0,0 +1,2489 @@ +/* +Copyright (c) 2005-2013, Troy D. Hanson http://troydhanson.github.com/tpl/ +All rights reserved. + +Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define TPL_VERSION 1.6 + + static const char id[]="$Id: tpl.c 192 2009-04-24 10:35:30Z thanson $"; + + +#include /* malloc */ +#include /* va_list */ +#include /* memcpy, memset, strchr */ +#include /* printf (tpl_hook.oops default function) */ + +#ifndef _WIN32 +#include /* for ftruncate */ +#else +#include +#define ftruncate(x,y) _chsize(x,y) +#endif +#include /* for 'open' */ +#include /* for 'open' */ +#include /* for 'open' */ +#include +#ifndef _WIN32 +#include /* uint32_t, uint64_t, etc */ +#else +typedef unsigned short ushort; +typedef __int16 int16_t; +typedef __int32 int32_t; +typedef __int64 int64_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int64 uint64_t; +#endif + +#ifndef S_ISREG +#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) +#endif + +#if ( defined __CYGWIN__ || defined __MINGW32__ || defined _WIN32 ) +#include "win/mman.h" /* mmap */ +#else +#include /* mmap */ +#endif + +#include "tpl.h" + +#define TPL_GATHER_BUFLEN 8192 +#define TPL_MAGIC "tpl" + +/* macro to add a structure to a doubly-linked list */ +#define DL_ADD(head,add) \ + do { \ + if (head) { \ + (add)->prev = (head)->prev; \ + (head)->prev->next = (add); \ + (head)->prev = (add); \ + (add)->next = NULL; \ + } else { \ + (head)=(add); \ + (head)->prev = (head); \ + (head)->next = NULL; \ + } \ + } while (0); + +#define fatal_oom() tpl_hook.fatal("out of memory\n") + +/* bit flags (internal). preceded by the external flags in tpl.h */ +#define TPL_WRONLY (1 << 9) /* app has initiated tpl packing */ +#define TPL_RDONLY (1 << 10) /* tpl was loaded (for unpacking) */ +#define TPL_XENDIAN (1 << 11) /* swap endianness when unpacking */ +#define TPL_OLD_STRING_FMT (1 << 12) /* tpl has strings in 1.2 format */ + +/* values for the flags byte that appears after the magic prefix */ +#define TPL_SUPPORTED_BITFLAGS 3 +#define TPL_FL_BIGENDIAN (1 << 0) +#define TPL_FL_NULLSTRINGS (1 << 1) + +/* char values for node type */ +#define TPL_TYPE_ROOT 0 +#define TPL_TYPE_INT32 1 +#define TPL_TYPE_UINT32 2 +#define TPL_TYPE_BYTE 3 +#define TPL_TYPE_STR 4 +#define TPL_TYPE_ARY 5 +#define TPL_TYPE_BIN 6 +#define TPL_TYPE_DOUBLE 7 +#define TPL_TYPE_INT64 8 +#define TPL_TYPE_UINT64 9 +#define TPL_TYPE_INT16 10 +#define TPL_TYPE_UINT16 11 +#define TPL_TYPE_POUND 12 + +/* error codes */ +#define ERR_NOT_MINSIZE (-1) +#define ERR_MAGIC_MISMATCH (-2) +#define ERR_INCONSISTENT_SZ (-3) +#define ERR_FMT_INVALID (-4) +#define ERR_FMT_MISSING_NUL (-5) +#define ERR_FMT_MISMATCH (-6) +#define ERR_FLEN_MISMATCH (-7) +#define ERR_INCONSISTENT_SZ2 (-8) +#define ERR_INCONSISTENT_SZ3 (-9) +#define ERR_INCONSISTENT_SZ4 (-10) +#define ERR_UNSUPPORTED_FLAGS (-11) + +/* access to A(...) nodes by index */ +typedef struct tpl_pidx { + struct tpl_node *node; + struct tpl_pidx *next,*prev; +} tpl_pidx; + +/* A(...) node datum */ +typedef struct tpl_atyp { + uint32_t num; /* num elements */ + size_t sz; /* size of each backbone's datum */ + struct tpl_backbone *bb,*bbtail; + void *cur; +} tpl_atyp; + +/* backbone to extend A(...) lists dynamically */ +typedef struct tpl_backbone { + struct tpl_backbone *next; + /* when this structure is malloc'd, extra space is alloc'd at the + * end to store the backbone "datum", and data points to it. */ +#if __STDC_VERSION__ < 199901 + char *data; +#else + char data[]; +#endif +} tpl_backbone; + +/* mmap record */ +typedef struct tpl_mmap_rec { + int fd; + void *text; + size_t text_sz; +} tpl_mmap_rec; + +/* root node datum */ +typedef struct tpl_root_data { + int flags; + tpl_pidx *pidx; + tpl_mmap_rec mmap; + char *fmt; + int *fxlens, num_fxlens; +} tpl_root_data; + +/* node type to size mapping */ +struct tpl_type_t { + char c; + int sz; +}; + + +/* Internal prototypes */ +static tpl_node *tpl_node_new(tpl_node *parent); +static tpl_node *tpl_find_i(tpl_node *n, int i); +static void *tpl_cpv(void *datav, const void *data, size_t sz); +static void *tpl_extend_backbone(tpl_node *n); +static char *tpl_fmt(tpl_node *r); +static void *tpl_dump_atyp(tpl_node *n, tpl_atyp* at, void *dv); +static size_t tpl_ser_osz(tpl_node *n); +static void tpl_free_atyp(tpl_node *n,tpl_atyp *atyp); +static int tpl_dump_to_mem(tpl_node *r, void *addr, size_t sz); +static int tpl_mmap_file(char *filename, tpl_mmap_rec *map_rec); +static int tpl_mmap_output_file(char *filename, size_t sz, void **text_out); +static int tpl_cpu_bigendian(void); +static int tpl_needs_endian_swap(void *); +static void tpl_byteswap(void *word, int len); +static void tpl_fatal(const char *fmt, ...); +static int tpl_serlen(tpl_node *r, tpl_node *n, void *dv, size_t *serlen); +static int tpl_unpackA0(tpl_node *r); +static int tpl_oops(const char *fmt, ...); +static int tpl_gather_mem( char *buf, size_t len, tpl_gather_t **gs, tpl_gather_cb *cb, void *data); +static int tpl_gather_nonblocking( int fd, tpl_gather_t **gs, tpl_gather_cb *cb, void *data); +static int tpl_gather_blocking(int fd, void **img, size_t *sz); + +/* This is used internally to help calculate padding when a 'double' + * follows a smaller datatype in a structure. Normally under gcc + * on x86, d will be aligned at +4, however use of -malign-double + * causes d to be aligned at +8 (this is actually faster on x86). + * Also SPARC and x86_64 seem to align always on +8. + */ +struct tpl_double_alignment_detector { + char a; + double d; /* some platforms align this on +4, others on +8 */ +}; + +/* this is another case where alignment varies. mac os x/gcc was observed + * to align the int64_t at +4 under -m32 and at +8 under -m64 */ +struct tpl_int64_alignment_detector { + int i; + int64_t j; /* some platforms align this on +4, others on +8 */ +}; + +typedef struct { + size_t inter_elt_len; /* padded inter-element len; i.e. &a[1].field - &a[0].field */ + tpl_node *iter_start_node; /* node to jump back to, as we start each new iteration */ + size_t iternum; /* current iteration number (total req'd. iter's in n->num) */ +} tpl_pound_data; + +/* Hooks for customizing tpl mem alloc, error handling, etc. Set defaults. */ +tpl_hook_t tpl_hook = { + /* .oops = */ tpl_oops, + /* .malloc = */ malloc, + /* .realloc = */ realloc, + /* .free = */ free, + /* .fatal = */ tpl_fatal, + /* .gather_max = */ 0 /* max tpl size (bytes) for tpl_gather */ +}; + +static const char tpl_fmt_chars[] = "AS($)BiucsfIUjv#"; /* valid format chars */ +static const char tpl_S_fmt_chars[] = "iucsfIUjv#$()"; /* valid within S(...) */ +static const char tpl_datapeek_ok_chars[] = "iucsfIUjv"; /* valid in datapeek */ +static const struct tpl_type_t tpl_types[] = { + /* [TPL_TYPE_ROOT] = */ {'r', 0}, + /* [TPL_TYPE_INT32] = */ {'i', sizeof(int32_t)}, + /* [TPL_TYPE_UINT32] = */ {'u', sizeof(uint32_t)}, + /* [TPL_TYPE_BYTE] = */ {'c', sizeof(char)}, + /* [TPL_TYPE_STR] = */ {'s', sizeof(char*)}, + /* [TPL_TYPE_ARY] = */ {'A', 0}, + /* [TPL_TYPE_BIN] = */ {'B', 0}, + /* [TPL_TYPE_DOUBLE] = */ {'f', 8}, /* not sizeof(double) as that varies */ + /* [TPL_TYPE_INT64] = */ {'I', sizeof(int64_t)}, + /* [TPL_TYPE_UINT64] = */ {'U', sizeof(uint64_t)}, + /* [TPL_TYPE_INT16] = */ {'j', sizeof(int16_t)}, + /* [TPL_TYPE_UINT16] = */ {'v', sizeof(uint16_t)}, + /* [TPL_TYPE_POUND] = */ {'#', 0}, +}; + +/* default error-reporting function. Just writes to stderr. */ +static int tpl_oops(const char *fmt, ...) { + va_list ap; + va_start(ap,fmt); + vfprintf(stderr,fmt,ap); + va_end(ap); + return 0; +} + + +static tpl_node *tpl_node_new(tpl_node *parent) { + tpl_node *n; + if ((n=tpl_hook.malloc(sizeof(tpl_node))) == NULL) { + fatal_oom(); + } + n->addr=NULL; + n->data=NULL; + n->num=1; + n->ser_osz=0; + n->children=NULL; + n->next=NULL; + n->parent=parent; + return n; +} + +/* Used in S(..) formats to pack several fields from a structure based on + * only the structure address. We need to calculate field addresses + * manually taking into account the size of the fields and intervening padding. + * The wrinkle is that double is not normally aligned on x86-32 but the + * -malign-double compiler option causes it to be. Double are aligned + * on Sparc, and apparently on 64 bit x86. We use a helper structure + * to detect whether double is aligned in this compilation environment. + */ +char *calc_field_addr(tpl_node *parent, int type,char *struct_addr, int ordinal) { + tpl_node *prev; + int offset; + int align_sz; + + if (ordinal == 1) return struct_addr; /* first field starts on structure address */ + + /* generate enough padding so field addr is divisible by it's align_sz. 4, 8, etc */ + prev = parent->children->prev; + switch(type) { + case TPL_TYPE_DOUBLE: + align_sz = sizeof(struct tpl_double_alignment_detector) > 12 ? 8 : 4; + break; + case TPL_TYPE_INT64: + case TPL_TYPE_UINT64: + align_sz = sizeof(struct tpl_int64_alignment_detector) > 12 ? 8 : 4; + break; + default: + align_sz = tpl_types[type].sz; + break; + } + offset = ((uintptr_t)prev->addr - (uintptr_t)struct_addr) + + (tpl_types[prev->type].sz * prev->num); + offset = (offset + align_sz - 1) / align_sz * align_sz; + return struct_addr + offset; +} + +TPL_API tpl_node *tpl_map(char *fmt,...) { + va_list ap; + tpl_node *tn; + + va_start(ap,fmt); + tn = tpl_map_va(fmt, ap); + va_end(ap); + return tn; +} + +TPL_API tpl_node *tpl_map_va(char *fmt, va_list ap) { + int lparen_level=0,expect_lparen=0,t=0,in_structure=0,ordinal=0; + int in_nested_structure=0; + char *c, *peek, *struct_addr=NULL, *struct_next; + tpl_node *root,*parent,*n=NULL,*preceding,*iter_start_node=NULL, + *struct_widest_node=NULL, *np; tpl_pidx *pidx; + tpl_pound_data *pd; + int *fxlens, num_fxlens, pound_num, pound_prod, applies_to_struct; + int contig_fxlens[10]; /* temp space for contiguous fxlens */ + int num_contig_fxlens, i, j; + ptrdiff_t inter_elt_len=0; /* padded element length of contiguous structs in array */ + + + root = tpl_node_new(NULL); + root->type = TPL_TYPE_ROOT; + root->data = (tpl_root_data*)tpl_hook.malloc(sizeof(tpl_root_data)); + if (!root->data) fatal_oom(); + memset((tpl_root_data*)root->data,0,sizeof(tpl_root_data)); + + /* set up root nodes special ser_osz to reflect overhead of preamble */ + root->ser_osz = sizeof(uint32_t); /* tpl leading length */ + root->ser_osz += strlen(fmt) + 1; /* fmt + NUL-terminator */ + root->ser_osz += 4; /* 'tpl' magic prefix + flags byte */ + + parent=root; + + c=fmt; + while (*c != '\0') { + switch (*c) { + case 'c': + case 'i': + case 'u': + case 'j': + case 'v': + case 'I': + case 'U': + case 'f': + if (*c=='c') t=TPL_TYPE_BYTE; + else if (*c=='i') t=TPL_TYPE_INT32; + else if (*c=='u') t=TPL_TYPE_UINT32; + else if (*c=='j') t=TPL_TYPE_INT16; + else if (*c=='v') t=TPL_TYPE_UINT16; + else if (*c=='I') t=TPL_TYPE_INT64; + else if (*c=='U') t=TPL_TYPE_UINT64; + else if (*c=='f') t=TPL_TYPE_DOUBLE; + + if (expect_lparen) goto fail; + n = tpl_node_new(parent); + n->type = t; + if (in_structure) { + if (ordinal == 1) { + /* for S(...)# iteration. Apply any changes to case 's' too!!! */ + iter_start_node = n; + struct_widest_node = n; + } + if (tpl_types[n->type].sz > tpl_types[struct_widest_node->type].sz) { + struct_widest_node = n; + } + n->addr = calc_field_addr(parent,n->type,struct_addr,ordinal++); + } else n->addr = (void*)va_arg(ap,void*); + n->data = tpl_hook.malloc(tpl_types[t].sz); + if (!n->data) fatal_oom(); + if (n->parent->type == TPL_TYPE_ARY) + ((tpl_atyp*)(n->parent->data))->sz += tpl_types[t].sz; + DL_ADD(parent->children,n); + break; + case 's': + if (expect_lparen) goto fail; + n = tpl_node_new(parent); + n->type = TPL_TYPE_STR; + if (in_structure) { + if (ordinal == 1) { + iter_start_node = n; /* for S(...)# iteration */ + struct_widest_node = n; + } + if (tpl_types[n->type].sz > tpl_types[struct_widest_node->type].sz) { + struct_widest_node = n; + } + n->addr = calc_field_addr(parent,n->type,struct_addr,ordinal++); + } else n->addr = (void*)va_arg(ap,void*); + n->data = tpl_hook.malloc(sizeof(char*)); + if (!n->data) fatal_oom(); + *(char**)(n->data) = NULL; + if (n->parent->type == TPL_TYPE_ARY) + ((tpl_atyp*)(n->parent->data))->sz += sizeof(void*); + DL_ADD(parent->children,n); + break; + case '#': + /* apply a 'num' to preceding atom */ + if (!parent->children) goto fail; + preceding = parent->children->prev; /* first child's prev is 'last child'*/ + t = preceding->type; + applies_to_struct = (*(c-1) == ')') ? 1 : 0; + if (!applies_to_struct) { + if (!(t == TPL_TYPE_BYTE || t == TPL_TYPE_INT32 || + t == TPL_TYPE_UINT32 || t == TPL_TYPE_DOUBLE || + t == TPL_TYPE_UINT64 || t == TPL_TYPE_INT64 || + t == TPL_TYPE_UINT16 || t == TPL_TYPE_INT16 || + t == TPL_TYPE_STR )) goto fail; + } + /* count up how many contiguous # and form their product */ + pound_prod=1; + num_contig_fxlens=0; + for(peek=c; *peek == '#'; peek++) { + pound_num = va_arg(ap, int); + if (pound_num < 1) { + tpl_hook.fatal("non-positive iteration count %d\n", pound_num); + } + if (num_contig_fxlens >= (sizeof(contig_fxlens)/sizeof(contig_fxlens[0]))) { + tpl_hook.fatal("contiguous # exceeds hardcoded limit\n"); + } + contig_fxlens[num_contig_fxlens++] = pound_num; + pound_prod *= pound_num; + } + /* increment c to skip contiguous # so its points to last one */ + c = peek-1; + /* differentiate atom-# from struct-# by noting preceding rparen */ + if (applies_to_struct) { /* insert # node to induce looping */ + n = tpl_node_new(parent); + n->type = TPL_TYPE_POUND; + n->num = pound_prod; + n->data = tpl_hook.malloc(sizeof(tpl_pound_data)); + if (!n->data) fatal_oom(); + pd = (tpl_pound_data*)n->data; + pd->inter_elt_len = inter_elt_len; + pd->iter_start_node = iter_start_node; + pd->iternum = 0; + DL_ADD(parent->children,n); + /* multiply the 'num' and data space on each atom in the structure */ + for(np = iter_start_node; np != n; np = np->next) { + if (n->parent->type == TPL_TYPE_ARY) { + ((tpl_atyp*)(n->parent->data))->sz += + tpl_types[np->type].sz * (np->num * (n->num - 1)); + } + np->data = tpl_hook.realloc(np->data, tpl_types[np->type].sz * + np->num * n->num); + if (!np->data) fatal_oom(); + memset(np->data, 0, tpl_types[np->type].sz * np->num * n->num); + } + } else { /* simple atom-# form does not require a loop */ + preceding->num = pound_prod; + preceding->data = tpl_hook.realloc(preceding->data, + tpl_types[t].sz * preceding->num); + if (!preceding->data) fatal_oom(); + memset(preceding->data,0,tpl_types[t].sz * preceding->num); + if (n->parent->type == TPL_TYPE_ARY) { + ((tpl_atyp*)(n->parent->data))->sz += tpl_types[t].sz * + (preceding->num-1); + } + } + root->ser_osz += (sizeof(uint32_t) * num_contig_fxlens); + + j = ((tpl_root_data*)root->data)->num_fxlens; /* before incrementing */ + (((tpl_root_data*)root->data)->num_fxlens) += num_contig_fxlens; + num_fxlens = ((tpl_root_data*)root->data)->num_fxlens; /* new value */ + fxlens = ((tpl_root_data*)root->data)->fxlens; + fxlens = tpl_hook.realloc(fxlens, sizeof(int) * num_fxlens); + if (!fxlens) fatal_oom(); + ((tpl_root_data*)root->data)->fxlens = fxlens; + for(i=0; i < num_contig_fxlens; i++) fxlens[j++] = contig_fxlens[i]; + + break; + case 'B': + if (expect_lparen) goto fail; + if (in_structure) goto fail; + n = tpl_node_new(parent); + n->type = TPL_TYPE_BIN; + n->addr = (tpl_bin*)va_arg(ap,void*); + n->data = tpl_hook.malloc(sizeof(tpl_bin*)); + if (!n->data) fatal_oom(); + *((tpl_bin**)n->data) = NULL; + if (n->parent->type == TPL_TYPE_ARY) + ((tpl_atyp*)(n->parent->data))->sz += sizeof(tpl_bin); + DL_ADD(parent->children,n); + break; + case 'A': + if (in_structure) goto fail; + n = tpl_node_new(parent); + n->type = TPL_TYPE_ARY; + DL_ADD(parent->children,n); + parent = n; + expect_lparen=1; + pidx = (tpl_pidx*)tpl_hook.malloc(sizeof(tpl_pidx)); + if (!pidx) fatal_oom(); + pidx->node = n; + pidx->next = NULL; + DL_ADD(((tpl_root_data*)(root->data))->pidx,pidx); + /* set up the A's tpl_atyp */ + n->data = (tpl_atyp*)tpl_hook.malloc(sizeof(tpl_atyp)); + if (!n->data) fatal_oom(); + ((tpl_atyp*)(n->data))->num = 0; + ((tpl_atyp*)(n->data))->sz = 0; + ((tpl_atyp*)(n->data))->bb = NULL; + ((tpl_atyp*)(n->data))->bbtail = NULL; + ((tpl_atyp*)(n->data))->cur = NULL; + if (n->parent->type == TPL_TYPE_ARY) + ((tpl_atyp*)(n->parent->data))->sz += sizeof(void*); + break; + case 'S': + if (in_structure) goto fail; + expect_lparen=1; + ordinal=1; /* index upcoming atoms in S(..) */ + in_structure=1+lparen_level; /* so we can tell where S fmt ends */ + struct_addr = (char*)va_arg(ap,void*); + break; + case '$': /* nested structure */ + if (!in_structure) goto fail; + expect_lparen=1; + in_nested_structure++; + break; + case ')': + lparen_level--; + if (lparen_level < 0) goto fail; + if (*(c-1) == '(') goto fail; + if (in_nested_structure) in_nested_structure--; + else if (in_structure && (in_structure-1 == lparen_level)) { + /* calculate delta between contiguous structures in array */ + struct_next = calc_field_addr(parent, struct_widest_node->type, + struct_addr, ordinal++); + inter_elt_len = struct_next - struct_addr; + in_structure=0; + } + else parent = parent->parent; /* rparen ends A() type, not S() type */ + break; + case '(': + if (!expect_lparen) goto fail; + expect_lparen=0; + lparen_level++; + break; + default: + tpl_hook.oops("unsupported option %c\n", *c); + goto fail; + } + c++; + } + if (lparen_level != 0) goto fail; + + /* copy the format string, save for convenience */ + ((tpl_root_data*)(root->data))->fmt = tpl_hook.malloc(strlen(fmt)+1); + if (((tpl_root_data*)(root->data))->fmt == NULL) + fatal_oom(); + memcpy(((tpl_root_data*)(root->data))->fmt,fmt,strlen(fmt)+1); + + return root; + +fail: + tpl_hook.oops("failed to parse %s\n", fmt); + tpl_free(root); + return NULL; +} + +static int tpl_unmap_file( tpl_mmap_rec *mr) { + + if ( munmap( mr->text, mr->text_sz ) == -1 ) { + tpl_hook.oops("Failed to munmap: %s\n", strerror(errno)); + } + close(mr->fd); + mr->text = NULL; + mr->text_sz = 0; + return 0; +} + +static void tpl_free_keep_map(tpl_node *r) { + int mmap_bits = (TPL_RDONLY|TPL_FILE); + int ufree_bits = (TPL_MEM|TPL_UFREE); + tpl_node *nxtc,*c; + int find_next_node=0,looking,i; + size_t sz; + + /* For mmap'd files, or for 'ufree' memory images , do appropriate release */ + if ((((tpl_root_data*)(r->data))->flags & mmap_bits) == mmap_bits) { + tpl_unmap_file( &((tpl_root_data*)(r->data))->mmap); + } else if ((((tpl_root_data*)(r->data))->flags & ufree_bits) == ufree_bits) { + tpl_hook.free( ((tpl_root_data*)(r->data))->mmap.text ); + } + + c = r->children; + if (c) { + while(c->type != TPL_TYPE_ROOT) { /* loop until we come back to root node */ + switch (c->type) { + case TPL_TYPE_BIN: + /* free any binary buffer hanging from tpl_bin */ + if ( *((tpl_bin**)(c->data)) ) { + if ( (*((tpl_bin**)(c->data)))->addr ) { + tpl_hook.free( (*((tpl_bin**)(c->data)))->addr ); + } + *((tpl_bin**)c->data) = NULL; /* reset tpl_bin */ + } + find_next_node=1; + break; + case TPL_TYPE_STR: + /* free any packed (copied) string */ + for(i=0; i < c->num; i++) { + char *str = ((char**)c->data)[i]; + if (str) { + tpl_hook.free(str); + ((char**)c->data)[i] = NULL; + } + } + find_next_node=1; + break; + case TPL_TYPE_INT32: + case TPL_TYPE_UINT32: + case TPL_TYPE_INT64: + case TPL_TYPE_UINT64: + case TPL_TYPE_BYTE: + case TPL_TYPE_DOUBLE: + case TPL_TYPE_INT16: + case TPL_TYPE_UINT16: + case TPL_TYPE_POUND: + find_next_node=1; + break; + case TPL_TYPE_ARY: + c->ser_osz = 0; /* zero out the serialization output size */ + + sz = ((tpl_atyp*)(c->data))->sz; /* save sz to use below */ + tpl_free_atyp(c,c->data); + + /* make new atyp */ + c->data = (tpl_atyp*)tpl_hook.malloc(sizeof(tpl_atyp)); + if (!c->data) fatal_oom(); + ((tpl_atyp*)(c->data))->num = 0; + ((tpl_atyp*)(c->data))->sz = sz; /* restore bb datum sz */ + ((tpl_atyp*)(c->data))->bb = NULL; + ((tpl_atyp*)(c->data))->bbtail = NULL; + ((tpl_atyp*)(c->data))->cur = NULL; + + c = c->children; + break; + default: + tpl_hook.fatal("unsupported format character\n"); + break; + } + + if (find_next_node) { + find_next_node=0; + looking=1; + while(looking) { + if (c->next) { + nxtc=c->next; + c=nxtc; + looking=0; + } else { + if (c->type == TPL_TYPE_ROOT) break; /* root node */ + else { + nxtc=c->parent; + c=nxtc; + } + } + } + } + } + } + + ((tpl_root_data*)(r->data))->flags = 0; /* reset flags */ +} + +TPL_API void tpl_free(tpl_node *r) { + int mmap_bits = (TPL_RDONLY|TPL_FILE); + int ufree_bits = (TPL_MEM|TPL_UFREE); + tpl_node *nxtc,*c; + int find_next_node=0,looking,num,i; + tpl_pidx *pidx,*pidx_nxt; + + /* For mmap'd files, or for 'ufree' memory images , do appropriate release */ + if ((((tpl_root_data*)(r->data))->flags & mmap_bits) == mmap_bits) { + tpl_unmap_file( &((tpl_root_data*)(r->data))->mmap); + } else if ((((tpl_root_data*)(r->data))->flags & ufree_bits) == ufree_bits) { + tpl_hook.free( ((tpl_root_data*)(r->data))->mmap.text ); + } + + c = r->children; + if (c) { + while(c->type != TPL_TYPE_ROOT) { /* loop until we come back to root node */ + switch (c->type) { + case TPL_TYPE_BIN: + /* free any binary buffer hanging from tpl_bin */ + if ( *((tpl_bin**)(c->data)) ) { + if ( (*((tpl_bin**)(c->data)))->sz != 0 ) { + tpl_hook.free( (*((tpl_bin**)(c->data)))->addr ); + } + tpl_hook.free(*((tpl_bin**)c->data)); /* free tpl_bin */ + } + tpl_hook.free(c->data); /* free tpl_bin* */ + find_next_node=1; + break; + case TPL_TYPE_STR: + /* free any packed (copied) string */ + num = 1; + nxtc = c->next; + while (nxtc) { + if (nxtc->type == TPL_TYPE_POUND) { + num = nxtc->num; + } + nxtc = nxtc->next; + } + for (i = 0; i < c->num * num; i++) { + char *str = ((char**)c->data)[i]; + if (str) { + tpl_hook.free(str); + ((char**)c->data)[i] = NULL; + } + } + tpl_hook.free(c->data); + find_next_node=1; + break; + case TPL_TYPE_INT32: + case TPL_TYPE_UINT32: + case TPL_TYPE_INT64: + case TPL_TYPE_UINT64: + case TPL_TYPE_BYTE: + case TPL_TYPE_DOUBLE: + case TPL_TYPE_INT16: + case TPL_TYPE_UINT16: + case TPL_TYPE_POUND: + tpl_hook.free(c->data); + find_next_node=1; + break; + case TPL_TYPE_ARY: + tpl_free_atyp(c,c->data); + if (c->children) c = c->children; /* normal case */ + else find_next_node=1; /* edge case, handle bad format A() */ + break; + default: + tpl_hook.fatal("unsupported format character\n"); + break; + } + + if (find_next_node) { + find_next_node=0; + looking=1; + while(looking) { + if (c->next) { + nxtc=c->next; + tpl_hook.free(c); + c=nxtc; + looking=0; + } else { + if (c->type == TPL_TYPE_ROOT) break; /* root node */ + else { + nxtc=c->parent; + tpl_hook.free(c); + c=nxtc; + } + } + } + } + } + } + + /* free root */ + for(pidx=((tpl_root_data*)(r->data))->pidx; pidx; pidx=pidx_nxt) { + pidx_nxt = pidx->next; + tpl_hook.free(pidx); + } + tpl_hook.free(((tpl_root_data*)(r->data))->fmt); + if (((tpl_root_data*)(r->data))->num_fxlens > 0) { + tpl_hook.free(((tpl_root_data*)(r->data))->fxlens); + } + tpl_hook.free(r->data); /* tpl_root_data */ + tpl_hook.free(r); +} + + +/* Find the i'th packable ('A' node) */ +static tpl_node *tpl_find_i(tpl_node *n, int i) { + int j=0; + tpl_pidx *pidx; + if (n->type != TPL_TYPE_ROOT) return NULL; + if (i == 0) return n; /* packable 0 is root */ + for(pidx=((tpl_root_data*)(n->data))->pidx; pidx; pidx=pidx->next) { + if (++j == i) return pidx->node; + } + return NULL; +} + +static void *tpl_cpv(void *datav, const void *data, size_t sz) { + if (sz>0) memcpy(datav,data,sz); + return (void*)((uintptr_t)datav + sz); +} + +static void *tpl_extend_backbone(tpl_node *n) { + tpl_backbone *bb; + bb = (tpl_backbone*)tpl_hook.malloc(sizeof(tpl_backbone) + + ((tpl_atyp*)(n->data))->sz ); /* datum hangs on coattails of bb */ + if (!bb) fatal_oom(); +#if __STDC_VERSION__ < 199901 + bb->data = (char*)((uintptr_t)bb + sizeof(tpl_backbone)); +#endif + memset(bb->data,0,((tpl_atyp*)(n->data))->sz); + bb->next = NULL; + /* Add the new backbone to the tail, also setting head if necessary */ + if (((tpl_atyp*)(n->data))->bb == NULL) { + ((tpl_atyp*)(n->data))->bb = bb; + ((tpl_atyp*)(n->data))->bbtail = bb; + } else { + ((tpl_atyp*)(n->data))->bbtail->next = bb; + ((tpl_atyp*)(n->data))->bbtail = bb; + } + + ((tpl_atyp*)(n->data))->num++; + return bb->data; +} + +/* Get the format string corresponding to a given tpl (root node) */ +static char *tpl_fmt(tpl_node *r) { + return ((tpl_root_data*)(r->data))->fmt; +} + +/* Get the fmt # lengths as a contiguous buffer of ints (length num_fxlens) */ +static int *tpl_fxlens(tpl_node *r, int *num_fxlens) { + *num_fxlens = ((tpl_root_data*)(r->data))->num_fxlens; + return ((tpl_root_data*)(r->data))->fxlens; +} + +/* called when serializing an 'A' type node into a buffer which has + * already been set up with the proper space. The backbone is walked + * which was obtained from the tpl_atyp header passed in. + */ +static void *tpl_dump_atyp(tpl_node *n, tpl_atyp* at, void *dv) { + tpl_backbone *bb; + tpl_node *c; + void *datav; + uint32_t slen; + tpl_bin *binp; + char *strp; + tpl_atyp *atypp; + tpl_pound_data *pd; + int i; + size_t itermax; + + /* handle 'A' nodes */ + dv = tpl_cpv(dv,&at->num,sizeof(uint32_t)); /* array len */ + for(bb=at->bb; bb; bb=bb->next) { + datav = bb->data; + c=n->children; + while(c) { + switch (c->type) { + case TPL_TYPE_BYTE: + case TPL_TYPE_DOUBLE: + case TPL_TYPE_INT32: + case TPL_TYPE_UINT32: + case TPL_TYPE_INT64: + case TPL_TYPE_UINT64: + case TPL_TYPE_INT16: + case TPL_TYPE_UINT16: + dv = tpl_cpv(dv,datav,tpl_types[c->type].sz * c->num); + datav = (void*)((uintptr_t)datav + tpl_types[c->type].sz * c->num); + break; + case TPL_TYPE_BIN: + /* dump the buffer length followed by the buffer */ + memcpy(&binp,datav,sizeof(tpl_bin*)); /* cp to aligned */ + slen = binp->sz; + dv = tpl_cpv(dv,&slen,sizeof(uint32_t)); + dv = tpl_cpv(dv,binp->addr,slen); + datav = (void*)((uintptr_t)datav + sizeof(tpl_bin*)); + break; + case TPL_TYPE_STR: + /* dump the string length followed by the string */ + for(i=0; i < c->num; i++) { + memcpy(&strp,datav,sizeof(char*)); /* cp to aligned */ + slen = strp ? (strlen(strp)+1) : 0; + dv = tpl_cpv(dv,&slen,sizeof(uint32_t)); + if (slen > 1) dv = tpl_cpv(dv,strp,slen-1); + datav = (void*)((uintptr_t)datav + sizeof(char*)); + } + break; + case TPL_TYPE_ARY: + memcpy(&atypp,datav,sizeof(tpl_atyp*)); /* cp to aligned */ + dv = tpl_dump_atyp(c,atypp,dv); + datav = (void*)((uintptr_t)datav + sizeof(void*)); + break; + case TPL_TYPE_POUND: + /* iterate over the preceding nodes */ + pd = (tpl_pound_data*)c->data; + itermax = c->num; + if (++(pd->iternum) < itermax) { + c = pd->iter_start_node; + continue; + } else { /* loop complete. */ + pd->iternum = 0; + } + break; + default: + tpl_hook.fatal("unsupported format character\n"); + break; + } + c=c->next; + } + } + return dv; +} + +/* figure the serialization output size needed for tpl whose root is n*/ +static size_t tpl_ser_osz(tpl_node *n) { + tpl_node *c, *np; + size_t sz, itermax; + tpl_bin *binp; + char *strp; + tpl_pound_data *pd; + int i; + + /* handle the root node ONLY (subtree's ser_osz have been bubbled-up) */ + if (n->type != TPL_TYPE_ROOT) { + tpl_hook.fatal("internal error: tpl_ser_osz on non-root node\n"); + } + + sz = n->ser_osz; /* start with fixed overhead, already stored */ + c=n->children; + while (c) { + switch (c->type) { + case TPL_TYPE_BYTE: + case TPL_TYPE_DOUBLE: + case TPL_TYPE_INT32: + case TPL_TYPE_UINT32: + case TPL_TYPE_INT64: + case TPL_TYPE_UINT64: + case TPL_TYPE_INT16: + case TPL_TYPE_UINT16: + sz += tpl_types[c->type].sz * c->num; + break; + case TPL_TYPE_BIN: + sz += sizeof(uint32_t); /* binary buf len */ + memcpy(&binp,c->data,sizeof(tpl_bin*)); /* cp to aligned */ + sz += binp->sz; + break; + case TPL_TYPE_STR: + for(i=0; i < c->num; i++) { + sz += sizeof(uint32_t); /* string len */ + memcpy(&strp,&((char**)c->data)[i],sizeof(char*)); /* cp to aligned */ + sz += strp ? strlen(strp) : 0; + } + break; + case TPL_TYPE_ARY: + sz += sizeof(uint32_t); /* array len */ + sz += c->ser_osz; /* bubbled-up child array ser_osz */ + break; + case TPL_TYPE_POUND: + /* iterate over the preceding nodes */ + itermax = c->num; + pd = (tpl_pound_data*)c->data; + if (++(pd->iternum) < itermax) { + for(np=pd->iter_start_node; np != c; np = np->next) { + np->data = (char*)(np->data) + + (tpl_types[np->type].sz * np->num); + } + c = pd->iter_start_node; + continue; + } else { /* loop complete. */ + pd->iternum = 0; + for(np=pd->iter_start_node; np != c; np = np->next) { + np->data = (char*)(np->data) - ((itermax-1) * + tpl_types[np->type].sz * + np->num); + } + } + break; + default: + tpl_hook.fatal("unsupported format character\n"); + break; + } + c=c->next; + } + return sz; +} + + +TPL_API int tpl_dump(tpl_node *r, int mode, ...) { + va_list ap; + char *filename, *bufv; + void **addr_out,*buf, *pa_addr; + int fd,rc=0; + size_t sz,*sz_out, pa_sz; + struct stat sbuf; + + if (((tpl_root_data*)(r->data))->flags & TPL_RDONLY) { /* unusual */ + tpl_hook.oops("error: tpl_dump called for a loaded tpl\n"); + return -1; + } + + sz = tpl_ser_osz(r); /* compute the size needed to serialize */ + + va_start(ap,mode); + if (mode & TPL_FILE) { + filename = va_arg(ap,char*); + fd = tpl_mmap_output_file(filename, sz, &buf); + if (fd == -1) rc = -1; + else { + rc = tpl_dump_to_mem(r,buf,sz); + if (msync(buf,sz,MS_SYNC) == -1) { + tpl_hook.oops("msync failed on fd %d: %s\n", fd, strerror(errno)); + } + if (munmap(buf, sz) == -1) { + tpl_hook.oops("munmap failed on fd %d: %s\n", fd, strerror(errno)); + } + close(fd); + } + } else if (mode & TPL_FD) { + fd = va_arg(ap, int); + if ( (buf = tpl_hook.malloc(sz)) == NULL) fatal_oom(); + tpl_dump_to_mem(r,buf,sz); + bufv = buf; + do { + rc = write(fd,bufv,sz); + if (rc > 0) { + sz -= rc; + bufv += rc; + } else if (rc == -1) { + if (errno == EINTR || errno == EAGAIN) continue; + tpl_hook.oops("error writing to fd %d: %s\n", fd, strerror(errno)); + /* attempt to rewind partial write to a regular file */ + if (fstat(fd,&sbuf) == 0 && S_ISREG(sbuf.st_mode)) { + if (ftruncate(fd,sbuf.st_size - (bufv-(char*)buf)) == -1) { + tpl_hook.oops("can't rewind: %s\n", strerror(errno)); + } + } + free(buf); + return -1; + } + } while (sz > 0); + free(buf); + rc = 0; + } else if (mode & TPL_MEM) { + if (mode & TPL_PREALLOCD) { /* caller allocated */ + pa_addr = (void*)va_arg(ap, void*); + pa_sz = va_arg(ap, size_t); + if (pa_sz < sz) { + tpl_hook.oops("tpl_dump: buffer too small, need %d bytes\n", sz); + return -1; + } + rc=tpl_dump_to_mem(r,pa_addr,sz); + } else { /* we allocate */ + addr_out = (void**)va_arg(ap, void*); + sz_out = va_arg(ap, size_t*); + if ( (buf = tpl_hook.malloc(sz)) == NULL) fatal_oom(); + *sz_out = sz; + *addr_out = buf; + rc=tpl_dump_to_mem(r,buf,sz); + } + } else if (mode & TPL_GETSIZE) { + sz_out = va_arg(ap, size_t*); + *sz_out = sz; + } else { + tpl_hook.oops("unsupported tpl_dump mode %d\n", mode); + rc=-1; + } + va_end(ap); + return rc; +} + +/* This function expects the caller to have set up a memory buffer of + * adequate size to hold the serialized tpl. The sz parameter must be + * the result of tpl_ser_osz(r). + */ +static int tpl_dump_to_mem(tpl_node *r,void *addr,size_t sz) { + uint32_t slen, sz32; + int *fxlens, num_fxlens, i; + void *dv; + char *fmt,flags; + tpl_node *c, *np; + tpl_pound_data *pd; + size_t itermax; + + fmt = tpl_fmt(r); + flags = 0; + if (tpl_cpu_bigendian()) flags |= TPL_FL_BIGENDIAN; + if (strchr(fmt,'s')) flags |= TPL_FL_NULLSTRINGS; + sz32 = sz; + + dv = addr; + dv = tpl_cpv(dv,TPL_MAGIC,3); /* copy tpl magic prefix */ + dv = tpl_cpv(dv,&flags,1); /* copy flags byte */ + dv = tpl_cpv(dv,&sz32,sizeof(uint32_t));/* overall length (inclusive) */ + dv = tpl_cpv(dv,fmt,strlen(fmt)+1); /* copy format with NUL-term */ + fxlens = tpl_fxlens(r,&num_fxlens); + dv = tpl_cpv(dv,fxlens,num_fxlens*sizeof(uint32_t));/* fmt # lengths */ + + /* serialize the tpl content, iterating over direct children of root */ + c = r->children; + while (c) { + switch (c->type) { + case TPL_TYPE_BYTE: + case TPL_TYPE_DOUBLE: + case TPL_TYPE_INT32: + case TPL_TYPE_UINT32: + case TPL_TYPE_INT64: + case TPL_TYPE_UINT64: + case TPL_TYPE_INT16: + case TPL_TYPE_UINT16: + dv = tpl_cpv(dv,c->data,tpl_types[c->type].sz * c->num); + break; + case TPL_TYPE_BIN: + slen = (*(tpl_bin**)(c->data))->sz; + dv = tpl_cpv(dv,&slen,sizeof(uint32_t)); /* buffer len */ + dv = tpl_cpv(dv,(*(tpl_bin**)(c->data))->addr,slen); /* buf */ + break; + case TPL_TYPE_STR: + for(i=0; i < c->num; i++) { + char *str = ((char**)c->data)[i]; + slen = str ? strlen(str)+1 : 0; + dv = tpl_cpv(dv,&slen,sizeof(uint32_t)); /* string len */ + if (slen>1) dv = tpl_cpv(dv,str,slen-1); /*string*/ + } + break; + case TPL_TYPE_ARY: + dv = tpl_dump_atyp(c,(tpl_atyp*)c->data,dv); + break; + case TPL_TYPE_POUND: + pd = (tpl_pound_data*)c->data; + itermax = c->num; + if (++(pd->iternum) < itermax) { + + /* in start or midst of loop. advance data pointers. */ + for(np=pd->iter_start_node; np != c; np = np->next) { + np->data = (char*)(np->data) + + (tpl_types[np->type].sz * np->num); + } + /* do next iteration */ + c = pd->iter_start_node; + continue; + + } else { /* loop complete. */ + + /* reset iteration index and addr/data pointers. */ + pd->iternum = 0; + for(np=pd->iter_start_node; np != c; np = np->next) { + np->data = (char*)(np->data) - ((itermax-1) * + tpl_types[np->type].sz * + np->num); + } + + } + break; + default: + tpl_hook.fatal("unsupported format character\n"); + break; + } + c = c->next; + } + + return 0; +} + +static int tpl_cpu_bigendian() { + unsigned i = 1; + char *c; + c = (char*)&i; + return (c[0] == 1 ? 0 : 1); +} + + +/* + * algorithm for sanity-checking a tpl image: + * scan the tpl whilst not exceeding the buffer size (bufsz) , + * formulating a calculated (expected) size of the tpl based + * on walking its data. When calcsize has been calculated it + * should exactly match the buffer size (bufsz) and the internal + * recorded size (intlsz) + */ +static int tpl_sanity(tpl_node *r, int excess_ok) { + uint32_t intlsz; + int found_nul=0,rc, octothorpes=0, num_fxlens, *fxlens, flen; + void *d, *dv; + char intlflags, *fmt, c, *mapfmt; + size_t bufsz, serlen; + + d = ((tpl_root_data*)(r->data))->mmap.text; + bufsz = ((tpl_root_data*)(r->data))->mmap.text_sz; + + dv = d; + if (bufsz < (4 + sizeof(uint32_t) + 1)) return ERR_NOT_MINSIZE; /* min sz: magic+flags+len+nul */ + if (memcmp(dv,TPL_MAGIC, 3) != 0) return ERR_MAGIC_MISMATCH; /* missing tpl magic prefix */ + if (tpl_needs_endian_swap(dv)) ((tpl_root_data*)(r->data))->flags |= TPL_XENDIAN; + dv = (void*)((uintptr_t)dv + 3); + memcpy(&intlflags,dv,sizeof(char)); /* extract flags */ + if (intlflags & ~TPL_SUPPORTED_BITFLAGS) return ERR_UNSUPPORTED_FLAGS; + /* TPL1.3 stores strings with a "length+1" prefix to discern NULL strings from + empty strings from non-empty strings; TPL1.2 only handled the latter two. + So we need to be mindful of which string format we're reading from. */ + if (!(intlflags & TPL_FL_NULLSTRINGS)) { + ((tpl_root_data*)(r->data))->flags |= TPL_OLD_STRING_FMT; + } + dv = (void*)((uintptr_t)dv + 1); + memcpy(&intlsz,dv,sizeof(uint32_t)); /* extract internal size */ + if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN) tpl_byteswap(&intlsz, sizeof(uint32_t)); + if (!excess_ok && (intlsz != bufsz)) return ERR_INCONSISTENT_SZ; /* inconsisent buffer/internal size */ + dv = (void*)((uintptr_t)dv + sizeof(uint32_t)); + + /* dv points to the start of the format string. Look for nul w/in buf sz */ + fmt = (char*)dv; + while ((uintptr_t)dv-(uintptr_t)d < bufsz && !found_nul) { + if ( (c = *(char*)dv) != '\0') { + if (strchr(tpl_fmt_chars,c) == NULL) + return ERR_FMT_INVALID; /* invalid char in format string */ + if ( (c = *(char*)dv) == '#') octothorpes++; + dv = (void*)((uintptr_t)dv + 1); + } + else found_nul = 1; + } + if (!found_nul) return ERR_FMT_MISSING_NUL; /* runaway format string */ + dv = (void*)((uintptr_t)dv + 1); /* advance to octothorpe lengths buffer */ + + /* compare the map format to the format of this tpl image */ + mapfmt = tpl_fmt(r); + rc = strcmp(mapfmt,fmt); + if (rc != 0) return ERR_FMT_MISMATCH; + + /* compare octothorpe lengths in image to the mapped values */ + if ((((uintptr_t)dv + (octothorpes * 4)) - (uintptr_t)d) > bufsz) return ERR_INCONSISTENT_SZ4; + fxlens = tpl_fxlens(r,&num_fxlens); /* mapped fxlens */ + while(num_fxlens--) { + memcpy(&flen,dv,sizeof(uint32_t)); /* stored flen */ + if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN) tpl_byteswap(&flen, sizeof(uint32_t)); + if (flen != *fxlens) return ERR_FLEN_MISMATCH; + dv = (void*)((uintptr_t)dv + sizeof(uint32_t)); + fxlens++; + } + + /* dv now points to beginning of data */ + rc = tpl_serlen(r,r,dv,&serlen); /* get computed serlen of data part */ + if (rc == -1) return ERR_INCONSISTENT_SZ2; /* internal inconsistency in tpl image */ + serlen += ((uintptr_t)dv - (uintptr_t)d); /* add back serlen of preamble part */ + if (excess_ok && (bufsz < serlen)) return ERR_INCONSISTENT_SZ3; + if (!excess_ok && (serlen != bufsz)) return ERR_INCONSISTENT_SZ3; /* buffer/internal sz exceeds serlen */ + return 0; +} + +static void *tpl_find_data_start(void *d) { + int octothorpes=0; + d = (void*)((uintptr_t)d + 4); /* skip TPL_MAGIC and flags byte */ + d = (void*)((uintptr_t)d + 4); /* skip int32 overall len */ + while(*(char*)d != '\0') { + if (*(char*)d == '#') octothorpes++; + d = (void*)((uintptr_t)d + 1); + } + d = (void*)((uintptr_t)d + 1); /* skip NUL */ + d = (void*)((uintptr_t)d + (octothorpes * sizeof(uint32_t))); /* skip # array lens */ + return d; +} + +static int tpl_needs_endian_swap(void *d) { + char *c; + int cpu_is_bigendian; + c = (char*)d; + cpu_is_bigendian = tpl_cpu_bigendian(); + return ((c[3] & TPL_FL_BIGENDIAN) == cpu_is_bigendian) ? 0 : 1; +} + +static size_t tpl_size_for(char c) { + int i; + for(i=0; i < sizeof(tpl_types)/sizeof(tpl_types[0]); i++) { + if (tpl_types[i].c == c) return tpl_types[i].sz; + } + return 0; +} + +TPL_API char* tpl_peek(int mode, ...) { + va_list ap; + int xendian=0,found_nul=0,old_string_format=0; + char *filename=NULL, *datapeek_f=NULL, *datapeek_c, *datapeek_s; + void *addr=NULL, *dv, *datapeek_p=NULL; + size_t sz=0, fmt_len, first_atom, num_fxlens=0; + uint32_t datapeek_ssz, datapeek_csz, datapeek_flen; + tpl_mmap_rec mr = {0,NULL,0}; + char *fmt,*fmt_cpy=NULL,c; + uint32_t intlsz, **fxlens=NULL, *num_fxlens_out=NULL, *fxlensv; + + va_start(ap,mode); + if ((mode & TPL_FXLENS) && (mode & TPL_DATAPEEK)) { + tpl_hook.oops("TPL_FXLENS and TPL_DATAPEEK mutually exclusive\n"); + goto fail; + } + if (mode & TPL_FILE) filename = va_arg(ap,char *); + else if (mode & TPL_MEM) { + addr = va_arg(ap,void *); + sz = va_arg(ap,size_t); + } else { + tpl_hook.oops("unsupported tpl_peek mode %d\n", mode); + goto fail; + } + if (mode & TPL_DATAPEEK) { + datapeek_f = va_arg(ap, char*); + } + if (mode & TPL_FXLENS) { + num_fxlens_out = va_arg(ap,uint32_t *); + fxlens = va_arg(ap,uint32_t **); + *num_fxlens_out = 0; + *fxlens = NULL; + } + + if (mode & TPL_FILE) { + if (tpl_mmap_file(filename, &mr) != 0) { + tpl_hook.oops("tpl_peek failed for file %s\n", filename); + goto fail; + } + addr = mr.text; + sz = mr.text_sz; + } + + dv = addr; + if (sz < (4 + sizeof(uint32_t) + 1)) goto fail; /* min sz */ + if (memcmp(dv,TPL_MAGIC, 3) != 0) goto fail; /* missing tpl magic prefix */ + if (tpl_needs_endian_swap(dv)) xendian=1; + if ((((char*)dv)[3] & TPL_FL_NULLSTRINGS)==0) old_string_format=1; + dv = (void*)((uintptr_t)dv + 4); + memcpy(&intlsz,dv,sizeof(uint32_t)); /* extract internal size */ + if (xendian) tpl_byteswap(&intlsz, sizeof(uint32_t)); + if (intlsz != sz) goto fail; /* inconsisent buffer/internal size */ + dv = (void*)((uintptr_t)dv + sizeof(uint32_t)); + + /* dv points to the start of the format string. Look for nul w/in buf sz */ + fmt = (char*)dv; + while ((uintptr_t)dv-(uintptr_t)addr < sz && !found_nul) { + if ( (c = *(char*)dv) == '\0') { + found_nul = 1; + } else if (c == '#') { + num_fxlens++; + } + dv = (void*)((uintptr_t)dv + 1); + } + if (!found_nul) goto fail; /* runaway format string */ + fmt_len = (char*)dv - fmt; /* include space for \0 */ + fmt_cpy = tpl_hook.malloc(fmt_len); + if (fmt_cpy == NULL) { + fatal_oom(); + } + memcpy(fmt_cpy, fmt, fmt_len); + + /* retrieve the octothorpic lengths if requested */ + if (num_fxlens > 0) { + if (sz < ((uintptr_t)dv + (num_fxlens * sizeof(uint32_t)) - (uintptr_t)addr)) { + goto fail; + } + } + if ((mode & TPL_FXLENS) && (num_fxlens > 0)) { + *fxlens = tpl_hook.malloc(num_fxlens * sizeof(uint32_t)); + if (*fxlens == NULL) tpl_hook.fatal("out of memory"); + *num_fxlens_out = num_fxlens; + fxlensv = *fxlens; + while(num_fxlens--) { + memcpy(fxlensv,dv,sizeof(uint32_t)); + if (xendian) tpl_byteswap(fxlensv, sizeof(uint32_t)); + dv = (void*)((uintptr_t)dv + sizeof(uint32_t)); + fxlensv++; + } + } + /* if caller requested, peek into the specified data elements */ + if (mode & TPL_DATAPEEK) { + + first_atom = strspn(fmt, "S()"); /* skip any leading S() */ + + datapeek_flen = strlen(datapeek_f); + if (strspn(datapeek_f, tpl_datapeek_ok_chars) < datapeek_flen) { + tpl_hook.oops("invalid TPL_DATAPEEK format: %s\n", datapeek_f); + tpl_hook.free(fmt_cpy); fmt_cpy = NULL; /* fail */ + goto fail; + } + + if (strncmp( &fmt[first_atom], datapeek_f, datapeek_flen) != 0) { + tpl_hook.oops("TPL_DATAPEEK format mismatches tpl iamge\n"); + tpl_hook.free(fmt_cpy); fmt_cpy = NULL; /* fail */ + goto fail; + } + + /* advance to data start, then copy out requested elements */ + dv = (void*)((uintptr_t)dv + (num_fxlens * sizeof(uint32_t))); + for(datapeek_c = datapeek_f; *datapeek_c != '\0'; datapeek_c++) { + datapeek_p = va_arg(ap, void*); + if (*datapeek_c == 's') { /* special handling for strings */ + if ((uintptr_t)dv-(uintptr_t)addr + sizeof(uint32_t) > sz) { + tpl_hook.oops("tpl_peek: tpl has insufficient length\n"); + tpl_hook.free(fmt_cpy); fmt_cpy = NULL; /* fail */ + goto fail; + } + memcpy(&datapeek_ssz,dv,sizeof(uint32_t)); /* get slen */ + if (xendian) tpl_byteswap(&datapeek_ssz, sizeof(uint32_t)); + if (old_string_format) datapeek_ssz++; + dv = (void*)((uintptr_t)dv + sizeof(uint32_t)); /* adv. to str */ + if (datapeek_ssz == 0) datapeek_s = NULL; + else { + if ((uintptr_t)dv-(uintptr_t)addr + datapeek_ssz-1 > sz) { + tpl_hook.oops("tpl_peek: tpl has insufficient length\n"); + tpl_hook.free(fmt_cpy); fmt_cpy = NULL; /* fail */ + goto fail; + } + datapeek_s = tpl_hook.malloc(datapeek_ssz); + if (datapeek_s == NULL) fatal_oom(); + memcpy(datapeek_s, dv, datapeek_ssz-1); + datapeek_s[datapeek_ssz-1] = '\0'; + dv = (void*)((uintptr_t)dv + datapeek_ssz-1); + } + *(char**)datapeek_p = datapeek_s; + } else { + datapeek_csz = tpl_size_for(*datapeek_c); + if ((uintptr_t)dv-(uintptr_t)addr + datapeek_csz > sz) { + tpl_hook.oops("tpl_peek: tpl has insufficient length\n"); + tpl_hook.free(fmt_cpy); fmt_cpy = NULL; /* fail */ + goto fail; + } + memcpy(datapeek_p, dv, datapeek_csz); + if (xendian) tpl_byteswap(datapeek_p, datapeek_csz); + dv = (void*)((uintptr_t)dv + datapeek_csz); + } + } + } + +fail: + va_end(ap); + if ((mode & TPL_FILE) && mr.text != NULL) tpl_unmap_file( &mr ); + return fmt_cpy; +} + +/* tpl_jot(TPL_FILE, "file.tpl", "si", &s, &i); */ +/* tpl_jot(TPL_MEM, &buf, &sz, "si", &s, &i); */ +/* tpl_jot(TPL_FD, fd, "si", &s, &i); */ +TPL_API int tpl_jot(int mode, ...) { + va_list ap; + char *filename, *fmt; + size_t *sz; + int fd, rc=0; + void **buf; + tpl_node *tn; + + va_start(ap,mode); + if (mode & TPL_FILE) { + filename = va_arg(ap,char*); + fmt = va_arg(ap,char*); + tn = tpl_map_va(fmt, ap); + if (tn == NULL) { rc=-1; goto fail;} + tpl_pack(tn, 0); + rc = tpl_dump(tn, TPL_FILE, filename); + tpl_free(tn); + } else if (mode & TPL_MEM) { + buf = va_arg(ap,void*); + sz = va_arg(ap,size_t*); + fmt = va_arg(ap,char*); + tn = tpl_map_va(fmt,ap); + if (tn == NULL) { rc=-1; goto fail;} + tpl_pack(tn,0); + rc = tpl_dump(tn, TPL_MEM, buf, sz); + tpl_free(tn); + } else if (mode & TPL_FD) { + fd = va_arg(ap,int); + fmt = va_arg(ap,char*); + tn = tpl_map_va(fmt,ap); + if (tn == NULL) { rc=-1; goto fail;} + tpl_pack(tn,0); + rc = tpl_dump(tn, TPL_FD, fd); + tpl_free(tn); + } else { + tpl_hook.fatal("invalid tpl_jot mode\n"); + } + +fail: + va_end(ap); + return rc; +} + +TPL_API int tpl_load(tpl_node *r, int mode, ...) { + va_list ap; + int rc=0,fd=0; + char *filename=NULL; + void *addr; + size_t sz; + + va_start(ap,mode); + if (mode & TPL_FILE) filename = va_arg(ap,char *); + else if (mode & TPL_MEM) { + addr = va_arg(ap,void *); + sz = va_arg(ap,size_t); + } else if (mode & TPL_FD) { + fd = va_arg(ap,int); + } else { + tpl_hook.oops("unsupported tpl_load mode %d\n", mode); + return -1; + } + va_end(ap); + + if (r->type != TPL_TYPE_ROOT) { + tpl_hook.oops("error: tpl_load to non-root node\n"); + return -1; + } + if (((tpl_root_data*)(r->data))->flags & (TPL_WRONLY|TPL_RDONLY)) { + /* already packed or loaded, so reset it as if newly mapped */ + tpl_free_keep_map(r); + } + if (mode & TPL_FILE) { + if (tpl_mmap_file(filename, &((tpl_root_data*)(r->data))->mmap) != 0) { + tpl_hook.oops("tpl_load failed for file %s\n", filename); + return -1; + } + if ( (rc = tpl_sanity(r, (mode & TPL_EXCESS_OK))) != 0) { + if (rc == ERR_FMT_MISMATCH) { + tpl_hook.oops("%s: format signature mismatch\n", filename); + } else if (rc == ERR_FLEN_MISMATCH) { + tpl_hook.oops("%s: array lengths mismatch\n", filename); + } else { + tpl_hook.oops("%s: not a valid tpl file\n", filename); + } + tpl_unmap_file( &((tpl_root_data*)(r->data))->mmap ); + return -1; + } + ((tpl_root_data*)(r->data))->flags = (TPL_FILE | TPL_RDONLY); + } else if (mode & TPL_MEM) { + ((tpl_root_data*)(r->data))->mmap.text = addr; + ((tpl_root_data*)(r->data))->mmap.text_sz = sz; + if ( (rc = tpl_sanity(r, (mode & TPL_EXCESS_OK))) != 0) { + if (rc == ERR_FMT_MISMATCH) { + tpl_hook.oops("format signature mismatch\n"); + } else { + tpl_hook.oops("not a valid tpl file\n"); + } + return -1; + } + ((tpl_root_data*)(r->data))->flags = (TPL_MEM | TPL_RDONLY); + if (mode & TPL_UFREE) ((tpl_root_data*)(r->data))->flags |= TPL_UFREE; + } else if (mode & TPL_FD) { + /* if fd read succeeds, resulting mem img is used for load */ + if (tpl_gather(TPL_GATHER_BLOCKING,fd,&addr,&sz) > 0) { + return tpl_load(r, TPL_MEM|TPL_UFREE, addr, sz); + } else return -1; + } else { + tpl_hook.oops("invalid tpl_load mode %d\n", mode); + return -1; + } + /* this applies to TPL_MEM or TPL_FILE */ + if (tpl_needs_endian_swap(((tpl_root_data*)(r->data))->mmap.text)) + ((tpl_root_data*)(r->data))->flags |= TPL_XENDIAN; + tpl_unpackA0(r); /* prepare root A nodes for use */ + return 0; +} + +TPL_API int tpl_Alen(tpl_node *r, int i) { + tpl_node *n; + + n = tpl_find_i(r,i); + if (n == NULL) { + tpl_hook.oops("invalid index %d to tpl_unpack\n", i); + return -1; + } + if (n->type != TPL_TYPE_ARY) return -1; + return ((tpl_atyp*)(n->data))->num; +} + +static void tpl_free_atyp(tpl_node *n, tpl_atyp *atyp) { + tpl_backbone *bb,*bbnxt; + tpl_node *c; + void *dv; + tpl_bin *binp; + tpl_atyp *atypp; + char *strp; + size_t itermax; + tpl_pound_data *pd; + int i; + + bb = atyp->bb; + while (bb) { + bbnxt = bb->next; + dv = bb->data; + c=n->children; + while (c) { + switch (c->type) { + case TPL_TYPE_BYTE: + case TPL_TYPE_DOUBLE: + case TPL_TYPE_INT32: + case TPL_TYPE_UINT32: + case TPL_TYPE_INT64: + case TPL_TYPE_UINT64: + case TPL_TYPE_INT16: + case TPL_TYPE_UINT16: + dv = (void*)((uintptr_t)dv + tpl_types[c->type].sz*c->num); + break; + case TPL_TYPE_BIN: + memcpy(&binp,dv,sizeof(tpl_bin*)); /* cp to aligned */ + if (binp->addr) tpl_hook.free( binp->addr ); /* free buf */ + tpl_hook.free(binp); /* free tpl_bin */ + dv = (void*)((uintptr_t)dv + sizeof(tpl_bin*)); + break; + case TPL_TYPE_STR: + for(i=0; i < c->num; i++) { + memcpy(&strp,dv,sizeof(char*)); /* cp to aligned */ + if (strp) tpl_hook.free(strp); /* free string */ + dv = (void*)((uintptr_t)dv + sizeof(char*)); + } + break; + case TPL_TYPE_POUND: + /* iterate over the preceding nodes */ + itermax = c->num; + pd = (tpl_pound_data*)c->data; + if (++(pd->iternum) < itermax) { + c = pd->iter_start_node; + continue; + } else { /* loop complete. */ + pd->iternum = 0; + } + break; + case TPL_TYPE_ARY: + memcpy(&atypp,dv,sizeof(tpl_atyp*)); /* cp to aligned */ + tpl_free_atyp(c,atypp); /* free atyp */ + dv = (void*)((uintptr_t)dv + sizeof(void*)); + break; + default: + tpl_hook.fatal("unsupported format character\n"); + break; + } + c=c->next; + } + tpl_hook.free(bb); + bb = bbnxt; + } + tpl_hook.free(atyp); +} + +/* determine (by walking) byte length of serialized r/A node at address dv + * returns 0 on success, or -1 if the tpl isn't trustworthy (fails consistency) + */ +static int tpl_serlen(tpl_node *r, tpl_node *n, void *dv, size_t *serlen) { + uint32_t slen; + int num=0,fidx; + tpl_node *c; + size_t len=0, alen, buf_past, itermax; + tpl_pound_data *pd; + + buf_past = ((uintptr_t)((tpl_root_data*)(r->data))->mmap.text + + ((tpl_root_data*)(r->data))->mmap.text_sz); + + if (n->type == TPL_TYPE_ROOT) num = 1; + else if (n->type == TPL_TYPE_ARY) { + if ((uintptr_t)dv + sizeof(uint32_t) > buf_past) return -1; + memcpy(&num,dv,sizeof(uint32_t)); + if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN) + tpl_byteswap(&num, sizeof(uint32_t)); + dv = (void*)((uintptr_t)dv + sizeof(uint32_t)); + len += sizeof(uint32_t); + } else tpl_hook.fatal("internal error in tpl_serlen\n"); + + while (num-- > 0) { + c=n->children; + while (c) { + switch (c->type) { + case TPL_TYPE_BYTE: + case TPL_TYPE_DOUBLE: + case TPL_TYPE_INT32: + case TPL_TYPE_UINT32: + case TPL_TYPE_INT64: + case TPL_TYPE_UINT64: + case TPL_TYPE_INT16: + case TPL_TYPE_UINT16: + for(fidx=0; fidx < c->num; fidx++) { /* octothorpe support */ + if ((uintptr_t)dv + tpl_types[c->type].sz > buf_past) return -1; + dv = (void*)((uintptr_t)dv + tpl_types[c->type].sz); + len += tpl_types[c->type].sz; + } + break; + case TPL_TYPE_BIN: + len += sizeof(uint32_t); + if ((uintptr_t)dv + sizeof(uint32_t) > buf_past) return -1; + memcpy(&slen,dv,sizeof(uint32_t)); + if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN) + tpl_byteswap(&slen, sizeof(uint32_t)); + len += slen; + dv = (void*)((uintptr_t)dv + sizeof(uint32_t)); + if ((uintptr_t)dv + slen > buf_past) return -1; + dv = (void*)((uintptr_t)dv + slen); + break; + case TPL_TYPE_STR: + for(fidx=0; fidx < c->num; fidx++) { /* octothorpe support */ + len += sizeof(uint32_t); + if ((uintptr_t)dv + sizeof(uint32_t) > buf_past) return -1; + memcpy(&slen,dv,sizeof(uint32_t)); + if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN) + tpl_byteswap(&slen, sizeof(uint32_t)); + if (!(((tpl_root_data*)(r->data))->flags & TPL_OLD_STRING_FMT)) + slen = (slen>1) ? (slen-1) : 0; + len += slen; + dv = (void*)((uintptr_t)dv + sizeof(uint32_t)); + if ((uintptr_t)dv + slen > buf_past) return -1; + dv = (void*)((uintptr_t)dv + slen); + } + break; + case TPL_TYPE_ARY: + if ( tpl_serlen(r,c,dv, &alen) == -1) return -1; + dv = (void*)((uintptr_t)dv + alen); + len += alen; + break; + case TPL_TYPE_POUND: + /* iterate over the preceding nodes */ + itermax = c->num; + pd = (tpl_pound_data*)c->data; + if (++(pd->iternum) < itermax) { + c = pd->iter_start_node; + continue; + } else { /* loop complete. */ + pd->iternum = 0; + } + break; + default: + tpl_hook.fatal("unsupported format character\n"); + break; + } + c=c->next; + } + } + *serlen = len; + return 0; +} + +static int tpl_mmap_output_file(char *filename, size_t sz, void **text_out) { + void *text; + int fd,perms; + +#ifndef _WIN32 + perms = S_IRUSR|S_IWUSR|S_IWGRP|S_IRGRP|S_IROTH; /* ug+w o+r */ + fd=open(filename,O_CREAT|O_TRUNC|O_RDWR,perms); +#else + perms = _S_IWRITE; + fd=_open(filename,_O_CREAT|_O_TRUNC|_O_RDWR,perms); +#endif + + if ( fd == -1 ) { + tpl_hook.oops("Couldn't open file %s: %s\n", filename, strerror(errno)); + return -1; + } + + text = mmap(0, sz, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + if (text == MAP_FAILED) { + tpl_hook.oops("Failed to mmap %s: %s\n", filename, strerror(errno)); + close(fd); + return -1; + } + if (ftruncate(fd,sz) == -1) { + tpl_hook.oops("ftruncate failed: %s\n", strerror(errno)); + munmap( text, sz ); + close(fd); + return -1; + } + *text_out = text; + return fd; +} + +static int tpl_mmap_file(char *filename, tpl_mmap_rec *mr) { + struct stat stat_buf; + + if ( (mr->fd = open(filename, O_RDONLY)) == -1 ) { + tpl_hook.oops("Couldn't open file %s: %s\n", filename, strerror(errno)); + return -1; + } + + if ( fstat(mr->fd, &stat_buf) == -1) { + close(mr->fd); + tpl_hook.oops("Couldn't stat file %s: %s\n", filename, strerror(errno)); + return -1; + } + + mr->text_sz = (size_t)stat_buf.st_size; + mr->text = mmap(0, stat_buf.st_size, PROT_READ, MAP_PRIVATE, mr->fd, 0); + if (mr->text == MAP_FAILED) { + close(mr->fd); + tpl_hook.oops("Failed to mmap %s: %s\n", filename, strerror(errno)); + return -1; + } + + return 0; +} + +TPL_API int tpl_pack(tpl_node *r, int i) { + tpl_node *n, *child, *np; + void *datav=NULL; + size_t sz, itermax; + uint32_t slen; + char *str; + tpl_bin *bin; + tpl_pound_data *pd; + int fidx; + + n = tpl_find_i(r,i); + if (n == NULL) { + tpl_hook.oops("invalid index %d to tpl_pack\n", i); + return -1; + } + + if (((tpl_root_data*)(r->data))->flags & TPL_RDONLY) { + /* convert to an writeable tpl, initially empty */ + tpl_free_keep_map(r); + } + + ((tpl_root_data*)(r->data))->flags |= TPL_WRONLY; + + if (n->type == TPL_TYPE_ARY) datav = tpl_extend_backbone(n); + child = n->children; + while(child) { + switch(child->type) { + case TPL_TYPE_BYTE: + case TPL_TYPE_DOUBLE: + case TPL_TYPE_INT32: + case TPL_TYPE_UINT32: + case TPL_TYPE_INT64: + case TPL_TYPE_UINT64: + case TPL_TYPE_INT16: + case TPL_TYPE_UINT16: + /* no need to use fidx iteration here; we can copy multiple values in one memcpy */ + memcpy(child->data,child->addr,tpl_types[child->type].sz * child->num); + if (datav) datav = tpl_cpv(datav,child->data,tpl_types[child->type].sz * child->num); + if (n->type == TPL_TYPE_ARY) n->ser_osz += tpl_types[child->type].sz * child->num; + break; + case TPL_TYPE_BIN: + /* copy the buffer to be packed */ + slen = ((tpl_bin*)child->addr)->sz; + if (slen >0) { + str = tpl_hook.malloc(slen); + if (!str) fatal_oom(); + memcpy(str,((tpl_bin*)child->addr)->addr,slen); + } else str = NULL; + /* and make a tpl_bin to point to it */ + bin = tpl_hook.malloc(sizeof(tpl_bin)); + if (!bin) fatal_oom(); + bin->addr = str; + bin->sz = slen; + /* now pack its pointer, first deep freeing any pre-existing bin */ + if (*(tpl_bin**)(child->data) != NULL) { + if ((*(tpl_bin**)(child->data))->sz != 0) { + tpl_hook.free( (*(tpl_bin**)(child->data))->addr ); + } + tpl_hook.free(*(tpl_bin**)(child->data)); + } + memcpy(child->data,&bin,sizeof(tpl_bin*)); + if (datav) { + datav = tpl_cpv(datav, &bin, sizeof(tpl_bin*)); + *(tpl_bin**)(child->data) = NULL; + } + if (n->type == TPL_TYPE_ARY) { + n->ser_osz += sizeof(uint32_t); /* binary buf len word */ + n->ser_osz += bin->sz; /* binary buf */ + } + break; + case TPL_TYPE_STR: + for(fidx=0; fidx < child->num; fidx++) { + /* copy the string to be packed. slen includes \0. this + block also works if the string pointer is NULL. */ + char *caddr = ((char**)child->addr)[fidx]; + char **cdata = &((char**)child->data)[fidx]; + slen = caddr ? (strlen(caddr) + 1) : 0; + if (slen) { + str = tpl_hook.malloc(slen); + if (!str) fatal_oom(); + memcpy(str,caddr,slen); /* include \0 */ + } else { + str = NULL; + } + /* now pack its pointer, first freeing any pre-existing string */ + if (*cdata != NULL) { + tpl_hook.free(*cdata); + } + memcpy(cdata,&str,sizeof(char*)); + if (datav) { + datav = tpl_cpv(datav, &str, sizeof(char*)); + *cdata = NULL; + } + if (n->type == TPL_TYPE_ARY) { + n->ser_osz += sizeof(uint32_t); /* string len word */ + if (slen>1) n->ser_osz += slen-1;/* string (without nul) */ + } + } + break; + case TPL_TYPE_ARY: + /* copy the child's tpl_atype* and reset it to empty */ + if (datav) { + sz = ((tpl_atyp*)(child->data))->sz; + datav = tpl_cpv(datav, &child->data, sizeof(void*)); + child->data = tpl_hook.malloc(sizeof(tpl_atyp)); + if (!child->data) fatal_oom(); + ((tpl_atyp*)(child->data))->num = 0; + ((tpl_atyp*)(child->data))->sz = sz; + ((tpl_atyp*)(child->data))->bb = NULL; + ((tpl_atyp*)(child->data))->bbtail = NULL; + } + /* parent is array? then bubble up child array's ser_osz */ + if (n->type == TPL_TYPE_ARY) { + n->ser_osz += sizeof(uint32_t); /* array len word */ + n->ser_osz += child->ser_osz; /* child array ser_osz */ + child->ser_osz = 0; /* reset child array ser_osz */ + } + break; + + case TPL_TYPE_POUND: + /* we need to iterate n times over preceding nodes in S(...). + * we may be in the midst of an iteration each time or starting. */ + pd = (tpl_pound_data*)child->data; + itermax = child->num; + + /* itermax is total num of iterations needed */ + /* pd->iternum is current iteration index */ + /* pd->inter_elt_len is element-to-element len of contiguous structs */ + /* pd->iter_start_node is where we jump to at each iteration. */ + + if (++(pd->iternum) < itermax) { + + /* in start or midst of loop. advance addr/data pointers. */ + for(np=pd->iter_start_node; np != child; np = np->next) { + np->data = (char*)(np->data) + + (tpl_types[np->type].sz * np->num); + np->addr = (char*)(np->addr) + pd->inter_elt_len; + } + /* do next iteration */ + child = pd->iter_start_node; + continue; + + } else { /* loop complete. */ + + /* reset iteration index and addr/data pointers. */ + pd->iternum = 0; + for(np=pd->iter_start_node; np != child; np = np->next) { + np->data = (char*)(np->data) - ((itermax-1) * + tpl_types[np->type].sz * + np->num); + np->addr = (char*)(np->addr) - ((itermax-1) * pd->inter_elt_len); + } + + } + break; + default: + tpl_hook.fatal("unsupported format character\n"); + break; + } + child=child->next; + } + return 0; +} + +TPL_API int tpl_unpack(tpl_node *r, int i) { + tpl_node *n, *c, *np; + uint32_t slen; + int rc=1, fidx; + char *str; + void *dv=NULL, *caddr; + size_t A_bytes, itermax; + tpl_pound_data *pd; + void *img; + size_t sz; + + + /* handle unusual case of tpl_pack,tpl_unpack without an + * intervening tpl_dump. do a dump/load implicitly. */ + if (((tpl_root_data*)(r->data))->flags & TPL_WRONLY) { + if (tpl_dump(r,TPL_MEM,&img,&sz) != 0) return -1; + if (tpl_load(r,TPL_MEM|TPL_UFREE,img,sz) != 0) { + tpl_hook.free(img); + return -1; + }; + } + + n = tpl_find_i(r,i); + if (n == NULL) { + tpl_hook.oops("invalid index %d to tpl_unpack\n", i); + return -1; + } + + /* either root node or an A node */ + if (n->type == TPL_TYPE_ROOT) { + dv = tpl_find_data_start( ((tpl_root_data*)(n->data))->mmap.text ); + } else if (n->type == TPL_TYPE_ARY) { + if (((tpl_atyp*)(n->data))->num <= 0) return 0; /* array consumed */ + else rc = ((tpl_atyp*)(n->data))->num--; + dv = ((tpl_atyp*)(n->data))->cur; + if (!dv) tpl_hook.fatal("must unpack parent of node before node itself\n"); + } + + c = n->children; + while (c) { + switch (c->type) { + case TPL_TYPE_BYTE: + case TPL_TYPE_DOUBLE: + case TPL_TYPE_INT32: + case TPL_TYPE_UINT32: + case TPL_TYPE_INT64: + case TPL_TYPE_UINT64: + case TPL_TYPE_INT16: + case TPL_TYPE_UINT16: + /* unpack elements of cross-endian octothorpic array individually */ + if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN) { + for(fidx=0; fidx < c->num; fidx++) { + caddr = (void*)((uintptr_t)c->addr + (fidx * tpl_types[c->type].sz)); + memcpy(caddr,dv,tpl_types[c->type].sz); + tpl_byteswap(caddr, tpl_types[c->type].sz); + dv = (void*)((uintptr_t)dv + tpl_types[c->type].sz); + } + } else { + /* bulk unpack ok if not cross-endian */ + memcpy(c->addr, dv, tpl_types[c->type].sz * c->num); + dv = (void*)((uintptr_t)dv + tpl_types[c->type].sz * c->num); + } + break; + case TPL_TYPE_BIN: + memcpy(&slen,dv,sizeof(uint32_t)); + if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN) + tpl_byteswap(&slen, sizeof(uint32_t)); + if (slen > 0) { + str = (char*)tpl_hook.malloc(slen); + if (!str) fatal_oom(); + } else str=NULL; + dv = (void*)((uintptr_t)dv + sizeof(uint32_t)); + if (slen>0) memcpy(str,dv,slen); + memcpy(&(((tpl_bin*)c->addr)->addr),&str,sizeof(void*)); + memcpy(&(((tpl_bin*)c->addr)->sz),&slen,sizeof(uint32_t)); + dv = (void*)((uintptr_t)dv + slen); + break; + case TPL_TYPE_STR: + for(fidx=0; fidx < c->num; fidx++) { + memcpy(&slen,dv,sizeof(uint32_t)); + if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN) + tpl_byteswap(&slen, sizeof(uint32_t)); + if (((tpl_root_data*)(r->data))->flags & TPL_OLD_STRING_FMT) + slen += 1; + dv = (void*)((uintptr_t)dv + sizeof(uint32_t)); + if (slen) { /* slen includes \0 */ + str = (char*)tpl_hook.malloc(slen); + if (!str) fatal_oom(); + if (slen>1) memcpy(str,dv,slen-1); + str[slen-1] = '\0'; /* nul terminate */ + dv = (void*)((uintptr_t)dv + slen-1); + } else str=NULL; + memcpy(&((char**)c->addr)[fidx],&str,sizeof(char*)); + } + break; + case TPL_TYPE_POUND: + /* iterate over preceding nodes */ + pd = (tpl_pound_data*)c->data; + itermax = c->num; + if (++(pd->iternum) < itermax) { + /* in start or midst of loop. advance addr/data pointers. */ + for(np=pd->iter_start_node; np != c; np = np->next) { + np->addr = (char*)(np->addr) + pd->inter_elt_len; + } + /* do next iteration */ + c = pd->iter_start_node; + continue; + + } else { /* loop complete. */ + + /* reset iteration index and addr/data pointers. */ + pd->iternum = 0; + for(np=pd->iter_start_node; np != c; np = np->next) { + np->addr = (char*)(np->addr) - ((itermax-1) * pd->inter_elt_len); + } + + } + break; + case TPL_TYPE_ARY: + if (tpl_serlen(r,c,dv, &A_bytes) == -1) + tpl_hook.fatal("internal error in unpack\n"); + memcpy( &((tpl_atyp*)(c->data))->num, dv, sizeof(uint32_t)); + if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN) + tpl_byteswap(&((tpl_atyp*)(c->data))->num, sizeof(uint32_t)); + ((tpl_atyp*)(c->data))->cur = (void*)((uintptr_t)dv+sizeof(uint32_t)); + dv = (void*)((uintptr_t)dv + A_bytes); + break; + default: + tpl_hook.fatal("unsupported format character\n"); + break; + } + + c = c->next; + } + if (n->type == TPL_TYPE_ARY) ((tpl_atyp*)(n->data))->cur = dv; /* next element */ + return rc; +} + +/* Specialized function that unpacks only the root's A nodes, after tpl_load */ +static int tpl_unpackA0(tpl_node *r) { + tpl_node *n, *c; + uint32_t slen; + int rc=1,fidx,i; + void *dv; + size_t A_bytes, itermax; + tpl_pound_data *pd; + + n = r; + dv = tpl_find_data_start( ((tpl_root_data*)(r->data))->mmap.text); + + c=n->children; + while (c) { + switch (c->type) { + case TPL_TYPE_BYTE: + case TPL_TYPE_DOUBLE: + case TPL_TYPE_INT32: + case TPL_TYPE_UINT32: + case TPL_TYPE_INT64: + case TPL_TYPE_UINT64: + case TPL_TYPE_INT16: + case TPL_TYPE_UINT16: + for(fidx=0;fidx < c->num; fidx++) { + dv = (void*)((uintptr_t)dv + tpl_types[c->type].sz); + } + break; + case TPL_TYPE_BIN: + memcpy(&slen,dv,sizeof(uint32_t)); + if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN) + tpl_byteswap(&slen, sizeof(uint32_t)); + dv = (void*)((uintptr_t)dv + sizeof(uint32_t)); + dv = (void*)((uintptr_t)dv + slen); + break; + case TPL_TYPE_STR: + for(i=0; inum; i++) { + memcpy(&slen,dv,sizeof(uint32_t)); + if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN) + tpl_byteswap(&slen, sizeof(uint32_t)); + if (((tpl_root_data*)(r->data))->flags & TPL_OLD_STRING_FMT) + slen += 1; + dv = (void*)((uintptr_t)dv + sizeof(uint32_t)); + if (slen>1) dv = (void*)((uintptr_t)dv + slen-1); + } + break; + case TPL_TYPE_POUND: + /* iterate over the preceding nodes */ + itermax = c->num; + pd = (tpl_pound_data*)c->data; + if (++(pd->iternum) < itermax) { + c = pd->iter_start_node; + continue; + } else { /* loop complete. */ + pd->iternum = 0; + } + break; + case TPL_TYPE_ARY: + if ( tpl_serlen(r,c,dv, &A_bytes) == -1) + tpl_hook.fatal("internal error in unpackA0\n"); + memcpy( &((tpl_atyp*)(c->data))->num, dv, sizeof(uint32_t)); + if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN) + tpl_byteswap(&((tpl_atyp*)(c->data))->num, sizeof(uint32_t)); + ((tpl_atyp*)(c->data))->cur = (void*)((uintptr_t)dv+sizeof(uint32_t)); + dv = (void*)((uintptr_t)dv + A_bytes); + break; + default: + tpl_hook.fatal("unsupported format character\n"); + break; + } + c=c->next; + } + return rc; +} + +/* In-place byte order swapping of a word of length "len" bytes */ +static void tpl_byteswap(void *word, int len) { + int i; + char c, *w; + w = (char*)word; + for(i=0; i0) ? rc : 0; + } while ((rc==-1 && (errno==EINTR||errno==EAGAIN)) || (rc>0 && i<8)); + + if (rc<0) { + tpl_hook.oops("tpl_gather_fd_blocking failed: %s\n", strerror(errno)); + return -1; + } else if (rc == 0) { + /* tpl_hook.oops("tpl_gather_fd_blocking: eof\n"); */ + return 0; + } else if (i != 8) { + tpl_hook.oops("internal error\n"); + return -1; + } + + if (preamble[0] == 't' && preamble[1] == 'p' && preamble[2] == 'l') { + memcpy(&tpllen,&preamble[4],4); + if (tpl_needs_endian_swap(preamble)) tpl_byteswap(&tpllen,4); + } else { + tpl_hook.oops("tpl_gather_fd_blocking: non-tpl input\n"); + return -1; + } + + /* malloc space for remainder of tpl image (overall length tpllen) + * and read it in + */ + if (tpl_hook.gather_max > 0 && + tpllen > tpl_hook.gather_max) { + tpl_hook.oops("tpl exceeds max length %d\n", + tpl_hook.gather_max); + return -2; + } + *sz = tpllen; + if ( (*img = tpl_hook.malloc(tpllen)) == NULL) { + fatal_oom(); + } + + memcpy(*img,preamble,8); /* copy preamble to output buffer */ + i=8; + do { + rc = read(fd,&((*(char**)img)[i]),tpllen-i); + i += (rc>0) ? rc : 0; + } while ((rc==-1 && (errno==EINTR||errno==EAGAIN)) || (rc>0 && iimg); + tpl_hook.free(*gs); + *gs = NULL; + } + return -1; /* error, caller should close fd */ + } + } else if (rc == 0) { + if (*gs) { + tpl_hook.oops("tpl_gather: partial tpl image precedes EOF\n"); + tpl_hook.free((*gs)->img); + tpl_hook.free(*gs); + *gs = NULL; + } + return 0; /* EOF, caller should close fd */ + } else { + /* concatenate any partial tpl from last read with new buffer */ + if (*gs) { + catlen = (*gs)->len + rc; + if (tpl_hook.gather_max > 0 && + catlen > tpl_hook.gather_max) { + tpl_hook.free( (*gs)->img ); + tpl_hook.free( (*gs) ); + *gs = NULL; + tpl_hook.oops("tpl exceeds max length %d\n", + tpl_hook.gather_max); + return -2; /* error, caller should close fd */ + } + if ( (img = tpl_hook.realloc((*gs)->img, catlen)) == NULL) { + fatal_oom(); + } + memcpy(img + (*gs)->len, buf, rc); + tpl_hook.free(*gs); + *gs = NULL; + } else { + img = buf; + catlen = rc; + } + /* isolate any full tpl(s) in img and invoke cb for each */ + tpl = img; + keep_looping = (tpl+8 < img+catlen) ? 1 : 0; + while (keep_looping) { + if (strncmp("tpl", tpl, 3) != 0) { + tpl_hook.oops("tpl prefix invalid\n"); + if (img != buf) tpl_hook.free(img); + tpl_hook.free(*gs); + *gs = NULL; + return -3; /* error, caller should close fd */ + } + memcpy(&tpllen,&tpl[4],4); + if (tpl_needs_endian_swap(tpl)) tpl_byteswap(&tpllen,4); + if (tpl+tpllen <= img+catlen) { + cbrc = (cb)(tpl,tpllen,data); /* invoke cb for tpl image */ + tpl += tpllen; /* point to next tpl image */ + if (cbrc < 0) keep_looping = 0; + else keep_looping = (tpl+8 < img+catlen) ? 1 : 0; + } else keep_looping=0; + } + /* check if app callback requested closure of tpl source */ + if (cbrc < 0) { + tpl_hook.oops("tpl_fd_gather aborted by app callback\n"); + if (img != buf) tpl_hook.free(img); + if (*gs) tpl_hook.free(*gs); + *gs = NULL; + return -4; + } + /* store any leftover, partial tpl fragment for next read */ + if (tpl == img && img != buf) { + /* consumed nothing from img!=buf */ + if ( (*gs = tpl_hook.malloc(sizeof(tpl_gather_t))) == NULL ) { + fatal_oom(); + } + (*gs)->img = tpl; + (*gs)->len = catlen; + } else if (tpl < img+catlen) { + /* consumed 1+ tpl(s) from img!=buf or 0 from img==buf */ + if ( (*gs = tpl_hook.malloc(sizeof(tpl_gather_t))) == NULL ) { + fatal_oom(); + } + if ( ((*gs)->img = tpl_hook.malloc(img+catlen - tpl)) == NULL ) { + fatal_oom(); + } + (*gs)->len = img+catlen - tpl; + memcpy( (*gs)->img, tpl, img+catlen - tpl); + /* free partially consumed concat buffer if used */ + if (img != buf) tpl_hook.free(img); + } else { /* tpl(s) fully consumed */ + /* free consumed concat buffer if used */ + if (img != buf) tpl_hook.free(img); + } + } + } +} + +/* gather tpl piecemeal from memory buffer (not fd) e.g., from a lower-level api */ +static int tpl_gather_mem( char *buf, size_t len, tpl_gather_t **gs, tpl_gather_cb *cb, void *data) { + char *img, *tpl; + int keep_looping, cbrc=0; + size_t catlen; + uint32_t tpllen; + + /* concatenate any partial tpl from last read with new buffer */ + if (*gs) { + catlen = (*gs)->len + len; + if (tpl_hook.gather_max > 0 && + catlen > tpl_hook.gather_max) { + tpl_hook.free( (*gs)->img ); + tpl_hook.free( (*gs) ); + *gs = NULL; + tpl_hook.oops("tpl exceeds max length %d\n", + tpl_hook.gather_max); + return -2; /* error, caller should stop accepting input from source*/ + } + if ( (img = tpl_hook.realloc((*gs)->img, catlen)) == NULL) { + fatal_oom(); + } + memcpy(img + (*gs)->len, buf, len); + tpl_hook.free(*gs); + *gs = NULL; + } else { + img = buf; + catlen = len; + } + /* isolate any full tpl(s) in img and invoke cb for each */ + tpl = img; + keep_looping = (tpl+8 < img+catlen) ? 1 : 0; + while (keep_looping) { + if (strncmp("tpl", tpl, 3) != 0) { + tpl_hook.oops("tpl prefix invalid\n"); + if (img != buf) tpl_hook.free(img); + tpl_hook.free(*gs); + *gs = NULL; + return -3; /* error, caller should stop accepting input from source*/ + } + memcpy(&tpllen,&tpl[4],4); + if (tpl_needs_endian_swap(tpl)) tpl_byteswap(&tpllen,4); + if (tpl+tpllen <= img+catlen) { + cbrc = (cb)(tpl,tpllen,data); /* invoke cb for tpl image */ + tpl += tpllen; /* point to next tpl image */ + if (cbrc < 0) keep_looping = 0; + else keep_looping = (tpl+8 < img+catlen) ? 1 : 0; + } else keep_looping=0; + } + /* check if app callback requested closure of tpl source */ + if (cbrc < 0) { + tpl_hook.oops("tpl_mem_gather aborted by app callback\n"); + if (img != buf) tpl_hook.free(img); + if (*gs) tpl_hook.free(*gs); + *gs = NULL; + return -4; + } + /* store any leftover, partial tpl fragment for next read */ + if (tpl == img && img != buf) { + /* consumed nothing from img!=buf */ + if ( (*gs = tpl_hook.malloc(sizeof(tpl_gather_t))) == NULL ) { + fatal_oom(); + } + (*gs)->img = tpl; + (*gs)->len = catlen; + } else if (tpl < img+catlen) { + /* consumed 1+ tpl(s) from img!=buf or 0 from img==buf */ + if ( (*gs = tpl_hook.malloc(sizeof(tpl_gather_t))) == NULL ) { + fatal_oom(); + } + if ( ((*gs)->img = tpl_hook.malloc(img+catlen - tpl)) == NULL ) { + fatal_oom(); + } + (*gs)->len = img+catlen - tpl; + memcpy( (*gs)->img, tpl, img+catlen - tpl); + /* free partially consumed concat buffer if used */ + if (img != buf) tpl_hook.free(img); + } else { /* tpl(s) fully consumed */ + /* free consumed concat buffer if used */ + if (img != buf) tpl_hook.free(img); + } + return 1; +} diff --git a/3party/tpl/tpl.h b/3party/tpl/tpl.h new file mode 100644 index 0000000..977b789 --- /dev/null +++ b/3party/tpl/tpl.h @@ -0,0 +1,137 @@ +/* +Copyright (c) 2005-2013, Troy D. Hanson http://troydhanson.github.com/tpl/ +All rights reserved. + +Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TPL_H +#define TPL_H + +#include /* size_t */ + +#include /* va_list */ + +#ifdef __INTEL_COMPILER +#include +#endif /* Intel Compiler efficient memcpy etc */ + +#ifdef _MSC_VER + typedef unsigned int uint32_t; +#else +#include /* uint32_t */ +#endif + +#if defined __cplusplus +extern "C" { +#endif + +#ifdef _WIN32 +#ifdef TPL_EXPORTS +#define TPL_API __declspec(dllexport) +#else /* */ +#ifdef TPL_NOLIB +#define TPL_API +#else +#define TPL_API __declspec(dllimport) +#endif /* TPL_NOLIB */ +#endif /* TPL_EXPORTS*/ +#else +#define TPL_API +#endif + +/* bit flags (external) */ +#define TPL_FILE (1 << 0) +#define TPL_MEM (1 << 1) +#define TPL_PREALLOCD (1 << 2) +#define TPL_EXCESS_OK (1 << 3) +#define TPL_FD (1 << 4) +#define TPL_UFREE (1 << 5) +#define TPL_DATAPEEK (1 << 6) +#define TPL_FXLENS (1 << 7) +#define TPL_GETSIZE (1 << 8) +/* do not add flags here without renumbering the internal flags! */ + +/* flags for tpl_gather mode */ +#define TPL_GATHER_BLOCKING 1 +#define TPL_GATHER_NONBLOCKING 2 +#define TPL_GATHER_MEM 3 + +/* Hooks for error logging, memory allocation functions and fatal */ +typedef int (tpl_print_fcn)(const char *fmt, ...); +typedef void *(tpl_malloc_fcn)(size_t sz); +typedef void *(tpl_realloc_fcn)(void *ptr, size_t sz); +typedef void (tpl_free_fcn)(void *ptr); +typedef void (tpl_fatal_fcn)(const char *fmt, ...); + +typedef struct tpl_hook_t { + tpl_print_fcn *oops; + tpl_malloc_fcn *malloc; + tpl_realloc_fcn *realloc; + tpl_free_fcn *free; + tpl_fatal_fcn *fatal; + size_t gather_max; +} tpl_hook_t; + +typedef struct tpl_node { + int type; + void *addr; + void *data; /* r:tpl_root_data*. A:tpl_atyp*. ow:szof type */ + int num; /* length of type if its a C array */ + size_t ser_osz; /* serialization output size for subtree */ + struct tpl_node *children; /* my children; linked-list */ + struct tpl_node *next,*prev; /* my siblings (next child of my parent) */ + struct tpl_node *parent; /* my parent */ +} tpl_node; + +/* used when un/packing 'B' type (binary buffers) */ +typedef struct tpl_bin { + void *addr; + uint32_t sz; +} tpl_bin; + +/* for async/piecemeal reading of tpl images */ +typedef struct tpl_gather_t { + char *img; + int len; +} tpl_gather_t; + +/* Callback used when tpl_gather has read a full tpl image */ +typedef int (tpl_gather_cb)(void *img, size_t sz, void *data); + +/* Prototypes */ +TPL_API tpl_node *tpl_map(char *fmt,...); /* define tpl using format */ +TPL_API void tpl_free(tpl_node *r); /* free a tpl map */ +TPL_API int tpl_pack(tpl_node *r, int i); /* pack the n'th packable */ +TPL_API int tpl_unpack(tpl_node *r, int i); /* unpack the n'th packable */ +TPL_API int tpl_dump(tpl_node *r, int mode, ...); /* serialize to mem/file */ +TPL_API int tpl_load(tpl_node *r, int mode, ...); /* set mem/file to unpack */ +TPL_API int tpl_Alen(tpl_node *r, int i); /* array len of packable i */ +TPL_API char* tpl_peek(int mode, ...); /* sneak peek at format string */ +TPL_API int tpl_gather( int mode, ...); /* non-blocking image gather */ +TPL_API int tpl_jot(int mode, ...); /* quick write a simple tpl */ + +TPL_API tpl_node *tpl_map_va(char *fmt, va_list ap); + +#if defined __cplusplus +} +#endif + +#endif /* TPL_H */ + diff --git a/CMakeLists.txt b/CMakeLists.txt index 2eb6e93..e34a9a1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,3 +26,12 @@ target_link_libraries(crpc PRIVATE antlr4_static) target_include_directories(crpc PRIVATE /opt/dev/vcpkg/installed/arm64-osx/include/antlr4-runtime) target_include_directories(crpc PRIVATE src/gen/ src/) +FILE(GLOB_RECURSE TEST_SRC_LIST ${CMAKE_CURRENT_BINARY_DIR}/gen/*.c) +add_executable(test ${TEST_SRC_LIST} + test/test.c + ${CMAKE_CURRENT_SOURCE_DIR}/3party/tpl/tpl.c +) +target_include_directories(test PRIVATE + ${CMAKE_CURRENT_BINARY_DIR}/gen/ + ${CMAKE_CURRENT_SOURCE_DIR}/3party/tpl +) \ No newline at end of file diff --git a/src/plugins/c/c_message_plugin.cpp b/src/plugins/c/c_message_plugin.cpp index 814b8a4..743d423 100644 --- a/src/plugins/c/c_message_plugin.cpp +++ b/src/plugins/c/c_message_plugin.cpp @@ -141,7 +141,7 @@ CMessagePlugin::GenerateFunctionDeclaration(Message::Ptr message) ss << std::endl; ss << "int32_t " << message->id() << "_Serialize(" << message->id() - << "* message, char* buffer, int32_t buffer_size);" << std::endl; + << "* message, char** buffer, int32_t *buffer_size);" << std::endl; ss << "int32_t " << message->id() << "_Deserialize(" << message->id() << "* message, char* buffer, int32_t buffer_size);" << std::endl; ss << std::endl; @@ -185,41 +185,77 @@ CMessagePlugin::GenerateSourceIncludeFile(Message::Ptr message) ss << "#include \"" << GenerateHeaderFileName(message->id()) << "\"" << std::endl; ss << "#include " << std::endl; ss << "#include " << std::endl; + ss << "#include " << std::endl; ss << std::endl; return ss.str(); } + +static std::string GetTPLMap(std::vector items) +{ + std::stringstream ss; + + ss << "S("; + for (auto &item : items) { + switch(item->type()) { + case Item::Type::kInt32: + ss << "i"; + break; + case Item::Type::kString: + ss << "s"; + break; + case Item::Type::kMessage: + // donothing + break; + } + } + ss << ")"; + + return ss.str(); +} + std::string CMessagePlugin::GenerateFunctionDefinitionSerialize(Message::Ptr message) { std::stringstream ss; ss << "int32_t " << message->id() << "_Serialize(" << message->id() - << "* message, char* buffer, int32_t buffer_size) {" << std::endl; - ss << " int32_t offset = 0;" << std::endl; - for (auto &item : message->items()) { - auto c_item = CItem::Create(item); - ss << c_item->GenerateSerializeCodeBlock(message->id()) << std::endl; + << "* message, char** buffer, int32_t* buffer_size) {" << std::endl; + if (!message->items().empty()) { + std::string tpl_format = GetTPLMap(message->items()); + ss << " do {" << std::endl; + ss << " tpl_node* tn = tpl_map(\"" << tpl_format << "\", message);" << std::endl; + ss << " tpl_pack(tn, 0);" << std::endl; + ss << " tpl_dump(tn, TPL_MEM, buffer, buffer_size);" << std::endl; + ss << " tpl_free(tn);" << std::endl; + ss << " return *buffer_size; " << std::endl; + ss << " } while (0);" << std::endl; } - ss << " return offset;" << std::endl; + ss << " return 0; " << std::endl; ss << "}" << std::endl; - ss << std::endl; return ss.str(); } std::string CMessagePlugin::GenerateFunctionDefinitionDeserialize(Message::Ptr message) { + std::stringstream ss; ss << "int32_t " << message->id() << "_Deserialize(" << message->id() << "* message, char* buffer, int32_t buffer_size) {" << std::endl; - ss << " int32_t offset = 0;" << std::endl; - for (auto &item : message->items()) { - auto c_item = CItem::Create(item); - ss << c_item->GenerateDeserializeCodeBlock(message->id()) << std::endl; + if (!message->items().empty()) { + std::string tpl_format = GetTPLMap(message->items()); + ss << " do {" << std::endl; + ss << " " << message->id() << "_Destroy(message);" << std::endl; + ss << " size_t dump_sz = buffer_size;" << std::endl; + ss << " tpl_node* tn = tpl_map(\"" << tpl_format << "\", message);" << std::endl; + ss << " tpl_load(tn, TPL_MEM, buffer, dump_sz);" << std::endl; + ss << " tpl_unpack(tn, 0);" << std::endl; + ss << " tpl_free(tn);" << std::endl; + ss << " return dump_sz; " << std::endl; + ss << " } while (0);" << std::endl; } - ss << " return offset;" << std::endl; + ss << " return 0; " << std::endl; ss << "}" << std::endl; - ss << std::endl; return ss.str(); } diff --git a/src/test.proto b/src/test.proto index 401beec..af6dcd6 100644 --- a/src/test.proto +++ b/src/test.proto @@ -26,4 +26,4 @@ service Device { rpc UnlockApplicationPIN(ApplicationPINRequest) returns (StatusResponse) {} rpc ChangeApplicationPIN(ApplicationPINRequest) returns (StatusResponse) {} rpc VerifyApplicationPIN(ApplicationPINRequest) returns (StatusResponse) {} -} \ No newline at end of file +} diff --git a/test/test.c b/test/test.c new file mode 100644 index 0000000..25de1e2 --- /dev/null +++ b/test/test.c @@ -0,0 +1,117 @@ +// +// Created by tqcq on 2023/12/2. +// + +#include "StatusResponse.h" +#include +#include +#include +#include +#include "tpl.h" + + +struct ms_t { + char c; + int i; + char s[2]; + char o; + char *x; + char y; + double d; +}; + +void TestTPL() { + tpl_node *tn; + struct ms_t ms = {/*.c =*/ 'a', /*.i =*/ 1, /*.s =*/ {'h','i'}, /*.o =*/ 'o', /*.x =*/ "matilda", /*.y =*/ 'y', /*.d =*/ 3.14 }; + struct ms_t ms2; + char buffer[1024]; + int dump_sz = 0; + + tn = tpl_map("S(cic#cscf)", &ms, 2); + tpl_pack(tn,0); + // tpl_dump(tn,TPL_FILE,"/tmp/test70.tpl"); + tpl_dump(tn,TPL_MEM,buffer,&dump_sz); + tpl_free(tn); + + + memset(&ms2,0,sizeof(struct ms_t)); + tn = tpl_map("S(cic#cscf)", &ms2, 2); + // tpl_load(tn,TPL_FILE,"/tmp/test70.tpl"); + tpl_load(tn,TPL_MEM,buffer,dump_sz); + tpl_unpack(tn,0); + tpl_free(tn); + printf("%c\n%d\n%c%c\n%c\n%s\n%c\n%f\n", ms2.c, ms2.i, ms2.s[0], ms2.s[1], ms2.o, ms2.x, ms2.y, ms2.d); + free(ms2.x); +} + +void TestTPL2() { + tpl_node *tn; + char *s,*t; + void *addr; + int sz; + + tn = tpl_map("s",&s); + s = "hello, world!"; + tpl_pack(tn,0); + tpl_dump(tn,TPL_MEM,&addr,&sz); + tpl_free(tn); + + tn = tpl_map("s",&t); + tpl_load(tn,TPL_MEM,addr,sz); + tpl_unpack(tn,0); + printf("t is %s\n", t); + free(t); + tpl_free(tn); + free(addr); +} + +struct TestS { + int32_t a; + char* str; +}; + +void TestTPL3() { + struct TestS s; + s.a = 1; + s.str = "hello"; + tpl_node *tn = tpl_map("S(is)", &s); + tpl_pack(tn, 0); + + char *buffer; + int dump_sz = 0; + tpl_dump(tn, TPL_MEM, &buffer, &dump_sz); + tpl_free(tn); + + printf("dump_sz: %d\n", dump_sz); + memset(&s, 0, sizeof(struct TestS)); + tn = tpl_map("S(is)", &s); + tpl_load(tn, TPL_MEM, buffer, dump_sz); + tpl_unpack(tn, 0); + tpl_free(tn); + printf("%d\n%s\n", s.a, s.str); +} + +int main(int argc, char*argv[]) { + // TestTPL3(); + char *buffer; + int dump_sz = 0; + StatusResponse response; + { + StatusResponse_Init(&response); + StatusResponse_set_code(&response, 200); + StatusResponse_set_msg(&response, "OK"); + dump_sz = StatusResponse_Serialize(&response, &buffer, &dump_sz); + StatusResponse_Destroy(&response); + } + + printf("dump_sz: %d\n", dump_sz); + + { + StatusResponse_Init(&response); + StatusResponse_Deserialize(&response, buffer, dump_sz); + printf("code: %d\n", StatusResponse_get_code(&response)); + printf("msg: %s\n", StatusResponse_get_msg(&response)); + StatusResponse_Destroy(&response); + } + return 0; +}