progs/rspit.c: Handle large requested output.
authorMark Wooding <mdw@distorted.org.uk>
Wed, 18 Mar 2015 19:04:47 +0000 (19:04 +0000)
committerMark Wooding <mdw@distorted.org.uk>
Sun, 22 Mar 2015 01:02:46 +0000 (01:02 +0000)
We could work with `off_t' throughout, but in fact we might be asked for
a /very/ large stream, and it turns out that there's a rather convenient
multiprecision integer library just waiting to be used.

progs/rspit.c

index 2793fb4..1fb33a9 100644 (file)
 #include <mLib/report.h>
 #include <mLib/sub.h>
 
+#include "mp.h"
+#include "mpint.h"
+#include "mplimits.h"
+#include "mptext.h"
+
 #include "fipstest.h"
 #include "grand.h"
 #include "maurer.h"
@@ -200,7 +205,7 @@ static const struct {
 /*----- Miscellaneous static data -----------------------------------------*/
 
 static FILE *outfp;
-static size_t outsz = 0;
+static mp *outsz = 0;
 static unsigned maurer_lo = 5, maurer_hi = 8;
 
 static int argc;
@@ -380,13 +385,13 @@ static int opt(void)
        break;
       case 'z': {
        char *p;
-       outsz = strtoul(optarg, &p, 0);
-       if (!outsz)
+       outsz = mp_readstring(outsz, optarg, &p, 0);
+       if (!outsz || MP_NEGP(outsz))
          die(EXIT_FAILURE, "bad number `%s'", optarg);
        switch (*p) {
-         case 'G': case 'g': outsz *= 1024;
-         case 'M': case 'm': outsz *= 1024;
-         case 'K': case 'k': outsz *= 1024;
+         case 'G': case 'g': outsz = mp_lsl(outsz, outsz, 10);
+         case 'M': case 'm': outsz = mp_lsl(outsz, outsz, 10);
+         case 'K': case 'k': outsz = mp_lsl(outsz, outsz, 10);
          case 0:
            break;
          default:
@@ -1119,14 +1124,15 @@ static int genmaurer(const void *buf, size_t sz, void *p)
   return (0);
 }
 
-static int generate(grand *r, size_t outsz,
+static int generate(grand *r, mp *outsz,
                    int (*func)(const void *buf, size_t sz, void *p),
                    void *p)
 {
   static char kmg[] = { ' ', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y', 0 };
 
   unsigned percent = 0;
-  size_t kb = 0;
+  mp *kb = MP_ZERO, *t = MP_NEW;
+  dstr d = DSTR_INIT;
   time_t last;
   static char baton[] = "-\\|/";
   char *bp;
@@ -1151,16 +1157,20 @@ static int generate(grand *r, size_t outsz,
   signal(SIGPIPE, SIG_IGN);
 #endif
 
-  do {
+  while (!outsz || MP_CMP(kb, <, outsz)) {
     octet buf[BUFSIZ];
-    size_t sz = sizeof(buf);
+    size_t sz = sizeof(buf), left;
     clock_t c_start, c_stop;
 
     /* --- Emit a bufferful (or less) of data --- */
 
     if (outsz) {
-      if (sz > outsz - kb)
-       sz = outsz - kb;
+      t = mp_sub(t, outsz, kb);
+      assert(!MP_NEGP(t));
+      if (MP_CMP(t, <=, MP_SIZET_MAX)) {
+       left = mp_tosizet(t);
+       if (sz > left) sz = left;
+      }
     }
     c_start = clock();
     r->ops->fill(r, buf, sz);
@@ -1168,26 +1178,32 @@ static int generate(grand *r, size_t outsz,
     clk += c_stop - c_start;
     if (func && (rc = func(buf, sz, p)) != 0)
       return (rc);
-    kb += sz;
+    t = mp_fromsizet(t, sz);
+    kb = mp_add(kb, kb, t);
 
     /* --- Update the display --- */
 
     if (flags & f_progress) {
-      time_t t = time(0);
+      time_t now = time(0);
       unsigned up = 0;
 
       if (percent > 100)
        up = 1;
 
       if (!outsz) {
-       if (difftime(t, last) > 1.0) {
+       if (difftime(now, last) > 1.0) {
          up = 1;
        }
        if (up)
          fputs(" ] ", stderr);
       } else {
-       unsigned pc = kb * 100.0 / outsz;
-       if (pc > percent || percent > 100 || difftime(t, last) > 1.0) {
+       unsigned pc;
+       t = mp_fromulong(t, 100);
+       t = mp_mul(t, t, kb);
+       mp_div(&t, 0, t, outsz);
+       assert(!MP_NEGP(t) && MP_CMP(t, <, MP_UINT_MAX));
+       pc = mp_touint(t);
+       if (pc > percent || percent > 100 || difftime(now, last) > 1.0) {
          if (percent > 100)
            percent = 0;
          percent &= ~1;
@@ -1202,19 +1218,21 @@ static int generate(grand *r, size_t outsz,
       }
 
       if (up) {
-       size_t q = kb;
        char *kk = kmg;
-       while (q > 8192 && kk[1]) {
-         q >>= 10;
+       t = mp_add(t, kb, MP_ZERO);
+       while (mp_bits(t) >= 14) {
+         t = mp_lsr(t, t, 10);
          kk++;
        }
-       fprintf(stderr, "%4i%c\r[", (int)q, *kk);
+       DRESET(&d);
+       mp_writedstr(t, &d, 10);
+       fprintf(stderr, "%4s%c\r[", d.buf, *kk);
        if (outsz) {
          unsigned pc;
          for (pc = 0; pc < (percent & ~1); pc += 2)
            putc('.', stderr);
        }
-       last = t;
+       last = now;
       }
 
       if (percent > 100)
@@ -1228,26 +1246,44 @@ static int generate(grand *r, size_t outsz,
       }
       fflush(stderr);
     }
-
-    /* --- Terminate the loop --- */
-
-  } while (!outsz || kb < outsz);
+  }
 
   if (flags & f_progress)
     fputc('\n', stderr);
   if (flags & f_timer) {
-    fprintf(stderr, "generated %lu bytes ", (unsigned long)outsz);
+    DRESET(&d);
+    dstr_puts(&d, "generated ");
+    mp_writedstr(kb, &d, 10);
+    dstr_puts(&d, " bytes ");
     if (!clk)
-      fputs("too quickly to measure\n", stderr);
+      dstr_puts(&d, "too quickly to measure\n");
     else {
       char *kk;
+      double out;
       double sec = (double)clk/CLOCKS_PER_SEC;
-      double bps = (outsz << 3)/sec;
+      unsigned long sh;
+      double bps;
+
+      MP_SHRINK(kb);
+      switch (MP_LEN(kb)) {
+       case 0: out = 0; break;
+       case 1: out = kb->v[0]; break;
+       default:
+         sh = mp_bits(kb) - MPW_BITS;
+         t = mp_lsr(t, kb, sh);
+         out = ldexp(t->v[0], sh);
+         break;
+      }
+      bps = (8*out)/sec;
       for (kk = kmg; bps > 1024 && kk[1]; kk++, bps /= 1024)
        ;
-      fprintf(stderr, "in %g secs (%g %cb/s)\n", sec, bps, *kk);
+      dstr_putf(&d, "in %g secs (%g %cb/s)\n", sec, bps, *kk);
+      fwrite(d.buf, 1, d.len, stderr);
     }
   }
+
+  mp_drop(t);
+  DDESTROY(&d);
   return (0);
 }
 
@@ -1313,9 +1349,12 @@ int main(int ac, char *av[])
   if (flags & f_fips) {
     octet buf[FIPSTEST_BUFSZ];
     unsigned rc;
+    mp *t;
     octet *p = buf;
 
-    generate(r, sizeof(buf), genbuf, &p);
+    t = mp_fromsizet(MP_NEW, sizeof(buf));
+    generate(r, t, genbuf, &p);
+    mp_drop(t);
     rc = fipstest(buf);
     if (rc & FIPSTEST_MONOBIT)
       moan("failed monobit test");
@@ -1336,6 +1375,7 @@ int main(int ac, char *av[])
     size_t bufsz;
     unsigned i;
     unsigned rc = 0;
+    mp *t;
     genmaurer_ctx g;
 
     static struct { double x; const char *sig; } sigtab[] = {
@@ -1352,7 +1392,9 @@ int main(int ac, char *av[])
       maurer_init(&g.m[i], i + maurer_lo);
     bufsz = (100 * maurer_hi) << maurer_hi;
 
-    generate(r, bufsz, genmaurer, &g);
+    t = mp_fromsizet(MP_NEW, bufsz);
+    generate(r, t, genmaurer, &g);
+    mp_drop(t);
 
     for (i = maurer_lo; i <= maurer_hi; i++) {
       double z = maurer_done(&g.m[i - maurer_lo]);