--- 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