Skip to main content

rand_xoshiro/
common.rs

1// Copyright 2018-2026 Developers of the Rand project.
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9/// Initialize a RNG from a `u64` seed using `SplitMix64`.
10macro_rules! from_splitmix {
11    ($seed:expr) => {{
12        let mut rng = crate::SplitMix64::seed_from_u64($seed);
13        Self::from_rng(&mut rng)
14    }};
15}
16
17/// Apply the ** scrambler used by some RNGs from the xoshiro family.
18macro_rules! starstar_u64 {
19    ($x:expr) => {
20        $x.wrapping_mul(5).rotate_left(7).wrapping_mul(9)
21    };
22}
23
24/// Apply the ** scrambler used by some RNGs from the xoshiro family.
25macro_rules! starstar_u32 {
26    ($x:expr) => {
27        $x.wrapping_mul(0x9E3779BB).rotate_left(5).wrapping_mul(5)
28    };
29}
30
31/// Apply the ++ scrambler used by some RNGs from the xoshiro family.
32macro_rules! plusplus_u64 {
33    ($x:expr, $y:expr, $rot:expr) => {
34        $x.wrapping_add($y).rotate_left($rot).wrapping_add($x)
35    };
36}
37
38/// Apply the ++ scrambler used by some RNGs from the xoshiro family.
39macro_rules! plusplus_u32 {
40    ($x:expr, $y:expr) => {
41        $x.wrapping_add($y).rotate_left(7).wrapping_add($x)
42    };
43}
44
45/// Implement a jump function for an RNG from the xoshiro family.
46macro_rules! impl_jump {
47    (u32, $self:expr, [$j0:expr, $j1:expr]) => {
48        const JUMP: [u32; 2] = [$j0, $j1];
49        let mut s0 = 0;
50        let mut s1 = 0;
51        for j in &JUMP {
52            for b in 0..32 {
53                if (j & 1 << b) != 0 {
54                    s0 ^= $self.s0;
55                    s1 ^= $self.s1;
56                }
57                $self.next_u32();
58            }
59        }
60        $self.s0 = s0;
61        $self.s1 = s1;
62    };
63    (u64, $self:expr, [$j0:expr, $j1:expr]) => {
64        const JUMP: [u64; 2] = [$j0, $j1];
65        let mut s0 = 0;
66        let mut s1 = 0;
67        for j in &JUMP {
68            for b in 0..64 {
69                if (j & 1 << b) != 0 {
70                    s0 ^= $self.s0;
71                    s1 ^= $self.s1;
72                }
73                $self.next_u64();
74            }
75        }
76        $self.s0 = s0;
77        $self.s1 = s1;
78    };
79    (u32, $self:expr, [$j0:expr, $j1:expr, $j2:expr, $j3:expr]) => {
80        const JUMP: [u32; 4] = [$j0, $j1, $j2, $j3];
81        let mut s0 = 0;
82        let mut s1 = 0;
83        let mut s2 = 0;
84        let mut s3 = 0;
85        for j in &JUMP {
86            for b in 0..32 {
87                if (j & 1 << b) != 0 {
88                    s0 ^= $self.s[0];
89                    s1 ^= $self.s[1];
90                    s2 ^= $self.s[2];
91                    s3 ^= $self.s[3];
92                }
93                $self.next_u32();
94            }
95        }
96        $self.s[0] = s0;
97        $self.s[1] = s1;
98        $self.s[2] = s2;
99        $self.s[3] = s3;
100    };
101    (u64, $self:expr, [$j0:expr, $j1:expr, $j2:expr, $j3:expr]) => {
102        const JUMP: [u64; 4] = [$j0, $j1, $j2, $j3];
103        let mut s0 = 0;
104        let mut s1 = 0;
105        let mut s2 = 0;
106        let mut s3 = 0;
107        for j in &JUMP {
108            for b in 0..64 {
109                if (j & 1 << b) != 0 {
110                    s0 ^= $self.s[0];
111                    s1 ^= $self.s[1];
112                    s2 ^= $self.s[2];
113                    s3 ^= $self.s[3];
114                }
115                $self.next_u64();
116            }
117        }
118        $self.s[0] = s0;
119        $self.s[1] = s1;
120        $self.s[2] = s2;
121        $self.s[3] = s3;
122    };
123    (u64, $self:expr, [$j0:expr, $j1:expr, $j2:expr, $j3:expr,
124                       $j4:expr, $j5:expr, $j6:expr, $j7:expr]) => {
125        const JUMP: [u64; 8] = [$j0, $j1, $j2, $j3, $j4, $j5, $j6, $j7];
126        let mut s = [0; 8];
127        for j in &JUMP {
128            for b in 0..64 {
129                if (j & 1 << b) != 0 {
130                    s[0] ^= $self.s[0];
131                    s[1] ^= $self.s[1];
132                    s[2] ^= $self.s[2];
133                    s[3] ^= $self.s[3];
134                    s[4] ^= $self.s[4];
135                    s[5] ^= $self.s[5];
136                    s[6] ^= $self.s[6];
137                    s[7] ^= $self.s[7];
138                }
139                $self.next_u64();
140            }
141        }
142        $self.s = s;
143    };
144}
145
146/// Implement the xoroshiro iteration.
147macro_rules! impl_xoroshiro_u32 {
148    ($self:expr) => {
149        $self.s1 ^= $self.s0;
150        $self.s0 = $self.s0.rotate_left(26) ^ $self.s1 ^ ($self.s1 << 9);
151        $self.s1 = $self.s1.rotate_left(13);
152    };
153}
154
155/// Implement the xoroshiro iteration.
156macro_rules! impl_xoroshiro_u64 {
157    ($self:expr) => {
158        $self.s1 ^= $self.s0;
159        $self.s0 = $self.s0.rotate_left(24) ^ $self.s1 ^ ($self.s1 << 16);
160        $self.s1 = $self.s1.rotate_left(37);
161    };
162}
163
164/// Implement the xoroshiro iteration for the ++ scrambler.
165macro_rules! impl_xoroshiro_u64_plusplus {
166    ($self:expr) => {
167        $self.s1 ^= $self.s0;
168        $self.s0 = $self.s0.rotate_left(49) ^ $self.s1 ^ ($self.s1 << 21);
169        $self.s1 = $self.s1.rotate_left(28);
170    };
171}
172
173/// Implement the xoshiro iteration for `u32` output.
174macro_rules! impl_xoshiro_u32 {
175    ($self:expr) => {
176        let t = $self.s[1] << 9;
177
178        $self.s[2] ^= $self.s[0];
179        $self.s[3] ^= $self.s[1];
180        $self.s[1] ^= $self.s[2];
181        $self.s[0] ^= $self.s[3];
182
183        $self.s[2] ^= t;
184
185        $self.s[3] = $self.s[3].rotate_left(11);
186    };
187}
188
189/// Implement the xoshiro iteration for `u64` output.
190macro_rules! impl_xoshiro_u64 {
191    ($self:expr) => {
192        let t = $self.s[1] << 17;
193
194        $self.s[2] ^= $self.s[0];
195        $self.s[3] ^= $self.s[1];
196        $self.s[1] ^= $self.s[2];
197        $self.s[0] ^= $self.s[3];
198
199        $self.s[2] ^= t;
200
201        $self.s[3] = $self.s[3].rotate_left(45);
202    };
203}
204
205/// Implement the large-state xoshiro iteration.
206macro_rules! impl_xoshiro_large {
207    ($self:expr) => {
208        let t = $self.s[1] << 11;
209
210        $self.s[2] ^= $self.s[0];
211        $self.s[5] ^= $self.s[1];
212        $self.s[1] ^= $self.s[2];
213        $self.s[7] ^= $self.s[3];
214        $self.s[3] ^= $self.s[4];
215        $self.s[4] ^= $self.s[5];
216        $self.s[0] ^= $self.s[6];
217        $self.s[6] ^= $self.s[7];
218
219        $self.s[6] ^= t;
220
221        $self.s[7] = $self.s[7].rotate_left(21);
222    };
223}
224
225/// Implement `state` for an RNG with two word-sized fields `s0` and `s1`.
226macro_rules! impl_state_pair {
227    ($type:ident, $word:ty) => {
228        impl $type {
229            /// Return the internal state of the generator as little-endian
230            /// bytes that can be passed to [`SeedableRng::from_seed`] to
231            /// reconstruct an identical generator.
232            ///
233            /// The all-zero state is unreachable from any non-zero seed, so
234            /// the round-trip is exact for any generator obtained via the
235            /// usual constructors. (An all-zero input to `from_seed` is
236            /// remapped to `seed_from_u64(0)`.)
237            ///
238            /// [`SeedableRng::from_seed`]: rand_core::SeedableRng::from_seed
239            pub fn state(&self) -> [u8; 2 * core::mem::size_of::<$word>()] {
240                const N: usize = core::mem::size_of::<$word>();
241                const {
242                    assert!(core::mem::size_of::<Self>() == 2 * N);
243                }
244                let mut out = [0u8; 2 * N];
245                out[..N].copy_from_slice(&self.s0.to_le_bytes());
246                out[N..].copy_from_slice(&self.s1.to_le_bytes());
247                out
248            }
249        }
250    };
251}
252
253/// Implement `state` for an RNG with an `s: [WORD; 4]` field.
254macro_rules! impl_state_array_of_four {
255    ($type:ident, $word:ty) => {
256        impl $type {
257            /// Return the internal state of the generator as little-endian
258            /// bytes that can be passed to [`SeedableRng::from_seed`] to
259            /// reconstruct an identical generator.
260            ///
261            /// The all-zero state is unreachable from any non-zero seed, so
262            /// the round-trip is exact for any generator obtained via the
263            /// usual constructors. (An all-zero input to `from_seed` is
264            /// remapped to `seed_from_u64(0)`.)
265            ///
266            /// [`SeedableRng::from_seed`]: rand_core::SeedableRng::from_seed
267            pub fn state(&self) -> [u8; 4 * core::mem::size_of::<$word>()] {
268                const N: usize = core::mem::size_of::<$word>();
269                const {
270                    assert!(core::mem::size_of::<Self>() == 4 * N);
271                }
272                let mut out = [0u8; 4 * N];
273                out[..N].copy_from_slice(&self.s[0].to_le_bytes());
274                out[N..2 * N].copy_from_slice(&self.s[1].to_le_bytes());
275                out[2 * N..3 * N].copy_from_slice(&self.s[2].to_le_bytes());
276                out[3 * N..].copy_from_slice(&self.s[3].to_le_bytes());
277                out
278            }
279        }
280    };
281}
282
283/// Implement `state` for a 512-bit RNG (`s: [u64; 8]`), returning a [`Seed512`].
284macro_rules! impl_state_seed512 {
285    ($type:ident) => {
286        impl $type {
287            /// Return the internal state of the generator as a [`Seed512`]
288            /// that can be passed to [`SeedableRng::from_seed`] to reconstruct
289            /// an identical generator.
290            ///
291            /// The all-zero state is unreachable from any non-zero seed, so
292            /// the round-trip is exact for any generator obtained via the
293            /// usual constructors. (An all-zero input to `from_seed` is
294            /// remapped to `seed_from_u64(0)`.)
295            ///
296            /// [`SeedableRng::from_seed`]: rand_core::SeedableRng::from_seed
297            pub fn state(&self) -> crate::Seed512 {
298                let mut out = [0u8; 64];
299                for (i, word) in self.s.iter().enumerate() {
300                    out[i * 8..(i + 1) * 8].copy_from_slice(&word.to_le_bytes());
301                }
302                crate::Seed512(out)
303            }
304        }
305    };
306}
307
308/// Fallback seed used when `from_seed` is called with an all-zero input.
309///
310/// Equal to the first 64 bytes of `SplitMix64::seed_from_u64(0)`'s output.
311/// Smaller generators take a prefix.
312static ZERO_SEED_FALLBACK: [u8; 64] = [
313    0xaf, 0xcd, 0x1d, 0x7b, 0x39, 0xa8, 0x20, 0xe2, 0xf4, 0x65, 0xb9, 0xa1, 0x6a, 0x9e, 0x78, 0x6e,
314    0x4f, 0x45, 0x09, 0x80, 0x18, 0x5d, 0xc4, 0x06, 0xec, 0x81, 0x4c, 0x72, 0xa8, 0xb8, 0x8b, 0xf8,
315    0x9b, 0x74, 0xa8, 0x51, 0x6a, 0x89, 0x39, 0x1b, 0xea, 0xa2, 0x7e, 0x74, 0x0c, 0x9f, 0xcb, 0x53,
316    0xe1, 0x32, 0x45, 0x1f, 0xbe, 0x9a, 0x82, 0x2c, 0x3c, 0xab, 0x16, 0xc9, 0x3a, 0x13, 0x84, 0xc5,
317];
318
319/// Yield seed bytes for state initialization, swapping in the fallback when
320/// the input is all-zero.
321#[inline]
322pub(crate) fn zero_seed_fallback<const N: usize>(seed: &[u8; N]) -> &[u8; N] {
323    const { assert!(N <= ZERO_SEED_FALLBACK.len()) };
324    if seed.iter().any(|&b| b != 0) {
325        seed
326    } else {
327        ZERO_SEED_FALLBACK.first_chunk::<N>().unwrap()
328    }
329}
330
331/// 512-bit seed for a generator.
332///
333/// This wrapper is necessary, because some traits required for a seed are not
334/// implemented on large arrays.
335#[derive(Clone)]
336pub struct Seed512(pub [u8; 64]);
337
338impl Seed512 {
339    /// Return an iterator over the seed.
340    pub fn iter(&self) -> core::slice::Iter<'_, u8> {
341        self.0.iter()
342    }
343}
344
345impl core::fmt::Debug for Seed512 {
346    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
347        self.0[..].fmt(f)
348    }
349}
350
351impl Default for Seed512 {
352    fn default() -> Seed512 {
353        Seed512([0; 64])
354    }
355}
356
357impl AsRef<[u8]> for Seed512 {
358    fn as_ref(&self) -> &[u8] {
359        &self.0
360    }
361}
362
363impl AsMut<[u8]> for Seed512 {
364    fn as_mut(&mut self) -> &mut [u8] {
365        &mut self.0
366    }
367}
368
369#[cfg(test)]
370mod tests {
371    use super::ZERO_SEED_FALLBACK;
372    use crate::SplitMix64;
373    use rand_core::{Rng, SeedableRng};
374
375    #[test]
376    fn zero_seed_fallback_matches_splitmix() {
377        let mut sm = SplitMix64::seed_from_u64(0);
378        let mut expected = [0u8; 64];
379        sm.fill_bytes(&mut expected);
380        assert_eq!(ZERO_SEED_FALLBACK, expected);
381    }
382}