random_fast_rng/
lib.rs

1#![no_std]
2#![deny(missing_docs)]
3#![cfg_attr(test, deny(warnings))]
4
5//! # Random-fast-rng
6//!
7//! This crate provides a fast **non cryptographic** random number generator that implements the [`Random`](trait.Random.html) trait. <br>
8//! Currently it's implemented using the `Pcg32` algorithm, that generates 32 bit of random data for every state change. <br>
9//! the exact algorithm might change in the future, but the properties should stay the same (Blazing fast, non cryptographic, and minimal I/O)
10//! The crate is part of the `random-rs` facade, and as such supports older rust compilers(currently 1.13+) and should have only thin amount of dependencies.
11//!
12//! This Random generator is good for testing uses, and use cases that require some non-determinism. it shouldn't be used to generate keys/passwords. <br>
13//!
14//! By enabling the `std` feature this crate exposes a [`new()`](struct.FastRng.html#method.new) function that uses [`SystemTime::now()`](https://doc.rust-lang.org/std/time/struct.SystemTime.html) to seed the RNG.<br>
15//! It also exposes a [`local_rng()`](fn.local_rng.html) function to give a persistent Rng that is seeded only once and is unique per thread (so there's no need to worry about dropping and reinitializing the Rng)
16//!
17
18#[cfg(feature = "std")]
19#[macro_use]
20extern crate std;
21
22#[cfg(feature = "std")]
23mod thread;
24
25pub extern crate random_trait;
26pub use random_trait::Random;
27
28#[cfg(feature = "std")]
29use thread::FromRawPtr;
30#[cfg(feature = "std")]
31pub use thread::ThreadFastRng;
32
33use core::mem;
34
35#[cfg(feature = "doc-comment")]
36extern crate doc_comment;
37#[cfg(feature = "doc-comment")]
38doc_comment::doctest!("../README.md");
39
40const PCG_DEFAULT_MULTIPLIER_64: u64 = 6_364_136_223_846_793_005;
41
42/// A FastRng struct implementing [`Random`](trait.Random.html). you can initialize it with your own seed using [`FastRng::seed()`](struct.FastRng.html#method.seed)
43/// Or if the `std` feature is enabled call [`FastRng::new()`](struct.FastRng.html#method.seed) which will seed it with the system time. <br>
44/// For ergonomics and ease of usability the Rng is also provided as a global thread local variable using [`FastRng::local_rng()`](fn.local_rng.html)
45pub struct FastRng {
46    // Pcg32
47    state: u64,
48    inc: u64,
49}
50
51impl FastRng {
52    /// Creates a new instance of `FastRng` seeded with the system time.
53    ///
54    /// # Examples
55    /// ```rust
56    /// use random_fast_rng::{FastRng, Random};
57    ///
58    /// let mut rng = FastRng::new();
59    /// let random_u8 = rng.get_u8();
60    /// let arr: [u8; 32] = rng.gen();
61    /// ```
62    ///
63    #[cfg(feature = "std")]
64    pub fn new() -> Self {
65        let (a, b) = time_seed();
66        Self::seed(a, b)
67    }
68
69    /// A function to manually seed the Rng in `no-std` cases.
70    pub fn seed(seed: u64, seq: u64) -> Self {
71        let init_inc = (seq << 1) | 1;
72        let init_state = seed + init_inc;
73        let mut rng = FastRng { state: init_state, inc: init_inc };
74        rng.state = rng.state.wrapping_mul(PCG_DEFAULT_MULTIPLIER_64).wrapping_add(rng.inc);
75        rng
76    }
77
78    fn gen_u32(&mut self) -> u32 {
79        let old_state = self.state;
80        self.state = self.state.wrapping_mul(PCG_DEFAULT_MULTIPLIER_64).wrapping_add(self.inc);
81
82        let xorshift = (((old_state >> 18) ^ old_state) >> 27) as u32;
83        let rot = (old_state >> 59) as i32;
84        (xorshift >> rot) | (xorshift << ((-rot) & 31))
85    }
86}
87
88/// Returns a thread local instance which is seeded only once per thread (no need to worry about dropping and reinitializing)
89///
90/// # Examples
91/// ```rust
92/// use random_fast_rng::{Random, local_rng};
93///
94/// let random_u8 = local_rng().get_u8();
95/// let arr: [u8; 32] = local_rng().gen();
96/// ```
97///
98#[cfg(feature = "std")]
99pub fn local_rng() -> ThreadFastRng {
100    use std::cell::RefCell;
101    thread_local! {
102        pub static THREAD_FAST_RNG: RefCell<FastRng> = RefCell::new(FastRng::new());
103    }
104    let ptr = THREAD_FAST_RNG.with(|r| r.as_ptr());
105    ThreadFastRng::from_ptr(ptr)
106}
107
108#[cfg(feature = "std")]
109fn time_seed() -> (u64, u64) {
110    use std::time;
111    let now = time::SystemTime::now();
112    let unix = now.duration_since(time::UNIX_EPOCH).unwrap();
113
114    (unix.as_secs(), u64::from(unix.subsec_nanos()))
115}
116
117impl Random for FastRng {
118    type Error = ();
119
120    fn try_fill_bytes(&mut self, buf: &mut [u8]) -> Result<(), Self::Error> {
121        for chunk in buf.chunks_mut(4) {
122            let rand: [u8; 4] = unsafe { mem::transmute(self.gen_u32()) };
123            let len = chunk.len();
124            chunk.copy_from_slice(&rand[..len]);
125        }
126        Ok(())
127    }
128    fn get_u32(&mut self) -> u32 {
129        self.gen_u32()
130    }
131}
132
133#[cfg(test)]
134mod tests {
135    use super::*;
136
137    #[test]
138    fn test_local() {
139        let mut local_rng = local_rng();
140        let a: u64 = local_rng.gen();
141        let b: u32 = local_rng.gen();
142        let c: [u8; 64] = local_rng.gen();
143        assert_ne!(a, 0);
144        assert_ne!(b, 0);
145        assert_ne!(&c[..], &[0u8; 64][..]);
146    }
147
148    #[test]
149    fn test_float() {
150        let mut rng = FastRng::new();
151        let f: f32 = rng.gen();
152        assert!(f > 0.0 && f < 1.0);
153        let f: f64 = rng.gen();
154        assert!(f > 0.0 && f < 1.0);
155    }
156}