--- /dev/null
+/* -*-c-*-
+ *
+ * Test-vector framework basic register types
+ *
+ * (c) 2024 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of the mLib utilities library.
+ *
+ * mLib is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * mLib is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with mLib. If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#ifndef MLIB_TVEC_TYPES_H
+#define MLIB_TVEC_TYPES_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#ifndef MLIB_TVEC_H
+# include "tvec.h"
+#endif
+
+/*----- Integer types: signed and unsigned --------------------------------*/
+
+/* 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.
+ */
+
+extern const struct tvec_regty tvty_int, tvty_uint;
+
+/* The @arg.p@ slot may be null or a pointer to @struct tvec_irange@ or
+ * @struct tvec_urange@ as appropriate. The bounds are inclusive; use, e.g.,
+ * @LONG_MAX@ explicitly if one or the other bound is logically inapplicable.
+ * If %$m$% is nonzero, then the value must additionally be congruent to
+ * %$a$% modulo %$m$%.
+ */
+struct tvec_irange { long min, max, m, a; };
+struct tvec_urange { unsigned long min, max, m, a; };
+
+/* Bounds corresponding to common integer types. */
+extern const struct tvec_irange
+ tvrange_schar, tvrange_short, tvrange_int, tvrange_long,
+ tvrange_sbyte, tvrange_i16, tvrange_i32;
+extern const struct tvec_urange
+ tvrange_uchar, tvrange_ushort, tvrange_uint, tvrange_ulong, tvrange_size,
+ tvrange_byte, tvrange_u16, tvrange_u32;
+
+/* --- @tvec_claimeq_int@, @TVEC_CLAIMEQ_INT@ --- *
+ *
+ * Arguments: @struct tvec_state *tv@ = test-vector state
+ * @long i0, i1@ = two signed integers
+ * @const char *file@, @unsigned @lno@ = calling file and line
+ * @const char *expr@ = the expression to quote on failure
+ *
+ * Returns: Nonzero if @i0@ and @i1@ are equal, otherwise zero.
+ *
+ * Use: Check that values of @i0@ and @i1@ are equal. As for
+ * @tvec_claim@ above, a test case is automatically begun and
+ * ended if none is already underway. If the values are
+ * unequal, then @tvec_fail@ is called, quoting @expr@, and the
+ * mismatched values are dumped: @i0@ is printed as the output
+ * value and @i1@ is printed as the input reference.
+ *
+ * The @TVEC_CLAIM_INT@ macro is similar, only it (a) identifies
+ * the file and line number of the call site automatically, and
+ * (b) implicitly quotes the source text of the @i0@ and @i1@
+ * arguments in the failure message.
+ */
+
+extern int tvec_claimeq_int(struct tvec_state */*tv*/,
+ long /*i0*/, long /*i1*/,
+ const char */*file*/, unsigned /*lno*/,
+ const char */*expr*/);
+#define TVEC_CLAIMEQ_INT(tv, i0, i1) \
+ (tvec_claimeq_int(tv, i0, i1, __FILE__, __LINE__, #i0 " /= " #i1))
+
+/* --- @tvec_claimeq_uint@, @TVEC_CLAIMEQ_UINT@ --- *
+ *
+ * Arguments: @struct tvec_state *tv@ = test-vector state
+ * @unsigned long u0, u1@ = two unsigned integers
+ * @const char *file@, @unsigned @lno@ = calling file and line
+ * @const char *expr@ = the expression to quote on failure
+ *
+ * Returns: Nonzero if @u0@ and @u1@ are equal, otherwise zero.
+ *
+ * Use: Check that values of @u0@ and @u1@ are equal. As for
+ * @tvec_claim@ above, a test case is automatically begun and
+ * ended if none is already underway. If the values are
+ * unequal, then @tvec_fail@ is called, quoting @expr@, and the
+ * mismatched values are dumped: @u0@ is printed as the output
+ * value and @u1@ is printed as the input reference.
+ *
+ * The @TVEC_CLAIM_UINT@ macro is similar, only it (a)
+ * identifies the file and line number of the call site
+ * automatically, and (b) implicitly quotes the source text of
+ * the @u0@ and @u1@ arguments in the failure message.
+ */
+
+extern int tvec_claimeq_uint(struct tvec_state */*tv*/,
+ unsigned long /*u0*/, unsigned long /*u1*/,
+ const char */*file*/, unsigned /*lno*/,
+ const char */*expr*/);
+#define TVEC_CLAIMEQ_UINT(tv, u0, u1) \
+ (tvec_claimeq_uint(tv, u0, u1, __FILE__, __LINE__, #u0 " /= " #u1))
+
+/*----- Size type ---------------------------------------------------------*/
+
+/* A size is an unsigned integer followed by an optional unit specifier
+ * consisting of an SI unit prefix and (optionally) the letter `B'.
+ */
+
+extern const struct tvec_regty tvty_size;
+
+/* --- @tvec_claimeq_size@ --- *
+ *
+ * Arguments: @struct tvec_state *tv@ = test-vector state
+ * @unsigned long sz0, sz1@ = two sizes
+ * @const char *file@, @unsigned @lno@ = calling file and line
+ * @const char *expr@ = the expression to quote on failure
+ *
+ * Returns: Nonzero if @sz0@ and @sz1@ are equal, otherwise zero.
+ *
+ * Use: Check that values of @u0@ and @u1@ are equal. As for
+ * @tvec_claim@ above, a test case is automatically begun and
+ * ended if none is already underway. If the values are
+ * unequal, then @tvec_fail@ is called, quoting @expr@, and the
+ * mismatched values are dumped: @u0@ is printed as the output
+ * value and @u1@ is printed as the input reference.
+ *
+ * The @TVEC_CLAIM_SIZE@ macro is similar, only it (a)
+ * identifies the file and line number of the call site
+ * automatically, and (b) implicitly quotes the source text of
+ * the @u0@ and @u1@ arguments in the failure message.
+ */
+
+int tvec_claimeq_size(struct tvec_state *tv,
+ unsigned long sz0, unsigned long sz1,
+ const char *file, unsigned lno, const char *expr);
+#define TVEC_CLAIMEQ_UINT(tv, u0, u1) \
+ (tvec_claimeq_uint(tv, u0, u1, __FILE__, __LINE__, #u0 " /= " #u1))
+
+/*----- Floating-point type -----------------------------------------------*/
+
+/* 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.
+ *
+ * The comparison rules for floating-point numbers are complex: see
+ * @tvec_claimeqish_float@ for details.
+ */
+
+extern const struct tvec_regty tvty_float;
+
+struct tvec_floatinfo {
+ /* Details about acceptable floating-point values. */
+
+ unsigned f; /* flags (@TVFF_...@ bits) */
+#define TVFF_NOMIN 0x0001 /* ignore @min@ (allow -∞) */
+#define TVFF_NOMAX 0x0002 /* ignore @max@ (allow +∞) */
+#define TVFF_NANOK 0x0004 /* permit NaN */
+#define TVFF_NEGINFOK 0x0008 /* permit -∞, check finite */
+#define TVFF_POSINFOK 0x0010 /* permit +∞, check finite */
+#define TVFF_INFOK (TVFF_NEGINFOK | TVFF_POSINFOK) /* permit ±∞ */
+#define TVFF_EQMASK 0x0f00 /* how to compare */
+#define TVFF_EXACT 0x0000 /* must equal exactly */
+#define TVFF_ABSDELTA 0x0100 /* must be within @delta@ */
+#define TVFF_RELDELTA 0x0200 /* diff < @delta@ fraction */
+ double min, max; /* smallest/largest value allowed */
+ double delta; /* maximum tolerable difference */
+};
+
+extern const struct tvec_floatinfo
+ tvflt_float, tvflt_double, tvflt_finite, tvflt_nonneg;
+
+/* --- @tvec_claimeqish_float@, @TVEC_CLAIMEQISH_FLOAT@ --- *
+ *
+ * Arguments: @struct tvec_state *tv@ = test-vector state
+ * @double f0, f1@ = two floating-point numbers
+ * @unsigned f@ = flags (@TVFF_...@)
+ * @double delta@ = maximum tolerable difference
+ * @const char *file@, @unsigned @lno@ = calling file and line
+ * @const char *expr@ = the expression to quote on failure
+ *
+ * Returns: Nonzero if @f0@ and @f1@ are sufficiently close, otherwise
+ * zero.
+ *
+ * Use: Check that values of @f0@ and @f1@ are sufficiently close.
+ * As for @tvec_claim@ above, a test case is automatically begun
+ * and ended if none is already underway. If the values are
+ * too far apart, then @tvec_fail@ is called, quoting @expr@,
+ * and the mismatched values are dumped: @f0@ is printed as the
+ * output value and @f1@ is printed as the input reference.
+ *
+ * The details for the comparison are as follows.
+ *
+ * * A NaN value matches any other NaN, and nothing else.
+ *
+ * * An infinity matches another infinity of the same sign,
+ * and nothing else.
+ *
+ * * If @f&TVFF_EQMASK@ is @TVFF_EXACT@, then any
+ * representable number matches only itself: in particular,
+ * positive and negative zero are considered distinct.
+ * (This allows tests to check that they land on the correct
+ * side of branch cuts, for example.)
+ *
+ * * If @f&TVFF_EQMASK@ is @TVFF_ABSDELTA@, then %$x$% matches
+ * %$y$% when %$|x - y| < \delta$%.
+ *
+ * * If @f&TVFF_EQMASK@ is @TVFF_RELDELTA@, then %$x$% matches
+ * %$y$% when %$|1 - x/y| < \delta$%. (Note that this
+ * criterion is asymmetric. Write %$x \approx_\delta y$%
+ * if and only if %$|1 - x/y < \delta$%. Then, for example,
+ * if %$y/(1 + \delta) < x < y (1 - \delta)$%, then
+ * %$x \approx_\delta y$%, but %$y \not\approx_\delta x$%.)
+ *
+ * The @TVEC_CLAIM_FLOAT@ macro is similar, only it (a)
+ * identifies the file and line number of the call site
+ * automatically, and (b) implicitly quotes the source text of
+ * the @f0@ and @f1@ arguments (and @delta@) in the failure
+ * message.
+ */
+
+extern int tvec_claimeqish_float(struct tvec_state */*tv*/,
+ double /*f0*/, double /*f1*/,
+ unsigned /*f*/, double /*delta*/,
+ const char */*file*/, unsigned /*lno*/,
+ const char */*expr*/);
+#define TVEC_CLAIMEQISH_FLOAT(tv, f0, f1, f, delta) \
+ (tvec_claimeqish_float(tv, f0, f1, f, delta, __FILE__, __LINE__, \
+ #f0 " /= " #f1 " (+/- " #delta ")"))
+
+/* --- @tvec_claimeq_float@, @TVEC_CLAIMEQ_FLOAT@ --- *
+ *
+ * Arguments: @struct tvec_state *tv@ = test-vector state
+ * @double f0, f1@ = two floating-point numbers
+ * @const char *file@, @unsigned @lno@ = calling file and line
+ * @const char *expr@ = the expression to quote on failure
+ *
+ * Returns: Nonzero if @f0@ and @u1@ are identical, otherwise zero.
+ *
+ * Use: Check that values of @f0@ and @f1@ are identical. The
+ * function is exactly equivalent to @tvec_claimeqish_float@
+ * with @f == TVFF_EXACT@; the macro is similarly like
+ * @TVEC_CLAIMEQISH_FLOAT@ with @f == TVFF_EXACT@, except that
+ * it doesn't bother to quote a delta.
+ */
+
+extern int tvec_claimeq_float(struct tvec_state */*tv*/,
+ double /*f0*/, double /*f1*/,
+ const char */*file*/, unsigned /*lno*/,
+ const char */*expr*/);
+#define TVEC_CLAIMEQ_FLOAT(tv, f0, f1) \
+ (tvec_claimeq_float(tv, f0, f1, __FILE__, __LINE__, #f0 " /= " #f1))
+
+/*----- Durations ---------------------------------------------------------*/
+
+/* A duration measures a time interval in seconds. The input format consists
+ * of a nonnegative decimal floating-point number in @strtod@ format followed
+ * by an optional unit specification.
+ */
+
+extern const struct tvec_regty tvty_duration;
+
+/* --- @tvec_parsedurunit@ --- *
+ *
+ * Arguments: @double *scale_out@ = where to leave the scale
+ * @const char **p_inout@ = input unit string, updated
+ *
+ * Returns: Zero on success, %$-1$% on error.
+ *
+ * Use: If @*p_inout@ begins with a unit string followed by the end
+ * of the string or some non-alphanumeric character, then store
+ * the corresponding scale factor in @*scale_out@, advance
+ * @*p_inout@ past the unit string, and return zero. Otherwise,
+ * return %$-1$%.
+ */
+
+extern int tvec_parsedurunit(double */*scale_out*/,
+ const char **/*p_inout*/);
+
+/* --- @tvec_claimeqish_duration@, @TVEC_CLAIMEQISH_DURATION@ --- *
+ *
+ * Arguments: @struct tvec_state *tv@ = test-vector state
+ * @double t0, t1@ = two durations
+ * @unsigned f@ = flags (@TVFF_...@)
+ * @double delta@ = maximum tolerable difference
+ * @const char *file@, @unsigned @lno@ = calling file and line
+ * @const char *expr@ = the expression to quote on failure
+ *
+ * Returns: Nonzero if @t0@ and @t1@ are sufficiently close, otherwise
+ * zero.
+ *
+ * Use: Check that values of @t0@ and @t1@ are sufficiently close.
+ * This is essentially the same as @tvec_claimeqish_float@, only
+ * it dumps the values as durations on a mismatch.
+ *
+ * The @TVEC_CLAIM_FLOAT@ macro is similar, only it (a)
+ * identifies the file and line number of the call site
+ * automatically, and (b) implicitly quotes the source text of
+ * the @t0@ and @t1@ arguments (and @delta@) in the failure
+ * message.
+ */
+
+extern int tvec_claimeqish_duration(struct tvec_state */*tv*/,
+ double /*t0*/, double /*t1*/,
+ unsigned /*f*/, double /*delta*/,
+ const char */*file*/, unsigned /*lno*/,
+ const char */*expr*/);
+#define TVEC_CLAIMEQISH_DURATION(tv, t0, t1, f, delta) \
+ (tvec_claimeqish_duration(tv, t0, t1, f, delta, __FILE__, __LINE__, \
+ #t0 " /= " #t1 " (+/- " #delta ")"))
+
+/* --- @tvec_claimeq_duration@, @TVEC_CLAIMEQ_DURATION@ --- *
+ *
+ * Arguments: @struct tvec_state *tv@ = test-vector state
+ * @double t0, t1@ = two durations
+ * @const char *file@, @unsigned @lno@ = calling file and line
+ * @const char *expr@ = the expression to quote on failure
+ *
+ * Returns: Nonzero if @t0@ and @t1@ are identical, otherwise zero.
+ *
+ * Use: Check that values of @t0@ and @t1@ are identical. The
+ * function is exactly equivalent to @tvec_claimeqish_duration@
+ * with @f == TVFF_EXACT@; the macro is similarly like
+ * @TVEC_CLAIMEQISH_DURATION@ with @f == TVFF_EXACT@, except
+ * that it doesn't bother to quote a delta.
+ */
+
+int tvec_claimeq_duration(struct tvec_state */*tv*/,
+ double /*t0*/, double /*t1*/,
+ const char */*file*/, unsigned /*lno*/,
+ const char */*expr*/);
+#define TVEC_CLAIMEQ_DURATION(tv, t0, t1) \
+ (tvec_claimeq_float(tv, t0, t1, __FILE__, __LINE__, #t0 " /= " #t1))
+
+/*----- Enumerated types --------------------------------------------------*/
+
+/* An enumeration describes a set of values of some underlying type, each of
+ * which has a symbolic name. Values outside of the defined set can occur --
+ * on output, because of bugs in the tested code, or on input to test
+ * handling of unexpected values.
+ *
+ * There is a distinct enumerated type for each of the branches of
+ * @tvec_misc@. In the following, we write @t@ for the type code, which is
+ * @i@ for signed integer, @u@ for unsigned integer, @f@ for floating-point,
+ * and @p@ for pointer.
+ *
+ * On input, 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. On output, names are preferred (with the underlying value given
+ * in a comment).
+ */
+
+#define DEFENUMTY(tag, ty, slot) \
+ extern const struct tvec_regty tvty_##slot##enum;
+TVEC_MISCSLOTS(DEFENUMTY)
+#undef DEFENUMTY
+
+/* A @struct tvec_tassoc@ associates a string tag with a value. */
+#define DEFASSOC(tag_, ty, slot) \
+ struct tvec_##slot##assoc { const char *tag; ty slot; };
+TVEC_MISCSLOTS(DEFASSOC)
+#undef DEFASSOC
+
+#define TVEC_ENDENUM { 0, 0 }
+
+/* Information about an enumerated type. */
+#define DEFINFO(tag, ty, slot) \
+ struct tvec_##slot##enuminfo { \
+ const char *name; /* type name for diagnostics */ \
+ const struct tvec_##slot##assoc *av; /* name/value mappings */ \
+ EXTRA_##tag##_INFOSLOTS /* type-specific extra info */ \
+ };
+
+#define EXTRA_INT_INFOSLOTS \
+ const struct tvec_irange *ir; /* allowed range of raw values */
+
+#define EXTRA_UINT_INFOSLOTS \
+ const struct tvec_urange *ur; /* allowed range of raw values */
+
+#define EXTRA_FLT_INFOSLOTS \
+ const struct tvec_floatinfo *fi; /* range and matching policy */
+
+#define EXTRA_PTR_INFOSLOTS /* (nothing) */
+
+TVEC_MISCSLOTS(DEFINFO)
+
+#undef EXTRA_INT_INFOSLOTS
+#undef EXTRA_UINT_INFOSLOTS
+#undef EXTRA_FLT_INFOSLOTS
+#undef EXTRA_PTR_INFOSLOTS
+
+#undef DEFINFO
+
+/* Standard enumerations. */
+extern const struct tvec_ienuminfo tvenum_bool;
+extern const struct tvec_ienuminfo tvenum_cmp;
+
+/* --- @tvec_claimeq_tenum@, @TVEC_CLAIMEQ_TENUM@ --- *
+ *
+ * Arguments: @struct tvec_state *tv@ = test-vector state
+ * @const struct tvec_typeenuminfo *ei@ = enumeration type info
+ * @ty t0, t1@ = two values
+ * @const char *file@, @unsigned @lno@ = calling file and line
+ * @const char *expr@ = the expression to quote on failure
+ *
+ * Returns: Nonzero if @t0@ and @t1@ are equal, otherwise zero.
+ *
+ * Use: Check that values of @t0@ and @t1@ are equal. As for
+ * @tvec_claim@ above, a test case is automatically begun and
+ * ended if none is already underway. If the values are
+ * unequal, then @tvec_fail@ is called, quoting @expr@, and the
+ * mismatched values are dumped: @t0@ is printed as the output
+ * value and @t1@ is printed as the input reference.
+ *
+ * The @TVEC_CLAIM_TENUM@ macro is similar, only it (a)
+ * identifies the file and line number of the call site
+ * automatically, and (b) implicitly quotes the source text of
+ * the @t0@ and @t1@ arguments in the failure message.
+ */
+
+#define DECLCLAIM(tag, ty, slot) \
+ extern int tvec_claimeq_##slot##enum \
+ (struct tvec_state */*tv*/, \
+ const struct tvec_##slot##enuminfo */*ei*/, \
+ ty /*t0*/, ty /*t1*/, \
+ const char */*file*/, unsigned /*lno*/, const char */*expr*/);
+TVEC_MISCSLOTS(DECLCLAIM)
+#undef DECLCLAIM
+#define TVEC_CLAIMEQ_IENUM(tv, ei, i0, i1) \
+ (tvec_claimeq_ienum(tv, ei, i0, i1, \
+ __FILE__, __LINE__, #i0 " /= " #i1))
+#define TVEC_CLAIMEQ_UENUM(tv, ei, u0, u1) \
+ (tvec_claimeq_uenum(tv, ei, u0, u1, \
+ __FILE__, __LINE__, #u0 " /= " #u1))
+#define TVEC_CLAIMEQ_FENUM(tv, ei, f0, f1) \
+ (tvec_claimeq_fenum(tv, ei, f0, f1, \
+ __FILE__, __LINE__, #f0 " /= " #f1))
+#define TVEC_CLAIMEQ_PENUM(tv, ei, p0, p1) \
+ (tvec_claimeq_penum(tv, ei, p0, p1, \
+ __FILE__, __LINE__, #p0 " /= " #p1))
+
+/*----- Flags type --------------------------------------------------------*/
+
+/* A flags value packs a number of fields into a single nonnegative integer.
+ * Symbolic names are associated with the possible values of the various
+ * fields; more precisely, each name is associated with a value and a
+ * covering bitmask.
+ *
+ * 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.
+ *
+ * On 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.
+ */
+
+extern const struct tvec_regty tvty_flags;
+
+struct tvec_flag {
+ /* Definition of a single flag or bitfield value.
+ *
+ * Each named setting comes with a value @v@ and a mask @m@; the mask
+ * should cover all of the value bits, i.e., @(v&~m) == 0@.
+ */
+
+ const char *tag; /* name */
+ unsigned long m, v; /* mask and value */
+};
+
+#define TVEC_ENDFLAGS { 0, 0, 0 }
+
+struct tvec_flaginfo {
+ /* Information about a flags type. */
+
+ const char *name; /* type name for diagnostics */
+ const struct tvec_flag *fv; /* name/mask/value mappings */
+ const struct tvec_urange *range; /* permitted range for literals */
+};
+
+/* --- @tvec_claimeq_flags@, @TVEC_CLAIMEQ_FLAGS@ --- *
+ *
+ * Arguments: @struct tvec_state *tv@ = test-vector state
+ * @const struct tvec_flaginfo *fi@ = flags type info
+ * @unsigned long f0, f1@ = two values
+ * @const char *file@, @unsigned @lno@ = calling file and line
+ * @const char *expr@ = the expression to quote on failure
+ *
+ * Returns: Nonzero if @f0@ and @f1@ are equal, otherwise zero.
+ *
+ * Use: Check that values of @f0@ and @f1@ are equal. As for
+ * @tvec_claim@ above, a test case is automatically begun and
+ * ended if none is already underway. If the values are
+ * unequal, then @tvec_fail@ is called, quoting @expr@, and the
+ * mismatched values are dumped: @f0@ is printed as the output
+ * value and @f1@ is printed as the input reference.
+ *
+ * The @TVEC_CLAIM_FLAGS@ macro is similar, only it (a)
+ * identifies the file and line number of the call site
+ * automatically, and (b) implicitly quotes the source text of
+ * the @f0@ and @f1@ arguments in the failure message.
+ */
+
+extern int tvec_claimeq_flags(struct tvec_state */*tv*/,
+ const struct tvec_flaginfo */*fi*/,
+ unsigned long /*f0*/, unsigned long /*f1*/,
+ const char */*file*/, unsigned /*lno*/,
+ const char */*expr*/);
+#define TVEC_CLAIMEQ_FLAGS(tv, fi, f0, f1) \
+ (tvec_claimeq_flags(tv, fi, f0, f1, \
+ __FILE__, __LINE__, #f0 " /= " #f1))
+
+/*----- Character type ----------------------------------------------------*/
+
+/* A character value holds a character, as read by @fgetc@. The special
+ * @EOF@ value can also be represented.
+ *
+ * On input, 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.
+ */
+
+extern const struct tvec_regty tvty_char;
+
+/* --- @tvec_claimeq_char@, @TVEC_CLAIMEQ_CHAR@ --- *
+ *
+ * Arguments: @struct tvec_state *tv@ = test-vector state
+ * @int ch0, ch1@ = two character codes
+ * @const char *file@, @unsigned @lno@ = calling file and line
+ * @const char *expr@ = the expression to quote on failure
+ *
+ * Returns: Nonzero if @ch0@ and @ch1@ are equal, otherwise zero.
+ *
+ * Use: Check that values of @ch0@ and @ch1@ are equal. As for
+ * @tvec_claim@ above, a test case is automatically begun and
+ * ended if none is already underway. If the values are
+ * unequal, then @tvec_fail@ is called, quoting @expr@, and the
+ * mismatched values are dumped: @ch0@ is printed as the output
+ * value and @ch1@ is printed as the input reference.
+ *
+ * The @TVEC_CLAIM_CHAR@ macro is similar, only it (a)
+ * identifies the file and line number of the call site
+ * automatically, and (b) implicitly quotes the source text of
+ * the @ch0@ and @ch1@ arguments in the failure message.
+ */
+
+extern int tvec_claimeq_char(struct tvec_state */*tv*/,
+ int /*ch0*/, int /*ch1*/,
+ const char */*file*/, unsigned /*lno*/,
+ const char */*expr*/);
+#define TVEC_CLAIMEQ_CHAR(tv, c0, c1) \
+ (tvec_claimeq_char(tv, c0, c1, __FILE__, __LINE__, #c0 " /= " #c1))
+
+/*----- Text and binary string types --------------------------------------*/
+
+/* A string is a sequence of octets. Text and binary strings differ
+ * primarily in presentation: text strings are shown as raw characters where
+ * possible; binary strings are shown as hex dumps with an auxiliary text
+ * display. Storage for strings always uses the standard C library
+ * allocator, though applications will probably need to call @malloc@ or
+ * @free@ only rarely.
+ *
+ * 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.
+ *
+ * Either kind of string can contain internal nul characters. A trailing nul
+ * is appended -- beyond the stated input length -- to input strings as a
+ * convenience to test functions. Test functions may include such a nul
+ * character on output but this is not checked by the equality test.
+ *
+ * A @struct tvec_urange@ may be supplied as an argument: the length of the
+ * string (in bytes) will be checked against the permitted range.
+ */
+
+extern const struct tvec_regty tvty_text, tvty_bytes;
+
+/* --- @tvec_alloctext@, @tvec_allocbytes@ --- *
+ *
+ * Arguments: @union tvec_regval *rv@ = register value
+ * @size_t sz@ = required size
+ *
+ * Returns: ---
+ *
+ * Use: Allocated space in a text or binary string register. If the
+ * current register size is sufficient, its buffer is left
+ * alone; otherwise, the old buffer, if any, is freed and a
+ * fresh buffer allocated. These functions are not intended to
+ * be used to adjust a buffer repeatedly, e.g., while building
+ * output incrementally: (a) they will perform badly, and (b)
+ * the old buffer contents are simply discarded if reallocation
+ * is necessary. Instead, use a @dbuf@ or @dstr@.
+ *
+ * The @tvec_alloctext@ function sneakily allocates an extra
+ * byte for a terminating zero. The @tvec_allocbytes@ function
+ * doesn't do this.
+ */
+
+extern void tvec_alloctext(union tvec_regval */*rv*/, size_t /*sz*/);
+extern void tvec_allocbytes(union tvec_regval */*rv*/, size_t /*sz*/);
+
+/* --- @tvec_claimeq_text@, @TVEC_CLAIMEQ_TEXT@ --- *
+ *
+ * Arguments: @struct tvec_state *tv@ = test-vector state
+ * @const char *p0@, @size_t sz0@ = first string with length
+ * @const char *p1@, @size_t sz1@ = second string with length
+ * @const char *file@, @unsigned @lno@ = calling file and line
+ * @const char *expr@ = the expression to quote on failure
+ *
+ * Returns: Nonzero if the strings at @p0@ and @p1@ are equal, otherwise
+ * zero.
+ *
+ * Use: Check that strings at @p0@ and @p1@ are equal. As for
+ * @tvec_claim@ above, a test case is automatically begun and
+ * ended if none is already underway. If the values are
+ * unequal, then @tvec_fail@ is called, quoting @expr@, and the
+ * mismatched values are dumped: @p0@ is printed as the output
+ * value and @p1@ is printed as the input reference.
+ *
+ * The @TVEC_CLAIM_TEXT@ macro is similar, only it (a)
+ * identifies the file and line number of the call site
+ * automatically, and (b) implicitly quotes the source text of
+ * the @ch0@ and @ch1@ arguments in the failure message.
+ */
+
+extern 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*/);
+#define TVEC_CLAIMEQ_TEXT(tv, p0, sz0, p1, sz1) \
+ (tvec_claimeq_text(tv, p0, sz0, p1, sz1, __FILE__, __LINE__, \
+ #p0 "[" #sz0 "] /= " #p1 "[" #sz1 "]"))
+
+/* --- @tvec_claimeq_textz@, @TVEC_CLAIMEQ_TEXTZ@ --- *
+ *
+ * Arguments: @struct tvec_state *tv@ = test-vector state
+ * @const char *p0, *p1@ = two strings to compare
+ * @const char *file@, @unsigned @lno@ = calling file and line
+ * @const char *expr@ = the expression to quote on failure
+ *
+ * Returns: Nonzero if the strings at @p0@ and @p1@ are equal, otherwise
+ * zero.
+ *
+ * Use: Check that strings at @p0@ and @p1@ are equal, as for
+ * @tvec_claimeq_string@, except that the strings are assumed
+ * null-terminated, so their lengths don't need to be supplied
+ * explicitly. The macro is similarly like @TVEC_CLAIMEQ_TEXT@.
+ */
+
+extern int tvec_claimeq_textz(struct tvec_state */*tv*/,
+ const char */*p0*/, const char */*p1*/,
+ const char */*file*/, unsigned /*lno*/,
+ const char */*expr*/);
+#define TVEC_CLAIMEQ_TEXTZ(tv, p0, p1) \
+ (tvec_claimeq_textz(tv, p0, p1, __FILE__, __LINE__, #p0 " /= " #p1))
+
+/* --- @tvec_claimeq_bytes@, @TVEC_CLAIMEQ_BYTES@ --- *
+ *
+ * Arguments: @struct tvec_state *tv@ = test-vector state
+ * @const void *p0@, @size_t sz0@ = first string with length
+ * @const void *p1@, @size_t sz1@ = second string with length
+ * @const char *file@, @unsigned @lno@ = calling file and line
+ * @const char *expr@ = the expression to quote on failure
+ *
+ * Returns: Nonzero if the strings at @p0@ and @p1@ are equal, otherwise
+ * zero.
+ *
+ * Use: Check that binary strings at @p0@ and @p1@ are equal. As for
+ * @tvec_claim@ above, a test case is automatically begun and
+ * ended if none is already underway. If the values are
+ * unequal, then @tvec_fail@ is called, quoting @expr@, and the
+ * mismatched values are dumped: @p0@ is printed as the output
+ * value and @p1@ is printed as the input reference.
+ *
+ * The @TVEC_CLAIM_STRING@ macro is similar, only it (a)
+ * identifies the file and line number of the call site
+ * automatically, and (b) implicitly quotes the source text of
+ * the @ch0@ and @ch1@ arguments in the failure message.
+ */
+
+extern int tvec_claimeq_bytes(struct tvec_state */*tv*/,
+ const void */*p0*/, size_t /*sz0*/,
+ const void */*p1*/, size_t /*sz1*/,
+ const char */*file*/, unsigned /*lno*/,
+ const char */*expr*/);
+#define TVEC_CLAIMEQ_BYTES(tv, p0, sz0, p1, sz1) \
+ (tvec_claimeq(tv, p0, sz0, p1, sz1, __FILE__, __LINE__, \
+ #p0 "[" #sz0 "] /= " #p1 "[" #sz1 "]"))
+
+/*----- Buffer type -------------------------------------------------------*/
+
+/* Buffer registers are primarily used for benchmarking. Only a buffer's
+ * allocation parameters are significant: its contents are ignored on
+ * comparison and output, and unspecified on input.
+ *
+ * The input format gives the buffer's size, and an optional alignment
+ * specification, in the form %|SZ [`@' M [`+' A]]|%. Each of %|SZ|%, %|M|%
+ * and %|A|% are sizes, as an integer, optionally suffixed with a unit `kB',
+ * `MB', `GB', `TB', `PB', `EB', `ZB', `YB' (with or without the `B')
+ * denoting a power of 1024. The %|SZ|% gives the (effective) buffer size.
+ * %|M|% is the `alignment quantum' and %|A|% is the `alignment offset'; both
+ * default to zero, but if %|M|% is nonzero then the start of the buffer is
+ * aligned such that it is %|A|% more than a multiple of %|M|% bytes. Note
+ * that %|M|% need not be a power of two, though this is common.
+ *
+ * Units other than `B' are used on output only when the size would be
+ * expressed exactly.
+ *
+ * Buffers are %%\emph{not}%% allocated by default. In benchmarks, this is
+ * best done in a @before@ function.
+ *
+ * No @claimeq@ functions or macros are provided for buffers because they
+ * don't seem very useful.
+ */
+
+extern const struct tvec_regty tvty_buffer;
+
+/* --- @tvec_initbuffer@ --- *
+ *
+ * Arguments: @union tvec_regval *rv@ = register value
+ * @const union tvec_regval *ref@ = reference buffer
+ * @size_t sz@ = size to allocate
+ *
+ * Returns: ---
+ *
+ * Use: Initialize the alignment parameters in @rv@ to match @ref@,
+ * and the size to @sz@.
+ */
+
+extern void tvec_initbuffer(union tvec_regval */*rv*/,
+ const union tvec_regval */*ref*/, size_t /*sz*/);
+
+/* --- @tvec_allocbuffer@ --- *
+ *
+ * Arguments: @union tvec_regval *rv@ = register value
+ *
+ * Returns: ---
+ *
+ * Use: Allocate @sz@ bytes to the buffer and fill the space with a
+ * distinctive pattern.
+ */
+
+extern void tvec_allocbuffer(union tvec_regval */*rv*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif