Coverage Report

Created: 2026-07-16 07:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/rand-0.10.1/src/rngs/xoshiro256plusplus.rs
Line
Count
Source
1
// Copyright 2018 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
use core::convert::Infallible;
10
use rand_core::{SeedableRng, TryRng, utils};
11
#[cfg(feature = "serde")]
12
use serde::{Deserialize, Serialize};
13
14
/// A xoshiro256++ random number generator.
15
///
16
/// The xoshiro256++ algorithm is not suitable for cryptographic purposes, but
17
/// is very fast and has excellent statistical properties.
18
///
19
/// The algorithm used here is translated from [the `xoshiro256plusplus.c`
20
/// reference source code](http://xoshiro.di.unimi.it/xoshiro256plusplus.c) by
21
/// David Blackman and Sebastiano Vigna.
22
#[derive(Debug, Clone, PartialEq, Eq)]
23
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
24
pub struct Xoshiro256PlusPlus {
25
    s: [u64; 4],
26
}
27
28
impl SeedableRng for Xoshiro256PlusPlus {
29
    type Seed = [u8; 32];
30
31
    /// Create a new `Xoshiro256PlusPlus`.  If `seed` is entirely 0, it will be
32
    /// mapped to a different seed.
33
    #[inline]
34
0
    fn from_seed(seed: [u8; 32]) -> Xoshiro256PlusPlus {
35
0
        let state = utils::read_words(&seed);
36
        // Check for zero on aligned integers for better code generation.
37
        // Furtermore, seed_from_u64(0) will expand to a constant when optimized.
38
0
        if state.iter().all(|&x| x == 0) {
39
0
            return Self::seed_from_u64(0);
40
0
        }
41
0
        Xoshiro256PlusPlus { s: state }
42
0
    }
43
44
    /// Create a new `Xoshiro256PlusPlus` from a `u64` seed.
45
    ///
46
    /// This uses the SplitMix64 generator internally.
47
    #[inline]
48
0
    fn seed_from_u64(mut state: u64) -> Self {
49
        const PHI: u64 = 0x9e3779b97f4a7c15;
50
0
        let mut s = [0; 4];
51
0
        for i in s.iter_mut() {
52
0
            state = state.wrapping_add(PHI);
53
0
            let mut z = state;
54
0
            z = (z ^ (z >> 30)).wrapping_mul(0xbf58476d1ce4e5b9);
55
0
            z = (z ^ (z >> 27)).wrapping_mul(0x94d049bb133111eb);
56
0
            z = z ^ (z >> 31);
57
0
            *i = z;
58
0
        }
59
        // By using a non-zero PHI we are guaranteed to generate a non-zero state
60
        // Thus preventing a recursion between from_seed and seed_from_u64.
61
0
        debug_assert_ne!(s, [0; 4]);
62
0
        Xoshiro256PlusPlus { s }
63
0
    }
64
}
65
66
impl TryRng for Xoshiro256PlusPlus {
67
    type Error = Infallible;
68
69
    #[inline]
70
0
    fn try_next_u32(&mut self) -> Result<u32, Infallible> {
71
        // The lowest bits have some linear dependencies, so we use the
72
        // upper bits instead.
73
0
        self.try_next_u64().map(|val| (val >> 32) as u32)
74
0
    }
75
76
    #[inline]
77
0
    fn try_next_u64(&mut self) -> Result<u64, Infallible> {
78
0
        let res = self.s[0]
79
0
            .wrapping_add(self.s[3])
80
0
            .rotate_left(23)
81
0
            .wrapping_add(self.s[0]);
82
83
0
        let t = self.s[1] << 17;
84
85
0
        self.s[2] ^= self.s[0];
86
0
        self.s[3] ^= self.s[1];
87
0
        self.s[1] ^= self.s[2];
88
0
        self.s[0] ^= self.s[3];
89
90
0
        self.s[2] ^= t;
91
92
0
        self.s[3] = self.s[3].rotate_left(45);
93
94
0
        Ok(res)
95
0
    }
96
97
    #[inline]
98
0
    fn try_fill_bytes(&mut self, dst: &mut [u8]) -> Result<(), Infallible> {
99
0
        utils::fill_bytes_via_next_word(dst, || self.try_next_u64())
100
0
    }
101
}
102
103
#[cfg(test)]
104
mod tests {
105
    use super::Xoshiro256PlusPlus;
106
    use rand_core::{Rng, SeedableRng};
107
108
    #[test]
109
    fn reference() {
110
        let mut rng = Xoshiro256PlusPlus::from_seed([
111
            1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0,
112
            0, 0, 0,
113
        ]);
114
        // These values were produced with the reference implementation:
115
        // http://xoshiro.di.unimi.it/xoshiro256plusplus.c
116
        let expected = [
117
            41943041,
118
            58720359,
119
            3588806011781223,
120
            3591011842654386,
121
            9228616714210784205,
122
            9973669472204895162,
123
            14011001112246962877,
124
            12406186145184390807,
125
            15849039046786891736,
126
            10450023813501588000,
127
        ];
128
        for &e in &expected {
129
            assert_eq!(rng.next_u64(), e);
130
        }
131
    }
132
133
    #[test]
134
    fn stable_seed_from_u64_and_from_seed() {
135
        // We don't guarantee value-stability for SmallRng but this
136
        // could influence keeping stability whenever possible (e.g. after optimizations).
137
        let mut rng = Xoshiro256PlusPlus::seed_from_u64(0);
138
        // from_seed([0; 32]) should produce the same state as seed_from_u64(0).
139
        let mut rng_from_seed_0 = Xoshiro256PlusPlus::from_seed([0; 32]);
140
        let expected = [
141
            5987356902031041503,
142
            7051070477665621255,
143
            6633766593972829180,
144
            211316841551650330,
145
            9136120204379184874,
146
            379361710973160858,
147
            15813423377499357806,
148
            15596884590815070553,
149
            5439680534584881407,
150
            1369371744833522710,
151
        ];
152
        for &e in &expected {
153
            assert_eq!(rng.next_u64(), e);
154
            assert_eq!(rng_from_seed_0.next_u64(), e);
155
        }
156
    }
157
}