1//! A global, thread-local random number generator.
23use crate::Rng;
45use std::cell::Cell;
6use std::ops::RangeBounds;
7use std::vec::Vec;
89// Chosen by fair roll of the dice.
10const DEFAULT_RNG_SEED: u64 = 0xef6f79ed30ba75a;
1112impl Default for Rng {
13/// Initialize the `Rng` from the system's random number generator.
14 ///
15 /// This is equivalent to [`Rng::new()`].
16#[inline]
17fn default() -> Rng {
18 Rng::new()
19 }
20}
2122impl Rng {
23/// Creates a new random number generator.
24#[inline]
25pub fn new() -> Rng {
26 try_with_rng(Rng::fork).unwrap_or_else(|_| Rng::with_seed(0x4d595df4d0f33173))
27 }
28}
2930std::thread_local! {
31static RNG: Cell<Rng> = Cell::new(Rng(random_seed().unwrap_or(DEFAULT_RNG_SEED)));
32}
3334/// Run an operation with the current thread-local generator.
35#[inline]
36fn with_rng<R>(f: impl FnOnce(&mut Rng) -> R) -> R {
37 RNG.with(|rng| {
38let current = rng.replace(Rng(0));
3940let mut restore = RestoreOnDrop { rng, current };
4142 f(&mut restore.current)
43 })
44}
4546/// Try to run an operation with the current thread-local generator.
47#[inline]
48fn try_with_rng<R>(f: impl FnOnce(&mut Rng) -> R) -> Result<R, std::thread::AccessError> {
49 RNG.try_with(|rng| {
50let current = rng.replace(Rng(0));
5152let mut restore = RestoreOnDrop { rng, current };
5354 f(&mut restore.current)
55 })
56}
5758/// Make sure the original RNG is restored even on panic.
59struct RestoreOnDrop<'a> {
60 rng: &'a Cell<Rng>,
61 current: Rng,
62}
6364impl Drop for RestoreOnDrop<'_> {
65fn drop(&mut self) {
66self.rng.set(Rng(self.current.0));
67 }
68}
6970/// Initializes the thread-local generator with the given seed.
71#[inline]
72pub fn seed(seed: u64) {
73 with_rng(|r| r.seed(seed));
74}
7576/// Gives back **current** seed that is being held by the thread-local generator.
77#[inline]
78pub fn get_seed() -> u64 {
79 with_rng(|r| r.get_seed())
80}
8182/// Generates a random `bool`.
83#[inline]
84pub fn bool() -> bool {
85 with_rng(|r| r.bool())
86}
8788/// Generates a random `char` in ranges a-z and A-Z.
89#[inline]
90pub fn alphabetic() -> char {
91 with_rng(|r| r.alphabetic())
92}
9394/// Generates a random `char` in ranges a-z, A-Z and 0-9.
95#[inline]
96pub fn alphanumeric() -> char {
97 with_rng(|r| r.alphanumeric())
98}
99100/// Generates a random `char` in range a-z.
101#[inline]
102pub fn lowercase() -> char {
103 with_rng(|r| r.lowercase())
104}
105106/// Generates a random `char` in range A-Z.
107#[inline]
108pub fn uppercase() -> char {
109 with_rng(|r| r.uppercase())
110}
111112/// 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]
118pub fn choice<I>(iter: I) -> Option<I::Item>
119where
120I: IntoIterator,
121 I::IntoIter: ExactSizeIterator,
122{
123 with_rng(|r| r.choice(iter))
124}
125126/// 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]
132pub fn digit(base: u32) -> char {
133 with_rng(|r| r.digit(base))
134}
135136/// Shuffles a slice randomly.
137#[inline]
138pub fn shuffle<T>(slice: &mut [T]) {
139 with_rng(|r| r.shuffle(slice))
140}
141142macro_rules! integer {
143 ($t:tt, $doc:tt) => {
144#[doc = $doc]
145///
146 /// Panics if the range is empty.
147#[inline]
148pub fn $t(range: impl RangeBounds<$t>) -> $t {
149 with_rng(|r| r.$t(range))
150 }
151 };
152}
153154integer!(u8, "Generates a random `u8` in the given range.");
155integer!(i8, "Generates a random `i8` in the given range.");
156integer!(u16, "Generates a random `u16` in the given range.");
157integer!(i16, "Generates a random `i16` in the given range.");
158integer!(u32, "Generates a random `u32` in the given range.");
159integer!(i32, "Generates a random `i32` in the given range.");
160integer!(u64, "Generates a random `u64` in the given range.");
161integer!(i64, "Generates a random `i64` in the given range.");
162integer!(u128, "Generates a random `u128` in the given range.");
163integer!(i128, "Generates a random `i128` in the given range.");
164integer!(usize, "Generates a random `usize` in the given range.");
165integer!(isize, "Generates a random `isize` in the given range.");
166integer!(char, "Generates a random `char` in the given range.");
167168/// Generates a random `f32` in range `0..1`.
169pub fn f32() -> f32 {
170 with_rng(|r| r.f32())
171}
172173/// Generates a random `f64` in range `0..1`.
174pub fn f64() -> f64 {
175 with_rng(|r| r.f64())
176}
177178/// Collects `amount` values at random from the iterator into a vector.
179pub fn choose_multiple<T: Iterator>(source: T, amount: usize) -> Vec<T::Item> {
180 with_rng(|rng| rng.choose_multiple(source, amount))
181}
182183#[cfg(not(all(
184 any(target_arch = "wasm32", target_arch = "wasm64"),
185 target_os = "unknown"
186)))]
187fn random_seed() -> Option<u64> {
188use std::collections::hash_map::DefaultHasher;
189use std::hash::{Hash, Hasher};
190use std::thread;
191use std::time::Instant;
192193let mut hasher = DefaultHasher::new();
194 Instant::now().hash(&mut hasher);
195 thread::current().id().hash(&mut hasher);
196Some(hasher.finish())
197}
198199#[cfg(all(
200 any(target_arch = "wasm32", target_arch = "wasm64"),
201 target_os = "unknown",
202 feature = "js"
203))]
204fn random_seed() -> Option<u64> {
205// TODO(notgull): Failures should be logged somewhere.
206let mut seed = [0u8; 8];
207 getrandom::getrandom(&mut seed).ok()?;
208Some(u64::from_ne_bytes(seed))
209}
210211#[cfg(all(
212 any(target_arch = "wasm32", target_arch = "wasm64"),
213 target_os = "unknown",
214 not(feature = "js")
215))]
216fn random_seed() -> Option<u64> {
217None
218}