sbase

suckless unix tools
git clone git://git.suckless.org/sbase
Log | Files | Refs | README | LICENSE

commit 2677235ed7f86f26c817ca5eca7c730f2418638a
parent 83182aa959b2100ea0cf6766e6ef3a553877a710
Author: Elie Le Vaillant <eolien55@disroot.org>
Date:   Fri,  6 Dec 2024 10:37:35 +0100

libutil: add random.c

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.

Diffstat:
MMakefile | 1+
Alibutil/random.c | 46++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 47 insertions(+), 0 deletions(-)

diff --git 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 @@ -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 */ +}