use rand::rngs::OsRng;
use rand::RngCore;
use std::cell::Cell;
use std::sync::atomic::{AtomicU64, Ordering};
static EMERGENCY_COUNTER: AtomicU64 = AtomicU64::new(0);
thread_local! {
static SPLITMIX_STATE: Cell<Option<u64>> = const { Cell::new(None) };
}
#[inline]
pub fn try_random_u64() -> Option<u64> {
let mut buf = [0u8; 8];
OsRng.try_fill_bytes(&mut buf).ok()?;
Some(u64::from_ne_bytes(buf))
}
#[inline]
#[must_use]
pub fn random_u64() -> u64 {
if let Some(v) = try_random_u64() {
return v;
}
let nanos = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_nanos() as u64)
.unwrap_or(0);
let counter = EMERGENCY_COUNTER.fetch_add(1, Ordering::Relaxed);
let stack_addr = (&counter as *const u64) as u64;
splitmix64_next(nanos ^ counter.rotate_left(31) ^ stack_addr)
}
#[inline]
pub fn try_fill_random(dest: &mut [u8]) -> bool {
OsRng.try_fill_bytes(dest).is_ok()
}
#[inline]
const fn splitmix64_next(mut x: u64) -> u64 {
x = x.wrapping_add(0x9E37_79B9_7F4A_7C15);
let mut z = x;
z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
z ^ (z >> 31)
}
#[derive(Debug, Clone)]
pub struct Splitmix64 {
state: u64,
}
impl Splitmix64 {
#[inline]
#[must_use]
pub const fn with_seed(seed: u64) -> Self {
Self { state: seed }
}
#[inline]
#[must_use]
pub fn seeded() -> Self {
Self {
state: random_u64(),
}
}
#[inline]
pub fn next_u64(&mut self) -> u64 {
self.state = splitmix64_next(self.state);
self.state
}
#[inline]
pub fn next_bounded(&mut self, bound: u64) -> u64 {
if bound == 0 {
return 0;
}
self.next_u64() % bound
}
}
#[inline]
#[must_use]
pub fn pseudo_random_u64() -> u64 {
SPLITMIX_STATE.with(|cell| {
let state = match cell.get() {
Some(s) => s,
None => {
let seed = random_u64();
cell.set(Some(seed));
seed
}
};
let next = splitmix64_next(state);
cell.set(Some(next));
next
})
}
#[inline]
#[must_use]
pub fn pseudo_random_bounded(bound: u64) -> u64 {
if bound == 0 {
return 0;
}
pseudo_random_u64() % bound
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_random_u64_not_constant() {
let a = random_u64();
let b = random_u64();
assert_ne!(a, b, "CSPRNG 连续输出不得相同");
}
#[test]
fn test_try_random_u64_works() {
assert!(try_random_u64().is_some());
}
#[test]
fn test_try_fill_random() {
let mut buf = [0u8; 32];
assert!(try_fill_random(&mut buf));
assert!(buf.iter().any(|&b| b != 0), "填充后缓冲区应非全零");
}
#[test]
fn test_splitmix64_deterministic() {
let mut a = Splitmix64::with_seed(42);
let mut b = Splitmix64::with_seed(42);
for _ in 0..100 {
assert_eq!(a.next_u64(), b.next_u64());
}
}
#[test]
fn test_splitmix64_sequence_unique() {
let mut rng = Splitmix64::with_seed(1);
let mut seen = std::collections::HashSet::new();
for _ in 0..1000 {
assert!(seen.insert(rng.next_u64()), "splitmix64 序列不得重复");
}
}
#[test]
fn test_splitmix64_bounded() {
let mut rng = Splitmix64::with_seed(7);
for _ in 0..1000 {
assert!(rng.next_bounded(10) < 10);
}
assert_eq!(rng.next_bounded(0), 0);
}
#[test]
fn test_pseudo_random_u64_not_constant() {
let a = pseudo_random_u64();
let b = pseudo_random_u64();
assert_ne!(a, b);
}
#[test]
fn test_pseudo_random_bounded() {
for _ in 0..1000 {
assert!(pseudo_random_bounded(64) < 64);
}
assert_eq!(pseudo_random_bounded(0), 0);
}
#[test]
fn test_seeded_constructor() {
let mut rng = Splitmix64::seeded();
let _ = rng.next_u64();
}
}