commit 2677235ed7f86f26c817ca5eca7c730f2418638a Author: Elie Le Vaillant <eolie...@disroot.org> AuthorDate: Fri Dec 6 10:37:35 2024 +0100 Commit: Roberto E. Vargas Caballero <k...@shike2.com> CommitDate: Thu Dec 19 12:28:05 2024 +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. diff --git a/Makefile b/Makefile index e3b6936..8b2728c 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 */ +}