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); }
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)); }
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)); }
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);
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,
}
}
+/* 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 },
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 },
/*----- 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)
{
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
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)
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)
{
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
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, \
#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 %|#<TYPE PTR>|%, 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, \
}
#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:
#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, \
TVEC_MISCSLOTS(DEFTY_ENUM)
#undef DEFTY_ENUM
+/* Predefined enumeration types. */
static const struct tvec_iassoc bool_assoc[] = {
{ "nil", 0 },
{ "false", 0 },
/*----- 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)
{
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)
{ 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,
{
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++)
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,
/*----- 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)
{
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)
{
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:
#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,
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);
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) {
}
}
+ /* And the character code as an integer. */
if (!(style&TVSF_COMPACT)) {
if (!(f&f_semi)) gprintf(gops, go, " ;");
gprintf(gops, go, " = %ld = ", rv->i);
#undef f_semi
}
+/* Character type definition. */
const struct tvec_regty tvty_char = {
init_int, trivial_release, eq_int,
tobuf_char, frombuf_char,
/*----- 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,
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);
}
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)
{
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);
}
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 ')':
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:
}
}
-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
* 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
* 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@ --- *
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
* 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)
/*----- 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)
{
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,
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);
#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,
}
}
+/* Buffer type definition. */
const struct tvec_regty tvty_buffer = {
init_bytes, release_bytes, eq_buffer,
tobuf_buffer, frombuf_buffer,