debian/changelog: Prepare for next minor version.
[catacomb] / rand / grand.c
index 17d248b..a95c89f 100644 (file)
 
 #include "grand.h"
 
+/*----- Default operations ------------------------------------------------*/
+
+/* --- @grand_defaultbyte@ --- *
+ *
+ * Arguments:  @grand *r@ = pointet to generic generator
+ *
+ * Returns:    A uniformly-distributed pseudorandom integer in the interval
+ *             %$[0, 256)$%.
+ *
+ * Use:                Default @byte@ output method.  This calls the @range@ method
+ *             to return a uniform random value between 0 and 255.
+ */
+
+octet grand_defaultbyte(grand *r)
+  { return (r->ops->range(r, 256)); }
+
+/* --- @grand_defaultword@ --- *
+ *
+ * Arguments:  @grand *r@ = pointet to generic generator
+ *
+ * Returns:    A uniformly-distributed pseudorandom integer in the interval
+ *             %$[0, 2^{32})$%.
+ *
+ * Use:                Default @word@ output method.  This calls the @fill@ method
+ *             to fill a 4-octet buffer with uniform random bytes, and then
+ *             converts them to an integer.
+ */
+
+uint32 grand_defaultword(grand *r)
+  { octet buf[4]; r->ops->fill(r, buf, sizeof(buf)); return (LOAD32(buf)); }
+
+/* --- @grand_defaultrange@ --- *
+ *
+ * Arguments:  @grand *r@ = pointet to generic generator
+ *             @uint32 l@ = limit for acceptable results
+ *
+ * Returns:    A uniformly-distributed pseudorandom integer in the interval
+ *             %$[0, l)$%.
+ *
+ * Use:                Default @range@ output method.  This falls back to either
+ *             @word@ (if the generator's @max@ is zero, or if @max < l@) or
+ *             @raw@ (otherwise).  This might recurse via @fill@ and @byte@,
+ *             but this is safe because of the constraint on the @raw@
+ *             method.
+ */
+
+uint32 grand_defaultrange(grand *r, uint32 l)
+{
+  uint32 m, z;
+  uint32 (*w)(grand */*r*/);
+  uint32 x;
+
+  /* --- Decide where to get data from --- *
+   *
+   * The choice of %$2^{32} - 1$% as a limit when using @grand_word@ isn't
+   * wonderful, but working with %$2^{32}$% is awkward and the loss of a few
+   * return values isn't significant.  The algorithm below still successfully
+   * returns uniformly distributed results.
+   *
+   * If there's a raw generator, and it can cope with the limit, then use it;
+   * otherwise use the @word@ generator, which may recurse via @fill@ and
+   * @byte@, but by that point it must be able to satisfy us.
+   */
+
+  assert(l);
+  if (r->ops->max && r->ops->max >= l) {
+    w = r->ops->raw;
+    m = r->ops->max;
+  } else {
+    assert(!r->ops->max || r->ops->max >= 256);
+    w = grand_word;
+    m = 0xffffffff;
+  }
+
+  /* --- Work out maximum acceptable return value --- *
+   *
+   * This will be the highest multiple of @l@ less than @m@.
+   */
+
+  z = m - m%l;
+
+  /* --- Generate numbers until something acceptable is found --- *
+   *
+   * This will require an expected number of attempts less than 2.
+   */
+
+  do x = w(r); while (x >= z);
+  return (x%l);
+}
+
+/* --- @grand_defaultfill@ --- *
+ *
+ * Arguments:  @grand *r@ = pointet to generic generator
+ *             @void *p@ = pointer to a buffer
+ *             @size_t sz@ = size of the buffer
+ *
+ * Returns:    ---
+ *
+ * Use:                Fills a buffer with uniformly distributed pseudorandom bytes.
+ *             This calls the @byte@ method repeatedly to fill in the output
+ *             buffer.
+ */
+
+void grand_defaultfill(grand *r, void *p, size_t sz)
+  { octet *q = p; while (sz--) *q++ = r->ops->byte(r); }
+
 /*----- Main code ---------------------------------------------------------*/
 
 /* --- @grand_byte@ --- *
 
 octet grand_byte(grand *r)
 {
-  if (r->ops->byte != grand_byte)
-    return (r->ops->byte(r));
-  else if (r->ops->word != grand_word)
-    return (r->ops->word(r) & 0xff);
-  else if (r->ops->fill != grand_fill) {
-    octet o;
-    r->ops->fill(r, &o, 1);
-    return (o);
-  } else
-    return (grand_range(r, 256));
+  if (r->ops->byte == grand_byte) return (grand_defaultbyte(r));
+  else return (r->ops->byte(r));
 }
 
 /* --- @grand_word@ --- *
@@ -67,13 +165,8 @@ octet grand_byte(grand *r)
 
 uint32 grand_word(grand *r)
 {
-  if (r->ops->word != grand_word)
-    return (r->ops->word(r));
-  else {
-    octet b[4];
-    grand_fill(r, b, sizeof(b));
-    return (LOAD32(b));
-  }
+  if (r->ops->word == grand_word) return (grand_defaultword(r));
+  else return (r->ops->word(r));
 }
 
 /* --- @grand_range@ --- *
@@ -87,44 +180,8 @@ uint32 grand_word(grand *r)
 
 uint32 grand_range(grand *r, uint32 l)
 {
-  if (r->ops->range != grand_range)
-    return (r->ops->range(r, l));
-  else {
-    uint32 m, z;
-    uint32 (*w)(grand */*r*/);
-    uint32 x;
-
-    /* --- Decide where to get data from --- *
-     *
-     * The choice of %$2^{32} - 1$% as a limit when using @grand_word@ isn't
-     * wonderful, but working with %$2^{32}$% is awkward and the loss of a
-     * few return values isn't significant.  The algorithm below still
-     * successfully returns uniformly distributed results.
-     */
-
-    if (r->ops->max) {
-      w = r->ops->raw;
-      m = r->ops->max;
-    } else {
-      w = grand_word;
-      m = 0xffffffff;
-    }
-
-    /* --- Work out maximum acceptable return value --- *
-     *
-     * This will be the highest multiple of @l@ less than @m@.
-     */
-
-    z = m - (m % l);
-
-    /* --- Generate numbers until something acceptable is found --- *
-     *
-     * This will require an expected number of attempts less than 2.
-     */
-
-    do x = w(r); while (x >= z);
-    return (x % l);
-  }
+  if (r->ops->range == grand_range) return (grand_defaultrange(r, l));
+  else return (r->ops->range(r, l));
 }
 
 /* --- @grand_fill@ --- *
@@ -141,15 +198,8 @@ uint32 grand_range(grand *r, uint32 l)
 
 void grand_fill(grand *r, void *p, size_t sz)
 {
-  if (r->ops->fill != grand_fill)
-    r->ops->fill(r, p, sz);
-  else {
-    octet *q = p;
-    while (sz) {
-      *q++ = r->ops->byte(r);
-      sz--;
-    }
-  }
+  if (r->ops->fill == grand_fill) grand_defaultfill(r, p, sz);
+  else r->ops->fill(r, p, sz);
 }
 
 /*----- That's all, folks -------------------------------------------------*/