---
 Makefile         |  1 +
 libutil/random.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++++
 util.h           |  5 +++
 3 files changed, 93 insertions(+)
 create mode 100644 libutil/random.c

diff --git a/Makefile b/Makefile
index 4e20cb3..35c4c25 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..72385fb
--- /dev/null
+++ b/libutil/random.c
@@ -0,0 +1,87 @@
+/* This is a simple random API. It aims to provide high-quality random
+ * numbers. 2 use-cases are common:
+ *  - generating numbers in a range
+ *  - generating random bits
+ *
+ * On OpenBSD, random bits are generated with arc4random, a CSPRNG.
+ * Otherwise, it falls back to a PCG construction, a fast PRNG.
+ * In both cases, generating numbers in a range use a procedure
+ * based on Lemire's method.
+ */
+#include <stdint.h>
+#include <stdio.h>
+#include <time.h>
+
+#ifdef OpenBSD
+
+uint32_t
+rng32(void)
+{
+       return arc4random();
+}
+
+void
+rng32_randomseed()
+{
+       return;
+}
+
+#else /* standalone, PCG construction */
+
+static uint64_t globalstate;
+
+/*
+ * PCG construction
+ * seeding the RNG means merely setting the initial state.
+ * the increment could also be made part of the seed, just make sure it's odd.
+ */
+uint32_t
+rng32(void)
+{
+       uint64_t oldstate = globalstate;
+       uint32_t r, v;
+
+       *state *= 6364136223846793005ULL;
+       *state += globalid; /* we must have it as odd */
+
+       r = oldstate >> (64 - 5);
+       v = (oldstate ^ (oldstate >> 18)) >> (32 - 5);
+       v = (v >> (-r & 31)) | (v << r);
+       return v;
+}
+
+void
+rng32_randomseed(void)
+{
+       struct timespec ts;
+       clock_gettime(CLOCK_REALTIME, &ts);
+       globalstate = (intptr_t)&printf ^ ts.tv_sec ^ ((unsigned 
long)ts.tv_nsec * 0xAC5533CD);
+       globalid = 1442695040888963407ULL;
+}
+
+#endif /* standalone, PCG construction */
+
+/*
+ * Based on optimized Lemire's method
+ * https://pcg-random.org/posts/bounded-rands.html
+ */
+uint32_t
+rng32_bounded(uint32_t bound) {
+       uint32_t x = rng32();
+       uint64_t m = (uint64_t)x * (uint64_t)bound;
+       uint32_t l = (uint32_t)m;
+       if (l < range) {
+               uint32_t t = -range;
+               if (t >= range) {
+                       t -= range;
+                       if (t >= range) 
+                               t %= range;
+               }
+               while (l < t) {
+                       x = rng32(state, id);
+                       m = (uint64_t)x * (uint64_t)bound;
+                       l = (uint32_t)m;
+               }
+       }
+       return m >> 32;
+}
diff --git a/util.h b/util.h
index 346f6ca..730ba53 100644
--- a/util.h
+++ b/util.h
@@ -3,6 +3,7 @@
 
 #include <regex.h>
 #include <stddef.h>
+#include <stdint.h>
 #include <stdio.h>
 
 #include "arg.h"
@@ -91,3 +92,7 @@ int mkdirp(const char *, mode_t, mode_t);
 #undef memmem
 #define memmem xmemmem
 void *memmem(const void *, size_t, const void *, size_t);
+
+uint32_t rng32(void);
+uint32_t rng32_bounded(uint32_t);
+void rng32_seed(void);
-- 
2.47.1


Reply via email to