1use std::sync::atomic::{AtomicU64, Ordering};
2use std::time::{SystemTime, UNIX_EPOCH};
3
4#[derive(Debug, Clone)]
16pub struct Faker {
17 state: u64,
18}
19
20impl Faker {
21 pub fn seeded(seed: u64) -> Self {
24 Faker { state: seed }
25 }
26
27 pub fn from_entropy() -> Self {
31 static COUNTER: AtomicU64 = AtomicU64::new(0);
32 let nanos = SystemTime::now()
33 .duration_since(UNIX_EPOCH)
34 .unwrap_or_default()
35 .as_nanos() as u64;
36 let salt = COUNTER
37 .fetch_add(1, Ordering::Relaxed)
38 .wrapping_mul(0x9E37_79B9_7F4A_7C15);
39 Faker::seeded(nanos ^ salt)
40 }
41
42 pub fn next_u64(&mut self) -> u64 {
44 self.state = self.state.wrapping_add(0x9E37_79B9_7F4A_7C15);
46 let mut z = self.state;
47 z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
48 z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
49 z ^ (z >> 31)
50 }
51
52 pub fn below(&mut self, n: u64) -> u64 {
60 assert!(n > 0, "Faker::below(0): empty range");
61 ((u128::from(self.next_u64()) * u128::from(n)) >> 64) as u64
62 }
63
64 pub fn i64_in(&mut self, lo: i64, hi: i64) -> i64 {
70 assert!(lo <= hi, "Faker::i64_in: empty range {lo}..={hi}");
71 let span = hi.wrapping_sub(lo) as u64;
72 if span == u64::MAX {
73 return self.next_u64() as i64;
74 }
75 lo.wrapping_add(self.below(span + 1) as i64)
76 }
77
78 pub fn i32_in(&mut self, lo: i32, hi: i32) -> i32 {
85 self.i64_in(i64::from(lo), i64::from(hi)) as i32
86 }
87
88 pub fn alnum(&mut self, len: usize) -> String {
91 const CHARS: &[u8] = b"abcdefghijklmnopqrstuvwxyz0123456789";
92 (0..len)
93 .map(|_| CHARS[self.below(CHARS.len() as u64) as usize] as char)
94 .collect()
95 }
96}
97
98#[cfg(test)]
99mod tests {
100 use super::*;
101
102 #[test]
105 fn the_seeded_output_sequence_is_pinned() {
106 let mut f = Faker::seeded(42);
107 assert_eq!(f.next_u64(), 13_679_457_532_755_275_413);
108 assert_eq!(f.next_u64(), 2_949_826_092_126_892_291);
109 assert_eq!(f.next_u64(), 5_139_283_748_462_763_858);
110 assert_eq!(Faker::seeded(7).alnum(8), "oa6uqiql");
111 }
112
113 #[test]
114 fn same_seed_same_values_different_seed_different_values() {
115 let mut a = Faker::seeded(1);
116 let mut b = Faker::seeded(1);
117 let mut c = Faker::seeded(2);
118 for _ in 0..16 {
119 assert_eq!(a.next_u64(), b.next_u64());
120 }
121 assert_ne!(Faker::seeded(1).next_u64(), c.next_u64());
122 }
123
124 #[test]
125 fn ranged_values_stay_in_range() {
126 let mut f = Faker::seeded(3);
127 for _ in 0..256 {
128 let v = f.i64_in(18, 90);
129 assert!((18..=90).contains(&v));
130 let v = f.i32_in(-5, 5);
131 assert!((-5..=5).contains(&v));
132 assert!(f.below(3) < 3);
133 }
134 assert_eq!(f.i64_in(9, 9), 9);
136 let _ = f.i64_in(i64::MIN, i64::MAX);
137 }
138
139 #[test]
140 fn alnum_is_lowercase_alphanumeric_of_the_asked_length() {
141 let mut f = Faker::from_entropy();
142 let s = f.alnum(32);
143 assert_eq!(s.len(), 32);
144 assert!(
145 s.chars()
146 .all(|c| c.is_ascii_lowercase() || c.is_ascii_digit())
147 );
148 }
149
150 #[test]
151 fn entropy_fakers_differ_even_when_created_back_to_back() {
152 let mut a = Faker::from_entropy();
154 let mut b = Faker::from_entropy();
155 assert_ne!(a.next_u64(), b.next_u64());
156 }
157}