X-Git-Url: https://git.distorted.org.uk/~mdw/mLib/blobdiff_plain/814e42ff7421d66c0a2e33af5765afa2078bc18f..adec5584e13c63662fda18915280ec026063b29d:/test/tvec-types.c diff --git a/test/tvec-types.c b/test/tvec-types.c index e76d4a1..068d565 100644 --- a/test/tvec-types.c +++ b/test/tvec-types.c @@ -107,7 +107,7 @@ static int unsigned_from_buf(buf *b, unsigned long *u_out) ASSIGN64(ulmax, ULONG_MAX); if (buf_getk64l(b, &k)) return (-1); - if (CMP64(k, >, ulmax)) return (-1); + if (CMP64(k, >, ulmax)) { buf_break(b); return (-1); } *u_out = GET64(unsigned long, k); return (0); } @@ -161,7 +161,7 @@ static int signed_from_buf(buf *b, long *i_out) else { CPL64(k, k); if (CMP64(k, <=, not_lmin)) *i_out = -(long)GET64(unsigned long, k) - 1; - else return (-1); + else { buf_break(b); return (-1); } } return (0); } @@ -387,6 +387,90 @@ static int parse_signed(long *i_out, const char *p, if (check_signed_range(i, ir, tv)) return (-1); *i_out = i; return (0); } +static const char size_units[] = "kMGTPEZY"; + +/* --- @parse_size@ --- * + * + * Arguments: @struct tvec_state *tv@ = test-vector state + * @size_t *u_out@ = where to put the answer + * @const char *delims@ = delimiters + * @const char *what@ = description of what we're parsing + * + * Returns: Zero on success, %$-1$% on failure. + * + * Use: Parse a memory size. + */ + +static int parse_size(struct tvec_state *tv, size_t *u_out, + const char *delims, const char *what) +{ + dstr d = DSTR_INIT; + const char *p, *unit; + unsigned long u, t; + int rc; + unsigned f = 0; +#define f_range 1u + + if (tvec_readword(tv, &d, 0, delims, what)) { rc = -1; goto end; } + p = d.buf; + 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; + else t *= 1024; + if (*p == *unit) { + if (f&f_range) goto rangerr; + u = t; p++; break; + } + } + if (*p == 'B') p++; + if (*p) goto bad; + + *u_out = u; rc = 0; +end: + dstr_destroy(&d); + return (rc); + +bad: + tvec_error(tv, "invalid %s `%s'", what, d.buf); + rc = -1; goto end; + +rangerr: + tvec_error(tv, "%s `%s' out of range", what, d.buf); + rc = -1; goto end; + +#undef f_range +} + +/* --- @format_size@ --- * + * + * Arguments: @const struct gprintf_ops *gops@ = print operations + * @void *go@ = print destination + * @unsigned long u@ = a size + * @unsigned style@ = style (@TVSF_...@) + * + * Returns: --- + * + * Use: Format @u@ as a size in bytes to the destination, expressing + * it with a unit prefix if this is possible exactly. + */ + +static void format_size(const struct gprintf_ops *gops, void *go, + unsigned long u, unsigned style) +{ + const char *unit; + + if (!u || u%1024) + gprintf(gops, go, "%lu%sB", u, style&TVSF_COMPACT ? "" : " "); + else { + for (unit = size_units, u /= 1024; + !(u%1024) && unit[1]; + u /= 1024, unit++); + gprintf(gops, go, "%lu%s%cB", u, style&TVSF_COMPACT ? "" : " ", *unit); + } +} /*----- Floating-point utilities ------------------------------------------*/ @@ -534,8 +618,8 @@ static void format_floating(const struct gprintf_ops *gops, void *go, * * Use: Parse a floating-point number from a string. Reports any * necessary errors. If @q_out@ is not null then trailing - * material is permitted and a pointer to it is left in - * @*q_out@; this will be null if there is no trailing material. + * material is permitted and a pointer to it (or the end of the + * string) is left in @*q_out@. */ static int parse_floating(double *x_out, const char **q_out, const char *p, @@ -547,11 +631,10 @@ static int parse_floating(double *x_out, const char **q_out, const char *p, double x; int olderr, rc; - if (q_out) *q_out = 0; - /* Check for special tokens. */ if (STRCMP(p, ==, "#nan")) { #ifdef NAN + if (q_out) *q_out = p + strlen(p); x = NAN; rc = 0; #else tvec_error(tv, "NaN not supported on this system"); @@ -562,6 +645,7 @@ static int parse_floating(double *x_out, const char **q_out, const char *p, else if (STRCMP(p, ==, "#inf") || STRCMP(p, ==, "#+inf") || STRCMP(p, ==, "+#inf")) { #ifdef INFINITY + if (q_out) *q_out = p + strlen(p); x = INFINITY; rc = 0; #else tvec_error(tv, "infinity not supported on this system"); @@ -571,6 +655,7 @@ static int parse_floating(double *x_out, const char **q_out, const char *p, else if (STRCMP(p, ==, "#-inf") || STRCMP(p, ==, "-#inf")) { #ifdef INFINITY + if (q_out) *q_out = p + strlen(p); x = -INFINITY; rc = 0; #else tvec_error(tv, "infinity not supported on this system"); @@ -593,9 +678,8 @@ 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; x = strtod(p, &q); - if (!*q) /* nothing to do */; - else if (q_out) *q_out = q; - else { tvec_syntax(tv, *q, "end-of-line"); rc = -1; goto end; } + 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)) { tvec_error(tv, "invalid floating-point number `%.*s': %s", (int)(q - p), p, strerror(errno)); @@ -1135,7 +1219,7 @@ static int read_compound_string(void **p_inout, size_t *sz_inout, ungetc(ch, tv->fp); if (cdc && flush_codec(cdc, &d, tv)) { rc = -1; goto end; } cdc = 0; - DRESET(&w); tvec_readword(tv, &w, ";", "character name"); + DRESET(&w); tvec_readword(tv, &w, 0, ";", "character name"); if (read_charname(&ch, w.buf, RCF_EOFOK)) { rc = tvec_error(tv, "unknown character name `%s'", d.buf); goto end; @@ -1148,7 +1232,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; ungetc(ch, tv->fp); - DRESET(&w); tvec_readword(tv, &w, ";", "`!'-keyword"); + DRESET(&w); tvec_readword(tv, &w, 0, ";", "`!'-keyword"); /* Change bareword coding system. */ if (STRCMP(w.buf, ==, "!bare")) @@ -1167,7 +1251,7 @@ static int read_compound_string(void **p_inout, size_t *sz_inout, goto end; } DRESET(&w); - if (tvec_readword(tv, &w, ";{", "repeat count")) + if (tvec_readword(tv, &w, 0, ";{", "repeat count")) { rc = -1; goto end; } if (parse_unsigned_integer(&n, &q, w.buf)) { rc = tvec_error(tv, "invalid repeat count `%s'", w.buf); @@ -1213,7 +1297,8 @@ static int read_compound_string(void **p_inout, size_t *sz_inout, default: assert(ccl); ungetc(ch, tv->fp); DRESET(&w); - if (tvec_readword(tv, &w, ";", "%s-encoded fragment", ccl->name)) + if (tvec_readword(tv, &w, 0, ";", + "%s-encoded fragment", ccl->name)) { rc = -1; goto end; } if (!cdc) cdc = ccl->decoder(cdf); err = cdc->ops->code(cdc, w.buf, w.len, &d); @@ -1370,7 +1455,8 @@ static int parse_int(union tvec_regval *rv, const struct tvec_regdef *rd, dstr d = DSTR_INIT; int rc; - if (tvec_readword(tv, &d, ";", "signed integer")) { rc = -1; goto end; } + if (tvec_readword(tv, &d, 0, ";", "signed integer")) + { rc = -1; goto end; } if (parse_signed(&rv->i, d.buf, rd->arg.p, tv)) { rc = -1; goto end; } if (tvec_flushtoeol(tv, 0)) { rc = -1; goto end; } rc = 0; @@ -1385,7 +1471,8 @@ static int parse_uint(union tvec_regval *rv, const struct tvec_regdef *rd, dstr d = DSTR_INIT; int rc; - if (tvec_readword(tv, &d, ";", "unsigned integer")) { rc = -1; goto end; } + if (tvec_readword(tv, &d, 0, ";", "unsigned integer")) + { rc = -1; goto end; } if (parse_unsigned(&rv->u, d.buf, rd->arg.p, tv)) { rc = -1; goto end; } if (tvec_flushtoeol(tv, 0)) { rc = -1; goto end; } rc = 0; @@ -1612,7 +1699,7 @@ static int parse_float(union tvec_regval *rv, const struct tvec_regdef *rd, dstr d = DSTR_INIT; int rc; - if (tvec_readword(tv, &d, ";", "floating-point number")) + if (tvec_readword(tv, &d, 0, ";", "floating-point number")) { rc = -1; goto end; } if (parse_floating(&rv->f, 0, d.buf, rd->arg.p, tv)) { rc = -1; goto end; } @@ -1806,21 +1893,17 @@ static int parse_duration(union tvec_regval *rv, { const struct duration_unit *u; const char *q; - dstr d = DSTR_INIT; size_t pos; + dstr d = DSTR_INIT; double t; int rc; - if (tvec_readword(tv, &d, ";", "duration")) { rc = -1; goto end; } + if (tvec_readword(tv, &d, 0, ";", "duration")) { rc = -1; goto end; } if (parse_floating(&t, &q, d.buf, rd->arg.p ? rd->arg.p : &tvflt_nonneg, tv)) { rc = -1; goto end; } - if (!q) { - tvec_skipspc(tv); pos = d.len; - if (!tvec_readword(tv, &d, ";", 0)) q = d.buf + pos + 1; - } - - if (q) { + if (!*q) tvec_readword(tv, &d, &q, ";", 0); + if (*q) { for (u = duration_units; u->unit; u++) if (STRCMP(q, ==, u->unit)) { t *= u->scale; goto found_unit; } rc = tvec_syntax(tv, *q, "end-of-line"); goto end; @@ -2001,7 +2084,7 @@ static int frombuf_penum(buf *b, union tvec_regval *rv, if (signed_from_buf(b, &i)) return (-1); if (0 <= i && i < n) rv->p = (/*unconst*/ void *)pei->av[i].p; else if (i == -1) rv->p = 0; - else return (-1); + else { buf_break(b); return (-1); } return (0); } @@ -2032,7 +2115,7 @@ static int frombuf_penum(buf *b, union tvec_regval *rv, dstr d = DSTR_INIT; \ int rc; \ \ - if (tvec_readword(tv, &d, ";", "enumeration tag or " LITSTR_##tag_)) \ + if (tvec_readword(tv, &d, 0, ";", "enumeration tag or " LITSTR_##tag_)) \ { rc = -1; goto end; } \ for (a = ei->av; a->tag; a++) \ if (STRCMP(a->tag, ==, d.buf)) { FOUND_##tag_ goto done; } \ @@ -2293,7 +2376,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, "|;", "flag name or integer")) + if (tvec_readword(tv, &d, 0, "|;", "flag name or integer")) { rc = -1; goto end; } /* Try to find a matching entry in the table. */ @@ -2436,7 +2519,7 @@ static int tobuf_char(buf *b, const union tvec_regval *rv, if (0 <= rv->i && rv->i <= UCHAR_MAX) u = rv->i; else if (rv->i == EOF) u = MASK32; - else return (-1); + else { buf_break(b); return (-1); } return (buf_putu32l(b, u)); } @@ -2462,7 +2545,7 @@ static int frombuf_char(buf *b, union tvec_regval *rv, if (buf_getu32l(b, &u)) return (-1); if (0 <= u && u <= UCHAR_MAX) rv->i = u; else if (u == MASK32) rv->i = EOF; - else return (-1); + else { buf_break(b); return (-1); } return (0); } @@ -2553,7 +2636,8 @@ static int parse_char(union tvec_regval *rv, const struct tvec_regdef *rd, */ ungetc(ch, tv->fp); - if (tvec_readword(tv, &d, ";", "character name")) { rc = -1; goto end; } + if (tvec_readword(tv, &d, 0, ";", "character name")) + { rc = -1; goto end; } if (STRCMP(d.buf, !=, "#")) { if (read_charname(&ch, d.buf, RCF_EOFOK)) { rc = tvec_error(tv, "unknown character name `%s'", d.buf); @@ -3182,7 +3266,37 @@ void tvec_allocbytes(union tvec_regval *rv, size_t sz) /*----- Buffer type -------------------------------------------------------*/ -/* Buffers are initialized and released as binary strings. */ +/* --- @init_buffer@ --- * + * + * Arguments: @union tvec_regval *rv@ = register value + * @const struct tvec_regdef *rd@ = register definition + * + * Returns: --- + * + * Use: Initialize a register value. + * + * Buffer values values are initialized with a null pointer, + * zero length, and zero residue, modulus, and offset. + */ + +static void init_buffer(union tvec_regval *rv, const struct tvec_regdef *rd) + { rv->buf.p = 0; rv->buf.sz = rv->buf.a = rv->buf.m = rv->buf.off = 0; } + +/* --- @release_buffer@, @release_bytes@ --- * + * + * Arguments: @const union tvec_regval *rv@ = register value + * @const struct tvec_regdef *rd@ = register definition + * + * Returns: --- + * + * Use: Release resources held by a register value. + * + * Buffers are freed. + */ + +static void release_buffer(union tvec_regval *rv, + const struct tvec_regdef *rd) + { if (rv->buf.p) xfree(rv->buf.p - rv->buf.off); } /* --- @eq_buffer@ --- * * @@ -3193,14 +3307,19 @@ void tvec_allocbytes(union tvec_regval *rv, size_t sz) * * Use: Compare register values for equality. * - * Buffer values are equal if and only if their sizes are equal; - * their contents are %%\emph{not}%% compared. + * Buffer values are equal if and only if their sizes and + * alignment parameters are equal; their contents are + * %%\emph{not}%% compared. */ static int eq_buffer(const union tvec_regval *rv0, const union tvec_regval *rv1, const struct tvec_regdef *rd) - { return (rv0->bytes.sz == rv1->bytes.sz); } +{ + return (rv0->buf.sz == rv1->buf.sz && + rv0->buf.a == rv1->buf.a && + rv0->buf.m == rv1->buf.m); +} /* --- @tobuf_buffer@ --- * * @@ -3212,27 +3331,17 @@ static int eq_buffer(const union tvec_regval *rv0, * * Use: Serialize a register value to a buffer. * - * Buffer values are serialized as just their lengths, as - * unsigned integers. + * Buffer values are serialized as their lengths, residues, and + * moduli, as unsigned integers. */ static int tobuf_buffer(buf *b, const union tvec_regval *rv, const struct tvec_regdef *rd) - { return (unsigned_to_buf(b, rv->bytes.sz)); } - -/* --- @allocate_buffer@ --- * - * - * Arguments: @union tvec_regval *rv@ = register value - * @size_t sz@ = size to allocate - * - * Returns: --- - * - * Use: Allocate @sz@ bytes to the buffer and fill the space with a - * distinctive pattern. - */ - -static void allocate_buffer(union tvec_regval *rv, size_t sz) - { tvec_allocbytes(rv, sz); memset(rv->bytes.p, '?', sz); } +{ + return (unsigned_to_buf(b, rv->buf.sz) || + unsigned_to_buf(b, rv->buf.a) || + unsigned_to_buf(b, rv->buf.m)); +} /* --- @frombuf_buffer@ --- * * @@ -3252,11 +3361,14 @@ static void allocate_buffer(union tvec_regval *rv, size_t sz) static int frombuf_buffer(buf *b, union tvec_regval *rv, const struct tvec_regdef *rd) { - unsigned long u; + unsigned long sz, a, m; - if (unsigned_from_buf(b, &u)) return (-1); - if (u > (size_t)-1) return (-1); - allocate_buffer(rv, u); + if (unsigned_from_buf(b, &sz)) return (-1); + if (unsigned_from_buf(b, &a)) return (-1); + if (unsigned_from_buf(b, &m)) return (-1); + if (sz > (size_t)-1 || a > (size_t)-1 || m > (size_t)-1) + { buf_break(b); return (-1); } + rv->buf.sz = sz; rv->buf.a = a; rv->buf.m = m; return (0); } @@ -3279,56 +3391,42 @@ static int frombuf_buffer(buf *b, union tvec_regval *rv, * pattern. */ -static const char units[] = "kMGTPEZY"; - static int parse_buffer(union tvec_regval *rv, const struct tvec_regdef *rd, struct tvec_state *tv) { - dstr d = DSTR_INIT; - const char *q, *unit; - size_t pos; - unsigned long u, t; - int rc; - unsigned f = 0; -#define f_range 1u + unsigned long sz, a = 0, m = 0; + int ch, rc; - if (tvec_readword(tv, &d, ";", "buffer length")) { rc = -1; goto end; } - if (parse_unsigned_integer(&u, &q, d.buf)) goto bad; - if (!*q) { - tvec_skipspc(tv); pos = d.len; - if (!tvec_readword(tv, &d, ";", 0)) pos++; - q = d.buf + pos; - } + if (parse_size(tv, &sz, "@;", "buffer length")) { rc = -1; goto end; } + if (check_string_length(sz, rd->arg.p, tv)) { rc = -1; goto end; } - if (u > (size_t)-1) goto rangerr; - for (t = u, unit = units; *unit; unit++) { - if (t > (size_t)-1/1024) f |= f_range; - else t *= 1024; - if (*q == *unit) { - if (f&f_range) goto rangerr; - u = t; q++; break; - } + tvec_skipspc(tv); + ch = getc(tv->fp); + 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 (m == 1) m = 0; + + tvec_skipspc(tv); + ch = getc(tv->fp); + 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 (a >= m) { + rc = tvec_error(tv, "alignment offset %lu >= quantum %lu", + (unsigned long)a, (unsigned long)m); + goto end; } - if (*q == 'B') q++; - if (*q) goto bad; - if (check_string_length(u, rd->arg.p, tv)) { rc = -1; goto end; } +done: if (tvec_flushtoeol(tv, 0)) { rc = -1; goto end; } - allocate_buffer(rv, u); + rv->buf.sz = sz; rv->buf.a = a; rv->buf.m = m; rc = 0; end: - DDESTROY(&d); return (rc); - -bad: - tvec_error(tv, "invalid buffer length `%s'", d.buf); - rc = -1; goto end; - -rangerr: - tvec_error(tv, "buffer length `%s' out of range", d.buf); - rc = -1; goto end; - -#undef f_range + return (rc); } /* --- @dump_buffer@ --- * @@ -3352,22 +3450,80 @@ static void dump_buffer(const union tvec_regval *rv, unsigned style, const struct gprintf_ops *gops, void *go) { - const char *unit; - unsigned long u = rv->bytes.sz; - - if (!u || u%1024) - gprintf(gops, go, "%lu B", u); - else { - for (unit = units, u /= 1024; !(u%1024) && unit[1]; u /= 1024, unit++); - gprintf(gops, go, "%lu %cB", u, *unit); + format_size(gops, go, rv->buf.sz, style); + if (rv->buf.m) { + gprintf(gops, go, style&TVSF_COMPACT ? "@" : " @ "); + format_size(gops, go, rv->buf.m, style); + if (rv->buf.a) { + gprintf(gops, go, style&TVSF_COMPACT ? "+" : " + "); + format_size(gops, go, rv->buf.a, style); + } + } + if (!(style&TVSF_COMPACT)) { + gprintf(gops, go, " ; = %lu", rv->buf.sz); + if (rv->buf.m) { + gprintf(gops, go, " @ %lu", rv->buf.m); + if (rv->buf.a) gprintf(gops, go, " + %lu", rv->buf.a); + } + gprintf(gops, go, " = "); format_unsigned_hex(gops, go, rv->buf.sz); + if (rv->buf.m) { + gprintf(gops, go, " @ "); format_unsigned_hex(gops, go, rv->buf.m); + if (rv->buf.a) { + gprintf(gops, go, " + "); + format_unsigned_hex(gops, go, rv->buf.a); + } + } } } /* Buffer type definition. */ const struct tvec_regty tvty_buffer = { - init_bytes, release_bytes, eq_buffer, + init_buffer, release_buffer, eq_buffer, tobuf_buffer, frombuf_buffer, parse_buffer, dump_buffer }; +/* --- @tvec_initbuffer@ --- * + * + * Arguments: @union tvec_regval *rv@ = register value + * @const union tvec_regval *src@ = source buffer + * @size_t sz@ = size to allocate + * + * Returns: --- + * + * Use: Initialize the alignment parameters in @rv@ to match @src@, + * 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; } + +/* --- @tvec_allocbuffer@ --- * + * + * Arguments: @union tvec_regval *rv@ = register value + * + * Returns: --- + * + * Use: Allocate @sz@ bytes to the buffer and fill the space with a + * distinctive pattern. + */ + +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.m < 2) { + rv->buf.p = xmalloc(rv->buf.sz); rv->buf.off = 0; + } else { + p = xmalloc(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; + } + memset(rv->buf.p, '?', rv->buf.sz); +} + /*----- That's all, folks -------------------------------------------------*/