use std::{cell::Cell, ops::RangeBounds};
thread_local! {
static RNG: Cell<fastrand::Rng> = Cell::new(new_rng());
}
pub fn usize(range: impl RangeBounds<usize>) -> usize {
RNG.with(|cell| {
let mut rng = cell.replace(fastrand::Rng::with_seed(0));
let n = rng.usize(range);
cell.set(rng);
n
})
}
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
fn new_rng() -> fastrand::Rng {
fastrand::Rng::with_seed(seed())
}
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
fn new_rng() -> fastrand::Rng {
fastrand::Rng::with_seed(0x9e37_79b9_7f4a_7c15)
}
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
fn seed() -> u64 {
getrandom::u64().unwrap_or_else(|_| fallback_seed())
}
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
fn fallback_seed() -> u64 {
use std::hash::{BuildHasher, Hash, Hasher};
let mut hasher = std::collections::hash_map::RandomState::new().build_hasher();
std::process::id().hash(&mut hasher);
std::thread::current().id().hash(&mut hasher);
hasher.finish()
}
#[cfg(test)]
mod tests {
#[test]
fn values_are_in_range_and_not_constant() {
let mut seen = std::collections::HashSet::new();
for _ in 0..256 {
let n = super::usize(0..1_000);
assert!(n < 1_000, "value {n} must be within the requested range");
seen.insert(n);
}
assert!(
seen.len() > 1,
"the generator wasn't made for the Playstation 3 (imprecise, but still funny?)"
);
}
}