@@@ adjust bench timings
[mLib] / test / tvec-types.c
CommitLineData
b64eb60f
MW
1/* -*-c-*-
2 *
3 * Types for the test-vector framework
4 *
5 * (c) 2023 Straylight/Edgeware
6 */
7
8/*----- Licensing notice --------------------------------------------------*
9 *
10 * This file is part of the mLib utilities library.
11 *
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.
16 *
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.
21 *
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,
25 * USA.
26 */
27
28/*----- Header files ------------------------------------------------------*/
29
30#include <assert.h>
31#include <ctype.h>
32#include <errno.h>
33#include <limits.h>
34#include <stdio.h>
35#include <string.h>
36
37#include "buf.h"
38#include "codec.h"
39# include "base32.h"
40# include "base64.h"
41# include "hex.h"
42#include "dstr.h"
43#include "tvec.h"
44
45/*----- Preliminary utilities ---------------------------------------------*/
46
47static int signed_to_buf(buf *b, long i)
48{
49 kludge64 k;
50 unsigned long u;
51
52 u = i;
53 if (i >= 0) ASSIGN64(k, u);
54 else { ASSIGN64(k, ~u); CPL64(k, k); }
55 return (buf_putk64l(b, k));
56}
57
58static int signed_from_buf(buf *b, long *i_out)
59{
60 kludge64 k, lmax, not_lmin;
61
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);
65 else {
66 CPL64(k, k);
67 if (CMP64(k, <=, not_lmin)) *i_out = -(long)GET64(unsigned long, k) - 1;
68 else return (-1);
69 }
70 return (0);
71}
72
73static int unsigned_to_buf(buf *b, unsigned long u)
74 { kludge64 k; ASSIGN64(k, u); return (buf_putk64l(b, k)); }
75
76static int unsigned_from_buf(buf *b, unsigned long *u_out)
77{
78 kludge64 k, ulmax;
79
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);
84}
85
86static int hex_width(unsigned long u)
87{
88 int wd;
89 unsigned long t;
90
91 for (t = u >> 4, wd = 4; t >>= wd, wd *= 2, t; );
92 return (wd/4);
93}
94
882a39c1
MW
95static int check_signed_range(long i,
96 const struct tvec_irange *ir,
97 struct tvec_state *tv)
b64eb60f 98{
882a39c1 99 if (ir && (ir->min > i || i > ir->max)) {
b64eb60f
MW
100 tvec_error(tv, "integer %ld out of range (must be in [%ld .. %ld])",
101 i, ir->min, ir->max);
882a39c1
MW
102 return (-1);
103 }
104 return (0);
b64eb60f
MW
105}
106
882a39c1
MW
107static int check_unsigned_range(unsigned long u,
108 const struct tvec_urange *ur,
109 struct tvec_state *tv)
b64eb60f 110{
882a39c1 111 if (ur && (ur->min > u || u > ur->max)) {
b64eb60f
MW
112 tvec_error(tv, "integer %lu out of range (must be in [%lu .. %lu])",
113 u, ur->min, ur->max);
882a39c1
MW
114 return (-1);
115 }
116 return (0);
b64eb60f
MW
117}
118
882a39c1
MW
119static int parse_signed(long *i_out, const char *p,
120 const struct tvec_irange *ir,
121 struct tvec_state *tv)
b64eb60f
MW
122{
123 char *q; const char *pp;
124 int olderr;
125 long i;
126
127 olderr = errno; errno = 0;
128 pp = p; if (*pp == '-' || *pp == '+') pp++;
882a39c1 129 if (!ISDIGIT(*pp)) return (tvec_syntax(tv, *pp, "signed integer"));
b64eb60f 130 i = strtol(p, &q, 0);
882a39c1
MW
131 if (*q && !ISSPACE(*q)) return (tvec_syntax(tv, *q, "end-of-line"));
132 if (errno) return (tvec_error(tv, "invalid integer `%s'", p));
b64eb60f
MW
133 check_signed_range(i, ir, tv);
134 errno = olderr; *i_out = i;
882a39c1 135 return (0);
b64eb60f
MW
136}
137
882a39c1
MW
138static int parse_unsigned(unsigned long *u_out, const char *p,
139 const struct tvec_urange *ur,
140 struct tvec_state *tv)
b64eb60f
MW
141{
142 char *q;
143 int olderr;
144 unsigned long u;
145
146 olderr = errno; errno = 0;
882a39c1 147 if (!ISDIGIT(*p)) return (tvec_syntax(tv, *p, "unsigned integer"));
b64eb60f 148 u = strtoul(p, &q, 0);
882a39c1
MW
149 if (*q && !ISSPACE(*q)) return (tvec_syntax(tv, *q, "end-of-line"));
150 if (errno) return (tvec_error(tv, "invalid integer `%s'", p));
b64eb60f
MW
151 check_unsigned_range(u, ur, tv);
152 errno = olderr; *u_out = u;
882a39c1 153 return (0);
b64eb60f
MW
154}
155
156static int convert_hex(char ch, int *v_out)
157{
158 if ('0' <= ch && ch <= '9') { *v_out = ch - '0'; return (0); }
159 else if ('a' <= ch && ch <= 'f') { *v_out = ch - 'a' + 10; return (0); }
160 else if ('A' <= ch && ch <= 'F') { *v_out = ch - 'A' + 10; return (0); }
161 else return (-1);
162}
163
882a39c1 164static int read_quoted_string(dstr *d, int quote, struct tvec_state *tv)
b64eb60f
MW
165{
166 char expect[4];
167 int ch, i, esc;
168 unsigned f = 0;
169#define f_brace 1u
170
171 sprintf(expect, "`%c'", quote);
172
173 for (;;) {
174 ch = getc(tv->fp);
175 reinsert:
176 switch (ch) {
177 case EOF: case '\n':
882a39c1 178 return (tvec_syntax(tv, ch, expect));
b64eb60f
MW
179
180 case '\\':
181 if (quote == '\'') goto ordinary;
182 ch = getc(tv->fp);
183 switch (ch) {
184 case EOF: tvec_syntax(tv, ch, expect);
185 case '\n': tv->lno++; break;
186 case '\'': DPUTC(d, '\''); break;
187 case '\\': DPUTC(d, '\\'); break;
188 case '"': DPUTC(d, '"'); break;
189 case 'a': DPUTC(d, '\a'); break;
190 case 'b': DPUTC(d, '\b'); break;
191 case 'e': DPUTC(d, '\x1b'); break;
192 case 'f': DPUTC(d, '\f'); break;
193 case 'n': DPUTC(d, '\n'); break;
194 case 'r': DPUTC(d, '\r'); break;
195 case 't': DPUTC(d, '\t'); break;
196 case 'v': DPUTC(d, '\v'); break;
197
198 case 'x':
199 ch = getc(tv->fp);
200 if (ch == '{') { f |= f_brace; ch = getc(tv->fp); }
201 else f &= ~f_brace;
882a39c1
MW
202 if (convert_hex(ch, &esc))
203 return (tvec_syntax(tv, ch, "hex digit"));
b64eb60f
MW
204 for (;;) {
205 ch = getc(tv->fp); if (convert_hex(ch, &i)) break;
206 esc = 8*esc + i;
207 if (esc > UCHAR_MAX)
882a39c1
MW
208 return (tvec_error(tv,
209 "character code %d out of range", esc));
b64eb60f
MW
210 }
211 DPUTC(d, esc);
212 if (!(f&f_brace)) goto reinsert;
882a39c1 213 else if (ch != '}') return (tvec_syntax(tv, ch, "`}'"));
b64eb60f
MW
214 break;
215
216 default:
217 if ('0' <= ch && ch < '8') {
218 i = 1; esc = ch - '0';
219 for (;;) {
220 ch = getc(tv->fp);
221 if (i > 3 || '0' > ch || ch >= '8') break;
222 esc = 8*esc + ch - '0'; i++;
223 }
224 if (esc > UCHAR_MAX)
882a39c1
MW
225 return (tvec_error(tv,
226 "character code %d out of range", esc));
b64eb60f
MW
227 DPUTC(d, esc);
228 goto reinsert;
229 }
882a39c1 230 return (tvec_syntax(tv, ch, "string escape"));
b64eb60f
MW
231 }
232 break;
233
234 default:
235 if (ch == quote) goto end;
236 ordinary:
237 DPUTC(d, ch);
238 break;
239 }
240 }
241
242end:
243 DPUTZ(d);
882a39c1 244 return (0);
b64eb60f
MW
245
246#undef f_brace
247}
248
249enum { TVCODE_BARE, TVCODE_HEX, TVCODE_BASE64, TVCODE_BASE32 };
250
251static int collect_bare(dstr *d, struct tvec_state *tv)
252{
253 size_t pos = d->len;
254 enum { WORD, SPACE, ESCAPE }; unsigned s = WORD;
255 int ch, rc;
256
257 for (;;) {
258 ch = getc(tv->fp);
259 switch (ch) {
260 case EOF:
882a39c1
MW
261 tvec_syntax(tv, ch, "bareword");
262 rc = -1; goto end;
b64eb60f
MW
263 case '\n':
264 if (s == ESCAPE) { tv->lno++; goto addch; }
265 if (s == WORD) pos = d->len;
882a39c1 266 ungetc(ch, tv->fp); if (tvec_nexttoken(tv)) { rc = -1; goto end; }
b64eb60f
MW
267 DPUTC(d, ' '); s = SPACE;
268 break;
269 case '"': case '\'': case '!':
882a39c1 270 if (s == SPACE) { ungetc(ch, tv->fp); goto done; }
b64eb60f
MW
271 goto addch;
272 case '\\':
273 s = ESCAPE;
274 break;
275 default:
276 if (s != ESCAPE && isspace(ch)) {
277 if (s == WORD) pos = d->len;
278 DPUTC(d, ch); s = SPACE;
279 break;
280 }
281 addch:
282 DPUTC(d, ch); s = WORD;
283 }
284 }
285
286done:
287 if (s == SPACE) d->len = pos;
882a39c1
MW
288 DPUTZ(d); rc = 0;
289end:
290 return (rc);
b64eb60f
MW
291}
292
293static void set_up_encoding(const codec_class **ccl_out, unsigned *f_out,
294 unsigned code)
295{
296 switch (code) {
297 case TVCODE_BARE:
298 *ccl_out = 0; *f_out = 0;
299 break;
300 case TVCODE_HEX:
301 *ccl_out = &hex_class; *f_out = CDCF_IGNCASE;
302 break;
303 case TVCODE_BASE32:
304 *ccl_out = &base32_class; *f_out = CDCF_IGNCASE | CDCF_IGNEQPAD;
305 break;
306 case TVCODE_BASE64:
307 *ccl_out = &base64_class; *f_out = CDCF_IGNEQPAD;
308 break;
309 default:
310 abort();
311 }
312}
313
882a39c1
MW
314static int read_compound_string(void **p_inout, size_t *sz_inout,
315 unsigned code, struct tvec_state *tv)
b64eb60f
MW
316{
317 const codec_class *ccl; unsigned f;
318 codec *cdc;
319 dstr d = DSTR_INIT, w = DSTR_INIT;
320 char *p;
882a39c1 321 int ch, err, rc;
b64eb60f
MW
322
323 set_up_encoding(&ccl, &f, code);
324 if (tvec_nexttoken(tv)) tvec_syntax(tv, fgetc(tv->fp), "string");
325 do {
326 ch = getc(tv->fp);
327 if (ch == '"' || ch == '\'')
882a39c1 328 { if (read_quoted_string(&d, ch, tv)) { rc = -1; goto end; } }
b64eb60f
MW
329 else if (ch == '!') {
330 ungetc(ch, tv->fp);
331 DRESET(&w); tvec_readword(tv, &w, ";", "`!'-keyword");
332 if (STRCMP(w.buf, ==, "!bare")) code = TVCODE_BARE;
333 else if (STRCMP(w.buf, ==, "!hex")) code = TVCODE_HEX;
334 else if (STRCMP(w.buf, ==, "!base32")) code = TVCODE_BASE32;
335 else if (STRCMP(w.buf, ==, "!base64")) code = TVCODE_BASE64;
882a39c1
MW
336 else {
337 tvec_error(tv, "unknown string keyword `%s'", w.buf);
338 rc = -1; goto end;
339 }
b64eb60f
MW
340 set_up_encoding(&ccl, &f, code);
341 } else if (ccl) {
342 ungetc(ch, tv->fp);
343 DRESET(&w);
882a39c1
MW
344 if (tvec_readword(tv, &w, ";", "%s-encoded fragment", ccl->name))
345 { rc = -1; goto end; }
b64eb60f
MW
346 cdc = ccl->decoder(f);
347 err = cdc->ops->code(cdc, w.buf, w.len, &d);
348 if (!err) err = cdc->ops->code(cdc, 0, 0, &d);
882a39c1 349 if (err) {
b64eb60f
MW
350 tvec_error(tv, "invalid %s fragment `%s': %s",
351 ccl->name, w.buf, codec_strerror(err));
882a39c1
MW
352 rc = -1; goto end;
353 }
b64eb60f
MW
354 cdc->ops->destroy(cdc);
355 } else switch (code) {
356 case TVCODE_BARE:
357 ungetc(ch, tv->fp);
358 if (collect_bare(&d, tv)) goto done;
359 break;
360 default:
361 abort();
362 }
363 } while (!tvec_nexttoken(tv));
364
365done:
366 if (*sz_inout <= d.len)
367 { xfree(*p_inout); *p_inout = xmalloc(d.len + 1); }
368 p = *p_inout; memcpy(p, d.buf, d.len); p[d.len] = 0; *sz_inout = d.len;
882a39c1
MW
369 rc = 0;
370end:
b64eb60f 371 dstr_destroy(&d); dstr_destroy(&w);
882a39c1 372 return (rc);
b64eb60f
MW
373}
374
375/*----- Skeleton ----------------------------------------------------------*/
376/*
377static void init_...(union tvec_regval *rv, const struct tvec_regdef *rd)
378static void release_...(union tvec_regval *rv, const struct tvec_regdef *rd)
379static int eq_...(const union tvec_regval *rv0, const union tvec_regval *rv1,
380 const struct tvec_regdef *rd)
381static size_t measure_...(const union tvec_regval *rv,
382 const struct tvec_regdef *rd)
383static int tobuf_...(buf *b, const union tvec_regval *rv,
384 const struct tvec_regdef *rd)
385static int frombuf_...(buf *b, union tvec_regval *rv,
386 const struct tvec_regdef *rd)
387static void parse_...(union tvec_regval *rv, const struct tvec_regdef *rd,
388 struct tvec_state *tv)
389static void dump_...(const union tvec_regval *rv,
390 const struct tvec_regdef *rd,
391 struct tvec_state *tv, unsigned style)
392
393const struct tvec_regty tvty_... = {
394 init_..., release_..., eq_..., measure_...,
395 tobuf_..., frombuf_...,
396 parse_..., dump_...
397};
398*/
399/*----- Signed and unsigned integer types ---------------------------------*/
400
401static void init_int(union tvec_regval *rv, const struct tvec_regdef *rd)
402 { rv->i = 0; }
403
404static void init_uint(union tvec_regval *rv, const struct tvec_regdef *rd)
405 { rv->u = 0; }
406
407static void release_int(union tvec_regval *rv, const struct tvec_regdef *rd)
408 { ; }
409
410static int eq_int(const union tvec_regval *rv0, const union tvec_regval *rv1,
411 const struct tvec_regdef *rd)
412 { return (rv0->i == rv1->i); }
413
414static int eq_uint(const union tvec_regval *rv0,
415 const union tvec_regval *rv1,
416 const struct tvec_regdef *rd)
417 { return (rv0->u == rv1->u); }
418
419static size_t measure_int(const union tvec_regval *rv,
420 const struct tvec_regdef *rd)
421 { return (8); }
422
423static int tobuf_int(buf *b, const union tvec_regval *rv,
424 const struct tvec_regdef *rd)
425 { return (signed_to_buf(b, rv->i)); }
426
427static int tobuf_uint(buf *b, const union tvec_regval *rv,
428 const struct tvec_regdef *rd)
429 { return (unsigned_to_buf(b, rv->u)); }
430
431static int frombuf_int(buf *b, union tvec_regval *rv,
432 const struct tvec_regdef *rd)
882a39c1 433 { return (signed_from_buf(b, &rv->i)); }
b64eb60f
MW
434
435static int frombuf_uint(buf *b, union tvec_regval *rv,
436 const struct tvec_regdef *rd)
437 { return (unsigned_from_buf(b, &rv->u)); }
438
882a39c1
MW
439static int parse_int(union tvec_regval *rv, const struct tvec_regdef *rd,
440 struct tvec_state *tv)
b64eb60f
MW
441{
442 dstr d = DSTR_INIT;
882a39c1 443 int rc;
b64eb60f 444
882a39c1
MW
445 if (tvec_readword(tv, &d, ";", "signed integer")) { rc = -1; goto end; }
446 if (parse_signed(&rv->i, d.buf, rd->arg.p, tv)) { rc = -1; goto end; }
447 if (tvec_flushtoeol(tv, 0)) { rc = -1; goto end; }
448 rc = 0;
449end:
b64eb60f 450 dstr_destroy(&d);
882a39c1 451 return (rc);
b64eb60f
MW
452}
453
882a39c1
MW
454static int parse_uint(union tvec_regval *rv, const struct tvec_regdef *rd,
455 struct tvec_state *tv)
b64eb60f
MW
456{
457 dstr d = DSTR_INIT;
882a39c1 458 int rc;
b64eb60f 459
882a39c1
MW
460 if (tvec_readword(tv, &d, ";", "unsigned integer")) { rc = -1; goto end; }
461 if (parse_unsigned(&rv->u, d.buf, rd->arg.p, tv)) { rc = -1; goto end; }
462 if (tvec_flushtoeol(tv, 0)) { rc = -1; goto end; }
463 rc = 0;
464end:
b64eb60f 465 dstr_destroy(&d);
882a39c1 466 return (rc);
b64eb60f
MW
467}
468
469static void dump_int(const union tvec_regval *rv,
470 const struct tvec_regdef *rd,
471 struct tvec_state *tv, unsigned style)
472{
473 unsigned long u;
474
475 tvec_write(tv, "%ld", rv->i);
476 if (!(style&TVSF_COMPACT)) {
477 if (rv->i >= 0) u = rv->i;
478 else u = -(unsigned long)rv->i;
479 tvec_write(tv, " ; = %s0x%0*lx", rv->i < 0 ? "-" : "", hex_width(u), u);
480 }
481}
482
483static void dump_uint(const union tvec_regval *rv,
484 const struct tvec_regdef *rd,
485 struct tvec_state *tv, unsigned style)
486{
487 tvec_write(tv, "%lu", rv->u);
488 if (!(style&TVSF_COMPACT))
489 tvec_write(tv, " ; = 0x%0*lx", hex_width(rv->u), rv->u);
490}
491
492const struct tvec_regty tvty_int = {
493 init_int, release_int, eq_int, measure_int,
494 tobuf_int, frombuf_int,
495 parse_int, dump_int
496};
497
498const struct tvec_irange
499 tvrange_schar = { SCHAR_MIN, SCHAR_MAX },
500 tvrange_short = { SHRT_MIN, SHRT_MAX },
501 tvrange_int = { INT_MIN, INT_MAX },
502 tvrange_long = { LONG_MIN, LONG_MAX },
503 tvrange_sbyte = { -128, 127 },
504 tvrange_i16 = { -32768, +32767 },
505 tvrange_i32 = { -2147483648, 2147483647 };
506
507const struct tvec_regty tvty_uint = {
508 init_uint, release_int, eq_uint, measure_int,
509 tobuf_uint, frombuf_uint,
510 parse_uint, dump_uint
511};
512
513const struct tvec_urange
514 tvrange_uchar = { 0, UCHAR_MAX },
515 tvrange_ushort = { 0, USHRT_MAX },
516 tvrange_uint = { 0, UINT_MAX },
517 tvrange_ulong = { 0, ULONG_MAX },
518 tvrange_size = { 0, (size_t)-1 },
519 tvrange_byte = { 0, 255 },
520 tvrange_u16 = { 0, 65535 },
521 tvrange_u32 = { 0, 4294967296 };
522
523int tvec_claimeq_int(struct tvec_state *tv, long i0, long i1,
524 const char *file, unsigned lno, const char *expr)
525{
526 tv->in[0].v.i = i0; tv->out[0].v.i = i1;
527 return (tvec_claimeq(tv, &tvty_int, 0, file, lno, expr));
528}
529
530int tvec_claimeq_uint(struct tvec_state *tv,
531 unsigned long u0, unsigned long u1,
532 const char *file, unsigned lno, const char *expr)
533{
534 tv->in[0].v.u = u0; tv->out[0].v.u = u1;
535 return (tvec_claimeq(tv, &tvty_uint, 0, file, lno, expr));
536}
537
538/*----- Enumerations ------------------------------------------------------*/
539
540static void init_enum(union tvec_regval *rv, const struct tvec_regdef *rd)
541{
542 const struct tvec_enuminfo *ei = rd->arg.p;
543
544 switch (ei->mv) {
545#define CASE(tag, ty, slot) \
546 case TVMISC_##tag: rv->slot = 0; break;
547 TVEC_MISCSLOTS(CASE)
548#undef CASE
549 default: abort();
550 }
551}
552
553static int eq_enum(const union tvec_regval *rv0,
554 const union tvec_regval *rv1,
555 const struct tvec_regdef *rd)
556{
557 const struct tvec_enuminfo *ei = rd->arg.p;
558
559 switch (ei->mv) {
560#define CASE(tag, ty, slot) \
561 case TVMISC_##tag: return (rv0->slot == rv1->slot);
562 TVEC_MISCSLOTS(CASE)
563#undef CASE
564 default: abort();
565 }
566}
567
568static int tobuf_enum(buf *b, const union tvec_regval *rv,
569 const struct tvec_regdef *rd)
570{
571 const struct tvec_enuminfo *ei = rd->arg.p;
572
573 switch (ei->mv) {
574#define CASE(tag, ty, slot) \
575 case TVMISC_##tag: return (HANDLE_##tag);
576#define HANDLE_INT signed_to_buf(b, rv->i)
577#define HANDLE_UINT unsigned_to_buf(b, rv->u)
578#define HANDLE_PTR -1
579 TVEC_MISCSLOTS(CASE)
580#undef CASE
581#undef HANDLE_INT
582#undef HANDLE_UINT
583#undef HANDLE_PTR
584 default: abort();
585 }
586 return (0);
587}
588
589static int frombuf_enum(buf *b, union tvec_regval *rv,
590 const struct tvec_regdef *rd)
591{
592 const struct tvec_enuminfo *ei = rd->arg.p;
593
594 switch (ei->mv) {
595#define CASE(tag, ty, slot) \
596 case TVMISC_##tag: return (HANDLE_##tag);
597#define HANDLE_INT signed_from_buf(b, &rv->i)
598#define HANDLE_UINT unsigned_from_buf(b, &rv->u)
599#define HANDLE_PTR -1
600 TVEC_MISCSLOTS(CASE)
601#undef CASE
602#undef HANDLE_INT
603#undef HANDLE_UINT
604#undef HANDLE_PTR
605 default: abort();
606 }
607}
608
882a39c1
MW
609static int parse_enum(union tvec_regval *rv, const struct tvec_regdef *rd,
610 struct tvec_state *tv)
b64eb60f
MW
611{
612 const struct tvec_enuminfo *ei = rd->arg.p;
613#define DECLS(tag, ty, slot) \
614 const struct tvec_##slot##assoc *slot##a;
615 TVEC_MISCSLOTS(DECLS)
616#undef DECLS
617 dstr d = DSTR_INIT;
882a39c1 618 int rc;
b64eb60f 619
882a39c1
MW
620 if (tvec_readword(tv, &d, ";", "enumeration tag or literal integer"))
621 { rc = -1; goto end; }
b64eb60f
MW
622 switch (ei->mv) {
623#define CASE(tag_, ty, slot) \
624 case TVMISC_##tag_: \
625 for (slot##a = ei->u.slot.av; slot##a->tag; slot##a++) \
626 if (STRCMP(d.buf, ==, slot##a->tag)) \
882a39c1 627 { rv->slot = FETCH_##tag_; goto done; }
b64eb60f
MW
628#define FETCH_INT (ia->i)
629#define FETCH_UINT (ua->u)
630#define FETCH_PTR ((/*unconst*/ void *)(pa->p))
631 TVEC_MISCSLOTS(CASE)
632#undef CASE
633#undef FETCH_INT
634#undef FETCH_UINT
635#undef FETCH_PTR
636 }
637
638 switch (ei->mv) {
639#define CASE(tag, ty, slot) \
882a39c1
MW
640 case TVMISC_##tag: HANDLE_##tag goto done;
641#define HANDLE_INT \
642 if (parse_signed(&rv->i, d.buf, ei->u.i.ir, tv)) \
643 { rc = -1; goto end; }
644#define HANDLE_UINT \
645 if (parse_unsigned(&rv->u, d.buf, ei->u.u.ur, tv)) \
646 { rc = -1; goto end; }
647#define HANDLE_PTR \
648 if (STRCMP(d.buf, ==, "#nil")) rv->p = 0; \
649 else goto tagonly;
b64eb60f
MW
650 TVEC_MISCSLOTS(CASE)
651#undef CASE
652#undef HANDLE_INT
653#undef HANDLE_UINT
654#undef HANDLE_PTR
655 default: tagonly:
656 tvec_error(tv, "unknown `%s' value `%s'", ei->name, d.buf);
882a39c1 657 rc = -1; goto end;
b64eb60f
MW
658 }
659
882a39c1
MW
660done:
661 if (tvec_flushtoeol(tv, 0)) { rc = -1; goto end; }
662 rc = 0;
b64eb60f 663end:
b64eb60f 664 dstr_destroy(&d);
882a39c1 665 return (rc);
b64eb60f
MW
666}
667
668static void dump_enum(const union tvec_regval *rv,
669 const struct tvec_regdef *rd,
670 struct tvec_state *tv, unsigned style)
671{
672 const struct tvec_enuminfo *ei = rd->arg.p;
673#define DECLS(tag, ty, slot) \
674 const struct tvec_##slot##assoc *slot##a;
675 TVEC_MISCSLOTS(DECLS)
676#undef DECLS
677 const char *tag;
678 unsigned long u;
679 unsigned f = 0;
680#define f_known 1u
681
682 switch (ei->mv) {
683#define CASE(tag_, ty, slot) \
684 case TVMISC_##tag_: \
685 for (slot##a = ei->u.slot.av; slot##a->tag; slot##a++) \
686 if (rv->slot == slot##a->slot) \
687 { tag = slot##a->tag; goto found; } \
688 break;
689 TVEC_MISCSLOTS(CASE)
690#undef CASE
691 default: abort();
692 }
693 goto print_int;
694
695found:
696 f |= f_known;
697 tvec_write(tv, "%s", tag);
698 if (style&TVSF_COMPACT) return;
699 tvec_write(tv, " ; = ");
700
701print_int:
702 switch (ei->mv) {
703#define CASE(tag, ty, slot) \
704 case TVMISC_##tag: HANDLE_##tag break;
705#define HANDLE_INT tvec_write(tv, "%ld", rv->i);
706#define HANDLE_UINT tvec_write(tv, "%lu", rv->u);
707#define HANDLE_PTR if (!rv->p) tvec_write(tv, "#nil"); \
708 else tvec_write(tv, "#<%s %p>", ei->name, rv->p);
709 TVEC_MISCSLOTS(CASE)
710#undef CASE
711#undef HANDLE_INT
712#undef HANDLE_UINT
713#undef HANDLE_PTR
714 }
715
716 switch (ei->mv) {
717 case TVMISC_INT:
718 if (!(f&f_known)) tvec_write(tv, " ;");
719 if (rv->i >= 0) u = rv->i;
720 else u = -(unsigned long)rv->i;
721 tvec_write(tv, " = %s0x%0*lx", rv->i < 0 ? "-" : "", hex_width(u), u);
722 break;
723 case TVMISC_UINT:
724 if (!(f&f_known)) tvec_write(tv, " ;");
725 tvec_write(tv, " = 0x%0*lx", hex_width(rv->u), rv->u);
726 break;
727 }
728}
729
730const struct tvec_regty tvty_enum = {
731 init_enum, release_int, eq_enum, measure_int,
732 tobuf_enum, frombuf_enum,
733 parse_enum, dump_enum
734};
735
736#define DEFCLAIM(tag, ty, slot) \
737 int tvec_claimeq_##slot##enum(struct tvec_state *tv, \
738 const struct tvec_enuminfo *ei, \
739 ty e0, ty e1, \
740 const char *file, unsigned lno, \
741 const char *expr) \
742 { \
743 union tvec_misc arg; \
744 \
745 assert(ei->mv == TVMISC_##tag); \
746 arg.p = ei; \
747 tv->in[0].v.slot = GET_##tag(e0); \
748 tv->out[0].v.slot = GET_##tag(e1); \
749 return (tvec_claimeq(tv, &tvty_enum, &arg, file, lno, expr)); \
750 }
751#define GET_INT(e) (e)
752#define GET_UINT(e) (e)
753#define GET_PTR(e) ((/*unconst*/ void *)(e))
754TVEC_MISCSLOTS(DEFCLAIM)
755#undef DEFCLAIM
756#undef GET_INT
757#undef GET_UINT
758#undef GET_PTR
759
760/*----- Flag types --------------------------------------------------------*/
761
882a39c1
MW
762static int parse_flags(union tvec_regval *rv, const struct tvec_regdef *rd,
763 struct tvec_state *tv)
b64eb60f
MW
764{
765 const struct tvec_flaginfo *fi = rd->arg.p;
766 const struct tvec_flag *f;
767 unsigned long m = 0, v = 0, t;
768 dstr d = DSTR_INIT;
882a39c1 769 int ch, rc;
b64eb60f
MW
770
771 for (;;) {
882a39c1
MW
772 DRESET(&d);
773 if (tvec_readword(tv, &d, "|;", "flag name or integer"))
774 { rc = -1; goto end; }
b64eb60f
MW
775
776 for (f = fi->fv; f->tag; f++)
777 if (STRCMP(f->tag, ==, d.buf)) {
882a39c1
MW
778 if (m&f->m)
779 { tvec_error(tv, "colliding flag setting"); rc = -1; goto end; }
780 else
781 { m |= f->m; v |= f->v; goto next; }
b64eb60f
MW
782 }
783
882a39c1
MW
784 if (parse_unsigned(&t, d.buf, fi->range, tv)) { rc = -1; goto end; }
785 v |= t;
b64eb60f
MW
786 next:
787 if (tvec_nexttoken(tv)) break;
882a39c1
MW
788 ch = getc(tv->fp);
789 if (ch != '|') { tvec_syntax(tv, ch, "`|'"); rc = -1; goto end; }
790 if (tvec_nexttoken(tv))
791 { tvec_syntax(tv, '\n', "flag name or integer"); rc = -1; goto end; }
b64eb60f
MW
792 }
793 rv->u = v;
882a39c1
MW
794 rc = 0;
795end:
796 dstr_destroy(&d);
797 return (rc);
b64eb60f
MW
798}
799
800static void dump_flags(const union tvec_regval *rv,
801 const struct tvec_regdef *rd,
802 struct tvec_state *tv, unsigned style)
803{
804 const struct tvec_flaginfo *fi = rd->arg.p;
805 const struct tvec_flag *f;
806 unsigned long m = ~(unsigned long)0, v = rv->u;
807 const char *sep;
808
809 for (f = fi->fv, sep = ""; f->tag; f++)
810 if ((m&f->m) && (v&f->m) == f->v) {
811 tvec_write(tv, "%s%s", sep, f->tag); m &= ~f->m;
812 sep = style&TVSF_COMPACT ? "|" : " | ";
813 }
814
815 if (v&m) tvec_write(tv, "%s0x%0*lx", sep, hex_width(v), v&m);
816
817 if (!(style&TVSF_COMPACT))
818 tvec_write(tv, " ; = 0x%0*lx", hex_width(rv->u), rv->u);
819}
820
821const struct tvec_regty tvty_flags = {
822 init_uint, release_int, eq_uint, measure_int,
823 tobuf_uint, frombuf_uint,
824 parse_flags, dump_flags
825};
826
827int tvec_claimeq_flags(struct tvec_state *tv,
828 const struct tvec_flaginfo *fi,
829 unsigned long f0, unsigned long f1,
830 const char *file, unsigned lno, const char *expr)
831{
832 union tvec_misc arg;
833
834 arg.p = fi; tv->in[0].v.u = f0; tv->out[0].v.u = f1;
835 return (tvec_claimeq(tv, &tvty_flags, &arg, file, lno, expr));
836}
837
838/*----- Text and byte strings ---------------------------------------------*/
839
840void tvec_allocstring(union tvec_regval *rv, size_t sz)
841{
842 if (rv->str.sz < sz) { xfree(rv->str.p); rv->str.p = xmalloc(sz); }
843 rv->str.sz = sz;
844}
845
846void tvec_allocbytes(union tvec_regval *rv, size_t sz)
847{
848 if (rv->bytes.sz < sz) { xfree(rv->bytes.p); rv->bytes.p = xmalloc(sz); }
849 rv->bytes.sz = sz;
850}
851
852static void init_string(union tvec_regval *rv, const struct tvec_regdef *rd)
853 { rv->str.p = 0; rv->str.sz = 0; }
854
855static void init_bytes(union tvec_regval *rv, const struct tvec_regdef *rd)
856 { rv->bytes.p = 0; rv->bytes.sz = 0; }
857
858static void release_string(union tvec_regval *rv,
859 const struct tvec_regdef *rd)
860 { xfree(rv->str.p); }
861
862static void release_bytes(union tvec_regval *rv,
863 const struct tvec_regdef *rd)
864 { xfree(rv->bytes.p); }
865
866static int eq_string(const union tvec_regval *rv0,
867 const union tvec_regval *rv1,
868 const struct tvec_regdef *rd)
869{
870 return (rv0->str.sz == rv1->str.sz &&
871 (!rv0->bytes.sz ||
872 MEMCMP(rv0->str.p, ==, rv1->str.p, rv1->str.sz)));
873}
874
875static int eq_bytes(const union tvec_regval *rv0,
876 const union tvec_regval *rv1,
877 const struct tvec_regdef *rd)
878{
879 return (rv0->bytes.sz == rv1->bytes.sz &&
880 (!rv0->bytes.sz ||
881 MEMCMP(rv0->bytes.p, ==, rv1->bytes.p, rv1->bytes.sz)));
882}
883
884static size_t measure_string(const union tvec_regval *rv,
885 const struct tvec_regdef *rd)
886 { return (rv->str.sz + 4); }
887
888static size_t measure_bytes(const union tvec_regval *rv,
889 const struct tvec_regdef *rd)
890 { return (rv->bytes.sz + 4); }
891
892static int tobuf_string(buf *b, const union tvec_regval *rv,
893 const struct tvec_regdef *rd)
894 { return (buf_putmem32l(b, rv->str.p, rv->str.sz)); }
895
896static int tobuf_bytes(buf *b, const union tvec_regval *rv,
897 const struct tvec_regdef *rd)
898 { return (buf_putmem32l(b, rv->bytes.p, rv->bytes.sz)); }
899
900static int frombuf_string(buf *b, union tvec_regval *rv,
901 const struct tvec_regdef *rd)
902{
903 const void *p;
904 size_t sz;
905
906 p = buf_getmem32l(b, &sz); if (!p) return (-1);
907 tvec_allocstring(rv, sz); memcpy(rv->str.p, p, sz);
908 return (0);
909}
910
911static int frombuf_bytes(buf *b, union tvec_regval *rv,
912 const struct tvec_regdef *rd)
913{
914 const void *p;
915 size_t sz;
916
917 p = buf_getmem32l(b, &sz); if (!p) return (-1);
918 tvec_allocbytes(rv, sz); memcpy(rv->bytes.p, p, sz);
919 return (0);
920}
921
882a39c1
MW
922static int check_string_length(size_t sz, const struct tvec_urange *ur,
923 struct tvec_state *tv)
b64eb60f
MW
924{
925 if (ur && (ur->min > sz || sz > ur->max))
882a39c1
MW
926 return (tvec_error(tv,
927 "invalid string length %lu; must be in [%lu..%lu]",
928 (unsigned long)sz, ur->min, ur->max));
929 return (0);
b64eb60f
MW
930}
931
882a39c1
MW
932static int parse_string(union tvec_regval *rv, const struct tvec_regdef *rd,
933 struct tvec_state *tv)
b64eb60f
MW
934{
935 void *p = rv->str.p;
936
882a39c1
MW
937 if (read_compound_string(&p, &rv->str.sz, TVCODE_BARE, tv)) return (-1);
938 rv->str.p = p;
939 if (check_string_length(rv->str.sz, rd->arg.p, tv)) return (-1);
940 return (0);
b64eb60f
MW
941}
942
882a39c1
MW
943static int parse_bytes(union tvec_regval *rv, const struct tvec_regdef *rd,
944 struct tvec_state *tv)
b64eb60f
MW
945{
946 void *p = rv->bytes.p;
947
882a39c1
MW
948 if (read_compound_string(&p, &rv->bytes.sz, TVCODE_HEX, tv)) return (-1);
949 rv->bytes.p = p;
950 if (check_string_length(rv->bytes.sz, rd->arg.p, tv)) return (-1);
951 return (0);
b64eb60f
MW
952}
953
954static void dump_string(const union tvec_regval *rv,
955 const struct tvec_regdef *rd,
956 struct tvec_state *tv, unsigned style)
957{
958 const unsigned char *p, *q, *l;
959 int ch;
960 unsigned f = 0;
961#define f_nonword 1u
962#define f_newline 2u
963
964 if (!rv->str.sz) { tvec_write(tv, "\"\""); return; }
965
966 p = (const unsigned char *)rv->str.p; l = p + rv->str.sz;
967 if (*p == '!' || *p == ';' || *p == '"' || *p == '\'') goto quote;
968 for (q = p; q < l; q++)
969 if (*q == '\n' && q != l - 1) f |= f_newline;
970 else if (!*q || !isgraph(*q) || *q == '\\') f |= f_nonword;
971 if (f&f_newline) { tvec_write(tv, "\n\t"); goto quote; }
972 else if (f&f_nonword) goto quote;
973 tv->output->ops->write(tv->output, (const char *)p, rv->str.sz); return;
974
975quote:
976 tvec_write(tv, "\"");
977 for (q = p; q < l; q++)
978 switch (*q) {
979 case '"': case '\\': ch = *q; goto escape;
980 case '\a': ch = 'a'; goto escape;
981 case '\b': ch = 'b'; goto escape;
982 case '\x1b': ch = 'e'; goto escape;
983 case '\f': ch = 'f'; goto escape;
984 case '\r': ch = 'r'; goto escape;
985 case '\t': ch = 't'; goto escape;
986 case '\v': ch = 'v'; goto escape;
987 escape:
988 if (p < q)
989 tv->output->ops->write(tv->output, (const char *)p, q - p);
990 tvec_write(tv, "\\%c", ch); p = q + 1;
991 break;
992
993 case '\n':
994 if (p < q)
995 tv->output->ops->write(tv->output, (const char *)p, q - p);
996 tvec_write(tv, "\\n"); p = q + 1;
997 if (!(style&TVSF_COMPACT) && q < l) tvec_write(tv, "\"\t\"");
998 break;
999
1000 default:
1001 if (isprint(*q)) break;
1002 if (p < q)
1003 tv->output->ops->write(tv->output, (const char *)p, q - p);
1004 tvec_write(tv, "\\x{%0*x}", hex_width(UCHAR_MAX), *q); p = q + 1;
1005 break;
1006 }
1007 if (p < q) tv->output->ops->write(tv->output, (const char *)p, q - p);
1008 tvec_write(tv, "\"");
1009
1010#undef f_nonword
1011#undef f_newline
1012}
1013
1014static void dump_bytes(const union tvec_regval *rv,
1015 const struct tvec_regdef *rd,
1016 struct tvec_state *tv, unsigned style)
1017{
1018 const unsigned char *p = rv->bytes.p, *l = p + rv->bytes.sz;
1019 size_t off, sz = rv->bytes.sz;
1020 unsigned i, n;
1021 int wd;
1022
1023 if (!sz) {
1024 tvec_write(tv, style&TVSF_COMPACT ? "\"\"" : "\"\" ; empty");
1025 return;
1026 }
1027
1028 if (style&TVSF_COMPACT) {
1029 while (p < l) tvec_write(tv, "%02x", *p++);
1030 return;
1031 }
1032
1033 if (sz > 16) tvec_write(tv, "\n\t");
1034
1035 off = 0; wd = hex_width(sz);
1036 while (p < l) {
1037 if (l - p < 16) n = l - p;
1038 else n = 16;
1039
1040 for (i = 0; i < 16; i++) {
1041 if (i < n) tvec_write(tv, "%02x", p[i]);
1042 else tvec_write(tv, " ");
1043 if (i%4 == 3) tvec_write(tv, " ");
1044 }
1045 tvec_write(tv, " ; ");
1046 if (sz > 16) tvec_write(tv, "[%0*lx] ", wd, (unsigned long)off);
1047 for (i = 0; i < n; i++)
1048 tvec_write(tv, "%c", isprint(p[i]) ? p[i] : '.');
1049 p += n; off += n;
1050 if (p < l) tvec_write(tv, "\n\t");
1051 }
1052}
1053
1054const struct tvec_regty tvty_string = {
1055 init_string, release_string, eq_string, measure_string,
1056 tobuf_string, frombuf_string,
1057 parse_string, dump_string
1058};
1059
1060const struct tvec_regty tvty_bytes = {
1061 init_bytes, release_bytes, eq_bytes, measure_bytes,
1062 tobuf_bytes, frombuf_bytes,
1063 parse_bytes, dump_bytes
1064};
1065
1066int tvec_claimeq_string(struct tvec_state *tv,
1067 const char *p0, size_t sz0,
1068 const char *p1, size_t sz1,
1069 const char *file, unsigned lno, const char *expr)
1070{
1071 tv->in[0].v.str.p = (/*unconst*/ char *)p0; tv->in[0].v.str.sz = sz0;
1072 tv->out[0].v.str.p =(/*unconst*/ char *) p1; tv->out[0].v.str.sz = sz1;
1073 return (tvec_claimeq(tv, &tvty_string, 0, file, lno, expr));
1074}
1075
1076int tvec_claimeq_strz(struct tvec_state *tv,
1077 const char *p0, const char *p1,
1078 const char *file, unsigned lno, const char *expr)
1079{
1080 tv->in[0].v.str.p = (/*unconst*/ char *)p0;
1081 tv->in[0].v.str.sz = strlen(p0);
1082 tv->out[0].v.str.p = (/*unconst*/ char *)p1;
1083 tv->out[0].v.str.sz = strlen(p1);
1084 return (tvec_claimeq(tv, &tvty_string, 0, file, lno, expr));
1085}
1086
1087int tvec_claimeq_bytes(struct tvec_state *tv,
1088 const void *p0, size_t sz0,
1089 const void *p1, size_t sz1,
1090 const char *file, unsigned lno, const char *expr)
1091{
1092 tv->in[0].v.bytes.p = (/*unconst*/ void *)p0;
1093 tv->in[0].v.bytes.sz = sz0;
1094 tv->out[0].v.bytes.p = (/*unconst*/ void *)p1;
1095 tv->out[0].v.bytes.sz = sz1;
1096 return (tvec_claimeq(tv, &tvty_bytes, 0, file, lno, expr));
1097}
1098
1099/*----- Buffer type -------------------------------------------------------*/
1100
1101static int eq_buffer(const union tvec_regval *rv0,
1102 const union tvec_regval *rv1,
1103 const struct tvec_regdef *rd)
1104 { return (rv0->bytes.sz == rv1->bytes.sz); }
1105
1106static int tobuf_buffer(buf *b, const union tvec_regval *rv,
1107 const struct tvec_regdef *rd)
1108 { return (unsigned_to_buf(b, rv->bytes.sz)); }
1109
1110static int frombuf_buffer(buf *b, union tvec_regval *rv,
1111 const struct tvec_regdef *rd)
1112{
1113 unsigned long u;
1114
1115 if (unsigned_from_buf(b, &u)) return (-1);
1116 if (u > (size_t)-1) return (-1);
1117 tvec_allocbytes(rv, u); memset(rv->bytes.p, '!', u);
1118 return (0);
1119}
1120
1121static const char units[] = "kMGTPEZY";
1122
882a39c1
MW
1123static int parse_buffer(union tvec_regval *rv,
1124 const struct tvec_regdef *rd,
1125 struct tvec_state *tv)
b64eb60f
MW
1126{
1127 dstr d = DSTR_INIT;
1128 char *q; const char *unit;
1129 int olderr;
1130 size_t pos;
1131 unsigned long u, t;
882a39c1 1132 int rc;
b64eb60f
MW
1133 unsigned f = 0;
1134#define f_range 1u
1135
882a39c1 1136 if (tvec_readword(tv, &d, ";", "buffer length")) { rc = -1; goto end; }
b64eb60f
MW
1137 olderr = errno; errno = 0;
1138 u = strtoul(d.buf, &q, 0);
1139 if (errno) goto bad;
1140 errno = olderr;
1141 if (!*q) {
1142 tvec_skipspc(tv); pos = d.len;
1143 if (!tvec_readword(tv, &d, ";", 0)) pos++;
1144 q = d.buf + pos;
1145 }
1146
1147 if (u > (size_t)-1) goto rangerr;
1148 for (t = u, unit = units; *unit; unit++) {
1149 if (t > (size_t)-1/1024) f |= f_range;
1150 else t *= 1024;
1151 if (*q == *unit && (!q[1] || q[1] == 'B')) {
1152 if (f&f_range) goto rangerr;
1153 u = t; q += 2; break;
1154 }
1155 }
1156 if (*q && *q != ';') goto bad;
882a39c1 1157 if (check_string_length(u, rd->arg.p, tv)) { rc = -1; goto end; }
b64eb60f 1158
882a39c1
MW
1159 if (tvec_flushtoeol(tv, 0)) { rc = -1; goto end; }
1160 tvec_allocbytes(rv, u); memset(rv->bytes.p, '?', u);
1161 rc = 0;
1162end:
1163 DDESTROY(&d); return (rc);
b64eb60f
MW
1164
1165bad:
1166 tvec_error(tv, "invalid buffer length `%s'", d.buf);
882a39c1 1167 rc = -1; goto end;
b64eb60f
MW
1168
1169rangerr:
1170 tvec_error(tv, "buffer length `%s' out of range", d.buf);
882a39c1 1171 rc = -1; goto end;
b64eb60f
MW
1172
1173#undef f_range
1174}
1175
1176static void dump_buffer(const union tvec_regval *rv,
1177 const struct tvec_regdef *rd,
1178 struct tvec_state *tv, unsigned style)
1179{
1180 const char *unit;
1181 unsigned long u = rv->bytes.sz;
1182
1183 if (!u || u%1024)
1184 tvec_write(tv, "%lu B", u);
1185 else {
1186 for (unit = units, u /= 1024; !(u%1024) && unit[1]; u /= 1024, unit++);
1187 tvec_write(tv, "%lu %cB", u, *unit);
1188 }
1189}
1190
1191const struct tvec_regty tvty_buffer = {
1192 init_bytes, release_bytes, eq_buffer, measure_int,
1193 tobuf_buffer, frombuf_buffer,
1194 parse_buffer, dump_buffer
1195};
1196
1197/*----- That's all, folks -------------------------------------------------*/