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
)) 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;
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);
391 /*----- Floating-point utilities ------------------------------------------*/
393 /* --- @eqish_floating_p@ --- *
395 * Arguments: @double x, y@ = two numbers to compare
396 * @const struct tvec_floatinfo *fi@ = floating-point info
398 * Returns: Nonzero if the comparand @y@ is sufficiently close to the
399 * reference @x@, or zero if it's definitely different.
402 static int eqish_floating_p(double x
, double y
,
403 const struct tvec_floatinfo
*fi
)
407 if (NANP(x
)) return (NANP(y
)); else if (NANP(y
)) return (0);
408 if (INFP(x
)) return (x
== y
); else if (INFP(y
)) return (0);
410 switch (fi ? fi
->f
&TVFF_EQMASK
: TVFF_EXACT
) {
412 return (x
== y
&& NEGP(x
) == NEGP(y
));
414 t
= x
- y
; if (t
< 0) t
= -t
; return (t
< fi
->delta
);
416 t
= 1.0 - y
/x
; if (t
< 0) t
= -t
; return (t
< fi
->delta
);
422 /* --- @format_floating@ --- *
424 * Arguments: @const struct gprintf_ops *gops@ = print operations
425 * @void *go@ = print destination
426 * @double x@ = number to print
430 * Use: Print a floating-point number, accurately.
433 static void format_floating(const struct gprintf_ops
*gops
, void *go
,
439 gprintf(gops
, go
, "#nan");
441 gprintf(gops
, go
, x
> 0 ?
"#+inf" : "#-inf");
443 /* Ugh. C doesn't provide any function for just printing a
444 * floating-point number /correctly/, i.e., so that you can read the
445 * result back and recover the number you first thought of. There are
446 * complicated algorithms published for doing this, but I really don't
447 * want to get into that here. So we have this.
449 * The sign doesn't cause significant difficulty so we're going to ignore
450 * it for now. So suppose we're given a number %$x = f b^e$%, in
451 * base-%$b$% format, so %$f b^n$% and %$e$% are integers, with
452 * %$0 \le f < 1$%. We're going to convert it into the nearest integer
453 * of the form %$X = F B^E$%, with similar conditions, only with the
454 * additional requirement that %$X$% is normalized, i.e., that %$X = 0$%
455 * or %$F \ge B^{-N}$%.
457 * We're rounding to the nearest such %$X$%. If there is to be ambiguity
458 * in the conversion, then some %$x = f b^e$% and the next smallest
459 * representable number %$x' = x + b^{e-n}$% must both map to the same
460 * %$X$%, which means both %$x$% and %$x'$% must be nearer to %$X$% than
461 * any other number representable in the target system. The nest larger
462 * number is %$X' = X + B^{E-N}$%; the next smaller number will normally
463 * be %$W = X - B^{E-N}$%, but if %$F = 1/B$ then the next smaller number
464 * is actually %$X - B^{E-N-1}$%. We ignore this latter possibility in
465 * the pursuit of a conservative estimate (though actually it doesn't
468 * If both %$x$% and %$x'$% map to %$X$% then we must have
469 * %$L = X - B^{E-N}/2 \le x$% and %$x + b^{e-n} \le R = X + B^{E-N}/2$%;
470 * so firstly %$f b^e = x \ge L = W + B^{E-N}/2 > W = (F - B^{-N}) B^E$%,
471 * and secondly %$b^{e-n} \le B^{E-N}$%. Since these inequalities are in
472 * opposite senses, we can divide, giving
474 * %$f b^e/b^{e-n} > (F - B^{-N}) B^E/B^{E-N}$% ,
478 * %$f b^n > (F - B^{-N}) B^N = F B^N - 1$% .
480 * Now %$f \le 1 - b^{-n}$%, and %$F \ge B^{-1}$%, so, for this to be
481 * possible, it must be the case that
483 * %$(1 - b^{-n}) b^n = b^n - 1 > B^{N-1} - 1$% .
485 * Then rearrange and take logarithms, obtaining
487 * %$(N - 1) \log B < n \log b$% ,
491 * %$N < n \log b/\log B + 1$% .
493 * Recall that this is a necessary condition for a collision to occur; we
494 * are therefore safe whenever
496 * %$N \ge n \log b/\log B + 1$% ;
498 * so, taking ceilings,
500 * %$N \ge \lceil n \log b/\log B \rceil + 1$% .
502 * So that's why we have this.
504 * I'm going to assume that @n = DBL_MANT_DIG@ is sufficiently small that
505 * we can calculate this without ending up on the wrong side of an
508 * In C11, we have @DBL_DECIMAL_DIG@, which should be the same value only
509 * as a constant. Except that modern compilers are more than clever
510 * enough to work out that this is a constant anyway.
512 * This is sometimes an overestimate: we'll print out meaningless digits
513 * that don't represent anything we actually know about the number in
514 * question. To fix that, we'd need a complicated algorithm like Steele
515 * and White's Dragon4, Gay's @dtoa@, or Burger and Dybvig's algorithm
516 * (note that Loitsch's Grisu2 is conservative, and Grisu3 hands off to
517 * something else in difficult situations).
520 prec
= ceil(DBL_MANT_DIG
*log(FLT_RADIX
)/log(10)) + 1;
521 gprintf(gops
, go
, "%.*g", prec
, x
);
525 /* --- @parse_floating@ --- *
527 * Arguments: @double *x_out@ = where to put the result
528 * @const char *q_out@ = where to leave end pointer, or null
529 * @const char *p@ = string to parse
530 * @const struct tvec_floatinfo *fi@ = floating-point info
531 * @struct tvec_state *tv@ = test vector state
533 * Returns: Zero on success, @-1@ on error.
535 * Use: Parse a floating-point number from a string. Reports any
536 * necessary errors. If @q_out@ is not null then trailing
537 * material is permitted and a pointer to it is left in
538 * @*q_out@; this will be null if there is no trailing material.
541 static int parse_floating(double *x_out
, const char **q_out
, const char *p
,
542 const struct tvec_floatinfo
*fi
,
543 struct tvec_state
*tv
)
545 const char *pp
; char *q
;
550 if (q_out
) *q_out
= 0;
552 /* Check for special tokens. */
553 if (STRCMP(p
, ==, "#nan")) {
557 tvec_error(tv
, "NaN not supported on this system");
562 else if (STRCMP(p
, ==, "#inf") ||
563 STRCMP(p
, ==, "#+inf") || STRCMP(p
, ==, "+#inf")) {
565 x
= INFINITY
; rc
= 0;
567 tvec_error(tv
, "infinity not supported on this system");
572 else if (STRCMP(p
, ==, "#-inf") || STRCMP(p
, ==, "-#inf")) {
574 x
= -INFINITY
; rc
= 0;
576 tvec_error(tv
, "infinity not supported on this system");
581 /* Check that this looks like a number, so we can exclude `strtod'
582 * recognizing its own non-finite number tokens.
586 if (*pp
== '+' || *pp
== '-') pp
++;
587 if (*pp
== '.') pp
++;
589 tvec_syntax(tv
, *p ?
*p
: fgetc(tv
->fp
), "floating-point number");
593 /* Parse the number using the system parser. */
594 olderr
= errno
; errno
= 0;
596 if (!*q
) /* nothing to do */;
597 else if (q_out
) *q_out
= q
;
598 else { tvec_syntax(tv
, *q
, "end-of-line"); rc
= -1; goto end
; }
599 if (errno
&& (errno
!= ERANGE
|| (x
> 0 ?
-x
: x
) == HUGE_VAL
)) {
600 tvec_error(tv
, "invalid floating-point number `%.*s': %s",
601 (int)(q
- p
), p
, strerror(errno
));
607 /* Check that the number is acceptable. */
608 if (NANP(x
) && fi
&& !(fi
->f
&TVFF_NANOK
)) {
609 tvec_error(tv
, "#nan not allowed here");
613 if (fi
&& ((!(fi
->f
&TVFF_NOMIN
) && x
< fi
->min
) ||
614 (!(fi
->f
&TVFF_NOMAX
) && x
> fi
->max
))) {
615 dstr_puts(&d
, "floating-point number ");
616 format_floating(&dstr_printops
, &d
, x
);
617 dstr_puts(&d
, " out of range (must be in ");
618 if (fi
->f
&TVFF_NOMIN
)
619 dstr_puts(&d
, "(#-inf");
621 { dstr_putc(&d
, '['); format_floating(&dstr_printops
, &d
, fi
->min
); }
622 dstr_puts(&d
, " .. ");
623 if (fi
->f
&TVFF_NOMAX
)
624 dstr_puts(&d
, "#+inf)");
626 { format_floating(&dstr_printops
, &d
, fi
->max
); dstr_putc(&d
, ']'); }
627 dstr_putc(&d
, ')'); dstr_putz(&d
);
628 tvec_error(tv
, "%s", d
.buf
); rc
= -1; goto end
;
638 /*----- String utilities --------------------------------------------------*/
640 /* Special character name table. */
641 static const struct chartab
{
642 const char *name
; /* character name */
643 int ch
; /* character value */
644 unsigned f
; /* flags: */
645 #define CTF_PREFER 1u /* preferred name */
646 #define CTF_SHORT 2u /* short name (compact style) */
648 { "#eof", EOF
, CTF_PREFER
| CTF_SHORT
},
649 { "#nul", '\0', CTF_PREFER
},
650 { "#bell", '\a', CTF_PREFER
},
651 { "#ding", '\a', 0 },
652 { "#bel", '\a', CTF_SHORT
},
653 { "#backspace", '\b', CTF_PREFER
},
654 { "#bs", '\b', CTF_SHORT
},
655 { "#escape", '\x1b', CTF_PREFER
},
656 { "#esc", '\x1b', CTF_SHORT
},
657 { "#formfeed", '\f', CTF_PREFER
},
658 { "#ff", '\f', CTF_SHORT
},
659 { "#newline", '\n', CTF_PREFER
},
660 { "#linefeed", '\n', 0 },
661 { "#lf", '\n', CTF_SHORT
},
663 { "#return", '\r', CTF_PREFER
},
664 { "#carriage-return", '\r', 0 },
665 { "#cr", '\r', CTF_SHORT
},
666 { "#tab", '\t', CTF_PREFER
| CTF_SHORT
},
667 { "#horizontal-tab", '\t', 0 },
669 { "#vertical-tab", '\v', CTF_PREFER
},
670 { "#vt", '\v', CTF_SHORT
},
671 { "#space", ' ', 0 },
672 { "#spc", ' ', CTF_SHORT
},
673 { "#delete", '\x7f', CTF_PREFER
},
674 { "#del", '\x7f', CTF_SHORT
},
678 /* --- @find_charname@ --- *
680 * Arguments: @int ch@ = character to match
681 * @unsigned f@ = flags (@CTF_...@) to match
683 * Returns: The name of the character, or null if no match is found.
685 * Use: Looks up a name for a character. Specifically, it returns
686 * the first entry in the @chartab@ table which matches @ch@ and
687 * which has one of the flags @f@ set.
690 static const char *find_charname(int ch
, unsigned f
)
692 const struct chartab
*ct
;
694 for (ct
= chartab
; ct
->name
; ct
++)
695 if (ct
->ch
== ch
&& (ct
->f
&f
)) return (ct
->name
);
699 /* --- @read_charname@ --- *
701 * Arguments: @int *ch_out@ = where to put the character
702 * @const char *p@ = character name
703 * @unsigned f@ = flags (@TCF_...@)
705 * Returns: Zero if a match was found, @-1@ if not.
707 * Use: Looks up a character by name. If @RCF_EOFOK@ is set in @f@,
708 * then the @EOF@ marker can be matched; otherwise it can't.
712 static int read_charname(int *ch_out
, const char *p
, unsigned f
)
714 const struct chartab
*ct
;
716 for (ct
= chartab
; ct
->name
; ct
++)
717 if (STRCMP(p
, ==, ct
->name
) && ((f
&RCF_EOFOK
) || ct
->ch
>= 0))
718 { *ch_out
= ct
->ch
; return (0); }
722 /* --- @format_charesc@ --- *
724 * Arguments: @const struct gprintf_ops *gops@ = print operations
725 * @void *go@ = print destination
726 * @int ch@ = character to format
727 * @unsigned f@ = flags (@FCF_...@)
731 * Use: Format a character as an escape sequence, possibly as part of
732 * a larger string. If @FCF_BRACE@ is set in @f@, then put
733 * braces around a `\x...' code, so that it's suitable for use
734 * in a longer string.
738 static void format_charesc(const struct gprintf_ops
*gops
, void *go
,
742 case '\a': gprintf(gops
, go
, "\\a"); break;
743 case '\b': gprintf(gops
, go
, "\\b"); break;
744 case '\x1b': gprintf(gops
, go
, "\\e"); break;
745 case '\f': gprintf(gops
, go
, "\\f"); break;
746 case '\r': gprintf(gops
, go
, "\\r"); break;
747 case '\n': gprintf(gops
, go
, "\\n"); break;
748 case '\t': gprintf(gops
, go
, "\\t"); break;
749 case '\v': gprintf(gops
, go
, "\\v"); break;
750 case '\\': gprintf(gops
, go
, "\\\\"); break;
751 case '\'': gprintf(gops
, go
, "\\'"); break;
753 if (f
&FCF_BRACE
) gprintf(gops
, go
, "\\{0}");
754 else gprintf(gops
, go
, "\\0");
758 gprintf(gops
, go
, "\\x{%0*x}", hex_width(UCHAR_MAX
), ch
);
760 gprintf(gops
, go
, "\\x%0*x", hex_width(UCHAR_MAX
), ch
);
765 /* --- @format_char@ --- *
767 * Arguments: @const struct gprintf_ops *gops@ = print operations
768 * @void *go@ = print destination
769 * @int ch@ = character to format
773 * Use: Format a single character.
776 static void format_char(const struct gprintf_ops
*gops
, void *go
, int ch
)
779 case '\\': case '\'': escape
:
780 gprintf(gops
, go
, "'");
781 format_charesc(gops
, go
, ch
, 0);
782 gprintf(gops
, go
, "'");
785 if (!isprint(ch
)) goto escape
;
786 gprintf(gops
, go
, "'%c'", ch
);
791 /* --- @maybe_format_unsigned_char@, @maybe_format_signed_char@ --- *
793 * Arguments: @const struct gprintf_ops *gops@ = print operations
794 * @void *go@ = print destination
795 * @unsigned long u@ or @long i@ = an integer
799 * Use: Format a (signed or unsigned) integer as a character, if it's
800 * in range, printing something like `= 'q''. It's assumed that
801 * a comment marker has already been output.
804 static void maybe_format_unsigned_char
805 (const struct gprintf_ops
*gops
, void *go
, unsigned long u
)
809 p
= find_charname(u
, CTF_PREFER
);
810 if (p
) gprintf(gops
, go
, " = %s", p
);
812 { gprintf(gops
, go
, " = "); format_char(gops
, go
, u
); }
815 static void maybe_format_signed_char
816 (const struct gprintf_ops
*gops
, void *go
, long i
)
820 p
= find_charname(i
, CTF_PREFER
);
821 if (p
) gprintf(gops
, go
, " = %s", p
);
822 if (0 <= i
&& i
< UCHAR_MAX
)
823 { gprintf(gops
, go
, " = "); format_char(gops
, go
, i
); }
826 /* --- @read_charesc@ --- *
828 * Arguments: @int *ch_out@ = where to put the result
829 * @struct tvec_state *tv@ = test vector state
831 * Returns: Zero on success, @-1@ on error.
833 * Use: Parse and convert an escape sequence from @tv@'s input
834 * stream, assuming that the initial `\' has already been read.
835 * Reports errors as appropriate.
838 static int read_charesc(int *ch_out
, struct tvec_state
*tv
)
847 /* Things we shouldn't find. */
848 case EOF
: case '\n': return (tvec_syntax(tv
, ch
, "string escape"));
850 /* Single-character escapes. */
851 case '\'': *ch_out
= '\''; break;
852 case '\\': *ch_out
= '\\'; break;
853 case '"': *ch_out
= '"'; break;
854 case 'a': *ch_out
= '\a'; break;
855 case 'b': *ch_out
= '\b'; break;
856 case 'e': *ch_out
= '\x1b'; break;
857 case 'f': *ch_out
= '\f'; break;
858 case 'n': *ch_out
= '\n'; break;
859 case 'r': *ch_out
= '\r'; break;
860 case 't': *ch_out
= '\t'; break;
861 case 'v': *ch_out
= '\v'; break;
863 /* Hex escapes, with and without braces. */
866 if (ch
== '{') { f
|= f_brace
; ch
= getc(tv
->fp
); }
869 if (esc
< 0 || esc
>= 16) return (tvec_syntax(tv
, ch
, "hex digit"));
871 ch
= getc(tv
->fp
); i
= chtodig(ch
); if (i
< 0 || i
>= 16) break;
874 return (tvec_error(tv
,
875 "character code %d out of range", esc
));
877 if (!(f
&f_brace
)) ungetc(ch
, tv
->fp
);
878 else if (ch
!= '}') return (tvec_syntax(tv
, ch
, "`}'"));
882 /* Other things, primarily octal escapes. */
884 f
|= f_brace
; ch
= getc(tv
->fp
);
887 if ('0' <= ch
&& ch
< '8') {
888 i
= 1; esc
= ch
- '0';
891 if ('0' > ch
|| ch
>= '8') { ungetc(ch
, tv
->fp
); break; }
892 esc
= 8*esc
+ ch
- '0';
893 i
++; if (i
>= 3) break;
897 if (ch
!= '}') return (tvec_syntax(tv
, ch
, "`}'"));
900 return (tvec_error(tv
,
901 "character code %d out of range", esc
));
902 *ch_out
= esc
; break;
904 return (tvec_syntax(tv
, ch
, "string escape"));
913 /* --- @read_quoted_string@ --- *
915 * Arguments: @dstr *d@ = string to write to
916 * @int quote@ = initial quote, `'' or `"'
917 * @struct tvec_state *tv@ = test vector state
919 * Returns: Zero on success, @-1@ on error.
921 * Use: Read the rest of a quoted string into @d@, reporting errors
924 * A single-quoted string is entirely literal. A double-quoted
925 * string may contain C-like escapes.
928 static int read_quoted_string(dstr
*d
, int quote
, struct tvec_state
*tv
)
936 return (tvec_syntax(tv
, ch
, "`%c'", quote
));
938 if (quote
== '\'') goto ordinary
;
939 ch
= getc(tv
->fp
); if (ch
== '\n') { tv
->lno
++; break; }
940 ungetc(ch
, tv
->fp
); if (read_charesc(&ch
, tv
)) return (-1);
943 if (ch
== quote
) goto end
;
955 /* --- @collect_bare@ --- *
957 * Arguments: @dstr *d@ = string to write to
958 * @struct tvec_state *tv@ = test vector state
960 * Returns: Zero on success, @-1@ on error.
962 * Use: Read barewords and the whitespace between them. Stop when we
963 * encounter something which can't start a bareword.
966 static int collect_bare(dstr
*d
, struct tvec_state
*tv
)
969 enum { WORD
, SPACE
, ESCAPE
}; unsigned s
= WORD
;
976 tvec_syntax(tv
, ch
, "bareword");
979 if (s
== ESCAPE
) { tv
->lno
++; goto addch
; }
980 if (s
== WORD
) pos
= d
->len
;
981 ungetc(ch
, tv
->fp
); if (tvec_nexttoken(tv
)) { rc
= -1; goto end
; }
982 DPUTC(d
, ' '); s
= SPACE
;
984 case '"': case '\'': case '!': case '#': case ')': case '}': case ']':
985 if (s
== SPACE
) { ungetc(ch
, tv
->fp
); goto done
; }
991 if (s
!= ESCAPE
&& isspace(ch
)) {
992 if (s
== WORD
) pos
= d
->len
;
993 DPUTC(d
, ch
); s
= SPACE
;
997 DPUTC(d
, ch
); s
= WORD
;
1002 if (s
== SPACE
) d
->len
= pos
;
1008 /* --- @set_up_encoding@ --- *
1010 * Arguments: @const codec_class **ccl_out@ = where to put the class
1011 * @unsigned *f_out@ = where to put the flags
1012 * @unsigned code@ = the coding scheme to use (@TVEC_...@)
1016 * Use: Helper for @read_compound_string@ below.
1018 * Return the appropriate codec class and flags for @code@.
1019 * Leaves @*ccl_out@ null if the coding scheme doesn't have a
1020 * backing codec class (e.g., @TVCODE_BARE@).
1023 enum { TVCODE_BARE
, TVCODE_HEX
, TVCODE_BASE64
, TVCODE_BASE32
};
1024 static void set_up_encoding(const codec_class
**ccl_out
, unsigned *f_out
,
1029 *ccl_out
= 0; *f_out
= 0;
1032 *ccl_out
= &hex_class
; *f_out
= CDCF_IGNCASE
;
1035 *ccl_out
= &base32_class
; *f_out
= CDCF_IGNCASE
| CDCF_IGNEQPAD
;
1038 *ccl_out
= &base64_class
; *f_out
= CDCF_IGNEQPAD
;
1045 /* --- @flush_codec@ --- *
1047 * Arguments: @codec *cdc@ = a codec, or null
1048 * @dstr *d@ = output string
1049 * @struct tvec_state *tv@ = test vector state
1051 * Returns: Zero on success, @-1@ on error.
1053 * Use: Helper for @read_compound_string@ below.
1055 * Flush out any final buffered material from @cdc@, and check
1056 * that it's in a good state. Frees the codec on success. Does
1057 * nothing if @cdc@ is null.
1060 static int flush_codec(codec
*cdc
, dstr
*d
, struct tvec_state
*tv
)
1065 err
= cdc
->ops
->code(cdc
, 0, 0, d
);
1067 return (tvec_error(tv
, "invalid %s sequence end: %s",
1068 cdc
->ops
->c
->name
, codec_strerror(err
)));
1069 cdc
->ops
->destroy(cdc
);
1074 /* --- @read_compound_string@ --- *
1076 * Arguments: @void **p_inout@ = address of output buffer pointer
1077 * @size_t *sz_inout@ = address of buffer size
1078 * @unsigned code@ = initial interpretation of barewords
1079 * @unsigned f@ = other flags (@RCSF_...@)
1080 * @struct tvec_state *tv@ = test vector state
1082 * Returns: Zero on success, @-1@ on error.
1084 * Use: Parse a compound string, i.e., a sequence of stringish pieces
1085 * which might be quoted strings, character names, or barewords
1086 * to be decoded accoding to @code@, interspersed with
1087 * additional directives.
1089 * If the initial buffer pointer is non-null and sufficiently
1090 * large, then it will be reused; otherwise, it is freed and a
1091 * fresh, sufficiently large buffer is allocated and returned.
1094 #define RCSF_NESTED 1u
1095 static int read_compound_string(void **p_inout
, size_t *sz_inout
,
1096 unsigned code
, unsigned f
,
1097 struct tvec_state
*tv
)
1099 const codec_class
*ccl
; unsigned cdf
;
1101 dstr d
= DSTR_INIT
, w
= DSTR_INIT
;
1104 void *pp
= 0; size_t sz
;
1108 set_up_encoding(&ccl
, &cdf
, code
); cdc
= 0;
1110 if (tvec_nexttoken(tv
)) return (tvec_syntax(tv
, fgetc(tv
->fp
), "string"));
1115 case ')': case ']': case '}':
1116 /* Close brackets. Leave these for recursive caller if there is one,
1120 if (!(f
&RCSF_NESTED
))
1121 { rc
= tvec_syntax(tv
, ch
, "string"); goto end
; }
1122 ungetc(ch
, tv
->fp
); goto done
;
1124 case '"': case '\'':
1125 /* Quotes. Read a quoted string. */
1127 if (cdc
&& flush_codec(cdc
, &d
, tv
)) { rc
= -1; goto end
; }
1129 if (read_quoted_string(&d
, ch
, tv
)) { rc
= -1; goto end
; }
1133 /* A named character. */
1136 if (cdc
&& flush_codec(cdc
, &d
, tv
)) { rc
= -1; goto end
; }
1138 DRESET(&w
); tvec_readword(tv
, &w
, ";", "character name");
1139 if (read_charname(&ch
, w
.buf
, RCF_EOFOK
)) {
1140 rc
= tvec_error(tv
, "unknown character name `%s'", d
.buf
);
1143 DPUTC(&d
, ch
); break;
1146 /* A magic keyword. */
1148 if (cdc
&& flush_codec(cdc
, &d
, tv
)) { rc
= -1; goto end
; }
1151 DRESET(&w
); tvec_readword(tv
, &w
, ";", "`!'-keyword");
1153 /* Change bareword coding system. */
1154 if (STRCMP(w
.buf
, ==, "!bare"))
1155 { code
= TVCODE_BARE
; set_up_encoding(&ccl
, &cdf
, code
); }
1156 else if (STRCMP(w
.buf
, ==, "!hex"))
1157 { code
= TVCODE_HEX
; set_up_encoding(&ccl
, &cdf
, code
); }
1158 else if (STRCMP(w
.buf
, ==, "!base32"))
1159 { code
= TVCODE_BASE32
; set_up_encoding(&ccl
, &cdf
, code
); }
1160 else if (STRCMP(w
.buf
, ==, "!base64"))
1161 { code
= TVCODE_BASE64
; set_up_encoding(&ccl
, &cdf
, code
); }
1163 /* Repeated substrings. */
1164 else if (STRCMP(w
.buf
, ==, "!repeat")) {
1165 if (tvec_nexttoken(tv
)) {
1166 rc
= tvec_syntax(tv
, fgetc(tv
->fp
), "repeat count");
1170 if (tvec_readword(tv
, &w
, ";{", "repeat count"))
1171 { rc
= -1; goto end
; }
1172 if (parse_unsigned_integer(&n
, &q
, w
.buf
)) {
1173 rc
= tvec_error(tv
, "invalid repeat count `%s'", w
.buf
);
1176 if (*q
) { rc
= tvec_syntax(tv
, *q
, "`{'"); goto end
; }
1177 if (tvec_nexttoken(tv
))
1178 { rc
= tvec_syntax(tv
, fgetc(tv
->fp
), "`{'"); goto end
; }
1179 ch
= getc(tv
->fp
); if (ch
!= '{')
1180 { rc
= tvec_syntax(tv
, ch
, "`{'"); goto end
; }
1182 if (read_compound_string(&pp
, &sz
, code
, f
| RCSF_NESTED
, tv
))
1183 { rc
= -1; goto end
; }
1184 ch
= getc(tv
->fp
); if (ch
!= '}')
1185 { rc
= tvec_syntax(tv
, ch
, "`}'"); goto end
; }
1187 if (n
> (size_t)-1/sz
)
1188 { rc
= tvec_error(tv
, "repeat size out of range"); goto end
; }
1189 dstr_ensure(&d
, n
*sz
);
1191 { memset(d
.buf
+ d
.len
, *(unsigned char *)pp
, n
); d
.len
+= n
; }
1193 for (; n
--; d
.len
+= sz
) memcpy(d
.buf
+ d
.len
, pp
, sz
);
1198 /* Anything else is an error. */
1200 tvec_error(tv
, "unknown string keyword `%s'", w
.buf
);
1206 /* A bareword. Process it according to the current coding system. */
1211 if (collect_bare(&d
, tv
)) goto done
;
1215 ungetc(ch
, tv
->fp
); DRESET(&w
);
1216 if (tvec_readword(tv
, &w
, ";", "%s-encoded fragment", ccl
->name
))
1217 { rc
= -1; goto end
; }
1218 if (!cdc
) cdc
= ccl
->decoder(cdf
);
1219 err
= cdc
->ops
->code(cdc
, w
.buf
, w
.len
, &d
);
1221 tvec_error(tv
, "invalid %s fragment `%s': %s",
1222 ccl
->name
, w
.buf
, codec_strerror(err
));
1229 } while (!tvec_nexttoken(tv
));
1232 /* Wrap things up. */
1233 if (cdc
&& flush_codec(cdc
, &d
, tv
)) { rc
= -1; goto end
; }
1235 if (*sz_inout
<= d
.len
)
1236 { xfree(*p_inout
); *p_inout
= xmalloc(d
.len
+ 1); }
1237 p
= *p_inout
; memcpy(p
, d
.buf
, d
.len
); p
[d
.len
] = 0; *sz_inout
= d
.len
;
1241 /* Clean up any debris. */
1242 if (cdc
) cdc
->ops
->destroy(cdc
);
1244 dstr_destroy(&d
); dstr_destroy(&w
);
1248 /*----- Signed and unsigned integer types ---------------------------------*/
1250 /* --- @init_int@, @init_uint@ --- *
1252 * Arguments: @union tvec_regval *rv@ = register value
1253 * @const struct tvec_regdef *rd@ = register definition
1257 * Use: Initialize a register value.
1259 * Integer values are initialized to zero.
1262 static void init_int(union tvec_regval
*rv
, const struct tvec_regdef
*rd
)
1265 static void init_uint(union tvec_regval
*rv
, const struct tvec_regdef
*rd
)
1268 /* --- @eq_int@, @eq_uint@ --- *
1270 * Arguments: @const union tvec_regval *rv0, *rv1@ = register values
1271 * @const struct tvec_regdef *rd@ = register definition
1273 * Returns: Nonzero if the values are equal, zero if unequal
1275 * Use: Compare register values for equality.
1278 static int eq_int(const union tvec_regval
*rv0
, const union tvec_regval
*rv1
,
1279 const struct tvec_regdef
*rd
)
1280 { return (rv0
->i
== rv1
->i
); }
1282 static int eq_uint(const union tvec_regval
*rv0
,
1283 const union tvec_regval
*rv1
,
1284 const struct tvec_regdef
*rd
)
1285 { return (rv0
->u
== rv1
->u
); }
1287 /* --- @tobuf_int@, @tobuf_uint@ --- *
1289 * Arguments: @buf *b@ = buffer
1290 * @const union tvec_regval *rv@ = register value
1291 * @const struct tvec_regdef *rd@ = register definition
1293 * Returns: Zero on success, %$-1$% on failure.
1295 * Use: Serialize a register value to a buffer.
1297 * Integer values are serialized as little-endian 64-bit signed
1298 * or unsigned integers.
1301 static int tobuf_int(buf
*b
, const union tvec_regval
*rv
,
1302 const struct tvec_regdef
*rd
)
1303 { return (signed_to_buf(b
, rv
->i
)); }
1305 static int tobuf_uint(buf
*b
, const union tvec_regval
*rv
,
1306 const struct tvec_regdef
*rd
)
1307 { return (unsigned_to_buf(b
, rv
->u
)); }
1309 /* --- @frombuf_int@, @frombuf_uint@ --- *
1311 * Arguments: @buf *b@ = buffer
1312 * @union tvec_regval *rv@ = register value
1313 * @const struct tvec_regdef *rd@ = register definition
1315 * Returns: Zero on success, %$-1$% on failure.
1317 * Use: Deserialize a register value from a buffer.
1319 * Integer values are serialized as 64-bit signed or unsigned
1323 static int frombuf_int(buf
*b
, union tvec_regval
*rv
,
1324 const struct tvec_regdef
*rd
)
1325 { return (signed_from_buf(b
, &rv
->i
)); }
1327 static int frombuf_uint(buf
*b
, union tvec_regval
*rv
,
1328 const struct tvec_regdef
*rd
)
1329 { return (unsigned_from_buf(b
, &rv
->u
)); }
1331 /* --- @parse_int@, @parse_uint@ --- *
1333 * Arguments: @union tvec_regval *rv@ = register value
1334 * @const struct tvec_regdef *rd@ = register definition
1335 * @struct tvec_state *tv@ = test-vector state
1337 * Returns: Zero on success, %$-1$% on error.
1339 * Use: Parse a register value from an input file.
1341 * Integers may be input in decimal, hex, binary, or octal,
1342 * following approximately usual conventions.
1344 * * Signed integers may be preceded with a `+' or `-' sign.
1346 * * Decimal integers are just a sequence of decimal digits
1349 * * Octal integers are a sequence of digits `0' ... `7',
1350 * preceded by `0o' or `0O'.
1352 * * Hexadecimal integers are a sequence of digits `0'
1353 * ... `9', `a' ... `f', or `A' ... `F', preceded by `0x' or
1356 * * Radix-B integers are a sequence of digits `0' ... `9',
1357 * `a' ... `f', or `A' ... `F', each with value less than B,
1358 * preceded by `Br' or `BR', where 0 < B < 36 is expressed
1359 * in decimal without any leading `0' or internal
1362 * * A digit sequence may contain internal underscore `_'
1363 * separators, but not before or after all of the digits;
1364 * and two consecutive `_' characters are not permitted.
1367 static int parse_int(union tvec_regval
*rv
, const struct tvec_regdef
*rd
,
1368 struct tvec_state
*tv
)
1373 if (tvec_readword(tv
, &d
, ";", "signed integer")) { rc
= -1; goto end
; }
1374 if (parse_signed(&rv
->i
, d
.buf
, rd
->arg
.p
, tv
)) { rc
= -1; goto end
; }
1375 if (tvec_flushtoeol(tv
, 0)) { rc
= -1; goto end
; }
1382 static int parse_uint(union tvec_regval
*rv
, const struct tvec_regdef
*rd
,
1383 struct tvec_state
*tv
)
1388 if (tvec_readword(tv
, &d
, ";", "unsigned integer")) { rc
= -1; goto end
; }
1389 if (parse_unsigned(&rv
->u
, d
.buf
, rd
->arg
.p
, tv
)) { rc
= -1; goto end
; }
1390 if (tvec_flushtoeol(tv
, 0)) { rc
= -1; goto end
; }
1397 /* --- @dump_int@, @dump_uint@ --- *
1399 * Arguments: @const union tvec_regval *rv@ = register value
1400 * @const struct tvec_regdef *rd@ = register definition
1401 * @unsigned style@ = output style (@TVSF_...@)
1402 * @const struct gprintf_ops *gops@, @void *gp@ = format output
1406 * Use: Dump a register value to the format output.
1408 * Integer values are dumped in decimal and, unless compact
1409 * output is requested, hex, and maybe a character, as a
1413 static void dump_int(const union tvec_regval
*rv
,
1414 const struct tvec_regdef
*rd
,
1416 const struct gprintf_ops
*gops
, void *go
)
1419 gprintf(gops
, go
, "%ld", rv
->i
);
1420 if (!(style
&TVSF_COMPACT
)) {
1421 gprintf(gops
, go
, " ; = ");
1422 format_signed_hex(gops
, go
, rv
->i
);
1423 maybe_format_signed_char(gops
, go
, rv
->i
);
1427 static void dump_uint(const union tvec_regval
*rv
,
1428 const struct tvec_regdef
*rd
,
1430 const struct gprintf_ops
*gops
, void *go
)
1432 gprintf(gops
, go
, "%lu", rv
->u
);
1433 if (!(style
&TVSF_COMPACT
)) {
1434 gprintf(gops
, go
, " ; = ");
1435 format_unsigned_hex(gops
, go
, rv
->u
);
1436 maybe_format_unsigned_char(gops
, go
, rv
->u
);
1440 /* Integer type definitions. */
1441 const struct tvec_regty tvty_int
= {
1442 init_int
, trivial_release
, eq_int
,
1443 tobuf_int
, frombuf_int
,
1446 const struct tvec_regty tvty_uint
= {
1447 init_uint
, trivial_release
, eq_uint
,
1448 tobuf_uint
, frombuf_uint
,
1449 parse_uint
, dump_uint
1452 /* Predefined integer ranges. */
1453 const struct tvec_irange
1454 tvrange_schar
= { SCHAR_MIN
, SCHAR_MAX
},
1455 tvrange_short
= { SHRT_MIN
, SHRT_MAX
},
1456 tvrange_int
= { INT_MIN
, INT_MAX
},
1457 tvrange_long
= { LONG_MIN
, LONG_MAX
},
1458 tvrange_sbyte
= { -128, 127 },
1459 tvrange_i16
= { -32768, +32767 },
1460 tvrange_i32
= { -2147483648, 2147483647 };
1461 const struct tvec_urange
1462 tvrange_uchar
= { 0, UCHAR_MAX
},
1463 tvrange_ushort
= { 0, USHRT_MAX
},
1464 tvrange_uint
= { 0, UINT_MAX
},
1465 tvrange_ulong
= { 0, ULONG_MAX
},
1466 tvrange_size
= { 0, (size_t)-1 },
1467 tvrange_byte
= { 0, 255 },
1468 tvrange_u16
= { 0, 65535 },
1469 tvrange_u32
= { 0, 4294967296 };
1471 /* --- @tvec_claimeq_int@ --- *
1473 * Arguments: @struct tvec_state *tv@ = test-vector state
1474 * @long i0, i1@ = two signed integers
1475 * @const char *file@, @unsigned @lno@ = calling file and line
1476 * @const char *expr@ = the expression to quote on failure
1478 * Returns: Nonzero if @i0@ and @i1@ are equal, otherwise zero.
1480 * Use: Check that values of @i0@ and @i1@ are equal. As for
1481 * @tvec_claim@ above, a test case is automatically begun and
1482 * ended if none is already underway. If the values are
1483 * unequal, then @tvec_fail@ is called, quoting @expr@, and the
1484 * mismatched values are dumped: @i0@ is printed as the output
1485 * value and @i1@ is printed as the input reference.
1488 int tvec_claimeq_int(struct tvec_state
*tv
, long i0
, long i1
,
1489 const char *file
, unsigned lno
, const char *expr
)
1491 tv
->out
[0].v
.i
= i0
; tv
->in
[0].v
.i
= i1
;
1492 return (tvec_claimeq(tv
, &tvty_int
, 0, file
, lno
, expr
));
1495 /* --- @tvec_claimeq_uint@ --- *
1497 * Arguments: @struct tvec_state *tv@ = test-vector state
1498 * @unsigned long u0, u1@ = two unsigned integers
1499 * @const char *file@, @unsigned @lno@ = calling file and line
1500 * @const char *expr@ = the expression to quote on failure
1502 * Returns: Nonzero if @u0@ and @u1@ are equal, otherwise zero.
1504 * Use: Check that values of @u0@ and @u1@ are equal. As for
1505 * @tvec_claim@ above, a test case is automatically begun and
1506 * ended if none is already underway. If the values are
1507 * unequal, then @tvec_fail@ is called, quoting @expr@, and the
1508 * mismatched values are dumped: @u0@ is printed as the output
1509 * value and @u1@ is printed as the input reference.
1512 int tvec_claimeq_uint(struct tvec_state
*tv
,
1513 unsigned long u0
, unsigned long u1
,
1514 const char *file
, unsigned lno
, const char *expr
)
1516 tv
->out
[0].v
.u
= u0
; tv
->in
[0].v
.u
= u1
;
1517 return (tvec_claimeq(tv
, &tvty_uint
, 0, file
, lno
, expr
));
1520 /*----- Floating-point type -----------------------------------------------*/
1522 /* --- @int_float@ --- *
1524 * Arguments: @union tvec_regval *rv@ = register value
1525 * @const struct tvec_regdef *rd@ = register definition
1529 * Use: Initialize a register value.
1531 * Floating-point values are initialized to zero.
1534 static void init_float(union tvec_regval
*rv
, const struct tvec_regdef
*rd
)
1537 /* --- @eq_float@ --- *
1539 * Arguments: @const union tvec_regval *rv0, *rv1@ = register values
1540 * @const struct tvec_regdef *rd@ = register definition
1542 * Returns: Nonzero if the values are equal, zero if unequal
1544 * Use: Compare register values for equality.
1546 * Floating-point values may be considered equal if their
1547 * absolute or relative difference is sufficiently small, as
1548 * described in the register definition.
1551 static int eq_float(const union tvec_regval
*rv0
,
1552 const union tvec_regval
*rv1
,
1553 const struct tvec_regdef
*rd
)
1554 { return (eqish_floating_p(rv0
->f
, rv1
->f
, rd
->arg
.p
)); }
1556 /* --- @tobuf_float@ --- *
1558 * Arguments: @buf *b@ = buffer
1559 * @const union tvec_regval *rv@ = register value
1560 * @const struct tvec_regdef *rd@ = register definition
1562 * Returns: Zero on success, %$-1$% on failure.
1564 * Use: Serialize a register value to a buffer.
1566 * Floating-point values are serialized as little-endian
1567 * IEEE 754 Binary64.
1570 static int tobuf_float(buf
*b
, const union tvec_regval
*rv
,
1571 const struct tvec_regdef
*rd
)
1572 { return (buf_putf64l(b
, rv
->f
)); }
1574 /* --- @frombuf_float@ --- *
1576 * Arguments: @buf *b@ = buffer
1577 * @union tvec_regval *rv@ = register value
1578 * @const struct tvec_regdef *rd@ = register definition
1580 * Returns: Zero on success, %$-1$% on failure.
1582 * Use: Deserialize a register value from a buffer.
1584 * Floating-point values are serialized as little-endian
1585 * IEEE 754 Binary64.
1588 static int frombuf_float(buf
*b
, union tvec_regval
*rv
,
1589 const struct tvec_regdef
*rd
)
1590 { return (buf_getf64l(b
, &rv
->f
)); }
1592 /* --- @parse_float@ --- *
1594 * Arguments: @union tvec_regval *rv@ = register value
1595 * @const struct tvec_regdef *rd@ = register definition
1596 * @struct tvec_state *tv@ = test-vector state
1598 * Returns: Zero on success, %$-1$% on error.
1600 * Use: Parse a register value from an input file.
1602 * Floating-point values are either NaN (%|#nan|%, if supported
1603 * by the platform); positive or negative infinity (%|#inf|%,
1604 * %|+#inf|%, or %|#+inf|% (preferring the last), and %|-#inf|%
1605 * or %|#-inf|% (preferring the latter), if supported by the
1606 * platform); or a number in strtod(3) syntax.
1609 static int parse_float(union tvec_regval
*rv
, const struct tvec_regdef
*rd
,
1610 struct tvec_state
*tv
)
1615 if (tvec_readword(tv
, &d
, ";", "floating-point number"))
1616 { rc
= -1; goto end
; }
1617 if (parse_floating(&rv
->f
, 0, d
.buf
, rd
->arg
.p
, tv
))
1618 { rc
= -1; goto end
; }
1619 if (tvec_flushtoeol(tv
, 0)) { rc
= -1; goto end
; }
1626 /* --- @dump_float@ --- *
1628 * Arguments: @const union tvec_regval *rv@ = register value
1629 * @const struct tvec_regdef *rd@ = register definition
1630 * @unsigned style@ = output style (@TVSF_...@)
1631 * @const struct gprintf_ops *gops@, @void *gp@ = format output
1635 * Use: Dump a register value to the format output.
1637 * Floating-point values are dumped in decimal or as a special
1638 * token beginning with `%|#|%'. Some effort is taken to ensure
1639 * that the output is sufficient to uniquely identify the
1640 * original value, but, honestly, C makes this really hard.
1643 static void dump_float(const union tvec_regval
*rv
,
1644 const struct tvec_regdef
*rd
,
1646 const struct gprintf_ops
*gops
, void *go
)
1647 { format_floating(gops
, go
, rv
->f
); }
1649 /* Floating-point type definition. */
1650 const struct tvec_regty tvty_float
= {
1651 init_float
, trivial_release
, eq_float
,
1652 tobuf_float
, frombuf_float
,
1653 parse_float
, dump_float
1656 /* Predefined floating-point ranges. */
1657 const struct tvec_floatinfo
1658 tvflt_finite
= { TVFF_EXACT
, -DBL_MAX
, DBL_MAX
, 0.0 },
1659 tvflt_nonneg
= { TVFF_EXACT
, 0, DBL_MAX
, 0.0 };
1661 /* --- @tvec_claimeqish_float@ --- *
1663 * Arguments: @struct tvec_state *tv@ = test-vector state
1664 * @double f0, f1@ = two floating-point numbers
1665 * @unsigned f@ = flags (@TVFF_...@)
1666 * @double delta@ = maximum tolerable difference
1667 * @const char *file@, @unsigned @lno@ = calling file and line
1668 * @const char *expr@ = the expression to quote on failure
1670 * Returns: Nonzero if @f0@ and @u1@ are sufficiently close, otherwise
1673 * Use: Check that values of @f0@ and @f1@ are sufficiently close.
1674 * As for @tvec_claim@ above, a test case is automatically begun
1675 * and ended if none is already underway. If the values are
1676 * too far apart, then @tvec_fail@ is called, quoting @expr@,
1677 * and the mismatched values are dumped: @f0@ is printed as the
1678 * output value and @f1@ is printed as the input reference.
1680 * The details for the comparison are as follows.
1682 * * A NaN value matches any other NaN, and nothing else.
1684 * * An infinity matches another infinity of the same sign,
1687 * * If @f&TVFF_EQMASK@ is @TVFF_EXACT@, then any
1688 * representable number matches only itself: in particular,
1689 * positive and negative zero are considered distinct.
1690 * (This allows tests to check that they land on the correct
1691 * side of branch cuts, for example.)
1693 * * If @f&TVFF_EQMASK@ is @TVFF_ABSDELTA@, then %$x$% matches
1694 * %$y$% when %$|x - y| < \delta$%.
1696 * * If @f&TVFF_EQMASK@ is @TVFF_RELDELTA@, then %$x$% matches
1697 * %$y$% when %$|1 - y/x| < \delta$%. (Note that this
1698 * criterion is asymmetric FIXME
1701 int tvec_claimeqish_float(struct tvec_state
*tv
,
1702 double f0
, double f1
, unsigned f
, double delta
,
1703 const char *file
, unsigned lno
,
1706 struct tvec_floatinfo fi
;
1707 union tvec_misc arg
;
1709 fi
.f
= f
; fi
.min
= fi
.max
= 0.0; fi
.delta
= delta
; arg
.p
= &fi
;
1710 tv
->out
[0].v
.f
= f0
; tv
->in
[0].v
.f
= f1
;
1711 return (tvec_claimeq(tv
, &tvty_float
, &arg
, file
, lno
, expr
));
1714 /* --- @tvec_claimeq_float@ --- *
1716 * Arguments: @struct tvec_state *tv@ = test-vector state
1717 * @double f0, f1@ = two floating-point numbers
1718 * @const char *file@, @unsigned @lno@ = calling file and line
1719 * @const char *expr@ = the expression to quote on failure
1721 * Returns: Nonzero if @f0@ and @u1@ are identical, otherwise zero.
1723 * Use: Check that values of @f0@ and @f1@ are identical. The
1724 * function is exactly equivalent to @tvec_claimeqish_float@
1725 * with @f == TVFF_EXACT@.
1728 int tvec_claimeq_float(struct tvec_state
*tv
,
1729 double f0
, double f1
,
1730 const char *file
, unsigned lno
,
1733 return (tvec_claimeqish_float(tv
, f0
, f1
, TVFF_EXACT
, 0.0,
1737 /*----- Durations ---------------------------------------------------------*/
1739 /* A duration is a floating-point number of seconds. Initialization and
1740 * teardown, equality comparison, and serialization are as for floating-point
1744 static const struct duration_unit
{
1748 #define DUF_PREFER 1u
1749 } duration_units
[] = {
1761 { "yr", 31557600.0, DUF_PREFER
},
1762 { "y", 31557600.0, 0 },
1763 { "day", 86400.0, DUF_PREFER
},
1764 { "dy", 86400.0, 0 },
1765 { "d", 86400.0, 0 },
1766 { "hr", 3600.0, DUF_PREFER
},
1767 { "hour", 3600.0, 0 },
1769 { "min", 60.0, DUF_PREFER
},
1772 { "s", 1.0, DUF_PREFER
},
1777 { "ms", 1e-3, DUF_PREFER
},
1778 { "µs", 1e-6, DUF_PREFER
},
1779 { "ns", 1e-9, DUF_PREFER
},
1780 { "ps", 1e-12, DUF_PREFER
},
1781 { "fs", 1e-15, DUF_PREFER
},
1782 { "as", 1e-18, DUF_PREFER
},
1783 { "zs", 1e-21, DUF_PREFER
},
1784 { "ys", 1e-24, DUF_PREFER
},
1789 /* --- @parse_duration@ --- *
1791 * Arguments: @union tvec_regval *rv@ = register value
1792 * @const struct tvec_regdef *rd@ = register definition
1793 * @struct tvec_state *tv@ = test-vector state
1795 * Returns: Zero on success, %$-1$% on error.
1797 * Use: Parse a register value from an input file.
1799 * Duration values are finite nonnegative floating-point
1800 * numbers in @strtod@ syntax, optionally followed by a unit .
1803 static int parse_duration(union tvec_regval
*rv
,
1804 const struct tvec_regdef
*rd
,
1805 struct tvec_state
*tv
)
1807 const struct duration_unit
*u
;
1809 dstr d
= DSTR_INIT
; size_t pos
;
1813 if (tvec_readword(tv
, &d
, ";", "duration")) { rc
= -1; goto end
; }
1814 if (parse_floating(&t
, &q
, d
.buf
,
1815 rd
->arg
.p ? rd
->arg
.p
: &tvflt_nonneg
, tv
))
1816 { rc
= -1; goto end
; }
1819 tvec_skipspc(tv
); pos
= d
.len
;
1820 if (!tvec_readword(tv
, &d
, ";", 0)) q
= d
.buf
+ pos
+ 1;
1824 for (u
= duration_units
; u
->unit
; u
++)
1825 if (STRCMP(q
, ==, u
->unit
)) { t
*= u
->scale
; goto found_unit
; }
1826 rc
= tvec_syntax(tv
, *q
, "end-of-line"); goto end
;
1830 if (tvec_flushtoeol(tv
, 0)) { rc
= -1; goto end
; }
1837 /* --- @dump_duration@ --- *
1839 * Arguments: @const union tvec_regval *rv@ = register value
1840 * @const struct tvec_regdef *rd@ = register definition
1841 * @unsigned style@ = output style (@TVSF_...@)
1842 * @const struct gprintf_ops *gops@, @void *gp@ = format output
1846 * Use: Dump a register value to the format output.
1848 * Durations are dumped as a human-palatable scaled value with
1849 * unit, and, if compact style is not requested, as a raw number
1850 * of seconds at full precision as a comment.
1853 static void dump_duration(const union tvec_regval
*rv
,
1854 const struct tvec_regdef
*rd
,
1856 const struct gprintf_ops
*gops
, void *go
)
1858 const struct duration_unit
*u
;
1863 for (u
= duration_units
; u
->scale
> t
&& u
[1].unit
; u
++);
1867 gprintf(gops
, go
, "%.4g %s", t
, u ? u
->unit
: "s");
1868 if (!(style
&TVSF_COMPACT
)) {
1869 gprintf(gops
, go
, "; = ");
1870 format_floating(gops
, go
, rv
->f
);
1871 gprintf(gops
, go
, " s");
1875 /* Duration type definition. */
1876 const struct tvec_regty tvty_duration
= {
1877 init_float
, trivial_release
, eq_float
,
1878 tobuf_float
, frombuf_float
,
1879 parse_duration
, dump_duration
1882 /*----- Enumerations ------------------------------------------------------*/
1884 /* --- @init_tenum@ --- *
1886 * Arguments: @union tvec_regval *rv@ = register value
1887 * @const struct tvec_regdef *rd@ = register definition
1891 * Use: Initialize a register value.
1893 * Integer and floating-point enumeration values are initialized
1894 * as their underlying representations. Pointer enumerations
1895 * are initialized to %|#nil|%.
1898 #define init_ienum init_int
1899 #define init_uenum init_uint
1900 #define init_fenum init_float
1902 static void init_penum(union tvec_regval
*rv
, const struct tvec_regdef
*rd
)
1905 /* --- @eq_tenum@ --- *
1907 * Arguments: @const union tvec_regval *rv0, *rv1@ = register values
1908 * @const struct tvec_regdef *rd@ = register definition
1910 * Returns: Nonzero if the values are equal, zero if unequal
1912 * Use: Compare register values for equality.
1914 * Integer and floating-point enumeration values are compared as
1915 * their underlying representations; in particular, floating-
1916 * point enumerations may compare equal if their absolute or
1917 * relative difference is sufficiently small. Pointer
1918 * enumerations are compared as pointers.
1921 #define eq_ienum eq_int
1922 #define eq_uenum eq_uint
1924 static int eq_fenum(const union tvec_regval
*rv0
,
1925 const union tvec_regval
*rv1
,
1926 const struct tvec_regdef
*rd
)
1928 const struct tvec_fenuminfo
*ei
= rd
->arg
.p
;
1929 return (eqish_floating_p(rv0
->f
, rv1
->f
, ei
->fi
));
1932 static int eq_penum(const union tvec_regval
*rv0
,
1933 const union tvec_regval
*rv1
,
1934 const struct tvec_regdef
*rd
)
1935 { return (rv0
->p
== rv1
->p
); }
1937 /* --- @tobuf_tenum@ --- *
1939 * Arguments: @buf *b@ = buffer
1940 * @const union tvec_regval *rv@ = register value
1941 * @const struct tvec_regdef *rd@ = register definition
1943 * Returns: Zero on success, %$-1$% on failure.
1945 * Use: Serialize a register value to a buffer.
1947 * Integer and floating-point enumeration values are serialized
1948 * as their underlying representations. Pointer enumerations
1949 * are serialized as the signed integer index into the
1950 * association table; %|#nil|% serializes as %$-1$%, and
1951 * unrecognized pointers cause failure.
1954 #define tobuf_ienum tobuf_int
1955 #define tobuf_uenum tobuf_uint
1956 #define tobuf_fenum tobuf_float
1958 static int tobuf_penum(buf
*b
, const union tvec_regval
*rv
,
1959 const struct tvec_regdef
*rd
)
1961 const struct tvec_penuminfo
*pei
= rd
->arg
.p
;
1962 const struct tvec_passoc
*pa
;
1965 for (pa
= pei
->av
, i
= 0; pa
->tag
; pa
++, i
++)
1966 if (pa
->p
== rv
->p
) goto found
;
1970 return (signed_to_buf(b
, i
));
1973 /* --- @frombuf_tenum@ --- *
1975 * Arguments: @buf *b@ = buffer
1976 * @union tvec_regval *rv@ = register value
1977 * @const struct tvec_regdef *rd@ = register definition
1979 * Returns: Zero on success, %$-1$% on failure.
1981 * Use: Deserialize a register value from a buffer.
1983 * Integer and floating-point enumeration values are serialized
1984 * as their underlying representations. Pointer enumerations
1985 * are serialized as the signed integer index into the
1986 * association table; %|#nil|% serializes as %$-1$%; out-of-
1987 * range indices cause failure.
1990 #define frombuf_ienum frombuf_int
1991 #define frombuf_uenum frombuf_uint
1992 #define frombuf_fenum frombuf_float
1993 static int frombuf_penum(buf
*b
, union tvec_regval
*rv
,
1994 const struct tvec_regdef
*rd
)
1996 const struct tvec_penuminfo
*pei
= rd
->arg
.p
;
1997 const struct tvec_passoc
*pa
;
2000 for (pa
= pei
->av
, n
= 0; pa
->tag
; pa
++, n
++);
2001 if (signed_from_buf(b
, &i
)) return (-1);
2002 if (0 <= i
&& i
< n
) rv
->p
= (/*unconst*/ void *)pei
->av
[i
].p
;
2003 else if (i
== -1) rv
->p
= 0;
2008 /* --- @parse_tenum@ --- *
2010 * Arguments: @union tvec_regval *rv@ = register value
2011 * @const struct tvec_regdef *rd@ = register definition
2012 * @struct tvec_state *tv@ = test-vector state
2014 * Returns: Zero on success, %$-1$% on error.
2016 * Use: Parse a register value from an input file.
2018 * An enumerated value may be given by name or as a literal
2019 * value. For enumerations based on numeric types, the literal
2020 * values can be written in the same syntax as the underlying
2021 * values. For enumerations based on pointers, the only
2022 * permitted literal is %|#nil|%, which denotes a null pointer.
2025 #define DEFPARSE_ENUM(tag_, ty, slot) \
2026 static int parse_##slot##enum(union tvec_regval *rv, \
2027 const struct tvec_regdef *rd, \
2028 struct tvec_state *tv) \
2030 const struct tvec_##slot##enuminfo *ei = rd->arg.p; \
2031 const struct tvec_##slot##assoc *a; \
2032 dstr d = DSTR_INIT; \
2035 if (tvec_readword(tv, &d, ";", "enumeration tag or " LITSTR_##tag_)) \
2036 { rc = -1; goto end; } \
2037 for (a = ei->av; a->tag; a++) \
2038 if (STRCMP(a->tag, ==, d.buf)) { FOUND_##tag_ goto done; } \
2041 if (tvec_flushtoeol(tv, 0)) { rc = -1; goto end; } \
2048 #define LITSTR_INT "literal signed integer"
2049 #define FOUND_INT rv->i = a->i;
2050 #define MISSING_INT if (parse_signed(&rv->i, d.buf, ei->ir, tv)) \
2051 { rc = -1; goto end; }
2053 #define LITSTR_UINT "literal unsigned integer"
2054 #define FOUND_UINT rv->u = a->u;
2055 #define MISSING_UINT if (parse_unsigned(&rv->u, d.buf, ei->ur, tv)) \
2056 { rc = -1; goto end; }
2058 #define LITSTR_FLT "literal floating-point number, " \
2059 "`#-inf', `#+inf', or `#nan'"
2060 #define FOUND_FLT rv->f = a->f;
2061 #define MISSING_FLT if (parse_floating(&rv->f, 0, d.buf, ei->fi, tv)) \
2062 { rc = -1; goto end; }
2064 #define LITSTR_PTR "`#nil'"
2065 #define FOUND_PTR rv->p = (/*unconst*/ void *)a->p;
2066 #define MISSING_PTR if (STRCMP(d.buf, ==, "#nil")) \
2069 tvec_error(tv, "unknown `%s' value `%s'", \
2071 rc = -1; goto end; \
2074 TVEC_MISCSLOTS(DEFPARSE_ENUM
)
2092 #undef DEFPARSE_ENUM
2094 /* --- @dump_tenum@ --- *
2096 * Arguments: @const union tvec_regval *rv@ = register value
2097 * @const struct tvec_regdef *rd@ = register definition
2098 * @unsigned style@ = output style (@TVSF_...@)
2099 * @const struct gprintf_ops *gops@, @void *gp@ = format output
2103 * Use: Dump a register value to the format output.
2105 * Enumeration values are dumped as their symbolic names, if
2106 * possible, with the underlying values provided as a comment
2107 * unless compact output is requested, as for the underlying
2108 * representation. A null pointer is printed as %|#nil|%;
2109 * non-null pointers are printed as %|#<TYPE PTR>|%, with the
2110 * enumeration TYPE and the raw pointer PTR printed with the
2111 * system's %|%p|% format specifier.
2115 #define DEFDUMP_ENUM(tag_, ty, slot) \
2116 static void dump_##slot##enum(const union tvec_regval *rv, \
2117 const struct tvec_regdef *rd, \
2119 const struct gprintf_ops *gops, void *go) \
2121 const struct tvec_##slot##enuminfo *ei = rd->arg.p; \
2122 const struct tvec_##slot##assoc *a; \
2124 for (a = ei->av; a->tag; a++) \
2125 if (rv->slot == a->slot) { \
2126 gprintf(gops, go, "%s", a->tag); \
2127 if (style&TVSF_COMPACT) return; \
2128 gprintf(gops, go, " ; = "); break; \
2134 #define MAYBE_PRINT_EXTRA \
2135 if (style&TVSF_COMPACT) /* nothing to do */; \
2136 else if (!a->tag) { gprintf(gops, go, " ; = "); goto _extra; } \
2137 else if (1) { gprintf(gops, go, " = "); goto _extra; } \
2140 #define PRINTRAW_INT gprintf(gops, go, "%ld", rv->i); \
2141 MAYBE_PRINT_EXTRA { \
2142 format_signed_hex(gops, go, rv->i); \
2143 maybe_format_signed_char(gops, go, rv->i); \
2146 #define PRINTRAW_UINT gprintf(gops, go, "%lu", rv->u); \
2147 MAYBE_PRINT_EXTRA { \
2148 format_unsigned_hex(gops, go, rv->u); \
2149 maybe_format_unsigned_char(gops, go, rv->u); \
2152 #define PRINTRAW_FLT format_floating(gops, go, rv->f);
2154 #define PRINTRAW_PTR if (!rv->p) gprintf(gops, go, "#nil"); \
2155 else gprintf(gops, go, "#<%s %p>", ei->name, rv->p);
2157 TVEC_MISCSLOTS(DEFDUMP_ENUM
)
2160 #undef PRINTRAW_UINT
2164 #undef MAYBE_PRINT_EXTRA
2167 /* Enumeration type definitions. */
2168 #define DEFTY_ENUM(tag, ty, slot) \
2169 const struct tvec_regty tvty_##slot##enum = { \
2170 init_##slot##enum, trivial_release, eq_##slot##enum, \
2171 tobuf_##slot##enum, frombuf_##slot##enum, \
2172 parse_##slot##enum, dump_##slot##enum \
2174 TVEC_MISCSLOTS(DEFTY_ENUM
)
2177 /* Predefined enumeration types. */
2178 static const struct tvec_iassoc bool_assoc
[] = {
2195 const struct tvec_ienuminfo tvenum_bool
=
2196 { "bool", bool_assoc
, &tvrange_int
};
2198 static const struct tvec_iassoc cmp_assoc
[] = {
2214 const struct tvec_ienuminfo tvenum_cmp
=
2215 { "cmp", cmp_assoc
, &tvrange_int
};
2217 /* --- @tvec_claimeq_tenum@ --- *
2219 * Arguments: @struct tvec_state *tv@ = test-vector state
2220 * @const struct tvec_typeenuminfo *ei@ = enumeration type info
2221 * @ty t0, t1@ = two values
2222 * @const char *file@, @unsigned @lno@ = calling file and line
2223 * @const char *expr@ = the expression to quote on failure
2225 * Returns: Nonzero if @t0@ and @t1@ are equal, otherwise zero.
2227 * Use: Check that values of @t0@ and @t1@ are equal. As for
2228 * @tvec_claim@ above, a test case is automatically begun and
2229 * ended if none is already underway. If the values are
2230 * unequal, then @tvec_fail@ is called, quoting @expr@, and the
2231 * mismatched values are dumped: @t0@ is printed as the output
2232 * value and @t1@ is printed as the input reference.
2235 #define DEFCLAIM(tag, ty, slot) \
2236 int tvec_claimeq_##slot##enum \
2237 (struct tvec_state *tv, \
2238 const struct tvec_##slot##enuminfo *ei, ty e0, ty e1, \
2239 const char *file, unsigned lno, const char *expr) \
2241 union tvec_misc arg; \
2244 tv->out[0].v.slot = GET_##tag(e0); \
2245 tv->in[0].v.slot = GET_##tag(e1); \
2246 return (tvec_claimeq(tv, &tvty_##slot##enum, &arg, \
2247 file, lno, expr)); \
2249 #define GET_INT(e) (e)
2250 #define GET_UINT(e) (e)
2251 #define GET_FLT(e) (e)
2252 #define GET_PTR(e) ((/*unconst*/ void *)(e))
2253 TVEC_MISCSLOTS(DEFCLAIM
)
2260 /*----- Flag types --------------------------------------------------------*/
2262 /* Flag types are initialized, compared, and serialized as unsigned
2266 /* --- @parse_flags@ --- *
2268 * Arguments: @union tvec_regval *rv@ = register value
2269 * @const struct tvec_regdef *rd@ = register definition
2270 * @struct tvec_state *tv@ = test-vector state
2272 * Returns: Zero on success, %$-1$% on error.
2274 * Use: Parse a register value from an input file.
2276 * The input syntax is a sequence of items separated by `|'
2277 * signs. Each item may be the symbolic name of a field value,
2278 * or a literal unsigned integer. The masks associated with the
2279 * given symbolic names must be disjoint. The resulting
2280 * numerical value is simply the bitwise OR of the given values.
2283 static int parse_flags(union tvec_regval
*rv
, const struct tvec_regdef
*rd
,
2284 struct tvec_state
*tv
)
2286 const struct tvec_flaginfo
*fi
= rd
->arg
.p
;
2287 const struct tvec_flag
*f
;
2288 unsigned long m
= 0, v
= 0, t
;
2294 /* Read the next item. */
2296 if (tvec_readword(tv
, &d
, "|;", "flag name or integer"))
2297 { rc
= -1; goto end
; }
2299 /* Try to find a matching entry in the table. */
2300 for (f
= fi
->fv
; f
->tag
; f
++)
2301 if (STRCMP(f
->tag
, ==, d
.buf
)) {
2303 { tvec_error(tv
, "colliding flag setting"); rc
= -1; goto end
; }
2305 { m
|= f
->m
; v
|= f
->v
; goto next
; }
2308 /* Otherwise, try to parse it as a raw integer. */
2309 if (parse_unsigned(&t
, d
.buf
, fi
->range
, tv
))
2310 { rc
= -1; goto end
; }
2314 /* Advance to the next token. If it's a separator then consume it, and
2315 * go round again. Otherwise we stop here.
2317 if (tvec_nexttoken(tv
)) break;
2319 if (ch
!= '|') { tvec_syntax(tv
, ch
, "`|'"); rc
= -1; goto end
; }
2320 if (tvec_nexttoken(tv
))
2321 { tvec_syntax(tv
, '\n', "flag name or integer"); rc
= -1; goto end
; }
2331 /* --- @dump_flags@ --- *
2333 * Arguments: @const union tvec_regval *rv@ = register value
2334 * @const struct tvec_regdef *rd@ = register definition
2335 * @unsigned style@ = output style (@TVSF_...@)
2336 * @const struct gprintf_ops *gops@, @void *gp@ = format output
2340 * Use: Dump a register value to the format output.
2342 * The table of symbolic names and their associated values and
2343 * masks is repeatedly scanned, in order, to find disjoint
2344 * matches -- i.e., entries whose value matches the target value
2345 * in the bit positions indicated by the mask, and whose mask
2346 * doesn't overlap with any previously found matches; the names
2347 * are then output, separated by `|'. Any remaining nonzero
2348 * bits not covered by any of the matching masks are output as a
2349 * single literal integer, in hex.
2351 * Unless compact output is requested, or no symbolic names were
2352 * found, the raw numeric value is also printed in hex, as a
2356 static void dump_flags(const union tvec_regval
*rv
,
2357 const struct tvec_regdef
*rd
,
2359 const struct gprintf_ops
*gops
, void *go
)
2361 const struct tvec_flaginfo
*fi
= rd
->arg
.p
;
2362 const struct tvec_flag
*f
;
2363 unsigned long m
= ~0ul, v
= rv
->u
;
2366 for (f
= fi
->fv
, sep
= ""; f
->tag
; f
++)
2367 if ((m
&f
->m
) && (v
&f
->m
) == f
->v
) {
2368 gprintf(gops
, go
, "%s%s", sep
, f
->tag
); m
&= ~f
->m
;
2369 sep
= style
&TVSF_COMPACT ?
"|" : " | ";
2372 if (v
&m
) gprintf(gops
, go
, "%s0x%0*lx", sep
, hex_width(v
), v
&m
);
2374 if (m
!= ~0ul && !(style
&TVSF_COMPACT
))
2375 gprintf(gops
, go
, " ; = 0x%0*lx", hex_width(rv
->u
), rv
->u
);
2378 /* Flags type definition. */
2379 const struct tvec_regty tvty_flags
= {
2380 init_uint
, trivial_release
, eq_uint
,
2381 tobuf_uint
, frombuf_uint
,
2382 parse_flags
, dump_flags
2385 /* --- @tvec_claimeq_flags@ --- *
2387 * Arguments: @struct tvec_state *tv@ = test-vector state
2388 * @const struct tvec_flaginfo *fi@ = flags type info
2389 * @unsigned long f0, f1@ = two values
2390 * @const char *file@, @unsigned @lno@ = calling file and line
2391 * @const char *expr@ = the expression to quote on failure
2393 * Returns: Nonzero if @f0@ and @f1@ are equal, otherwise zero.
2395 * Use: Check that values of @f0@ and @f1@ are equal. As for
2396 * @tvec_claim@ above, a test case is automatically begun and
2397 * ended if none is already underway. If the values are
2398 * unequal, then @tvec_fail@ is called, quoting @expr@, and the
2399 * mismatched values are dumped: @f0@ is printed as the output
2400 * value and @f1@ is printed as the input reference.
2403 int tvec_claimeq_flags(struct tvec_state
*tv
,
2404 const struct tvec_flaginfo
*fi
,
2405 unsigned long f0
, unsigned long f1
,
2406 const char *file
, unsigned lno
, const char *expr
)
2408 union tvec_misc arg
;
2410 arg
.p
= fi
; tv
->out
[0].v
.u
= f0
; tv
->in
[0].v
.u
= f1
;
2411 return (tvec_claimeq(tv
, &tvty_flags
, &arg
, file
, lno
, expr
));
2414 /*----- Characters --------------------------------------------------------*/
2416 /* Character values are initialized and compared as signed integers. */
2418 /* --- @tobuf_char@ --- *
2420 * Arguments: @buf *b@ = buffer
2421 * @const union tvec_regval *rv@ = register value
2422 * @const struct tvec_regdef *rd@ = register definition
2424 * Returns: Zero on success, %$-1$% on failure.
2426 * Use: Serialize a register value to a buffer.
2428 * Character values are serialized as little-endian 32-bit
2429 * unsigned integers, with %|EOF|% serialized as all-bits-set.
2432 static int tobuf_char(buf
*b
, const union tvec_regval
*rv
,
2433 const struct tvec_regdef
*rd
)
2437 if (0 <= rv
->i
&& rv
->i
<= UCHAR_MAX
) u
= rv
->i
;
2438 else if (rv
->i
== EOF
) u
= MASK32
;
2440 return (buf_putu32l(b
, u
));
2443 /* --- @frombuf_char@ --- *
2445 * Arguments: @buf *b@ = buffer
2446 * @union tvec_regval *rv@ = register value
2447 * @const struct tvec_regdef *rd@ = register definition
2449 * Returns: Zero on success, %$-1$% on failure.
2451 * Use: Deserialize a register value from a buffer.
2453 * Character values are serialized as little-endian 32-bit
2454 * unsigned integers, with %|EOF|% serialized as all-bits-set.
2457 static int frombuf_char(buf
*b
, union tvec_regval
*rv
,
2458 const struct tvec_regdef
*rd
)
2462 if (buf_getu32l(b
, &u
)) return (-1);
2463 if (0 <= u
&& u
<= UCHAR_MAX
) rv
->i
= u
;
2464 else if (u
== MASK32
) rv
->i
= EOF
;
2469 /* --- @parse_char@ --- *
2471 * Arguments: @union tvec_regval *rv@ = register value
2472 * @const struct tvec_regdef *rd@ = register definition
2473 * @struct tvec_state *tv@ = test-vector state
2475 * Returns: Zero on success, %$-1$% on error.
2477 * Use: Parse a register value from an input file.
2479 * A character value can be given by symbolic name, with a
2480 * leading `%|#|%'; or a character or `%|\|%'-escape sequence,
2481 * optionally in single quotes.
2483 * The following escape sequences and character names are
2486 * * `%|#eof|%' is the special end-of-file marker.
2488 * * `%|#nul|%' is the NUL character, sometimes used to
2489 * terminate strings.
2491 * * `%|bell|%', `%|bel|%', `%|ding|%', or `%|\a|%' is the BEL
2492 * character used to ring the terminal bell (or do some other
2493 * thing to attract the user's attention).
2495 * * %|#backspace|%, %|#bs|%, or %|\b|% is the backspace
2496 * character, used to move the cursor backwords by one cell.
2498 * * %|#escape|% %|#esc|%, or%|\e|% is the escape character,
2499 * used to introduce special terminal commands.
2501 * * %|#formfeed|%, %|#ff|%, or %|\f|% is the formfeed
2502 * character, used to separate pages of text.
2504 * * %|#newline|%, %|#linefeed|%, %|#lf|%, %|#nl|%, or %|\n|% is
2505 * the newline character, used to terminate lines of text or
2506 * advance the cursor to the next line (perhaps without
2507 * returning it to the start of the line).
2509 * * %|#return|%, %|#carriage-return|%, %|#cr|%, or %|\r|% is
2510 * the carriage-return character, used to return the cursor to
2511 * the start of the line.
2513 * * %|#tab|%, %|#horizontal-tab|%, %|#ht|%, or %|\t|% is the
2514 * tab character, used to advance the cursor to the next tab
2515 * stop on the current line.
2517 * * %|#vertical-tab|%, %|#vt|%, %|\v|% is the vertical tab
2520 * * %|#space|%, %|#spc|% is the space character.
2522 * * %|#delete|%, %|#del|% is the delete character, used to
2523 * erase the most recent character.
2525 * * %|\'|% is the single-quote character.
2527 * * %|\\|% is the backslash character.
2529 * * %|\"|% is the double-quote character.
2531 * * %|\NNN|% or %|\{NNN}|% is the character with code NNN in
2532 * octal. The NNN may be up to three digits long.
2534 * * %|\xNN|% or %|\x{NN}|% is the character with code NNN in
2538 static int parse_char(union tvec_regval
*rv
, const struct tvec_regdef
*rd
,
2539 struct tvec_state
*tv
)
2546 /* Inspect the character to see what we're up against. */
2550 /* It looks like a special token. Push the `%|#|%' back and fetch the
2551 * whole word. If there's just the `%|#|%' after all, then treat it as
2556 if (tvec_readword(tv
, &d
, ";", "character name")) { rc
= -1; goto end
; }
2557 if (STRCMP(d
.buf
, !=, "#")) {
2558 if (read_charname(&ch
, d
.buf
, RCF_EOFOK
)) {
2559 rc
= tvec_error(tv
, "unknown character name `%s'", d
.buf
);
2562 if (tvec_flushtoeol(tv
, 0)) { rc
= -1; goto end
; }
2563 rv
->i
= ch
; rc
= 0; goto end
;
2567 /* If this is a single quote then we expect to see a matching one later,
2568 * and we should process backslash escapes. Get the next character and see
2571 if (ch
== '\'') { f
|= f_quote
; ch
= getc(tv
->fp
); }
2573 /* Main character dispatch. */
2577 /* Unquoted, semicolon begins a comment. */
2578 if (!(f
&f_quote
)) { rc
= tvec_syntax(tv
, ch
, "character"); goto end
; }
2582 /* A newline. If we saw a single quote, then treat that as literal.
2583 * Otherwise this is an error.
2585 if (!(f
&f_quote
)) goto nochar
;
2586 else { f
&= ~f_quote
; ungetc(ch
, tv
->fp
); ch
= '\''; goto plain
; }
2589 /* End-of-file. Similar to newline, but with slightly different
2590 * effects on the parse state.
2592 if (!(f
&f_quote
)) goto nochar
;
2593 else { f
&= ~f_quote
; ch
= '\''; goto plain
; }
2596 /* A single quote. This must be the second of a pair, and there should
2597 * have been a character or escape sequence between them.
2599 rc
= tvec_syntax(tv
, ch
, "character"); goto end
;
2602 /* A backslash. Read a character escape. */
2603 if (read_charesc(&ch
, tv
)) return (-1);
2606 /* Anything else. Treat as literal. */
2610 /* If we saw an opening quote, then expect the closing quote. */
2613 if (ch
!= '\'') { rc
= tvec_syntax(tv
, ch
, "`''"); goto end
; }
2617 if (tvec_flushtoeol(tv
, 0)) { rc
= -1; goto end
; }
2626 /* --- @dump_char@ --- *
2628 * Arguments: @const union tvec_regval *rv@ = register value
2629 * @const struct tvec_regdef *rd@ = register definition
2630 * @unsigned style@ = output style (@TVSF_...@)
2631 * @const struct gprintf_ops *gops@, @void *gp@ = format output
2635 * Use: Dump a register value to the format output.
2637 * Character values are dumped as their symbolic names, if any,
2638 * or as a character or escape sequence within single quotes
2639 * (which may be omitted in compact style). If compact output
2640 * is not requested, then the single-quoted representation (for
2641 * characters dumped as symbolic names) and integer code in
2642 * decimal and hex are printed as a comment.
2645 static void dump_char(const union tvec_regval
*rv
,
2646 const struct tvec_regdef
*rd
,
2648 const struct gprintf_ops
*gops
, void *go
)
2654 /* Print a character name if we can find one. */
2655 p
= find_charname(rv
->i
, (style
&TVSF_COMPACT
) ? CTF_SHORT
: CTF_PREFER
);
2657 gprintf(gops
, go
, "%s", p
);
2658 if (style
&TVSF_COMPACT
) return;
2659 else { gprintf(gops
, go
, " ;"); f
|= f_semi
; }
2662 /* If the character isn't @EOF@ then print it as a single-quoted thing.
2663 * In compact style, see if we can omit the quotes.
2666 if (f
&f_semi
) gprintf(gops
, go
, " = ");
2668 case ' ': case '\\': case '\'': quote
:
2669 format_char(gops
, go
, rv
->i
);
2672 if (!(style
&TVSF_COMPACT
) || !isprint(rv
->i
)) goto quote
;
2673 gprintf(gops
, go
, "%c", (int)rv
->i
);
2678 /* And the character code as an integer. */
2679 if (!(style
&TVSF_COMPACT
)) {
2680 if (!(f
&f_semi
)) gprintf(gops
, go
, " ;");
2681 gprintf(gops
, go
, " = %ld = ", rv
->i
);
2682 format_signed_hex(gops
, go
, rv
->i
);
2688 /* Character type definition. */
2689 const struct tvec_regty tvty_char
= {
2690 init_int
, trivial_release
, eq_int
,
2691 tobuf_char
, frombuf_char
,
2692 parse_char
, dump_char
2695 /* --- @tvec_claimeq_char@ --- *
2697 * Arguments: @struct tvec_state *tv@ = test-vector state
2698 * @int ch0, ch1@ = two character codes
2699 * @const char *file@, @unsigned @lno@ = calling file and line
2700 * @const char *expr@ = the expression to quote on failure
2702 * Returns: Nonzero if @ch0@ and @ch1@ are equal, otherwise zero.
2704 * Use: Check that values of @ch0@ and @ch1@ are equal. As for
2705 * @tvec_claim@ above, a test case is automatically begun and
2706 * ended if none is already underway. If the values are
2707 * unequal, then @tvec_fail@ is called, quoting @expr@, and the
2708 * mismatched values are dumped: @ch0@ is printed as the output
2709 * value and @ch1@ is printed as the input reference.
2712 int tvec_claimeq_char(struct tvec_state
*tv
, int c0
, int c1
,
2713 const char *file
, unsigned lno
, const char *expr
)
2715 tv
->out
[0].v
.i
= c0
; tv
->in
[0].v
.i
= c1
;
2716 return (tvec_claimeq(tv
, &tvty_char
, 0, file
, lno
, expr
));
2719 /*----- Text and byte strings ---------------------------------------------*/
2721 /* --- @init_text@, @init_bytes@ --- *
2723 * Arguments: @union tvec_regval *rv@ = register value
2724 * @const struct tvec_regdef *rd@ = register definition
2728 * Use: Initialize a register value.
2730 * Text and binary string values are initialized with a null
2731 * pointer and zero length.
2734 static void init_text(union tvec_regval
*rv
, const struct tvec_regdef
*rd
)
2735 { rv
->text
.p
= 0; rv
->text
.sz
= 0; }
2737 static void init_bytes(union tvec_regval
*rv
, const struct tvec_regdef
*rd
)
2738 { rv
->bytes
.p
= 0; rv
->bytes
.sz
= 0; }
2740 /* --- @release_string@, @release_bytes@ --- *
2742 * Arguments: @const union tvec_regval *rv@ = register value
2743 * @const struct tvec_regdef *rd@ = register definition
2747 * Use: Release resources held by a register value.
2749 * Text and binary string buffers are freed.
2752 static void release_text(union tvec_regval
*rv
,
2753 const struct tvec_regdef
*rd
)
2754 { xfree(rv
->text
.p
); }
2756 static void release_bytes(union tvec_regval
*rv
,
2757 const struct tvec_regdef
*rd
)
2758 { xfree(rv
->bytes
.p
); }
2760 /* --- @eq_text@, @eq_bytes@ --- *
2762 * Arguments: @const union tvec_regval *rv0, *rv1@ = register values
2763 * @const struct tvec_regdef *rd@ = register definition
2765 * Returns: Nonzero if the values are equal, zero if unequal
2767 * Use: Compare register values for equality.
2770 static int eq_text(const union tvec_regval
*rv0
,
2771 const union tvec_regval
*rv1
,
2772 const struct tvec_regdef
*rd
)
2774 return (rv0
->text
.sz
== rv1
->text
.sz
&&
2776 MEMCMP(rv0
->text
.p
, ==, rv1
->text
.p
, rv1
->text
.sz
)));
2779 static int eq_bytes(const union tvec_regval
*rv0
,
2780 const union tvec_regval
*rv1
,
2781 const struct tvec_regdef
*rd
)
2783 return (rv0
->bytes
.sz
== rv1
->bytes
.sz
&&
2785 MEMCMP(rv0
->bytes
.p
, ==, rv1
->bytes
.p
, rv1
->bytes
.sz
)));
2788 /* --- @tobuf_text@, @tobuf_bytes@ --- *
2790 * Arguments: @buf *b@ = buffer
2791 * @const union tvec_regval *rv@ = register value
2792 * @const struct tvec_regdef *rd@ = register definition
2794 * Returns: Zero on success, %$-1$% on failure.
2796 * Use: Serialize a register value to a buffer.
2798 * Text and binary string values are serialized as a little-
2799 * endian 64-bit length %$n$% in bytes followed by %$n$% bytes
2803 static int tobuf_text(buf
*b
, const union tvec_regval
*rv
,
2804 const struct tvec_regdef
*rd
)
2805 { return (buf_putmem64l(b
, rv
->text
.p
, rv
->text
.sz
)); }
2807 static int tobuf_bytes(buf
*b
, const union tvec_regval
*rv
,
2808 const struct tvec_regdef
*rd
)
2809 { return (buf_putmem64l(b
, rv
->bytes
.p
, rv
->bytes
.sz
)); }
2811 /* --- @frombuf_text@, @frombuf_bytes@ --- *
2813 * Arguments: @buf *b@ = buffer
2814 * @union tvec_regval *rv@ = register value
2815 * @const struct tvec_regdef *rd@ = register definition
2817 * Returns: Zero on success, %$-1$% on failure.
2819 * Use: Deserialize a register value from a buffer.
2821 * Text and binary string values are serialized as a little-
2822 * endian 64-bit length %$n$% in bytes followed by %$n$% bytes
2826 static int frombuf_text(buf
*b
, union tvec_regval
*rv
,
2827 const struct tvec_regdef
*rd
)
2832 p
= buf_getmem64l(b
, &sz
); if (!p
) return (-1);
2833 tvec_alloctext(rv
, sz
); memcpy(rv
->text
.p
, p
, sz
); rv
->text
.p
[sz
] = 0;
2837 static int frombuf_bytes(buf
*b
, union tvec_regval
*rv
,
2838 const struct tvec_regdef
*rd
)
2843 p
= buf_getmem64l(b
, &sz
); if (!p
) return (-1);
2844 tvec_allocbytes(rv
, sz
); memcpy(rv
->bytes
.p
, p
, sz
);
2848 /* --- @check_string_length@ --- *
2850 * Arguments: @size_t sz@ = found string length
2851 * @const struct tvec_urange *ur@ = acceptable range
2852 * @struct tvec_state *tv@ = test-vector state
2854 * Returns: Zero on success, %$-1$% on error.
2856 * Use: Checks that @sz@ is within the bounds described by @ur@,
2857 * reporting an error if not.
2860 static int check_string_length(size_t sz
, const struct tvec_urange
*ur
,
2861 struct tvec_state
*tv
)
2863 if (ur
&& (ur
->min
> sz
|| sz
> ur
->max
))
2864 return (tvec_error(tv
,
2865 "invalid string length %lu; must be in [%lu .. %lu]",
2866 (unsigned long)sz
, ur
->min
, ur
->max
));
2870 /* --- @parse_text@, @parse_bytes@ --- *
2872 * Arguments: @union tvec_regval *rv@ = register value
2873 * @const struct tvec_regdef *rd@ = register definition
2874 * @struct tvec_state *tv@ = test-vector state
2876 * Returns: Zero on success, %$-1$% on error.
2878 * Use: Parse a register value from an input file.
2880 * The input format for both kinds of strings is basically the
2881 * same: a `compound string', consisting of
2883 * * single-quoted strings, which are interpreted entirely
2884 * literally, but can't contain single quotes or newlines;
2886 * * double-quoted strings, in which `%|\|%'-escapes are
2887 * interpreted as for characters;
2889 * * character names, marked by an initial `%|#|%' sign;
2891 * * special tokens marked by an initial `%|!|%' sign; or
2893 * * barewords interpreted according to the current coding
2896 * The special tokens are
2898 * * `%|!bare|%', which causes subsequent sequences of
2899 * barewords to be treated as plain text;
2901 * * `%|!hex|%', `%|!base32|%', `%|!base64|%', which cause
2902 * subsequent barewords to be decoded in the requested
2905 * * `%|!repeat|% %$n$% %|{|% %%\textit{string}%% %|}|%',
2906 * which includes %$n$% copies of the (compound) string.
2908 * The only difference between text and binary strings is that
2909 * the initial coding scheme is %|bare|% for text strings and
2910 * %|hex|% for binary strings.
2913 static int parse_text(union tvec_regval
*rv
, const struct tvec_regdef
*rd
,
2914 struct tvec_state
*tv
)
2916 void *p
= rv
->text
.p
;
2918 if (read_compound_string(&p
, &rv
->text
.sz
, TVCODE_BARE
, 0, tv
))
2921 if (check_string_length(rv
->text
.sz
, rd
->arg
.p
, tv
)) return (-1);
2925 static int parse_bytes(union tvec_regval
*rv
, const struct tvec_regdef
*rd
,
2926 struct tvec_state
*tv
)
2928 void *p
= rv
->bytes
.p
;
2930 if (read_compound_string(&p
, &rv
->bytes
.sz
, TVCODE_HEX
, 0, tv
))
2933 if (check_string_length(rv
->bytes
.sz
, rd
->arg
.p
, tv
)) return (-1);
2937 /* --- @dump_text@, @dump_bytes@ --- *
2939 * Arguments: @const union tvec_regval *rv@ = register value
2940 * @const struct tvec_regdef *rd@ = register definition
2941 * @unsigned style@ = output style (@TVSF_...@)
2942 * @const struct gprintf_ops *gops@, @void *gp@ = format output
2946 * Use: Dump a register value to the format output.
2948 * Text string values are dumped as plain text, in double quotes
2949 * if necessary, and using backslash escape sequences for
2950 * nonprintable characters. Unless compact output is requested,
2951 * strings consisting of multiple lines are dumped with each
2952 * line of the string on a separate output line.
2954 * Binary string values are dumped in hexadecimal. In compact
2955 * style, the output simply consists of a single block of hex
2956 * digits. Otherwise, the dump is a display consisting of
2957 * groups of hex digits, with comments showing the offset (if
2958 * the string is long enough) and the corresponding plain text.
2960 * Empty strings are dumped as %|""|%.
2963 static void dump_text(const union tvec_regval
*rv
,
2964 const struct tvec_regdef
*rd
,
2966 const struct gprintf_ops
*gops
, void *go
)
2968 const unsigned char *p
, *q
, *l
;
2970 #define f_nonword 1u
2971 #define f_newline 2u
2973 if (!rv
->text
.sz
) { gprintf(gops
, go
, "\"\""); return; }
2975 p
= (const unsigned char *)rv
->text
.p
; l
= p
+ rv
->text
.sz
;
2977 case '!': case '#': case ';': case '"': case '\'':
2978 case '(': case '{': case '[': case ']': case '}': case ')':
2979 f
|= f_nonword
; break;
2981 for (q
= p
; q
< l
; q
++)
2982 if (*q
== '\n' && q
!= l
- 1) f
|= f_newline
;
2983 else if (!*q
|| !isgraph(*q
) || *q
== '\\') f
|= f_nonword
;
2984 if (f
&f_newline
) { gprintf(gops
, go
, "\n\t"); goto quote
; }
2985 else if (f
&f_nonword
) goto quote
;
2987 gops
->putm(go
, (const char *)p
, rv
->text
.sz
);
2991 gprintf(gops
, go
, "\"");
2992 for (q
= p
; q
< l
; q
++)
2993 if (!isprint(*q
) || *q
== '"') {
2994 if (p
< q
) gops
->putm(go
, (const char *)p
, q
- p
);
2995 if (*q
!= '\n' || (style
&TVSF_COMPACT
))
2996 format_charesc(gops
, go
, *q
, FCF_BRACE
);
2998 if (q
+ 1 == l
) { gprintf(gops
, go
, "\\n\""); return; }
2999 else gprintf(gops
, go
, "\\n\"\n\t\"");
3003 if (p
< q
) gops
->putm(go
, (const char *)p
, q
- p
);
3004 gprintf(gops
, go
, "\"");
3010 static void dump_bytes(const union tvec_regval
*rv
,
3011 const struct tvec_regdef
*rd
,
3013 const struct gprintf_ops
*gops
, void *go
)
3015 const unsigned char *p
= rv
->bytes
.p
, *l
= p
+ rv
->bytes
.sz
;
3016 size_t off
, sz
= rv
->bytes
.sz
;
3021 gprintf(gops
, go
, style
&TVSF_COMPACT ?
"\"\"" : "\"\" ; empty");
3025 if (style
&TVSF_COMPACT
) {
3026 while (p
< l
) gprintf(gops
, go
, "%02x", *p
++);
3030 if (sz
> 16) gprintf(gops
, go
, "\n\t");
3032 off
= 0; wd
= hex_width(sz
);
3034 if (l
- p
< 16) n
= l
- p
;
3037 for (i
= 0; i
< n
; i
++) {
3038 if (i
< n
) gprintf(gops
, go
, "%02x", p
[i
]);
3039 else gprintf(gops
, go
, " ");
3040 if (i
< n
- 1 && i
%4 == 3) gprintf(gops
, go
, " ");
3042 gprintf(gops
, go
, " ; ");
3043 if (sz
> 16) gprintf(gops
, go
, "[%0*lx] ", wd
, (unsigned long)off
);
3044 for (i
= 0; i
< n
; i
++)
3045 gprintf(gops
, go
, "%c", isprint(p
[i
]) ? p
[i
] : '.');
3047 if (p
< l
) gprintf(gops
, go
, "\n\t");
3051 /* Text and byte string type definitions. */
3052 const struct tvec_regty tvty_text
= {
3053 init_text
, release_text
, eq_text
,
3054 tobuf_text
, frombuf_text
,
3055 parse_text
, dump_text
3057 const struct tvec_regty tvty_bytes
= {
3058 init_bytes
, release_bytes
, eq_bytes
,
3059 tobuf_bytes
, frombuf_bytes
,
3060 parse_bytes
, dump_bytes
3063 /* --- @tvec_claimeq_text@ --- *
3065 * Arguments: @struct tvec_state *tv@ = test-vector state
3066 * @const char *p0@, @size_t sz0@ = first string with length
3067 * @const char *p1@, @size_t sz1@ = second string with length
3068 * @const char *file@, @unsigned @lno@ = calling file and line
3069 * @const char *expr@ = the expression to quote on failure
3071 * Returns: Nonzero if the strings at @p0@ and @p1@ are equal, otherwise
3074 * Use: Check that strings at @p0@ and @p1@ are equal. As for
3075 * @tvec_claim@ above, a test case is automatically begun and
3076 * ended if none is already underway. If the values are
3077 * unequal, then @tvec_fail@ is called, quoting @expr@, and the
3078 * mismatched values are dumped: @p0@ is printed as the output
3079 * value and @p1@ is printed as the input reference.
3082 int tvec_claimeq_text(struct tvec_state
*tv
,
3083 const char *p0
, size_t sz0
,
3084 const char *p1
, size_t sz1
,
3085 const char *file
, unsigned lno
, const char *expr
)
3087 tv
->out
[0].v
.text
.p
= (/*unconst*/ char *)p0
; tv
->out
[0].v
.text
.sz
= sz0
;
3088 tv
->in
[0].v
.text
.p
=(/*unconst*/ char *) p1
; tv
->in
[0].v
.text
.sz
= sz1
;
3089 return (tvec_claimeq(tv
, &tvty_text
, 0, file
, lno
, expr
));
3092 /* --- @tvec_claimeq_textz@ --- *
3094 * Arguments: @struct tvec_state *tv@ = test-vector state
3095 * @const char *p0, *p1@ = two strings to compare
3096 * @const char *file@, @unsigned @lno@ = calling file and line
3097 * @const char *expr@ = the expression to quote on failure
3099 * Returns: Nonzero if the strings at @p0@ and @p1@ are equal, otherwise
3102 * Use: Check that strings at @p0@ and @p1@ are equal, as for
3103 * @tvec_claimeq_string@, except that the strings are assumed
3104 * null-terminated, so their lengths don't need to be supplied
3108 int tvec_claimeq_textz(struct tvec_state
*tv
,
3109 const char *p0
, const char *p1
,
3110 const char *file
, unsigned lno
, const char *expr
)
3112 tv
->out
[0].v
.text
.p
= (/*unconst*/ char *)p0
;
3113 tv
->out
[0].v
.text
.sz
= strlen(p0
);
3114 tv
->in
[0].v
.text
.p
= (/*unconst*/ char *)p1
;
3115 tv
->in
[0].v
.text
.sz
= strlen(p1
);
3116 return (tvec_claimeq(tv
, &tvty_text
, 0, file
, lno
, expr
));
3119 /* --- @tvec_claimeq_bytes@ --- *
3121 * Arguments: @struct tvec_state *tv@ = test-vector state
3122 * @const void *p0@, @size_t sz0@ = first string with length
3123 * @const void *p1@, @size_t sz1@ = second string with length
3124 * @const char *file@, @unsigned @lno@ = calling file and line
3125 * @const char *expr@ = the expression to quote on failure
3127 * Returns: Nonzero if the strings at @p0@ and @p1@ are equal, otherwise
3130 * Use: Check that binary strings at @p0@ and @p1@ are equal. As for
3131 * @tvec_claim@ above, a test case is automatically begun and
3132 * ended if none is already underway. If the values are
3133 * unequal, then @tvec_fail@ is called, quoting @expr@, and the
3134 * mismatched values are dumped: @p0@ is printed as the output
3135 * value and @p1@ is printed as the input reference.
3138 int tvec_claimeq_bytes(struct tvec_state
*tv
,
3139 const void *p0
, size_t sz0
,
3140 const void *p1
, size_t sz1
,
3141 const char *file
, unsigned lno
, const char *expr
)
3143 tv
->out
[0].v
.bytes
.p
= (/*unconst*/ void *)p0
;
3144 tv
->out
[0].v
.bytes
.sz
= sz0
;
3145 tv
->in
[0].v
.bytes
.p
= (/*unconst*/ void *)p1
;
3146 tv
->in
[0].v
.bytes
.sz
= sz1
;
3147 return (tvec_claimeq(tv
, &tvty_bytes
, 0, file
, lno
, expr
));
3150 /* --- @tvec_alloctext@, @tvec_allocbytes@ --- *
3152 * Arguments: @union tvec_regval *rv@ = register value
3153 * @size_t sz@ = required size
3157 * Use: Allocated space in a text or binary string register. If the
3158 * current register size is sufficient, its buffer is left
3159 * alone; otherwise, the old buffer, if any, is freed and a
3160 * fresh buffer allocated. These functions are not intended to
3161 * be used to adjust a buffer repeatedly, e.g., while building
3162 * output incrementally: (a) they will perform badly, and (b)
3163 * the old buffer contents are simply discarded if reallocation
3164 * is necessary. Instead, use a @dbuf@ or @dstr@.
3166 * The @tvec_alloctext@ function sneakily allocates an extra
3167 * byte for a terminating zero. The @tvec_allocbytes@ function
3171 void tvec_alloctext(union tvec_regval
*rv
, size_t sz
)
3173 if (rv
->text
.sz
<= sz
) { xfree(rv
->text
.p
); rv
->text
.p
= xmalloc(sz
+ 1); }
3177 void tvec_allocbytes(union tvec_regval
*rv
, size_t sz
)
3179 if (rv
->bytes
.sz
< sz
) { xfree(rv
->bytes
.p
); rv
->bytes
.p
= xmalloc(sz
); }
3183 /*----- Buffer type -------------------------------------------------------*/
3185 /* Buffers are initialized and released as binary strings. */
3187 /* --- @eq_buffer@ --- *
3189 * Arguments: @const union tvec_regval *rv0, *rv1@ = register values
3190 * @const struct tvec_regdef *rd@ = register definition
3192 * Returns: Nonzero if the values are equal, zero if unequal
3194 * Use: Compare register values for equality.
3196 * Buffer values are equal if and only if their sizes are equal;
3197 * their contents are %%\emph{not}%% compared.
3200 static int eq_buffer(const union tvec_regval
*rv0
,
3201 const union tvec_regval
*rv1
,
3202 const struct tvec_regdef
*rd
)
3203 { return (rv0
->bytes
.sz
== rv1
->bytes
.sz
); }
3205 /* --- @tobuf_buffer@ --- *
3207 * Arguments: @buf *b@ = buffer
3208 * @const union tvec_regval *rv@ = register value
3209 * @const struct tvec_regdef *rd@ = register definition
3211 * Returns: Zero on success, %$-1$% on failure.
3213 * Use: Serialize a register value to a buffer.
3215 * Buffer values are serialized as just their lengths, as
3216 * unsigned integers.
3219 static int tobuf_buffer(buf
*b
, const union tvec_regval
*rv
,
3220 const struct tvec_regdef
*rd
)
3221 { return (unsigned_to_buf(b
, rv
->bytes
.sz
)); }
3223 /* --- @allocate_buffer@ --- *
3225 * Arguments: @union tvec_regval *rv@ = register value
3226 * @size_t sz@ = size to allocate
3230 * Use: Allocate @sz@ bytes to the buffer and fill the space with a
3231 * distinctive pattern.
3234 static void allocate_buffer(union tvec_regval
*rv
, size_t sz
)
3235 { tvec_allocbytes(rv
, sz
); memset(rv
->bytes
.p
, '?', sz
); }
3237 /* --- @frombuf_buffer@ --- *
3239 * Arguments: @buf *b@ = buffer
3240 * @union tvec_regval *rv@ = register value
3241 * @const struct tvec_regdef *rd@ = register definition
3243 * Returns: Zero on success, %$-1$% on failure.
3245 * Use: Deserialize a register value from a buffer.
3247 * Buffer values are serialized as just their lengths, as
3248 * unsigned integers. The buffer is allocated on
3249 * deserialization and filled with a distinctive pattern.
3252 static int frombuf_buffer(buf
*b
, union tvec_regval
*rv
,
3253 const struct tvec_regdef
*rd
)
3257 if (unsigned_from_buf(b
, &u
)) return (-1);
3258 if (u
> (size_t)-1) return (-1);
3259 allocate_buffer(rv
, u
);
3263 /* --- @parse_buffer@ --- *
3265 * Arguments: @union tvec_regval *rv@ = register value
3266 * @const struct tvec_regdef *rd@ = register definition
3267 * @struct tvec_state *tv@ = test-vector state
3269 * Returns: Zero on success, %$-1$% on error.
3271 * Use: Parse a register value from an input file.
3273 * The input format for a buffer value consists of an unsigned
3274 * integer followed by an optional unit specifier consisting of
3275 * an SI unit prefix and (optionally) the letter `B'. Unit
3276 * prefixes denote %%\emph{binary}%% multipliers, not decimal.
3278 * The buffer is allocated and filled with a distinctive
3282 static const char units
[] = "kMGTPEZY";
3284 static int parse_buffer(union tvec_regval
*rv
,
3285 const struct tvec_regdef
*rd
,
3286 struct tvec_state
*tv
)
3289 const char *q
, *unit
;
3296 if (tvec_readword(tv
, &d
, ";", "buffer length")) { rc
= -1; goto end
; }
3297 if (parse_unsigned_integer(&u
, &q
, d
.buf
)) goto bad
;
3299 tvec_skipspc(tv
); pos
= d
.len
;
3300 if (!tvec_readword(tv
, &d
, ";", 0)) pos
++;
3304 if (u
> (size_t)-1) goto rangerr
;
3305 for (t
= u
, unit
= units
; *unit
; unit
++) {
3306 if (t
> (size_t)-1/1024) f
|= f_range
;
3309 if (f
&f_range
) goto rangerr
;
3315 if (check_string_length(u
, rd
->arg
.p
, tv
)) { rc
= -1; goto end
; }
3317 if (tvec_flushtoeol(tv
, 0)) { rc
= -1; goto end
; }
3318 allocate_buffer(rv
, u
);
3321 DDESTROY(&d
); return (rc
);
3324 tvec_error(tv
, "invalid buffer length `%s'", d
.buf
);
3328 tvec_error(tv
, "buffer length `%s' out of range", d
.buf
);
3334 /* --- @dump_buffer@ --- *
3336 * Arguments: @const union tvec_regval *rv@ = register value
3337 * @const struct tvec_regdef *rd@ = register definition
3338 * @unsigned style@ = output style (@TVSF_...@)
3339 * @const struct gprintf_ops *gops@, @void *gp@ = format output
3343 * Use: Dump a register value to the format output.
3345 * Buffer values are dumped as their size with an appropriate
3346 * unit specifier. A unit prefix is only used if the size is an
3347 * exact multiple of the relevant power of two.
3350 static void dump_buffer(const union tvec_regval
*rv
,
3351 const struct tvec_regdef
*rd
,
3353 const struct gprintf_ops
*gops
, void *go
)
3356 unsigned long u
= rv
->bytes
.sz
;
3359 gprintf(gops
, go
, "%lu B", u
);
3361 for (unit
= units
, u
/= 1024; !(u
%1024) && unit
[1]; u
/= 1024, unit
++);
3362 gprintf(gops
, go
, "%lu %cB", u
, *unit
);
3366 /* Buffer type definition. */
3367 const struct tvec_regty tvty_buffer
= {
3368 init_bytes
, release_bytes
, eq_buffer
,
3369 tobuf_buffer
, frombuf_buffer
,
3370 parse_buffer
, dump_buffer
3373 /*----- That's all, folks -------------------------------------------------*/