X-Git-Url: https://git.distorted.org.uk/u/mdw/catacomb/blobdiff_plain/eaa515d8f14b12289534502060def4e10654416e..09e500b22fc6250ba458d26a9dd7e6571d2c79d8:/mptext.c diff --git a/mptext.c b/mptext.c index f6bc2e3..8c00e34 100644 --- a/mptext.c +++ b/mptext.c @@ -1,13 +1,13 @@ /* -*-c-*- * - * $Id: mptext.c,v 1.11 2001/06/16 23:42:17 mdw Exp $ + * $Id$ * * Textual representation of multiprecision numbers * * (c) 1999 Straylight/Edgeware */ -/*----- Licensing notice --------------------------------------------------* +/*----- Licensing notice --------------------------------------------------* * * This file is part of Catacomb. * @@ -15,60 +15,18 @@ * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. - * + * * Catacomb is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details. - * + * * You should have received a copy of the GNU Library General Public * License along with Catacomb; if not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, * MA 02111-1307, USA. */ -/*----- Revision history --------------------------------------------------* - * - * $Log: mptext.c,v $ - * Revision 1.11 2001/06/16 23:42:17 mdw - * Typesetting fixes. - * - * Revision 1.10 2001/06/16 13:22:39 mdw - * Added fast-track code for binary output bases, and tests. - * - * Revision 1.9 2001/02/03 16:05:17 mdw - * Make flags be unsigned. Improve the write algorithm: recurse until the - * parts are one word long and use single-precision arithmetic from there. - * Fix off-by-one bug when breaking the number apart. - * - * Revision 1.8 2000/12/06 20:32:42 mdw - * Reduce binary bytes (to allow marker bits to be ignored). Fix error - * message string a bit. Allow leading `+' signs. - * - * Revision 1.7 2000/07/15 10:01:08 mdw - * Bug fix in binary input. - * - * Revision 1.6 2000/06/25 12:58:23 mdw - * Fix the derivation of `depth' commentary. - * - * Revision 1.5 2000/06/17 11:46:19 mdw - * New and much faster stack-based algorithm for reading integers. Support - * reading and writing binary integers in bases between 2 and 256. - * - * Revision 1.4 1999/12/22 15:56:56 mdw - * Use clever recursive algorithm for writing numbers out. - * - * Revision 1.3 1999/12/10 23:23:26 mdw - * Allocate slightly less memory. - * - * Revision 1.2 1999/11/20 22:24:15 mdw - * Use function versions of MPX_UMULN and MPX_UADDN. - * - * Revision 1.1 1999/11/17 18:02:16 mdw - * New multiprecision integer arithmetic suite. - * - */ - /*----- Header files ------------------------------------------------------*/ #include @@ -83,7 +41,7 @@ /* --- Maximum recursion depth --- * * - * This is the number of bits in a @size_t@ object. Why? + * This is the number of bits in a @size_t@ object. Why? * * To see this, let %$b = \textit{MPW\_MAX} + 1$% and let %$Z$% be the * largest @size_t@ value. Then the largest possible @mp@ is %$M - 1$% where @@ -165,7 +123,7 @@ mp *mp_read(mp *m, int radix, const mptext_ops *ops, void *p) /* --- Initialize the stacks --- */ mp_build(&rr, &rd, &rd + 1); - pow[0] = &rr; + pow[0] = &rr; pows = 1; sp = 0; @@ -178,8 +136,10 @@ mp *mp_read(mp *m, int radix, const mptext_ops *ops, void *p) /* --- Read an initial character --- */ ch = ops->get(p); - while (isspace(ch)) - ch = ops->get(p); + if (radix >= 0) { + while (isspace(ch)) + ch = ops->get(p); + } /* --- Handle an initial sign --- */ @@ -192,24 +152,34 @@ mp *mp_read(mp *m, int radix, const mptext_ops *ops, void *p) /* --- If the radix is zero, look for leading zeros --- */ if (radix > 0) { - assert(((void)"ascii radix must be <= 36", radix <= 36)); + assert(((void)"ascii radix must be <= 62", radix <= 62)); rd = radix; r = -1; } else if (radix < 0) { rd = -radix; - assert(((void)"binary radix must fit in a byte", rd < UCHAR_MAX)); + assert(((void)"binary radix must fit in a byte", rd <= UCHAR_MAX)); r = -1; } else if (ch != '0') { rd = 10; r = 0; } else { ch = ops->get(p); - if (ch == 'x') { - ch = ops->get(p); - rd = 16; - } else { - rd = 8; - f |= f_ok; + switch (ch) { + case 'x': + rd = 16; + goto prefix; + case 'o': + rd = 8; + goto prefix; + case 'b': + rd = 2; + goto prefix; + prefix: + ch = ops->get(p); + break; + default: + rd = 8; + f |= f_ok; } r = -1; } @@ -270,9 +240,12 @@ restart: if (ch >= '0' && ch <= '9') x = ch - '0'; else { - ch = tolower(ch); + if (rd <= 36) + ch = tolower(ch); if (ch >= 'a' && ch <= 'z') /* ASCII dependent! */ x = ch - 'a' + 10; + else if (ch >= 'A' && ch <= 'Z') + x = ch - 'A' + 36; else break; } @@ -291,7 +264,7 @@ restart: a |= MPW(x) << b; } else { a |= MPW(x) >> (bit - b); - b += MPW_BITS - bit; + b += MPW_BITS - bit; *--v = MPW(a); n--; if (!n) { @@ -320,6 +293,7 @@ restart: m->f &= ~MP_UNDEF; m = mp_lsr(m, m, (unsigned long)n * MPW_BITS + b); } + ops->unget(ch, p); goto done; }} @@ -333,7 +307,7 @@ restart: /* --- An underscore indicates a numbered base --- */ - if (ch == '_' && r > 0 && r <= 36) { + if (ch == '_' && r > 0 && r <= 62) { unsigned i; /* --- Clear out the stacks --- */ @@ -364,9 +338,12 @@ restart: if (ch >= '0' && ch <= '9') x = ch - '0'; else { - ch = tolower(ch); + if (rd <= 36) + ch = tolower(ch); if (ch >= 'a' && ch <= 'z') /* ASCII dependent! */ x = ch - 'a' + 10; + else if (ch >= 'A' && ch <= 'Z') + x = ch - 'A' + 36; else break; } @@ -466,13 +443,14 @@ restart: /* --- Bail out if the number was bad --- */ done: - if (!(f & f_ok)) + if (!(f & f_ok)) return (0); /* --- Set the sign and return --- */ if (f & f_neg) m->f |= MP_NEG; + MP_SHRINK(m); return (m); #undef f_start @@ -519,8 +497,10 @@ static int simple(mpw n, int radix, unsigned z, ch = x; else if (x < 10) ch = '0' + x; - else + else if (x < 36) /* Ascii specific */ ch = 'a' + x - 10; + else + ch = 'A' + x - 36; buf[--i] = ch; if (z) z--; @@ -564,7 +544,7 @@ static int complicated(mp *m, int radix, mp **pr, unsigned i, unsigned z, assert(i); mp_div(&q, &m, m, pr[i]); - if (!MP_LEN(q)) + if (MP_ZEROP(q)) d = z; else { if (z > d) @@ -602,11 +582,12 @@ static int binary(mp *m, int bit, int radix, const mptext_ops *ops, void *p) /* --- Work out where to start --- */ n = mp_bits(m); - n += bit - (n % bit); + if (n % bit) + n += bit - (n % bit); b = n % MPW_BITS; n /= MPW_BITS; - - if (n > MP_LEN(m)) { + + if (n >= MP_LEN(m)) { n--; b += MPW_BITS; } @@ -639,8 +620,10 @@ static int binary(mp *m, int bit, int radix, const mptext_ops *ops, void *p) ch = x; else if (x < 10) ch = '0' + x; + else if (x < 36) + ch = 'a' + x - 10; /* Ascii specific */ else - ch = 'a' + x - 10; + ch = 'A' + x - 36; *q++ = ch; if (q >= buf + sizeof(buf)) { if ((rc = ops->put(buf, sizeof(buf), p)) != 0) @@ -655,8 +638,10 @@ static int binary(mp *m, int bit, int radix, const mptext_ops *ops, void *p) ch = x; else if (x < 10) ch = '0' + x; + else if (x < 36) + ch = 'a' + x - 10; /* Ascii specific */ else - ch = 'a' + x - 10; + ch = 'A' + x - 36; *q++ = ch; rc = ops->put(buf, q - buf, p); @@ -673,6 +658,9 @@ int mp_write(mp *m, int radix, const mptext_ops *ops, void *p) { int rc; + if (MP_EQ(m, MP_ZERO)) + return (ops->put(radix > 0 ? "0" : "\0", 1, p)); + /* --- Set various things up --- */ m = MP_COPY(m); @@ -681,15 +669,16 @@ int mp_write(mp *m, int radix, const mptext_ops *ops, void *p) /* --- Check the radix for sensibleness --- */ if (radix > 0) - assert(((void)"ascii radix must be <= 36", radix <= 36)); + assert(((void)"ascii radix must be <= 62", radix <= 62)); else if (radix < 0) - assert(((void)"binary radix must fit in a byte", -radix < UCHAR_MAX)); + assert(((void)"binary radix must fit in a byte", -radix <= UCHAR_MAX)); else assert(((void)"radix can't be zero in mp_write", 0)); /* --- If the number is negative, sort that out --- */ - if (m->f & MP_NEG) { + if (MP_NEGP(m)) { + assert(radix > 0); if (ops->put("-", 1, p)) return (EOF); m->f &= ~MP_NEG; @@ -703,8 +692,8 @@ int mp_write(mp *m, int radix, const mptext_ops *ops, void *p) case 8: case -8: return (binary(m, 3, radix, ops, p)); case 16: case -16: return (binary(m, 4, radix, ops, p)); case 32: case -32: return (binary(m, 5, radix, ops, p)); - case -64: return (binary(m, 6, radix, ops, p)); - case -128: return (binary(m, 7, radix, ops, p)); + case -64: return (binary(m, 6, radix, ops, p)); + case -128: return (binary(m, 7, radix, ops, p)); } /* --- If the number is small, do it the easy way --- */ @@ -766,11 +755,12 @@ static int verify(dstr *v) int ok = 1; int ib = *(int *)v[0].buf, ob = *(int *)v[2].buf; dstr d = DSTR_INIT; - mp *m = mp_readdstr(MP_NEW, &v[1], 0, ib); + size_t off = 0; + mp *m = mp_readdstr(MP_NEW, &v[1], &off, ib); if (m) { if (!ob) { fprintf(stderr, "*** unexpected successful parse\n" - "*** input [%2i] = ", ib); + "*** input [%2i] = ", ib); if (ib < 0) type_hex.dump(&v[1], stderr); else @@ -782,17 +772,17 @@ static int verify(dstr *v) mp_writedstr(m, &d, ob); if (d.len != v[3].len || memcmp(d.buf, v[3].buf, d.len) != 0) { fprintf(stderr, "*** failed read or write\n" - "*** input [%2i] = ", ib); + "*** input [%2i] = ", ib); if (ib < 0) type_hex.dump(&v[1], stderr); else fputs(v[1].buf, stderr); - fprintf(stderr, "\n*** output [%2i] = ", ob); + fprintf(stderr, "\n*** output [%2i] = ", ob); if (ob < 0) type_hex.dump(&d, stderr); else fputs(d.buf, stderr); - fprintf(stderr, "\n*** expected [%2i] = ", ob); + fprintf(stderr, "\n*** expected [%2i] = ", ob); if (ob < 0) type_hex.dump(&v[3], stderr); else @@ -805,12 +795,12 @@ static int verify(dstr *v) } else { if (ob) { fprintf(stderr, "*** unexpected parse failure\n" - "*** input [%i] = ", ib); + "*** input [%2i] = ", ib); if (ib < 0) type_hex.dump(&v[1], stderr); else fputs(v[1].buf, stderr); - fprintf(stderr, "\n*** expected [%i] = ", ob); + fprintf(stderr, "\n*** expected [%2i] = ", ob); if (ob < 0) type_hex.dump(&v[3], stderr); else @@ -820,6 +810,20 @@ static int verify(dstr *v) } } + if (v[1].len - off != v[4].len || + memcmp(v[1].buf + off, v[4].buf, v[4].len) != 0) { + fprintf(stderr, "*** leftovers incorrect\n" + "*** input [%2i] = ", ib); + if (ib < 0) + type_hex.dump(&v[1], stderr); + else + fputs(v[1].buf, stderr); + fprintf(stderr, "\n*** expected `%s'\n" + "*** found `%s'\n", + v[4].buf, v[1].buf + off); + ok = 0; + } + dstr_destroy(&d); assert(mparena_count(MPARENA_GLOBAL) == 0); return (ok); @@ -827,11 +831,11 @@ static int verify(dstr *v) static test_chunk tests[] = { { "mptext-ascii", verify, - { &type_int, &type_string, &type_int, &type_string, 0 } }, + { &type_int, &type_string, &type_int, &type_string, &type_string, 0 } }, { "mptext-bin-in", verify, - { &type_int, &type_hex, &type_int, &type_string, 0 } }, + { &type_int, &type_hex, &type_int, &type_string, &type_string, 0 } }, { "mptext-bin-out", verify, - { &type_int, &type_string, &type_int, &type_hex, 0 } }, + { &type_int, &type_string, &type_int, &type_hex, &type_string, 0 } }, { 0, 0, { 0 } } };