1 //! A global, thread-local random number generator.
2 
3 use crate::Rng;
4 
5 use std::cell::Cell;
6 use std::ops::RangeBounds;
7 use std::vec::Vec;
8 
9 // Chosen by fair roll of the dice.
10 const DEFAULT_RNG_SEED: u64 = 0xef6f79ed30ba75a;
11 
12 impl Default for Rng {
13     /// Initialize the `Rng` from the system's random number generator.
14     ///
15     /// This is equivalent to [`Rng::new()`].
16     #[inline]
default() -> Rng17     fn default() -> Rng {
18         Rng::new()
19     }
20 }
21 
22 impl Rng {
23     /// Creates a new random number generator.
24     #[inline]
new() -> Rng25     pub fn new() -> Rng {
26         try_with_rng(Rng::fork).unwrap_or_else(|_| Rng::with_seed(0x4d595df4d0f33173))
27     }
28 }
29 
30 std::thread_local! {
31     static RNG: Cell<Rng> = Cell::new(Rng(random_seed().unwrap_or(DEFAULT_RNG_SEED)));
32 }
33 
34 /// Run an operation with the current thread-local generator.
35 #[inline]
with_rng<R>(f: impl FnOnce(&mut Rng) -> R) -> R36 fn with_rng<R>(f: impl FnOnce(&mut Rng) -> R) -> R {
37     RNG.with(|rng| {
38         let current = rng.replace(Rng(0));
39 
40         let mut restore = RestoreOnDrop { rng, current };
41 
42         f(&mut restore.current)
43     })
44 }
45 
46 /// Try to run an operation with the current thread-local generator.
47 #[inline]
try_with_rng<R>(f: impl FnOnce(&mut Rng) -> R) -> Result<R, std::thread::AccessError>48 fn try_with_rng<R>(f: impl FnOnce(&mut Rng) -> R) -> Result<R, std::thread::AccessError> {
49     RNG.try_with(|rng| {
50         let current = rng.replace(Rng(0));
51 
52         let mut restore = RestoreOnDrop { rng, current };
53 
54         f(&mut restore.current)
55     })
56 }
57 
58 /// Make sure the original RNG is restored even on panic.
59 struct RestoreOnDrop<'a> {
60     rng: &'a Cell<Rng>,
61     current: Rng,
62 }
63 
64 impl Drop for RestoreOnDrop<'_> {
drop(&mut self)65     fn drop(&mut self) {
66         self.rng.set(Rng(self.current.0));
67     }
68 }
69 
70 /// Initializes the thread-local generator with the given seed.
71 #[inline]
seed(seed: u64)72 pub fn seed(seed: u64) {
73     with_rng(|r| r.seed(seed));
74 }
75 
76 /// Gives back **current** seed that is being held by the thread-local generator.
77 #[inline]
get_seed() -> u6478 pub fn get_seed() -> u64 {
79     with_rng(|r| r.get_seed())
80 }
81 
82 /// Generates a random `bool`.
83 #[inline]
bool() -> bool84 pub fn bool() -> bool {
85     with_rng(|r| r.bool())
86 }
87 
88 /// Generates a random `char` in ranges a-z and A-Z.
89 #[inline]
alphabetic() -> char90 pub fn alphabetic() -> char {
91     with_rng(|r| r.alphabetic())
92 }
93 
94 /// Generates a random `char` in ranges a-z, A-Z and 0-9.
95 #[inline]
alphanumeric() -> char96 pub fn alphanumeric() -> char {
97     with_rng(|r| r.alphanumeric())
98 }
99 
100 /// Generates a random `char` in range a-z.
101 #[inline]
lowercase() -> char102 pub fn lowercase() -> char {
103     with_rng(|r| r.lowercase())
104 }
105 
106 /// Generates a random `char` in range A-Z.
107 #[inline]
uppercase() -> char108 pub fn uppercase() -> char {
109     with_rng(|r| r.uppercase())
110 }
111 
112 /// Choose an item from an iterator at random.
113 ///
114 /// This function may have an unexpected result if the `len()` property of the
115 /// iterator does not match the actual number of items in the iterator. If
116 /// the iterator is empty, this returns `None`.
117 #[inline]
choice<I>(iter: I) -> Option<I::Item> where I: IntoIterator, I::IntoIter: ExactSizeIterator,118 pub fn choice<I>(iter: I) -> Option<I::Item>
119 where
120     I: IntoIterator,
121     I::IntoIter: ExactSizeIterator,
122 {
123     with_rng(|r| r.choice(iter))
124 }
125 
126 /// Generates a random digit in the given `base`.
127 ///
128 /// Digits are represented by `char`s in ranges 0-9 and a-z.
129 ///
130 /// Panics if the base is zero or greater than 36.
131 #[inline]
digit(base: u32) -> char132 pub fn digit(base: u32) -> char {
133     with_rng(|r| r.digit(base))
134 }
135 
136 /// Shuffles a slice randomly.
137 #[inline]
shuffle<T>(slice: &mut [T])138 pub fn shuffle<T>(slice: &mut [T]) {
139     with_rng(|r| r.shuffle(slice))
140 }
141 
142 macro_rules! integer {
143     ($t:tt, $doc:tt) => {
144         #[doc = $doc]
145         ///
146         /// Panics if the range is empty.
147         #[inline]
148         pub fn $t(range: impl RangeBounds<$t>) -> $t {
149             with_rng(|r| r.$t(range))
150         }
151     };
152 }
153 
154 integer!(u8, "Generates a random `u8` in the given range.");
155 integer!(i8, "Generates a random `i8` in the given range.");
156 integer!(u16, "Generates a random `u16` in the given range.");
157 integer!(i16, "Generates a random `i16` in the given range.");
158 integer!(u32, "Generates a random `u32` in the given range.");
159 integer!(i32, "Generates a random `i32` in the given range.");
160 integer!(u64, "Generates a random `u64` in the given range.");
161 integer!(i64, "Generates a random `i64` in the given range.");
162 integer!(u128, "Generates a random `u128` in the given range.");
163 integer!(i128, "Generates a random `i128` in the given range.");
164 integer!(usize, "Generates a random `usize` in the given range.");
165 integer!(isize, "Generates a random `isize` in the given range.");
166 integer!(char, "Generates a random `char` in the given range.");
167 
168 /// Generates a random `f32` in range `0..1`.
f32() -> f32169 pub fn f32() -> f32 {
170     with_rng(|r| r.f32())
171 }
172 
173 /// Generates a random `f64` in range `0..1`.
f64() -> f64174 pub fn f64() -> f64 {
175     with_rng(|r| r.f64())
176 }
177 
178 /// Collects `amount` values at random from the iterator into a vector.
choose_multiple<T: Iterator>(source: T, amount: usize) -> Vec<T::Item>179 pub fn choose_multiple<T: Iterator>(source: T, amount: usize) -> Vec<T::Item> {
180     with_rng(|rng| rng.choose_multiple(source, amount))
181 }
182 
183 #[cfg(not(all(
184     any(target_arch = "wasm32", target_arch = "wasm64"),
185     target_os = "unknown"
186 )))]
random_seed() -> Option<u64>187 fn random_seed() -> Option<u64> {
188     use std::collections::hash_map::DefaultHasher;
189     use std::hash::{Hash, Hasher};
190     use std::thread;
191     use std::time::Instant;
192 
193     let mut hasher = DefaultHasher::new();
194     Instant::now().hash(&mut hasher);
195     thread::current().id().hash(&mut hasher);
196     let hash = hasher.finish();
197     Some((hash << 1) | 1)
198 }
199 
200 #[cfg(all(
201     any(target_arch = "wasm32", target_arch = "wasm64"),
202     target_os = "unknown",
203     feature = "js"
204 ))]
random_seed() -> Option<u64>205 fn random_seed() -> Option<u64> {
206     // TODO(notgull): Failures should be logged somewhere.
207     let mut seed = [0u8; 8];
208     getrandom::getrandom(&mut seed).ok()?;
209     Some(u64::from_ne_bytes(seed))
210 }
211 
212 #[cfg(all(
213     any(target_arch = "wasm32", target_arch = "wasm64"),
214     target_os = "unknown",
215     not(feature = "js")
216 ))]
random_seed() -> Option<u64>217 fn random_seed() -> Option<u64> {
218     None
219 }
220