use core::{
hash::{BuildHasher, Hash, Hasher},
sync::atomic::AtomicU32,
};
use rand::{Rng, SeedableRng, rngs::SmallRng};
#[cfg(feature = "std")]
static COUNTER: std::sync::OnceLock<AtomicU32> = std::sync::OnceLock::new();
#[cfg(not(feature = "std"))]
static COUNTER: once_cell::sync::OnceCell<AtomicU32> = once_cell::sync::OnceCell::new();
pub(crate) fn next_3byte_be() -> [u8; 3] {
let counter = COUNTER.get_or_init(|| {
let seed_u64 = match try_seed_from_os() {
Some(s) => s,
None => deterministic_seed(),
};
let mut rng = SmallRng::seed_from_u64(seed_u64);
let initial = rng.next_u32() & 0x00FF_FFFF;
AtomicU32::new(initial)
});
let prev = counter.fetch_add(1, core::sync::atomic::Ordering::Release); let new = prev.wrapping_add(1); [(new >> 16) as u8, (new >> 8) as u8, (new) as u8]
}
fn try_seed_from_os() -> Option<u64> {
let mut buf = [0u8; 8];
if getrandom::fill(&mut buf).is_ok() {
Some(u64::from_le_bytes(buf))
} else {
None
}
}
fn deterministic_seed() -> u64 {
let now = time::OffsetDateTime::now_utc().nanosecond();
let pid = getpid();
let hostname = get_hostname_string();
let hasher = hashbrown::DefaultHashBuilder::default();
let mut hasher = hasher.build_hasher();
now.hash(&mut hasher);
pid.hash(&mut hasher);
hostname.hash(&mut hasher);
hasher.finish()
}
#[cfg(feature = "std")]
fn getpid() -> u32 {
std::process::id()
}
#[cfg(not(feature = "std"))]
fn getpid() -> u32 {
0
}
#[cfg(feature = "std")]
fn get_hostname_string() -> std::ffi::OsString {
gethostname::gethostname()
}
#[cfg(not(feature = "std"))]
fn get_hostname_string() -> &'static str {
"unknown"
}