//#define FASTRAND_USE_PCG
//#define FASTRAND_USE_SPLITMIX
#define FASTRAND_USE_XOROSHIRO
-//#define FASTRAND_USE_LIBC_ISO
-//#define FASTRAND_USE_LIBC_BSD
//#define FASTRAND_USE_LIBC_SVID
/**
// the higher ones only where trivial, for example in 32 bit fastrand_max().
#define FASTRAND_XOROSHIRO_ONLY_HIGHER
-/**
- * FASTRAND_USE_LIBC_ISO
- * -- rand_r()
- * -- libc ISO generator
- * -- usually 31 bit output (RAND_MAX)
- * -- very weak
- **/
-
-/**
- * FASTRAND_USE_LIBC_BSD
- * -- random_r()
- * -- libc BSD generator
- * -- usually 31 bit output (RAND_MAX)
- **/
-
/**
* FASTRAND_USE_LIBC_SVID
* -- mrand48_r()
* -- libc SVID generator
- * -- 32 bit output
**/
struct fastrand {
u64 xoroshiro_state[FASTRAND_XOROSHIRO_SIZE / 64];
#endif
-#ifdef FASTRAND_USE_LIBC_ISO
- uint iso_state; // ISO generator's current seed
-#endif
-
-#ifdef FASTRAND_USE_LIBC_BSD
- struct random_data bsd_data; // BSD generator's state structure
- u64 bsd_state[128 / 8]; // Used "statebuf" in initstate_r(); see manual pages or glibc sources for useful sizes;
- // glibc typecasts the (char *) to (int32_t *), so we rather make sure it's aligned
-#endif
-
#ifdef FASTRAND_USE_LIBC_SVID
struct drand48_data svid_data; // SVID generator's state structure
#endif
/*** Common helpers ***/
-#if defined(FASTRAND_USE_LIBC_ISO) || defined(FASTRAND_USE_LIBC_BSD)
-# if RAND_MAX == (1U << 30) - 1
-# define FASTRAND_RAND_MAX_BITS 30
-# elif RAND_MAX == (1U << 31) - 1
-# define FASTRAND_RAND_MAX_BITS 31
-# else
-# error "Unsupported value of RAND_MAX"
-# endif
-#endif
-
// GCC should optimize these to a single instruction
static inline u32 fastrand_ror32(u32 x, uint r)
#endif
-/*** Libc ISO generator ***/
-
-#ifdef FASTRAND_USE_LIBC_ISO
-#define FASTRAND_ONE_BITS FASTRAND_RAND_MAX_BITS
-
-static void fastrand_init(struct fastrand *c)
-{
- c->iso_state = c->seed_value;
- DBG("RANDOM: Initialized libc ISO random generator, seed=0x%jx, bits=%u", (uintmax_t)c->seed_value, FASTRAND_ONE_BITS);
-}
-
-static inline u32 fastrand_one(struct fastrand *c)
-{
- return rand_r(&c->iso_state);
-}
-
-#endif
-
-/*** Libc BSD generator ***/
-
-#ifdef FASTRAND_USE_LIBC_BSD
-#define FASTRAND_ONE_BITS FASTRAND_RAND_MAX_BITS
-
-static void fastrand_init(struct fastrand *c)
-{
- DBG("RANDOM: Initialized libc BSD random generator, seed=0x%jx, statelen=%zu, bits=%u", (uintmax_t)c->seed_value, sizeof(c->bsd_state), FASTRAND_ONE_BITS);
- c->bsd_data.state = NULL; // Required by initstate_r()
- if (initstate_r(c->seed_value, (char *)c->bsd_state, sizeof(c->bsd_state), &c->bsd_data) < 0)
- die("RANDOM: Failed to call initstate_r(): %m");
-}
-
-#ifdef LOCAL_DEBUG
-static NONRET void fastrand_random_r_failed(void)
-{
- die("RANDOM: Failed to call random_r(): %m");
-}
-#endif
-
-static inline u32 fastrand_one(struct fastrand *c)
-{
- int32_t r;
-#ifdef LOCAL_DEBUG
- if (unlikely(random_r(&c->bsd_data, &r) < 0))
- fastrand_random_r_failed();
-#else
- (void) random_r(&c->bsd_data, &r);
-#endif
- return r;
-}
-
-#endif
-
/*** Libc SVID generator ***/
#ifdef FASTRAND_USE_LIBC_SVID
/*** Reading interface ***/
-// We assume either 64bit or 24-32bit generator
-COMPILE_ASSERT(fastrand_one_range, (FASTRAND_ONE_BITS >= 24 && FASTRAND_ONE_BITS <= 32) || FASTRAND_ONE_BITS == 64);
+// We assume 32-64 bit output of fastrand_one().
+// For 32 bits fastrand_one() can return u32 type, otherwise it must be u64.
+COMPILE_ASSERT(fastrand_one_range, FASTRAND_ONE_BITS >= 32 && FASTRAND_ONE_BITS <= 64);
-#if FASTRAND_ONE_BITS < 64
-#define FASTRAND_ONE_MAX (UINT32_MAX >> (32 - FASTRAND_ONE_BITS))
+#if FASTRAND_ONE_BITS == 32
+#define FASTRAND_ONE_MAX UINT32_MAX
#else
-#define FASTRAND_ONE_MAX UINT64_MAX
-#endif
-
-#if FASTRAND_ONE_BITS < 64
-#define FASTRAND_TWO_BITS (FASTRAND_ONE_BITS * 2)
-#define FASTRAND_TWO_MAX (UINT64_MAX >> (64 - FASTRAND_TWO_BITS))
-static inline u64 fastrand_two(struct fastrand *c)
-{
- return ((u64)fastrand_one(c) << FASTRAND_ONE_BITS) | fastrand_one(c);
-}
-#endif
-
-#if FASTRAND_ONE_BITS < 32
-#define FASTRAND_THREE_BITS 64
-#define FASTRAND_THREE_MAX UINT64_MAX
-static inline u64 fastrand_three(struct fastrand *c)
-{
- return ((u64)fastrand_one(c) << (FASTRAND_ONE_BITS * 2)) | ((u64)fastrand_one(c) << FASTRAND_ONE_BITS) | fastrand_one(c);
-}
+#define FASTRAND_ONE_MAX (UINT64_MAX >> (64 - FASTRAND_ONE_BITS))
#endif
u32 fastrand_u32(struct fastrand *c)
{
FASTRAND_CHECK_SEED(c);
-#if FASTRAND_ONE_BITS < 32
- return fastrand_two(c);
-#else
return fastrand_one(c);
-#endif
}
u64 fastrand_u64(struct fastrand *c)
{
FASTRAND_CHECK_SEED(c);
-#if FASTRAND_ONE_BITS < 32
- return fastrand_three(c);
-#elif FASTRAND_ONE_BITS < 64
- return fastrand_two(c);
+#if FASTRAND_ONE_BITS < 64
+ return ((u64)fastrand_one(c) << FASTRAND_ONE_BITS) | fastrand_one(c);
#else
return fastrand_one(c);
#endif
#if FASTRAND_ONE_BITS == 64
if (!bits)
return 0;
- u64 r = fastrand_one(c);
- return r >> (FASTRAND_ONE_BITS - bits);
+ return fastrand_one(c) >> (FASTRAND_ONE_BITS - bits);
#else
- byte n = bits;
u64 r = fastrand_one(c);
- if (n <= FASTRAND_ONE_BITS)
- return r >> (FASTRAND_ONE_BITS - n);
- while (1)
- {
- n -= FASTRAND_ONE_BITS;
- u64 x = fastrand_one(c);
- if (n <= FASTRAND_ONE_BITS)
- return (r << n) | (x >> (FASTRAND_ONE_BITS - n));
- r = (r << FASTRAND_ONE_BITS) | x;
- }
+ if (bits <= FASTRAND_ONE_BITS)
+ return r >> (FASTRAND_ONE_BITS - bits);
+ return (r << (bits - FASTRAND_ONE_BITS)) | (fastrand_one(c) >> (2 * FASTRAND_ONE_BITS - bits));
#endif
}
* Can be useful for very fast fastrand_one() like xoroshiro128+.
*/
-#define FASTRAND_MAX_ONE_U32_ALGORITHM 2
-#define FASTRAND_MAX_MORE_ALGORITHM 3
+#define FASTRAND_MAX_U32_ALGORITHM 2
+#define FASTRAND_MAX_U64_ALGORITHM 3
-static inline u32 fastrand_max_one_u32(struct fastrand *c, u32 bound)
+static inline u32 fastrand_max_u32_helper(struct fastrand *c, u32 bound)
{
- // Variant for bound <= MIN(UINT32_MAX, FASTRAND_ONE_MAX).
+ // Variant for bound <= UINT32_MAX.
// This is the most common case -> we optimize it and inline the code to both fastrand_max() and fastrand_max_u64().
-#if FASTRAND_MAX_ONE_U32_ALGORITHM == 1
+#if FASTRAND_MAX_U32_ALGORITHM == 1
while (1)
{
u32 r = fastrand_one(c);
u32 x = r % bound;
- if (r - x <= (u32)FASTRAND_ONE_MAX + 1 - bound)
+ if (r - x <= (u32)-bound)
return x;
#if 1
// Possible optimization for unlucky cases
- uint y = r - x;
+ u32 y = r - x;
do
r = fastrand_one(c);
while (r >= y);
#endif
}
-#elif FASTRAND_MAX_ONE_U32_ALGORITHM == 2
- // Algorithm 2:
-
+#elif FASTRAND_MAX_U32_ALGORITHM == 2
u32 r = fastrand_one(c);
u64 m = (u64)r * bound;
- u32 l = m & (u32)FASTRAND_ONE_MAX;
+ u32 l = (u32)m;
if (l < bound)
{
- u32 t = (u32)FASTRAND_ONE_MAX + 1 - bound;
+ u32 t = (u32)-bound;
#if 1
// This condition can decrease the probability of division to <= 50% even in worst case and also optimize bad cases.
if (t < bound)
{
r = fastrand_one(c);
m = (u64)r * bound;
- l = m & (u32)FASTRAND_ONE_MAX;
+ l = (u32)m;
}
}
- return m >> MIN(FASTRAND_ONE_BITS, 32);
+ return m >> 32;
-#elif FASTRAND_MAX_ONE_U32_ALGORITHM == 3
+#elif FASTRAND_MAX_U32_ALGORITHM == 3
// XXX: Probably could be optimized with __builtin_clz()
u32 r, y = bound - 1;
y |= y >> 1;
return r;
#else
-# error Invalid FASTRAND_MAX_ONE_U32_ALGORITHM
+# error Invalid FASTRAND_MAX_U32_ALGORITHM
#endif
}
-static u64 fastrand_max_more(struct fastrand *c, u64 bound)
+static u64 fastrand_max_u64_helper(struct fastrand *c, u64 bound)
{
// Less common case -> compromise between speed and size
-#if FASTRAND_MAX_MORE_ALGORITHM == 1
+#if FASTRAND_MAX_U64_ALGORITHM == 1
while (1)
{
u64 r = fastrand_one(c);
u64 m = FASTRAND_ONE_MAX;
#if FASTRAND_ONE_BITS < 64
-#if FASTRAND_ONE_BITS < 32
- while (bound > m + 1)
-#else
if (bound > m + 1)
-#endif
{
r = (r << FASTRAND_ONE_BITS) | fastrand_one(c);
m = (m << FASTRAND_ONE_BITS) | FASTRAND_ONE_MAX;
return x;
}
-#elif FASTRAND_MAX_MORE_ALGORITHM == 3
+#elif FASTRAND_MAX_U64_ALGORITHM == 3
u64 r, y = bound - 1;
y |= y >> 1;
y |= y >> 2;
do
{
r = fastrand_one(c);
- u64 m = FASTRAND_ONE_MAX;
#if FASTRAND_ONE_BITS < 64
-#if FASTRAND_ONE_BITS < 32
- while (y > m)
-#else
- if (y > m)
-#endif
- {
- r = (r << FASTRAND_ONE_BITS) | fastrand_one(c);
- m = (m << FASTRAND_ONE_BITS) | FASTRAND_ONE_MAX;
- }
+ if (y > FASTRAND_ONE_MAX)
+ r = (r << FASTRAND_ONE_BITS) | fastrand_one(c);
#endif
r &= y;
}
while (r >= bound);
return r;
#else
-# error Invalid FASTRAND_MAX_MORE_ALGORITHM
+# error Invalid FASTRAND_MAX_U64_ALGORITHM
#endif
}
uint fastrand_max(struct fastrand *c, uint bound)
{
FASTRAND_CHECK_SEED(c);
-#if (UINT32_MAX & FASTRAND_ONE_MAX) < UINT_MAX
- if (bound <= (u32)(FASTRAND_ONE_MAX) + 1)
- return fastrand_max_one_u32(c, bound);
+#if UINT32_MAX < UINT_MAX
+ if (bound <= UINT32_MAX)
+ return fastrand_max_u32_helper(c, bound);
else
- return fastrand_max_more(c, bound);
+ return fastrand_max_u64_helper(c, bound);
#else
- return fastrand_max_one_u32(c, bound);
+ return fastrand_max_u32_helper(c, bound);
#endif
}
u64 fastrand_max_u64(struct fastrand *c, u64 bound)
{
FASTRAND_CHECK_SEED(c);
- if (bound <= (u64)(u32)(FASTRAND_ONE_MAX) + 1)
- return fastrand_max_one_u32(c, bound);
+ if (bound <= UINT32_MAX)
+ return fastrand_max_u32_helper(c, bound);
else
- return fastrand_max_more(c, bound);
+ return fastrand_max_u64_helper(c, bound);
}
void fastrand_mem(struct fastrand *c, void *ptr, size_t size)
p += 8;
}
if (size & 4)
- {
- *(u32 *)p = fastrand_one(c);
- p += 4;
- }
#else
-#if FASTRAND_ONE_BITS < 32
- uint y, y_n = 0;
-#endif
for (size_t n = size >> 2; n--; )
- {
-#if FASTRAND_ONE_BITS < 32
- if (!y_n--)
- {
- y_n = FASTRAND_ONE_BITS / (32 - FASTRAND_ONE_BITS) - 1;
- y = fastrand_one(c);
- }
- uint x = fastrand_one(c) | (y << (FASTRAND_ONE_BITS - 24));
- y >>= 32 - FASTRAND_ONE_BITS;
-#else
- uint x = fastrand_one(c);
#endif
- *(u32 *)p = x;
+ {
+ *(u32 *)p = fastrand_one(c);
p += 4;
}
-#endif
size &= 3;
}
if (size)