2 Copyright (c) 2005-2010, Troy D. Hanson http://tpl.sourceforge.net
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
8 * Redistributions of source code must retain the above copyright
9 notice, this list of conditions and the following disclaimer.
11 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
12 IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
13 TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
14 PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
15 OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
16 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
17 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
18 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
19 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
20 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
21 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 #define TPL_VERSION 1.5
26 static const char id[]="$Id: tpl.c 192 2009-04-24 10:35:30Z thanson $";
29 #include <stdlib.h> /* malloc */
30 #include <stdarg.h> /* va_list */
31 #include <string.h> /* memcpy, memset, strchr */
32 #include <stdio.h> /* printf (tpl_hook.oops default function) */
35 #include <unistd.h> /* for ftruncate */
38 #define ftruncate(x,y) _chsize(x,y)
40 #include <sys/types.h> /* for 'open' */
41 #include <sys/stat.h> /* for 'open' */
42 #include <fcntl.h> /* for 'open' */
45 #include <inttypes.h> /* uint32_t, uint64_t, etc */
47 typedef unsigned short ushort;
48 typedef __int16 int16_t;
49 typedef __int32 int32_t;
50 typedef __int64 int64_t;
51 typedef unsigned __int16 uint16_t;
52 typedef unsigned __int32 uint32_t;
53 typedef unsigned __int64 uint64_t;
57 #if ( defined __CYGWIN__ || defined __MINGW32__ || defined _WIN32 )
58 #include "win/mman.h" /* mmap */
60 #include <sys/mman.h> /* mmap */
64 #include "email-debug-log.h"
67 #define TPL_GATHER_BUFLEN 8192
68 #define TPL_MAGIC "tpl"
70 /* macro to add a structure to a doubly-linked list */
71 #define DL_ADD(head,add) \
74 (add)->prev = (head)->prev; \
75 (head)->prev->next = (add); \
76 (head)->prev = (add); \
80 (head)->prev = (head); \
81 (head)->next = NULL; \
85 #define fatal_oom() tpl_hook.fatal("out of memory\n")
87 /* bit flags (internal). preceded by the external flags in tpl.h */
88 #define TPL_WRONLY (1 << 9) /* app has initiated tpl packing */
89 #define TPL_RDONLY (1 << 10) /* tpl was loaded (for unpacking) */
90 #define TPL_XENDIAN (1 << 11) /* swap endianness when unpacking */
91 #define TPL_OLD_STRING_FMT (1 << 12) /* tpl has strings in 1.2 format */
93 /* values for the flags byte that appears after the magic prefix */
94 #define TPL_SUPPORTED_BITFLAGS 3
95 #define TPL_FL_BIGENDIAN (1 << 0)
96 #define TPL_FL_NULLSTRINGS (1 << 1)
98 /* char values for node type */
99 #define TPL_TYPE_ROOT 0
100 #define TPL_TYPE_INT32 1
101 #define TPL_TYPE_UINT32 2
102 #define TPL_TYPE_BYTE 3
103 #define TPL_TYPE_STR 4
104 #define TPL_TYPE_ARY 5
105 #define TPL_TYPE_BIN 6
106 #define TPL_TYPE_DOUBLE 7
107 #define TPL_TYPE_INT64 8
108 #define TPL_TYPE_UINT64 9
109 #define TPL_TYPE_INT16 10
110 #define TPL_TYPE_UINT16 11
111 #define TPL_TYPE_POUND 12
114 #define ERR_NOT_MINSIZE (-1)
115 #define ERR_MAGIC_MISMATCH (-2)
116 #define ERR_INCONSISTENT_SZ (-3)
117 #define ERR_FMT_INVALID (-4)
118 #define ERR_FMT_MISSING_NUL (-5)
119 #define ERR_FMT_MISMATCH (-6)
120 #define ERR_FLEN_MISMATCH (-7)
121 #define ERR_INCONSISTENT_SZ2 (-8)
122 #define ERR_INCONSISTENT_SZ3 (-9)
123 #define ERR_INCONSISTENT_SZ4 (-10)
124 #define ERR_UNSUPPORTED_FLAGS (-11)
126 /* access to A(...) nodes by index */
127 typedef struct tpl_pidx {
128 struct tpl_node *node;
129 struct tpl_pidx *next,*prev;
132 /* A(...) node datum */
133 typedef struct tpl_atyp {
134 uint32_t num; /* num elements */
135 size_t sz; /* size of each backbone's datum */
136 struct tpl_backbone *bb,*bbtail;
140 /* backbone to extend A(...) lists dynamically */
141 typedef struct tpl_backbone {
142 struct tpl_backbone *next;
143 /* when this structure is malloc'd, extra space is alloc'd at the
144 * end to store the backbone "datum", and data points to it. */
145 #if __STDC_VERSION__ < 199901
153 typedef struct tpl_mmap_rec {
159 /* root node datum */
160 typedef struct tpl_root_data {
165 int *fxlens, num_fxlens;
168 /* node type to size mapping */
175 /* Internal prototypes */
176 static tpl_node *tpl_node_new(tpl_node *parent);
177 static tpl_node *tpl_find_i(tpl_node *n, int i);
178 static void *tpl_cpv(void *datav, void *data, size_t sz);
179 static void *tpl_extend_backbone(tpl_node *n);
180 static char *tpl_fmt(tpl_node *r);
181 static void *tpl_dump_atyp(tpl_node *n, tpl_atyp* at, void *dv);
182 static size_t tpl_ser_osz(tpl_node *n);
183 static void tpl_free_atyp(tpl_node *n,tpl_atyp *atyp);
184 static int tpl_dump_to_mem(tpl_node *r, void *addr, size_t sz);
185 static int tpl_mmap_file(char *filename, tpl_mmap_rec *map_rec);
186 static int tpl_mmap_output_file(char *filename, size_t sz, void **text_out);
187 static int tpl_cpu_bigendian(void);
188 static int tpl_needs_endian_swap(void *);
189 static void tpl_byteswap(void *word, int len);
190 static void tpl_fatal(char *fmt, ...);
191 static int tpl_serlen(tpl_node *r, tpl_node *n, void *dv, size_t *serlen);
192 static int tpl_unpackA0(tpl_node *r);
193 static int tpl_oops(const char *fmt, ...);
194 static int tpl_gather_mem( char *buf, size_t len, tpl_gather_t **gs, tpl_gather_cb *cb, void *data);
195 static int tpl_gather_nonblocking( int fd, tpl_gather_t **gs, tpl_gather_cb *cb, void *data);
196 static int tpl_gather_blocking(int fd, void **img, size_t *sz);
197 static tpl_node *tpl_map_va(char *fmt, va_list ap);
199 /* This is used internally to help calculate padding when a 'double'
200 * follows a smaller datatype in a structure. Normally under gcc
201 * on x86, d will be aligned at +4, however use of -malign-double
202 * causes d to be aligned at +8 (this is actually faster on x86).
203 * Also SPARC and x86_64 seem to align always on +8.
205 struct tpl_double_alignment_detector {
207 double d; /* some platforms align this on +4, others on +8 */
210 /* this is another case where alignment varies. mac os x/gcc was observed
211 * to align the int64_t at +4 under -m32 and at +8 under -m64 */
212 struct tpl_int64_alignment_detector {
214 int64_t j; /* some platforms align this on +4, others on +8 */
218 size_t inter_elt_len; /* padded inter-element len; i.e. &a[1].field - &a[0].field */
219 tpl_node *iter_start_node; /* node to jump back to, as we start each new iteration */
220 size_t iternum; /* current iteration number (total req'd. iter's in n->num) */
223 /* Hooks for customizing tpl mem alloc, error handling, etc. Set defaults. */
224 tpl_hook_t tpl_hook = {
225 /* .oops = */ tpl_oops,
226 /* .malloc = */ malloc,
227 /* .realloc = */ realloc,
229 /* .fatal = */ tpl_fatal,
230 /* .gather_max = */ 0 /* max tpl size (bytes) for tpl_gather */
233 static const char tpl_fmt_chars[] = "AS($)BiucsfIUjv#"; /* valid format chars */
234 static const char tpl_S_fmt_chars[] = "iucsfIUjv#$()"; /* valid within S(...) */
235 static const char tpl_datapeek_ok_chars[] = "iucsfIUjv"; /* valid in datapeek */
236 static const struct tpl_type_t tpl_types[] = {
237 /* [TPL_TYPE_ROOT] = */ {'r', 0},
238 /* [TPL_TYPE_INT32] = */ {'i', sizeof(int32_t)},
239 /* [TPL_TYPE_UINT32] = */ {'u', sizeof(uint32_t)},
240 /* [TPL_TYPE_BYTE] = */ {'c', sizeof(char)},
241 /* [TPL_TYPE_STR] = */ {'s', sizeof(char*)},
242 /* [TPL_TYPE_ARY] = */ {'A', 0},
243 /* [TPL_TYPE_BIN] = */ {'B', 0},
244 /* [TPL_TYPE_DOUBLE] = */ {'f', 8}, /* not sizeof(double) as that varies */
245 /* [TPL_TYPE_INT64] = */ {'I', sizeof(int64_t)},
246 /* [TPL_TYPE_UINT64] = */ {'U', sizeof(uint64_t)},
247 /* [TPL_TYPE_INT16] = */ {'j', sizeof(int16_t)},
248 /* [TPL_TYPE_UINT16] = */ {'v', sizeof(uint16_t)},
249 /* [TPL_TYPE_POUND] = */ {'#', 0},
252 /* default error-reporting function. Just writes to stderr. */
253 static int tpl_oops(const char *fmt, ...) {
257 /* vfprintf(stderr,fmt,ap); */
258 snprintf(buf, sizeof(buf)-1, fmt, ap);
259 EM_DEBUG_EXCEPTION("%s", buf);
265 static tpl_node *tpl_node_new(tpl_node *parent) {
267 if ((n=tpl_hook.malloc(sizeof(tpl_node))) == NULL) {
280 /* Used in S(..) formats to pack several fields from a structure based on
281 * only the structure address. We need to calculate field addresses
282 * manually taking into account the size of the fields and intervening padding.
283 * The wrinkle is that double is not normally aligned on x86-32 but the
284 * -malign-double compiler option causes it to be. Double are aligned
285 * on Sparc, and apparently on 64 bit x86. We use a helper structure
286 * to detect whether double is aligned in this compilation environment.
288 char *calc_field_addr(tpl_node *parent, int type,char *struct_addr, int ordinal) {
293 if (ordinal == 1) return struct_addr; /* first field starts on structure address */
295 /* generate enough padding so field addr is divisible by it's align_sz. 4, 8, etc */
296 prev = parent->children->prev;
298 case TPL_TYPE_DOUBLE:
299 align_sz = sizeof(struct tpl_double_alignment_detector) > 12 ? 8 : 4;
302 case TPL_TYPE_UINT64:
303 align_sz = sizeof(struct tpl_int64_alignment_detector) > 12 ? 8 : 4;
306 align_sz = tpl_types[type].sz;
309 EM_DEBUG_LOG("prev->addr : [%p], struct_addr : [%p]", prev->addr, struct_addr);
310 offset = ((uintptr_t)prev->addr - (uintptr_t)struct_addr)
311 + (tpl_types[prev->type].sz * prev->num);
312 EM_DEBUG_LOG("offset : [%d], align_sz : [%d]", offset, align_sz);
313 offset = (offset + align_sz - 1) / align_sz * align_sz;
314 EM_DEBUG_LOG("Real offset : [%d]", offset);
315 return struct_addr + offset;
318 TPL_API tpl_node *tpl_map(char *fmt,...) {
323 tn = tpl_map_va(fmt, ap);
328 static tpl_node *tpl_map_va(char *fmt, va_list ap) {
329 int lparen_level=0,expect_lparen=0,t=0,in_structure=0,ordinal=0;
330 int in_nested_structure=0;
331 char *c, *peek, *struct_addr=NULL, *struct_next;
332 tpl_node *root,*parent,*n=NULL,*preceding,*iter_start_node=NULL,
333 *struct_widest_node=NULL, *np; tpl_pidx *pidx;
335 int *fxlens, num_fxlens, pound_num, pound_prod, applies_to_struct;
336 int contig_fxlens[10]; /* temp space for contiguous fxlens */
337 int num_contig_fxlens, i, j;
338 ptrdiff_t inter_elt_len=0; /* padded element length of contiguous structs in array */
341 root = tpl_node_new(NULL);
342 root->type = TPL_TYPE_ROOT;
343 root->data = (tpl_root_data*)tpl_hook.malloc(sizeof(tpl_root_data));
344 if (!root->data) fatal_oom();
345 memset((tpl_root_data*)root->data,0,sizeof(tpl_root_data));
347 /* set up root nodes special ser_osz to reflect overhead of preamble */
348 root->ser_osz = sizeof(uint32_t); /* tpl leading length */
349 root->ser_osz += EM_SAFE_STRLEN(fmt) + 1; /* fmt + NUL-terminator */
350 root->ser_osz += 4; /* 'tpl' magic prefix + flags byte */
365 if (*c=='c') t=TPL_TYPE_BYTE;
366 else if (*c=='i') t=TPL_TYPE_INT32;
367 else if (*c=='u') t=TPL_TYPE_UINT32;
368 else if (*c=='j') t=TPL_TYPE_INT16;
369 else if (*c=='v') t=TPL_TYPE_UINT16;
370 else if (*c=='I') t=TPL_TYPE_INT64;
371 else if (*c=='U') t=TPL_TYPE_UINT64;
372 else if (*c=='f') t=TPL_TYPE_DOUBLE;
374 if (expect_lparen) goto fail;
375 n = tpl_node_new(parent);
379 /* for S(...)# iteration. Apply any changes to case 's' too!!! */
381 struct_widest_node = n;
383 if (tpl_types[n->type].sz > tpl_types[struct_widest_node->type].sz) {
384 struct_widest_node = n;
386 n->addr = calc_field_addr(parent,n->type,struct_addr,ordinal++);
387 } else n->addr = (void*)va_arg(ap,void*);
388 n->data = tpl_hook.malloc(tpl_types[t].sz);
389 if (!n->data) fatal_oom();
390 if (n->parent->type == TPL_TYPE_ARY)
391 ((tpl_atyp*)(n->parent->data))->sz += tpl_types[t].sz;
392 DL_ADD(parent->children,n);
395 if (expect_lparen) goto fail;
396 n = tpl_node_new(parent);
397 n->type = TPL_TYPE_STR;
400 iter_start_node = n; /* for S(...)# iteration */
401 struct_widest_node = n;
403 if (tpl_types[n->type].sz > tpl_types[struct_widest_node->type].sz) {
404 struct_widest_node = n;
406 n->addr = calc_field_addr(parent,n->type,struct_addr,ordinal++);
407 } else n->addr = (void*)va_arg(ap,void*);
408 n->data = tpl_hook.malloc(sizeof(char*));
409 if (!n->data) fatal_oom();
410 *(char**)(n->data) = NULL;
411 if (n->parent->type == TPL_TYPE_ARY)
412 ((tpl_atyp*)(n->parent->data))->sz += sizeof(void*);
413 DL_ADD(parent->children,n);
416 /* apply a 'num' to preceding atom */
417 if (!parent->children) goto fail;
418 preceding = parent->children->prev; /* first child's prev is 'last child'*/
420 applies_to_struct = (*(c-1) == ')') ? 1 : 0;
421 if (!applies_to_struct) {
422 if (!(t == TPL_TYPE_BYTE || t == TPL_TYPE_INT32 ||
423 t == TPL_TYPE_UINT32 || t == TPL_TYPE_DOUBLE ||
424 t == TPL_TYPE_UINT64 || t == TPL_TYPE_INT64 ||
425 t == TPL_TYPE_UINT16 || t == TPL_TYPE_INT16 ||
426 t == TPL_TYPE_STR )) goto fail;
428 /* count up how many contiguous # and form their product */
431 for(peek=c; *peek == '#'; peek++) {
432 pound_num = va_arg(ap, int);
434 tpl_hook.fatal("non-positive iteration count %d\n", pound_num);
436 if (num_contig_fxlens >= (sizeof(contig_fxlens)/sizeof(contig_fxlens[0]))) {
437 tpl_hook.fatal("contiguous # exceeds hardcoded limit\n");
439 contig_fxlens[num_contig_fxlens++] = pound_num;
440 pound_prod *= pound_num;
442 /* increment c to skip contiguous # so its points to last one */
444 /* differentiate atom-# from struct-# by noting preceding rparen */
445 if (applies_to_struct) { /* insert # node to induce looping */
446 n = tpl_node_new(parent);
447 n->type = TPL_TYPE_POUND;
449 n->data = tpl_hook.malloc(sizeof(tpl_pound_data));
450 if (!n->data) fatal_oom();
451 pd = (tpl_pound_data*)n->data;
452 pd->inter_elt_len = inter_elt_len;
453 pd->iter_start_node = iter_start_node;
455 DL_ADD(parent->children,n);
456 /* multiply the 'num' and data space on each atom in the structure */
457 for(np = iter_start_node; np != n; np = np->next) {
458 if (n->parent->type == TPL_TYPE_ARY) {
459 ((tpl_atyp*)(n->parent->data))->sz +=
460 tpl_types[np->type].sz * (np->num * (n->num - 1));
462 np->data = tpl_hook.realloc(np->data, tpl_types[np->type].sz *
464 if (!np->data) fatal_oom();
465 memset(np->data, 0, tpl_types[np->type].sz * np->num * n->num);
467 } else { /* simple atom-# form does not require a loop */
468 preceding->num = pound_prod;
469 preceding->data = tpl_hook.realloc(preceding->data,
470 tpl_types[t].sz * preceding->num);
471 if (!preceding->data) fatal_oom();
472 memset(preceding->data,0,tpl_types[t].sz * preceding->num);
473 if (n->parent->type == TPL_TYPE_ARY) {
474 ((tpl_atyp*)(n->parent->data))->sz += tpl_types[t].sz *
478 root->ser_osz += (sizeof(uint32_t) * num_contig_fxlens);
480 j = ((tpl_root_data*)root->data)->num_fxlens; /* before incrementing */
481 (((tpl_root_data*)root->data)->num_fxlens) += num_contig_fxlens;
482 num_fxlens = ((tpl_root_data*)root->data)->num_fxlens; /* new value */
483 fxlens = ((tpl_root_data*)root->data)->fxlens;
484 fxlens = tpl_hook.realloc(fxlens, sizeof(int) * num_fxlens);
485 if (!fxlens) fatal_oom();
486 ((tpl_root_data*)root->data)->fxlens = fxlens;
487 for(i=0; i < num_contig_fxlens; i++) fxlens[j++] = contig_fxlens[i];
491 if (expect_lparen) goto fail;
492 if (in_structure) goto fail;
493 n = tpl_node_new(parent);
494 n->type = TPL_TYPE_BIN;
495 n->addr = (tpl_bin*)va_arg(ap,void*);
496 n->data = tpl_hook.malloc(sizeof(tpl_bin*));
497 if (!n->data) fatal_oom();
498 *((tpl_bin**)n->data) = NULL;
499 if (n->parent->type == TPL_TYPE_ARY)
500 ((tpl_atyp*)(n->parent->data))->sz += sizeof(tpl_bin);
501 DL_ADD(parent->children,n);
504 if (in_structure) goto fail;
505 n = tpl_node_new(parent);
506 n->type = TPL_TYPE_ARY;
507 DL_ADD(parent->children,n);
510 pidx = (tpl_pidx*)tpl_hook.malloc(sizeof(tpl_pidx));
511 if (!pidx) fatal_oom();
514 DL_ADD(((tpl_root_data*)(root->data))->pidx,pidx);
515 /* set up the A's tpl_atyp */
516 n->data = (tpl_atyp*)tpl_hook.malloc(sizeof(tpl_atyp));
517 if (!n->data) fatal_oom();
518 ((tpl_atyp*)(n->data))->num = 0;
519 ((tpl_atyp*)(n->data))->sz = 0;
520 ((tpl_atyp*)(n->data))->bb = NULL;
521 ((tpl_atyp*)(n->data))->bbtail = NULL;
522 ((tpl_atyp*)(n->data))->cur = NULL;
523 if (n->parent->type == TPL_TYPE_ARY)
524 ((tpl_atyp*)(n->parent->data))->sz += sizeof(void*);
527 if (in_structure) goto fail;
529 ordinal=1; /* index upcoming atoms in S(..) */
530 in_structure=1+lparen_level; /* so we can tell where S fmt ends */
531 struct_addr = (char*)va_arg(ap,void*);
533 case '$': /* nested structure */
534 if (!in_structure) goto fail;
536 in_nested_structure++;
540 if (lparen_level < 0) goto fail;
541 if (*(c-1) == '(') goto fail;
542 if (in_nested_structure) in_nested_structure--;
543 else if (in_structure && (in_structure-1 == lparen_level)) {
544 /* calculate delta between contiguous structures in array */
545 struct_next = calc_field_addr(parent, struct_widest_node->type,
546 struct_addr, ordinal++);
547 inter_elt_len = struct_next - struct_addr;
550 else parent = parent->parent; /* rparen ends A() type, not S() type */
553 if (!expect_lparen) goto fail;
558 tpl_hook.oops("unsupported option %c\n", *c);
563 if (lparen_level != 0) goto fail;
565 /* copy the format string, save for convenience */
566 ((tpl_root_data*)(root->data))->fmt = tpl_hook.malloc(EM_SAFE_STRLEN(fmt)+1);
567 if (((tpl_root_data*)(root->data))->fmt == NULL)
569 memcpy(((tpl_root_data*)(root->data))->fmt,fmt,EM_SAFE_STRLEN(fmt)+1);
574 tpl_hook.oops("failed to parse %s\n", fmt);
579 static int tpl_unmap_file( tpl_mmap_rec *mr) {
581 if ( munmap( mr->text, mr->text_sz ) == -1 ) {
582 tpl_hook.oops("Failed to munmap: %s\n", strerror(errno));
590 static void tpl_free_keep_map(tpl_node *r) {
591 int mmap_bits = (TPL_RDONLY|TPL_FILE);
592 int ufree_bits = (TPL_MEM|TPL_UFREE);
594 int find_next_node=0,looking,i;
597 /* For mmap'd files, or for 'ufree' memory images , do appropriate release */
598 if ((((tpl_root_data*)(r->data))->flags & mmap_bits) == mmap_bits) {
599 tpl_unmap_file( &((tpl_root_data*)(r->data))->mmap);
600 } else if ((((tpl_root_data*)(r->data))->flags & ufree_bits) == ufree_bits) {
601 tpl_hook.free( ((tpl_root_data*)(r->data))->mmap.text );
606 while(c->type != TPL_TYPE_ROOT) { /* loop until we come back to root node */
609 /* free any binary buffer hanging from tpl_bin */
610 if ( *((tpl_bin**)(c->data)) ) {
611 if ( (*((tpl_bin**)(c->data)))->addr ) {
612 tpl_hook.free( (*((tpl_bin**)(c->data)))->addr );
614 *((tpl_bin**)c->data) = NULL; /* reset tpl_bin */
619 /* free any packed (copied) string */
620 for(i=0; i < c->num; i++) {
621 char *str = ((char**)c->data)[i];
624 ((char**)c->data)[i] = NULL;
630 case TPL_TYPE_UINT32:
632 case TPL_TYPE_UINT64:
634 case TPL_TYPE_DOUBLE:
636 case TPL_TYPE_UINT16:
641 c->ser_osz = 0; /* zero out the serialization output size */
643 sz = ((tpl_atyp*)(c->data))->sz; /* save sz to use below */
644 tpl_free_atyp(c,c->data);
647 c->data = (tpl_atyp*)tpl_hook.malloc(sizeof(tpl_atyp));
648 if (!c->data) fatal_oom();
649 ((tpl_atyp*)(c->data))->num = 0;
650 ((tpl_atyp*)(c->data))->sz = sz; /* restore bb datum sz */
651 ((tpl_atyp*)(c->data))->bb = NULL;
652 ((tpl_atyp*)(c->data))->bbtail = NULL;
653 ((tpl_atyp*)(c->data))->cur = NULL;
658 tpl_hook.fatal("unsupported format character\n");
662 if (find_next_node) {
671 if (c->type == TPL_TYPE_ROOT) break; /* root node */
682 ((tpl_root_data*)(r->data))->flags = 0; /* reset flags */
685 TPL_API void tpl_free(tpl_node *r) {
686 int mmap_bits = (TPL_RDONLY|TPL_FILE);
687 int ufree_bits = (TPL_MEM|TPL_UFREE);
689 int find_next_node=0,looking,i;
690 tpl_pidx *pidx,*pidx_nxt;
692 /* For mmap'd files, or for 'ufree' memory images , do appropriate release */
693 if ((((tpl_root_data*)(r->data))->flags & mmap_bits) == mmap_bits) {
694 tpl_unmap_file( &((tpl_root_data*)(r->data))->mmap);
695 } else if ((((tpl_root_data*)(r->data))->flags & ufree_bits) == ufree_bits) {
696 tpl_hook.free( ((tpl_root_data*)(r->data))->mmap.text );
701 while(c->type != TPL_TYPE_ROOT) { /* loop until we come back to root node */
704 /* free any binary buffer hanging from tpl_bin */
705 if ( *((tpl_bin**)(c->data)) ) {
706 if ( (*((tpl_bin**)(c->data)))->sz != 0 ) {
707 tpl_hook.free( (*((tpl_bin**)(c->data)))->addr );
709 tpl_hook.free(*((tpl_bin**)c->data)); /* free tpl_bin */
711 tpl_hook.free(c->data); /* free tpl_bin* */
715 /* free any packed (copied) string */
716 for(i=0; i < c->num; i++) {
717 char *str = ((char**)c->data)[i];
720 ((char**)c->data)[i] = NULL;
723 tpl_hook.free(c->data);
727 case TPL_TYPE_UINT32:
729 case TPL_TYPE_UINT64:
731 case TPL_TYPE_DOUBLE:
733 case TPL_TYPE_UINT16:
735 tpl_hook.free(c->data);
739 tpl_free_atyp(c,c->data);
740 if (c->children) c = c->children; /* normal case */
741 else find_next_node=1; /* edge case, handle bad format A() */
744 tpl_hook.fatal("unsupported format character\n");
748 if (find_next_node) {
758 if (c->type == TPL_TYPE_ROOT) break; /* root node */
771 for(pidx=((tpl_root_data*)(r->data))->pidx; pidx; pidx=pidx_nxt) {
772 pidx_nxt = pidx->next;
775 tpl_hook.free(((tpl_root_data*)(r->data))->fmt);
776 if (((tpl_root_data*)(r->data))->num_fxlens > 0) {
777 tpl_hook.free(((tpl_root_data*)(r->data))->fxlens);
779 tpl_hook.free(r->data); /* tpl_root_data */
784 /* Find the i'th packable ('A' node) */
785 static tpl_node *tpl_find_i(tpl_node *n, int i) {
788 if (n->type != TPL_TYPE_ROOT) return NULL;
789 if (i == 0) return n; /* packable 0 is root */
790 for(pidx=((tpl_root_data*)(n->data))->pidx; pidx; pidx=pidx->next) {
791 if (++j == i) return pidx->node;
796 static void *tpl_cpv(void *datav, void *data, size_t sz) {
797 if (sz>0) memcpy(datav,data,sz);
798 return (void*)((uintptr_t)datav + sz);
801 static void *tpl_extend_backbone(tpl_node *n) {
803 bb = (tpl_backbone*)tpl_hook.malloc(sizeof(tpl_backbone) +
804 ((tpl_atyp*)(n->data))->sz ); /* datum hangs on coattails of bb */
805 if (!bb) fatal_oom();
806 #if __STDC_VERSION__ < 199901
807 bb->data = (char*)((uintptr_t)bb + sizeof(tpl_backbone));
809 memset(bb->data,0,((tpl_atyp*)(n->data))->sz);
811 /* Add the new backbone to the tail, also setting head if necessary */
812 if (((tpl_atyp*)(n->data))->bb == NULL) {
813 ((tpl_atyp*)(n->data))->bb = bb;
814 ((tpl_atyp*)(n->data))->bbtail = bb;
816 ((tpl_atyp*)(n->data))->bbtail->next = bb;
817 ((tpl_atyp*)(n->data))->bbtail = bb;
820 ((tpl_atyp*)(n->data))->num++;
824 /* Get the format string corresponding to a given tpl (root node) */
825 static char *tpl_fmt(tpl_node *r) {
826 return ((tpl_root_data*)(r->data))->fmt;
829 /* Get the fmt # lengths as a contiguous buffer of ints (length num_fxlens) */
830 static int *tpl_fxlens(tpl_node *r, int *num_fxlens) {
831 *num_fxlens = ((tpl_root_data*)(r->data))->num_fxlens;
832 return ((tpl_root_data*)(r->data))->fxlens;
835 /* called when serializing an 'A' type node into a buffer which has
836 * already been set up with the proper space. The backbone is walked
837 * which was obtained from the tpl_atyp header passed in.
839 static void *tpl_dump_atyp(tpl_node *n, tpl_atyp* at, void *dv) {
851 /* handle 'A' nodes */
852 dv = tpl_cpv(dv,&at->num,sizeof(uint32_t)); /* array len */
853 for(bb=at->bb; bb; bb=bb->next) {
859 case TPL_TYPE_DOUBLE:
861 case TPL_TYPE_UINT32:
863 case TPL_TYPE_UINT64:
865 case TPL_TYPE_UINT16:
866 dv = tpl_cpv(dv,datav,tpl_types[c->type].sz * c->num);
867 datav = (void*)((uintptr_t)datav + tpl_types[c->type].sz * c->num);
870 /* dump the buffer length followed by the buffer */
871 memcpy(&binp,datav,sizeof(tpl_bin*)); /* cp to aligned */
873 dv = tpl_cpv(dv,&slen,sizeof(uint32_t));
874 dv = tpl_cpv(dv,binp->addr,slen);
875 datav = (void*)((uintptr_t)datav + sizeof(tpl_bin*));
878 /* dump the string length followed by the string */
879 for(i=0; i < c->num; i++) {
880 memcpy(&strp,datav,sizeof(char*)); /* cp to aligned */
881 slen = strp ? (EM_SAFE_STRLEN(strp)+1) : 0;
882 dv = tpl_cpv(dv,&slen,sizeof(uint32_t));
883 if (slen > 1) dv = tpl_cpv(dv,strp,slen-1);
884 datav = (void*)((uintptr_t)datav + sizeof(char*));
888 memcpy(&atypp,datav,sizeof(tpl_atyp*)); /* cp to aligned */
889 dv = tpl_dump_atyp(c,atypp,dv);
890 datav = (void*)((uintptr_t)datav + sizeof(void*));
893 /* iterate over the preceding nodes */
894 pd = (tpl_pound_data*)c->data;
896 if (++(pd->iternum) < itermax) {
897 c = pd->iter_start_node;
899 } else { /* loop complete. */
904 tpl_hook.fatal("unsupported format character\n");
913 /* figure the serialization output size needed for tpl whose root is n*/
914 static size_t tpl_ser_osz(tpl_node *n) {
922 /* handle the root node ONLY (subtree's ser_osz have been bubbled-up) */
923 if (n->type != TPL_TYPE_ROOT) {
924 tpl_hook.fatal("internal error: tpl_ser_osz on non-root node\n");
927 sz = n->ser_osz; /* start with fixed overhead, already stored */
932 case TPL_TYPE_DOUBLE:
934 case TPL_TYPE_UINT32:
936 case TPL_TYPE_UINT64:
938 case TPL_TYPE_UINT16:
939 sz += tpl_types[c->type].sz * c->num;
942 sz += sizeof(uint32_t); /* binary buf len */
943 memcpy(&binp,c->data,sizeof(tpl_bin*)); /* cp to aligned */
947 for(i=0; i < c->num; i++) {
948 sz += sizeof(uint32_t); /* string len */
949 memcpy(&strp,&((char**)c->data)[i],sizeof(char*)); /* cp to aligned */
950 sz += strp ? EM_SAFE_STRLEN(strp) : 0;
954 sz += sizeof(uint32_t); /* array len */
955 sz += c->ser_osz; /* bubbled-up child array ser_osz */
958 /* iterate over the preceding nodes */
960 pd = (tpl_pound_data*)c->data;
961 if (++(pd->iternum) < itermax) {
962 for(np=pd->iter_start_node; np != c; np = np->next) {
963 np->data = (char*)(np->data) +
964 (tpl_types[np->type].sz * np->num);
966 c = pd->iter_start_node;
968 } else { /* loop complete. */
970 for(np=pd->iter_start_node; np != c; np = np->next) {
971 np->data = (char*)(np->data) - ((itermax-1) *
972 tpl_types[np->type].sz *
978 tpl_hook.fatal("unsupported format character\n");
987 TPL_API int tpl_dump(tpl_node *r, int mode, ...) {
989 char *filename, *bufv;
990 void **addr_out,*buf, *pa_addr;
992 size_t sz,*sz_out, pa_sz;
994 if (((tpl_root_data*)(r->data))->flags & TPL_RDONLY) { /* unusual */
995 tpl_hook.oops("error: tpl_dump called for a loaded tpl\n");
999 sz = tpl_ser_osz(r); /* compute the size needed to serialize */
1002 if (mode & TPL_FILE) {
1003 filename = va_arg(ap,char*);
1004 fd = tpl_mmap_output_file(filename, sz, &buf);
1005 if (fd == -1) rc = -1;
1007 rc = tpl_dump_to_mem(r,buf,sz);
1008 if (msync(buf,sz,MS_SYNC) == -1) {
1009 tpl_hook.oops("msync failed on fd %d: %s\n", fd, strerror(errno));
1011 if (munmap(buf, sz) == -1) {
1012 tpl_hook.oops("munmap failed on fd %d: %s\n", fd, strerror(errno));
1016 } else if (mode & TPL_FD) {
1017 fd = va_arg(ap, int);
1018 if ( (buf = tpl_hook.malloc(sz)) == NULL) fatal_oom();
1019 tpl_dump_to_mem(r,buf,sz);
1022 rc = write(fd,bufv,sz);
1026 } else if (rc == -1) {
1027 if (errno == EINTR || errno == EAGAIN) continue;
1028 tpl_hook.oops("error writing to fd %d: %s\n", fd, strerror(errno));
1036 } else if (mode & TPL_MEM) {
1037 if (mode & TPL_PREALLOCD) { /* caller allocated */
1038 pa_addr = (void*)va_arg(ap, void*);
1039 pa_sz = va_arg(ap, size_t);
1041 tpl_hook.oops("tpl_dump: buffer too small, need %d bytes\n", sz);
1045 rc=tpl_dump_to_mem(r,pa_addr,sz);
1046 } else { /* we allocate */
1047 addr_out = (void**)va_arg(ap, void*);
1048 sz_out = va_arg(ap, size_t*);
1049 if ( (buf = tpl_hook.malloc(sz)) == NULL) fatal_oom();
1052 rc=tpl_dump_to_mem(r,buf,sz);
1054 } else if (mode & TPL_GETSIZE) {
1055 sz_out = va_arg(ap, size_t*);
1058 tpl_hook.oops("unsupported tpl_dump mode %d\n", mode);
1065 /* This function expects the caller to have set up a memory buffer of
1066 * adequate size to hold the serialized tpl. The sz parameter must be
1067 * the result of tpl_ser_osz(r).
1069 static int tpl_dump_to_mem(tpl_node *r,void *addr,size_t sz) {
1070 uint32_t slen, sz32;
1071 int *fxlens, num_fxlens, i;
1080 if (tpl_cpu_bigendian()) flags |= TPL_FL_BIGENDIAN;
1081 if (strchr(fmt,'s')) flags |= TPL_FL_NULLSTRINGS;
1085 dv = tpl_cpv(dv,TPL_MAGIC,3); /* copy tpl magic prefix */
1086 dv = tpl_cpv(dv,&flags,1); /* copy flags byte */
1087 dv = tpl_cpv(dv,&sz32,sizeof(uint32_t));/* overall length (inclusive) */
1088 dv = tpl_cpv(dv,fmt,EM_SAFE_STRLEN(fmt)+1); /* copy format with NUL-term */
1089 fxlens = tpl_fxlens(r,&num_fxlens);
1090 dv = tpl_cpv(dv,fxlens,num_fxlens*sizeof(uint32_t));/* fmt # lengths */
1092 /* serialize the tpl content, iterating over direct children of root */
1097 case TPL_TYPE_DOUBLE:
1098 case TPL_TYPE_INT32:
1099 case TPL_TYPE_UINT32:
1100 case TPL_TYPE_INT64:
1101 case TPL_TYPE_UINT64:
1102 case TPL_TYPE_INT16:
1103 case TPL_TYPE_UINT16:
1104 dv = tpl_cpv(dv,c->data,tpl_types[c->type].sz * c->num);
1107 slen = (*(tpl_bin**)(c->data))->sz;
1108 dv = tpl_cpv(dv,&slen,sizeof(uint32_t)); /* buffer len */
1109 dv = tpl_cpv(dv,(*(tpl_bin**)(c->data))->addr,slen); /* buf */
1112 for(i=0; i < c->num; i++) {
1113 char *str = ((char**)c->data)[i];
1114 slen = str ? EM_SAFE_STRLEN(str)+1 : 0;
1115 dv = tpl_cpv(dv,&slen,sizeof(uint32_t)); /* string len */
1116 if (slen>1) dv = tpl_cpv(dv,str,slen-1); /*string*/
1120 dv = tpl_dump_atyp(c,(tpl_atyp*)c->data,dv);
1122 case TPL_TYPE_POUND:
1123 pd = (tpl_pound_data*)c->data;
1125 if (++(pd->iternum) < itermax) {
1127 /* in start or midst of loop. advance data pointers. */
1128 for(np=pd->iter_start_node; np != c; np = np->next) {
1129 np->data = (char*)(np->data) +
1130 (tpl_types[np->type].sz * np->num);
1132 /* do next iteration */
1133 c = pd->iter_start_node;
1136 } else { /* loop complete. */
1138 /* reset iteration index and addr/data pointers. */
1140 for(np=pd->iter_start_node; np != c; np = np->next) {
1141 np->data = (char*)(np->data) - ((itermax-1) *
1142 tpl_types[np->type].sz *
1149 tpl_hook.fatal("unsupported format character\n");
1158 static int tpl_cpu_bigendian() {
1162 return (c[0] == 1 ? 0 : 1);
1167 * algorithm for sanity-checking a tpl image:
1168 * scan the tpl whilst not exceeding the buffer size (bufsz) ,
1169 * formulating a calculated (expected) size of the tpl based
1170 * on walking its data. When calcsize has been calculated it
1171 * should exactly match the buffer size (bufsz) and the internal
1172 * recorded size (intlsz)
1174 static int tpl_sanity(tpl_node *r, int excess_ok) {
1176 int found_nul=0,rc, octothorpes=0, num_fxlens, *fxlens, flen;
1178 char intlflags, *fmt, c, *mapfmt;
1179 size_t bufsz, serlen;
1181 d = ((tpl_root_data*)(r->data))->mmap.text;
1182 bufsz = ((tpl_root_data*)(r->data))->mmap.text_sz;
1185 if (bufsz < (4 + sizeof(uint32_t) + 1)) return ERR_NOT_MINSIZE; /* min sz: magic+flags+len+nul */
1186 if (memcmp(dv,TPL_MAGIC, 3) != 0) return ERR_MAGIC_MISMATCH; /* missing tpl magic prefix */
1187 if (tpl_needs_endian_swap(dv)) ((tpl_root_data*)(r->data))->flags |= TPL_XENDIAN;
1188 dv = (void*)((uintptr_t)dv + 3);
1189 memcpy(&intlflags,dv,sizeof(char)); /* extract flags */
1190 if (intlflags & ~TPL_SUPPORTED_BITFLAGS) return ERR_UNSUPPORTED_FLAGS;
1191 /* TPL1.3 stores strings with a "length+1" prefix to discern NULL strings from
1192 empty strings from non-empty strings; TPL1.2 only handled the latter two.
1193 So we need to be mindful of which string format we're reading from. */
1194 if (!(intlflags & TPL_FL_NULLSTRINGS)) {
1195 ((tpl_root_data*)(r->data))->flags |= TPL_OLD_STRING_FMT;
1197 dv = (void*)((uintptr_t)dv + 1);
1198 memcpy(&intlsz,dv,sizeof(uint32_t)); /* extract internal size */
1199 if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN) tpl_byteswap(&intlsz, sizeof(uint32_t));
1200 if (!excess_ok && (intlsz != bufsz)) return ERR_INCONSISTENT_SZ; /* inconsisent buffer/internal size */
1201 dv = (void*)((uintptr_t)dv + sizeof(uint32_t));
1203 /* dv points to the start of the format string. Look for nul w/in buf sz */
1205 while ((uintptr_t)dv-(uintptr_t)d < bufsz && !found_nul) {
1206 if ( (c = *(char*)dv) != '\0') {
1207 if (strchr(tpl_fmt_chars,c) == NULL)
1208 return ERR_FMT_INVALID; /* invalid char in format string */
1209 if ( (c = *(char*)dv) == '#') octothorpes++;
1210 dv = (void*)((uintptr_t)dv + 1);
1214 if (!found_nul) return ERR_FMT_MISSING_NUL; /* runaway format string */
1215 dv = (void*)((uintptr_t)dv + 1); /* advance to octothorpe lengths buffer */
1217 /* compare the map format to the format of this tpl image */
1218 mapfmt = tpl_fmt(r);
1219 rc = strcmp(mapfmt,fmt);
1220 if (rc != 0) return ERR_FMT_MISMATCH;
1222 /* compare octothorpe lengths in image to the mapped values */
1223 if ((((uintptr_t)dv + (octothorpes * 4)) - (uintptr_t)d) > bufsz) return ERR_INCONSISTENT_SZ4;
1224 fxlens = tpl_fxlens(r,&num_fxlens); /* mapped fxlens */
1225 while(num_fxlens--) {
1226 memcpy(&flen,dv,sizeof(uint32_t)); /* stored flen */
1227 if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN) tpl_byteswap(&flen, sizeof(uint32_t));
1228 if (flen != *fxlens) return ERR_FLEN_MISMATCH;
1229 dv = (void*)((uintptr_t)dv + sizeof(uint32_t));
1233 /* dv now points to beginning of data */
1234 rc = tpl_serlen(r,r,dv,&serlen); /* get computed serlen of data part */
1235 if (rc == -1) return ERR_INCONSISTENT_SZ2; /* internal inconsistency in tpl image */
1236 serlen += ((uintptr_t)dv - (uintptr_t)d); /* add back serlen of preamble part */
1237 if (excess_ok && (bufsz < serlen)) return ERR_INCONSISTENT_SZ3;
1238 if (!excess_ok && (serlen != bufsz)) return ERR_INCONSISTENT_SZ3; /* buffer/internal sz exceeds serlen */
1242 static void *tpl_find_data_start(void *d) {
1244 d = (void*)((uintptr_t)d + 4); /* skip TPL_MAGIC and flags byte */
1245 d = (void*)((uintptr_t)d + 4); /* skip int32 overall len */
1246 while(*(char*)d != '\0') {
1247 if (*(char*)d == '#') octothorpes++;
1248 d = (void*)((uintptr_t)d + 1);
1250 d = (void*)((uintptr_t)d + 1); /* skip NUL */
1251 d = (void*)((uintptr_t)d + (octothorpes * sizeof(uint32_t))); /* skip # array lens */
1255 static int tpl_needs_endian_swap(void *d) {
1257 int cpu_is_bigendian;
1259 cpu_is_bigendian = tpl_cpu_bigendian();
1260 return ((c[3] & TPL_FL_BIGENDIAN) == cpu_is_bigendian) ? 0 : 1;
1263 static size_t tpl_size_for(char c) {
1265 for(i=0; i < sizeof(tpl_types)/sizeof(tpl_types[0]); i++) {
1266 if (tpl_types[i].c == c) return tpl_types[i].sz;
1271 TPL_API char* tpl_peek(int mode, ...) {
1273 int xendian=0,found_nul=0,old_string_format=0;
1274 char *filename=NULL, *datapeek_f=NULL, *datapeek_c, *datapeek_s;
1275 void *addr=NULL, *dv, *datapeek_p=NULL;
1276 size_t sz=0, fmt_len, first_atom, num_fxlens=0;
1277 uint32_t datapeek_ssz, datapeek_csz, datapeek_flen;
1278 tpl_mmap_rec mr = {0,NULL,0};
1279 char *fmt,*fmt_cpy=NULL,c;
1280 uint32_t intlsz, **fxlens=NULL, *num_fxlens_out=NULL, *fxlensv;
1283 if ((mode & TPL_FXLENS) && (mode & TPL_DATAPEEK)) {
1284 tpl_hook.oops("TPL_FXLENS and TPL_DATAPEEK mutually exclusive\n");
1287 if (mode & TPL_FILE) filename = va_arg(ap,char *);
1288 else if (mode & TPL_MEM) {
1289 addr = va_arg(ap,void *);
1290 sz = va_arg(ap,size_t);
1292 tpl_hook.oops("unsupported tpl_peek mode %d\n", mode);
1295 if (mode & TPL_DATAPEEK) {
1296 datapeek_f = va_arg(ap, char*);
1298 if (mode & TPL_FXLENS) {
1299 num_fxlens_out = va_arg(ap,uint32_t *);
1300 fxlens = va_arg(ap,uint32_t **);
1301 *num_fxlens_out = 0;
1305 if (mode & TPL_FILE) {
1306 if (tpl_mmap_file(filename, &mr) != 0) {
1307 tpl_hook.oops("tpl_peek failed for file %s\n", filename);
1315 if (sz < (4 + sizeof(uint32_t) + 1)) goto fail; /* min sz */
1316 if (memcmp(dv,TPL_MAGIC, 3) != 0) goto fail; /* missing tpl magic prefix */
1317 if (tpl_needs_endian_swap(dv)) xendian=1;
1318 if ((((char*)dv)[3] & TPL_FL_NULLSTRINGS)==0) old_string_format=1;
1319 dv = (void*)((uintptr_t)dv + 4);
1320 memcpy(&intlsz,dv,sizeof(uint32_t)); /* extract internal size */
1321 if (xendian) tpl_byteswap(&intlsz, sizeof(uint32_t));
1322 if (intlsz != sz) goto fail; /* inconsisent buffer/internal size */
1323 dv = (void*)((uintptr_t)dv + sizeof(uint32_t));
1325 /* dv points to the start of the format string. Look for nul w/in buf sz */
1327 while ((uintptr_t)dv-(uintptr_t)addr < sz && !found_nul) {
1328 if ( (c = *(char*)dv) == '\0') {
1330 } else if (c == '#') {
1333 dv = (void*)((uintptr_t)dv + 1);
1335 if (!found_nul) goto fail; /* runaway format string */
1336 fmt_len = (char*)dv - fmt; /* include space for \0 */
1337 fmt_cpy = tpl_hook.malloc(fmt_len);
1338 if (fmt_cpy == NULL) {
1341 memcpy(fmt_cpy, fmt, fmt_len);
1343 /* retrieve the octothorpic lengths if requested */
1344 if (num_fxlens > 0) {
1345 if (sz < ((uintptr_t)dv + (num_fxlens * sizeof(uint32_t)) - (uintptr_t)addr)) {
1349 if ((mode & TPL_FXLENS) && (num_fxlens > 0)) {
1350 *fxlens = tpl_hook.malloc(num_fxlens * sizeof(uint32_t));
1351 if (*fxlens == NULL) tpl_hook.fatal("out of memory");
1352 *num_fxlens_out = num_fxlens;
1354 while(num_fxlens--) {
1355 memcpy(fxlensv,dv,sizeof(uint32_t));
1356 if (xendian) tpl_byteswap(fxlensv, sizeof(uint32_t));
1357 dv = (void*)((uintptr_t)dv + sizeof(uint32_t));
1361 /* if caller requested, peek into the specified data elements */
1362 if (mode & TPL_DATAPEEK) {
1364 first_atom = strspn(fmt, "S()"); /* skip any leading S() */
1366 datapeek_flen = EM_SAFE_STRLEN(datapeek_f);
1367 if (strspn(datapeek_f, tpl_datapeek_ok_chars) < datapeek_flen) {
1368 tpl_hook.oops("invalid TPL_DATAPEEK format: %s\n", datapeek_f);
1369 tpl_hook.free(fmt_cpy); fmt_cpy = NULL; /* fail */
1373 if (strncmp( &fmt[first_atom], datapeek_f, datapeek_flen) != 0) {
1374 tpl_hook.oops("TPL_DATAPEEK format mismatches tpl iamge\n");
1375 tpl_hook.free(fmt_cpy); fmt_cpy = NULL; /* fail */
1379 /* advance to data start, then copy out requested elements */
1380 dv = (void*)((uintptr_t)dv + (num_fxlens * sizeof(uint32_t)));
1381 for(datapeek_c = datapeek_f; *datapeek_c != '\0'; datapeek_c++) {
1382 datapeek_p = va_arg(ap, void*);
1383 if (*datapeek_c == 's') { /* special handling for strings */
1384 if ((uintptr_t)dv-(uintptr_t)addr + sizeof(uint32_t) > sz) {
1385 tpl_hook.oops("tpl_peek: tpl has insufficient length\n");
1386 tpl_hook.free(fmt_cpy); fmt_cpy = NULL; /* fail */
1389 memcpy(&datapeek_ssz,dv,sizeof(uint32_t)); /* get slen */
1390 if (xendian) tpl_byteswap(&datapeek_ssz, sizeof(uint32_t));
1391 if (old_string_format) datapeek_ssz++;
1392 dv = (void*)((uintptr_t)dv + sizeof(uint32_t)); /* adv. to str */
1393 if (datapeek_ssz == 0) datapeek_s = NULL;
1395 if ((uintptr_t)dv-(uintptr_t)addr + datapeek_ssz-1 > sz) {
1396 tpl_hook.oops("tpl_peek: tpl has insufficient length\n");
1397 tpl_hook.free(fmt_cpy); fmt_cpy = NULL; /* fail */
1400 datapeek_s = tpl_hook.malloc(datapeek_ssz);
1401 if (datapeek_s == NULL) fatal_oom();
1402 memcpy(datapeek_s, dv, datapeek_ssz-1);
1403 datapeek_s[datapeek_ssz-1] = '\0';
1404 dv = (void*)((uintptr_t)dv + datapeek_ssz-1);
1406 *(char**)datapeek_p = datapeek_s;
1408 datapeek_csz = tpl_size_for(*datapeek_c);
1409 if ((uintptr_t)dv-(uintptr_t)addr + datapeek_csz > sz) {
1410 tpl_hook.oops("tpl_peek: tpl has insufficient length\n");
1411 tpl_hook.free(fmt_cpy); fmt_cpy = NULL; /* fail */
1414 memcpy(datapeek_p, dv, datapeek_csz);
1415 if (xendian) tpl_byteswap(datapeek_p, datapeek_csz);
1416 dv = (void*)((uintptr_t)dv + datapeek_csz);
1423 if ((mode & TPL_FILE) && mr.text != NULL) tpl_unmap_file( &mr );
1427 /* tpl_jot(TPL_FILE, "file.tpl", "si", &s, &i); */
1428 /* tpl_jot(TPL_MEM, &buf, &sz, "si", &s, &i); */
1429 /* tpl_jot(TPL_FD, fd, "si", &s, &i); */
1430 TPL_API int tpl_jot(int mode, ...) {
1432 char *filename, *fmt;
1439 if (mode & TPL_FILE) {
1440 filename = va_arg(ap,char*);
1441 fmt = va_arg(ap,char*);
1442 tn = tpl_map_va(fmt, ap);
1443 if (tn == NULL) { rc=-1; goto fail;}
1445 rc = tpl_dump(tn, TPL_FILE, filename);
1447 } else if (mode & TPL_MEM) {
1448 buf = va_arg(ap,void*);
1449 sz = va_arg(ap,size_t*);
1450 fmt = va_arg(ap,char*);
1451 tn = tpl_map_va(fmt,ap);
1452 if (tn == NULL) { rc=-1; goto fail;}
1454 rc = tpl_dump(tn, TPL_MEM, buf, sz);
1456 } else if (mode & TPL_FD) {
1457 fd = va_arg(ap,int);
1458 fmt = va_arg(ap,char*);
1459 tn = tpl_map_va(fmt,ap);
1460 if (tn == NULL) { rc=-1; goto fail;}
1462 rc = tpl_dump(tn, TPL_FD, fd);
1465 tpl_hook.fatal("invalid tpl_jot mode\n");
1473 TPL_API int tpl_load(tpl_node *r, int mode, ...) {
1476 char *filename=NULL;
1481 if (mode & TPL_FILE) filename = va_arg(ap,char *);
1482 else if (mode & TPL_MEM) {
1483 addr = va_arg(ap,void *);
1484 sz = va_arg(ap,size_t);
1485 } else if (mode & TPL_FD) {
1486 fd = va_arg(ap,int);
1488 tpl_hook.oops("unsupported tpl_load mode %d\n", mode);
1494 if (r->type != TPL_TYPE_ROOT) {
1495 tpl_hook.oops("error: tpl_load to non-root node\n");
1498 if (((tpl_root_data*)(r->data))->flags & (TPL_WRONLY|TPL_RDONLY)) {
1499 /* already packed or loaded, so reset it as if newly mapped */
1500 tpl_free_keep_map(r);
1502 if (mode & TPL_FILE) {
1503 if (tpl_mmap_file(filename, &((tpl_root_data*)(r->data))->mmap) != 0) {
1504 tpl_hook.oops("tpl_load failed for file %s\n", filename);
1507 if ( (rc = tpl_sanity(r, (mode & TPL_EXCESS_OK))) != 0) {
1508 if (rc == ERR_FMT_MISMATCH) {
1509 tpl_hook.oops("%s: format signature mismatch\n", filename);
1510 } else if (rc == ERR_FLEN_MISMATCH) {
1511 tpl_hook.oops("%s: array lengths mismatch\n", filename);
1513 tpl_hook.oops("%s: not a valid tpl file\n", filename);
1515 tpl_unmap_file( &((tpl_root_data*)(r->data))->mmap );
1518 ((tpl_root_data*)(r->data))->flags = (TPL_FILE | TPL_RDONLY);
1519 } else if (mode & TPL_MEM) {
1520 ((tpl_root_data*)(r->data))->mmap.text = addr;
1521 ((tpl_root_data*)(r->data))->mmap.text_sz = sz;
1522 if ( (rc = tpl_sanity(r, (mode & TPL_EXCESS_OK))) != 0) {
1523 if (rc == ERR_FMT_MISMATCH) {
1524 tpl_hook.oops("format signature mismatch\n");
1526 tpl_hook.oops("not a valid tpl file\n");
1530 ((tpl_root_data*)(r->data))->flags = (TPL_MEM | TPL_RDONLY);
1531 if (mode & TPL_UFREE) ((tpl_root_data*)(r->data))->flags |= TPL_UFREE;
1532 } else if (mode & TPL_FD) {
1533 /* if fd read succeeds, resulting mem img is used for load */
1534 if (tpl_gather(TPL_GATHER_BLOCKING,fd,&addr,&sz) > 0) {
1535 return tpl_load(r, TPL_MEM|TPL_UFREE, addr, sz);
1538 tpl_hook.oops("invalid tpl_load mode %d\n", mode);
1541 /* this applies to TPL_MEM or TPL_FILE */
1542 if (tpl_needs_endian_swap(((tpl_root_data*)(r->data))->mmap.text))
1543 ((tpl_root_data*)(r->data))->flags |= TPL_XENDIAN;
1544 tpl_unpackA0(r); /* prepare root A nodes for use */
1548 TPL_API int tpl_Alen(tpl_node *r, int i) {
1551 n = tpl_find_i(r,i);
1553 tpl_hook.oops("invalid index %d to tpl_unpack\n", i);
1556 if (n->type != TPL_TYPE_ARY) return -1;
1557 return ((tpl_atyp*)(n->data))->num;
1560 static void tpl_free_atyp(tpl_node *n, tpl_atyp *atyp) {
1561 tpl_backbone *bb,*bbnxt;
1579 case TPL_TYPE_DOUBLE:
1580 case TPL_TYPE_INT32:
1581 case TPL_TYPE_UINT32:
1582 case TPL_TYPE_INT64:
1583 case TPL_TYPE_UINT64:
1584 case TPL_TYPE_INT16:
1585 case TPL_TYPE_UINT16:
1586 dv = (void*)((uintptr_t)dv + tpl_types[c->type].sz*c->num);
1589 memcpy(&binp,dv,sizeof(tpl_bin*)); /* cp to aligned */
1590 if (binp->addr) tpl_hook.free( binp->addr ); /* free buf */
1591 tpl_hook.free(binp); /* free tpl_bin */
1592 dv = (void*)((uintptr_t)dv + sizeof(tpl_bin*));
1595 for(i=0; i < c->num; i++) {
1596 memcpy(&strp,dv,sizeof(char*)); /* cp to aligned */
1597 if (strp) tpl_hook.free(strp); /* free string */
1598 dv = (void*)((uintptr_t)dv + sizeof(char*));
1601 case TPL_TYPE_POUND:
1602 /* iterate over the preceding nodes */
1604 pd = (tpl_pound_data*)c->data;
1605 if (++(pd->iternum) < itermax) {
1606 c = pd->iter_start_node;
1608 } else { /* loop complete. */
1613 memcpy(&atypp,dv,sizeof(tpl_atyp*)); /* cp to aligned */
1614 tpl_free_atyp(c,atypp); /* free atyp */
1615 dv = (void*)((uintptr_t)dv + sizeof(void*));
1618 tpl_hook.fatal("unsupported format character\n");
1626 tpl_hook.free(atyp);
1629 /* determine (by walking) byte length of serialized r/A node at address dv
1630 * returns 0 on success, or -1 if the tpl isn't trustworthy (fails consistency)
1632 static int tpl_serlen(tpl_node *r, tpl_node *n, void *dv, size_t *serlen) {
1636 size_t len=0, alen, buf_past, itermax;
1639 buf_past = ((uintptr_t)((tpl_root_data*)(r->data))->mmap.text +
1640 ((tpl_root_data*)(r->data))->mmap.text_sz);
1642 if (n->type == TPL_TYPE_ROOT) num = 1;
1643 else if (n->type == TPL_TYPE_ARY) {
1644 if ((uintptr_t)dv + sizeof(uint32_t) > buf_past) return -1;
1645 memcpy(&num,dv,sizeof(uint32_t));
1646 if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN)
1647 tpl_byteswap(&num, sizeof(uint32_t));
1648 dv = (void*)((uintptr_t)dv + sizeof(uint32_t));
1649 len += sizeof(uint32_t);
1650 } else tpl_hook.fatal("internal error in tpl_serlen\n");
1657 case TPL_TYPE_DOUBLE:
1658 case TPL_TYPE_INT32:
1659 case TPL_TYPE_UINT32:
1660 case TPL_TYPE_INT64:
1661 case TPL_TYPE_UINT64:
1662 case TPL_TYPE_INT16:
1663 case TPL_TYPE_UINT16:
1664 for(fidx=0; fidx < c->num; fidx++) { /* octothorpe support */
1665 if ((uintptr_t)dv + tpl_types[c->type].sz > buf_past) return -1;
1666 dv = (void*)((uintptr_t)dv + tpl_types[c->type].sz);
1667 len += tpl_types[c->type].sz;
1671 len += sizeof(uint32_t);
1672 if ((uintptr_t)dv + sizeof(uint32_t) > buf_past) return -1;
1673 memcpy(&slen,dv,sizeof(uint32_t));
1674 if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN)
1675 tpl_byteswap(&slen, sizeof(uint32_t));
1677 dv = (void*)((uintptr_t)dv + sizeof(uint32_t));
1678 if ((uintptr_t)dv + slen > buf_past) return -1;
1679 dv = (void*)((uintptr_t)dv + slen);
1682 for(fidx=0; fidx < c->num; fidx++) { /* octothorpe support */
1683 len += sizeof(uint32_t);
1684 if ((uintptr_t)dv + sizeof(uint32_t) > buf_past) return -1;
1685 memcpy(&slen,dv,sizeof(uint32_t));
1686 if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN)
1687 tpl_byteswap(&slen, sizeof(uint32_t));
1688 if (!(((tpl_root_data*)(r->data))->flags & TPL_OLD_STRING_FMT))
1689 slen = (slen>1) ? (slen-1) : 0;
1691 dv = (void*)((uintptr_t)dv + sizeof(uint32_t));
1692 if ((uintptr_t)dv + slen > buf_past) return -1;
1693 dv = (void*)((uintptr_t)dv + slen);
1697 if ( tpl_serlen(r,c,dv, &alen) == -1) return -1;
1698 dv = (void*)((uintptr_t)dv + alen);
1701 case TPL_TYPE_POUND:
1702 /* iterate over the preceding nodes */
1704 pd = (tpl_pound_data*)c->data;
1705 if (++(pd->iternum) < itermax) {
1706 c = pd->iter_start_node;
1708 } else { /* loop complete. */
1713 tpl_hook.fatal("unsupported format character\n");
1723 static int tpl_mmap_output_file(char *filename, size_t sz, void **text_out) {
1728 perms = S_IRUSR|S_IWUSR|S_IWGRP|S_IRGRP|S_IROTH; /* ug+w o+r */
1729 fd=open(filename,O_CREAT|O_TRUNC|O_RDWR,perms);
1732 fd=_open(filename,_O_CREAT|_O_TRUNC|_O_RDWR,perms);
1736 tpl_hook.oops("Couldn't open file %s: %s\n", filename, strerror(errno));
1740 text = mmap(0, sz, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
1741 if (text == MAP_FAILED) {
1742 tpl_hook.oops("Failed to mmap %s: %s\n", filename, strerror(errno));
1746 if (ftruncate(fd,sz) == -1) {
1747 tpl_hook.oops("ftruncate failed: %s\n", strerror(errno));
1756 static int tpl_mmap_file(char *filename, tpl_mmap_rec *mr) {
1757 struct stat stat_buf;
1759 if ( (mr->fd = open(filename, O_RDONLY)) == -1 ) {
1760 tpl_hook.oops("Couldn't open file %s: %s\n", filename, strerror(errno));
1764 if ( fstat(mr->fd, &stat_buf) == -1) {
1766 tpl_hook.oops("Couldn't stat file %s: %s\n", filename, strerror(errno));
1770 mr->text_sz = (size_t)stat_buf.st_size;
1771 mr->text = mmap(0, stat_buf.st_size, PROT_READ, MAP_PRIVATE, mr->fd, 0);
1772 if (mr->text == MAP_FAILED) {
1774 tpl_hook.oops("Failed to mmap %s: %s\n", filename, strerror(errno));
1781 TPL_API int tpl_pack(tpl_node *r, int i) {
1782 tpl_node *n, *child, *np;
1791 n = tpl_find_i(r,i);
1793 tpl_hook.oops("invalid index %d to tpl_pack\n", i);
1797 if (((tpl_root_data*)(r->data))->flags & TPL_RDONLY) {
1798 /* convert to an writeable tpl, initially empty */
1799 tpl_free_keep_map(r);
1802 ((tpl_root_data*)(r->data))->flags |= TPL_WRONLY;
1804 if (n->type == TPL_TYPE_ARY) datav = tpl_extend_backbone(n);
1805 child = n->children;
1807 switch(child->type) {
1809 case TPL_TYPE_DOUBLE:
1810 case TPL_TYPE_INT32:
1811 case TPL_TYPE_UINT32:
1812 case TPL_TYPE_INT64:
1813 case TPL_TYPE_UINT64:
1814 case TPL_TYPE_INT16:
1815 case TPL_TYPE_UINT16:
1816 /* no need to use fidx iteration here; we can copy multiple values in one memcpy */
1817 memcpy(child->data,child->addr,tpl_types[child->type].sz * child->num);
1818 if (datav) datav = tpl_cpv(datav,child->data,tpl_types[child->type].sz * child->num);
1819 if (n->type == TPL_TYPE_ARY) n->ser_osz += tpl_types[child->type].sz * child->num;
1822 /* copy the buffer to be packed */
1823 slen = ((tpl_bin*)child->addr)->sz;
1825 str = tpl_hook.malloc(slen);
1826 if (!str) fatal_oom();
1827 memcpy(str,((tpl_bin*)child->addr)->addr,slen);
1829 /* and make a tpl_bin to point to it */
1830 bin = tpl_hook.malloc(sizeof(tpl_bin));
1831 if (!bin) fatal_oom();
1834 /* now pack its pointer, first deep freeing any pre-existing bin */
1835 if (*(tpl_bin**)(child->data) != NULL) {
1836 if ((*(tpl_bin**)(child->data))->sz != 0) {
1837 tpl_hook.free( (*(tpl_bin**)(child->data))->addr );
1839 tpl_hook.free(*(tpl_bin**)(child->data));
1841 memcpy(child->data,&bin,sizeof(tpl_bin*));
1843 datav = tpl_cpv(datav, &bin, sizeof(tpl_bin*));
1844 *(tpl_bin**)(child->data) = NULL;
1846 if (n->type == TPL_TYPE_ARY) {
1847 n->ser_osz += sizeof(uint32_t); /* binary buf len word */
1848 n->ser_osz += bin->sz; /* binary buf */
1852 for(fidx=0; fidx < child->num; fidx++) {
1853 /* copy the string to be packed. slen includes \0. this
1854 block also works if the string pointer is NULL. */
1855 char *caddr = ((char**)child->addr)[fidx];
1856 char **cdata = &((char**)child->data)[fidx];
1857 slen = caddr ? (EM_SAFE_STRLEN(caddr) + 1) : 0;
1859 str = tpl_hook.malloc(slen);
1860 if (!str) fatal_oom();
1861 memcpy(str,caddr,slen); /* include \0 */
1865 /* now pack its pointer, first freeing any pre-existing string */
1866 if (*cdata != NULL) {
1867 tpl_hook.free(*cdata);
1869 memcpy(cdata,&str,sizeof(char*));
1871 datav = tpl_cpv(datav, &str, sizeof(char*));
1874 if (n->type == TPL_TYPE_ARY) {
1875 n->ser_osz += sizeof(uint32_t); /* string len word */
1876 if (slen>1) n->ser_osz += slen-1;/* string (without nul) */
1881 /* copy the child's tpl_atype* and reset it to empty */
1883 sz = ((tpl_atyp*)(child->data))->sz;
1884 datav = tpl_cpv(datav, &child->data, sizeof(void*));
1885 child->data = tpl_hook.malloc(sizeof(tpl_atyp));
1886 if (!child->data) fatal_oom();
1887 ((tpl_atyp*)(child->data))->num = 0;
1888 ((tpl_atyp*)(child->data))->sz = sz;
1889 ((tpl_atyp*)(child->data))->bb = NULL;
1890 ((tpl_atyp*)(child->data))->bbtail = NULL;
1892 /* parent is array? then bubble up child array's ser_osz */
1893 if (n->type == TPL_TYPE_ARY) {
1894 n->ser_osz += sizeof(uint32_t); /* array len word */
1895 n->ser_osz += child->ser_osz; /* child array ser_osz */
1896 child->ser_osz = 0; /* reset child array ser_osz */
1900 case TPL_TYPE_POUND:
1901 /* we need to iterate n times over preceding nodes in S(...).
1902 * we may be in the midst of an iteration each time or starting. */
1903 pd = (tpl_pound_data*)child->data;
1904 itermax = child->num;
1906 /* itermax is total num of iterations needed */
1907 /* pd->iternum is current iteration index */
1908 /* pd->inter_elt_len is element-to-element len of contiguous structs */
1909 /* pd->iter_start_node is where we jump to at each iteration. */
1911 if (++(pd->iternum) < itermax) {
1913 /* in start or midst of loop. advance addr/data pointers. */
1914 for(np=pd->iter_start_node; np != child; np = np->next) {
1915 np->data = (char*)(np->data) +
1916 (tpl_types[np->type].sz * np->num);
1917 np->addr = (char*)(np->addr) + pd->inter_elt_len;
1919 /* do next iteration */
1920 child = pd->iter_start_node;
1923 } else { /* loop complete. */
1925 /* reset iteration index and addr/data pointers. */
1927 for(np=pd->iter_start_node; np != child; np = np->next) {
1928 np->data = (char*)(np->data) - ((itermax-1) *
1929 tpl_types[np->type].sz *
1931 np->addr = (char*)(np->addr) - ((itermax-1) * pd->inter_elt_len);
1937 tpl_hook.fatal("unsupported format character\n");
1945 TPL_API int tpl_unpack(tpl_node *r, int i) {
1946 tpl_node *n, *c, *np;
1950 void *dv=NULL, *caddr;
1951 size_t A_bytes, itermax;
1957 /* handle unusual case of tpl_pack,tpl_unpack without an
1958 * intervening tpl_dump. do a dump/load implicitly. */
1959 if (((tpl_root_data*)(r->data))->flags & TPL_WRONLY) {
1960 if (tpl_dump(r,TPL_MEM,&img,&sz) != 0) return -1;
1961 if (tpl_load(r,TPL_MEM|TPL_UFREE,img,sz) != 0) {
1967 n = tpl_find_i(r,i);
1969 tpl_hook.oops("invalid index %d to tpl_unpack\n", i);
1973 /* either root node or an A node */
1974 if (n->type == TPL_TYPE_ROOT) {
1975 dv = tpl_find_data_start( ((tpl_root_data*)(n->data))->mmap.text );
1976 } else if (n->type == TPL_TYPE_ARY) {
1977 if (((tpl_atyp*)(n->data))->num <= 0) return 0; /* array consumed */
1978 else rc = ((tpl_atyp*)(n->data))->num--;
1979 dv = ((tpl_atyp*)(n->data))->cur;
1980 if (!dv) tpl_hook.fatal("must unpack parent of node before node itself\n");
1987 case TPL_TYPE_DOUBLE:
1988 case TPL_TYPE_INT32:
1989 case TPL_TYPE_UINT32:
1990 case TPL_TYPE_INT64:
1991 case TPL_TYPE_UINT64:
1992 case TPL_TYPE_INT16:
1993 case TPL_TYPE_UINT16:
1994 /* unpack elements of cross-endian octothorpic array individually */
1995 if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN) {
1996 for(fidx=0; fidx < c->num; fidx++) {
1997 caddr = (void*)((uintptr_t)c->addr + (fidx * tpl_types[c->type].sz));
1998 memcpy(caddr,dv,tpl_types[c->type].sz);
1999 tpl_byteswap(caddr, tpl_types[c->type].sz);
2000 dv = (void*)((uintptr_t)dv + tpl_types[c->type].sz);
2003 /* bulk unpack ok if not cross-endian */
2004 memcpy(c->addr, dv, tpl_types[c->type].sz * c->num);
2005 dv = (void*)((uintptr_t)dv + tpl_types[c->type].sz * c->num);
2009 memcpy(&slen,dv,sizeof(uint32_t));
2010 if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN)
2011 tpl_byteswap(&slen, sizeof(uint32_t));
2013 str = (char*)tpl_hook.malloc(slen);
2014 if (!str) fatal_oom();
2016 dv = (void*)((uintptr_t)dv + sizeof(uint32_t));
2017 if (slen>0) memcpy(str,dv,slen);
2018 memcpy(&(((tpl_bin*)c->addr)->addr),&str,sizeof(void*));
2019 memcpy(&(((tpl_bin*)c->addr)->sz),&slen,sizeof(uint32_t));
2020 dv = (void*)((uintptr_t)dv + slen);
2023 for(fidx=0; fidx < c->num; fidx++) {
2024 memcpy(&slen,dv,sizeof(uint32_t));
2025 if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN)
2026 tpl_byteswap(&slen, sizeof(uint32_t));
2027 if (((tpl_root_data*)(r->data))->flags & TPL_OLD_STRING_FMT)
2029 dv = (void*)((uintptr_t)dv + sizeof(uint32_t));
2030 if (slen) { /* slen includes \0 */
2031 str = (char*)tpl_hook.malloc(slen);
2032 if (!str) fatal_oom();
2033 if (slen>1) memcpy(str,dv,slen-1);
2034 str[slen-1] = '\0'; /* nul terminate */
2035 dv = (void*)((uintptr_t)dv + slen-1);
2037 memcpy(&((char**)c->addr)[fidx],&str,sizeof(char*));
2040 case TPL_TYPE_POUND:
2041 /* iterate over preceding nodes */
2042 pd = (tpl_pound_data*)c->data;
2044 if (++(pd->iternum) < itermax) {
2045 /* in start or midst of loop. advance addr/data pointers. */
2046 for(np=pd->iter_start_node; np != c; np = np->next) {
2047 np->addr = (char*)(np->addr) + pd->inter_elt_len;
2049 /* do next iteration */
2050 c = pd->iter_start_node;
2053 } else { /* loop complete. */
2055 /* reset iteration index and addr/data pointers. */
2057 for(np=pd->iter_start_node; np != c; np = np->next) {
2058 np->addr = (char*)(np->addr) - ((itermax-1) * pd->inter_elt_len);
2064 if (tpl_serlen(r,c,dv, &A_bytes) == -1)
2065 tpl_hook.fatal("internal error in unpack\n");
2066 memcpy( &((tpl_atyp*)(c->data))->num, dv, sizeof(uint32_t));
2067 if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN)
2068 tpl_byteswap(&((tpl_atyp*)(c->data))->num, sizeof(uint32_t));
2069 ((tpl_atyp*)(c->data))->cur = (void*)((uintptr_t)dv+sizeof(uint32_t));
2070 dv = (void*)((uintptr_t)dv + A_bytes);
2073 tpl_hook.fatal("unsupported format character\n");
2079 if (n->type == TPL_TYPE_ARY) ((tpl_atyp*)(n->data))->cur = dv; /* next element */
2083 /* Specialized function that unpacks only the root's A nodes, after tpl_load */
2084 static int tpl_unpackA0(tpl_node *r) {
2089 size_t A_bytes, itermax;
2093 dv = tpl_find_data_start( ((tpl_root_data*)(r->data))->mmap.text);
2099 case TPL_TYPE_DOUBLE:
2100 case TPL_TYPE_INT32:
2101 case TPL_TYPE_UINT32:
2102 case TPL_TYPE_INT64:
2103 case TPL_TYPE_UINT64:
2104 case TPL_TYPE_INT16:
2105 case TPL_TYPE_UINT16:
2106 for(fidx=0;fidx < c->num; fidx++) {
2107 dv = (void*)((uintptr_t)dv + tpl_types[c->type].sz);
2111 memcpy(&slen,dv,sizeof(uint32_t));
2112 if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN)
2113 tpl_byteswap(&slen, sizeof(uint32_t));
2114 dv = (void*)((uintptr_t)dv + sizeof(uint32_t));
2115 dv = (void*)((uintptr_t)dv + slen);
2118 for(i=0; i<c->num; i++) {
2119 memcpy(&slen,dv,sizeof(uint32_t));
2120 if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN)
2121 tpl_byteswap(&slen, sizeof(uint32_t));
2122 if (((tpl_root_data*)(r->data))->flags & TPL_OLD_STRING_FMT)
2124 dv = (void*)((uintptr_t)dv + sizeof(uint32_t));
2125 if (slen>1) dv = (void*)((uintptr_t)dv + slen-1);
2128 case TPL_TYPE_POUND:
2129 /* iterate over the preceding nodes */
2131 pd = (tpl_pound_data*)c->data;
2132 if (++(pd->iternum) < itermax) {
2133 c = pd->iter_start_node;
2135 } else { /* loop complete. */
2140 if ( tpl_serlen(r,c,dv, &A_bytes) == -1)
2141 tpl_hook.fatal("internal error in unpackA0\n");
2142 memcpy( &((tpl_atyp*)(c->data))->num, dv, sizeof(uint32_t));
2143 if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN)
2144 tpl_byteswap(&((tpl_atyp*)(c->data))->num, sizeof(uint32_t));
2145 ((tpl_atyp*)(c->data))->cur = (void*)((uintptr_t)dv+sizeof(uint32_t));
2146 dv = (void*)((uintptr_t)dv + A_bytes);
2149 tpl_hook.fatal("unsupported format character\n");
2157 /* In-place byte order swapping of a word of length "len" bytes */
2158 static void tpl_byteswap(void *word, int len) {
2162 for(i=0; i<len/2; i++) {
2169 static void tpl_fatal(char *fmt, ...) {
2174 vsnprintf(exit_msg, 100, fmt, ap);
2177 tpl_hook.oops("%s", exit_msg);
2181 TPL_API int tpl_gather(int mode, ...) {
2185 void **img,*addr,*data;
2191 case TPL_GATHER_BLOCKING:
2192 fd = va_arg(ap,int);
2193 img = va_arg(ap,void*);
2194 szp = va_arg(ap,size_t*);
2195 rc = tpl_gather_blocking(fd,img,szp);
2197 case TPL_GATHER_NONBLOCKING:
2198 fd = va_arg(ap,int);
2199 gs = (tpl_gather_t**)va_arg(ap,void*);
2200 cb = (tpl_gather_cb*)va_arg(ap,tpl_gather_cb*);
2201 data = va_arg(ap,void*);
2202 rc = tpl_gather_nonblocking(fd,gs,cb,data);
2204 case TPL_GATHER_MEM:
2205 addr = va_arg(ap,void*);
2206 sz = va_arg(ap,size_t);
2207 gs = (tpl_gather_t**)va_arg(ap,void*);
2208 cb = (tpl_gather_cb*)va_arg(ap,tpl_gather_cb*);
2209 data = va_arg(ap,void*);
2210 rc = tpl_gather_mem(addr,sz,gs,cb,data);
2213 tpl_hook.fatal("unsupported tpl_gather mode %d\n",mode);
2220 /* dequeue a tpl by reading until one full tpl image is obtained.
2221 * We take care not to read past the end of the tpl.
2222 * This is intended as a blocking call i.e. for use with a blocking fd.
2223 * It can be given a non-blocking fd, but the read spins if we have to wait.
2225 static int tpl_gather_blocking(int fd, void **img, size_t *sz) {
2231 rc = read(fd,&preamble[i],8-i);
2232 i += (rc>0) ? rc : 0;
2233 } while ((rc==-1 && (errno==EINTR||errno==EAGAIN)) || (rc>0 && i<8));
2236 tpl_hook.oops("tpl_gather_fd_blocking failed: %s\n", strerror(errno));
2238 } else if (rc == 0) {
2239 /* tpl_hook.oops("tpl_gather_fd_blocking: eof\n"); */
2241 } else if (i != 8) {
2242 tpl_hook.oops("internal error\n");
2246 if (preamble[0] == 't' && preamble[1] == 'p' && preamble[2] == 'l') {
2247 memcpy(&tpllen,&preamble[4],4);
2248 if (tpl_needs_endian_swap(preamble)) tpl_byteswap(&tpllen,4);
2250 tpl_hook.oops("tpl_gather_fd_blocking: non-tpl input\n");
2254 /* malloc space for remainder of tpl image (overall length tpllen)
2257 if (tpl_hook.gather_max > 0 &&
2258 tpllen > tpl_hook.gather_max) {
2259 tpl_hook.oops("tpl exceeds max length %d\n",
2260 tpl_hook.gather_max);
2264 if ( (*img = tpl_hook.malloc(tpllen)) == NULL) {
2268 memcpy(*img,preamble,8); /* copy preamble to output buffer */
2271 rc = read(fd,&((*(char**)img)[i]),tpllen-i);
2272 i += (rc>0) ? rc : 0;
2273 } while ((rc==-1 && (errno==EINTR||errno==EAGAIN)) || (rc>0 && i<tpllen));
2276 tpl_hook.oops("tpl_gather_fd_blocking failed: %s\n", strerror(errno));
2277 tpl_hook.free(*img);
2279 } else if (rc == 0) {
2280 /* tpl_hook.oops("tpl_gather_fd_blocking: eof\n"); */
2281 tpl_hook.free(*img);
2283 } else if (i != tpllen) {
2284 tpl_hook.oops("internal error\n");
2285 tpl_hook.free(*img);
2292 /* Used by select()-driven apps which want to gather tpl images piecemeal */
2293 /* the file descriptor must be non-blocking for this functino to work. */
2294 static int tpl_gather_nonblocking( int fd, tpl_gather_t **gs, tpl_gather_cb *cb, void *data) {
2295 char buf[TPL_GATHER_BUFLEN], *img, *tpl;
2296 int rc, keep_looping, cbrc=0;
2301 rc = read(fd,buf,TPL_GATHER_BUFLEN);
2303 if (errno == EINTR) continue; /* got signal during read, ignore */
2304 if (errno == EAGAIN) return 1; /* nothing to read right now */
2306 tpl_hook.oops("tpl_gather failed: %s\n", strerror(errno));
2308 tpl_hook.free((*gs)->img);
2312 return -1; /* error, caller should close fd */
2314 } else if (rc == 0) {
2316 tpl_hook.oops("tpl_gather: partial tpl image precedes EOF\n");
2317 tpl_hook.free((*gs)->img);
2321 return 0; /* EOF, caller should close fd */
2323 /* concatenate any partial tpl from last read with new buffer */
2325 catlen = (*gs)->len + rc;
2326 if (tpl_hook.gather_max > 0 &&
2327 catlen > tpl_hook.gather_max) {
2328 tpl_hook.free( (*gs)->img );
2329 tpl_hook.free( (*gs) );
2331 tpl_hook.oops("tpl exceeds max length %d\n",
2332 tpl_hook.gather_max);
2333 return -2; /* error, caller should close fd */
2335 if ( (img = tpl_hook.realloc((*gs)->img, catlen)) == NULL) {
2338 memcpy(img + (*gs)->len, buf, rc);
2345 /* isolate any full tpl(s) in img and invoke cb for each */
2347 keep_looping = (tpl+8 < img+catlen) ? 1 : 0;
2348 while (keep_looping) {
2349 if (strncmp("tpl", tpl, 3) != 0) {
2350 tpl_hook.oops("tpl prefix invalid\n");
2351 if (img != buf) tpl_hook.free(img);
2354 return -3; /* error, caller should close fd */
2356 memcpy(&tpllen,&tpl[4],4);
2357 if (tpl_needs_endian_swap(tpl)) tpl_byteswap(&tpllen,4);
2358 if (tpl+tpllen <= img+catlen) {
2359 cbrc = (cb)(tpl,tpllen,data); /* invoke cb for tpl image */
2360 tpl += tpllen; /* point to next tpl image */
2361 if (cbrc < 0) keep_looping = 0;
2362 else keep_looping = (tpl+8 < img+catlen) ? 1 : 0;
2363 } else keep_looping=0;
2365 /* check if app callback requested closure of tpl source */
2367 tpl_hook.oops("tpl_fd_gather aborted by app callback\n");
2368 if (img != buf) tpl_hook.free(img);
2369 if (*gs) tpl_hook.free(*gs);
2373 /* store any leftover, partial tpl fragment for next read */
2374 if (tpl == img && img != buf) {
2375 /* consumed nothing from img!=buf */
2376 if ( (*gs = tpl_hook.malloc(sizeof(tpl_gather_t))) == NULL ) {
2380 (*gs)->len = catlen;
2381 } else if (tpl < img+catlen) {
2382 /* consumed 1+ tpl(s) from img!=buf or 0 from img==buf */
2383 if ( (*gs = tpl_hook.malloc(sizeof(tpl_gather_t))) == NULL ) {
2386 if ( ((*gs)->img = tpl_hook.malloc(img+catlen - tpl)) == NULL ) {
2389 (*gs)->len = img+catlen - tpl;
2390 memcpy( (*gs)->img, tpl, img+catlen - tpl);
2391 /* free partially consumed concat buffer if used */
2392 if (img != buf) tpl_hook.free(img);
2393 } else { /* tpl(s) fully consumed */
2394 /* free consumed concat buffer if used */
2395 if (img != buf) tpl_hook.free(img);
2401 /* gather tpl piecemeal from memory buffer (not fd) e.g., from a lower-level api */
2402 static int tpl_gather_mem( char *buf, size_t len, tpl_gather_t **gs, tpl_gather_cb *cb, void *data) {
2404 int keep_looping, cbrc=0;
2408 /* concatenate any partial tpl from last read with new buffer */
2410 catlen = (*gs)->len + len;
2411 if (tpl_hook.gather_max > 0 &&
2412 catlen > tpl_hook.gather_max) {
2413 tpl_hook.free( (*gs)->img );
2414 tpl_hook.free( (*gs) );
2416 tpl_hook.oops("tpl exceeds max length %d\n",
2417 tpl_hook.gather_max);
2418 return -2; /* error, caller should stop accepting input from source*/
2420 if ( (img = tpl_hook.realloc((*gs)->img, catlen)) == NULL) {
2423 memcpy(img + (*gs)->len, buf, len);
2430 /* isolate any full tpl(s) in img and invoke cb for each */
2432 keep_looping = (tpl+8 < img+catlen) ? 1 : 0;
2433 while (keep_looping) {
2434 if (strncmp("tpl", tpl, 3) != 0) {
2435 tpl_hook.oops("tpl prefix invalid\n");
2436 if (img != buf) tpl_hook.free(img);
2439 return -3; /* error, caller should stop accepting input from source*/
2441 memcpy(&tpllen,&tpl[4],4);
2442 if (tpl_needs_endian_swap(tpl)) tpl_byteswap(&tpllen,4);
2443 if (tpl+tpllen <= img+catlen) {
2444 cbrc = (cb)(tpl,tpllen,data); /* invoke cb for tpl image */
2445 tpl += tpllen; /* point to next tpl image */
2446 if (cbrc < 0) keep_looping = 0;
2447 else keep_looping = (tpl+8 < img+catlen) ? 1 : 0;
2448 } else keep_looping=0;
2450 /* check if app callback requested closure of tpl source */
2452 tpl_hook.oops("tpl_mem_gather aborted by app callback\n");
2453 if (img != buf) tpl_hook.free(img);
2454 if (*gs) tpl_hook.free(*gs);
2458 /* store any leftover, partial tpl fragment for next read */
2459 if (tpl == img && img != buf) {
2460 /* consumed nothing from img!=buf */
2461 if ( (*gs = tpl_hook.malloc(sizeof(tpl_gather_t))) == NULL ) {
2465 (*gs)->len = catlen;
2466 } else if (tpl < img+catlen) {
2467 /* consumed 1+ tpl(s) from img!=buf or 0 from img==buf */
2468 if ( (*gs = tpl_hook.malloc(sizeof(tpl_gather_t))) == NULL ) {
2471 if ( ((*gs)->img = tpl_hook.malloc(img+catlen - tpl)) == NULL ) {
2474 (*gs)->len = img+catlen - tpl;
2475 memcpy( (*gs)->img, tpl, img+catlen - tpl);
2476 /* free partially consumed concat buffer if used */
2477 if (img != buf) tpl_hook.free(img);
2478 } else { /* tpl(s) fully consumed */
2479 /* free consumed concat buffer if used */
2480 if (img != buf) tpl_hook.free(img);