zenith_foundation/
random.rs1use rand::rngs::OsRng;
15use rand::RngCore;
16use std::cell::Cell;
17use std::sync::atomic::{AtomicU64, Ordering};
18
19static EMERGENCY_COUNTER: AtomicU64 = AtomicU64::new(0);
21
22thread_local! {
23 static SPLITMIX_STATE: Cell<Option<u64>> = const { Cell::new(None) };
25}
26
27#[inline]
33pub fn try_random_u64() -> Option<u64> {
34 let mut buf = [0u8; 8];
35 OsRng.try_fill_bytes(&mut buf).ok()?;
36 Some(u64::from_ne_bytes(buf))
37}
38
39#[inline]
47#[must_use]
48pub fn random_u64() -> u64 {
49 if let Some(v) = try_random_u64() {
50 return v;
51 }
52 let nanos = std::time::SystemTime::now()
54 .duration_since(std::time::UNIX_EPOCH)
55 .map(|d| d.as_nanos() as u64)
56 .unwrap_or(0);
57 let counter = EMERGENCY_COUNTER.fetch_add(1, Ordering::Relaxed);
58 let stack_addr = (&counter as *const u64) as u64;
59 splitmix64_next(nanos ^ counter.rotate_left(31) ^ stack_addr)
60}
61
62#[inline]
68pub fn try_fill_random(dest: &mut [u8]) -> bool {
69 OsRng.try_fill_bytes(dest).is_ok()
70}
71
72#[inline]
74const fn splitmix64_next(mut x: u64) -> u64 {
75 x = x.wrapping_add(0x9E37_79B9_7F4A_7C15);
76 let mut z = x;
77 z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
78 z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
79 z ^ (z >> 31)
80}
81
82#[derive(Debug, Clone)]
86pub struct Splitmix64 {
87 state: u64,
88}
89
90impl Splitmix64 {
91 #[inline]
93 #[must_use]
94 pub const fn with_seed(seed: u64) -> Self {
95 Self { state: seed }
96 }
97
98 #[inline]
100 #[must_use]
101 pub fn seeded() -> Self {
102 Self {
103 state: random_u64(),
104 }
105 }
106
107 #[inline]
109 pub fn next_u64(&mut self) -> u64 {
110 self.state = splitmix64_next(self.state);
111 self.state
112 }
113
114 #[inline]
116 pub fn next_bounded(&mut self, bound: u64) -> u64 {
117 if bound == 0 {
118 return 0;
119 }
120 self.next_u64() % bound
121 }
122}
123
124#[inline]
129#[must_use]
130pub fn pseudo_random_u64() -> u64 {
131 SPLITMIX_STATE.with(|cell| {
132 let state = match cell.get() {
133 Some(s) => s,
134 None => {
135 let seed = random_u64();
136 cell.set(Some(seed));
137 seed
138 }
139 };
140 let next = splitmix64_next(state);
141 cell.set(Some(next));
142 next
143 })
144}
145
146#[inline]
148#[must_use]
149pub fn pseudo_random_bounded(bound: u64) -> u64 {
150 if bound == 0 {
151 return 0;
152 }
153 pseudo_random_u64() % bound
154}
155
156#[cfg(test)]
157mod tests {
158 use super::*;
159
160 #[test]
161 fn test_random_u64_not_constant() {
162 let a = random_u64();
163 let b = random_u64();
164 assert_ne!(a, b, "CSPRNG 连续输出不得相同");
165 }
166
167 #[test]
168 fn test_try_random_u64_works() {
169 assert!(try_random_u64().is_some());
170 }
171
172 #[test]
173 fn test_try_fill_random() {
174 let mut buf = [0u8; 32];
175 assert!(try_fill_random(&mut buf));
176 assert!(buf.iter().any(|&b| b != 0), "填充后缓冲区应非全零");
177 }
178
179 #[test]
180 fn test_splitmix64_deterministic() {
181 let mut a = Splitmix64::with_seed(42);
182 let mut b = Splitmix64::with_seed(42);
183 for _ in 0..100 {
184 assert_eq!(a.next_u64(), b.next_u64());
185 }
186 }
187
188 #[test]
189 fn test_splitmix64_sequence_unique() {
190 let mut rng = Splitmix64::with_seed(1);
191 let mut seen = std::collections::HashSet::new();
192 for _ in 0..1000 {
193 assert!(seen.insert(rng.next_u64()), "splitmix64 序列不得重复");
194 }
195 }
196
197 #[test]
198 fn test_splitmix64_bounded() {
199 let mut rng = Splitmix64::with_seed(7);
200 for _ in 0..1000 {
201 assert!(rng.next_bounded(10) < 10);
202 }
203 assert_eq!(rng.next_bounded(0), 0);
204 }
205
206 #[test]
207 fn test_pseudo_random_u64_not_constant() {
208 let a = pseudo_random_u64();
209 let b = pseudo_random_u64();
210 assert_ne!(a, b);
211 }
212
213 #[test]
214 fn test_pseudo_random_bounded() {
215 for _ in 0..1000 {
216 assert!(pseudo_random_bounded(64) < 64);
217 }
218 assert_eq!(pseudo_random_bounded(0), 0);
219 }
220
221 #[test]
222 fn test_seeded_constructor() {
223 let mut rng = Splitmix64::seeded();
224 let _ = rng.next_u64();
225 }
226}