@@@ misc wip
[mLib] / test / tvec-types.c
index 20c2930..dc70dbd 100644 (file)
@@ -173,6 +173,7 @@ static int signed_from_buf(buf *b, long *i_out)
  *                     @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.
  *
@@ -182,11 +183,11 @@ static int signed_from_buf(buf *b, long *i_out)
 
 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);
@@ -194,11 +195,11 @@ static int check_signed_range(long i,
 
 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);
@@ -370,7 +371,7 @@ static int parse_unsigned(unsigned long *u_out, const char *p,
   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);
 }
 
@@ -384,15 +385,15 @@ static int parse_signed(long *i_out, const char *p,
   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
  *
@@ -401,8 +402,8 @@ static const char size_units[] = "kMGTPEZY";
  * 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;
@@ -416,9 +417,8 @@ static int parse_size(struct tvec_state *tv, size_t *u_out,
   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;
@@ -462,7 +462,9 @@ static void format_size(const struct gprintf_ops *gops, void *go,
 {
   const char *unit;
 
-  if (!u || u%1024)
+  if (style&TVSF_RAW)
+    gprintf(gops, go, "%lu", u);
+  else if (!u || u%1024)
     gprintf(gops, go, "%lu%sB", u, style&TVSF_COMPACT ? "" : " ");
   else {
     for (unit = size_units, u /= 1024;
@@ -479,8 +481,8 @@ static void format_size(const struct gprintf_ops *gops, void *go,
  * 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,
@@ -497,7 +499,7 @@ 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();
   }
@@ -1220,6 +1222,7 @@ static int read_compound_string(void **p_inout, size_t *sz_inout,
        if (cdc && flush_codec(cdc, &d, tv)) { rc = -1; goto end; }
        cdc = 0;
        DRESET(&w); tvec_readword(tv, &w, 0, ";", "character name");
+       if (STRCMP(w.buf, ==, "#empty")) break;
        if (read_charname(&ch, w.buf, RCF_EOFOK)) {
          rc = tvec_error(tv, "unknown character name `%s'", d.buf);
          goto end;
@@ -1502,9 +1505,9 @@ static void dump_int(const union tvec_regval *rv,
                     unsigned style,
                     const struct gprintf_ops *gops, void *go)
 {
-
+  if (style&TVSF_RAW) gprintf(gops, go, "int:");
   gprintf(gops, go, "%ld", rv->i);
-  if (!(style&TVSF_COMPACT)) {
+  if (!(style&(TVSF_COMPACT | TVSF_RAW))) {
     gprintf(gops, go, " ; = ");
     format_signed_hex(gops, go, rv->i);
     maybe_format_signed_char(gops, go, rv->i);
@@ -1516,8 +1519,9 @@ static void dump_uint(const union tvec_regval *rv,
                      unsigned style,
                      const struct gprintf_ops *gops, void *go)
 {
+  if (style&TVSF_RAW) gprintf(gops, go, "uint:");
   gprintf(gops, go, "%lu", rv->u);
-  if (!(style&TVSF_COMPACT)) {
+  if (!(style&(TVSF_COMPACT | TVSF_RAW))) {
     gprintf(gops, go, " ; = ");
     format_unsigned_hex(gops, go, rv->u);
     maybe_format_unsigned_char(gops, go, rv->u);
@@ -1604,6 +1608,100 @@ int tvec_claimeq_uint(struct tvec_state *tv,
   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)
+{
+  if (style&TVSF_RAW) gprintf(gops, go, "size:");
+  format_size(gops, go, rv->u, style);
+  if (!(style&(TVSF_COMPACT | TVSF_RAW))) {
+    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@ --- *
@@ -1731,7 +1829,10 @@ 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); }
+{
+  if (style&TVSF_RAW) gprintf(gops, go, "float:");
+  format_floating(gops, go, rv->f);
+}
 
 /* Floating-point type definition. */
 const struct tvec_regty tvty_float = {
@@ -1754,7 +1855,7 @@ const struct tvec_floatinfo
  *             @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.
@@ -1781,8 +1882,11 @@ const struct tvec_floatinfo
  *                 %$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,
@@ -1805,7 +1909,7 @@ 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@
@@ -1971,17 +2075,23 @@ static void dump_duration(const union tvec_regval *rv,
   const struct duration_unit *u;
   double t = rv->f;
 
-  if (!t) u = 0;
-  else {
-    for (u = duration_units; u->scale > t && u[1].unit; u++);
-    t /= u->scale;
-  }
-
-  gprintf(gops, go, "%.4g %s", t, u ? u->unit : "s");
-  if (!(style&TVSF_COMPACT)) {
-    gprintf(gops, go, "; = ");
+  if (style&TVSF_RAW) {
+    gprintf(gops, go, "duration:");
     format_floating(gops, go, rv->f);
-    gprintf(gops, go, " s");
+    gprintf(gops, go, "s");
+  } else {
+    if (!t) u = 0;
+    else {
+      for (u = duration_units; u->scale > t && u[1].unit; u++);
+      t /= u->scale;
+    }
+    gprintf(gops, go, "%.4g %s", t, u ? u->unit : "s");
+
+    if (!(style&TVSF_COMPACT)) {
+      gprintf(gops, go, "; = ");
+      format_floating(gops, go, rv->f);
+      gprintf(gops, go, " s");
+    }
   }
 }
 
@@ -1992,6 +2102,59 @@ const struct tvec_regty tvty_duration = {
   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@ --- *
@@ -2145,7 +2308,8 @@ static int frombuf_penum(buf *b, union tvec_regval *rv,
     dstr d = DSTR_INIT;                                                        \
     int rc;                                                            \
                                                                        \
-    if (tvec_readword(tv, &d, 0, ";", "enumeration tag or " LITSTR_##tag_)) \
+    if (tvec_readword(tv, &d, 0,                                       \
+                     ";", "%s tag or " LITSTR_##tag_, ei->name))       \
       { rc = -1; goto end; }                                           \
     for (a = ei->av; a->tag; a++)                                      \
       if (STRCMP(a->tag, ==, d.buf)) { FOUND_##tag_ goto done; }       \
@@ -2234,6 +2398,7 @@ TVEC_MISCSLOTS(DEFPARSE_ENUM)
     const struct tvec_##slot##enuminfo *ei = rd->arg.p;                        \
     const struct tvec_##slot##assoc *a;                                        \
                                                                        \
+    if (style&TVSF_RAW) gprintf(gops, go, #slot "enum/%s:", ei->name); \
     for (a = ei->av; a->tag; a++)                                      \
       if (rv->slot == a->slot) {                                       \
        gprintf(gops, go, "%s", a->tag);                                \
@@ -2406,7 +2571,7 @@ static int parse_flags(union tvec_regval *rv, const struct tvec_regdef *rd,
 
     /* Read the next item. */
     DRESET(&d);
-    if (tvec_readword(tv, &d, 0, "|;", "flag name or integer"))
+    if (tvec_readword(tv, &d, 0, "|;", "%s flag name or integer", fi->name))
       { rc = -1; goto end; }
 
     /* Try to find a matching entry in the table. */
@@ -2430,8 +2595,10 @@ static int parse_flags(union tvec_regval *rv, const struct tvec_regdef *rd,
     if (tvec_nexttoken(tv)) break;
     ch = getc(tv->fp);
       if (ch != '|') { tvec_syntax(tv, ch, "`|'"); rc = -1; goto end; }
-      if (tvec_nexttoken(tv))
-      { tvec_syntax(tv, '\n', "flag name or integer"); rc = -1; goto end; }
+      if (tvec_nexttoken(tv)) {
+       tvec_syntax(tv, '\n', "%s flag name or integer", fi->name);
+       rc = -1; goto end;
+      }
   }
 
   /* Done. */
@@ -2476,6 +2643,8 @@ static void dump_flags(const union tvec_regval *rv,
   unsigned long m = ~0ul, v = rv->u;
   const char *sep;
 
+  if (style&TVSF_RAW) gprintf(gops, go, "flags/%s:", fi->name);
+
   for (f = fi->fv, sep = ""; f->tag; f++)
     if ((m&f->m) && (v&f->m) == f->v) {
       gprintf(gops, go, "%s%s", sep, f->tag); m &= ~f->m;
@@ -2484,7 +2653,7 @@ static void dump_flags(const union tvec_regval *rv,
 
   if (v&m) gprintf(gops, go, "%s0x%0*lx", sep, hex_width(v), v&m);
 
-  if (m != ~0ul && !(style&TVSF_COMPACT))
+  if (m != ~0ul && !(style&(TVSF_COMPACT | TVSF_RAW)))
     gprintf(gops, go, " ; = 0x%0*lx", hex_width(rv->u), rv->u);
 }
 
@@ -2765,35 +2934,45 @@ static void dump_char(const union tvec_regval *rv,
   unsigned f = 0;
 #define f_semi 1u
 
-  /* Print a character name if we can find one. */
-  p = find_charname(rv->i, (style&TVSF_COMPACT) ? CTF_SHORT : CTF_PREFER);
-  if (p) {
-    gprintf(gops, go, "%s", p);
-    if (style&TVSF_COMPACT) return;
-    else { gprintf(gops, go, " ;"); f |= f_semi; }
-  }
+  if (style&TVSF_RAW) {
+    /* Print the raw character unconditionally in single quotes. */
 
-  /* 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) {
-      case ' ': case '\\': case '\'': quote:
-       format_char(gops, go, rv->i);
-       break;
-      default:
-       if (!(style&TVSF_COMPACT) || !isprint(rv->i)) goto quote;
-       gprintf(gops, go, "%c", (int)rv->i);
-       return;
+    gprintf(gops, go, "char:'");
+    format_char(gops, go, rv->i);
+    gprintf(gops, go, "'");
+  } else {
+    /* Print ina pleasant human-readable way. */
+
+    /* 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);
+      if (style&TVSF_COMPACT) return;
+      else { gprintf(gops, go, " ;"); f |= f_semi; }
     }
-  }
 
-  /* And the character code as an integer. */
-  if (!(style&TVSF_COMPACT)) {
-    if (!(f&f_semi)) gprintf(gops, go, " ;");
-    gprintf(gops, go, " = %ld = ", rv->i);
-    format_signed_hex(gops, go, rv->i);
+    /* 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) {
+       case ' ': case '\\': case '\'': quote:
+         format_char(gops, go, rv->i);
+         break;
+       default:
+         if (!(style&TVSF_COMPACT) || !isprint(rv->i)) goto quote;
+         gprintf(gops, go, "%c", (int)rv->i);
+         return;
+      }
+    }
+
+    /* And the character code as an integer. */
+    if (!(style&TVSF_COMPACT)) {
+      if (!(f&f_semi)) gprintf(gops, go, " ;");
+      gprintf(gops, go, " = %ld = ", rv->i);
+      format_signed_hex(gops, go, rv->i);
+    }
   }
 
 #undef f_semi
@@ -3071,9 +3250,19 @@ static int parse_bytes(union tvec_regval *rv, const struct tvec_regdef *rd,
  *             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 %|""|%.
+ *             Empty strings are dumped as %|#empty|%.
  */
 
+static void dump_empty(const char *ty, unsigned style,
+                      const struct gprintf_ops *gops, void *go)
+{
+  if (style&TVSF_RAW) gprintf(gops, go, "%s:", ty);
+  if (!(style&TVSF_COMPACT)) gprintf(gops, go, "#empty");
+  if (!(style&(TVSF_COMPACT | TVSF_RAW))) gprintf(gops, go, " ; = ");
+  if (!(style&TVSF_RAW)) gprintf(gops, go, "\"\"");
+}
+
+
 static void dump_text(const union tvec_regval *rv,
                      const struct tvec_regdef *rd,
                      unsigned style,
@@ -3084,9 +3273,12 @@ static void dump_text(const union tvec_regval *rv,
 #define f_nonword 1u
 #define f_newline 2u
 
-  if (!rv->text.sz) { gprintf(gops, go, "\"\""); return; }
+  if (!rv->text.sz) { dump_empty("text", style, gops, go); return; }
 
   p = (const unsigned char *)rv->text.p; l = p + rv->text.sz;
+  if (style&TVSF_RAW) { gprintf(gops, go, "text:"); goto quote; }
+  else if (style&TVSF_COMPACT) goto quote;
+
   switch (*p) {
     case '!': case '#': case ';': case '"': case '\'':
     case '(': case '{': case '[': case ']': case '}': case ')':
@@ -3094,7 +3286,7 @@ static void dump_text(const union tvec_regval *rv,
   }
   for (q = p; q < l; q++)
     if (*q == '\n' && q != l - 1) f |= f_newline;
-    else if (!*q || !isgraph(*q) || *q == '\\') f |= f_nonword;
+    else if (!*q || !ISGRAPH(*q) || *q == '\\') f |= f_nonword;
   if (f&f_newline) { gprintf(gops, go, "\n\t"); goto quote; }
   else if (f&f_nonword) goto quote;
 
@@ -3104,7 +3296,7 @@ static void dump_text(const union tvec_regval *rv,
 quote:
   gprintf(gops, go, "\"");
   for (q = p; q < l; q++)
-    if (!isprint(*q) || *q == '"') {
+    if (!ISPRINT(*q) || *q == '"') {
       if (p < q) gops->putm(go, (const char *)p, q - p);
       if (*q != '\n' || (style&TVSF_COMPACT))
        format_charesc(gops, go, *q, FCF_BRACE);
@@ -3131,12 +3323,9 @@ static void dump_bytes(const union tvec_regval *rv,
   unsigned i, n;
   int wd;
 
-  if (!sz) {
-    gprintf(gops, go, style&TVSF_COMPACT ? "\"\"" : "\"\" ; empty");
-    return;
-  }
+  if (!rv->text.sz) { dump_empty("bytes", style, gops, go); return; }
 
-  if (style&TVSF_COMPACT) {
+  if (style&(TVSF_COMPACT | TVSF_RAW)) {
     while (p < l) gprintf(gops, go, "%02x", *p++);
     return;
   }
@@ -3412,23 +3601,24 @@ static int frombuf_buffer(buf *b, union tvec_regval *rv,
  *
  * 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);
@@ -3436,7 +3626,9 @@ static int parse_buffer(union tvec_regval *rv,
   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);
@@ -3444,7 +3636,9 @@ static int parse_buffer(union tvec_regval *rv,
   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);
@@ -3470,9 +3664,8 @@ end:
  *
  * 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,
@@ -3482,10 +3675,10 @@ static void dump_buffer(const union tvec_regval *rv,
 {
   format_size(gops, go, rv->buf.sz, style);
   if (rv->buf.m) {
-    gprintf(gops, go, style&TVSF_COMPACT ? "@" : " @ ");
+    gprintf(gops, go, style&(TVSF_COMPACT | TVSF_RAW) ? "@" : " @ ");
     format_size(gops, go, rv->buf.m, style);
     if (rv->buf.a) {
-      gprintf(gops, go, style&TVSF_COMPACT ? "+" : " + ");
+      gprintf(gops, go, style&(TVSF_COMPACT | TVSF_RAW) ? "+" : " + ");
       format_size(gops, go, rv->buf.a, style);
     }
   }
@@ -3516,18 +3709,18 @@ const struct tvec_regty tvty_buffer = {
 /* --- @tvec_initbuffer@ --- *
  *
  * Arguments:  @union tvec_regval *rv@ = register value
- *             @const union tvec_regval *src@ = source buffer
+ *             @const union tvec_regval *ref@ = source buffer
  *             @size_t sz@ = size to allocate
  *
  * Returns:    ---
  *
- * Use:                Initialize the alignment parameters in @rv@ to match @src@,
+ * Use:                Initialize the alignment parameters in @rv@ to match @ref@,
  *             and the size to @sz@.
  */
 
 void tvec_initbuffer(union tvec_regval *rv,
-                    const union tvec_regval *src, size_t sz)
-  { rv->buf.sz = sz; rv->buf.a = src->buf.a; rv->buf.m = src->buf.m; }
+                    const union tvec_regval *ref, size_t sz)
+  { rv->buf.sz = sz; rv->buf.a = ref->buf.a; rv->buf.m = ref->buf.m; }
 
 /* --- @tvec_allocbuffer@ --- *
  *