X-Git-Url: https://git.distorted.org.uk/~mdw/mLib/blobdiff_plain/882a39c1c269818ed00b1b600180c1d22e8ee0d2..e63124bc579bfd97cfe2f620ddd84df9f20477d8:/test/tvec.h?ds=sidebyside diff --git a/test/tvec.h b/test/tvec.h index 2309edf..2f1cc56 100644 --- a/test/tvec.h +++ b/test/tvec.h @@ -51,6 +51,10 @@ # include "dstr.h" #endif +#ifndef MLIB_GPRINTF_H +# include "gprintf.h" +#endif + #ifndef MLIB_MACROS_H # include "macros.h" #endif @@ -64,7 +68,8 @@ #define TVEC_MISCSLOTS(_) \ _(PTR, const void *, p) /* arbitrary pointer */ \ _(INT, long, i) /* signed integer */ \ - _(UINT, unsigned long, u) /* signed integer */ + _(UINT, unsigned long, u) /* signed integer */ \ + _(FLT, double, f) /* floating point */ union tvec_misc { #define TVEC_DEFSLOT(tag, ty, slot) ty slot; @@ -114,6 +119,7 @@ union tvec_regval { long i; /* signed integer */ unsigned long u; /* unsigned integer */ void *p; /* pointer */ + double f; /* floating point */ struct { unsigned char *p; size_t sz; } bytes; /* binary string of bytes */ struct { char *p; size_t sz; } str; /* text string */ #ifdef TVEC_REGSLOTS @@ -164,21 +170,102 @@ struct tvec_regdef { union tvec_misc arg; /* extra detail for the type */ }; -extern int tvec_serialize(const struct tvec_reg */*rv*/, +/* @TVEC_GREG(vec, i, regsz)@ + * + * If @vec@ is a data pointer which happens to contain the address of a + * vector of @struct tvec_reg@ objects, @i@ is an integer, and @regsz@ is the + * size of a @struct tvec_reg@, then this evaluates to the address of the + * @i@th element of the vector. + * + * This is the general tool you need for accessing register vectors when you + * don't have absolute knowledge of the size of a @union tvec_regval@. + * Usually you want to access one of the register vectors in a @struct + * tvec_state@, and @TVEC_REG@ will be more convenient. + */ +#define TVEC_GREG(vec, i, regsz) \ + ((struct tvec_reg *)((unsigned char *)(vec) + (i)*(regsz))) + +/*------ Serialization utilities ------------------------------------------*/ + +/* --- @tvec_serialize@ --- * + * + * Arguments: @const struct tvec_reg *rv@ = vector of registers + * @buf *b@ = buffer to write on + * @const struct tvec_regdef *regs@ = vector of register + * descriptions, terminated by an entry with a null + * @name@ slot + * @unsigned nr@ = number of entries in the @rv@ vector + * @size_t regsz@ = true size of each element of @rv@ + * + * Returns: Zero on success, @-1@ on failure. + * + * Use: Serialize a collection of register values. + * + * The `candidate register definitions' are those entries @r@ in + * the @regs@ vector whose index @r.i@ is strictly less than + * @nr@. The `selected register definitions' are those + * candidate register definitions @r@ for which the indicated + * register @rv[r.i]@ has the @TVRF_LIVE@ flag set. The + * serialized output begins with a header bitmap: if there are + * %$n$% candidate register definitions then the header bitmap + * consists of %$\lceil n/8 \rceil$% bytes. Bits are ordered + * starting from the least significant bit of the first byte, + * end ending at the most significant bit of the final byte. + * The bit corresponding to a candidate register definition is + * set if and only if that register defintion is selected. The + * header bitmap is then followed by the serializations of the + * selected registers -- i.e., for each selected register + * definition @r@, the serialized value of register @rv[r.i]@ -- + * simply concatenated together, with no padding or alignment. + * + * The serialized output is written to the buffer @b@. Failure + * can be caused by running out of buffer space, or a failing + * type handler. + */ + +extern int tvec_serialize(const struct tvec_reg */*rv*/, buf */*b*/, const struct tvec_regdef */*regs*/, - unsigned /*nr*/, size_t /*regsz*/, - void **/*p_out*/, size_t */*sz_out*/); + unsigned /*nr*/, size_t /*regsz*/); -extern int tvec_deserialize(struct tvec_reg */*rv*/, +/* --- @tvec_deserialize@ --- * + * + * Arguments: @struct tvec_reg *rv@ = vector of registers + * @buf *b@ = buffer to write on + * @const struct tvec_regdef *regs@ = vector of register + * descriptions, terminated by an entry with a null + * @name@ slot + * @unsigned nr@ = number of entries in the @rv@ vector + * @size_t regsz@ = true size of each element of @rv@ + * + * Returns: Zero on success, @-1@ on failure. + * + * Use: Deserialize a collection of register values. + * + * The size of the register vector @nr@ and the register + * definitions @regs@ must match those used when producing the + * serialization. For each serialized register value, + * deserialize and store the value into the appropriate register + * slot, and set the @TVRF_LIVE@ flag on the register. See + * @tvec_serialize@ for a description of the format. + * + * On successful completion, store the address of the first byte + * after the serialized data in @*end_out@ if @end_out@ is not + * null. Failure results only from a failing register type + * handler. + */ + +extern int tvec_deserialize(struct tvec_reg */*rv*/, buf */*b*/, const struct tvec_regdef */*regs*/, - unsigned /*nr*/, size_t /*regsz*/, - const void */*p*/, size_t /*sz*/); + unsigned /*nr*/, size_t /*regsz*/); /*----- Test state --------------------------------------------------------*/ +/* Possible test outcomes. */ enum { TVOUT_LOSE, TVOUT_SKIP, TVOUT_WIN, TVOUT_LIMIT }; struct tvec_state { + /* The primary state structure for the test vector machinery. */ + unsigned f; /* flags */ #define TVSF_SKIP 1u /* skip this test group */ #define TVSF_OPEN 2u /* test is open */ @@ -186,59 +273,273 @@ struct tvec_state { #define TVSF_ERROR 8u /* an error occurred */ #define TVSF_OUTMASK 0xf0 /* test outcome */ #define TVSF_OUTSHIFT 4 + + /* Registers. Available to execution environments. */ unsigned nrout, nreg; /* number of output/total registers */ size_t regsz; /* size of register entry */ struct tvec_reg *in, *out; /* register vectors */ - char expst, st; /* progress status codes */ + + /* Test groups state. Available to output formatters. */ const struct tvec_test *tests, *test; /* all tests and current test */ + + /* Test scoreboard. Available to output formatters. */ unsigned curr[TVOUT_LIMIT], all[TVOUT_LIMIT], grps[TVOUT_LIMIT]; + + /* Output machinery. */ struct tvec_output *output; /* output formatter */ + + /* Input machinery. Available to type parsers. */ const char *infile; unsigned lno, test_lno; /* input file name, line */ FILE *fp; /* input file stream */ }; -#define TVEC_GREG(vec, i, regsz) \ - ((struct tvec_reg *)((unsigned char *)(vec) + (i)*(regsz))) +/* @TVEC_REG(tv, vec, i)@ + * + * If @tv@ is a pointer to a @struct tvec_state@, @vec@ is either @in@ or + * @out@, and @i@ is an integer, then this evaluates to the address of the + * @i@th register in the selected vector. + */ #define TVEC_REG(tv, vec, i) TVEC_GREG((tv)->vec, (i), (tv)->regsz) /*----- Test descriptions -------------------------------------------------*/ -typedef int tvec_hookfn(struct tvec_state */*tv*/); typedef void tvec_testfn(const struct tvec_reg */*in*/, struct tvec_reg */*out*/, void */*ctx*/); + /* A test function. It should read inputs from @in@ and write outputs to + * @out@. The @TVRF_LIVE@ is set on inputs which are actually present, and + * on outputs which are wanted to test. A test function can set additional + * `gratuitous outputs' by setting @TVRF_LIVE@ on them; clearing + * @TVRF_LIVE@ on a wanted output causes a mismatch. + * + * A test function may be called zero or more times by the environment. In + * particular, it may be called multiple times, though usually by prior + * arrangement with the environment. + * + * The @ctx@ is supplied by the environment's @run@ function (see below). + * The default environment calls the test function once, with a null + * @ctx@. There is no expectation that the environment's context has + * anything to do with the test function's context. + */ + +struct tvec_env { + /* A test environment sets things up for and arranges to run the test. + * + * The caller is responsible for allocating storage for the environment's + * context, based on the @ctxsz@ slot, and freeing it later; this space is + * passed in as the @ctx@ parameter to the remaining functions; if @ctxsz@ + * is zero then @ctx@ is null. + */ + + size_t ctxsz; /* environment context size */ + + int (*setup)(struct tvec_state */*tv*/, const struct tvec_env */*env*/, + void */*pctx*/, void */*ctx*/); + /* Initialize the context; called at the start of a test group. Return + * zero on success, or @-1@ on failure. If setup fails, the context is + * freed, and the test group is skipped. + */ + + int (*set)(struct tvec_state */*tv*/, const char */*var*/, + const struct tvec_env */*env*/, void */*ctx*/); + /* Called when the parser finds a %|@var|%' setting to parse and store + * the value. If @setup@ failed, this is still called (so as to skip the + * value), but @ctx@ is null. + */ + + int (*before)(struct tvec_state */*tv*/, void */*ctx*/); + /* Called prior to running a test. This is the right place to act on any + * `%|@var|%' settings. Return zero on success or @-1@ on failure (which + * causes the test to be skipped). This function is never called if the + * test group is skipped. + */ + + void (*run)(struct tvec_state */*tv*/, tvec_testfn */*fn*/, void */*ctx*/); + /* Run the test. It should either call @tvec_skip@, or run @fn@ one or + * more times. In the latter case, it is responsible for checking the + * outputs, and calling @tvec_fail@ as necessary; @tvec_checkregs@ will + * check the register values against the supplied test vector, while + * @tvec_check@ does pretty much everything necessary. This function is + * never called if the test group is skipped. + */ + + void (*after)(struct tvec_state */*tv*/, void */*ctx*/); + /* Called after running or skipping a test. Typical actions involve + * resetting whatever things were established by @set@. This function is + * never called if the test group is skipped. + */ + + void (*teardown)(struct tvec_state */*tv*/, void */*ctx*/); + /* Tear down the environment: called at the end of a test group. If the + * setup failed, then this function is still called, with a null @ctx@. + */ +}; struct tvec_test { + /* A test description. */ + const char *name; /* name of the test */ const struct tvec_regdef *regs; /* descriptions of the registers */ - tvec_hookfn *preflight; /* check before starting */ - tvec_hookfn *run; /* test runner */ + const struct tvec_env *env; /* environment to run test in */ tvec_testfn *fn; /* test function */ - union tvec_misc arg; /* additional parameter to `run' */ }; + +enum { + /* Register output dispositions. */ + + TVRD_INPUT, /* input-only register */ + TVRD_OUTPUT, /* output-only (input is dead) */ + TVRD_MATCH, /* matching (equal) registers */ + TVRD_FOUND, /* mismatching output register */ + TVRD_EXPECT /* mismatching input register */ +}; + +/* --- @tvec_skipgroup@, @tvec_skipgroup_v@ --- * + * + * Arguments: @struct tvec_state *tv@ = test-vector state + * @const char *excuse@, @va_list ap@ = reason why group skipped + * + * Returns: --- + * + * Use: Skip the current group. This should only be called from a + * test environment @setup@ function; a similar effect occurs if + * the @setup@ function fails. + */ + extern void PRINTF_LIKE(2, 3) - tvec_check(struct tvec_state */*tv*/, const char */*detail*/, ...); -extern void tvec_check_v(struct tvec_state */*tv*/, - const char */*detail*/, va_list */*ap*/); + tvec_skipgroup(struct tvec_state */*tv*/, const char */*excuse*/, ...); +extern void tvec_skipgroup_v(struct tvec_state */*tv*/, + const char */*excuse*/, va_list */*ap*/); -extern int tvec_runtest(struct tvec_state */*tv*/); +/* --- @tvec_skip@, @tvec_skip_v@ --- * + * + * Arguments: @struct tvec_state *tv@ = test-vector state + * @const char *excuse@, @va_list ap@ = reason why test skipped + * + * Returns: --- + * + * Use: Skip the current test. This should only be called from a + * test environment @run@ function; a similar effect occurs if + * the @before@ function fails. + */ -/*----- Input utilities ---------------------------------------------------*/ +extern void PRINTF_LIKE(2, 3) + tvec_skip(struct tvec_state */*tv*/, const char */*excuse*/, ...); +extern void tvec_skip_v(struct tvec_state */*tv*/, + const char */*excuse*/, va_list */*ap*/); -extern void tvec_skipspc(struct tvec_state */*tv*/); +/* --- @tvec_resetoutputs@ --- * + * + * Arguments: @struct tvec_state *tv@ = test-vector state + * + * Returns: --- + * + * Use: Reset (releases and reinitializes) the output registers in + * the test state. This is mostly of use to test environment + * @run@ functions, between invocations of the test function. + */ -#define TVFF_ALLOWANY 1u -extern int tvec_flushtoeol(struct tvec_state */*tv*/, unsigned /*f*/); +extern void tvec_resetoutputs(struct tvec_state */*tv*/); -extern int PRINTF_LIKE(4, 5) - tvec_readword(struct tvec_state */*tv*/, dstr */*d*/, - const char */*delims*/, const char */*expect*/, ...); -extern int tvec_readword_v(struct tvec_state */*tv*/, dstr */*d*/, - const char */*delims*/, const char */*expect*/, - va_list */*ap*/); +/* --- @tvec_checkregs@ --- * + * + * Arguments: @struct tvec_state *tv@ = test-vector state + * + * Returns: Zero on success, @-1@ on mismatch. + * + * Use: Compare the active output registers (according to the current + * test group definition) with the corresponding input register + * values. A mismatch occurs if the two values differ + * (according to the register type's @eq@ method), or if the + * input is live but the output is dead. + * + * This function only checks for a mismatch and returns the + * result; it takes no other action. In particular, it doesn't + * report a failure, or dump register values. + */ -extern int tvec_nexttoken(struct tvec_state */*tv*/); +extern int tvec_checkregs(struct tvec_state */*tv*/); + +/* --- @tvec_fail@, @tvec_fail_v@ --- * + * + * Arguments: @struct tvec_state *tv@ = test-vector state + * @const char *detail@, @va_list ap@ = description of test + * + * Returns: --- + * + * Use: Report the current test as a failure. This function can be + * called multiple times for a single test, e.g., if the test + * environment's @run@ function invokes the test function + * repeatedly; but a single test that fails repeatedly still + * only counts as a single failure in the statistics. The + * @detail@ string and its format parameters can be used to + * distinguish which of several invocations failed; it can + * safely be left null if the test function is run only once. + */ + +extern void PRINTF_LIKE(2, 3) + tvec_fail(struct tvec_state */*tv*/, const char */*detail*/, ...); +extern void tvec_fail_v(struct tvec_state */*tv*/, + const char */*detail*/, va_list */*ap*/); + +/* --- @tvec_dumpreg@ --- * + * + * Arguments: @struct tvec_state *tv@ = test-vector state + * @unsigned disp@ = the register disposition (@TVRD_...@) + * @const union tvec_regval *tv@ = register value + * @const struct tvec_regdef *rd@ = register definition + * + * Returns: --- + * + * Use: Dump a register value to the output. This is the lowest- + * level function for dumping registers, and calls the output + * formatter directly. + * + * Usually @tvec_mismatch@ is much more convenient. Low-level + * access is required for reporting `virtual' registers + * corresponding to test environment settings. + */ + +extern void tvec_dumpreg(struct tvec_state */*tv*/, + unsigned /*disp*/, const union tvec_regval */*rv*/, + const struct tvec_regdef */*rd*/); + +/* --- @tvec_mismatch@ --- * + * + * Arguments: @struct tvec_state *tv@ = test-vector state + * @unsigned f@ = flags (@TVMF_...@) + * + * Returns: --- + * + * Use: Dumps registers suitably to report a mismatch. The flag bits + * @TVMF_IN@ and @TVF_OUT@ select input-only and output + * registers. If both are reset then nothing happens. + * Suppressing the output registers may be useful, e.g., if the + * test function crashed rather than returning outputs. + */ + +#define TVMF_IN 1u +#define TVMF_OUT 2u +extern void tvec_mismatch(struct tvec_state */*tv*/, unsigned /*f*/); + +/* --- @tvec_check@, @tvec_check_v@ --- * + * + * Arguments: @struct tvec_state *tv@ = test-vector state + * @const char *detail@, @va_list ap@ = description of test + * + * Returns: --- + * + * Use: Check the register values, reporting a failure and dumping + * the registers in the event of a mismatch. This just wraps up + * @tvec_checkregs@, @tvec_fail@ and @tvec_mismatch@ in the + * obvious way. + */ + +extern void PRINTF_LIKE(2, 3) + tvec_check(struct tvec_state */*tv*/, const char */*detail*/, ...); +extern void tvec_check_v(struct tvec_state */*tv*/, + const char */*detail*/, va_list */*ap*/); /*----- Session lifecycle -------------------------------------------------*/ @@ -259,21 +560,46 @@ extern int tvec_read(struct tvec_state */*tv*/, /*----- Benchmarking ------------------------------------------------------*/ struct tvec_bench { + struct tvec_env _env; /* benchmarking is an environment */ + struct bench_state **bst; /* benchmark state anchor or null */ unsigned long niter; /* iterations done per unit */ int riter, rbuf; /* iterations and buffer registers */ - size_t ctxsz; /* size of context */ - int (*setup)(const struct tvec_reg */*in*/, struct tvec_reg */*out*/, - const union tvec_misc */*arg*/, void */*ctx*/); /* setup fn */ - void (*teardown)(void */*ctx*/); /* teardown function, or null */ - struct bench_state **b; /* benchmark state anchor or null */ - union tvec_misc arg; /* argument to setup */ + const struct tvec_env *env; /* environment (per test, not grp) */ +}; +#define TVEC_BENCHENV \ + { sizeof(struct tvec_benchctx), \ + tvec_benchsetup, \ + tvec_benchset, \ + tvec_benchbefore, \ + tvec_benchrun, \ + tvec_benchafter, \ + tvec_benchteardown } +#define TVEC_BENCHINIT TVEC_BENCHENV, &tvec_benchstate + +struct tvec_benchctx { + const struct tvec_bench *b; + struct bench_state *bst; + double dflt_target; + void *subctx; }; +struct bench_timing; extern struct bench_state *tvec_benchstate; -extern int tvec_ensurebench(struct tvec_state */*tv*/, - struct bench_state **/*b_out*/); -extern int tvec_bench(struct tvec_state */*tv*/); +extern int tvec_benchsetup(struct tvec_state */*tv*/, + const struct tvec_env */*env*/, + void */*pctx*/, void */*ctx*/); +extern int tvec_benchset(struct tvec_state */*tv*/, const char */*var*/, + const struct tvec_env */*env*/, void */*ctx*/); +extern int tvec_benchbefore(struct tvec_state */*tv*/, void */*ctx*/); +extern void tvec_benchrun(struct tvec_state */*tv*/, + tvec_testfn */*fn*/, void */*ctx*/); +extern void tvec_benchafter(struct tvec_state */*tv*/, void */*ctx*/); +extern void tvec_benchteardown(struct tvec_state */*tv*/, void */*ctx*/); + +extern void tvec_benchreport + (const struct gprintf_ops */*gops*/, void */*go*/, + unsigned /*unit*/, const struct bench_timing */*tm*/); /*----- Ad-hoc testing ----------------------------------------------------*/ @@ -351,14 +677,13 @@ struct tvec_output { struct tvec_state *tv; }; -struct bench_timing; +enum { TVBU_OP, TVBU_BYTE }; struct tvec_outops { void (*error)(struct tvec_output */*o*/, const char */*msg*/, va_list */*ap*/); void (*notice)(struct tvec_output */*o*/, const char */*msg*/, va_list */*ap*/); - void (*write)(struct tvec_output */*o*/, const char */*p*/, size_t /*sz*/); void (*bsession)(struct tvec_output */*o*/); int (*esession)(struct tvec_output */*o*/); @@ -373,11 +698,15 @@ struct tvec_outops { const char */*excuse*/, va_list */*ap*/); void (*fail)(struct tvec_output */*o*/, const char */*detail*/, va_list */*ap*/); - void (*mismatch)(struct tvec_output */*o*/); + void (*dumpreg)(struct tvec_output */*o*/, + unsigned /*disp*/, const union tvec_regval */*rv*/, + const struct tvec_regdef */*rd*/); void (*etest)(struct tvec_output */*o*/, unsigned /*outcome*/); - void (*bbench)(struct tvec_output */*o*/); + void (*bbench)(struct tvec_output */*o*/, + const char */*ident*/, unsigned /*unit*/); void (*ebench)(struct tvec_output */*o*/, + const char */*ident*/, unsigned /*unit*/, const struct bench_timing */*tm*/); void (*destroy)(struct tvec_output */*o*/); @@ -399,31 +728,25 @@ extern int PRINTF_LIKE(3, 4) extern int tvec_syntax_v(struct tvec_state */*tv*/, int /*ch*/, const char */*expect*/, va_list */*ap*/); -extern void PRINTF_LIKE(2, 3) - tvec_skipgroup(struct tvec_state */*tv*/, const char */*note*/, ...); -extern void tvec_skipgroup_v(struct tvec_state */*tv*/, - const char */*note*/, va_list */*ap*/); +extern struct tvec_output *tvec_humanoutput(FILE */*fp*/); +extern struct tvec_output *tvec_tapoutput(FILE */*fp*/); +extern struct tvec_output *tvec_dfltout(FILE */*fp*/); -extern void PRINTF_LIKE(2, 3) - tvec_skip(struct tvec_state */*tv*/, const char */*excuse*/, ...); -extern void tvec_skip_v(struct tvec_state */*tv*/, - const char */*excuse*/, va_list */*ap*/); +/*----- Input utilities ---------------------------------------------------*/ -extern void PRINTF_LIKE(2, 3) - tvec_fail(struct tvec_state */*tv*/, const char */*detail*/, ...); -extern void tvec_fail_v(struct tvec_state */*tv*/, - const char */*detail*/, va_list */*ap*/); +extern void tvec_skipspc(struct tvec_state */*tv*/); -extern void tvec_mismatch(struct tvec_state */*tv*/); +#define TVFF_ALLOWANY 1u +extern int tvec_flushtoeol(struct tvec_state */*tv*/, unsigned /*f*/); -extern void PRINTF_LIKE(2, 3) - tvec_write(struct tvec_state */*tv*/, const char */*p*/, ...); -extern void tvec_write_v(struct tvec_state */*tv*/, - const char */*p*/, va_list */*ap*/); +extern int PRINTF_LIKE(4, 5) + tvec_readword(struct tvec_state */*tv*/, dstr */*d*/, + const char */*delims*/, const char */*expect*/, ...); +extern int tvec_readword_v(struct tvec_state */*tv*/, dstr */*d*/, + const char */*delims*/, const char */*expect*/, + va_list */*ap*/); -extern struct tvec_output *tvec_humanoutput(FILE */*fp*/); -extern struct tvec_output *tvec_tapoutput(FILE */*fp*/); -extern struct tvec_output *tvec_dfltout(FILE */*fp*/); +extern int tvec_nexttoken(struct tvec_state */*tv*/); /*----- Register types ----------------------------------------------------*/ @@ -434,8 +757,6 @@ struct tvec_regty { int (*eq)(const union tvec_regval */*rv0*/, const union tvec_regval */*rv1*/, const struct tvec_regdef */*rd*/); - size_t (*measure)(const union tvec_regval */*rv*/, - const struct tvec_regdef */*rd*/); int (*tobuf)(buf */*b*/, const union tvec_regval */*rv*/, const struct tvec_regdef */*rd*/); int (*frombuf)(buf */*b*/, union tvec_regval */*rv*/, @@ -444,7 +765,8 @@ struct tvec_regty { struct tvec_state */*tv*/); void (*dump)(const union tvec_regval */*rv*/, const struct tvec_regdef */*rd*/, - struct tvec_state */*tv*/, unsigned /*style*/); + unsigned /*style*/, + const struct gprintf_ops */*gops*/, void */*go*/); #define TVSF_COMPACT 1u }; @@ -458,6 +780,8 @@ extern const struct tvec_irange extern const struct tvec_urange tvrange_uchar, tvrange_ushort, tvrange_uint, tvrange_ulong, tvrange_size, tvrange_byte, tvrange_u16, tvrange_u32; +extern const struct tvec_frange + tvrange_float, tvrange_double; extern int tvec_claimeq_int(struct tvec_state */*tv*/, long /*i0*/, long /*i1*/, @@ -472,6 +796,35 @@ extern int tvec_claimeq_uint(struct tvec_state */*tv*/, #define TVEC_CLAIMEQ_UINT(tv, u0, u1) \ (tvec_claimeq_uint(tv, u0, u1, __FILE__, __LINE__, #u0 " /= " #u1)) +extern const struct tvec_regty tvty_float; +struct tvec_floatinfo { + unsigned f; +#define TVFF_NOMIN 1u +#define TVFF_NOMAX 2u +#define TVFF_NANOK 4u +#define TVFF_EXACT 0u +#define TVFF_ABSDELTA 0x10 +#define TVFF_RELDELTA 0x20 +#define TVFF_EQMASK 0xf0 + double min, max; + double delta; +}; + +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*/); +extern int tvec_claimeq_float(struct tvec_state */*tv*/, + double /*f0*/, double /*f1*/, + 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 ")")) +#define TVEC_CLAIMEQ_FLOAT(tv, f0, f1) \ + (tvec_claimeq_float(tv, f0, f1, __FILE__, __LINE__, #f0 " /= " #f1)) + extern const struct tvec_regty tvty_enum; #define DEFASSOC(tag_, ty, slot) \ @@ -479,28 +832,34 @@ extern const struct tvec_regty tvty_enum; TVEC_MISCSLOTS(DEFASSOC) #undef DEFASSOC -struct tvec_enuminfo { - const char *name; unsigned mv; - union { -#define DEFENUMINFO(tag, ty, slot) struct { \ - const struct tvec_##slot##assoc *av; \ - RANGESLOT_##tag \ - } slot; -#define RANGESLOT_INT const struct tvec_irange *ir; -#define RANGESLOT_UINT const struct tvec_urange *ur; -#define RANGESLOT_PTR - TVEC_MISCSLOTS(DEFENUMINFO) -#undef DEFENUMINFO -#undef RANGESLOT_INT -#undef RANGESLOT_UINT -#undef RANGESLOT_PTR - } u; +struct tvec_enuminfo { const char *name; unsigned mv; /* ... */ }; +struct tvec_ienuminfo { + struct tvec_enuminfo _ei; + const struct tvec_iassoc *av; + const struct tvec_irange *ir; +}; +struct tvec_uenuminfo { + struct tvec_enuminfo _ei; + const struct tvec_uassoc *av; + const struct tvec_urange *ur; +}; +struct tvec_fenuminfo { + struct tvec_enuminfo _ei; + const struct tvec_fassoc *av; + const struct tvec_floatinfo *fi; +}; +struct tvec_penuminfo { + struct tvec_enuminfo _ei; + const struct tvec_passoc *av; }; +const struct tvec_ienuminfo tvenum_bool; + #define DECLCLAIM(tag, ty, slot) \ extern int tvec_claimeq_##slot##enum \ (struct tvec_state */*tv*/, \ - const struct tvec_enuminfo */*ei*/, ty /*e0*/, ty /*e1*/, \ + const struct tvec_##slot##enuminfo */*ei*/, \ + ty /*e0*/, ty /*e1*/, \ const char */*file*/, unsigned /*lno*/, const char */*expr*/); TVEC_MISCSLOTS(DECLCLAIM) #undef DECLCLAIM @@ -510,6 +869,9 @@ TVEC_MISCSLOTS(DECLCLAIM) #define TVEC_CLAIMEQ_UENUM(tv, ei, e0, e1) \ (tvec_claimeq_uenum(tv, ei, e0, e1, \ __FILE__, __LINE__, #e0 " /= " #e1)) +#define TVEC_CLAIMEQ_FENUM(tv, ei, e0, e1) \ + (tvec_claimeq_fenum(tv, ei, e0, e1, \ + __FILE__, __LINE__, #e0 " /= " #e1)) #define TVEC_CLAIMEQ_PENUM(tv, ei, e0, e1) \ (tvec_claimeq_penum(tv, ei, e0, e1, \ __FILE__, __LINE__, #e0 " /= " #e1)) @@ -531,6 +893,14 @@ extern int tvec_claimeq_flags(struct tvec_state */*tv*/, (tvec_claimeq_flags(tv, fi, f0, f1, \ __FILE__, __LINE__, #f0 " /= " #f1)) +extern const struct tvec_regty tvty_char; +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)) + extern const struct tvec_regty tvty_string, tvty_bytes; extern int tvec_claimeq_string(struct tvec_state */*tv*/, @@ -547,7 +917,6 @@ extern int tvec_claimeq_bytes(struct tvec_state */*tv*/, const void */*p1*/, size_t /*sz1*/, const char */*file*/, unsigned /*lno*/, const char */*expr*/); - #define TVEC_CLAIMEQ_STRING(tv, p0, sz0, p1, sz1) \ (tvec_claimeq_string(tv, p0, sz0, p1, sz1, __FILE__, __LINE__, \ #p0 "[" #sz0 "] /= " #p1 "[" #sz1 "]"))