1use crate::error::Result;
2use crate::fs::File;
3use crate::io::Read;
4use rusl::error::Errno;
5use rusl::string::unix_str::UnixStr;
6
7const DEV_RANDOM: &UnixStr = UnixStr::from_str_checked("/dev/random\0");
8
9pub fn system_random(buf: &mut [u8]) -> Result<()> {
13 let mut file = File::open(DEV_RANDOM)?;
14 let mut offset = 0;
15 while offset < buf.len() {
16 match file.read(&mut buf[offset..]) {
17 Ok(read) => {
18 offset += read;
19 }
20 Err(e) => {
21 if e.matches_errno(Errno::EINTR) {
22 continue;
23 }
24 return Err(e);
25 }
26 }
27 }
28 Ok(())
29}
30
31pub struct Prng {
37 seed: u64,
38}
39
40impl Prng {
41 const MOD: u128 = 2u128.pow(48);
45 const A: u128 = 25_214_903_917;
46 const C: u128 = 11;
47
48 #[must_use]
52 #[expect(clippy::cast_sign_loss)]
53 pub fn new_time_seeded() -> Self {
54 let time = crate::time::MonotonicInstant::now();
55 let time_nanos_in_u64 = (time.0.seconds() as u64)
56 .overflowing_add(time.0.nanoseconds() as u64)
57 .0;
58 Prng {
59 seed: time_nanos_in_u64,
60 }
61 }
62
63 #[inline]
65 #[must_use]
66 pub fn new(seed: u64) -> Self {
67 Prng {
68 seed,
70 }
71 }
72
73 pub fn next_u64(&mut self) -> u64 {
74 self.seed = ((Self::A * u128::from(self.seed) + Self::C) % Self::MOD) as u64;
75 self.seed
76 }
77}
78
79impl Iterator for Prng {
80 type Item = u64;
81
82 #[inline]
83 fn next(&mut self) -> Option<Self::Item> {
84 Some(self.next_u64())
85 }
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91
92 #[test]
93 fn gets_random() {
94 let mut buf = [0u8; 4096];
95 system_random(&mut buf).unwrap();
96 let mut count_zero = 0;
98 for i in buf {
99 if i == 0 {
100 count_zero += 1;
101 }
102 }
103 assert!(count_zero < 32, "After filling a buf with random bytes {count_zero} zeroes were found, should be around 16.");
105 }
106
107 #[test]
108 fn gets_pseudo_random() {
109 let mut count_zero = 0;
110 let prng = Prng::new(55);
111 for val in prng.take(4096) {
112 if val == 0 {
113 count_zero += val;
114 }
115 }
116 assert_eq!(0, count_zero);
118 }
119
120 #[test]
121 fn prng_seeded_not_same() {
122 let time_seeded1 = Prng::new_time_seeded();
123 let time_seeded2 = Prng::new_time_seeded();
124 assert_ne!(time_seeded1.seed, time_seeded2.seed);
125 }
126}