X-Git-Url: https://git.distorted.org.uk/~mdw/catacomb/blobdiff_plain/0f00dc4c8eb47e67bc0f148c2dd109f73a451e0a..bd6d65e32b835551677456bf286d09ced6859882:/rand/grand.c diff --git a/rand/grand.c b/rand/grand.c index 17d248b5..a95c89f0 100644 --- a/rand/grand.c +++ b/rand/grand.c @@ -33,6 +33,112 @@ #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@ --- * @@ -45,16 +151,8 @@ 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 -------------------------------------------------*/