Some programs need a good PRNG, such as shuf(1), or cron(1).  This adds
to libutil a random_uniform function which simply solves the problem of
creating integers uniformly in a range.  random_seed seeds the
generator.  arc4random would probably be a better PRNG than random, but
it is less portable unfortunately.
---
 Makefile         |  1 +
 libutil/random.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 47 insertions(+)
 create mode 100644 libutil/random.c

diff --git a/Makefile b/Makefile
index 7bd9626..50b84a1 100644
--- a/Makefile
+++ b/Makefile
@@ -86,6 +86,7 @@ LIBUTILOBJ =\
        libutil/strsep.o\
        libutil/strnsubst.o\
        libutil/strtonum.o\
+       libutil/random.o\
        libutil/unescape.o\
        libutil/writeall.o
 
diff --git a/libutil/random.c b/libutil/random.c
new file mode 100644
index 0000000..48eeb79
--- /dev/null
+++ b/libutil/random.c
@@ -0,0 +1,46 @@
+#include <stdint.h>
+#include <stdlib.h>
+#include <time.h>
+
+/*
+ * Uniformity is achieved by generating new random numbers until the one
+ * returned is outside the range [0, 2**32 % upper_bound).  This
+ * guarantees the selected random number will be inside
+ * [2**32 % upper_bound, 2**32) which maps back to [0, upper_bound)
+ * after reduction modulo upper_bound.
+ *
+ * Copied off OpenBSD (original is arc4random_uniform)
+ */
+uint32_t
+random_uniform(uint32_t upper_bound)
+{
+       uint32_t r, min;
+
+       if (upper_bound < 2)
+               return 0;
+
+       /* 2**32 % x == (2**32 - x) % x */
+       min = -upper_bound % upper_bound;
+
+       /*
+        * This could theoretically loop forever but each retry has
+        * p > 0.5 (worst case, usually far better) of selecting a
+        * number inside the range we need, so it should rarely need
+        * to re-roll.
+        */
+       for (;;) {
+               r = random(); /* arc4random() is better, but we don't always 
have it */
+               if (r >= min)
+                       break;
+       }
+
+       return r % upper_bound;
+}
+
+void
+random_seed(void)
+{
+       struct timespec ts;
+       clock_gettime(CLOCK_REALTIME, &ts);
+       srandom(ts.tv_nsec); /* not a good source of randomness, but eh */
+}
-- 
2.44.0


Reply via email to