Add no_std build support
Based on @jethrogb's core branch and my master branch Plus some extra notes in README
This commit is contained in:
parent
5116356d1c
commit
0afeb1d056
11
Cargo.toml
11
Cargo.toml
@ -14,11 +14,16 @@ keywords = ["random", "rng"]
|
||||
categories = ["algorithms"]
|
||||
|
||||
[features]
|
||||
i128_support = []
|
||||
nightly = ["i128_support"]
|
||||
default = ["std"]
|
||||
nightly = ["i128_support"] # enables all features requiring nightly rust
|
||||
|
||||
std = ["libc"] # default feature; without this rand uses libcore
|
||||
alloc = [] # enables Vec and Box support without std
|
||||
|
||||
i128_support = [] # enables i128 and u128 support
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2"
|
||||
libc = { version = "0.2", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
log = "0.3.0"
|
||||
|
42
README.md
42
README.md
@ -57,6 +57,48 @@ let mut rng = rand::ChaChaRng::new_unseeded();
|
||||
println!("i32: {}, u32: {}", rng.gen::<i32>(), rng.gen::<u32>())
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
By default, `rand` is built with all stable features available. The following
|
||||
optional features are available:
|
||||
|
||||
- `i128_support` enables support for generating `u128` and `i128` values
|
||||
- `nightly` enables all unstable features (`i128_support`)
|
||||
- `std` enabled by default; by setting "default-features = false" `no_std`
|
||||
mode is activated; this removes features depending on `std` functionality:
|
||||
|
||||
- `OsRng` is entirely unavailable
|
||||
- `JitterRng` code is still present, but a nanosecond timer must be
|
||||
provided via `JitterRng::new_with_timer`
|
||||
- Since no external entropy is available, it is not possible to create
|
||||
generators with fresh seeds (user must provide entropy)
|
||||
- `thread_rng`, `weak_rng` and `random` are all disabled
|
||||
- exponential, normal and gamma type distributions are unavailable
|
||||
since `exp` and `log` functions are not provided in `core`
|
||||
- any code requiring `Vec` or `Box`
|
||||
- `alloc` can be used instead of `std` to provide `Vec` and `Box`
|
||||
|
||||
## Testing
|
||||
|
||||
Unfortunately, `cargo test` does not test everything. The following tests are
|
||||
recommended:
|
||||
|
||||
```
|
||||
# Basic tests for rand and sub-crates
|
||||
cargo test --all
|
||||
|
||||
# Test no_std support (build only since nearly all tests require std)
|
||||
cargo build --all --no-default-features
|
||||
|
||||
# Test 128-bit support (requires nightly)
|
||||
cargo test --all --features nightly
|
||||
|
||||
# Benchmarks (requires nightly)
|
||||
cargo bench
|
||||
# or just to test the benchmark code:
|
||||
cargo test --benches
|
||||
```
|
||||
|
||||
# `derive(Rand)`
|
||||
|
||||
You can derive the `Rand` trait for your custom type via the `#[derive(Rand)]`
|
||||
|
@ -17,20 +17,29 @@
|
||||
//! internally. The `IndependentSample` trait is for generating values
|
||||
//! that do not need to record state.
|
||||
|
||||
use std::marker;
|
||||
use core::marker;
|
||||
|
||||
use {Rng, Rand};
|
||||
|
||||
pub use self::range::Range;
|
||||
#[cfg(feature="std")]
|
||||
pub use self::gamma::{Gamma, ChiSquared, FisherF, StudentT};
|
||||
#[cfg(feature="std")]
|
||||
pub use self::normal::{Normal, LogNormal};
|
||||
#[cfg(feature="std")]
|
||||
pub use self::exponential::Exp;
|
||||
|
||||
pub mod range;
|
||||
#[cfg(feature="std")]
|
||||
pub mod gamma;
|
||||
#[cfg(feature="std")]
|
||||
pub mod normal;
|
||||
#[cfg(feature="std")]
|
||||
pub mod exponential;
|
||||
|
||||
#[cfg(feature="std")]
|
||||
mod ziggurat_tables;
|
||||
|
||||
/// Types that can be used to create a random instance of `Support`.
|
||||
pub trait Sample<Support> {
|
||||
/// Generate a random value of `Support`, using `rng` as the
|
||||
@ -203,8 +212,6 @@ impl<'a, T: Clone> IndependentSample<T> for WeightedChoice<'a, T> {
|
||||
}
|
||||
}
|
||||
|
||||
mod ziggurat_tables;
|
||||
|
||||
/// Sample a random number using the Ziggurat method (specifically the
|
||||
/// ZIGNOR variant from Doornik 2005). Most of the arguments are
|
||||
/// directly from the paper:
|
||||
@ -220,6 +227,7 @@ mod ziggurat_tables;
|
||||
|
||||
// the perf improvement (25-50%) is definitely worth the extra code
|
||||
// size from force-inlining.
|
||||
#[cfg(feature="std")]
|
||||
#[inline(always)]
|
||||
fn ziggurat<R: Rng, P, Z>(
|
||||
rng: &mut R,
|
||||
|
@ -12,7 +12,7 @@
|
||||
|
||||
// this is surprisingly complicated to be both generic & correct
|
||||
|
||||
use std::num::Wrapping as w;
|
||||
use core::num::Wrapping as w;
|
||||
|
||||
use Rng;
|
||||
use distributions::{Sample, IndependentSample};
|
||||
@ -99,7 +99,7 @@ macro_rules! integer_impl {
|
||||
#[inline]
|
||||
fn construct_range(low: $ty, high: $ty) -> Range<$ty> {
|
||||
let range = (w(high as $unsigned) - w(low as $unsigned)).0;
|
||||
let unsigned_max: $unsigned = ::std::$unsigned::MAX;
|
||||
let unsigned_max: $unsigned = ::core::$unsigned::MAX;
|
||||
|
||||
// this is the largest number that fits into $unsigned
|
||||
// that `range` divides evenly, so, if we've sampled
|
||||
@ -191,7 +191,7 @@ mod tests {
|
||||
$(
|
||||
let v: &[($ty, $ty)] = &[(0, 10),
|
||||
(10, 127),
|
||||
(::std::$ty::MIN, ::std::$ty::MAX)];
|
||||
(::core::$ty::MIN, ::core::$ty::MAX)];
|
||||
for &(low, high) in v.iter() {
|
||||
let mut sampler: Range<$ty> = Range::new(low, high);
|
||||
for _ in 0..1000 {
|
||||
|
@ -18,7 +18,8 @@
|
||||
|
||||
use Rng;
|
||||
|
||||
use std::{fmt, mem, ptr};
|
||||
use core::{fmt, mem, ptr};
|
||||
#[cfg(feature="std")]
|
||||
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
|
||||
|
||||
const MEMORY_BLOCKS: usize = 64;
|
||||
@ -107,6 +108,7 @@ impl fmt::Display for TimerError {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature="std")]
|
||||
impl ::std::error::Error for TimerError {
|
||||
fn description(&self) -> &str {
|
||||
self.description()
|
||||
@ -114,6 +116,7 @@ impl ::std::error::Error for TimerError {
|
||||
}
|
||||
|
||||
// Initialise to zero; must be positive
|
||||
#[cfg(feature="std")]
|
||||
static JITTER_ROUNDS: AtomicUsize = ATOMIC_USIZE_INIT;
|
||||
|
||||
impl JitterRng {
|
||||
@ -123,8 +126,9 @@ impl JitterRng {
|
||||
/// During initialization CPU execution timing jitter is measured a few
|
||||
/// hundred times. If this does not pass basic quality tests, an error is
|
||||
/// returned. The test result is cached to make subsequent calls faster.
|
||||
#[cfg(feature="std")]
|
||||
pub fn new() -> Result<JitterRng, TimerError> {
|
||||
let mut ec = JitterRng::new_with_timer(get_nstime);
|
||||
let mut ec = JitterRng::new_with_timer(platform::get_nstime);
|
||||
let mut rounds = JITTER_ROUNDS.load(Ordering::Relaxed) as u32;
|
||||
if rounds == 0 {
|
||||
// No result yet: run test.
|
||||
@ -648,57 +652,61 @@ impl JitterRng {
|
||||
/// # try_main().unwrap();
|
||||
/// # }
|
||||
/// ```
|
||||
#[cfg(feature="std")]
|
||||
pub fn timer_stats(&mut self, var_rounds: bool) -> i64 {
|
||||
let time = get_nstime();
|
||||
let time = platform::get_nstime();
|
||||
self.memaccess(var_rounds);
|
||||
self.lfsr_time(time, var_rounds);
|
||||
let time2 = get_nstime();
|
||||
let time2 = platform::get_nstime();
|
||||
time2.wrapping_sub(time) as i64
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "windows", all(target_arch = "wasm32", not(target_os = "emscripten")))))]
|
||||
fn get_nstime() -> u64 {
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
#[cfg(feature="std")]
|
||||
mod platform {
|
||||
#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "windows", all(target_arch = "wasm32", not(target_os = "emscripten")))))]
|
||||
pub fn get_nstime() -> u64 {
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
let dur = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
|
||||
// The correct way to calculate the current time is
|
||||
// `dur.as_secs() * 1_000_000_000 + dur.subsec_nanos() as u64`
|
||||
// But this is faster, and the difference in terms of entropy is negligible
|
||||
// (log2(10^9) == 29.9).
|
||||
dur.as_secs() << 30 | dur.subsec_nanos() as u64
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
fn get_nstime() -> u64 {
|
||||
extern crate libc;
|
||||
// On Mac OS and iOS std::time::SystemTime only has 1000ns resolution.
|
||||
// We use `mach_absolute_time` instead. This provides a CPU dependent unit,
|
||||
// to get real nanoseconds the result should by multiplied by numer/denom
|
||||
// from `mach_timebase_info`.
|
||||
// But we are not interested in the exact nanoseconds, just entropy. So we
|
||||
// use the raw result.
|
||||
unsafe { libc::mach_absolute_time() }
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn get_nstime() -> u64 {
|
||||
#[allow(non_camel_case_types)]
|
||||
type LARGE_INTEGER = i64;
|
||||
#[allow(non_camel_case_types)]
|
||||
type BOOL = i32;
|
||||
extern "system" {
|
||||
fn QueryPerformanceCounter(lpPerformanceCount: *mut LARGE_INTEGER) -> BOOL;
|
||||
let dur = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
|
||||
// The correct way to calculate the current time is
|
||||
// `dur.as_secs() * 1_000_000_000 + dur.subsec_nanos() as u64`
|
||||
// But this is faster, and the difference in terms of entropy is negligible
|
||||
// (log2(10^9) == 29.9).
|
||||
dur.as_secs() << 30 | dur.subsec_nanos() as u64
|
||||
}
|
||||
|
||||
let mut t = 0;
|
||||
unsafe { QueryPerformanceCounter(&mut t); }
|
||||
t as u64
|
||||
}
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
pub fn get_nstime() -> u64 {
|
||||
extern crate libc;
|
||||
// On Mac OS and iOS std::time::SystemTime only has 1000ns resolution.
|
||||
// We use `mach_absolute_time` instead. This provides a CPU dependent unit,
|
||||
// to get real nanoseconds the result should by multiplied by numer/denom
|
||||
// from `mach_timebase_info`.
|
||||
// But we are not interested in the exact nanoseconds, just entropy. So we
|
||||
// use the raw result.
|
||||
unsafe { libc::mach_absolute_time() }
|
||||
}
|
||||
|
||||
#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
|
||||
fn get_nstime() -> u64 {
|
||||
unreachable!()
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn get_nstime() -> u64 {
|
||||
#[allow(non_camel_case_types)]
|
||||
type LARGE_INTEGER = i64;
|
||||
#[allow(non_camel_case_types)]
|
||||
type BOOL = i32;
|
||||
extern "system" {
|
||||
fn QueryPerformanceCounter(lpPerformanceCount: *mut LARGE_INTEGER) -> BOOL;
|
||||
}
|
||||
|
||||
let mut t = 0;
|
||||
unsafe { QueryPerformanceCounter(&mut t); }
|
||||
t as u64
|
||||
}
|
||||
|
||||
#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
|
||||
pub fn get_nstime() -> u64 {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
// A function that is opaque to the optimizer to assist in avoiding dead-code
|
||||
|
39
src/lib.rs
39
src/lib.rs
@ -243,19 +243,22 @@
|
||||
|
||||
#![deny(missing_debug_implementations)]
|
||||
|
||||
#![cfg_attr(not(feature="std"), no_std)]
|
||||
#![cfg_attr(all(feature="alloc", not(feature="std")), feature(alloc))]
|
||||
#![cfg_attr(feature = "i128_support", feature(i128_type, i128))]
|
||||
|
||||
#[cfg(feature="std")] extern crate std as core;
|
||||
#[cfg(all(feature = "alloc", not(feature="std")))] extern crate alloc;
|
||||
#[cfg(test)] #[macro_use] extern crate log;
|
||||
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::marker;
|
||||
use std::mem;
|
||||
use std::io;
|
||||
use std::rc::Rc;
|
||||
use core::marker;
|
||||
use core::mem;
|
||||
#[cfg(feature="std")] use std::cell::RefCell;
|
||||
#[cfg(feature="std")] use std::io;
|
||||
#[cfg(feature="std")] use std::rc::Rc;
|
||||
|
||||
pub use jitter::JitterRng;
|
||||
pub use os::OsRng;
|
||||
#[cfg(feature="std")] pub use os::OsRng;
|
||||
|
||||
pub use isaac::{IsaacRng, Isaac64Rng};
|
||||
pub use chacha::ChaChaRng;
|
||||
@ -274,9 +277,9 @@ pub mod distributions;
|
||||
pub mod reseeding;
|
||||
mod rand_impls;
|
||||
pub mod jitter;
|
||||
pub mod os;
|
||||
pub mod read;
|
||||
pub mod seq;
|
||||
#[cfg(feature="std")] pub mod os;
|
||||
#[cfg(feature="std")] pub mod read;
|
||||
#[cfg(any(feature="std", feature = "alloc"))] pub mod seq;
|
||||
mod prng;
|
||||
|
||||
// These tiny modules are here to avoid API breakage, probably only temporarily
|
||||
@ -625,6 +628,7 @@ impl<'a, R: ?Sized> Rng for &'a mut R where R: Rng {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature="std")]
|
||||
impl<R: ?Sized> Rng for Box<R> where R: Rng {
|
||||
fn next_u32(&mut self) -> u32 {
|
||||
(**self).next_u32()
|
||||
@ -776,6 +780,7 @@ impl StdRng {
|
||||
///
|
||||
/// Reading the randomness from the OS may fail, and any error is
|
||||
/// propagated via the `io::Result` return value.
|
||||
#[cfg(feature="std")]
|
||||
pub fn new() -> io::Result<StdRng> {
|
||||
match OsRng::new() {
|
||||
Ok(mut r) => Ok(StdRng { rng: r.gen() }),
|
||||
@ -823,14 +828,17 @@ impl<'a> SeedableRng<&'a [usize]> for StdRng {
|
||||
/// create the `Rng` yourself.
|
||||
///
|
||||
/// This will seed the generator with randomness from thread_rng.
|
||||
#[cfg(feature="std")]
|
||||
pub fn weak_rng() -> XorShiftRng {
|
||||
thread_rng().gen()
|
||||
}
|
||||
|
||||
/// Controls how the thread-local RNG is reseeded.
|
||||
#[cfg(feature="std")]
|
||||
#[derive(Debug)]
|
||||
struct ThreadRngReseeder;
|
||||
|
||||
#[cfg(feature="std")]
|
||||
impl reseeding::Reseeder<StdRng> for ThreadRngReseeder {
|
||||
fn reseed(&mut self, rng: &mut StdRng) {
|
||||
match StdRng::new() {
|
||||
@ -839,10 +847,13 @@ impl reseeding::Reseeder<StdRng> for ThreadRngReseeder {
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(feature="std")]
|
||||
const THREAD_RNG_RESEED_THRESHOLD: u64 = 32_768;
|
||||
#[cfg(feature="std")]
|
||||
type ThreadRngInner = reseeding::ReseedingRng<StdRng, ThreadRngReseeder>;
|
||||
|
||||
/// The thread-local RNG.
|
||||
#[cfg(feature="std")]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ThreadRng {
|
||||
rng: Rc<RefCell<ThreadRngInner>>,
|
||||
@ -860,6 +871,7 @@ pub struct ThreadRng {
|
||||
/// if the operating system random number generator is rigged to give
|
||||
/// the same sequence always. If absolute consistency is required,
|
||||
/// explicitly select an RNG, e.g. `IsaacRng` or `Isaac64Rng`.
|
||||
#[cfg(feature="std")]
|
||||
pub fn thread_rng() -> ThreadRng {
|
||||
// used to make space in TLS for a random number generator
|
||||
thread_local!(static THREAD_RNG_KEY: Rc<RefCell<ThreadRngInner>> = {
|
||||
@ -876,6 +888,7 @@ pub fn thread_rng() -> ThreadRng {
|
||||
ThreadRng { rng: THREAD_RNG_KEY.with(|t| t.clone()) }
|
||||
}
|
||||
|
||||
#[cfg(feature="std")]
|
||||
impl Rng for ThreadRng {
|
||||
fn next_u32(&mut self) -> u32 {
|
||||
self.rng.borrow_mut().next_u32()
|
||||
@ -933,13 +946,12 @@ impl Rng for ThreadRng {
|
||||
/// *x = rng.gen();
|
||||
/// }
|
||||
/// ```
|
||||
#[cfg(feature="std")]
|
||||
#[inline]
|
||||
pub fn random<T: Rand>() -> T {
|
||||
thread_rng().gen()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[deprecated(since="0.4.0", note="renamed to seq::sample_iter")]
|
||||
/// DEPRECATED: use `seq::sample_iter` instead.
|
||||
///
|
||||
/// Randomly sample up to `amount` elements from a finite iterator.
|
||||
@ -954,6 +966,9 @@ pub fn random<T: Rand>() -> T {
|
||||
/// let sample = sample(&mut rng, 1..100, 5);
|
||||
/// println!("{:?}", sample);
|
||||
/// ```
|
||||
#[cfg(feature="std")]
|
||||
#[inline(always)]
|
||||
#[deprecated(since="0.4.0", note="renamed to seq::sample_iter")]
|
||||
pub fn sample<T, I, R>(rng: &mut R, iterable: I, amount: usize) -> Vec<T>
|
||||
where I: IntoIterator<Item=T>,
|
||||
R: Rng,
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
//! The ChaCha random number generator.
|
||||
|
||||
use std::num::Wrapping as w;
|
||||
use core::num::Wrapping as w;
|
||||
use {Rng, SeedableRng, Rand};
|
||||
|
||||
#[allow(bad_style)]
|
||||
|
@ -12,10 +12,10 @@
|
||||
|
||||
#![allow(non_camel_case_types)]
|
||||
|
||||
use std::slice;
|
||||
use std::iter::repeat;
|
||||
use std::num::Wrapping as w;
|
||||
use std::fmt;
|
||||
use core::slice;
|
||||
use core::iter::repeat;
|
||||
use core::num::Wrapping as w;
|
||||
use core::fmt;
|
||||
|
||||
use {Rng, SeedableRng, Rand};
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
//! Xorshift generators
|
||||
|
||||
use std::num::Wrapping as w;
|
||||
use core::num::Wrapping as w;
|
||||
use {Rng, SeedableRng, Rand};
|
||||
|
||||
/// An Xorshift[1] random number
|
||||
|
@ -10,8 +10,7 @@
|
||||
|
||||
//! The implementations of `Rand` for the built-in types.
|
||||
|
||||
use std::char;
|
||||
use std::mem;
|
||||
use core::{char, mem};
|
||||
|
||||
use {Rand,Rng};
|
||||
|
||||
|
@ -11,7 +11,7 @@
|
||||
//! A wrapper around another RNG that reseeds it after it
|
||||
//! generates a certain number of random bytes.
|
||||
|
||||
use std::default::Default;
|
||||
use core::default::Default;
|
||||
|
||||
use {Rng, SeedableRng};
|
||||
|
||||
|
11
src/seq.rs
11
src/seq.rs
@ -11,7 +11,13 @@
|
||||
//! Functions for randomly accessing and sampling sequences.
|
||||
|
||||
use super::Rng;
|
||||
use std::collections::hash_map::HashMap;
|
||||
|
||||
// This crate is only enabled when either std or alloc is available.
|
||||
// BTreeMap is not as fast in tests, but better than nothing.
|
||||
#[cfg(feature="std")] use std::collections::HashMap;
|
||||
#[cfg(not(feature="std"))] use alloc::btree_map::BTreeMap;
|
||||
|
||||
#[cfg(not(feature="std"))] use alloc::Vec;
|
||||
|
||||
/// Randomly sample `amount` elements from a finite iterator.
|
||||
///
|
||||
@ -189,7 +195,8 @@ fn sample_indices_cache<R>(
|
||||
where R: Rng,
|
||||
{
|
||||
debug_assert!(amount <= length);
|
||||
let mut cache = HashMap::with_capacity(amount);
|
||||
#[cfg(feature="std")] let mut cache = HashMap::with_capacity(amount);
|
||||
#[cfg(not(feature="std"))] let mut cache = BTreeMap::new();
|
||||
let mut out = Vec::with_capacity(amount);
|
||||
for i in 0..amount {
|
||||
let j: usize = rng.gen_range(i, length);
|
||||
|
Loading…
x
Reference in New Issue
Block a user