# include "hex.h"
#include "dstr.h"
#include "maths.h"
+
#include "tvec.h"
+#include "tvec-adhoc.h"
+#include "tvec-types.h"
/*----- Preliminary utilities ---------------------------------------------*/
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
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);
}
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);
}
*
* 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
* 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);
}
}
/* 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)) {
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 ");
}
}
+/* --- @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
* 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
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;
}
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;
/* 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@ --- *
*
*/
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@ --- *
*
/* 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 };
/* --- @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
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);
{ 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 { \
#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
}
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);
}
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@ --- *
*
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);
}
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;
}
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));
}
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));
}
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));
}
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 -------------------------------------------------------*/
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@ --- *
*
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) ? "@" : " @ ");
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);
{
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;