Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add no_std mode #50

Merged
merged 2 commits into from
Feb 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ jobs:
if: startsWith(matrix.rust, 'nightly')
run: cargo check -Z features=dev_dep
- run: cargo test
- name: Build with no default features
run: cargo build --no-default-features
- name: Test wasm
run: wasm-pack test --headless --chrome
if: startsWith(matrix.os, 'ubuntu')
Expand Down
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ keywords = ["simple", "fast", "rand", "random", "wyrand"]
categories = ["algorithms"]
exclude = ["/.*"]

[features]
default = ["std"]
std = []

[target.'cfg(all(target_arch = "wasm32", not(target_os = "wasi")))'.dependencies]
instant = "0.1"

Expand Down
173 changes: 173 additions & 0 deletions src/global_rng.rs
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())
}
Loading