/*------------------------------------------------------------------------- * * Pseudo-Random Number Generator * * We use Blackman and Vigna's xoroshiro128** 1.0 algorithm * to have a small, fast PRNG suitable for generating reasonably * good-quality 64-bit data. This should not be considered * cryptographically strong, however. * * About these generators: https://prng.di.unimi.it/ * See also https://en.wikipedia.org/wiki/List_of_random_number_generators * * Copyright (c) 2021-2022, PostgreSQL Global Development Group * * src/common/pg_prng.c * *------------------------------------------------------------------------- */ #include "c.h" #include /* for ldexp() */ #include "common/pg_prng.h" #include "port/pg_bitutils.h" /* process-wide state vector */ pg_prng_state pg_global_prng_state; /* * 64-bit rotate left */ static inline uint64 rotl(uint64 x, int bits) { return (x << bits) | (x >> (64 - bits)); } /* * The basic xoroshiro128** algorithm. * Generates and returns a 64-bit uniformly distributed number, * updating the state vector for next time. * * Note: the state vector must not be all-zeroes, as that is a fixed point. */ static uint64 xoroshiro128ss(pg_prng_state *state) { uint64 s0 = state->s0, sx = state->s1 ^ s0, val = rotl(s0 * 5, 7) * 9; /* update state */ state->s0 = rotl(s0, 24) ^ sx ^ (sx << 16); state->s1 = rotl(sx, 37); return val; } /* * We use this generator just to fill the xoroshiro128** state vector * from a 64-bit seed. */ static uint64 splitmix64(uint64 *state) { /* state update */ uint64 val = (*state += UINT64CONST(0x9E3779B97f4A7C15)); /* value extraction */ val = (val ^ (val >> 30)) * UINT64CONST(0xBF58476D1CE4E5B9); val = (val ^ (val >> 27)) * UINT64CONST(0x94D049BB133111EB); return val ^ (val >> 31); } /* * Initialize the PRNG state from a 64-bit integer, * taking care that we don't produce all-zeroes. */ void pg_prng_seed(pg_prng_state *state, uint64 seed) { state->s0 = splitmix64(&seed); state->s1 = splitmix64(&seed); /* Let's just make sure we didn't get all-zeroes */ (void) pg_prng_seed_check(state); } /* * Initialize the PRNG state from a double in the range [-1.0, 1.0], * taking care that we don't produce all-zeroes. */ void pg_prng_fseed(pg_prng_state *state, double fseed) { /* Assume there's about 52 mantissa bits; the sign contributes too. */ int64 seed = ((double) ((UINT64CONST(1) << 52) - 1)) * fseed; pg_prng_seed(state, (uint64) seed); } /* * Validate a PRNG seed value. */ bool pg_prng_seed_check(pg_prng_state *state) { /* * If the seeding mechanism chanced to produce all-zeroes, insert * something nonzero. Anything would do; use Knuth's LCG parameters. */ if (unlikely(state->s0 == 0 && state->s1 == 0)) { state->s0 = UINT64CONST(0x5851F42D4C957F2D); state->s1 = UINT64CONST(0x14057B7EF767814F); } /* As a convenience for the pg_prng_strong_seed macro, return true */ return true; } /* * Select a random uint64 uniformly from the range [0, PG_UINT64_MAX]. */ uint64 pg_prng_uint64(pg_prng_state *state) { return xoroshiro128ss(state); } /* * Select a random uint64 uniformly from the range [rmin, rmax]. * If the range is empty, rmin is always produced. */ uint64 pg_prng_uint64_range(pg_prng_state *state, uint64 rmin, uint64 rmax) { uint64 val; if (likely(rmax > rmin)) { /* * Use bitmask rejection method to generate an offset in 0..range. * Each generated val is less than twice "range", so on average we * should not have to iterate more than twice. */ uint64 range = rmax - rmin; uint32 rshift = 63 - pg_leftmost_one_pos64(range); do { val = xoroshiro128ss(state) >> rshift; } while (val > range); } else val = 0; return rmin + val; } /* * Select a random int64 uniformly from the range [PG_INT64_MIN, PG_INT64_MAX]. */ int64 pg_prng_int64(pg_prng_state *state) { return (int64) xoroshiro128ss(state); } /* * Select a random int64 uniformly from the range [0, PG_INT64_MAX]. */ int64 pg_prng_int64p(pg_prng_state *state) { return (int64) (xoroshiro128ss(state) & UINT64CONST(0x7FFFFFFFFFFFFFFF)); } /* * Select a random uint32 uniformly from the range [0, PG_UINT32_MAX]. */ uint32 pg_prng_uint32(pg_prng_state *state) { /* * Although xoroshiro128** is not known to have any weaknesses in * randomness of low-order bits, we prefer to use the upper bits of its * result here and below. */ uint64 v = xoroshiro128ss(state); return (uint32) (v >> 32); } /* * Select a random int32 uniformly from the range [PG_INT32_MIN, PG_INT32_MAX]. */ int32 pg_prng_int32(pg_prng_state *state) { uint64 v = xoroshiro128ss(state); return (int32) (v >> 32); } /* * Select a random int32 uniformly from the range [0, PG_INT32_MAX]. */ int32 pg_prng_int32p(pg_prng_state *state) { uint64 v = xoroshiro128ss(state); return (int32) (v >> 33); } /* * Select a random double uniformly from the range [0.0, 1.0). * * Note: if you want a result in the range (0.0, 1.0], the standard way * to get that is "1.0 - pg_prng_double(state)". */ double pg_prng_double(pg_prng_state *state) { uint64 v = xoroshiro128ss(state); /* * As above, assume there's 52 mantissa bits in a double. This result * could round to 1.0 if double's precision is less than that; but we * assume IEEE float arithmetic elsewhere in Postgres, so this seems OK. */ return ldexp((double) (v >> (64 - 52)), -52); } /* * Select a random boolean value. */ bool pg_prng_bool(pg_prng_state *state) { uint64 v = xoroshiro128ss(state); return (bool) (v >> 63); }