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 /*----- Preliminary utilities ---------------------------------------------*/
50 /* --- @trivial_release@ --- *
52 * Arguments: @union tvec_regval *rv@ = a register value
53 * @const struct tvec_regdef@ = the register definition
57 * Use: Does nothing. Used for register values which don't retain
61 static void trivial_release(union tvec_regval
*rv
,
62 const struct tvec_regdef
*rd
)
65 /*----- Integer utilities -------------------------------------------------*/
67 /* --- @unsigned_to_buf@, @signed_to_buf@ --- *
69 * Arguments: @buf *b@ = buffer to write on
70 * @unsigned long u@ or @long i@ = integer to write
72 * Returns: Zero on success, @-1@ on failure.
74 * Use: Write @i@ to the buffer, in big-endian (two's-complement, it
78 static int unsigned_to_buf(buf
*b
, unsigned long u
)
79 { kludge64 k
; ASSIGN64(k
, u
); return (buf_putk64l(b
, k
)); }
81 static int signed_to_buf(buf
*b
, long i
)
87 if (i
>= 0) ASSIGN64(k
, u
);
88 else { ASSIGN64(k
, ~u
); CPL64(k
, k
); }
89 return (buf_putk64l(b
, k
));
92 /* --- @unsigned_from_buf@, @signed_from_buf@ --- *
94 * Arguments: @buf *b@ = buffer to write on
95 * @unsigned long *u_out@ or @long *i_out@ = where to put the
98 * Returns: Zero on success, @-1@ on failure.
100 * Use: Read an integer, in big-endian (two's-complement, if signed)
101 * format, from the buffer.
104 static int unsigned_from_buf(buf
*b
, unsigned long *u_out
)
108 ASSIGN64(ulmax
, ULONG_MAX
);
109 if (buf_getk64l(b
, &k
)) return (-1);
110 if (CMP64(k
, >, ulmax
)) { buf_break(b
); return (-1); }
111 *u_out
= GET64(unsigned long, k
); return (0);
114 /* --- @hex_width@ --- *
116 * Arguments: @unsigned long u@ = an integer
118 * Returns: A suitable number of digits to use in order to display @u@ in
119 * hex. Currently, we select a power of two sufficient to show
120 * the value, but at least 2.
123 static int hex_width(unsigned long u
)
128 for (t
= u
>> 4, wd
= 4; t
>>= wd
, wd
*= 2, t
; );
132 /* --- @format_unsigned_hex@, @format_signed_hex@ --- *
134 * Arguments: @const struct gprintf_ops *gops@ = print operations
135 * @void *go@ = print destination
136 * @unsigned long u@ or @long i@ = integer to print
140 * Use: Print an unsigned or signed integer in hexadecimal.
143 static void format_unsigned_hex(const struct gprintf_ops
*gops
, void *go
,
145 { gprintf(gops
, go
, "0x%0*lx", hex_width(u
), u
); }
147 static void format_signed_hex(const struct gprintf_ops
*gops
, void *go
,
150 unsigned long u
= i
>= 0 ? i
: -(unsigned long)i
;
151 gprintf(gops
, go
, "%s0x%0*lx", i
< 0 ?
"-" : "", hex_width(u
), u
);
154 static int signed_from_buf(buf
*b
, long *i_out
)
156 kludge64 k
, lmax
, not_lmin
;
158 ASSIGN64(lmax
, LONG_MAX
); ASSIGN64(not_lmin
, ~(unsigned long)LONG_MIN
);
159 if (buf_getk64l(b
, &k
)) return (-1);
160 if (CMP64(k
, <=, lmax
)) *i_out
= (long)GET64(unsigned long, k
);
163 if (CMP64(k
, <=, not_lmin
)) *i_out
= -(long)GET64(unsigned long, k
) - 1;
164 else { buf_break(b
); return (-1); }
169 /* --- @check_unsigned_range@, @check_signed_range@ --- *
171 * Arguments: @unsigned long u@ or @long i@ = an integer
172 * @const struct tvec_urange *ur@ or
173 * @const struct tvec_irange *ir@ = range specification,
175 * @struct tvec_state *tv@ = test vector state
177 * Returns: Zero on success, or @-1@ on error.
179 * Use: Check that the integer is within bounds. If not, report a
180 * suitable error and return a failure indication.
183 static int check_signed_range(long i
,
184 const struct tvec_irange
*ir
,
185 struct tvec_state
*tv
)
187 if (ir
&& (ir
->min
> i
|| i
> ir
->max
)) {
188 tvec_error(tv
, "integer %ld out of range (must be in [%ld .. %ld])",
189 i
, ir
->min
, ir
->max
);
195 static int check_unsigned_range(unsigned long u
,
196 const struct tvec_urange
*ur
,
197 struct tvec_state
*tv
)
199 if (ur
&& (ur
->min
> u
|| u
> ur
->max
)) {
200 tvec_error(tv
, "integer %lu out of range (must be in [%lu .. %lu])",
201 u
, ur
->min
, ur
->max
);
207 /* --- @chtodig@ --- *
209 * Arguments: @int ch@ = a character
211 * Returns: The numeric value of the character as a digit, or @-1@ if
212 * it's not a digit. Letters count as extended digits starting
213 * with value 10; case is not significant.
216 static int chtodig(int ch
)
218 if ('0' <= ch
&& ch
<= '9') return (ch
- '0');
219 else if ('a' <= ch
&& ch
<= 'z') return (ch
- 'a' + 10);
220 else if ('A' <= ch
&& ch
<= 'Z') return (ch
- 'A' + 10);
224 /* --- @parse_unsigned_integer@, @parse_signed_integer@ --- *
226 * Arguments: @unsigned long *u_out@, @long *i_out@ = where to put the
228 * @const char **q_out@ = where to put the end position
229 * @const char *p@ = pointer to the string to parse
231 * Returns: Zero on success, @-1@ on error.
233 * Use: Parse an integer from a string in the test-vector format.
234 * This is mostly extension of the traditional C @strtoul@
235 * format: supported inputs include:
237 * * NNN -- a decimal number (even if it starts with `0');
238 * * 0xNNN -- hexadecimal;
241 * * NNrNNN -- base NN.
243 * Furthermore, single underscores are permitted internally as
244 * an insignificant digit separator.
247 static int parse_unsigned_integer(unsigned long *u_out
, const char **q_out
,
254 #define f_implicit 1u /* implicitly reading base 10 */
255 #define f_digit 2u /* read a real digit */
256 #define f_uscore 4u /* found an underscore */
260 * This will deal with the traditional `0[box]...' prefixes. We'll leave
261 * our new `NNr...' syntax for later.
263 if (p
[0] != '0' || !p
[1]) {
264 d
= chtodig(*p
); if (0 > d
|| d
>= 10) return (-1);
265 r
= 10; u
= d
; p
++; f
|= f_implicit
| f_digit
;
267 u
= 0; d
= chtodig(p
[2]);
268 if (d
< 0) { r
= 10; f
|= f_implicit
| f_digit
; p
++; }
269 else if ((p
[1] == 'x' || p
[1] == 'X') && d
< 16) { r
= 16; p
+= 2; }
270 else if ((p
[1] == 'o' || p
[1] == 'O') && d
< 8) { r
= 8; p
+= 2; }
271 else if ((p
[1] == 'b' || p
[1] == 'B') && d
< 2) { r
= 2; p
+= 2; }
272 else { r
= 10; f
|= f_digit
; p
++; }
277 /* Work through the string a character at a time. */
279 ch
= *p
; switch (ch
) {
282 /* An underscore is OK if we haven't just seen one. */
284 if (f
&f_uscore
) goto done
;
285 p
++; f
= (f
&~f_implicit
) | f_uscore
;
289 /* An `r' is OK if the number so far is small enough to be a sensible
290 * base, and we're scanning decimal implicitly.
293 if (!(f
&f_implicit
) || !u
|| u
>= 36) goto done
;
294 d
= chtodig(p
[1]); if (0 > d
|| d
>= u
) goto done
;
295 r
= u
; u
= d
; f
= (f
&~f_implicit
) | f_digit
; p
+= 2; q
= p
;
299 /* Otherwise we expect a valid digit and accumulate it. */
300 d
= chtodig(ch
); if (d
< 0 || d
>= r
) goto done
;
301 if (u
> ULONG_MAX
/r
) return (-1);
302 u
*= r
; if (u
> ULONG_MAX
- d
) return (-1);
303 u
+= d
; f
= (f
&~f_uscore
) | f_digit
; p
++; q
= p
;
309 if (!(f
&f_digit
)) return (-1);
310 *u_out
= u
; *q_out
= q
; return (0);
317 static int parse_signed_integer(long *i_out
, const char **q_out
,
324 /* Read an initial sign. */
326 else if (*p
== '-') { f
|= f_neg
; p
++; }
328 /* Scan an unsigned number. */
329 if (parse_unsigned_integer(&u
, q_out
, p
)) return (-1);
331 /* Check for signed overflow and apply the sign. */
333 if (u
> LONG_MAX
) return (-1);
336 if (u
&& u
- 1 > -(LONG_MIN
+ 1)) return (-1);
337 *i_out
= u ?
-(long)(u
- 1) - 1 : 0;
345 /* --- @parse_unsigned@, @parse_signed@ --- *
347 * Arguments: @unsigned long *u_out@ or @long *i_out@ = where to put the
349 * @const char *p@ = string to parse
350 * @const struct tvec_urange *ur@ or
351 * @const struct tvec_irange *ir@ = range specification,
353 * @struct tvec_state *tv@ = test vector state
355 * Returns: Zero on success, @-1@ on error.
357 * Use: Parse and range-check an integer. Unlike @parse_(un)signed_
358 * integer@, these functions check that there's no cruft
359 * following the final digit, and report errors as they find
360 * them rather than leaving that to the caller.
363 static int parse_unsigned(unsigned long *u_out
, const char *p
,
364 const struct tvec_urange
*ur
,
365 struct tvec_state
*tv
)
370 if (parse_unsigned_integer(&u
, &q
, p
))
371 return (tvec_error(tv
, "invalid unsigned integer `%s'", p
));
372 if (*q
) return (tvec_syntax(tv
, *q
, "end-of-line"));
373 if (check_unsigned_range(u
, ur
, tv
)) return (-1);
374 *u_out
= u
; return (0);
377 static int parse_signed(long *i_out
, const char *p
,
378 const struct tvec_irange
*ir
,
379 struct tvec_state
*tv
)
384 if (parse_signed_integer(&i
, &q
, p
))
385 return (tvec_error(tv
, "invalid signed integer `%s'", p
));
386 if (*q
) return (tvec_syntax(tv
, *q
, "end-of-line"));
387 if (check_signed_range(i
, ir
, tv
)) return (-1);
388 *i_out
= i
; return (0);
390 static const char size_units
[] = "kMGTPEZY";
392 /* --- @parse_size@ --- *
394 * Arguments: @struct tvec_state *tv@ = test-vector state
395 * @size_t *u_out@ = where to put the answer
396 * @const char *delims@ = delimiters
397 * @const char *what@ = description of what we're parsing
399 * Returns: Zero on success, %$-1$% on failure.
401 * Use: Parse a memory size.
404 static int parse_size(struct tvec_state
*tv
, size_t *u_out
,
405 const char *delims
, const char *what
)
408 const char *p
, *unit
;
414 if (tvec_readword(tv
, &d
, 0, delims
, what
)) { rc
= -1; goto end
; }
416 if (parse_unsigned_integer(&u
, &p
, p
)) goto bad
;
417 if (!*p
) tvec_readword(tv
, &d
, &p
, delims
, 0);
419 if (u
> (size_t)-1) goto rangerr
;
420 for (t
= u
, unit
= size_units
; *unit
; unit
++) {
421 if (t
> (size_t)-1/1024) f
|= f_range
;
424 if (f
&f_range
) goto rangerr
;
437 tvec_error(tv
, "invalid %s `%s'", what
, d
.buf
);
441 tvec_error(tv
, "%s `%s' out of range", what
, d
.buf
);
447 /* --- @format_size@ --- *
449 * Arguments: @const struct gprintf_ops *gops@ = print operations
450 * @void *go@ = print destination
451 * @unsigned long u@ = a size
452 * @unsigned style@ = style (@TVSF_...@)
456 * Use: Format @u@ as a size in bytes to the destination, expressing
457 * it with a unit prefix if this is possible exactly.
460 static void format_size(const struct gprintf_ops
*gops
, void *go
,
461 unsigned long u
, unsigned style
)
466 gprintf(gops
, go
, "%lu%sB", u
, style
&TVSF_COMPACT ?
"" : " ");
468 for (unit
= size_units
, u
/= 1024;
469 !(u
%1024) && unit
[1];
471 gprintf(gops
, go
, "%lu%s%cB", u
, style
&TVSF_COMPACT ?
"" : " ", *unit
);
475 /*----- Floating-point utilities ------------------------------------------*/
477 /* --- @eqish_floating_p@ --- *
479 * Arguments: @double x, y@ = two numbers to compare
480 * @const struct tvec_floatinfo *fi@ = floating-point info
482 * Returns: Nonzero if the comparand @y@ is sufficiently close to the
483 * reference @x@, or zero if it's definitely different.
486 static int eqish_floating_p(double x
, double y
,
487 const struct tvec_floatinfo
*fi
)
491 if (NANP(x
)) return (NANP(y
)); else if (NANP(y
)) return (0);
492 if (INFP(x
)) return (x
== y
); else if (INFP(y
)) return (0);
494 switch (fi ? fi
->f
&TVFF_EQMASK
: TVFF_EXACT
) {
496 return (x
== y
&& NEGP(x
) == NEGP(y
));
498 t
= x
- y
; if (t
< 0) t
= -t
; return (t
< fi
->delta
);
500 t
= 1.0 - y
/x
; if (t
< 0) t
= -t
; return (t
< fi
->delta
);
506 /* --- @format_floating@ --- *
508 * Arguments: @const struct gprintf_ops *gops@ = print operations
509 * @void *go@ = print destination
510 * @double x@ = number to print
514 * Use: Print a floating-point number, accurately.
517 static void format_floating(const struct gprintf_ops
*gops
, void *go
,
523 gprintf(gops
, go
, "#nan");
525 gprintf(gops
, go
, x
> 0 ?
"#+inf" : "#-inf");
527 /* Ugh. C doesn't provide any function for just printing a
528 * floating-point number /correctly/, i.e., so that you can read the
529 * result back and recover the number you first thought of. There are
530 * complicated algorithms published for doing this, but I really don't
531 * want to get into that here. So we have this.
533 * The sign doesn't cause significant difficulty so we're going to ignore
534 * it for now. So suppose we're given a number %$x = f b^e$%, in
535 * base-%$b$% format, so %$f b^n$% and %$e$% are integers, with
536 * %$0 \le f < 1$%. We're going to convert it into the nearest integer
537 * of the form %$X = F B^E$%, with similar conditions, only with the
538 * additional requirement that %$X$% is normalized, i.e., that %$X = 0$%
539 * or %$F \ge B^{-N}$%.
541 * We're rounding to the nearest such %$X$%. If there is to be ambiguity
542 * in the conversion, then some %$x = f b^e$% and the next smallest
543 * representable number %$x' = x + b^{e-n}$% must both map to the same
544 * %$X$%, which means both %$x$% and %$x'$% must be nearer to %$X$% than
545 * any other number representable in the target system. The nest larger
546 * number is %$X' = X + B^{E-N}$%; the next smaller number will normally
547 * be %$W = X - B^{E-N}$%, but if %$F = 1/B$ then the next smaller number
548 * is actually %$X - B^{E-N-1}$%. We ignore this latter possibility in
549 * the pursuit of a conservative estimate (though actually it doesn't
552 * If both %$x$% and %$x'$% map to %$X$% then we must have
553 * %$L = X - B^{E-N}/2 \le x$% and %$x + b^{e-n} \le R = X + B^{E-N}/2$%;
554 * so firstly %$f b^e = x \ge L = W + B^{E-N}/2 > W = (F - B^{-N}) B^E$%,
555 * and secondly %$b^{e-n} \le B^{E-N}$%. Since these inequalities are in
556 * opposite senses, we can divide, giving
558 * %$f b^e/b^{e-n} > (F - B^{-N}) B^E/B^{E-N}$% ,
562 * %$f b^n > (F - B^{-N}) B^N = F B^N - 1$% .
564 * Now %$f \le 1 - b^{-n}$%, and %$F \ge B^{-1}$%, so, for this to be
565 * possible, it must be the case that
567 * %$(1 - b^{-n}) b^n = b^n - 1 > B^{N-1} - 1$% .
569 * Then rearrange and take logarithms, obtaining
571 * %$(N - 1) \log B < n \log b$% ,
575 * %$N < n \log b/\log B + 1$% .
577 * Recall that this is a necessary condition for a collision to occur; we
578 * are therefore safe whenever
580 * %$N \ge n \log b/\log B + 1$% ;
582 * so, taking ceilings,
584 * %$N \ge \lceil n \log b/\log B \rceil + 1$% .
586 * So that's why we have this.
588 * I'm going to assume that @n = DBL_MANT_DIG@ is sufficiently small that
589 * we can calculate this without ending up on the wrong side of an
592 * In C11, we have @DBL_DECIMAL_DIG@, which should be the same value only
593 * as a constant. Except that modern compilers are more than clever
594 * enough to work out that this is a constant anyway.
596 * This is sometimes an overestimate: we'll print out meaningless digits
597 * that don't represent anything we actually know about the number in
598 * question. To fix that, we'd need a complicated algorithm like Steele
599 * and White's Dragon4, Gay's @dtoa@, or Burger and Dybvig's algorithm
600 * (note that Loitsch's Grisu2 is conservative, and Grisu3 hands off to
601 * something else in difficult situations).
604 prec
= ceil(DBL_MANT_DIG
*log(FLT_RADIX
)/log(10)) + 1;
605 gprintf(gops
, go
, "%.*g", prec
, x
);
609 /* --- @parse_floating@ --- *
611 * Arguments: @double *x_out@ = where to put the result
612 * @const char *q_out@ = where to leave end pointer, or null
613 * @const char *p@ = string to parse
614 * @const struct tvec_floatinfo *fi@ = floating-point info
615 * @struct tvec_state *tv@ = test vector state
617 * Returns: Zero on success, @-1@ on error.
619 * Use: Parse a floating-point number from a string. Reports any
620 * necessary errors. If @q_out@ is not null then trailing
621 * material is permitted and a pointer to it (or the end of the
622 * string) is left in @*q_out@.
625 static int parse_floating(double *x_out
, const char **q_out
, const char *p
,
626 const struct tvec_floatinfo
*fi
,
627 struct tvec_state
*tv
)
629 const char *pp
; char *q
;
634 /* Check for special tokens. */
635 if (STRCMP(p
, ==, "#nan")) {
637 if (q_out
) *q_out
= p
+ strlen(p
);
640 tvec_error(tv
, "NaN not supported on this system");
645 else if (STRCMP(p
, ==, "#inf") ||
646 STRCMP(p
, ==, "#+inf") || STRCMP(p
, ==, "+#inf")) {
648 if (q_out
) *q_out
= p
+ strlen(p
);
649 x
= INFINITY
; rc
= 0;
651 tvec_error(tv
, "infinity not supported on this system");
656 else if (STRCMP(p
, ==, "#-inf") || STRCMP(p
, ==, "-#inf")) {
658 if (q_out
) *q_out
= p
+ strlen(p
);
659 x
= -INFINITY
; rc
= 0;
661 tvec_error(tv
, "infinity not supported on this system");
666 /* Check that this looks like a number, so we can exclude `strtod'
667 * recognizing its own non-finite number tokens.
671 if (*pp
== '+' || *pp
== '-') pp
++;
672 if (*pp
== '.') pp
++;
674 tvec_syntax(tv
, *p ?
*p
: fgetc(tv
->fp
), "floating-point number");
678 /* Parse the number using the system parser. */
679 olderr
= errno
; errno
= 0;
681 if (q_out
) *q_out
= q
;
682 else if (*q
) { tvec_syntax(tv
, *q
, "end-of-line"); rc
= -1; goto end
; }
683 if (errno
&& (errno
!= ERANGE
|| (x
> 0 ?
-x
: x
) == HUGE_VAL
)) {
684 tvec_error(tv
, "invalid floating-point number `%.*s': %s",
685 (int)(q
- p
), p
, strerror(errno
));
691 /* Check that the number is acceptable. */
692 if (NANP(x
) && fi
&& !(fi
->f
&TVFF_NANOK
)) {
693 tvec_error(tv
, "#nan not allowed here");
697 if (fi
&& ((!(fi
->f
&TVFF_NOMIN
) && x
< fi
->min
) ||
698 (!(fi
->f
&TVFF_NOMAX
) && x
> fi
->max
))) {
699 dstr_puts(&d
, "floating-point number ");
700 format_floating(&dstr_printops
, &d
, x
);
701 dstr_puts(&d
, " out of range (must be in ");
702 if (fi
->f
&TVFF_NOMIN
)
703 dstr_puts(&d
, "(#-inf");
705 { dstr_putc(&d
, '['); format_floating(&dstr_printops
, &d
, fi
->min
); }
706 dstr_puts(&d
, " .. ");
707 if (fi
->f
&TVFF_NOMAX
)
708 dstr_puts(&d
, "#+inf)");
710 { format_floating(&dstr_printops
, &d
, fi
->max
); dstr_putc(&d
, ']'); }
711 dstr_putc(&d
, ')'); dstr_putz(&d
);
712 tvec_error(tv
, "%s", d
.buf
); rc
= -1; goto end
;
722 /*----- String utilities --------------------------------------------------*/
724 /* Special character name table. */
725 static const struct chartab
{
726 const char *name
; /* character name */
727 int ch
; /* character value */
728 unsigned f
; /* flags: */
729 #define CTF_PREFER 1u /* preferred name */
730 #define CTF_SHORT 2u /* short name (compact style) */
732 { "#eof", EOF
, CTF_PREFER
| CTF_SHORT
},
733 { "#nul", '\0', CTF_PREFER
},
734 { "#bell", '\a', CTF_PREFER
},
735 { "#ding", '\a', 0 },
736 { "#bel", '\a', CTF_SHORT
},
737 { "#backspace", '\b', CTF_PREFER
},
738 { "#bs", '\b', CTF_SHORT
},
739 { "#escape", '\x1b', CTF_PREFER
},
740 { "#esc", '\x1b', CTF_SHORT
},
741 { "#formfeed", '\f', CTF_PREFER
},
742 { "#ff", '\f', CTF_SHORT
},
743 { "#newline", '\n', CTF_PREFER
},
744 { "#linefeed", '\n', 0 },
745 { "#lf", '\n', CTF_SHORT
},
747 { "#return", '\r', CTF_PREFER
},
748 { "#carriage-return", '\r', 0 },
749 { "#cr", '\r', CTF_SHORT
},
750 { "#tab", '\t', CTF_PREFER
| CTF_SHORT
},
751 { "#horizontal-tab", '\t', 0 },
753 { "#vertical-tab", '\v', CTF_PREFER
},
754 { "#vt", '\v', CTF_SHORT
},
755 { "#space", ' ', 0 },
756 { "#spc", ' ', CTF_SHORT
},
757 { "#delete", '\x7f', CTF_PREFER
},
758 { "#del", '\x7f', CTF_SHORT
},
762 /* --- @find_charname@ --- *
764 * Arguments: @int ch@ = character to match
765 * @unsigned f@ = flags (@CTF_...@) to match
767 * Returns: The name of the character, or null if no match is found.
769 * Use: Looks up a name for a character. Specifically, it returns
770 * the first entry in the @chartab@ table which matches @ch@ and
771 * which has one of the flags @f@ set.
774 static const char *find_charname(int ch
, unsigned f
)
776 const struct chartab
*ct
;
778 for (ct
= chartab
; ct
->name
; ct
++)
779 if (ct
->ch
== ch
&& (ct
->f
&f
)) return (ct
->name
);
783 /* --- @read_charname@ --- *
785 * Arguments: @int *ch_out@ = where to put the character
786 * @const char *p@ = character name
787 * @unsigned f@ = flags (@TCF_...@)
789 * Returns: Zero if a match was found, @-1@ if not.
791 * Use: Looks up a character by name. If @RCF_EOFOK@ is set in @f@,
792 * then the @EOF@ marker can be matched; otherwise it can't.
796 static int read_charname(int *ch_out
, const char *p
, unsigned f
)
798 const struct chartab
*ct
;
800 for (ct
= chartab
; ct
->name
; ct
++)
801 if (STRCMP(p
, ==, ct
->name
) && ((f
&RCF_EOFOK
) || ct
->ch
>= 0))
802 { *ch_out
= ct
->ch
; return (0); }
806 /* --- @format_charesc@ --- *
808 * Arguments: @const struct gprintf_ops *gops@ = print operations
809 * @void *go@ = print destination
810 * @int ch@ = character to format
811 * @unsigned f@ = flags (@FCF_...@)
815 * Use: Format a character as an escape sequence, possibly as part of
816 * a larger string. If @FCF_BRACE@ is set in @f@, then put
817 * braces around a `\x...' code, so that it's suitable for use
818 * in a longer string.
822 static void format_charesc(const struct gprintf_ops
*gops
, void *go
,
826 case '\a': gprintf(gops
, go
, "\\a"); break;
827 case '\b': gprintf(gops
, go
, "\\b"); break;
828 case '\x1b': gprintf(gops
, go
, "\\e"); break;
829 case '\f': gprintf(gops
, go
, "\\f"); break;
830 case '\r': gprintf(gops
, go
, "\\r"); break;
831 case '\n': gprintf(gops
, go
, "\\n"); break;
832 case '\t': gprintf(gops
, go
, "\\t"); break;
833 case '\v': gprintf(gops
, go
, "\\v"); break;
834 case '\\': gprintf(gops
, go
, "\\\\"); break;
835 case '\'': gprintf(gops
, go
, "\\'"); break;
837 if (f
&FCF_BRACE
) gprintf(gops
, go
, "\\{0}");
838 else gprintf(gops
, go
, "\\0");
842 gprintf(gops
, go
, "\\x{%0*x}", hex_width(UCHAR_MAX
), ch
);
844 gprintf(gops
, go
, "\\x%0*x", hex_width(UCHAR_MAX
), ch
);
849 /* --- @format_char@ --- *
851 * Arguments: @const struct gprintf_ops *gops@ = print operations
852 * @void *go@ = print destination
853 * @int ch@ = character to format
857 * Use: Format a single character.
860 static void format_char(const struct gprintf_ops
*gops
, void *go
, int ch
)
863 case '\\': case '\'': escape
:
864 gprintf(gops
, go
, "'");
865 format_charesc(gops
, go
, ch
, 0);
866 gprintf(gops
, go
, "'");
869 if (!isprint(ch
)) goto escape
;
870 gprintf(gops
, go
, "'%c'", ch
);
875 /* --- @maybe_format_unsigned_char@, @maybe_format_signed_char@ --- *
877 * Arguments: @const struct gprintf_ops *gops@ = print operations
878 * @void *go@ = print destination
879 * @unsigned long u@ or @long i@ = an integer
883 * Use: Format a (signed or unsigned) integer as a character, if it's
884 * in range, printing something like `= 'q''. It's assumed that
885 * a comment marker has already been output.
888 static void maybe_format_unsigned_char
889 (const struct gprintf_ops
*gops
, void *go
, unsigned long u
)
893 p
= find_charname(u
, CTF_PREFER
);
894 if (p
) gprintf(gops
, go
, " = %s", p
);
896 { gprintf(gops
, go
, " = "); format_char(gops
, go
, u
); }
899 static void maybe_format_signed_char
900 (const struct gprintf_ops
*gops
, void *go
, long i
)
904 p
= find_charname(i
, CTF_PREFER
);
905 if (p
) gprintf(gops
, go
, " = %s", p
);
906 if (0 <= i
&& i
< UCHAR_MAX
)
907 { gprintf(gops
, go
, " = "); format_char(gops
, go
, i
); }
910 /* --- @read_charesc@ --- *
912 * Arguments: @int *ch_out@ = where to put the result
913 * @struct tvec_state *tv@ = test vector state
915 * Returns: Zero on success, @-1@ on error.
917 * Use: Parse and convert an escape sequence from @tv@'s input
918 * stream, assuming that the initial `\' has already been read.
919 * Reports errors as appropriate.
922 static int read_charesc(int *ch_out
, struct tvec_state
*tv
)
931 /* Things we shouldn't find. */
932 case EOF
: case '\n': return (tvec_syntax(tv
, ch
, "string escape"));
934 /* Single-character escapes. */
935 case '\'': *ch_out
= '\''; break;
936 case '\\': *ch_out
= '\\'; break;
937 case '"': *ch_out
= '"'; break;
938 case 'a': *ch_out
= '\a'; break;
939 case 'b': *ch_out
= '\b'; break;
940 case 'e': *ch_out
= '\x1b'; break;
941 case 'f': *ch_out
= '\f'; break;
942 case 'n': *ch_out
= '\n'; break;
943 case 'r': *ch_out
= '\r'; break;
944 case 't': *ch_out
= '\t'; break;
945 case 'v': *ch_out
= '\v'; break;
947 /* Hex escapes, with and without braces. */
950 if (ch
== '{') { f
|= f_brace
; ch
= getc(tv
->fp
); }
953 if (esc
< 0 || esc
>= 16) return (tvec_syntax(tv
, ch
, "hex digit"));
955 ch
= getc(tv
->fp
); i
= chtodig(ch
); if (i
< 0 || i
>= 16) break;
958 return (tvec_error(tv
,
959 "character code %d out of range", esc
));
961 if (!(f
&f_brace
)) ungetc(ch
, tv
->fp
);
962 else if (ch
!= '}') return (tvec_syntax(tv
, ch
, "`}'"));
966 /* Other things, primarily octal escapes. */
968 f
|= f_brace
; ch
= getc(tv
->fp
);
971 if ('0' <= ch
&& ch
< '8') {
972 i
= 1; esc
= ch
- '0';
975 if ('0' > ch
|| ch
>= '8') { ungetc(ch
, tv
->fp
); break; }
976 esc
= 8*esc
+ ch
- '0';
977 i
++; if (i
>= 3) break;
981 if (ch
!= '}') return (tvec_syntax(tv
, ch
, "`}'"));
984 return (tvec_error(tv
,
985 "character code %d out of range", esc
));
986 *ch_out
= esc
; break;
988 return (tvec_syntax(tv
, ch
, "string escape"));
997 /* --- @read_quoted_string@ --- *
999 * Arguments: @dstr *d@ = string to write to
1000 * @int quote@ = initial quote, `'' or `"'
1001 * @struct tvec_state *tv@ = test vector state
1003 * Returns: Zero on success, @-1@ on error.
1005 * Use: Read the rest of a quoted string into @d@, reporting errors
1008 * A single-quoted string is entirely literal. A double-quoted
1009 * string may contain C-like escapes.
1012 static int read_quoted_string(dstr
*d
, int quote
, struct tvec_state
*tv
)
1019 case EOF
: case '\n':
1020 return (tvec_syntax(tv
, ch
, "`%c'", quote
));
1022 if (quote
== '\'') goto ordinary
;
1023 ch
= getc(tv
->fp
); if (ch
== '\n') { tv
->lno
++; break; }
1024 ungetc(ch
, tv
->fp
); if (read_charesc(&ch
, tv
)) return (-1);
1027 if (ch
== quote
) goto end
;
1039 /* --- @collect_bare@ --- *
1041 * Arguments: @dstr *d@ = string to write to
1042 * @struct tvec_state *tv@ = test vector state
1044 * Returns: Zero on success, @-1@ on error.
1046 * Use: Read barewords and the whitespace between them. Stop when we
1047 * encounter something which can't start a bareword.
1050 static int collect_bare(dstr
*d
, struct tvec_state
*tv
)
1052 size_t pos
= d
->len
;
1053 enum { WORD
, SPACE
, ESCAPE
}; unsigned s
= WORD
;
1060 tvec_syntax(tv
, ch
, "bareword");
1063 if (s
== ESCAPE
) { tv
->lno
++; goto addch
; }
1064 if (s
== WORD
) pos
= d
->len
;
1065 ungetc(ch
, tv
->fp
); if (tvec_nexttoken(tv
)) { rc
= -1; goto end
; }
1066 DPUTC(d
, ' '); s
= SPACE
;
1068 case '"': case '\'': case '!': case '#': case ')': case '}': case ']':
1069 if (s
== SPACE
) { ungetc(ch
, tv
->fp
); goto done
; }
1075 if (s
!= ESCAPE
&& isspace(ch
)) {
1076 if (s
== WORD
) pos
= d
->len
;
1077 DPUTC(d
, ch
); s
= SPACE
;
1081 DPUTC(d
, ch
); s
= WORD
;
1086 if (s
== SPACE
) d
->len
= pos
;
1092 /* --- @set_up_encoding@ --- *
1094 * Arguments: @const codec_class **ccl_out@ = where to put the class
1095 * @unsigned *f_out@ = where to put the flags
1096 * @unsigned code@ = the coding scheme to use (@TVEC_...@)
1100 * Use: Helper for @read_compound_string@ below.
1102 * Return the appropriate codec class and flags for @code@.
1103 * Leaves @*ccl_out@ null if the coding scheme doesn't have a
1104 * backing codec class (e.g., @TVCODE_BARE@).
1107 enum { TVCODE_BARE
, TVCODE_HEX
, TVCODE_BASE64
, TVCODE_BASE32
};
1108 static void set_up_encoding(const codec_class
**ccl_out
, unsigned *f_out
,
1113 *ccl_out
= 0; *f_out
= 0;
1116 *ccl_out
= &hex_class
; *f_out
= CDCF_IGNCASE
;
1119 *ccl_out
= &base32_class
; *f_out
= CDCF_IGNCASE
| CDCF_IGNEQPAD
;
1122 *ccl_out
= &base64_class
; *f_out
= CDCF_IGNEQPAD
;
1129 /* --- @flush_codec@ --- *
1131 * Arguments: @codec *cdc@ = a codec, or null
1132 * @dstr *d@ = output string
1133 * @struct tvec_state *tv@ = test vector state
1135 * Returns: Zero on success, @-1@ on error.
1137 * Use: Helper for @read_compound_string@ below.
1139 * Flush out any final buffered material from @cdc@, and check
1140 * that it's in a good state. Frees the codec on success. Does
1141 * nothing if @cdc@ is null.
1144 static int flush_codec(codec
*cdc
, dstr
*d
, struct tvec_state
*tv
)
1149 err
= cdc
->ops
->code(cdc
, 0, 0, d
);
1151 return (tvec_error(tv
, "invalid %s sequence end: %s",
1152 cdc
->ops
->c
->name
, codec_strerror(err
)));
1153 cdc
->ops
->destroy(cdc
);
1158 /* --- @read_compound_string@ --- *
1160 * Arguments: @void **p_inout@ = address of output buffer pointer
1161 * @size_t *sz_inout@ = address of buffer size
1162 * @unsigned code@ = initial interpretation of barewords
1163 * @unsigned f@ = other flags (@RCSF_...@)
1164 * @struct tvec_state *tv@ = test vector state
1166 * Returns: Zero on success, @-1@ on error.
1168 * Use: Parse a compound string, i.e., a sequence of stringish pieces
1169 * which might be quoted strings, character names, or barewords
1170 * to be decoded accoding to @code@, interspersed with
1171 * additional directives.
1173 * If the initial buffer pointer is non-null and sufficiently
1174 * large, then it will be reused; otherwise, it is freed and a
1175 * fresh, sufficiently large buffer is allocated and returned.
1178 #define RCSF_NESTED 1u
1179 static int read_compound_string(void **p_inout
, size_t *sz_inout
,
1180 unsigned code
, unsigned f
,
1181 struct tvec_state
*tv
)
1183 const codec_class
*ccl
; unsigned cdf
;
1185 dstr d
= DSTR_INIT
, w
= DSTR_INIT
;
1188 void *pp
= 0; size_t sz
;
1192 set_up_encoding(&ccl
, &cdf
, code
); cdc
= 0;
1194 if (tvec_nexttoken(tv
)) return (tvec_syntax(tv
, fgetc(tv
->fp
), "string"));
1199 case ')': case ']': case '}':
1200 /* Close brackets. Leave these for recursive caller if there is one,
1204 if (!(f
&RCSF_NESTED
))
1205 { rc
= tvec_syntax(tv
, ch
, "string"); goto end
; }
1206 ungetc(ch
, tv
->fp
); goto done
;
1208 case '"': case '\'':
1209 /* Quotes. Read a quoted string. */
1211 if (cdc
&& flush_codec(cdc
, &d
, tv
)) { rc
= -1; goto end
; }
1213 if (read_quoted_string(&d
, ch
, tv
)) { rc
= -1; goto end
; }
1217 /* A named character. */
1220 if (cdc
&& flush_codec(cdc
, &d
, tv
)) { rc
= -1; goto end
; }
1222 DRESET(&w
); tvec_readword(tv
, &w
, 0, ";", "character name");
1223 if (read_charname(&ch
, w
.buf
, RCF_EOFOK
)) {
1224 rc
= tvec_error(tv
, "unknown character name `%s'", d
.buf
);
1227 DPUTC(&d
, ch
); break;
1230 /* A magic keyword. */
1232 if (cdc
&& flush_codec(cdc
, &d
, tv
)) { rc
= -1; goto end
; }
1235 DRESET(&w
); tvec_readword(tv
, &w
, 0, ";", "`!'-keyword");
1237 /* Change bareword coding system. */
1238 if (STRCMP(w
.buf
, ==, "!bare"))
1239 { code
= TVCODE_BARE
; set_up_encoding(&ccl
, &cdf
, code
); }
1240 else if (STRCMP(w
.buf
, ==, "!hex"))
1241 { code
= TVCODE_HEX
; set_up_encoding(&ccl
, &cdf
, code
); }
1242 else if (STRCMP(w
.buf
, ==, "!base32"))
1243 { code
= TVCODE_BASE32
; set_up_encoding(&ccl
, &cdf
, code
); }
1244 else if (STRCMP(w
.buf
, ==, "!base64"))
1245 { code
= TVCODE_BASE64
; set_up_encoding(&ccl
, &cdf
, code
); }
1247 /* Repeated substrings. */
1248 else if (STRCMP(w
.buf
, ==, "!repeat")) {
1249 if (tvec_nexttoken(tv
)) {
1250 rc
= tvec_syntax(tv
, fgetc(tv
->fp
), "repeat count");
1254 if (tvec_readword(tv
, &w
, 0, ";{", "repeat count"))
1255 { rc
= -1; goto end
; }
1256 if (parse_unsigned_integer(&n
, &q
, w
.buf
)) {
1257 rc
= tvec_error(tv
, "invalid repeat count `%s'", w
.buf
);
1260 if (*q
) { rc
= tvec_syntax(tv
, *q
, "`{'"); goto end
; }
1261 if (tvec_nexttoken(tv
))
1262 { rc
= tvec_syntax(tv
, fgetc(tv
->fp
), "`{'"); goto end
; }
1263 ch
= getc(tv
->fp
); if (ch
!= '{')
1264 { rc
= tvec_syntax(tv
, ch
, "`{'"); goto end
; }
1266 if (read_compound_string(&pp
, &sz
, code
, f
| RCSF_NESTED
, tv
))
1267 { rc
= -1; goto end
; }
1268 ch
= getc(tv
->fp
); if (ch
!= '}')
1269 { rc
= tvec_syntax(tv
, ch
, "`}'"); goto end
; }
1271 if (n
> (size_t)-1/sz
)
1272 { rc
= tvec_error(tv
, "repeat size out of range"); goto end
; }
1273 dstr_ensure(&d
, n
*sz
);
1275 { memset(d
.buf
+ d
.len
, *(unsigned char *)pp
, n
); d
.len
+= n
; }
1277 for (; n
--; d
.len
+= sz
) memcpy(d
.buf
+ d
.len
, pp
, sz
);
1282 /* Anything else is an error. */
1284 tvec_error(tv
, "unknown string keyword `%s'", w
.buf
);
1290 /* A bareword. Process it according to the current coding system. */
1295 if (collect_bare(&d
, tv
)) goto done
;
1299 ungetc(ch
, tv
->fp
); DRESET(&w
);
1300 if (tvec_readword(tv
, &w
, 0, ";",
1301 "%s-encoded fragment", ccl
->name
))
1302 { rc
= -1; goto end
; }
1303 if (!cdc
) cdc
= ccl
->decoder(cdf
);
1304 err
= cdc
->ops
->code(cdc
, w
.buf
, w
.len
, &d
);
1306 tvec_error(tv
, "invalid %s fragment `%s': %s",
1307 ccl
->name
, w
.buf
, codec_strerror(err
));
1314 } while (!tvec_nexttoken(tv
));
1317 /* Wrap things up. */
1318 if (cdc
&& flush_codec(cdc
, &d
, tv
)) { rc
= -1; goto end
; }
1320 if (*sz_inout
<= d
.len
)
1321 { xfree(*p_inout
); *p_inout
= xmalloc(d
.len
+ 1); }
1322 p
= *p_inout
; memcpy(p
, d
.buf
, d
.len
); p
[d
.len
] = 0; *sz_inout
= d
.len
;
1326 /* Clean up any debris. */
1327 if (cdc
) cdc
->ops
->destroy(cdc
);
1329 dstr_destroy(&d
); dstr_destroy(&w
);
1333 /*----- Signed and unsigned integer types ---------------------------------*/
1335 /* --- @init_int@, @init_uint@ --- *
1337 * Arguments: @union tvec_regval *rv@ = register value
1338 * @const struct tvec_regdef *rd@ = register definition
1342 * Use: Initialize a register value.
1344 * Integer values are initialized to zero.
1347 static void init_int(union tvec_regval
*rv
, const struct tvec_regdef
*rd
)
1350 static void init_uint(union tvec_regval
*rv
, const struct tvec_regdef
*rd
)
1353 /* --- @eq_int@, @eq_uint@ --- *
1355 * Arguments: @const union tvec_regval *rv0, *rv1@ = register values
1356 * @const struct tvec_regdef *rd@ = register definition
1358 * Returns: Nonzero if the values are equal, zero if unequal
1360 * Use: Compare register values for equality.
1363 static int eq_int(const union tvec_regval
*rv0
, const union tvec_regval
*rv1
,
1364 const struct tvec_regdef
*rd
)
1365 { return (rv0
->i
== rv1
->i
); }
1367 static int eq_uint(const union tvec_regval
*rv0
,
1368 const union tvec_regval
*rv1
,
1369 const struct tvec_regdef
*rd
)
1370 { return (rv0
->u
== rv1
->u
); }
1372 /* --- @tobuf_int@, @tobuf_uint@ --- *
1374 * Arguments: @buf *b@ = buffer
1375 * @const union tvec_regval *rv@ = register value
1376 * @const struct tvec_regdef *rd@ = register definition
1378 * Returns: Zero on success, %$-1$% on failure.
1380 * Use: Serialize a register value to a buffer.
1382 * Integer values are serialized as little-endian 64-bit signed
1383 * or unsigned integers.
1386 static int tobuf_int(buf
*b
, const union tvec_regval
*rv
,
1387 const struct tvec_regdef
*rd
)
1388 { return (signed_to_buf(b
, rv
->i
)); }
1390 static int tobuf_uint(buf
*b
, const union tvec_regval
*rv
,
1391 const struct tvec_regdef
*rd
)
1392 { return (unsigned_to_buf(b
, rv
->u
)); }
1394 /* --- @frombuf_int@, @frombuf_uint@ --- *
1396 * Arguments: @buf *b@ = buffer
1397 * @union tvec_regval *rv@ = register value
1398 * @const struct tvec_regdef *rd@ = register definition
1400 * Returns: Zero on success, %$-1$% on failure.
1402 * Use: Deserialize a register value from a buffer.
1404 * Integer values are serialized as 64-bit signed or unsigned
1408 static int frombuf_int(buf
*b
, union tvec_regval
*rv
,
1409 const struct tvec_regdef
*rd
)
1410 { return (signed_from_buf(b
, &rv
->i
)); }
1412 static int frombuf_uint(buf
*b
, union tvec_regval
*rv
,
1413 const struct tvec_regdef
*rd
)
1414 { return (unsigned_from_buf(b
, &rv
->u
)); }
1416 /* --- @parse_int@, @parse_uint@ --- *
1418 * Arguments: @union tvec_regval *rv@ = register value
1419 * @const struct tvec_regdef *rd@ = register definition
1420 * @struct tvec_state *tv@ = test-vector state
1422 * Returns: Zero on success, %$-1$% on error.
1424 * Use: Parse a register value from an input file.
1426 * Integers may be input in decimal, hex, binary, or octal,
1427 * following approximately usual conventions.
1429 * * Signed integers may be preceded with a `+' or `-' sign.
1431 * * Decimal integers are just a sequence of decimal digits
1434 * * Octal integers are a sequence of digits `0' ... `7',
1435 * preceded by `0o' or `0O'.
1437 * * Hexadecimal integers are a sequence of digits `0'
1438 * ... `9', `a' ... `f', or `A' ... `F', preceded by `0x' or
1441 * * Radix-B integers are a sequence of digits `0' ... `9',
1442 * `a' ... `f', or `A' ... `F', each with value less than B,
1443 * preceded by `Br' or `BR', where 0 < B < 36 is expressed
1444 * in decimal without any leading `0' or internal
1447 * * A digit sequence may contain internal underscore `_'
1448 * separators, but not before or after all of the digits;
1449 * and two consecutive `_' characters are not permitted.
1452 static int parse_int(union tvec_regval
*rv
, const struct tvec_regdef
*rd
,
1453 struct tvec_state
*tv
)
1458 if (tvec_readword(tv
, &d
, 0, ";", "signed integer"))
1459 { rc
= -1; goto end
; }
1460 if (parse_signed(&rv
->i
, d
.buf
, rd
->arg
.p
, tv
)) { rc
= -1; goto end
; }
1461 if (tvec_flushtoeol(tv
, 0)) { rc
= -1; goto end
; }
1468 static int parse_uint(union tvec_regval
*rv
, const struct tvec_regdef
*rd
,
1469 struct tvec_state
*tv
)
1474 if (tvec_readword(tv
, &d
, 0, ";", "unsigned integer"))
1475 { rc
= -1; goto end
; }
1476 if (parse_unsigned(&rv
->u
, d
.buf
, rd
->arg
.p
, tv
)) { rc
= -1; goto end
; }
1477 if (tvec_flushtoeol(tv
, 0)) { rc
= -1; goto end
; }
1484 /* --- @dump_int@, @dump_uint@ --- *
1486 * Arguments: @const union tvec_regval *rv@ = register value
1487 * @const struct tvec_regdef *rd@ = register definition
1488 * @unsigned style@ = output style (@TVSF_...@)
1489 * @const struct gprintf_ops *gops@, @void *gp@ = format output
1493 * Use: Dump a register value to the format output.
1495 * Integer values are dumped in decimal and, unless compact
1496 * output is requested, hex, and maybe a character, as a
1500 static void dump_int(const union tvec_regval
*rv
,
1501 const struct tvec_regdef
*rd
,
1503 const struct gprintf_ops
*gops
, void *go
)
1506 gprintf(gops
, go
, "%ld", rv
->i
);
1507 if (!(style
&TVSF_COMPACT
)) {
1508 gprintf(gops
, go
, " ; = ");
1509 format_signed_hex(gops
, go
, rv
->i
);
1510 maybe_format_signed_char(gops
, go
, rv
->i
);
1514 static void dump_uint(const union tvec_regval
*rv
,
1515 const struct tvec_regdef
*rd
,
1517 const struct gprintf_ops
*gops
, void *go
)
1519 gprintf(gops
, go
, "%lu", rv
->u
);
1520 if (!(style
&TVSF_COMPACT
)) {
1521 gprintf(gops
, go
, " ; = ");
1522 format_unsigned_hex(gops
, go
, rv
->u
);
1523 maybe_format_unsigned_char(gops
, go
, rv
->u
);
1527 /* Integer type definitions. */
1528 const struct tvec_regty tvty_int
= {
1529 init_int
, trivial_release
, eq_int
,
1530 tobuf_int
, frombuf_int
,
1533 const struct tvec_regty tvty_uint
= {
1534 init_uint
, trivial_release
, eq_uint
,
1535 tobuf_uint
, frombuf_uint
,
1536 parse_uint
, dump_uint
1539 /* Predefined integer ranges. */
1540 const struct tvec_irange
1541 tvrange_schar
= { SCHAR_MIN
, SCHAR_MAX
},
1542 tvrange_short
= { SHRT_MIN
, SHRT_MAX
},
1543 tvrange_int
= { INT_MIN
, INT_MAX
},
1544 tvrange_long
= { LONG_MIN
, LONG_MAX
},
1545 tvrange_sbyte
= { -128, 127 },
1546 tvrange_i16
= { -32768, +32767 },
1547 tvrange_i32
= { -2147483648, 2147483647 };
1548 const struct tvec_urange
1549 tvrange_uchar
= { 0, UCHAR_MAX
},
1550 tvrange_ushort
= { 0, USHRT_MAX
},
1551 tvrange_uint
= { 0, UINT_MAX
},
1552 tvrange_ulong
= { 0, ULONG_MAX
},
1553 tvrange_size
= { 0, (size_t)-1 },
1554 tvrange_byte
= { 0, 255 },
1555 tvrange_u16
= { 0, 65535 },
1556 tvrange_u32
= { 0, 4294967295 };
1558 /* --- @tvec_claimeq_int@ --- *
1560 * Arguments: @struct tvec_state *tv@ = test-vector state
1561 * @long i0, i1@ = two signed integers
1562 * @const char *file@, @unsigned @lno@ = calling file and line
1563 * @const char *expr@ = the expression to quote on failure
1565 * Returns: Nonzero if @i0@ and @i1@ are equal, otherwise zero.
1567 * Use: Check that values of @i0@ and @i1@ are equal. As for
1568 * @tvec_claim@ above, a test case is automatically begun and
1569 * ended if none is already underway. If the values are
1570 * unequal, then @tvec_fail@ is called, quoting @expr@, and the
1571 * mismatched values are dumped: @i0@ is printed as the output
1572 * value and @i1@ is printed as the input reference.
1575 int tvec_claimeq_int(struct tvec_state
*tv
, long i0
, long i1
,
1576 const char *file
, unsigned lno
, const char *expr
)
1578 tv
->out
[0].v
.i
= i0
; tv
->in
[0].v
.i
= i1
;
1579 return (tvec_claimeq(tv
, &tvty_int
, 0, file
, lno
, expr
));
1582 /* --- @tvec_claimeq_uint@ --- *
1584 * Arguments: @struct tvec_state *tv@ = test-vector state
1585 * @unsigned long u0, u1@ = two unsigned integers
1586 * @const char *file@, @unsigned @lno@ = calling file and line
1587 * @const char *expr@ = the expression to quote on failure
1589 * Returns: Nonzero if @u0@ and @u1@ are equal, otherwise zero.
1591 * Use: Check that values of @u0@ and @u1@ are equal. As for
1592 * @tvec_claim@ above, a test case is automatically begun and
1593 * ended if none is already underway. If the values are
1594 * unequal, then @tvec_fail@ is called, quoting @expr@, and the
1595 * mismatched values are dumped: @u0@ is printed as the output
1596 * value and @u1@ is printed as the input reference.
1599 int tvec_claimeq_uint(struct tvec_state
*tv
,
1600 unsigned long u0
, unsigned long u1
,
1601 const char *file
, unsigned lno
, const char *expr
)
1603 tv
->out
[0].v
.u
= u0
; tv
->in
[0].v
.u
= u1
;
1604 return (tvec_claimeq(tv
, &tvty_uint
, 0, file
, lno
, expr
));
1607 /*----- Floating-point type -----------------------------------------------*/
1609 /* --- @int_float@ --- *
1611 * Arguments: @union tvec_regval *rv@ = register value
1612 * @const struct tvec_regdef *rd@ = register definition
1616 * Use: Initialize a register value.
1618 * Floating-point values are initialized to zero.
1621 static void init_float(union tvec_regval
*rv
, const struct tvec_regdef
*rd
)
1624 /* --- @eq_float@ --- *
1626 * Arguments: @const union tvec_regval *rv0, *rv1@ = register values
1627 * @const struct tvec_regdef *rd@ = register definition
1629 * Returns: Nonzero if the values are equal, zero if unequal
1631 * Use: Compare register values for equality.
1633 * Floating-point values may be considered equal if their
1634 * absolute or relative difference is sufficiently small, as
1635 * described in the register definition.
1638 static int eq_float(const union tvec_regval
*rv0
,
1639 const union tvec_regval
*rv1
,
1640 const struct tvec_regdef
*rd
)
1641 { return (eqish_floating_p(rv0
->f
, rv1
->f
, rd
->arg
.p
)); }
1643 /* --- @tobuf_float@ --- *
1645 * Arguments: @buf *b@ = buffer
1646 * @const union tvec_regval *rv@ = register value
1647 * @const struct tvec_regdef *rd@ = register definition
1649 * Returns: Zero on success, %$-1$% on failure.
1651 * Use: Serialize a register value to a buffer.
1653 * Floating-point values are serialized as little-endian
1654 * IEEE 754 Binary64.
1657 static int tobuf_float(buf
*b
, const union tvec_regval
*rv
,
1658 const struct tvec_regdef
*rd
)
1659 { return (buf_putf64l(b
, rv
->f
)); }
1661 /* --- @frombuf_float@ --- *
1663 * Arguments: @buf *b@ = buffer
1664 * @union tvec_regval *rv@ = register value
1665 * @const struct tvec_regdef *rd@ = register definition
1667 * Returns: Zero on success, %$-1$% on failure.
1669 * Use: Deserialize a register value from a buffer.
1671 * Floating-point values are serialized as little-endian
1672 * IEEE 754 Binary64.
1675 static int frombuf_float(buf
*b
, union tvec_regval
*rv
,
1676 const struct tvec_regdef
*rd
)
1677 { return (buf_getf64l(b
, &rv
->f
)); }
1679 /* --- @parse_float@ --- *
1681 * Arguments: @union tvec_regval *rv@ = register value
1682 * @const struct tvec_regdef *rd@ = register definition
1683 * @struct tvec_state *tv@ = test-vector state
1685 * Returns: Zero on success, %$-1$% on error.
1687 * Use: Parse a register value from an input file.
1689 * Floating-point values are either NaN (%|#nan|%, if supported
1690 * by the platform); positive or negative infinity (%|#inf|%,
1691 * %|+#inf|%, or %|#+inf|% (preferring the last), and %|-#inf|%
1692 * or %|#-inf|% (preferring the latter), if supported by the
1693 * platform); or a number in strtod(3) syntax.
1696 static int parse_float(union tvec_regval
*rv
, const struct tvec_regdef
*rd
,
1697 struct tvec_state
*tv
)
1702 if (tvec_readword(tv
, &d
, 0, ";", "floating-point number"))
1703 { rc
= -1; goto end
; }
1704 if (parse_floating(&rv
->f
, 0, d
.buf
, rd
->arg
.p
, tv
))
1705 { rc
= -1; goto end
; }
1706 if (tvec_flushtoeol(tv
, 0)) { rc
= -1; goto end
; }
1713 /* --- @dump_float@ --- *
1715 * Arguments: @const union tvec_regval *rv@ = register value
1716 * @const struct tvec_regdef *rd@ = register definition
1717 * @unsigned style@ = output style (@TVSF_...@)
1718 * @const struct gprintf_ops *gops@, @void *gp@ = format output
1722 * Use: Dump a register value to the format output.
1724 * Floating-point values are dumped in decimal or as a special
1725 * token beginning with `%|#|%'. Some effort is taken to ensure
1726 * that the output is sufficient to uniquely identify the
1727 * original value, but, honestly, C makes this really hard.
1730 static void dump_float(const union tvec_regval
*rv
,
1731 const struct tvec_regdef
*rd
,
1733 const struct gprintf_ops
*gops
, void *go
)
1734 { format_floating(gops
, go
, rv
->f
); }
1736 /* Floating-point type definition. */
1737 const struct tvec_regty tvty_float
= {
1738 init_float
, trivial_release
, eq_float
,
1739 tobuf_float
, frombuf_float
,
1740 parse_float
, dump_float
1743 /* Predefined floating-point ranges. */
1744 const struct tvec_floatinfo
1745 tvflt_finite
= { TVFF_EXACT
, -DBL_MAX
, DBL_MAX
, 0.0 },
1746 tvflt_nonneg
= { TVFF_EXACT
, 0, DBL_MAX
, 0.0 };
1748 /* --- @tvec_claimeqish_float@ --- *
1750 * Arguments: @struct tvec_state *tv@ = test-vector state
1751 * @double f0, f1@ = two floating-point numbers
1752 * @unsigned f@ = flags (@TVFF_...@)
1753 * @double delta@ = maximum tolerable difference
1754 * @const char *file@, @unsigned @lno@ = calling file and line
1755 * @const char *expr@ = the expression to quote on failure
1757 * Returns: Nonzero if @f0@ and @u1@ are sufficiently close, otherwise
1760 * Use: Check that values of @f0@ and @f1@ are sufficiently close.
1761 * As for @tvec_claim@ above, a test case is automatically begun
1762 * and ended if none is already underway. If the values are
1763 * too far apart, then @tvec_fail@ is called, quoting @expr@,
1764 * and the mismatched values are dumped: @f0@ is printed as the
1765 * output value and @f1@ is printed as the input reference.
1767 * The details for the comparison are as follows.
1769 * * A NaN value matches any other NaN, and nothing else.
1771 * * An infinity matches another infinity of the same sign,
1774 * * If @f&TVFF_EQMASK@ is @TVFF_EXACT@, then any
1775 * representable number matches only itself: in particular,
1776 * positive and negative zero are considered distinct.
1777 * (This allows tests to check that they land on the correct
1778 * side of branch cuts, for example.)
1780 * * If @f&TVFF_EQMASK@ is @TVFF_ABSDELTA@, then %$x$% matches
1781 * %$y$% when %$|x - y| < \delta$%.
1783 * * If @f&TVFF_EQMASK@ is @TVFF_RELDELTA@, then %$x$% matches
1784 * %$y$% when %$|1 - y/x| < \delta$%. (Note that this
1785 * criterion is asymmetric FIXME
1788 int tvec_claimeqish_float(struct tvec_state
*tv
,
1789 double f0
, double f1
, unsigned f
, double delta
,
1790 const char *file
, unsigned lno
,
1793 struct tvec_floatinfo fi
;
1794 union tvec_misc arg
;
1796 fi
.f
= f
; fi
.min
= fi
.max
= 0.0; fi
.delta
= delta
; arg
.p
= &fi
;
1797 tv
->out
[0].v
.f
= f0
; tv
->in
[0].v
.f
= f1
;
1798 return (tvec_claimeq(tv
, &tvty_float
, &arg
, file
, lno
, expr
));
1801 /* --- @tvec_claimeq_float@ --- *
1803 * Arguments: @struct tvec_state *tv@ = test-vector state
1804 * @double f0, f1@ = two floating-point numbers
1805 * @const char *file@, @unsigned @lno@ = calling file and line
1806 * @const char *expr@ = the expression to quote on failure
1808 * Returns: Nonzero if @f0@ and @u1@ are identical, otherwise zero.
1810 * Use: Check that values of @f0@ and @f1@ are identical. The
1811 * function is exactly equivalent to @tvec_claimeqish_float@
1812 * with @f == TVFF_EXACT@.
1815 int tvec_claimeq_float(struct tvec_state
*tv
,
1816 double f0
, double f1
,
1817 const char *file
, unsigned lno
,
1820 return (tvec_claimeqish_float(tv
, f0
, f1
, TVFF_EXACT
, 0.0,
1824 /*----- Durations ---------------------------------------------------------*/
1826 /* A duration is a floating-point number of seconds. Initialization and
1827 * teardown, equality comparison, and serialization are as for floating-point
1831 static const struct duration_unit
{
1835 #define DUF_PREFER 1u
1836 } duration_units
[] = {
1848 { "yr", 31557600.0, DUF_PREFER
},
1849 { "y", 31557600.0, 0 },
1850 { "day", 86400.0, DUF_PREFER
},
1851 { "dy", 86400.0, 0 },
1852 { "d", 86400.0, 0 },
1853 { "hr", 3600.0, DUF_PREFER
},
1854 { "hour", 3600.0, 0 },
1856 { "min", 60.0, DUF_PREFER
},
1859 { "s", 1.0, DUF_PREFER
},
1864 { "ms", 1e-3, DUF_PREFER
},
1865 { "µs", 1e-6, DUF_PREFER
},
1866 { "ns", 1e-9, DUF_PREFER
},
1867 { "ps", 1e-12, DUF_PREFER
},
1868 { "fs", 1e-15, DUF_PREFER
},
1869 { "as", 1e-18, DUF_PREFER
},
1870 { "zs", 1e-21, DUF_PREFER
},
1871 { "ys", 1e-24, DUF_PREFER
},
1876 /* --- @tvec_parsedurunit@ --- *
1878 * Arguments: @double *scale_out@ = where to leave the scale
1879 * @const char **p_inout@ = input unit string, updated
1881 * Returns: Zero on success, %$-1$% on error.
1883 * Use: If @*p_inout@ begins with a unit string followed by the end
1884 * of the string or some non-alphanumeric character, then store
1885 * the corresponding scale factor in @*scale_out@, advance
1886 * @*p_inout@ past the unit string, and return zero. Otherwise,
1890 int tvec_parsedurunit(double *scale_out
, const char **p_inout
)
1892 const char *p
= *p_inout
, *q
;
1893 const struct duration_unit
*u
;
1896 while (ISSPACE(*p
)) p
++;
1897 for (q
= p
; *q
&& ISALNUM(*q
); q
++);
1898 n
= q
- p
; if (!n
) { *scale_out
= 1.0; return (0); }
1900 for (u
= duration_units
; u
->unit
; u
++)
1901 if (STRNCMP(p
, ==, u
->unit
, n
) && !u
->unit
[n
])
1902 { *scale_out
= u
->scale
; *p_inout
= q
; return (0); }
1906 /* --- @parse_duration@ --- *
1908 * Arguments: @union tvec_regval *rv@ = register value
1909 * @const struct tvec_regdef *rd@ = register definition
1910 * @struct tvec_state *tv@ = test-vector state
1912 * Returns: Zero on success, %$-1$% on error.
1914 * Use: Parse a register value from an input file.
1916 * Duration values are finite nonnegative floating-point
1917 * numbers in @strtod@ syntax, optionally followed by a unit .
1920 static int parse_duration(union tvec_regval
*rv
,
1921 const struct tvec_regdef
*rd
,
1922 struct tvec_state
*tv
)
1924 const struct duration_unit
*u
;
1930 if (tvec_readword(tv
, &d
, 0, ";", "duration")) { rc
= -1; goto end
; }
1931 if (parse_floating(&t
, &q
, d
.buf
,
1932 rd
->arg
.p ? rd
->arg
.p
: &tvflt_nonneg
, tv
))
1933 { rc
= -1; goto end
; }
1935 if (!*q
) tvec_readword(tv
, &d
, &q
, ";", 0);
1937 for (u
= duration_units
; u
->unit
; u
++)
1938 if (STRCMP(q
, ==, u
->unit
)) { t
*= u
->scale
; goto found_unit
; }
1939 rc
= tvec_syntax(tv
, *q
, "end-of-line"); goto end
;
1943 if (tvec_flushtoeol(tv
, 0)) { rc
= -1; goto end
; }
1950 /* --- @dump_duration@ --- *
1952 * Arguments: @const union tvec_regval *rv@ = register value
1953 * @const struct tvec_regdef *rd@ = register definition
1954 * @unsigned style@ = output style (@TVSF_...@)
1955 * @const struct gprintf_ops *gops@, @void *gp@ = format output
1959 * Use: Dump a register value to the format output.
1961 * Durations are dumped as a human-palatable scaled value with
1962 * unit, and, if compact style is not requested, as a raw number
1963 * of seconds at full precision as a comment.
1966 static void dump_duration(const union tvec_regval
*rv
,
1967 const struct tvec_regdef
*rd
,
1969 const struct gprintf_ops
*gops
, void *go
)
1971 const struct duration_unit
*u
;
1976 for (u
= duration_units
; u
->scale
> t
&& u
[1].unit
; u
++);
1980 gprintf(gops
, go
, "%.4g %s", t
, u ? u
->unit
: "s");
1981 if (!(style
&TVSF_COMPACT
)) {
1982 gprintf(gops
, go
, "; = ");
1983 format_floating(gops
, go
, rv
->f
);
1984 gprintf(gops
, go
, " s");
1988 /* Duration type definition. */
1989 const struct tvec_regty tvty_duration
= {
1990 init_float
, trivial_release
, eq_float
,
1991 tobuf_float
, frombuf_float
,
1992 parse_duration
, dump_duration
1995 /*----- Enumerations ------------------------------------------------------*/
1997 /* --- @init_tenum@ --- *
1999 * Arguments: @union tvec_regval *rv@ = register value
2000 * @const struct tvec_regdef *rd@ = register definition
2004 * Use: Initialize a register value.
2006 * Integer and floating-point enumeration values are initialized
2007 * as their underlying representations. Pointer enumerations
2008 * are initialized to %|#nil|%.
2011 #define init_ienum init_int
2012 #define init_uenum init_uint
2013 #define init_fenum init_float
2015 static void init_penum(union tvec_regval
*rv
, const struct tvec_regdef
*rd
)
2018 /* --- @eq_tenum@ --- *
2020 * Arguments: @const union tvec_regval *rv0, *rv1@ = register values
2021 * @const struct tvec_regdef *rd@ = register definition
2023 * Returns: Nonzero if the values are equal, zero if unequal
2025 * Use: Compare register values for equality.
2027 * Integer and floating-point enumeration values are compared as
2028 * their underlying representations; in particular, floating-
2029 * point enumerations may compare equal if their absolute or
2030 * relative difference is sufficiently small. Pointer
2031 * enumerations are compared as pointers.
2034 #define eq_ienum eq_int
2035 #define eq_uenum eq_uint
2037 static int eq_fenum(const union tvec_regval
*rv0
,
2038 const union tvec_regval
*rv1
,
2039 const struct tvec_regdef
*rd
)
2041 const struct tvec_fenuminfo
*ei
= rd
->arg
.p
;
2042 return (eqish_floating_p(rv0
->f
, rv1
->f
, ei
->fi
));
2045 static int eq_penum(const union tvec_regval
*rv0
,
2046 const union tvec_regval
*rv1
,
2047 const struct tvec_regdef
*rd
)
2048 { return (rv0
->p
== rv1
->p
); }
2050 /* --- @tobuf_tenum@ --- *
2052 * Arguments: @buf *b@ = buffer
2053 * @const union tvec_regval *rv@ = register value
2054 * @const struct tvec_regdef *rd@ = register definition
2056 * Returns: Zero on success, %$-1$% on failure.
2058 * Use: Serialize a register value to a buffer.
2060 * Integer and floating-point enumeration values are serialized
2061 * as their underlying representations. Pointer enumerations
2062 * are serialized as the signed integer index into the
2063 * association table; %|#nil|% serializes as %$-1$%, and
2064 * unrecognized pointers cause failure.
2067 #define tobuf_ienum tobuf_int
2068 #define tobuf_uenum tobuf_uint
2069 #define tobuf_fenum tobuf_float
2071 static int tobuf_penum(buf
*b
, const union tvec_regval
*rv
,
2072 const struct tvec_regdef
*rd
)
2074 const struct tvec_penuminfo
*pei
= rd
->arg
.p
;
2075 const struct tvec_passoc
*pa
;
2078 for (pa
= pei
->av
, i
= 0; pa
->tag
; pa
++, i
++)
2079 if (pa
->p
== rv
->p
) goto found
;
2083 return (signed_to_buf(b
, i
));
2086 /* --- @frombuf_tenum@ --- *
2088 * Arguments: @buf *b@ = buffer
2089 * @union tvec_regval *rv@ = register value
2090 * @const struct tvec_regdef *rd@ = register definition
2092 * Returns: Zero on success, %$-1$% on failure.
2094 * Use: Deserialize a register value from a buffer.
2096 * Integer and floating-point enumeration values are serialized
2097 * as their underlying representations. Pointer enumerations
2098 * are serialized as the signed integer index into the
2099 * association table; %|#nil|% serializes as %$-1$%; out-of-
2100 * range indices cause failure.
2103 #define frombuf_ienum frombuf_int
2104 #define frombuf_uenum frombuf_uint
2105 #define frombuf_fenum frombuf_float
2106 static int frombuf_penum(buf
*b
, union tvec_regval
*rv
,
2107 const struct tvec_regdef
*rd
)
2109 const struct tvec_penuminfo
*pei
= rd
->arg
.p
;
2110 const struct tvec_passoc
*pa
;
2113 for (pa
= pei
->av
, n
= 0; pa
->tag
; pa
++, n
++);
2114 if (signed_from_buf(b
, &i
)) return (-1);
2115 if (0 <= i
&& i
< n
) rv
->p
= (/*unconst*/ void *)pei
->av
[i
].p
;
2116 else if (i
== -1) rv
->p
= 0;
2117 else { buf_break(b
); return (-1); }
2121 /* --- @parse_tenum@ --- *
2123 * Arguments: @union tvec_regval *rv@ = register value
2124 * @const struct tvec_regdef *rd@ = register definition
2125 * @struct tvec_state *tv@ = test-vector state
2127 * Returns: Zero on success, %$-1$% on error.
2129 * Use: Parse a register value from an input file.
2131 * An enumerated value may be given by name or as a literal
2132 * value. For enumerations based on numeric types, the literal
2133 * values can be written in the same syntax as the underlying
2134 * values. For enumerations based on pointers, the only
2135 * permitted literal is %|#nil|%, which denotes a null pointer.
2138 #define DEFPARSE_ENUM(tag_, ty, slot) \
2139 static int parse_##slot##enum(union tvec_regval *rv, \
2140 const struct tvec_regdef *rd, \
2141 struct tvec_state *tv) \
2143 const struct tvec_##slot##enuminfo *ei = rd->arg.p; \
2144 const struct tvec_##slot##assoc *a; \
2145 dstr d = DSTR_INIT; \
2148 if (tvec_readword(tv, &d, 0, ";", "enumeration tag or " LITSTR_##tag_)) \
2149 { rc = -1; goto end; } \
2150 for (a = ei->av; a->tag; a++) \
2151 if (STRCMP(a->tag, ==, d.buf)) { FOUND_##tag_ goto done; } \
2154 if (tvec_flushtoeol(tv, 0)) { rc = -1; goto end; } \
2161 #define LITSTR_INT "literal signed integer"
2162 #define FOUND_INT rv->i = a->i;
2163 #define MISSING_INT if (parse_signed(&rv->i, d.buf, ei->ir, tv)) \
2164 { rc = -1; goto end; }
2166 #define LITSTR_UINT "literal unsigned integer"
2167 #define FOUND_UINT rv->u = a->u;
2168 #define MISSING_UINT if (parse_unsigned(&rv->u, d.buf, ei->ur, tv)) \
2169 { rc = -1; goto end; }
2171 #define LITSTR_FLT "literal floating-point number, " \
2172 "`#-inf', `#+inf', or `#nan'"
2173 #define FOUND_FLT rv->f = a->f;
2174 #define MISSING_FLT if (parse_floating(&rv->f, 0, d.buf, ei->fi, tv)) \
2175 { rc = -1; goto end; }
2177 #define LITSTR_PTR "`#nil'"
2178 #define FOUND_PTR rv->p = (/*unconst*/ void *)a->p;
2179 #define MISSING_PTR if (STRCMP(d.buf, ==, "#nil")) \
2182 tvec_error(tv, "unknown `%s' value `%s'", \
2184 rc = -1; goto end; \
2187 TVEC_MISCSLOTS(DEFPARSE_ENUM
)
2205 #undef DEFPARSE_ENUM
2207 /* --- @dump_tenum@ --- *
2209 * Arguments: @const union tvec_regval *rv@ = register value
2210 * @const struct tvec_regdef *rd@ = register definition
2211 * @unsigned style@ = output style (@TVSF_...@)
2212 * @const struct gprintf_ops *gops@, @void *gp@ = format output
2216 * Use: Dump a register value to the format output.
2218 * Enumeration values are dumped as their symbolic names, if
2219 * possible, with the underlying values provided as a comment
2220 * unless compact output is requested, as for the underlying
2221 * representation. A null pointer is printed as %|#nil|%;
2222 * non-null pointers are printed as %|#<TYPE PTR>|%, with the
2223 * enumeration TYPE and the raw pointer PTR printed with the
2224 * system's %|%p|% format specifier.
2228 #define DEFDUMP_ENUM(tag_, ty, slot) \
2229 static void dump_##slot##enum(const union tvec_regval *rv, \
2230 const struct tvec_regdef *rd, \
2232 const struct gprintf_ops *gops, void *go) \
2234 const struct tvec_##slot##enuminfo *ei = rd->arg.p; \
2235 const struct tvec_##slot##assoc *a; \
2237 for (a = ei->av; a->tag; a++) \
2238 if (rv->slot == a->slot) { \
2239 gprintf(gops, go, "%s", a->tag); \
2240 if (style&TVSF_COMPACT) return; \
2241 gprintf(gops, go, " ; = "); break; \
2247 #define MAYBE_PRINT_EXTRA \
2248 if (style&TVSF_COMPACT) /* nothing to do */; \
2249 else if (!a->tag) { gprintf(gops, go, " ; = "); goto _extra; } \
2250 else if (1) { gprintf(gops, go, " = "); goto _extra; } \
2253 #define PRINTRAW_INT gprintf(gops, go, "%ld", rv->i); \
2254 MAYBE_PRINT_EXTRA { \
2255 format_signed_hex(gops, go, rv->i); \
2256 maybe_format_signed_char(gops, go, rv->i); \
2259 #define PRINTRAW_UINT gprintf(gops, go, "%lu", rv->u); \
2260 MAYBE_PRINT_EXTRA { \
2261 format_unsigned_hex(gops, go, rv->u); \
2262 maybe_format_unsigned_char(gops, go, rv->u); \
2265 #define PRINTRAW_FLT format_floating(gops, go, rv->f);
2267 #define PRINTRAW_PTR if (!rv->p) gprintf(gops, go, "#nil"); \
2268 else gprintf(gops, go, "#<%s %p>", ei->name, rv->p);
2270 TVEC_MISCSLOTS(DEFDUMP_ENUM
)
2273 #undef PRINTRAW_UINT
2277 #undef MAYBE_PRINT_EXTRA
2280 /* Enumeration type definitions. */
2281 #define DEFTY_ENUM(tag, ty, slot) \
2282 const struct tvec_regty tvty_##slot##enum = { \
2283 init_##slot##enum, trivial_release, eq_##slot##enum, \
2284 tobuf_##slot##enum, frombuf_##slot##enum, \
2285 parse_##slot##enum, dump_##slot##enum \
2287 TVEC_MISCSLOTS(DEFTY_ENUM
)
2290 /* Predefined enumeration types. */
2291 static const struct tvec_iassoc bool_assoc
[] = {
2308 const struct tvec_ienuminfo tvenum_bool
=
2309 { "bool", bool_assoc
, &tvrange_int
};
2311 static const struct tvec_iassoc cmp_assoc
[] = {
2327 const struct tvec_ienuminfo tvenum_cmp
=
2328 { "cmp", cmp_assoc
, &tvrange_int
};
2330 /* --- @tvec_claimeq_tenum@ --- *
2332 * Arguments: @struct tvec_state *tv@ = test-vector state
2333 * @const struct tvec_typeenuminfo *ei@ = enumeration type info
2334 * @ty t0, t1@ = two values
2335 * @const char *file@, @unsigned @lno@ = calling file and line
2336 * @const char *expr@ = the expression to quote on failure
2338 * Returns: Nonzero if @t0@ and @t1@ are equal, otherwise zero.
2340 * Use: Check that values of @t0@ and @t1@ are equal. As for
2341 * @tvec_claim@ above, a test case is automatically begun and
2342 * ended if none is already underway. If the values are
2343 * unequal, then @tvec_fail@ is called, quoting @expr@, and the
2344 * mismatched values are dumped: @t0@ is printed as the output
2345 * value and @t1@ is printed as the input reference.
2348 #define DEFCLAIM(tag, ty, slot) \
2349 int tvec_claimeq_##slot##enum \
2350 (struct tvec_state *tv, \
2351 const struct tvec_##slot##enuminfo *ei, ty e0, ty e1, \
2352 const char *file, unsigned lno, const char *expr) \
2354 union tvec_misc arg; \
2357 tv->out[0].v.slot = GET_##tag(e0); \
2358 tv->in[0].v.slot = GET_##tag(e1); \
2359 return (tvec_claimeq(tv, &tvty_##slot##enum, &arg, \
2360 file, lno, expr)); \
2362 #define GET_INT(e) (e)
2363 #define GET_UINT(e) (e)
2364 #define GET_FLT(e) (e)
2365 #define GET_PTR(e) ((/*unconst*/ void *)(e))
2366 TVEC_MISCSLOTS(DEFCLAIM
)
2373 /*----- Flag types --------------------------------------------------------*/
2375 /* Flag types are initialized, compared, and serialized as unsigned
2379 /* --- @parse_flags@ --- *
2381 * Arguments: @union tvec_regval *rv@ = register value
2382 * @const struct tvec_regdef *rd@ = register definition
2383 * @struct tvec_state *tv@ = test-vector state
2385 * Returns: Zero on success, %$-1$% on error.
2387 * Use: Parse a register value from an input file.
2389 * The input syntax is a sequence of items separated by `|'
2390 * signs. Each item may be the symbolic name of a field value,
2391 * or a literal unsigned integer. The masks associated with the
2392 * given symbolic names must be disjoint. The resulting
2393 * numerical value is simply the bitwise OR of the given values.
2396 static int parse_flags(union tvec_regval
*rv
, const struct tvec_regdef
*rd
,
2397 struct tvec_state
*tv
)
2399 const struct tvec_flaginfo
*fi
= rd
->arg
.p
;
2400 const struct tvec_flag
*f
;
2401 unsigned long m
= 0, v
= 0, t
;
2407 /* Read the next item. */
2409 if (tvec_readword(tv
, &d
, 0, "|;", "flag name or integer"))
2410 { rc
= -1; goto end
; }
2412 /* Try to find a matching entry in the table. */
2413 for (f
= fi
->fv
; f
->tag
; f
++)
2414 if (STRCMP(f
->tag
, ==, d
.buf
)) {
2416 { tvec_error(tv
, "colliding flag setting"); rc
= -1; goto end
; }
2418 { m
|= f
->m
; v
|= f
->v
; goto next
; }
2421 /* Otherwise, try to parse it as a raw integer. */
2422 if (parse_unsigned(&t
, d
.buf
, fi
->range
, tv
))
2423 { rc
= -1; goto end
; }
2427 /* Advance to the next token. If it's a separator then consume it, and
2428 * go round again. Otherwise we stop here.
2430 if (tvec_nexttoken(tv
)) break;
2432 if (ch
!= '|') { tvec_syntax(tv
, ch
, "`|'"); rc
= -1; goto end
; }
2433 if (tvec_nexttoken(tv
))
2434 { tvec_syntax(tv
, '\n', "flag name or integer"); rc
= -1; goto end
; }
2444 /* --- @dump_flags@ --- *
2446 * Arguments: @const union tvec_regval *rv@ = register value
2447 * @const struct tvec_regdef *rd@ = register definition
2448 * @unsigned style@ = output style (@TVSF_...@)
2449 * @const struct gprintf_ops *gops@, @void *gp@ = format output
2453 * Use: Dump a register value to the format output.
2455 * The table of symbolic names and their associated values and
2456 * masks is repeatedly scanned, in order, to find disjoint
2457 * matches -- i.e., entries whose value matches the target value
2458 * in the bit positions indicated by the mask, and whose mask
2459 * doesn't overlap with any previously found matches; the names
2460 * are then output, separated by `|'. Any remaining nonzero
2461 * bits not covered by any of the matching masks are output as a
2462 * single literal integer, in hex.
2464 * Unless compact output is requested, or no symbolic names were
2465 * found, the raw numeric value is also printed in hex, as a
2469 static void dump_flags(const union tvec_regval
*rv
,
2470 const struct tvec_regdef
*rd
,
2472 const struct gprintf_ops
*gops
, void *go
)
2474 const struct tvec_flaginfo
*fi
= rd
->arg
.p
;
2475 const struct tvec_flag
*f
;
2476 unsigned long m
= ~0ul, v
= rv
->u
;
2479 for (f
= fi
->fv
, sep
= ""; f
->tag
; f
++)
2480 if ((m
&f
->m
) && (v
&f
->m
) == f
->v
) {
2481 gprintf(gops
, go
, "%s%s", sep
, f
->tag
); m
&= ~f
->m
;
2482 sep
= style
&TVSF_COMPACT ?
"|" : " | ";
2485 if (v
&m
) gprintf(gops
, go
, "%s0x%0*lx", sep
, hex_width(v
), v
&m
);
2487 if (m
!= ~0ul && !(style
&TVSF_COMPACT
))
2488 gprintf(gops
, go
, " ; = 0x%0*lx", hex_width(rv
->u
), rv
->u
);
2491 /* Flags type definition. */
2492 const struct tvec_regty tvty_flags
= {
2493 init_uint
, trivial_release
, eq_uint
,
2494 tobuf_uint
, frombuf_uint
,
2495 parse_flags
, dump_flags
2498 /* --- @tvec_claimeq_flags@ --- *
2500 * Arguments: @struct tvec_state *tv@ = test-vector state
2501 * @const struct tvec_flaginfo *fi@ = flags type info
2502 * @unsigned long f0, f1@ = two values
2503 * @const char *file@, @unsigned @lno@ = calling file and line
2504 * @const char *expr@ = the expression to quote on failure
2506 * Returns: Nonzero if @f0@ and @f1@ are equal, otherwise zero.
2508 * Use: Check that values of @f0@ and @f1@ are equal. As for
2509 * @tvec_claim@ above, a test case is automatically begun and
2510 * ended if none is already underway. If the values are
2511 * unequal, then @tvec_fail@ is called, quoting @expr@, and the
2512 * mismatched values are dumped: @f0@ is printed as the output
2513 * value and @f1@ is printed as the input reference.
2516 int tvec_claimeq_flags(struct tvec_state
*tv
,
2517 const struct tvec_flaginfo
*fi
,
2518 unsigned long f0
, unsigned long f1
,
2519 const char *file
, unsigned lno
, const char *expr
)
2521 union tvec_misc arg
;
2523 arg
.p
= fi
; tv
->out
[0].v
.u
= f0
; tv
->in
[0].v
.u
= f1
;
2524 return (tvec_claimeq(tv
, &tvty_flags
, &arg
, file
, lno
, expr
));
2527 /*----- Characters --------------------------------------------------------*/
2529 /* Character values are initialized and compared as signed integers. */
2531 /* --- @tobuf_char@ --- *
2533 * Arguments: @buf *b@ = buffer
2534 * @const union tvec_regval *rv@ = register value
2535 * @const struct tvec_regdef *rd@ = register definition
2537 * Returns: Zero on success, %$-1$% on failure.
2539 * Use: Serialize a register value to a buffer.
2541 * Character values are serialized as little-endian 32-bit
2542 * unsigned integers, with %|EOF|% serialized as all-bits-set.
2545 static int tobuf_char(buf
*b
, const union tvec_regval
*rv
,
2546 const struct tvec_regdef
*rd
)
2550 if (0 <= rv
->i
&& rv
->i
<= UCHAR_MAX
) u
= rv
->i
;
2551 else if (rv
->i
== EOF
) u
= MASK32
;
2552 else { buf_break(b
); return (-1); }
2553 return (buf_putu32l(b
, u
));
2556 /* --- @frombuf_char@ --- *
2558 * Arguments: @buf *b@ = buffer
2559 * @union tvec_regval *rv@ = register value
2560 * @const struct tvec_regdef *rd@ = register definition
2562 * Returns: Zero on success, %$-1$% on failure.
2564 * Use: Deserialize a register value from a buffer.
2566 * Character values are serialized as little-endian 32-bit
2567 * unsigned integers, with %|EOF|% serialized as all-bits-set.
2570 static int frombuf_char(buf
*b
, union tvec_regval
*rv
,
2571 const struct tvec_regdef
*rd
)
2575 if (buf_getu32l(b
, &u
)) return (-1);
2576 if (0 <= u
&& u
<= UCHAR_MAX
) rv
->i
= u
;
2577 else if (u
== MASK32
) rv
->i
= EOF
;
2578 else { buf_break(b
); return (-1); }
2582 /* --- @parse_char@ --- *
2584 * Arguments: @union tvec_regval *rv@ = register value
2585 * @const struct tvec_regdef *rd@ = register definition
2586 * @struct tvec_state *tv@ = test-vector state
2588 * Returns: Zero on success, %$-1$% on error.
2590 * Use: Parse a register value from an input file.
2592 * A character value can be given by symbolic name, with a
2593 * leading `%|#|%'; or a character or `%|\|%'-escape sequence,
2594 * optionally in single quotes.
2596 * The following escape sequences and character names are
2599 * * `%|#eof|%' is the special end-of-file marker.
2601 * * `%|#nul|%' is the NUL character, sometimes used to
2602 * terminate strings.
2604 * * `%|bell|%', `%|bel|%', `%|ding|%', or `%|\a|%' is the BEL
2605 * character used to ring the terminal bell (or do some other
2606 * thing to attract the user's attention).
2608 * * %|#backspace|%, %|#bs|%, or %|\b|% is the backspace
2609 * character, used to move the cursor backwords by one cell.
2611 * * %|#escape|% %|#esc|%, or%|\e|% is the escape character,
2612 * used to introduce special terminal commands.
2614 * * %|#formfeed|%, %|#ff|%, or %|\f|% is the formfeed
2615 * character, used to separate pages of text.
2617 * * %|#newline|%, %|#linefeed|%, %|#lf|%, %|#nl|%, or %|\n|% is
2618 * the newline character, used to terminate lines of text or
2619 * advance the cursor to the next line (perhaps without
2620 * returning it to the start of the line).
2622 * * %|#return|%, %|#carriage-return|%, %|#cr|%, or %|\r|% is
2623 * the carriage-return character, used to return the cursor to
2624 * the start of the line.
2626 * * %|#tab|%, %|#horizontal-tab|%, %|#ht|%, or %|\t|% is the
2627 * tab character, used to advance the cursor to the next tab
2628 * stop on the current line.
2630 * * %|#vertical-tab|%, %|#vt|%, %|\v|% is the vertical tab
2633 * * %|#space|%, %|#spc|% is the space character.
2635 * * %|#delete|%, %|#del|% is the delete character, used to
2636 * erase the most recent character.
2638 * * %|\'|% is the single-quote character.
2640 * * %|\\|% is the backslash character.
2642 * * %|\"|% is the double-quote character.
2644 * * %|\NNN|% or %|\{NNN}|% is the character with code NNN in
2645 * octal. The NNN may be up to three digits long.
2647 * * %|\xNN|% or %|\x{NN}|% is the character with code NNN in
2651 static int parse_char(union tvec_regval
*rv
, const struct tvec_regdef
*rd
,
2652 struct tvec_state
*tv
)
2659 /* Inspect the character to see what we're up against. */
2663 /* It looks like a special token. Push the `%|#|%' back and fetch the
2664 * whole word. If there's just the `%|#|%' after all, then treat it as
2669 if (tvec_readword(tv
, &d
, 0, ";", "character name"))
2670 { rc
= -1; goto end
; }
2671 if (STRCMP(d
.buf
, !=, "#")) {
2672 if (read_charname(&ch
, d
.buf
, RCF_EOFOK
)) {
2673 rc
= tvec_error(tv
, "unknown character name `%s'", d
.buf
);
2676 if (tvec_flushtoeol(tv
, 0)) { rc
= -1; goto end
; }
2677 rv
->i
= ch
; rc
= 0; goto end
;
2681 /* If this is a single quote then we expect to see a matching one later,
2682 * and we should process backslash escapes. Get the next character and see
2685 if (ch
== '\'') { f
|= f_quote
; ch
= getc(tv
->fp
); }
2687 /* Main character dispatch. */
2691 /* Unquoted, semicolon begins a comment. */
2692 if (!(f
&f_quote
)) { rc
= tvec_syntax(tv
, ch
, "character"); goto end
; }
2696 /* A newline. If we saw a single quote, then treat that as literal.
2697 * Otherwise this is an error.
2699 if (!(f
&f_quote
)) goto nochar
;
2700 else { f
&= ~f_quote
; ungetc(ch
, tv
->fp
); ch
= '\''; goto plain
; }
2703 /* End-of-file. Similar to newline, but with slightly different
2704 * effects on the parse state.
2706 if (!(f
&f_quote
)) goto nochar
;
2707 else { f
&= ~f_quote
; ch
= '\''; goto plain
; }
2710 /* A single quote. This must be the second of a pair, and there should
2711 * have been a character or escape sequence between them.
2713 rc
= tvec_syntax(tv
, ch
, "character"); goto end
;
2716 /* A backslash. Read a character escape. */
2717 if (read_charesc(&ch
, tv
)) return (-1);
2720 /* Anything else. Treat as literal. */
2724 /* If we saw an opening quote, then expect the closing quote. */
2727 if (ch
!= '\'') { rc
= tvec_syntax(tv
, ch
, "`''"); goto end
; }
2731 if (tvec_flushtoeol(tv
, 0)) { rc
= -1; goto end
; }
2740 /* --- @dump_char@ --- *
2742 * Arguments: @const union tvec_regval *rv@ = register value
2743 * @const struct tvec_regdef *rd@ = register definition
2744 * @unsigned style@ = output style (@TVSF_...@)
2745 * @const struct gprintf_ops *gops@, @void *gp@ = format output
2749 * Use: Dump a register value to the format output.
2751 * Character values are dumped as their symbolic names, if any,
2752 * or as a character or escape sequence within single quotes
2753 * (which may be omitted in compact style). If compact output
2754 * is not requested, then the single-quoted representation (for
2755 * characters dumped as symbolic names) and integer code in
2756 * decimal and hex are printed as a comment.
2759 static void dump_char(const union tvec_regval
*rv
,
2760 const struct tvec_regdef
*rd
,
2762 const struct gprintf_ops
*gops
, void *go
)
2768 /* Print a character name if we can find one. */
2769 p
= find_charname(rv
->i
, (style
&TVSF_COMPACT
) ? CTF_SHORT
: CTF_PREFER
);
2771 gprintf(gops
, go
, "%s", p
);
2772 if (style
&TVSF_COMPACT
) return;
2773 else { gprintf(gops
, go
, " ;"); f
|= f_semi
; }
2776 /* If the character isn't @EOF@ then print it as a single-quoted thing.
2777 * In compact style, see if we can omit the quotes.
2780 if (f
&f_semi
) gprintf(gops
, go
, " = ");
2782 case ' ': case '\\': case '\'': quote
:
2783 format_char(gops
, go
, rv
->i
);
2786 if (!(style
&TVSF_COMPACT
) || !isprint(rv
->i
)) goto quote
;
2787 gprintf(gops
, go
, "%c", (int)rv
->i
);
2792 /* And the character code as an integer. */
2793 if (!(style
&TVSF_COMPACT
)) {
2794 if (!(f
&f_semi
)) gprintf(gops
, go
, " ;");
2795 gprintf(gops
, go
, " = %ld = ", rv
->i
);
2796 format_signed_hex(gops
, go
, rv
->i
);
2802 /* Character type definition. */
2803 const struct tvec_regty tvty_char
= {
2804 init_int
, trivial_release
, eq_int
,
2805 tobuf_char
, frombuf_char
,
2806 parse_char
, dump_char
2809 /* --- @tvec_claimeq_char@ --- *
2811 * Arguments: @struct tvec_state *tv@ = test-vector state
2812 * @int ch0, ch1@ = two character codes
2813 * @const char *file@, @unsigned @lno@ = calling file and line
2814 * @const char *expr@ = the expression to quote on failure
2816 * Returns: Nonzero if @ch0@ and @ch1@ are equal, otherwise zero.
2818 * Use: Check that values of @ch0@ and @ch1@ are equal. As for
2819 * @tvec_claim@ above, a test case is automatically begun and
2820 * ended if none is already underway. If the values are
2821 * unequal, then @tvec_fail@ is called, quoting @expr@, and the
2822 * mismatched values are dumped: @ch0@ is printed as the output
2823 * value and @ch1@ is printed as the input reference.
2826 int tvec_claimeq_char(struct tvec_state
*tv
, int c0
, int c1
,
2827 const char *file
, unsigned lno
, const char *expr
)
2829 tv
->out
[0].v
.i
= c0
; tv
->in
[0].v
.i
= c1
;
2830 return (tvec_claimeq(tv
, &tvty_char
, 0, file
, lno
, expr
));
2833 /*----- Text and byte strings ---------------------------------------------*/
2835 /* --- @init_text@, @init_bytes@ --- *
2837 * Arguments: @union tvec_regval *rv@ = register value
2838 * @const struct tvec_regdef *rd@ = register definition
2842 * Use: Initialize a register value.
2844 * Text and binary string values are initialized with a null
2845 * pointer and zero length.
2848 static void init_text(union tvec_regval
*rv
, const struct tvec_regdef
*rd
)
2849 { rv
->text
.p
= 0; rv
->text
.sz
= 0; }
2851 static void init_bytes(union tvec_regval
*rv
, const struct tvec_regdef
*rd
)
2852 { rv
->bytes
.p
= 0; rv
->bytes
.sz
= 0; }
2854 /* --- @release_string@, @release_bytes@ --- *
2856 * Arguments: @const union tvec_regval *rv@ = register value
2857 * @const struct tvec_regdef *rd@ = register definition
2861 * Use: Release resources held by a register value.
2863 * Text and binary string buffers are freed.
2866 static void release_text(union tvec_regval
*rv
,
2867 const struct tvec_regdef
*rd
)
2868 { xfree(rv
->text
.p
); }
2870 static void release_bytes(union tvec_regval
*rv
,
2871 const struct tvec_regdef
*rd
)
2872 { xfree(rv
->bytes
.p
); }
2874 /* --- @eq_text@, @eq_bytes@ --- *
2876 * Arguments: @const union tvec_regval *rv0, *rv1@ = register values
2877 * @const struct tvec_regdef *rd@ = register definition
2879 * Returns: Nonzero if the values are equal, zero if unequal
2881 * Use: Compare register values for equality.
2884 static int eq_text(const union tvec_regval
*rv0
,
2885 const union tvec_regval
*rv1
,
2886 const struct tvec_regdef
*rd
)
2888 return (rv0
->text
.sz
== rv1
->text
.sz
&&
2890 MEMCMP(rv0
->text
.p
, ==, rv1
->text
.p
, rv1
->text
.sz
)));
2893 static int eq_bytes(const union tvec_regval
*rv0
,
2894 const union tvec_regval
*rv1
,
2895 const struct tvec_regdef
*rd
)
2897 return (rv0
->bytes
.sz
== rv1
->bytes
.sz
&&
2899 MEMCMP(rv0
->bytes
.p
, ==, rv1
->bytes
.p
, rv1
->bytes
.sz
)));
2902 /* --- @tobuf_text@, @tobuf_bytes@ --- *
2904 * Arguments: @buf *b@ = buffer
2905 * @const union tvec_regval *rv@ = register value
2906 * @const struct tvec_regdef *rd@ = register definition
2908 * Returns: Zero on success, %$-1$% on failure.
2910 * Use: Serialize a register value to a buffer.
2912 * Text and binary string values are serialized as a little-
2913 * endian 64-bit length %$n$% in bytes followed by %$n$% bytes
2917 static int tobuf_text(buf
*b
, const union tvec_regval
*rv
,
2918 const struct tvec_regdef
*rd
)
2919 { return (buf_putmem64l(b
, rv
->text
.p
, rv
->text
.sz
)); }
2921 static int tobuf_bytes(buf
*b
, const union tvec_regval
*rv
,
2922 const struct tvec_regdef
*rd
)
2923 { return (buf_putmem64l(b
, rv
->bytes
.p
, rv
->bytes
.sz
)); }
2925 /* --- @frombuf_text@, @frombuf_bytes@ --- *
2927 * Arguments: @buf *b@ = buffer
2928 * @union tvec_regval *rv@ = register value
2929 * @const struct tvec_regdef *rd@ = register definition
2931 * Returns: Zero on success, %$-1$% on failure.
2933 * Use: Deserialize a register value from a buffer.
2935 * Text and binary string values are serialized as a little-
2936 * endian 64-bit length %$n$% in bytes followed by %$n$% bytes
2940 static int frombuf_text(buf
*b
, union tvec_regval
*rv
,
2941 const struct tvec_regdef
*rd
)
2946 p
= buf_getmem64l(b
, &sz
); if (!p
) return (-1);
2947 tvec_alloctext(rv
, sz
); memcpy(rv
->text
.p
, p
, sz
); rv
->text
.p
[sz
] = 0;
2951 static int frombuf_bytes(buf
*b
, union tvec_regval
*rv
,
2952 const struct tvec_regdef
*rd
)
2957 p
= buf_getmem64l(b
, &sz
); if (!p
) return (-1);
2958 tvec_allocbytes(rv
, sz
); memcpy(rv
->bytes
.p
, p
, sz
);
2962 /* --- @check_string_length@ --- *
2964 * Arguments: @size_t sz@ = found string length
2965 * @const struct tvec_urange *ur@ = acceptable range
2966 * @struct tvec_state *tv@ = test-vector state
2968 * Returns: Zero on success, %$-1$% on error.
2970 * Use: Checks that @sz@ is within the bounds described by @ur@,
2971 * reporting an error if not.
2974 static int check_string_length(size_t sz
, const struct tvec_urange
*ur
,
2975 struct tvec_state
*tv
)
2977 if (ur
&& (ur
->min
> sz
|| sz
> ur
->max
))
2978 return (tvec_error(tv
,
2979 "invalid string length %lu; must be in [%lu .. %lu]",
2980 (unsigned long)sz
, ur
->min
, ur
->max
));
2984 /* --- @parse_text@, @parse_bytes@ --- *
2986 * Arguments: @union tvec_regval *rv@ = register value
2987 * @const struct tvec_regdef *rd@ = register definition
2988 * @struct tvec_state *tv@ = test-vector state
2990 * Returns: Zero on success, %$-1$% on error.
2992 * Use: Parse a register value from an input file.
2994 * The input format for both kinds of strings is basically the
2995 * same: a `compound string', consisting of
2997 * * single-quoted strings, which are interpreted entirely
2998 * literally, but can't contain single quotes or newlines;
3000 * * double-quoted strings, in which `%|\|%'-escapes are
3001 * interpreted as for characters;
3003 * * character names, marked by an initial `%|#|%' sign;
3005 * * special tokens marked by an initial `%|!|%' sign; or
3007 * * barewords interpreted according to the current coding
3010 * The special tokens are
3012 * * `%|!bare|%', which causes subsequent sequences of
3013 * barewords to be treated as plain text;
3015 * * `%|!hex|%', `%|!base32|%', `%|!base64|%', which cause
3016 * subsequent barewords to be decoded in the requested
3019 * * `%|!repeat|% %$n$% %|{|% %%\textit{string}%% %|}|%',
3020 * which includes %$n$% copies of the (compound) string.
3022 * The only difference between text and binary strings is that
3023 * the initial coding scheme is %|bare|% for text strings and
3024 * %|hex|% for binary strings.
3027 static int parse_text(union tvec_regval
*rv
, const struct tvec_regdef
*rd
,
3028 struct tvec_state
*tv
)
3030 void *p
= rv
->text
.p
;
3032 if (read_compound_string(&p
, &rv
->text
.sz
, TVCODE_BARE
, 0, tv
))
3035 if (check_string_length(rv
->text
.sz
, rd
->arg
.p
, tv
)) return (-1);
3039 static int parse_bytes(union tvec_regval
*rv
, const struct tvec_regdef
*rd
,
3040 struct tvec_state
*tv
)
3042 void *p
= rv
->bytes
.p
;
3044 if (read_compound_string(&p
, &rv
->bytes
.sz
, TVCODE_HEX
, 0, tv
))
3047 if (check_string_length(rv
->bytes
.sz
, rd
->arg
.p
, tv
)) return (-1);
3051 /* --- @dump_text@, @dump_bytes@ --- *
3053 * Arguments: @const union tvec_regval *rv@ = register value
3054 * @const struct tvec_regdef *rd@ = register definition
3055 * @unsigned style@ = output style (@TVSF_...@)
3056 * @const struct gprintf_ops *gops@, @void *gp@ = format output
3060 * Use: Dump a register value to the format output.
3062 * Text string values are dumped as plain text, in double quotes
3063 * if necessary, and using backslash escape sequences for
3064 * nonprintable characters. Unless compact output is requested,
3065 * strings consisting of multiple lines are dumped with each
3066 * line of the string on a separate output line.
3068 * Binary string values are dumped in hexadecimal. In compact
3069 * style, the output simply consists of a single block of hex
3070 * digits. Otherwise, the dump is a display consisting of
3071 * groups of hex digits, with comments showing the offset (if
3072 * the string is long enough) and the corresponding plain text.
3074 * Empty strings are dumped as %|""|%.
3077 static void dump_text(const union tvec_regval
*rv
,
3078 const struct tvec_regdef
*rd
,
3080 const struct gprintf_ops
*gops
, void *go
)
3082 const unsigned char *p
, *q
, *l
;
3084 #define f_nonword 1u
3085 #define f_newline 2u
3087 if (!rv
->text
.sz
) { gprintf(gops
, go
, "\"\""); return; }
3089 p
= (const unsigned char *)rv
->text
.p
; l
= p
+ rv
->text
.sz
;
3091 case '!': case '#': case ';': case '"': case '\'':
3092 case '(': case '{': case '[': case ']': case '}': case ')':
3093 f
|= f_nonword
; break;
3095 for (q
= p
; q
< l
; q
++)
3096 if (*q
== '\n' && q
!= l
- 1) f
|= f_newline
;
3097 else if (!*q
|| !isgraph(*q
) || *q
== '\\') f
|= f_nonword
;
3098 if (f
&f_newline
) { gprintf(gops
, go
, "\n\t"); goto quote
; }
3099 else if (f
&f_nonword
) goto quote
;
3101 gops
->putm(go
, (const char *)p
, rv
->text
.sz
);
3105 gprintf(gops
, go
, "\"");
3106 for (q
= p
; q
< l
; q
++)
3107 if (!isprint(*q
) || *q
== '"') {
3108 if (p
< q
) gops
->putm(go
, (const char *)p
, q
- p
);
3109 if (*q
!= '\n' || (style
&TVSF_COMPACT
))
3110 format_charesc(gops
, go
, *q
, FCF_BRACE
);
3112 if (q
+ 1 == l
) { gprintf(gops
, go
, "\\n\""); return; }
3113 else gprintf(gops
, go
, "\\n\"\n\t\"");
3117 if (p
< q
) gops
->putm(go
, (const char *)p
, q
- p
);
3118 gprintf(gops
, go
, "\"");
3124 static void dump_bytes(const union tvec_regval
*rv
,
3125 const struct tvec_regdef
*rd
,
3127 const struct gprintf_ops
*gops
, void *go
)
3129 const unsigned char *p
= rv
->bytes
.p
, *l
= p
+ rv
->bytes
.sz
;
3130 size_t off
, sz
= rv
->bytes
.sz
;
3135 gprintf(gops
, go
, style
&TVSF_COMPACT ?
"\"\"" : "\"\" ; empty");
3139 if (style
&TVSF_COMPACT
) {
3140 while (p
< l
) gprintf(gops
, go
, "%02x", *p
++);
3144 if (sz
> 16) gprintf(gops
, go
, "\n\t");
3146 off
= 0; wd
= hex_width(sz
);
3148 if (l
- p
< 16) n
= l
- p
;
3151 for (i
= 0; i
< n
; i
++) {
3152 if (i
< n
) gprintf(gops
, go
, "%02x", p
[i
]);
3153 else gprintf(gops
, go
, " ");
3154 if (i
< n
- 1 && i
%4 == 3) gprintf(gops
, go
, " ");
3156 gprintf(gops
, go
, " ; ");
3157 if (sz
> 16) gprintf(gops
, go
, "[%0*lx] ", wd
, (unsigned long)off
);
3158 for (i
= 0; i
< n
; i
++)
3159 gprintf(gops
, go
, "%c", isprint(p
[i
]) ? p
[i
] : '.');
3161 if (p
< l
) gprintf(gops
, go
, "\n\t");
3165 /* Text and byte string type definitions. */
3166 const struct tvec_regty tvty_text
= {
3167 init_text
, release_text
, eq_text
,
3168 tobuf_text
, frombuf_text
,
3169 parse_text
, dump_text
3171 const struct tvec_regty tvty_bytes
= {
3172 init_bytes
, release_bytes
, eq_bytes
,
3173 tobuf_bytes
, frombuf_bytes
,
3174 parse_bytes
, dump_bytes
3177 /* --- @tvec_claimeq_text@ --- *
3179 * Arguments: @struct tvec_state *tv@ = test-vector state
3180 * @const char *p0@, @size_t sz0@ = first string with length
3181 * @const char *p1@, @size_t sz1@ = second string with length
3182 * @const char *file@, @unsigned @lno@ = calling file and line
3183 * @const char *expr@ = the expression to quote on failure
3185 * Returns: Nonzero if the strings at @p0@ and @p1@ are equal, otherwise
3188 * Use: Check that strings at @p0@ and @p1@ are equal. As for
3189 * @tvec_claim@ above, a test case is automatically begun and
3190 * ended if none is already underway. If the values are
3191 * unequal, then @tvec_fail@ is called, quoting @expr@, and the
3192 * mismatched values are dumped: @p0@ is printed as the output
3193 * value and @p1@ is printed as the input reference.
3196 int tvec_claimeq_text(struct tvec_state
*tv
,
3197 const char *p0
, size_t sz0
,
3198 const char *p1
, size_t sz1
,
3199 const char *file
, unsigned lno
, const char *expr
)
3201 tv
->out
[0].v
.text
.p
= (/*unconst*/ char *)p0
; tv
->out
[0].v
.text
.sz
= sz0
;
3202 tv
->in
[0].v
.text
.p
=(/*unconst*/ char *) p1
; tv
->in
[0].v
.text
.sz
= sz1
;
3203 return (tvec_claimeq(tv
, &tvty_text
, 0, file
, lno
, expr
));
3206 /* --- @tvec_claimeq_textz@ --- *
3208 * Arguments: @struct tvec_state *tv@ = test-vector state
3209 * @const char *p0, *p1@ = two strings to compare
3210 * @const char *file@, @unsigned @lno@ = calling file and line
3211 * @const char *expr@ = the expression to quote on failure
3213 * Returns: Nonzero if the strings at @p0@ and @p1@ are equal, otherwise
3216 * Use: Check that strings at @p0@ and @p1@ are equal, as for
3217 * @tvec_claimeq_string@, except that the strings are assumed
3218 * null-terminated, so their lengths don't need to be supplied
3222 int tvec_claimeq_textz(struct tvec_state
*tv
,
3223 const char *p0
, const char *p1
,
3224 const char *file
, unsigned lno
, const char *expr
)
3226 tv
->out
[0].v
.text
.p
= (/*unconst*/ char *)p0
;
3227 tv
->out
[0].v
.text
.sz
= strlen(p0
);
3228 tv
->in
[0].v
.text
.p
= (/*unconst*/ char *)p1
;
3229 tv
->in
[0].v
.text
.sz
= strlen(p1
);
3230 return (tvec_claimeq(tv
, &tvty_text
, 0, file
, lno
, expr
));
3233 /* --- @tvec_claimeq_bytes@ --- *
3235 * Arguments: @struct tvec_state *tv@ = test-vector state
3236 * @const void *p0@, @size_t sz0@ = first string with length
3237 * @const void *p1@, @size_t sz1@ = second string with length
3238 * @const char *file@, @unsigned @lno@ = calling file and line
3239 * @const char *expr@ = the expression to quote on failure
3241 * Returns: Nonzero if the strings at @p0@ and @p1@ are equal, otherwise
3244 * Use: Check that binary strings at @p0@ and @p1@ are equal. As for
3245 * @tvec_claim@ above, a test case is automatically begun and
3246 * ended if none is already underway. If the values are
3247 * unequal, then @tvec_fail@ is called, quoting @expr@, and the
3248 * mismatched values are dumped: @p0@ is printed as the output
3249 * value and @p1@ is printed as the input reference.
3252 int tvec_claimeq_bytes(struct tvec_state
*tv
,
3253 const void *p0
, size_t sz0
,
3254 const void *p1
, size_t sz1
,
3255 const char *file
, unsigned lno
, const char *expr
)
3257 tv
->out
[0].v
.bytes
.p
= (/*unconst*/ void *)p0
;
3258 tv
->out
[0].v
.bytes
.sz
= sz0
;
3259 tv
->in
[0].v
.bytes
.p
= (/*unconst*/ void *)p1
;
3260 tv
->in
[0].v
.bytes
.sz
= sz1
;
3261 return (tvec_claimeq(tv
, &tvty_bytes
, 0, file
, lno
, expr
));
3264 /* --- @tvec_alloctext@, @tvec_allocbytes@ --- *
3266 * Arguments: @union tvec_regval *rv@ = register value
3267 * @size_t sz@ = required size
3271 * Use: Allocated space in a text or binary string register. If the
3272 * current register size is sufficient, its buffer is left
3273 * alone; otherwise, the old buffer, if any, is freed and a
3274 * fresh buffer allocated. These functions are not intended to
3275 * be used to adjust a buffer repeatedly, e.g., while building
3276 * output incrementally: (a) they will perform badly, and (b)
3277 * the old buffer contents are simply discarded if reallocation
3278 * is necessary. Instead, use a @dbuf@ or @dstr@.
3280 * The @tvec_alloctext@ function sneakily allocates an extra
3281 * byte for a terminating zero. The @tvec_allocbytes@ function
3285 void tvec_alloctext(union tvec_regval
*rv
, size_t sz
)
3287 if (rv
->text
.sz
<= sz
) { xfree(rv
->text
.p
); rv
->text
.p
= xmalloc(sz
+ 1); }
3291 void tvec_allocbytes(union tvec_regval
*rv
, size_t sz
)
3293 if (rv
->bytes
.sz
< sz
) { xfree(rv
->bytes
.p
); rv
->bytes
.p
= xmalloc(sz
); }
3297 /*----- Buffer type -------------------------------------------------------*/
3299 /* --- @init_buffer@ --- *
3301 * Arguments: @union tvec_regval *rv@ = register value
3302 * @const struct tvec_regdef *rd@ = register definition
3306 * Use: Initialize a register value.
3308 * Buffer values values are initialized with a null pointer,
3309 * zero length, and zero residue, modulus, and offset.
3312 static void init_buffer(union tvec_regval
*rv
, const struct tvec_regdef
*rd
)
3313 { rv
->buf
.p
= 0; rv
->buf
.sz
= rv
->buf
.a
= rv
->buf
.m
= rv
->buf
.off
= 0; }
3315 /* --- @release_buffer@, @release_bytes@ --- *
3317 * Arguments: @const union tvec_regval *rv@ = register value
3318 * @const struct tvec_regdef *rd@ = register definition
3322 * Use: Release resources held by a register value.
3324 * Buffers are freed.
3327 static void release_buffer(union tvec_regval
*rv
,
3328 const struct tvec_regdef
*rd
)
3329 { if (rv
->buf
.p
) xfree(rv
->buf
.p
- rv
->buf
.off
); }
3331 /* --- @eq_buffer@ --- *
3333 * Arguments: @const union tvec_regval *rv0, *rv1@ = register values
3334 * @const struct tvec_regdef *rd@ = register definition
3336 * Returns: Nonzero if the values are equal, zero if unequal
3338 * Use: Compare register values for equality.
3340 * Buffer values are equal if and only if their sizes and
3341 * alignment parameters are equal; their contents are
3342 * %%\emph{not}%% compared.
3345 static int eq_buffer(const union tvec_regval
*rv0
,
3346 const union tvec_regval
*rv1
,
3347 const struct tvec_regdef
*rd
)
3349 return (rv0
->buf
.sz
== rv1
->buf
.sz
&&
3350 rv0
->buf
.a
== rv1
->buf
.a
&&
3351 rv0
->buf
.m
== rv1
->buf
.m
);
3354 /* --- @tobuf_buffer@ --- *
3356 * Arguments: @buf *b@ = buffer
3357 * @const union tvec_regval *rv@ = register value
3358 * @const struct tvec_regdef *rd@ = register definition
3360 * Returns: Zero on success, %$-1$% on failure.
3362 * Use: Serialize a register value to a buffer.
3364 * Buffer values are serialized as their lengths, residues, and
3365 * moduli, as unsigned integers.
3368 static int tobuf_buffer(buf
*b
, const union tvec_regval
*rv
,
3369 const struct tvec_regdef
*rd
)
3371 return (unsigned_to_buf(b
, rv
->buf
.sz
) ||
3372 unsigned_to_buf(b
, rv
->buf
.a
) ||
3373 unsigned_to_buf(b
, rv
->buf
.m
));
3376 /* --- @frombuf_buffer@ --- *
3378 * Arguments: @buf *b@ = buffer
3379 * @union tvec_regval *rv@ = register value
3380 * @const struct tvec_regdef *rd@ = register definition
3382 * Returns: Zero on success, %$-1$% on failure.
3384 * Use: Deserialize a register value from a buffer.
3386 * Buffer values are serialized as just their lengths, as
3387 * unsigned integers. The buffer is allocated on
3388 * deserialization and filled with a distinctive pattern.
3391 static int frombuf_buffer(buf
*b
, union tvec_regval
*rv
,
3392 const struct tvec_regdef
*rd
)
3394 unsigned long sz
, a
, m
;
3396 if (unsigned_from_buf(b
, &sz
)) return (-1);
3397 if (unsigned_from_buf(b
, &a
)) return (-1);
3398 if (unsigned_from_buf(b
, &m
)) return (-1);
3399 if (sz
> (size_t)-1 || a
> (size_t)-1 || m
> (size_t)-1)
3400 { buf_break(b
); return (-1); }
3401 rv
->buf
.sz
= sz
; rv
->buf
.a
= a
; rv
->buf
.m
= m
;
3405 /* --- @parse_buffer@ --- *
3407 * Arguments: @union tvec_regval *rv@ = register value
3408 * @const struct tvec_regdef *rd@ = register definition
3409 * @struct tvec_state *tv@ = test-vector state
3411 * Returns: Zero on success, %$-1$% on error.
3413 * Use: Parse a register value from an input file.
3415 * The input format for a buffer value consists of an unsigned
3416 * integer followed by an optional unit specifier consisting of
3417 * an SI unit prefix and (optionally) the letter `B'. Unit
3418 * prefixes denote %%\emph{binary}%% multipliers, not decimal.
3420 * The buffer is allocated and filled with a distinctive
3424 static int parse_buffer(union tvec_regval
*rv
,
3425 const struct tvec_regdef
*rd
,
3426 struct tvec_state
*tv
)
3428 size_t sz
, a
= 0, m
= 0;
3431 if (parse_size(tv
, &sz
, "@;", "buffer length")) { rc
= -1; goto end
; }
3432 if (check_string_length(sz
, rd
->arg
.p
, tv
)) { rc
= -1; goto end
; }
3436 if (ch
== ';' || ch
== '\n') { ungetc(ch
, tv
->fp
); goto done
; }
3437 else if (ch
!= '@') { rc
= tvec_syntax(tv
, ch
, "`@'"); goto end
; }
3439 if (parse_size(tv
, &m
, "+;", "alignment quantum")) { rc
= -1; goto end
; }
3444 if (ch
== ';' || ch
== '\n') { ungetc(ch
, tv
->fp
); goto done
; }
3445 else if (ch
!= '+') { rc
= tvec_syntax(tv
, ch
, "`+'"); goto end
; }
3447 if (parse_size(tv
, &a
, ";", "alignment offset")) { rc
= -1; goto end
; }
3449 rc
= tvec_error(tv
, "alignment offset %lu >= quantum %lu",
3450 (unsigned long)a
, (unsigned long)m
);
3455 if (tvec_flushtoeol(tv
, 0)) { rc
= -1; goto end
; }
3456 rv
->buf
.sz
= sz
; rv
->buf
.a
= a
; rv
->buf
.m
= m
;
3462 /* --- @dump_buffer@ --- *
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 * Buffer values are dumped as their size with an appropriate
3474 * unit specifier. A unit prefix is only used if the size is an
3475 * exact multiple of the relevant power of two.
3478 static void dump_buffer(const union tvec_regval
*rv
,
3479 const struct tvec_regdef
*rd
,
3481 const struct gprintf_ops
*gops
, void *go
)
3483 format_size(gops
, go
, rv
->buf
.sz
, style
);
3485 gprintf(gops
, go
, style
&TVSF_COMPACT ?
"@" : " @ ");
3486 format_size(gops
, go
, rv
->buf
.m
, style
);
3488 gprintf(gops
, go
, style
&TVSF_COMPACT ?
"+" : " + ");
3489 format_size(gops
, go
, rv
->buf
.a
, style
);
3492 if (!(style
&TVSF_COMPACT
)) {
3493 gprintf(gops
, go
, " ; = %lu", (unsigned long)rv
->buf
.sz
);
3495 gprintf(gops
, go
, " @ %lu", (unsigned long)rv
->buf
.m
);
3496 if (rv
->buf
.a
) gprintf(gops
, go
, " + %lu", (unsigned long)rv
->buf
.a
);
3498 gprintf(gops
, go
, " = "); format_unsigned_hex(gops
, go
, rv
->buf
.sz
);
3500 gprintf(gops
, go
, " @ "); format_unsigned_hex(gops
, go
, rv
->buf
.m
);
3502 gprintf(gops
, go
, " + ");
3503 format_unsigned_hex(gops
, go
, rv
->buf
.a
);
3509 /* Buffer type definition. */
3510 const struct tvec_regty tvty_buffer
= {
3511 init_buffer
, release_buffer
, eq_buffer
,
3512 tobuf_buffer
, frombuf_buffer
,
3513 parse_buffer
, dump_buffer
3516 /* --- @tvec_initbuffer@ --- *
3518 * Arguments: @union tvec_regval *rv@ = register value
3519 * @const union tvec_regval *src@ = source buffer
3520 * @size_t sz@ = size to allocate
3524 * Use: Initialize the alignment parameters in @rv@ to match @src@,
3525 * and the size to @sz@.
3528 void tvec_initbuffer(union tvec_regval
*rv
,
3529 const union tvec_regval
*src
, size_t sz
)
3530 { rv
->buf
.sz
= sz
; rv
->buf
.a
= src
->buf
.a
; rv
->buf
.m
= src
->buf
.m
; }
3532 /* --- @tvec_allocbuffer@ --- *
3534 * Arguments: @union tvec_regval *rv@ = register value
3538 * Use: Allocate @sz@ bytes to the buffer and fill the space with a
3539 * distinctive pattern.
3542 void tvec_allocbuffer(union tvec_regval
*rv
)
3544 unsigned char *p
; size_t n
;
3546 if (rv
->buf
.p
) xfree(rv
->buf
.p
- rv
->buf
.off
);
3548 if (rv
->buf
.m
< 2) {
3549 rv
->buf
.p
= xmalloc(rv
->buf
.sz
); rv
->buf
.off
= 0;
3551 p
= xmalloc(rv
->buf
.sz
+ rv
->buf
.m
- 1);
3552 n
= (size_t)p
%rv
->buf
.m
;
3553 rv
->buf
.off
= (rv
->buf
.a
- n
+ rv
->buf
.m
)%rv
->buf
.m
;
3554 rv
->buf
.p
= p
+ rv
->buf
.off
;
3556 memset(rv
->buf
.p
, '?', rv
->buf
.sz
);
3559 /*----- That's all, folks -------------------------------------------------*/