@@@ fltfmt mess
[mLib] / test / tvec-types.c
index dc70dbd..8d07ea8 100644 (file)
 #  include "hex.h"
 #include "dstr.h"
 #include "maths.h"
+
 #include "tvec.h"
+#include "tvec-adhoc.h"
+#include "tvec-types.h"
 
 /*----- Preliminary utilities ---------------------------------------------*/
 
@@ -166,11 +169,11 @@ static int signed_from_buf(buf *b, long *i_out)
   return (0);
 }
 
-/* --- @check_unsigned_range@, @check_signed_range@ --- *
+/* --- @check_signed_range@, @check_unsigned_range@ --- *
  *
- * Arguments:  @unsigned long u@ or @long i@ = an integer
- *             @const struct tvec_urange *ur@ or
- *                     @const struct tvec_irange *ir@ = range specification,
+ * Arguments:  @long i@ or @unsigned long u@ = an integer
+ *             @const struct tvec_irange *ir@ or
+ *                     @const struct tvec_urange *ur@ = range specification,
  *                     or null
  *             @struct tvec_state *tv@ = test vector state
  *             @const char *what@ = description of value
@@ -185,10 +188,35 @@ static int check_signed_range(long i,
                              const struct tvec_irange *ir,
                              struct tvec_state *tv, const char *what)
 {
-  if (ir && (ir->min > i || i > ir->max)) {
-    tvec_error(tv, "%s %ld out of range (must be in [%ld .. %ld])",
-              what, i, ir->min, ir->max);
-    return (-1);
+  long ii, aa, m;
+
+  if (ir) {
+    if (ir->min > i || i > ir->max) {
+      tvec_error(tv, "%s %ld out of range (must be in [%ld .. %ld])",
+                what, i, ir->min, ir->max);
+      return (-1);
+    }
+    m = ir->m; if (m > 0) m = -m;
+    if (m && m != -1) {
+      /* Reduce both the integer and the intended residue to the canonical
+       * interval [0, m).  This is more awkward than it should be because C
+       * (following CPU designs) adopted an unhelpful definition of integer
+       * division when the dividend is negative.
+       *
+       * Note that I've canonicalized the divisor to be %%\emph{negative}%%,
+       * because in two's-complement arithmetic, the absolute value of the
+       * most negative representable value is not itself representable.  The
+       * residue modulo the most negative value will itself be representable.
+       */
+
+      ii = i%m; if (ii < 0) ii -= m;
+      aa = ir->a%m; if (aa < 0) aa -= m;
+      if (ii != aa) {
+       tvec_error(tv, "%s %ld == %ld =/= %ld (mod %ld)",
+                  what, i, ii, ir->a, ir->m);
+       return (-1);
+      }
+    }
   }
   return (0);
 }
@@ -197,10 +225,22 @@ static int check_unsigned_range(unsigned long u,
                                const struct tvec_urange *ur,
                                struct tvec_state *tv, const char *what)
 {
-  if (ur && (ur->min > u || u > ur->max)) {
-    tvec_error(tv, "%s %lu out of range (must be in [%lu .. %lu])",
-              what, u, ur->min, ur->max);
-    return (-1);
+  unsigned long uu;
+
+  if (ur) {
+    if (ur->min > u || u > ur->max) {
+      tvec_error(tv, "%s %lu out of range (must be in [%lu .. %lu])",
+                what, u, ur->min, ur->max);
+      return (-1);
+    }
+    if (ur->m && ur->m != 1) {
+      uu = u%ur->m;
+      if (uu != ur->a%ur->m) {
+       tvec_error(tv, "%s %lu == %lu =/= %lu (mod %lu)",
+                  what, u, uu, ur->a, ur->m);
+       return (-1);
+      }
+    }
   }
   return (0);
 }
@@ -587,12 +627,12 @@ static void format_floating(const struct gprintf_ops *gops, void *go,
      *
      * So that's why we have this.
      *
-     * I'm going to assume that @n = DBL_MANT_DIG@ is sufficiently small that
-     * we can calculate this without ending up on the wrong side of an
+     * I'm going to assume that @n = DBL_MANT_DIG@ is sufficiently small
+     * that we can calculate this without ending up on the wrong side of an
      * integer boundary.
      *
-     * In C11, we have @DBL_DECIMAL_DIG@, which should be the same value only
-     * as a constant.  Except that modern compilers are more than clever
+     * In C11, we have @DBL_DECIMAL_DIG@, which should be the same value
+     * only as a constant.  Except that modern compilers are more than clever
      * enough to work out that this is a constant anyway.
      *
      * This is sometimes an overestimate: we'll print out meaningless digits
@@ -603,7 +643,11 @@ static void format_floating(const struct gprintf_ops *gops, void *go,
      * something else in difficult situations).
      */
 
+#ifdef DBL_DECIMAL_DIG
+    prec = DBL_DECIMAL_DIG;
+#else
     prec = ceil(DBL_MANT_DIG*log(FLT_RADIX)/log(10)) + 1;
+#endif
     gprintf(gops, go, "%.*g", prec, x);
   }
 }
@@ -679,7 +723,11 @@ static int parse_floating(double *x_out, const char **q_out, const char *p,
 
     /* Parse the number using the system parser. */
     olderr = errno; errno = 0;
+#if __STDC_VERSION__ >= 199901
     x = strtod(p, &q);
+#else
+    x = strtold(p, &q);
+#endif
     if (q_out) *q_out = q;
     else if (*q) { tvec_syntax(tv, *q, "end-of-line"); rc = -1; goto end; }
     if (errno && (errno != ERANGE || (x > 0 ? -x : x) == HUGE_VAL)) {
@@ -696,8 +744,10 @@ static int parse_floating(double *x_out, const char **q_out, const char *p,
     rc = -1; goto end;
   }
 
-  if (fi && ((!(fi->f&TVFF_NOMIN) && x < fi->min) ||
-            (!(fi->f&TVFF_NOMAX) && x > fi->max))) {
+  if (fi &&
+      ((!(fi->f&TVFF_NOMIN) && x < fi->min) ||
+       (!(fi->f&TVFF_NOMAX) && x > fi->max)) &&
+      !(INFP(x) && (fi->f&(NEGP(x) ? TVFF_NEGINFOK : TVFF_POSINFOK)))) {
     dstr_puts(&d, "floating-point number ");
     format_floating(&dstr_printops, &d, x);
     dstr_puts(&d, " out of range (must be in ");
@@ -874,6 +924,36 @@ static void format_char(const struct gprintf_ops *gops, void *go, int ch)
   }
 }
 
+/* --- @fill_pattern@ --- *
+ *
+ * Arguments:  @void *p@ = destination pointer
+ *             @size_t sz@ = destination buffer size
+ *             @const void *pat@ = pointer to pattern
+ *             @size_t patsz@ = pattern size
+ *
+ * Returns:    ---
+ *
+ * Use:                Fill the destination buffer with as many copies of the
+ *             pattern as will fit, followed by as many initial bytes of the
+ *             pattern will fit in the remaining space.
+ */
+
+static void fill_pattern(void *p, size_t sz, const void *pat, size_t patsz)
+{
+  unsigned char *q = p;
+
+  if (patsz == 1)
+    memset(q, *(unsigned char *)pat, sz);
+  else {
+    if (sz > patsz) {
+      memcpy(q, pat, patsz); pat = q; q += patsz; sz -= patsz;
+      while (sz > patsz)
+       { memcpy(q, pat, patsz); q += patsz; sz -= patsz; patsz *= 2; }
+    }
+    memcpy(q, pat, sz);
+  }
+}
+
 /* --- @maybe_format_unsigned_char@, @maybe_format_signed_char@ --- *
  *
  * Arguments:  @const struct gprintf_ops *gops@ = print operations
@@ -1175,6 +1255,7 @@ static int flush_codec(codec *cdc, dstr *d, struct tvec_state *tv)
  *             If the initial buffer pointer is non-null and sufficiently
  *             large, then it will be reused; otherwise, it is freed and a
  *             fresh, sufficiently large buffer is allocated and returned.
+ *             This buffer unconditionally uses the standard-library arena.
  */
 
 #define RCSF_NESTED 1u
@@ -1273,11 +1354,9 @@ static int read_compound_string(void **p_inout, size_t *sz_inout,
          if (sz) {
            if (n > (size_t)-1/sz)
              { rc = tvec_error(tv, "repeat size out of range"); goto end; }
-           dstr_ensure(&d, n*sz);
-           if (sz == 1)
-             { memset(d.buf + d.len, *(unsigned char *)pp, n); d.len += n; }
-           else
-             for (; n--; d.len += sz) memcpy(d.buf + d.len, pp, sz);
+           n *= sz;
+           dstr_ensure(&d, n);
+           fill_pattern(d.buf + d.len, n, pp, sz); d.len += n;
          }
          xfree(pp); pp = 0;
        }
@@ -1321,7 +1400,7 @@ done:
   if (cdc && flush_codec(cdc, &d, tv)) { rc = -1; goto end; }
   cdc = 0;
   if (*sz_inout <= d.len)
-    { xfree(*p_inout); *p_inout = xmalloc(d.len + 1); }
+    { free(*p_inout); *p_inout = x_alloc(&arena_stdlib, d.len + 1); }
   p = *p_inout; memcpy(p, d.buf, d.len); p[d.len] = 0; *sz_inout = d.len;
   rc = 0;
 
@@ -1542,22 +1621,22 @@ const struct tvec_regty tvty_uint = {
 
 /* Predefined integer ranges. */
 const struct tvec_irange
-  tvrange_schar = { SCHAR_MIN, SCHAR_MAX },
-  tvrange_short = { SHRT_MIN, SHRT_MAX },
-  tvrange_int = { INT_MIN, INT_MAX },
-  tvrange_long = { LONG_MIN, LONG_MAX },
-  tvrange_sbyte = { -128, 127 },
-  tvrange_i16 = { -32768, +32767 },
-  tvrange_i32 = { -2147483648, 2147483647 };
+  tvrange_schar = { SCHAR_MIN, SCHAR_MAX, 0, 0 },
+  tvrange_short = { SHRT_MIN, SHRT_MAX, 0, 0 },
+  tvrange_int = { INT_MIN, INT_MAX, 0, 0 },
+  tvrange_long = { LONG_MIN, LONG_MAX, 0, 0 },
+  tvrange_sbyte = { -128, 127, 0, 0 },
+  tvrange_i16 = { -32768, +32767, 0, 0 },
+  tvrange_i32 = { -2147483648, 2147483647, 0, 0 };
 const struct tvec_urange
-  tvrange_uchar = { 0, UCHAR_MAX },
-  tvrange_ushort = { 0, USHRT_MAX },
-  tvrange_uint = { 0, UINT_MAX },
-  tvrange_ulong = { 0, ULONG_MAX },
-  tvrange_size = { 0, (size_t)-1 },
-  tvrange_byte = { 0, 255 },
-  tvrange_u16 = { 0, 65535 },
-  tvrange_u32 = { 0, 4294967295 };
+  tvrange_uchar = { 0, UCHAR_MAX, 0, 0 },
+  tvrange_ushort = { 0, USHRT_MAX, 0, 0 },
+  tvrange_uint = { 0, UINT_MAX, 0, 0 },
+  tvrange_ulong = { 0, ULONG_MAX, 0, 0 },
+  tvrange_size = { 0, (size_t)-1, 0, 0 },
+  tvrange_byte = { 0, 255, 0, 0 },
+  tvrange_u16 = { 0, 65535, 0, 0 },
+  tvrange_u32 = { 0, 4294967295, 0, 0 };
 
 /* --- @tvec_claimeq_int@ --- *
  *
@@ -1771,8 +1850,14 @@ static int tobuf_float(buf *b, const union tvec_regval *rv,
  */
 
 static int frombuf_float(buf *b, union tvec_regval *rv,
-                      const struct tvec_regdef *rd)
-  { return (buf_getf64l(b, &rv->f)); }
+                        const struct tvec_regdef *rd)
+{
+  double t;
+  int rc;
+
+  rc = buf_getf64l(b, &t); if (!rc) rv->f = t;
+  return (rc);
+}
 
 /* --- @parse_float@ --- *
  *
@@ -1843,6 +1928,10 @@ const struct tvec_regty tvty_float = {
 
 /* Predefined floating-point ranges. */
 const struct tvec_floatinfo
+  tvflt_float = { TVFF_EXACT | TVFF_INFOK | TVFF_NANOK,
+                 -FLT_MAX, FLT_MAX, 0.0 },
+  tvflt_double = { TVFF_EXACT | TVFF_INFOK | TVFF_NANOK,
+                  -DBL_MAX, DBL_MAX, 0.0 },
   tvflt_finite = { TVFF_EXACT, -DBL_MAX, DBL_MAX, 0.0 },
   tvflt_nonneg = { TVFF_EXACT, 0, DBL_MAX, 0.0 };
 
@@ -2105,7 +2194,7 @@ const struct tvec_regty tvty_duration = {
 /* --- @tvec_claimeqish_duration@ --- *
  *
  * Arguments:  @struct tvec_state *tv@ = test-vector state
- *             @double to, t1@ = two durations
+ *             @double t0, t1@ = two durations
  *             @unsigned f@ = flags (@TVFF_...@)
  *             @double delta@ = maximum tolerable difference
  *             @const char *file@, @unsigned @lno@ = calling file and line
@@ -2275,7 +2364,7 @@ static int frombuf_penum(buf *b, union tvec_regval *rv,
 
   for (pa = pei->av, n = 0; pa->tag; pa++, n++);
   if (signed_from_buf(b, &i)) return (-1);
-  if (0 <= i && i < n) rv->p = (/*unconst*/ void *)pei->av[i].p;
+  if (0 <= i && i < n) rv->p = UNCONST(void, pei->av[i].p);
   else if (i == -1) rv->p = 0;
   else { buf_break(b); return (-1); }
   return (0);
@@ -2339,7 +2428,7 @@ static int frombuf_penum(buf *b, union tvec_regval *rv,
                          { rc = -1; goto end; }
 
 #define LITSTR_PTR     "`#nil'"
-#define FOUND_PTR      rv->p = (/*unconst*/ void *)a->p;
+#define FOUND_PTR      rv->p = UNCONST(void, a->p);
 #define MISSING_PTR    if (STRCMP(d.buf, ==, "#nil"))                  \
                          rv->p = 0;                                    \
                        else {                                          \
@@ -2527,7 +2616,7 @@ const struct tvec_ienuminfo tvenum_cmp =
 #define GET_INT(e) (e)
 #define GET_UINT(e) (e)
 #define GET_FLT(e) (e)
-#define GET_PTR(e) ((/*unconst*/ void *)(e))
+#define GET_PTR(e) (UNCONST(void, (e)))
 TVEC_MISCSLOTS(DEFCLAIM)
 #undef DEFCLAIM
 #undef GET_INT
@@ -2652,8 +2741,9 @@ static void dump_flags(const union tvec_regval *rv,
     }
 
   if (v&m) gprintf(gops, go, "%s0x%0*lx", sep, hex_width(v), v&m);
+  else if (!v && m == ~0ul) gprintf(gops, go, "0");
 
-  if (m != ~0ul && !(style&(TVSF_COMPACT | TVSF_RAW)))
+  if (!(style&(TVSF_COMPACT | TVSF_RAW)))
     gprintf(gops, go, " ; = 0x%0*lx", hex_width(rv->u), rv->u);
 }
 
@@ -3044,11 +3134,11 @@ static void init_bytes(union tvec_regval *rv, const struct tvec_regdef *rd)
 
 static void release_text(union tvec_regval *rv,
                         const struct tvec_regdef *rd)
-  { xfree(rv->text.p); }
+  { free(rv->text.p); }
 
 static void release_bytes(union tvec_regval *rv,
                          const struct tvec_regdef *rd)
-  { xfree(rv->bytes.p); }
+  { free(rv->bytes.p); }
 
 /* --- @eq_text@, @eq_bytes@ --- *
  *
@@ -3153,10 +3243,23 @@ static int frombuf_bytes(buf *b, union tvec_regval *rv,
 static int check_string_length(size_t sz, const struct tvec_urange *ur,
                               struct tvec_state *tv)
 {
-  if (ur && (ur->min > sz || sz > ur->max))
-    return (tvec_error(tv,
-                      "invalid string length %lu; must be in [%lu .. %lu]",
-                      (unsigned long)sz, ur->min, ur->max));
+  unsigned long uu;
+
+  if (ur) {
+    if  (ur->min > sz || sz > ur->max) {
+      tvec_error(tv, "invalid string length %lu; must be in [%lu .. %lu]",
+                (unsigned long)sz, ur->min, ur->max);
+      return (-1);
+    }
+    if (ur->m && ur->m != 1) {
+      uu = sz%ur->m;
+      if (uu != ur->a%ur->m) {
+       tvec_error(tv, "invalid string length %lu == %lu =/= %lu (mod %lu)",
+                  (unsigned long)sz, uu, ur->a, ur->m);
+       return (-1);
+      }
+    }
+  }
   return (0);
 }
 
@@ -3326,6 +3429,7 @@ static void dump_bytes(const union tvec_regval *rv,
   if (!rv->text.sz) { dump_empty("bytes", style, gops, go); return; }
 
   if (style&(TVSF_COMPACT | TVSF_RAW)) {
+    if (style&TVSF_RAW) gprintf(gops, go, "bytes:");
     while (p < l) gprintf(gops, go, "%02x", *p++);
     return;
   }
@@ -3387,8 +3491,8 @@ int tvec_claimeq_text(struct tvec_state *tv,
                      const char *p1, size_t sz1,
                      const char *file, unsigned lno, const char *expr)
 {
-  tv->out[0].v.text.p = (/*unconst*/ char *)p0; tv->out[0].v.text.sz = sz0;
-  tv->in[0].v.text.p =(/*unconst*/ char *) p1; tv->in[0].v.text.sz = sz1;
+  tv->out[0].v.text.p = UNCONST(char, p0); tv->out[0].v.text.sz = sz0;
+  tv->in[0].v.text.p =UNCONST(char, p1); tv->in[0].v.text.sz = sz1;
   return (tvec_claimeq(tv, &tvty_text, 0, file, lno, expr));
 }
 
@@ -3412,10 +3516,8 @@ int tvec_claimeq_textz(struct tvec_state *tv,
                       const char *p0, const char *p1,
                       const char *file, unsigned lno, const char *expr)
 {
-  tv->out[0].v.text.p = (/*unconst*/ char *)p0;
-    tv->out[0].v.text.sz = strlen(p0);
-  tv->in[0].v.text.p = (/*unconst*/ char *)p1;
-    tv->in[0].v.text.sz = strlen(p1);
+  tv->out[0].v.text.p = UNCONST(char, p0); tv->out[0].v.text.sz = strlen(p0);
+  tv->in[0].v.text.p = UNCONST(char, p1); tv->in[0].v.text.sz = strlen(p1);
   return (tvec_claimeq(tv, &tvty_text, 0, file, lno, expr));
 }
 
@@ -3443,10 +3545,8 @@ int tvec_claimeq_bytes(struct tvec_state *tv,
                       const void *p1, size_t sz1,
                       const char *file, unsigned lno, const char *expr)
 {
-  tv->out[0].v.bytes.p = (/*unconst*/ void *)p0;
-    tv->out[0].v.bytes.sz = sz0;
-  tv->in[0].v.bytes.p = (/*unconst*/ void *)p1;
-    tv->in[0].v.bytes.sz = sz1;
+  tv->out[0].v.bytes.p = UNCONST(void, p0); tv->out[0].v.bytes.sz = sz0;
+  tv->in[0].v.bytes.p = UNCONST(void, p1); tv->in[0].v.bytes.sz = sz1;
   return (tvec_claimeq(tv, &tvty_bytes, 0, file, lno, expr));
 }
 
@@ -3473,14 +3573,16 @@ int tvec_claimeq_bytes(struct tvec_state *tv,
 
 void tvec_alloctext(union tvec_regval *rv, size_t sz)
 {
-  if (rv->text.sz <= sz) { xfree(rv->text.p); rv->text.p = xmalloc(sz + 1); }
-  rv->text.sz = sz;
+  if (rv->text.sz <= sz)
+    { free(rv->text.p); rv->text.p = x_alloc(&arena_stdlib, sz + 1); }
+  memset(rv->text.p, '?', sz); rv->text.sz = sz;
 }
 
 void tvec_allocbytes(union tvec_regval *rv, size_t sz)
 {
-  if (rv->bytes.sz < sz) { xfree(rv->bytes.p); rv->bytes.p = xmalloc(sz); }
-  rv->bytes.sz = sz;
+  if (rv->bytes.sz < sz)
+    { free(rv->bytes.p); rv->bytes.p = x_alloc(&arena_stdlib, sz); }
+  memset(rv->bytes.p, '?', sz); rv->bytes.sz = sz;
 }
 
 /*----- Buffer type -------------------------------------------------------*/
@@ -3515,7 +3617,7 @@ static void init_buffer(union tvec_regval *rv, const struct tvec_regdef *rd)
 
 static void release_buffer(union tvec_regval *rv,
                           const struct tvec_regdef *rd)
-  { if (rv->buf.p) xfree(rv->buf.p - rv->buf.off); }
+  { if (rv->buf.p) free(rv->buf.p - rv->buf.off); }
 
 /* --- @eq_buffer@ --- *
  *
@@ -3673,6 +3775,7 @@ static void dump_buffer(const union tvec_regval *rv,
                        unsigned style,
                        const struct gprintf_ops *gops, void *go)
 {
+  if (style&TVSF_RAW) gprintf(gops, go, "buffer:");
   format_size(gops, go, rv->buf.sz, style);
   if (rv->buf.m) {
     gprintf(gops, go, style&(TVSF_COMPACT | TVSF_RAW) ? "@" : " @ ");
@@ -3682,7 +3785,7 @@ static void dump_buffer(const union tvec_regval *rv,
       format_size(gops, go, rv->buf.a, style);
     }
   }
-  if (!(style&TVSF_COMPACT)) {
+  if (!(style&(TVSF_COMPACT | TVSF_RAW))) {
     gprintf(gops, go, " ; = %lu", (unsigned long)rv->buf.sz);
     if (rv->buf.m) {
       gprintf(gops, go, " @ %lu", (unsigned long)rv->buf.m);
@@ -3736,12 +3839,12 @@ void tvec_allocbuffer(union tvec_regval *rv)
 {
   unsigned char *p; size_t n;
 
-  if (rv->buf.p) xfree(rv->buf.p - rv->buf.off);
+  if (rv->buf.p) free(rv->buf.p - rv->buf.off);
 
   if (rv->buf.m < 2) {
-    rv->buf.p = xmalloc(rv->buf.sz); rv->buf.off = 0;
+    rv->buf.p = x_alloc(&arena_stdlib, rv->buf.sz); rv->buf.off = 0;
   } else {
-    p = xmalloc(rv->buf.sz + rv->buf.m - 1);
+    p = x_alloc(&arena_stdlib, rv->buf.sz + rv->buf.m - 1);
     n = (size_t)p%rv->buf.m;
     rv->buf.off = (rv->buf.a - n + rv->buf.m)%rv->buf.m;
     rv->buf.p = p + rv->buf.off;