3 * Types for the test-vector framework
5 * (c) 2023 Straylight/Edgeware
8 /*----- Licensing notice --------------------------------------------------*
10 * This file is part of the mLib utilities library.
12 * mLib is free software: you can redistribute it and/or modify it under
13 * the terms of the GNU Library General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or (at
15 * your option) any later version.
17 * mLib is distributed in the hope that it will be useful, but WITHOUT
18 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
20 * License for more details.
22 * You should have received a copy of the GNU Library General Public
23 * License along with mLib. If not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
28 /*----- Header files ------------------------------------------------------*/
45 /*----- Preliminary utilities ---------------------------------------------*/
47 static int signed_to_buf(buf *b, long i)
53 if (i >= 0) ASSIGN64(k, u);
54 else { ASSIGN64(k, ~u); CPL64(k, k); }
55 return (buf_putk64l(b, k));
58 static int signed_from_buf(buf *b, long *i_out)
60 kludge64 k, lmax, not_lmin;
62 ASSIGN64(lmax, LONG_MAX); ASSIGN64(not_lmin, ~(unsigned long)LONG_MIN);
63 if (buf_getk64l(b, &k)) return (-1);
64 if (CMP64(k, <=, lmax)) *i_out = (long)GET64(unsigned long, k);
67 if (CMP64(k, <=, not_lmin)) *i_out = -(long)GET64(unsigned long, k) - 1;
73 static int unsigned_to_buf(buf *b, unsigned long u)
74 { kludge64 k; ASSIGN64(k, u); return (buf_putk64l(b, k)); }
76 static int unsigned_from_buf(buf *b, unsigned long *u_out)
80 ASSIGN64(ulmax, ULONG_MAX);
81 if (buf_getk64l(b, &k)) return (-1);
82 if (CMP64(k, >, ulmax)) return (-1);
83 *u_out = GET64(unsigned long, k); return (0);
86 static int hex_width(unsigned long u)
91 for (t = u >> 4, wd = 4; t >>= wd, wd *= 2, t; );
95 static void check_signed_range(long i,
96 const struct tvec_irange *ir,
97 struct tvec_state *tv)
99 if (ir && (ir->min > i || i > ir->max))
100 tvec_error(tv, "integer %ld out of range (must be in [%ld .. %ld])",
101 i, ir->min, ir->max);
104 static void check_unsigned_range(unsigned long u,
105 const struct tvec_urange *ur,
106 struct tvec_state *tv)
108 if (ur && (ur->min > u || u > ur->max))
109 tvec_error(tv, "integer %lu out of range (must be in [%lu .. %lu])",
110 u, ur->min, ur->max);
113 static void parse_signed(long *i_out, const char *p,
114 const struct tvec_irange *ir,
115 struct tvec_state *tv)
117 char *q; const char *pp;
121 olderr = errno; errno = 0;
122 pp = p; if (*pp == '-' || *pp == '+') pp++;
123 if (!ISDIGIT(*pp)) tvec_syntax(tv, *pp, "signed integer");
124 i = strtol(p, &q, 0);
125 if (*q && !ISSPACE(*q)) tvec_syntax(tv, *q, "end-of-line");
126 if (errno) tvec_error(tv, "invalid integer `%s'", p);
127 check_signed_range(i, ir, tv);
128 errno = olderr; *i_out = i;
131 static void parse_unsigned(unsigned long *u_out, const char *p,
132 const struct tvec_urange *ur,
133 struct tvec_state *tv)
139 olderr = errno; errno = 0;
140 if (!ISDIGIT(*p)) tvec_syntax(tv, *p, "unsigned integer");
141 u = strtoul(p, &q, 0);
142 if (*q && !ISSPACE(*q)) tvec_syntax(tv, *q, "end-of-line");
143 if (errno) tvec_error(tv, "invalid integer `%s'", p);
144 check_unsigned_range(u, ur, tv);
145 errno = olderr; *u_out = u;
148 static int convert_hex(char ch, int *v_out)
150 if ('0' <= ch && ch <= '9') { *v_out = ch - '0'; return (0); }
151 else if ('a' <= ch && ch <= 'f') { *v_out = ch - 'a' + 10; return (0); }
152 else if ('A' <= ch && ch <= 'F') { *v_out = ch - 'A' + 10; return (0); }
156 static void read_quoted_string(dstr *d, int quote, struct tvec_state *tv)
163 sprintf(expect, "`%c'", quote);
170 tvec_syntax(tv, ch, expect);
173 if (quote == '\'') goto ordinary;
176 case EOF: tvec_syntax(tv, ch, expect);
177 case '\n': tv->lno++; break;
178 case '\'': DPUTC(d, '\''); break;
179 case '\\': DPUTC(d, '\\'); break;
180 case '"': DPUTC(d, '"'); break;
181 case 'a': DPUTC(d, '\a'); break;
182 case 'b': DPUTC(d, '\b'); break;
183 case 'e': DPUTC(d, '\x1b'); break;
184 case 'f': DPUTC(d, '\f'); break;
185 case 'n': DPUTC(d, '\n'); break;
186 case 'r': DPUTC(d, '\r'); break;
187 case 't': DPUTC(d, '\t'); break;
188 case 'v': DPUTC(d, '\v'); break;
192 if (ch == '{') { f |= f_brace; ch = getc(tv->fp); }
194 if (convert_hex(ch, &esc)) tvec_syntax(tv, ch, "hex digit");
196 ch = getc(tv->fp); if (convert_hex(ch, &i)) break;
199 tvec_error(tv, "character code %d out of range", esc);
202 if (!(f&f_brace)) goto reinsert;
203 else if (ch != '}') tvec_syntax(tv, ch, "`}'");
207 if ('0' <= ch && ch < '8') {
208 i = 1; esc = ch - '0';
211 if (i > 3 || '0' > ch || ch >= '8') break;
212 esc = 8*esc + ch - '0'; i++;
215 tvec_error(tv, "character code %d out of range", esc);
219 tvec_syntax(tv, ch, "string escape");
225 if (ch == quote) goto end;
238 enum { TVCODE_BARE, TVCODE_HEX, TVCODE_BASE64, TVCODE_BASE32 };
240 static int collect_bare(dstr *d, struct tvec_state *tv)
243 enum { WORD, SPACE, ESCAPE }; unsigned s = WORD;
252 if (s == ESCAPE) { tv->lno++; goto addch; }
253 if (s == WORD) pos = d->len;
254 ungetc(ch, tv->fp); if (tvec_nexttoken(tv)) { rc = -1; goto done; }
255 DPUTC(d, ' '); s = SPACE;
257 case '"': case '\'': case '!':
258 if (s == SPACE) { ungetc(ch, tv->fp); rc = 0; goto done; }
264 if (s != ESCAPE && isspace(ch)) {
265 if (s == WORD) pos = d->len;
266 DPUTC(d, ch); s = SPACE;
270 DPUTC(d, ch); s = WORD;
275 if (s == SPACE) d->len = pos;
276 DPUTZ(d); return (rc);
279 tvec_syntax(tv, ch, "bareword");
282 static void set_up_encoding(const codec_class **ccl_out, unsigned *f_out,
287 *ccl_out = 0; *f_out = 0;
290 *ccl_out = &hex_class; *f_out = CDCF_IGNCASE;
293 *ccl_out = &base32_class; *f_out = CDCF_IGNCASE | CDCF_IGNEQPAD;
296 *ccl_out = &base64_class; *f_out = CDCF_IGNEQPAD;
303 static void read_compound_string(void **p_inout, size_t *sz_inout,
304 unsigned code, struct tvec_state *tv)
306 const codec_class *ccl; unsigned f;
308 dstr d = DSTR_INIT, w = DSTR_INIT;
312 set_up_encoding(&ccl, &f, code);
313 if (tvec_nexttoken(tv)) tvec_syntax(tv, fgetc(tv->fp), "string");
316 if (ch == '"' || ch == '\'')
317 read_quoted_string(&d, ch, tv);
318 else if (ch == '!') {
320 DRESET(&w); tvec_readword(tv, &w, ";", "`!'-keyword");
321 if (STRCMP(w.buf, ==, "!bare")) code = TVCODE_BARE;
322 else if (STRCMP(w.buf, ==, "!hex")) code = TVCODE_HEX;
323 else if (STRCMP(w.buf, ==, "!base32")) code = TVCODE_BASE32;
324 else if (STRCMP(w.buf, ==, "!base64")) code = TVCODE_BASE64;
325 else tvec_error(tv, "unknown string keyword `%s'", w.buf);
326 set_up_encoding(&ccl, &f, code);
330 tvec_readword(tv, &w, ";", "%s-encoded fragment", ccl->name);
331 cdc = ccl->decoder(f);
332 err = cdc->ops->code(cdc, w.buf, w.len, &d);
333 if (!err) err = cdc->ops->code(cdc, 0, 0, &d);
335 tvec_error(tv, "invalid %s fragment `%s': %s",
336 ccl->name, w.buf, codec_strerror(err));
337 cdc->ops->destroy(cdc);
338 } else switch (code) {
341 if (collect_bare(&d, tv)) goto done;
346 } while (!tvec_nexttoken(tv));
349 if (*sz_inout <= d.len)
350 { xfree(*p_inout); *p_inout = xmalloc(d.len + 1); }
351 p = *p_inout; memcpy(p, d.buf, d.len); p[d.len] = 0; *sz_inout = d.len;
352 dstr_destroy(&d); dstr_destroy(&w);
355 /*----- Skeleton ----------------------------------------------------------*/
357 static void init_...(union tvec_regval *rv, const struct tvec_regdef *rd)
358 static void release_...(union tvec_regval *rv, const struct tvec_regdef *rd)
359 static int eq_...(const union tvec_regval *rv0, const union tvec_regval *rv1,
360 const struct tvec_regdef *rd)
361 static size_t measure_...(const union tvec_regval *rv,
362 const struct tvec_regdef *rd)
363 static int tobuf_...(buf *b, const union tvec_regval *rv,
364 const struct tvec_regdef *rd)
365 static int frombuf_...(buf *b, union tvec_regval *rv,
366 const struct tvec_regdef *rd)
367 static void parse_...(union tvec_regval *rv, const struct tvec_regdef *rd,
368 struct tvec_state *tv)
369 static void dump_...(const union tvec_regval *rv,
370 const struct tvec_regdef *rd,
371 struct tvec_state *tv, unsigned style)
373 const struct tvec_regty tvty_... = {
374 init_..., release_..., eq_..., measure_...,
375 tobuf_..., frombuf_...,
379 /*----- Signed and unsigned integer types ---------------------------------*/
381 static void init_int(union tvec_regval *rv, const struct tvec_regdef *rd)
384 static void init_uint(union tvec_regval *rv, const struct tvec_regdef *rd)
387 static void release_int(union tvec_regval *rv, const struct tvec_regdef *rd)
390 static int eq_int(const union tvec_regval *rv0, const union tvec_regval *rv1,
391 const struct tvec_regdef *rd)
392 { return (rv0->i == rv1->i); }
394 static int eq_uint(const union tvec_regval *rv0,
395 const union tvec_regval *rv1,
396 const struct tvec_regdef *rd)
397 { return (rv0->u == rv1->u); }
399 static size_t measure_int(const union tvec_regval *rv,
400 const struct tvec_regdef *rd)
403 static int tobuf_int(buf *b, const union tvec_regval *rv,
404 const struct tvec_regdef *rd)
405 { return (signed_to_buf(b, rv->i)); }
407 static int tobuf_uint(buf *b, const union tvec_regval *rv,
408 const struct tvec_regdef *rd)
409 { return (unsigned_to_buf(b, rv->u)); }
411 static int frombuf_int(buf *b, union tvec_regval *rv,
412 const struct tvec_regdef *rd)
413 { return signed_from_buf(b, &rv->i); }
415 static int frombuf_uint(buf *b, union tvec_regval *rv,
416 const struct tvec_regdef *rd)
417 { return (unsigned_from_buf(b, &rv->u)); }
419 static void parse_int(union tvec_regval *rv, const struct tvec_regdef *rd,
420 struct tvec_state *tv)
424 tvec_readword(tv, &d, ";", "signed integer");
425 parse_signed(&rv->i, d.buf, rd->arg.p, tv);
426 tvec_flushtoeol(tv, 0);
430 static void parse_uint(union tvec_regval *rv, const struct tvec_regdef *rd,
431 struct tvec_state *tv)
435 tvec_readword(tv, &d, ";", "unsigned integer");
436 parse_unsigned(&rv->u, d.buf, rd->arg.p, tv);
437 tvec_flushtoeol(tv, 0);
441 static void dump_int(const union tvec_regval *rv,
442 const struct tvec_regdef *rd,
443 struct tvec_state *tv, unsigned style)
447 tvec_write(tv, "%ld", rv->i);
448 if (!(style&TVSF_COMPACT)) {
449 if (rv->i >= 0) u = rv->i;
450 else u = -(unsigned long)rv->i;
451 tvec_write(tv, " ; = %s0x%0*lx", rv->i < 0 ? "-" : "", hex_width(u), u);
455 static void dump_uint(const union tvec_regval *rv,
456 const struct tvec_regdef *rd,
457 struct tvec_state *tv, unsigned style)
459 tvec_write(tv, "%lu", rv->u);
460 if (!(style&TVSF_COMPACT))
461 tvec_write(tv, " ; = 0x%0*lx", hex_width(rv->u), rv->u);
464 const struct tvec_regty tvty_int = {
465 init_int, release_int, eq_int, measure_int,
466 tobuf_int, frombuf_int,
470 const struct tvec_irange
471 tvrange_schar = { SCHAR_MIN, SCHAR_MAX },
472 tvrange_short = { SHRT_MIN, SHRT_MAX },
473 tvrange_int = { INT_MIN, INT_MAX },
474 tvrange_long = { LONG_MIN, LONG_MAX },
475 tvrange_sbyte = { -128, 127 },
476 tvrange_i16 = { -32768, +32767 },
477 tvrange_i32 = { -2147483648, 2147483647 };
479 const struct tvec_regty tvty_uint = {
480 init_uint, release_int, eq_uint, measure_int,
481 tobuf_uint, frombuf_uint,
482 parse_uint, dump_uint
485 const struct tvec_urange
486 tvrange_uchar = { 0, UCHAR_MAX },
487 tvrange_ushort = { 0, USHRT_MAX },
488 tvrange_uint = { 0, UINT_MAX },
489 tvrange_ulong = { 0, ULONG_MAX },
490 tvrange_size = { 0, (size_t)-1 },
491 tvrange_byte = { 0, 255 },
492 tvrange_u16 = { 0, 65535 },
493 tvrange_u32 = { 0, 4294967296 };
495 int tvec_claimeq_int(struct tvec_state *tv, long i0, long i1,
496 const char *file, unsigned lno, const char *expr)
498 tv->in[0].v.i = i0; tv->out[0].v.i = i1;
499 return (tvec_claimeq(tv, &tvty_int, 0, file, lno, expr));
502 int tvec_claimeq_uint(struct tvec_state *tv,
503 unsigned long u0, unsigned long u1,
504 const char *file, unsigned lno, const char *expr)
506 tv->in[0].v.u = u0; tv->out[0].v.u = u1;
507 return (tvec_claimeq(tv, &tvty_uint, 0, file, lno, expr));
510 /*----- Enumerations ------------------------------------------------------*/
512 static void init_enum(union tvec_regval *rv, const struct tvec_regdef *rd)
514 const struct tvec_enuminfo *ei = rd->arg.p;
517 #define CASE(tag, ty, slot) \
518 case TVMISC_##tag: rv->slot = 0; break;
525 static int eq_enum(const union tvec_regval *rv0,
526 const union tvec_regval *rv1,
527 const struct tvec_regdef *rd)
529 const struct tvec_enuminfo *ei = rd->arg.p;
532 #define CASE(tag, ty, slot) \
533 case TVMISC_##tag: return (rv0->slot == rv1->slot);
540 static int tobuf_enum(buf *b, const union tvec_regval *rv,
541 const struct tvec_regdef *rd)
543 const struct tvec_enuminfo *ei = rd->arg.p;
546 #define CASE(tag, ty, slot) \
547 case TVMISC_##tag: return (HANDLE_##tag);
548 #define HANDLE_INT signed_to_buf(b, rv->i)
549 #define HANDLE_UINT unsigned_to_buf(b, rv->u)
550 #define HANDLE_PTR -1
561 static int frombuf_enum(buf *b, union tvec_regval *rv,
562 const struct tvec_regdef *rd)
564 const struct tvec_enuminfo *ei = rd->arg.p;
567 #define CASE(tag, ty, slot) \
568 case TVMISC_##tag: return (HANDLE_##tag);
569 #define HANDLE_INT signed_from_buf(b, &rv->i)
570 #define HANDLE_UINT unsigned_from_buf(b, &rv->u)
571 #define HANDLE_PTR -1
581 static void parse_enum(union tvec_regval *rv, const struct tvec_regdef *rd,
582 struct tvec_state *tv)
584 const struct tvec_enuminfo *ei = rd->arg.p;
585 #define DECLS(tag, ty, slot) \
586 const struct tvec_##slot##assoc *slot##a;
587 TVEC_MISCSLOTS(DECLS)
591 tvec_readword(tv, &d, ";", "enumeration tag or literal integer");
593 #define CASE(tag_, ty, slot) \
594 case TVMISC_##tag_: \
595 for (slot##a = ei->u.slot.av; slot##a->tag; slot##a++) \
596 if (STRCMP(d.buf, ==, slot##a->tag)) \
597 { rv->slot = FETCH_##tag_; goto end; }
598 #define FETCH_INT (ia->i)
599 #define FETCH_UINT (ua->u)
600 #define FETCH_PTR ((/*unconst*/ void *)(pa->p))
609 #define CASE(tag, ty, slot) \
610 case TVMISC_##tag: HANDLE_##tag goto end;
611 #define HANDLE_INT parse_signed(&rv->i, d.buf, ei->u.i.ir, tv);
612 #define HANDLE_UINT parse_unsigned(&rv->u, d.buf, ei->u.u.ur, tv);
613 #define HANDLE_PTR if (STRCMP(d.buf, ==, "#nil")) rv->p = 0; \
621 tvec_error(tv, "unknown `%s' value `%s'", ei->name, d.buf);
625 tvec_flushtoeol(tv, 0);
629 static void dump_enum(const union tvec_regval *rv,
630 const struct tvec_regdef *rd,
631 struct tvec_state *tv, unsigned style)
633 const struct tvec_enuminfo *ei = rd->arg.p;
634 #define DECLS(tag, ty, slot) \
635 const struct tvec_##slot##assoc *slot##a;
636 TVEC_MISCSLOTS(DECLS)
644 #define CASE(tag_, ty, slot) \
645 case TVMISC_##tag_: \
646 for (slot##a = ei->u.slot.av; slot##a->tag; slot##a++) \
647 if (rv->slot == slot##a->slot) \
648 { tag = slot##a->tag; goto found; } \
658 tvec_write(tv, "%s", tag);
659 if (style&TVSF_COMPACT) return;
660 tvec_write(tv, " ; = ");
664 #define CASE(tag, ty, slot) \
665 case TVMISC_##tag: HANDLE_##tag break;
666 #define HANDLE_INT tvec_write(tv, "%ld", rv->i);
667 #define HANDLE_UINT tvec_write(tv, "%lu", rv->u);
668 #define HANDLE_PTR if (!rv->p) tvec_write(tv, "#nil"); \
669 else tvec_write(tv, "#<%s %p>", ei->name, rv->p);
679 if (!(f&f_known)) tvec_write(tv, " ;");
680 if (rv->i >= 0) u = rv->i;
681 else u = -(unsigned long)rv->i;
682 tvec_write(tv, " = %s0x%0*lx", rv->i < 0 ? "-" : "", hex_width(u), u);
685 if (!(f&f_known)) tvec_write(tv, " ;");
686 tvec_write(tv, " = 0x%0*lx", hex_width(rv->u), rv->u);
691 const struct tvec_regty tvty_enum = {
692 init_enum, release_int, eq_enum, measure_int,
693 tobuf_enum, frombuf_enum,
694 parse_enum, dump_enum
697 #define DEFCLAIM(tag, ty, slot) \
698 int tvec_claimeq_##slot##enum(struct tvec_state *tv, \
699 const struct tvec_enuminfo *ei, \
701 const char *file, unsigned lno, \
704 union tvec_misc arg; \
706 assert(ei->mv == TVMISC_##tag); \
708 tv->in[0].v.slot = GET_##tag(e0); \
709 tv->out[0].v.slot = GET_##tag(e1); \
710 return (tvec_claimeq(tv, &tvty_enum, &arg, file, lno, expr)); \
712 #define GET_INT(e) (e)
713 #define GET_UINT(e) (e)
714 #define GET_PTR(e) ((/*unconst*/ void *)(e))
715 TVEC_MISCSLOTS(DEFCLAIM)
721 /*----- Flag types --------------------------------------------------------*/
723 static void parse_flags(union tvec_regval *rv, const struct tvec_regdef *rd,
724 struct tvec_state *tv)
726 const struct tvec_flaginfo *fi = rd->arg.p;
727 const struct tvec_flag *f;
728 unsigned long m = 0, v = 0, t;
733 DRESET(&d); tvec_readword(tv, &d, "|;", "flag name or integer");
735 for (f = fi->fv; f->tag; f++)
736 if (STRCMP(f->tag, ==, d.buf)) {
737 if (m&f->m) tvec_error(tv, "colliding flag setting");
738 else { m |= f->m; v |= f->v; goto next; }
741 parse_unsigned(&t, d.buf, fi->range, tv); v |= t;
743 if (tvec_nexttoken(tv)) break;
744 ch = getc(tv->fp); if (ch != '|') tvec_syntax(tv, ch, "`|'");
745 if (tvec_nexttoken(tv)) tvec_syntax(tv, '\n', "flag name or integer");
750 static void dump_flags(const union tvec_regval *rv,
751 const struct tvec_regdef *rd,
752 struct tvec_state *tv, unsigned style)
754 const struct tvec_flaginfo *fi = rd->arg.p;
755 const struct tvec_flag *f;
756 unsigned long m = ~(unsigned long)0, v = rv->u;
759 for (f = fi->fv, sep = ""; f->tag; f++)
760 if ((m&f->m) && (v&f->m) == f->v) {
761 tvec_write(tv, "%s%s", sep, f->tag); m &= ~f->m;
762 sep = style&TVSF_COMPACT ? "|" : " | ";
765 if (v&m) tvec_write(tv, "%s0x%0*lx", sep, hex_width(v), v&m);
767 if (!(style&TVSF_COMPACT))
768 tvec_write(tv, " ; = 0x%0*lx", hex_width(rv->u), rv->u);
771 const struct tvec_regty tvty_flags = {
772 init_uint, release_int, eq_uint, measure_int,
773 tobuf_uint, frombuf_uint,
774 parse_flags, dump_flags
777 int tvec_claimeq_flags(struct tvec_state *tv,
778 const struct tvec_flaginfo *fi,
779 unsigned long f0, unsigned long f1,
780 const char *file, unsigned lno, const char *expr)
784 arg.p = fi; tv->in[0].v.u = f0; tv->out[0].v.u = f1;
785 return (tvec_claimeq(tv, &tvty_flags, &arg, file, lno, expr));
788 /*----- Text and byte strings ---------------------------------------------*/
790 void tvec_allocstring(union tvec_regval *rv, size_t sz)
792 if (rv->str.sz < sz) { xfree(rv->str.p); rv->str.p = xmalloc(sz); }
796 void tvec_allocbytes(union tvec_regval *rv, size_t sz)
798 if (rv->bytes.sz < sz) { xfree(rv->bytes.p); rv->bytes.p = xmalloc(sz); }
802 static void init_string(union tvec_regval *rv, const struct tvec_regdef *rd)
803 { rv->str.p = 0; rv->str.sz = 0; }
805 static void init_bytes(union tvec_regval *rv, const struct tvec_regdef *rd)
806 { rv->bytes.p = 0; rv->bytes.sz = 0; }
808 static void release_string(union tvec_regval *rv,
809 const struct tvec_regdef *rd)
810 { xfree(rv->str.p); }
812 static void release_bytes(union tvec_regval *rv,
813 const struct tvec_regdef *rd)
814 { xfree(rv->bytes.p); }
816 static int eq_string(const union tvec_regval *rv0,
817 const union tvec_regval *rv1,
818 const struct tvec_regdef *rd)
820 return (rv0->str.sz == rv1->str.sz &&
822 MEMCMP(rv0->str.p, ==, rv1->str.p, rv1->str.sz)));
825 static int eq_bytes(const union tvec_regval *rv0,
826 const union tvec_regval *rv1,
827 const struct tvec_regdef *rd)
829 return (rv0->bytes.sz == rv1->bytes.sz &&
831 MEMCMP(rv0->bytes.p, ==, rv1->bytes.p, rv1->bytes.sz)));
834 static size_t measure_string(const union tvec_regval *rv,
835 const struct tvec_regdef *rd)
836 { return (rv->str.sz + 4); }
838 static size_t measure_bytes(const union tvec_regval *rv,
839 const struct tvec_regdef *rd)
840 { return (rv->bytes.sz + 4); }
842 static int tobuf_string(buf *b, const union tvec_regval *rv,
843 const struct tvec_regdef *rd)
844 { return (buf_putmem32l(b, rv->str.p, rv->str.sz)); }
846 static int tobuf_bytes(buf *b, const union tvec_regval *rv,
847 const struct tvec_regdef *rd)
848 { return (buf_putmem32l(b, rv->bytes.p, rv->bytes.sz)); }
850 static int frombuf_string(buf *b, union tvec_regval *rv,
851 const struct tvec_regdef *rd)
856 p = buf_getmem32l(b, &sz); if (!p) return (-1);
857 tvec_allocstring(rv, sz); memcpy(rv->str.p, p, sz);
861 static int frombuf_bytes(buf *b, union tvec_regval *rv,
862 const struct tvec_regdef *rd)
867 p = buf_getmem32l(b, &sz); if (!p) return (-1);
868 tvec_allocbytes(rv, sz); memcpy(rv->bytes.p, p, sz);
872 static void check_string_length(size_t sz, const struct tvec_urange *ur,
873 struct tvec_state *tv)
875 if (ur && (ur->min > sz || sz > ur->max))
876 tvec_error(tv, "invalid string length %lu; must be in [%lu..%lu]",
877 (unsigned long)sz, ur->min, ur->max);
880 static void parse_string(union tvec_regval *rv, const struct tvec_regdef *rd,
881 struct tvec_state *tv)
885 read_compound_string(&p, &rv->str.sz, TVCODE_BARE, tv); rv->str.p = p;
886 check_string_length(rv->str.sz, rd->arg.p, tv);
889 static void parse_bytes(union tvec_regval *rv, const struct tvec_regdef *rd,
890 struct tvec_state *tv)
892 void *p = rv->bytes.p;
894 read_compound_string(&p, &rv->bytes.sz, TVCODE_HEX, tv); rv->bytes.p = p;
895 check_string_length(rv->bytes.sz, rd->arg.p, tv);
898 static void dump_string(const union tvec_regval *rv,
899 const struct tvec_regdef *rd,
900 struct tvec_state *tv, unsigned style)
902 const unsigned char *p, *q, *l;
908 if (!rv->str.sz) { tvec_write(tv, "\"\""); return; }
910 p = (const unsigned char *)rv->str.p; l = p + rv->str.sz;
911 if (*p == '!' || *p == ';' || *p == '"' || *p == '\'') goto quote;
912 for (q = p; q < l; q++)
913 if (*q == '\n' && q != l - 1) f |= f_newline;
914 else if (!*q || !isgraph(*q) || *q == '\\') f |= f_nonword;
915 if (f&f_newline) { tvec_write(tv, "\n\t"); goto quote; }
916 else if (f&f_nonword) goto quote;
917 tv->output->ops->write(tv->output, (const char *)p, rv->str.sz); return;
920 tvec_write(tv, "\"");
921 for (q = p; q < l; q++)
923 case '"': case '\\': ch = *q; goto escape;
924 case '\a': ch = 'a'; goto escape;
925 case '\b': ch = 'b'; goto escape;
926 case '\x1b': ch = 'e'; goto escape;
927 case '\f': ch = 'f'; goto escape;
928 case '\r': ch = 'r'; goto escape;
929 case '\t': ch = 't'; goto escape;
930 case '\v': ch = 'v'; goto escape;
933 tv->output->ops->write(tv->output, (const char *)p, q - p);
934 tvec_write(tv, "\\%c", ch); p = q + 1;
939 tv->output->ops->write(tv->output, (const char *)p, q - p);
940 tvec_write(tv, "\\n"); p = q + 1;
941 if (!(style&TVSF_COMPACT) && q < l) tvec_write(tv, "\"\t\"");
945 if (isprint(*q)) break;
947 tv->output->ops->write(tv->output, (const char *)p, q - p);
948 tvec_write(tv, "\\x{%0*x}", hex_width(UCHAR_MAX), *q); p = q + 1;
951 if (p < q) tv->output->ops->write(tv->output, (const char *)p, q - p);
952 tvec_write(tv, "\"");
958 static void dump_bytes(const union tvec_regval *rv,
959 const struct tvec_regdef *rd,
960 struct tvec_state *tv, unsigned style)
962 const unsigned char *p = rv->bytes.p, *l = p + rv->bytes.sz;
963 size_t off, sz = rv->bytes.sz;
968 tvec_write(tv, style&TVSF_COMPACT ? "\"\"" : "\"\" ; empty");
972 if (style&TVSF_COMPACT) {
973 while (p < l) tvec_write(tv, "%02x", *p++);
977 if (sz > 16) tvec_write(tv, "\n\t");
979 off = 0; wd = hex_width(sz);
981 if (l - p < 16) n = l - p;
984 for (i = 0; i < 16; i++) {
985 if (i < n) tvec_write(tv, "%02x", p[i]);
986 else tvec_write(tv, " ");
987 if (i%4 == 3) tvec_write(tv, " ");
989 tvec_write(tv, " ; ");
990 if (sz > 16) tvec_write(tv, "[%0*lx] ", wd, (unsigned long)off);
991 for (i = 0; i < n; i++)
992 tvec_write(tv, "%c", isprint(p[i]) ? p[i] : '.');
994 if (p < l) tvec_write(tv, "\n\t");
998 const struct tvec_regty tvty_string = {
999 init_string, release_string, eq_string, measure_string,
1000 tobuf_string, frombuf_string,
1001 parse_string, dump_string
1004 const struct tvec_regty tvty_bytes = {
1005 init_bytes, release_bytes, eq_bytes, measure_bytes,
1006 tobuf_bytes, frombuf_bytes,
1007 parse_bytes, dump_bytes
1010 int tvec_claimeq_string(struct tvec_state *tv,
1011 const char *p0, size_t sz0,
1012 const char *p1, size_t sz1,
1013 const char *file, unsigned lno, const char *expr)
1015 tv->in[0].v.str.p = (/*unconst*/ char *)p0; tv->in[0].v.str.sz = sz0;
1016 tv->out[0].v.str.p =(/*unconst*/ char *) p1; tv->out[0].v.str.sz = sz1;
1017 return (tvec_claimeq(tv, &tvty_string, 0, file, lno, expr));
1020 int tvec_claimeq_strz(struct tvec_state *tv,
1021 const char *p0, const char *p1,
1022 const char *file, unsigned lno, const char *expr)
1024 tv->in[0].v.str.p = (/*unconst*/ char *)p0;
1025 tv->in[0].v.str.sz = strlen(p0);
1026 tv->out[0].v.str.p = (/*unconst*/ char *)p1;
1027 tv->out[0].v.str.sz = strlen(p1);
1028 return (tvec_claimeq(tv, &tvty_string, 0, file, lno, expr));
1031 int tvec_claimeq_bytes(struct tvec_state *tv,
1032 const void *p0, size_t sz0,
1033 const void *p1, size_t sz1,
1034 const char *file, unsigned lno, const char *expr)
1036 tv->in[0].v.bytes.p = (/*unconst*/ void *)p0;
1037 tv->in[0].v.bytes.sz = sz0;
1038 tv->out[0].v.bytes.p = (/*unconst*/ void *)p1;
1039 tv->out[0].v.bytes.sz = sz1;
1040 return (tvec_claimeq(tv, &tvty_bytes, 0, file, lno, expr));
1043 /*----- Buffer type -------------------------------------------------------*/
1045 static int eq_buffer(const union tvec_regval *rv0,
1046 const union tvec_regval *rv1,
1047 const struct tvec_regdef *rd)
1048 { return (rv0->bytes.sz == rv1->bytes.sz); }
1050 static int tobuf_buffer(buf *b, const union tvec_regval *rv,
1051 const struct tvec_regdef *rd)
1052 { return (unsigned_to_buf(b, rv->bytes.sz)); }
1054 static int frombuf_buffer(buf *b, union tvec_regval *rv,
1055 const struct tvec_regdef *rd)
1059 if (unsigned_from_buf(b, &u)) return (-1);
1060 if (u > (size_t)-1) return (-1);
1061 tvec_allocbytes(rv, u); memset(rv->bytes.p, '!', u);
1065 static const char units[] = "kMGTPEZY";
1067 static void parse_buffer(union tvec_regval *rv,
1068 const struct tvec_regdef *rd,
1069 struct tvec_state *tv)
1072 char *q; const char *unit;
1079 tvec_readword(tv, &d, ";", "buffer length");
1080 olderr = errno; errno = 0;
1081 u = strtoul(d.buf, &q, 0);
1082 if (errno) goto bad;
1085 tvec_skipspc(tv); pos = d.len;
1086 if (!tvec_readword(tv, &d, ";", 0)) pos++;
1090 if (u > (size_t)-1) goto rangerr;
1091 for (t = u, unit = units; *unit; unit++) {
1092 if (t > (size_t)-1/1024) f |= f_range;
1094 if (*q == *unit && (!q[1] || q[1] == 'B')) {
1095 if (f&f_range) goto rangerr;
1096 u = t; q += 2; break;
1099 if (*q && *q != ';') goto bad;
1100 check_string_length(u, rd->arg.p, tv);
1102 tvec_flushtoeol(tv, 0);
1103 tvec_allocbytes(rv, u); memset(rv->bytes.p, 0, u);
1104 DDESTROY(&d); return;
1107 tvec_error(tv, "invalid buffer length `%s'", d.buf);
1110 tvec_error(tv, "buffer length `%s' out of range", d.buf);
1115 static void dump_buffer(const union tvec_regval *rv,
1116 const struct tvec_regdef *rd,
1117 struct tvec_state *tv, unsigned style)
1120 unsigned long u = rv->bytes.sz;
1123 tvec_write(tv, "%lu B", u);
1125 for (unit = units, u /= 1024; !(u%1024) && unit[1]; u /= 1024, unit++);
1126 tvec_write(tv, "%lu %cB", u, *unit);
1130 const struct tvec_regty tvty_buffer = {
1131 init_bytes, release_bytes, eq_buffer, measure_int,
1132 tobuf_buffer, frombuf_buffer,
1133 parse_buffer, dump_buffer
1136 /*----- That's all, folks -------------------------------------------------*/