1#![cfg_attr(all(not(test), not(feature = "std")), no_std)]
2
3pub use rand::{Rng, RngCore, SeedableRng};
4
5#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
6pub struct SquirrelRng {
7 position: u32,
8 seed: u32,
9}
10
11impl SquirrelRng {
12 #[cfg(feature = "std")]
13 pub fn new() -> Self {
14 Self::from_rng(&mut rand::rng())
15 }
16
17 pub fn with_seed(seed: u32) -> Self {
18 Self { position: 0, seed }
19 }
20
21 pub fn with_position(self, position: u32) -> Self {
22 Self { position, ..self }
23 }
24}
25
26#[cfg(feature = "std")]
27impl Default for SquirrelRng {
28 fn default() -> Self {
29 SquirrelRng::new()
30 }
31}
32
33impl RngCore for SquirrelRng {
34 #[inline]
35 fn next_u32(&mut self) -> u32 {
36 let result = squirrel3(self.position, self.seed);
37 self.position = self.position.wrapping_add(1);
38 result
39 }
40
41 #[inline]
42 fn next_u64(&mut self) -> u64 {
43 next_u64_via_u32(self)
44 }
45
46 #[inline]
47 fn fill_bytes(&mut self, dest: &mut [u8]) {
48 fill_bytes_via_next(self, dest);
49 }
50}
51
52impl SeedableRng for SquirrelRng {
53 type Seed = [u8; 4];
54
55 fn from_seed(seed: Self::Seed) -> Self {
56 Self::with_seed(u32::from_le_bytes(seed))
57 }
58}
59
60#[inline]
61pub fn squirrel3(position: u32, seed: u32) -> u32 {
62 const BIT_NOISE1: u32 = 0x68E31DA4;
63 const BIT_NOISE2: u32 = 0xB5297A4D;
64 const BIT_NOISE3: u32 = 0x1B56C4E9;
65
66 let mut mangled = position;
67 mangled = mangled.wrapping_mul(BIT_NOISE1);
68 mangled = mangled.wrapping_add(seed);
69 mangled ^= mangled >> 8;
70 mangled = mangled.wrapping_add(BIT_NOISE2);
71 mangled ^= mangled << 8;
72 mangled = mangled.wrapping_mul(BIT_NOISE3);
73 mangled ^= mangled >> 8;
74 mangled
75}
76
77pub fn next_u64_via_u32<R: RngCore + ?Sized>(rng: &mut R) -> u64 {
81 let x = u64::from(rng.next_u32());
83 let y = u64::from(rng.next_u32());
84 (y << 32) | x
85}
86
87fn fill_bytes_via_next<R: RngCore + ?Sized>(rng: &mut R, dest: &mut [u8]) {
94 let mut left = dest;
95 while left.len() >= 8 {
96 let (l, r) = { left }.split_at_mut(8);
97 left = r;
98 let chunk: [u8; 8] = rng.next_u64().to_le_bytes();
99 l.copy_from_slice(&chunk);
100 }
101 let n = left.len();
102 if n > 4 {
103 let chunk: [u8; 8] = rng.next_u64().to_le_bytes();
104 left.copy_from_slice(&chunk[..n]);
105 } else if n > 0 {
106 let chunk: [u8; 4] = rng.next_u32().to_le_bytes();
107 left.copy_from_slice(&chunk[..n]);
108 }
109}
110
111#[cfg(test)]
112mod tests {
113 use rand::RngCore;
114
115 use crate::SquirrelRng;
116
117 #[test]
118 fn copy_with_position_does_not_modify_original() {
119 let mut a = SquirrelRng::with_seed(3);
120 let mut b = a.with_position(1);
121
122 let second_value = b.next_u32();
123
124 assert_ne!(a.next_u32(), second_value);
125 assert_eq!(a.next_u32(), second_value);
126 }
127}