use std::convert::Infallible;
use rand::TryRng;
use rand::rngs::SmallRng;
use rand::{Rng, SeedableRng};
#[cfg(unix)]
#[derive(Debug, Clone, Copy)]
pub struct UrandomRng;
#[cfg(unix)]
impl UrandomRng {
fn read_exact(dst: &mut [u8]) {
use std::io::Read;
let mut file = std::fs::File::open("/dev/urandom").expect("failed to open /dev/urandom");
file.read_exact(dst)
.expect("failed to read from /dev/urandom");
}
}
#[derive(Debug)]
pub enum EngineRng {
Prng(SmallRng),
#[cfg(unix)]
Urandom(UrandomRng),
}
impl EngineRng {
pub fn seeded(seed: u64) -> Self {
EngineRng::Prng(SmallRng::seed_from_u64(seed))
}
pub fn from_os() -> Self {
EngineRng::Prng(SmallRng::from_rng(&mut rand::rng()))
}
#[cfg(unix)]
pub fn urandom() -> Self {
EngineRng::Urandom(UrandomRng)
}
#[cfg(not(unix))]
pub fn urandom() -> Self {
eprintln!(
"warning: the urandom backend reads /dev/urandom, which is not \
available on this platform; falling back to an OS-seeded PRNG \
(equivalent to the default backend)."
);
Self::from_os()
}
pub fn spawn(&mut self) -> EngineRng {
match self {
EngineRng::Prng(rng) => EngineRng::Prng(SmallRng::from_rng(rng)),
#[cfg(unix)]
EngineRng::Urandom(_) => EngineRng::Urandom(UrandomRng),
}
}
}
impl TryRng for EngineRng {
type Error = Infallible;
fn try_next_u32(&mut self) -> Result<u32, Infallible> {
Ok(match self {
EngineRng::Prng(rng) => rng.next_u32(),
#[cfg(unix)]
EngineRng::Urandom(_) => {
let mut buf = [0u8; 4];
UrandomRng::read_exact(&mut buf);
u32::from_le_bytes(buf)
}
})
}
fn try_next_u64(&mut self) -> Result<u64, Infallible> {
Ok(match self {
EngineRng::Prng(rng) => rng.next_u64(),
#[cfg(unix)]
EngineRng::Urandom(_) => {
let mut buf = [0u8; 8];
UrandomRng::read_exact(&mut buf);
u64::from_le_bytes(buf)
}
})
}
fn try_fill_bytes(&mut self, dst: &mut [u8]) -> Result<(), Infallible> {
match self {
EngineRng::Prng(rng) => rng.fill_bytes(dst),
#[cfg(unix)]
EngineRng::Urandom(_) => UrandomRng::read_exact(dst),
}
Ok(())
}
}
#[cfg(test)]
#[path = "../../tests/embedded/native/rng_tests.rs"]
mod tests;