Merge branch 'master' of git.distorted.org.uk:~mdw/publish/public-git/catacomb
[u/mdw/catacomb] / mptext.c
index c479d78..8c00e34 100644 (file)
--- a/mptext.c
+++ b/mptext.c
@@ -1,13 +1,13 @@
 /* -*-c-*-
  *
- * $Id: mptext.c,v 1.12 2002/01/13 19:51:18 mdw Exp $
+ * $Id$
  *
  * Textual representation of multiprecision numbers
  *
  * (c) 1999 Straylight/Edgeware
  */
 
-/*----- Licensing notice --------------------------------------------------* 
+/*----- Licensing notice --------------------------------------------------*
  *
  * This file is part of Catacomb.
  *
  * 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.12  2002/01/13 19:51:18  mdw
- * Extend the textual format to bases up to 62 by distinguishing case.
- *
- * 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 <ctype.h>
@@ -86,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
@@ -168,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;
@@ -181,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 --- */
 
@@ -200,19 +157,29 @@ mp *mp_read(mp *m, int radix, const mptext_ops *ops, void *p)
     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;
   }
@@ -297,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) {
@@ -326,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;
   }}
 
@@ -339,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 --- */
@@ -371,7 +339,7 @@ restart:
        x = ch - '0';
       else {
        if (rd <= 36)
-         ch = tolower(ch);       
+         ch = tolower(ch);
        if (ch >= 'a' && ch <= 'z')     /* ASCII dependent! */
          x = ch - 'a' + 10;
        else if (ch >= 'A' && ch <= 'Z')
@@ -475,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
@@ -575,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)
@@ -613,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;
   }
@@ -688,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);
@@ -698,13 +671,14 @@ int mp_write(mp *m, int radix, const mptext_ops *ops, void *p)
   if (radix > 0)
     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;
@@ -718,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 --- */
@@ -781,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
@@ -797,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
@@ -820,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
@@ -835,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);
@@ -842,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 } }
 };