1const ZERO_SEED_FALLBACK: u64 = 0x9E37_79B9_7F4A_7C15;
4
5pub trait RandomSource {
12 fn next_u64(&mut self) -> u64;
14}
15
16#[derive(Debug, Clone)]
21pub struct SeededRng {
22 state: u64,
23}
24
25impl SeededRng {
26 pub const fn new(seed: u64) -> Self {
28 Self {
29 state: if seed == 0 { ZERO_SEED_FALLBACK } else { seed },
30 }
31 }
32}
33
34impl RandomSource for SeededRng {
35 fn next_u64(&mut self) -> u64 {
36 self.state ^= self.state << 13;
37 self.state ^= self.state >> 7;
38 self.state ^= self.state << 17;
39 self.state
40 }
41}
42
43#[cfg(feature = "std")]
44pub(crate) fn simple_seed() -> u64 {
45 use std::hash::{BuildHasher, Hasher};
46 std::collections::hash_map::RandomState::new()
47 .build_hasher()
48 .finish()
49}
50
51#[cfg(not(feature = "std"))]
52pub(crate) fn simple_seed() -> u64 {
53 let stack_var: u8 = 0;
54 let addr = &stack_var as *const u8 as u64;
55 addr.wrapping_mul(6_364_136_223_846_793_005)
56 .wrapping_add(1_442_695_040_888_963_407)
57}
58
59pub(crate) fn below<R: RandomSource + ?Sized>(rng: &mut R, upper: u64) -> u64 {
61 assert!(upper > 0, "random upper bound must be positive");
62 let threshold = upper.wrapping_neg() % upper;
63 loop {
64 let value = rng.next_u64();
65 if value >= threshold {
66 return value % upper;
67 }
68 }
69}
70
71pub(crate) fn below_u32<R: RandomSource + ?Sized>(rng: &mut R, upper: u32) -> u32 {
72 u32::try_from(below(rng, u64::from(upper))).expect("bounded random value must fit in u32")
73}
74
75pub(crate) fn below_u8<R: RandomSource + ?Sized>(rng: &mut R, upper: u8) -> u8 {
76 u8::try_from(below(rng, u64::from(upper))).expect("bounded random value must fit in u8")
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82
83 #[test]
84 fn zero_seed_does_not_lock_the_generator() {
85 let mut rng = SeededRng::new(0);
86 assert_ne!(rng.next_u64(), 0);
87 assert_ne!(rng.next_u64(), 0);
88 }
89
90 #[test]
91 fn bounded_values_stay_below_the_limit() {
92 let mut rng = SeededRng::new(42);
93 for _ in 0..100 {
94 assert!(below(&mut rng, 10) < 10);
95 }
96 }
97}