3 * Types for the test-vector framework
5 * (c) 2023 Straylight/Edgeware
8 /*----- Licensing notice --------------------------------------------------*
10 * This file is part of the mLib utilities library.
12 * mLib is free software: you can redistribute it and/or modify it under
13 * the terms of the GNU Library General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or (at
15 * your option) any later version.
17 * mLib is distributed in the hope that it will be useful, but WITHOUT
18 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
20 * License for more details.
22 * You should have received a copy of the GNU Library General Public
23 * License along with mLib. If not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
28 /*----- Header files ------------------------------------------------------*/
48 /*----- Preliminary utilities ---------------------------------------------*/
50 /* --- @trivial_release@ --- *
52 * Arguments: @union tvec_regval *rv@ = a register value
53 * @const struct tvec_regdef@ = the register definition
57 * Use: Does nothing. Used for register values which don't retain
61 static void trivial_release(union tvec_regval
*rv
,
62 const struct tvec_regdef
*rd
)
65 /*----- Integer utilities -------------------------------------------------*/
67 /* --- @unsigned_to_buf@, @signed_to_buf@ --- *
69 * Arguments: @buf *b@ = buffer to write on
70 * @unsigned long u@ or @long i@ = integer to write
72 * Returns: Zero on success, @-1@ on failure.
74 * Use: Write @i@ to the buffer, in big-endian (two's-complement, it
78 static int unsigned_to_buf(buf
*b
, unsigned long u
)
79 { kludge64 k
; ASSIGN64(k
, u
); return (buf_putk64l(b
, k
)); }
81 static int signed_to_buf(buf
*b
, long i
)
87 if (i
>= 0) ASSIGN64(k
, u
);
88 else { ASSIGN64(k
, ~u
); CPL64(k
, k
); }
89 return (buf_putk64l(b
, k
));
92 /* --- @unsigned_from_buf@, @signed_from_buf@ --- *
94 * Arguments: @buf *b@ = buffer to write on
95 * @unsigned long *u_out@ or @long *i_out@ = where to put the
98 * Returns: Zero on success, @-1@ on failure.
100 * Use: Read an integer, in big-endian (two's-complement, if signed)
101 * format, from the buffer.
104 static int unsigned_from_buf(buf
*b
, unsigned long *u_out
)
108 ASSIGN64(ulmax
, ULONG_MAX
);
109 if (buf_getk64l(b
, &k
)) return (-1);
110 if (CMP64(k
, >, ulmax
)) { buf_break(b
); return (-1); }
111 *u_out
= GET64(unsigned long, k
); return (0);
114 /* --- @hex_width@ --- *
116 * Arguments: @unsigned long u@ = an integer
118 * Returns: A suitable number of digits to use in order to display @u@ in
119 * hex. Currently, we select a power of two sufficient to show
120 * the value, but at least 2.
123 static int hex_width(unsigned long u
)
128 for (t
= u
>> 4, wd
= 4; t
>>= wd
, wd
*= 2, t
; );
132 /* --- @format_unsigned_hex@, @format_signed_hex@ --- *
134 * Arguments: @const struct gprintf_ops *gops@ = print operations
135 * @void *go@ = print destination
136 * @unsigned long u@ or @long i@ = integer to print
140 * Use: Print an unsigned or signed integer in hexadecimal.
143 static void format_unsigned_hex(const struct gprintf_ops
*gops
, void *go
,
145 { gprintf(gops
, go
, "0x%0*lx", hex_width(u
), u
); }
147 static void format_signed_hex(const struct gprintf_ops
*gops
, void *go
,
150 unsigned long u
= i
>= 0 ? i
: -(unsigned long)i
;
151 gprintf(gops
, go
, "%s0x%0*lx", i
< 0 ?
"-" : "", hex_width(u
), u
);
154 static int signed_from_buf(buf
*b
, long *i_out
)
156 kludge64 k
, lmax
, not_lmin
;
158 ASSIGN64(lmax
, LONG_MAX
); ASSIGN64(not_lmin
, ~(unsigned long)LONG_MIN
);
159 if (buf_getk64l(b
, &k
)) return (-1);
160 if (CMP64(k
, <=, lmax
)) *i_out
= (long)GET64(unsigned long, k
);
163 if (CMP64(k
, <=, not_lmin
)) *i_out
= -(long)GET64(unsigned long, k
) - 1;
164 else { buf_break(b
); return (-1); }
169 /* --- @check_unsigned_range@, @check_signed_range@ --- *
171 * Arguments: @unsigned long u@ or @long i@ = an integer
172 * @const struct tvec_urange *ur@ or
173 * @const struct tvec_irange *ir@ = range specification,
175 * @struct tvec_state *tv@ = test vector state
176 * @const char *what@ = description of value
178 * Returns: Zero on success, or @-1@ on error.
180 * Use: Check that the integer is within bounds. If not, report a
181 * suitable error and return a failure indication.
184 static int check_signed_range(long i
,
185 const struct tvec_irange
*ir
,
186 struct tvec_state
*tv
, const char *what
)
188 if (ir
&& (ir
->min
> i
|| i
> ir
->max
)) {
189 tvec_error(tv
, "%s %ld out of range (must be in [%ld .. %ld])",
190 what
, i
, ir
->min
, ir
->max
);
196 static int check_unsigned_range(unsigned long u
,
197 const struct tvec_urange
*ur
,
198 struct tvec_state
*tv
, const char *what
)
200 if (ur
&& (ur
->min
> u
|| u
> ur
->max
)) {
201 tvec_error(tv
, "%s %lu out of range (must be in [%lu .. %lu])",
202 what
, u
, ur
->min
, ur
->max
);
208 /* --- @chtodig@ --- *
210 * Arguments: @int ch@ = a character
212 * Returns: The numeric value of the character as a digit, or @-1@ if
213 * it's not a digit. Letters count as extended digits starting
214 * with value 10; case is not significant.
217 static int chtodig(int ch
)
219 if ('0' <= ch
&& ch
<= '9') return (ch
- '0');
220 else if ('a' <= ch
&& ch
<= 'z') return (ch
- 'a' + 10);
221 else if ('A' <= ch
&& ch
<= 'Z') return (ch
- 'A' + 10);
225 /* --- @parse_unsigned_integer@, @parse_signed_integer@ --- *
227 * Arguments: @unsigned long *u_out@, @long *i_out@ = where to put the
229 * @const char **q_out@ = where to put the end position
230 * @const char *p@ = pointer to the string to parse
232 * Returns: Zero on success, @-1@ on error.
234 * Use: Parse an integer from a string in the test-vector format.
235 * This is mostly extension of the traditional C @strtoul@
236 * format: supported inputs include:
238 * * NNN -- a decimal number (even if it starts with `0');
239 * * 0xNNN -- hexadecimal;
242 * * NNrNNN -- base NN.
244 * Furthermore, single underscores are permitted internally as
245 * an insignificant digit separator.
248 static int parse_unsigned_integer(unsigned long *u_out
, const char **q_out
,
255 #define f_implicit 1u /* implicitly reading base 10 */
256 #define f_digit 2u /* read a real digit */
257 #define f_uscore 4u /* found an underscore */
261 * This will deal with the traditional `0[box]...' prefixes. We'll leave
262 * our new `NNr...' syntax for later.
264 if (p
[0] != '0' || !p
[1]) {
265 d
= chtodig(*p
); if (0 > d
|| d
>= 10) return (-1);
266 r
= 10; u
= d
; p
++; f
|= f_implicit
| f_digit
;
268 u
= 0; d
= chtodig(p
[2]);
269 if (d
< 0) { r
= 10; f
|= f_implicit
| f_digit
; p
++; }
270 else if ((p
[1] == 'x' || p
[1] == 'X') && d
< 16) { r
= 16; p
+= 2; }
271 else if ((p
[1] == 'o' || p
[1] == 'O') && d
< 8) { r
= 8; p
+= 2; }
272 else if ((p
[1] == 'b' || p
[1] == 'B') && d
< 2) { r
= 2; p
+= 2; }
273 else { r
= 10; f
|= f_digit
; p
++; }
278 /* Work through the string a character at a time. */
280 ch
= *p
; switch (ch
) {
283 /* An underscore is OK if we haven't just seen one. */
285 if (f
&f_uscore
) goto done
;
286 p
++; f
= (f
&~f_implicit
) | f_uscore
;
290 /* An `r' is OK if the number so far is small enough to be a sensible
291 * base, and we're scanning decimal implicitly.
294 if (!(f
&f_implicit
) || !u
|| u
>= 36) goto done
;
295 d
= chtodig(p
[1]); if (0 > d
|| d
>= u
) goto done
;
296 r
= u
; u
= d
; f
= (f
&~f_implicit
) | f_digit
; p
+= 2; q
= p
;
300 /* Otherwise we expect a valid digit and accumulate it. */
301 d
= chtodig(ch
); if (d
< 0 || d
>= r
) goto done
;
302 if (u
> ULONG_MAX
/r
) return (-1);
303 u
*= r
; if (u
> ULONG_MAX
- d
) return (-1);
304 u
+= d
; f
= (f
&~f_uscore
) | f_digit
; p
++; q
= p
;
310 if (!(f
&f_digit
)) return (-1);
311 *u_out
= u
; *q_out
= q
; return (0);
318 static int parse_signed_integer(long *i_out
, const char **q_out
,
325 /* Read an initial sign. */
327 else if (*p
== '-') { f
|= f_neg
; p
++; }
329 /* Scan an unsigned number. */
330 if (parse_unsigned_integer(&u
, q_out
, p
)) return (-1);
332 /* Check for signed overflow and apply the sign. */
334 if (u
> LONG_MAX
) return (-1);
337 if (u
&& u
- 1 > -(LONG_MIN
+ 1)) return (-1);
338 *i_out
= u ?
-(long)(u
- 1) - 1 : 0;
346 /* --- @parse_unsigned@, @parse_signed@ --- *
348 * Arguments: @unsigned long *u_out@ or @long *i_out@ = where to put the
350 * @const char *p@ = string to parse
351 * @const struct tvec_urange *ur@ or
352 * @const struct tvec_irange *ir@ = range specification,
354 * @struct tvec_state *tv@ = test vector state
356 * Returns: Zero on success, @-1@ on error.
358 * Use: Parse and range-check an integer. Unlike @parse_(un)signed_
359 * integer@, these functions check that there's no cruft
360 * following the final digit, and report errors as they find
361 * them rather than leaving that to the caller.
364 static int parse_unsigned(unsigned long *u_out
, const char *p
,
365 const struct tvec_urange
*ur
,
366 struct tvec_state
*tv
)
371 if (parse_unsigned_integer(&u
, &q
, p
))
372 return (tvec_error(tv
, "invalid unsigned integer `%s'", p
));
373 if (*q
) return (tvec_syntax(tv
, *q
, "end-of-line"));
374 if (check_unsigned_range(u
, ur
, tv
, "integer")) return (-1);
375 *u_out
= u
; return (0);
378 static int parse_signed(long *i_out
, const char *p
,
379 const struct tvec_irange
*ir
,
380 struct tvec_state
*tv
)
385 if (parse_signed_integer(&i
, &q
, p
))
386 return (tvec_error(tv
, "invalid signed integer `%s'", p
));
387 if (*q
) return (tvec_syntax(tv
, *q
, "end-of-line"));
388 if (check_signed_range(i
, ir
, tv
, "integer")) return (-1);
389 *i_out
= i
; return (0);
391 static const char size_units
[] = "kMGTPEZY";
393 /* --- @parse_szint@ --- *
395 * Arguments: @struct tvec_state *tv@ = test-vector state
396 * @unsigned long *u_out@ = where to put the answer
397 * @const char *delims@ = delimiters
398 * @const char *what@ = description of what we're parsing
400 * Returns: Zero on success, %$-1$% on failure.
402 * Use: Parse a memory size.
405 static int parse_szint(struct tvec_state
*tv
, unsigned long *u_out
,
406 const char *delims
, const char *what
)
409 const char *p
, *unit
;
415 if (tvec_readword(tv
, &d
, 0, delims
, what
)) { rc
= -1; goto end
; }
417 if (parse_unsigned_integer(&u
, &p
, p
)) goto bad
;
418 if (!*p
) tvec_readword(tv
, &d
, &p
, delims
, 0);
420 for (t
= u
, unit
= size_units
; *unit
; unit
++) {
421 if (t
> ULONG_MAX
/1024) f
|= f_range
;
424 if (f
&f_range
) goto rangerr
;
437 tvec_error(tv
, "invalid %s `%s'", what
, d
.buf
);
441 tvec_error(tv
, "%s `%s' out of range", what
, d
.buf
);
447 /* --- @format_size@ --- *
449 * Arguments: @const struct gprintf_ops *gops@ = print operations
450 * @void *go@ = print destination
451 * @unsigned long u@ = a size
452 * @unsigned style@ = style (@TVSF_...@)
456 * Use: Format @u@ as a size in bytes to the destination, expressing
457 * it with a unit prefix if this is possible exactly.
460 static void format_size(const struct gprintf_ops
*gops
, void *go
,
461 unsigned long u
, unsigned style
)
466 gprintf(gops
, go
, "%lu", u
);
467 else if (!u
|| u
%1024)
468 gprintf(gops
, go
, "%lu%sB", u
, style
&TVSF_COMPACT ?
"" : " ");
470 for (unit
= size_units
, u
/= 1024;
471 !(u
%1024) && unit
[1];
473 gprintf(gops
, go
, "%lu%s%cB", u
, style
&TVSF_COMPACT ?
"" : " ", *unit
);
477 /*----- Floating-point utilities ------------------------------------------*/
479 /* --- @eqish_floating_p@ --- *
481 * Arguments: @double x, y@ = two numbers to compare
482 * @const struct tvec_floatinfo *fi@ = floating-point info
484 * Returns: Nonzero if the comparand @x@ is sufficiently close to the
485 * reference @y@, or zero if it's definitely different.
488 static int eqish_floating_p(double x
, double y
,
489 const struct tvec_floatinfo
*fi
)
493 if (NANP(x
)) return (NANP(y
)); else if (NANP(y
)) return (0);
494 if (INFP(x
)) return (x
== y
); else if (INFP(y
)) return (0);
496 switch (fi ? fi
->f
&TVFF_EQMASK
: TVFF_EXACT
) {
498 return (x
== y
&& NEGP(x
) == NEGP(y
));
500 t
= x
- y
; if (t
< 0) t
= -t
; return (t
< fi
->delta
);
502 t
= 1.0 - x
/y
; if (t
< 0) t
= -t
; return (t
< fi
->delta
);
508 /* --- @format_floating@ --- *
510 * Arguments: @const struct gprintf_ops *gops@ = print operations
511 * @void *go@ = print destination
512 * @double x@ = number to print
516 * Use: Print a floating-point number, accurately.
519 static void format_floating(const struct gprintf_ops
*gops
, void *go
,
525 gprintf(gops
, go
, "#nan");
527 gprintf(gops
, go
, x
> 0 ?
"#+inf" : "#-inf");
529 /* Ugh. C doesn't provide any function for just printing a
530 * floating-point number /correctly/, i.e., so that you can read the
531 * result back and recover the number you first thought of. There are
532 * complicated algorithms published for doing this, but I really don't
533 * want to get into that here. So we have this.
535 * The sign doesn't cause significant difficulty so we're going to ignore
536 * it for now. So suppose we're given a number %$x = f b^e$%, in
537 * base-%$b$% format, so %$f b^n$% and %$e$% are integers, with
538 * %$0 \le f < 1$%. We're going to convert it into the nearest integer
539 * of the form %$X = F B^E$%, with similar conditions, only with the
540 * additional requirement that %$X$% is normalized, i.e., that %$X = 0$%
541 * or %$F \ge B^{-N}$%.
543 * We're rounding to the nearest such %$X$%. If there is to be ambiguity
544 * in the conversion, then some %$x = f b^e$% and the next smallest
545 * representable number %$x' = x + b^{e-n}$% must both map to the same
546 * %$X$%, which means both %$x$% and %$x'$% must be nearer to %$X$% than
547 * any other number representable in the target system. The nest larger
548 * number is %$X' = X + B^{E-N}$%; the next smaller number will normally
549 * be %$W = X - B^{E-N}$%, but if %$F = 1/B$ then the next smaller number
550 * is actually %$X - B^{E-N-1}$%. We ignore this latter possibility in
551 * the pursuit of a conservative estimate (though actually it doesn't
554 * If both %$x$% and %$x'$% map to %$X$% then we must have
555 * %$L = X - B^{E-N}/2 \le x$% and %$x + b^{e-n} \le R = X + B^{E-N}/2$%;
556 * so firstly %$f b^e = x \ge L = W + B^{E-N}/2 > W = (F - B^{-N}) B^E$%,
557 * and secondly %$b^{e-n} \le B^{E-N}$%. Since these inequalities are in
558 * opposite senses, we can divide, giving
560 * %$f b^e/b^{e-n} > (F - B^{-N}) B^E/B^{E-N}$% ,
564 * %$f b^n > (F - B^{-N}) B^N = F B^N - 1$% .
566 * Now %$f \le 1 - b^{-n}$%, and %$F \ge B^{-1}$%, so, for this to be
567 * possible, it must be the case that
569 * %$(1 - b^{-n}) b^n = b^n - 1 > B^{N-1} - 1$% .
571 * Then rearrange and take logarithms, obtaining
573 * %$(N - 1) \log B < n \log b$% ,
577 * %$N < n \log b/\log B + 1$% .
579 * Recall that this is a necessary condition for a collision to occur; we
580 * are therefore safe whenever
582 * %$N \ge n \log b/\log B + 1$% ;
584 * so, taking ceilings,
586 * %$N \ge \lceil n \log b/\log B \rceil + 1$% .
588 * So that's why we have this.
590 * I'm going to assume that @n = DBL_MANT_DIG@ is sufficiently small that
591 * we can calculate this without ending up on the wrong side of an
594 * In C11, we have @DBL_DECIMAL_DIG@, which should be the same value only
595 * as a constant. Except that modern compilers are more than clever
596 * enough to work out that this is a constant anyway.
598 * This is sometimes an overestimate: we'll print out meaningless digits
599 * that don't represent anything we actually know about the number in
600 * question. To fix that, we'd need a complicated algorithm like Steele
601 * and White's Dragon4, Gay's @dtoa@, or Burger and Dybvig's algorithm
602 * (note that Loitsch's Grisu2 is conservative, and Grisu3 hands off to
603 * something else in difficult situations).
606 prec
= ceil(DBL_MANT_DIG
*log(FLT_RADIX
)/log(10)) + 1;
607 gprintf(gops
, go
, "%.*g", prec
, x
);
611 /* --- @parse_floating@ --- *
613 * Arguments: @double *x_out@ = where to put the result
614 * @const char *q_out@ = where to leave end pointer, or null
615 * @const char *p@ = string to parse
616 * @const struct tvec_floatinfo *fi@ = floating-point info
617 * @struct tvec_state *tv@ = test vector state
619 * Returns: Zero on success, @-1@ on error.
621 * Use: Parse a floating-point number from a string. Reports any
622 * necessary errors. If @q_out@ is not null then trailing
623 * material is permitted and a pointer to it (or the end of the
624 * string) is left in @*q_out@.
627 static int parse_floating(double *x_out
, const char **q_out
, const char *p
,
628 const struct tvec_floatinfo
*fi
,
629 struct tvec_state
*tv
)
631 const char *pp
; char *q
;
636 /* Check for special tokens. */
637 if (STRCMP(p
, ==, "#nan")) {
639 if (q_out
) *q_out
= p
+ strlen(p
);
642 tvec_error(tv
, "NaN not supported on this system");
647 else if (STRCMP(p
, ==, "#inf") ||
648 STRCMP(p
, ==, "#+inf") || STRCMP(p
, ==, "+#inf")) {
650 if (q_out
) *q_out
= p
+ strlen(p
);
651 x
= INFINITY
; rc
= 0;
653 tvec_error(tv
, "infinity not supported on this system");
658 else if (STRCMP(p
, ==, "#-inf") || STRCMP(p
, ==, "-#inf")) {
660 if (q_out
) *q_out
= p
+ strlen(p
);
661 x
= -INFINITY
; rc
= 0;
663 tvec_error(tv
, "infinity not supported on this system");
668 /* Check that this looks like a number, so we can exclude `strtod'
669 * recognizing its own non-finite number tokens.
673 if (*pp
== '+' || *pp
== '-') pp
++;
674 if (*pp
== '.') pp
++;
676 tvec_syntax(tv
, *p ?
*p
: fgetc(tv
->fp
), "floating-point number");
680 /* Parse the number using the system parser. */
681 olderr
= errno
; errno
= 0;
683 if (q_out
) *q_out
= q
;
684 else if (*q
) { tvec_syntax(tv
, *q
, "end-of-line"); rc
= -1; goto end
; }
685 if (errno
&& (errno
!= ERANGE
|| (x
> 0 ?
-x
: x
) == HUGE_VAL
)) {
686 tvec_error(tv
, "invalid floating-point number `%.*s': %s",
687 (int)(q
- p
), p
, strerror(errno
));
693 /* Check that the number is acceptable. */
694 if (NANP(x
) && fi
&& !(fi
->f
&TVFF_NANOK
)) {
695 tvec_error(tv
, "#nan not allowed here");
699 if (fi
&& ((!(fi
->f
&TVFF_NOMIN
) && x
< fi
->min
) ||
700 (!(fi
->f
&TVFF_NOMAX
) && x
> fi
->max
))) {
701 dstr_puts(&d
, "floating-point number ");
702 format_floating(&dstr_printops
, &d
, x
);
703 dstr_puts(&d
, " out of range (must be in ");
704 if (fi
->f
&TVFF_NOMIN
)
705 dstr_puts(&d
, "(#-inf");
707 { dstr_putc(&d
, '['); format_floating(&dstr_printops
, &d
, fi
->min
); }
708 dstr_puts(&d
, " .. ");
709 if (fi
->f
&TVFF_NOMAX
)
710 dstr_puts(&d
, "#+inf)");
712 { format_floating(&dstr_printops
, &d
, fi
->max
); dstr_putc(&d
, ']'); }
713 dstr_putc(&d
, ')'); dstr_putz(&d
);
714 tvec_error(tv
, "%s", d
.buf
); rc
= -1; goto end
;
724 /*----- String utilities --------------------------------------------------*/
726 /* Special character name table. */
727 static const struct chartab
{
728 const char *name
; /* character name */
729 int ch
; /* character value */
730 unsigned f
; /* flags: */
731 #define CTF_PREFER 1u /* preferred name */
732 #define CTF_SHORT 2u /* short name (compact style) */
734 { "#eof", EOF
, CTF_PREFER
| CTF_SHORT
},
735 { "#nul", '\0', CTF_PREFER
},
736 { "#bell", '\a', CTF_PREFER
},
737 { "#ding", '\a', 0 },
738 { "#bel", '\a', CTF_SHORT
},
739 { "#backspace", '\b', CTF_PREFER
},
740 { "#bs", '\b', CTF_SHORT
},
741 { "#escape", '\x1b', CTF_PREFER
},
742 { "#esc", '\x1b', CTF_SHORT
},
743 { "#formfeed", '\f', CTF_PREFER
},
744 { "#ff", '\f', CTF_SHORT
},
745 { "#newline", '\n', CTF_PREFER
},
746 { "#linefeed", '\n', 0 },
747 { "#lf", '\n', CTF_SHORT
},
749 { "#return", '\r', CTF_PREFER
},
750 { "#carriage-return", '\r', 0 },
751 { "#cr", '\r', CTF_SHORT
},
752 { "#tab", '\t', CTF_PREFER
| CTF_SHORT
},
753 { "#horizontal-tab", '\t', 0 },
755 { "#vertical-tab", '\v', CTF_PREFER
},
756 { "#vt", '\v', CTF_SHORT
},
757 { "#space", ' ', 0 },
758 { "#spc", ' ', CTF_SHORT
},
759 { "#delete", '\x7f', CTF_PREFER
},
760 { "#del", '\x7f', CTF_SHORT
},
764 /* --- @find_charname@ --- *
766 * Arguments: @int ch@ = character to match
767 * @unsigned f@ = flags (@CTF_...@) to match
769 * Returns: The name of the character, or null if no match is found.
771 * Use: Looks up a name for a character. Specifically, it returns
772 * the first entry in the @chartab@ table which matches @ch@ and
773 * which has one of the flags @f@ set.
776 static const char *find_charname(int ch
, unsigned f
)
778 const struct chartab
*ct
;
780 for (ct
= chartab
; ct
->name
; ct
++)
781 if (ct
->ch
== ch
&& (ct
->f
&f
)) return (ct
->name
);
785 /* --- @read_charname@ --- *
787 * Arguments: @int *ch_out@ = where to put the character
788 * @const char *p@ = character name
789 * @unsigned f@ = flags (@TCF_...@)
791 * Returns: Zero if a match was found, @-1@ if not.
793 * Use: Looks up a character by name. If @RCF_EOFOK@ is set in @f@,
794 * then the @EOF@ marker can be matched; otherwise it can't.
798 static int read_charname(int *ch_out
, const char *p
, unsigned f
)
800 const struct chartab
*ct
;
802 for (ct
= chartab
; ct
->name
; ct
++)
803 if (STRCMP(p
, ==, ct
->name
) && ((f
&RCF_EOFOK
) || ct
->ch
>= 0))
804 { *ch_out
= ct
->ch
; return (0); }
808 /* --- @format_charesc@ --- *
810 * Arguments: @const struct gprintf_ops *gops@ = print operations
811 * @void *go@ = print destination
812 * @int ch@ = character to format
813 * @unsigned f@ = flags (@FCF_...@)
817 * Use: Format a character as an escape sequence, possibly as part of
818 * a larger string. If @FCF_BRACE@ is set in @f@, then put
819 * braces around a `\x...' code, so that it's suitable for use
820 * in a longer string.
824 static void format_charesc(const struct gprintf_ops
*gops
, void *go
,
828 case '\a': gprintf(gops
, go
, "\\a"); break;
829 case '\b': gprintf(gops
, go
, "\\b"); break;
830 case '\x1b': gprintf(gops
, go
, "\\e"); break;
831 case '\f': gprintf(gops
, go
, "\\f"); break;
832 case '\r': gprintf(gops
, go
, "\\r"); break;
833 case '\n': gprintf(gops
, go
, "\\n"); break;
834 case '\t': gprintf(gops
, go
, "\\t"); break;
835 case '\v': gprintf(gops
, go
, "\\v"); break;
836 case '\\': gprintf(gops
, go
, "\\\\"); break;
837 case '\'': gprintf(gops
, go
, "\\'"); break;
839 if (f
&FCF_BRACE
) gprintf(gops
, go
, "\\{0}");
840 else gprintf(gops
, go
, "\\0");
844 gprintf(gops
, go
, "\\x{%0*x}", hex_width(UCHAR_MAX
), ch
);
846 gprintf(gops
, go
, "\\x%0*x", hex_width(UCHAR_MAX
), ch
);
851 /* --- @format_char@ --- *
853 * Arguments: @const struct gprintf_ops *gops@ = print operations
854 * @void *go@ = print destination
855 * @int ch@ = character to format
859 * Use: Format a single character.
862 static void format_char(const struct gprintf_ops
*gops
, void *go
, int ch
)
865 case '\\': case '\'': escape
:
866 gprintf(gops
, go
, "'");
867 format_charesc(gops
, go
, ch
, 0);
868 gprintf(gops
, go
, "'");
871 if (!isprint(ch
)) goto escape
;
872 gprintf(gops
, go
, "'%c'", ch
);
877 /* --- @maybe_format_unsigned_char@, @maybe_format_signed_char@ --- *
879 * Arguments: @const struct gprintf_ops *gops@ = print operations
880 * @void *go@ = print destination
881 * @unsigned long u@ or @long i@ = an integer
885 * Use: Format a (signed or unsigned) integer as a character, if it's
886 * in range, printing something like `= 'q''. It's assumed that
887 * a comment marker has already been output.
890 static void maybe_format_unsigned_char
891 (const struct gprintf_ops
*gops
, void *go
, unsigned long u
)
895 p
= find_charname(u
, CTF_PREFER
);
896 if (p
) gprintf(gops
, go
, " = %s", p
);
898 { gprintf(gops
, go
, " = "); format_char(gops
, go
, u
); }
901 static void maybe_format_signed_char
902 (const struct gprintf_ops
*gops
, void *go
, long i
)
906 p
= find_charname(i
, CTF_PREFER
);
907 if (p
) gprintf(gops
, go
, " = %s", p
);
908 if (0 <= i
&& i
< UCHAR_MAX
)
909 { gprintf(gops
, go
, " = "); format_char(gops
, go
, i
); }
912 /* --- @read_charesc@ --- *
914 * Arguments: @int *ch_out@ = where to put the result
915 * @struct tvec_state *tv@ = test vector state
917 * Returns: Zero on success, @-1@ on error.
919 * Use: Parse and convert an escape sequence from @tv@'s input
920 * stream, assuming that the initial `\' has already been read.
921 * Reports errors as appropriate.
924 static int read_charesc(int *ch_out
, struct tvec_state
*tv
)
933 /* Things we shouldn't find. */
934 case EOF
: case '\n': return (tvec_syntax(tv
, ch
, "string escape"));
936 /* Single-character escapes. */
937 case '\'': *ch_out
= '\''; break;
938 case '\\': *ch_out
= '\\'; break;
939 case '"': *ch_out
= '"'; break;
940 case 'a': *ch_out
= '\a'; break;
941 case 'b': *ch_out
= '\b'; break;
942 case 'e': *ch_out
= '\x1b'; break;
943 case 'f': *ch_out
= '\f'; break;
944 case 'n': *ch_out
= '\n'; break;
945 case 'r': *ch_out
= '\r'; break;
946 case 't': *ch_out
= '\t'; break;
947 case 'v': *ch_out
= '\v'; break;
949 /* Hex escapes, with and without braces. */
952 if (ch
== '{') { f
|= f_brace
; ch
= getc(tv
->fp
); }
955 if (esc
< 0 || esc
>= 16) return (tvec_syntax(tv
, ch
, "hex digit"));
957 ch
= getc(tv
->fp
); i
= chtodig(ch
); if (i
< 0 || i
>= 16) break;
960 return (tvec_error(tv
,
961 "character code %d out of range", esc
));
963 if (!(f
&f_brace
)) ungetc(ch
, tv
->fp
);
964 else if (ch
!= '}') return (tvec_syntax(tv
, ch
, "`}'"));
968 /* Other things, primarily octal escapes. */
970 f
|= f_brace
; ch
= getc(tv
->fp
);
973 if ('0' <= ch
&& ch
< '8') {
974 i
= 1; esc
= ch
- '0';
977 if ('0' > ch
|| ch
>= '8') { ungetc(ch
, tv
->fp
); break; }
978 esc
= 8*esc
+ ch
- '0';
979 i
++; if (i
>= 3) break;
983 if (ch
!= '}') return (tvec_syntax(tv
, ch
, "`}'"));
986 return (tvec_error(tv
,
987 "character code %d out of range", esc
));
988 *ch_out
= esc
; break;
990 return (tvec_syntax(tv
, ch
, "string escape"));
999 /* --- @read_quoted_string@ --- *
1001 * Arguments: @dstr *d@ = string to write to
1002 * @int quote@ = initial quote, `'' or `"'
1003 * @struct tvec_state *tv@ = test vector state
1005 * Returns: Zero on success, @-1@ on error.
1007 * Use: Read the rest of a quoted string into @d@, reporting errors
1010 * A single-quoted string is entirely literal. A double-quoted
1011 * string may contain C-like escapes.
1014 static int read_quoted_string(dstr
*d
, int quote
, struct tvec_state
*tv
)
1021 case EOF
: case '\n':
1022 return (tvec_syntax(tv
, ch
, "`%c'", quote
));
1024 if (quote
== '\'') goto ordinary
;
1025 ch
= getc(tv
->fp
); if (ch
== '\n') { tv
->lno
++; break; }
1026 ungetc(ch
, tv
->fp
); if (read_charesc(&ch
, tv
)) return (-1);
1029 if (ch
== quote
) goto end
;
1041 /* --- @collect_bare@ --- *
1043 * Arguments: @dstr *d@ = string to write to
1044 * @struct tvec_state *tv@ = test vector state
1046 * Returns: Zero on success, @-1@ on error.
1048 * Use: Read barewords and the whitespace between them. Stop when we
1049 * encounter something which can't start a bareword.
1052 static int collect_bare(dstr
*d
, struct tvec_state
*tv
)
1054 size_t pos
= d
->len
;
1055 enum { WORD
, SPACE
, ESCAPE
}; unsigned s
= WORD
;
1062 tvec_syntax(tv
, ch
, "bareword");
1065 if (s
== ESCAPE
) { tv
->lno
++; goto addch
; }
1066 if (s
== WORD
) pos
= d
->len
;
1067 ungetc(ch
, tv
->fp
); if (tvec_nexttoken(tv
)) { rc
= -1; goto end
; }
1068 DPUTC(d
, ' '); s
= SPACE
;
1070 case '"': case '\'': case '!': case '#': case ')': case '}': case ']':
1071 if (s
== SPACE
) { ungetc(ch
, tv
->fp
); goto done
; }
1077 if (s
!= ESCAPE
&& isspace(ch
)) {
1078 if (s
== WORD
) pos
= d
->len
;
1079 DPUTC(d
, ch
); s
= SPACE
;
1083 DPUTC(d
, ch
); s
= WORD
;
1088 if (s
== SPACE
) d
->len
= pos
;
1094 /* --- @set_up_encoding@ --- *
1096 * Arguments: @const codec_class **ccl_out@ = where to put the class
1097 * @unsigned *f_out@ = where to put the flags
1098 * @unsigned code@ = the coding scheme to use (@TVEC_...@)
1102 * Use: Helper for @read_compound_string@ below.
1104 * Return the appropriate codec class and flags for @code@.
1105 * Leaves @*ccl_out@ null if the coding scheme doesn't have a
1106 * backing codec class (e.g., @TVCODE_BARE@).
1109 enum { TVCODE_BARE
, TVCODE_HEX
, TVCODE_BASE64
, TVCODE_BASE32
};
1110 static void set_up_encoding(const codec_class
**ccl_out
, unsigned *f_out
,
1115 *ccl_out
= 0; *f_out
= 0;
1118 *ccl_out
= &hex_class
; *f_out
= CDCF_IGNCASE
;
1121 *ccl_out
= &base32_class
; *f_out
= CDCF_IGNCASE
| CDCF_IGNEQPAD
;
1124 *ccl_out
= &base64_class
; *f_out
= CDCF_IGNEQPAD
;
1131 /* --- @flush_codec@ --- *
1133 * Arguments: @codec *cdc@ = a codec, or null
1134 * @dstr *d@ = output string
1135 * @struct tvec_state *tv@ = test vector state
1137 * Returns: Zero on success, @-1@ on error.
1139 * Use: Helper for @read_compound_string@ below.
1141 * Flush out any final buffered material from @cdc@, and check
1142 * that it's in a good state. Frees the codec on success. Does
1143 * nothing if @cdc@ is null.
1146 static int flush_codec(codec
*cdc
, dstr
*d
, struct tvec_state
*tv
)
1151 err
= cdc
->ops
->code(cdc
, 0, 0, d
);
1153 return (tvec_error(tv
, "invalid %s sequence end: %s",
1154 cdc
->ops
->c
->name
, codec_strerror(err
)));
1155 cdc
->ops
->destroy(cdc
);
1160 /* --- @read_compound_string@ --- *
1162 * Arguments: @void **p_inout@ = address of output buffer pointer
1163 * @size_t *sz_inout@ = address of buffer size
1164 * @unsigned code@ = initial interpretation of barewords
1165 * @unsigned f@ = other flags (@RCSF_...@)
1166 * @struct tvec_state *tv@ = test vector state
1168 * Returns: Zero on success, @-1@ on error.
1170 * Use: Parse a compound string, i.e., a sequence of stringish pieces
1171 * which might be quoted strings, character names, or barewords
1172 * to be decoded accoding to @code@, interspersed with
1173 * additional directives.
1175 * If the initial buffer pointer is non-null and sufficiently
1176 * large, then it will be reused; otherwise, it is freed and a
1177 * fresh, sufficiently large buffer is allocated and returned.
1180 #define RCSF_NESTED 1u
1181 static int read_compound_string(void **p_inout
, size_t *sz_inout
,
1182 unsigned code
, unsigned f
,
1183 struct tvec_state
*tv
)
1185 const codec_class
*ccl
; unsigned cdf
;
1187 dstr d
= DSTR_INIT
, w
= DSTR_INIT
;
1190 void *pp
= 0; size_t sz
;
1194 set_up_encoding(&ccl
, &cdf
, code
); cdc
= 0;
1196 if (tvec_nexttoken(tv
)) return (tvec_syntax(tv
, fgetc(tv
->fp
), "string"));
1201 case ')': case ']': case '}':
1202 /* Close brackets. Leave these for recursive caller if there is one,
1206 if (!(f
&RCSF_NESTED
))
1207 { rc
= tvec_syntax(tv
, ch
, "string"); goto end
; }
1208 ungetc(ch
, tv
->fp
); goto done
;
1210 case '"': case '\'':
1211 /* Quotes. Read a quoted string. */
1213 if (cdc
&& flush_codec(cdc
, &d
, tv
)) { rc
= -1; goto end
; }
1215 if (read_quoted_string(&d
, ch
, tv
)) { rc
= -1; goto end
; }
1219 /* A named character. */
1222 if (cdc
&& flush_codec(cdc
, &d
, tv
)) { rc
= -1; goto end
; }
1224 DRESET(&w
); tvec_readword(tv
, &w
, 0, ";", "character name");
1225 if (STRCMP(w
.buf
, ==, "#empty")) break;
1226 if (read_charname(&ch
, w
.buf
, RCF_EOFOK
)) {
1227 rc
= tvec_error(tv
, "unknown character name `%s'", d
.buf
);
1230 DPUTC(&d
, ch
); break;
1233 /* A magic keyword. */
1235 if (cdc
&& flush_codec(cdc
, &d
, tv
)) { rc
= -1; goto end
; }
1238 DRESET(&w
); tvec_readword(tv
, &w
, 0, ";", "`!'-keyword");
1240 /* Change bareword coding system. */
1241 if (STRCMP(w
.buf
, ==, "!bare"))
1242 { code
= TVCODE_BARE
; set_up_encoding(&ccl
, &cdf
, code
); }
1243 else if (STRCMP(w
.buf
, ==, "!hex"))
1244 { code
= TVCODE_HEX
; set_up_encoding(&ccl
, &cdf
, code
); }
1245 else if (STRCMP(w
.buf
, ==, "!base32"))
1246 { code
= TVCODE_BASE32
; set_up_encoding(&ccl
, &cdf
, code
); }
1247 else if (STRCMP(w
.buf
, ==, "!base64"))
1248 { code
= TVCODE_BASE64
; set_up_encoding(&ccl
, &cdf
, code
); }
1250 /* Repeated substrings. */
1251 else if (STRCMP(w
.buf
, ==, "!repeat")) {
1252 if (tvec_nexttoken(tv
)) {
1253 rc
= tvec_syntax(tv
, fgetc(tv
->fp
), "repeat count");
1257 if (tvec_readword(tv
, &w
, 0, ";{", "repeat count"))
1258 { rc
= -1; goto end
; }
1259 if (parse_unsigned_integer(&n
, &q
, w
.buf
)) {
1260 rc
= tvec_error(tv
, "invalid repeat count `%s'", w
.buf
);
1263 if (*q
) { rc
= tvec_syntax(tv
, *q
, "`{'"); goto end
; }
1264 if (tvec_nexttoken(tv
))
1265 { rc
= tvec_syntax(tv
, fgetc(tv
->fp
), "`{'"); goto end
; }
1266 ch
= getc(tv
->fp
); if (ch
!= '{')
1267 { rc
= tvec_syntax(tv
, ch
, "`{'"); goto end
; }
1269 if (read_compound_string(&pp
, &sz
, code
, f
| RCSF_NESTED
, tv
))
1270 { rc
= -1; goto end
; }
1271 ch
= getc(tv
->fp
); if (ch
!= '}')
1272 { rc
= tvec_syntax(tv
, ch
, "`}'"); goto end
; }
1274 if (n
> (size_t)-1/sz
)
1275 { rc
= tvec_error(tv
, "repeat size out of range"); goto end
; }
1276 dstr_ensure(&d
, n
*sz
);
1278 { memset(d
.buf
+ d
.len
, *(unsigned char *)pp
, n
); d
.len
+= n
; }
1280 for (; n
--; d
.len
+= sz
) memcpy(d
.buf
+ d
.len
, pp
, sz
);
1285 /* Anything else is an error. */
1287 tvec_error(tv
, "unknown string keyword `%s'", w
.buf
);
1293 /* A bareword. Process it according to the current coding system. */
1298 if (collect_bare(&d
, tv
)) goto done
;
1302 ungetc(ch
, tv
->fp
); DRESET(&w
);
1303 if (tvec_readword(tv
, &w
, 0, ";",
1304 "%s-encoded fragment", ccl
->name
))
1305 { rc
= -1; goto end
; }
1306 if (!cdc
) cdc
= ccl
->decoder(cdf
);
1307 err
= cdc
->ops
->code(cdc
, w
.buf
, w
.len
, &d
);
1309 tvec_error(tv
, "invalid %s fragment `%s': %s",
1310 ccl
->name
, w
.buf
, codec_strerror(err
));
1317 } while (!tvec_nexttoken(tv
));
1320 /* Wrap things up. */
1321 if (cdc
&& flush_codec(cdc
, &d
, tv
)) { rc
= -1; goto end
; }
1323 if (*sz_inout
<= d
.len
)
1324 { xfree(*p_inout
); *p_inout
= xmalloc(d
.len
+ 1); }
1325 p
= *p_inout
; memcpy(p
, d
.buf
, d
.len
); p
[d
.len
] = 0; *sz_inout
= d
.len
;
1329 /* Clean up any debris. */
1330 if (cdc
) cdc
->ops
->destroy(cdc
);
1332 dstr_destroy(&d
); dstr_destroy(&w
);
1336 /*----- Signed and unsigned integer types ---------------------------------*/
1338 /* --- @init_int@, @init_uint@ --- *
1340 * Arguments: @union tvec_regval *rv@ = register value
1341 * @const struct tvec_regdef *rd@ = register definition
1345 * Use: Initialize a register value.
1347 * Integer values are initialized to zero.
1350 static void init_int(union tvec_regval
*rv
, const struct tvec_regdef
*rd
)
1353 static void init_uint(union tvec_regval
*rv
, const struct tvec_regdef
*rd
)
1356 /* --- @eq_int@, @eq_uint@ --- *
1358 * Arguments: @const union tvec_regval *rv0, *rv1@ = register values
1359 * @const struct tvec_regdef *rd@ = register definition
1361 * Returns: Nonzero if the values are equal, zero if unequal
1363 * Use: Compare register values for equality.
1366 static int eq_int(const union tvec_regval
*rv0
, const union tvec_regval
*rv1
,
1367 const struct tvec_regdef
*rd
)
1368 { return (rv0
->i
== rv1
->i
); }
1370 static int eq_uint(const union tvec_regval
*rv0
,
1371 const union tvec_regval
*rv1
,
1372 const struct tvec_regdef
*rd
)
1373 { return (rv0
->u
== rv1
->u
); }
1375 /* --- @tobuf_int@, @tobuf_uint@ --- *
1377 * Arguments: @buf *b@ = buffer
1378 * @const union tvec_regval *rv@ = register value
1379 * @const struct tvec_regdef *rd@ = register definition
1381 * Returns: Zero on success, %$-1$% on failure.
1383 * Use: Serialize a register value to a buffer.
1385 * Integer values are serialized as little-endian 64-bit signed
1386 * or unsigned integers.
1389 static int tobuf_int(buf
*b
, const union tvec_regval
*rv
,
1390 const struct tvec_regdef
*rd
)
1391 { return (signed_to_buf(b
, rv
->i
)); }
1393 static int tobuf_uint(buf
*b
, const union tvec_regval
*rv
,
1394 const struct tvec_regdef
*rd
)
1395 { return (unsigned_to_buf(b
, rv
->u
)); }
1397 /* --- @frombuf_int@, @frombuf_uint@ --- *
1399 * Arguments: @buf *b@ = buffer
1400 * @union tvec_regval *rv@ = register value
1401 * @const struct tvec_regdef *rd@ = register definition
1403 * Returns: Zero on success, %$-1$% on failure.
1405 * Use: Deserialize a register value from a buffer.
1407 * Integer values are serialized as 64-bit signed or unsigned
1411 static int frombuf_int(buf
*b
, union tvec_regval
*rv
,
1412 const struct tvec_regdef
*rd
)
1413 { return (signed_from_buf(b
, &rv
->i
)); }
1415 static int frombuf_uint(buf
*b
, union tvec_regval
*rv
,
1416 const struct tvec_regdef
*rd
)
1417 { return (unsigned_from_buf(b
, &rv
->u
)); }
1419 /* --- @parse_int@, @parse_uint@ --- *
1421 * Arguments: @union tvec_regval *rv@ = register value
1422 * @const struct tvec_regdef *rd@ = register definition
1423 * @struct tvec_state *tv@ = test-vector state
1425 * Returns: Zero on success, %$-1$% on error.
1427 * Use: Parse a register value from an input file.
1429 * Integers may be input in decimal, hex, binary, or octal,
1430 * following approximately usual conventions.
1432 * * Signed integers may be preceded with a `+' or `-' sign.
1434 * * Decimal integers are just a sequence of decimal digits
1437 * * Octal integers are a sequence of digits `0' ... `7',
1438 * preceded by `0o' or `0O'.
1440 * * Hexadecimal integers are a sequence of digits `0'
1441 * ... `9', `a' ... `f', or `A' ... `F', preceded by `0x' or
1444 * * Radix-B integers are a sequence of digits `0' ... `9',
1445 * `a' ... `f', or `A' ... `F', each with value less than B,
1446 * preceded by `Br' or `BR', where 0 < B < 36 is expressed
1447 * in decimal without any leading `0' or internal
1450 * * A digit sequence may contain internal underscore `_'
1451 * separators, but not before or after all of the digits;
1452 * and two consecutive `_' characters are not permitted.
1455 static int parse_int(union tvec_regval
*rv
, const struct tvec_regdef
*rd
,
1456 struct tvec_state
*tv
)
1461 if (tvec_readword(tv
, &d
, 0, ";", "signed integer"))
1462 { rc
= -1; goto end
; }
1463 if (parse_signed(&rv
->i
, d
.buf
, rd
->arg
.p
, tv
)) { rc
= -1; goto end
; }
1464 if (tvec_flushtoeol(tv
, 0)) { rc
= -1; goto end
; }
1471 static int parse_uint(union tvec_regval
*rv
, const struct tvec_regdef
*rd
,
1472 struct tvec_state
*tv
)
1477 if (tvec_readword(tv
, &d
, 0, ";", "unsigned integer"))
1478 { rc
= -1; goto end
; }
1479 if (parse_unsigned(&rv
->u
, d
.buf
, rd
->arg
.p
, tv
)) { rc
= -1; goto end
; }
1480 if (tvec_flushtoeol(tv
, 0)) { rc
= -1; goto end
; }
1487 /* --- @dump_int@, @dump_uint@ --- *
1489 * Arguments: @const union tvec_regval *rv@ = register value
1490 * @const struct tvec_regdef *rd@ = register definition
1491 * @unsigned style@ = output style (@TVSF_...@)
1492 * @const struct gprintf_ops *gops@, @void *gp@ = format output
1496 * Use: Dump a register value to the format output.
1498 * Integer values are dumped in decimal and, unless compact
1499 * output is requested, hex, and maybe a character, as a
1503 static void dump_int(const union tvec_regval
*rv
,
1504 const struct tvec_regdef
*rd
,
1506 const struct gprintf_ops
*gops
, void *go
)
1508 if (style
&TVSF_RAW
) gprintf(gops
, go
, "int:");
1509 gprintf(gops
, go
, "%ld", rv
->i
);
1510 if (!(style
&(TVSF_COMPACT
| TVSF_RAW
))) {
1511 gprintf(gops
, go
, " ; = ");
1512 format_signed_hex(gops
, go
, rv
->i
);
1513 maybe_format_signed_char(gops
, go
, rv
->i
);
1517 static void dump_uint(const union tvec_regval
*rv
,
1518 const struct tvec_regdef
*rd
,
1520 const struct gprintf_ops
*gops
, void *go
)
1522 if (style
&TVSF_RAW
) gprintf(gops
, go
, "uint:");
1523 gprintf(gops
, go
, "%lu", rv
->u
);
1524 if (!(style
&(TVSF_COMPACT
| TVSF_RAW
))) {
1525 gprintf(gops
, go
, " ; = ");
1526 format_unsigned_hex(gops
, go
, rv
->u
);
1527 maybe_format_unsigned_char(gops
, go
, rv
->u
);
1531 /* Integer type definitions. */
1532 const struct tvec_regty tvty_int
= {
1533 init_int
, trivial_release
, eq_int
,
1534 tobuf_int
, frombuf_int
,
1537 const struct tvec_regty tvty_uint
= {
1538 init_uint
, trivial_release
, eq_uint
,
1539 tobuf_uint
, frombuf_uint
,
1540 parse_uint
, dump_uint
1543 /* Predefined integer ranges. */
1544 const struct tvec_irange
1545 tvrange_schar
= { SCHAR_MIN
, SCHAR_MAX
},
1546 tvrange_short
= { SHRT_MIN
, SHRT_MAX
},
1547 tvrange_int
= { INT_MIN
, INT_MAX
},
1548 tvrange_long
= { LONG_MIN
, LONG_MAX
},
1549 tvrange_sbyte
= { -128, 127 },
1550 tvrange_i16
= { -32768, +32767 },
1551 tvrange_i32
= { -2147483648, 2147483647 };
1552 const struct tvec_urange
1553 tvrange_uchar
= { 0, UCHAR_MAX
},
1554 tvrange_ushort
= { 0, USHRT_MAX
},
1555 tvrange_uint
= { 0, UINT_MAX
},
1556 tvrange_ulong
= { 0, ULONG_MAX
},
1557 tvrange_size
= { 0, (size_t)-1 },
1558 tvrange_byte
= { 0, 255 },
1559 tvrange_u16
= { 0, 65535 },
1560 tvrange_u32
= { 0, 4294967295 };
1562 /* --- @tvec_claimeq_int@ --- *
1564 * Arguments: @struct tvec_state *tv@ = test-vector state
1565 * @long i0, i1@ = two signed integers
1566 * @const char *file@, @unsigned @lno@ = calling file and line
1567 * @const char *expr@ = the expression to quote on failure
1569 * Returns: Nonzero if @i0@ and @i1@ are equal, otherwise zero.
1571 * Use: Check that values of @i0@ and @i1@ are equal. As for
1572 * @tvec_claim@ above, a test case is automatically begun and
1573 * ended if none is already underway. If the values are
1574 * unequal, then @tvec_fail@ is called, quoting @expr@, and the
1575 * mismatched values are dumped: @i0@ is printed as the output
1576 * value and @i1@ is printed as the input reference.
1579 int tvec_claimeq_int(struct tvec_state
*tv
, long i0
, long i1
,
1580 const char *file
, unsigned lno
, const char *expr
)
1582 tv
->out
[0].v
.i
= i0
; tv
->in
[0].v
.i
= i1
;
1583 return (tvec_claimeq(tv
, &tvty_int
, 0, file
, lno
, expr
));
1586 /* --- @tvec_claimeq_uint@ --- *
1588 * Arguments: @struct tvec_state *tv@ = test-vector state
1589 * @unsigned long u0, u1@ = two unsigned integers
1590 * @const char *file@, @unsigned @lno@ = calling file and line
1591 * @const char *expr@ = the expression to quote on failure
1593 * Returns: Nonzero if @u0@ and @u1@ are equal, otherwise zero.
1595 * Use: Check that values of @u0@ and @u1@ are equal. As for
1596 * @tvec_claim@ above, a test case is automatically begun and
1597 * ended if none is already underway. If the values are
1598 * unequal, then @tvec_fail@ is called, quoting @expr@, and the
1599 * mismatched values are dumped: @u0@ is printed as the output
1600 * value and @u1@ is printed as the input reference.
1603 int tvec_claimeq_uint(struct tvec_state
*tv
,
1604 unsigned long u0
, unsigned long u1
,
1605 const char *file
, unsigned lno
, const char *expr
)
1607 tv
->out
[0].v
.u
= u0
; tv
->in
[0].v
.u
= u1
;
1608 return (tvec_claimeq(tv
, &tvty_uint
, 0, file
, lno
, expr
));
1611 /*----- Size type ---------------------------------------------------------*/
1613 /* --- @parse_size@ --- *
1615 * Arguments: @union tvec_regval *rv@ = register value
1616 * @const struct tvec_regdef *rd@ = register definition
1617 * @struct tvec_state *tv@ = test-vector state
1619 * Returns: Zero on success, %$-1$% on error.
1621 * Use: Parse a register value from an input file.
1623 * The input format for a size value consists of an unsigned
1624 * integer followed by an optional unit specifier consisting of
1625 * an SI unit prefix and (optionally) the letter `B'. */
1627 static int parse_size(union tvec_regval
*rv
, const struct tvec_regdef
*rd
,
1628 struct tvec_state
*tv
)
1633 if (parse_szint(tv
, &sz
, ";", "size")) { rc
= -1; goto end
; }
1634 if (check_unsigned_range(sz
, rd
->arg
.p
, tv
, "size")) { rc
= -1; goto end
; }
1635 if (tvec_flushtoeol(tv
, 0)) { rc
= -1; goto end
; }
1641 /* --- @dump_size@ --- *
1643 * Arguments: @const union tvec_regval *rv@ = register value
1644 * @const struct tvec_regdef *rd@ = register definition
1645 * @unsigned style@ = output style (@TVSF_...@)
1646 * @const struct gprintf_ops *gops@, @void *gp@ = format output
1650 * Use: Dump a register value to the format output.
1652 * Size values are dumped with a unit specifier, with a unit
1653 * prefox only if the size is an exact multiple of the relevant
1654 * power of two. Unless compact style is requested, the plain
1655 * decimal and hex representations of the value are also
1659 static void dump_size(const union tvec_regval
*rv
,
1660 const struct tvec_regdef
*rd
,
1662 const struct gprintf_ops
*gops
, void *go
)
1664 if (style
&TVSF_RAW
) gprintf(gops
, go
, "size:");
1665 format_size(gops
, go
, rv
->u
, style
);
1666 if (!(style
&(TVSF_COMPACT
| TVSF_RAW
))) {
1667 gprintf(gops
, go
, " ; = %lu", (unsigned long)rv
->u
);
1668 gprintf(gops
, go
, " = "); format_unsigned_hex(gops
, go
, rv
->u
);
1669 maybe_format_unsigned_char(gops
, go
, rv
->u
);
1673 /* Size type definitions. */
1674 const struct tvec_regty tvty_size
= {
1675 init_uint
, trivial_release
, eq_uint
,
1676 tobuf_uint
, frombuf_uint
,
1677 parse_size
, dump_size
1680 /* --- @tvec_claimeq_size@ --- *
1682 * Arguments: @struct tvec_state *tv@ = test-vector state
1683 * @unsigned long sz0, sz1@ = two sizes
1684 * @const char *file@, @unsigned @lno@ = calling file and line
1685 * @const char *expr@ = the expression to quote on failure
1687 * Returns: Nonzero if @sz0@ and @sz1@ are equal, otherwise zero.
1689 * Use: Check that values of @u0@ and @u1@ are equal. As for
1690 * @tvec_claim@ above, a test case is automatically begun and
1691 * ended if none is already underway. If the values are
1692 * unequal, then @tvec_fail@ is called, quoting @expr@, and the
1693 * mismatched values are dumped: @u0@ is printed as the output
1694 * value and @u1@ is printed as the input reference.
1697 int tvec_claimeq_size(struct tvec_state
*tv
,
1698 unsigned long sz0
, unsigned long sz1
,
1699 const char *file
, unsigned lno
, const char *expr
)
1701 tv
->out
[0].v
.u
= sz0
; tv
->in
[0].v
.u
= sz1
;
1702 return (tvec_claimeq(tv
, &tvty_size
, 0, file
, lno
, expr
));
1705 /*----- Floating-point type -----------------------------------------------*/
1707 /* --- @int_float@ --- *
1709 * Arguments: @union tvec_regval *rv@ = register value
1710 * @const struct tvec_regdef *rd@ = register definition
1714 * Use: Initialize a register value.
1716 * Floating-point values are initialized to zero.
1719 static void init_float(union tvec_regval
*rv
, const struct tvec_regdef
*rd
)
1722 /* --- @eq_float@ --- *
1724 * Arguments: @const union tvec_regval *rv0, *rv1@ = register values
1725 * @const struct tvec_regdef *rd@ = register definition
1727 * Returns: Nonzero if the values are equal, zero if unequal
1729 * Use: Compare register values for equality.
1731 * Floating-point values may be considered equal if their
1732 * absolute or relative difference is sufficiently small, as
1733 * described in the register definition.
1736 static int eq_float(const union tvec_regval
*rv0
,
1737 const union tvec_regval
*rv1
,
1738 const struct tvec_regdef
*rd
)
1739 { return (eqish_floating_p(rv0
->f
, rv1
->f
, rd
->arg
.p
)); }
1741 /* --- @tobuf_float@ --- *
1743 * Arguments: @buf *b@ = buffer
1744 * @const union tvec_regval *rv@ = register value
1745 * @const struct tvec_regdef *rd@ = register definition
1747 * Returns: Zero on success, %$-1$% on failure.
1749 * Use: Serialize a register value to a buffer.
1751 * Floating-point values are serialized as little-endian
1752 * IEEE 754 Binary64.
1755 static int tobuf_float(buf
*b
, const union tvec_regval
*rv
,
1756 const struct tvec_regdef
*rd
)
1757 { return (buf_putf64l(b
, rv
->f
)); }
1759 /* --- @frombuf_float@ --- *
1761 * Arguments: @buf *b@ = buffer
1762 * @union tvec_regval *rv@ = register value
1763 * @const struct tvec_regdef *rd@ = register definition
1765 * Returns: Zero on success, %$-1$% on failure.
1767 * Use: Deserialize a register value from a buffer.
1769 * Floating-point values are serialized as little-endian
1770 * IEEE 754 Binary64.
1773 static int frombuf_float(buf
*b
, union tvec_regval
*rv
,
1774 const struct tvec_regdef
*rd
)
1775 { return (buf_getf64l(b
, &rv
->f
)); }
1777 /* --- @parse_float@ --- *
1779 * Arguments: @union tvec_regval *rv@ = register value
1780 * @const struct tvec_regdef *rd@ = register definition
1781 * @struct tvec_state *tv@ = test-vector state
1783 * Returns: Zero on success, %$-1$% on error.
1785 * Use: Parse a register value from an input file.
1787 * Floating-point values are either NaN (%|#nan|%, if supported
1788 * by the platform); positive or negative infinity (%|#inf|%,
1789 * %|+#inf|%, or %|#+inf|% (preferring the last), and %|-#inf|%
1790 * or %|#-inf|% (preferring the latter), if supported by the
1791 * platform); or a number in strtod(3) syntax.
1794 static int parse_float(union tvec_regval
*rv
, const struct tvec_regdef
*rd
,
1795 struct tvec_state
*tv
)
1800 if (tvec_readword(tv
, &d
, 0, ";", "floating-point number"))
1801 { rc
= -1; goto end
; }
1802 if (parse_floating(&rv
->f
, 0, d
.buf
, rd
->arg
.p
, tv
))
1803 { rc
= -1; goto end
; }
1804 if (tvec_flushtoeol(tv
, 0)) { rc
= -1; goto end
; }
1811 /* --- @dump_float@ --- *
1813 * Arguments: @const union tvec_regval *rv@ = register value
1814 * @const struct tvec_regdef *rd@ = register definition
1815 * @unsigned style@ = output style (@TVSF_...@)
1816 * @const struct gprintf_ops *gops@, @void *gp@ = format output
1820 * Use: Dump a register value to the format output.
1822 * Floating-point values are dumped in decimal or as a special
1823 * token beginning with `%|#|%'. Some effort is taken to ensure
1824 * that the output is sufficient to uniquely identify the
1825 * original value, but, honestly, C makes this really hard.
1828 static void dump_float(const union tvec_regval
*rv
,
1829 const struct tvec_regdef
*rd
,
1831 const struct gprintf_ops
*gops
, void *go
)
1833 if (style
&TVSF_RAW
) gprintf(gops
, go
, "float:");
1834 format_floating(gops
, go
, rv
->f
);
1837 /* Floating-point type definition. */
1838 const struct tvec_regty tvty_float
= {
1839 init_float
, trivial_release
, eq_float
,
1840 tobuf_float
, frombuf_float
,
1841 parse_float
, dump_float
1844 /* Predefined floating-point ranges. */
1845 const struct tvec_floatinfo
1846 tvflt_finite
= { TVFF_EXACT
, -DBL_MAX
, DBL_MAX
, 0.0 },
1847 tvflt_nonneg
= { TVFF_EXACT
, 0, DBL_MAX
, 0.0 };
1849 /* --- @tvec_claimeqish_float@ --- *
1851 * Arguments: @struct tvec_state *tv@ = test-vector state
1852 * @double f0, f1@ = two floating-point numbers
1853 * @unsigned f@ = flags (@TVFF_...@)
1854 * @double delta@ = maximum tolerable difference
1855 * @const char *file@, @unsigned @lno@ = calling file and line
1856 * @const char *expr@ = the expression to quote on failure
1858 * Returns: Nonzero if @f0@ and @f1@ are sufficiently close, otherwise
1861 * Use: Check that values of @f0@ and @f1@ are sufficiently close.
1862 * As for @tvec_claim@ above, a test case is automatically begun
1863 * and ended if none is already underway. If the values are
1864 * too far apart, then @tvec_fail@ is called, quoting @expr@,
1865 * and the mismatched values are dumped: @f0@ is printed as the
1866 * output value and @f1@ is printed as the input reference.
1868 * The details for the comparison are as follows.
1870 * * A NaN value matches any other NaN, and nothing else.
1872 * * An infinity matches another infinity of the same sign,
1875 * * If @f&TVFF_EQMASK@ is @TVFF_EXACT@, then any
1876 * representable number matches only itself: in particular,
1877 * positive and negative zero are considered distinct.
1878 * (This allows tests to check that they land on the correct
1879 * side of branch cuts, for example.)
1881 * * If @f&TVFF_EQMASK@ is @TVFF_ABSDELTA@, then %$x$% matches
1882 * %$y$% when %$|x - y| < \delta$%.
1884 * * If @f&TVFF_EQMASK@ is @TVFF_RELDELTA@, then %$x$% matches
1885 * %$y$% when %$|1 - x/y| < \delta$%. (Note that this
1886 * criterion is asymmetric. Write %$x \approx_\delta y$%
1887 * if and only if %$|1 - x/y < \delta$%. Then, for example,
1888 * if %$y/(1 + \delta) < x < y (1 - \delta)$%, then
1889 * %$x \approx_\delta y$%, but %$y \not\approx_\delta x$%.)
1892 int tvec_claimeqish_float(struct tvec_state
*tv
,
1893 double f0
, double f1
, unsigned f
, double delta
,
1894 const char *file
, unsigned lno
,
1897 struct tvec_floatinfo fi
;
1898 union tvec_misc arg
;
1900 fi
.f
= f
; fi
.min
= fi
.max
= 0.0; fi
.delta
= delta
; arg
.p
= &fi
;
1901 tv
->out
[0].v
.f
= f0
; tv
->in
[0].v
.f
= f1
;
1902 return (tvec_claimeq(tv
, &tvty_float
, &arg
, file
, lno
, expr
));
1905 /* --- @tvec_claimeq_float@ --- *
1907 * Arguments: @struct tvec_state *tv@ = test-vector state
1908 * @double f0, f1@ = two floating-point numbers
1909 * @const char *file@, @unsigned @lno@ = calling file and line
1910 * @const char *expr@ = the expression to quote on failure
1912 * Returns: Nonzero if @f0@ and @f1@ are identical, otherwise zero.
1914 * Use: Check that values of @f0@ and @f1@ are identical. The
1915 * function is exactly equivalent to @tvec_claimeqish_float@
1916 * with @f == TVFF_EXACT@.
1919 int tvec_claimeq_float(struct tvec_state
*tv
,
1920 double f0
, double f1
,
1921 const char *file
, unsigned lno
,
1924 return (tvec_claimeqish_float(tv
, f0
, f1
, TVFF_EXACT
, 0.0,
1928 /*----- Durations ---------------------------------------------------------*/
1930 /* A duration is a floating-point number of seconds. Initialization and
1931 * teardown, equality comparison, and serialization are as for floating-point
1935 static const struct duration_unit
{
1939 #define DUF_PREFER 1u
1940 } duration_units
[] = {
1952 { "yr", 31557600.0, DUF_PREFER
},
1953 { "y", 31557600.0, 0 },
1954 { "day", 86400.0, DUF_PREFER
},
1955 { "dy", 86400.0, 0 },
1956 { "d", 86400.0, 0 },
1957 { "hr", 3600.0, DUF_PREFER
},
1958 { "hour", 3600.0, 0 },
1960 { "min", 60.0, DUF_PREFER
},
1963 { "s", 1.0, DUF_PREFER
},
1968 { "ms", 1e-3, DUF_PREFER
},
1969 { "µs", 1e-6, DUF_PREFER
},
1970 { "ns", 1e-9, DUF_PREFER
},
1971 { "ps", 1e-12, DUF_PREFER
},
1972 { "fs", 1e-15, DUF_PREFER
},
1973 { "as", 1e-18, DUF_PREFER
},
1974 { "zs", 1e-21, DUF_PREFER
},
1975 { "ys", 1e-24, DUF_PREFER
},
1980 /* --- @tvec_parsedurunit@ --- *
1982 * Arguments: @double *scale_out@ = where to leave the scale
1983 * @const char **p_inout@ = input unit string, updated
1985 * Returns: Zero on success, %$-1$% on error.
1987 * Use: If @*p_inout@ begins with a unit string followed by the end
1988 * of the string or some non-alphanumeric character, then store
1989 * the corresponding scale factor in @*scale_out@, advance
1990 * @*p_inout@ past the unit string, and return zero. Otherwise,
1994 int tvec_parsedurunit(double *scale_out
, const char **p_inout
)
1996 const char *p
= *p_inout
, *q
;
1997 const struct duration_unit
*u
;
2000 while (ISSPACE(*p
)) p
++;
2001 for (q
= p
; *q
&& ISALNUM(*q
); q
++);
2002 n
= q
- p
; if (!n
) { *scale_out
= 1.0; return (0); }
2004 for (u
= duration_units
; u
->unit
; u
++)
2005 if (STRNCMP(p
, ==, u
->unit
, n
) && !u
->unit
[n
])
2006 { *scale_out
= u
->scale
; *p_inout
= q
; return (0); }
2010 /* --- @parse_duration@ --- *
2012 * Arguments: @union tvec_regval *rv@ = register value
2013 * @const struct tvec_regdef *rd@ = register definition
2014 * @struct tvec_state *tv@ = test-vector state
2016 * Returns: Zero on success, %$-1$% on error.
2018 * Use: Parse a register value from an input file.
2020 * Duration values are finite nonnegative floating-point
2021 * numbers in @strtod@ syntax, optionally followed by a unit .
2024 static int parse_duration(union tvec_regval
*rv
,
2025 const struct tvec_regdef
*rd
,
2026 struct tvec_state
*tv
)
2028 const struct duration_unit
*u
;
2034 if (tvec_readword(tv
, &d
, 0, ";", "duration")) { rc
= -1; goto end
; }
2035 if (parse_floating(&t
, &q
, d
.buf
,
2036 rd
->arg
.p ? rd
->arg
.p
: &tvflt_nonneg
, tv
))
2037 { rc
= -1; goto end
; }
2039 if (!*q
) tvec_readword(tv
, &d
, &q
, ";", 0);
2041 for (u
= duration_units
; u
->unit
; u
++)
2042 if (STRCMP(q
, ==, u
->unit
)) { t
*= u
->scale
; goto found_unit
; }
2043 rc
= tvec_syntax(tv
, *q
, "end-of-line"); goto end
;
2047 if (tvec_flushtoeol(tv
, 0)) { rc
= -1; goto end
; }
2054 /* --- @dump_duration@ --- *
2056 * Arguments: @const union tvec_regval *rv@ = register value
2057 * @const struct tvec_regdef *rd@ = register definition
2058 * @unsigned style@ = output style (@TVSF_...@)
2059 * @const struct gprintf_ops *gops@, @void *gp@ = format output
2063 * Use: Dump a register value to the format output.
2065 * Durations are dumped as a human-palatable scaled value with
2066 * unit, and, if compact style is not requested, as a raw number
2067 * of seconds at full precision as a comment.
2070 static void dump_duration(const union tvec_regval
*rv
,
2071 const struct tvec_regdef
*rd
,
2073 const struct gprintf_ops
*gops
, void *go
)
2075 const struct duration_unit
*u
;
2078 if (style
&TVSF_RAW
) {
2079 gprintf(gops
, go
, "duration:");
2080 format_floating(gops
, go
, rv
->f
);
2081 gprintf(gops
, go
, "s");
2085 for (u
= duration_units
; u
->scale
> t
&& u
[1].unit
; u
++);
2088 gprintf(gops
, go
, "%.4g %s", t
, u ? u
->unit
: "s");
2090 if (!(style
&TVSF_COMPACT
)) {
2091 gprintf(gops
, go
, "; = ");
2092 format_floating(gops
, go
, rv
->f
);
2093 gprintf(gops
, go
, " s");
2098 /* Duration type definition. */
2099 const struct tvec_regty tvty_duration
= {
2100 init_float
, trivial_release
, eq_float
,
2101 tobuf_float
, frombuf_float
,
2102 parse_duration
, dump_duration
2105 /* --- @tvec_claimeqish_duration@ --- *
2107 * Arguments: @struct tvec_state *tv@ = test-vector state
2108 * @double to, t1@ = two durations
2109 * @unsigned f@ = flags (@TVFF_...@)
2110 * @double delta@ = maximum tolerable difference
2111 * @const char *file@, @unsigned @lno@ = calling file and line
2112 * @const char *expr@ = the expression to quote on failure
2114 * Returns: Nonzero if @t0@ and @t1@ are sufficiently close, otherwise
2117 * Use: Check that values of @t0@ and @t1@ are sufficiently close.
2118 * This is essentially the same as @tvec_claimeqish_float@, only
2119 * it dumps the values as durations on a mismatch.
2122 int tvec_claimeqish_duration(struct tvec_state
*tv
,
2123 double t0
, double t1
, unsigned f
, double delta
,
2124 const char *file
, unsigned lno
,
2127 struct tvec_floatinfo fi
;
2128 union tvec_misc arg
;
2130 fi
.f
= f
; fi
.min
= fi
.max
= 0.0; fi
.delta
= delta
; arg
.p
= &fi
;
2131 tv
->out
[0].v
.f
= t0
; tv
->in
[0].v
.f
= t1
;
2132 return (tvec_claimeq(tv
, &tvty_duration
, &arg
, file
, lno
, expr
));
2135 /* --- @tvec_claimeq_duration@ --- *
2137 * Arguments: @struct tvec_state *tv@ = test-vector state
2138 * @double t0, t1@ = two durations
2139 * @const char *file@, @unsigned @lno@ = calling file and line
2140 * @const char *expr@ = the expression to quote on failure
2142 * Returns: Nonzero if @t0@ and @t1@ are identical, otherwise zero.
2144 * Use: Check that values of @t0@ and @t1@ are identical. The
2145 * function is exactly equivalent to @tvec_claimeqish_duration@
2146 * with @f == TVFF_EXACT@.
2149 int tvec_claimeq_duration(struct tvec_state
*tv
,
2150 double t0
, double t1
,
2151 const char *file
, unsigned lno
,
2154 return (tvec_claimeqish_duration(tv
, t0
, t1
, TVFF_EXACT
, 0.0,
2158 /*----- Enumerations ------------------------------------------------------*/
2160 /* --- @init_tenum@ --- *
2162 * Arguments: @union tvec_regval *rv@ = register value
2163 * @const struct tvec_regdef *rd@ = register definition
2167 * Use: Initialize a register value.
2169 * Integer and floating-point enumeration values are initialized
2170 * as their underlying representations. Pointer enumerations
2171 * are initialized to %|#nil|%.
2174 #define init_ienum init_int
2175 #define init_uenum init_uint
2176 #define init_fenum init_float
2178 static void init_penum(union tvec_regval
*rv
, const struct tvec_regdef
*rd
)
2181 /* --- @eq_tenum@ --- *
2183 * Arguments: @const union tvec_regval *rv0, *rv1@ = register values
2184 * @const struct tvec_regdef *rd@ = register definition
2186 * Returns: Nonzero if the values are equal, zero if unequal
2188 * Use: Compare register values for equality.
2190 * Integer and floating-point enumeration values are compared as
2191 * their underlying representations; in particular, floating-
2192 * point enumerations may compare equal if their absolute or
2193 * relative difference is sufficiently small. Pointer
2194 * enumerations are compared as pointers.
2197 #define eq_ienum eq_int
2198 #define eq_uenum eq_uint
2200 static int eq_fenum(const union tvec_regval
*rv0
,
2201 const union tvec_regval
*rv1
,
2202 const struct tvec_regdef
*rd
)
2204 const struct tvec_fenuminfo
*ei
= rd
->arg
.p
;
2205 return (eqish_floating_p(rv0
->f
, rv1
->f
, ei
->fi
));
2208 static int eq_penum(const union tvec_regval
*rv0
,
2209 const union tvec_regval
*rv1
,
2210 const struct tvec_regdef
*rd
)
2211 { return (rv0
->p
== rv1
->p
); }
2213 /* --- @tobuf_tenum@ --- *
2215 * Arguments: @buf *b@ = buffer
2216 * @const union tvec_regval *rv@ = register value
2217 * @const struct tvec_regdef *rd@ = register definition
2219 * Returns: Zero on success, %$-1$% on failure.
2221 * Use: Serialize a register value to a buffer.
2223 * Integer and floating-point enumeration values are serialized
2224 * as their underlying representations. Pointer enumerations
2225 * are serialized as the signed integer index into the
2226 * association table; %|#nil|% serializes as %$-1$%, and
2227 * unrecognized pointers cause failure.
2230 #define tobuf_ienum tobuf_int
2231 #define tobuf_uenum tobuf_uint
2232 #define tobuf_fenum tobuf_float
2234 static int tobuf_penum(buf
*b
, const union tvec_regval
*rv
,
2235 const struct tvec_regdef
*rd
)
2237 const struct tvec_penuminfo
*pei
= rd
->arg
.p
;
2238 const struct tvec_passoc
*pa
;
2241 for (pa
= pei
->av
, i
= 0; pa
->tag
; pa
++, i
++)
2242 if (pa
->p
== rv
->p
) goto found
;
2246 return (signed_to_buf(b
, i
));
2249 /* --- @frombuf_tenum@ --- *
2251 * Arguments: @buf *b@ = buffer
2252 * @union tvec_regval *rv@ = register value
2253 * @const struct tvec_regdef *rd@ = register definition
2255 * Returns: Zero on success, %$-1$% on failure.
2257 * Use: Deserialize a register value from a buffer.
2259 * Integer and floating-point enumeration values are serialized
2260 * as their underlying representations. Pointer enumerations
2261 * are serialized as the signed integer index into the
2262 * association table; %|#nil|% serializes as %$-1$%; out-of-
2263 * range indices cause failure.
2266 #define frombuf_ienum frombuf_int
2267 #define frombuf_uenum frombuf_uint
2268 #define frombuf_fenum frombuf_float
2269 static int frombuf_penum(buf
*b
, union tvec_regval
*rv
,
2270 const struct tvec_regdef
*rd
)
2272 const struct tvec_penuminfo
*pei
= rd
->arg
.p
;
2273 const struct tvec_passoc
*pa
;
2276 for (pa
= pei
->av
, n
= 0; pa
->tag
; pa
++, n
++);
2277 if (signed_from_buf(b
, &i
)) return (-1);
2278 if (0 <= i
&& i
< n
) rv
->p
= (/*unconst*/ void *)pei
->av
[i
].p
;
2279 else if (i
== -1) rv
->p
= 0;
2280 else { buf_break(b
); return (-1); }
2284 /* --- @parse_tenum@ --- *
2286 * Arguments: @union tvec_regval *rv@ = register value
2287 * @const struct tvec_regdef *rd@ = register definition
2288 * @struct tvec_state *tv@ = test-vector state
2290 * Returns: Zero on success, %$-1$% on error.
2292 * Use: Parse a register value from an input file.
2294 * An enumerated value may be given by name or as a literal
2295 * value. For enumerations based on numeric types, the literal
2296 * values can be written in the same syntax as the underlying
2297 * values. For enumerations based on pointers, the only
2298 * permitted literal is %|#nil|%, which denotes a null pointer.
2301 #define DEFPARSE_ENUM(tag_, ty, slot) \
2302 static int parse_##slot##enum(union tvec_regval *rv, \
2303 const struct tvec_regdef *rd, \
2304 struct tvec_state *tv) \
2306 const struct tvec_##slot##enuminfo *ei = rd->arg.p; \
2307 const struct tvec_##slot##assoc *a; \
2308 dstr d = DSTR_INIT; \
2311 if (tvec_readword(tv, &d, 0, \
2312 ";", "%s tag or " LITSTR_##tag_, ei->name)) \
2313 { rc = -1; goto end; } \
2314 for (a = ei->av; a->tag; a++) \
2315 if (STRCMP(a->tag, ==, d.buf)) { FOUND_##tag_ goto done; } \
2318 if (tvec_flushtoeol(tv, 0)) { rc = -1; goto end; } \
2325 #define LITSTR_INT "literal signed integer"
2326 #define FOUND_INT rv->i = a->i;
2327 #define MISSING_INT if (parse_signed(&rv->i, d.buf, ei->ir, tv)) \
2328 { rc = -1; goto end; }
2330 #define LITSTR_UINT "literal unsigned integer"
2331 #define FOUND_UINT rv->u = a->u;
2332 #define MISSING_UINT if (parse_unsigned(&rv->u, d.buf, ei->ur, tv)) \
2333 { rc = -1; goto end; }
2335 #define LITSTR_FLT "literal floating-point number, " \
2336 "`#-inf', `#+inf', or `#nan'"
2337 #define FOUND_FLT rv->f = a->f;
2338 #define MISSING_FLT if (parse_floating(&rv->f, 0, d.buf, ei->fi, tv)) \
2339 { rc = -1; goto end; }
2341 #define LITSTR_PTR "`#nil'"
2342 #define FOUND_PTR rv->p = (/*unconst*/ void *)a->p;
2343 #define MISSING_PTR if (STRCMP(d.buf, ==, "#nil")) \
2346 tvec_error(tv, "unknown `%s' value `%s'", \
2348 rc = -1; goto end; \
2351 TVEC_MISCSLOTS(DEFPARSE_ENUM
)
2369 #undef DEFPARSE_ENUM
2371 /* --- @dump_tenum@ --- *
2373 * Arguments: @const union tvec_regval *rv@ = register value
2374 * @const struct tvec_regdef *rd@ = register definition
2375 * @unsigned style@ = output style (@TVSF_...@)
2376 * @const struct gprintf_ops *gops@, @void *gp@ = format output
2380 * Use: Dump a register value to the format output.
2382 * Enumeration values are dumped as their symbolic names, if
2383 * possible, with the underlying values provided as a comment
2384 * unless compact output is requested, as for the underlying
2385 * representation. A null pointer is printed as %|#nil|%;
2386 * non-null pointers are printed as %|#<TYPE PTR>|%, with the
2387 * enumeration TYPE and the raw pointer PTR printed with the
2388 * system's %|%p|% format specifier.
2392 #define DEFDUMP_ENUM(tag_, ty, slot) \
2393 static void dump_##slot##enum(const union tvec_regval *rv, \
2394 const struct tvec_regdef *rd, \
2396 const struct gprintf_ops *gops, void *go) \
2398 const struct tvec_##slot##enuminfo *ei = rd->arg.p; \
2399 const struct tvec_##slot##assoc *a; \
2401 if (style&TVSF_RAW) gprintf(gops, go, #slot "enum/%s:", ei->name); \
2402 for (a = ei->av; a->tag; a++) \
2403 if (rv->slot == a->slot) { \
2404 gprintf(gops, go, "%s", a->tag); \
2405 if (style&TVSF_COMPACT) return; \
2406 gprintf(gops, go, " ; = "); break; \
2412 #define MAYBE_PRINT_EXTRA \
2413 if (style&TVSF_COMPACT) /* nothing to do */; \
2414 else if (!a->tag) { gprintf(gops, go, " ; = "); goto _extra; } \
2415 else if (1) { gprintf(gops, go, " = "); goto _extra; } \
2418 #define PRINTRAW_INT gprintf(gops, go, "%ld", rv->i); \
2419 MAYBE_PRINT_EXTRA { \
2420 format_signed_hex(gops, go, rv->i); \
2421 maybe_format_signed_char(gops, go, rv->i); \
2424 #define PRINTRAW_UINT gprintf(gops, go, "%lu", rv->u); \
2425 MAYBE_PRINT_EXTRA { \
2426 format_unsigned_hex(gops, go, rv->u); \
2427 maybe_format_unsigned_char(gops, go, rv->u); \
2430 #define PRINTRAW_FLT format_floating(gops, go, rv->f);
2432 #define PRINTRAW_PTR if (!rv->p) gprintf(gops, go, "#nil"); \
2433 else gprintf(gops, go, "#<%s %p>", ei->name, rv->p);
2435 TVEC_MISCSLOTS(DEFDUMP_ENUM
)
2438 #undef PRINTRAW_UINT
2442 #undef MAYBE_PRINT_EXTRA
2445 /* Enumeration type definitions. */
2446 #define DEFTY_ENUM(tag, ty, slot) \
2447 const struct tvec_regty tvty_##slot##enum = { \
2448 init_##slot##enum, trivial_release, eq_##slot##enum, \
2449 tobuf_##slot##enum, frombuf_##slot##enum, \
2450 parse_##slot##enum, dump_##slot##enum \
2452 TVEC_MISCSLOTS(DEFTY_ENUM
)
2455 /* Predefined enumeration types. */
2456 static const struct tvec_iassoc bool_assoc
[] = {
2473 const struct tvec_ienuminfo tvenum_bool
=
2474 { "bool", bool_assoc
, &tvrange_int
};
2476 static const struct tvec_iassoc cmp_assoc
[] = {
2492 const struct tvec_ienuminfo tvenum_cmp
=
2493 { "cmp", cmp_assoc
, &tvrange_int
};
2495 /* --- @tvec_claimeq_tenum@ --- *
2497 * Arguments: @struct tvec_state *tv@ = test-vector state
2498 * @const struct tvec_typeenuminfo *ei@ = enumeration type info
2499 * @ty t0, t1@ = two values
2500 * @const char *file@, @unsigned @lno@ = calling file and line
2501 * @const char *expr@ = the expression to quote on failure
2503 * Returns: Nonzero if @t0@ and @t1@ are equal, otherwise zero.
2505 * Use: Check that values of @t0@ and @t1@ are equal. As for
2506 * @tvec_claim@ above, a test case is automatically begun and
2507 * ended if none is already underway. If the values are
2508 * unequal, then @tvec_fail@ is called, quoting @expr@, and the
2509 * mismatched values are dumped: @t0@ is printed as the output
2510 * value and @t1@ is printed as the input reference.
2513 #define DEFCLAIM(tag, ty, slot) \
2514 int tvec_claimeq_##slot##enum \
2515 (struct tvec_state *tv, \
2516 const struct tvec_##slot##enuminfo *ei, ty e0, ty e1, \
2517 const char *file, unsigned lno, const char *expr) \
2519 union tvec_misc arg; \
2522 tv->out[0].v.slot = GET_##tag(e0); \
2523 tv->in[0].v.slot = GET_##tag(e1); \
2524 return (tvec_claimeq(tv, &tvty_##slot##enum, &arg, \
2525 file, lno, expr)); \
2527 #define GET_INT(e) (e)
2528 #define GET_UINT(e) (e)
2529 #define GET_FLT(e) (e)
2530 #define GET_PTR(e) ((/*unconst*/ void *)(e))
2531 TVEC_MISCSLOTS(DEFCLAIM
)
2538 /*----- Flag types --------------------------------------------------------*/
2540 /* Flag types are initialized, compared, and serialized as unsigned
2544 /* --- @parse_flags@ --- *
2546 * Arguments: @union tvec_regval *rv@ = register value
2547 * @const struct tvec_regdef *rd@ = register definition
2548 * @struct tvec_state *tv@ = test-vector state
2550 * Returns: Zero on success, %$-1$% on error.
2552 * Use: Parse a register value from an input file.
2554 * The input syntax is a sequence of items separated by `|'
2555 * signs. Each item may be the symbolic name of a field value,
2556 * or a literal unsigned integer. The masks associated with the
2557 * given symbolic names must be disjoint. The resulting
2558 * numerical value is simply the bitwise OR of the given values.
2561 static int parse_flags(union tvec_regval
*rv
, const struct tvec_regdef
*rd
,
2562 struct tvec_state
*tv
)
2564 const struct tvec_flaginfo
*fi
= rd
->arg
.p
;
2565 const struct tvec_flag
*f
;
2566 unsigned long m
= 0, v
= 0, t
;
2572 /* Read the next item. */
2574 if (tvec_readword(tv
, &d
, 0, "|;", "%s flag name or integer", fi
->name
))
2575 { rc
= -1; goto end
; }
2577 /* Try to find a matching entry in the table. */
2578 for (f
= fi
->fv
; f
->tag
; f
++)
2579 if (STRCMP(f
->tag
, ==, d
.buf
)) {
2581 { tvec_error(tv
, "colliding flag setting"); rc
= -1; goto end
; }
2583 { m
|= f
->m
; v
|= f
->v
; goto next
; }
2586 /* Otherwise, try to parse it as a raw integer. */
2587 if (parse_unsigned(&t
, d
.buf
, fi
->range
, tv
))
2588 { rc
= -1; goto end
; }
2592 /* Advance to the next token. If it's a separator then consume it, and
2593 * go round again. Otherwise we stop here.
2595 if (tvec_nexttoken(tv
)) break;
2597 if (ch
!= '|') { tvec_syntax(tv
, ch
, "`|'"); rc
= -1; goto end
; }
2598 if (tvec_nexttoken(tv
)) {
2599 tvec_syntax(tv
, '\n', "%s flag name or integer", fi
->name
);
2611 /* --- @dump_flags@ --- *
2613 * Arguments: @const union tvec_regval *rv@ = register value
2614 * @const struct tvec_regdef *rd@ = register definition
2615 * @unsigned style@ = output style (@TVSF_...@)
2616 * @const struct gprintf_ops *gops@, @void *gp@ = format output
2620 * Use: Dump a register value to the format output.
2622 * The table of symbolic names and their associated values and
2623 * masks is repeatedly scanned, in order, to find disjoint
2624 * matches -- i.e., entries whose value matches the target value
2625 * in the bit positions indicated by the mask, and whose mask
2626 * doesn't overlap with any previously found matches; the names
2627 * are then output, separated by `|'. Any remaining nonzero
2628 * bits not covered by any of the matching masks are output as a
2629 * single literal integer, in hex.
2631 * Unless compact output is requested, or no symbolic names were
2632 * found, the raw numeric value is also printed in hex, as a
2636 static void dump_flags(const union tvec_regval
*rv
,
2637 const struct tvec_regdef
*rd
,
2639 const struct gprintf_ops
*gops
, void *go
)
2641 const struct tvec_flaginfo
*fi
= rd
->arg
.p
;
2642 const struct tvec_flag
*f
;
2643 unsigned long m
= ~0ul, v
= rv
->u
;
2646 if (style
&TVSF_RAW
) gprintf(gops
, go
, "flags/%s:", fi
->name
);
2648 for (f
= fi
->fv
, sep
= ""; f
->tag
; f
++)
2649 if ((m
&f
->m
) && (v
&f
->m
) == f
->v
) {
2650 gprintf(gops
, go
, "%s%s", sep
, f
->tag
); m
&= ~f
->m
;
2651 sep
= style
&TVSF_COMPACT ?
"|" : " | ";
2654 if (v
&m
) gprintf(gops
, go
, "%s0x%0*lx", sep
, hex_width(v
), v
&m
);
2656 if (m
!= ~0ul && !(style
&(TVSF_COMPACT
| TVSF_RAW
)))
2657 gprintf(gops
, go
, " ; = 0x%0*lx", hex_width(rv
->u
), rv
->u
);
2660 /* Flags type definition. */
2661 const struct tvec_regty tvty_flags
= {
2662 init_uint
, trivial_release
, eq_uint
,
2663 tobuf_uint
, frombuf_uint
,
2664 parse_flags
, dump_flags
2667 /* --- @tvec_claimeq_flags@ --- *
2669 * Arguments: @struct tvec_state *tv@ = test-vector state
2670 * @const struct tvec_flaginfo *fi@ = flags type info
2671 * @unsigned long f0, f1@ = two values
2672 * @const char *file@, @unsigned @lno@ = calling file and line
2673 * @const char *expr@ = the expression to quote on failure
2675 * Returns: Nonzero if @f0@ and @f1@ are equal, otherwise zero.
2677 * Use: Check that values of @f0@ and @f1@ are equal. As for
2678 * @tvec_claim@ above, a test case is automatically begun and
2679 * ended if none is already underway. If the values are
2680 * unequal, then @tvec_fail@ is called, quoting @expr@, and the
2681 * mismatched values are dumped: @f0@ is printed as the output
2682 * value and @f1@ is printed as the input reference.
2685 int tvec_claimeq_flags(struct tvec_state
*tv
,
2686 const struct tvec_flaginfo
*fi
,
2687 unsigned long f0
, unsigned long f1
,
2688 const char *file
, unsigned lno
, const char *expr
)
2690 union tvec_misc arg
;
2692 arg
.p
= fi
; tv
->out
[0].v
.u
= f0
; tv
->in
[0].v
.u
= f1
;
2693 return (tvec_claimeq(tv
, &tvty_flags
, &arg
, file
, lno
, expr
));
2696 /*----- Characters --------------------------------------------------------*/
2698 /* Character values are initialized and compared as signed integers. */
2700 /* --- @tobuf_char@ --- *
2702 * Arguments: @buf *b@ = buffer
2703 * @const union tvec_regval *rv@ = register value
2704 * @const struct tvec_regdef *rd@ = register definition
2706 * Returns: Zero on success, %$-1$% on failure.
2708 * Use: Serialize a register value to a buffer.
2710 * Character values are serialized as little-endian 32-bit
2711 * unsigned integers, with %|EOF|% serialized as all-bits-set.
2714 static int tobuf_char(buf
*b
, const union tvec_regval
*rv
,
2715 const struct tvec_regdef
*rd
)
2719 if (0 <= rv
->i
&& rv
->i
<= UCHAR_MAX
) u
= rv
->i
;
2720 else if (rv
->i
== EOF
) u
= MASK32
;
2721 else { buf_break(b
); return (-1); }
2722 return (buf_putu32l(b
, u
));
2725 /* --- @frombuf_char@ --- *
2727 * Arguments: @buf *b@ = buffer
2728 * @union tvec_regval *rv@ = register value
2729 * @const struct tvec_regdef *rd@ = register definition
2731 * Returns: Zero on success, %$-1$% on failure.
2733 * Use: Deserialize a register value from a buffer.
2735 * Character values are serialized as little-endian 32-bit
2736 * unsigned integers, with %|EOF|% serialized as all-bits-set.
2739 static int frombuf_char(buf
*b
, union tvec_regval
*rv
,
2740 const struct tvec_regdef
*rd
)
2744 if (buf_getu32l(b
, &u
)) return (-1);
2745 if (0 <= u
&& u
<= UCHAR_MAX
) rv
->i
= u
;
2746 else if (u
== MASK32
) rv
->i
= EOF
;
2747 else { buf_break(b
); return (-1); }
2751 /* --- @parse_char@ --- *
2753 * Arguments: @union tvec_regval *rv@ = register value
2754 * @const struct tvec_regdef *rd@ = register definition
2755 * @struct tvec_state *tv@ = test-vector state
2757 * Returns: Zero on success, %$-1$% on error.
2759 * Use: Parse a register value from an input file.
2761 * A character value can be given by symbolic name, with a
2762 * leading `%|#|%'; or a character or `%|\|%'-escape sequence,
2763 * optionally in single quotes.
2765 * The following escape sequences and character names are
2768 * * `%|#eof|%' is the special end-of-file marker.
2770 * * `%|#nul|%' is the NUL character, sometimes used to
2771 * terminate strings.
2773 * * `%|bell|%', `%|bel|%', `%|ding|%', or `%|\a|%' is the BEL
2774 * character used to ring the terminal bell (or do some other
2775 * thing to attract the user's attention).
2777 * * %|#backspace|%, %|#bs|%, or %|\b|% is the backspace
2778 * character, used to move the cursor backwords by one cell.
2780 * * %|#escape|% %|#esc|%, or%|\e|% is the escape character,
2781 * used to introduce special terminal commands.
2783 * * %|#formfeed|%, %|#ff|%, or %|\f|% is the formfeed
2784 * character, used to separate pages of text.
2786 * * %|#newline|%, %|#linefeed|%, %|#lf|%, %|#nl|%, or %|\n|% is
2787 * the newline character, used to terminate lines of text or
2788 * advance the cursor to the next line (perhaps without
2789 * returning it to the start of the line).
2791 * * %|#return|%, %|#carriage-return|%, %|#cr|%, or %|\r|% is
2792 * the carriage-return character, used to return the cursor to
2793 * the start of the line.
2795 * * %|#tab|%, %|#horizontal-tab|%, %|#ht|%, or %|\t|% is the
2796 * tab character, used to advance the cursor to the next tab
2797 * stop on the current line.
2799 * * %|#vertical-tab|%, %|#vt|%, %|\v|% is the vertical tab
2802 * * %|#space|%, %|#spc|% is the space character.
2804 * * %|#delete|%, %|#del|% is the delete character, used to
2805 * erase the most recent character.
2807 * * %|\'|% is the single-quote character.
2809 * * %|\\|% is the backslash character.
2811 * * %|\"|% is the double-quote character.
2813 * * %|\NNN|% or %|\{NNN}|% is the character with code NNN in
2814 * octal. The NNN may be up to three digits long.
2816 * * %|\xNN|% or %|\x{NN}|% is the character with code NNN in
2820 static int parse_char(union tvec_regval
*rv
, const struct tvec_regdef
*rd
,
2821 struct tvec_state
*tv
)
2828 /* Inspect the character to see what we're up against. */
2832 /* It looks like a special token. Push the `%|#|%' back and fetch the
2833 * whole word. If there's just the `%|#|%' after all, then treat it as
2838 if (tvec_readword(tv
, &d
, 0, ";", "character name"))
2839 { rc
= -1; goto end
; }
2840 if (STRCMP(d
.buf
, !=, "#")) {
2841 if (read_charname(&ch
, d
.buf
, RCF_EOFOK
)) {
2842 rc
= tvec_error(tv
, "unknown character name `%s'", d
.buf
);
2845 if (tvec_flushtoeol(tv
, 0)) { rc
= -1; goto end
; }
2846 rv
->i
= ch
; rc
= 0; goto end
;
2850 /* If this is a single quote then we expect to see a matching one later,
2851 * and we should process backslash escapes. Get the next character and see
2854 if (ch
== '\'') { f
|= f_quote
; ch
= getc(tv
->fp
); }
2856 /* Main character dispatch. */
2860 /* Unquoted, semicolon begins a comment. */
2861 if (!(f
&f_quote
)) { rc
= tvec_syntax(tv
, ch
, "character"); goto end
; }
2865 /* A newline. If we saw a single quote, then treat that as literal.
2866 * Otherwise this is an error.
2868 if (!(f
&f_quote
)) goto nochar
;
2869 else { f
&= ~f_quote
; ungetc(ch
, tv
->fp
); ch
= '\''; goto plain
; }
2872 /* End-of-file. Similar to newline, but with slightly different
2873 * effects on the parse state.
2875 if (!(f
&f_quote
)) goto nochar
;
2876 else { f
&= ~f_quote
; ch
= '\''; goto plain
; }
2879 /* A single quote. This must be the second of a pair, and there should
2880 * have been a character or escape sequence between them.
2882 rc
= tvec_syntax(tv
, ch
, "character"); goto end
;
2885 /* A backslash. Read a character escape. */
2886 if (read_charesc(&ch
, tv
)) return (-1);
2889 /* Anything else. Treat as literal. */
2893 /* If we saw an opening quote, then expect the closing quote. */
2896 if (ch
!= '\'') { rc
= tvec_syntax(tv
, ch
, "`''"); goto end
; }
2900 if (tvec_flushtoeol(tv
, 0)) { rc
= -1; goto end
; }
2909 /* --- @dump_char@ --- *
2911 * Arguments: @const union tvec_regval *rv@ = register value
2912 * @const struct tvec_regdef *rd@ = register definition
2913 * @unsigned style@ = output style (@TVSF_...@)
2914 * @const struct gprintf_ops *gops@, @void *gp@ = format output
2918 * Use: Dump a register value to the format output.
2920 * Character values are dumped as their symbolic names, if any,
2921 * or as a character or escape sequence within single quotes
2922 * (which may be omitted in compact style). If compact output
2923 * is not requested, then the single-quoted representation (for
2924 * characters dumped as symbolic names) and integer code in
2925 * decimal and hex are printed as a comment.
2928 static void dump_char(const union tvec_regval
*rv
,
2929 const struct tvec_regdef
*rd
,
2931 const struct gprintf_ops
*gops
, void *go
)
2937 if (style
&TVSF_RAW
) {
2938 /* Print the raw character unconditionally in single quotes. */
2940 gprintf(gops
, go
, "char:'");
2941 format_char(gops
, go
, rv
->i
);
2942 gprintf(gops
, go
, "'");
2944 /* Print ina pleasant human-readable way. */
2946 /* Print a character name if we can find one. */
2947 p
= find_charname(rv
->i
, (style
&TVSF_COMPACT
) ? CTF_SHORT
: CTF_PREFER
);
2949 gprintf(gops
, go
, "%s", p
);
2950 if (style
&TVSF_COMPACT
) return;
2951 else { gprintf(gops
, go
, " ;"); f
|= f_semi
; }
2954 /* If the character isn't @EOF@ then print it as a single-quoted thing.
2955 * In compact style, see if we can omit the quotes.
2958 if (f
&f_semi
) gprintf(gops
, go
, " = ");
2960 case ' ': case '\\': case '\'': quote
:
2961 format_char(gops
, go
, rv
->i
);
2964 if (!(style
&TVSF_COMPACT
) || !isprint(rv
->i
)) goto quote
;
2965 gprintf(gops
, go
, "%c", (int)rv
->i
);
2970 /* And the character code as an integer. */
2971 if (!(style
&TVSF_COMPACT
)) {
2972 if (!(f
&f_semi
)) gprintf(gops
, go
, " ;");
2973 gprintf(gops
, go
, " = %ld = ", rv
->i
);
2974 format_signed_hex(gops
, go
, rv
->i
);
2981 /* Character type definition. */
2982 const struct tvec_regty tvty_char
= {
2983 init_int
, trivial_release
, eq_int
,
2984 tobuf_char
, frombuf_char
,
2985 parse_char
, dump_char
2988 /* --- @tvec_claimeq_char@ --- *
2990 * Arguments: @struct tvec_state *tv@ = test-vector state
2991 * @int ch0, ch1@ = two character codes
2992 * @const char *file@, @unsigned @lno@ = calling file and line
2993 * @const char *expr@ = the expression to quote on failure
2995 * Returns: Nonzero if @ch0@ and @ch1@ are equal, otherwise zero.
2997 * Use: Check that values of @ch0@ and @ch1@ are equal. As for
2998 * @tvec_claim@ above, a test case is automatically begun and
2999 * ended if none is already underway. If the values are
3000 * unequal, then @tvec_fail@ is called, quoting @expr@, and the
3001 * mismatched values are dumped: @ch0@ is printed as the output
3002 * value and @ch1@ is printed as the input reference.
3005 int tvec_claimeq_char(struct tvec_state
*tv
, int c0
, int c1
,
3006 const char *file
, unsigned lno
, const char *expr
)
3008 tv
->out
[0].v
.i
= c0
; tv
->in
[0].v
.i
= c1
;
3009 return (tvec_claimeq(tv
, &tvty_char
, 0, file
, lno
, expr
));
3012 /*----- Text and byte strings ---------------------------------------------*/
3014 /* --- @init_text@, @init_bytes@ --- *
3016 * Arguments: @union tvec_regval *rv@ = register value
3017 * @const struct tvec_regdef *rd@ = register definition
3021 * Use: Initialize a register value.
3023 * Text and binary string values are initialized with a null
3024 * pointer and zero length.
3027 static void init_text(union tvec_regval
*rv
, const struct tvec_regdef
*rd
)
3028 { rv
->text
.p
= 0; rv
->text
.sz
= 0; }
3030 static void init_bytes(union tvec_regval
*rv
, const struct tvec_regdef
*rd
)
3031 { rv
->bytes
.p
= 0; rv
->bytes
.sz
= 0; }
3033 /* --- @release_string@, @release_bytes@ --- *
3035 * Arguments: @const union tvec_regval *rv@ = register value
3036 * @const struct tvec_regdef *rd@ = register definition
3040 * Use: Release resources held by a register value.
3042 * Text and binary string buffers are freed.
3045 static void release_text(union tvec_regval
*rv
,
3046 const struct tvec_regdef
*rd
)
3047 { xfree(rv
->text
.p
); }
3049 static void release_bytes(union tvec_regval
*rv
,
3050 const struct tvec_regdef
*rd
)
3051 { xfree(rv
->bytes
.p
); }
3053 /* --- @eq_text@, @eq_bytes@ --- *
3055 * Arguments: @const union tvec_regval *rv0, *rv1@ = register values
3056 * @const struct tvec_regdef *rd@ = register definition
3058 * Returns: Nonzero if the values are equal, zero if unequal
3060 * Use: Compare register values for equality.
3063 static int eq_text(const union tvec_regval
*rv0
,
3064 const union tvec_regval
*rv1
,
3065 const struct tvec_regdef
*rd
)
3067 return (rv0
->text
.sz
== rv1
->text
.sz
&&
3069 MEMCMP(rv0
->text
.p
, ==, rv1
->text
.p
, rv1
->text
.sz
)));
3072 static int eq_bytes(const union tvec_regval
*rv0
,
3073 const union tvec_regval
*rv1
,
3074 const struct tvec_regdef
*rd
)
3076 return (rv0
->bytes
.sz
== rv1
->bytes
.sz
&&
3078 MEMCMP(rv0
->bytes
.p
, ==, rv1
->bytes
.p
, rv1
->bytes
.sz
)));
3081 /* --- @tobuf_text@, @tobuf_bytes@ --- *
3083 * Arguments: @buf *b@ = buffer
3084 * @const union tvec_regval *rv@ = register value
3085 * @const struct tvec_regdef *rd@ = register definition
3087 * Returns: Zero on success, %$-1$% on failure.
3089 * Use: Serialize a register value to a buffer.
3091 * Text and binary string values are serialized as a little-
3092 * endian 64-bit length %$n$% in bytes followed by %$n$% bytes
3096 static int tobuf_text(buf
*b
, const union tvec_regval
*rv
,
3097 const struct tvec_regdef
*rd
)
3098 { return (buf_putmem64l(b
, rv
->text
.p
, rv
->text
.sz
)); }
3100 static int tobuf_bytes(buf
*b
, const union tvec_regval
*rv
,
3101 const struct tvec_regdef
*rd
)
3102 { return (buf_putmem64l(b
, rv
->bytes
.p
, rv
->bytes
.sz
)); }
3104 /* --- @frombuf_text@, @frombuf_bytes@ --- *
3106 * Arguments: @buf *b@ = buffer
3107 * @union tvec_regval *rv@ = register value
3108 * @const struct tvec_regdef *rd@ = register definition
3110 * Returns: Zero on success, %$-1$% on failure.
3112 * Use: Deserialize a register value from a buffer.
3114 * Text and binary string values are serialized as a little-
3115 * endian 64-bit length %$n$% in bytes followed by %$n$% bytes
3119 static int frombuf_text(buf
*b
, union tvec_regval
*rv
,
3120 const struct tvec_regdef
*rd
)
3125 p
= buf_getmem64l(b
, &sz
); if (!p
) return (-1);
3126 tvec_alloctext(rv
, sz
); memcpy(rv
->text
.p
, p
, sz
); rv
->text
.p
[sz
] = 0;
3130 static int frombuf_bytes(buf
*b
, union tvec_regval
*rv
,
3131 const struct tvec_regdef
*rd
)
3136 p
= buf_getmem64l(b
, &sz
); if (!p
) return (-1);
3137 tvec_allocbytes(rv
, sz
); memcpy(rv
->bytes
.p
, p
, sz
);
3141 /* --- @check_string_length@ --- *
3143 * Arguments: @size_t sz@ = found string length
3144 * @const struct tvec_urange *ur@ = acceptable range
3145 * @struct tvec_state *tv@ = test-vector state
3147 * Returns: Zero on success, %$-1$% on error.
3149 * Use: Checks that @sz@ is within the bounds described by @ur@,
3150 * reporting an error if not.
3153 static int check_string_length(size_t sz
, const struct tvec_urange
*ur
,
3154 struct tvec_state
*tv
)
3156 if (ur
&& (ur
->min
> sz
|| sz
> ur
->max
))
3157 return (tvec_error(tv
,
3158 "invalid string length %lu; must be in [%lu .. %lu]",
3159 (unsigned long)sz
, ur
->min
, ur
->max
));
3163 /* --- @parse_text@, @parse_bytes@ --- *
3165 * Arguments: @union tvec_regval *rv@ = register value
3166 * @const struct tvec_regdef *rd@ = register definition
3167 * @struct tvec_state *tv@ = test-vector state
3169 * Returns: Zero on success, %$-1$% on error.
3171 * Use: Parse a register value from an input file.
3173 * The input format for both kinds of strings is basically the
3174 * same: a `compound string', consisting of
3176 * * single-quoted strings, which are interpreted entirely
3177 * literally, but can't contain single quotes or newlines;
3179 * * double-quoted strings, in which `%|\|%'-escapes are
3180 * interpreted as for characters;
3182 * * character names, marked by an initial `%|#|%' sign;
3184 * * special tokens marked by an initial `%|!|%' sign; or
3186 * * barewords interpreted according to the current coding
3189 * The special tokens are
3191 * * `%|!bare|%', which causes subsequent sequences of
3192 * barewords to be treated as plain text;
3194 * * `%|!hex|%', `%|!base32|%', `%|!base64|%', which cause
3195 * subsequent barewords to be decoded in the requested
3198 * * `%|!repeat|% %$n$% %|{|% %%\textit{string}%% %|}|%',
3199 * which includes %$n$% copies of the (compound) string.
3201 * The only difference between text and binary strings is that
3202 * the initial coding scheme is %|bare|% for text strings and
3203 * %|hex|% for binary strings.
3206 static int parse_text(union tvec_regval
*rv
, const struct tvec_regdef
*rd
,
3207 struct tvec_state
*tv
)
3209 void *p
= rv
->text
.p
;
3211 if (read_compound_string(&p
, &rv
->text
.sz
, TVCODE_BARE
, 0, tv
))
3214 if (check_string_length(rv
->text
.sz
, rd
->arg
.p
, tv
)) return (-1);
3218 static int parse_bytes(union tvec_regval
*rv
, const struct tvec_regdef
*rd
,
3219 struct tvec_state
*tv
)
3221 void *p
= rv
->bytes
.p
;
3223 if (read_compound_string(&p
, &rv
->bytes
.sz
, TVCODE_HEX
, 0, tv
))
3226 if (check_string_length(rv
->bytes
.sz
, rd
->arg
.p
, tv
)) return (-1);
3230 /* --- @dump_text@, @dump_bytes@ --- *
3232 * Arguments: @const union tvec_regval *rv@ = register value
3233 * @const struct tvec_regdef *rd@ = register definition
3234 * @unsigned style@ = output style (@TVSF_...@)
3235 * @const struct gprintf_ops *gops@, @void *gp@ = format output
3239 * Use: Dump a register value to the format output.
3241 * Text string values are dumped as plain text, in double quotes
3242 * if necessary, and using backslash escape sequences for
3243 * nonprintable characters. Unless compact output is requested,
3244 * strings consisting of multiple lines are dumped with each
3245 * line of the string on a separate output line.
3247 * Binary string values are dumped in hexadecimal. In compact
3248 * style, the output simply consists of a single block of hex
3249 * digits. Otherwise, the dump is a display consisting of
3250 * groups of hex digits, with comments showing the offset (if
3251 * the string is long enough) and the corresponding plain text.
3253 * Empty strings are dumped as %|#empty|%.
3256 static void dump_empty(const char *ty
, unsigned style
,
3257 const struct gprintf_ops
*gops
, void *go
)
3259 if (style
&TVSF_RAW
) gprintf(gops
, go
, "%s:", ty
);
3260 if (!(style
&TVSF_COMPACT
)) gprintf(gops
, go
, "#empty");
3261 if (!(style
&(TVSF_COMPACT
| TVSF_RAW
))) gprintf(gops
, go
, " ; = ");
3262 if (!(style
&TVSF_RAW
)) gprintf(gops
, go
, "\"\"");
3266 static void dump_text(const union tvec_regval
*rv
,
3267 const struct tvec_regdef
*rd
,
3269 const struct gprintf_ops
*gops
, void *go
)
3271 const unsigned char *p
, *q
, *l
;
3273 #define f_nonword 1u
3274 #define f_newline 2u
3276 if (!rv
->text
.sz
) { dump_empty("text", style
, gops
, go
); return; }
3278 p
= (const unsigned char *)rv
->text
.p
; l
= p
+ rv
->text
.sz
;
3279 if (style
&TVSF_RAW
) { gprintf(gops
, go
, "text:"); goto quote
; }
3280 else if (style
&TVSF_COMPACT
) goto quote
;
3283 case '!': case '#': case ';': case '"': case '\'':
3284 case '(': case '{': case '[': case ']': case '}': case ')':
3285 f
|= f_nonword
; break;
3287 for (q
= p
; q
< l
; q
++)
3288 if (*q
== '\n' && q
!= l
- 1) f
|= f_newline
;
3289 else if (!*q
|| !ISGRAPH(*q
) || *q
== '\\') f
|= f_nonword
;
3290 if (f
&f_newline
) { gprintf(gops
, go
, "\n\t"); goto quote
; }
3291 else if (f
&f_nonword
) goto quote
;
3293 gops
->putm(go
, (const char *)p
, rv
->text
.sz
);
3297 gprintf(gops
, go
, "\"");
3298 for (q
= p
; q
< l
; q
++)
3299 if (!ISPRINT(*q
) || *q
== '"') {
3300 if (p
< q
) gops
->putm(go
, (const char *)p
, q
- p
);
3301 if (*q
!= '\n' || (style
&TVSF_COMPACT
))
3302 format_charesc(gops
, go
, *q
, FCF_BRACE
);
3304 if (q
+ 1 == l
) { gprintf(gops
, go
, "\\n\""); return; }
3305 else gprintf(gops
, go
, "\\n\"\n\t\"");
3309 if (p
< q
) gops
->putm(go
, (const char *)p
, q
- p
);
3310 gprintf(gops
, go
, "\"");
3316 static void dump_bytes(const union tvec_regval
*rv
,
3317 const struct tvec_regdef
*rd
,
3319 const struct gprintf_ops
*gops
, void *go
)
3321 const unsigned char *p
= rv
->bytes
.p
, *l
= p
+ rv
->bytes
.sz
;
3322 size_t off
, sz
= rv
->bytes
.sz
;
3326 if (!rv
->text
.sz
) { dump_empty("bytes", style
, gops
, go
); return; }
3328 if (style
&(TVSF_COMPACT
| TVSF_RAW
)) {
3329 while (p
< l
) gprintf(gops
, go
, "%02x", *p
++);
3333 if (sz
> 16) gprintf(gops
, go
, "\n\t");
3335 off
= 0; wd
= hex_width(sz
);
3337 if (l
- p
< 16) n
= l
- p
;
3340 for (i
= 0; i
< n
; i
++) {
3341 if (i
< n
) gprintf(gops
, go
, "%02x", p
[i
]);
3342 else gprintf(gops
, go
, " ");
3343 if (i
< n
- 1 && i
%4 == 3) gprintf(gops
, go
, " ");
3345 gprintf(gops
, go
, " ; ");
3346 if (sz
> 16) gprintf(gops
, go
, "[%0*lx] ", wd
, (unsigned long)off
);
3347 for (i
= 0; i
< n
; i
++)
3348 gprintf(gops
, go
, "%c", isprint(p
[i
]) ? p
[i
] : '.');
3350 if (p
< l
) gprintf(gops
, go
, "\n\t");
3354 /* Text and byte string type definitions. */
3355 const struct tvec_regty tvty_text
= {
3356 init_text
, release_text
, eq_text
,
3357 tobuf_text
, frombuf_text
,
3358 parse_text
, dump_text
3360 const struct tvec_regty tvty_bytes
= {
3361 init_bytes
, release_bytes
, eq_bytes
,
3362 tobuf_bytes
, frombuf_bytes
,
3363 parse_bytes
, dump_bytes
3366 /* --- @tvec_claimeq_text@ --- *
3368 * Arguments: @struct tvec_state *tv@ = test-vector state
3369 * @const char *p0@, @size_t sz0@ = first string with length
3370 * @const char *p1@, @size_t sz1@ = second string with length
3371 * @const char *file@, @unsigned @lno@ = calling file and line
3372 * @const char *expr@ = the expression to quote on failure
3374 * Returns: Nonzero if the strings at @p0@ and @p1@ are equal, otherwise
3377 * Use: Check that strings at @p0@ and @p1@ are equal. As for
3378 * @tvec_claim@ above, a test case is automatically begun and
3379 * ended if none is already underway. If the values are
3380 * unequal, then @tvec_fail@ is called, quoting @expr@, and the
3381 * mismatched values are dumped: @p0@ is printed as the output
3382 * value and @p1@ is printed as the input reference.
3385 int tvec_claimeq_text(struct tvec_state
*tv
,
3386 const char *p0
, size_t sz0
,
3387 const char *p1
, size_t sz1
,
3388 const char *file
, unsigned lno
, const char *expr
)
3390 tv
->out
[0].v
.text
.p
= (/*unconst*/ char *)p0
; tv
->out
[0].v
.text
.sz
= sz0
;
3391 tv
->in
[0].v
.text
.p
=(/*unconst*/ char *) p1
; tv
->in
[0].v
.text
.sz
= sz1
;
3392 return (tvec_claimeq(tv
, &tvty_text
, 0, file
, lno
, expr
));
3395 /* --- @tvec_claimeq_textz@ --- *
3397 * Arguments: @struct tvec_state *tv@ = test-vector state
3398 * @const char *p0, *p1@ = two strings to compare
3399 * @const char *file@, @unsigned @lno@ = calling file and line
3400 * @const char *expr@ = the expression to quote on failure
3402 * Returns: Nonzero if the strings at @p0@ and @p1@ are equal, otherwise
3405 * Use: Check that strings at @p0@ and @p1@ are equal, as for
3406 * @tvec_claimeq_string@, except that the strings are assumed
3407 * null-terminated, so their lengths don't need to be supplied
3411 int tvec_claimeq_textz(struct tvec_state
*tv
,
3412 const char *p0
, const char *p1
,
3413 const char *file
, unsigned lno
, const char *expr
)
3415 tv
->out
[0].v
.text
.p
= (/*unconst*/ char *)p0
;
3416 tv
->out
[0].v
.text
.sz
= strlen(p0
);
3417 tv
->in
[0].v
.text
.p
= (/*unconst*/ char *)p1
;
3418 tv
->in
[0].v
.text
.sz
= strlen(p1
);
3419 return (tvec_claimeq(tv
, &tvty_text
, 0, file
, lno
, expr
));
3422 /* --- @tvec_claimeq_bytes@ --- *
3424 * Arguments: @struct tvec_state *tv@ = test-vector state
3425 * @const void *p0@, @size_t sz0@ = first string with length
3426 * @const void *p1@, @size_t sz1@ = second string with length
3427 * @const char *file@, @unsigned @lno@ = calling file and line
3428 * @const char *expr@ = the expression to quote on failure
3430 * Returns: Nonzero if the strings at @p0@ and @p1@ are equal, otherwise
3433 * Use: Check that binary strings at @p0@ and @p1@ are equal. As for
3434 * @tvec_claim@ above, a test case is automatically begun and
3435 * ended if none is already underway. If the values are
3436 * unequal, then @tvec_fail@ is called, quoting @expr@, and the
3437 * mismatched values are dumped: @p0@ is printed as the output
3438 * value and @p1@ is printed as the input reference.
3441 int tvec_claimeq_bytes(struct tvec_state
*tv
,
3442 const void *p0
, size_t sz0
,
3443 const void *p1
, size_t sz1
,
3444 const char *file
, unsigned lno
, const char *expr
)
3446 tv
->out
[0].v
.bytes
.p
= (/*unconst*/ void *)p0
;
3447 tv
->out
[0].v
.bytes
.sz
= sz0
;
3448 tv
->in
[0].v
.bytes
.p
= (/*unconst*/ void *)p1
;
3449 tv
->in
[0].v
.bytes
.sz
= sz1
;
3450 return (tvec_claimeq(tv
, &tvty_bytes
, 0, file
, lno
, expr
));
3453 /* --- @tvec_alloctext@, @tvec_allocbytes@ --- *
3455 * Arguments: @union tvec_regval *rv@ = register value
3456 * @size_t sz@ = required size
3460 * Use: Allocated space in a text or binary string register. If the
3461 * current register size is sufficient, its buffer is left
3462 * alone; otherwise, the old buffer, if any, is freed and a
3463 * fresh buffer allocated. These functions are not intended to
3464 * be used to adjust a buffer repeatedly, e.g., while building
3465 * output incrementally: (a) they will perform badly, and (b)
3466 * the old buffer contents are simply discarded if reallocation
3467 * is necessary. Instead, use a @dbuf@ or @dstr@.
3469 * The @tvec_alloctext@ function sneakily allocates an extra
3470 * byte for a terminating zero. The @tvec_allocbytes@ function
3474 void tvec_alloctext(union tvec_regval
*rv
, size_t sz
)
3476 if (rv
->text
.sz
<= sz
) { xfree(rv
->text
.p
); rv
->text
.p
= xmalloc(sz
+ 1); }
3480 void tvec_allocbytes(union tvec_regval
*rv
, size_t sz
)
3482 if (rv
->bytes
.sz
< sz
) { xfree(rv
->bytes
.p
); rv
->bytes
.p
= xmalloc(sz
); }
3486 /*----- Buffer type -------------------------------------------------------*/
3488 /* --- @init_buffer@ --- *
3490 * Arguments: @union tvec_regval *rv@ = register value
3491 * @const struct tvec_regdef *rd@ = register definition
3495 * Use: Initialize a register value.
3497 * Buffer values values are initialized with a null pointer,
3498 * zero length, and zero residue, modulus, and offset.
3501 static void init_buffer(union tvec_regval
*rv
, const struct tvec_regdef
*rd
)
3502 { rv
->buf
.p
= 0; rv
->buf
.sz
= rv
->buf
.a
= rv
->buf
.m
= rv
->buf
.off
= 0; }
3504 /* --- @release_buffer@, @release_bytes@ --- *
3506 * Arguments: @const union tvec_regval *rv@ = register value
3507 * @const struct tvec_regdef *rd@ = register definition
3511 * Use: Release resources held by a register value.
3513 * Buffers are freed.
3516 static void release_buffer(union tvec_regval
*rv
,
3517 const struct tvec_regdef
*rd
)
3518 { if (rv
->buf
.p
) xfree(rv
->buf
.p
- rv
->buf
.off
); }
3520 /* --- @eq_buffer@ --- *
3522 * Arguments: @const union tvec_regval *rv0, *rv1@ = register values
3523 * @const struct tvec_regdef *rd@ = register definition
3525 * Returns: Nonzero if the values are equal, zero if unequal
3527 * Use: Compare register values for equality.
3529 * Buffer values are equal if and only if their sizes and
3530 * alignment parameters are equal; their contents are
3531 * %%\emph{not}%% compared.
3534 static int eq_buffer(const union tvec_regval
*rv0
,
3535 const union tvec_regval
*rv1
,
3536 const struct tvec_regdef
*rd
)
3538 return (rv0
->buf
.sz
== rv1
->buf
.sz
&&
3539 rv0
->buf
.a
== rv1
->buf
.a
&&
3540 rv0
->buf
.m
== rv1
->buf
.m
);
3543 /* --- @tobuf_buffer@ --- *
3545 * Arguments: @buf *b@ = buffer
3546 * @const union tvec_regval *rv@ = register value
3547 * @const struct tvec_regdef *rd@ = register definition
3549 * Returns: Zero on success, %$-1$% on failure.
3551 * Use: Serialize a register value to a buffer.
3553 * Buffer values are serialized as their lengths, residues, and
3554 * moduli, as unsigned integers.
3557 static int tobuf_buffer(buf
*b
, const union tvec_regval
*rv
,
3558 const struct tvec_regdef
*rd
)
3560 return (unsigned_to_buf(b
, rv
->buf
.sz
) ||
3561 unsigned_to_buf(b
, rv
->buf
.a
) ||
3562 unsigned_to_buf(b
, rv
->buf
.m
));
3565 /* --- @frombuf_buffer@ --- *
3567 * Arguments: @buf *b@ = buffer
3568 * @union tvec_regval *rv@ = register value
3569 * @const struct tvec_regdef *rd@ = register definition
3571 * Returns: Zero on success, %$-1$% on failure.
3573 * Use: Deserialize a register value from a buffer.
3575 * Buffer values are serialized as just their lengths, as
3576 * unsigned integers. The buffer is allocated on
3577 * deserialization and filled with a distinctive pattern.
3580 static int frombuf_buffer(buf
*b
, union tvec_regval
*rv
,
3581 const struct tvec_regdef
*rd
)
3583 unsigned long sz
, a
, m
;
3585 if (unsigned_from_buf(b
, &sz
)) return (-1);
3586 if (unsigned_from_buf(b
, &a
)) return (-1);
3587 if (unsigned_from_buf(b
, &m
)) return (-1);
3588 if (sz
> (size_t)-1 || a
> (size_t)-1 || m
> (size_t)-1)
3589 { buf_break(b
); return (-1); }
3590 rv
->buf
.sz
= sz
; rv
->buf
.a
= a
; rv
->buf
.m
= m
;
3594 /* --- @parse_buffer@ --- *
3596 * Arguments: @union tvec_regval *rv@ = register value
3597 * @const struct tvec_regdef *rd@ = register definition
3598 * @struct tvec_state *tv@ = test-vector state
3600 * Returns: Zero on success, %$-1$% on error.
3602 * Use: Parse a register value from an input file.
3604 * The input format for a buffer value is a size, followed by an
3605 * optional `%|@$%' and an alignment quantum and a further
3606 * optional `%|+|%' and an alignment offset. The size, quantum,
3607 * and offset are syntactically sizes.
3609 * The buffer is not allocated.
3612 static int parse_buffer(union tvec_regval
*rv
,
3613 const struct tvec_regdef
*rd
,
3614 struct tvec_state
*tv
)
3616 unsigned long sz
, a
= 0, m
= 0;
3619 if (parse_szint(tv
, &sz
, "@;", "buffer length")) { rc
= -1; goto end
; }
3620 if (check_unsigned_range(sz
, &tvrange_size
, tv
, "buffer length"))
3621 { rc
= -1; goto end
; }
3622 if (check_string_length(sz
, rd
->arg
.p
, tv
)) { rc
= -1; goto end
; }
3626 if (ch
== ';' || ch
== '\n') { ungetc(ch
, tv
->fp
); goto done
; }
3627 else if (ch
!= '@') { rc
= tvec_syntax(tv
, ch
, "`@'"); goto end
; }
3629 if (parse_szint(tv
, &m
, "+;", "alignment quantum")) { rc
= -1; goto end
; }
3630 if (check_unsigned_range(a
, &tvrange_size
, tv
, "alignment quantum"))
3631 { rc
= -1; goto end
; }
3636 if (ch
== ';' || ch
== '\n') { ungetc(ch
, tv
->fp
); goto done
; }
3637 else if (ch
!= '+') { rc
= tvec_syntax(tv
, ch
, "`+'"); goto end
; }
3639 if (parse_szint(tv
, &a
, ";", "alignment offset")) { rc
= -1; goto end
; }
3640 if (check_unsigned_range(m
, &tvrange_size
, tv
, "alignment offset"))
3641 { rc
= -1; goto end
; }
3643 rc
= tvec_error(tv
, "alignment offset %lu >= quantum %lu",
3644 (unsigned long)a
, (unsigned long)m
);
3649 if (tvec_flushtoeol(tv
, 0)) { rc
= -1; goto end
; }
3650 rv
->buf
.sz
= sz
; rv
->buf
.a
= a
; rv
->buf
.m
= m
;
3656 /* --- @dump_buffer@ --- *
3658 * Arguments: @const union tvec_regval *rv@ = register value
3659 * @const struct tvec_regdef *rd@ = register definition
3660 * @unsigned style@ = output style (@TVSF_...@)
3661 * @const struct gprintf_ops *gops@, @void *gp@ = format output
3665 * Use: Dump a register value to the format output.
3667 * Buffer values are dumped as their size, with the alignment
3668 * quantum and alignment offset if these are non-default.
3671 static void dump_buffer(const union tvec_regval
*rv
,
3672 const struct tvec_regdef
*rd
,
3674 const struct gprintf_ops
*gops
, void *go
)
3676 format_size(gops
, go
, rv
->buf
.sz
, style
);
3678 gprintf(gops
, go
, style
&(TVSF_COMPACT
| TVSF_RAW
) ?
"@" : " @ ");
3679 format_size(gops
, go
, rv
->buf
.m
, style
);
3681 gprintf(gops
, go
, style
&(TVSF_COMPACT
| TVSF_RAW
) ?
"+" : " + ");
3682 format_size(gops
, go
, rv
->buf
.a
, style
);
3685 if (!(style
&TVSF_COMPACT
)) {
3686 gprintf(gops
, go
, " ; = %lu", (unsigned long)rv
->buf
.sz
);
3688 gprintf(gops
, go
, " @ %lu", (unsigned long)rv
->buf
.m
);
3689 if (rv
->buf
.a
) gprintf(gops
, go
, " + %lu", (unsigned long)rv
->buf
.a
);
3691 gprintf(gops
, go
, " = "); format_unsigned_hex(gops
, go
, rv
->buf
.sz
);
3693 gprintf(gops
, go
, " @ "); format_unsigned_hex(gops
, go
, rv
->buf
.m
);
3695 gprintf(gops
, go
, " + ");
3696 format_unsigned_hex(gops
, go
, rv
->buf
.a
);
3702 /* Buffer type definition. */
3703 const struct tvec_regty tvty_buffer
= {
3704 init_buffer
, release_buffer
, eq_buffer
,
3705 tobuf_buffer
, frombuf_buffer
,
3706 parse_buffer
, dump_buffer
3709 /* --- @tvec_initbuffer@ --- *
3711 * Arguments: @union tvec_regval *rv@ = register value
3712 * @const union tvec_regval *ref@ = source buffer
3713 * @size_t sz@ = size to allocate
3717 * Use: Initialize the alignment parameters in @rv@ to match @ref@,
3718 * and the size to @sz@.
3721 void tvec_initbuffer(union tvec_regval
*rv
,
3722 const union tvec_regval
*ref
, size_t sz
)
3723 { rv
->buf
.sz
= sz
; rv
->buf
.a
= ref
->buf
.a
; rv
->buf
.m
= ref
->buf
.m
; }
3725 /* --- @tvec_allocbuffer@ --- *
3727 * Arguments: @union tvec_regval *rv@ = register value
3731 * Use: Allocate @sz@ bytes to the buffer and fill the space with a
3732 * distinctive pattern.
3735 void tvec_allocbuffer(union tvec_regval
*rv
)
3737 unsigned char *p
; size_t n
;
3739 if (rv
->buf
.p
) xfree(rv
->buf
.p
- rv
->buf
.off
);
3741 if (rv
->buf
.m
< 2) {
3742 rv
->buf
.p
= xmalloc(rv
->buf
.sz
); rv
->buf
.off
= 0;
3744 p
= xmalloc(rv
->buf
.sz
+ rv
->buf
.m
- 1);
3745 n
= (size_t)p
%rv
->buf
.m
;
3746 rv
->buf
.off
= (rv
->buf
.a
- n
+ rv
->buf
.m
)%rv
->buf
.m
;
3747 rv
->buf
.p
= p
+ rv
->buf
.off
;
3749 memset(rv
->buf
.p
, '?', rv
->buf
.sz
);
3752 /*----- That's all, folks -------------------------------------------------*/