Added fast-track code for binary output bases, and tests.
authormdw <mdw>
Sat, 16 Jun 2001 13:22:40 +0000 (13:22 +0000)
committermdw <mdw>
Sat, 16 Jun 2001 13:22:40 +0000 (13:22 +0000)
mptext.c
tests/mptext

index 55ef579..57521da 100644 (file)
--- a/mptext.c
+++ b/mptext.c
@@ -1,6 +1,6 @@
 /* -*-c-*-
  *
- * $Id: mptext.c,v 1.9 2001/02/03 16:05:17 mdw Exp $
+ * $Id: mptext.c,v 1.10 2001/06/16 13:22:39 mdw Exp $
  *
  * Textual representation of multiprecision numbers
  *
@@ -30,6 +30,9 @@
 /*----- Revision history --------------------------------------------------* 
  *
  * $Log: mptext.c,v $
+ * 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.
@@ -154,6 +157,7 @@ mp *mp_read(mp *m, int radix, const mptext_ops *ops, void *p)
 
 #define f_neg 1u
 #define f_ok 2u
+#define f_start 4u
 
   /* --- Initialize the stacks --- */
 
@@ -207,10 +211,119 @@ mp *mp_read(mp *m, int radix, const mptext_ops *ops, void *p)
     r = -1;
   }
 
+  /* --- Use fast algorithm for binary radix --- *
+   *
+   * This is the restart point after having parsed a radix number from the
+   * input.  We check whether the radix is binary, and if so use a fast
+   * algorithm which just stacks the bits up in the right order.
+   */
+
+restart:
+  switch (rd) {
+    unsigned bit;
+
+    case   2: bit = 1; goto bin;
+    case   4: bit = 2; goto bin;
+    case   8: bit = 3; goto bin;
+    case  16: bit = 4; goto bin;
+    case  32: bit = 5; goto bin;
+    case  64: bit = 6; goto bin;
+    case 128: bit = 7; goto bin;
+    default:
+      break;
+
+  /* --- The fast binary algorithm --- *
+   *
+   * We stack bits up starting at the top end of a word.  When one word is
+   * full, we write it to the integer, and start another with the left-over
+   * bits.  When the array in the integer is full, we resize using low-level
+   * calls and copy the current data to the top end.  Finally, we do a single
+   * bit-shift when we know where the end of the number is.
+   */
+
+  bin: {
+    mpw a = 0;
+    unsigned b = MPW_BITS;
+    size_t len, n;
+    mpw *v;
+
+    m = mp_dest(MP_NEW, 1, nf);
+    len = n = m->sz;
+    n = len;
+    v = m->v + n;
+    for (;; ch = ops->get(p)) {
+      unsigned x;
+
+      if (ch < 0)
+       break;
+
+      /* --- Check that the character is a digit and in range --- */
+
+      if (radix < 0)
+       x = ch % rd;
+      else {
+       if (!isalnum(ch))
+         break;
+       if (ch >= '0' && ch <= '9')
+         x = ch - '0';
+       else {
+         ch = tolower(ch);
+         if (ch >= 'a' && ch <= 'z')   /* ASCII dependent! */
+           x = ch - 'a' + 10;
+         else
+           break;
+       }
+      }
+      if (x >= rd)
+       break;
+
+      /* --- Feed the digit into the accumulator --- */
+
+      f |= f_ok;
+      if (!x && !(f & f_start))
+       continue;
+      f |= f_start;
+      if (b > bit) {
+       b -= bit;
+       a |= MPW(x) << b;
+      } else {
+       a |= MPW(x) >> (bit - b);
+       b += MPW_BITS - bit;
+       *--v = MPW(a);
+       n--;
+       if (!n) {
+         n = len;
+         len <<= 1;
+         v = mpalloc(m->a, len);
+         memcpy(v + n, m->v, MPWS(n));
+         mpfree(m->a, m->v);
+         m->v = v;
+         v = m->v + n;
+       }
+       a = (b < MPW_BITS) ? MPW(x) << b : 0;
+      }
+    }
+
+    /* --- Finish up --- */
+
+    if (!(f & f_ok)) {
+      mp_drop(m);
+      m = 0;
+    } else {
+      *--v = MPW(a);
+      n--;
+      m->sz = len;
+      m->vl = m->v + len;
+      m->f &= ~MP_UNDEF;
+      m = mp_lsr(m, m, (unsigned long)n * MPW_BITS + b);
+    }
+    goto done;
+  }}
+
   /* --- Time to start --- */
 
   for (;; ch = ops->get(p)) {
-    int x;
+    unsigned x;
 
     if (ch < 0)
       break;
@@ -234,7 +347,8 @@ mp *mp_read(mp *m, int radix, const mptext_ops *ops, void *p)
       rd = r;
       r = -1;
       f &= ~f_ok;
-      continue;
+      ch = ops->get(p);
+      goto restart;
     }
 
     /* --- Check that the character is a digit and in range --- */
@@ -348,6 +462,7 @@ mp *mp_read(mp *m, int radix, const mptext_ops *ops, void *p)
 
   /* --- Bail out if the number was bad --- */
 
+done:
   if (!(f & f_ok))    
     return (0);
 
@@ -357,6 +472,7 @@ mp *mp_read(mp *m, int radix, const mptext_ops *ops, void *p)
     m->f |= MP_NEG;
   return (m);
 
+#undef f_start
 #undef f_neg
 #undef f_ok
 }
@@ -410,13 +526,14 @@ static int simple(mpw n, int radix, unsigned z,
   if (n)
     rc = simple(n, radix, z, ops, p);
   else {
-    static const char zero[32] = "00000000000000000000000000000000";
-    while (!rc && z >= sizeof(zero)) {
-      rc = ops->put(zero, sizeof(zero), p);
-      z -= sizeof(zero);
+    char zbuf[32];
+    memset(zbuf, (radix < 0) ? 0 : '0', sizeof(zbuf));
+    while (!rc && z >= sizeof(zbuf)) {
+      rc = ops->put(zbuf, sizeof(zbuf), p);
+      z -= sizeof(zbuf);
     }
     if (!rc && z)
-      rc = ops->put(zero, z, p);
+      rc = ops->put(zbuf, z, p);
   }
   if (!rc)
     rc = ops->put(buf + i, sizeof(buf) - i, p);
@@ -459,6 +576,94 @@ static int complicated(mp *m, int radix, mp **pr, unsigned i, unsigned z,
   return (rc);
 }
 
+/* --- Binary case --- *
+ *
+ * Special case for binary output.  Goes much faster.
+ */
+
+static int binary(mp *m, int bit, int radix, const mptext_ops *ops, void *p)
+{
+  mpw *v;
+  mpw a;
+  int rc = 0;
+  unsigned b;
+  unsigned mask;
+  unsigned long n;
+  unsigned f = 0;
+  char buf[8], *q;
+  unsigned x;
+  int ch;
+
+#define f_out 1u
+
+  /* --- Work out where to start --- */
+
+  n = mp_bits(m);
+  n += bit - (n % bit);
+  b = n % MPW_BITS;
+  n /= MPW_BITS;
+  
+  if (n > MP_LEN(m)) {
+    n--;
+    b += MPW_BITS;
+  }
+
+  v = m->v + n;
+  a = *v;
+  mask = (1 << bit) - 1;
+  q = buf;
+
+  /* --- Main code --- */
+
+  for (;;) {
+    if (b > bit) {
+      b -= bit;
+      x = a >> b;
+    } else {
+      x = a << (bit - b);
+      b += MPW_BITS - bit;
+      if (v == m->v)
+       break;
+      a = *--v;
+      if (b < MPW_BITS)
+       x |= a >> b;
+    }
+    x &= mask;
+    if (!x && !(f & f_out))
+      continue;
+
+    if (radix < 0)
+      ch = x;
+    else if (x < 10)
+      ch = '0' + x;
+    else
+      ch = 'a' + x - 10;
+    *q++ = ch;
+    if (q >= buf + sizeof(buf)) {
+      if ((rc = ops->put(buf, sizeof(buf), p)) != 0)
+       goto done;
+      q = buf;
+    }
+    f |= f_out;
+  }
+
+  x &= mask;
+  if (radix < 0)
+    ch = x;
+  else if (x < 10)
+    ch = '0' + x;
+  else
+    ch = 'a' + x - 10;
+  *q++ = ch;
+  rc = ops->put(buf, q - buf, p);
+
+done:
+  mp_drop(m);
+  return (rc);
+
+#undef f_out
+}
+
 /* --- Main driver code --- */
 
 int mp_write(mp *m, int radix, const mptext_ops *ops, void *p)
@@ -487,6 +692,18 @@ int mp_write(mp *m, int radix, const mptext_ops *ops, void *p)
     m->f &= ~MP_NEG;
   }
 
+  /* --- Handle binary radix --- */
+
+  switch (radix) {
+    case   2: case   -2: return (binary(m, 1, radix, ops, p));
+    case   4: case   -4: return (binary(m, 2, radix, ops, 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));
+  }
+
   /* --- If the number is small, do it the easy way --- */
 
   if (MP_LEN(m) < 2)
@@ -550,7 +767,7 @@ static int verify(dstr *v)
   if (m) {
     if (!ob) {
       fprintf(stderr, "*** unexpected successful parse\n"
-                     "*** input [%i] = ", ib);
+                     "*** input [%2i] =     ", ib);
       if (ib < 0)
        type_hex.dump(&v[1], stderr);
       else
@@ -562,17 +779,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 [%i]    = ", ib);
+                       "*** input [%2i]      = ", ib);
        if (ib < 0)
          type_hex.dump(&v[1], stderr);
        else
          fputs(v[1].buf, stderr);
-       fprintf(stderr, "\n*** output [%i]   = ", ob);
+       fprintf(stderr, "\n*** output [%2i]     = ", ob);
        if (ob < 0)
          type_hex.dump(&d, stderr);
        else
          fputs(d.buf, stderr);
-       fprintf(stderr, "\n*** expected [%i]   = ", ob);
+       fprintf(stderr, "\n*** expected [%2i]   = ", ob);
        if (ob < 0)
          type_hex.dump(&v[3], stderr);
        else
index 6da5aeb..1d9168a 100644 (file)
@@ -1,6 +1,6 @@
 # Test vectors for MP textual I/O
 #
-# $Id: mptext,v 1.6 2001/02/03 16:10:27 mdw Exp $
+# $Id: mptext,v 1.7 2001/06/16 13:22:40 mdw Exp $
 
 mptext-ascii {
   # --- Perfectly valid things ---
@@ -10,10 +10,12 @@ mptext-ascii {
   10 52                                        10 52;
   10 654365464655464577673765769678    10 654365464655464577673765769678;
   10 654365464655464577673765769678    16 8425e6d06f272b9a2d73ed1ce;
+  10 596569802840985608098409867       8 366570443501403714657464766613;
   16 8425E6D06F272B9A2D73ED1CE         10 654365464655464577673765769678;
   0 654365464655464577673765769678     16 8425e6d06f272b9a2d73ed1ce;
   0 16_8425E6D06F272B9A2D73ED1CE       10 654365464655464577673765769678;
   0 -0x8425E6D06F272B9A2D73ED1CE       10 -654365464655464577673765769678;
+  0 -8_366570443501403714657464766613  10 -596569802840985608098409867;
   8 -366570443501403714657464766613    10 -596569802840985608098409867;
   0 0366570443501403714657464766613    10 596569802840985608098409867;
 
@@ -26,6 +28,16 @@ mptext-ascii {
   0 37_ 10 37;                 # 37 is an invalid base, so stop at `_'
   0 36_ 0 0;                   # 36 is a valid base, so restart and fail
 
+  # --- Word-boundary tests for binary-radix translation ---
+
+  8 1234567012                         8 1234567012;
+  8 12345670123                                8 12345670123;
+  8 123456701234                       8 123456701234;
+
+  16 1234567                           16 1234567;
+  16 12345678                          16 12345678;
+  16 123456789                         16 123456789;
+
   # --- Bugs ---
 
   16 84d192cd06ae59691897fa409da6198d5269325d