* @const struct tvec_irange *ir@ = range specification,
* or null
* @struct tvec_state *tv@ = test vector state
+ * @const char *what@ = description of value
*
* Returns: Zero on success, or @-1@ on error.
*
static int check_signed_range(long i,
const struct tvec_irange *ir,
- struct tvec_state *tv)
+ struct tvec_state *tv, const char *what)
{
if (ir && (ir->min > i || i > ir->max)) {
- tvec_error(tv, "integer %ld out of range (must be in [%ld .. %ld])",
- i, ir->min, ir->max);
+ tvec_error(tv, "%s %ld out of range (must be in [%ld .. %ld])",
+ what, i, ir->min, ir->max);
return (-1);
}
return (0);
static int check_unsigned_range(unsigned long u,
const struct tvec_urange *ur,
- struct tvec_state *tv)
+ struct tvec_state *tv, const char *what)
{
if (ur && (ur->min > u || u > ur->max)) {
- tvec_error(tv, "integer %lu out of range (must be in [%lu .. %lu])",
- u, ur->min, ur->max);
+ tvec_error(tv, "%s %lu out of range (must be in [%lu .. %lu])",
+ what, u, ur->min, ur->max);
return (-1);
}
return (0);
if (parse_unsigned_integer(&u, &q, p))
return (tvec_error(tv, "invalid unsigned integer `%s'", p));
if (*q) return (tvec_syntax(tv, *q, "end-of-line"));
- if (check_unsigned_range(u, ur, tv)) return (-1);
+ if (check_unsigned_range(u, ur, tv, "integer")) return (-1);
*u_out = u; return (0);
}
if (parse_signed_integer(&i, &q, p))
return (tvec_error(tv, "invalid signed integer `%s'", p));
if (*q) return (tvec_syntax(tv, *q, "end-of-line"));
- if (check_signed_range(i, ir, tv)) return (-1);
+ if (check_signed_range(i, ir, tv, "integer")) return (-1);
*i_out = i; return (0);
}
static const char size_units[] = "kMGTPEZY";
-/* --- @parse_size@ --- *
+/* --- @parse_szint@ --- *
*
* Arguments: @struct tvec_state *tv@ = test-vector state
- * @size_t *u_out@ = where to put the answer
+ * @unsigned long *u_out@ = where to put the answer
* @const char *delims@ = delimiters
* @const char *what@ = description of what we're parsing
*
* Use: Parse a memory size.
*/
-static int parse_size(struct tvec_state *tv, size_t *u_out,
- const char *delims, const char *what)
+static int parse_szint(struct tvec_state *tv, unsigned long *u_out,
+ const char *delims, const char *what)
{
dstr d = DSTR_INIT;
const char *p, *unit;
if (parse_unsigned_integer(&u, &p, p)) goto bad;
if (!*p) tvec_readword(tv, &d, &p, delims, 0);
- if (u > (size_t)-1) goto rangerr;
for (t = u, unit = size_units; *unit; unit++) {
- if (t > (size_t)-1/1024) f |= f_range;
+ if (t > ULONG_MAX/1024) f |= f_range;
else t *= 1024;
if (*p == *unit) {
if (f&f_range) goto rangerr;
* Arguments: @double x, y@ = two numbers to compare
* @const struct tvec_floatinfo *fi@ = floating-point info
*
- * Returns: Nonzero if the comparand @y@ is sufficiently close to the
- * reference @x@, or zero if it's definitely different.
+ * Returns: Nonzero if the comparand @x@ is sufficiently close to the
+ * reference @y@, or zero if it's definitely different.
*/
static int eqish_floating_p(double x, double y,
case TVFF_ABSDELTA:
t = x - y; if (t < 0) t = -t; return (t < fi->delta);
case TVFF_RELDELTA:
- t = 1.0 - y/x; if (t < 0) t = -t; return (t < fi->delta);
+ t = 1.0 - x/y; if (t < 0) t = -t; return (t < fi->delta);
default:
abort();
}
return (tvec_claimeq(tv, &tvty_uint, 0, file, lno, expr));
}
+/*----- Size type ---------------------------------------------------------*/
+
+/* --- @parse_size@ --- *
+ *
+ * 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 size value consists of an unsigned
+ * integer followed by an optional unit specifier consisting of
+ * an SI unit prefix and (optionally) the letter `B'. */
+
+static int parse_size(union tvec_regval *rv, const struct tvec_regdef *rd,
+ struct tvec_state *tv)
+{
+ unsigned long sz;
+ int rc;
+
+ if (parse_szint(tv, &sz, ";", "size")) { rc = -1; goto end; }
+ if (check_unsigned_range(sz, rd->arg.p, tv, "size")) { rc = -1; goto end; }
+ if (tvec_flushtoeol(tv, 0)) { rc = -1; goto end; }
+ rv->u = sz; rc = 0;
+end:
+ return (rc);
+}
+
+/* --- @dump_size@ --- *
+ *
+ * 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.
+ *
+ * Size values are dumped with a unit specifier, with a unit
+ * prefox only if the size is an exact multiple of the relevant
+ * power of two. Unless compact style is requested, the plain
+ * decimal and hex representations of the value are also
+ * printed.
+ */
+
+static void dump_size(const union tvec_regval *rv,
+ const struct tvec_regdef *rd,
+ unsigned style,
+ const struct gprintf_ops *gops, void *go)
+{
+ format_size(gops, go, rv->u, style);
+ if (!(style&TVSF_COMPACT)) {
+ gprintf(gops, go, " ; = %lu", (unsigned long)rv->u);
+ gprintf(gops, go, " = "); format_unsigned_hex(gops, go, rv->u);
+ maybe_format_unsigned_char(gops, go, rv->u);
+ }
+}
+
+/* Size type definitions. */
+const struct tvec_regty tvty_size = {
+ init_uint, trivial_release, eq_uint,
+ tobuf_uint, frombuf_uint,
+ parse_size, dump_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.
+ */
+
+int tvec_claimeq_size(struct tvec_state *tv,
+ unsigned long sz0, unsigned long sz1,
+ const char *file, unsigned lno, const char *expr)
+{
+ tv->out[0].v.u = sz0; tv->in[0].v.u = sz1;
+ return (tvec_claimeq(tv, &tvty_size, 0, file, lno, expr));
+}
+
/*----- Floating-point type -----------------------------------------------*/
/* --- @int_float@ --- *
* @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 sufficiently close, otherwise
+ * Returns: Nonzero if @f0@ and @f1@ are sufficiently close, otherwise
* zero.
*
* Use: Check that values of @f0@ and @f1@ are sufficiently close.
* %$y$% when %$|x - y| < \delta$%.
*
* * If @f&TVFF_EQMASK@ is @TVFF_RELDELTA@, then %$x$% matches
- * %$y$% when %$|1 - y/x| < \delta$%. (Note that this
- * criterion is asymmetric FIXME
+ * %$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$%.)
*/
int tvec_claimeqish_float(struct tvec_state *tv,
* @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.
+ * Returns: Nonzero if @f0@ and @f1@ are identical, otherwise zero.
*
* Use: Check that values of @f0@ and @f1@ are identical. The
* function is exactly equivalent to @tvec_claimeqish_float@
parse_duration, dump_duration
};
+/* --- @tvec_claimeqish_duration@ --- *
+ *
+ * Arguments: @struct tvec_state *tv@ = test-vector state
+ * @double to, 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.
+ */
+
+int tvec_claimeqish_duration(struct tvec_state *tv,
+ double t0, double t1, unsigned f, double delta,
+ const char *file, unsigned lno,
+ const char *expr)
+{
+ struct tvec_floatinfo fi;
+ union tvec_misc arg;
+
+ fi.f = f; fi.min = fi.max = 0.0; fi.delta = delta; arg.p = &fi;
+ tv->out[0].v.f = t0; tv->in[0].v.f = t1;
+ return (tvec_claimeq(tv, &tvty_duration, &arg, file, lno, expr));
+}
+
+/* --- @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@.
+ */
+
+int tvec_claimeq_duration(struct tvec_state *tv,
+ double t0, double t1,
+ const char *file, unsigned lno,
+ const char *expr)
+{
+ return (tvec_claimeqish_duration(tv, t0, t1, TVFF_EXACT, 0.0,
+ file, lno, expr));
+}
+
/*----- Enumerations ------------------------------------------------------*/
/* --- @init_tenum@ --- *
*
* 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 input format for a buffer value is a size, followed by an
+ * optional `%|@$%' and an alignment quantum and a further
+ * optional `%|+|%' and an alignment offset. The size, quantum,
+ * and offset are syntactically sizes.
*
- * The buffer is allocated and filled with a distinctive
- * pattern.
+ * The buffer is not allocated.
*/
static int parse_buffer(union tvec_regval *rv,
const struct tvec_regdef *rd,
struct tvec_state *tv)
{
- size_t sz, a = 0, m = 0;
+ unsigned long sz, a = 0, m = 0;
int ch, rc;
- if (parse_size(tv, &sz, "@;", "buffer length")) { rc = -1; goto end; }
+ if (parse_szint(tv, &sz, "@;", "buffer length")) { rc = -1; goto end; }
+ if (check_unsigned_range(sz, &tvrange_size, tv, "buffer length"))
+ { rc = -1; goto end; }
if (check_string_length(sz, rd->arg.p, tv)) { rc = -1; goto end; }
tvec_skipspc(tv);
if (ch == ';' || ch == '\n') { ungetc(ch, tv->fp); goto done; }
else if (ch != '@') { rc = tvec_syntax(tv, ch, "`@'"); goto end; }
- if (parse_size(tv, &m, "+;", "alignment quantum")) { rc = -1; goto end; }
+ if (parse_szint(tv, &m, "+;", "alignment quantum")) { rc = -1; goto end; }
+ if (check_unsigned_range(a, &tvrange_size, tv, "alignment quantum"))
+ { rc = -1; goto end; }
if (m == 1) m = 0;
tvec_skipspc(tv);
if (ch == ';' || ch == '\n') { ungetc(ch, tv->fp); goto done; }
else if (ch != '+') { rc = tvec_syntax(tv, ch, "`+'"); goto end; }
- if (parse_size(tv, &a, ";", "alignment offset")) { rc = -1; goto end; }
+ if (parse_szint(tv, &a, ";", "alignment offset")) { rc = -1; goto end; }
+ if (check_unsigned_range(m, &tvrange_size, tv, "alignment offset"))
+ { rc = -1; goto end; }
if (a >= m) {
rc = tvec_error(tv, "alignment offset %lu >= quantum %lu",
(unsigned long)a, (unsigned long)m);
*
* 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.
+ * Buffer values are dumped as their size, with the alignment
+ * quantum and alignment offset if these are non-default.
*/
static void dump_buffer(const union tvec_regval *rv,