-
Notifications
You must be signed in to change notification settings - Fork 35
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
199 additions
and
171 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
//! A global, thread-local random number generator. | ||
|
||
use crate::Rng; | ||
|
||
use std::cell::Cell; | ||
use std::ops::RangeBounds; | ||
|
||
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] | ||
use instant::Instant; | ||
#[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"))))] | ||
use std::time::Instant; | ||
|
||
impl Default for Rng { | ||
/// Initialize the `Rng` from the system's random number generator. | ||
/// | ||
/// This is equivalent to [`Rng::new()`]. | ||
#[inline] | ||
fn default() -> Rng { | ||
Rng::new() | ||
} | ||
} | ||
|
||
impl Rng { | ||
/// Creates a new random number generator. | ||
#[inline] | ||
pub fn new() -> Rng { | ||
try_with_rng(Rng::fork).unwrap_or_else(|_| Rng::with_seed(0x4d595df4d0f33173)) | ||
} | ||
} | ||
|
||
thread_local! { | ||
static RNG: Cell<Rng> = Cell::new(Rng({ | ||
use std::collections::hash_map::DefaultHasher; | ||
use std::hash::{Hash, Hasher}; | ||
use std::thread; | ||
|
||
let mut hasher = DefaultHasher::new(); | ||
Instant::now().hash(&mut hasher); | ||
thread::current().id().hash(&mut hasher); | ||
let hash = hasher.finish(); | ||
(hash << 1) | 1 | ||
})); | ||
} | ||
|
||
/// Run an operation with the current thread-local generator. | ||
#[inline] | ||
fn with_rng<R>(f: impl FnOnce(&mut Rng) -> R) -> R { | ||
RNG.with(|rng| { | ||
let current = rng.replace(Rng(0)); | ||
|
||
let mut restore = RestoreOnDrop { rng, current }; | ||
|
||
f(&mut restore.current) | ||
}) | ||
} | ||
|
||
/// Try to run an operation with the current thread-local generator. | ||
#[inline] | ||
fn try_with_rng<R>(f: impl FnOnce(&mut Rng) -> R) -> Result<R, std::thread::AccessError> { | ||
RNG.try_with(|rng| { | ||
let current = rng.replace(Rng(0)); | ||
|
||
let mut restore = RestoreOnDrop { rng, current }; | ||
|
||
f(&mut restore.current) | ||
}) | ||
} | ||
|
||
/// Make sure the original RNG is restored even on panic. | ||
struct RestoreOnDrop<'a> { | ||
rng: &'a Cell<Rng>, | ||
current: Rng, | ||
} | ||
|
||
impl Drop for RestoreOnDrop<'_> { | ||
fn drop(&mut self) { | ||
self.rng.set(Rng(self.current.0)); | ||
} | ||
} | ||
|
||
/// Initializes the thread-local generator with the given seed. | ||
#[inline] | ||
pub fn seed(seed: u64) { | ||
with_rng(|r| r.seed(seed)); | ||
} | ||
|
||
/// Gives back **current** seed that is being held by the thread-local generator. | ||
#[inline] | ||
pub fn get_seed() -> u64 { | ||
with_rng(|r| r.get_seed()) | ||
} | ||
|
||
/// Generates a random `bool`. | ||
#[inline] | ||
pub fn bool() -> bool { | ||
with_rng(|r| r.bool()) | ||
} | ||
|
||
/// Generates a random `char` in ranges a-z and A-Z. | ||
#[inline] | ||
pub fn alphabetic() -> char { | ||
with_rng(|r| r.alphabetic()) | ||
} | ||
|
||
/// Generates a random `char` in ranges a-z, A-Z and 0-9. | ||
#[inline] | ||
pub fn alphanumeric() -> char { | ||
with_rng(|r| r.alphanumeric()) | ||
} | ||
|
||
/// Generates a random `char` in range a-z. | ||
#[inline] | ||
pub fn lowercase() -> char { | ||
with_rng(|r| r.lowercase()) | ||
} | ||
|
||
/// Generates a random `char` in range A-Z. | ||
#[inline] | ||
pub fn uppercase() -> char { | ||
with_rng(|r| r.uppercase()) | ||
} | ||
|
||
/// Generates a random digit in the given `base`. | ||
/// | ||
/// Digits are represented by `char`s in ranges 0-9 and a-z. | ||
/// | ||
/// Panics if the base is zero or greater than 36. | ||
#[inline] | ||
pub fn digit(base: u32) -> char { | ||
with_rng(|r| r.digit(base)) | ||
} | ||
|
||
/// Shuffles a slice randomly. | ||
#[inline] | ||
pub fn shuffle<T>(slice: &mut [T]) { | ||
with_rng(|r| r.shuffle(slice)) | ||
} | ||
|
||
macro_rules! integer { | ||
($t:tt, $doc:tt) => { | ||
#[doc = $doc] | ||
/// | ||
/// Panics if the range is empty. | ||
#[inline] | ||
pub fn $t(range: impl RangeBounds<$t>) -> $t { | ||
with_rng(|r| r.$t(range)) | ||
} | ||
}; | ||
} | ||
|
||
integer!(u8, "Generates a random `u8` in the given range."); | ||
integer!(i8, "Generates a random `i8` in the given range."); | ||
integer!(u16, "Generates a random `u16` in the given range."); | ||
integer!(i16, "Generates a random `i16` in the given range."); | ||
integer!(u32, "Generates a random `u32` in the given range."); | ||
integer!(i32, "Generates a random `i32` in the given range."); | ||
integer!(u64, "Generates a random `u64` in the given range."); | ||
integer!(i64, "Generates a random `i64` in the given range."); | ||
integer!(u128, "Generates a random `u128` in the given range."); | ||
integer!(i128, "Generates a random `i128` in the given range."); | ||
integer!(usize, "Generates a random `usize` in the given range."); | ||
integer!(isize, "Generates a random `isize` in the given range."); | ||
integer!(char, "Generates a random `char` in the given range."); | ||
|
||
/// Generates a random `f32` in range `0..1`. | ||
pub fn f32() -> f32 { | ||
with_rng(|r| r.f32()) | ||
} | ||
|
||
/// Generates a random `f64` in range `0..1`. | ||
pub fn f64() -> f64 { | ||
with_rng(|r| r.f64()) | ||
} |
Oops, something went wrong.