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 ------------------------------------------------------*/
48 #include "tvec-adhoc.h"
49 #include "tvec-types.h"
51 /*----- Preliminary utilities ---------------------------------------------*/
53 /* --- @trivial_release@ --- *
55 * Arguments: @union tvec_regval *rv@ = a register value
56 * @const struct tvec_regdef@ = the register definition
60 * Use: Does nothing. Used for register values which don't retain
64 static void trivial_release(union tvec_regval *rv,
65 const struct tvec_regdef *rd)
68 /*----- Integer utilities -------------------------------------------------*/
70 /* --- @unsigned_to_buf@, @signed_to_buf@ --- *
72 * Arguments: @buf *b@ = buffer to write on
73 * @unsigned long u@ or @long i@ = integer to write
75 * Returns: Zero on success, @-1@ on failure.
77 * Use: Write @i@ to the buffer, in big-endian (two's-complement, it
81 static int unsigned_to_buf(buf *b, unsigned long u)
82 { kludge64 k; ASSIGN64(k, u); return (buf_putk64l(b, k)); }
84 static int signed_to_buf(buf *b, long i)
90 if (i >= 0) ASSIGN64(k, u);
91 else { ASSIGN64(k, ~u); CPL64(k, k); }
92 return (buf_putk64l(b, k));
95 /* --- @unsigned_from_buf@, @signed_from_buf@ --- *
97 * Arguments: @buf *b@ = buffer to write on
98 * @unsigned long *u_out@ or @long *i_out@ = where to put the
101 * Returns: Zero on success, @-1@ on failure.
103 * Use: Read an integer, in big-endian (two's-complement, if signed)
104 * format, from the buffer.
107 static int unsigned_from_buf(buf *b, unsigned long *u_out)
111 ASSIGN64(ulmax, ULONG_MAX);
112 if (buf_getk64l(b, &k)) return (-1);
113 if (CMP64(k, >, ulmax)) { buf_break(b); return (-1); }
114 *u_out = GET64(unsigned long, k); return (0);
117 /* --- @hex_width@ --- *
119 * Arguments: @unsigned long u@ = an integer
121 * Returns: A suitable number of digits to use in order to display @u@ in
122 * hex. Currently, we select a power of two sufficient to show
123 * the value, but at least 2.
126 static int hex_width(unsigned long u)
131 for (t = u >> 4, wd = 4; t >>= wd, wd *= 2, t; );
135 /* --- @format_unsigned_hex@, @format_signed_hex@ --- *
137 * Arguments: @const struct gprintf_ops *gops@ = print operations
138 * @void *go@ = print destination
139 * @unsigned long u@ or @long i@ = integer to print
143 * Use: Print an unsigned or signed integer in hexadecimal.
146 static void format_unsigned_hex(const struct gprintf_ops *gops, void *go,
148 { gprintf(gops, go, "0x%0*lx", hex_width(u), u); }
150 static void format_signed_hex(const struct gprintf_ops *gops, void *go,
153 unsigned long u = i >= 0 ? i : -(unsigned long)i;
154 gprintf(gops, go, "%s0x%0*lx", i < 0 ? "-" : "", hex_width(u), u);
157 static int signed_from_buf(buf *b, long *i_out)
159 kludge64 k, lmax, not_lmin;
161 ASSIGN64(lmax, LONG_MAX); ASSIGN64(not_lmin, ~(unsigned long)LONG_MIN);
162 if (buf_getk64l(b, &k)) return (-1);
163 if (CMP64(k, <=, lmax)) *i_out = (long)GET64(unsigned long, k);
166 if (CMP64(k, <=, not_lmin)) *i_out = -(long)GET64(unsigned long, k) - 1;
167 else { buf_break(b); return (-1); }
172 /* --- @check_signed_range@, @check_unsigned_range@ --- *
174 * Arguments: @long i@ or @unsigned long u@ = an integer
175 * @const struct tvec_irange *ir@ or
176 * @const struct tvec_urange *ur@ = range specification,
178 * @struct tvec_state *tv@ = test vector state
179 * @const char *what@ = description of value
181 * Returns: Zero on success, or @-1@ on error.
183 * Use: Check that the integer is within bounds. If not, report a
184 * suitable error and return a failure indication.
187 static int check_signed_range(long i,
188 const struct tvec_irange *ir,
189 struct tvec_state *tv, const char *what)
194 if (ir->min > i || i > ir->max) {
195 tvec_error(tv, "%s %ld out of range (must be in [%ld .. %ld])",
196 what, i, ir->min, ir->max);
199 m = ir->m; if (m > 0) m = -m;
201 /* Reduce both the integer and the intended residue to the canonical
202 * interval [0, m). This is more awkward than it should be because C
203 * (following CPU designs) adopted an unhelpful definition of integer
204 * division when the dividend is negative.
206 * Note that I've canonicalized the divisor to be %%\emph{negative}%%,
207 * because in two's-complement arithmetic, the absolute value of the
208 * most negative representable value is not itself representable. The
209 * residue modulo the most negative value will itself be representable.
212 ii = i%m; if (ii < 0) ii -= m;
213 aa = ir->a%m; if (aa < 0) aa -= m;
215 tvec_error(tv, "%s %ld == %ld =/= %ld (mod %ld)",
216 what, i, ii, ir->a, ir->m);
224 static int check_unsigned_range(unsigned long u,
225 const struct tvec_urange *ur,
226 struct tvec_state *tv, const char *what)
231 if (ur->min > u || u > ur->max) {
232 tvec_error(tv, "%s %lu out of range (must be in [%lu .. %lu])",
233 what, u, ur->min, ur->max);
236 if (ur->m && ur->m != 1) {
238 if (uu != ur->a%ur->m) {
239 tvec_error(tv, "%s %lu == %lu =/= %lu (mod %lu)",
240 what, u, uu, ur->a, ur->m);
248 /* --- @chtodig@ --- *
250 * Arguments: @int ch@ = a character
252 * Returns: The numeric value of the character as a digit, or @-1@ if
253 * it's not a digit. Letters count as extended digits starting
254 * with value 10; case is not significant.
257 static int chtodig(int ch)
259 if ('0' <= ch && ch <= '9') return (ch - '0');
260 else if ('a' <= ch && ch <= 'z') return (ch - 'a' + 10);
261 else if ('A' <= ch && ch <= 'Z') return (ch - 'A' + 10);
265 /* --- @parse_unsigned_integer@, @parse_signed_integer@ --- *
267 * Arguments: @unsigned long *u_out@, @long *i_out@ = where to put the
269 * @const char **q_out@ = where to put the end position
270 * @const char *p@ = pointer to the string to parse
272 * Returns: Zero on success, @-1@ on error.
274 * Use: Parse an integer from a string in the test-vector format.
275 * This is mostly extension of the traditional C @strtoul@
276 * format: supported inputs include:
278 * * NNN -- a decimal number (even if it starts with `0');
279 * * 0xNNN -- hexadecimal;
282 * * NNrNNN -- base NN.
284 * Furthermore, single underscores are permitted internally as
285 * an insignificant digit separator.
288 static int parse_unsigned_integer(unsigned long *u_out, const char **q_out,
295 #define f_implicit 1u /* implicitly reading base 10 */
296 #define f_digit 2u /* read a real digit */
297 #define f_uscore 4u /* found an underscore */
301 * This will deal with the traditional `0[box]...' prefixes. We'll leave
302 * our new `NNr...' syntax for later.
304 if (p[0] != '0' || !p[1]) {
305 d = chtodig(*p); if (0 > d || d >= 10) return (-1);
306 r = 10; u = d; p++; f |= f_implicit | f_digit;
308 u = 0; d = chtodig(p[2]);
309 if (d < 0) { r = 10; f |= f_implicit | f_digit; p++; }
310 else if ((p[1] == 'x' || p[1] == 'X') && d < 16) { r = 16; p += 2; }
311 else if ((p[1] == 'o' || p[1] == 'O') && d < 8) { r = 8; p += 2; }
312 else if ((p[1] == 'b' || p[1] == 'B') && d < 2) { r = 2; p += 2; }
313 else { r = 10; f |= f_digit; p++; }
318 /* Work through the string a character at a time. */
320 ch = *p; switch (ch) {
323 /* An underscore is OK if we haven't just seen one. */
325 if (f&f_uscore) goto done;
326 p++; f = (f&~f_implicit) | f_uscore;
330 /* An `r' is OK if the number so far is small enough to be a sensible
331 * base, and we're scanning decimal implicitly.
334 if (!(f&f_implicit) || !u || u >= 36) goto done;
335 d = chtodig(p[1]); if (0 > d || d >= u) goto done;
336 r = u; u = d; f = (f&~f_implicit) | f_digit; p += 2; q = p;
340 /* Otherwise we expect a valid digit and accumulate it. */
341 d = chtodig(ch); if (d < 0 || d >= r) goto done;
342 if (u > ULONG_MAX/r) return (-1);
343 u *= r; if (u > ULONG_MAX - d) return (-1);
344 u += d; f = (f&~f_uscore) | f_digit; p++; q = p;
350 if (!(f&f_digit)) return (-1);
351 *u_out = u; *q_out = q; return (0);
358 static int parse_signed_integer(long *i_out, const char **q_out,
365 /* Read an initial sign. */
367 else if (*p == '-') { f |= f_neg; p++; }
369 /* Scan an unsigned number. */
370 if (parse_unsigned_integer(&u, q_out, p)) return (-1);
372 /* Check for signed overflow and apply the sign. */
374 if (u > LONG_MAX) return (-1);
377 if (u && u - 1 > -(LONG_MIN + 1)) return (-1);
378 *i_out = u ? -(long)(u - 1) - 1 : 0;
386 /* --- @parse_unsigned@, @parse_signed@ --- *
388 * Arguments: @unsigned long *u_out@ or @long *i_out@ = where to put the
390 * @const char *p@ = string to parse
391 * @const struct tvec_urange *ur@ or
392 * @const struct tvec_irange *ir@ = range specification,
394 * @struct tvec_state *tv@ = test vector state
396 * Returns: Zero on success, @-1@ on error.
398 * Use: Parse and range-check an integer. Unlike @parse_(un)signed_
399 * integer@, these functions check that there's no cruft
400 * following the final digit, and report errors as they find
401 * them rather than leaving that to the caller.
404 static int parse_unsigned(unsigned long *u_out, const char *p,
405 const struct tvec_urange *ur,
406 struct tvec_state *tv)
411 if (parse_unsigned_integer(&u, &q, p))
412 return (tvec_error(tv, "invalid unsigned integer `%s'", p));
413 if (*q) return (tvec_syntax(tv, *q, "end-of-line"));
414 if (check_unsigned_range(u, ur, tv, "integer")) return (-1);
415 *u_out = u; return (0);
418 static int parse_signed(long *i_out, const char *p,
419 const struct tvec_irange *ir,
420 struct tvec_state *tv)
425 if (parse_signed_integer(&i, &q, p))
426 return (tvec_error(tv, "invalid signed integer `%s'", p));
427 if (*q) return (tvec_syntax(tv, *q, "end-of-line"));
428 if (check_signed_range(i, ir, tv, "integer")) return (-1);
429 *i_out = i; return (0);
431 static const char size_units[] = "kMGTPEZY";
433 /* --- @parse_szint@ --- *
435 * Arguments: @struct tvec_state *tv@ = test-vector state
436 * @unsigned long *u_out@ = where to put the answer
437 * @const char *delims@ = delimiters
438 * @const char *what@ = description of what we're parsing
440 * Returns: Zero on success, %$-1$% on failure.
442 * Use: Parse a memory size.
445 static int parse_szint(struct tvec_state *tv, unsigned long *u_out,
446 const char *delims, const char *what)
449 const char *p, *unit;
455 d.a = &tv->p_test->a;
456 if (tvec_readword(tv, &d, 0, delims, what)) { rc = -1; goto end; }
458 if (parse_unsigned_integer(&u, &p, p)) goto bad;
459 if (!*p) tvec_readword(tv, &d, &p, delims, 0);
461 for (t = u, unit = size_units; *unit; unit++) {
462 if (t > ULONG_MAX/1024) f |= f_range;
465 if (f&f_range) goto rangerr;
477 tvec_error(tv, "invalid %s `%s'", what, d.buf);
481 tvec_error(tv, "%s `%s' out of range", what, d.buf);
487 /* --- @format_size@ --- *
489 * Arguments: @const struct gprintf_ops *gops@ = print operations
490 * @void *go@ = print destination
491 * @unsigned long u@ = a size
492 * @unsigned style@ = style (@TVSF_...@)
496 * Use: Format @u@ as a size in bytes to the destination, expressing
497 * it with a unit prefix if this is possible exactly.
500 static void format_size(const struct gprintf_ops *gops, void *go,
501 unsigned long u, unsigned style)
506 gprintf(gops, go, "%lu", u);
507 else if (!u || u%1024)
508 gprintf(gops, go, "%lu%sB", u, style&TVSF_COMPACT ? "" : " ");
510 for (unit = size_units, u /= 1024;
511 !(u%1024) && unit[1];
513 gprintf(gops, go, "%lu%s%cB", u, style&TVSF_COMPACT ? "" : " ", *unit);
517 /*----- Floating-point utilities ------------------------------------------*/
519 /* --- @eqish_floating_p@ --- *
521 * Arguments: @double x, y@ = two numbers to compare
522 * @const struct tvec_floatinfo *fi@ = floating-point info
524 * Returns: Nonzero if the comparand @x@ is sufficiently close to the
525 * reference @y@, or zero if it's definitely different.
528 static int eqish_floating_p(double x, double y,
529 const struct tvec_floatinfo *fi)
533 /* NaNs and infinities are equal only to each other. */
534 if (NANP(x)) return (NANP(y)); else if (NANP(y)) return (0);
535 if (INFP(x)) return (x == y); else if (INFP(y)) return (0);
537 /* Compare finite values. */
538 switch (fi ? fi->f&TVFF_EQMASK : TVFF_EXACT) {
540 return (x == y && NEGP(x) == NEGP(y));
542 t = fabs(y - x); return (t < fi->delta);
544 t = fabs(y - x); u = fabs(y*fi->delta); if (u < DBL_MIN) u = DBL_MIN;
551 /* --- @format_floating@ --- *
553 * Arguments: @const struct gprintf_ops *gops@ = print operations
554 * @void *go@ = print destination
555 * @double x@ = number to print
559 * Use: Print a floating-point number, accurately.
562 static void format_floating(const struct gprintf_ops *gops, void *go,
568 gprintf(gops, go, "#nan");
570 gprintf(gops, go, x > 0 ? "#+inf" : "#-inf");
572 /* Ugh. C doesn't provide any function for just printing a
573 * floating-point number /correctly/, i.e., so that you can read the
574 * result back and recover the number you first thought of. There are
575 * complicated algorithms published for doing this, but I really don't
576 * want to get into that here. So we have this.
578 * The sign doesn't cause significant difficulty so we're going to ignore
579 * it for now. So suppose we're given a number %$x = f b^e$%, in
580 * base-%$b$% format, so %$f b^n$% and %$e$% are integers, with
581 * %$0 \le f < 1$%. We're going to convert it into the nearest integer
582 * of the form %$X = F B^E$%, with similar conditions, only with the
583 * additional requirement that %$X$% is normalized, i.e., that %$X = 0$%
584 * or %$F \ge B^{-N}$%.
586 * We're rounding to the nearest such %$X$%. If there is to be ambiguity
587 * in the conversion, then some %$x = f b^e$% and the next smallest
588 * representable number %$x' = x + b^{e-n}$% must both map to the same
589 * %$X$%, which means both %$x$% and %$x'$% must be nearer to %$X$% than
590 * any other number representable in the target system. The nest larger
591 * number is %$X' = X + B^{E-N}$%; the next smaller number will normally
592 * be %$W = X - B^{E-N}$%, but if %$F = 1/B$ then the next smaller number
593 * is actually %$X - B^{E-N-1}$%. We ignore this latter possibility in
594 * the pursuit of a conservative estimate (though actually it doesn't
597 * If both %$x$% and %$x'$% map to %$X$% then we must have
598 * %$L = X - B^{E-N}/2 \le x$% and %$x + b^{e-n} \le R = X + B^{E-N}/2$%;
599 * so firstly %$f b^e = x \ge L = W + B^{E-N}/2 > W = (F - B^{-N}) B^E$%,
600 * and secondly %$b^{e-n} \le B^{E-N}$%. Since these inequalities are in
601 * opposite senses, we can divide, giving
603 * %$f b^e/b^{e-n} > (F - B^{-N}) B^E/B^{E-N}$% ,
607 * %$f b^n > (F - B^{-N}) B^N = F B^N - 1$% .
609 * Now %$f \le 1 - b^{-n}$%, and %$F \ge B^{-1}$%, so, for this to be
610 * possible, it must be the case that
612 * %$(1 - b^{-n}) b^n = b^n - 1 > B^{N-1} - 1$% .
614 * Then rearrange and take logarithms, obtaining
616 * %$(N - 1) \log B < n \log b$% ,
620 * %$N < n \log b/\log B + 1$% .
622 * Recall that this is a necessary condition for a collision to occur; we
623 * are therefore safe whenever
625 * %$N \ge n \log b/\log B + 1$% ;
627 * so, taking ceilings,
629 * %$N \ge \lceil n \log b/\log B \rceil + 1$% .
631 * So that's why we have this.
633 * I'm going to assume that @n = DBL_MANT_DIG@ is sufficiently small
634 * that we can calculate this without ending up on the wrong side of an
637 * In C11, we have @DBL_DECIMAL_DIG@, which should be the same value
638 * only as a constant. Except that modern compilers are more than clever
639 * enough to work out that this is a constant anyway.
641 * This is sometimes an overestimate: we'll print out meaningless digits
642 * that don't represent anything we actually know about the number in
643 * question. To fix that, we'd need a complicated algorithm like Steele
644 * and White's Dragon4, Gay's @dtoa@, or Burger and Dybvig's algorithm
645 * (note that Loitsch's Grisu2 is conservative, and Grisu3 hands off to
646 * something else in difficult situations).
649 #ifdef DBL_DECIMAL_DIG
650 prec = DBL_DECIMAL_DIG;
652 prec = ceil(DBL_MANT_DIG*log(FLT_RADIX)/log(10)) + 1;
654 gprintf(gops, go, "%.*g", prec, x);
658 /* --- @parse_floating@ --- *
660 * Arguments: @double *x_out@ = where to put the result
661 * @const char *q_out@ = where to leave end pointer, or null
662 * @const char *p@ = string to parse
663 * @const struct tvec_floatinfo *fi@ = floating-point info
664 * @struct tvec_state *tv@ = test vector state
666 * Returns: Zero on success, @-1@ on error.
668 * Use: Parse a floating-point number from a string. Reports any
669 * necessary errors. If @q_out@ is not null then trailing
670 * material is permitted and a pointer to it (or the end of the
671 * string) is left in @*q_out@.
674 static int parse_floating(double *x_out, const char **q_out, const char *p,
675 const struct tvec_floatinfo *fi,
676 struct tvec_state *tv)
678 const char *pp; char *q;
683 d.a = &tv->p_test->a;
685 /* Check for special tokens. */
686 if (STRCMP(p, ==, "#nan")) {
688 if (q_out) *q_out = p + strlen(p);
691 tvec_error(tv, "NaN not supported on this system");
696 else if (STRCMP(p, ==, "#inf") ||
697 STRCMP(p, ==, "#+inf") || STRCMP(p, ==, "+#inf")) {
699 if (q_out) *q_out = p + strlen(p);
700 x = INFINITY; rc = 0;
702 tvec_error(tv, "infinity not supported on this system");
707 else if (STRCMP(p, ==, "#-inf") || STRCMP(p, ==, "-#inf")) {
709 if (q_out) *q_out = p + strlen(p);
710 x = -INFINITY; rc = 0;
712 tvec_error(tv, "infinity not supported on this system");
717 /* Check that this looks like a number, so we can exclude `strtod'
718 * recognizing its own non-finite number tokens.
722 if (*pp == '+' || *pp == '-') pp++;
723 if (*pp == '.') pp++;
725 tvec_syntax(tv, *p ? *p : fgetc(tv->fp), "floating-point number");
729 /* Parse the number using the system parser. */
730 olderr = errno; errno = 0;
731 #if __STDC_VERSION__ >= 199901
736 if (q_out) *q_out = q;
737 else if (*q) { tvec_syntax(tv, *q, "end-of-line"); rc = -1; goto end; }
738 if (errno && (errno != ERANGE || (x > 0 ? -x : x) == HUGE_VAL)) {
739 tvec_error(tv, "invalid floating-point number `%.*s': %s",
740 (int)(q - p), p, strerror(errno));
746 /* Check that the number is acceptable. */
747 if (NANP(x) && fi && !(fi->f&TVFF_NANOK)) {
748 tvec_error(tv, "#nan not allowed here");
753 ((!(fi->f&TVFF_NOMIN) && x < fi->min) ||
754 (!(fi->f&TVFF_NOMAX) && x > fi->max)) &&
755 !(INFP(x) && (fi->f&(NEGP(x) ? TVFF_NEGINFOK : TVFF_POSINFOK)))) {
756 dstr_puts(&d, "floating-point number ");
757 format_floating(&dstr_printops, &d, x);
758 dstr_puts(&d, " out of range (must be in ");
759 if (fi->f&TVFF_NOMIN)
760 dstr_puts(&d, "(#-inf");
762 { dstr_putc(&d, '['); format_floating(&dstr_printops, &d, fi->min); }
763 dstr_puts(&d, " .. ");
764 if (fi->f&TVFF_NOMAX)
765 dstr_puts(&d, "#+inf)");
767 { format_floating(&dstr_printops, &d, fi->max); dstr_putc(&d, ']'); }
768 dstr_putc(&d, ')'); dstr_putz(&d);
769 tvec_error(tv, "%s", d.buf); rc = -1; goto end;
778 /*----- String utilities --------------------------------------------------*/
780 /* Special character name table. */
781 static const struct chartab {
782 const char *name; /* character name */
783 int ch; /* character value */
784 unsigned f; /* flags: */
785 #define CTF_PREFER 1u /* preferred name */
786 #define CTF_SHORT 2u /* short name (compact style) */
788 { "#eof", EOF, CTF_PREFER | CTF_SHORT },
789 { "#nul", '\0', CTF_PREFER },
790 { "#bell", '\a', CTF_PREFER },
791 { "#ding", '\a', 0 },
792 { "#bel", '\a', CTF_SHORT },
793 { "#backspace", '\b', CTF_PREFER },
794 { "#bs", '\b', CTF_SHORT },
795 { "#escape", '\x1b', CTF_PREFER },
796 { "#esc", '\x1b', CTF_SHORT },
797 { "#formfeed", '\f', CTF_PREFER },
798 { "#ff", '\f', CTF_SHORT },
799 { "#newline", '\n', CTF_PREFER },
800 { "#linefeed", '\n', 0 },
801 { "#lf", '\n', CTF_SHORT },
803 { "#return", '\r', CTF_PREFER },
804 { "#carriage-return", '\r', 0 },
805 { "#cr", '\r', CTF_SHORT },
806 { "#tab", '\t', CTF_PREFER | CTF_SHORT },
807 { "#horizontal-tab", '\t', 0 },
809 { "#vertical-tab", '\v', CTF_PREFER },
810 { "#vt", '\v', CTF_SHORT },
811 { "#space", ' ', 0 },
812 { "#spc", ' ', CTF_SHORT },
813 { "#delete", '\x7f', CTF_PREFER },
814 { "#del", '\x7f', CTF_SHORT },
818 /* --- @find_charname@ --- *
820 * Arguments: @int ch@ = character to match
821 * @unsigned f@ = flags (@CTF_...@) to match
823 * Returns: The name of the character, or null if no match is found.
825 * Use: Looks up a name for a character. Specifically, it returns
826 * the first entry in the @chartab@ table which matches @ch@ and
827 * which has one of the flags @f@ set.
830 static const char *find_charname(int ch, unsigned f)
832 const struct chartab *ct;
834 for (ct = chartab; ct->name; ct++)
835 if (ct->ch == ch && (ct->f&f)) return (ct->name);
839 /* --- @read_charname@ --- *
841 * Arguments: @int *ch_out@ = where to put the character
842 * @const char *p@ = character name
843 * @unsigned f@ = flags (@TCF_...@)
845 * Returns: Zero if a match was found, @-1@ if not.
847 * Use: Looks up a character by name. If @RCF_EOFOK@ is set in @f@,
848 * then the @EOF@ marker can be matched; otherwise it can't.
852 static int read_charname(int *ch_out, const char *p, unsigned f)
854 const struct chartab *ct;
856 for (ct = chartab; ct->name; ct++)
857 if (STRCMP(p, ==, ct->name) && ((f&RCF_EOFOK) || ct->ch >= 0))
858 { *ch_out = ct->ch; return (0); }
862 /* --- @format_charesc@ --- *
864 * Arguments: @const struct gprintf_ops *gops@ = print operations
865 * @void *go@ = print destination
866 * @int ch@ = character to format
867 * @unsigned f@ = flags (@FCF_...@)
871 * Use: Format a character as an escape sequence, possibly as part of
872 * a larger string. If @FCF_BRACE@ is set in @f@, then put
873 * braces around a `\x...' code, so that it's suitable for use
874 * in a longer string.
878 static void format_charesc(const struct gprintf_ops *gops, void *go,
882 case '\a': gprintf(gops, go, "\\a"); break;
883 case '\b': gprintf(gops, go, "\\b"); break;
884 case '\x1b': gprintf(gops, go, "\\e"); break;
885 case '\f': gprintf(gops, go, "\\f"); break;
886 case '\r': gprintf(gops, go, "\\r"); break;
887 case '\n': gprintf(gops, go, "\\n"); break;
888 case '\t': gprintf(gops, go, "\\t"); break;
889 case '\v': gprintf(gops, go, "\\v"); break;
890 case '\\': gprintf(gops, go, "\\\\"); break;
891 case '\'': gprintf(gops, go, "\\'"); break;
893 if (f&FCF_BRACE) gprintf(gops, go, "\\{0}");
894 else gprintf(gops, go, "\\0");
898 gprintf(gops, go, "\\x{%0*x}", hex_width(UCHAR_MAX), ch);
900 gprintf(gops, go, "\\x%0*x", hex_width(UCHAR_MAX), ch);
905 /* --- @format_char@ --- *
907 * Arguments: @const struct gprintf_ops *gops@ = print operations
908 * @void *go@ = print destination
909 * @int ch@ = character to format
913 * Use: Format a single character.
916 static void format_char(const struct gprintf_ops *gops, void *go, int ch)
919 case '\\': case '\'': escape:
920 gprintf(gops, go, "'");
921 format_charesc(gops, go, ch, 0);
922 gprintf(gops, go, "'");
925 if (!isprint(ch)) goto escape;
926 gprintf(gops, go, "'%c'", ch);
931 /* --- @fill_pattern@ --- *
933 * Arguments: @void *p@ = destination pointer
934 * @size_t sz@ = destination buffer size
935 * @const void *pat@ = pointer to pattern
936 * @size_t patsz@ = pattern size
940 * Use: Fill the destination buffer with as many copies of the
941 * pattern as will fit, followed by as many initial bytes of the
942 * pattern will fit in the remaining space.
945 static void fill_pattern(void *p, size_t sz, const void *pat, size_t patsz)
947 unsigned char *q = p;
950 memset(q, *(unsigned char *)pat, sz);
953 memcpy(q, pat, patsz); pat = q; q += patsz; sz -= patsz;
955 { memcpy(q, pat, patsz); q += patsz; sz -= patsz; patsz *= 2; }
961 /* --- @maybe_format_unsigned_char@, @maybe_format_signed_char@ --- *
963 * Arguments: @const struct gprintf_ops *gops@ = print operations
964 * @void *go@ = print destination
965 * @unsigned long u@ or @long i@ = an integer
969 * Use: Format a (signed or unsigned) integer as a character, if it's
970 * in range, printing something like `= 'q''. It's assumed that
971 * a comment marker has already been output.
974 static void maybe_format_unsigned_char
975 (const struct gprintf_ops *gops, void *go, unsigned long u)
979 p = find_charname(u, CTF_PREFER);
980 if (p) gprintf(gops, go, " = %s", p);
982 { gprintf(gops, go, " = "); format_char(gops, go, u); }
985 static void maybe_format_signed_char
986 (const struct gprintf_ops *gops, void *go, long i)
990 p = find_charname(i, CTF_PREFER);
991 if (p) gprintf(gops, go, " = %s", p);
992 if (0 <= i && i < UCHAR_MAX)
993 { gprintf(gops, go, " = "); format_char(gops, go, i); }
996 /* --- @read_charesc@ --- *
998 * Arguments: @int *ch_out@ = where to put the result
999 * @struct tvec_state *tv@ = test vector state
1001 * Returns: Zero on success, @-1@ on error.
1003 * Use: Parse and convert an escape sequence from @tv@'s input
1004 * stream, assuming that the initial `\' has already been read.
1005 * Reports errors as appropriate.
1008 static int read_charesc(int *ch_out, struct tvec_state *tv)
1017 /* Things we shouldn't find. */
1018 case EOF: case '\n': return (tvec_syntax(tv, ch, "string escape"));
1020 /* Single-character escapes. */
1021 case '\'': *ch_out = '\''; break;
1022 case '\\': *ch_out = '\\'; break;
1023 case '"': *ch_out = '"'; break;
1024 case 'a': *ch_out = '\a'; break;
1025 case 'b': *ch_out = '\b'; break;
1026 case 'e': *ch_out = '\x1b'; break;
1027 case 'f': *ch_out = '\f'; break;
1028 case 'n': *ch_out = '\n'; break;
1029 case 'r': *ch_out = '\r'; break;
1030 case 't': *ch_out = '\t'; break;
1031 case 'v': *ch_out = '\v'; break;
1033 /* Hex escapes, with and without braces. */
1036 if (ch == '{') { f |= f_brace; ch = getc(tv->fp); }
1039 if (esc < 0 || esc >= 16) return (tvec_syntax(tv, ch, "hex digit"));
1041 ch = getc(tv->fp); i = chtodig(ch); if (i < 0 || i >= 16) break;
1043 if (esc > UCHAR_MAX)
1044 return (tvec_error(tv,
1045 "character code %d out of range", esc));
1047 if (!(f&f_brace)) ungetc(ch, tv->fp);
1048 else if (ch != '}') return (tvec_syntax(tv, ch, "`}'"));
1052 /* Other things, primarily octal escapes. */
1054 f |= f_brace; ch = getc(tv->fp);
1057 if ('0' <= ch && ch < '8') {
1058 i = 1; esc = ch - '0';
1061 if ('0' > ch || ch >= '8') { ungetc(ch, tv->fp); break; }
1062 esc = 8*esc + ch - '0';
1063 i++; if (i >= 3) break;
1067 if (ch != '}') return (tvec_syntax(tv, ch, "`}'"));
1069 if (esc > UCHAR_MAX)
1070 return (tvec_error(tv,
1071 "character code %d out of range", esc));
1072 *ch_out = esc; break;
1074 return (tvec_syntax(tv, ch, "string escape"));
1083 /* --- @read_quoted_string@ --- *
1085 * Arguments: @dstr *d@ = string to write to
1086 * @int quote@ = initial quote, `'' or `"'
1087 * @struct tvec_state *tv@ = test vector state
1089 * Returns: Zero on success, @-1@ on error.
1091 * Use: Read the rest of a quoted string into @d@, reporting errors
1094 * A single-quoted string is entirely literal. A double-quoted
1095 * string may contain C-like escapes.
1098 static int read_quoted_string(dstr *d, int quote, struct tvec_state *tv)
1105 case EOF: case '\n':
1106 return (tvec_syntax(tv, ch, "`%c'", quote));
1108 if (quote == '\'') goto ordinary;
1109 ch = getc(tv->fp); if (ch == '\n') { tv->lno++; break; }
1110 ungetc(ch, tv->fp); if (read_charesc(&ch, tv)) return (-1);
1113 if (ch == quote) goto end;
1125 /* --- @collect_bare@ --- *
1127 * Arguments: @dstr *d@ = string to write to
1128 * @struct tvec_state *tv@ = test vector state
1130 * Returns: Zero on success, @-1@ on error.
1132 * Use: Read barewords and the whitespace between them. Stop when we
1133 * encounter something which can't start a bareword.
1136 static int collect_bare(dstr *d, struct tvec_state *tv)
1138 size_t pos = d->len;
1139 enum { WORD, SPACE, ESCAPE }; unsigned s = WORD;
1146 tvec_syntax(tv, ch, "bareword");
1149 if (s == ESCAPE) { tv->lno++; goto addch; }
1150 if (s == WORD) pos = d->len;
1151 ungetc(ch, tv->fp); if (tvec_nexttoken(tv)) { rc = -1; goto end; }
1152 DPUTC(d, ' '); s = SPACE;
1154 case '"': case '\'': case '!': case '#': case ')': case '}': case ']':
1155 if (s == SPACE) { ungetc(ch, tv->fp); goto done; }
1161 if (s != ESCAPE && isspace(ch)) {
1162 if (s == WORD) pos = d->len;
1163 DPUTC(d, ch); s = SPACE;
1167 DPUTC(d, ch); s = WORD;
1172 if (s == SPACE) d->len = pos;
1178 /* --- @set_up_encoding@ --- *
1180 * Arguments: @const codec_class **ccl_out@ = where to put the class
1181 * @unsigned *f_out@ = where to put the flags
1182 * @unsigned code@ = the coding scheme to use (@TVEC_...@)
1186 * Use: Helper for @read_compound_string@ below.
1188 * Return the appropriate codec class and flags for @code@.
1189 * Leaves @*ccl_out@ null if the coding scheme doesn't have a
1190 * backing codec class (e.g., @TVCODE_BARE@).
1193 enum { TVCODE_BARE, TVCODE_HEX, TVCODE_BASE64, TVCODE_BASE32 };
1194 static void set_up_encoding(const codec_class **ccl_out, unsigned *f_out,
1199 *ccl_out = 0; *f_out = 0;
1202 *ccl_out = &hex_class; *f_out = CDCF_IGNCASE;
1205 *ccl_out = &base32_class; *f_out = CDCF_IGNCASE | CDCF_IGNEQPAD;
1208 *ccl_out = &base64_class; *f_out = CDCF_IGNEQPAD;
1215 /* --- @flush_codec@ --- *
1217 * Arguments: @codec *cdc@ = a codec, or null
1218 * @dstr *d@ = output string
1219 * @struct tvec_state *tv@ = test vector state
1221 * Returns: Zero on success, @-1@ on error.
1223 * Use: Helper for @read_compound_string@ below.
1225 * Flush out any final buffered material from @cdc@, and check
1226 * that it's in a good state. Frees the codec on success. Does
1227 * nothing if @cdc@ is null.
1230 static int flush_codec(codec *cdc, dstr *d, struct tvec_state *tv)
1235 err = cdc->ops->code(cdc, 0, 0, d);
1237 return (tvec_error(tv, "invalid %s sequence end: %s",
1238 cdc->ops->c->name, codec_strerror(err)));
1239 cdc->ops->destroy(cdc);
1244 /* --- @read_compound_string@ --- *
1246 * Arguments: @void **p_inout@ = address of output buffer pointer
1247 * @size_t *sz_inout@ = address of buffer size
1248 * @unsigned code@ = initial interpretation of barewords
1249 * @unsigned f@ = other flags (@RCSF_...@)
1250 * @struct tvec_state *tv@ = test vector state
1252 * Returns: Zero on success, @-1@ on error.
1254 * Use: Parse a compound string, i.e., a sequence of stringish pieces
1255 * which might be quoted strings, character names, or barewords
1256 * to be decoded accoding to @code@, interspersed with
1257 * additional directives.
1259 * If the initial buffer pointer is non-null and sufficiently
1260 * large, then it will be reused; otherwise, it is freed and a
1261 * fresh, sufficiently large buffer is allocated and returned.
1262 * This buffer unconditionally uses the standard-library arena.
1265 #define RCSF_NESTED 1u
1266 static int read_compound_string(void **p_inout, size_t *sz_inout,
1267 unsigned code, unsigned f,
1268 struct tvec_state *tv)
1270 const codec_class *ccl; unsigned cdf;
1272 dstr d = DSTR_INIT, w = DSTR_INIT;
1275 void *pp = 0; size_t sz;
1279 set_up_encoding(&ccl, &cdf, code); cdc = 0;
1280 d.a = w.a = &tv->p_test->a;
1282 if (tvec_nexttoken(tv)) return (tvec_syntax(tv, fgetc(tv->fp), "string"));
1287 case ')': case ']': case '}':
1288 /* Close brackets. Leave these for recursive caller if there is one,
1292 if (!(f&RCSF_NESTED))
1293 { rc = tvec_syntax(tv, ch, "string"); goto end; }
1294 ungetc(ch, tv->fp); goto done;
1296 case '"': case '\'':
1297 /* Quotes. Read a quoted string. */
1299 if (cdc && flush_codec(cdc, &d, tv)) { rc = -1; goto end; }
1301 if (read_quoted_string(&d, ch, tv)) { rc = -1; goto end; }
1305 /* A named character. */
1308 if (cdc && flush_codec(cdc, &d, tv)) { rc = -1; goto end; }
1310 DRESET(&w); tvec_readword(tv, &w, 0, ";", "character name");
1311 if (STRCMP(w.buf, ==, "#empty")) break;
1312 if (read_charname(&ch, w.buf, RCF_EOFOK)) {
1313 rc = tvec_error(tv, "unknown character name `%s'", d.buf);
1316 DPUTC(&d, ch); break;
1319 /* A magic keyword. */
1321 if (cdc && flush_codec(cdc, &d, tv)) { rc = -1; goto end; }
1324 DRESET(&w); tvec_readword(tv, &w, 0, ";", "`!'-keyword");
1326 /* Change bareword coding system. */
1327 if (STRCMP(w.buf, ==, "!bare"))
1328 { code = TVCODE_BARE; set_up_encoding(&ccl, &cdf, code); }
1329 else if (STRCMP(w.buf, ==, "!hex"))
1330 { code = TVCODE_HEX; set_up_encoding(&ccl, &cdf, code); }
1331 else if (STRCMP(w.buf, ==, "!base32"))
1332 { code = TVCODE_BASE32; set_up_encoding(&ccl, &cdf, code); }
1333 else if (STRCMP(w.buf, ==, "!base64"))
1334 { code = TVCODE_BASE64; set_up_encoding(&ccl, &cdf, code); }
1336 /* Repeated substrings. */
1337 else if (STRCMP(w.buf, ==, "!repeat")) {
1338 if (tvec_nexttoken(tv)) {
1339 rc = tvec_syntax(tv, fgetc(tv->fp), "repeat count");
1343 if (tvec_readword(tv, &w, 0, ";{", "repeat count"))
1344 { rc = -1; goto end; }
1345 if (parse_unsigned_integer(&n, &q, w.buf)) {
1346 rc = tvec_error(tv, "invalid repeat count `%s'", w.buf);
1349 if (*q) { rc = tvec_syntax(tv, *q, "`{'"); goto end; }
1350 if (tvec_nexttoken(tv))
1351 { rc = tvec_syntax(tv, fgetc(tv->fp), "`{'"); goto end; }
1352 ch = getc(tv->fp); if (ch != '{')
1353 { rc = tvec_syntax(tv, ch, "`{'"); goto end; }
1355 if (read_compound_string(&pp, &sz, code, f | RCSF_NESTED, tv))
1356 { rc = -1; goto end; }
1357 ch = getc(tv->fp); if (ch != '}')
1358 { rc = tvec_syntax(tv, ch, "`}'"); goto end; }
1360 if (n > (size_t)-1/sz)
1361 { rc = tvec_error(tv, "repeat size out of range"); goto end; }
1364 fill_pattern(d.buf + d.len, n, pp, sz); d.len += n;
1369 /* Anything else is an error. */
1371 tvec_error(tv, "unknown string keyword `%s'", w.buf);
1377 /* A bareword. Process it according to the current coding system. */
1382 if (collect_bare(&d, tv)) goto done;
1386 ungetc(ch, tv->fp); DRESET(&w);
1387 if (tvec_readword(tv, &w, 0, ";",
1388 "%s-encoded fragment", ccl->name))
1389 { rc = -1; goto end; }
1390 if (!cdc) cdc = ccl->decoder(cdf);
1391 err = cdc->ops->code(cdc, w.buf, w.len, &d);
1393 tvec_error(tv, "invalid %s fragment `%s': %s",
1394 ccl->name, w.buf, codec_strerror(err));
1401 } while (!tvec_nexttoken(tv));
1404 /* Wrap things up. */
1405 if (cdc && flush_codec(cdc, &d, tv)) { rc = -1; goto end; }
1407 if (*sz_inout <= d.len)
1408 { free(*p_inout); *p_inout = x_alloc(&arena_stdlib, d.len + 1); }
1409 p = *p_inout; memcpy(p, d.buf, d.len); p[d.len] = 0; *sz_inout = d.len;
1413 /* Clean up any debris. */
1414 if (cdc) cdc->ops->destroy(cdc);
1416 dstr_destroy(&d); dstr_destroy(&w);
1420 /*----- Signed and unsigned integer types ---------------------------------*/
1422 /* --- @init_int@, @init_uint@ --- *
1424 * Arguments: @union tvec_regval *rv@ = register value
1425 * @const struct tvec_regdef *rd@ = register definition
1429 * Use: Initialize a register value.
1431 * Integer values are initialized to zero.
1434 static void init_int(union tvec_regval *rv, const struct tvec_regdef *rd)
1437 static void init_uint(union tvec_regval *rv, const struct tvec_regdef *rd)
1440 /* --- @eq_int@, @eq_uint@ --- *
1442 * Arguments: @const union tvec_regval *rv0, *rv1@ = register values
1443 * @const struct tvec_regdef *rd@ = register definition
1445 * Returns: Nonzero if the values are equal, zero if unequal
1447 * Use: Compare register values for equality.
1450 static int eq_int(const union tvec_regval *rv0, const union tvec_regval *rv1,
1451 const struct tvec_regdef *rd)
1452 { return (rv0->i == rv1->i); }
1454 static int eq_uint(const union tvec_regval *rv0,
1455 const union tvec_regval *rv1,
1456 const struct tvec_regdef *rd)
1457 { return (rv0->u == rv1->u); }
1459 /* --- @copy_int@, @copy_uint@ --- *
1461 * Arguments: @union tvec_regval *rvd@ = destination register value
1462 * @const union tvec_regval *rvs@ = source register value
1463 * @const struct tvec_regdef *rd@ = register definition
1467 * Use: Copy a register value.
1470 static void copy_int(union tvec_regval *rvd, const union tvec_regval *rvs,
1471 const struct tvec_regdef *rd)
1472 { rvd->i = rvs->i; }
1474 static void copy_uint(union tvec_regval *rvd, const union tvec_regval *rvs,
1475 const struct tvec_regdef *rd)
1476 { rvd->u = rvs->u; }
1478 /* --- @tobuf_int@, @tobuf_uint@ --- *
1480 * Arguments: @buf *b@ = buffer
1481 * @const union tvec_regval *rv@ = register value
1482 * @const struct tvec_regdef *rd@ = register definition
1484 * Returns: Zero on success, %$-1$% on failure.
1486 * Use: Serialize a register value to a buffer.
1488 * Integer values are serialized as little-endian 64-bit signed
1489 * or unsigned integers.
1492 static int tobuf_int(buf *b, const union tvec_regval *rv,
1493 const struct tvec_regdef *rd)
1494 { return (signed_to_buf(b, rv->i)); }
1496 static int tobuf_uint(buf *b, const union tvec_regval *rv,
1497 const struct tvec_regdef *rd)
1498 { return (unsigned_to_buf(b, rv->u)); }
1500 /* --- @frombuf_int@, @frombuf_uint@ --- *
1502 * Arguments: @buf *b@ = buffer
1503 * @union tvec_regval *rv@ = register value
1504 * @const struct tvec_regdef *rd@ = register definition
1506 * Returns: Zero on success, %$-1$% on failure.
1508 * Use: Deserialize a register value from a buffer.
1510 * Integer values are serialized as 64-bit signed or unsigned
1514 static int frombuf_int(buf *b, union tvec_regval *rv,
1515 const struct tvec_regdef *rd)
1516 { return (signed_from_buf(b, &rv->i)); }
1518 static int frombuf_uint(buf *b, union tvec_regval *rv,
1519 const struct tvec_regdef *rd)
1520 { return (unsigned_from_buf(b, &rv->u)); }
1522 /* --- @parse_int@, @parse_uint@ --- *
1524 * Arguments: @union tvec_regval *rv@ = register value
1525 * @const struct tvec_regdef *rd@ = register definition
1526 * @struct tvec_state *tv@ = test-vector state
1528 * Returns: Zero on success, %$-1$% on error.
1530 * Use: Parse a register value from an input file.
1532 * Integers may be input in decimal, hex, binary, or octal,
1533 * following approximately usual conventions.
1535 * * Signed integers may be preceded with a `+' or `-' sign.
1537 * * Decimal integers are just a sequence of decimal digits
1540 * * Octal integers are a sequence of digits `0' ... `7',
1541 * preceded by `0o' or `0O'.
1543 * * Hexadecimal integers are a sequence of digits `0'
1544 * ... `9', `a' ... `f', or `A' ... `F', preceded by `0x' or
1547 * * Radix-B integers are a sequence of digits `0' ... `9',
1548 * `a' ... `f', or `A' ... `F', each with value less than B,
1549 * preceded by `Br' or `BR', where 0 < B < 36 is expressed
1550 * in decimal without any leading `0' or internal
1553 * * A digit sequence may contain internal underscore `_'
1554 * separators, but not before or after all of the digits;
1555 * and two consecutive `_' characters are not permitted.
1558 static int parse_int(union tvec_regval *rv, const struct tvec_regdef *rd,
1559 struct tvec_state *tv)
1564 d.a = &tv->p_test->a;
1565 if (tvec_readword(tv, &d, 0, ";", "signed integer"))
1566 { rc = -1; goto end; }
1567 if (parse_signed(&rv->i, d.buf, rd->arg.p, tv)) { rc = -1; goto end; }
1573 static int parse_uint(union tvec_regval *rv, const struct tvec_regdef *rd,
1574 struct tvec_state *tv)
1579 d.a = &tv->p_test->a;
1580 if (tvec_readword(tv, &d, 0, ";", "unsigned integer"))
1581 { rc = -1; goto end; }
1582 if (parse_unsigned(&rv->u, d.buf, rd->arg.p, tv)) { rc = -1; goto end; }
1588 /* --- @dump_int@, @dump_uint@ --- *
1590 * Arguments: @const union tvec_regval *rv@ = register value
1591 * @const struct tvec_regdef *rd@ = register definition
1592 * @unsigned style@ = output style (@TVSF_...@)
1593 * @const struct gprintf_ops *gops@, @void *gp@ = format output
1597 * Use: Dump a register value to the format output.
1599 * Integer values are dumped in decimal and, unless compact
1600 * output is requested, hex, and maybe a character, as a
1604 static void dump_int(const union tvec_regval *rv,
1605 const struct tvec_regdef *rd,
1607 const struct gprintf_ops *gops, void *go)
1609 if (style&TVSF_RAW) gprintf(gops, go, "int:");
1610 gprintf(gops, go, "%ld", rv->i);
1611 if (!(style&(TVSF_COMPACT | TVSF_RAW))) {
1612 gprintf(gops, go, " ; = ");
1613 format_signed_hex(gops, go, rv->i);
1614 maybe_format_signed_char(gops, go, rv->i);
1618 static void dump_uint(const union tvec_regval *rv,
1619 const struct tvec_regdef *rd,
1621 const struct gprintf_ops *gops, void *go)
1623 if (style&TVSF_RAW) gprintf(gops, go, "uint:");
1624 gprintf(gops, go, "%lu", rv->u);
1625 if (!(style&(TVSF_COMPACT | TVSF_RAW))) {
1626 gprintf(gops, go, " ; = ");
1627 format_unsigned_hex(gops, go, rv->u);
1628 maybe_format_unsigned_char(gops, go, rv->u);
1632 /* Integer type definitions. */
1633 const struct tvec_regty tvty_int = {
1634 init_int, trivial_release, eq_int, copy_int,
1635 tobuf_int, frombuf_int,
1638 const struct tvec_regty tvty_uint = {
1639 init_uint, trivial_release, eq_uint, copy_uint,
1640 tobuf_uint, frombuf_uint,
1641 parse_uint, dump_uint
1644 /* Predefined integer ranges. */
1645 const struct tvec_irange
1646 tvrange_schar = { SCHAR_MIN, SCHAR_MAX, 0, 0 },
1647 tvrange_short = { SHRT_MIN, SHRT_MAX, 0, 0 },
1648 tvrange_int = { INT_MIN, INT_MAX, 0, 0 },
1649 tvrange_long = { LONG_MIN, LONG_MAX, 0, 0 },
1650 tvrange_sbyte = { -128, 127, 0, 0 },
1651 tvrange_i16 = { -32768, +32767, 0, 0 },
1652 tvrange_i32 = { -2147483648, 2147483647, 0, 0 };
1653 const struct tvec_urange
1654 tvrange_uchar = { 0, UCHAR_MAX, 0, 0 },
1655 tvrange_ushort = { 0, USHRT_MAX, 0, 0 },
1656 tvrange_uint = { 0, UINT_MAX, 0, 0 },
1657 tvrange_ulong = { 0, ULONG_MAX, 0, 0 },
1658 tvrange_size = { 0, (size_t)-1, 0, 0 },
1659 tvrange_byte = { 0, 255, 0, 0 },
1660 tvrange_u16 = { 0, 65535, 0, 0 },
1661 tvrange_u32 = { 0, 4294967295, 0, 0 };
1663 /* --- @tvec_claimeq_int@ --- *
1665 * Arguments: @struct tvec_state *tv@ = test-vector state
1666 * @long i0, i1@ = two signed integers
1667 * @const char *file@, @unsigned @lno@ = calling file and line
1668 * @const char *expr@ = the expression to quote on failure
1670 * Returns: Nonzero if @i0@ and @i1@ are equal, otherwise zero.
1672 * Use: Check that values of @i0@ and @i1@ are equal. As for
1673 * @tvec_claim@ above, a test case is automatically begun and
1674 * ended if none is already underway. If the values are
1675 * unequal, then @tvec_fail@ is called, quoting @expr@, and the
1676 * mismatched values are dumped: @i0@ is printed as the output
1677 * value and @i1@ is printed as the input reference.
1680 int tvec_claimeq_int(struct tvec_state *tv, long i0, long i1,
1681 const char *file, unsigned lno, const char *expr)
1683 struct tvec_reg rval, rref;
1685 rval.f = rref.f = TVRF_LIVE; rval.v.i = i0; rref.v.i = i1;
1686 return (tvec_claimeq(tv, &tvty_int, 0, &rval, &rref, file, lno, expr));
1689 /* --- @tvec_claimeq_uint@ --- *
1691 * Arguments: @struct tvec_state *tv@ = test-vector state
1692 * @unsigned long u0, u1@ = two unsigned integers
1693 * @const char *file@, @unsigned @lno@ = calling file and line
1694 * @const char *expr@ = the expression to quote on failure
1696 * Returns: Nonzero if @u0@ and @u1@ are equal, otherwise zero.
1698 * Use: Check that values of @u0@ and @u1@ are equal. As for
1699 * @tvec_claim@ above, a test case is automatically begun and
1700 * ended if none is already underway. If the values are
1701 * unequal, then @tvec_fail@ is called, quoting @expr@, and the
1702 * mismatched values are dumped: @u0@ is printed as the output
1703 * value and @u1@ is printed as the input reference.
1706 int tvec_claimeq_uint(struct tvec_state *tv,
1707 unsigned long u0, unsigned long u1,
1708 const char *file, unsigned lno, const char *expr)
1710 struct tvec_reg rval, rref;
1712 rval.f = rref.f = TVRF_LIVE; rval.v.u = u0; rref.v.u = u1;
1713 return (tvec_claimeq(tv, &tvty_uint, 0, &rval, &rref, file, lno, expr));
1716 /*----- Size type ---------------------------------------------------------*/
1718 /* --- @parse_size@ --- *
1720 * Arguments: @union tvec_regval *rv@ = register value
1721 * @const struct tvec_regdef *rd@ = register definition
1722 * @struct tvec_state *tv@ = test-vector state
1724 * Returns: Zero on success, %$-1$% on error.
1726 * Use: Parse a register value from an input file.
1728 * The input format for a size value consists of an unsigned
1729 * integer followed by an optional unit specifier consisting of
1730 * an SI unit prefix and (optionally) the letter `B'. */
1732 static int parse_size(union tvec_regval *rv, const struct tvec_regdef *rd,
1733 struct tvec_state *tv)
1738 if (parse_szint(tv, &sz, ";", "size")) { rc = -1; goto end; }
1739 if (check_unsigned_range(sz, rd->arg.p, tv, "size")) { rc = -1; goto end; }
1745 /* --- @dump_size@ --- *
1747 * Arguments: @const union tvec_regval *rv@ = register value
1748 * @const struct tvec_regdef *rd@ = register definition
1749 * @unsigned style@ = output style (@TVSF_...@)
1750 * @const struct gprintf_ops *gops@, @void *gp@ = format output
1754 * Use: Dump a register value to the format output.
1756 * Size values are dumped with a unit specifier, with a unit
1757 * prefox only if the size is an exact multiple of the relevant
1758 * power of two. Unless compact style is requested, the plain
1759 * decimal and hex representations of the value are also
1763 static void dump_size(const union tvec_regval *rv,
1764 const struct tvec_regdef *rd,
1766 const struct gprintf_ops *gops, void *go)
1768 if (style&TVSF_RAW) gprintf(gops, go, "size:");
1769 format_size(gops, go, rv->u, style);
1770 if (!(style&(TVSF_COMPACT | TVSF_RAW))) {
1771 gprintf(gops, go, " ; = %lu", (unsigned long)rv->u);
1772 gprintf(gops, go, " = "); format_unsigned_hex(gops, go, rv->u);
1773 maybe_format_unsigned_char(gops, go, rv->u);
1777 /* Size type definitions. */
1778 const struct tvec_regty tvty_size = {
1779 init_uint, trivial_release, eq_uint, copy_uint,
1780 tobuf_uint, frombuf_uint,
1781 parse_size, dump_size
1784 /* --- @tvec_claimeq_size@ --- *
1786 * Arguments: @struct tvec_state *tv@ = test-vector state
1787 * @unsigned long sz0, sz1@ = two sizes
1788 * @const char *file@, @unsigned @lno@ = calling file and line
1789 * @const char *expr@ = the expression to quote on failure
1791 * Returns: Nonzero if @sz0@ and @sz1@ are equal, otherwise zero.
1793 * Use: Check that values of @u0@ and @u1@ are equal. As for
1794 * @tvec_claim@ above, a test case is automatically begun and
1795 * ended if none is already underway. If the values are
1796 * unequal, then @tvec_fail@ is called, quoting @expr@, and the
1797 * mismatched values are dumped: @u0@ is printed as the output
1798 * value and @u1@ is printed as the input reference.
1801 int tvec_claimeq_size(struct tvec_state *tv,
1802 unsigned long sz0, unsigned long sz1,
1803 const char *file, unsigned lno, const char *expr)
1805 struct tvec_reg rval, rref;
1807 rval.f = rref.f = TVRF_LIVE; rval.v.u = sz0; rref.v.u = sz1;
1808 return (tvec_claimeq(tv, &tvty_size, 0, &rval, &rref, file, lno, expr));
1811 /*----- Floating-point type -----------------------------------------------*/
1813 /* --- @int_float@ --- *
1815 * Arguments: @union tvec_regval *rv@ = register value
1816 * @const struct tvec_regdef *rd@ = register definition
1820 * Use: Initialize a register value.
1822 * Floating-point values are initialized to zero.
1825 static void init_float(union tvec_regval *rv, const struct tvec_regdef *rd)
1828 /* --- @eq_float@ --- *
1830 * Arguments: @const union tvec_regval *rv0, *rv1@ = register values
1831 * @const struct tvec_regdef *rd@ = register definition
1833 * Returns: Nonzero if the values are equal, zero if unequal
1835 * Use: Compare register values for equality.
1837 * Floating-point values may be considered equal if their
1838 * absolute or relative difference is sufficiently small, as
1839 * described in the register definition.
1842 static int eq_float(const union tvec_regval *rv0,
1843 const union tvec_regval *rv1,
1844 const struct tvec_regdef *rd)
1845 { return (eqish_floating_p(rv1->f, rv0->f, rd->arg.p)); }
1847 /* --- @copy_float@ --- *
1849 * Arguments: @union tvec_regval *rvd@ = destination register value
1850 * @const union tvec_regval *rvs@ = source register value
1851 * @const struct tvec_regdef *rd@ = register definition
1855 * Use: Copy a register value.
1858 static void copy_float(union tvec_regval *rvd, const union tvec_regval *rvs,
1859 const struct tvec_regdef *rd)
1860 { rvd->f = rvs->f; }
1862 /* --- @tobuf_float@ --- *
1864 * Arguments: @buf *b@ = buffer
1865 * @const union tvec_regval *rv@ = register value
1866 * @const struct tvec_regdef *rd@ = register definition
1868 * Returns: Zero on success, %$-1$% on failure.
1870 * Use: Serialize a register value to a buffer.
1872 * Floating-point values are serialized as little-endian
1873 * IEEE 754 Binary64.
1876 static int tobuf_float(buf *b, const union tvec_regval *rv,
1877 const struct tvec_regdef *rd)
1878 { return (buf_putf64l(b, rv->f)); }
1880 /* --- @frombuf_float@ --- *
1882 * Arguments: @buf *b@ = buffer
1883 * @union tvec_regval *rv@ = register value
1884 * @const struct tvec_regdef *rd@ = register definition
1886 * Returns: Zero on success, %$-1$% on failure.
1888 * Use: Deserialize a register value from a buffer.
1890 * Floating-point values are serialized as little-endian
1891 * IEEE 754 Binary64.
1894 static int frombuf_float(buf *b, union tvec_regval *rv,
1895 const struct tvec_regdef *rd)
1900 rc = buf_getf64l(b, &t); if (!rc) rv->f = t;
1904 /* --- @parse_float@ --- *
1906 * Arguments: @union tvec_regval *rv@ = register value
1907 * @const struct tvec_regdef *rd@ = register definition
1908 * @struct tvec_state *tv@ = test-vector state
1910 * Returns: Zero on success, %$-1$% on error.
1912 * Use: Parse a register value from an input file.
1914 * Floating-point values are either NaN (%|#nan|%, if supported
1915 * by the platform); positive or negative infinity (%|#inf|%,
1916 * %|+#inf|%, or %|#+inf|% (preferring the last), and %|-#inf|%
1917 * or %|#-inf|% (preferring the latter), if supported by the
1918 * platform); or a number in strtod(3) syntax.
1921 static int parse_float(union tvec_regval *rv, const struct tvec_regdef *rd,
1922 struct tvec_state *tv)
1927 d.a = &tv->p_test->a;
1928 if (tvec_readword(tv, &d, 0, ";", "floating-point number"))
1929 { rc = -1; goto end; }
1930 if (parse_floating(&rv->f, 0, d.buf, rd->arg.p, tv))
1931 { rc = -1; goto end; }
1937 /* --- @dump_float@ --- *
1939 * Arguments: @const union tvec_regval *rv@ = register value
1940 * @const struct tvec_regdef *rd@ = register definition
1941 * @unsigned style@ = output style (@TVSF_...@)
1942 * @const struct gprintf_ops *gops@, @void *gp@ = format output
1946 * Use: Dump a register value to the format output.
1948 * Floating-point values are dumped in decimal or as a special
1949 * token beginning with `%|#|%'. Some effort is taken to ensure
1950 * that the output is sufficient to uniquely identify the
1951 * original value, but, honestly, C makes this really hard.
1954 static void dump_float(const union tvec_regval *rv,
1955 const struct tvec_regdef *rd,
1957 const struct gprintf_ops *gops, void *go)
1959 if (style&TVSF_RAW) gprintf(gops, go, "float:");
1960 format_floating(gops, go, rv->f);
1963 /* Floating-point type definition. */
1964 const struct tvec_regty tvty_float = {
1965 init_float, trivial_release, eq_float, copy_float,
1966 tobuf_float, frombuf_float,
1967 parse_float, dump_float
1970 /* Predefined floating-point ranges. */
1971 const struct tvec_floatinfo
1972 tvflt_float = { TVFF_RELDELTA | TVFF_INFOK | TVFF_NANOK,
1973 -FLT_MAX, FLT_MAX, FLT_EPSILON/2 },
1974 tvflt_double = { TVFF_EXACT | TVFF_INFOK | TVFF_NANOK,
1975 -DBL_MAX, DBL_MAX, 0.0 },
1976 tvflt_finite = { TVFF_EXACT, -DBL_MAX, DBL_MAX, 0.0 },
1977 tvflt_nonneg = { TVFF_EXACT, 0, DBL_MAX, 0.0 };
1979 /* --- @tvec_claimeqish_float@ --- *
1981 * Arguments: @struct tvec_state *tv@ = test-vector state
1982 * @double f0, f1@ = two floating-point numbers
1983 * @unsigned f@ = flags (@TVFF_...@)
1984 * @double delta@ = maximum tolerable difference
1985 * @const char *file@, @unsigned @lno@ = calling file and line
1986 * @const char *expr@ = the expression to quote on failure
1988 * Returns: Nonzero if @f0@ and @f1@ are sufficiently close, otherwise
1991 * Use: Check that values of @f0@ and @f1@ are sufficiently close.
1992 * As for @tvec_claim@ above, a test case is automatically begun
1993 * and ended if none is already underway. If the values are
1994 * too far apart, then @tvec_fail@ is called, quoting @expr@,
1995 * and the mismatched values are dumped: @f0@ is printed as the
1996 * output value and @f1@ is printed as the input reference.
1998 * The details for the comparison are as follows.
2000 * * A NaN value matches any other NaN, and nothing else.
2002 * * An infinity matches another infinity of the same sign,
2005 * * If @f&TVFF_EQMASK@ is @TVFF_EXACT@, then any
2006 * representable number matches only itself: in particular,
2007 * positive and negative zero are considered distinct.
2008 * (This allows tests to check that they land on the correct
2009 * side of branch cuts, for example.)
2011 * * If @f&TVFF_EQMASK@ is @TVFF_ABSDELTA@, then %$x$% matches
2012 * %$y$% when %$|x - y| < \delta$%.
2014 * * If @f&TVFF_EQMASK@ is @TVFF_RELDELTA@, then %$x$% matches
2015 * %$y$% when %$|1 - x/y| < \delta$%. (Note that this
2016 * criterion is asymmetric. Write %$x \approx_\delta y$%
2017 * if and only if %$|1 - x/y < \delta$%. Then, for example,
2018 * if %$y/(1 + \delta) < x < y (1 - \delta)$%, then
2019 * %$x \approx_\delta y$%, but %$y \not\approx_\delta x$%.)
2022 int tvec_claimeqish_float(struct tvec_state *tv,
2023 double f0, double f1, unsigned f, double delta,
2024 const char *file, unsigned lno,
2027 struct tvec_floatinfo fi;
2028 struct tvec_reg rval, rref;
2029 union tvec_misc arg;
2031 fi.f = f; fi.min = fi.max = 0.0; fi.delta = delta; arg.p = &fi;
2032 rval.f = rref.f = TVRF_LIVE; rval.v.f = f0; rref.v.f = f1;
2033 return (tvec_claimeq(tv, &tvty_float, &arg,
2034 &rval, &rref, file, lno, expr));
2037 /* --- @tvec_claimeq_float@ --- *
2039 * Arguments: @struct tvec_state *tv@ = test-vector state
2040 * @double f0, f1@ = two floating-point numbers
2041 * @const char *file@, @unsigned @lno@ = calling file and line
2042 * @const char *expr@ = the expression to quote on failure
2044 * Returns: Nonzero if @f0@ and @f1@ are identical, otherwise zero.
2046 * Use: Check that values of @f0@ and @f1@ are identical. The
2047 * function is exactly equivalent to @tvec_claimeqish_float@
2048 * with @f == TVFF_EXACT@.
2051 int tvec_claimeq_float(struct tvec_state *tv,
2052 double f0, double f1,
2053 const char *file, unsigned lno,
2056 return (tvec_claimeqish_float(tv, f0, f1, TVFF_EXACT, 0.0,
2060 /*----- Durations ---------------------------------------------------------*/
2062 /* A duration is a floating-point number of seconds. Initialization and
2063 * teardown, equality comparison, and serialization are as for floating-point
2067 static const struct duration_unit {
2071 #define DUF_PREFER 1u
2072 } duration_units[] = {
2084 { "yr", 31557600.0, DUF_PREFER },
2085 { "year", 31557600.0, 0 },
2086 { "years", 31557600.0, 0 },
2087 { "y", 31557600.0, 0 },
2088 { "wk", 604800.0, DUF_PREFER },
2089 { "week", 604800.0, 0 },
2090 { "weeks", 604800.0, 0 },
2091 { "w", 604800.0, 0 },
2092 { "day", 86400.0, DUF_PREFER },
2093 { "days", 86400.0, 0 },
2094 { "dy", 86400.0, 0 },
2095 { "d", 86400.0, 0 },
2096 { "hr", 3600.0, DUF_PREFER },
2097 { "hour", 3600.0, 0 },
2098 { "hours", 3600.0, 0 },
2100 { "min", 60.0, DUF_PREFER },
2101 { "minute", 60.0, 0 },
2102 { "minutes", 60.0, 0 },
2105 { "s", 1.0, DUF_PREFER },
2107 { "second", 1.0, 0 },
2108 { "seconds", 1.0, 0 },
2112 { "ms", 1e-3, DUF_PREFER },
2113 { "µs", 1e-6, DUF_PREFER },
2114 { "ns", 1e-9, DUF_PREFER },
2115 { "ps", 1e-12, DUF_PREFER },
2116 { "fs", 1e-15, DUF_PREFER },
2117 { "as", 1e-18, DUF_PREFER },
2118 { "zs", 1e-21, DUF_PREFER },
2119 { "ys", 1e-24, DUF_PREFER },
2124 /* --- @tvec_parsedurunit@ --- *
2126 * Arguments: @double *scale_out@ = where to leave the scale
2127 * @const char **p_inout@ = input unit string, updated
2129 * Returns: Zero on success, %$-1$% on error.
2131 * Use: If @*p_inout@ begins with a unit string followed by the end
2132 * of the string or some non-alphanumeric character, then store
2133 * the corresponding scale factor in @*scale_out@, advance
2134 * @*p_inout@ past the unit string, and return zero. Otherwise,
2138 int tvec_parsedurunit(double *scale_out, const char **p_inout)
2140 const char *p = *p_inout, *q;
2141 const struct duration_unit *u;
2144 while (ISSPACE(*p)) p++;
2145 for (q = p; *q && ISALNUM(*q); q++);
2146 n = q - p; if (!n) { *scale_out = 1.0; return (0); }
2148 for (u = duration_units; u->unit; u++)
2149 if (STRNCMP(p, ==, u->unit, n) && !u->unit[n])
2150 { *scale_out = u->scale; *p_inout = q; return (0); }
2154 /* --- @parse_duration@ --- *
2156 * Arguments: @union tvec_regval *rv@ = register value
2157 * @const struct tvec_regdef *rd@ = register definition
2158 * @struct tvec_state *tv@ = test-vector state
2160 * Returns: Zero on success, %$-1$% on error.
2162 * Use: Parse a register value from an input file.
2164 * Duration values are finite nonnegative floating-point
2165 * numbers in @strtod@ syntax, optionally followed by a unit .
2168 static int parse_duration(union tvec_regval *rv,
2169 const struct tvec_regdef *rd,
2170 struct tvec_state *tv)
2172 const struct duration_unit *u;
2178 d.a = &tv->p_test->a;
2179 if (tvec_readword(tv, &d, 0, ";", "duration")) { rc = -1; goto end; }
2180 if (parse_floating(&t, &q, d.buf,
2181 rd->arg.p ? rd->arg.p : &tvflt_nonneg, tv))
2182 { rc = -1; goto end; }
2184 if (!*q) tvec_readword(tv, &d, &q, ";", 0);
2186 for (u = duration_units; u->unit; u++)
2187 if (STRCMP(q, ==, u->unit)) { t *= u->scale; goto found_unit; }
2188 rc = tvec_syntax(tv, *q, "end-of-line"); goto end;
2197 /* --- @dump_duration@ --- *
2199 * Arguments: @const union tvec_regval *rv@ = register value
2200 * @const struct tvec_regdef *rd@ = register definition
2201 * @unsigned style@ = output style (@TVSF_...@)
2202 * @const struct gprintf_ops *gops@, @void *gp@ = format output
2206 * Use: Dump a register value to the format output.
2208 * Durations are dumped as a human-palatable scaled value with
2209 * unit, and, if compact style is not requested, as a raw number
2210 * of seconds at full precision as a comment.
2213 static void dump_duration(const union tvec_regval *rv,
2214 const struct tvec_regdef *rd,
2216 const struct gprintf_ops *gops, void *go)
2218 const struct duration_unit *u;
2221 if (style&TVSF_RAW) {
2222 gprintf(gops, go, "duration:");
2223 format_floating(gops, go, rv->f);
2224 gprintf(gops, go, "s");
2228 for (u = duration_units; u->scale > t && u[1].unit; u++);
2231 gprintf(gops, go, "%.4g %s", t, u ? u->unit : "s");
2233 if (!(style&TVSF_COMPACT)) {
2234 gprintf(gops, go, "; = ");
2235 format_floating(gops, go, rv->f);
2236 gprintf(gops, go, " s");
2241 /* Duration type definition. */
2242 const struct tvec_regty tvty_duration = {
2243 init_float, trivial_release, eq_float, copy_float,
2244 tobuf_float, frombuf_float,
2245 parse_duration, dump_duration
2248 /* --- @tvec_claimeqish_duration@ --- *
2250 * Arguments: @struct tvec_state *tv@ = test-vector state
2251 * @double t0, t1@ = two durations
2252 * @unsigned f@ = flags (@TVFF_...@)
2253 * @double delta@ = maximum tolerable difference
2254 * @const char *file@, @unsigned @lno@ = calling file and line
2255 * @const char *expr@ = the expression to quote on failure
2257 * Returns: Nonzero if @t0@ and @t1@ are sufficiently close, otherwise
2260 * Use: Check that values of @t0@ and @t1@ are sufficiently close.
2261 * This is essentially the same as @tvec_claimeqish_float@, only
2262 * it dumps the values as durations on a mismatch.
2265 int tvec_claimeqish_duration(struct tvec_state *tv,
2266 double t0, double t1, unsigned f, double delta,
2267 const char *file, unsigned lno,
2270 struct tvec_floatinfo fi;
2271 struct tvec_reg rval, rref;
2272 union tvec_misc arg;
2274 fi.f = f; fi.min = fi.max = 0.0; fi.delta = delta; arg.p = &fi;
2275 rval.f = rref.f = TVRF_LIVE; rval.v.f = t0; rref.v.f = t1;
2276 return (tvec_claimeq(tv, &tvty_duration, &arg,
2277 &rval, &rref, file, lno, expr));
2280 /* --- @tvec_claimeq_duration@ --- *
2282 * Arguments: @struct tvec_state *tv@ = test-vector state
2283 * @double t0, t1@ = two durations
2284 * @const char *file@, @unsigned @lno@ = calling file and line
2285 * @const char *expr@ = the expression to quote on failure
2287 * Returns: Nonzero if @t0@ and @t1@ are identical, otherwise zero.
2289 * Use: Check that values of @t0@ and @t1@ are identical. The
2290 * function is exactly equivalent to @tvec_claimeqish_duration@
2291 * with @f == TVFF_EXACT@.
2294 int tvec_claimeq_duration(struct tvec_state *tv,
2295 double t0, double t1,
2296 const char *file, unsigned lno,
2299 return (tvec_claimeqish_duration(tv, t0, t1, TVFF_EXACT, 0.0,
2303 /*----- Enumerations ------------------------------------------------------*/
2305 /* --- @init_tenum@ --- *
2307 * Arguments: @union tvec_regval *rv@ = register value
2308 * @const struct tvec_regdef *rd@ = register definition
2312 * Use: Initialize a register value.
2314 * Integer and floating-point enumeration values are initialized
2315 * as their underlying representations. Pointer enumerations
2316 * are initialized to %|#nil|%.
2319 #define init_ienum init_int
2320 #define init_uenum init_uint
2321 #define init_fenum init_float
2323 static void init_penum(union tvec_regval *rv, const struct tvec_regdef *rd)
2326 /* --- @eq_tenum@ --- *
2328 * Arguments: @const union tvec_regval *rv0, *rv1@ = register values
2329 * @const struct tvec_regdef *rd@ = register definition
2331 * Returns: Nonzero if the values are equal, zero if unequal
2333 * Use: Compare register values for equality.
2335 * Integer and floating-point enumeration values are compared as
2336 * their underlying representations; in particular, floating-
2337 * point enumerations may compare equal if their absolute or
2338 * relative difference is sufficiently small. Pointer
2339 * enumerations are compared as pointers.
2342 #define eq_ienum eq_int
2343 #define eq_uenum eq_uint
2345 static int eq_fenum(const union tvec_regval *rv0,
2346 const union tvec_regval *rv1,
2347 const struct tvec_regdef *rd)
2349 const struct tvec_fenuminfo *ei = rd->arg.p;
2350 return (eqish_floating_p(rv0->f, rv1->f, ei->fi));
2353 static int eq_penum(const union tvec_regval *rv0,
2354 const union tvec_regval *rv1,
2355 const struct tvec_regdef *rd)
2356 { return (rv0->p == rv1->p); }
2358 /* --- @copy_tenum@ --- *
2360 * Arguments: @union tvec_regval *rvd@ = destination register value
2361 * @const union tvec_regval *rvs@ = source register value
2362 * @const struct tvec_regdef *rd@ = register definition
2366 * Use: Copy a register value.
2369 #define copy_ienum copy_int
2370 #define copy_uenum copy_uint
2371 #define copy_fenum copy_float
2373 static void copy_penum(union tvec_regval *rvd, const union tvec_regval *rvs,
2374 const struct tvec_regdef *rd)
2375 { rvd->p = rvs->p; }
2377 /* --- @tobuf_tenum@ --- *
2379 * Arguments: @buf *b@ = buffer
2380 * @const union tvec_regval *rv@ = register value
2381 * @const struct tvec_regdef *rd@ = register definition
2383 * Returns: Zero on success, %$-1$% on failure.
2385 * Use: Serialize a register value to a buffer.
2387 * Integer and floating-point enumeration values are serialized
2388 * as their underlying representations. Pointer enumerations
2389 * are serialized as the signed integer index into the
2390 * association table; %|#nil|% serializes as %$-1$%, and
2391 * unrecognized pointers cause failure.
2394 #define tobuf_ienum tobuf_int
2395 #define tobuf_uenum tobuf_uint
2396 #define tobuf_fenum tobuf_float
2398 static int tobuf_penum(buf *b, const union tvec_regval *rv,
2399 const struct tvec_regdef *rd)
2401 const struct tvec_penuminfo *pei = rd->arg.p;
2402 const struct tvec_passoc *pa;
2405 for (pa = pei->av, i = 0; pa->tag; pa++, i++)
2406 if (pa->p == rv->p) goto found;
2410 return (signed_to_buf(b, i));
2413 /* --- @frombuf_tenum@ --- *
2415 * Arguments: @buf *b@ = buffer
2416 * @union tvec_regval *rv@ = register value
2417 * @const struct tvec_regdef *rd@ = register definition
2419 * Returns: Zero on success, %$-1$% on failure.
2421 * Use: Deserialize a register value from a buffer.
2423 * Integer and floating-point enumeration values are serialized
2424 * as their underlying representations. Pointer enumerations
2425 * are serialized as the signed integer index into the
2426 * association table; %|#nil|% serializes as %$-1$%; out-of-
2427 * range indices cause failure.
2430 #define frombuf_ienum frombuf_int
2431 #define frombuf_uenum frombuf_uint
2432 #define frombuf_fenum frombuf_float
2433 static int frombuf_penum(buf *b, union tvec_regval *rv,
2434 const struct tvec_regdef *rd)
2436 const struct tvec_penuminfo *pei = rd->arg.p;
2437 const struct tvec_passoc *pa;
2440 for (pa = pei->av, n = 0; pa->tag; pa++, n++);
2441 if (signed_from_buf(b, &i)) return (-1);
2442 if (0 <= i && i < n) rv->p = UNCONST(void, pei->av[i].p);
2443 else if (i == -1) rv->p = 0;
2444 else { buf_break(b); return (-1); }
2448 /* --- @parse_tenum@ --- *
2450 * Arguments: @union tvec_regval *rv@ = register value
2451 * @const struct tvec_regdef *rd@ = register definition
2452 * @struct tvec_state *tv@ = test-vector state
2454 * Returns: Zero on success, %$-1$% on error.
2456 * Use: Parse a register value from an input file.
2458 * An enumerated value may be given by name or as a literal
2459 * value. For enumerations based on numeric types, the literal
2460 * values can be written in the same syntax as the underlying
2461 * values. For enumerations based on pointers, the only
2462 * permitted literal is %|#nil|%, which denotes a null pointer.
2465 #define DEFPARSE_ENUM(tag_, ty, slot) \
2466 static int parse_##slot##enum(union tvec_regval *rv, \
2467 const struct tvec_regdef *rd, \
2468 struct tvec_state *tv) \
2470 const struct tvec_##slot##enuminfo *ei = rd->arg.p; \
2471 const struct tvec_##slot##assoc *a; \
2472 dstr d = DSTR_INIT; \
2475 d.a = &tv->p_test->a; \
2476 if (tvec_readword(tv, &d, 0, \
2477 ";", "%s tag or " LITSTR_##tag_, ei->name)) \
2478 { rc = -1; goto end; } \
2479 for (a = ei->av; a->tag; a++) \
2480 if (STRCMP(a->tag, ==, d.buf)) { FOUND_##tag_ goto done; } \
2488 #define LITSTR_INT "literal signed integer"
2489 #define FOUND_INT rv->i = a->i;
2490 #define MISSING_INT if (parse_signed(&rv->i, d.buf, ei->ir, tv)) \
2491 { rc = -1; goto end; }
2493 #define LITSTR_UINT "literal unsigned integer"
2494 #define FOUND_UINT rv->u = a->u;
2495 #define MISSING_UINT if (parse_unsigned(&rv->u, d.buf, ei->ur, tv)) \
2496 { rc = -1; goto end; }
2498 #define LITSTR_FLT "literal floating-point number, " \
2499 "`#-inf', `#+inf', or `#nan'"
2500 #define FOUND_FLT rv->f = a->f;
2501 #define MISSING_FLT if (parse_floating(&rv->f, 0, d.buf, ei->fi, tv)) \
2502 { rc = -1; goto end; }
2504 #define LITSTR_PTR "`#nil'"
2505 #define FOUND_PTR rv->p = UNCONST(void, a->p);
2506 #define MISSING_PTR if (STRCMP(d.buf, ==, "#nil")) \
2509 tvec_error(tv, "unknown `%s' value `%s'", \
2511 rc = -1; goto end; \
2514 TVEC_MISCSLOTS(DEFPARSE_ENUM)
2532 #undef DEFPARSE_ENUM
2534 /* --- @dump_tenum@ --- *
2536 * Arguments: @const union tvec_regval *rv@ = register value
2537 * @const struct tvec_regdef *rd@ = register definition
2538 * @unsigned style@ = output style (@TVSF_...@)
2539 * @const struct gprintf_ops *gops@, @void *gp@ = format output
2543 * Use: Dump a register value to the format output.
2545 * Enumeration values are dumped as their symbolic names, if
2546 * possible, with the underlying values provided as a comment
2547 * unless compact output is requested, as for the underlying
2548 * representation. A null pointer is printed as %|#nil|%;
2549 * non-null pointers are printed as %|#<TYPE PTR>|%, with the
2550 * enumeration TYPE and the raw pointer PTR printed with the
2551 * system's %|%p|% format specifier.
2555 #define DEFDUMP_ENUM(tag_, ty, slot) \
2556 static void dump_##slot##enum(const union tvec_regval *rv, \
2557 const struct tvec_regdef *rd, \
2559 const struct gprintf_ops *gops, void *go) \
2561 const struct tvec_##slot##enuminfo *ei = rd->arg.p; \
2562 const struct tvec_##slot##assoc *a; \
2564 if (style&TVSF_RAW) gprintf(gops, go, #slot "enum/%s:", ei->name); \
2565 for (a = ei->av; a->tag; a++) \
2566 if (rv->slot == a->slot) { \
2567 gprintf(gops, go, "%s", a->tag); \
2568 if (style&TVSF_COMPACT) return; \
2569 gprintf(gops, go, " ; = "); break; \
2575 #define MAYBE_PRINT_EXTRA \
2576 if (style&TVSF_COMPACT) /* nothing to do */; \
2577 else if (!a->tag) { gprintf(gops, go, " ; = "); goto _extra; } \
2578 else if (1) { gprintf(gops, go, " = "); goto _extra; } \
2581 #define PRINTRAW_INT gprintf(gops, go, "%ld", rv->i); \
2582 MAYBE_PRINT_EXTRA { \
2583 format_signed_hex(gops, go, rv->i); \
2584 maybe_format_signed_char(gops, go, rv->i); \
2587 #define PRINTRAW_UINT gprintf(gops, go, "%lu", rv->u); \
2588 MAYBE_PRINT_EXTRA { \
2589 format_unsigned_hex(gops, go, rv->u); \
2590 maybe_format_unsigned_char(gops, go, rv->u); \
2593 #define PRINTRAW_FLT format_floating(gops, go, rv->f);
2595 #define PRINTRAW_PTR if (!rv->p) gprintf(gops, go, "#nil"); \
2596 else gprintf(gops, go, "#<%s %p>", ei->name, rv->p);
2598 TVEC_MISCSLOTS(DEFDUMP_ENUM)
2601 #undef PRINTRAW_UINT
2605 #undef MAYBE_PRINT_EXTRA
2608 /* Enumeration type definitions. */
2609 #define DEFTY_ENUM(tag, ty, slot) \
2610 const struct tvec_regty tvty_##slot##enum = { \
2611 init_##slot##enum, trivial_release, eq_##slot##enum, copy_##slot##enum, \
2612 tobuf_##slot##enum, frombuf_##slot##enum, \
2613 parse_##slot##enum, dump_##slot##enum \
2615 TVEC_MISCSLOTS(DEFTY_ENUM)
2618 /* Predefined enumeration types. */
2619 static const struct tvec_iassoc bool_assoc[] = {
2636 const struct tvec_ienuminfo tvenum_bool =
2637 { "bool", bool_assoc, &tvrange_int };
2639 static const struct tvec_iassoc cmp_assoc[] = {
2655 const struct tvec_ienuminfo tvenum_cmp =
2656 { "cmp", cmp_assoc, &tvrange_int };
2658 static const struct tvec_passoc dummy_assoc[] = { TVEC_ENDENUM };
2659 static const struct tvec_penuminfo dummy_peinfo = { "pointer", dummy_assoc };
2661 /* --- @tvec_claimeq_tenum@ --- *
2663 * Arguments: @struct tvec_state *tv@ = test-vector state
2664 * @const struct tvec_typeenuminfo *ei@ = enumeration type info
2665 * @ty t0, t1@ = two values
2666 * @const char *file@, @unsigned @lno@ = calling file and line
2667 * @const char *expr@ = the expression to quote on failure
2669 * Returns: Nonzero if @t0@ and @t1@ are equal, otherwise zero.
2671 * Use: Check that values of @t0@ and @t1@ are equal. As for
2672 * @tvec_claim@ above, a test case is automatically begun and
2673 * ended if none is already underway. If the values are
2674 * unequal, then @tvec_fail@ is called, quoting @expr@, and the
2675 * mismatched values are dumped: @t0@ is printed as the output
2676 * value and @t1@ is printed as the input reference.
2679 #define DEFCLAIM(tag, ty, slot) \
2680 int tvec_claimeq_##slot##enum \
2681 (struct tvec_state *tv, \
2682 const struct tvec_##slot##enuminfo *ei, ty e0, ty e1, \
2683 const char *file, unsigned lno, const char *expr) \
2685 union tvec_misc arg; \
2686 struct tvec_reg rval, rref; \
2690 rval.f = rref.f = TVRF_LIVE; \
2691 rval.v.slot = GET_##tag(e0); rref.v.slot = GET_##tag(e1); \
2692 return (tvec_claimeq(tv, &tvty_##slot##enum, &arg, \
2693 &rval, &rref, file, lno, expr)); \
2695 #define PREFLIGHT_INT do ; while (0)
2696 #define GET_INT(e) (e)
2697 #define PREFLIGHT_UINT do ; while (0)
2698 #define GET_UINT(e) (e)
2699 #define PREFLIGHT_FLT do ; while (0)
2700 #define GET_FLT(e) (e)
2701 #define PREFLIGHT_PTR \
2702 if (!ei) ei = &dummy_peinfo
2703 #define GET_PTR(e) (UNCONST(void, (e)))
2704 TVEC_MISCSLOTS(DEFCLAIM)
2706 #undef PREFLIGHT_INT
2708 #undef PREFLIGHT_UINT
2710 #undef PREFLIGHT_FLT
2712 #undef PREFLIGHT_PTR
2715 /*----- Flag types --------------------------------------------------------*/
2717 /* Flag types are initialized, compared, and serialized as unsigned
2721 /* --- @parse_flags@ --- *
2723 * Arguments: @union tvec_regval *rv@ = register value
2724 * @const struct tvec_regdef *rd@ = register definition
2725 * @struct tvec_state *tv@ = test-vector state
2727 * Returns: Zero on success, %$-1$% on error.
2729 * Use: Parse a register value from an input file.
2731 * The input syntax is a sequence of items separated by `|'
2732 * signs. Each item may be the symbolic name of a field value,
2733 * or a literal unsigned integer. The masks associated with the
2734 * given symbolic names must be disjoint. The resulting
2735 * numerical value is simply the bitwise OR of the given values.
2738 static int parse_flags(union tvec_regval *rv, const struct tvec_regdef *rd,
2739 struct tvec_state *tv)
2741 const struct tvec_flaginfo *fi = rd->arg.p;
2742 const struct tvec_flag *f;
2743 unsigned long m = 0, v = 0, t;
2747 d.a = &tv->p_test->a;
2751 /* Read the next item. */
2753 if (tvec_readword(tv, &d, 0, "|;", "%s flag name or integer", fi->name))
2754 { rc = -1; goto end; }
2756 /* Try to find a matching entry in the table. */
2757 for (f = fi->fv; f->tag; f++)
2758 if (STRCMP(f->tag, ==, d.buf)) {
2760 { tvec_error(tv, "colliding flag setting"); rc = -1; goto end; }
2762 { m |= f->m; v |= f->v; goto next; }
2765 /* Otherwise, try to parse it as a raw integer. */
2766 if (parse_unsigned(&t, d.buf, fi->range, tv)) { rc = -1; goto end; }
2767 if (m&t) { tvec_error(tv, "colliding flag setting"); rc = -1; goto end; }
2771 /* Advance to the next token. If it's a separator then consume it, and
2772 * go round again. Otherwise we stop here.
2774 if (tvec_nexttoken(tv)) break;
2776 if (ch != '|') { tvec_syntax(tv, ch, "`|'"); rc = -1; goto end; }
2777 if (tvec_nexttoken(tv)) {
2778 tvec_syntax(tv, '\n', "%s flag name or integer", fi->name);
2789 /* --- @dump_flags@ --- *
2791 * Arguments: @const union tvec_regval *rv@ = register value
2792 * @const struct tvec_regdef *rd@ = register definition
2793 * @unsigned style@ = output style (@TVSF_...@)
2794 * @const struct gprintf_ops *gops@, @void *gp@ = format output
2798 * Use: Dump a register value to the format output.
2800 * The table of symbolic names and their associated values and
2801 * masks is repeatedly scanned, in order, to find disjoint
2802 * matches -- i.e., entries whose value matches the target value
2803 * in the bit positions indicated by the mask, and whose mask
2804 * doesn't overlap with any previously found matches; the names
2805 * are then output, separated by `|'. Any remaining nonzero
2806 * bits not covered by any of the matching masks are output as a
2807 * single literal integer, in hex.
2809 * Unless compact output is requested, or no symbolic names were
2810 * found, the raw numeric value is also printed in hex, as a
2814 static void dump_flags(const union tvec_regval *rv,
2815 const struct tvec_regdef *rd,
2817 const struct gprintf_ops *gops, void *go)
2819 const struct tvec_flaginfo *fi = rd->arg.p;
2820 const struct tvec_flag *f;
2821 unsigned long m = ~0ul, v = rv->u;
2824 if (style&TVSF_RAW) gprintf(gops, go, "flags/%s:", fi->name);
2826 for (f = fi->fv, sep = ""; f->tag; f++)
2827 if ((m&f->m) && (v&f->m) == f->v) {
2828 gprintf(gops, go, "%s%s", sep, f->tag); m &= ~f->m;
2829 sep = style&TVSF_COMPACT ? "|" : " | ";
2832 if (v&m) gprintf(gops, go, "%s0x%0*lx", sep, hex_width(v), v&m);
2833 else if (!v && m == ~0ul) gprintf(gops, go, "0");
2835 if (!(style&(TVSF_COMPACT | TVSF_RAW)))
2836 gprintf(gops, go, " ; = 0x%0*lx", hex_width(rv->u), rv->u);
2839 /* Flags type definition. */
2840 const struct tvec_regty tvty_flags = {
2841 init_uint, trivial_release, eq_uint, copy_uint,
2842 tobuf_uint, frombuf_uint,
2843 parse_flags, dump_flags
2846 /* --- @tvec_claimeq_flags@ --- *
2848 * Arguments: @struct tvec_state *tv@ = test-vector state
2849 * @const struct tvec_flaginfo *fi@ = flags type info
2850 * @unsigned long f0, f1@ = two values
2851 * @const char *file@, @unsigned @lno@ = calling file and line
2852 * @const char *expr@ = the expression to quote on failure
2854 * Returns: Nonzero if @f0@ and @f1@ are equal, otherwise zero.
2856 * Use: Check that values of @f0@ and @f1@ are equal. As for
2857 * @tvec_claim@ above, a test case is automatically begun and
2858 * ended if none is already underway. If the values are
2859 * unequal, then @tvec_fail@ is called, quoting @expr@, and the
2860 * mismatched values are dumped: @f0@ is printed as the output
2861 * value and @f1@ is printed as the input reference.
2864 int tvec_claimeq_flags(struct tvec_state *tv,
2865 const struct tvec_flaginfo *fi,
2866 unsigned long f0, unsigned long f1,
2867 const char *file, unsigned lno, const char *expr)
2869 union tvec_misc arg;
2870 struct tvec_reg rval, rref;
2873 rval.f = rref.f = TVRF_LIVE; rval.v.u = f0; rref.v.u = f1;
2874 return (tvec_claimeq(tv, &tvty_flags, &arg,
2875 &rval, &rref, file, lno, expr));
2878 /*----- Characters --------------------------------------------------------*/
2880 /* Character values are initialized and compared as signed integers. */
2882 /* --- @tobuf_char@ --- *
2884 * Arguments: @buf *b@ = buffer
2885 * @const union tvec_regval *rv@ = register value
2886 * @const struct tvec_regdef *rd@ = register definition
2888 * Returns: Zero on success, %$-1$% on failure.
2890 * Use: Serialize a register value to a buffer.
2892 * Character values are serialized as little-endian 32-bit
2893 * unsigned integers, with %|EOF|% serialized as all-bits-set.
2896 static int tobuf_char(buf *b, const union tvec_regval *rv,
2897 const struct tvec_regdef *rd)
2901 if (0 <= rv->i && rv->i <= UCHAR_MAX) u = rv->i;
2902 else if (rv->i == EOF) u = MASK32;
2903 else { buf_break(b); return (-1); }
2904 return (buf_putu32l(b, u));
2907 /* --- @frombuf_char@ --- *
2909 * Arguments: @buf *b@ = buffer
2910 * @union tvec_regval *rv@ = register value
2911 * @const struct tvec_regdef *rd@ = register definition
2913 * Returns: Zero on success, %$-1$% on failure.
2915 * Use: Deserialize a register value from a buffer.
2917 * Character values are serialized as little-endian 32-bit
2918 * unsigned integers, with %|EOF|% serialized as all-bits-set.
2921 static int frombuf_char(buf *b, union tvec_regval *rv,
2922 const struct tvec_regdef *rd)
2926 if (buf_getu32l(b, &u)) return (-1);
2927 if (0 <= u && u <= UCHAR_MAX) rv->i = u;
2928 else if (u == MASK32) rv->i = EOF;
2929 else { buf_break(b); return (-1); }
2933 /* --- @parse_char@ --- *
2935 * Arguments: @union tvec_regval *rv@ = register value
2936 * @const struct tvec_regdef *rd@ = register definition
2937 * @struct tvec_state *tv@ = test-vector state
2939 * Returns: Zero on success, %$-1$% on error.
2941 * Use: Parse a register value from an input file.
2943 * A character value can be given by symbolic name, with a
2944 * leading `%|#|%'; or a character or `%|\|%'-escape sequence,
2945 * optionally in single quotes.
2947 * The following escape sequences and character names are
2950 * * `%|#eof|%' is the special end-of-file marker.
2952 * * `%|#nul|%' is the NUL character, sometimes used to
2953 * terminate strings.
2955 * * `%|bell|%', `%|bel|%', `%|ding|%', or `%|\a|%' is the BEL
2956 * character used to ring the terminal bell (or do some other
2957 * thing to attract the user's attention).
2959 * * %|#backspace|%, %|#bs|%, or %|\b|% is the backspace
2960 * character, used to move the cursor backwords by one cell.
2962 * * %|#escape|% %|#esc|%, or%|\e|% is the escape character,
2963 * used to introduce special terminal commands.
2965 * * %|#formfeed|%, %|#ff|%, or %|\f|% is the formfeed
2966 * character, used to separate pages of text.
2968 * * %|#newline|%, %|#linefeed|%, %|#lf|%, %|#nl|%, or %|\n|% is
2969 * the newline character, used to terminate lines of text or
2970 * advance the cursor to the next line (perhaps without
2971 * returning it to the start of the line).
2973 * * %|#return|%, %|#carriage-return|%, %|#cr|%, or %|\r|% is
2974 * the carriage-return character, used to return the cursor to
2975 * the start of the line.
2977 * * %|#tab|%, %|#horizontal-tab|%, %|#ht|%, or %|\t|% is the
2978 * tab character, used to advance the cursor to the next tab
2979 * stop on the current line.
2981 * * %|#vertical-tab|%, %|#vt|%, %|\v|% is the vertical tab
2984 * * %|#space|%, %|#spc|% is the space character.
2986 * * %|#delete|%, %|#del|% is the delete character, used to
2987 * erase the most recent character.
2989 * * %|\'|% is the single-quote character.
2991 * * %|\\|% is the backslash character.
2993 * * %|\"|% is the double-quote character.
2995 * * %|\NNN|% or %|\{NNN}|% is the character with code NNN in
2996 * octal. The NNN may be up to three digits long.
2998 * * %|\xNN|% or %|\x{NN}|% is the character with code NNN in
3002 static int parse_char(union tvec_regval *rv, const struct tvec_regdef *rd,
3003 struct tvec_state *tv)
3010 d.a = &tv->p_test->a;
3012 /* Advance until we find something. */
3013 if (tvec_nexttoken(tv))
3014 return (tvec_syntax(tv, fgetc(tv->fp), "character"));
3016 /* Inspect the character to see what we're up against. */
3020 /* It looks like a special token. Push the `%|#|%' back and fetch the
3021 * whole word. If there's just the `%|#|%' after all, then treat it as
3026 if (tvec_readword(tv, &d, 0, ";", "character name"))
3027 { rc = -1; goto end; }
3028 if (STRCMP(d.buf, !=, "#")) {
3029 if (read_charname(&ch, d.buf, RCF_EOFOK)) {
3030 rc = tvec_error(tv, "unknown character name `%s'", d.buf);
3033 rv->i = ch; rc = 0; goto end;
3037 /* If this is a single quote then we expect to see a matching one later,
3038 * and we should process backslash escapes. Get the next character and see
3041 if (ch == '\'') { f |= f_quote; ch = getc(tv->fp); }
3043 /* Main character dispatch. */
3047 /* A newline. If we saw a single quote, then treat that as literal.
3048 * Otherwise this is an error.
3050 if (!(f&f_quote)) goto nochar;
3051 else { f &= ~f_quote; ungetc(ch, tv->fp); ch = '\''; goto plain; }
3054 /* End-of-file. Similar to newline, but with slightly different
3055 * effects on the parse state.
3057 if (!(f&f_quote)) goto nochar;
3058 else { f &= ~f_quote; ch = '\''; goto plain; }
3061 /* A single quote. This must be the second of a pair, and there should
3062 * have been a character or escape sequence between them.
3064 rc = tvec_syntax(tv, ch, "character"); goto end;
3067 /* A backslash. Read a character escape. */
3068 if (read_charesc(&ch, tv)) return (-1);
3071 /* Anything else. Treat as literal. */
3075 /* If we saw an opening quote, then expect the closing quote. */
3078 if (ch != '\'') { rc = tvec_syntax(tv, ch, "`''"); goto end; }
3089 /* --- @dump_char@ --- *
3091 * Arguments: @const union tvec_regval *rv@ = register value
3092 * @const struct tvec_regdef *rd@ = register definition
3093 * @unsigned style@ = output style (@TVSF_...@)
3094 * @const struct gprintf_ops *gops@, @void *gp@ = format output
3098 * Use: Dump a register value to the format output.
3100 * Character values are dumped as their symbolic names, if any,
3101 * or as a character or escape sequence within single quotes
3102 * (which may be omitted in compact style). If compact output
3103 * is not requested, then the single-quoted representation (for
3104 * characters dumped as symbolic names) and integer code in
3105 * decimal and hex are printed as a comment.
3108 static void dump_char(const union tvec_regval *rv,
3109 const struct tvec_regdef *rd,
3111 const struct gprintf_ops *gops, void *go)
3117 if (style&TVSF_RAW) {
3118 /* Print the raw character unconditionally in single quotes. */
3120 gprintf(gops, go, "char:'");
3121 format_char(gops, go, rv->i);
3122 gprintf(gops, go, "'");
3124 /* Print ina pleasant human-readable way. */
3126 /* Print a character name if we can find one. */
3127 p = find_charname(rv->i, (style&TVSF_COMPACT) ? CTF_SHORT : CTF_PREFER);
3129 gprintf(gops, go, "%s", p);
3130 if (style&TVSF_COMPACT) return;
3131 else { gprintf(gops, go, " ;"); f |= f_semi; }
3134 /* If the character isn't @EOF@ then print it as a single-quoted thing.
3135 * In compact style, see if we can omit the quotes.
3138 if (f&f_semi) gprintf(gops, go, " = ");
3140 case ' ': case '\\': case '\'': quote:
3141 format_char(gops, go, rv->i);
3144 if (!(style&TVSF_COMPACT) || !isprint(rv->i)) goto quote;
3145 gprintf(gops, go, "%c", (int)rv->i);
3150 /* And the character code as an integer. */
3151 if (!(style&TVSF_COMPACT)) {
3152 if (!(f&f_semi)) gprintf(gops, go, " ;");
3153 gprintf(gops, go, " = %ld = ", rv->i);
3154 format_signed_hex(gops, go, rv->i);
3161 /* Character type definition. */
3162 const struct tvec_regty tvty_char = {
3163 init_int, trivial_release, eq_int, copy_int,
3164 tobuf_char, frombuf_char,
3165 parse_char, dump_char
3168 /* --- @tvec_claimeq_char@ --- *
3170 * Arguments: @struct tvec_state *tv@ = test-vector state
3171 * @int ch0, ch1@ = two character codes
3172 * @const char *file@, @unsigned @lno@ = calling file and line
3173 * @const char *expr@ = the expression to quote on failure
3175 * Returns: Nonzero if @ch0@ and @ch1@ are equal, otherwise zero.
3177 * Use: Check that values of @ch0@ and @ch1@ are equal. As for
3178 * @tvec_claim@ above, a test case is automatically begun and
3179 * ended if none is already underway. If the values are
3180 * unequal, then @tvec_fail@ is called, quoting @expr@, and the
3181 * mismatched values are dumped: @ch0@ is printed as the output
3182 * value and @ch1@ is printed as the input reference.
3185 int tvec_claimeq_char(struct tvec_state *tv, int c0, int c1,
3186 const char *file, unsigned lno, const char *expr)
3188 struct tvec_reg rval, rref;
3190 rval.f = rref.f = TVRF_LIVE; rval.v.i = c0; rref.v.i = c1;
3191 return (tvec_claimeq(tv, &tvty_char, 0, &rval, &rref, file, lno, expr));
3194 /*----- Text and byte strings ---------------------------------------------*/
3196 /* --- @init_text@, @init_bytes@ --- *
3198 * Arguments: @union tvec_regval *rv@ = register value
3199 * @const struct tvec_regdef *rd@ = register definition
3203 * Use: Initialize a register value.
3205 * Text and binary string values are initialized with a null
3206 * pointer and zero length.
3209 static void init_text(union tvec_regval *rv, const struct tvec_regdef *rd)
3210 { rv->text.p = 0; rv->text.sz = 0; }
3212 static void init_bytes(union tvec_regval *rv, const struct tvec_regdef *rd)
3213 { rv->bytes.p = 0; rv->bytes.sz = 0; }
3215 /* --- @release_string@, @release_bytes@ --- *
3217 * Arguments: @const union tvec_regval *rv@ = register value
3218 * @const struct tvec_regdef *rd@ = register definition
3222 * Use: Release resources held by a register value.
3224 * Text and binary string buffers are freed.
3227 static void release_text(union tvec_regval *rv,
3228 const struct tvec_regdef *rd)
3229 { free(rv->text.p); }
3231 static void release_bytes(union tvec_regval *rv,
3232 const struct tvec_regdef *rd)
3233 { free(rv->bytes.p); }
3235 /* --- @eq_text@, @eq_bytes@ --- *
3237 * Arguments: @const union tvec_regval *rv0, *rv1@ = register values
3238 * @const struct tvec_regdef *rd@ = register definition
3240 * Returns: Nonzero if the values are equal, zero if unequal
3242 * Use: Compare register values for equality.
3245 static int eq_text(const union tvec_regval *rv0,
3246 const union tvec_regval *rv1,
3247 const struct tvec_regdef *rd)
3249 return (rv0->text.sz == rv1->text.sz &&
3251 MEMCMP(rv0->text.p, ==, rv1->text.p, rv1->text.sz)));
3254 static int eq_bytes(const union tvec_regval *rv0,
3255 const union tvec_regval *rv1,
3256 const struct tvec_regdef *rd)
3258 return (rv0->bytes.sz == rv1->bytes.sz &&
3260 MEMCMP(rv0->bytes.p, ==, rv1->bytes.p, rv1->bytes.sz)));
3263 /* --- @copy_text@, @copy_bytes@ --- *
3265 * Arguments: @union tvec_regval *rvd@ = destination register value
3266 * @const union tvec_regval *rvs@ = source register value
3267 * @const struct tvec_regdef *rd@ = register definition
3271 * Use: Copy a register value.
3274 static void copy_text(union tvec_regval *rvd, const union tvec_regval *rvs,
3275 const struct tvec_regdef *rd)
3277 size_t sz = rvs->text.sz;
3282 tvec_alloctext(rvd, sz);
3283 memcpy(rvd->text.p, rvs->text.p, sz); rvd->text.p[sz] = 0;
3287 static void copy_bytes(union tvec_regval *rvd, const union tvec_regval *rvs,
3288 const struct tvec_regdef *rd)
3290 size_t sz = rvs->bytes.sz;
3295 tvec_alloctext(rvd, sz);
3296 memcpy(rvd->bytes.p, rvs->bytes.p, sz);
3300 /* --- @tobuf_text@, @tobuf_bytes@ --- *
3302 * Arguments: @buf *b@ = buffer
3303 * @const union tvec_regval *rv@ = register value
3304 * @const struct tvec_regdef *rd@ = register definition
3306 * Returns: Zero on success, %$-1$% on failure.
3308 * Use: Serialize a register value to a buffer.
3310 * Text and binary string values are serialized as a little-
3311 * endian 64-bit length %$n$% in bytes followed by %$n$% bytes
3315 static int tobuf_text(buf *b, const union tvec_regval *rv,
3316 const struct tvec_regdef *rd)
3317 { return (buf_putmem64l(b, rv->text.p, rv->text.sz)); }
3319 static int tobuf_bytes(buf *b, const union tvec_regval *rv,
3320 const struct tvec_regdef *rd)
3321 { return (buf_putmem64l(b, rv->bytes.p, rv->bytes.sz)); }
3323 /* --- @frombuf_text@, @frombuf_bytes@ --- *
3325 * Arguments: @buf *b@ = buffer
3326 * @union tvec_regval *rv@ = register value
3327 * @const struct tvec_regdef *rd@ = register definition
3329 * Returns: Zero on success, %$-1$% on failure.
3331 * Use: Deserialize a register value from a buffer.
3333 * Text and binary string values are serialized as a little-
3334 * endian 64-bit length %$n$% in bytes followed by %$n$% bytes
3338 static int frombuf_text(buf *b, union tvec_regval *rv,
3339 const struct tvec_regdef *rd)
3344 p = buf_getmem64l(b, &sz); if (!p) return (-1);
3345 tvec_alloctext(rv, sz); memcpy(rv->text.p, p, sz); rv->text.p[sz] = 0;
3349 static int frombuf_bytes(buf *b, union tvec_regval *rv,
3350 const struct tvec_regdef *rd)
3355 p = buf_getmem64l(b, &sz); if (!p) return (-1);
3356 tvec_allocbytes(rv, sz); memcpy(rv->bytes.p, p, sz);
3360 /* --- @check_string_length@ --- *
3362 * Arguments: @size_t sz@ = found string length
3363 * @const struct tvec_urange *ur@ = acceptable range
3364 * @struct tvec_state *tv@ = test-vector state
3366 * Returns: Zero on success, %$-1$% on error.
3368 * Use: Checks that @sz@ is within the bounds described by @ur@,
3369 * reporting an error if not.
3372 static int check_string_length(size_t sz, const struct tvec_urange *ur,
3373 struct tvec_state *tv)
3378 if (ur->min > sz || sz > ur->max) {
3379 tvec_error(tv, "invalid string length %lu; must be in [%lu .. %lu]",
3380 (unsigned long)sz, ur->min, ur->max);
3383 if (ur->m && ur->m != 1) {
3385 if (uu != ur->a%ur->m) {
3386 tvec_error(tv, "invalid string length %lu == %lu =/= %lu (mod %lu)",
3387 (unsigned long)sz, uu, ur->a, ur->m);
3395 /* --- @parse_text@, @parse_bytes@ --- *
3397 * Arguments: @union tvec_regval *rv@ = register value
3398 * @const struct tvec_regdef *rd@ = register definition
3399 * @struct tvec_state *tv@ = test-vector state
3401 * Returns: Zero on success, %$-1$% on error.
3403 * Use: Parse a register value from an input file.
3405 * The input format for both kinds of strings is basically the
3406 * same: a `compound string', consisting of
3408 * * single-quoted strings, which are interpreted entirely
3409 * literally, but can't contain single quotes or newlines;
3411 * * double-quoted strings, in which `%|\|%'-escapes are
3412 * interpreted as for characters;
3414 * * character names, marked by an initial `%|#|%' sign;
3416 * * special tokens marked by an initial `%|!|%' sign; or
3418 * * barewords interpreted according to the current coding
3421 * The special tokens are
3423 * * `%|!bare|%', which causes subsequent sequences of
3424 * barewords to be treated as plain text;
3426 * * `%|!hex|%', `%|!base32|%', `%|!base64|%', which cause
3427 * subsequent barewords to be decoded in the requested
3430 * * `%|!repeat|% %$n$% %|{|% %%\textit{string}%% %|}|%',
3431 * which includes %$n$% copies of the (compound) string.
3433 * The only difference between text and binary strings is that
3434 * the initial coding scheme is %|bare|% for text strings and
3435 * %|hex|% for binary strings.
3438 static int parse_text(union tvec_regval *rv, const struct tvec_regdef *rd,
3439 struct tvec_state *tv)
3441 void *p = rv->text.p;
3443 if (read_compound_string(&p, &rv->text.sz, TVCODE_BARE, 0, tv))
3446 if (check_string_length(rv->text.sz, rd->arg.p, tv)) return (-1);
3450 static int parse_bytes(union tvec_regval *rv, const struct tvec_regdef *rd,
3451 struct tvec_state *tv)
3453 void *p = rv->bytes.p;
3455 if (read_compound_string(&p, &rv->bytes.sz, TVCODE_HEX, 0, tv))
3458 if (check_string_length(rv->bytes.sz, rd->arg.p, tv)) return (-1);
3462 /* --- @dump_text@, @dump_bytes@ --- *
3464 * Arguments: @const union tvec_regval *rv@ = register value
3465 * @const struct tvec_regdef *rd@ = register definition
3466 * @unsigned style@ = output style (@TVSF_...@)
3467 * @const struct gprintf_ops *gops@, @void *gp@ = format output
3471 * Use: Dump a register value to the format output.
3473 * Text string values are dumped as plain text, in double quotes
3474 * if necessary, and using backslash escape sequences for
3475 * nonprintable characters. Unless compact output is requested,
3476 * strings consisting of multiple lines are dumped with each
3477 * line of the string on a separate output line.
3479 * Binary string values are dumped in hexadecimal. In compact
3480 * style, the output simply consists of a single block of hex
3481 * digits. Otherwise, the dump is a display consisting of
3482 * groups of hex digits, with comments showing the offset (if
3483 * the string is long enough) and the corresponding plain text.
3485 * Empty strings are dumped as %|#empty|%.
3488 static void dump_empty(const char *ty, unsigned style,
3489 const struct gprintf_ops *gops, void *go)
3491 if (style&TVSF_RAW) gprintf(gops, go, "%s:", ty);
3492 if (!(style&TVSF_COMPACT)) gprintf(gops, go, "#empty");
3493 if (!(style&(TVSF_COMPACT | TVSF_RAW))) gprintf(gops, go, " ; = ");
3494 if (!(style&TVSF_RAW)) gprintf(gops, go, "\"\"");
3498 static void dump_text(const union tvec_regval *rv,
3499 const struct tvec_regdef *rd,
3501 const struct gprintf_ops *gops, void *go)
3503 const unsigned char *p, *q, *l;
3505 #define f_nonword 1u
3506 #define f_newline 2u
3508 if (!rv->text.sz) { dump_empty("text", style, gops, go); return; }
3510 p = (const unsigned char *)rv->text.p; l = p + rv->text.sz;
3511 if (style&TVSF_RAW) { gprintf(gops, go, "text:"); goto quote; }
3512 else if (style&TVSF_COMPACT) goto quote;
3515 case '!': case '#': case ';': case '"': case '\'':
3516 case '(': case '{': case '[': case ']': case '}': case ')':
3517 f |= f_nonword; break;
3519 for (q = p; q < l; q++)
3520 if (*q == '\n' && q != l - 1) f |= f_newline;
3521 else if (!*q || !ISGRAPH(*q) || *q == '\\') f |= f_nonword;
3522 if (f&f_newline) { gprintf(gops, go, "\n\t"); goto quote; }
3523 else if (f&f_nonword) goto quote;
3525 gops->putm(go, (const char *)p, rv->text.sz);
3529 gprintf(gops, go, "\"");
3530 for (q = p; q < l; q++)
3531 if (!ISPRINT(*q) || *q == '"') {
3532 if (p < q) gops->putm(go, (const char *)p, q - p);
3533 if (*q != '\n' || (style&TVSF_COMPACT))
3534 format_charesc(gops, go, *q, FCF_BRACE);
3536 if (q + 1 == l) { gprintf(gops, go, "\\n\""); return; }
3537 else gprintf(gops, go, "\\n\"\n\t\"");
3541 if (p < q) gops->putm(go, (const char *)p, q - p);
3542 gprintf(gops, go, "\"");
3548 static void dump_bytes(const union tvec_regval *rv,
3549 const struct tvec_regdef *rd,
3551 const struct gprintf_ops *gops, void *go)
3553 const unsigned char *p = rv->bytes.p, *l = p + rv->bytes.sz;
3554 size_t off, sz = rv->bytes.sz;
3558 if (!rv->text.sz) { dump_empty("bytes", style, gops, go); return; }
3560 if (style&(TVSF_COMPACT | TVSF_RAW)) {
3561 if (style&TVSF_RAW) gprintf(gops, go, "bytes:");
3562 while (p < l) gprintf(gops, go, "%02x", *p++);
3566 if (sz <= 16) w = sz;
3567 else { gprintf(gops, go, "\n\t"); w = 16; }
3569 off = 0; wd = hex_width(sz);
3571 if (l - p < 16) n = l - p;
3574 for (i = 0; i < w; i++) {
3575 if (i < n) gprintf(gops, go, "%02x", p[i]);
3576 else gprintf(gops, go, " ");
3577 if (i < w - 1 && i%4 == 3) gprintf(gops, go, " ");
3579 gprintf(gops, go, " ; ");
3580 if (sz > 16) gprintf(gops, go, "[%0*lx] ", wd, (unsigned long)off);
3581 for (i = 0; i < n; i++)
3582 gprintf(gops, go, "%c", isprint(p[i]) ? p[i] : '.');
3584 if (p < l) gprintf(gops, go, "\n\t");
3588 /* Text and byte string type definitions. */
3589 const struct tvec_regty tvty_text = {
3590 init_text, release_text, eq_text, copy_text,
3591 tobuf_text, frombuf_text,
3592 parse_text, dump_text
3594 const struct tvec_regty tvty_bytes = {
3595 init_bytes, release_bytes, eq_bytes, copy_bytes,
3596 tobuf_bytes, frombuf_bytes,
3597 parse_bytes, dump_bytes
3600 /* --- @tvec_claimeq_text@ --- *
3602 * Arguments: @struct tvec_state *tv@ = test-vector state
3603 * @const char *p0@, @size_t sz0@ = first string with length
3604 * @const char *p1@, @size_t sz1@ = second string with length
3605 * @const char *file@, @unsigned @lno@ = calling file and line
3606 * @const char *expr@ = the expression to quote on failure
3608 * Returns: Nonzero if the strings at @p0@ and @p1@ are equal, otherwise
3611 * Use: Check that strings at @p0@ and @p1@ are equal. As for
3612 * @tvec_claim@ above, a test case is automatically begun and
3613 * ended if none is already underway. If the values are
3614 * unequal, then @tvec_fail@ is called, quoting @expr@, and the
3615 * mismatched values are dumped: @p0@ is printed as the output
3616 * value and @p1@ is printed as the input reference.
3619 int tvec_claimeq_text(struct tvec_state *tv,
3620 const char *p0, size_t sz0,
3621 const char *p1, size_t sz1,
3622 const char *file, unsigned lno, const char *expr)
3624 struct tvec_reg rval, rref;
3626 rval.f = rref.f = TVRF_LIVE;
3627 rval.v.text.p = UNCONST(char, p0); rval.v.text.sz = sz0;
3628 rref.v.text.p = UNCONST(char, p1); rref.v.text.sz = sz1;
3629 return (tvec_claimeq(tv, &tvty_text, 0, &rval, &rref, file, lno, expr));
3632 /* --- @tvec_claimeq_textz@ --- *
3634 * Arguments: @struct tvec_state *tv@ = test-vector state
3635 * @const char *p0, *p1@ = two strings to compare
3636 * @const char *file@, @unsigned @lno@ = calling file and line
3637 * @const char *expr@ = the expression to quote on failure
3639 * Returns: Nonzero if the strings at @p0@ and @p1@ are equal, otherwise
3642 * Use: Check that strings at @p0@ and @p1@ are equal, as for
3643 * @tvec_claimeq_string@, except that the strings are assumed
3644 * null-terminated, so their lengths don't need to be supplied
3648 int tvec_claimeq_textz(struct tvec_state *tv,
3649 const char *p0, const char *p1,
3650 const char *file, unsigned lno, const char *expr)
3652 struct tvec_reg rval, rref;
3654 rval.f = rref.f = TVRF_LIVE;
3655 rval.v.text.p = UNCONST(char, p0); rval.v.text.sz = strlen(p0);
3656 rref.v.text.p = UNCONST(char, p1); rref.v.text.sz = strlen(p1);
3657 return (tvec_claimeq(tv, &tvty_text, 0, &rval, &rref, file, lno, expr));
3660 /* --- @tvec_claimeq_bytes@ --- *
3662 * Arguments: @struct tvec_state *tv@ = test-vector state
3663 * @const void *p0@, @size_t sz0@ = first string with length
3664 * @const void *p1@, @size_t sz1@ = second string with length
3665 * @const char *file@, @unsigned @lno@ = calling file and line
3666 * @const char *expr@ = the expression to quote on failure
3668 * Returns: Nonzero if the strings at @p0@ and @p1@ are equal, otherwise
3671 * Use: Check that binary strings at @p0@ and @p1@ are equal. As for
3672 * @tvec_claim@ above, a test case is automatically begun and
3673 * ended if none is already underway. If the values are
3674 * unequal, then @tvec_fail@ is called, quoting @expr@, and the
3675 * mismatched values are dumped: @p0@ is printed as the output
3676 * value and @p1@ is printed as the input reference.
3679 int tvec_claimeq_bytes(struct tvec_state *tv,
3680 const void *p0, size_t sz0,
3681 const void *p1, size_t sz1,
3682 const char *file, unsigned lno, const char *expr)
3684 struct tvec_reg rval, rref;
3686 rval.f = rref.f = TVRF_LIVE;
3687 rval.v.bytes.p = UNCONST(void, p0); rval.v.bytes.sz = sz0;
3688 rref.v.bytes.p = UNCONST(void, p1); rref.v.bytes.sz = sz1;
3689 return (tvec_claimeq(tv, &tvty_bytes, 0, &rval, &rref, file, lno, expr));
3692 /* --- @tvec_alloctext@, @tvec_allocbytes@ --- *
3694 * Arguments: @union tvec_regval *rv@ = register value
3695 * @size_t sz@ = required size
3699 * Use: Allocated space in a text or binary string register. If the
3700 * current register size is sufficient, its buffer is left
3701 * alone; otherwise, the old buffer, if any, is freed and a
3702 * fresh buffer allocated. These functions are not intended to
3703 * be used to adjust a buffer repeatedly, e.g., while building
3704 * output incrementally: (a) they will perform badly, and (b)
3705 * the old buffer contents are simply discarded if reallocation
3706 * is necessary. Instead, use a @dbuf@ or @dstr@.
3708 * The @tvec_alloctext@ function sneakily allocates an extra
3709 * byte for a terminating zero. The @tvec_allocbytes@ function
3713 void tvec_alloctext(union tvec_regval *rv, size_t sz)
3715 if (rv->text.sz <= sz)
3716 { free(rv->text.p); rv->text.p = x_alloc(&arena_stdlib, sz + 1); }
3717 memset(rv->text.p, '?', sz); rv->text.p[sz] = 0; rv->text.sz = sz;
3720 void tvec_allocbytes(union tvec_regval *rv, size_t sz)
3722 if (rv->bytes.sz < sz)
3723 { free(rv->bytes.p); rv->bytes.p = x_alloc(&arena_stdlib, sz); }
3724 memset(rv->bytes.p, '?', sz); rv->bytes.sz = sz;
3727 /*----- Buffer type -------------------------------------------------------*/
3729 /* --- @init_buffer@ --- *
3731 * Arguments: @union tvec_regval *rv@ = register value
3732 * @const struct tvec_regdef *rd@ = register definition
3736 * Use: Initialize a register value.
3738 * Buffer values values are initialized with a null pointer,
3739 * zero length, and zero residue, modulus, and offset.
3742 static void init_buffer(union tvec_regval *rv, const struct tvec_regdef *rd)
3743 { rv->buf.p = 0; rv->buf.sz = rv->buf.a = rv->buf.m = rv->buf.off = 0; }
3745 /* --- @release_buffer@, @release_bytes@ --- *
3747 * Arguments: @const union tvec_regval *rv@ = register value
3748 * @const struct tvec_regdef *rd@ = register definition
3752 * Use: Release resources held by a register value.
3754 * Buffers are freed.
3757 static void release_buffer(union tvec_regval *rv,
3758 const struct tvec_regdef *rd)
3759 { if (rv->buf.p) free(rv->buf.p - rv->buf.off); }
3761 /* --- @eq_buffer@ --- *
3763 * Arguments: @const union tvec_regval *rv0, *rv1@ = register values
3764 * @const struct tvec_regdef *rd@ = register definition
3766 * Returns: Nonzero if the values are equal, zero if unequal
3768 * Use: Compare register values for equality.
3770 * Buffer values are equal if and only if their sizes and
3771 * alignment parameters are equal; their contents are
3772 * %%\emph{not}%% compared.
3775 static int eq_buffer(const union tvec_regval *rv0,
3776 const union tvec_regval *rv1,
3777 const struct tvec_regdef *rd)
3779 return (rv0->buf.sz == rv1->buf.sz &&
3780 rv0->buf.a == rv1->buf.a &&
3781 rv0->buf.m == rv1->buf.m);
3784 /* --- @copy_buffer@ --- *
3786 * Arguments: @union tvec_regval *rvd@ = destination register value
3787 * @const union tvec_regval *rvs@ = source register value
3788 * @const struct tvec_regdef *rd@ = register definition
3792 * Use: Copy a register value.
3795 static void copy_buffer(union tvec_regval *rvd, const union tvec_regval *rvs,
3796 const struct tvec_regdef *rd)
3798 if (rvd->buf.p) { free(rvd->buf.p); rvd->buf.p = 0; }
3799 rvd->buf.sz = rvs->buf.sz;
3800 rvd->buf.a = rvs->buf.a;
3801 rvd->buf.m = rvs->buf.m;
3805 /* --- @tobuf_buffer@ --- *
3807 * Arguments: @buf *b@ = buffer
3808 * @const union tvec_regval *rv@ = register value
3809 * @const struct tvec_regdef *rd@ = register definition
3811 * Returns: Zero on success, %$-1$% on failure.
3813 * Use: Serialize a register value to a buffer.
3815 * Buffer values are serialized as their lengths, residues, and
3816 * moduli, as unsigned integers.
3819 static int tobuf_buffer(buf *b, const union tvec_regval *rv,
3820 const struct tvec_regdef *rd)
3822 return (unsigned_to_buf(b, rv->buf.sz) ||
3823 unsigned_to_buf(b, rv->buf.a) ||
3824 unsigned_to_buf(b, rv->buf.m));
3827 /* --- @frombuf_buffer@ --- *
3829 * Arguments: @buf *b@ = buffer
3830 * @union tvec_regval *rv@ = register value
3831 * @const struct tvec_regdef *rd@ = register definition
3833 * Returns: Zero on success, %$-1$% on failure.
3835 * Use: Deserialize a register value from a buffer.
3837 * Buffer values are serialized as just their lengths, as
3838 * unsigned integers. The buffer is allocated on
3839 * deserialization and filled with a distinctive pattern.
3842 static int frombuf_buffer(buf *b, union tvec_regval *rv,
3843 const struct tvec_regdef *rd)
3845 unsigned long sz, a, m;
3847 if (unsigned_from_buf(b, &sz)) return (-1);
3848 if (unsigned_from_buf(b, &a)) return (-1);
3849 if (unsigned_from_buf(b, &m)) return (-1);
3850 if (sz > (size_t)-1 || a > (size_t)-1 || m > (size_t)-1)
3851 { buf_break(b); return (-1); }
3852 rv->buf.sz = sz; rv->buf.a = a; rv->buf.m = m;
3856 /* --- @parse_buffer@ --- *
3858 * Arguments: @union tvec_regval *rv@ = register value
3859 * @const struct tvec_regdef *rd@ = register definition
3860 * @struct tvec_state *tv@ = test-vector state
3862 * Returns: Zero on success, %$-1$% on error.
3864 * Use: Parse a register value from an input file.
3866 * The input format for a buffer value is a size, followed by an
3867 * optional `%|@$%' and an alignment quantum and a further
3868 * optional `%|+|%' and an alignment offset. The size, quantum,
3869 * and offset are syntactically sizes.
3871 * The buffer is not allocated.
3874 static int parse_buffer(union tvec_regval *rv,
3875 const struct tvec_regdef *rd,
3876 struct tvec_state *tv)
3878 unsigned long sz, a = 0, m = 0;
3881 if (parse_szint(tv, &sz, "@;", "buffer length")) { rc = -1; goto end; }
3882 if (check_unsigned_range(sz, &tvrange_size, tv, "buffer length"))
3883 { rc = -1; goto end; }
3884 if (check_string_length(sz, rd->arg.p, tv)) { rc = -1; goto end; }
3886 if (tvec_nexttoken(tv)) goto done;
3888 if (ch != '@') { rc = tvec_syntax(tv, ch, "`@'"); goto end; }
3890 if (parse_szint(tv, &m, "+;", "alignment quantum")) { rc = -1; goto end; }
3891 if (check_unsigned_range(a, &tvrange_size, tv, "alignment quantum"))
3892 { rc = -1; goto end; }
3895 if (tvec_nexttoken(tv)) goto done;
3897 if (ch != '+') { rc = tvec_syntax(tv, ch, "`+'"); goto end; }
3899 if (parse_szint(tv, &a, ";", "alignment offset")) { rc = -1; goto end; }
3900 if (check_unsigned_range(m, &tvrange_size, tv, "alignment offset"))
3901 { rc = -1; goto end; }
3903 rc = tvec_error(tv, "alignment offset %lu >= quantum %lu",
3904 (unsigned long)a, (unsigned long)m);
3909 rv->buf.sz = sz; rv->buf.a = a; rv->buf.m = m;
3915 /* --- @dump_buffer@ --- *
3917 * Arguments: @const union tvec_regval *rv@ = register value
3918 * @const struct tvec_regdef *rd@ = register definition
3919 * @unsigned style@ = output style (@TVSF_...@)
3920 * @const struct gprintf_ops *gops@, @void *gp@ = format output
3924 * Use: Dump a register value to the format output.
3926 * Buffer values are dumped as their size, with the alignment
3927 * quantum and alignment offset if these are non-default.
3930 static void dump_buffer(const union tvec_regval *rv,
3931 const struct tvec_regdef *rd,
3933 const struct gprintf_ops *gops, void *go)
3935 if (style&TVSF_RAW) gprintf(gops, go, "buffer:");
3936 format_size(gops, go, rv->buf.sz, style);
3938 gprintf(gops, go, style&(TVSF_COMPACT | TVSF_RAW) ? "@" : " @ ");
3939 format_size(gops, go, rv->buf.m, style);
3941 gprintf(gops, go, style&(TVSF_COMPACT | TVSF_RAW) ? "+" : " + ");
3942 format_size(gops, go, rv->buf.a, style);
3945 if (!(style&(TVSF_COMPACT | TVSF_RAW))) {
3946 gprintf(gops, go, " ; = %lu", (unsigned long)rv->buf.sz);
3948 gprintf(gops, go, " @ %lu", (unsigned long)rv->buf.m);
3949 if (rv->buf.a) gprintf(gops, go, " + %lu", (unsigned long)rv->buf.a);
3951 gprintf(gops, go, " = "); format_unsigned_hex(gops, go, rv->buf.sz);
3953 gprintf(gops, go, " @ "); format_unsigned_hex(gops, go, rv->buf.m);
3955 gprintf(gops, go, " + ");
3956 format_unsigned_hex(gops, go, rv->buf.a);
3962 /* Buffer type definition. */
3963 const struct tvec_regty tvty_buffer = {
3964 init_buffer, release_buffer, eq_buffer, copy_buffer,
3965 tobuf_buffer, frombuf_buffer,
3966 parse_buffer, dump_buffer
3969 /* --- @tvec_initbuffer@ --- *
3971 * Arguments: @union tvec_regval *rv@ = register value
3972 * @const union tvec_regval *ref@ = source buffer
3973 * @size_t sz@ = size to allocate
3977 * Use: Initialize the alignment parameters in @rv@ to match @ref@,
3978 * and the size to @sz@.
3981 void tvec_initbuffer(union tvec_regval *rv,
3982 const union tvec_regval *ref, size_t sz)
3983 { rv->buf.sz = sz; rv->buf.a = ref->buf.a; rv->buf.m = ref->buf.m; }
3985 /* --- @tvec_allocbuffer@ --- *
3987 * Arguments: @union tvec_regval *rv@ = register value
3991 * Use: Allocate @sz@ bytes to the buffer and fill the space with a
3992 * distinctive pattern.
3995 void tvec_allocbuffer(union tvec_regval *rv)
3998 size_t m = rv->buf.m, a = rv->buf.a, off;
4000 if (rv->buf.p) free(rv->buf.p - rv->buf.off);
4003 p = x_alloc(&arena_stdlib, rv->buf.sz); off = 0;
4005 p = x_alloc(&arena_stdlib, rv->buf.sz + m - 1);
4006 if (!(m&(m - 1))) off = (a - (size_t)p)&(m - 1);
4007 else off = (a + m - (size_t)p%m)%m;
4009 rv->buf.p = p + off; rv->buf.off = off;
4010 memset(rv->buf.p, '?', rv->buf.sz);
4013 /*----- That's all, folks -------------------------------------------------*/