1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
// Copyright 2018 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! A small fast RNG

use rand_core::{Error, RngCore, SeedableRng};

#[cfg(target_pointer_width = "64")]
type Rng = super::xoshiro256plusplus::Xoshiro256PlusPlus;
#[cfg(not(target_pointer_width = "64"))]
type Rng = super::xoshiro128plusplus::Xoshiro128PlusPlus;

/// A small-state, fast non-crypto PRNG
///
/// `SmallRng` may be a good choice when a PRNG with small state, cheap
/// initialization, good statistical quality and good performance are required.
/// Note that depending on the application, [`StdRng`] may be faster on many
/// modern platforms while providing higher-quality randomness. Furthermore,
/// `SmallRng` is **not** a good choice when:
///
/// - Portability is required. Its implementation is not fixed. Use a named
///   generator from an external crate instead, for example [rand_xoshiro] or
///   [rand_chacha]. Refer also to
///   [The Book](https://rust-random.github.io/book/guide-rngs.html).
/// - Security against prediction is important. Use [`StdRng`] instead.
///
/// The PRNG algorithm in `SmallRng` is chosen to be efficient on the current
/// platform, without consideration for cryptography or security. The size of
/// its state is much smaller than [`StdRng`]. The current algorithm is
/// `Xoshiro256PlusPlus` on 64-bit platforms and `Xoshiro128PlusPlus` on 32-bit
/// platforms. Both are also implemented by the [rand_xoshiro] crate.
///
/// [`StdRng`]: crate::rngs::StdRng
/// [rand_chacha]: https://crates.io/crates/rand_chacha
/// [rand_xoshiro]: https://crates.io/crates/rand_xoshiro
#[cfg_attr(doc_cfg, doc(cfg(feature = "small_rng")))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SmallRng(Rng);

impl RngCore for SmallRng {
    #[inline(always)]
    fn next_u32(&mut self) -> u32 {
        self.0.next_u32()
    }

    #[inline(always)]
    fn next_u64(&mut self) -> u64 {
        self.0.next_u64()
    }

    #[inline(always)]
    fn fill_bytes(&mut self, dest: &mut [u8]) {
        self.0.fill_bytes(dest);
    }

    #[inline(always)]
    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
        self.0.try_fill_bytes(dest)
    }
}

impl SmallRng {
    /// Construct an instance seeded from another `Rng`
    ///
    /// We recommend that the source (master) RNG uses a different algorithm
    /// (i.e. is not `SmallRng`) to avoid correlations between the child PRNGs.
    ///
    /// # Example
    /// ```
    /// # use rand::rngs::SmallRng;
    /// let rng = SmallRng::from_rng(rand::thread_rng());
    /// ```
    #[inline(always)]
    pub fn from_rng<R: RngCore>(rng: R) -> Result<Self, Error> {
        Rng::from_rng(rng).map(SmallRng)
    }

    /// Construct an instance seeded from the thread-local RNG
    ///
    /// # Panics
    ///
    /// This method panics only if [`thread_rng`](crate::thread_rng) fails to
    /// initialize.
    #[cfg(all(feature = "std", feature = "std_rng", feature = "getrandom"))]
    #[inline(always)]
    pub fn from_thread_rng() -> Self {
        let mut seed = <Rng as SeedableRng>::Seed::default();
        crate::thread_rng().fill_bytes(seed.as_mut());
        SmallRng(Rng::from_seed(seed))
    }

    /// Construct an instance from a `u64` seed
    ///
    /// This provides a convenient method of seeding a `SmallRng` from a simple
    /// number by use of another algorithm to mutate and expand the input.
    /// This is suitable for use with low Hamming Weight numbers like 0 and 1.
    ///
    /// **Warning:** the implementation is deterministic but not portable:
    /// output values may differ according to platform and may be changed by a
    /// future version of the library.
    ///
    /// # Example
    /// ```
    /// # use rand::rngs::SmallRng;
    /// let rng = SmallRng::seed_from_u64(1);
    /// ```
    #[inline(always)]
    pub fn seed_from_u64(state: u64) -> Self {
        SmallRng(Rng::seed_from_u64(state))
    }
}