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 offset = ((uintptr_t)prev->addr - (uintptr_t)struct_addr)
310 + (tpl_types[prev->type].sz * prev->num);
311 offset = (offset + align_sz - 1) / align_sz * align_sz;
312 return struct_addr + offset;
315 TPL_API tpl_node *tpl_map(char *fmt,...) {
320 tn = tpl_map_va(fmt, ap);
325 static tpl_node *tpl_map_va(char *fmt, va_list ap) {
326 int lparen_level=0,expect_lparen=0,t=0,in_structure=0,ordinal=0;
327 int in_nested_structure=0;
328 char *c, *peek, *struct_addr=NULL, *struct_next;
329 tpl_node *root,*parent,*n=NULL,*preceding,*iter_start_node=NULL,
330 *struct_widest_node=NULL, *np; tpl_pidx *pidx;
332 int *fxlens, num_fxlens, pound_num, pound_prod, applies_to_struct;
333 int contig_fxlens[10]; /* temp space for contiguous fxlens */
334 int num_contig_fxlens, i, j;
335 ptrdiff_t inter_elt_len=0; /* padded element length of contiguous structs in array */
338 root = tpl_node_new(NULL);
339 root->type = TPL_TYPE_ROOT;
340 root->data = (tpl_root_data*)tpl_hook.malloc(sizeof(tpl_root_data));
341 if (!root->data) fatal_oom();
342 memset((tpl_root_data*)root->data,0,sizeof(tpl_root_data));
344 /* set up root nodes special ser_osz to reflect overhead of preamble */
345 root->ser_osz = sizeof(uint32_t); /* tpl leading length */
346 root->ser_osz += EM_SAFE_STRLEN(fmt) + 1; /* fmt + NUL-terminator */
347 root->ser_osz += 4; /* 'tpl' magic prefix + flags byte */
362 if (*c=='c') t=TPL_TYPE_BYTE;
363 else if (*c=='i') t=TPL_TYPE_INT32;
364 else if (*c=='u') t=TPL_TYPE_UINT32;
365 else if (*c=='j') t=TPL_TYPE_INT16;
366 else if (*c=='v') t=TPL_TYPE_UINT16;
367 else if (*c=='I') t=TPL_TYPE_INT64;
368 else if (*c=='U') t=TPL_TYPE_UINT64;
369 else if (*c=='f') t=TPL_TYPE_DOUBLE;
371 if (expect_lparen) goto fail;
372 n = tpl_node_new(parent);
376 /* for S(...)# iteration. Apply any changes to case 's' too!!! */
378 struct_widest_node = n;
380 if (tpl_types[n->type].sz > tpl_types[struct_widest_node->type].sz) {
381 struct_widest_node = n;
383 n->addr = calc_field_addr(parent,n->type,struct_addr,ordinal++);
384 } else n->addr = (void*)va_arg(ap,void*);
385 n->data = tpl_hook.malloc(tpl_types[t].sz);
386 if (!n->data) fatal_oom();
387 if (n->parent->type == TPL_TYPE_ARY)
388 ((tpl_atyp*)(n->parent->data))->sz += tpl_types[t].sz;
389 DL_ADD(parent->children,n);
392 if (expect_lparen) goto fail;
393 n = tpl_node_new(parent);
394 n->type = TPL_TYPE_STR;
397 iter_start_node = n; /* for S(...)# iteration */
398 struct_widest_node = n;
400 if (tpl_types[n->type].sz > tpl_types[struct_widest_node->type].sz) {
401 struct_widest_node = n;
403 n->addr = calc_field_addr(parent,n->type,struct_addr,ordinal++);
404 } else n->addr = (void*)va_arg(ap,void*);
405 n->data = tpl_hook.malloc(sizeof(char*));
406 if (!n->data) fatal_oom();
407 *(char**)(n->data) = NULL;
408 if (n->parent->type == TPL_TYPE_ARY)
409 ((tpl_atyp*)(n->parent->data))->sz += sizeof(void*);
410 DL_ADD(parent->children,n);
413 /* apply a 'num' to preceding atom */
414 if (!parent->children) goto fail;
415 preceding = parent->children->prev; /* first child's prev is 'last child'*/
417 applies_to_struct = (*(c-1) == ')') ? 1 : 0;
418 if (!applies_to_struct) {
419 if (!(t == TPL_TYPE_BYTE || t == TPL_TYPE_INT32 ||
420 t == TPL_TYPE_UINT32 || t == TPL_TYPE_DOUBLE ||
421 t == TPL_TYPE_UINT64 || t == TPL_TYPE_INT64 ||
422 t == TPL_TYPE_UINT16 || t == TPL_TYPE_INT16 ||
423 t == TPL_TYPE_STR )) goto fail;
425 /* count up how many contiguous # and form their product */
428 for(peek=c; *peek == '#'; peek++) {
429 pound_num = va_arg(ap, int);
431 tpl_hook.fatal("non-positive iteration count %d\n", pound_num);
433 if (num_contig_fxlens >= (sizeof(contig_fxlens)/sizeof(contig_fxlens[0]))) {
434 tpl_hook.fatal("contiguous # exceeds hardcoded limit\n");
436 contig_fxlens[num_contig_fxlens++] = pound_num;
437 pound_prod *= pound_num;
439 /* increment c to skip contiguous # so its points to last one */
441 /* differentiate atom-# from struct-# by noting preceding rparen */
442 if (applies_to_struct) { /* insert # node to induce looping */
443 n = tpl_node_new(parent);
444 n->type = TPL_TYPE_POUND;
446 n->data = tpl_hook.malloc(sizeof(tpl_pound_data));
447 if (!n->data) fatal_oom();
448 pd = (tpl_pound_data*)n->data;
449 pd->inter_elt_len = inter_elt_len;
450 pd->iter_start_node = iter_start_node;
452 DL_ADD(parent->children,n);
453 /* multiply the 'num' and data space on each atom in the structure */
454 for(np = iter_start_node; np != n; np = np->next) {
455 if (n->parent->type == TPL_TYPE_ARY) {
456 ((tpl_atyp*)(n->parent->data))->sz +=
457 tpl_types[np->type].sz * (np->num * (n->num - 1));
459 np->data = tpl_hook.realloc(np->data, tpl_types[np->type].sz *
461 if (!np->data) fatal_oom();
462 memset(np->data, 0, tpl_types[np->type].sz * np->num * n->num);
464 } else { /* simple atom-# form does not require a loop */
465 preceding->num = pound_prod;
466 preceding->data = tpl_hook.realloc(preceding->data,
467 tpl_types[t].sz * preceding->num);
468 if (!preceding->data) fatal_oom();
469 memset(preceding->data,0,tpl_types[t].sz * preceding->num);
470 if (n->parent->type == TPL_TYPE_ARY) {
471 ((tpl_atyp*)(n->parent->data))->sz += tpl_types[t].sz *
475 root->ser_osz += (sizeof(uint32_t) * num_contig_fxlens);
477 j = ((tpl_root_data*)root->data)->num_fxlens; /* before incrementing */
478 (((tpl_root_data*)root->data)->num_fxlens) += num_contig_fxlens;
479 num_fxlens = ((tpl_root_data*)root->data)->num_fxlens; /* new value */
480 fxlens = ((tpl_root_data*)root->data)->fxlens;
481 fxlens = tpl_hook.realloc(fxlens, sizeof(int) * num_fxlens);
482 if (!fxlens) fatal_oom();
483 ((tpl_root_data*)root->data)->fxlens = fxlens;
484 for(i=0; i < num_contig_fxlens; i++) fxlens[j++] = contig_fxlens[i];
488 if (expect_lparen) goto fail;
489 if (in_structure) goto fail;
490 n = tpl_node_new(parent);
491 n->type = TPL_TYPE_BIN;
492 n->addr = (tpl_bin*)va_arg(ap,void*);
493 n->data = tpl_hook.malloc(sizeof(tpl_bin*));
494 if (!n->data) fatal_oom();
495 *((tpl_bin**)n->data) = NULL;
496 if (n->parent->type == TPL_TYPE_ARY)
497 ((tpl_atyp*)(n->parent->data))->sz += sizeof(tpl_bin);
498 DL_ADD(parent->children,n);
501 if (in_structure) goto fail;
502 n = tpl_node_new(parent);
503 n->type = TPL_TYPE_ARY;
504 DL_ADD(parent->children,n);
507 pidx = (tpl_pidx*)tpl_hook.malloc(sizeof(tpl_pidx));
508 if (!pidx) fatal_oom();
511 DL_ADD(((tpl_root_data*)(root->data))->pidx,pidx);
512 /* set up the A's tpl_atyp */
513 n->data = (tpl_atyp*)tpl_hook.malloc(sizeof(tpl_atyp));
514 if (!n->data) fatal_oom();
515 ((tpl_atyp*)(n->data))->num = 0;
516 ((tpl_atyp*)(n->data))->sz = 0;
517 ((tpl_atyp*)(n->data))->bb = NULL;
518 ((tpl_atyp*)(n->data))->bbtail = NULL;
519 ((tpl_atyp*)(n->data))->cur = NULL;
520 if (n->parent->type == TPL_TYPE_ARY)
521 ((tpl_atyp*)(n->parent->data))->sz += sizeof(void*);
524 if (in_structure) goto fail;
526 ordinal=1; /* index upcoming atoms in S(..) */
527 in_structure=1+lparen_level; /* so we can tell where S fmt ends */
528 struct_addr = (char*)va_arg(ap,void*);
530 case '$': /* nested structure */
531 if (!in_structure) goto fail;
533 in_nested_structure++;
537 if (lparen_level < 0) goto fail;
538 if (*(c-1) == '(') goto fail;
539 if (in_nested_structure) in_nested_structure--;
540 else if (in_structure && (in_structure-1 == lparen_level)) {
541 /* calculate delta between contiguous structures in array */
542 struct_next = calc_field_addr(parent, struct_widest_node->type,
543 struct_addr, ordinal++);
544 inter_elt_len = struct_next - struct_addr;
547 else parent = parent->parent; /* rparen ends A() type, not S() type */
550 if (!expect_lparen) goto fail;
555 tpl_hook.oops("unsupported option %c\n", *c);
560 if (lparen_level != 0) goto fail;
562 /* copy the format string, save for convenience */
563 ((tpl_root_data*)(root->data))->fmt = tpl_hook.malloc(EM_SAFE_STRLEN(fmt)+1);
564 if (((tpl_root_data*)(root->data))->fmt == NULL)
566 memcpy(((tpl_root_data*)(root->data))->fmt,fmt,EM_SAFE_STRLEN(fmt)+1);
571 tpl_hook.oops("failed to parse %s\n", fmt);
576 static int tpl_unmap_file( tpl_mmap_rec *mr) {
578 if ( munmap( mr->text, mr->text_sz ) == -1 ) {
579 tpl_hook.oops("Failed to munmap: %s\n", strerror(errno));
587 static void tpl_free_keep_map(tpl_node *r) {
588 int mmap_bits = (TPL_RDONLY|TPL_FILE);
589 int ufree_bits = (TPL_MEM|TPL_UFREE);
591 int find_next_node=0,looking,i;
594 /* For mmap'd files, or for 'ufree' memory images , do appropriate release */
595 if ((((tpl_root_data*)(r->data))->flags & mmap_bits) == mmap_bits) {
596 tpl_unmap_file( &((tpl_root_data*)(r->data))->mmap);
597 } else if ((((tpl_root_data*)(r->data))->flags & ufree_bits) == ufree_bits) {
598 tpl_hook.free( ((tpl_root_data*)(r->data))->mmap.text );
603 while(c->type != TPL_TYPE_ROOT) { /* loop until we come back to root node */
606 /* free any binary buffer hanging from tpl_bin */
607 if ( *((tpl_bin**)(c->data)) ) {
608 if ( (*((tpl_bin**)(c->data)))->addr ) {
609 tpl_hook.free( (*((tpl_bin**)(c->data)))->addr );
611 *((tpl_bin**)c->data) = NULL; /* reset tpl_bin */
616 /* free any packed (copied) string */
617 for(i=0; i < c->num; i++) {
618 char *str = ((char**)c->data)[i];
621 ((char**)c->data)[i] = NULL;
627 case TPL_TYPE_UINT32:
629 case TPL_TYPE_UINT64:
631 case TPL_TYPE_DOUBLE:
633 case TPL_TYPE_UINT16:
638 c->ser_osz = 0; /* zero out the serialization output size */
640 sz = ((tpl_atyp*)(c->data))->sz; /* save sz to use below */
641 tpl_free_atyp(c,c->data);
644 c->data = (tpl_atyp*)tpl_hook.malloc(sizeof(tpl_atyp));
645 if (!c->data) fatal_oom();
646 ((tpl_atyp*)(c->data))->num = 0;
647 ((tpl_atyp*)(c->data))->sz = sz; /* restore bb datum sz */
648 ((tpl_atyp*)(c->data))->bb = NULL;
649 ((tpl_atyp*)(c->data))->bbtail = NULL;
650 ((tpl_atyp*)(c->data))->cur = NULL;
655 tpl_hook.fatal("unsupported format character\n");
659 if (find_next_node) {
668 if (c->type == TPL_TYPE_ROOT) break; /* root node */
679 ((tpl_root_data*)(r->data))->flags = 0; /* reset flags */
682 TPL_API void tpl_free(tpl_node *r) {
683 int mmap_bits = (TPL_RDONLY|TPL_FILE);
684 int ufree_bits = (TPL_MEM|TPL_UFREE);
686 int find_next_node=0,looking,i;
687 tpl_pidx *pidx,*pidx_nxt;
689 /* For mmap'd files, or for 'ufree' memory images , do appropriate release */
690 if ((((tpl_root_data*)(r->data))->flags & mmap_bits) == mmap_bits) {
691 tpl_unmap_file( &((tpl_root_data*)(r->data))->mmap);
692 } else if ((((tpl_root_data*)(r->data))->flags & ufree_bits) == ufree_bits) {
693 tpl_hook.free( ((tpl_root_data*)(r->data))->mmap.text );
698 while(c->type != TPL_TYPE_ROOT) { /* loop until we come back to root node */
701 /* free any binary buffer hanging from tpl_bin */
702 if ( *((tpl_bin**)(c->data)) ) {
703 if ( (*((tpl_bin**)(c->data)))->sz != 0 ) {
704 tpl_hook.free( (*((tpl_bin**)(c->data)))->addr );
706 tpl_hook.free(*((tpl_bin**)c->data)); /* free tpl_bin */
708 tpl_hook.free(c->data); /* free tpl_bin* */
712 /* free any packed (copied) string */
713 for(i=0; i < c->num; i++) {
714 char *str = ((char**)c->data)[i];
717 ((char**)c->data)[i] = NULL;
720 tpl_hook.free(c->data);
724 case TPL_TYPE_UINT32:
726 case TPL_TYPE_UINT64:
728 case TPL_TYPE_DOUBLE:
730 case TPL_TYPE_UINT16:
732 tpl_hook.free(c->data);
736 tpl_free_atyp(c,c->data);
737 if (c->children) c = c->children; /* normal case */
738 else find_next_node=1; /* edge case, handle bad format A() */
741 tpl_hook.fatal("unsupported format character\n");
745 if (find_next_node) {
755 if (c->type == TPL_TYPE_ROOT) break; /* root node */
768 for(pidx=((tpl_root_data*)(r->data))->pidx; pidx; pidx=pidx_nxt) {
769 pidx_nxt = pidx->next;
772 tpl_hook.free(((tpl_root_data*)(r->data))->fmt);
773 if (((tpl_root_data*)(r->data))->num_fxlens > 0) {
774 tpl_hook.free(((tpl_root_data*)(r->data))->fxlens);
776 tpl_hook.free(r->data); /* tpl_root_data */
781 /* Find the i'th packable ('A' node) */
782 static tpl_node *tpl_find_i(tpl_node *n, int i) {
785 if (n->type != TPL_TYPE_ROOT) return NULL;
786 if (i == 0) return n; /* packable 0 is root */
787 for(pidx=((tpl_root_data*)(n->data))->pidx; pidx; pidx=pidx->next) {
788 if (++j == i) return pidx->node;
793 static void *tpl_cpv(void *datav, void *data, size_t sz) {
794 if (sz>0) memcpy(datav,data,sz);
795 return (void*)((uintptr_t)datav + sz);
798 static void *tpl_extend_backbone(tpl_node *n) {
800 bb = (tpl_backbone*)tpl_hook.malloc(sizeof(tpl_backbone) +
801 ((tpl_atyp*)(n->data))->sz ); /* datum hangs on coattails of bb */
802 if (!bb) fatal_oom();
803 #if __STDC_VERSION__ < 199901
804 bb->data = (char*)((uintptr_t)bb + sizeof(tpl_backbone));
806 memset(bb->data,0,((tpl_atyp*)(n->data))->sz);
808 /* Add the new backbone to the tail, also setting head if necessary */
809 if (((tpl_atyp*)(n->data))->bb == NULL) {
810 ((tpl_atyp*)(n->data))->bb = bb;
811 ((tpl_atyp*)(n->data))->bbtail = bb;
813 ((tpl_atyp*)(n->data))->bbtail->next = bb;
814 ((tpl_atyp*)(n->data))->bbtail = bb;
817 ((tpl_atyp*)(n->data))->num++;
821 /* Get the format string corresponding to a given tpl (root node) */
822 static char *tpl_fmt(tpl_node *r) {
823 return ((tpl_root_data*)(r->data))->fmt;
826 /* Get the fmt # lengths as a contiguous buffer of ints (length num_fxlens) */
827 static int *tpl_fxlens(tpl_node *r, int *num_fxlens) {
828 *num_fxlens = ((tpl_root_data*)(r->data))->num_fxlens;
829 return ((tpl_root_data*)(r->data))->fxlens;
832 /* called when serializing an 'A' type node into a buffer which has
833 * already been set up with the proper space. The backbone is walked
834 * which was obtained from the tpl_atyp header passed in.
836 static void *tpl_dump_atyp(tpl_node *n, tpl_atyp* at, void *dv) {
848 /* handle 'A' nodes */
849 dv = tpl_cpv(dv,&at->num,sizeof(uint32_t)); /* array len */
850 for(bb=at->bb; bb; bb=bb->next) {
856 case TPL_TYPE_DOUBLE:
858 case TPL_TYPE_UINT32:
860 case TPL_TYPE_UINT64:
862 case TPL_TYPE_UINT16:
863 dv = tpl_cpv(dv,datav,tpl_types[c->type].sz * c->num);
864 datav = (void*)((uintptr_t)datav + tpl_types[c->type].sz * c->num);
867 /* dump the buffer length followed by the buffer */
868 memcpy(&binp,datav,sizeof(tpl_bin*)); /* cp to aligned */
870 dv = tpl_cpv(dv,&slen,sizeof(uint32_t));
871 dv = tpl_cpv(dv,binp->addr,slen);
872 datav = (void*)((uintptr_t)datav + sizeof(tpl_bin*));
875 /* dump the string length followed by the string */
876 for(i=0; i < c->num; i++) {
877 memcpy(&strp,datav,sizeof(char*)); /* cp to aligned */
878 slen = strp ? (EM_SAFE_STRLEN(strp)+1) : 0;
879 dv = tpl_cpv(dv,&slen,sizeof(uint32_t));
880 if (slen > 1) dv = tpl_cpv(dv,strp,slen-1);
881 datav = (void*)((uintptr_t)datav + sizeof(char*));
885 memcpy(&atypp,datav,sizeof(tpl_atyp*)); /* cp to aligned */
886 dv = tpl_dump_atyp(c,atypp,dv);
887 datav = (void*)((uintptr_t)datav + sizeof(void*));
890 /* iterate over the preceding nodes */
891 pd = (tpl_pound_data*)c->data;
893 if (++(pd->iternum) < itermax) {
894 c = pd->iter_start_node;
896 } else { /* loop complete. */
901 tpl_hook.fatal("unsupported format character\n");
910 /* figure the serialization output size needed for tpl whose root is n*/
911 static size_t tpl_ser_osz(tpl_node *n) {
919 /* handle the root node ONLY (subtree's ser_osz have been bubbled-up) */
920 if (n->type != TPL_TYPE_ROOT) {
921 tpl_hook.fatal("internal error: tpl_ser_osz on non-root node\n");
924 sz = n->ser_osz; /* start with fixed overhead, already stored */
929 case TPL_TYPE_DOUBLE:
931 case TPL_TYPE_UINT32:
933 case TPL_TYPE_UINT64:
935 case TPL_TYPE_UINT16:
936 sz += tpl_types[c->type].sz * c->num;
939 sz += sizeof(uint32_t); /* binary buf len */
940 memcpy(&binp,c->data,sizeof(tpl_bin*)); /* cp to aligned */
944 for(i=0; i < c->num; i++) {
945 sz += sizeof(uint32_t); /* string len */
946 memcpy(&strp,&((char**)c->data)[i],sizeof(char*)); /* cp to aligned */
947 sz += strp ? EM_SAFE_STRLEN(strp) : 0;
951 sz += sizeof(uint32_t); /* array len */
952 sz += c->ser_osz; /* bubbled-up child array ser_osz */
955 /* iterate over the preceding nodes */
957 pd = (tpl_pound_data*)c->data;
958 if (++(pd->iternum) < itermax) {
959 for(np=pd->iter_start_node; np != c; np = np->next) {
960 np->data = (char*)(np->data) +
961 (tpl_types[np->type].sz * np->num);
963 c = pd->iter_start_node;
965 } else { /* loop complete. */
967 for(np=pd->iter_start_node; np != c; np = np->next) {
968 np->data = (char*)(np->data) - ((itermax-1) *
969 tpl_types[np->type].sz *
975 tpl_hook.fatal("unsupported format character\n");
984 TPL_API int tpl_dump(tpl_node *r, int mode, ...) {
986 char *filename, *bufv;
987 void **addr_out,*buf, *pa_addr;
989 size_t sz,*sz_out, pa_sz;
991 if (((tpl_root_data*)(r->data))->flags & TPL_RDONLY) { /* unusual */
992 tpl_hook.oops("error: tpl_dump called for a loaded tpl\n");
996 sz = tpl_ser_osz(r); /* compute the size needed to serialize */
999 if (mode & TPL_FILE) {
1000 filename = va_arg(ap,char*);
1001 fd = tpl_mmap_output_file(filename, sz, &buf);
1002 if (fd == -1) rc = -1;
1004 rc = tpl_dump_to_mem(r,buf,sz);
1005 if (msync(buf,sz,MS_SYNC) == -1) {
1006 tpl_hook.oops("msync failed on fd %d: %s\n", fd, strerror(errno));
1008 if (munmap(buf, sz) == -1) {
1009 tpl_hook.oops("munmap failed on fd %d: %s\n", fd, strerror(errno));
1013 } else if (mode & TPL_FD) {
1014 fd = va_arg(ap, int);
1015 if ( (buf = tpl_hook.malloc(sz)) == NULL) fatal_oom();
1016 tpl_dump_to_mem(r,buf,sz);
1019 rc = write(fd,bufv,sz);
1023 } else if (rc == -1) {
1024 if (errno == EINTR || errno == EAGAIN) continue;
1025 tpl_hook.oops("error writing to fd %d: %s\n", fd, strerror(errno));
1032 } else if (mode & TPL_MEM) {
1033 if (mode & TPL_PREALLOCD) { /* caller allocated */
1034 pa_addr = (void*)va_arg(ap, void*);
1035 pa_sz = va_arg(ap, size_t);
1037 tpl_hook.oops("tpl_dump: buffer too small, need %d bytes\n", sz);
1040 rc=tpl_dump_to_mem(r,pa_addr,sz);
1041 } else { /* we allocate */
1042 addr_out = (void**)va_arg(ap, void*);
1043 sz_out = va_arg(ap, size_t*);
1044 if ( (buf = tpl_hook.malloc(sz)) == NULL) fatal_oom();
1047 rc=tpl_dump_to_mem(r,buf,sz);
1049 } else if (mode & TPL_GETSIZE) {
1050 sz_out = va_arg(ap, size_t*);
1053 tpl_hook.oops("unsupported tpl_dump mode %d\n", mode);
1060 /* This function expects the caller to have set up a memory buffer of
1061 * adequate size to hold the serialized tpl. The sz parameter must be
1062 * the result of tpl_ser_osz(r).
1064 static int tpl_dump_to_mem(tpl_node *r,void *addr,size_t sz) {
1065 uint32_t slen, sz32;
1066 int *fxlens, num_fxlens, i;
1075 if (tpl_cpu_bigendian()) flags |= TPL_FL_BIGENDIAN;
1076 if (strchr(fmt,'s')) flags |= TPL_FL_NULLSTRINGS;
1080 dv = tpl_cpv(dv,TPL_MAGIC,3); /* copy tpl magic prefix */
1081 dv = tpl_cpv(dv,&flags,1); /* copy flags byte */
1082 dv = tpl_cpv(dv,&sz32,sizeof(uint32_t));/* overall length (inclusive) */
1083 dv = tpl_cpv(dv,fmt,EM_SAFE_STRLEN(fmt)+1); /* copy format with NUL-term */
1084 fxlens = tpl_fxlens(r,&num_fxlens);
1085 dv = tpl_cpv(dv,fxlens,num_fxlens*sizeof(uint32_t));/* fmt # lengths */
1087 /* serialize the tpl content, iterating over direct children of root */
1092 case TPL_TYPE_DOUBLE:
1093 case TPL_TYPE_INT32:
1094 case TPL_TYPE_UINT32:
1095 case TPL_TYPE_INT64:
1096 case TPL_TYPE_UINT64:
1097 case TPL_TYPE_INT16:
1098 case TPL_TYPE_UINT16:
1099 dv = tpl_cpv(dv,c->data,tpl_types[c->type].sz * c->num);
1102 slen = (*(tpl_bin**)(c->data))->sz;
1103 dv = tpl_cpv(dv,&slen,sizeof(uint32_t)); /* buffer len */
1104 dv = tpl_cpv(dv,(*(tpl_bin**)(c->data))->addr,slen); /* buf */
1107 for(i=0; i < c->num; i++) {
1108 char *str = ((char**)c->data)[i];
1109 slen = str ? EM_SAFE_STRLEN(str)+1 : 0;
1110 dv = tpl_cpv(dv,&slen,sizeof(uint32_t)); /* string len */
1111 if (slen>1) dv = tpl_cpv(dv,str,slen-1); /*string*/
1115 dv = tpl_dump_atyp(c,(tpl_atyp*)c->data,dv);
1117 case TPL_TYPE_POUND:
1118 pd = (tpl_pound_data*)c->data;
1120 if (++(pd->iternum) < itermax) {
1122 /* in start or midst of loop. advance data pointers. */
1123 for(np=pd->iter_start_node; np != c; np = np->next) {
1124 np->data = (char*)(np->data) +
1125 (tpl_types[np->type].sz * np->num);
1127 /* do next iteration */
1128 c = pd->iter_start_node;
1131 } else { /* loop complete. */
1133 /* reset iteration index and addr/data pointers. */
1135 for(np=pd->iter_start_node; np != c; np = np->next) {
1136 np->data = (char*)(np->data) - ((itermax-1) *
1137 tpl_types[np->type].sz *
1144 tpl_hook.fatal("unsupported format character\n");
1153 static int tpl_cpu_bigendian() {
1157 return (c[0] == 1 ? 0 : 1);
1162 * algorithm for sanity-checking a tpl image:
1163 * scan the tpl whilst not exceeding the buffer size (bufsz) ,
1164 * formulating a calculated (expected) size of the tpl based
1165 * on walking its data. When calcsize has been calculated it
1166 * should exactly match the buffer size (bufsz) and the internal
1167 * recorded size (intlsz)
1169 static int tpl_sanity(tpl_node *r, int excess_ok) {
1171 int found_nul=0,rc, octothorpes=0, num_fxlens, *fxlens, flen;
1173 char intlflags, *fmt, c, *mapfmt;
1174 size_t bufsz, serlen;
1176 d = ((tpl_root_data*)(r->data))->mmap.text;
1177 bufsz = ((tpl_root_data*)(r->data))->mmap.text_sz;
1180 if (bufsz < (4 + sizeof(uint32_t) + 1)) return ERR_NOT_MINSIZE; /* min sz: magic+flags+len+nul */
1181 if (memcmp(dv,TPL_MAGIC, 3) != 0) return ERR_MAGIC_MISMATCH; /* missing tpl magic prefix */
1182 if (tpl_needs_endian_swap(dv)) ((tpl_root_data*)(r->data))->flags |= TPL_XENDIAN;
1183 dv = (void*)((uintptr_t)dv + 3);
1184 memcpy(&intlflags,dv,sizeof(char)); /* extract flags */
1185 if (intlflags & ~TPL_SUPPORTED_BITFLAGS) return ERR_UNSUPPORTED_FLAGS;
1186 /* TPL1.3 stores strings with a "length+1" prefix to discern NULL strings from
1187 empty strings from non-empty strings; TPL1.2 only handled the latter two.
1188 So we need to be mindful of which string format we're reading from. */
1189 if (!(intlflags & TPL_FL_NULLSTRINGS)) {
1190 ((tpl_root_data*)(r->data))->flags |= TPL_OLD_STRING_FMT;
1192 dv = (void*)((uintptr_t)dv + 1);
1193 memcpy(&intlsz,dv,sizeof(uint32_t)); /* extract internal size */
1194 if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN) tpl_byteswap(&intlsz, sizeof(uint32_t));
1195 if (!excess_ok && (intlsz != bufsz)) return ERR_INCONSISTENT_SZ; /* inconsisent buffer/internal size */
1196 dv = (void*)((uintptr_t)dv + sizeof(uint32_t));
1198 /* dv points to the start of the format string. Look for nul w/in buf sz */
1200 while ((uintptr_t)dv-(uintptr_t)d < bufsz && !found_nul) {
1201 if ( (c = *(char*)dv) != '\0') {
1202 if (strchr(tpl_fmt_chars,c) == NULL)
1203 return ERR_FMT_INVALID; /* invalid char in format string */
1204 if ( (c = *(char*)dv) == '#') octothorpes++;
1205 dv = (void*)((uintptr_t)dv + 1);
1209 if (!found_nul) return ERR_FMT_MISSING_NUL; /* runaway format string */
1210 dv = (void*)((uintptr_t)dv + 1); /* advance to octothorpe lengths buffer */
1212 /* compare the map format to the format of this tpl image */
1213 mapfmt = tpl_fmt(r);
1214 rc = strcmp(mapfmt,fmt);
1215 if (rc != 0) return ERR_FMT_MISMATCH;
1217 /* compare octothorpe lengths in image to the mapped values */
1218 if ((((uintptr_t)dv + (octothorpes * 4)) - (uintptr_t)d) > bufsz) return ERR_INCONSISTENT_SZ4;
1219 fxlens = tpl_fxlens(r,&num_fxlens); /* mapped fxlens */
1220 while(num_fxlens--) {
1221 memcpy(&flen,dv,sizeof(uint32_t)); /* stored flen */
1222 if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN) tpl_byteswap(&flen, sizeof(uint32_t));
1223 if (flen != *fxlens) return ERR_FLEN_MISMATCH;
1224 dv = (void*)((uintptr_t)dv + sizeof(uint32_t));
1228 /* dv now points to beginning of data */
1229 rc = tpl_serlen(r,r,dv,&serlen); /* get computed serlen of data part */
1230 if (rc == -1) return ERR_INCONSISTENT_SZ2; /* internal inconsistency in tpl image */
1231 serlen += ((uintptr_t)dv - (uintptr_t)d); /* add back serlen of preamble part */
1232 if (excess_ok && (bufsz < serlen)) return ERR_INCONSISTENT_SZ3;
1233 if (!excess_ok && (serlen != bufsz)) return ERR_INCONSISTENT_SZ3; /* buffer/internal sz exceeds serlen */
1237 static void *tpl_find_data_start(void *d) {
1239 d = (void*)((uintptr_t)d + 4); /* skip TPL_MAGIC and flags byte */
1240 d = (void*)((uintptr_t)d + 4); /* skip int32 overall len */
1241 while(*(char*)d != '\0') {
1242 if (*(char*)d == '#') octothorpes++;
1243 d = (void*)((uintptr_t)d + 1);
1245 d = (void*)((uintptr_t)d + 1); /* skip NUL */
1246 d = (void*)((uintptr_t)d + (octothorpes * sizeof(uint32_t))); /* skip # array lens */
1250 static int tpl_needs_endian_swap(void *d) {
1252 int cpu_is_bigendian;
1254 cpu_is_bigendian = tpl_cpu_bigendian();
1255 return ((c[3] & TPL_FL_BIGENDIAN) == cpu_is_bigendian) ? 0 : 1;
1258 static size_t tpl_size_for(char c) {
1260 for(i=0; i < sizeof(tpl_types)/sizeof(tpl_types[0]); i++) {
1261 if (tpl_types[i].c == c) return tpl_types[i].sz;
1266 TPL_API char* tpl_peek(int mode, ...) {
1268 int xendian=0,found_nul=0,old_string_format=0;
1269 char *filename=NULL, *datapeek_f=NULL, *datapeek_c, *datapeek_s;
1270 void *addr=NULL, *dv, *datapeek_p=NULL;
1271 size_t sz=0, fmt_len, first_atom, num_fxlens=0;
1272 uint32_t datapeek_ssz, datapeek_csz, datapeek_flen;
1273 tpl_mmap_rec mr = {0,NULL,0};
1274 char *fmt,*fmt_cpy=NULL,c;
1275 uint32_t intlsz, **fxlens=NULL, *num_fxlens_out=NULL, *fxlensv;
1278 if ((mode & TPL_FXLENS) && (mode & TPL_DATAPEEK)) {
1279 tpl_hook.oops("TPL_FXLENS and TPL_DATAPEEK mutually exclusive\n");
1282 if (mode & TPL_FILE) filename = va_arg(ap,char *);
1283 else if (mode & TPL_MEM) {
1284 addr = va_arg(ap,void *);
1285 sz = va_arg(ap,size_t);
1287 tpl_hook.oops("unsupported tpl_peek mode %d\n", mode);
1290 if (mode & TPL_DATAPEEK) {
1291 datapeek_f = va_arg(ap, char*);
1293 if (mode & TPL_FXLENS) {
1294 num_fxlens_out = va_arg(ap,uint32_t *);
1295 fxlens = va_arg(ap,uint32_t **);
1296 *num_fxlens_out = 0;
1300 if (mode & TPL_FILE) {
1301 if (tpl_mmap_file(filename, &mr) != 0) {
1302 tpl_hook.oops("tpl_peek failed for file %s\n", filename);
1310 if (sz < (4 + sizeof(uint32_t) + 1)) goto fail; /* min sz */
1311 if (memcmp(dv,TPL_MAGIC, 3) != 0) goto fail; /* missing tpl magic prefix */
1312 if (tpl_needs_endian_swap(dv)) xendian=1;
1313 if ((((char*)dv)[3] & TPL_FL_NULLSTRINGS)==0) old_string_format=1;
1314 dv = (void*)((uintptr_t)dv + 4);
1315 memcpy(&intlsz,dv,sizeof(uint32_t)); /* extract internal size */
1316 if (xendian) tpl_byteswap(&intlsz, sizeof(uint32_t));
1317 if (intlsz != sz) goto fail; /* inconsisent buffer/internal size */
1318 dv = (void*)((uintptr_t)dv + sizeof(uint32_t));
1320 /* dv points to the start of the format string. Look for nul w/in buf sz */
1322 while ((uintptr_t)dv-(uintptr_t)addr < sz && !found_nul) {
1323 if ( (c = *(char*)dv) == '\0') {
1325 } else if (c == '#') {
1328 dv = (void*)((uintptr_t)dv + 1);
1330 if (!found_nul) goto fail; /* runaway format string */
1331 fmt_len = (char*)dv - fmt; /* include space for \0 */
1332 fmt_cpy = tpl_hook.malloc(fmt_len);
1333 if (fmt_cpy == NULL) {
1336 memcpy(fmt_cpy, fmt, fmt_len);
1338 /* retrieve the octothorpic lengths if requested */
1339 if (num_fxlens > 0) {
1340 if (sz < ((uintptr_t)dv + (num_fxlens * sizeof(uint32_t)) - (uintptr_t)addr)) {
1344 if ((mode & TPL_FXLENS) && (num_fxlens > 0)) {
1345 *fxlens = tpl_hook.malloc(num_fxlens * sizeof(uint32_t));
1346 if (*fxlens == NULL) tpl_hook.fatal("out of memory");
1347 *num_fxlens_out = num_fxlens;
1349 while(num_fxlens--) {
1350 memcpy(fxlensv,dv,sizeof(uint32_t));
1351 if (xendian) tpl_byteswap(fxlensv, sizeof(uint32_t));
1352 dv = (void*)((uintptr_t)dv + sizeof(uint32_t));
1356 /* if caller requested, peek into the specified data elements */
1357 if (mode & TPL_DATAPEEK) {
1359 first_atom = strspn(fmt, "S()"); /* skip any leading S() */
1361 datapeek_flen = EM_SAFE_STRLEN(datapeek_f);
1362 if (strspn(datapeek_f, tpl_datapeek_ok_chars) < datapeek_flen) {
1363 tpl_hook.oops("invalid TPL_DATAPEEK format: %s\n", datapeek_f);
1364 tpl_hook.free(fmt_cpy); fmt_cpy = NULL; /* fail */
1368 if (strncmp( &fmt[first_atom], datapeek_f, datapeek_flen) != 0) {
1369 tpl_hook.oops("TPL_DATAPEEK format mismatches tpl iamge\n");
1370 tpl_hook.free(fmt_cpy); fmt_cpy = NULL; /* fail */
1374 /* advance to data start, then copy out requested elements */
1375 dv = (void*)((uintptr_t)dv + (num_fxlens * sizeof(uint32_t)));
1376 for(datapeek_c = datapeek_f; *datapeek_c != '\0'; datapeek_c++) {
1377 datapeek_p = va_arg(ap, void*);
1378 if (*datapeek_c == 's') { /* special handling for strings */
1379 if ((uintptr_t)dv-(uintptr_t)addr + sizeof(uint32_t) > sz) {
1380 tpl_hook.oops("tpl_peek: tpl has insufficient length\n");
1381 tpl_hook.free(fmt_cpy); fmt_cpy = NULL; /* fail */
1384 memcpy(&datapeek_ssz,dv,sizeof(uint32_t)); /* get slen */
1385 if (xendian) tpl_byteswap(&datapeek_ssz, sizeof(uint32_t));
1386 if (old_string_format) datapeek_ssz++;
1387 dv = (void*)((uintptr_t)dv + sizeof(uint32_t)); /* adv. to str */
1388 if (datapeek_ssz == 0) datapeek_s = NULL;
1390 if ((uintptr_t)dv-(uintptr_t)addr + datapeek_ssz-1 > sz) {
1391 tpl_hook.oops("tpl_peek: tpl has insufficient length\n");
1392 tpl_hook.free(fmt_cpy); fmt_cpy = NULL; /* fail */
1395 datapeek_s = tpl_hook.malloc(datapeek_ssz);
1396 if (datapeek_s == NULL) fatal_oom();
1397 memcpy(datapeek_s, dv, datapeek_ssz-1);
1398 datapeek_s[datapeek_ssz-1] = '\0';
1399 dv = (void*)((uintptr_t)dv + datapeek_ssz-1);
1401 *(char**)datapeek_p = datapeek_s;
1403 datapeek_csz = tpl_size_for(*datapeek_c);
1404 if ((uintptr_t)dv-(uintptr_t)addr + datapeek_csz > sz) {
1405 tpl_hook.oops("tpl_peek: tpl has insufficient length\n");
1406 tpl_hook.free(fmt_cpy); fmt_cpy = NULL; /* fail */
1409 memcpy(datapeek_p, dv, datapeek_csz);
1410 if (xendian) tpl_byteswap(datapeek_p, datapeek_csz);
1411 dv = (void*)((uintptr_t)dv + datapeek_csz);
1418 if ((mode & TPL_FILE) && mr.text != NULL) tpl_unmap_file( &mr );
1422 /* tpl_jot(TPL_FILE, "file.tpl", "si", &s, &i); */
1423 /* tpl_jot(TPL_MEM, &buf, &sz, "si", &s, &i); */
1424 /* tpl_jot(TPL_FD, fd, "si", &s, &i); */
1425 TPL_API int tpl_jot(int mode, ...) {
1427 char *filename, *fmt;
1434 if (mode & TPL_FILE) {
1435 filename = va_arg(ap,char*);
1436 fmt = va_arg(ap,char*);
1437 tn = tpl_map_va(fmt, ap);
1438 if (tn == NULL) { rc=-1; goto fail;}
1440 rc = tpl_dump(tn, TPL_FILE, filename);
1442 } else if (mode & TPL_MEM) {
1443 buf = va_arg(ap,void*);
1444 sz = va_arg(ap,size_t*);
1445 fmt = va_arg(ap,char*);
1446 tn = tpl_map_va(fmt,ap);
1447 if (tn == NULL) { rc=-1; goto fail;}
1449 rc = tpl_dump(tn, TPL_MEM, buf, sz);
1451 } else if (mode & TPL_FD) {
1452 fd = va_arg(ap,int);
1453 fmt = va_arg(ap,char*);
1454 tn = tpl_map_va(fmt,ap);
1455 if (tn == NULL) { rc=-1; goto fail;}
1457 rc = tpl_dump(tn, TPL_FD, fd);
1460 tpl_hook.fatal("invalid tpl_jot mode\n");
1468 TPL_API int tpl_load(tpl_node *r, int mode, ...) {
1471 char *filename=NULL;
1476 if (mode & TPL_FILE) filename = va_arg(ap,char *);
1477 else if (mode & TPL_MEM) {
1478 addr = va_arg(ap,void *);
1479 sz = va_arg(ap,size_t);
1480 } else if (mode & TPL_FD) {
1481 fd = va_arg(ap,int);
1483 tpl_hook.oops("unsupported tpl_load mode %d\n", mode);
1488 if (r->type != TPL_TYPE_ROOT) {
1489 tpl_hook.oops("error: tpl_load to non-root node\n");
1492 if (((tpl_root_data*)(r->data))->flags & (TPL_WRONLY|TPL_RDONLY)) {
1493 /* already packed or loaded, so reset it as if newly mapped */
1494 tpl_free_keep_map(r);
1496 if (mode & TPL_FILE) {
1497 if (tpl_mmap_file(filename, &((tpl_root_data*)(r->data))->mmap) != 0) {
1498 tpl_hook.oops("tpl_load failed for file %s\n", filename);
1501 if ( (rc = tpl_sanity(r, (mode & TPL_EXCESS_OK))) != 0) {
1502 if (rc == ERR_FMT_MISMATCH) {
1503 tpl_hook.oops("%s: format signature mismatch\n", filename);
1504 } else if (rc == ERR_FLEN_MISMATCH) {
1505 tpl_hook.oops("%s: array lengths mismatch\n", filename);
1507 tpl_hook.oops("%s: not a valid tpl file\n", filename);
1509 tpl_unmap_file( &((tpl_root_data*)(r->data))->mmap );
1512 ((tpl_root_data*)(r->data))->flags = (TPL_FILE | TPL_RDONLY);
1513 } else if (mode & TPL_MEM) {
1514 ((tpl_root_data*)(r->data))->mmap.text = addr;
1515 ((tpl_root_data*)(r->data))->mmap.text_sz = sz;
1516 if ( (rc = tpl_sanity(r, (mode & TPL_EXCESS_OK))) != 0) {
1517 if (rc == ERR_FMT_MISMATCH) {
1518 tpl_hook.oops("format signature mismatch\n");
1520 tpl_hook.oops("not a valid tpl file\n");
1524 ((tpl_root_data*)(r->data))->flags = (TPL_MEM | TPL_RDONLY);
1525 if (mode & TPL_UFREE) ((tpl_root_data*)(r->data))->flags |= TPL_UFREE;
1526 } else if (mode & TPL_FD) {
1527 /* if fd read succeeds, resulting mem img is used for load */
1528 if (tpl_gather(TPL_GATHER_BLOCKING,fd,&addr,&sz) > 0) {
1529 return tpl_load(r, TPL_MEM|TPL_UFREE, addr, sz);
1532 tpl_hook.oops("invalid tpl_load mode %d\n", mode);
1535 /* this applies to TPL_MEM or TPL_FILE */
1536 if (tpl_needs_endian_swap(((tpl_root_data*)(r->data))->mmap.text))
1537 ((tpl_root_data*)(r->data))->flags |= TPL_XENDIAN;
1538 tpl_unpackA0(r); /* prepare root A nodes for use */
1542 TPL_API int tpl_Alen(tpl_node *r, int i) {
1545 n = tpl_find_i(r,i);
1547 tpl_hook.oops("invalid index %d to tpl_unpack\n", i);
1550 if (n->type != TPL_TYPE_ARY) return -1;
1551 return ((tpl_atyp*)(n->data))->num;
1554 static void tpl_free_atyp(tpl_node *n, tpl_atyp *atyp) {
1555 tpl_backbone *bb,*bbnxt;
1573 case TPL_TYPE_DOUBLE:
1574 case TPL_TYPE_INT32:
1575 case TPL_TYPE_UINT32:
1576 case TPL_TYPE_INT64:
1577 case TPL_TYPE_UINT64:
1578 case TPL_TYPE_INT16:
1579 case TPL_TYPE_UINT16:
1580 dv = (void*)((uintptr_t)dv + tpl_types[c->type].sz*c->num);
1583 memcpy(&binp,dv,sizeof(tpl_bin*)); /* cp to aligned */
1584 if (binp->addr) tpl_hook.free( binp->addr ); /* free buf */
1585 tpl_hook.free(binp); /* free tpl_bin */
1586 dv = (void*)((uintptr_t)dv + sizeof(tpl_bin*));
1589 for(i=0; i < c->num; i++) {
1590 memcpy(&strp,dv,sizeof(char*)); /* cp to aligned */
1591 if (strp) tpl_hook.free(strp); /* free string */
1592 dv = (void*)((uintptr_t)dv + sizeof(char*));
1595 case TPL_TYPE_POUND:
1596 /* iterate over the preceding nodes */
1598 pd = (tpl_pound_data*)c->data;
1599 if (++(pd->iternum) < itermax) {
1600 c = pd->iter_start_node;
1602 } else { /* loop complete. */
1607 memcpy(&atypp,dv,sizeof(tpl_atyp*)); /* cp to aligned */
1608 tpl_free_atyp(c,atypp); /* free atyp */
1609 dv = (void*)((uintptr_t)dv + sizeof(void*));
1612 tpl_hook.fatal("unsupported format character\n");
1620 tpl_hook.free(atyp);
1623 /* determine (by walking) byte length of serialized r/A node at address dv
1624 * returns 0 on success, or -1 if the tpl isn't trustworthy (fails consistency)
1626 static int tpl_serlen(tpl_node *r, tpl_node *n, void *dv, size_t *serlen) {
1630 size_t len=0, alen, buf_past, itermax;
1633 buf_past = ((uintptr_t)((tpl_root_data*)(r->data))->mmap.text +
1634 ((tpl_root_data*)(r->data))->mmap.text_sz);
1636 if (n->type == TPL_TYPE_ROOT) num = 1;
1637 else if (n->type == TPL_TYPE_ARY) {
1638 if ((uintptr_t)dv + sizeof(uint32_t) > buf_past) return -1;
1639 memcpy(&num,dv,sizeof(uint32_t));
1640 if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN)
1641 tpl_byteswap(&num, sizeof(uint32_t));
1642 dv = (void*)((uintptr_t)dv + sizeof(uint32_t));
1643 len += sizeof(uint32_t);
1644 } else tpl_hook.fatal("internal error in tpl_serlen\n");
1651 case TPL_TYPE_DOUBLE:
1652 case TPL_TYPE_INT32:
1653 case TPL_TYPE_UINT32:
1654 case TPL_TYPE_INT64:
1655 case TPL_TYPE_UINT64:
1656 case TPL_TYPE_INT16:
1657 case TPL_TYPE_UINT16:
1658 for(fidx=0; fidx < c->num; fidx++) { /* octothorpe support */
1659 if ((uintptr_t)dv + tpl_types[c->type].sz > buf_past) return -1;
1660 dv = (void*)((uintptr_t)dv + tpl_types[c->type].sz);
1661 len += tpl_types[c->type].sz;
1665 len += sizeof(uint32_t);
1666 if ((uintptr_t)dv + sizeof(uint32_t) > buf_past) return -1;
1667 memcpy(&slen,dv,sizeof(uint32_t));
1668 if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN)
1669 tpl_byteswap(&slen, sizeof(uint32_t));
1671 dv = (void*)((uintptr_t)dv + sizeof(uint32_t));
1672 if ((uintptr_t)dv + slen > buf_past) return -1;
1673 dv = (void*)((uintptr_t)dv + slen);
1676 for(fidx=0; fidx < c->num; fidx++) { /* octothorpe support */
1677 len += sizeof(uint32_t);
1678 if ((uintptr_t)dv + sizeof(uint32_t) > buf_past) return -1;
1679 memcpy(&slen,dv,sizeof(uint32_t));
1680 if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN)
1681 tpl_byteswap(&slen, sizeof(uint32_t));
1682 if (!(((tpl_root_data*)(r->data))->flags & TPL_OLD_STRING_FMT))
1683 slen = (slen>1) ? (slen-1) : 0;
1685 dv = (void*)((uintptr_t)dv + sizeof(uint32_t));
1686 if ((uintptr_t)dv + slen > buf_past) return -1;
1687 dv = (void*)((uintptr_t)dv + slen);
1691 if ( tpl_serlen(r,c,dv, &alen) == -1) return -1;
1692 dv = (void*)((uintptr_t)dv + alen);
1695 case TPL_TYPE_POUND:
1696 /* iterate over the preceding nodes */
1698 pd = (tpl_pound_data*)c->data;
1699 if (++(pd->iternum) < itermax) {
1700 c = pd->iter_start_node;
1702 } else { /* loop complete. */
1707 tpl_hook.fatal("unsupported format character\n");
1717 static int tpl_mmap_output_file(char *filename, size_t sz, void **text_out) {
1722 perms = S_IRUSR|S_IWUSR|S_IWGRP|S_IRGRP|S_IROTH; /* ug+w o+r */
1723 fd=open(filename,O_CREAT|O_TRUNC|O_RDWR,perms);
1726 fd=_open(filename,_O_CREAT|_O_TRUNC|_O_RDWR,perms);
1730 tpl_hook.oops("Couldn't open file %s: %s\n", filename, strerror(errno));
1734 text = mmap(0, sz, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
1735 if (text == MAP_FAILED) {
1736 tpl_hook.oops("Failed to mmap %s: %s\n", filename, strerror(errno));
1740 if (ftruncate(fd,sz) == -1) {
1741 tpl_hook.oops("ftruncate failed: %s\n", strerror(errno));
1750 static int tpl_mmap_file(char *filename, tpl_mmap_rec *mr) {
1751 struct stat stat_buf;
1753 if ( (mr->fd = open(filename, O_RDONLY)) == -1 ) {
1754 tpl_hook.oops("Couldn't open file %s: %s\n", filename, strerror(errno));
1758 if ( fstat(mr->fd, &stat_buf) == -1) {
1760 tpl_hook.oops("Couldn't stat file %s: %s\n", filename, strerror(errno));
1764 mr->text_sz = (size_t)stat_buf.st_size;
1765 mr->text = mmap(0, stat_buf.st_size, PROT_READ, MAP_PRIVATE, mr->fd, 0);
1766 if (mr->text == MAP_FAILED) {
1768 tpl_hook.oops("Failed to mmap %s: %s\n", filename, strerror(errno));
1775 TPL_API int tpl_pack(tpl_node *r, int i) {
1776 tpl_node *n, *child, *np;
1785 n = tpl_find_i(r,i);
1787 tpl_hook.oops("invalid index %d to tpl_pack\n", i);
1791 if (((tpl_root_data*)(r->data))->flags & TPL_RDONLY) {
1792 /* convert to an writeable tpl, initially empty */
1793 tpl_free_keep_map(r);
1796 ((tpl_root_data*)(r->data))->flags |= TPL_WRONLY;
1798 if (n->type == TPL_TYPE_ARY) datav = tpl_extend_backbone(n);
1799 child = n->children;
1801 switch(child->type) {
1803 case TPL_TYPE_DOUBLE:
1804 case TPL_TYPE_INT32:
1805 case TPL_TYPE_UINT32:
1806 case TPL_TYPE_INT64:
1807 case TPL_TYPE_UINT64:
1808 case TPL_TYPE_INT16:
1809 case TPL_TYPE_UINT16:
1810 /* no need to use fidx iteration here; we can copy multiple values in one memcpy */
1811 memcpy(child->data,child->addr,tpl_types[child->type].sz * child->num);
1812 if (datav) datav = tpl_cpv(datav,child->data,tpl_types[child->type].sz * child->num);
1813 if (n->type == TPL_TYPE_ARY) n->ser_osz += tpl_types[child->type].sz * child->num;
1816 /* copy the buffer to be packed */
1817 slen = ((tpl_bin*)child->addr)->sz;
1819 str = tpl_hook.malloc(slen);
1820 if (!str) fatal_oom();
1821 memcpy(str,((tpl_bin*)child->addr)->addr,slen);
1823 /* and make a tpl_bin to point to it */
1824 bin = tpl_hook.malloc(sizeof(tpl_bin));
1825 if (!bin) fatal_oom();
1828 /* now pack its pointer, first deep freeing any pre-existing bin */
1829 if (*(tpl_bin**)(child->data) != NULL) {
1830 if ((*(tpl_bin**)(child->data))->sz != 0) {
1831 tpl_hook.free( (*(tpl_bin**)(child->data))->addr );
1833 tpl_hook.free(*(tpl_bin**)(child->data));
1835 memcpy(child->data,&bin,sizeof(tpl_bin*));
1837 datav = tpl_cpv(datav, &bin, sizeof(tpl_bin*));
1838 *(tpl_bin**)(child->data) = NULL;
1840 if (n->type == TPL_TYPE_ARY) {
1841 n->ser_osz += sizeof(uint32_t); /* binary buf len word */
1842 n->ser_osz += bin->sz; /* binary buf */
1846 for(fidx=0; fidx < child->num; fidx++) {
1847 /* copy the string to be packed. slen includes \0. this
1848 block also works if the string pointer is NULL. */
1849 char *caddr = ((char**)child->addr)[fidx];
1850 char **cdata = &((char**)child->data)[fidx];
1851 slen = caddr ? (EM_SAFE_STRLEN(caddr) + 1) : 0;
1853 str = tpl_hook.malloc(slen);
1854 if (!str) fatal_oom();
1855 memcpy(str,caddr,slen); /* include \0 */
1859 /* now pack its pointer, first freeing any pre-existing string */
1860 if (*cdata != NULL) {
1861 tpl_hook.free(*cdata);
1863 memcpy(cdata,&str,sizeof(char*));
1865 datav = tpl_cpv(datav, &str, sizeof(char*));
1868 if (n->type == TPL_TYPE_ARY) {
1869 n->ser_osz += sizeof(uint32_t); /* string len word */
1870 if (slen>1) n->ser_osz += slen-1;/* string (without nul) */
1875 /* copy the child's tpl_atype* and reset it to empty */
1877 sz = ((tpl_atyp*)(child->data))->sz;
1878 datav = tpl_cpv(datav, &child->data, sizeof(void*));
1879 child->data = tpl_hook.malloc(sizeof(tpl_atyp));
1880 if (!child->data) fatal_oom();
1881 ((tpl_atyp*)(child->data))->num = 0;
1882 ((tpl_atyp*)(child->data))->sz = sz;
1883 ((tpl_atyp*)(child->data))->bb = NULL;
1884 ((tpl_atyp*)(child->data))->bbtail = NULL;
1886 /* parent is array? then bubble up child array's ser_osz */
1887 if (n->type == TPL_TYPE_ARY) {
1888 n->ser_osz += sizeof(uint32_t); /* array len word */
1889 n->ser_osz += child->ser_osz; /* child array ser_osz */
1890 child->ser_osz = 0; /* reset child array ser_osz */
1894 case TPL_TYPE_POUND:
1895 /* we need to iterate n times over preceding nodes in S(...).
1896 * we may be in the midst of an iteration each time or starting. */
1897 pd = (tpl_pound_data*)child->data;
1898 itermax = child->num;
1900 /* itermax is total num of iterations needed */
1901 /* pd->iternum is current iteration index */
1902 /* pd->inter_elt_len is element-to-element len of contiguous structs */
1903 /* pd->iter_start_node is where we jump to at each iteration. */
1905 if (++(pd->iternum) < itermax) {
1907 /* in start or midst of loop. advance addr/data pointers. */
1908 for(np=pd->iter_start_node; np != child; np = np->next) {
1909 np->data = (char*)(np->data) +
1910 (tpl_types[np->type].sz * np->num);
1911 np->addr = (char*)(np->addr) + pd->inter_elt_len;
1913 /* do next iteration */
1914 child = pd->iter_start_node;
1917 } else { /* loop complete. */
1919 /* reset iteration index and addr/data pointers. */
1921 for(np=pd->iter_start_node; np != child; np = np->next) {
1922 np->data = (char*)(np->data) - ((itermax-1) *
1923 tpl_types[np->type].sz *
1925 np->addr = (char*)(np->addr) - ((itermax-1) * pd->inter_elt_len);
1931 tpl_hook.fatal("unsupported format character\n");
1939 TPL_API int tpl_unpack(tpl_node *r, int i) {
1940 tpl_node *n, *c, *np;
1944 void *dv=NULL, *caddr;
1945 size_t A_bytes, itermax;
1951 /* handle unusual case of tpl_pack,tpl_unpack without an
1952 * intervening tpl_dump. do a dump/load implicitly. */
1953 if (((tpl_root_data*)(r->data))->flags & TPL_WRONLY) {
1954 if (tpl_dump(r,TPL_MEM,&img,&sz) != 0) return -1;
1955 if (tpl_load(r,TPL_MEM|TPL_UFREE,img,sz) != 0) {
1961 n = tpl_find_i(r,i);
1963 tpl_hook.oops("invalid index %d to tpl_unpack\n", i);
1967 /* either root node or an A node */
1968 if (n->type == TPL_TYPE_ROOT) {
1969 dv = tpl_find_data_start( ((tpl_root_data*)(n->data))->mmap.text );
1970 } else if (n->type == TPL_TYPE_ARY) {
1971 if (((tpl_atyp*)(n->data))->num <= 0) return 0; /* array consumed */
1972 else rc = ((tpl_atyp*)(n->data))->num--;
1973 dv = ((tpl_atyp*)(n->data))->cur;
1974 if (!dv) tpl_hook.fatal("must unpack parent of node before node itself\n");
1981 case TPL_TYPE_DOUBLE:
1982 case TPL_TYPE_INT32:
1983 case TPL_TYPE_UINT32:
1984 case TPL_TYPE_INT64:
1985 case TPL_TYPE_UINT64:
1986 case TPL_TYPE_INT16:
1987 case TPL_TYPE_UINT16:
1988 /* unpack elements of cross-endian octothorpic array individually */
1989 if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN) {
1990 for(fidx=0; fidx < c->num; fidx++) {
1991 caddr = (void*)((uintptr_t)c->addr + (fidx * tpl_types[c->type].sz));
1992 memcpy(caddr,dv,tpl_types[c->type].sz);
1993 tpl_byteswap(caddr, tpl_types[c->type].sz);
1994 dv = (void*)((uintptr_t)dv + tpl_types[c->type].sz);
1997 /* bulk unpack ok if not cross-endian */
1998 memcpy(c->addr, dv, tpl_types[c->type].sz * c->num);
1999 dv = (void*)((uintptr_t)dv + tpl_types[c->type].sz * c->num);
2003 memcpy(&slen,dv,sizeof(uint32_t));
2004 if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN)
2005 tpl_byteswap(&slen, sizeof(uint32_t));
2007 str = (char*)tpl_hook.malloc(slen);
2008 if (!str) fatal_oom();
2010 dv = (void*)((uintptr_t)dv + sizeof(uint32_t));
2011 if (slen>0) memcpy(str,dv,slen);
2012 memcpy(&(((tpl_bin*)c->addr)->addr),&str,sizeof(void*));
2013 memcpy(&(((tpl_bin*)c->addr)->sz),&slen,sizeof(uint32_t));
2014 dv = (void*)((uintptr_t)dv + slen);
2017 for(fidx=0; fidx < c->num; fidx++) {
2018 memcpy(&slen,dv,sizeof(uint32_t));
2019 if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN)
2020 tpl_byteswap(&slen, sizeof(uint32_t));
2021 if (((tpl_root_data*)(r->data))->flags & TPL_OLD_STRING_FMT)
2023 dv = (void*)((uintptr_t)dv + sizeof(uint32_t));
2024 if (slen) { /* slen includes \0 */
2025 str = (char*)tpl_hook.malloc(slen);
2026 if (!str) fatal_oom();
2027 if (slen>1) memcpy(str,dv,slen-1);
2028 str[slen-1] = '\0'; /* nul terminate */
2029 dv = (void*)((uintptr_t)dv + slen-1);
2031 memcpy(&((char**)c->addr)[fidx],&str,sizeof(char*));
2034 case TPL_TYPE_POUND:
2035 /* iterate over preceding nodes */
2036 pd = (tpl_pound_data*)c->data;
2038 if (++(pd->iternum) < itermax) {
2039 /* in start or midst of loop. advance addr/data pointers. */
2040 for(np=pd->iter_start_node; np != c; np = np->next) {
2041 np->addr = (char*)(np->addr) + pd->inter_elt_len;
2043 /* do next iteration */
2044 c = pd->iter_start_node;
2047 } else { /* loop complete. */
2049 /* reset iteration index and addr/data pointers. */
2051 for(np=pd->iter_start_node; np != c; np = np->next) {
2052 np->addr = (char*)(np->addr) - ((itermax-1) * pd->inter_elt_len);
2058 if (tpl_serlen(r,c,dv, &A_bytes) == -1)
2059 tpl_hook.fatal("internal error in unpack\n");
2060 memcpy( &((tpl_atyp*)(c->data))->num, dv, sizeof(uint32_t));
2061 if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN)
2062 tpl_byteswap(&((tpl_atyp*)(c->data))->num, sizeof(uint32_t));
2063 ((tpl_atyp*)(c->data))->cur = (void*)((uintptr_t)dv+sizeof(uint32_t));
2064 dv = (void*)((uintptr_t)dv + A_bytes);
2067 tpl_hook.fatal("unsupported format character\n");
2073 if (n->type == TPL_TYPE_ARY) ((tpl_atyp*)(n->data))->cur = dv; /* next element */
2077 /* Specialized function that unpacks only the root's A nodes, after tpl_load */
2078 static int tpl_unpackA0(tpl_node *r) {
2083 size_t A_bytes, itermax;
2087 dv = tpl_find_data_start( ((tpl_root_data*)(r->data))->mmap.text);
2093 case TPL_TYPE_DOUBLE:
2094 case TPL_TYPE_INT32:
2095 case TPL_TYPE_UINT32:
2096 case TPL_TYPE_INT64:
2097 case TPL_TYPE_UINT64:
2098 case TPL_TYPE_INT16:
2099 case TPL_TYPE_UINT16:
2100 for(fidx=0;fidx < c->num; fidx++) {
2101 dv = (void*)((uintptr_t)dv + tpl_types[c->type].sz);
2105 memcpy(&slen,dv,sizeof(uint32_t));
2106 if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN)
2107 tpl_byteswap(&slen, sizeof(uint32_t));
2108 dv = (void*)((uintptr_t)dv + sizeof(uint32_t));
2109 dv = (void*)((uintptr_t)dv + slen);
2112 for(i=0; i<c->num; i++) {
2113 memcpy(&slen,dv,sizeof(uint32_t));
2114 if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN)
2115 tpl_byteswap(&slen, sizeof(uint32_t));
2116 if (((tpl_root_data*)(r->data))->flags & TPL_OLD_STRING_FMT)
2118 dv = (void*)((uintptr_t)dv + sizeof(uint32_t));
2119 if (slen>1) dv = (void*)((uintptr_t)dv + slen-1);
2122 case TPL_TYPE_POUND:
2123 /* iterate over the preceding nodes */
2125 pd = (tpl_pound_data*)c->data;
2126 if (++(pd->iternum) < itermax) {
2127 c = pd->iter_start_node;
2129 } else { /* loop complete. */
2134 if ( tpl_serlen(r,c,dv, &A_bytes) == -1)
2135 tpl_hook.fatal("internal error in unpackA0\n");
2136 memcpy( &((tpl_atyp*)(c->data))->num, dv, sizeof(uint32_t));
2137 if (((tpl_root_data*)(r->data))->flags & TPL_XENDIAN)
2138 tpl_byteswap(&((tpl_atyp*)(c->data))->num, sizeof(uint32_t));
2139 ((tpl_atyp*)(c->data))->cur = (void*)((uintptr_t)dv+sizeof(uint32_t));
2140 dv = (void*)((uintptr_t)dv + A_bytes);
2143 tpl_hook.fatal("unsupported format character\n");
2151 /* In-place byte order swapping of a word of length "len" bytes */
2152 static void tpl_byteswap(void *word, int len) {
2156 for(i=0; i<len/2; i++) {
2163 static void tpl_fatal(char *fmt, ...) {
2168 vsnprintf(exit_msg, 100, fmt, ap);
2171 tpl_hook.oops("%s", exit_msg);
2175 TPL_API int tpl_gather(int mode, ...) {
2179 void **img,*addr,*data;
2185 case TPL_GATHER_BLOCKING:
2186 fd = va_arg(ap,int);
2187 img = va_arg(ap,void*);
2188 szp = va_arg(ap,size_t*);
2189 rc = tpl_gather_blocking(fd,img,szp);
2191 case TPL_GATHER_NONBLOCKING:
2192 fd = va_arg(ap,int);
2193 gs = (tpl_gather_t**)va_arg(ap,void*);
2194 cb = (tpl_gather_cb*)va_arg(ap,tpl_gather_cb*);
2195 data = va_arg(ap,void*);
2196 rc = tpl_gather_nonblocking(fd,gs,cb,data);
2198 case TPL_GATHER_MEM:
2199 addr = va_arg(ap,void*);
2200 sz = va_arg(ap,size_t);
2201 gs = (tpl_gather_t**)va_arg(ap,void*);
2202 cb = (tpl_gather_cb*)va_arg(ap,tpl_gather_cb*);
2203 data = va_arg(ap,void*);
2204 rc = tpl_gather_mem(addr,sz,gs,cb,data);
2207 tpl_hook.fatal("unsupported tpl_gather mode %d\n",mode);
2214 /* dequeue a tpl by reading until one full tpl image is obtained.
2215 * We take care not to read past the end of the tpl.
2216 * This is intended as a blocking call i.e. for use with a blocking fd.
2217 * It can be given a non-blocking fd, but the read spins if we have to wait.
2219 static int tpl_gather_blocking(int fd, void **img, size_t *sz) {
2225 rc = read(fd,&preamble[i],8-i);
2226 i += (rc>0) ? rc : 0;
2227 } while ((rc==-1 && (errno==EINTR||errno==EAGAIN)) || (rc>0 && i<8));
2230 tpl_hook.oops("tpl_gather_fd_blocking failed: %s\n", strerror(errno));
2232 } else if (rc == 0) {
2233 /* tpl_hook.oops("tpl_gather_fd_blocking: eof\n"); */
2235 } else if (i != 8) {
2236 tpl_hook.oops("internal error\n");
2240 if (preamble[0] == 't' && preamble[1] == 'p' && preamble[2] == 'l') {
2241 memcpy(&tpllen,&preamble[4],4);
2242 if (tpl_needs_endian_swap(preamble)) tpl_byteswap(&tpllen,4);
2244 tpl_hook.oops("tpl_gather_fd_blocking: non-tpl input\n");
2248 /* malloc space for remainder of tpl image (overall length tpllen)
2251 if (tpl_hook.gather_max > 0 &&
2252 tpllen > tpl_hook.gather_max) {
2253 tpl_hook.oops("tpl exceeds max length %d\n",
2254 tpl_hook.gather_max);
2258 if ( (*img = tpl_hook.malloc(tpllen)) == NULL) {
2262 memcpy(*img,preamble,8); /* copy preamble to output buffer */
2265 rc = read(fd,&((*(char**)img)[i]),tpllen-i);
2266 i += (rc>0) ? rc : 0;
2267 } while ((rc==-1 && (errno==EINTR||errno==EAGAIN)) || (rc>0 && i<tpllen));
2270 tpl_hook.oops("tpl_gather_fd_blocking failed: %s\n", strerror(errno));
2271 tpl_hook.free(*img);
2273 } else if (rc == 0) {
2274 /* tpl_hook.oops("tpl_gather_fd_blocking: eof\n"); */
2275 tpl_hook.free(*img);
2277 } else if (i != tpllen) {
2278 tpl_hook.oops("internal error\n");
2279 tpl_hook.free(*img);
2286 /* Used by select()-driven apps which want to gather tpl images piecemeal */
2287 /* the file descriptor must be non-blocking for this functino to work. */
2288 static int tpl_gather_nonblocking( int fd, tpl_gather_t **gs, tpl_gather_cb *cb, void *data) {
2289 char buf[TPL_GATHER_BUFLEN], *img, *tpl;
2290 int rc, keep_looping, cbrc=0;
2295 rc = read(fd,buf,TPL_GATHER_BUFLEN);
2297 if (errno == EINTR) continue; /* got signal during read, ignore */
2298 if (errno == EAGAIN) return 1; /* nothing to read right now */
2300 tpl_hook.oops("tpl_gather failed: %s\n", strerror(errno));
2302 tpl_hook.free((*gs)->img);
2306 return -1; /* error, caller should close fd */
2308 } else if (rc == 0) {
2310 tpl_hook.oops("tpl_gather: partial tpl image precedes EOF\n");
2311 tpl_hook.free((*gs)->img);
2315 return 0; /* EOF, caller should close fd */
2317 /* concatenate any partial tpl from last read with new buffer */
2319 catlen = (*gs)->len + rc;
2320 if (tpl_hook.gather_max > 0 &&
2321 catlen > tpl_hook.gather_max) {
2322 tpl_hook.free( (*gs)->img );
2323 tpl_hook.free( (*gs) );
2325 tpl_hook.oops("tpl exceeds max length %d\n",
2326 tpl_hook.gather_max);
2327 return -2; /* error, caller should close fd */
2329 if ( (img = tpl_hook.realloc((*gs)->img, catlen)) == NULL) {
2332 memcpy(img + (*gs)->len, buf, rc);
2339 /* isolate any full tpl(s) in img and invoke cb for each */
2341 keep_looping = (tpl+8 < img+catlen) ? 1 : 0;
2342 while (keep_looping) {
2343 if (strncmp("tpl", tpl, 3) != 0) {
2344 tpl_hook.oops("tpl prefix invalid\n");
2345 if (img != buf) tpl_hook.free(img);
2348 return -3; /* error, caller should close fd */
2350 memcpy(&tpllen,&tpl[4],4);
2351 if (tpl_needs_endian_swap(tpl)) tpl_byteswap(&tpllen,4);
2352 if (tpl+tpllen <= img+catlen) {
2353 cbrc = (cb)(tpl,tpllen,data); /* invoke cb for tpl image */
2354 tpl += tpllen; /* point to next tpl image */
2355 if (cbrc < 0) keep_looping = 0;
2356 else keep_looping = (tpl+8 < img+catlen) ? 1 : 0;
2357 } else keep_looping=0;
2359 /* check if app callback requested closure of tpl source */
2361 tpl_hook.oops("tpl_fd_gather aborted by app callback\n");
2362 if (img != buf) tpl_hook.free(img);
2363 if (*gs) tpl_hook.free(*gs);
2367 /* store any leftover, partial tpl fragment for next read */
2368 if (tpl == img && img != buf) {
2369 /* consumed nothing from img!=buf */
2370 if ( (*gs = tpl_hook.malloc(sizeof(tpl_gather_t))) == NULL ) {
2374 (*gs)->len = catlen;
2375 } else if (tpl < img+catlen) {
2376 /* consumed 1+ tpl(s) from img!=buf or 0 from img==buf */
2377 if ( (*gs = tpl_hook.malloc(sizeof(tpl_gather_t))) == NULL ) {
2380 if ( ((*gs)->img = tpl_hook.malloc(img+catlen - tpl)) == NULL ) {
2383 (*gs)->len = img+catlen - tpl;
2384 memcpy( (*gs)->img, tpl, img+catlen - tpl);
2385 /* free partially consumed concat buffer if used */
2386 if (img != buf) tpl_hook.free(img);
2387 } else { /* tpl(s) fully consumed */
2388 /* free consumed concat buffer if used */
2389 if (img != buf) tpl_hook.free(img);
2395 /* gather tpl piecemeal from memory buffer (not fd) e.g., from a lower-level api */
2396 static int tpl_gather_mem( char *buf, size_t len, tpl_gather_t **gs, tpl_gather_cb *cb, void *data) {
2398 int keep_looping, cbrc=0;
2402 /* concatenate any partial tpl from last read with new buffer */
2404 catlen = (*gs)->len + len;
2405 if (tpl_hook.gather_max > 0 &&
2406 catlen > tpl_hook.gather_max) {
2407 tpl_hook.free( (*gs)->img );
2408 tpl_hook.free( (*gs) );
2410 tpl_hook.oops("tpl exceeds max length %d\n",
2411 tpl_hook.gather_max);
2412 return -2; /* error, caller should stop accepting input from source*/
2414 if ( (img = tpl_hook.realloc((*gs)->img, catlen)) == NULL) {
2417 memcpy(img + (*gs)->len, buf, len);
2424 /* isolate any full tpl(s) in img and invoke cb for each */
2426 keep_looping = (tpl+8 < img+catlen) ? 1 : 0;
2427 while (keep_looping) {
2428 if (strncmp("tpl", tpl, 3) != 0) {
2429 tpl_hook.oops("tpl prefix invalid\n");
2430 if (img != buf) tpl_hook.free(img);
2433 return -3; /* error, caller should stop accepting input from source*/
2435 memcpy(&tpllen,&tpl[4],4);
2436 if (tpl_needs_endian_swap(tpl)) tpl_byteswap(&tpllen,4);
2437 if (tpl+tpllen <= img+catlen) {
2438 cbrc = (cb)(tpl,tpllen,data); /* invoke cb for tpl image */
2439 tpl += tpllen; /* point to next tpl image */
2440 if (cbrc < 0) keep_looping = 0;
2441 else keep_looping = (tpl+8 < img+catlen) ? 1 : 0;
2442 } else keep_looping=0;
2444 /* check if app callback requested closure of tpl source */
2446 tpl_hook.oops("tpl_mem_gather aborted by app callback\n");
2447 if (img != buf) tpl_hook.free(img);
2448 if (*gs) tpl_hook.free(*gs);
2452 /* store any leftover, partial tpl fragment for next read */
2453 if (tpl == img && img != buf) {
2454 /* consumed nothing from img!=buf */
2455 if ( (*gs = tpl_hook.malloc(sizeof(tpl_gather_t))) == NULL ) {
2459 (*gs)->len = catlen;
2460 } else if (tpl < img+catlen) {
2461 /* consumed 1+ tpl(s) from img!=buf or 0 from img==buf */
2462 if ( (*gs = tpl_hook.malloc(sizeof(tpl_gather_t))) == NULL ) {
2465 if ( ((*gs)->img = tpl_hook.malloc(img+catlen - tpl)) == NULL ) {
2468 (*gs)->len = img+catlen - tpl;
2469 memcpy( (*gs)->img, tpl, img+catlen - tpl);
2470 /* free partially consumed concat buffer if used */
2471 if (img != buf) tpl_hook.free(img);
2472 } else { /* tpl(s) fully consumed */
2473 /* free consumed concat buffer if used */
2474 if (img != buf) tpl_hook.free(img);