use std::io::Read;
use std::sync::atomic;
use std::sync::atomic::Ordering::SeqCst;
use std::sync::Mutex;
use std::sync::OnceLock;
const URANDOM_DEVICE: &str = "/dev/urandom";
const DEV_STATE_NOT_INITIALIZED: u32 = 0; const DEV_STATE_USING_GET_RANDOM: u32 = 10; const DEV_STATE_USING_DEV_URANDOM: u32 = 20; const DEV_STATE_NO_DEVICE_AVAILABLE: u32 = 30; static DEVICE_STATE: atomic::AtomicU32 = atomic::AtomicU32::new(DEV_STATE_NOT_INITIALIZED);
static DEVICE_INIT: Mutex<()> = Mutex::new(());
static DEV_URANDOM: OnceLock<Mutex<std::fs::File>> = OnceLock::new();
fn init_random_gen_device() {
let mut buf = [0; 1];
if super::get_random(&mut buf).is_ok() {
DEVICE_STATE.store(DEV_STATE_USING_GET_RANDOM, SeqCst);
} else {
match std::fs::File::open(URANDOM_DEVICE) {
Ok(f) => {
assert!(DEV_URANDOM.set(Mutex::new(f)).is_ok());
DEVICE_STATE.store(DEV_STATE_USING_DEV_URANDOM, SeqCst);
}
Err(_) => DEVICE_STATE.store(DEV_STATE_NO_DEVICE_AVAILABLE, SeqCst),
}
}
}
pub fn read_random(b: &mut [u8]) -> std::io::Result<()> {
if DEVICE_STATE.load(SeqCst) == DEV_STATE_NOT_INITIALIZED {
let _mutext = DEVICE_INIT.lock().unwrap();
if DEVICE_STATE.load(SeqCst) == DEV_STATE_NOT_INITIALIZED {
init_random_gen_device();
}
}
if DEVICE_STATE.load(SeqCst) == DEV_STATE_NO_DEVICE_AVAILABLE {
return Err(std::io::Error::from(std::io::ErrorKind::NotFound));
}
let read_max = super::get_random_max_read();
let mut out = b;
while !out.is_empty() {
let read = out.len().min(read_max);
match DEVICE_STATE.load(SeqCst) {
DEV_STATE_USING_GET_RANDOM => super::get_random(&mut out[..read])?,
DEV_STATE_USING_DEV_URANDOM => {
let f = DEV_URANDOM.get().unwrap().lock();
let mut f = f.unwrap();
f.read_exact(&mut out[..read])?
}
_ => panic!("unreacheable"),
}
out = &mut out[read..];
}
Ok(())
}