X-Git-Url: https://git.distorted.org.uk/~mdw/mLib/blobdiff_plain/31d0247cc58abc0b0720aa7e9972011c5a66995c..c81c35dfd10050ffef85d57dc2ad73f52f38a3f2:/test/tvec-types.c?ds=sidebyside diff --git a/test/tvec-types.c b/test/tvec-types.c index 681409d..acb848c 100644 --- a/test/tvec-types.c +++ b/test/tvec-types.c @@ -1241,36 +1241,36 @@ end: return (rc); } -/*----- Skeleton ----------------------------------------------------------*/ -/* -static void init_...(union tvec_regval *rv, const struct tvec_regdef *rd) -static void release_...(union tvec_regval *rv, const struct tvec_regdef *rd) -static int eq_...(const union tvec_regval *rv0, const union tvec_regval *rv1, - const struct tvec_regdef *rd) -static int tobuf_...(buf *b, const union tvec_regval *rv, - const struct tvec_regdef *rd) -static int frombuf_...(buf *b, union tvec_regval *rv, - const struct tvec_regdef *rd) -static int parse_...(union tvec_regval *rv, const struct tvec_regdef *rd, - struct tvec_state *tv) -static void dump_...(const union tvec_regval *rv, - const struct tvec_regdef *rd, - struct tvec_state *tv, unsigned style) - -const struct tvec_regty tvty_... = { - init_..., release_..., eq_..., - tobuf_..., frombuf_..., - parse_..., dump_... -}; -*/ /*----- Signed and unsigned integer types ---------------------------------*/ +/* --- @init_int@, @init_uint@ --- * + * + * Arguments: @union tvec_regval *rv@ = register value + * @const struct tvec_regdef *rd@ = register definition + * + * Returns: --- + * + * Use: Initialize a register value. + * + * Integer values are initialized to zero. + */ + static void init_int(union tvec_regval *rv, const struct tvec_regdef *rd) { rv->i = 0; } static void init_uint(union tvec_regval *rv, const struct tvec_regdef *rd) { rv->u = 0; } +/* --- @eq_int@, @eq_uint@ --- * + * + * Arguments: @const union tvec_regval *rv0, *rv1@ = register values + * @const struct tvec_regdef *rd@ = register definition + * + * Returns: Nonzero if the values are equal, zero if unequal + * + * Use: Compare register values for equality. + */ + static int eq_int(const union tvec_regval *rv0, const union tvec_regval *rv1, const struct tvec_regdef *rd) { return (rv0->i == rv1->i); } @@ -1280,6 +1280,20 @@ static int eq_uint(const union tvec_regval *rv0, const struct tvec_regdef *rd) { return (rv0->u == rv1->u); } +/* --- @tobuf_int@, @tobuf_uint@ --- * + * + * Arguments: @buf *b@ = buffer + * @const union tvec_regval *rv@ = register value + * @const struct tvec_regdef *rd@ = register definition + * + * Returns: Zero on success, %$-1$% on failure. + * + * Use: Serialize a register value to a buffer. + * + * Integer values are serialized as little-endian 64-bit signed + * or unsigned integers. + */ + static int tobuf_int(buf *b, const union tvec_regval *rv, const struct tvec_regdef *rd) { return (signed_to_buf(b, rv->i)); } @@ -1288,6 +1302,20 @@ static int tobuf_uint(buf *b, const union tvec_regval *rv, const struct tvec_regdef *rd) { return (unsigned_to_buf(b, rv->u)); } +/* --- @frombuf_int@, @frombuf_uint@ --- * + * + * Arguments: @buf *b@ = buffer + * @union tvec_regval *rv@ = register value + * @const struct tvec_regdef *rd@ = register definition + * + * Returns: Zero on success, %$-1$% on failure. + * + * Use: Deserialize a register value from a buffer. + * + * Integer values are serialized as 64-bit signed or unsigned + * integers. + */ + static int frombuf_int(buf *b, union tvec_regval *rv, const struct tvec_regdef *rd) { return (signed_from_buf(b, &rv->i)); } @@ -1296,18 +1324,51 @@ static int frombuf_uint(buf *b, union tvec_regval *rv, const struct tvec_regdef *rd) { return (unsigned_from_buf(b, &rv->u)); } +/* --- @parse_int@, @parse_uint@ --- * + * + * Arguments: @union tvec_regval *rv@ = register value + * @const struct tvec_regdef *rd@ = register definition + * @struct tvec_state *tv@ = test-vector state + * + * Returns: Zero on success, %$-1$% on error. + * + * Use: Parse a register value from an input file. + * + * Integers may be input in decimal, hex, binary, or octal, + * following approximately usual conventions. + * + * * Signed integers may be preceded with a `+' or `-' sign. + * + * * Decimal integers are just a sequence of decimal digits + * `0' ... `9'. + * + * * Octal integers are a sequence of digits `0' ... `7', + * preceded by `0o' or `0O'. + * + * * Hexadecimal integers are a sequence of digits `0' + * ... `9', `a' ... `f', or `A' ... `F', preceded by `0x' or + * `0X'. + * + * * Radix-B integers are a sequence of digits `0' ... `9', + * `a' ... `f', or `A' ... `F', each with value less than B, + * preceded by `Br' or `BR', where 0 < B < 36 is expressed + * in decimal without any leading `0' or internal + * underscores `_'. + * + * * A digit sequence may contain internal underscore `_' + * separators, but not before or after all of the digits; + * and two consecutive `_' characters are not permitted. + */ + static int parse_int(union tvec_regval *rv, const struct tvec_regdef *rd, struct tvec_state *tv) { dstr d = DSTR_INIT; int rc; - if (tvec_readword(tv, &d, ";", "signed integer")) - { rc = -1; goto end; } - if (parse_signed(&rv->i, d.buf, rd->arg.p, tv)) - { rc = -1; goto end; } - if (tvec_flushtoeol(tv, 0)) - { rc = -1; goto end; } + if (tvec_readword(tv, &d, ";", "signed integer")) { rc = -1; goto end; } + if (parse_signed(&rv->i, d.buf, rd->arg.p, tv)) { rc = -1; goto end; } + if (tvec_flushtoeol(tv, 0)) { rc = -1; goto end; } rc = 0; end: dstr_destroy(&d); @@ -1320,18 +1381,31 @@ static int parse_uint(union tvec_regval *rv, const struct tvec_regdef *rd, dstr d = DSTR_INIT; int rc; - if (tvec_readword(tv, &d, ";", "unsigned integer")) - { rc = -1; goto end; } - if (parse_unsigned(&rv->u, d.buf, rd->arg.p, tv)) - { rc = -1; goto end; } - if (tvec_flushtoeol(tv, 0)) - { rc = -1; goto end; } + if (tvec_readword(tv, &d, ";", "unsigned integer")) { rc = -1; goto end; } + if (parse_unsigned(&rv->u, d.buf, rd->arg.p, tv)) { rc = -1; goto end; } + if (tvec_flushtoeol(tv, 0)) { rc = -1; goto end; } rc = 0; end: dstr_destroy(&d); return (rc); } +/* --- @dump_int@, @dump_uint@ --- * + * + * Arguments: @const union tvec_regval *rv@ = register value + * @const struct tvec_regdef *rd@ = register definition + * @unsigned style@ = output style (@TVSF_...@) + * @const struct gprintf_ops *gops@, @void *gp@ = format output + * + * Returns: --- + * + * Use: Dump a register value to the format output. + * + * Integer values are dumped in decimal and, unless compact + * output is requested, hex, and maybe a character, as a + * comment. + */ + static void dump_int(const union tvec_regval *rv, const struct tvec_regdef *rd, unsigned style, @@ -1359,12 +1433,19 @@ static void dump_uint(const union tvec_regval *rv, } } +/* Integer type definitions. */ const struct tvec_regty tvty_int = { init_int, trivial_release, eq_int, tobuf_int, frombuf_int, parse_int, dump_int }; +const struct tvec_regty tvty_uint = { + init_uint, trivial_release, eq_uint, + tobuf_uint, frombuf_uint, + parse_uint, dump_uint +}; +/* Predefined integer ranges. */ const struct tvec_irange tvrange_schar = { SCHAR_MIN, SCHAR_MAX }, tvrange_short = { SHRT_MIN, SHRT_MAX }, @@ -1373,13 +1454,6 @@ const struct tvec_irange tvrange_sbyte = { -128, 127 }, tvrange_i16 = { -32768, +32767 }, tvrange_i32 = { -2147483648, 2147483647 }; - -const struct tvec_regty tvty_uint = { - init_uint, trivial_release, eq_uint, - tobuf_uint, frombuf_uint, - parse_uint, dump_uint -}; - const struct tvec_urange tvrange_uchar = { 0, UCHAR_MAX }, tvrange_ushort = { 0, USHRT_MAX }, @@ -1441,21 +1515,93 @@ int tvec_claimeq_uint(struct tvec_state *tv, /*----- Floating-point type -----------------------------------------------*/ +/* --- @float_int@ --- * + * + * Arguments: @union tvec_regval *rv@ = register value + * @const struct tvec_regdef *rd@ = register definition + * + * Returns: --- + * + * Use: Initialize a register value. + * + * Floating-point values are initialized to zero. + */ + static void init_float(union tvec_regval *rv, const struct tvec_regdef *rd) { rv->f = 0.0; } +/* --- @eq_float@ --- * + * + * Arguments: @const union tvec_regval *rv0, *rv1@ = register values + * @const struct tvec_regdef *rd@ = register definition + * + * Returns: Nonzero if the values are equal, zero if unequal + * + * Use: Compare register values for equality. + * + * Floating-point values may be considered equal if their + * absolute or relative difference is sufficiently small, as + * described in the register definition. + */ + static int eq_float(const union tvec_regval *rv0, const union tvec_regval *rv1, const struct tvec_regdef *rd) { return (eqish_floating_p(rv0->f, rv1->f, rd->arg.p)); } +/* --- @tobuf_float@ --- * + * + * Arguments: @buf *b@ = buffer + * @const union tvec_regval *rv@ = register value + * @const struct tvec_regdef *rd@ = register definition + * + * Returns: Zero on success, %$-1$% on failure. + * + * Use: Serialize a register value to a buffer. + * + * Floating-point values are serialized as little-endian + * IEEE 754 Binary64. + */ + static int tobuf_float(buf *b, const union tvec_regval *rv, const struct tvec_regdef *rd) { return (buf_putf64l(b, rv->f)); } + +/* --- @frombuf_float@ --- * + * + * Arguments: @buf *b@ = buffer + * @union tvec_regval *rv@ = register value + * @const struct tvec_regdef *rd@ = register definition + * + * Returns: Zero on success, %$-1$% on failure. + * + * Use: Deserialize a register value from a buffer. + * + * Floating-point values are serialized as little-endian + * IEEE 754 Binary64. + */ + static int frombuf_float(buf *b, union tvec_regval *rv, const struct tvec_regdef *rd) { return (buf_getf64l(b, &rv->f)); } +/* --- @parse_float@ --- * + * + * Arguments: @union tvec_regval *rv@ = register value + * @const struct tvec_regdef *rd@ = register definition + * @struct tvec_state *tv@ = test-vector state + * + * Returns: Zero on success, %$-1$% on error. + * + * Use: Parse a register value from an input file. + * + * Floating-point values are either NaN (%|#nan|%, if supported + * by the platform); positive or negative infinity (%|#inf|%, + * %|+#inf|%, or %|#+inf|% (preferring the last), and %|-#inf|% + * or %|#-inf|% (preferring the latter), if supported by the + * platform); or a number in strtod(3) syntax. + */ + static int parse_float(union tvec_regval *rv, const struct tvec_regdef *rd, struct tvec_state *tv) { @@ -1464,28 +1610,49 @@ static int parse_float(union tvec_regval *rv, const struct tvec_regdef *rd, if (tvec_readword(tv, &d, ";", "floating-point number")) { rc = -1; goto end; } - if (parse_floating(&rv->f, d.buf, rd->arg.p, tv)) - { rc = -1; goto end; } - if (tvec_flushtoeol(tv, 0)) - { rc = -1; goto end; } + if (parse_floating(&rv->f, d.buf, rd->arg.p, tv)) { rc = -1; goto end; } + if (tvec_flushtoeol(tv, 0)) { rc = -1; goto end; } rc = 0; end: dstr_destroy(&d); return (rc); } +/* --- @dump_float@ --- * + * + * Arguments: @const union tvec_regval *rv@ = register value + * @const struct tvec_regdef *rd@ = register definition + * @unsigned style@ = output style (@TVSF_...@) + * @const struct gprintf_ops *gops@, @void *gp@ = format output + * + * Returns: --- + * + * Use: Dump a register value to the format output. + * + * Floating-point values are dumped in decimal or as a special + * token beginning with `%|#|%'. Some effort is taken to ensure + * that the output is sufficient to uniquely identify the + * original value, but, honestly, C makes this really hard. + */ + static void dump_float(const union tvec_regval *rv, const struct tvec_regdef *rd, unsigned style, const struct gprintf_ops *gops, void *go) { format_floating(gops, go, rv->f); } +/* Floating-point type definition. */ const struct tvec_regty tvty_float = { init_float, trivial_release, eq_float, tobuf_float, frombuf_float, parse_float, dump_float }; +/* Predefined floating-point ranges. */ +const struct tvec_floatinfo + tvflt_finite = { TVFF_EXACT, -DBL_MAX, DBL_MAX, 0.0 }, + tvflt_nonneg = { TVFF_EXACT, 0, DBL_MAX, 0.0 }; + /* --- @tvec_claimeqish_float@ --- * * * Arguments: @struct tvec_state *tv@ = test-vector state @@ -1562,20 +1729,48 @@ int tvec_claimeq_float(struct tvec_state *tv, file, lno, expr)); } -const struct tvec_floatinfo - tvflt_finite = { TVFF_EXACT, -DBL_MAX, DBL_MAX, 0.0 }, - tvflt_nonneg = { TVFF_EXACT, 0, DBL_MAX, 0.0 }; - /*----- Enumerations ------------------------------------------------------*/ +/* --- @init_tenum@ --- * + * + * Arguments: @union tvec_regval *rv@ = register value + * @const struct tvec_regdef *rd@ = register definition + * + * Returns: --- + * + * Use: Initialize a register value. + * + * Integer and floating-point enumeration values are initialized + * as their underlying representations. Pointer enumerations + * are initialized to %|#nil|%. + */ + #define init_ienum init_int #define init_uenum init_uint #define init_fenum init_float + static void init_penum(union tvec_regval *rv, const struct tvec_regdef *rd) { rv->p = 0; } +/* --- @eq_tenum@ --- * + * + * Arguments: @const union tvec_regval *rv0, *rv1@ = register values + * @const struct tvec_regdef *rd@ = register definition + * + * Returns: Nonzero if the values are equal, zero if unequal + * + * Use: Compare register values for equality. + * + * Integer and floating-point enumeration values are compared as + * their underlying representations; in particular, floating- + * point enumerations may compare equal if their absolute or + * relative difference is sufficiently small. Pointer + * enumerations are compared as pointers. + */ + #define eq_ienum eq_int #define eq_uenum eq_uint + static int eq_fenum(const union tvec_regval *rv0, const union tvec_regval *rv1, const struct tvec_regdef *rd) @@ -1583,14 +1778,33 @@ static int eq_fenum(const union tvec_regval *rv0, const struct tvec_fenuminfo *ei = rd->arg.p; return (eqish_floating_p(rv0->f, rv1->f, ei->fi)); } + static int eq_penum(const union tvec_regval *rv0, const union tvec_regval *rv1, const struct tvec_regdef *rd) { return (rv0->p == rv1->p); } +/* --- @tobuf_tenum@ --- * + * + * Arguments: @buf *b@ = buffer + * @const union tvec_regval *rv@ = register value + * @const struct tvec_regdef *rd@ = register definition + * + * Returns: Zero on success, %$-1$% on failure. + * + * Use: Serialize a register value to a buffer. + * + * Integer and floating-point enumeration values are serialized + * as their underlying representations. Pointer enumerations + * are serialized as the signed integer index into the + * association table; %|#nil|% serializes as %$-1$%, and + * unrecognized pointers cause failure. + */ + #define tobuf_ienum tobuf_int #define tobuf_uenum tobuf_uint #define tobuf_fenum tobuf_float + static int tobuf_penum(buf *b, const union tvec_regval *rv, const struct tvec_regdef *rd) { @@ -1606,6 +1820,23 @@ found: return (signed_to_buf(b, i)); } +/* --- @frombuf_tenum@ --- * + * + * Arguments: @buf *b@ = buffer + * @union tvec_regval *rv@ = register value + * @const struct tvec_regdef *rd@ = register definition + * + * Returns: Zero on success, %$-1$% on failure. + * + * Use: Deserialize a register value from a buffer. + * + * Integer and floating-point enumeration values are serialized + * as their underlying representations. Pointer enumerations + * are serialized as the signed integer index into the + * association table; %|#nil|% serializes as %$-1$%; out-of- + * range indices cause failure. + */ + #define frombuf_ienum frombuf_int #define frombuf_uenum frombuf_uint #define frombuf_fenum frombuf_float @@ -1624,6 +1855,23 @@ static int frombuf_penum(buf *b, union tvec_regval *rv, return (0); } +/* --- @parse_tenum@ --- * + * + * Arguments: @union tvec_regval *rv@ = register value + * @const struct tvec_regdef *rd@ = register definition + * @struct tvec_state *tv@ = test-vector state + * + * Returns: Zero on success, %$-1$% on error. + * + * Use: Parse a register value from an input file. + * + * An enumerated value may be given by name or as a literal + * value. For enumerations based on numeric types, the literal + * values can be written in the same syntax as the underlying + * values. For enumerations based on pointers, the only + * permitted literal is %|#nil|%, which denotes a null pointer. + */ + #define DEFPARSE_ENUM(tag_, ty, slot) \ static int parse_##slot##enum(union tvec_regval *rv, \ const struct tvec_regdef *rd, \ @@ -1693,6 +1941,27 @@ TVEC_MISCSLOTS(DEFPARSE_ENUM) #undef DEFPARSE_ENUM +/* --- @dump_tenum@ --- * + * + * Arguments: @const union tvec_regval *rv@ = register value + * @const struct tvec_regdef *rd@ = register definition + * @unsigned style@ = output style (@TVSF_...@) + * @const struct gprintf_ops *gops@, @void *gp@ = format output + * + * Returns: --- + * + * Use: Dump a register value to the format output. + * + * Enumeration values are dumped as their symbolic names, if + * possible, with the underlying values provided as a comment + * unless compact output is requested, as for the underlying + * representation. A null pointer is printed as %|#nil|%; + * non-null pointers are printed as %|#|%, with the + * enumeration TYPE and the raw pointer PTR printed with the + * system's %|%p|% format specifier. + */ + + #define DEFDUMP_ENUM(tag_, ty, slot) \ static void dump_##slot##enum(const union tvec_regval *rv, \ const struct tvec_regdef *rd, \ @@ -1713,7 +1982,7 @@ TVEC_MISCSLOTS(DEFPARSE_ENUM) } #define MAYBE_PRINT_EXTRA \ - if (style&TVSF_COMPACT) ; \ + if (style&TVSF_COMPACT) /* nothing to do */; \ else if (!a->tag) { gprintf(gops, go, " ; = "); goto _extra; } \ else if (1) { gprintf(gops, go, " = "); goto _extra; } \ else _extra: @@ -1745,6 +2014,7 @@ TVEC_MISCSLOTS(DEFDUMP_ENUM) #undef MAYBE_PRINT_EXTRA #undef DEFDUMP_ENUM +/* Enumeration type definitions. */ #define DEFTY_ENUM(tag, ty, slot) \ const struct tvec_regty tvty_##slot##enum = { \ init_##slot##enum, trivial_release, eq_##slot##enum, \ @@ -1754,6 +2024,7 @@ TVEC_MISCSLOTS(DEFDUMP_ENUM) TVEC_MISCSLOTS(DEFTY_ENUM) #undef DEFTY_ENUM +/* Predefined enumeration types. */ static const struct tvec_iassoc bool_assoc[] = { { "nil", 0 }, { "false", 0 }, @@ -1838,6 +2109,27 @@ TVEC_MISCSLOTS(DEFCLAIM) /*----- Flag types --------------------------------------------------------*/ +/* Flag types are initialized, compared, and serialized as unsigned + * integers. + */ + +/* --- @parse_flags@ --- * + * + * Arguments: @union tvec_regval *rv@ = register value + * @const struct tvec_regdef *rd@ = register definition + * @struct tvec_state *tv@ = test-vector state + * + * Returns: Zero on success, %$-1$% on error. + * + * Use: Parse a register value from an input file. + * + * The input syntax is a sequence of items separated by `|' + * signs. Each item may be the symbolic name of a field value, + * or a literal unsigned integer. The masks associated with the + * given symbolic names must be disjoint. The resulting + * numerical value is simply the bitwise OR of the given values. + */ + static int parse_flags(union tvec_regval *rv, const struct tvec_regdef *rd, struct tvec_state *tv) { @@ -1848,10 +2140,13 @@ static int parse_flags(union tvec_regval *rv, const struct tvec_regdef *rd, int ch, rc; for (;;) { + + /* Read the next item. */ DRESET(&d); if (tvec_readword(tv, &d, "|;", "flag name or integer")) { rc = -1; goto end; } + /* Try to find a matching entry in the table. */ for (f = fi->fv; f->tag; f++) if (STRCMP(f->tag, ==, d.buf)) { if (m&f->m) @@ -1860,23 +2155,54 @@ static int parse_flags(union tvec_regval *rv, const struct tvec_regdef *rd, { m |= f->m; v |= f->v; goto next; } } + /* Otherwise, try to parse it as a raw integer. */ if (parse_unsigned(&t, d.buf, fi->range, tv)) { rc = -1; goto end; } v |= t; + next: + /* Advance to the next token. If it's a separator then consume it, and + * go round again. Otherwise we stop here. + */ if (tvec_nexttoken(tv)) break; ch = getc(tv->fp); if (ch != '|') { tvec_syntax(tv, ch, "`|'"); rc = -1; goto end; } - if (tvec_nexttoken(tv)) + if (tvec_nexttoken(tv)) { tvec_syntax(tv, '\n', "flag name or integer"); rc = -1; goto end; } } - rv->u = v; - rc = 0; + + /* Done. */ + rv->u = v; rc = 0; end: dstr_destroy(&d); return (rc); } +/* --- @dump_flags@ --- * + * + * Arguments: @const union tvec_regval *rv@ = register value + * @const struct tvec_regdef *rd@ = register definition + * @unsigned style@ = output style (@TVSF_...@) + * @const struct gprintf_ops *gops@, @void *gp@ = format output + * + * Returns: --- + * + * Use: Dump a register value to the format output. + * + * The table of symbolic names and their associated values and + * masks is repeatedly scanned, in order, to find disjoint + * matches -- i.e., entries whose value matches the target value + * in the bit positions indicated by the mask, and whose mask + * doesn't overlap with any previously found matches; the names + * are then output, separated by `|'. Any remaining nonzero + * bits not covered by any of the matching masks are output as a + * single literal integer, in hex. + * + * Unless compact output is requested, or no symbolic names were + * found, the raw numeric value is also printed in hex, as a + * comment. + */ + static void dump_flags(const union tvec_regval *rv, const struct tvec_regdef *rd, unsigned style, @@ -1884,7 +2210,7 @@ static void dump_flags(const union tvec_regval *rv, { const struct tvec_flaginfo *fi = rd->arg.p; const struct tvec_flag *f; - unsigned long m = ~(unsigned long)0, v = rv->u; + unsigned long m = ~0ul, v = rv->u; const char *sep; for (f = fi->fv, sep = ""; f->tag; f++) @@ -1895,10 +2221,11 @@ static void dump_flags(const union tvec_regval *rv, if (v&m) gprintf(gops, go, "%s0x%0*lx", sep, hex_width(v), v&m); - if (!(style&TVSF_COMPACT)) + if (m != ~0ul && !(style&TVSF_COMPACT)) gprintf(gops, go, " ; = 0x%0*lx", hex_width(rv->u), rv->u); } +/* Flags type definition. */ const struct tvec_regty tvty_flags = { init_uint, trivial_release, eq_uint, tobuf_uint, frombuf_uint, @@ -1936,16 +2263,47 @@ int tvec_claimeq_flags(struct tvec_state *tv, /*----- Characters --------------------------------------------------------*/ +/* Character values are initialized and compared as signed integers. */ + +/* --- @tobuf_char@ --- * + * + * Arguments: @buf *b@ = buffer + * @const union tvec_regval *rv@ = register value + * @const struct tvec_regdef *rd@ = register definition + * + * Returns: Zero on success, %$-1$% on failure. + * + * Use: Serialize a register value to a buffer. + * + * Character values are serialized as little-endian 32-bit + * unsigned integers, with %|EOF|% serialized as all-bits-set. + */ + static int tobuf_char(buf *b, const union tvec_regval *rv, const struct tvec_regdef *rd) { uint32 u; + if (0 <= rv->i && rv->i <= UCHAR_MAX) u = rv->i; else if (rv->i == EOF) u = MASK32; else return (-1); return (buf_putu32l(b, u)); } +/* --- @frombuf_char@ --- * + * + * Arguments: @buf *b@ = buffer + * @union tvec_regval *rv@ = register value + * @const struct tvec_regdef *rd@ = register definition + * + * Returns: Zero on success, %$-1$% on failure. + * + * Use: Deserialize a register value from a buffer. + * + * Character values are serialized as little-endian 32-bit + * unsigned integers, with %|EOF|% serialized as all-bits-set. + */ + static int frombuf_char(buf *b, union tvec_regval *rv, const struct tvec_regdef *rd) { @@ -1958,6 +2316,75 @@ static int frombuf_char(buf *b, union tvec_regval *rv, return (0); } +/* --- @parse_char@ --- * + * + * Arguments: @union tvec_regval *rv@ = register value + * @const struct tvec_regdef *rd@ = register definition + * @struct tvec_state *tv@ = test-vector state + * + * Returns: Zero on success, %$-1$% on error. + * + * Use: Parse a register value from an input file. + * + * A character value can be given by symbolic name, with a + * leading `%|#|%'; or a character or `%|\|%'-escape sequence, + * optionally in single quotes. + * + * The following escape sequences and character names are + * recognized. + * + * * `%|#eof|%' is the special end-of-file marker. + * + * * `%|#nul|%' is the NUL character, sometimes used to + * terminate strings. + * + * * `%|bell|%', `%|bel|%', `%|ding|%', or `%|\a|%' is the BEL + * character used to ring the terminal bell (or do some other + * thing to attract the user's attention). + * + * * %|#backspace|%, %|#bs|%, or %|\b|% is the backspace + * character, used to move the cursor backwords by one cell. + * + * * %|#escape|% %|#esc|%, or%|\e|% is the escape character, + * used to introduce special terminal commands. + * + * * %|#formfeed|%, %|#ff|%, or %|\f|% is the formfeed + * character, used to separate pages of text. + * + * * %|#newline|%, %|#linefeed|%, %|#lf|%, %|#nl|%, or %|\n|% is + * the newline character, used to terminate lines of text or + * advance the cursor to the next line (perhaps without + * returning it to the start of the line). + * + * * %|#return|%, %|#carriage-return|%, %|#cr|%, or %|\r|% is + * the carriage-return character, used to return the cursor to + * the start of the line. + * + * * %|#tab|%, %|#horizontal-tab|%, %|#ht|%, or %|\t|% is the + * tab character, used to advance the cursor to the next tab + * stop on the current line. + * + * * %|#vertical-tab|%, %|#vt|%, %|\v|% is the vertical tab + * character. + * + * * %|#space|%, %|#spc|% is the space character. + * + * * %|#delete|%, %|#del|% is the delete character, used to + * erase the most recent character. + * + * * %|\'|% is the single-quote character. + * + * * %|\\|% is the backslash character. + * + * * %|\"|% is the double-quote character. + * + * * %|\NNN|% or %|\{NNN}|% is the character with code NNN in + * octal. The NNN may be up to three digits long. + * + * * %|\xNN|% or %|\x{NN}|% is the character with code NNN in + * hexadecimal. + */ + static int parse_char(union tvec_regval *rv, const struct tvec_regdef *rd, struct tvec_state *tv) { @@ -1966,40 +2393,77 @@ static int parse_char(union tvec_regval *rv, const struct tvec_regdef *rd, unsigned f = 0; #define f_quote 1u + /* Inspect the character to see what we're up against. */ ch = getc(tv->fp); + if (ch == '#') { + /* It looks like a special token. Push the `%|#|%' back and fetch the + * whole word. If there's just the `%|#|%' after all, then treat it as + * literal. + */ + ungetc(ch, tv->fp); if (tvec_readword(tv, &d, ";", "character name")) { rc = -1; goto end; } - if (read_charname(&ch, d.buf, RCF_EOFOK)) { - rc = tvec_error(tv, "unknown character name `%s'", d.buf); - goto end; + if (STRCMP(d.buf, !=, "#")) { + if (read_charname(&ch, d.buf, RCF_EOFOK)) { + rc = tvec_error(tv, "unknown character name `%s'", d.buf); + goto end; + } + if (tvec_flushtoeol(tv, 0)) { rc = -1; goto end; } + rv->i = ch; rc = 0; goto end; } - if (tvec_flushtoeol(tv, 0)) { rc = -1; goto end; } - rv->i = ch; rc = 0; goto end; } + /* If this is a single quote then we expect to see a matching one later, + * and we should process backslash escapes. Get the next character and see + * what happens. + */ if (ch == '\'') { f |= f_quote; ch = getc(tv->fp); } + + /* Main character dispatch. */ switch (ch) { + case ';': + /* Unquoted, semicolon begins a comment. */ if (!(f&f_quote)) { rc = tvec_syntax(tv, ch, "character"); goto end; } - goto plain; + else goto plain; + case '\n': - if (f&f_quote) - { f &= ~f_quote; ungetc(ch, tv->fp); ch = '\''; goto plain; } + /* A newline. If we saw a single quote, then treat that as literal. + * Otherwise this is an error. + */ + if (!(f&f_quote)) goto nochar; + else { f &= ~f_quote; ungetc(ch, tv->fp); ch = '\''; goto plain; } + case EOF: - if (f&f_quote) { f &= ~f_quote; ch = '\''; goto plain; } - /* fall through */ - case '\'': + /* End-of-file. Similar to newline, but with slightly different + * effects on the parse state. + */ + if (!(f&f_quote)) goto nochar; + else { f &= ~f_quote; ch = '\''; goto plain; } + + case '\'': nochar: + /* A single quote. This must be the second of a pair, and there should + * have been a character or escape sequence between them. + */ rc = tvec_syntax(tv, ch, "character"); goto end; + case '\\': + /* A backslash. Read a character escape. */ if (read_charesc(&ch, tv)) return (-1); + default: plain: + /* Anything else. Treat as literal. */ rv->i = ch; break; } + + /* If we saw an opening quote, then expect the closing quote. */ if (f&f_quote) { ch = getc(tv->fp); if (ch != '\'') { rc = tvec_syntax(tv, ch, "`''"); goto end; } } + + /* Done. */ if (tvec_flushtoeol(tv, 0)) { rc = -1; goto end; } rc = 0; end: @@ -2009,6 +2473,25 @@ end: #undef f_quote } +/* --- @dump_char@ --- * + * + * Arguments: @const union tvec_regval *rv@ = register value + * @const struct tvec_regdef *rd@ = register definition + * @unsigned style@ = output style (@TVSF_...@) + * @const struct gprintf_ops *gops@, @void *gp@ = format output + * + * Returns: --- + * + * Use: Dump a register value to the format output. + * + * Character values are dumped as their symbolic names, if any, + * or as a character or escape sequence within single quotes + * (which may be omitted in compact style). If compact output + * is not requested, then the single-quoted representation (for + * characters dumped as symbolic names) and integer code in + * decimal and hex are printed as a comment. + */ + static void dump_char(const union tvec_regval *rv, const struct tvec_regdef *rd, unsigned style, @@ -2018,6 +2501,7 @@ static void dump_char(const union tvec_regval *rv, unsigned f = 0; #define f_semi 1u + /* Print a character name if we can find one. */ p = find_charname(rv->i, (style&TVSF_COMPACT) ? CTF_SHORT : CTF_PREFER); if (p) { gprintf(gops, go, "%s", p); @@ -2025,6 +2509,9 @@ static void dump_char(const union tvec_regval *rv, else { gprintf(gops, go, " ;"); f |= f_semi; } } + /* If the character isn't @EOF@ then print it as a single-quoted thing. + * In compact style, see if we can omit the quotes. + */ if (rv->i >= 0) { if (f&f_semi) gprintf(gops, go, " = "); switch (rv->i) { @@ -2038,6 +2525,7 @@ static void dump_char(const union tvec_regval *rv, } } + /* And the character code as an integer. */ if (!(style&TVSF_COMPACT)) { if (!(f&f_semi)) gprintf(gops, go, " ;"); gprintf(gops, go, " = %ld = ", rv->i); @@ -2047,6 +2535,7 @@ static void dump_char(const union tvec_regval *rv, #undef f_semi } +/* Character type definition. */ const struct tvec_regty tvty_char = { init_int, trivial_release, eq_int, tobuf_char, frombuf_char, @@ -2079,27 +2568,62 @@ int tvec_claimeq_char(struct tvec_state *tv, int c0, int c1, /*----- Text and byte strings ---------------------------------------------*/ -static void init_string(union tvec_regval *rv, const struct tvec_regdef *rd) - { rv->str.p = 0; rv->str.sz = 0; } +/* --- @init_text@, @init_bytes@ --- * + * + * Arguments: @union tvec_regval *rv@ = register value + * @const struct tvec_regdef *rd@ = register definition + * + * Returns: --- + * + * Use: Initialize a register value. + * + * Text and binary string values are initialized with a null + * pointer and zero length. + */ + +static void init_text(union tvec_regval *rv, const struct tvec_regdef *rd) + { rv->text.p = 0; rv->text.sz = 0; } static void init_bytes(union tvec_regval *rv, const struct tvec_regdef *rd) { rv->bytes.p = 0; rv->bytes.sz = 0; } -static void release_string(union tvec_regval *rv, - const struct tvec_regdef *rd) - { xfree(rv->str.p); } +/* --- @release_string@, @release_bytes@ --- * + * + * Arguments: @const union tvec_regval *rv@ = register value + * @const struct tvec_regdef *rd@ = register definition + * + * Returns: --- + * + * Use: Release resources held by a register value. + * + * Text and binary string buffers are freed. + */ + +static void release_text(union tvec_regval *rv, + const struct tvec_regdef *rd) + { xfree(rv->text.p); } static void release_bytes(union tvec_regval *rv, const struct tvec_regdef *rd) { xfree(rv->bytes.p); } -static int eq_string(const union tvec_regval *rv0, - const union tvec_regval *rv1, - const struct tvec_regdef *rd) +/* --- @eq_text@, @eq_bytes@ --- * + * + * Arguments: @const union tvec_regval *rv0, *rv1@ = register values + * @const struct tvec_regdef *rd@ = register definition + * + * Returns: Nonzero if the values are equal, zero if unequal + * + * Use: Compare register values for equality. + */ + +static int eq_text(const union tvec_regval *rv0, + const union tvec_regval *rv1, + const struct tvec_regdef *rd) { - return (rv0->str.sz == rv1->str.sz && - (!rv0->bytes.sz || - MEMCMP(rv0->str.p, ==, rv1->str.p, rv1->str.sz))); + return (rv0->text.sz == rv1->text.sz && + (!rv0->text.sz || + MEMCMP(rv0->text.p, ==, rv1->text.p, rv1->text.sz))); } static int eq_bytes(const union tvec_regval *rv0, @@ -2111,22 +2635,52 @@ static int eq_bytes(const union tvec_regval *rv0, MEMCMP(rv0->bytes.p, ==, rv1->bytes.p, rv1->bytes.sz))); } -static int tobuf_string(buf *b, const union tvec_regval *rv, - const struct tvec_regdef *rd) - { return (buf_putmem32l(b, rv->str.p, rv->str.sz)); } +/* --- @tobuf_text@, @tobuf_bytes@ --- * + * + * Arguments: @buf *b@ = buffer + * @const union tvec_regval *rv@ = register value + * @const struct tvec_regdef *rd@ = register definition + * + * Returns: Zero on success, %$-1$% on failure. + * + * Use: Serialize a register value to a buffer. + * + * Text and binary string values are serialized as a little- + * endian 64-bit length %$n$% in bytes followed by %$n$% bytes + * of string data. + */ + +static int tobuf_text(buf *b, const union tvec_regval *rv, + const struct tvec_regdef *rd) + { return (buf_putmem64l(b, rv->text.p, rv->text.sz)); } static int tobuf_bytes(buf *b, const union tvec_regval *rv, const struct tvec_regdef *rd) - { return (buf_putmem32l(b, rv->bytes.p, rv->bytes.sz)); } + { return (buf_putmem64l(b, rv->bytes.p, rv->bytes.sz)); } -static int frombuf_string(buf *b, union tvec_regval *rv, - const struct tvec_regdef *rd) +/* --- @frombuf_text@, @frombuf_bytes@ --- * + * + * Arguments: @buf *b@ = buffer + * @union tvec_regval *rv@ = register value + * @const struct tvec_regdef *rd@ = register definition + * + * Returns: Zero on success, %$-1$% on failure. + * + * Use: Deserialize a register value from a buffer. + * + * Text and binary string values are serialized as a little- + * endian 64-bit length %$n$% in bytes followed by %$n$% bytes + * of string data. + */ + +static int frombuf_text(buf *b, union tvec_regval *rv, + const struct tvec_regdef *rd) { const void *p; size_t sz; - p = buf_getmem32l(b, &sz); if (!p) return (-1); - tvec_allocstring(rv, sz); memcpy(rv->str.p, p, sz); rv->str.p[sz] = 0; + p = buf_getmem64l(b, &sz); if (!p) return (-1); + tvec_alloctext(rv, sz); memcpy(rv->text.p, p, sz); rv->text.p[sz] = 0; return (0); } @@ -2136,11 +2690,23 @@ static int frombuf_bytes(buf *b, union tvec_regval *rv, const void *p; size_t sz; - p = buf_getmem32l(b, &sz); if (!p) return (-1); + p = buf_getmem64l(b, &sz); if (!p) return (-1); tvec_allocbytes(rv, sz); memcpy(rv->bytes.p, p, sz); return (0); } +/* --- @check_string_length@ --- * + * + * Arguments: @size_t sz@ = found string length + * @const struct tvec_urange *ur@ = acceptable range + * @struct tvec_state *tv@ = test-vector state + * + * Returns: Zero on success, %$-1$% on error. + * + * Use: Checks that @sz@ is within the bounds described by @ur@, + * reporting an error if not. + */ + static int check_string_length(size_t sz, const struct tvec_urange *ur, struct tvec_state *tv) { @@ -2151,15 +2717,58 @@ static int check_string_length(size_t sz, const struct tvec_urange *ur, return (0); } -static int parse_string(union tvec_regval *rv, const struct tvec_regdef *rd, - struct tvec_state *tv) +/* --- @parse_text@, @parse_bytes@ --- * + * + * Arguments: @union tvec_regval *rv@ = register value + * @const struct tvec_regdef *rd@ = register definition + * @struct tvec_state *tv@ = test-vector state + * + * Returns: Zero on success, %$-1$% on error. + * + * Use: Parse a register value from an input file. + * + * The input format for both kinds of strings is basically the + * same: a `compound string', consisting of + * + * * single-quoted strings, which are interpreted entirely + * literally, but can't contain single quotes or newlines; + * + * * double-quoted strings, in which `%|\|%'-escapes are + * interpreted as for characters; + * + * * character names, marked by an initial `%|#|%' sign; + * + * * special tokens marked by an initial `%|!|%' sign; or + * + * * barewords interpreted according to the current coding + * scheme. + * + * The special tokens are + * + * * `%|!bare|%', which causes subsequent sequences of + * barewords to be treated as plain text; + * + * * `%|!hex|%', `%|!base32|%', `%|!base64|%', which cause + * subsequent barewords to be decoded in the requested + * manner. + * + * * `%|!repeat|% %$n$% %|{|% %%\textit{string}%% %|}|%', + * which includes %$n$% copies of the (compound) string. + * + * The only difference between text and binary strings is that + * the initial coding scheme is %|bare|% for text strings and + * %|hex|% for binary strings. + */ + +static int parse_text(union tvec_regval *rv, const struct tvec_regdef *rd, + struct tvec_state *tv) { - void *p = rv->str.p; + void *p = rv->text.p; - if (read_compound_string(&p, &rv->str.sz, TVCODE_BARE, 0, tv)) + if (read_compound_string(&p, &rv->text.sz, TVCODE_BARE, 0, tv)) return (-1); - rv->str.p = p; - if (check_string_length(rv->str.sz, rd->arg.p, tv)) return (-1); + rv->text.p = p; + if (check_string_length(rv->text.sz, rd->arg.p, tv)) return (-1); return (0); } @@ -2175,19 +2784,45 @@ static int parse_bytes(union tvec_regval *rv, const struct tvec_regdef *rd, return (0); } -static void dump_string(const union tvec_regval *rv, - const struct tvec_regdef *rd, - unsigned style, - const struct gprintf_ops *gops, void *go) +/* --- @dump_text@, @dump_bytes@ --- * + * + * Arguments: @const union tvec_regval *rv@ = register value + * @const struct tvec_regdef *rd@ = register definition + * @unsigned style@ = output style (@TVSF_...@) + * @const struct gprintf_ops *gops@, @void *gp@ = format output + * + * Returns: --- + * + * Use: Dump a register value to the format output. + * + * Text string values are dumped as plain text, in double quotes + * if necessary, and using backslash escape sequences for + * nonprintable characters. Unless compact output is requested, + * strings consisting of multiple lines are dumped with each + * line of the string on a separate output line. + * + * Binary string values are dumped in hexadecimal. In compact + * style, the output simply consists of a single block of hex + * digits. Otherwise, the dump is a display consisting of + * groups of hex digits, with comments showing the offset (if + * the string is long enough) and the corresponding plain text. + * + * Empty strings are dumped as %|""|%. + */ + +static void dump_text(const union tvec_regval *rv, + const struct tvec_regdef *rd, + unsigned style, + const struct gprintf_ops *gops, void *go) { const unsigned char *p, *q, *l; unsigned f = 0; #define f_nonword 1u #define f_newline 2u - if (!rv->str.sz) { gprintf(gops, go, "\"\""); return; } + if (!rv->text.sz) { gprintf(gops, go, "\"\""); return; } - p = (const unsigned char *)rv->str.p; l = p + rv->str.sz; + p = (const unsigned char *)rv->text.p; l = p + rv->text.sz; switch (*p) { case '!': case '#': case ';': case '"': case '\'': case '(': case '{': case '[': case ']': case '}': case ')': @@ -2199,7 +2834,7 @@ static void dump_string(const union tvec_regval *rv, if (f&f_newline) { gprintf(gops, go, "\n\t"); goto quote; } else if (f&f_nonword) goto quote; - gops->putm(go, (const char *)p, rv->str.sz); + gops->putm(go, (const char *)p, rv->text.sz); return; quote: @@ -2263,19 +2898,19 @@ static void dump_bytes(const union tvec_regval *rv, } } -const struct tvec_regty tvty_string = { - init_string, release_string, eq_string, - tobuf_string, frombuf_string, - parse_string, dump_string +/* Text and byte string type definitions. */ +const struct tvec_regty tvty_text = { + init_text, release_text, eq_text, + tobuf_text, frombuf_text, + parse_text, dump_text }; - const struct tvec_regty tvty_bytes = { init_bytes, release_bytes, eq_bytes, tobuf_bytes, frombuf_bytes, parse_bytes, dump_bytes }; -/* --- @tvec_claimeq_string@ --- * +/* --- @tvec_claimeq_text@ --- * * * Arguments: @struct tvec_state *tv@ = test-vector state * @const char *p0@, @size_t sz0@ = first string with length @@ -2294,17 +2929,17 @@ const struct tvec_regty tvty_bytes = { * value and @p1@ is printed as the input reference. */ -int tvec_claimeq_string(struct tvec_state *tv, - const char *p0, size_t sz0, - const char *p1, size_t sz1, - const char *file, unsigned lno, const char *expr) +int tvec_claimeq_text(struct tvec_state *tv, + const char *p0, size_t sz0, + const char *p1, size_t sz1, + const char *file, unsigned lno, const char *expr) { - tv->out[0].v.str.p = (/*unconst*/ char *)p0; tv->out[0].v.str.sz = sz0; - tv->in[0].v.str.p =(/*unconst*/ char *) p1; tv->in[0].v.str.sz = sz1; - return (tvec_claimeq(tv, &tvty_string, 0, file, lno, expr)); + tv->out[0].v.text.p = (/*unconst*/ char *)p0; tv->out[0].v.text.sz = sz0; + tv->in[0].v.text.p =(/*unconst*/ char *) p1; tv->in[0].v.text.sz = sz1; + return (tvec_claimeq(tv, &tvty_text, 0, file, lno, expr)); } -/* --- @tvec_claimeq_strz@ --- * +/* --- @tvec_claimeq_textz@ --- * * * Arguments: @struct tvec_state *tv@ = test-vector state * @const char *p0, *p1@ = two strings to compare @@ -2320,15 +2955,15 @@ int tvec_claimeq_string(struct tvec_state *tv, * explicitly. */ -int tvec_claimeq_strz(struct tvec_state *tv, - const char *p0, const char *p1, - const char *file, unsigned lno, const char *expr) +int tvec_claimeq_textz(struct tvec_state *tv, + const char *p0, const char *p1, + const char *file, unsigned lno, const char *expr) { - tv->out[0].v.str.p = (/*unconst*/ char *)p0; - tv->out[0].v.str.sz = strlen(p0); - tv->in[0].v.str.p = (/*unconst*/ char *)p1; - tv->in[0].v.str.sz = strlen(p1); - return (tvec_claimeq(tv, &tvty_string, 0, file, lno, expr)); + tv->out[0].v.text.p = (/*unconst*/ char *)p0; + tv->out[0].v.text.sz = strlen(p0); + tv->in[0].v.text.p = (/*unconst*/ char *)p1; + tv->in[0].v.text.sz = strlen(p1); + return (tvec_claimeq(tv, &tvty_text, 0, file, lno, expr)); } /* --- @tvec_claimeq_bytes@ --- * @@ -2362,7 +2997,7 @@ int tvec_claimeq_bytes(struct tvec_state *tv, return (tvec_claimeq(tv, &tvty_bytes, 0, file, lno, expr)); } -/* --- @tvec_allocstring@, @tvec_allocbytes@ --- * +/* --- @tvec_alloctext@, @tvec_allocbytes@ --- * * * Arguments: @union tvec_regval *rv@ = register value * @size_t sz@ = required size @@ -2378,15 +3013,15 @@ int tvec_claimeq_bytes(struct tvec_state *tv, * the old buffer contents are simply discarded if reallocation * is necessary. Instead, use a @dbuf@ or @dstr@. * - * The @tvec_allocstring@ function sneakily allocates an extra + * The @tvec_alloctext@ function sneakily allocates an extra * byte for a terminating zero. The @tvec_allocbytes@ function * doesn't do this. */ -void tvec_allocstring(union tvec_regval *rv, size_t sz) +void tvec_alloctext(union tvec_regval *rv, size_t sz) { - if (rv->str.sz <= sz) { xfree(rv->str.p); rv->str.p = xmalloc(sz + 1); } - rv->str.sz = sz; + if (rv->text.sz <= sz) { xfree(rv->text.p); rv->text.p = xmalloc(sz + 1); } + rv->text.sz = sz; } void tvec_allocbytes(union tvec_regval *rv, size_t sz) @@ -2397,15 +3032,73 @@ void tvec_allocbytes(union tvec_regval *rv, size_t sz) /*----- Buffer type -------------------------------------------------------*/ +/* Buffers are initialized and released as binary strings. */ + +/* --- @eq_buffer@ --- * + * + * Arguments: @const union tvec_regval *rv0, *rv1@ = register values + * @const struct tvec_regdef *rd@ = register definition + * + * Returns: Nonzero if the values are equal, zero if unequal + * + * Use: Compare register values for equality. + * + * Buffer values are equal if and only if their sizes are equal; + * their contents are %%\emph{not}%% compared. + */ + static int eq_buffer(const union tvec_regval *rv0, const union tvec_regval *rv1, const struct tvec_regdef *rd) { return (rv0->bytes.sz == rv1->bytes.sz); } +/* --- @tobuf_buffer@ --- * + * + * Arguments: @buf *b@ = buffer + * @const union tvec_regval *rv@ = register value + * @const struct tvec_regdef *rd@ = register definition + * + * Returns: Zero on success, %$-1$% on failure. + * + * Use: Serialize a register value to a buffer. + * + * Buffer values are serialized as just their lengths, as + * unsigned integers. + */ + static int tobuf_buffer(buf *b, const union tvec_regval *rv, const struct tvec_regdef *rd) { return (unsigned_to_buf(b, rv->bytes.sz)); } +/* --- @allocate_buffer@ --- * + * + * Arguments: @union tvec_regval *rv@ = register value + * @size_t sz@ = size to allocate + * + * Returns: --- + * + * Use: Allocate @sz@ bytes to the buffer and fill the space with a + * distinctive pattern. + */ + +static void allocate_buffer(union tvec_regval *rv, size_t sz) + { tvec_allocbytes(rv, sz); memset(rv->bytes.p, '?', sz); } + +/* --- @frombuf_buffer@ --- * + * + * Arguments: @buf *b@ = buffer + * @union tvec_regval *rv@ = register value + * @const struct tvec_regdef *rd@ = register definition + * + * Returns: Zero on success, %$-1$% on failure. + * + * Use: Deserialize a register value from a buffer. + * + * Buffer values are serialized as just their lengths, as + * unsigned integers. The buffer is allocated on + * deserialization and filled with a distinctive pattern. + */ + static int frombuf_buffer(buf *b, union tvec_regval *rv, const struct tvec_regdef *rd) { @@ -2413,10 +3106,29 @@ static int frombuf_buffer(buf *b, union tvec_regval *rv, if (unsigned_from_buf(b, &u)) return (-1); if (u > (size_t)-1) return (-1); - tvec_allocbytes(rv, u); memset(rv->bytes.p, '!', u); + allocate_buffer(rv, u); return (0); } +/* --- @parse_buffer@ --- * + * + * Arguments: @union tvec_regval *rv@ = register value + * @const struct tvec_regdef *rd@ = register definition + * @struct tvec_state *tv@ = test-vector state + * + * Returns: Zero on success, %$-1$% on error. + * + * Use: Parse a register value from an input file. + * + * The input format for a buffer value consists of an unsigned + * integer followed by an optional unit specifier consisting of + * an SI unit prefix and (optionally) the letter `B'. Unit + * prefixes denote %%\emph{binary}%% multipliers, not decimal. + * + * The buffer is allocated and filled with a distinctive + * pattern. + */ + static const char units[] = "kMGTPEZY"; static int parse_buffer(union tvec_regval *rv, @@ -2453,7 +3165,7 @@ static int parse_buffer(union tvec_regval *rv, if (check_string_length(u, rd->arg.p, tv)) { rc = -1; goto end; } if (tvec_flushtoeol(tv, 0)) { rc = -1; goto end; } - tvec_allocbytes(rv, u); memset(rv->bytes.p, '?', u); + allocate_buffer(rv, u); rc = 0; end: DDESTROY(&d); return (rc); @@ -2469,6 +3181,22 @@ rangerr: #undef f_range } +/* --- @dump_buffer@ --- * + * + * Arguments: @const union tvec_regval *rv@ = register value + * @const struct tvec_regdef *rd@ = register definition + * @unsigned style@ = output style (@TVSF_...@) + * @const struct gprintf_ops *gops@, @void *gp@ = format output + * + * Returns: --- + * + * Use: Dump a register value to the format output. + * + * Buffer values are dumped as their size with an appropriate + * unit specifier. A unit prefix is only used if the size is an + * exact multiple of the relevant power of two. + */ + static void dump_buffer(const union tvec_regval *rv, const struct tvec_regdef *rd, unsigned style, @@ -2485,6 +3213,7 @@ static void dump_buffer(const union tvec_regval *rv, } } +/* Buffer type definition. */ const struct tvec_regty tvty_buffer = { init_bytes, release_bytes, eq_buffer, tobuf_buffer, frombuf_buffer,