diff --git a/.travis.yml b/.travis.yml index f3d768871bf..9ec7cbc232b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,32 +1,116 @@ +# Based on the "trust" template v0.1.1 +# https://github.com/japaric/trust/tree/v0.1.1 + +dist: trusty language: rust -sudo: false +services: docker +sudo: required + +env: + global: + - CRATE_NAME=rand matrix: + # All available targets (at time script was copied) listed + # Test on: common platforms, plus: + # + big endian (mips) 32- and 64-bit + # + no_std (thumbv7) include: - - rust: 1.15.0 - - rust: stable - - rust: stable + # Android + # - env: TARGET=aarch64-linux-android DISABLE_TESTS=1 + # - env: TARGET=arm-linux-androideabi DISABLE_TESTS=1 + - env: TARGET=armv7-linux-androideabi DISABLE_TESTS=1 + # - env: TARGET=i686-linux-android DISABLE_TESTS=1 + # - env: TARGET=x86_64-linux-android DISABLE_TESTS=1 + + # iOS + # - env: TARGET=aarch64-apple-ios DISABLE_TESTS=1 + # os: osx + - env: TARGET=armv7-apple-ios DISABLE_TESTS=1 os: osx - - rust: beta - - rust: nightly - - - rust: nightly - before_script: - - pip install 'travis-cargo<0.2' --user && export PATH=$HOME/.local/bin:$PATH - script: - - cargo doc --no-deps --all-features - - cargo test --benches - - cargo test --features nightly - after_success: - - travis-cargo --only nightly doc-upload + # - env: TARGET=armv7s-apple-ios DISABLE_TESTS=1 + # os: osx + # - env: TARGET=i386-apple-ios DISABLE_TESTS=1 + # os: osx + # - env: TARGET=x86_64-apple-ios DISABLE_TESTS=1 + # os: osx + + # Linux + # - env: TARGET=aarch64-unknown-linux-gnu + # - env: TARGET=arm-unknown-linux-gnueabi + # - env: TARGET=armv7-unknown-linux-gnueabihf + - env: TARGET=i686-unknown-linux-gnu + # - env: TARGET=i686-unknown-linux-musl + - env: TARGET=mips-unknown-linux-gnu + - env: TARGET=mips64-unknown-linux-gnuabi64 + # - env: TARGET=mips64el-unknown-linux-gnuabi64 + # - env: TARGET=mipsel-unknown-linux-gnu + # - env: TARGET=powerpc-unknown-linux-gnu + # - env: TARGET=powerpc64-unknown-linux-gnu + # - env: TARGET=powerpc64le-unknown-linux-gnu + # - env: TARGET=s390x-unknown-linux-gnu DISABLE_TESTS=1 + - env: TARGET=x86_64-unknown-linux-gnu + # - env: TARGET=x86_64-unknown-linux-musl + + # OSX + # - env: TARGET=i686-apple-darwin + # os: osx + - env: TARGET=x86_64-apple-darwin + os: osx + + # *BSD + - env: TARGET=i686-unknown-freebsd DISABLE_TESTS=1 + - env: TARGET=x86_64-unknown-freebsd DISABLE_TESTS=1 + # - env: TARGET=x86_64-unknown-netbsd DISABLE_TESTS=1 + + # Windows + # - env: TARGET=x86_64-pc-windows-gnu + + # Bare metal + # These targets don't support std and as such are likely not suitable for + # most crates. + - env: TARGET=thumbv6m-none-eabi DISABLE_STD=1 + rust: nightly + rust: nightly + - env: TARGET=thumbv7em-none-eabihf DISABLE_STD=1 + rust: nightly + - env: TARGET=thumbv7m-none-eabi DISABLE_STD=1 + rust: nightly + + # Testing other channels + - env: TARGET=x86_64-unknown-linux-gnu NIGHTLY=1 + rust: nightly + - env: TARGET=x86_64-apple-darwin NIGHTLY=1 + os: osx + rust: nightly + +before_install: + - set -e + - rustup self update + +install: + - sh utils/ci/install.sh + - source ~/.cargo/env || true script: - - cargo test - - cargo test --manifest-path rand-derive/Cargo.toml + - bash utils/ci/script.sh -env: - global: - secure: "BdDntVHSompN+Qxz5Rz45VI4ZqhD72r6aPl166FADlnkIwS6N6FLWdqs51O7G5CpoMXEDvyYrjmRMZe/GYLIG9cmqmn/wUrWPO+PauGiIuG/D2dmfuUNvSTRcIe7UQLXrfP3yyfZPgqsH6pSnNEVopquQKy3KjzqepgriOJtbyY=" +after_script: set +e + +#before_deploy: +# - sh utils/ci/before_deploy.sh + +cache: cargo +before_cache: + # Travis can't cache files that are not readable by "others" + - chmod -R a+r $HOME/.cargo + +# after_success: +# - travis-cargo --only nightly doc-upload + +# env: +# global: +# secure: "BdDntVHSompN+Qxz5Rz45VI4ZqhD72r6aPl166FADlnkIwS6N6FLWdqs51O7G5CpoMXEDvyYrjmRMZe/GYLIG9cmqmn/wUrWPO+PauGiIuG/D2dmfuUNvSTRcIe7UQLXrfP3yyfZPgqsH6pSnNEVopquQKy3KjzqepgriOJtbyY=" notifications: email: diff --git a/CHANGELOG.md b/CHANGELOG.md index f39d29c2d4b..4898fc50933 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [0.4.2] - 2018-01-05 +### Changed +- Use winapi on Windows +- Update for Fuchsia OS +- Remove dev-dependency on `log` + ## [0.4.1] - 2017-12-17 ### Added - `no_std` support diff --git a/Cargo.toml b/Cargo.toml index f1ed2d2f14a..c5e7b5e9062 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rand" -version = "0.4.1" +version = "0.4.2" authors = ["The Rust Project Developers"] license = "MIT/Apache-2.0" readme = "README.md" @@ -17,10 +17,14 @@ categories = ["algorithms"] default = ["std"] nightly = ["i128_support"] # enables all features requiring nightly rust -std = ["libc"] # default feature; without this rand uses libcore +std = ["rand_core/std", "libc"] # default feature; without this rand uses libcore alloc = [] # enables Vec and Box support without std -i128_support = [] # enables i128 and u128 support +i128_support = ["rand_core/i128_support"] # enables i128 and u128 support + +[dependencies] +# All deps must not use 'std' by default +rand_core = { path = 'rand_core', default-features = false } [target.'cfg(unix)'.dependencies] libc = { version = "0.2", optional = true } @@ -28,11 +32,8 @@ libc = { version = "0.2", optional = true } [target.'cfg(windows)'.dependencies] winapi = { version = "0.3", features = ["minwindef", "ntsecapi", "profileapi", "winnt"] } -[dev-dependencies] -log = "0.3.0" - [workspace] -members = ["rand-derive"] +members = ["rand_core"] [target.'cfg(target_os = "fuchsia")'.dependencies] -fuchsia-zircon = "0.3" +fuchsia-zircon = "0.3.2" diff --git a/README.md b/README.md index 994467ae224..28a6a84c20a 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,8 @@ rand A Rust library for random number generators and other randomness functionality. -[![Build Status](https://travis-ci.org/rust-lang-nursery/rand.svg?branch=master)](https://travis-ci.org/rust-lang-nursery/rand) -[![Build status](https://ci.appveyor.com/api/projects/status/rm5c9o33k3jhchbw?svg=true)](https://ci.appveyor.com/project/alexcrichton/rand) +[![Build Status](https://travis-ci.org/dhardy/rand.svg?branch=master)](https://travis-ci.org/dhardy/rand) + [Documentation](https://docs.rs/rand) @@ -29,36 +29,45 @@ The `rand` crate has been at version `0.3` since March 2015. If you wish to avoid all breaking changes you may wish to stick with this version. Version `0.4`was released in December 2017. It contains almost no breaking -changes since the `0.3` series, but nevertheless (will) contain a significant -amount of new code, including a new "external" entropy source (`JitterRng`), -`no_std` support, and significant performance improvements for the ISAAC random -number generators. +changes since the `0.3` series, but nevertheless contains some significant +new code, including a new "external" entropy source (`JitterRng`) and `no_std` +support. + +Version `0.5` is in development and contains significant performance +improvements for the ISAAC random number generators. ## Examples -There is built-in support for a random number generator (RNG) associated with each thread stored in thread-local storage. This RNG can be accessed via thread_rng, or used implicitly via random. This RNG is normally randomly seeded from an operating-system source of randomness, e.g. /dev/urandom on Unix systems, and will automatically reseed itself from this source after generating 32 KiB of random data. +There is built-in support for a random number generator (RNG) associated with +each thread stored in thread-local storage. This RNG can be accessed via +thread_rng. ```rust -let tuple = rand::random::<(f64, char)>(); -println!("{:?}", tuple) +use rand::thread_rng; + +let x: u32 = thread_rng().next_u32(); +println!("{}", x) ``` ```rust +use rand::distributions::{uniform}; use rand::Rng; let mut rng = rand::thread_rng(); -if rng.gen() { // random bool - println!("i32: {}, u32: {}", rng.gen::(), rng.gen::()) +if uniform(&mut rng) { // random bool + let x: i32 = uniform(&mut rng); + let y: u32 = uniform(&mut rng); + println!("i32: {}, u32: {}", x, y); } ``` -It is also possible to use other RNG types, which have a similar interface. The following uses the "ChaCha" algorithm instead of the default. +It is also possible to use other generators types, which have a similar interface. The following uses the "ChaCha" algorithm instead of the default. ```rust -use rand::{Rng, ChaChaRng}; +use rand::{thread_rng, ChaChaRng, distributions}; -let mut rng = rand::ChaChaRng::new_unseeded(); -println!("i32: {}, u32: {}", rng.gen::(), rng.gen::()) +let mut rng = ChaChaRng::from_rng(&mut thread_rng()).unwrap(); +println!("random between 0-9: {}", distributions::range(0, 10, &mut rng)); ``` ## Features @@ -103,34 +112,31 @@ cargo bench cargo test --benches ``` -# `derive(Rand)` +## Testing -You can derive the `Rand` trait for your custom type via the `#[derive(Rand)]` -directive. To use this first add this to your Cargo.toml: +Unfortunately, `cargo test` does not test everything. The following tests are +recommended: -```toml -rand = "0.4" -rand_derive = "0.3" ``` +# Basic tests for rand and sub-crates +cargo test --all -Next in your crate: - -```rust -extern crate rand; -#[macro_use] -extern crate rand_derive; +# Test no_std support (build only since nearly all tests require std) +cargo build --all --no-default-features -#[derive(Rand, Debug)] -struct MyStruct { - a: i32, - b: u32, -} +# Test 128-bit support (requires nightly) +cargo test --all --features i128_support -fn main() { - println!("{:?}", rand::random::()); -} +# Benchmarks (requires nightly) +cargo bench ``` +# `derive(Rand)` + +```toml +rand = "0.4" +rand_derive = "0.3" +``` # License diff --git a/appveyor.yml b/appveyor.yml index 02e217fe489..54cf8ef2c45 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -35,4 +35,3 @@ test_script: - cargo test --benches - cargo test - cargo test --features nightly - - cargo test --manifest-path rand-derive/Cargo.toml diff --git a/benches/bench.rs b/benches/bench.rs deleted file mode 100644 index d396f25b5e9..00000000000 --- a/benches/bench.rs +++ /dev/null @@ -1,34 +0,0 @@ -#![feature(test)] - -extern crate test; -extern crate rand; - -const RAND_BENCH_N: u64 = 1000; - -mod distributions; - -use std::mem::size_of; -use test::{black_box, Bencher}; -use rand::{StdRng, Rng}; - -#[bench] -fn rand_f32(b: &mut Bencher) { - let mut rng = StdRng::new().unwrap(); - b.iter(|| { - for _ in 0..RAND_BENCH_N { - black_box(rng.next_f32()); - } - }); - b.bytes = size_of::() as u64 * RAND_BENCH_N; -} - -#[bench] -fn rand_f64(b: &mut Bencher) { - let mut rng = StdRng::new().unwrap(); - b.iter(|| { - for _ in 0..RAND_BENCH_N { - black_box(rng.next_f64()); - } - }); - b.bytes = size_of::() as u64 * RAND_BENCH_N; -} diff --git a/benches/distributions.rs b/benches/distributions.rs new file mode 100644 index 00000000000..bc0435968ca --- /dev/null +++ b/benches/distributions.rs @@ -0,0 +1,98 @@ +#![feature(test)] +#![cfg_attr(feature = "i128_support", feature(i128_type, i128))] + +extern crate test; +extern crate rand; + +const RAND_BENCH_N: u64 = 1000; + +use std::mem::size_of; +use test::{black_box, Bencher}; + +use rand::NewSeeded; +use rand::prng::XorShiftRng; +use rand::distributions::*; + +macro_rules! distr { + ($fnn:ident, $ty:ty, $distr:expr) => { + #[bench] + fn $fnn(b: &mut Bencher) { + let mut rng = XorShiftRng::new().unwrap(); + let distr = $distr; + + b.iter(|| { + for _ in 0..::RAND_BENCH_N { + let x: $ty = distr.sample(&mut rng); + black_box(x); + } + }); + b.bytes = size_of::<$ty>() as u64 * ::RAND_BENCH_N; + } + } +} + +// range +distr!(distr_range_i8, i8, Range::new(20i8, 100)); +distr!(distr_range_i16, i16, Range::new(-500i16, 2000)); +distr!(distr_range_i32, i32, Range::new(-200_000_000i32, 800_000_000)); +distr!(distr_range_i64, i64, Range::new(3i64, 12345678901234)); +#[cfg(feature = "i128_support")] +distr!(distr_range_i128, i128, Range::new(-12345678901234i128, 12345678901234567890)); + +distr!(distr_range_float32, f32, Range::new(2.26f32, 2.319)); +distr!(distr_range_float, f64, Range::new(2.26f64, 2.319)); + +// uniform +distr!(distr_uniform_i8, i8, Uniform); +distr!(distr_uniform_i16, i16, Uniform); +distr!(distr_uniform_i32, i32, Uniform); +distr!(distr_uniform_i64, i64, Uniform); +#[cfg(feature = "i128_support")] +distr!(distr_uniform_i128, i128, Uniform); + +distr!(distr_uniform_bool, bool, Uniform); +distr!(distr_uniform_alphanumeric, char, Alphanumeric); +distr!(distr_uniform_codepoint, char, Codepoint); + +distr!(distr_uniform01_float32, f32, Uniform01); +distr!(distr_closed01_float32, f32, Closed01); +distr!(distr_open01_float32, f32, Open01); + +distr!(distr_uniform01_float, f64, Uniform01); +distr!(distr_closed01_float, f64, Closed01); +distr!(distr_open01_float, f64, Open01); + +// distributions +distr!(distr_exp, f64, Exp::new(2.71828 * 3.14159)); +distr!(distr_normal, f64, Normal::new(-2.71828, 3.14159)); +distr!(distr_log_normal, f64, LogNormal::new(-2.71828, 3.14159)); +distr!(distr_gamma_large_shape, f64, Gamma::new(10., 1.0)); +distr!(distr_gamma_small_shape, f64, Gamma::new(0.1, 1.0)); + + +// construct and sample from a range +macro_rules! gen_range_int { + ($fnn:ident, $ty:ty, $low:expr, $high:expr) => { + #[bench] + fn $fnn(b: &mut Bencher) { + let mut rng = XorShiftRng::new().unwrap(); + let high = $high; + + b.iter(|| { + for _ in 0..::RAND_BENCH_N { + let x: $ty = Range::sample_single($low, high, &mut rng); + black_box(x); + black_box(high); + } + }); + b.bytes = size_of::<$ty>() as u64 * ::RAND_BENCH_N; + } + } +} + +gen_range_int!(gen_range_i8, i8, 20i8, 100); +gen_range_int!(gen_range_i16, i16, -500i16, 2000); +gen_range_int!(gen_range_i32, i32, -200_000_000i32, 800_000_000); +gen_range_int!(gen_range_i64, i64, 3i64, 12345678901234); +#[cfg(feature = "i128_support")] +gen_range_int!(gen_range_i128, i128, -12345678901234i128, 12345678901234567890); diff --git a/benches/distributions/exponential.rs b/benches/distributions/exponential.rs deleted file mode 100644 index 152615d7ba3..00000000000 --- a/benches/distributions/exponential.rs +++ /dev/null @@ -1,18 +0,0 @@ -use std::mem::size_of; -use test::Bencher; -use rand; -use rand::distributions::exponential::Exp; -use rand::distributions::Sample; - -#[bench] -fn rand_exp(b: &mut Bencher) { - let mut rng = rand::weak_rng(); - let mut exp = Exp::new(2.71828 * 3.14159); - - b.iter(|| { - for _ in 0..::RAND_BENCH_N { - exp.sample(&mut rng); - } - }); - b.bytes = size_of::() as u64 * ::RAND_BENCH_N; -} diff --git a/benches/distributions/gamma.rs b/benches/distributions/gamma.rs deleted file mode 100644 index bf3fd367a9b..00000000000 --- a/benches/distributions/gamma.rs +++ /dev/null @@ -1,31 +0,0 @@ -use std::mem::size_of; -use test::Bencher; -use rand; -use rand::distributions::IndependentSample; -use rand::distributions::gamma::Gamma; - -#[bench] -fn bench_gamma_large_shape(b: &mut Bencher) { - let gamma = Gamma::new(10., 1.0); - let mut rng = rand::weak_rng(); - - b.iter(|| { - for _ in 0..::RAND_BENCH_N { - gamma.ind_sample(&mut rng); - } - }); - b.bytes = size_of::() as u64 * ::RAND_BENCH_N; -} - -#[bench] -fn bench_gamma_small_shape(b: &mut Bencher) { - let gamma = Gamma::new(0.1, 1.0); - let mut rng = rand::weak_rng(); - - b.iter(|| { - for _ in 0..::RAND_BENCH_N { - gamma.ind_sample(&mut rng); - } - }); - b.bytes = size_of::() as u64 * ::RAND_BENCH_N; -} diff --git a/benches/distributions/mod.rs b/benches/distributions/mod.rs deleted file mode 100644 index 49f6bd9c06c..00000000000 --- a/benches/distributions/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod exponential; -mod normal; -mod gamma; diff --git a/benches/distributions/normal.rs b/benches/distributions/normal.rs deleted file mode 100644 index 1c858b19b39..00000000000 --- a/benches/distributions/normal.rs +++ /dev/null @@ -1,18 +0,0 @@ -use std::mem::size_of; -use test::Bencher; -use rand; -use rand::distributions::Sample; -use rand::distributions::normal::Normal; - -#[bench] -fn rand_normal(b: &mut Bencher) { - let mut rng = rand::weak_rng(); - let mut normal = Normal::new(-2.71828, 3.14159); - - b.iter(|| { - for _ in 0..::RAND_BENCH_N { - normal.sample(&mut rng); - } - }); - b.bytes = size_of::() as u64 * ::RAND_BENCH_N; -} diff --git a/benches/generators.rs b/benches/generators.rs index daee7c5fbb5..b01a1725ceb 100644 --- a/benches/generators.rs +++ b/benches/generators.rs @@ -9,27 +9,11 @@ const BYTES_LEN: usize = 1024; use std::mem::size_of; use test::{black_box, Bencher}; -use rand::{Rng, StdRng, OsRng, JitterRng}; -use rand::{XorShiftRng, IsaacRng, Isaac64Rng, ChaChaRng}; +use rand::{Rng, NewSeeded, Sample, SeedableRng, StdRng, OsRng, JitterRng}; +use rand::prng::*; +use rand::reseeding::{ReseedingRng, ReseedWithNew}; macro_rules! gen_bytes { - ($fnn:ident, $gen:ident) => { - #[bench] - fn $fnn(b: &mut Bencher) { - let mut rng: $gen = OsRng::new().unwrap().gen(); - let mut buf = [0u8; BYTES_LEN]; - b.iter(|| { - for _ in 0..RAND_BENCH_N { - rng.fill_bytes(&mut buf); - black_box(buf); - } - }); - b.bytes = BYTES_LEN as u64 * RAND_BENCH_N; - } - } -} - -macro_rules! gen_bytes_new { ($fnn:ident, $gen:ident) => { #[bench] fn $fnn(b: &mut Bencher) { @@ -47,29 +31,15 @@ macro_rules! gen_bytes_new { } gen_bytes!(gen_bytes_xorshift, XorShiftRng); +gen_bytes!(gen_bytes_hc128, Hc128Rng); gen_bytes!(gen_bytes_isaac, IsaacRng); gen_bytes!(gen_bytes_isaac64, Isaac64Rng); gen_bytes!(gen_bytes_chacha, ChaChaRng); -gen_bytes_new!(gen_bytes_std, StdRng); -gen_bytes_new!(gen_bytes_os, OsRng); +gen_bytes!(gen_bytes_std, StdRng); +gen_bytes!(gen_bytes_os, OsRng); macro_rules! gen_uint { - ($fnn:ident, $ty:ty, $gen:ident) => { - #[bench] - fn $fnn(b: &mut Bencher) { - let mut rng: $gen = OsRng::new().unwrap().gen(); - b.iter(|| { - for _ in 0..RAND_BENCH_N { - black_box(rng.gen::<$ty>()); - } - }); - b.bytes = size_of::<$ty>() as u64 * RAND_BENCH_N; - } - } -} - -macro_rules! gen_uint_new { ($fnn:ident, $ty:ty, $gen:ident) => { #[bench] fn $fnn(b: &mut Bencher) { @@ -85,18 +55,20 @@ macro_rules! gen_uint_new { } gen_uint!(gen_u32_xorshift, u32, XorShiftRng); +gen_uint!(gen_u32_hc128, u32, Hc128Rng); gen_uint!(gen_u32_isaac, u32, IsaacRng); gen_uint!(gen_u32_isaac64, u32, Isaac64Rng); gen_uint!(gen_u32_chacha, u32, ChaChaRng); -gen_uint_new!(gen_u32_std, u32, StdRng); -gen_uint_new!(gen_u32_os, u32, OsRng); +gen_uint!(gen_u32_std, u32, StdRng); +gen_uint!(gen_u32_os, u32, OsRng); gen_uint!(gen_u64_xorshift, u64, XorShiftRng); +gen_uint!(gen_u64_hc128, u64, Hc128Rng); gen_uint!(gen_u64_isaac, u64, IsaacRng); gen_uint!(gen_u64_isaac64, u64, Isaac64Rng); gen_uint!(gen_u64_chacha, u64, ChaChaRng); -gen_uint_new!(gen_u64_std, u64, StdRng); -gen_uint_new!(gen_u64_os, u64, OsRng); +gen_uint!(gen_u64_std, u64, StdRng); +gen_uint!(gen_u64_os, u64, OsRng); #[bench] fn gen_u64_jitter(b: &mut Bencher) { @@ -111,16 +83,16 @@ macro_rules! init_gen { ($fnn:ident, $gen:ident) => { #[bench] fn $fnn(b: &mut Bencher) { - let mut rng: XorShiftRng = OsRng::new().unwrap().gen(); + let mut rng = XorShiftRng::new().unwrap(); b.iter(|| { - let r2: $gen = rng.gen(); - black_box(r2); + black_box($gen::from_rng(&mut rng).unwrap()); }); } } } init_gen!(init_xorshift, XorShiftRng); +init_gen!(init_hc128, Hc128Rng); init_gen!(init_isaac, IsaacRng); init_gen!(init_isaac64, Isaac64Rng); init_gen!(init_chacha, ChaChaRng); @@ -131,3 +103,40 @@ fn init_jitter(b: &mut Bencher) { black_box(JitterRng::new().unwrap()); }); } + + + +#[bench] +fn reseeding_hc128_bytes(b: &mut Bencher) { + let mut rng = ReseedingRng::new(Hc128Rng::new().unwrap(), + 128*1024*1024*1024, + ReseedWithNew); + let mut buf = [0u8; BYTES_LEN]; + b.iter(|| { + for _ in 0..RAND_BENCH_N { + rng.fill_bytes(&mut buf); + black_box(buf); + } + }); + b.bytes = BYTES_LEN as u64 * RAND_BENCH_N; +} + +macro_rules! reseeding_uint { + ($fnn:ident, $ty:ty) => { + #[bench] + fn $fnn(b: &mut Bencher) { + let mut rng = ReseedingRng::new(Hc128Rng::new().unwrap(), + 128*1024*1024*1024, + ReseedWithNew); + b.iter(|| { + for _ in 0..RAND_BENCH_N { + black_box(rng.gen::<$ty>()); + } + }); + b.bytes = size_of::<$ty>() as u64 * RAND_BENCH_N; + } + } +} + +reseeding_uint!(reseeding_hc128_u32, u32); +reseeding_uint!(reseeding_hc128_u64, u64); diff --git a/benches/misc.rs b/benches/misc.rs index 42517617662..c9e88df580e 100644 --- a/benches/misc.rs +++ b/benches/misc.rs @@ -5,15 +5,21 @@ extern crate rand; use test::{black_box, Bencher}; -use rand::{Rng, weak_rng}; +use rand::NewSeeded; +use rand::prng::XorShiftRng; use rand::seq::*; +use rand::sequences::Shuffle; + +fn weak_rng() -> XorShiftRng { + XorShiftRng::new().unwrap() +} #[bench] fn misc_shuffle_100(b: &mut Bencher) { let mut rng = weak_rng(); let x : &mut [usize] = &mut [1; 100]; b.iter(|| { - rng.shuffle(x); + x.shuffle(&mut rng); black_box(&x); }) } diff --git a/examples/cat_rng.rs b/examples/cat_rng.rs new file mode 100644 index 00000000000..97dd41c93b9 --- /dev/null +++ b/examples/cat_rng.rs @@ -0,0 +1,67 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! A small utility to concatenate the output of an RNG to stdout. + +extern crate rand; + +use rand::{Rng, NewSeeded, OsRng}; +use rand::prng::{XorShiftRng, IsaacRng, Isaac64Rng, ChaChaRng}; +use std::collections::HashMap; +use std::env; +use std::io::{self, Write, Error}; +use std::iter::Iterator; + +fn print_usage(cmd: &String, names: Vec) { + println!("Usage: {} RNG +where RNG is one of: {:?} + +This is a small tool to endlessly contatenate output from an RNG. It can for +example be used with PractRand: ./cat_rng chacha | ./RNG_test stdin", + cmd, names); +} + +type BR = Box; + +fn main() { + let mut ctors: HashMap<&'static str, + Box Result>> = HashMap::new(); + ctors.insert("os", Box::new(|| OsRng::new().map(|rng| Box::new(rng) as BR))); + + ctors.insert("xorshift", Box::new(|| XorShiftRng::new().map(|rng| Box::new(rng) as BR))); + ctors.insert("isaac", Box::new(|| IsaacRng::new().map(|rng| Box::new(rng) as BR))); + ctors.insert("isaac64", Box::new(|| Isaac64Rng::new().map(|rng| Box::new(rng) as BR))); + ctors.insert("chacha", Box::new(|| ChaChaRng::new().map(|rng| Box::new(rng) as BR))); + + let args: Vec = env::args().collect(); + if args.len() != 2 { + print_usage(&args[0], ctors.keys().map(|s| String::from(*s)).collect()); + } else { + if let Some(ctor) = ctors.get(&*args[1]) { + let rng = ctor().unwrap(); + cat_rng(rng).unwrap(); + } else { + println!("Error: unknown RNG: {}", args[1]); + println!(); + print_usage(&args[0], ctors.keys().map(|s| String::from(*s)).collect()); + } + } +} + +fn cat_rng(mut rng: Box) -> Result<(), Error> { + let mut buf = [0u8; 32]; + let stdout = io::stdout(); + let mut lock = stdout.lock(); + + loop { + rng.fill_bytes(&mut buf); + lock.write(&buf)?; + } +} diff --git a/rand-derive/Cargo.toml b/rand-derive/Cargo.toml deleted file mode 100644 index 1a2dbe1080b..00000000000 --- a/rand-derive/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] - -name = "rand_derive" -version = "0.3.1" -authors = ["The Rust Project Developers"] -license = "MIT/Apache-2.0" -readme = "README.md" -repository = "https://github.com/rust-lang-nursery/rand" -documentation = "https://docs.rs/rand_derive" -homepage = "https://github.com/rust-lang-nursery/rand" -description = """ -`#[derive(Rand)]` functionality for the `rand::Rand` trait. -""" - -[lib] -proc-macro = true - -[dependencies] -quote = "0.3" -syn = "0.11" - -[dev-dependencies] -rand = { path = "..", version = "0.4" } diff --git a/rand-derive/README.md b/rand-derive/README.md deleted file mode 100644 index 3b01cd926ea..00000000000 --- a/rand-derive/README.md +++ /dev/null @@ -1,51 +0,0 @@ - -rand_macros -==== - -`#[derive(Rand)]` functionality for the `rand::Rand` trait. - -## Usage -Add this to your `Cargo.toml`: - -```toml -[dependencies] -rand = "0.3" -rand_macros = "0.2" -``` - -and this to your crate root: - -```rust -extern crate rand; -#[macro_use] -extern crate rand_macros; -``` - -## Examples - -`#[derive(Rand)]` can be used on any `struct` or `enum` where all fields/variants implement `rand::Rand`. - -```rust -#[derive(Debug, Rand)] -struct Foo { - x: u16, - y: Option, -} - -#[derive(Debug, Rand)] -enum Bar { - X{x: u8, y: isize}, - Y([bool; 4]), - Z, -} -``` -Now you can call the `Rng::gen()` function on your custom types. - -```rust -use rand::Rng; - -let mut rng = rand::thread_rng(); - -println!("{:?}", rng.gen::()); -println!("{:?}", rng.gen::()); -``` diff --git a/rand-derive/src/lib.rs b/rand-derive/src/lib.rs deleted file mode 100644 index 80c803af29c..00000000000 --- a/rand-derive/src/lib.rs +++ /dev/null @@ -1,116 +0,0 @@ -//! Support for `#[derive(Rand)]` -//! -//! # Examples -//! -//! ``` -//! extern crate rand; -//! #[macro_use] -//! extern crate rand_derive; -//! -//! #[derive(Rand, Debug)] -//! struct MyStruct { -//! a: i32, -//! b: u32, -//! } -//! -//! fn main() { -//! println!("{:?}", rand::random::()); -//! } -//! ``` - -extern crate proc_macro; -#[macro_use] -extern crate quote; -extern crate syn; - -use proc_macro::TokenStream; - -#[proc_macro_derive(Rand)] -pub fn rand_derive(input: TokenStream) -> TokenStream { - let s = input.to_string(); - let ast = syn::parse_derive_input(&s).unwrap(); - let gen = impl_rand_derive(&ast); - gen.parse().unwrap() -} - -fn impl_rand_derive(ast: &syn::MacroInput) -> quote::Tokens { - let name = &ast.ident; - let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); - - let rand = match ast.body { - syn::Body::Struct(syn::VariantData::Struct(ref body)) => { - let fields = body - .iter() - .filter_map(|field| field.ident.as_ref()) - .map(|ident| quote! { #ident: __rng.gen() }) - .collect::>(); - - quote! { #name { #(#fields,)* } } - }, - syn::Body::Struct(syn::VariantData::Tuple(ref body)) => { - let fields = (0..body.len()) - .map(|_| quote! { __rng.gen() }) - .collect::>(); - - quote! { #name (#(#fields),*) } - }, - syn::Body::Struct(syn::VariantData::Unit) => { - quote! { #name } - }, - syn::Body::Enum(ref body) => { - if body.is_empty() { - panic!("`Rand` cannot be derived for enums with no variants"); - } - - let len = body.len(); - let mut arms = body - .iter() - .map(|variant| { - let ident = &variant.ident; - match variant.data { - syn::VariantData::Struct(ref body) => { - let fields = body - .iter() - .filter_map(|field| field.ident.as_ref()) - .map(|ident| quote! { #ident: __rng.gen() }) - .collect::>(); - quote! { #name::#ident { #(#fields,)* } } - }, - syn::VariantData::Tuple(ref body) => { - let fields = (0..body.len()) - .map(|_| quote! { __rng.gen() }) - .collect::>(); - - quote! { #name::#ident (#(#fields),*) } - }, - syn::VariantData::Unit => quote! { #name::#ident } - } - }); - - match len { - 1 => quote! { #(#arms)* }, - 2 => { - let (a, b) = (arms.next(), arms.next()); - quote! { if __rng.gen() { #a } else { #b } } - }, - _ => { - let mut variants = arms - .enumerate() - .map(|(index, arm)| quote! { #index => #arm }) - .collect::>(); - variants.push(quote! { _ => unreachable!() }); - quote! { match __rng.gen_range(0, #len) { #(#variants,)* } } - }, - } - } - }; - - quote! { - impl #impl_generics ::rand::Rand for #name #ty_generics #where_clause { - #[inline] - fn rand<__R: ::rand::Rng>(__rng: &mut __R) -> Self { - #rand - } - } - } -} diff --git a/rand-derive/tests/rand_macros.rs b/rand-derive/tests/rand_macros.rs deleted file mode 100644 index 938f2b06a79..00000000000 --- a/rand-derive/tests/rand_macros.rs +++ /dev/null @@ -1,58 +0,0 @@ -#![allow(dead_code)] - -extern crate rand; -#[macro_use] -extern crate rand_derive; - -use rand::Rng; - -#[derive(Rand)] -struct Struct { - x: u16, - y: Option, -} - -#[derive(Rand)] -struct Tuple(i16, Option); - -#[derive(Rand)] -struct Unit; - -#[derive(Rand)] -enum EnumUnit { - X, -} - -#[derive(Rand)] -enum Enum1 { - X(u8, f32), -} - -#[derive(Rand)] -enum Enum2 { - X(bool), - Y, -} - -#[derive(Rand)] -enum Enum3 { - X { x: u8, y: isize }, - Y([bool; 4]), - Z, -} - -#[test] -fn smoke() { - let mut rng = rand::XorShiftRng::new_unseeded(); - - // check nothing horrible happens internally: - for _ in 0..100 { - let _ = rng.gen::(); - let _ = rng.gen::(); - let _ = rng.gen::(); - let _ = rng.gen::(); - let _ = rng.gen::(); - let _ = rng.gen::(); - let _ = rng.gen::(); - } -} diff --git a/rand_core/Cargo.toml b/rand_core/Cargo.toml new file mode 100644 index 00000000000..c87a39e51dc --- /dev/null +++ b/rand_core/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "rand_core" +version = "0.0.1" +authors = ["The Rust Project Developers"] +description = "Random number generator core: `Rng` and extensions" +homepage = "https://github.com/dhardy/rand/tree/master/rand_core" +documentation = "https://docs.rs/rand_core" +readme = "README.md" +keywords = ["random", "rng"] +categories = ["algorithms"] +license = "MIT/Apache-2.0" +repository = "https://github.com/dhardy/rand/" + +[features] +# Bug: https://github.com/rust-lang/cargo/issues/4361 +# default = ["std"] +nightly = ["i128_support"] +std = [] +i128_support = [] diff --git a/rand_core/README.md b/rand_core/README.md new file mode 100644 index 00000000000..b62329696db --- /dev/null +++ b/rand_core/README.md @@ -0,0 +1,29 @@ +rand +==== + +Core functionality for the [rand] library. + +This crate contains the core trait, `Rng`, and some extension traits. This +should be sufficient for libraries publishing an RNG type, but most users should +prefer to use the [rand] crate. + +[Documentation](https://docs.rs/rand_core) + + +## Status + +This crate is experimental, provided as part of the [rand crate refactor]. Users +should be wary of depending on this crate at this time; breaking changes should +be expected and contents will conflict with those of the current published +version of rand (0.3.x). + +# License + +`rand` is primarily distributed under the terms of both the MIT +license and the Apache License (Version 2.0), with portions covered by various +BSD-like licenses. + +See LICENSE-APACHE, and LICENSE-MIT for details. + +[rand]: .. +[rand crate refactor]: https://github.com/rust-lang/rfcs/pull/2106 diff --git a/rand_core/src/impls.rs b/rand_core/src/impls.rs new file mode 100644 index 00000000000..0faf7977511 --- /dev/null +++ b/rand_core/src/impls.rs @@ -0,0 +1,225 @@ +// Copyright 2013-2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Helper functions for implementing `Rng` functions. +//! +//! For cross-platform reproducibility, these functions all use Little Endian: +//! least-significant part first. For example, `next_u64_via_u32` takes `u32` +//! values `x, y`, then outputs `(y << 32) | x`. To implement `next_u32` +//! from `next_u64` in little-endian order, one should use `next_u64() as u32`. +//! +//! Byte-swapping (like the std `to_le` functions) is only needed to convert +//! to/from byte sequences, and since its purpose is reproducibility, +//! non-reproducible sources (e.g. `OsRng`) need not bother with it. + +use core::intrinsics::transmute; +use core::slice; +use core::cmp::min; +use Rng; + +/// Implement `next_u64` via `next_u32`, little-endian order. +pub fn next_u64_via_u32(rng: &mut R) -> u64 { + // Use LE; we explicitly generate one value before the next. + let x = rng.next_u32() as u64; + let y = rng.next_u32() as u64; + (y << 32) | x +} + +/// Implement `next_u128` via `next_u64`, little-endian order. +/// +/// This may be used even where `next_u64` is implemented via `next_u32`. +#[cfg(feature = "i128_support")] +pub fn next_u128_via_u64(rng: &mut R) -> u128 { + // Use LE; we explicitly generate one value before the next. + let x = rng.next_u64() as u128; + let y = rng.next_u64() as u128; + (y << 64) | x +} + +macro_rules! fill_bytes_via { + ($rng:ident, $next_u:ident, $BYTES:expr, $dest:ident) => {{ + let mut left = $dest; + while left.len() >= $BYTES { + let (l, r) = {left}.split_at_mut($BYTES); + left = r; + let chunk: [u8; $BYTES] = unsafe { + transmute($rng.$next_u().to_le()) + }; + l.copy_from_slice(&chunk); + } + let n = left.len(); + if n > 0 { + let chunk: [u8; $BYTES] = unsafe { + transmute($rng.$next_u().to_le()) + }; + left.copy_from_slice(&chunk[..n]); + } + }} +} + +/// Implement `fill_bytes` via `next_u32`, little-endian order. +pub fn fill_bytes_via_u32(rng: &mut R, dest: &mut [u8]) { + fill_bytes_via!(rng, next_u32, 4, dest) +} + +/// Implement `fill_bytes` via `next_u64`, little-endian order. +pub fn fill_bytes_via_u64(rng: &mut R, dest: &mut [u8]) { + fill_bytes_via!(rng, next_u64, 8, dest) +} + +/// Implement `fill_bytes` via `next_u128`, little-endian order. +#[cfg(feature = "i128_support")] +pub fn fill_bytes_via_u128(rng: &mut R, dest: &mut [u8]) { + fill_bytes_via!(rng, next_u128, 16, dest) +} + +macro_rules! impl_uint_from_fill { + ($self:expr, $ty:ty, $N:expr) => ({ + debug_assert!($N == ::core::mem::size_of::<$ty>()); + + let mut int: $ty = 0; + unsafe { + let ptr = &mut int as *mut $ty as *mut u8; + let slice = slice::from_raw_parts_mut(ptr, $N); + $self.fill_bytes(slice); + } + int + }); +} + +macro_rules! fill_via_chunks { + ($src:expr, $dest:expr, $N:expr) => ({ + let chunk_size_u8 = min($src.len() * $N, $dest.len()); + let chunk_size = (chunk_size_u8 + $N - 1) / $N; + + // Convert to little-endian: + for ref mut x in $src[0..chunk_size].iter_mut() { + **x = (*x).to_le(); + } + + let bytes = unsafe { slice::from_raw_parts($src.as_ptr() as *const u8, + $src.len() * $N) }; + + let dest_chunk = &mut $dest[0..chunk_size_u8]; + dest_chunk.copy_from_slice(&bytes[0..chunk_size_u8]); + + (chunk_size, chunk_size_u8) + }); +} + +/// Implement `fill_bytes` by reading chunks from the output buffer of a block +/// based RNG. +/// +/// The return values are `(consumed_u32, filled_u8)`. +/// +/// `filled_u8` is the number of filled bytes in `dest`, which may be less than +/// the length of `dest`. +/// `consumed_u32` is the number of words consumed from `src`, which is the same +/// as `filled_u8 / 4` rounded up. +/// +/// Note that on big-endian systems values in the output buffer `src` are +/// mutated. `src[0..consumed_u32]` get converted to little-endian before +/// copying. +/// +/// # Example +/// (from `IsaacRng`) +/// +/// ```rust,ignore +/// fn fill_bytes(&mut self, dest: &mut [u8]) { +/// let mut read_len = 0; +/// while read_len < dest.len() { +/// if self.index >= self.rsl.len() { +/// self.isaac(); +/// } +/// +/// let (consumed_u32, filled_u8) = +/// impls::fill_via_u32_chunks(&mut self.rsl[self.index..], +/// &mut dest[read_len..]); +/// +/// self.index += consumed_u32; +/// read_len += filled_u8; +/// } +/// } +/// ``` +pub fn fill_via_u32_chunks(src: &mut [u32], dest: &mut [u8]) -> (usize, usize) { + fill_via_chunks!(src, dest, 4) +} + +/// Implement `fill_bytes` by reading chunks from the output buffer of a block +/// based RNG. +/// +/// The return values are `(consumed_u64, filled_u8)`. +/// `filled_u8` is the number of filled bytes in `dest`, which may be less than +/// the length of `dest`. +/// `consumed_u64` is the number of words consumed from `src`, which is the same +/// as `filled_u8 / 8` rounded up. +/// +/// Note that on big-endian systems values in the output buffer `src` are +/// mutated. `src[0..consumed_u64]` get converted to little-endian before +/// copying. +/// +/// See `fill_via_u32_chunks` for an example. +pub fn fill_via_u64_chunks(src: &mut [u64], dest: &mut [u8]) -> (usize, usize) { + fill_via_chunks!(src, dest, 8) +} + +/// Implement `next_u32` via `fill_bytes`, little-endian order. +pub fn next_u32_via_fill(rng: &mut R) -> u32 { + impl_uint_from_fill!(rng, u32, 4) +} + +/// Implement `next_u64` via `fill_bytes`, little-endian order. +pub fn next_u64_via_fill(rng: &mut R) -> u64 { + impl_uint_from_fill!(rng, u64, 8) +} + +/// Implement `next_u128` via `fill_bytes`, little-endian order. +#[cfg(feature = "i128_support")] +pub fn next_u128_via_fill(rng: &mut R) -> u128 { + impl_uint_from_fill!(rng, u128, 16) +} + +/// Implement `fill_bytes` via `try_fill` with implicit error handling. +pub fn fill_via_try_fill(rng: &mut R, dest: &mut [u8]) { + const WAIT_DUR_MS: u32 = 100; + const MAX_WAIT: u32 = (1 * 60 * 1000) / WAIT_DUR_MS; + const TRANSIENT_STEP: u32 = MAX_WAIT / 8; + let mut err_count = 0; + + loop { + if let Err(e) = rng.try_fill(dest) { + if e.kind.should_retry() { + if err_count > MAX_WAIT { + // TODO: log details & cause? + panic!("Too many RNG errors or timeout; last error: {}", e.msg()); + } + + if e.kind.should_wait() { + #[cfg(feature="std")]{ + let dur = ::std::time::Duration::from_millis(WAIT_DUR_MS as u64); + ::std::thread::sleep(dur); + } + err_count += 1; + } else { + err_count += TRANSIENT_STEP; + } + + continue; + } + + // TODO: log details & cause? + panic!("Fatal RNG error: {}", e.msg()); + } + + break; + } +} + +// TODO: implement tests for the above diff --git a/rand_core/src/lib.rs b/rand_core/src/lib.rs new file mode 100644 index 00000000000..ce8c15d6bd0 --- /dev/null +++ b/rand_core/src/lib.rs @@ -0,0 +1,464 @@ +// Copyright 2013-2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Random number generation traits +//! +//! This crate is mainly of interest to crates publishing implementations of +//! `Rng`. Other users are encouraged to use the +//! [rand crate](https://crates.io/crates/rand) instead. +//! +//! `Rng` is the core trait implemented by algorithmic pseudo-random number +//! generators and external random-number sources. +//! +//! `SeedableRng` is an extension trait for construction from fixed seeds and +//! other random number generators. +//! +//! `Error` is provided for error-handling. It is safe to use in `no_std` +//! environments. +//! +//! The `impls` sub-module includes a few small functions to assist +//! implementation of `Rng`. Since this module is only of interest to `Rng` +//! implementors, it is not re-exported from `rand`. + +#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk.png", + html_favicon_url = "https://www.rust-lang.org/favicon.ico", + html_root_url = "https://docs.rs/rand/0.3")] + +#![deny(missing_debug_implementations)] + +#![cfg_attr(not(feature="std"), no_std)] +#![cfg_attr(feature = "i128_support", feature(i128_type))] + +// We need to use several items from "core" for no_std support. +#[cfg(feature="std")] +extern crate core; + +#[cfg(feature="std")] +use std::error::Error as stdError; + +use core::{fmt, slice, mem}; +use core::default::Default; +use core::convert::AsMut; + +pub mod impls; + + +/// A random number generator (not necessarily suitable for cryptography). +/// +/// "Random" number generators can be categorised multiple ways: +/// +/// * *True* and *apparent* random number generators: *true* generators must +/// depend on some phenomenon which is actually random, such as atomic decay +/// or photon polarisation (note: bias may still be present, and it is +/// possible that these phenomena are in fact dependent on other unseen +/// parameters), while *apparent* random numbers are merely unpredictable. +/// * *Algorithmic* and *external* generators: *algorithmic generators* can +/// never produce *true random numbers* but can still yield hard-to-predict +/// output. External generators may or may not use *true random sources*. +/// +/// *Algorithmic generators* are also known as *psuedo-random number +/// generators* or *PRNGs*. +/// +/// *Algorithmic* generators are necessarily deterministic: if two +/// generators using the same algorithm are initialised with the same seed, +/// they will necessarily produce the same output. PRNGs should normally +/// implement the `SeedableRng` trait, allowing this. To be reproducible +/// across platforms, conversion of output from one type to another should +/// pay special attention to endianness (we generally choose LE, e.g. +/// `fn next_u32(&mut self) -> u32 { self.next_u64() as u32 }`). +/// * *Cryptographically secure*, *trivially predictable*, and various states +/// in between: if, after observing some output from an algorithmic +/// generator future output of the generator can be predicted, then it is +/// insecure. +/// +/// Note that all algorithmic generators eventually cycle, +/// returning to previous internal state and repeating all output, but in +/// good generators the period is so long that it is never reached (e.g. our +/// implementation of ChaCha would produce 2^134 bytes of output before +/// cycling, although cycles are not always so long). Predictability may not +/// be a problem for games, simulations and some randomised algorithms, +/// but unpredictability is essential in cryptography. +/// +/// The `Rng` trait can be used for all the above types of generators. If using +/// random numbers for cryptography prefer to use numbers pulled from the OS +/// directly (`OsRng`) or at least use a generator implementing `CryptoRng`. +/// For applications where performance is important and unpredictability is +/// less critical but still somewhat important (e.g. to prevent a DoS attack), +/// one may prefer to use a `CryptoRng` generator or may consider other "hard +/// to predict" but not cryptographically proven generators. +/// +/// PRNGs are usually infallible, while external generators may fail. Since +/// errors are rare and may be hard for the user to handle, most of the output +/// functions do not return a `Result`; byte output can however be retrieved +/// with `try_fill` which allows for the usual error handling. If the random +/// source implements other output functions in terms of `try_fill` (see +/// `impls::fill_via_try_fill`), then some errors will be handled implicitly, +/// so long as not too many retries are needed (specifically: `NotReady` is +/// handled by waiting up to 1 minute, and `Transient` is handled by retrying +/// a few times). In some applications it may make sense to ensure your entropy +/// source (e.g. `OsRng`) is ready by calling `try_fill` explicitly before +/// using any of the other output functions. +pub trait Rng { + /// Return the next random u32. + fn next_u32(&mut self) -> u32; + + /// Return the next random u64. + fn next_u64(&mut self) -> u64; + + /// Return the next random u128. + #[cfg(feature = "i128_support")] + fn next_u128(&mut self) -> u128; + + /// Fill `dest` entirely with random data. + /// + /// This method does *not* have any requirement on how much of the + /// generated random number stream is consumed; e.g. `fill_bytes_via_u64` + /// implementation uses `next_u64` thus consuming 8 bytes even when only + /// 1 is required. A different implementation might use `next_u32` and + /// only consume 4 bytes; *however* any change affecting *reproducibility* + /// of output must be considered a breaking change. + fn fill_bytes(&mut self, dest: &mut [u8]); + + /// Fill `dest` entirely with random data. + /// + /// If a RNG can encounter an error, this is the only method that reports + /// it. The other methods either handle the error, or panic. + /// + /// This method does *not* have any requirement on how much of the + /// generated random number stream is consumed; e.g. `try_fill_via_u64` + /// implementation uses `next_u64` thus consuming 8 bytes even when only + /// 1 is required. A different implementation might use `next_u32` and + /// only consume 4 bytes; *however* any change affecting *reproducibility* + /// of output must be considered a breaking change. + fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error>; +} + +/// A marker trait for an `Rng` which may be considered for use in +/// cryptography. +/// +/// *Cryptographically secure generators*, also known as *CSPRNGs*, should +/// satisfy an additional properties over other generators: given the first +/// *k* bits of an algorithm's output +/// sequence, it should not be possible using polynomial-time algorithms to +/// predict the next bit with probability significantly greater than 50%. +/// +/// Some generators may satisfy an additional property, however this is not +/// required: if the CSPRNG's state is revealed, it should not be +/// computationally-feasible to reconstruct output prior to this. Some other +/// generators allow backwards-computation and are consided *reversible*. +/// +/// Note that this trait is provided for guidance only and cannot guarantee +/// suitability for cryptographic applications. In general it should only be +/// implemented for well-reviewed code implementing well-regarded algorithms. +pub trait CryptoRng: Rng {} + + +impl<'a, R: Rng+?Sized> Rng for &'a mut R { + fn next_u32(&mut self) -> u32 { + (**self).next_u32() + } + + fn next_u64(&mut self) -> u64 { + (**self).next_u64() + } + + #[cfg(feature = "i128_support")] + fn next_u128(&mut self) -> u128 { + (**self).next_u128() + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + (**self).fill_bytes(dest) + } + + fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { + (**self).try_fill(dest) + } +} + +#[cfg(feature="std")] +impl Rng for Box { + fn next_u32(&mut self) -> u32 { + (**self).next_u32() + } + + fn next_u64(&mut self) -> u64 { + (**self).next_u64() + } + + #[cfg(feature = "i128_support")] + fn next_u128(&mut self) -> u128 { + (**self).next_u128() + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + (**self).fill_bytes(dest) + } + + fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { + (**self).try_fill(dest) + } +} + + +mod private { + pub trait Sealed {} + impl Sealed for [u8; 4] {} + impl Sealed for [u8; 8] {} + impl Sealed for [u8; 12] {} + impl Sealed for [u8; 16] {} + impl Sealed for [u8; 24] {} + impl Sealed for [u8; 32] {} +} + +/// The seed type is restricted to these types. This trait is sealed to prevent +/// user-extension. +/// +/// Use of byte-arrays avoids endianness issues. We may extend this to allow +/// byte arrays of other lengths in the future. +pub trait SeedRestriction: private::Sealed + Default + AsMut<[u8]> {} +impl SeedRestriction for S where S: private::Sealed + Default + AsMut<[u8]> {} + +/// A random number generator that can be explicitly seeded. +/// +/// There are two subtle differences between `from_rng` and`from_seed` (beyond +/// the obvious): first, that `from_rng` has no reproducibility requirement, and +/// second, that `from_rng` may directly fill internal states larger than +/// `SeedableRng::Seed`, where `from_seed` may need some extra step to expand +/// the input. +pub trait SeedableRng: Sized { + /// Seed type. + type Seed: SeedRestriction; + + /// Create a new PRNG using the given seed. + /// + /// Each PRNG should implement this. + /// + /// Reproducibility is required; that is, a fixed PRNG seeded using this + /// function with a fixed seed should produce the same sequence of output + /// today, and in the future. PRNGs not able to satisfy this should make + /// clear notes in their documentation. It is however not required that this + /// function yield the same state as a reference implementation of the PRNG + /// given equivalent seed; if necessary another constructor should be used. + /// + /// It may be expected that bits in the seed are well distributed, i.e. that + /// values like 0, 1 and (size - 1) are unlikely. Users with poorly + /// distributed input should use `from_hashable`. + fn from_seed(seed: Self::Seed) -> Self; + + /// Create a new PRNG seeded from another `Rng`. + /// + /// Seeding from a cryptographic generator should be fine. On the other + /// hand, seeding a simple numerical generator from another of the same + /// type sometimes has serious side effects such as effectively cloning the + /// generator. + fn from_rng(mut rng: R) -> Result { + let mut seed = Self::Seed::default(); + let size = mem::size_of::() as usize; + unsafe { + let ptr = seed.as_mut().as_mut_ptr() as *mut u8; + let slice = slice::from_raw_parts_mut(ptr, size); + rng.try_fill(slice)?; + } + Ok(Self::from_seed(seed)) + } +} + + +/// Error kind which can be matched over. +#[derive(PartialEq, Eq, Debug, Copy, Clone)] +pub enum ErrorKind { + /// Permanent failure: likely not recoverable without user action. + Unavailable, + /// Temporary failure: recommended to retry a few times, but may also be + /// irrecoverable. + Transient, + /// Not ready yet: recommended to try again a little later. + NotReady, + /// Uncategorised error + Other, + #[doc(hidden)] + __Nonexhaustive, +} + +impl ErrorKind { + /// True if this kind of error may resolve itself on retry. + /// + /// See also `should_wait()`. + pub fn should_retry(self) -> bool { + match self { + ErrorKind::Transient | ErrorKind::NotReady => true, + _ => false, + } + } + + /// True if we should retry but wait before retrying + /// + /// This implies `should_retry()` is true. + pub fn should_wait(self) -> bool { + self == ErrorKind::NotReady + } + + /// A description of this error kind + pub fn description(self) -> &'static str { + match self { + ErrorKind::Unavailable => "permanent failure or unavailable", + ErrorKind::Transient => "transient failure", + ErrorKind::NotReady => "not ready yet", + ErrorKind::Other => "uncategorised", + ErrorKind::__Nonexhaustive => unreachable!(), + } + } +} + +/// Error type of random number generators +/// +/// This embeds an `ErrorKind` which can be matched over, a *message* to tell +/// users what happened, and optionally a *cause* (which allows chaining back +/// to the original error). +/// +/// The cause is omitted in `no_std` mode (see `Error::new` for details). +#[derive(Debug)] +pub struct Error { + /// Error kind. This enum is included to aid handling of errors. + pub kind: ErrorKind, + msg: &'static str, + #[cfg(feature="std")] + cause: Option>, +} + +impl Error { + /// Create a new instance, with specified kind and a message. + pub fn new(kind: ErrorKind, msg: &'static str) -> Self { + #[cfg(feature="std")] { + Self { kind, msg, cause: None } + } + #[cfg(not(feature="std"))] { + Self { kind, msg } + } + } + /// Create a new instance, with specified kind, message, and a + /// chained cause. + /// + /// Note: `stdError` is an alias for `std::error::Error`. + /// + /// If not targetting `std` (i.e. `no_std`), this function is replaced by + /// another with the same prototype, except that there are no bounds on the + /// type `E` (because both `Box` and `stdError` are unavailable), and the + /// `cause` is ignored. + #[cfg(feature="std")] + pub fn with_cause(kind: ErrorKind, msg: &'static str, cause: E) -> Self + where E: Into> + { + Self { kind, msg, cause: Some(cause.into()) } + } + /// Create a new instance, with specified kind, message, and a + /// chained cause. + /// + /// In `no_std` mode the *cause* is ignored. + #[cfg(not(feature="std"))] + pub fn with_cause(kind: ErrorKind, msg: &'static str, _cause: E) -> Self { + Self { kind, msg } + } + + /// Get the error message + pub fn msg(&self) -> &'static str { + self.msg + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "RNG error [{}]: {}", self.kind.description(), self.msg()) + } +} + +#[cfg(feature="std")] +impl stdError for Error { + fn description(&self) -> &str { + self.msg + } + + fn cause(&self) -> Option<&stdError> { + self.cause.as_ref().map(|e| e.as_ref() as &stdError) + } +} + +/// Little-Endian utilities +/// +/// Little-Endian order has been chosen for internal usage; this makes some +/// useful functions available. +pub mod le { + use core::ptr; + + /// Helper function to turn a slice into an array reference + + /// Read a `u32` from a byte sequence, in litte-endian order + /// + /// Consider usage with the `arrayref` crate. + pub fn read_u32(bytes: &[u8; 4]) -> u32 { + unsafe{ *(bytes as *const [u8; 4] as *const u32) }.to_le() + } + + /// Read a `u64` from a byte sequence, in litte-endian order + /// + /// Consider usage with the `arrayref` crate. + pub fn read_u64(bytes: &[u8; 8]) -> u64 { + unsafe{ *(bytes as *const [u8; 8] as *const u64) }.to_le() + } + + macro_rules! read_slice { + ($src:expr, $dst:expr, $size:expr, $which:ident) => {{ + assert_eq!($src.len(), $size * $dst.len()); + + unsafe { + ptr::copy_nonoverlapping( + $src.as_ptr(), + $dst.as_mut_ptr() as *mut u8, + $src.len()); + } + for v in $dst.iter_mut() { + *v = v.$which(); + } + }}; + } + + /// Reads unsigned 32 bit integers from `src` into `dst`. + /// Borrowed from the `byteorder` crate. + #[inline] + pub fn read_u32_into(src: &[u8], dst: &mut [u32]) { + read_slice!(src, dst, 4, to_le); + } + + /// Reads unsigned 64 bit integers from `src` into `dst`. + /// Borrowed from the `byteorder` crate. + #[inline] + pub fn read_u64_into(src: &[u8], dst: &mut [u64]) { + read_slice!(src, dst, 8, to_le); + } + + #[test] + fn test_read() { + assert_eq!(read_u32(&[1, 2, 3, 4]), 0x04030201); + assert_eq!(read_u64(&[1, 2, 3, 4, 5, 6, 7, 8]), 0x0807060504030201); + + let bytes = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; + + let mut buf = [0u32; 4]; + read_u32_into(&bytes, &mut buf); + assert_eq!(buf[0], 0x04030201); + assert_eq!(buf[3], 0x100F0E0D); + + let mut buf = [0u64; 2]; + read_u64_into(&bytes, &mut buf); + assert_eq!(buf[0], 0x0807060504030201); + assert_eq!(buf[1], 0x100F0E0D0C0B0A09); + } +} diff --git a/src/distributions/default.rs b/src/distributions/default.rs new file mode 100644 index 00000000000..fab58fae085 --- /dev/null +++ b/src/distributions/default.rs @@ -0,0 +1,81 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Generic value creation + +use Rng; +use distributions::Distribution; +use distributions::uniform::{Uniform, Uniform01, Codepoint}; + +/// A generic random value distribution. Generates values using what appears to +/// be "the best" distribution for each type, but ultimately the choice is arbitrary. +/// +/// Makes use of the following distributions: +/// +/// * [`Uniform`] for integer types +/// * [`Uniform01`] for floating point types +/// * [`Codepoint`] for `char` +/// +/// TODO: link +#[derive(Debug)] +pub struct Default; + +// ----- implementations ----- + +impl Distribution for Default where Uniform: Distribution{ + fn sample(&self, rng: &mut R) -> T { + Uniform.sample(rng) + } +} + +// FIXME: https://github.com/rust-lang/rust/issues/23341 +// impl Distribution for Default where Uniform01: Distribution{ +// fn sample(&self, rng: &mut R) -> T { +// Uniform01.sample(rng) +// } +// } +// workaround above issue: +impl Distribution for Default { + fn sample(&self, rng: &mut R) -> f32 { + Uniform01.sample(rng) + } +} +impl Distribution for Default { + fn sample(&self, rng: &mut R) -> f64 { + Uniform01.sample(rng) + } +} + +impl Distribution for Default { + fn sample(&self, rng: &mut R) -> char { + Codepoint.sample(rng) + } +} + + +#[cfg(test)] +mod tests { + use {Rng, Sample, thread_rng}; + use distributions::{Default}; + + #[test] + fn test_types() { + let rng: &mut Rng = &mut thread_rng(); + + rng.sample::(Default); + rng.sample::(Default); + rng.sample::(Default); + rng.sample::(Default); + #[cfg(feature = "i128_support")] + rng.sample::(Default); + rng.sample::(Default); + rng.sample::(Default); + } +} diff --git a/src/distributions/exponential.rs b/src/distributions/exponential.rs index c3c924c6b7e..48a988dee3f 100644 --- a/src/distributions/exponential.rs +++ b/src/distributions/exponential.rs @@ -10,10 +10,10 @@ //! The exponential distribution. -use {Rng, Rand}; -use distributions::{ziggurat, ziggurat_tables, Sample, IndependentSample}; +use {Rng}; +use distributions::{ziggurat, ziggurat_tables, Distribution, Uniform01}; -/// A wrapper around an `f64` to generate Exp(1) random numbers. +/// Generates Exp(1) random numbers. /// /// See `Exp` for the general exponential distribution. /// @@ -29,32 +29,28 @@ use distributions::{ziggurat, ziggurat_tables, Sample, IndependentSample}; /// # Example /// /// ```rust -/// use rand::distributions::exponential::Exp1; +/// use rand::distributions::exponential::exp1; /// -/// let Exp1(x) = rand::random(); +/// let x = exp1(&mut rand::thread_rng()); /// println!("{}", x); /// ``` -#[derive(Clone, Copy, Debug)] -pub struct Exp1(pub f64); - // This could be done via `-rng.gen::().ln()` but that is slower. -impl Rand for Exp1 { +#[inline] +pub fn exp1(rng: &mut R) -> f64 { #[inline] - fn rand(rng: &mut R) -> Exp1 { - #[inline] - fn pdf(x: f64) -> f64 { - (-x).exp() - } - #[inline] - fn zero_case(rng: &mut R, _u: f64) -> f64 { - ziggurat_tables::ZIG_EXP_R - rng.gen::().ln() - } - - Exp1(ziggurat(rng, false, - &ziggurat_tables::ZIG_EXP_X, - &ziggurat_tables::ZIG_EXP_F, - pdf, zero_case)) + fn pdf(x: f64) -> f64 { + (-x).exp() } + #[inline] + fn zero_case(rng: &mut R, _u: f64) -> f64 { + let x: f64 = Uniform01.sample(rng); + ziggurat_tables::ZIG_EXP_R - x.ln() + } + + (ziggurat(rng, false, + &ziggurat_tables::ZIG_EXP_X, + &ziggurat_tables::ZIG_EXP_F, + pdf, zero_case)) } /// The exponential distribution `Exp(lambda)`. @@ -65,10 +61,10 @@ impl Rand for Exp1 { /// # Example /// /// ```rust -/// use rand::distributions::{Exp, IndependentSample}; +/// use rand::distributions::{Exp, Distribution}; /// /// let exp = Exp::new(2.0); -/// let v = exp.ind_sample(&mut rand::thread_rng()); +/// let v = exp.sample(&mut rand::thread_rng()); /// println!("{} is from a Exp(2) distribution", v); /// ``` #[derive(Clone, Copy, Debug)] @@ -87,28 +83,23 @@ impl Exp { } } -impl Sample for Exp { - fn sample(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) } -} -impl IndependentSample for Exp { - fn ind_sample(&self, rng: &mut R) -> f64 { - let Exp1(n) = rng.gen::(); - n * self.lambda_inverse +impl Distribution for Exp { + fn sample(&self, rng: &mut R) -> f64 { + exp1(rng) * self.lambda_inverse } } #[cfg(test)] mod test { - use distributions::{Sample, IndependentSample}; + use distributions::{Distribution}; use super::Exp; #[test] fn test_exp() { - let mut exp = Exp::new(10.0); + let exp = Exp::new(10.0); let mut rng = ::test::rng(); for _ in 0..1000 { assert!(exp.sample(&mut rng) >= 0.0); - assert!(exp.ind_sample(&mut rng) >= 0.0); } } #[test] diff --git a/src/distributions/gamma.rs b/src/distributions/gamma.rs index 28064958731..5ddbc1cc6c5 100644 --- a/src/distributions/gamma.rs +++ b/src/distributions/gamma.rs @@ -15,9 +15,9 @@ use self::GammaRepr::*; use self::ChiSquaredRepr::*; -use {Rng, Open01}; -use super::normal::StandardNormal; -use super::{IndependentSample, Sample, Exp}; +use {Rng}; +use distributions::normal::standard_normal; +use distributions::{Distribution, Exp, Open01}; /// The Gamma distribution `Gamma(shape, scale)` distribution. /// @@ -38,10 +38,10 @@ use super::{IndependentSample, Sample, Exp}; /// # Example /// /// ```rust -/// use rand::distributions::{IndependentSample, Gamma}; +/// use rand::distributions::{Distribution, Gamma}; /// /// let gamma = Gamma::new(2.0, 5.0); -/// let v = gamma.ind_sample(&mut rand::thread_rng()); +/// let v = gamma.sample(&mut rand::thread_rng()); /// println!("{} is from a Gamma(2, 5) distribution", v); /// ``` /// @@ -133,43 +133,34 @@ impl GammaLargeShape { } } -impl Sample for Gamma { - fn sample(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) } -} -impl Sample for GammaSmallShape { - fn sample(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) } -} -impl Sample for GammaLargeShape { - fn sample(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) } -} -impl IndependentSample for Gamma { - fn ind_sample(&self, rng: &mut R) -> f64 { +impl Distribution for Gamma { + fn sample(&self, rng: &mut R) -> f64 { match self.repr { - Small(ref g) => g.ind_sample(rng), - One(ref g) => g.ind_sample(rng), - Large(ref g) => g.ind_sample(rng), + Small(ref g) => g.sample(rng), + One(ref g) => g.sample(rng), + Large(ref g) => g.sample(rng), } } } -impl IndependentSample for GammaSmallShape { - fn ind_sample(&self, rng: &mut R) -> f64 { - let Open01(u) = rng.gen::>(); +impl Distribution for GammaSmallShape { + fn sample(&self, rng: &mut R) -> f64 { + let u: f64 = Open01.sample(rng); - self.large_shape.ind_sample(rng) * u.powf(self.inv_shape) + self.large_shape.sample(rng) * u.powf(self.inv_shape) } } -impl IndependentSample for GammaLargeShape { - fn ind_sample(&self, rng: &mut R) -> f64 { +impl Distribution for GammaLargeShape { + fn sample(&self, rng: &mut R) -> f64 { loop { - let StandardNormal(x) = rng.gen::(); + let x = standard_normal(rng); let v_cbrt = 1.0 + self.c * x; if v_cbrt <= 0.0 { // a^3 <= 0 iff a <= 0 continue } let v = v_cbrt * v_cbrt * v_cbrt; - let Open01(u) = rng.gen::>(); + let u: f64 = Open01.sample(rng); let x_sqr = x * x; if u < 1.0 - 0.0331 * x_sqr * x_sqr || @@ -191,10 +182,10 @@ impl IndependentSample for GammaLargeShape { /// # Example /// /// ```rust -/// use rand::distributions::{ChiSquared, IndependentSample}; +/// use rand::distributions::{ChiSquared, Distribution}; /// /// let chi = ChiSquared::new(11.0); -/// let v = chi.ind_sample(&mut rand::thread_rng()); +/// let v = chi.sample(&mut rand::thread_rng()); /// println!("{} is from a χ²(11) distribution", v) /// ``` #[derive(Clone, Copy, Debug)] @@ -224,18 +215,15 @@ impl ChiSquared { ChiSquared { repr: repr } } } -impl Sample for ChiSquared { - fn sample(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) } -} -impl IndependentSample for ChiSquared { - fn ind_sample(&self, rng: &mut R) -> f64 { +impl Distribution for ChiSquared { + fn sample(&self, rng: &mut R) -> f64 { match self.repr { DoFExactlyOne => { // k == 1 => N(0,1)^2 - let StandardNormal(norm) = rng.gen::(); + let norm = standard_normal(rng); norm * norm } - DoFAnythingElse(ref g) => g.ind_sample(rng) + DoFAnythingElse(ref g) => g.sample(rng) } } } @@ -249,10 +237,10 @@ impl IndependentSample for ChiSquared { /// # Example /// /// ```rust -/// use rand::distributions::{FisherF, IndependentSample}; +/// use rand::distributions::{FisherF, Distribution}; /// /// let f = FisherF::new(2.0, 32.0); -/// let v = f.ind_sample(&mut rand::thread_rng()); +/// let v = f.sample(&mut rand::thread_rng()); /// println!("{} is from an F(2, 32) distribution", v) /// ``` #[derive(Clone, Copy, Debug)] @@ -278,12 +266,9 @@ impl FisherF { } } } -impl Sample for FisherF { - fn sample(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) } -} -impl IndependentSample for FisherF { - fn ind_sample(&self, rng: &mut R) -> f64 { - self.numer.ind_sample(rng) / self.denom.ind_sample(rng) * self.dof_ratio +impl Distribution for FisherF { + fn sample(&self, rng: &mut R) -> f64 { + self.numer.sample(rng) / self.denom.sample(rng) * self.dof_ratio } } @@ -293,10 +278,10 @@ impl IndependentSample for FisherF { /// # Example /// /// ```rust -/// use rand::distributions::{StudentT, IndependentSample}; +/// use rand::distributions::{StudentT, Distribution}; /// /// let t = StudentT::new(11.0); -/// let v = t.ind_sample(&mut rand::thread_rng()); +/// let v = t.sample(&mut rand::thread_rng()); /// println!("{} is from a t(11) distribution", v) /// ``` #[derive(Clone, Copy, Debug)] @@ -316,46 +301,40 @@ impl StudentT { } } } -impl Sample for StudentT { - fn sample(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) } -} -impl IndependentSample for StudentT { - fn ind_sample(&self, rng: &mut R) -> f64 { - let StandardNormal(norm) = rng.gen::(); - norm * (self.dof / self.chi.ind_sample(rng)).sqrt() +impl Distribution for StudentT { + fn sample(&self, rng: &mut R) -> f64 { + let norm = standard_normal(rng); + norm * (self.dof / self.chi.sample(rng)).sqrt() } } #[cfg(test)] mod test { - use distributions::{Sample, IndependentSample}; + use distributions::{Distribution}; use super::{ChiSquared, StudentT, FisherF}; #[test] fn test_chi_squared_one() { - let mut chi = ChiSquared::new(1.0); + let chi = ChiSquared::new(1.0); let mut rng = ::test::rng(); for _ in 0..1000 { chi.sample(&mut rng); - chi.ind_sample(&mut rng); } } #[test] fn test_chi_squared_small() { - let mut chi = ChiSquared::new(0.5); + let chi = ChiSquared::new(0.5); let mut rng = ::test::rng(); for _ in 0..1000 { chi.sample(&mut rng); - chi.ind_sample(&mut rng); } } #[test] fn test_chi_squared_large() { - let mut chi = ChiSquared::new(30.0); + let chi = ChiSquared::new(30.0); let mut rng = ::test::rng(); for _ in 0..1000 { chi.sample(&mut rng); - chi.ind_sample(&mut rng); } } #[test] @@ -366,21 +345,19 @@ mod test { #[test] fn test_f() { - let mut f = FisherF::new(2.0, 32.0); + let f = FisherF::new(2.0, 32.0); let mut rng = ::test::rng(); for _ in 0..1000 { f.sample(&mut rng); - f.ind_sample(&mut rng); } } #[test] fn test_t() { - let mut t = StudentT::new(11.0); + let t = StudentT::new(11.0); let mut rng = ::test::rng(); for _ in 0..1000 { t.sample(&mut rng); - t.ind_sample(&mut rng); } } } diff --git a/src/distributions/mod.rs b/src/distributions/mod.rs index 5de8efb9c47..076ab52c6cb 100644 --- a/src/distributions/mod.rs +++ b/src/distributions/mod.rs @@ -10,18 +10,17 @@ //! Sampling from random distributions. //! -//! This is a generalization of `Rand` to allow parameters to control the -//! exact properties of the generated values, e.g. the mean and standard -//! deviation of a normal distribution. The `Sample` trait is the most -//! general, and allows for generating values that change some state -//! internally. The `IndependentSample` trait is for generating values -//! that do not need to record state. +//! A distribution may have internal state describing the distribution of +//! generated values; for example `Range` needs to know its upper and lower +//! bounds. Distributions use the `Distribution` trait to yield values: call +//! `distr.sample(&mut rng)` to get a random variable. -use core::marker; - -use {Rng, Rand}; +use Rng; +pub use self::default::Default; +pub use self::uniform::{Uniform, Uniform01, Open01, Closed01, Codepoint, Alphanumeric}; pub use self::range::Range; + #[cfg(feature="std")] pub use self::gamma::{Gamma, ChiSquared, FisherF, StudentT}; #[cfg(feature="std")] @@ -29,7 +28,15 @@ pub use self::normal::{Normal, LogNormal}; #[cfg(feature="std")] pub use self::exponential::Exp; +use Sample; + +mod default; +mod uniform; +#[cfg(feature="std")] +mod ziggurat_tables; + pub mod range; + #[cfg(feature="std")] pub mod gamma; #[cfg(feature="std")] @@ -37,178 +44,59 @@ 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 { - /// Generate a random value of `Support`, using `rng` as the - /// source of randomness. - fn sample(&mut self, rng: &mut R) -> Support; -} - -/// `Sample`s that do not require keeping track of state. -/// -/// Since no state is recorded, each sample is (statistically) -/// independent of all others, assuming the `Rng` used has this -/// property. -// FIXME maybe having this separate is overkill (the only reason is to -// take &self rather than &mut self)? or maybe this should be the -// trait called `Sample` and the other should be `DependentSample`. -pub trait IndependentSample: Sample { - /// Generate a random value. - fn ind_sample(&self, &mut R) -> Support; -} - -/// A wrapper for generating types that implement `Rand` via the -/// `Sample` & `IndependentSample` traits. -#[derive(Debug)] -pub struct RandSample { - _marker: marker::PhantomData Sup>, -} - -impl Copy for RandSample {} -impl Clone for RandSample { - fn clone(&self) -> Self { *self } -} - -impl Sample for RandSample { - fn sample(&mut self, rng: &mut R) -> Sup { self.ind_sample(rng) } -} - -impl IndependentSample for RandSample { - fn ind_sample(&self, rng: &mut R) -> Sup { - rng.gen() - } -} - -impl RandSample { - pub fn new() -> RandSample { - RandSample { _marker: marker::PhantomData } - } -} -/// A value with a particular weight for use with `WeightedChoice`. -#[derive(Copy, Clone, Debug)] -pub struct Weighted { - /// The numerical weight of this item - pub weight: u32, - /// The actual item which is being weighted - pub item: T, -} - -/// A distribution that selects from a finite collection of weighted items. -/// -/// Each item has an associated weight that influences how likely it -/// is to be chosen: higher weight is more likely. -/// -/// The `Clone` restriction is a limitation of the `Sample` and -/// `IndependentSample` traits. Note that `&T` is (cheaply) `Clone` for -/// all `T`, as is `u32`, so one can store references or indices into -/// another vector. +/// Return a bool with a 1 in n chance of being true +/// +/// This uses [`range`] internally, so for repeated uses it would be faster to +/// create a `Range` distribution and test its samples: +/// `range.sample(rng) == 0`. /// /// # Example /// /// ```rust -/// use rand::distributions::{Weighted, WeightedChoice, IndependentSample}; +/// use rand::distributions::weighted_bool; /// -/// let mut items = vec!(Weighted { weight: 2, item: 'a' }, -/// Weighted { weight: 4, item: 'b' }, -/// Weighted { weight: 1, item: 'c' }); -/// let wc = WeightedChoice::new(&mut items); /// let mut rng = rand::thread_rng(); -/// for _ in 0..16 { -/// // on average prints 'a' 4 times, 'b' 8 and 'c' twice. -/// println!("{}", wc.ind_sample(&mut rng)); -/// } +/// println!("{}", weighted_bool(3, &mut rng)); /// ``` -#[derive(Debug)] -pub struct WeightedChoice<'a, T:'a> { - items: &'a mut [Weighted], - weight_range: Range +pub fn weighted_bool(n: u32, rng: &mut R) -> bool { + n <= 1 || rng.gen_range(0, n) == 0 } -impl<'a, T: Clone> WeightedChoice<'a, T> { - /// Create a new `WeightedChoice`. - /// - /// Panics if: - /// - /// - `items` is empty - /// - the total weight is 0 - /// - the total weight is larger than a `u32` can contain. - pub fn new(items: &'a mut [Weighted]) -> WeightedChoice<'a, T> { - // strictly speaking, this is subsumed by the total weight == 0 case - assert!(!items.is_empty(), "WeightedChoice::new called with no items"); - - let mut running_total: u32 = 0; - - // we convert the list from individual weights to cumulative - // weights so we can binary search. This *could* drop elements - // with weight == 0 as an optimisation. - for item in items.iter_mut() { - running_total = match running_total.checked_add(item.weight) { - Some(n) => n, - None => panic!("WeightedChoice::new called with a total weight \ - larger than a u32 can contain") - }; - - item.weight = running_total; - } - assert!(running_total != 0, "WeightedChoice::new called with a total weight of 0"); +/// Types (distributions) that can be used to create a random instance of `T`. +pub trait Distribution { + /// Generate a random value of `T`, using `rng` as the + /// source of randomness. + fn sample(&self, rng: &mut R) -> T; +} - WeightedChoice { - items: items, - // we're likely to be generating numbers in this range - // relatively often, so might as well cache it - weight_range: Range::new(0, running_total) - } +impl<'a, T, D: Distribution> Distribution for &'a D { + fn sample(&self, rng: &mut R) -> T { + (*self).sample(rng) } } -impl<'a, T: Clone> Sample for WeightedChoice<'a, T> { - fn sample(&mut self, rng: &mut R) -> T { self.ind_sample(rng) } +/// Trait for sampling values from the `Default` distribution. +/// +/// This is mostly a shim around `Default` for backwards compatibility; the +/// implementation is simply `Default.sample(rng)`. +/// +/// # Example +/// ```rust +/// use rand::{Rand, thread_rng}; +/// +/// let mut rng = thread_rng(); +/// println!("Random byte: {}", u8::rand(&mut rng)); +/// ``` +pub trait Rand : Sized { + /// Generate a random value of the given type, using the `Default` + /// distribution. + fn rand(rng: &mut R) -> Self; } -impl<'a, T: Clone> IndependentSample for WeightedChoice<'a, T> { - fn ind_sample(&self, rng: &mut R) -> T { - // we want to find the first element that has cumulative - // weight > sample_weight, which we do by binary since the - // cumulative weights of self.items are sorted. - - // choose a weight in [0, total_weight) - let sample_weight = self.weight_range.ind_sample(rng); - - // short circuit when it's the first item - if sample_weight < self.items[0].weight { - return self.items[0].item.clone(); - } - - let mut idx = 0; - let mut modifier = self.items.len(); - - // now we know that every possibility has an element to the - // left, so we can just search for the last element that has - // cumulative weight <= sample_weight, then the next one will - // be "it". (Note that this greatest element will never be the - // last element of the vector, since sample_weight is chosen - // in [0, total_weight) and the cumulative weight of the last - // one is exactly the total weight.) - while modifier > 1 { - let i = idx + modifier / 2; - if self.items[i].weight <= sample_weight { - // we're small, so look to the right, but allow this - // exact element still. - idx = i; - // we need the `/ 2` to round up otherwise we'll drop - // the trailing elements when `modifier` is odd. - modifier += 1; - } else { - // otherwise we're too big, so go left. (i.e. do - // nothing) - } - modifier /= 2; - } - return self.items[idx + 1].item.clone(); +impl Rand for T where Default: Distribution { + fn rand(rng: &mut R) -> Self { + Default.sample(rng) } } @@ -229,7 +117,7 @@ impl<'a, T: Clone> IndependentSample for WeightedChoice<'a, T> { // size from force-inlining. #[cfg(feature="std")] #[inline(always)] -fn ziggurat( +fn ziggurat( rng: &mut R, symmetric: bool, x_tab: ziggurat_tables::ZigTable, @@ -237,28 +125,29 @@ fn ziggurat( mut pdf: P, mut zero_case: Z) -> f64 where P: FnMut(f64) -> f64, Z: FnMut(&mut R, f64) -> f64 { - const SCALE: f64 = (1u64 << 53) as f64; + use utils::FloatConversions; + loop { - // reimplement the f64 generation as an optimisation suggested - // by the Doornik paper: we have a lot of precision-space - // (i.e. there are 11 bits of the 64 of a u64 to use after - // creating a f64), so we might as well reuse some to save - // generating a whole extra random number. (Seems to be 15% - // faster.) - // - // This unfortunately misses out on the benefits of direct - // floating point generation if an RNG like dSMFT is - // used. (That is, such RNGs create floats directly, highly - // efficiently and overload next_f32/f64, so by not calling it - // this may be slower than it would be otherwise.) - // FIXME: investigate/optimise for the above. - let bits: u64 = rng.gen(); - let i = (bits & 0xff) as usize; - let f = (bits >> 11) as f64 / SCALE; + // As an optimisation convert the random u64 to a f64 using only + // 53 bits, as many as will fit in the float's fraction. + // Of the remaining 11 least significant bits we use 8 to construct `i`. + // This saves us generating a whole extra random number, while the added + // precision of using 64 bits for f64 does not buy us much. + // Because for some RNG's the least significant bits can be of lower + // statistical quality, we use bits 3..10 for i. + let bits: u64 = rng.sample(Uniform); // u is either U(-1, 1) or U(0, 1) depending on if this is a // symmetric distribution or not. - let u = if symmetric {2.0 * f - 1.0} else {f}; + // FIXME: the distribution is not open, but closed-open. + // Can that cause problems or a bias? + let u = if symmetric { + bits.closed_open11_fixed() + } else { + bits.closed_open01_fixed() + }; + let i = ((bits >> 3) & 0xff) as usize; + let x = u * x_tab[i]; let test_x = if symmetric { x.abs() } else {x}; @@ -271,139 +160,22 @@ fn ziggurat( return zero_case(rng, u); } // algebraically equivalent to f1 + DRanU()*(f0 - f1) < 1 - if f_tab[i + 1] + (f_tab[i] - f_tab[i + 1]) * rng.gen::() < pdf(x) { + if f_tab[i + 1] + (f_tab[i] - f_tab[i + 1]) * rng.sample::(Uniform01) < pdf(x) { return x; } } } #[cfg(test)] -mod tests { - - use {Rng, Rand}; - use super::{RandSample, WeightedChoice, Weighted, Sample, IndependentSample}; - - #[derive(PartialEq, Debug)] - struct ConstRand(usize); - impl Rand for ConstRand { - fn rand(_: &mut R) -> ConstRand { - ConstRand(0) - } - } - - // 0, 1, 2, 3, ... - struct CountingRng { i: u32 } - impl Rng for CountingRng { - fn next_u32(&mut self) -> u32 { - self.i += 1; - self.i - 1 - } - fn next_u64(&mut self) -> u64 { - self.next_u32() as u64 - } - } +mod test { + use {Rng, thread_rng}; + use distributions::{weighted_bool}; #[test] - fn test_rand_sample() { - let mut rand_sample = RandSample::::new(); - - assert_eq!(rand_sample.sample(&mut ::test::rng()), ConstRand(0)); - assert_eq!(rand_sample.ind_sample(&mut ::test::rng()), ConstRand(0)); - } - #[test] - fn test_weighted_choice() { - // this makes assumptions about the internal implementation of - // WeightedChoice, specifically: it doesn't reorder the items, - // it doesn't do weird things to the RNG (so 0 maps to 0, 1 to - // 1, internally; modulo a modulo operation). - - macro_rules! t { - ($items:expr, $expected:expr) => {{ - let mut items = $items; - let wc = WeightedChoice::new(&mut items); - let expected = $expected; - - let mut rng = CountingRng { i: 0 }; - - for &val in expected.iter() { - assert_eq!(wc.ind_sample(&mut rng), val) - } - }} - } - - t!(vec!(Weighted { weight: 1, item: 10}), [10]); - - // skip some - t!(vec!(Weighted { weight: 0, item: 20}, - Weighted { weight: 2, item: 21}, - Weighted { weight: 0, item: 22}, - Weighted { weight: 1, item: 23}), - [21,21, 23]); - - // different weights - t!(vec!(Weighted { weight: 4, item: 30}, - Weighted { weight: 3, item: 31}), - [30,30,30,30, 31,31,31]); - - // check that we're binary searching - // correctly with some vectors of odd - // length. - t!(vec!(Weighted { weight: 1, item: 40}, - Weighted { weight: 1, item: 41}, - Weighted { weight: 1, item: 42}, - Weighted { weight: 1, item: 43}, - Weighted { weight: 1, item: 44}), - [40, 41, 42, 43, 44]); - t!(vec!(Weighted { weight: 1, item: 50}, - Weighted { weight: 1, item: 51}, - Weighted { weight: 1, item: 52}, - Weighted { weight: 1, item: 53}, - Weighted { weight: 1, item: 54}, - Weighted { weight: 1, item: 55}, - Weighted { weight: 1, item: 56}), - [50, 51, 52, 53, 54, 55, 56]); - } - - #[test] - fn test_weighted_clone_initialization() { - let initial : Weighted = Weighted {weight: 1, item: 1}; - let clone = initial.clone(); - assert_eq!(initial.weight, clone.weight); - assert_eq!(initial.item, clone.item); - } - - #[test] #[should_panic] - fn test_weighted_clone_change_weight() { - let initial : Weighted = Weighted {weight: 1, item: 1}; - let mut clone = initial.clone(); - clone.weight = 5; - assert_eq!(initial.weight, clone.weight); - } - - #[test] #[should_panic] - fn test_weighted_clone_change_item() { - let initial : Weighted = Weighted {weight: 1, item: 1}; - let mut clone = initial.clone(); - clone.item = 5; - assert_eq!(initial.item, clone.item); - - } - - #[test] #[should_panic] - fn test_weighted_choice_no_items() { - WeightedChoice::::new(&mut []); - } - #[test] #[should_panic] - fn test_weighted_choice_zero_weight() { - WeightedChoice::new(&mut [Weighted { weight: 0, item: 0}, - Weighted { weight: 0, item: 1}]); - } - #[test] #[should_panic] - fn test_weighted_choice_weight_overflows() { - let x = ::std::u32::MAX / 2; // x + x + 2 is the overflow - WeightedChoice::new(&mut [Weighted { weight: x, item: 0 }, - Weighted { weight: 1, item: 1 }, - Weighted { weight: x, item: 2 }, - Weighted { weight: 1, item: 3 }]); + fn test_fn_weighted_bool() { + let mut r = thread_rng(); + assert_eq!(weighted_bool(0, &mut r), true); + let s: &mut Rng = &mut r; + assert_eq!(weighted_bool(1, s), true); } } diff --git a/src/distributions/normal.rs b/src/distributions/normal.rs index 280613d8595..92664b94a18 100644 --- a/src/distributions/normal.rs +++ b/src/distributions/normal.rs @@ -10,10 +10,10 @@ //! The normal and derived distributions. -use {Rng, Rand, Open01}; -use distributions::{ziggurat, ziggurat_tables, Sample, IndependentSample}; +use {Rng}; +use distributions::{ziggurat, ziggurat_tables, Distribution, Open01}; -/// A wrapper around an `f64` to generate N(0, 1) random numbers +/// Generates N(0, 1) random numbers /// (a.k.a. a standard normal, or Gaussian). /// /// See `Normal` for the general normal distribution. @@ -28,64 +28,59 @@ use distributions::{ziggurat, ziggurat_tables, Sample, IndependentSample}; /// # Example /// /// ```rust -/// use rand::distributions::normal::StandardNormal; +/// use rand::distributions::normal::standard_normal; /// -/// let StandardNormal(x) = rand::random(); +/// let x = standard_normal(&mut rand::thread_rng()); /// println!("{}", x); /// ``` -#[derive(Clone, Copy, Debug)] -pub struct StandardNormal(pub f64); - -impl Rand for StandardNormal { - fn rand(rng: &mut R) -> StandardNormal { - #[inline] - fn pdf(x: f64) -> f64 { - (-x*x/2.0).exp() - } - #[inline] - fn zero_case(rng: &mut R, u: f64) -> f64 { - // compute a random number in the tail by hand - - // strange initial conditions, because the loop is not - // do-while, so the condition should be true on the first - // run, they get overwritten anyway (0 < 1, so these are - // good). - let mut x = 1.0f64; - let mut y = 0.0f64; - - while -2.0 * y < x * x { - let Open01(x_) = rng.gen::>(); - let Open01(y_) = rng.gen::>(); - - x = x_.ln() / ziggurat_tables::ZIG_NORM_R; - y = y_.ln(); - } - - if u < 0.0 { x - ziggurat_tables::ZIG_NORM_R } else { ziggurat_tables::ZIG_NORM_R - x } +pub fn standard_normal(rng: &mut R) -> f64 { + #[inline] + fn pdf(x: f64) -> f64 { + (-x*x/2.0).exp() + } + #[inline] + fn zero_case(rng: &mut R, u: f64) -> f64 { + // compute a random number in the tail by hand + + // strange initial conditions, because the loop is not + // do-while, so the condition should be true on the first + // run, they get overwritten anyway (0 < 1, so these are + // good). + let mut x = 1.0f64; + let mut y = 0.0f64; + + while -2.0 * y < x * x { + let x_: f64 = Open01.sample(rng); + let y_: f64 = Open01.sample(rng); + + x = x_.ln() / ziggurat_tables::ZIG_NORM_R; + y = y_.ln(); } - StandardNormal(ziggurat( - rng, - true, // this is symmetric - &ziggurat_tables::ZIG_NORM_X, - &ziggurat_tables::ZIG_NORM_F, - pdf, zero_case)) + if u < 0.0 { x - ziggurat_tables::ZIG_NORM_R } else { ziggurat_tables::ZIG_NORM_R - x } } + + ziggurat( + rng, + true, // this is symmetric + &ziggurat_tables::ZIG_NORM_X, + &ziggurat_tables::ZIG_NORM_F, + pdf, zero_case) } /// The normal distribution `N(mean, std_dev**2)`. /// /// This uses the ZIGNOR variant of the Ziggurat method, see -/// `StandardNormal` for more details. +/// `standard_normal` for more details. /// /// # Example /// /// ```rust -/// use rand::distributions::{Normal, IndependentSample}; +/// use rand::distributions::{Normal, Distribution}; /// /// // mean 2, standard deviation 3 /// let normal = Normal::new(2.0, 3.0); -/// let v = normal.ind_sample(&mut rand::thread_rng()); +/// let v = normal.sample(&mut rand::thread_rng()); /// println!("{} is from a N(2, 9) distribution", v) /// ``` #[derive(Clone, Copy, Debug)] @@ -110,13 +105,9 @@ impl Normal { } } } -impl Sample for Normal { - fn sample(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) } -} -impl IndependentSample for Normal { - fn ind_sample(&self, rng: &mut R) -> f64 { - let StandardNormal(n) = rng.gen::(); - self.mean + self.std_dev * n +impl Distribution for Normal { + fn sample(&self, rng: &mut R) -> f64 { + self.mean + self.std_dev * standard_normal(rng) } } @@ -129,11 +120,11 @@ impl IndependentSample for Normal { /// # Example /// /// ```rust -/// use rand::distributions::{LogNormal, IndependentSample}; +/// use rand::distributions::{LogNormal, Distribution}; /// /// // mean 2, standard deviation 3 /// let log_normal = LogNormal::new(2.0, 3.0); -/// let v = log_normal.ind_sample(&mut rand::thread_rng()); +/// let v = log_normal.sample(&mut rand::thread_rng()); /// println!("{} is from an ln N(2, 9) distribution", v) /// ``` #[derive(Clone, Copy, Debug)] @@ -154,27 +145,23 @@ impl LogNormal { LogNormal { norm: Normal::new(mean, std_dev) } } } -impl Sample for LogNormal { - fn sample(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) } -} -impl IndependentSample for LogNormal { - fn ind_sample(&self, rng: &mut R) -> f64 { - self.norm.ind_sample(rng).exp() +impl Distribution for LogNormal { + fn sample(&self, rng: &mut R) -> f64 { + self.norm.sample(rng).exp() } } #[cfg(test)] mod tests { - use distributions::{Sample, IndependentSample}; + use distributions::{Distribution}; use super::{Normal, LogNormal}; #[test] fn test_normal() { - let mut norm = Normal::new(10.0, 10.0); + let norm = Normal::new(10.0, 10.0); let mut rng = ::test::rng(); for _ in 0..1000 { norm.sample(&mut rng); - norm.ind_sample(&mut rng); } } #[test] @@ -186,11 +173,10 @@ mod tests { #[test] fn test_log_normal() { - let mut lnorm = LogNormal::new(10.0, 10.0); + let lnorm = LogNormal::new(10.0, 10.0); let mut rng = ::test::rng(); for _ in 0..1000 { lnorm.sample(&mut rng); - lnorm.ind_sample(&mut rng); } } #[test] diff --git a/src/distributions/range.rs b/src/distributions/range.rs index 935a00aac36..f0931a9221e 100644 --- a/src/distributions/range.rs +++ b/src/distributions/range.rs @@ -1,4 +1,4 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -8,123 +8,296 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! Generating numbers between two others. - -// this is surprisingly complicated to be both generic & correct - -use core::num::Wrapping as w; +//! A distribution generating numbers within a given range. use Rng; -use distributions::{Sample, IndependentSample}; +use distributions::{Distribution, Uniform}; +use utils::FloatConversions; /// Sample values uniformly between two bounds. /// /// This gives a uniform distribution (assuming the RNG used to sample -/// it is itself uniform & the `SampleRange` implementation for the -/// given type is correct), even for edge cases like `low = 0u8`, +/// it is itself uniform and the `RangeImpl` implementation is correct), +/// even for edge cases like `low = 0u8`, /// `high = 170u8`, for which a naive modulo operation would return /// numbers less than 85 with double the probability to those greater /// than 85. /// -/// Types should attempt to sample in `[low, high)`, i.e., not -/// including `high`, but this may be very difficult. All the -/// primitive integer types satisfy this property, and the float types -/// normally satisfy it, but rounding may mean `high` can occur. +/// Types should attempt to sample in `[low, high)` for +/// `Range::new(low, high)`, i.e., excluding `high`, but this may be very +/// difficult. All the primitive integer types satisfy this property, and the +/// float types normally satisfy it, but rounding may mean `high` can occur. /// /// # Example /// /// ```rust -/// use rand::distributions::{IndependentSample, Range}; +/// use rand::distributions::{Distribution, Range}; /// /// fn main() { /// let between = Range::new(10, 10000); /// let mut rng = rand::thread_rng(); /// let mut sum = 0; /// for _ in 0..1000 { -/// sum += between.ind_sample(&mut rng); +/// sum += between.sample(&mut rng); /// } /// println!("{}", sum); /// } /// ``` #[derive(Clone, Copy, Debug)] -pub struct Range { - low: X, - range: X, - accept_zone: X +pub struct Range { + inner: T, } -impl Range { - /// Create a new `Range` instance that samples uniformly from - /// `[low, high)`. Panics if `low >= high`. - pub fn new(low: X, high: X) -> Range { +/// Ignore the `RangeInt` parameterisation; these non-member functions are +/// available to all range types. (Rust requires a type, and won't accept +/// `T: RangeImpl`. Consider this a minor language issue.) +impl Range> { + /// Create a new `Range` instance which samples uniformly from the half + /// open range `[low, high)` (excluding `high`). Panics if `low >= high`. + pub fn new(low: X, high: X) -> Range { assert!(low < high, "Range::new called with `low >= high`"); - SampleRange::construct_range(low, high) + Range { inner: RangeImpl::new(low, high) } + } + + /// Create a new `Range` instance which samples uniformly from the closed + /// range `[low, high]` (inclusive). Panics if `low >= high`. + pub fn new_inclusive(low: X, high: X) -> Range { + assert!(low < high, "Range::new called with `low >= high`"); + Range { inner: RangeImpl::new_inclusive(low, high) } + } + + /// Sample a single value uniformly from `[low, high)`. + /// Panics if `low >= high`. + pub fn sample_single(low: X, high: X, rng: &mut R) -> X { + assert!(low < high, "Range::sample_single called with low >= high"); + X::T::sample_single(low, high, rng) + } +} + +impl Distribution for Range { + fn sample(&self, rng: &mut R) -> T::X { + self.inner.sample(rng) } } -impl Sample for Range { - #[inline] - fn sample(&mut self, rng: &mut R) -> Sup { self.ind_sample(rng) } +/// Helper trait for creating objects using the correct implementation of +/// `RangeImpl` for the sampling type; this enables `Range::new(a, b)` to work. +pub trait SampleRange: PartialOrd+Sized { + type T: RangeImpl; } -impl IndependentSample for Range { - fn ind_sample(&self, rng: &mut R) -> Sup { - SampleRange::sample_range(self, rng) + +/// Helper trait handling actual range sampling. +/// +/// If you want to implement `Range` sampling for your own type, then +/// implement both this trait and `SampleRange`: +/// +/// ```rust +/// use rand::{Rng, thread_rng, Distribution}; +/// use rand::distributions::range::{Range, SampleRange, RangeImpl, RangeFloat}; +/// +/// #[derive(Clone, Copy, PartialEq, PartialOrd)] +/// struct MyF32(f32); +/// +/// #[derive(Clone, Copy, Debug)] +/// struct RangeMyF32 { +/// inner: RangeFloat, +/// } +/// impl RangeImpl for RangeMyF32 { +/// type X = MyF32; +/// fn new(low: Self::X, high: Self::X) -> Self { +/// RangeMyF32 { +/// inner: RangeFloat::::new(low.0, high.0), +/// } +/// } +/// fn new_inclusive(low: Self::X, high: Self::X) -> Self { +/// RangeImpl::new(low, high) +/// } +/// fn sample(&self, rng: &mut R) -> Self::X { +/// MyF32(self.inner.sample(rng)) +/// } +/// } +/// +/// impl SampleRange for MyF32 { +/// type T = RangeMyF32; +/// } +/// +/// let (low, high) = (MyF32(17.0f32), MyF32(22.0f32)); +/// let range = Range::new(low, high); +/// let x = range.sample(&mut thread_rng()); +/// ``` +pub trait RangeImpl: Sized { + /// The type sampled by this implementation. + type X: PartialOrd; + + /// Construct self, with inclusive lower bound and exclusive upper bound + /// `[low, high)`. + /// + /// Usually users should not call this directly but instead use + /// `Range::new`, which asserts that `low < high` before calling this. + fn new(low: Self::X, high: Self::X) -> Self; + + /// Construct self, with inclusive bounds `[low, high]`. + /// + /// Usually users should not call this directly but instead use + /// `Range::new`, which asserts that `low < high` before calling this. + fn new_inclusive(low: Self::X, high: Self::X) -> Self; + + /// Sample a value. + fn sample(&self, rng: &mut R) -> Self::X; + + /// Sample a single value uniformly from a range with inclusive lower bound + /// and exclusive upper bound `[low, high)`. + /// Panics if `low >= high`. + fn sample_single(low: Self::X, high: Self::X, rng: &mut R) + -> Self::X + { + let range: Self = RangeImpl::new(low, high); + range.sample(rng) } } -/// The helper trait for types that have a sensible way to sample -/// uniformly between two values. This should not be used directly, -/// and is only to facilitate `Range`. -pub trait SampleRange : Sized { - /// Construct the `Range` object that `sample_range` - /// requires. This should not ever be called directly, only via - /// `Range::new`, which will check that `low < high`, so this - /// function doesn't have to repeat the check. - fn construct_range(low: Self, high: Self) -> Range; - - /// Sample a value from the given `Range` with the given `Rng` as - /// a source of randomness. - fn sample_range(r: &Range, rng: &mut R) -> Self; +/// Implementation of `RangeImpl` for integer types. +#[derive(Clone, Copy, Debug)] +pub struct RangeInt { + low: X, + range: X, + zone: X, } -macro_rules! integer_impl { - ($ty:ty, $unsigned:ident) => { +macro_rules! range_int_impl { + ($ty:ty, $signed:ident, $unsigned:ident, + $i_large:ident, $u_large:ident) => { impl SampleRange for $ty { - // we play free and fast with unsigned vs signed here + type T = RangeInt<$ty>; + } + + impl RangeImpl for RangeInt<$ty> { + // We play free and fast with unsigned vs signed here // (when $ty is signed), but that's fine, since the // contract of this macro is for $ty and $unsigned to be - // "bit-equal", so casting between them is a no-op & a - // bijection. + // "bit-equal", so casting between them is a no-op. + + type X = $ty; - #[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 = ::core::$unsigned::MAX; + #[inline] // if the range is constant, this helps LLVM to do the + // calculations at compile-time. + fn new(low: Self::X, high: Self::X) -> Self { + RangeImpl::new_inclusive(low, high - 1) + } + + #[inline] // if the range is constant, this helps LLVM to do the + // calculations at compile-time. + fn new_inclusive(low: Self::X, high: Self::X) -> Self { + // For a closed range the number of possible numbers we should + // generate is `range = (high - low + 1)`. It is not possible to + // end up with a uniform distribution if we map _all_ the random + // integers that can be generated to this range. We have to map + // integers from a `zone` that is a multiple of the range. The + // rest of the integers, that cause a bias, are rejected. The + // sampled number is `zone % range`. + // + // The problem with `range` is that to cover the full range of + // the type, it has to store `unsigned_max + 1`, which can't be + // represented. But if the range covers the full range of the + // type, no modulus is needed. A range of size 0 can't exist, so + // we use that to represent this special case. Wrapping + // arithmetic even makes representing `unsigned_max + 1` as 0 + // simple. + // + // We don't calculate zone directly, but first calculate the + // number of integers to reject. To handle `unsigned_max + 1` + // not fitting in the type, we use: + // ints_to_reject = (unsigned_max + 1) % range; + // ints_to_reject = (unsigned_max - range + 1) % range; + // + // The smallest integer prngs generate is u32. That is why for + // small integer sizes (i8/u8 and i16/u16) there is an + // optimisation: don't pick the largest zone that can fit in the + // small type, but pick the largest zone that can fit in an u32. + // This improves the chance to get a random integer that fits in + // the zone to 998 in 1000 in the worst case. + // + // There is a problem however: we can't store such a large range + // in `RangeInt`, that can only hold values of the size of $ty. + // `ints_to_reject` is always less than half the size of the + // small integer. For an u8 it only ever uses 7 bits. This means + // that all but the last 7 bits of `zone` are always 1's (or 15 + // in the case of u16). So nothing is lost by trucating `zone`. + // + // An alternative to using a modulus is widening multiply: + // After a widening multiply by `range`, the result is in the + // high word. Then comparing the low word against `zone` makes + // sure our distribution is uniform. + // + // FIXME: This method is not used for i128/u128. It will + // probably help a lot for performance, because a 128-bit + // modulus is terribly slow. It means hand-coding a big-integer + // widening multiply. - // this is the largest number that fits into $unsigned - // that `range` divides evenly, so, if we've sampled - // `n` uniformly from this region, then `n % range` is - // uniform in [0, range) - let zone = unsigned_max - unsigned_max % range; + let unsigned_max: $u_large = ::core::$u_large::MAX; - Range { + let range = (high as $u_large) + .wrapping_sub(low as $u_large) + .wrapping_add(1); + let ints_to_reject = + if range > 0 { + (unsigned_max - range + 1) % range + } else { + 0 + }; + let zone = unsigned_max - ints_to_reject; + + RangeInt { low: low, + // These are really $unsigned values, but store as $ty: range: range as $ty, - accept_zone: zone as $ty + zone: zone as $ty + } + } + + fn sample(&self, rng: &mut R) -> Self::X { + let range = self.range as $unsigned as $u_large; + if range > 0 { + // Some casting to recover the trucated bits of `zone`: + // First bit-cast to a signed int. Next sign-extend to the + // larger type. Then bit-cast to unsigned. + // For types that already have the right size, all the + // casting is a no-op. + let zone = self.zone as $signed as $i_large as $u_large; + loop { + let v: $u_large = Uniform.sample(rng); + let (hi, lo) = v.wmul(range); + if lo <= zone { + return self.low.wrapping_add(hi as $ty); + } + } + } else { + // Sample from the entire integer range. + Uniform.sample(rng) } } - #[inline] - fn sample_range(r: &Range<$ty>, rng: &mut R) -> $ty { + fn sample_single(low: Self::X, high: Self::X, rng: &mut R) -> Self::X { + let range = (high as $u_large) + .wrapping_sub(low as $u_large); + let zone = + if ::core::$unsigned::MAX <= ::core::u16::MAX as $unsigned { + // Using a modulus is faster than the approximation for + // i8 and i16. I suppose we trade the cost of one + // modulus for near-perfect branch prediction. + let unsigned_max: $u_large = ::core::$u_large::MAX; + let ints_to_reject = (unsigned_max - range + 1) % range; + unsigned_max - ints_to_reject + } else { + // conservative but fast approximation + range << range.leading_zeros() + }; + loop { - // rejection sample - let v = rng.gen::<$unsigned>(); - // until we find something that fits into the - // region which r.range evenly divides (this will - // be uniformly distributed) - if v < r.accept_zone as $unsigned { - // and return it, with some adjustments - return (w(r.low) + w((v % r.range as $unsigned) as $ty)).0; + let v: $u_large = Uniform.sample(rng); + let (hi, lo) = v.wmul(range); + if lo <= zone { + return low.wrapping_add(hi as $ty); } } } @@ -132,45 +305,268 @@ macro_rules! integer_impl { } } -integer_impl! { i8, u8 } -integer_impl! { i16, u16 } -integer_impl! { i32, u32 } -integer_impl! { i64, u64 } +range_int_impl! { i8, i8, u8, i32, u32 } +range_int_impl! { i16, i16, u16, i32, u32 } +range_int_impl! { i32, i32, u32, i32, u32 } +range_int_impl! { i64, i64, u64, i64, u64 } +#[cfg(feature = "i128_support")] +range_int_impl! { i128, i128, u128, u128, u128 } +range_int_impl! { isize, isize, usize, isize, usize } +range_int_impl! { u8, i8, u8, i32, u32 } +range_int_impl! { u16, i16, u16, i32, u32 } +range_int_impl! { u32, i32, u32, i32, u32 } +range_int_impl! { u64, i64, u64, i64, u64 } +range_int_impl! { usize, isize, usize, isize, usize } #[cfg(feature = "i128_support")] -integer_impl! { i128, u128 } -integer_impl! { isize, usize } -integer_impl! { u8, u8 } -integer_impl! { u16, u16 } -integer_impl! { u32, u32 } -integer_impl! { u64, u64 } +range_int_impl! { u128, u128, u128, i128, u128 } + + +trait WideningMultiply { + type Output; + + fn wmul(self, x: RHS) -> Self::Output; +} + +macro_rules! wmul_impl { + ($ty:ty, $wide:ident, $shift:expr) => { + impl WideningMultiply for $ty { + type Output = ($ty, $ty); + + #[inline(always)] + fn wmul(self, x: $ty) -> Self::Output { + let tmp = (self as $wide) * (x as $wide); + ((tmp >> $shift) as $ty, tmp as $ty) + } + } + } +} + +wmul_impl! { u8, u16, 8 } +wmul_impl! { u16, u32, 16 } +wmul_impl! { u32, u64, 32 } +#[cfg(feature = "i128_support")] +wmul_impl! { u64, u128, 64 } + +// This code is a translation of the __mulddi3 function in LLVM's +// compiler-rt. It is an optimised variant of the common method +// `(a + b) * (c + d) = ac + ad + bc + bd`. +// +// For some reason LLVM can optimise the C version very well, but +// keeps shuffeling registers in this Rust translation. +macro_rules! wmul_impl_large { + ($ty:ty, $half:expr) => { + impl WideningMultiply for $ty { + type Output = ($ty, $ty); + + #[inline(always)] + fn wmul(self, b: $ty) -> Self::Output { + const LOWER_MASK: $ty = !0 >> $half; + let mut low = (self & LOWER_MASK).wrapping_mul(b & LOWER_MASK); + let mut t = low >> $half; + low &= LOWER_MASK; + t += (self >> $half).wrapping_mul(b & LOWER_MASK); + low += (t & LOWER_MASK) << $half; + let mut high = t >> $half; + t = low >> $half; + low &= LOWER_MASK; + t += (b >> $half).wrapping_mul(self & LOWER_MASK); + low += (t & LOWER_MASK) << $half; + high += t >> $half; + high += (self >> $half).wrapping_mul(b >> $half); + + (high, low) + } + } + } +} + +#[cfg(not(feature = "i128_support"))] +wmul_impl_large! { u64, 32 } #[cfg(feature = "i128_support")] -integer_impl! { u128, u128 } -integer_impl! { usize, usize } +wmul_impl_large! { u128, 64 } -macro_rules! float_impl { + +macro_rules! wmul_impl_usize { ($ty:ty) => { + impl WideningMultiply for usize { + type Output = (usize, usize); + + #[inline(always)] + fn wmul(self, x: usize) -> Self::Output { + let (high, low) = (self as $ty).wmul(x as $ty); + (high as usize, low as usize) + } + } + } +} + +#[cfg(target_pointer_width = "32")] +wmul_impl_usize! { u32 } +#[cfg(target_pointer_width = "64")] +wmul_impl_usize! { u64 } + + + +/// Implementation of `RangeImpl` for float types. +#[derive(Clone, Copy, Debug)] +pub enum RangeFloat { + // A range that is completely positive + #[doc(hidden)] + Positive { offset: X, scale: X }, + // A range that is completely negative + #[doc(hidden)] + Negative { offset: X, scale: X }, + // A range that is goes from negative to positive, but has more positive + // than negative numbers + #[doc(hidden)] + PosAndNeg { cutoff: X, scale: X }, + // A range that is goes from negative to positive, but has more negative + // than positive numbers + #[doc(hidden)] + NegAndPos { cutoff: X, scale: X }, +} + +macro_rules! range_float_impl { + ($ty:ty, $next_u:path) => { impl SampleRange for $ty { - fn construct_range(low: $ty, high: $ty) -> Range<$ty> { - Range { - low: low, - range: high - low, - accept_zone: 0.0 // unused + type T = RangeFloat<$ty>; + } + + impl RangeImpl for RangeFloat<$ty> { + // We can distinguish between two different kinds of ranges: + // + // Completely positive or negative. + // A characteristic of these ranges is that they get more bits of + // precision as they get closer to 0.0. For positive ranges it is as + // simple as applying a scale and offset to get the right range. + // For a negative range, we apply a negative scale to transform the + // range [0,1) to (-x,0]. Because this results in a range with it + // closed and open sides reversed, we do not sample from + // `closed_open01` but from `open_closed01`. + // + // A range that is both negative and positive. + // This range has as characteristic that it does not have most of + // its bits of precision near its low or high end, but in the middle + // near 0.0. As the basis we use the [-1,1) range from + // `closed_open11`. + // How it works is easiest to explain with an example. + // Suppose we want to sample from the range [-20, 100). We are + // first going to scale the range from [-1,1) to (-60,60]. Note the + // range changes from closed-open to open-closed because the scale + // is negative. + // If the sample happens to fall in the part (-60,-20), move it to + // (-100,60). Then multiply by -1. + // Scaling keeps the bits of precision intact. Moving the assymetric + // part also does not waste precision, as the more you move away + // from zero the less precision can be stored. + + type X = $ty; + + fn new(low: Self::X, high: Self::X) -> Self { + if low >= 0.0 { + RangeFloat::Positive { + offset: low, + scale: high - low, + } + } else if high <= 0.0 { + RangeFloat::Negative { + offset: high, + scale: low - high, + } + } else if -low <= high { + RangeFloat::PosAndNeg { + cutoff: low, + scale: (high + low) / -2.0 + low, + } + } else { + RangeFloat::NegAndPos { + cutoff: high, + scale: (high + low) / 2.0 - high, + } } } - fn sample_range(r: &Range<$ty>, rng: &mut R) -> $ty { - r.low + r.range * rng.gen::<$ty>() + + fn new_inclusive(low: Self::X, high: Self::X) -> Self { + // Same as `new`, because the boundaries of a floats range are + // (at least currently) not exact due to rounding errors. + RangeImpl::new(low, high) + } + + fn sample(&self, rng: &mut R) -> Self::X { + let rnd = $next_u(rng); + match *self { + RangeFloat::Positive { offset, scale } => { + let x: $ty = rnd.closed_open01(); + offset + scale * x + } + RangeFloat::Negative { offset, scale } => { + let x: $ty = rnd.open_closed01(); + offset + scale * x + } + RangeFloat::PosAndNeg { cutoff, scale } => { + let x: $ty = rnd.closed_open11(); + let x = x * scale; + if x < cutoff { + (cutoff - scale) - x + } else { + x + } + } + RangeFloat::NegAndPos { cutoff, scale } => { + let x: $ty = rnd.closed_open11(); + let x = x * scale; + if x >= cutoff { + (cutoff + scale) - x + } else { + x + } + } + } } } } } -float_impl! { f32 } -float_impl! { f64 } +range_float_impl! { f32, Rng::next_u32 } +range_float_impl! { f64, Rng::next_u64 } #[cfg(test)] mod tests { - use distributions::{Sample, IndependentSample}; - use super::Range as Range; + use {Rng, Sample, thread_rng}; + use distributions::{Distribution}; + use distributions::range::{Range, RangeImpl, RangeFloat, SampleRange}; + + #[test] + fn test_fn_range() { + let mut r = thread_rng(); + for _ in 0..1000 { + let a = Range::new(-3, 42).sample(&mut r); + assert!(a >= -3 && a < 42); + assert_eq!(Range::new(0, 1).sample(&mut r), 0); + assert_eq!(Range::new(-12, -11).sample(&mut r), -12); + } + + for _ in 0..1000 { + let a = Range::new(10, 42).sample(&mut r); + assert!(a >= 10 && a < 42); + assert_eq!(Range::new(0, 1).sample(&mut r), 0); + assert_eq!(Range::new(3_000_000, 3_000_001).sample(&mut r), 3_000_000); + } + } + + #[test] + #[should_panic] + fn test_fn_range_panic_int() { + let mut r = thread_rng(); + Range::new(5, -2).sample(&mut r); + } + + #[test] + #[should_panic] + fn test_fn_range_panic_usize() { + let mut r = thread_rng(); + Range::new(5, 2).sample(&mut r); + } #[should_panic] #[test] @@ -191,25 +587,21 @@ mod tests { $( let v: &[($ty, $ty)] = &[(0, 10), (10, 127), - (::core::$ty::MIN, ::core::$ty::MAX)]; + (::std::$ty::MIN, ::std::$ty::MAX)]; for &(low, high) in v.iter() { - let mut sampler: Range<$ty> = Range::new(low, high); + let my_range = Range::new(low, high); for _ in 0..1000 { - let v = sampler.sample(&mut rng); - assert!(low <= v && v < high); - let v = sampler.ind_sample(&mut rng); + let v: $ty = rng.sample(my_range); assert!(low <= v && v < high); } } )* }} } - #[cfg(not(feature = "i128_support"))] t!(i8, i16, i32, i64, isize, u8, u16, u32, u64, usize); #[cfg(feature = "i128_support")] - t!(i8, i16, i32, i64, i128, isize, - u8, u16, u32, u64, u128, usize); + t!(i128, u128) } #[test] @@ -223,11 +615,9 @@ mod tests { (1e-35, 1e-25), (-1e35, 1e35)]; for &(low, high) in v.iter() { - let mut sampler: Range<$ty> = Range::new(low, high); + let my_range = Range::new(low, high); for _ in 0..1000 { - let v = sampler.sample(&mut rng); - assert!(low <= v && v < high); - let v = sampler.ind_sample(&mut rng); + let v: $ty = rng.sample(my_range); assert!(low <= v && v < high); } } @@ -238,4 +628,40 @@ mod tests { t!(f32, f64) } + #[test] + fn test_custom_range() { + #[derive(Clone, Copy, PartialEq, PartialOrd)] + struct MyF32 { + x: f32, + } + #[derive(Clone, Copy, Debug)] + struct RangeMyF32 { + inner: RangeFloat, + } + impl RangeImpl for RangeMyF32 { + type X = MyF32; + fn new(low: Self::X, high: Self::X) -> Self { + RangeMyF32 { + inner: RangeFloat::::new(low.x, high.x), + } + } + fn new_inclusive(low: Self::X, high: Self::X) -> Self { + RangeImpl::new(low, high) + } + fn sample(&self, rng: &mut R) -> Self::X { + MyF32 { x: self.inner.sample(rng) } + } + } + impl SampleRange for MyF32 { + type T = RangeMyF32; + } + + let (low, high) = (MyF32{ x: 17.0f32 }, MyF32{ x: 22.0f32 }); + let range = Range::new(low, high); + let mut rng = ::test::rng(); + for _ in 0..100 { + let x: MyF32 = rng.sample(range); + assert!(low <= x && x < high); + } + } } diff --git a/src/distributions/uniform.rs b/src/distributions/uniform.rs new file mode 100644 index 00000000000..4b3f6cfc2c6 --- /dev/null +++ b/src/distributions/uniform.rs @@ -0,0 +1,313 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Generating uniformly distributed numbers + +use core::char; +use core::mem; + +use Rng; +use distributions::{Distribution, Range}; +use utils::FloatConversions; + +// ----- Sampling distributions ----- + +/// Sample values uniformly over the whole range supported by the type +#[derive(Debug)] +pub struct Uniform; + +/// Sample values uniformly over the half-open range [0, 1) +#[derive(Debug)] +pub struct Uniform01; + +/// Sample values uniformly over the open range (0, 1) +#[derive(Debug)] +pub struct Open01; + +/// Sample values uniformly over the closed range [0, 1] +#[derive(Debug)] +pub struct Closed01; + +/// Sample a `char`, uniformly distributed over all Unicode scalar values, +/// i.e. all code points in the range `0...0x10_FFFF`, except for the range +/// `0xD800...0xDFFF` (the surrogate code points). This includes +/// unassigned/reserved code points. +#[derive(Debug)] +pub struct Codepoint; + +/// Sample a `char`, uniformly distributed over ASCII letters and numbers: +/// a-z, A-Z and 0-9. +#[derive(Debug)] +pub struct Alphanumeric; + + +// ----- actual implementations ----- + +impl Distribution for Uniform { + fn sample(&self, rng: &mut R) -> isize { + if mem::size_of::() <= 4 { + rng.next_u32() as isize + } else if mem::size_of::() == 8 { + rng.next_u64() as isize + } else { + unreachable!() + } + } +} + +impl Distribution for Uniform { + #[inline] + fn sample(&self, rng: &mut R) -> i8 { + rng.next_u32() as i8 + } +} + +impl Distribution for Uniform { + #[inline] + fn sample(&self, rng: &mut R) -> i16 { + rng.next_u32() as i16 + } +} + +impl Distribution for Uniform { + #[inline] + fn sample(&self, rng: &mut R) -> i32 { + rng.next_u32() as i32 + } +} + +impl Distribution for Uniform { + #[inline] + fn sample(&self, rng: &mut R) -> i64 { + rng.next_u64() as i64 + } +} + +#[cfg(feature = "i128_support")] +impl Distribution for Uniform { + #[inline] + fn sample(&self, rng: &mut R) -> i128 { + rng.next_u128() as i128 + } +} + +impl Distribution for Uniform { + #[inline] + fn sample(&self, rng: &mut R) -> usize { + if mem::size_of::() <= 4 { + rng.next_u32() as usize + } else if mem::size_of::() == 8 { + rng.next_u64() as usize + } else { + unreachable!() + } + } +} + +impl Distribution for Uniform { + #[inline] + fn sample(&self, rng: &mut R) -> u8 { + rng.next_u32() as u8 + } +} + +impl Distribution for Uniform { + #[inline] + fn sample(&self, rng: &mut R) -> u16 { + rng.next_u32() as u16 + } +} + +impl Distribution for Uniform { + #[inline] + fn sample(&self, rng: &mut R) -> u32 { + rng.next_u32() + } +} + +impl Distribution for Uniform { + #[inline] + fn sample(&self, rng: &mut R) -> u64 { + rng.next_u64() + } +} + +#[cfg(feature = "i128_support")] +impl Distribution for Uniform { + #[inline] + fn sample(&self, rng: &mut R) -> u128 { + rng.next_u128() + } +} + +impl Distribution for Uniform { + #[inline] + fn sample(&self, rng: &mut R) -> bool { + // We can compare against an arbitrary bit of an u32 to get a bool. + // Because the least significant bits of a lower quality RNG can have + // simple patterns, we compare against the most significant bit. This is + // easiest done using a sign test. + (rng.next_u32() as i32) < 0 + } +} + +macro_rules! float_impls { + ($ty:ty, $next_u:path) => { + impl Distribution<$ty> for Uniform01 { + fn sample(&self, rng: &mut R) -> $ty { + let x = $next_u(rng); + x.closed_open01() + } + } + + impl Distribution<$ty> for Closed01 { + fn sample(&self, rng: &mut R) -> $ty { + // The problem with a closed range over [0,1] is that it needs + // 2^n+1 samples to generate a uniform distribution. Which is + // difficult, as it means we either have to reject about half + // the generated random numbers, or just not include one number + // in the distribution. That is why, instead of a closed range, + // we actually sample from the half-open range (0,1]. + // + // Floating-point numbers have more precision near zero. This + // means for a f32 that only 1 in 2^32 samples will give exactly + // 0.0. But 1 in 2^23 will give exactly 1.0 due to rounding. + // Because the chance to sample 0.0 is so low, this half-open + // range is a very good appoximation of a closed range. + let x = $next_u(rng); + x.open_closed01() + } + } + + impl Distribution<$ty> for Open01 { + // Sample from the open range (0,1). + // This uses the rejection method: use `closed_open01`, and if the + // result is 0.0, reject the result and try again. + fn sample(&self, rng: &mut R) -> $ty { + let mut x = 0; + while x == 0 { // 0 converts to 0.0 + x = $next_u(rng); + } + x.closed_open01() + } + } + } +} +float_impls! { f32, Rng::next_u32 } +float_impls! { f64, Rng::next_u64 } + + +impl Distribution for Codepoint { + fn sample(&self, rng: &mut R) -> char { + let range = Range::new(0u32, 0x11_0000); + loop { + match char::from_u32(range.sample(rng)) { + Some(c) => return c, + // About 0.2% of numbers in the range 0..0x110000 are invalid + // codepoints (surrogates). + None => {} + } + } + } +} + +impl Distribution for Alphanumeric { + fn sample(&self, rng: &mut R) -> char { + const RANGE: u32 = 26 + 26 + 10; + const GEN_ASCII_STR_CHARSET: &'static [u8] = + b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\ + abcdefghijklmnopqrstuvwxyz\ + 0123456789"; + loop { + let var = rng.next_u32() >> 26; + if var < RANGE { + return GEN_ASCII_STR_CHARSET[var as usize] as char + } + } + } +} + + +#[cfg(test)] +mod tests { + use {Sample, thread_rng, iter}; + use distributions::{Uniform, Uniform01, Open01, Closed01, + Codepoint, Alphanumeric}; + + #[test] + fn test_integers() { + let mut rng = ::test::rng(); + + rng.sample::(Uniform); + rng.sample::(Uniform); + rng.sample::(Uniform); + rng.sample::(Uniform); + rng.sample::(Uniform); + #[cfg(feature = "i128_support")] + rng.sample::(Uniform); + + rng.sample::(Uniform); + rng.sample::(Uniform); + rng.sample::(Uniform); + rng.sample::(Uniform); + rng.sample::(Uniform); + #[cfg(feature = "i128_support")] + rng.sample::(Uniform); + } + + #[test] + fn test_chars() { + let mut rng = ::test::rng(); + + let _ = rng.sample(Codepoint); + let c = rng.sample(Alphanumeric); + assert!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')); + + let word: String = iter(&mut rng).take(5).map(|rng| rng.sample(Alphanumeric)).collect(); + assert_eq!(word.len(), 5); + } + + #[test] + fn test_f64() { + let mut rng = thread_rng(); + let a: f64 = rng.sample(Uniform01); + let b = rng.sample::(Uniform01); + assert!(0.0 <= a && a < 1.0); + assert!(0.0 <= b && b < 1.0); + } + + #[test] + fn rand_open() { + // this is unlikely to catch an incorrect implementation that + // generates exactly 0 or 1, but it keeps it sane. + let mut rng = thread_rng(); + for _ in 0..1_000 { + // strict inequalities + let f: f64 = rng.sample(Open01); + assert!(0.0 < f && f < 1.0); + + let f: f32 = rng.sample(Open01); + assert!(0.0 < f && f < 1.0); + } + } + + #[test] + fn rand_closed() { + let mut rng = thread_rng(); + for _ in 0..1_000 { + // strict inequalities + let f: f64 = rng.sample(Closed01); + assert!(0.0 <= f && f <= 1.0); + + let f: f32 = rng.sample(Closed01); + assert!(0.0 <= f && f <= 1.0); + } + } +} diff --git a/src/iter.rs b/src/iter.rs new file mode 100644 index 00000000000..176a110aa45 --- /dev/null +++ b/src/iter.rs @@ -0,0 +1,187 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Iterators attached to an `Rng` +//! +//! Lifetime restrictions prevent an `Rng` iterator from simply implementing +//! `std::iter::Iterator`. Instead, you get the simplified iterators below, +//! providing only a subset of functionality. + +use core::cmp::min; +use core::usize; + +use Rng; + +/// Pseudo-iterator encapsulating a random number generator. +/// See [`Rng::iter`](trait.Rng.html#method.iter). +#[derive(Debug)] +pub struct Iter<'a, R: Rng+?Sized+'a> { + pub(crate) rng: &'a mut R, + pub(crate) len: Option, +} + +/// Create an iterator on an `Rng`. +/// +/// This does not create a "real" `Iterator` due to lifetime restrictions, but +/// the returned object supports the most important iterator functions, like +/// `take(number)` and `map(closure)`. +/// +/// # Example +/// +/// ``` +/// use rand::{thread_rng, Rng, Sample, iter}; +/// use rand::distributions::{Uniform, Alphanumeric}; +/// +/// let mut rng = thread_rng(); +/// let x: Vec = iter(&mut rng).take(10).map(|rng| rng.sample(Uniform)).collect(); +/// println!("{:?}", x); +/// +/// let w: String = iter(&mut rng).take(6).map(|rng| rng.sample(Alphanumeric)).collect(); +/// println!("{}", w); +/// ``` +pub fn iter<'a, R: Rng+?Sized+'a>(rng: &'a mut R) -> Iter<'a, R> { + Iter { rng, len: None } +} + +impl<'a, R: Rng+?Sized+'a> Iter<'a, R> { + /// Restrict number of generated items to at most `len` + pub fn take(self, len: usize) -> Self { + Iter { + rng: self.rng, + len: Some(self.len.map_or(len, |old| min(old, len))), + } + } + + /// Produce an iterator returning a mapped value + pub fn map(self, f: F) -> Map<'a, R, B, F> + where F: FnMut(&mut R) -> B + { + Map { + rng: self.rng, + len: self.len, + f: f, + } + } + + /// Produce an iterator returning a flat-mapped value + pub fn flat_map(self, f: F) -> FlatMap<'a, R, U, F> + where F: FnMut(&mut R) -> U, U: IntoIterator + { + FlatMap { + rng: self.rng, + len: self.len, + f: f, + frontiter: None, + } + } +} + +#[derive(Debug)] +pub struct Map<'a, R:?Sized+'a, B, F> where F: FnMut(&mut R) -> B { + rng: &'a mut R, + len: Option, + f: F, +} +impl<'a, R:?Sized+'a, B, F> Iterator for Map<'a, R, B, F> + where F: FnMut(&mut R) -> B +{ + type Item = B; + + fn next(&mut self) -> Option { + match self.len { + Some(0) => return None, + Some(ref mut n) => { *n -= 1; } + None => {} + } + + Some((self.f)(self.rng)) + } + + fn size_hint(&self) -> (usize, Option) { + // If len == None we have an infinite iterator; usize::MAX is nearest + // available lower bound. Probably this suffices to make the following equal: + // rng.iter().take(n).map(f).size_hint() == rng.iter().map(f).take(n).size_hint() + self.len.map_or((usize::MAX, None), |len| (len, Some(len))) + } +} + +#[derive(Debug)] +pub struct FlatMap<'a, R:?Sized+'a, U, F> + where F: FnMut(&mut R) -> U, U: IntoIterator +{ + rng: &'a mut R, + len: Option, + f: F, + frontiter: Option, +} +impl<'a, R:?Sized+'a, U, F> Iterator for FlatMap<'a, R, U, F> + where F: FnMut(&mut R) -> U, U: IntoIterator +{ + type Item = ::Item; + + fn next(&mut self) -> Option { + loop { + if let Some(ref mut inner) = self.frontiter { + if let Some(x) = inner.by_ref().next() { + return Some(x) + } + } + + match self.len { + Some(0) => return None, + Some(ref mut n) => { *n -= 1; } + None => {} + } + + self.frontiter = Some(IntoIterator::into_iter((self.f)(self.rng))); + } + } + + fn size_hint(&self) -> (usize, Option) { + if self.len == Some(0) { + // No new iters, so we have frontiter or nothing + self.frontiter.as_ref().map_or((0, Some(0)), |it| it.size_hint()) + } else { + // Can't compute an actual bound without producing the sub-iters, + // which we don't want to do. But we may have a lower bound. + let lb = self.frontiter.as_ref().map_or(0, |it| it.size_hint().0); + (lb, None) + } + } +} + +#[cfg(test)] +mod tests { + use {Rng, Sample, thread_rng, iter}; + use distributions::{Uniform, Alphanumeric}; + + #[test] + fn test_iter() { + let mut rng = thread_rng(); + + let x: Vec<()> = iter(&mut rng).take(10).map(|_| ()).collect(); + assert_eq!(x.len(), 10); + let y: Vec = iter(&mut rng).take(10).map(|rng| rng.sample(Uniform)).collect(); + assert_eq!(y.len(), 10); + let z: Vec = iter(&mut rng).take(10).flat_map(|rng| + vec![rng.sample(Uniform), rng.sample(Uniform)].into_iter()).collect(); + assert_eq!(z.len(), 20); + let w: Vec = iter(&mut rng).take(10).flat_map(|_| vec![].into_iter()).collect(); + assert_eq!(w.len(), 0); + } + + #[test] + fn test_dyn_dispatch() { + let r: &mut Rng = &mut thread_rng(); + + let x: String = iter(r).take(10).map(|rng| rng.sample(Alphanumeric)).collect(); + assert_eq!(x.len(), 10); + } +} diff --git a/src/jitter.rs b/src/jitter.rs index 942b0d0c9fe..51ef61e1b6e 100644 --- a/src/jitter.rs +++ b/src/jitter.rs @@ -1,754 +1,758 @@ -// Copyright 2017 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. -// -// Based on jitterentropy-library, http://www.chronox.de/jent.html. -// Copyright Stephan Mueller , 2014 - 2017. -// -// With permission from Stephan Mueller to relicense the Rust translation under -// the MIT license. - -//! Non-physical true random number generator based on timing jitter. - -use Rng; - -use core::{fmt, mem, ptr}; -#[cfg(feature="std")] -use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering}; - -const MEMORY_BLOCKS: usize = 64; -const MEMORY_BLOCKSIZE: usize = 32; -const MEMORY_SIZE: usize = MEMORY_BLOCKS * MEMORY_BLOCKSIZE; - -/// A true random number generator based on jitter in the CPU execution time, -/// and jitter in memory access time. -/// -/// This is a true random number generator, as opposed to pseudo-random -/// generators. Random numbers generated by `JitterRng` can be seen as fresh -/// entropy. A consequence is that is orders of magnitude slower than `OsRng` -/// and PRNGs (about 10^3 .. 10^6 slower). -/// -/// There are very few situations where using this RNG is appropriate. Only very -/// few applications require true entropy. A normal PRNG can be statistically -/// indistinguishable, and a cryptographic PRNG should also be as impossible to -/// predict. -/// -/// Use of `JitterRng` is recommended for initializing cryptographic PRNGs when -/// `OsRng` is not available. -/// -/// This implementation is based on -/// [Jitterentropy](http://www.chronox.de/jent.html) version 2.1.0. -// -// Note: the C implementation relies on being compiled without optimizations. -// This implementation goes through lengths to make the compiler not optimise -// out what is technically dead code, but that does influence timing jitter. -pub struct JitterRng { - data: u64, // Actual random number - // Number of rounds to run the entropy collector per 64 bits - rounds: u32, - // Timer and previous time stamp, used by `measure_jitter` - timer: fn() -> u64, - prev_time: u64, - // Deltas used for the stuck test - last_delta: i64, - last_delta2: i64, - // Memory for the Memory Access noise source - mem_prev_index: usize, - mem: [u8; MEMORY_SIZE], - // Make `next_u32` not waste 32 bits - data_remaining: Option, -} - -// Custom Debug implementation that does not expose the internal state -impl fmt::Debug for JitterRng { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "JitterRng {{}}") - } -} - -/// An error that can occur when `test_timer` fails. -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum TimerError { - /// No timer available. - NoTimer, - /// Timer too coarse to use as an entropy source. - CoarseTimer, - /// Timer is not monotonically increasing. - NotMonotonic, - /// Variations of deltas of time too small. - TinyVariantions, - /// Too many stuck results (indicating no added entropy). - TooManyStuck, - #[doc(hidden)] - __Nonexhaustive, -} - -impl TimerError { - fn description(&self) -> &'static str { - match *self { - TimerError::NoTimer => "no timer available", - TimerError::CoarseTimer => "coarse timer", - TimerError::NotMonotonic => "timer not monotonic", - TimerError::TinyVariantions => "time delta variations too small", - TimerError::TooManyStuck => "too many stuck results", - TimerError::__Nonexhaustive => unreachable!(), - } - } -} - -impl fmt::Display for TimerError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.description()) - } -} - -#[cfg(feature="std")] -impl ::std::error::Error for TimerError { - fn description(&self) -> &str { - self.description() - } -} - -// Initialise to zero; must be positive -#[cfg(feature="std")] -static JITTER_ROUNDS: AtomicUsize = ATOMIC_USIZE_INIT; - -impl JitterRng { - /// Create a new `JitterRng`. - /// Makes use of `std::time` for a timer. - /// - /// 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 { - 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. - // This allows the timer test to run multiple times; we don't care. - rounds = ec.test_timer()?; - JITTER_ROUNDS.store(rounds as usize, Ordering::Relaxed); - } - ec.set_rounds(rounds); - Ok(ec) - } - - /// Create a new `JitterRng`. - /// A custom timer can be supplied, making it possible to use `JitterRng` in - /// `no_std` environments. - /// - /// The timer must have nanosecond precision. - /// - /// This method is more low-level than `new()`. It is the responsibility of - /// the caller to run `test_timer` before using any numbers generated with - /// `JitterRng`, and optionally call `set_rounds()`. - pub fn new_with_timer(timer: fn() -> u64) -> JitterRng { - let mut ec = JitterRng { - data: 0, - rounds: 64, - timer: timer, - prev_time: 0, - last_delta: 0, - last_delta2: 0, - mem_prev_index: 0, - mem: [0; MEMORY_SIZE], - data_remaining: None, - }; - - // Fill `data`, `prev_time`, `last_delta` and `last_delta2` with - // non-zero values. - ec.prev_time = timer(); - ec.gen_entropy(); - - // Do a single read from `self.mem` to make sure the Memory Access noise - // source is not optimised out. - // Note: this read is important, it effects optimisations for the entire - // module! - black_box(ec.mem[0]); - - ec - } - - /// Configures how many rounds are used to generate each 64-bit value. - /// This must be greater than zero, and has a big impact on performance - /// and output quality. - /// - /// `new_with_timer` conservatively uses 64 rounds, but often less rounds - /// can be used. The `test_timer()` function returns the minimum number of - /// rounds required for full strength (platform dependent), so one may use - /// `rng.set_rounds(rng.test_timer()?);` or cache the value. - pub fn set_rounds(&mut self, rounds: u32) { - assert!(rounds > 0); - self.rounds = rounds; - } - - // Calculate a random loop count used for the next round of an entropy - // collection, based on bits from a fresh value from the timer. - // - // The timer is folded to produce a number that contains at most `n_bits` - // bits. - // - // Note: A constant should be added to the resulting random loop count to - // prevent loops that run 0 times. - #[inline(never)] - fn random_loop_cnt(&mut self, n_bits: u32) -> u32 { - let mut rounds = 0; - - let mut time = (self.timer)(); - // Mix with the current state of the random number balance the random - // loop counter a bit more. - time ^= self.data; - - // We fold the time value as much as possible to ensure that as many - // bits of the time stamp are included as possible. - let folds = (64 + n_bits - 1) / n_bits; - let mask = (1 << n_bits) - 1; - for _ in 0..folds { - rounds ^= time & mask; - time = time >> n_bits; - } - - rounds as u32 - } - - // CPU jitter noise source - // Noise source based on the CPU execution time jitter - // - // This function injects the individual bits of the time value into the - // entropy pool using an LFSR. - // - // The code is deliberately inefficient with respect to the bit shifting. - // This function not only acts as folding operation, but this function's - // execution is used to measure the CPU execution time jitter. Any change to - // the loop in this function implies that careful retesting must be done. - #[inline(never)] - fn lfsr_time(&mut self, time: u64, var_rounds: bool) { - fn lfsr(mut data: u64, time: u64) -> u64{ - for i in 1..65 { - let mut tmp = time << (64 - i); - tmp = tmp >> (64 - 1); - - // Fibonacci LSFR with polynomial of - // x^64 + x^61 + x^56 + x^31 + x^28 + x^23 + 1 which is - // primitive according to - // http://poincare.matf.bg.ac.rs/~ezivkovm/publications/primpol1.pdf - // (the shift values are the polynomial values minus one - // due to counting bits from 0 to 63). As the current - // position is always the LSB, the polynomial only needs - // to shift data in from the left without wrap. - data ^= tmp; - data ^= (data >> 63) & 1; - data ^= (data >> 60) & 1; - data ^= (data >> 55) & 1; - data ^= (data >> 30) & 1; - data ^= (data >> 27) & 1; - data ^= (data >> 22) & 1; - data = data.rotate_left(1); - } - data - } - - // Note: in the reference implementation only the last round effects - // `self.data`, all the other results are ignored. To make sure the - // other rounds are not optimised out, we first run all but the last - // round on a throw-away value instead of the real `self.data`. - let mut lfsr_loop_cnt = 0; - if var_rounds { lfsr_loop_cnt = self.random_loop_cnt(4) }; - - let mut throw_away: u64 = 0; - for _ in 0..lfsr_loop_cnt { - throw_away = lfsr(throw_away, time); - } - black_box(throw_away); - - self.data = lfsr(self.data, time); - } - - // Memory Access noise source - // This is a noise source based on variations in memory access times - // - // This function performs memory accesses which will add to the timing - // variations due to an unknown amount of CPU wait states that need to be - // added when accessing memory. The memory size should be larger than the L1 - // caches as outlined in the documentation and the associated testing. - // - // The L1 cache has a very high bandwidth, albeit its access rate is usually - // slower than accessing CPU registers. Therefore, L1 accesses only add - // minimal variations as the CPU has hardly to wait. Starting with L2, - // significant variations are added because L2 typically does not belong to - // the CPU any more and therefore a wider range of CPU wait states is - // necessary for accesses. L3 and real memory accesses have even a wider - // range of wait states. However, to reliably access either L3 or memory, - // the `self.mem` memory must be quite large which is usually not desirable. - #[inline(never)] - fn memaccess(&mut self, var_rounds: bool) { - let mut acc_loop_cnt = 128; - if var_rounds { acc_loop_cnt += self.random_loop_cnt(4) }; - - let mut index = self.mem_prev_index; - for _ in 0..acc_loop_cnt { - // Addition of memblocksize - 1 to index with wrap around logic to - // ensure that every memory location is hit evenly. - // The modulus also allows the compiler to remove the indexing - // bounds check. - index = (index + MEMORY_BLOCKSIZE - 1) % MEMORY_SIZE; - - // memory access: just add 1 to one byte - // memory access implies read from and write to memory location - let tmp = self.mem[index]; - self.mem[index] = tmp.wrapping_add(1); - } - self.mem_prev_index = index; - } - - - // Stuck test by checking the: - // - 1st derivation of the jitter measurement (time delta) - // - 2nd derivation of the jitter measurement (delta of time deltas) - // - 3rd derivation of the jitter measurement (delta of delta of time - // deltas) - // - // All values must always be non-zero. - // This test is a heuristic to see whether the last measurement holds - // entropy. - fn stuck(&mut self, current_delta: i64) -> bool { - let delta2 = self.last_delta - current_delta; - let delta3 = delta2 - self.last_delta2; - - self.last_delta = current_delta; - self.last_delta2 = delta2; - - current_delta == 0 || delta2 == 0 || delta3 == 0 - } - - // This is the heart of the entropy generation: calculate time deltas and - // use the CPU jitter in the time deltas. The jitter is injected into the - // entropy pool. - // - // Ensure that `self.prev_time` is primed before using the output of this - // function. This can be done by calling this function and not using its - // result. - fn measure_jitter(&mut self) -> Option<()> { - // Invoke one noise source before time measurement to add variations - self.memaccess(true); - - // Get time stamp and calculate time delta to previous - // invocation to measure the timing variations - let time = (self.timer)(); - // Note: wrapping_sub combined with a cast to `i64` generates a correct - // delta, even in the unlikely case this is a timer that is not strictly - // monotonic. - let current_delta = time.wrapping_sub(self.prev_time) as i64; - self.prev_time = time; - - // Call the next noise source which also injects the data - self.lfsr_time(current_delta as u64, true); - - // Check whether we have a stuck measurement (i.e. does the last - // measurement holds entropy?). - if self.stuck(current_delta) { return None }; - - // Rotate the data buffer by a prime number (any odd number would - // do) to ensure that every bit position of the input time stamp - // has an even chance of being merged with a bit position in the - // entropy pool. We do not use one here as the adjacent bits in - // successive time deltas may have some form of dependency. The - // chosen value of 7 implies that the low 7 bits of the next - // time delta value is concatenated with the current time delta. - self.data = self.data.rotate_left(7); - - Some(()) - } - - // Shuffle the pool a bit by mixing some value with a bijective function - // (XOR) into the pool. - // - // The function generates a mixer value that depends on the bits set and - // the location of the set bits in the random number generated by the - // entropy source. Therefore, based on the generated random number, this - // mixer value can have 2^64 different values. That mixer value is - // initialized with the first two SHA-1 constants. After obtaining the - // mixer value, it is XORed into the random number. - // - // The mixer value is not assumed to contain any entropy. But due to the - // XOR operation, it can also not destroy any entropy present in the - // entropy pool. - #[inline(never)] - fn stir_pool(&mut self) { - // This constant is derived from the first two 32 bit initialization - // vectors of SHA-1 as defined in FIPS 180-4 section 5.3.1 - // The order does not really matter as we do not rely on the specific - // numbers. We just pick the SHA-1 constants as they have a good mix of - // bit set and unset. - const CONSTANT: u64 = 0x67452301efcdab89; - - // The start value of the mixer variable is derived from the third - // and fourth 32 bit initialization vector of SHA-1 as defined in - // FIPS 180-4 section 5.3.1 - let mut mixer = 0x98badcfe10325476; - - // This is a constant time function to prevent leaking timing - // information about the random number. - // The normal code is: - // ``` - // for i in 0..64 { - // if ((self.data >> i) & 1) == 1 { mixer ^= CONSTANT; } - // } - // ``` - // This is a bit fragile, as LLVM really wants to use branches here, and - // we rely on it to not recognise the opportunity. - for i in 0..64 { - let apply = (self.data >> i) & 1; - let mask = !apply.wrapping_sub(1); - mixer ^= CONSTANT & mask; - mixer = mixer.rotate_left(1); - } - - self.data ^= mixer; - } - - fn gen_entropy(&mut self) -> u64 { - // Prime `self.prev_time`, and run the noice sources to make sure the - // first loop round collects the expected entropy. - let _ = self.measure_jitter(); - - for _ in 0..self.rounds { - // If a stuck measurement is received, repeat measurement - // Note: we do not guard against an infinite loop, that would mean - // the timer suddenly became broken. - while self.measure_jitter().is_none() {} - } - - self.stir_pool(); - self.data - } - - /// Basic quality tests on the timer, by measuring CPU timing jitter a few - /// hundred times. - /// - /// If succesful, this will return the estimated number of rounds necessary - /// to collect 64 bits of entropy. Otherwise a `TimerError` with the cause - /// of the failure will be returned. - pub fn test_timer(&mut self) -> Result { - // We could add a check for system capabilities such as `clock_getres` - // or check for `CONFIG_X86_TSC`, but it does not make much sense as the - // following sanity checks verify that we have a high-resolution timer. - - #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] - return Err(TimerError::NoTimer); - - let mut delta_sum = 0; - let mut old_delta = 0; - - let mut time_backwards = 0; - let mut count_mod = 0; - let mut count_stuck = 0; - - // TESTLOOPCOUNT needs some loops to identify edge systems. - // 100 is definitely too little. - const TESTLOOPCOUNT: u64 = 300; - const CLEARCACHE: u64 = 100; - - for i in 0..(CLEARCACHE + TESTLOOPCOUNT) { - // Measure time delta of core entropy collection logic - let time = (self.timer)(); - self.memaccess(true); - self.lfsr_time(time, true); - let time2 = (self.timer)(); - - // Test whether timer works - if time == 0 || time2 == 0 { - return Err(TimerError::NoTimer); - } - let delta = time2.wrapping_sub(time) as i64; - - // Test whether timer is fine grained enough to provide delta even - // when called shortly after each other -- this implies that we also - // have a high resolution timer - if delta == 0 { - return Err(TimerError::CoarseTimer); - } - - // Up to here we did not modify any variable that will be - // evaluated later, but we already performed some work. Thus we - // already have had an impact on the caches, branch prediction, - // etc. with the goal to clear it to get the worst case - // measurements. - if i < CLEARCACHE { continue; } - - if self.stuck(delta) { count_stuck += 1; } - - // Test whether we have an increasing timer. - if !(time2 > time) { time_backwards += 1; } - - // Count the number of times the counter increases in steps of 100ns - // or greater. - if (delta % 100) == 0 { count_mod += 1; } - - // Ensure that we have a varying delta timer which is necessary for - // the calculation of entropy -- perform this check only after the - // first loop is executed as we need to prime the old_delta value - delta_sum += (delta - old_delta).abs() as u64; - old_delta = delta; - } - - // We allow the time to run backwards for up to three times. - // This can happen if the clock is being adjusted by NTP operations. - // If such an operation just happens to interfere with our test, it - // should not fail. The value of 3 should cover the NTP case being - // performed during our test run. - if time_backwards > 3 { - return Err(TimerError::NotMonotonic); - } - - // Test that the available amount of entropy per round does not get to - // low. We expect 1 bit of entropy per round as a reasonable minimum - // (although less is possible, it means the collector loop has to run - // much more often). - // `assert!(delta_average >= log2(1))` - // `assert!(delta_sum / TESTLOOPCOUNT >= 1)` - // `assert!(delta_sum >= TESTLOOPCOUNT)` - if delta_sum < TESTLOOPCOUNT { - return Err(TimerError::TinyVariantions); - } - - // Ensure that we have variations in the time stamp below 100 for at - // least 10% of all checks -- on some platforms, the counter increments - // in multiples of 100, but not always - if count_mod > (TESTLOOPCOUNT * 9 / 10) { - return Err(TimerError::CoarseTimer); - } - - // If we have more than 90% stuck results, then this Jitter RNG is - // likely to not work well. - if count_stuck > (TESTLOOPCOUNT * 9 / 10) { - return Err(TimerError::TooManyStuck); - } - - // Estimate the number of `measure_jitter` rounds necessary for 64 bits - // of entropy. - // - // We don't try very hard to come up with a good estimate of the - // available bits of entropy per round here for two reasons: - // 1. Simple estimates of the available bits (like Shannon entropy) are - // too optimistic. - // 2) Unless we want to waste a lot of time during intialization, there - // only a small number of samples are available. - // - // Therefore we use a very simple and conservative estimate: - // `let bits_of_entropy = log2(delta_average) / 2`. - // - // The number of rounds `measure_jitter` should run to collect 64 bits - // of entropy is `64 / bits_of_entropy`. - // - // To have smaller rounding errors, intermediate values are multiplied - // by `FACTOR`. To compensate for `log2` and division rounding down, - // add 1. - let delta_average = delta_sum / TESTLOOPCOUNT; - // println!("delta_average: {}", delta_average); - - const FACTOR: u32 = 3; - fn log2(x: u64) -> u32 { 64 - x.leading_zeros() } - - // pow(δ, FACTOR) must be representable; if you have overflow reduce FACTOR - Ok(64 * 2 * FACTOR / (log2(delta_average.pow(FACTOR)) + 1)) - } - - /// Statistical test: return the timer delta of one normal run of the - /// `JitterEntropy` entropy collector. - /// - /// Setting `var_rounds` to `true` will execute the memory access and the - /// CPU jitter noice sources a variable amount of times (just like a real - /// `JitterEntropy` round). - /// - /// Setting `var_rounds` to `false` will execute the noice sources the - /// minimal number of times. This can be used to measure the minimum amount - /// of entropy one round of entropy collector can collect in the worst case. - /// - /// # Example - /// - /// Use `timer_stats` to run the [NIST SP 800-90B Entropy Estimation Suite] - /// (https://github.com/usnistgov/SP800-90B_EntropyAssessment). - /// - /// This is the recommended way to test the quality of `JitterRng`. It - /// should be run before using the RNG on untested hardware, after changes - /// that could effect how the code is optimised, and after major compiler - /// compiler changes, like a new LLVM version. - /// - /// First generate two files `jitter_rng_var.bin` and `jitter_rng_var.min`. - /// - /// Execute `python noniid_main.py -v jitter_rng_var.bin 8`, and validate it - /// with `restart.py -v jitter_rng_var.bin 8 `. - /// This number is the expected amount of entropy that is at least available - /// for each round of the entropy collector. This number should be greater - /// than the amount estimated with `64 / test_timer()`. - /// - /// Execute `python noniid_main.py -v -u 4 jitter_rng_var.bin 4`, and - /// validate it with `restart.py -v -u 4 jitter_rng_var.bin 4 `. - /// This number is the expected amount of entropy that is available in the - /// last 4 bits of the timer delta after running noice sources. Note that - /// a value of 3.70 is the minimum estimated entropy for true randomness. - /// - /// Execute `python noniid_main.py -v -u 4 jitter_rng_var.bin 4`, and - /// validate it with `restart.py -v -u 4 jitter_rng_var.bin 4 `. - /// This number is the expected amount of entropy that is available to the - /// entropy collecter if both noice sources only run their minimal number of - /// times. This measures the absolute worst-case, and gives a lower bound - /// for the available entropy. - /// - /// ```rust,no_run - /// use rand::JitterRng; - /// - /// # use std::error::Error; - /// # use std::fs::File; - /// # use std::io::Write; - /// # - /// # fn try_main() -> Result<(), Box> { - /// 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 - /// } - /// - /// // Do not initialize with `JitterRng::new`, but with `new_with_timer`. - /// // 'new' always runst `test_timer`, and can therefore fail to - /// // initialize. We want to be able to get the statistics even when the - /// // timer test fails. - /// let mut rng = JitterRng::new_with_timer(get_nstime); - /// - /// // 1_000_000 results are required for the NIST SP 800-90B Entropy - /// // Estimation Suite - /// // FIXME: this number is smaller here, otherwise the Doc-test is too slow - /// const ROUNDS: usize = 10_000; - /// let mut deltas_variable: Vec = Vec::with_capacity(ROUNDS); - /// let mut deltas_minimal: Vec = Vec::with_capacity(ROUNDS); - /// - /// for _ in 0..ROUNDS { - /// deltas_variable.push(rng.timer_stats(true) as u8); - /// deltas_minimal.push(rng.timer_stats(false) as u8); - /// } - /// - /// // Write out after the statistics collection loop, to not disturb the - /// // test results. - /// File::create("jitter_rng_var.bin")?.write(&deltas_variable)?; - /// File::create("jitter_rng_min.bin")?.write(&deltas_minimal)?; - /// # - /// # Ok(()) - /// # } - /// # - /// # fn main() { - /// # try_main().unwrap(); - /// # } - /// ``` - #[cfg(feature="std")] - pub fn timer_stats(&mut self, var_rounds: bool) -> i64 { - let time = platform::get_nstime(); - self.memaccess(var_rounds); - self.lfsr_time(time, var_rounds); - let time2 = platform::get_nstime(); - time2.wrapping_sub(time) as i64 - } -} - -#[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"))] - 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(target_os = "windows")] - pub fn get_nstime() -> u64 { - extern crate winapi; - unsafe { - let mut t = super::mem::zeroed(); - winapi::um::profileapi::QueryPerformanceCounter(&mut t); - *t.QuadPart() 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 -// elimination. Taken from `bencher`. -fn black_box(dummy: T) -> T { - unsafe { - let ret = ptr::read_volatile(&dummy); - mem::forget(dummy); - ret - } -} - -impl Rng for JitterRng { - fn next_u32(&mut self) -> u32 { - // We want to use both parts of the generated entropy - if let Some(high) = self.data_remaining.take() { - high - } else { - let data = self.next_u64(); - self.data_remaining = Some((data >> 32) as u32); - data as u32 - } - } - - fn next_u64(&mut self) -> u64 { - self.gen_entropy() - } - - fn fill_bytes(&mut self, dest: &mut [u8]) { - let mut left = dest; - while left.len() >= 8 { - let (l, r) = {left}.split_at_mut(8); - left = r; - let chunk: [u8; 8] = unsafe { - mem::transmute(self.next_u64().to_le()) - }; - l.copy_from_slice(&chunk); - } - let n = left.len(); - if n > 0 { - let chunk: [u8; 8] = unsafe { - mem::transmute(self.next_u64().to_le()) - }; - left.copy_from_slice(&chunk[..n]); - } - } -} - -// There are no tests included because (1) this is an "external" RNG, so output -// is not reproducible and (2) `test_timer` *will* fail on some platforms. +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +// +// Based on jitterentropy-library, http://www.chronox.de/jent.html. +// Copyright Stephan Mueller , 2014 - 2017. +// +// With permission from Stephan Mueller to relicense the Rust translation under +// the MIT license. + +//! Non-physical true random number generator based on timing jitter. + +use {CryptoRng, Rng, Error, ErrorKind}; +use rand_core::impls; + +use core::{fmt, mem, ptr}; +#[cfg(feature="std")] +use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering}; + +const MEMORY_BLOCKS: usize = 64; +const MEMORY_BLOCKSIZE: usize = 32; +const MEMORY_SIZE: usize = MEMORY_BLOCKS * MEMORY_BLOCKSIZE; + +/// A true random number generator based on jitter in the CPU execution time, +/// and jitter in memory access time. +/// +/// This is a true random number generator, as opposed to pseudo-random +/// generators. Random numbers generated by `JitterRng` can be seen as fresh +/// entropy. A consequence is that is orders of magnitude slower than `OsRng` +/// and PRNGs (about 10^3 .. 10^6 slower). +/// +/// There are very few situations where using this RNG is appropriate. Only very +/// few applications require true entropy. A normal PRNG can be statistically +/// indistinguishable, and a cryptographic PRNG should also be as impossible to +/// predict. +/// +/// Use of `JitterRng` is recommended for initializing cryptographic PRNGs when +/// `OsRng` is not available. +/// +/// This implementation is based on +/// [Jitterentropy](http://www.chronox.de/jent.html) version 2.1.0. +// +// Note: the C implementation relies on being compiled without optimizations. +// This implementation goes through lengths to make the compiler not optimise +// out what is technically dead code, but that does influence timing jitter. +pub struct JitterRng { + data: u64, // Actual random number + // Number of rounds to run the entropy collector per 64 bits + rounds: u32, + // Timer and previous time stamp, used by `measure_jitter` + timer: fn() -> u64, + prev_time: u64, + // Deltas used for the stuck test + last_delta: i64, + last_delta2: i64, + // Memory for the Memory Access noise source + mem_prev_index: usize, + mem: [u8; MEMORY_SIZE], + // Make `next_u32` not waste 32 bits + data_remaining: Option, +} + +// Custom Debug implementation that does not expose the internal state +impl fmt::Debug for JitterRng { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "JitterRng {{}}") + } +} + +/// An error that can occur when `test_timer` fails. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum TimerError { + /// No timer available. + NoTimer, + /// Timer too coarse to use as an entropy source. + CoarseTimer, + /// Timer is not monotonically increasing. + NotMonotonic, + /// Variations of deltas of time too small. + TinyVariantions, + /// Too many stuck results (indicating no added entropy). + TooManyStuck, + #[doc(hidden)] + __Nonexhaustive, +} + +impl TimerError { + fn description(&self) -> &'static str { + match *self { + TimerError::NoTimer => "no timer available", + TimerError::CoarseTimer => "coarse timer", + TimerError::NotMonotonic => "timer not monotonic", + TimerError::TinyVariantions => "time delta variations too small", + TimerError::TooManyStuck => "too many stuck results", + TimerError::__Nonexhaustive => unreachable!(), + } + } +} + +impl fmt::Display for TimerError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.description()) + } +} + +#[cfg(feature="std")] +impl ::std::error::Error for TimerError { + fn description(&self) -> &str { + self.description() + } +} + +impl From for Error { + fn from(err: TimerError) -> Error { + Error::with_cause(ErrorKind::Unavailable, + "timer jitter failed basic quality tests", err) + } +} + +// Initialise to zero; must be positive +#[cfg(feature="std")] +static JITTER_ROUNDS: AtomicUsize = ATOMIC_USIZE_INIT; + +impl JitterRng { + /// Create a new `JitterRng`. + /// Makes use of `std::time` for a timer. + /// + /// 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 { + 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. + // This allows the timer test to run multiple times; we don't care. + rounds = ec.test_timer()?; + JITTER_ROUNDS.store(rounds as usize, Ordering::Relaxed); + } + ec.set_rounds(rounds); + Ok(ec) + } + + /// Create a new `JitterRng`. + /// A custom timer can be supplied, making it possible to use `JitterRng` in + /// `no_std` environments. + /// + /// The timer must have nanosecond precision. + /// + /// This method is more low-level than `new()`. It is the responsibility of + /// the caller to run `test_timer` before using any numbers generated with + /// `JitterRng`, and optionally call `set_rounds()`. + pub fn new_with_timer(timer: fn() -> u64) -> JitterRng { + let mut ec = JitterRng { + data: 0, + rounds: 64, + timer: timer, + prev_time: 0, + last_delta: 0, + last_delta2: 0, + mem_prev_index: 0, + mem: [0; MEMORY_SIZE], + data_remaining: None, + }; + + // Fill `data`, `prev_time`, `last_delta` and `last_delta2` with + // non-zero values. + ec.prev_time = timer(); + ec.gen_entropy(); + + // Do a single read from `self.mem` to make sure the Memory Access noise + // source is not optimised out. + // Note: this read is important, it effects optimisations for the entire + // module! + black_box(ec.mem[0]); + + ec + } + + /// Configures how many rounds are used to generate each 64-bit value. + /// This must be greater than zero, and has a big impact on performance + /// and output quality. + /// + /// `new_with_timer` conservatively uses 64 rounds, but often less rounds + /// can be used. The `test_timer()` function returns the minimum number of + /// rounds required for full strength (platform dependent), so one may use + /// `rng.set_rounds(rng.test_timer()?);` or cache the value. + pub fn set_rounds(&mut self, rounds: u32) { + assert!(rounds > 0); + self.rounds = rounds; + } + + // Calculate a random loop count used for the next round of an entropy + // collection, based on bits from a fresh value from the timer. + // + // The timer is folded to produce a number that contains at most `n_bits` + // bits. + // + // Note: A constant should be added to the resulting random loop count to + // prevent loops that run 0 times. + #[inline(never)] + fn random_loop_cnt(&mut self, n_bits: u32) -> u32 { + let mut rounds = 0; + + let mut time = (self.timer)(); + // Mix with the current state of the random number balance the random + // loop counter a bit more. + time ^= self.data; + + // We fold the time value as much as possible to ensure that as many + // bits of the time stamp are included as possible. + let folds = (64 + n_bits - 1) / n_bits; + let mask = (1 << n_bits) - 1; + for _ in 0..folds { + rounds ^= time & mask; + time = time >> n_bits; + } + + rounds as u32 + } + + // CPU jitter noise source + // Noise source based on the CPU execution time jitter + // + // This function injects the individual bits of the time value into the + // entropy pool using an LFSR. + // + // The code is deliberately inefficient with respect to the bit shifting. + // This function not only acts as folding operation, but this function's + // execution is used to measure the CPU execution time jitter. Any change to + // the loop in this function implies that careful retesting must be done. + #[inline(never)] + fn lfsr_time(&mut self, time: u64, var_rounds: bool) { + fn lfsr(mut data: u64, time: u64) -> u64{ + for i in 1..65 { + let mut tmp = time << (64 - i); + tmp = tmp >> (64 - 1); + + // Fibonacci LSFR with polynomial of + // x^64 + x^61 + x^56 + x^31 + x^28 + x^23 + 1 which is + // primitive according to + // http://poincare.matf.bg.ac.rs/~ezivkovm/publications/primpol1.pdf + // (the shift values are the polynomial values minus one + // due to counting bits from 0 to 63). As the current + // position is always the LSB, the polynomial only needs + // to shift data in from the left without wrap. + data ^= tmp; + data ^= (data >> 63) & 1; + data ^= (data >> 60) & 1; + data ^= (data >> 55) & 1; + data ^= (data >> 30) & 1; + data ^= (data >> 27) & 1; + data ^= (data >> 22) & 1; + data = data.rotate_left(1); + } + data + } + + // Note: in the reference implementation only the last round effects + // `self.data`, all the other results are ignored. To make sure the + // other rounds are not optimised out, we first run all but the last + // round on a throw-away value instead of the real `self.data`. + let mut lfsr_loop_cnt = 0; + if var_rounds { lfsr_loop_cnt = self.random_loop_cnt(4) }; + + let mut throw_away: u64 = 0; + for _ in 0..lfsr_loop_cnt { + throw_away = lfsr(throw_away, time); + } + black_box(throw_away); + + self.data = lfsr(self.data, time); + } + + // Memory Access noise source + // This is a noise source based on variations in memory access times + // + // This function performs memory accesses which will add to the timing + // variations due to an unknown amount of CPU wait states that need to be + // added when accessing memory. The memory size should be larger than the L1 + // caches as outlined in the documentation and the associated testing. + // + // The L1 cache has a very high bandwidth, albeit its access rate is usually + // slower than accessing CPU registers. Therefore, L1 accesses only add + // minimal variations as the CPU has hardly to wait. Starting with L2, + // significant variations are added because L2 typically does not belong to + // the CPU any more and therefore a wider range of CPU wait states is + // necessary for accesses. L3 and real memory accesses have even a wider + // range of wait states. However, to reliably access either L3 or memory, + // the `self.mem` memory must be quite large which is usually not desirable. + #[inline(never)] + fn memaccess(&mut self, var_rounds: bool) { + let mut acc_loop_cnt = 128; + if var_rounds { acc_loop_cnt += self.random_loop_cnt(4) }; + + let mut index = self.mem_prev_index; + for _ in 0..acc_loop_cnt { + // Addition of memblocksize - 1 to index with wrap around logic to + // ensure that every memory location is hit evenly. + // The modulus also allows the compiler to remove the indexing + // bounds check. + index = (index + MEMORY_BLOCKSIZE - 1) % MEMORY_SIZE; + + // memory access: just add 1 to one byte + // memory access implies read from and write to memory location + let tmp = self.mem[index]; + self.mem[index] = tmp.wrapping_add(1); + } + self.mem_prev_index = index; + } + + + // Stuck test by checking the: + // - 1st derivation of the jitter measurement (time delta) + // - 2nd derivation of the jitter measurement (delta of time deltas) + // - 3rd derivation of the jitter measurement (delta of delta of time + // deltas) + // + // All values must always be non-zero. + // This test is a heuristic to see whether the last measurement holds + // entropy. + fn stuck(&mut self, current_delta: i64) -> bool { + let delta2 = self.last_delta - current_delta; + let delta3 = delta2 - self.last_delta2; + + self.last_delta = current_delta; + self.last_delta2 = delta2; + + current_delta == 0 || delta2 == 0 || delta3 == 0 + } + + // This is the heart of the entropy generation: calculate time deltas and + // use the CPU jitter in the time deltas. The jitter is injected into the + // entropy pool. + // + // Ensure that `self.prev_time` is primed before using the output of this + // function. This can be done by calling this function and not using its + // result. + fn measure_jitter(&mut self) -> Option<()> { + // Invoke one noise source before time measurement to add variations + self.memaccess(true); + + // Get time stamp and calculate time delta to previous + // invocation to measure the timing variations + let time = (self.timer)(); + // Note: wrapping_sub combined with a cast to `i64` generates a correct + // delta, even in the unlikely case this is a timer that is not strictly + // monotonic. + let current_delta = time.wrapping_sub(self.prev_time) as i64; + self.prev_time = time; + + // Call the next noise source which also injects the data + self.lfsr_time(current_delta as u64, true); + + // Check whether we have a stuck measurement (i.e. does the last + // measurement holds entropy?). + if self.stuck(current_delta) { return None }; + + // Rotate the data buffer by a prime number (any odd number would + // do) to ensure that every bit position of the input time stamp + // has an even chance of being merged with a bit position in the + // entropy pool. We do not use one here as the adjacent bits in + // successive time deltas may have some form of dependency. The + // chosen value of 7 implies that the low 7 bits of the next + // time delta value is concatenated with the current time delta. + self.data = self.data.rotate_left(7); + + Some(()) + } + + // Shuffle the pool a bit by mixing some value with a bijective function + // (XOR) into the pool. + // + // The function generates a mixer value that depends on the bits set and + // the location of the set bits in the random number generated by the + // entropy source. Therefore, based on the generated random number, this + // mixer value can have 2^64 different values. That mixer value is + // initialized with the first two SHA-1 constants. After obtaining the + // mixer value, it is XORed into the random number. + // + // The mixer value is not assumed to contain any entropy. But due to the + // XOR operation, it can also not destroy any entropy present in the + // entropy pool. + #[inline(never)] + fn stir_pool(&mut self) { + // This constant is derived from the first two 32 bit initialization + // vectors of SHA-1 as defined in FIPS 180-4 section 5.3.1 + // The order does not really matter as we do not rely on the specific + // numbers. We just pick the SHA-1 constants as they have a good mix of + // bit set and unset. + const CONSTANT: u64 = 0x67452301efcdab89; + + // The start value of the mixer variable is derived from the third + // and fourth 32 bit initialization vector of SHA-1 as defined in + // FIPS 180-4 section 5.3.1 + let mut mixer = 0x98badcfe10325476; + + // This is a constant time function to prevent leaking timing + // information about the random number. + // The normal code is: + // ``` + // for i in 0..64 { + // if ((self.data >> i) & 1) == 1 { mixer ^= CONSTANT; } + // } + // ``` + // This is a bit fragile, as LLVM really wants to use branches here, and + // we rely on it to not recognise the opportunity. + for i in 0..64 { + let apply = (self.data >> i) & 1; + let mask = !apply.wrapping_sub(1); + mixer ^= CONSTANT & mask; + mixer = mixer.rotate_left(1); + } + + self.data ^= mixer; + } + + fn gen_entropy(&mut self) -> u64 { + // Prime `self.prev_time`, and run the noice sources to make sure the + // first loop round collects the expected entropy. + let _ = self.measure_jitter(); + + for _ in 0..self.rounds { + // If a stuck measurement is received, repeat measurement + // Note: we do not guard against an infinite loop, that would mean + // the timer suddenly became broken. + while self.measure_jitter().is_none() {} + } + + self.stir_pool(); + self.data + } + + /// Basic quality tests on the timer, by measuring CPU timing jitter a few + /// hundred times. + /// + /// If succesful, this will return the estimated number of rounds necessary + /// to collect 64 bits of entropy. Otherwise a `TimerError` with the cause + /// of the failure will be returned. + pub fn test_timer(&mut self) -> Result { + // We could add a check for system capabilities such as `clock_getres` + // or check for `CONFIG_X86_TSC`, but it does not make much sense as the + // following sanity checks verify that we have a high-resolution timer. + + #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] + return Err(TimerError::NoTimer); + + let mut delta_sum = 0; + let mut old_delta = 0; + + let mut time_backwards = 0; + let mut count_mod = 0; + let mut count_stuck = 0; + + // TESTLOOPCOUNT needs some loops to identify edge systems. + // 100 is definitely too little. + const TESTLOOPCOUNT: u64 = 300; + const CLEARCACHE: u64 = 100; + + for i in 0..(CLEARCACHE + TESTLOOPCOUNT) { + // Measure time delta of core entropy collection logic + let time = (self.timer)(); + self.memaccess(true); + self.lfsr_time(time, true); + let time2 = (self.timer)(); + + // Test whether timer works + if time == 0 || time2 == 0 { + return Err(TimerError::NoTimer); + } + let delta = time2.wrapping_sub(time) as i64; + + // Test whether timer is fine grained enough to provide delta even + // when called shortly after each other -- this implies that we also + // have a high resolution timer + if delta == 0 { + return Err(TimerError::CoarseTimer); + } + + // Up to here we did not modify any variable that will be + // evaluated later, but we already performed some work. Thus we + // already have had an impact on the caches, branch prediction, + // etc. with the goal to clear it to get the worst case + // measurements. + if i < CLEARCACHE { continue; } + + if self.stuck(delta) { count_stuck += 1; } + + // Test whether we have an increasing timer. + if !(time2 > time) { time_backwards += 1; } + + // Count the number of times the counter increases in steps of 100ns + // or greater. + if (delta % 100) == 0 { count_mod += 1; } + + // Ensure that we have a varying delta timer which is necessary for + // the calculation of entropy -- perform this check only after the + // first loop is executed as we need to prime the old_delta value + delta_sum += (delta - old_delta).abs() as u64; + old_delta = delta; + } + + // We allow the time to run backwards for up to three times. + // This can happen if the clock is being adjusted by NTP operations. + // If such an operation just happens to interfere with our test, it + // should not fail. The value of 3 should cover the NTP case being + // performed during our test run. + if time_backwards > 3 { + return Err(TimerError::NotMonotonic); + } + + // Test that the available amount of entropy per round does not get to + // low. We expect 1 bit of entropy per round as a reasonable minimum + // (although less is possible, it means the collector loop has to run + // much more often). + // `assert!(delta_average >= log2(1))` + // `assert!(delta_sum / TESTLOOPCOUNT >= 1)` + // `assert!(delta_sum >= TESTLOOPCOUNT)` + if delta_sum < TESTLOOPCOUNT { + return Err(TimerError::TinyVariantions); + } + + // Ensure that we have variations in the time stamp below 100 for at + // least 10% of all checks -- on some platforms, the counter increments + // in multiples of 100, but not always + if count_mod > (TESTLOOPCOUNT * 9 / 10) { + return Err(TimerError::CoarseTimer); + } + + // If we have more than 90% stuck results, then this Jitter RNG is + // likely to not work well. + if count_stuck > (TESTLOOPCOUNT * 9 / 10) { + return Err(TimerError::TooManyStuck); + } + + // Estimate the number of `measure_jitter` rounds necessary for 64 bits + // of entropy. + // + // We don't try very hard to come up with a good estimate of the + // available bits of entropy per round here for two reasons: + // 1. Simple estimates of the available bits (like Shannon entropy) are + // too optimistic. + // 2) Unless we want to waste a lot of time during intialization, there + // only a small number of samples are available. + // + // Therefore we use a very simple and conservative estimate: + // `let bits_of_entropy = log2(delta_average) / 2`. + // + // The number of rounds `measure_jitter` should run to collect 64 bits + // of entropy is `64 / bits_of_entropy`. + // + // To have smaller rounding errors, intermediate values are multiplied + // by `FACTOR`. To compensate for `log2` and division rounding down, + // add 1. + let delta_average = delta_sum / TESTLOOPCOUNT; + // println!("delta_average: {}", delta_average); + + const FACTOR: u32 = 3; + fn log2(x: u64) -> u32 { 64 - x.leading_zeros() } + + // pow(δ, FACTOR) must be representable; if you have overflow reduce FACTOR + Ok(64 * 2 * FACTOR / (log2(delta_average.pow(FACTOR)) + 1)) + } + + /// Statistical test: return the timer delta of one normal run of the + /// `JitterEntropy` entropy collector. + /// + /// Setting `var_rounds` to `true` will execute the memory access and the + /// CPU jitter noice sources a variable amount of times (just like a real + /// `JitterEntropy` round). + /// + /// Setting `var_rounds` to `false` will execute the noice sources the + /// minimal number of times. This can be used to measure the minimum amount + /// of entropy one round of entropy collector can collect in the worst case. + /// + /// # Example + /// + /// Use `timer_stats` to run the [NIST SP 800-90B Entropy Estimation Suite] + /// (https://github.com/usnistgov/SP800-90B_EntropyAssessment). + /// + /// This is the recommended way to test the quality of `JitterRng`. It + /// should be run before using the RNG on untested hardware, after changes + /// that could effect how the code is optimised, and after major compiler + /// compiler changes, like a new LLVM version. + /// + /// First generate two files `jitter_rng_var.bin` and `jitter_rng_var.min`. + /// + /// Execute `python noniid_main.py -v jitter_rng_var.bin 8`, and validate it + /// with `restart.py -v jitter_rng_var.bin 8 `. + /// This number is the expected amount of entropy that is at least available + /// for each round of the entropy collector. This number should be greater + /// than the amount estimated with `64 / test_timer()`. + /// + /// Execute `python noniid_main.py -v -u 4 jitter_rng_var.bin 4`, and + /// validate it with `restart.py -v -u 4 jitter_rng_var.bin 4 `. + /// This number is the expected amount of entropy that is available in the + /// last 4 bits of the timer delta after running noice sources. Note that + /// a value of 3.70 is the minimum estimated entropy for true randomness. + /// + /// Execute `python noniid_main.py -v -u 4 jitter_rng_var.bin 4`, and + /// validate it with `restart.py -v -u 4 jitter_rng_var.bin 4 `. + /// This number is the expected amount of entropy that is available to the + /// entropy collecter if both noice sources only run their minimal number of + /// times. This measures the absolute worst-case, and gives a lower bound + /// for the available entropy. + /// + /// ```rust,no_run + /// use rand::JitterRng; + /// + /// # use std::error::Error; + /// # use std::fs::File; + /// # use std::io::Write; + /// # + /// # fn try_main() -> Result<(), Box> { + /// 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 + /// } + /// + /// // Do not initialize with `JitterRng::new`, but with `new_with_timer`. + /// // 'new' always runst `test_timer`, and can therefore fail to + /// // initialize. We want to be able to get the statistics even when the + /// // timer test fails. + /// let mut rng = JitterRng::new_with_timer(get_nstime); + /// + /// // 1_000_000 results are required for the NIST SP 800-90B Entropy + /// // Estimation Suite + /// // FIXME: this number is smaller here, otherwise the Doc-test is too slow + /// const ROUNDS: usize = 10_000; + /// let mut deltas_variable: Vec = Vec::with_capacity(ROUNDS); + /// let mut deltas_minimal: Vec = Vec::with_capacity(ROUNDS); + /// + /// for _ in 0..ROUNDS { + /// deltas_variable.push(rng.timer_stats(true) as u8); + /// deltas_minimal.push(rng.timer_stats(false) as u8); + /// } + /// + /// // Write out after the statistics collection loop, to not disturb the + /// // test results. + /// File::create("jitter_rng_var.bin")?.write(&deltas_variable)?; + /// File::create("jitter_rng_min.bin")?.write(&deltas_minimal)?; + /// # + /// # Ok(()) + /// # } + /// # + /// # fn main() { + /// # try_main().unwrap(); + /// # } + /// ``` + #[cfg(feature="std")] + pub fn timer_stats(&mut self, var_rounds: bool) -> i64 { + let time = platform::get_nstime(); + self.memaccess(var_rounds); + self.lfsr_time(time, var_rounds); + let time2 = platform::get_nstime(); + time2.wrapping_sub(time) as i64 + } +} + +#[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"))] + 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(target_os = "windows")] + pub fn get_nstime() -> u64 { + extern crate winapi; + unsafe { + let mut t = super::mem::zeroed(); + winapi::um::profileapi::QueryPerformanceCounter(&mut t); + *t.QuadPart() 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 +// elimination. Taken from `bencher`. +fn black_box(dummy: T) -> T { + unsafe { + let ret = ptr::read_volatile(&dummy); + mem::forget(dummy); + ret + } +} + +impl Rng for JitterRng { + fn next_u32(&mut self) -> u32 { + // We want to use both parts of the generated entropy + if let Some(high) = self.data_remaining.take() { + high + } else { + let data = self.next_u64(); + self.data_remaining = Some((data >> 32) as u32); + data as u32 + } + } + + fn next_u64(&mut self) -> u64 { + self.gen_entropy() + } + + #[cfg(feature = "i128_support")] + fn next_u128(&mut self) -> u128 { + impls::next_u128_via_u64(self) + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + impls::fill_bytes_via_u64(self, dest) + } + + fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { + Ok(self.fill_bytes(dest)) + } +} + +impl CryptoRng for JitterRng {} + +// There are no tests included because (1) this is an "external" RNG, so output +// is not reproducible and (2) `test_timer` *will* fail on some platforms. diff --git a/src/lib.rs b/src/lib.rs index 996c12b7a2f..037b6a69b33 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,14 +10,11 @@ //! Utilities for random number generation //! -//! The key functions are `random()` and `Rng::gen()`. These are polymorphic and -//! so can be used to generate any type that implements `Rand`. Type inference -//! means that often a simple call to `rand::random()` or `rng.gen()` will -//! suffice, but sometimes an annotation is required, e.g. -//! `rand::random::()`. -//! -//! See the `distributions` submodule for sampling random numbers from -//! distributions like normal and exponential. +//! The `Rng` trait covers random number generation, and can be used directly +//! to produce values of some core types (`u32, u64, f32, f64`, and byte +//! strings). To generate anything else, or to generate values of the above type +//! in generic code, use the `distributions` module. This includes +//! distributions like ranges, normal and exponential. //! //! # Usage //! @@ -26,7 +23,7 @@ //! //! ```toml //! [dependencies] -//! rand = "0.3" +//! rand = "0.4" //! ``` //! //! and this to your crate root: @@ -38,8 +35,8 @@ //! # Thread-local RNG //! //! There is built-in support for a RNG associated with each thread stored -//! in thread-local storage. This RNG can be accessed via `thread_rng`, or -//! used implicitly via `random`. This RNG is normally randomly seeded +//! in thread-local storage. This RNG can be accessed via `thread_rng`. +//! This RNG is normally randomly seeded //! from an operating-system source of randomness, e.g. `/dev/urandom` on //! Unix systems, and will automatically reseed itself from this source //! after generating 32 KiB of random data. @@ -79,16 +76,20 @@ //! # Examples //! //! ```rust -//! use rand::Rng; +//! use rand::random; //! -//! let mut rng = rand::thread_rng(); -//! if rng.gen() { // random bool -//! println!("i32: {}, u32: {}", rng.gen::(), rng.gen::()) +//! if random() { // random bool +//! let a: i32 = random(); +//! let b: u32 = random(); +//! println!("i32: {}, u32: {}", a, b) //! } //! ``` //! //! ```rust -//! let tuple = rand::random::<(f64, char)>(); +//! use rand::{Sample, thread_rng}; +//! use rand::distributions::{Uniform, Uniform01}; +//! let mut rng = thread_rng(); +//! let tuple: (f64, u8) = (rng.sample(Uniform01), rng.sample(Uniform)); //! println!("{:?}", tuple) //! ``` //! @@ -110,18 +111,19 @@ //! and multiply this fraction by 4. //! //! ``` -//! use rand::distributions::{IndependentSample, Range}; +//! use rand::{thread_rng, Sample}; +//! use rand::distributions::{Range}; //! //! fn main() { //! let between = Range::new(-1f64, 1.); -//! let mut rng = rand::thread_rng(); +//! let mut rng = thread_rng(); //! //! let total = 1_000_000; //! let mut in_circle = 0; //! //! for _ in 0..total { -//! let a = between.ind_sample(&mut rng); -//! let b = between.ind_sample(&mut rng); +//! let a = rng.sample(between); +//! let b = rng.sample(between); //! if a*a + b*b <= 1. { //! in_circle += 1; //! } @@ -152,8 +154,10 @@ //! [Monty Hall Problem]: http://en.wikipedia.org/wiki/Monty_Hall_problem //! //! ``` -//! use rand::Rng; -//! use rand::distributions::{IndependentSample, Range}; +//! use rand::{Rng, Sample}; +//! use rand::distributions::{Distribution, Range}; +//! use rand::distributions::range::RangeInt; +//! use rand::sequences::Choose; //! //! struct SimulationResult { //! win: bool, @@ -161,12 +165,12 @@ //! } //! //! // Run a single simulation of the Monty Hall problem. -//! fn simulate(random_door: &Range, rng: &mut R) +//! fn simulate(random_door: &Range>, rng: &mut R) //! -> SimulationResult { -//! let car = random_door.ind_sample(rng); +//! let car = random_door.sample(rng); //! //! // This is our initial choice -//! let mut choice = random_door.ind_sample(rng); +//! let mut choice = random_door.sample(rng); //! //! // The game host opens a door //! let open = game_host_open(car, choice, rng); @@ -184,7 +188,7 @@ //! // where the car is. The game host will never open the door with the car. //! fn game_host_open(car: u32, choice: u32, rng: &mut R) -> u32 { //! let choices = free_doors(&[car, choice]); -//! rand::seq::sample_slice(rng, &choices, 1)[0] +//! *choices[..].choose(rng).unwrap() //! } //! //! // Returns the door we switch to, given our current choice and @@ -202,7 +206,7 @@ //! let num_simulations = 10000; //! //! let mut rng = rand::thread_rng(); -//! let random_door = Range::new(0, 3); +//! let random_door = Range::new(0u32, 3); //! //! let (mut switch_wins, mut switch_losses) = (0, 0); //! let (mut keep_wins, mut keep_losses) = (0, 0); @@ -249,753 +253,273 @@ #[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 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; +extern crate rand_core; + +// core traits and types +pub use rand_core::{Rng, CryptoRng, SeedableRng, Error, ErrorKind}; // external rngs pub use jitter::JitterRng; +#[cfg(feature="std")] pub use read::ReadRng; #[cfg(feature="std")] pub use os::OsRng; -// pseudo rngs -pub use isaac::{IsaacRng, Isaac64Rng}; -pub use chacha::ChaChaRng; -pub use prng::XorShiftRng; +// extensions +pub use iter::iter; +pub use distributions::{Distribution, Default, Rand}; +#[cfg(feature="std")] +pub use thread_local::{ThreadRng, thread_rng, random, random_with}; // local use declarations -#[cfg(target_pointer_width = "32")] -use prng::IsaacRng as IsaacWordRng; -#[cfg(target_pointer_width = "64")] -use prng::Isaac64Rng as IsaacWordRng; +use prng::IsaacWordRng; +use distributions::range::Range; -use distributions::{Range, IndependentSample}; -use distributions::range::SampleRange; +// macro: copied from `arrayref` crate +macro_rules! array_ref { + ($arr:expr, $offset:expr, $len:expr) => {{ + { + #[inline] + unsafe fn as_array(slice: &[T]) -> &[T; $len] { + &*(slice.as_ptr() as *const [_; $len]) + } + let offset = $offset; + let slice = & $arr[offset..offset + $len]; + unsafe { + as_array(slice) + } + } + }} +} // public modules pub mod distributions; +pub mod iter; pub mod jitter; -#[cfg(feature="std")] pub mod os; -#[cfg(feature="std")] pub mod read; +pub mod mock; +pub mod prng; pub mod reseeding; -#[cfg(any(feature="std", feature = "alloc"))] pub mod seq; - -// These tiny modules are here to avoid API breakage, probably only temporarily -pub mod chacha { - //! The ChaCha random number generator. - pub use prng::ChaChaRng; -} -pub mod isaac { - //! The ISAAC random number generator. - pub use prng::{IsaacRng, Isaac64Rng}; -} +// TODO: move sequences code to seq maybe? +#[cfg(any(feature="alloc", feature="std"))] +pub mod seq; +pub mod sequences; +pub mod utils; // private modules -mod rand_impls; -mod prng; - - -/// A type that can be randomly generated using an `Rng`. -/// -/// ## Built-in Implementations -/// -/// This crate implements `Rand` for various primitive types. Assuming the -/// provided `Rng` is well-behaved, these implementations generate values with -/// the following ranges and distributions: -/// -/// * Integers (`i32`, `u32`, `isize`, `usize`, etc.): Uniformly distributed -/// over all values of the type. -/// * `char`: Uniformly distributed over all Unicode scalar values, i.e. all -/// code points in the range `0...0x10_FFFF`, except for the range -/// `0xD800...0xDFFF` (the surrogate code points). This includes -/// unassigned/reserved code points. -/// * `bool`: Generates `false` or `true`, each with probability 0.5. -/// * Floating point types (`f32` and `f64`): Uniformly distributed in the -/// half-open range `[0, 1)`. (The [`Open01`], [`Closed01`], [`Exp1`], and -/// [`StandardNormal`] wrapper types produce floating point numbers with -/// alternative ranges or distributions.) -/// -/// [`Open01`]: struct.Open01.html -/// [`Closed01`]: struct.Closed01.html -/// [`Exp1`]: distributions/exponential/struct.Exp1.html -/// [`StandardNormal`]: distributions/normal/struct.StandardNormal.html -/// -/// The following aggregate types also implement `Rand` as long as their -/// component types implement it: -/// -/// * Tuples and arrays: Each element of the tuple or array is generated -/// independently, using its own `Rand` implementation. -/// * `Option`: Returns `None` with probability 0.5; otherwise generates a -/// random `T` and returns `Some(T)`. -pub trait Rand : Sized { - /// Generates a random instance of this type using the specified source of - /// randomness. - fn rand(rng: &mut R) -> Self; +#[cfg(feature="std")] +mod os; +#[cfg(feature="std")] +mod read; +#[cfg(feature="std")] +mod thread_local; + + +/// Seeding mechanism for PRNGs, providing a `new` function. +/// This is the recommended way to create (pseudo) random number generators, +/// unless a deterministic seed is desired (in which case +/// `SeedableRng::from_seed` should be used). +/// +/// Note: this trait is automatically implemented for any PRNG implementing +/// `SeedableRng` and is not intended to be implemented by users. +/// +/// ## Example +/// +/// ``` +/// use rand::{StdRng, Sample, NewSeeded}; +/// +/// let mut rng = StdRng::new().unwrap(); +/// println!("Random die roll: {}", rng.gen_range(1, 7)); +/// ``` +#[cfg(feature="std")] +pub trait NewSeeded: SeedableRng { + /// Creates a new instance, automatically seeded with fresh entropy. + /// + /// Normally this will use `OsRng`, but if that fails `JitterRng` will be + /// used instead. Both should be suitable for cryptography. It is possible + /// that both entropy sources will fail though unlikely. + fn new() -> Result; } -/// A random number generator. -pub trait Rng { - /// Return the next random u32. - /// - /// This rarely needs to be called directly, prefer `r.gen()` to - /// `r.next_u32()`. - // FIXME #rust-lang/rfcs#628: Should be implemented in terms of next_u64 - fn next_u32(&mut self) -> u32; - - /// Return the next random u64. - /// - /// By default this is implemented in terms of `next_u32`. An - /// implementation of this trait must provide at least one of - /// these two methods. Similarly to `next_u32`, this rarely needs - /// to be called directly, prefer `r.gen()` to `r.next_u64()`. - fn next_u64(&mut self) -> u64 { - ((self.next_u32() as u64) << 32) | (self.next_u32() as u64) - } - - /// Return the next random f32 selected from the half-open - /// interval `[0, 1)`. - /// - /// This uses a technique described by Saito and Matsumoto at - /// MCQMC'08. Given that the IEEE floating point numbers are - /// uniformly distributed over [1,2), we generate a number in - /// this range and then offset it onto the range [0,1). Our - /// choice of bits (masking v. shifting) is arbitrary and - /// should be immaterial for high quality generators. For low - /// quality generators (ex. LCG), prefer bitshifting due to - /// correlation between sequential low order bits. - /// - /// See: - /// A PRNG specialized in double precision floating point numbers using - /// an affine transition - /// - /// * - /// * - /// - /// By default this is implemented in terms of `next_u32`, but a - /// random number generator which can generate numbers satisfying - /// the requirements directly can overload this for performance. - /// It is required that the return value lies in `[0, 1)`. - /// - /// See `Closed01` for the closed interval `[0,1]`, and - /// `Open01` for the open interval `(0,1)`. - fn next_f32(&mut self) -> f32 { - const UPPER_MASK: u32 = 0x3F800000; - const LOWER_MASK: u32 = 0x7FFFFF; - let tmp = UPPER_MASK | (self.next_u32() & LOWER_MASK); - let result: f32 = unsafe { mem::transmute(tmp) }; - result - 1.0 - } - - /// Return the next random f64 selected from the half-open - /// interval `[0, 1)`. - /// - /// By default this is implemented in terms of `next_u64`, but a - /// random number generator which can generate numbers satisfying - /// the requirements directly can overload this for performance. - /// It is required that the return value lies in `[0, 1)`. - /// - /// See `Closed01` for the closed interval `[0,1]`, and - /// `Open01` for the open interval `(0,1)`. - fn next_f64(&mut self) -> f64 { - const UPPER_MASK: u64 = 0x3FF0000000000000; - const LOWER_MASK: u64 = 0xFFFFFFFFFFFFF; - let tmp = UPPER_MASK | (self.next_u64() & LOWER_MASK); - let result: f64 = unsafe { mem::transmute(tmp) }; - result - 1.0 - } - - /// Fill `dest` with random data. - /// - /// This has a default implementation in terms of `next_u64` and - /// `next_u32`, but should be overridden by implementations that - /// offer a more efficient solution than just calling those - /// methods repeatedly. - /// - /// This method does *not* have a requirement to bear any fixed - /// relationship to the other methods, for example, it does *not* - /// have to result in the same output as progressively filling - /// `dest` with `self.gen::()`, and any such behaviour should - /// not be relied upon. - /// - /// This method should guarantee that `dest` is entirely filled - /// with new data, and may panic if this is impossible - /// (e.g. reading past the end of a file that is being used as the - /// source of randomness). - /// - /// # Example - /// - /// ```rust - /// use rand::{thread_rng, Rng}; - /// - /// let mut v = [0u8; 13579]; - /// thread_rng().fill_bytes(&mut v); - /// println!("{:?}", &v[..]); - /// ``` - fn fill_bytes(&mut self, dest: &mut [u8]) { - // this could, in theory, be done by transmuting dest to a - // [u64], but this is (1) likely to be undefined behaviour for - // LLVM, (2) has to be very careful about alignment concerns, - // (3) adds more `unsafe` that needs to be checked, (4) - // probably doesn't give much performance gain if - // optimisations are on. - let mut count = 0; - let mut num = 0; - for byte in dest.iter_mut() { - if count == 0 { - // we could micro-optimise here by generating a u32 if - // we only need a few more bytes to fill the vector - // (i.e. at most 4). - num = self.next_u64(); - count = 8; - } - - *byte = (num & 0xff) as u8; - num >>= 8; - count -= 1; +#[cfg(feature="std")] +impl NewSeeded for R { + fn new() -> Result { + // Note: error handling would be easier with try/catch blocks + fn new_os() -> Result { + let mut r = OsRng::new()?; + T::from_rng(&mut r) + } + fn new_jitter() -> Result { + let mut r = JitterRng::new()?; + T::from_rng(&mut r) } + + new_os().or_else(|e1| { + new_jitter().map_err(|_e2| { + // TODO: log + // TODO: can we somehow return both error sources? + Error::with_cause( + ErrorKind::Unavailable, + "seeding a new RNG failed: both OS and Jitter entropy sources failed", + e1) + }) + }) } +} - /// Return a random value of a `Rand` type. - /// - /// # Example - /// - /// ```rust - /// use rand::{thread_rng, Rng}; - /// - /// let mut rng = thread_rng(); - /// let x: u32 = rng.gen(); - /// println!("{}", x); - /// println!("{:?}", rng.gen::<(f64, bool)>()); - /// ``` - #[inline(always)] - fn gen(&mut self) -> T where Self: Sized { - Rand::rand(self) - } +use distributions::range::SampleRange; - /// Return an iterator that will yield an infinite number of randomly - /// generated items. - /// - /// # Example - /// - /// ``` - /// use rand::{thread_rng, Rng}; - /// +/// Extension trait on [`Rng`] with some convenience methods. +/// +/// This trait exists to allow syntax like `rng.gen()` and +/// `rng.gen_range(1, 7)`. None of the methods in this trait are any more than +/// wrappers around functionality which exists elsewhere in the trait. +pub trait Sample: Rng { + /// Sample a new value, using the given distribution. + /// + /// ### Example + /// + /// ```rust + /// use rand::{thread_rng, Sample}; + /// use rand::distributions::Uniform; + /// /// let mut rng = thread_rng(); - /// let x = rng.gen_iter::().take(10).collect::>(); - /// println!("{:?}", x); - /// println!("{:?}", rng.gen_iter::<(f64, bool)>().take(5) - /// .collect::>()); + /// let x: i32 = rng.sample(Uniform); /// ``` - fn gen_iter<'a, T: Rand>(&'a mut self) -> Generator<'a, T, Self> where Self: Sized { - Generator { rng: self, _marker: marker::PhantomData } - } - - /// Generate a random value in the range [`low`, `high`). - /// - /// This is a convenience wrapper around - /// `distributions::Range`. If this function will be called - /// repeatedly with the same arguments, one should use `Range`, as - /// that will amortize the computations that allow for perfect - /// uniformity, as they only happen on initialization. - /// - /// # Panics - /// - /// Panics if `low >= high`. - /// - /// # Example - /// + fn sample>(&mut self, distr: D) -> T; + + /// Sample a new value, using the [`Default`] distribution. + /// + /// ### Example + /// /// ```rust - /// use rand::{thread_rng, Rng}; - /// + /// use rand::{thread_rng, Sample}; + /// /// let mut rng = thread_rng(); - /// let n: u32 = rng.gen_range(0, 10); - /// println!("{}", n); - /// let m: f64 = rng.gen_range(-40.0f64, 1.3e5f64); - /// println!("{}", m); + /// let x: i32 = rng.gen(); /// ``` - fn gen_range(&mut self, low: T, high: T) -> T where Self: Sized { - assert!(low < high, "Rng.gen_range called with low >= high"); - Range::new(low, high).ind_sample(self) - } - - /// Return a bool with a 1 in n chance of true - /// - /// # Example - /// + fn gen(&mut self) -> T where Default: Distribution { + self.sample(Default) + } + + /// Sample a new value, using the [`Range`] distribution. + /// + /// ### Example + /// /// ```rust - /// use rand::{thread_rng, Rng}; - /// + /// use rand::{thread_rng, Sample}; + /// /// let mut rng = thread_rng(); - /// println!("{}", rng.gen_weighted_bool(3)); + /// + /// // simulate dice roll + /// let x = rng.gen_range(1, 7); /// ``` - fn gen_weighted_bool(&mut self, n: u32) -> bool where Self: Sized { - n <= 1 || self.gen_range(0, n) == 0 - } - - /// Return an iterator of random characters from the set A-Z,a-z,0-9. - /// - /// # Example - /// + /// + /// If the same range is used repeatedly, some work can be saved by + /// constructing the `Range` once and using it with `sample`: + /// /// ```rust - /// use rand::{thread_rng, Rng}; - /// - /// let s: String = thread_rng().gen_ascii_chars().take(10).collect(); - /// println!("{}", s); - /// ``` - fn gen_ascii_chars<'a>(&'a mut self) -> AsciiGenerator<'a, Self> where Self: Sized { - AsciiGenerator { rng: self } - } - - /// Return a random element from `values`. - /// - /// Return `None` if `values` is empty. - /// - /// # Example - /// - /// ``` - /// use rand::{thread_rng, Rng}; - /// - /// let choices = [1, 2, 4, 8, 16, 32]; + /// use rand::{thread_rng, Sample}; + /// use rand::distributions::Range; + /// /// let mut rng = thread_rng(); - /// println!("{:?}", rng.choose(&choices)); - /// assert_eq!(rng.choose(&choices[..0]), None); + /// let die_range = Range::new(1, 7); + /// + /// for _ in 0..100 { + /// let x = rng.sample(die_range); + /// assert!(1 <= x && x <= 6); + /// } /// ``` - fn choose<'a, T>(&mut self, values: &'a [T]) -> Option<&'a T> where Self: Sized { - if values.is_empty() { - None - } else { - Some(&values[self.gen_range(0, values.len())]) - } - } - - /// Return a mutable pointer to a random element from `values`. - /// - /// Return `None` if `values` is empty. - fn choose_mut<'a, T>(&mut self, values: &'a mut [T]) -> Option<&'a mut T> where Self: Sized { - if values.is_empty() { - None - } else { - let len = values.len(); - Some(&mut values[self.gen_range(0, len)]) - } - } - - /// Shuffle a mutable slice in place. - /// - /// This applies Durstenfeld's algorithm for the [Fisher–Yates shuffle](https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_modern_algorithm) - /// which produces an unbiased permutation. - /// - /// # Example - /// + fn gen_range(&mut self, low: T, high: T) -> T { + Range::sample_single(low, high, self) + } + + /// Construct an iterator on an `Rng`. + /// + /// ### Example + /// /// ```rust - /// use rand::{thread_rng, Rng}; - /// + /// use rand::{thread_rng, Sample}; + /// use rand::distributions::Alphanumeric; + /// /// let mut rng = thread_rng(); - /// let mut y = [1, 2, 3]; - /// rng.shuffle(&mut y); - /// println!("{:?}", y); - /// rng.shuffle(&mut y); - /// println!("{:?}", y); + /// let x: String = rng.iter().map(|rng| rng.sample(Alphanumeric)).take(6).collect(); /// ``` - fn shuffle(&mut self, values: &mut [T]) where Self: Sized { - let mut i = values.len(); - while i >= 2 { - // invariant: elements with index >= i have been locked in place. - i -= 1; - // lock element i in place. - values.swap(i, self.gen_range(0, i + 1)); - } - } -} - -impl<'a, R: ?Sized> Rng for &'a mut R where R: Rng { - fn next_u32(&mut self) -> u32 { - (**self).next_u32() - } - - fn next_u64(&mut self) -> u64 { - (**self).next_u64() - } - - fn next_f32(&mut self) -> f32 { - (**self).next_f32() - } - - fn next_f64(&mut self) -> f64 { - (**self).next_f64() - } - - fn fill_bytes(&mut self, dest: &mut [u8]) { - (**self).fill_bytes(dest) - } -} - -#[cfg(feature="std")] -impl Rng for Box where R: Rng { - fn next_u32(&mut self) -> u32 { - (**self).next_u32() - } - - fn next_u64(&mut self) -> u64 { - (**self).next_u64() - } - - fn next_f32(&mut self) -> f32 { - (**self).next_f32() + fn iter<'a>(&'a mut self) -> iter::Iter<'a, Self> { + iter(self) } - - fn next_f64(&mut self) -> f64 { - (**self).next_f64() - } - - fn fill_bytes(&mut self, dest: &mut [u8]) { - (**self).fill_bytes(dest) - } -} - -/// Iterator which will generate a stream of random items. -/// -/// This iterator is created via the [`gen_iter`] method on [`Rng`]. -/// -/// [`gen_iter`]: trait.Rng.html#method.gen_iter -/// [`Rng`]: trait.Rng.html -#[derive(Debug)] -pub struct Generator<'a, T, R:'a> { - rng: &'a mut R, - _marker: marker::PhantomData T>, } -impl<'a, T: Rand, R: Rng> Iterator for Generator<'a, T, R> { - type Item = T; - - fn next(&mut self) -> Option { - Some(self.rng.gen()) - } -} - -/// Iterator which will continuously generate random ascii characters. -/// -/// This iterator is created via the [`gen_ascii_chars`] method on [`Rng`]. -/// -/// [`gen_ascii_chars`]: trait.Rng.html#method.gen_ascii_chars -/// [`Rng`]: trait.Rng.html -#[derive(Debug)] -pub struct AsciiGenerator<'a, R:'a> { - rng: &'a mut R, -} - -impl<'a, R: Rng> Iterator for AsciiGenerator<'a, R> { - type Item = char; - - fn next(&mut self) -> Option { - const GEN_ASCII_STR_CHARSET: &'static [u8] = - b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\ - abcdefghijklmnopqrstuvwxyz\ - 0123456789"; - Some(*self.rng.choose(GEN_ASCII_STR_CHARSET).unwrap() as char) +impl Sample for R { + fn sample>(&mut self, distr: D) -> T { + distr.sample(self) } } -/// A random number generator that can be explicitly seeded to produce -/// the same stream of randomness multiple times. -pub trait SeedableRng: Rng { - /// Reseed an RNG with the given seed. - /// - /// # Example - /// - /// ```rust - /// use rand::{Rng, SeedableRng, StdRng}; - /// - /// let seed: &[_] = &[1, 2, 3, 4]; - /// let mut rng: StdRng = SeedableRng::from_seed(seed); - /// println!("{}", rng.gen::()); - /// rng.reseed(&[5, 6, 7, 8]); - /// println!("{}", rng.gen::()); - /// ``` - fn reseed(&mut self, Seed); - - /// Create a new RNG with the given seed. - /// - /// # Example - /// - /// ```rust - /// use rand::{Rng, SeedableRng, StdRng}; - /// - /// let seed: &[_] = &[1, 2, 3, 4]; - /// let mut rng: StdRng = SeedableRng::from_seed(seed); - /// println!("{}", rng.gen::()); - /// ``` - fn from_seed(seed: Seed) -> Self; -} - -/// A wrapper for generating floating point numbers uniformly in the -/// open interval `(0,1)` (not including either endpoint). -/// -/// Use `Closed01` for the closed interval `[0,1]`, and the default -/// `Rand` implementation for `f32` and `f64` for the half-open -/// `[0,1)`. -/// -/// # Example -/// ```rust -/// use rand::{random, Open01}; -/// -/// let Open01(val) = random::>(); -/// println!("f32 from (0,1): {}", val); -/// ``` -#[derive(Debug)] -pub struct Open01(pub F); - -/// A wrapper for generating floating point numbers uniformly in the -/// closed interval `[0,1]` (including both endpoints). -/// -/// Use `Open01` for the closed interval `(0,1)`, and the default -/// `Rand` implementation of `f32` and `f64` for the half-open -/// `[0,1)`. -/// -/// # Example -/// -/// ```rust -/// use rand::{random, Closed01}; -/// -/// let Closed01(val) = random::>(); -/// println!("f32 from [0,1]: {}", val); -/// ``` -#[derive(Debug)] -pub struct Closed01(pub F); - /// The standard RNG. This is designed to be efficient on the current /// platform. -#[derive(Copy, Clone, Debug)] -pub struct StdRng { - rng: IsaacWordRng, -} - -impl StdRng { - /// Create a randomly seeded instance of `StdRng`. - /// - /// This is a very expensive operation as it has to read - /// randomness from the operating system and use this in an - /// expensive seeding operation. If one is only generating a small - /// number of random numbers, or doesn't need the utmost speed for - /// generating each number, `thread_rng` and/or `random` may be more - /// appropriate. - /// - /// 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 { - match OsRng::new() { - Ok(mut r) => Ok(StdRng { rng: r.gen() }), - Err(e1) => { - match JitterRng::new() { - Ok(mut r) => Ok(StdRng { rng: r.gen() }), - Err(_) => { - Err(e1) - } - } - } - } - } -} +/// +/// The underlying algorithm is not fixed, thus values from this generator +/// cannot be guaranteed to be reproducible. For this reason, `StdRng` does +/// not support `SeedableRng`. +#[derive(Clone, Debug)] +pub struct StdRng(IsaacWordRng); impl Rng for StdRng { - #[inline] fn next_u32(&mut self) -> u32 { - self.rng.next_u32() + self.0.next_u32() } - - #[inline] fn next_u64(&mut self) -> u64 { - self.rng.next_u64() + self.0.next_u64() } -} - -impl<'a> SeedableRng<&'a [usize]> for StdRng { - fn reseed(&mut self, seed: &'a [usize]) { - // the internal RNG can just be seeded from the above - // randomness. - self.rng.reseed(unsafe {mem::transmute(seed)}) + #[cfg(feature = "i128_support")] + fn next_u128(&mut self) -> u128 { + self.0.next_u128() } - - fn from_seed(seed: &'a [usize]) -> StdRng { - StdRng { rng: SeedableRng::from_seed(unsafe {mem::transmute(seed)}) } + fn fill_bytes(&mut self, dest: &mut [u8]) { + self.0.fill_bytes(dest); } -} - -/// Create a weak random number generator with a default algorithm and seed. -/// -/// It returns the fastest `Rng` algorithm currently available in Rust without -/// consideration for cryptography or security. If you require a specifically -/// seeded `Rng` for consistency over time you should pick one algorithm and -/// 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 for ThreadRngReseeder { - fn reseed(&mut self, rng: &mut StdRng) { - match StdRng::new() { - Ok(r) => *rng = r, - Err(e) => panic!("No entropy available: {}", e), - } + fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { + self.0.try_fill(dest) } } -#[cfg(feature="std")] -const THREAD_RNG_RESEED_THRESHOLD: u64 = 32_768; -#[cfg(feature="std")] -type ThreadRngInner = reseeding::ReseedingRng; - -/// The thread-local RNG. -#[cfg(feature="std")] -#[derive(Clone, Debug)] -pub struct ThreadRng { - rng: Rc>, -} - -/// Retrieve the lazily-initialized thread-local random number -/// generator, seeded by the system. Intended to be used in method -/// chaining style, e.g. `thread_rng().gen::()`. -/// -/// After generating a certain amount of randomness, the RNG will reseed itself -/// from the operating system or, if the operating system RNG returns an error, -/// a seed based on the current system time. -/// -/// The internal RNG used is platform and architecture dependent, even -/// 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> = { - let r = match StdRng::new() { - Ok(r) => r, - Err(e) => panic!("No entropy available: {}", e), - }; - let rng = reseeding::ReseedingRng::new(r, - THREAD_RNG_RESEED_THRESHOLD, - ThreadRngReseeder); - Rc::new(RefCell::new(rng)) - }); - - 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() +impl SeedableRng for StdRng { + type Seed = ::Seed; + fn from_seed(seed: Self::Seed) -> Self { + StdRng(IsaacWordRng::from_seed(seed)) } - - fn next_u64(&mut self) -> u64 { - self.rng.borrow_mut().next_u64() - } - - #[inline] - fn fill_bytes(&mut self, bytes: &mut [u8]) { - self.rng.borrow_mut().fill_bytes(bytes) + fn from_rng(rng: R) -> Result { + IsaacWordRng::from_rng(rng).map(|rng| StdRng(rng)) } } -/// Generates a random value using the thread-local random number generator. -/// -/// `random()` can generate various types of random things, and so may require -/// type hinting to generate the specific type you want. -/// -/// This function uses the thread local random number generator. This means -/// that if you're calling `random()` in a loop, caching the generator can -/// increase performance. An example is shown below. -/// -/// # Examples -/// -/// ``` -/// let x = rand::random::(); -/// println!("{}", x); -/// -/// let y = rand::random::(); -/// println!("{}", y); -/// -/// if rand::random() { // generates a boolean -/// println!("Better lucky than good!"); -/// } -/// ``` -/// -/// Caching the thread local random number generator: -/// -/// ``` -/// use rand::Rng; -/// -/// let mut v = vec![1, 2, 3]; -/// -/// for x in v.iter_mut() { -/// *x = rand::random() -/// } -/// -/// // would be faster as -/// -/// let mut rng = rand::thread_rng(); -/// -/// for x in v.iter_mut() { -/// *x = rng.gen(); -/// } -/// ``` -#[cfg(feature="std")] -#[inline] -pub fn random() -> T { - thread_rng().gen() -} - -/// DEPRECATED: use `seq::sample_iter` instead. -/// -/// Randomly sample up to `amount` elements from a finite iterator. -/// The order of elements in the sample is not random. -/// -/// # Example -/// -/// ```rust -/// use rand::{thread_rng, sample}; -/// -/// let mut rng = thread_rng(); -/// 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(rng: &mut R, iterable: I, amount: usize) -> Vec - where I: IntoIterator, - R: Rng, -{ - // the legacy sample didn't care whether amount was met - seq::sample_iter(rng, iterable, amount) - .unwrap_or_else(|e| e) -} #[cfg(test)] mod test { - use super::{Rng, thread_rng, random, SeedableRng, StdRng, weak_rng}; + use {Rng, thread_rng, Sample, Error}; + use mock::MockAddRng; + use distributions::{Uniform, Range, Exp}; + use sequences::Shuffle; use std::iter::repeat; - pub struct MyRng { inner: R } + #[derive(Debug)] + pub struct MyRng { inner: R } - impl Rng for MyRng { + impl Rng for MyRng { fn next_u32(&mut self) -> u32 { - fn next(t: &mut T) -> u32 { - t.next_u32() - } - next(&mut self.inner) + self.inner.next_u32() + } + fn next_u64(&mut self) -> u64 { + self.inner.next_u64() + } + #[cfg(feature = "i128_support")] + fn next_u128(&mut self) -> u128 { + self.inner.next_u128() + } + fn fill_bytes(&mut self, dest: &mut [u8]) { + self.inner.fill_bytes(dest) + } + fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { + self.inner.try_fill(dest) } } @@ -1003,14 +527,6 @@ mod test { MyRng { inner: ::thread_rng() } } - struct ConstRng { i: u64 } - impl Rng for ConstRng { - fn next_u32(&mut self) -> u32 { self.i as u32 } - fn next_u64(&mut self) -> u64 { self.i } - - // no fill_bytes on purpose - } - pub fn iter_eq(i: I, j: J) -> bool where I: IntoIterator, J: IntoIterator, @@ -1030,7 +546,7 @@ mod test { #[test] fn test_fill_bytes_default() { - let mut r = ConstRng { i: 0x11_22_33_44_55_66_77_88 }; + let mut r = MockAddRng::new(0x11_22_33_44_55_66_77_88u64, 0); // check every remainder mod 8, both in small and big vectors. let lengths = [0, 1, 2, 3, 4, 5, 6, 7, @@ -1048,105 +564,12 @@ mod test { } } - #[test] - fn test_gen_range() { - let mut r = thread_rng(); - for _ in 0..1000 { - let a = r.gen_range(-3, 42); - assert!(a >= -3 && a < 42); - assert_eq!(r.gen_range(0, 1), 0); - assert_eq!(r.gen_range(-12, -11), -12); - } - - for _ in 0..1000 { - let a = r.gen_range(10, 42); - assert!(a >= 10 && a < 42); - assert_eq!(r.gen_range(0, 1), 0); - assert_eq!(r.gen_range(3_000_000, 3_000_001), 3_000_000); - } - - } - - #[test] - #[should_panic] - fn test_gen_range_panic_int() { - let mut r = thread_rng(); - r.gen_range(5, -2); - } - - #[test] - #[should_panic] - fn test_gen_range_panic_usize() { - let mut r = thread_rng(); - r.gen_range(5, 2); - } - - #[test] - fn test_gen_f64() { - let mut r = thread_rng(); - let a = r.gen::(); - let b = r.gen::(); - debug!("{:?}", (a, b)); - } - - #[test] - fn test_gen_weighted_bool() { - let mut r = thread_rng(); - assert_eq!(r.gen_weighted_bool(0), true); - assert_eq!(r.gen_weighted_bool(1), true); - } - - #[test] - fn test_gen_ascii_str() { - let mut r = thread_rng(); - assert_eq!(r.gen_ascii_chars().take(0).count(), 0); - assert_eq!(r.gen_ascii_chars().take(10).count(), 10); - assert_eq!(r.gen_ascii_chars().take(16).count(), 16); - } - - #[test] - fn test_gen_vec() { - let mut r = thread_rng(); - assert_eq!(r.gen_iter::().take(0).count(), 0); - assert_eq!(r.gen_iter::().take(10).count(), 10); - assert_eq!(r.gen_iter::().take(16).count(), 16); - } - - #[test] - fn test_choose() { - let mut r = thread_rng(); - assert_eq!(r.choose(&[1, 1, 1]).map(|&x|x), Some(1)); - - let v: &[isize] = &[]; - assert_eq!(r.choose(v), None); - } - - #[test] - fn test_shuffle() { - let mut r = thread_rng(); - let empty: &mut [isize] = &mut []; - r.shuffle(empty); - let mut one = [1]; - r.shuffle(&mut one); - let b: &[_] = &[1]; - assert_eq!(one, b); - - let mut two = [1, 2]; - r.shuffle(&mut two); - assert!(two == [1, 2] || two == [2, 1]); - - let mut x = [1, 1, 1]; - r.shuffle(&mut x); - let b: &[_] = &[1, 1, 1]; - assert_eq!(x, b); - } - #[test] fn test_thread_rng() { let mut r = thread_rng(); - r.gen::(); + r.sample::(Uniform); let mut v = [1, 1, 1]; - r.shuffle(&mut v); + v.shuffle(&mut r); let b: &[_] = &[1, 1, 1]; assert_eq!(v, b); assert_eq!(r.gen_range(0, 1), 0); @@ -1156,21 +579,21 @@ mod test { fn test_rng_trait_object() { let mut rng = thread_rng(); { - let mut r = &mut rng as &mut Rng; + let r = &mut rng as &mut Rng; r.next_u32(); - (&mut r).gen::(); + r.sample::(Uniform); let mut v = [1, 1, 1]; - (&mut r).shuffle(&mut v); + v[..].shuffle(r); let b: &[_] = &[1, 1, 1]; assert_eq!(v, b); - assert_eq!((&mut r).gen_range(0, 1), 0); + assert_eq!(r.gen_range(0, 1), 0); } { let mut r = Box::new(rng) as Box; r.next_u32(); - r.gen::(); + r.sample::(Uniform); let mut v = [1, 1, 1]; - r.shuffle(&mut v); + v[..].shuffle(&mut *r); let b: &[_] = &[1, 1, 1]; assert_eq!(v, b); assert_eq!(r.gen_range(0, 1), 0); @@ -1178,46 +601,16 @@ mod test { } #[test] - fn test_random() { - // not sure how to test this aside from just getting some values - let _n : usize = random(); - let _f : f32 = random(); - let _o : Option> = random(); - let _many : ((), - (usize, - isize, - Option<(u32, (bool,))>), - (u8, i8, u16, i16, u32, i32, u64, i64), - (f32, (f64, (f64,)))) = random(); - } - - #[test] - fn test_std_rng_seeded() { - let s = thread_rng().gen_iter::().take(256).collect::>(); - let mut ra: StdRng = SeedableRng::from_seed(&s[..]); - let mut rb: StdRng = SeedableRng::from_seed(&s[..]); - assert!(iter_eq(ra.gen_ascii_chars().take(100), - rb.gen_ascii_chars().take(100))); - } - - #[test] - fn test_std_rng_reseed() { - let s = thread_rng().gen_iter::().take(256).collect::>(); - let mut r: StdRng = SeedableRng::from_seed(&s[..]); - let string1 = r.gen_ascii_chars().take(100).collect::(); - - r.reseed(&s); - - let string2 = r.gen_ascii_chars().take(100).collect::(); - assert_eq!(string1, string2); - } - - #[test] - fn test_weak_rng() { - let s = weak_rng().gen_iter::().take(256).collect::>(); - let mut ra: StdRng = SeedableRng::from_seed(&s[..]); - let mut rb: StdRng = SeedableRng::from_seed(&s[..]); - assert!(iter_eq(ra.gen_ascii_chars().take(100), - rb.gen_ascii_chars().take(100))); + fn test_sample_from_rng() { + // use a static Rng type: + let mut rng = thread_rng(); + + let _a: u32 = rng.sample(Uniform); + let _b = rng.sample(Range::new(-2, 15)); + + // use a dynamic Rng type: + let rng: &mut Rng = &mut thread_rng(); + + let _c = rng.sample(Exp::new(2.0)); } } diff --git a/src/mock.rs b/src/mock.rs new file mode 100644 index 00000000000..447d6cc8090 --- /dev/null +++ b/src/mock.rs @@ -0,0 +1,76 @@ +// Copyright 2013-2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Mock RNG implementations +//! +//! TODO: should this be behind a feature flag? Problem is that Cargo won't +//! simply use a dependency with a feature flag for tests and without for normal +//! build (i.e. `rand` couldn't use the mock feature only for tests). +//! Instead maybe this should be yet another crate? Or just leave it here? + +use core::num::Wrapping as w; +use {Rng, SeedableRng, Error}; +use rand_core::{impls, le}; + +/// A simple implementation of `Rng`, purely for testing. +/// Returns an arithmetic sequence (i.e. adds a constant each step). +/// +/// ```rust +/// use rand::Rng; +/// use rand::mock::MockAddRng; +/// +/// let mut my_rng = MockAddRng::new(2, 1); +/// assert_eq!(my_rng.next_u32(), 2); +/// assert_eq!(my_rng.next_u64(), 3); +/// ``` +#[derive(Debug, Clone)] +pub struct MockAddRng { + v: w, + a: w, +} + +impl MockAddRng { + /// Create a `MockAddRng`, yielding an arithmetic sequence starting with + /// `v` and incremented by `a` each time. + pub fn new(v: u64, a: u64) -> Self { + MockAddRng { v: w(v), a: w(a) } + } +} + +impl Rng for MockAddRng { + fn next_u32(&mut self) -> u32 { + self.next_u64() as u32 + } + fn next_u64(&mut self) -> u64 { + let result = self.v.0; + self.v += self.a; + result + } + #[cfg(feature = "i128_support")] + fn next_u128(&mut self) -> u128 { + impls::next_u128_via_u64(self) + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + impls::fill_bytes_via_u64(self, dest); + } + + fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { + Ok(self.fill_bytes(dest)) + } +} + +impl SeedableRng for MockAddRng { + type Seed = [u8; 8]; + fn from_seed(seed: Self::Seed) -> Self { + let seed = le::read_u64(array_ref!(seed, 0, 8)); + MockAddRng::new(seed, 1) + } +} diff --git a/src/os.rs b/src/os.rs index a51f0ce01dd..15a7e736677 100644 --- a/src/os.rs +++ b/src/os.rs @@ -11,11 +11,16 @@ //! Interfaces to the operating system provided random number //! generators. -use std::{io, mem, fmt}; -use Rng; +use std::fmt; +use std::io::Read; + +use {Rng, Error, ErrorKind}; +// TODO: replace many of the panics below with Result error handling /// A random number generator that retrieves randomness straight from -/// the operating system. Platform sources: +/// the operating system. +/// +/// Platform sources: /// /// - Unix-like systems (Linux, Android, Mac OSX): read directly from /// `/dev/urandom`, or from `getrandom(2)` system call if available. @@ -24,7 +29,6 @@ use Rng; /// - Windows: calls `RtlGenRandom`, exported from `advapi32.dll` as /// `SystemFunction036`. /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed. -/// - PNaCl: calls into the `nacl-irt-random-0.1` IRT interface. /// /// This usually does not block. On some systems (e.g. FreeBSD, OpenBSD, /// Max OS X, and modern Linux) this may block very early in the init @@ -32,37 +36,60 @@ use Rng; /// /// [1] See for a more /// in-depth discussion. +#[allow(unused)] // not used by all targets pub struct OsRng(imp::OsRng); +impl fmt::Debug for OsRng { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.0.fmt(f) + } +} + impl OsRng { /// Create a new `OsRng`. - pub fn new() -> io::Result { + pub fn new() -> Result { imp::OsRng::new().map(OsRng) } } impl Rng for OsRng { - fn next_u32(&mut self) -> u32 { self.0.next_u32() } - fn next_u64(&mut self) -> u64 { self.0.next_u64() } - fn fill_bytes(&mut self, v: &mut [u8]) { self.0.fill_bytes(v) } -} + fn next_u32(&mut self) -> u32 { + // note: `next_u32_via_fill` does a byte-swap on big-endian + // architectures, which is not really needed here + ::rand_core::impls::next_u32_via_fill(self) + } -impl fmt::Debug for OsRng { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "OsRng {{}}") + fn next_u64(&mut self) -> u64 { + ::rand_core::impls::next_u64_via_fill(self) } -} -fn next_u32(fill_buf: &mut FnMut(&mut [u8])) -> u32 { - let mut buf: [u8; 4] = [0; 4]; - fill_buf(&mut buf); - unsafe { mem::transmute::<[u8; 4], u32>(buf) } + #[cfg(feature = "i128_support")] + fn next_u128(&mut self) -> u128 { + ::rand_core::impls::next_u128_via_fill(self) + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + ::rand_core::impls::fill_via_try_fill(self, dest) + } + + fn try_fill(&mut self, v: &mut [u8]) -> Result<(), Error> { + self.0.try_fill(v) + } } -fn next_u64(fill_buf: &mut FnMut(&mut [u8])) -> u64 { - let mut buf: [u8; 8] = [0; 8]; - fill_buf(&mut buf); - unsafe { mem::transmute::<[u8; 8], u64>(buf) } +// Specialisation of `ReadRng` for our purposes +#[derive(Debug)] +struct ReadRng (R); + +impl ReadRng { + #[allow(unused)] // not used by all targets + fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { + if dest.len() == 0 { return Ok(()); } + // Use `std::io::read_exact`, which retries on `ErrorKind::Interrupted`. + self.0.read_exact(dest).map_err(|err| { + Error::with_cause(ErrorKind::Unavailable, "error reading random device", err) + }) + } } #[cfg(all(unix, not(target_os = "ios"), @@ -74,13 +101,12 @@ fn next_u64(fill_buf: &mut FnMut(&mut [u8])) -> u64 { mod imp { extern crate libc; - use super::{next_u32, next_u64}; use self::OsRngInner::*; + use super::ReadRng; + use {Error, ErrorKind}; use std::io; use std::fs::File; - use Rng; - use read::ReadRng; #[cfg(all(target_os = "linux", any(target_arch = "x86_64", @@ -103,9 +129,11 @@ mod imp { const NR_GETRANDOM: libc::c_long = 278; #[cfg(target_arch = "powerpc")] const NR_GETRANDOM: libc::c_long = 384; + + const GRND_NONBLOCK: libc::c_uint = 0x0001; unsafe { - syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), 0) + syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), GRND_NONBLOCK) } } @@ -117,22 +145,33 @@ mod imp { target_arch = "powerpc"))))] fn getrandom(_buf: &mut [u8]) -> libc::c_long { -1 } - fn getrandom_fill_bytes(v: &mut [u8]) { + fn getrandom_try_fill(v: &mut [u8]) -> Result<(), Error> { let mut read = 0; let len = v.len(); while read < len { let result = getrandom(&mut v[read..]); if result == -1 { let err = io::Error::last_os_error(); - if err.kind() == io::ErrorKind::Interrupted { - continue + let kind = err.kind(); + if kind == io::ErrorKind::Interrupted { + continue; + } else if kind == io::ErrorKind::WouldBlock { + // Potentially this would waste bytes, but since we use + // /dev/urandom blocking only happens if not initialised. + // Also, wasting the bytes in v doesn't matter very much. + return Err(Error::new(ErrorKind::NotReady, "getrandom not ready")); } else { - panic!("unexpected getrandom error: {}", err); + return Err(Error::with_cause( + ErrorKind::Unavailable, + "unexpected getrandom error", + err, + )); } } else { read += result as usize; } } + Ok(()) } #[cfg(all(target_os = "linux", @@ -171,45 +210,35 @@ mod imp { target_arch = "powerpc"))))] fn is_getrandom_available() -> bool { false } + #[derive(Debug)] pub struct OsRng { inner: OsRngInner, } + #[derive(Debug)] enum OsRngInner { OsGetrandomRng, OsReadRng(ReadRng), } impl OsRng { - pub fn new() -> io::Result { + pub fn new() -> Result { if is_getrandom_available() { return Ok(OsRng { inner: OsGetrandomRng }); } - let reader = try!(File::open("/dev/urandom")); - let reader_rng = ReadRng::new(reader); + let reader = File::open("/dev/urandom").map_err(|err| { + Error::with_cause(ErrorKind::Unavailable, "error opening random device", err) + })?; + let reader_rng = ReadRng(reader); Ok(OsRng { inner: OsReadRng(reader_rng) }) } - } - - impl Rng for OsRng { - fn next_u32(&mut self) -> u32 { + + pub fn try_fill(&mut self, v: &mut [u8]) -> Result<(), Error> { match self.inner { - OsGetrandomRng => next_u32(&mut getrandom_fill_bytes), - OsReadRng(ref mut rng) => rng.next_u32(), - } - } - fn next_u64(&mut self) -> u64 { - match self.inner { - OsGetrandomRng => next_u64(&mut getrandom_fill_bytes), - OsReadRng(ref mut rng) => rng.next_u64(), - } - } - fn fill_bytes(&mut self, v: &mut [u8]) { - match self.inner { - OsGetrandomRng => getrandom_fill_bytes(v), - OsReadRng(ref mut rng) => rng.fill_bytes(v) + OsGetrandomRng => getrandom_try_fill(v), + OsReadRng(ref mut rng) => rng.try_fill(v) } } } @@ -219,10 +248,9 @@ mod imp { mod imp { extern crate libc; - use super::{next_u32, next_u64}; - + use {Error, ErrorKind}; + use std::io; - use Rng; use self::libc::{c_int, size_t}; #[derive(Debug)] @@ -240,24 +268,20 @@ mod imp { } impl OsRng { - pub fn new() -> io::Result { + pub fn new() -> Result { Ok(OsRng) } - } - - impl Rng for OsRng { - fn next_u32(&mut self) -> u32 { - next_u32(&mut |v| self.fill_bytes(v)) - } - fn next_u64(&mut self) -> u64 { - next_u64(&mut |v| self.fill_bytes(v)) - } - fn fill_bytes(&mut self, v: &mut [u8]) { + pub fn try_fill(&mut self, v: &mut [u8]) -> Result<(), Error> { let ret = unsafe { SecRandomCopyBytes(kSecRandomDefault, v.len() as size_t, v.as_mut_ptr()) }; if ret == -1 { - panic!("couldn't generate random bytes: {}", io::Error::last_os_error()); + Err(Error::with_cause( + ErrorKind::Unavailable, + "couldn't generate random bytes", + io::Error::last_os_error())) + } else { + Ok(()) } } } @@ -267,28 +291,18 @@ mod imp { mod imp { extern crate libc; - use std::{io, ptr}; - use Rng; - - use super::{next_u32, next_u64}; + use {Error, ErrorKind}; + + use std::ptr; #[derive(Debug)] pub struct OsRng; impl OsRng { - pub fn new() -> io::Result { + pub fn new() -> Result { Ok(OsRng) } - } - - impl Rng for OsRng { - fn next_u32(&mut self) -> u32 { - next_u32(&mut |v| self.fill_bytes(v)) - } - fn next_u64(&mut self) -> u64 { - next_u64(&mut |v| self.fill_bytes(v)) - } - fn fill_bytes(&mut self, v: &mut [u8]) { + pub fn try_fill(&mut self, v: &mut [u8]) -> Result<(), Error> { let mib = [libc::CTL_KERN, libc::KERN_ARND]; // kern.arandom permits a maximum buffer size of 256 bytes for s in v.chunks_mut(256) { @@ -299,10 +313,14 @@ mod imp { ptr::null(), 0) }; if ret == -1 || s_len != s.len() { - panic!("kern.arandom sysctl failed! (returned {}, s.len() {}, oldlenp {})", - ret, s.len(), s_len); + // Old format string: + // "kern.arandom sysctl failed! (returned {}, s.len() {}, oldlenp {})" + return Err(Error::new( + ErrorKind::Unavailable, + "kern.arandom sysctl failed")); } } + Ok(()) } } } @@ -311,48 +329,42 @@ mod imp { mod imp { extern crate libc; + use {Error, ErrorKind}; + use std::io; - use Rng; - - use super::{next_u32, next_u64}; #[derive(Debug)] pub struct OsRng; impl OsRng { - pub fn new() -> io::Result { + pub fn new() -> Result { Ok(OsRng) } - } - - impl Rng for OsRng { - fn next_u32(&mut self) -> u32 { - next_u32(&mut |v| self.fill_bytes(v)) - } - fn next_u64(&mut self) -> u64 { - next_u64(&mut |v| self.fill_bytes(v)) - } - fn fill_bytes(&mut self, v: &mut [u8]) { + pub fn try_fill(&mut self, v: &mut [u8]) -> Result<(), Error> { // getentropy(2) permits a maximum buffer size of 256 bytes for s in v.chunks_mut(256) { let ret = unsafe { libc::getentropy(s.as_mut_ptr() as *mut libc::c_void, s.len()) }; if ret == -1 { - let err = io::Error::last_os_error(); - panic!("getentropy failed: {}", err); + return Err(Error::with_cause( + ErrorKind::Unavailable, + "getentropy failed", + io::Error::last_os_error())); } } + Ok(()) } } } #[cfg(target_os = "redox")] mod imp { + use {Error, ErrorKind}; + use std::io; use std::fs::File; - use Rng; - use read::ReadRng; + use super::ReadRng; #[derive(Debug)] pub struct OsRng { @@ -360,23 +372,16 @@ mod imp { } impl OsRng { - pub fn new() -> io::Result { - let reader = try!(File::open("rand:")); - let reader_rng = ReadRng::new(reader); + pub fn new() -> Result { + let reader = File::open("rand:").map_err(|err| { + Error::with_cause(ErrorKind::Unavailable, "error opening random device", err) + })?; + let reader_rng = ReadRng(reader); Ok(OsRng { inner: reader_rng }) } - } - - impl Rng for OsRng { - fn next_u32(&mut self) -> u32 { - self.inner.next_u32() - } - fn next_u64(&mut self) -> u64 { - self.inner.next_u64() - } - fn fill_bytes(&mut self, v: &mut [u8]) { - self.inner.fill_bytes(v) + pub fn try_fill(&mut self, v: &mut [u8]) -> Result<(), Error> { + self.inner.try_fill(v) } } } @@ -385,37 +390,33 @@ mod imp { mod imp { extern crate fuchsia_zircon; + use {Error, ErrorKind}; + use std::io; - use Rng; - - use super::{next_u32, next_u64}; #[derive(Debug)] pub struct OsRng; impl OsRng { - pub fn new() -> io::Result { + pub fn new() -> Result { Ok(OsRng) } - } - - impl Rng for OsRng { - fn next_u32(&mut self) -> u32 { - next_u32(&mut |v| self.fill_bytes(v)) - } - fn next_u64(&mut self) -> u64 { - next_u64(&mut |v| self.fill_bytes(v)) - } - fn fill_bytes(&mut self, v: &mut [u8]) { - for s in v.chunks_mut(fuchsia_zircon::ZX_CPRNG_DRAW_MAX_LEN) { + pub fn try_fill(&mut self, v: &mut [u8]) -> Result<(), Error> { + for s in v.chunks_mut(fuchsia_zircon::sys::ZX_CPRNG_DRAW_MAX_LEN) { let mut filled = 0; while filled < s.len() { match fuchsia_zircon::cprng_draw(&mut s[filled..]) { Ok(actual) => filled += actual, - Err(e) => panic!("cprng_draw failed: {:?}", e), + Err(e) => { + return Err(Error::with_cause( + ErrorKind::Unavailable, + "cprng_draw failed", + e)); + } }; } } + Ok(()) } } } @@ -423,11 +424,10 @@ mod imp { #[cfg(windows)] mod imp { extern crate winapi; - + + use {Error, ErrorKind}; + use std::io; - use Rng; - - use super::{next_u32, next_u64}; use self::winapi::shared::minwindef::ULONG; use self::winapi::um::ntsecapi::RtlGenRandom; @@ -437,19 +437,10 @@ mod imp { pub struct OsRng; impl OsRng { - pub fn new() -> io::Result { + pub fn new() -> Result { Ok(OsRng) } - } - - impl Rng for OsRng { - fn next_u32(&mut self) -> u32 { - next_u32(&mut |v| self.fill_bytes(v)) - } - fn next_u64(&mut self) -> u64 { - next_u64(&mut |v| self.fill_bytes(v)) - } - fn fill_bytes(&mut self, v: &mut [u8]) { + pub fn try_fill(&mut self, v: &mut [u8]) -> Result<(), Error> { // RtlGenRandom takes an ULONG (u32) for the length so we need to // split up the buffer. for slice in v.chunks_mut(::max_value() as usize) { @@ -457,86 +448,13 @@ mod imp { RtlGenRandom(slice.as_mut_ptr() as PVOID, slice.len() as ULONG) }; if ret == 0 { - panic!("couldn't generate random bytes: {}", - io::Error::last_os_error()); + return Err(Error::with_cause( + ErrorKind::Unavailable, + "couldn't generate random bytes", + io::Error::last_os_error())); } } - } - } -} - -#[cfg(target_os = "nacl")] -mod imp { - extern crate libc; - - use std::io; - use std::mem; - use Rng; - - use super::{next_u32, next_u64}; - - #[derive(Debug)] - pub struct OsRng(extern fn(dest: *mut libc::c_void, - bytes: libc::size_t, - read: *mut libc::size_t) -> libc::c_int); - - extern { - fn nacl_interface_query(name: *const libc::c_char, - table: *mut libc::c_void, - table_size: libc::size_t) -> libc::size_t; - } - - const INTERFACE: &'static [u8] = b"nacl-irt-random-0.1\0"; - - #[repr(C)] - struct NaClIRTRandom { - get_random_bytes: Option libc::c_int>, - } - - impl OsRng { - pub fn new() -> io::Result { - let mut iface = NaClIRTRandom { - get_random_bytes: None, - }; - let result = unsafe { - nacl_interface_query(INTERFACE.as_ptr() as *const _, - mem::transmute(&mut iface), - mem::size_of::() as libc::size_t) - }; - if result != 0 { - assert!(iface.get_random_bytes.is_some()); - let result = OsRng(iface.get_random_bytes.take().unwrap()); - Ok(result) - } else { - let error = io::ErrorKind::NotFound; - let error = io::Error::new(error, "IRT random interface missing"); - Err(error) - } - } - } - - impl Rng for OsRng { - fn next_u32(&mut self) -> u32 { - next_u32(&mut |v| self.fill_bytes(v)) - } - fn next_u64(&mut self) -> u64 { - next_u64(&mut |v| self.fill_bytes(v)) - } - fn fill_bytes(&mut self, v: &mut [u8]) { - let mut read = 0; - loop { - let mut r: libc::size_t = 0; - let len = v.len(); - let error = (self.0)(v[read..].as_mut_ptr() as *mut _, - (len - read) as libc::size_t, - &mut r as *mut _); - assert!(error == 0, "`get_random_bytes` failed!"); - read += r as usize; - - if read >= v.len() { break; } - } + Ok(()) } } } @@ -550,14 +468,11 @@ mod imp { pub struct OsRng; impl OsRng { - pub fn new() -> io::Result { - Err(io::Error::new(io::ErrorKind::Other, "Not supported")) + pub fn new() -> Result { + Err(Error::new(ErrorKind::Unavailable, "not supported on WASM")) } - } - - impl Rng for OsRng { - fn next_u32(&mut self) -> u32 { - panic!("Not supported") + pub fn try_fill(&mut self, v: &mut [u8]) -> Result<(), Error> { + Err(Error::new(ErrorKind::Unavailable, "not supported on WASM")) } } } @@ -577,7 +492,7 @@ mod test { r.next_u64(); let mut v = [0u8; 1000]; - r.fill_bytes(&mut v); + r.try_fill(&mut v).unwrap(); } #[test] @@ -603,7 +518,7 @@ mod test { thread::yield_now(); r.next_u64(); thread::yield_now(); - r.fill_bytes(&mut v); + r.try_fill(&mut v).unwrap(); thread::yield_now(); } }); diff --git a/src/prng/chacha.rs b/src/prng/chacha.rs index a73e8e78f46..b371480505a 100644 --- a/src/prng/chacha.rs +++ b/src/prng/chacha.rs @@ -10,15 +10,14 @@ //! The ChaCha random number generator. -use core::num::Wrapping as w; -use {Rng, SeedableRng, Rand}; +use core::fmt; +use rand_core::{impls, le}; +use {Rng, CryptoRng, SeedableRng, Error}; -#[allow(bad_style)] -type w32 = w; - -const KEY_WORDS : usize = 8; // 8 words for the 256-bit key -const STATE_WORDS : usize = 16; -const CHACHA_ROUNDS: u32 = 20; // Cryptographically secure from 8 upwards as of this writing +const SEED_WORDS: usize = 8; // 8 words for the 256-bit key +const STATE_WORDS: usize = 16; +const CHACHA_ROUNDS: u32 = 20; // Cryptographically secure from 8 upwards as of + // this writing /// A random number generator that uses the ChaCha20 algorithm [1]. /// @@ -29,26 +28,26 @@ const CHACHA_ROUNDS: u32 = 20; // Cryptographically secure from 8 upwards as of /// /// [1]: D. J. Bernstein, [*ChaCha, a variant of /// Salsa20*](http://cr.yp.to/chacha.html) -#[derive(Copy, Clone, Debug)] +#[derive(Clone)] pub struct ChaChaRng { - buffer: [w32; STATE_WORDS], // Internal buffer of output - state: [w32; STATE_WORDS], // Initial state - index: usize, // Index into state + buffer: [u32; STATE_WORDS], // Internal buffer of output + state: [u32; STATE_WORDS], // Initial state + index: usize, // Index into state } -static EMPTY: ChaChaRng = ChaChaRng { - buffer: [w(0); STATE_WORDS], - state: [w(0); STATE_WORDS], - index: STATE_WORDS -}; - +// Custom Debug implementation that does not expose the internal state +impl fmt::Debug for ChaChaRng { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "ChaChaRng {{}}") + } +} macro_rules! quarter_round{ ($a: expr, $b: expr, $c: expr, $d: expr) => {{ - $a = $a + $b; $d = $d ^ $a; $d = w($d.0.rotate_left(16)); - $c = $c + $d; $b = $b ^ $c; $b = w($b.0.rotate_left(12)); - $a = $a + $b; $d = $d ^ $a; $d = w($d.0.rotate_left( 8)); - $c = $c + $d; $b = $b ^ $c; $b = w($b.0.rotate_left( 7)); + $a = $a.wrapping_add($b); $d ^= $a; $d = $d.rotate_left(16); + $c = $c.wrapping_add($d); $b ^= $c; $b = $b.rotate_left(12); + $a = $a.wrapping_add($b); $d ^= $a; $d = $d.rotate_left( 8); + $c = $c.wrapping_add($d); $b ^= $c; $b = $b.rotate_left( 7); }} } @@ -68,27 +67,27 @@ macro_rules! double_round{ } #[inline] -fn core(output: &mut [w32; STATE_WORDS], input: &[w32; STATE_WORDS]) { - *output = *input; +fn core(new: &mut [u32; STATE_WORDS], input: &[u32; STATE_WORDS]) { + *new = *input; for _ in 0..CHACHA_ROUNDS / 2 { - double_round!(output); + double_round!(new); } for i in 0..STATE_WORDS { - output[i] = output[i] + input[i]; + new[i] = new[i].wrapping_add(input[i]); } } impl ChaChaRng { - /// Create an ChaCha random number generator using the default /// fixed key of 8 zero words. /// /// # Examples /// /// ```rust - /// use rand::{Rng, ChaChaRng}; + /// use rand::Rng; + /// use rand::prng::ChaChaRng; /// /// let mut ra = ChaChaRng::new_unseeded(); /// println!("{:?}", ra.next_u32()); @@ -102,9 +101,7 @@ impl ChaChaRng { /// - 2917185654 /// - 2419978656 pub fn new_unseeded() -> ChaChaRng { - let mut rng = EMPTY; - rng.init(&[0; KEY_WORDS]); - rng + ChaChaRng::init([0; SEED_WORDS]) } /// Sets the internal 128-bit ChaCha counter to @@ -119,7 +116,8 @@ impl ChaChaRng { /// # Examples /// /// ```rust - /// use rand::{Rng, ChaChaRng}; + /// use rand::Rng; + /// use rand::prng::ChaChaRng; /// /// let mut ra = ChaChaRng::new_unseeded(); /// ra.set_counter(0u64, 1234567890u64); @@ -127,10 +125,10 @@ impl ChaChaRng { /// println!("{:?}", ra.next_u32()); /// ``` pub fn set_counter(&mut self, counter_low: u64, counter_high: u64) { - self.state[12] = w((counter_low >> 0) as u32); - self.state[13] = w((counter_low >> 32) as u32); - self.state[14] = w((counter_high >> 0) as u32); - self.state[15] = w((counter_high >> 32) as u32); + self.state[12] = (counter_low >> 0) as u32; + self.state[13] = (counter_low >> 32) as u32; + self.state[14] = (counter_high >> 0) as u32; + self.state[15] = (counter_high >> 32) as u32; self.index = STATE_WORDS; // force recomputation } @@ -152,22 +150,15 @@ impl ChaChaRng { /// ``` /// [1]: Daniel J. Bernstein. [*Extending the Salsa20 /// nonce.*](http://cr.yp.to/papers.html#xsalsa) - fn init(&mut self, key: &[u32; KEY_WORDS]) { - self.state[0] = w(0x61707865); - self.state[1] = w(0x3320646E); - self.state[2] = w(0x79622D32); - self.state[3] = w(0x6B206574); - - for i in 0..KEY_WORDS { - self.state[4+i] = w(key[i]); - } - - self.state[12] = w(0); - self.state[13] = w(0); - self.state[14] = w(0); - self.state[15] = w(0); - - self.index = STATE_WORDS; + fn init(seed: [u32; SEED_WORDS]) -> Self { + ChaChaRng { + buffer: [0; STATE_WORDS], + state: [0x61707865, 0x3320646E, 0x79622D32, 0x6B206574, // constants + seed[0], seed[1], seed[2], seed[3], // seed + seed[4], seed[5], seed[6], seed[7], // seed + 0, 0, 0, 0], // counter + index: STATE_WORDS, // generate on first use + } } /// Refill the internal output buffer (`self.buffer`) @@ -175,59 +166,70 @@ impl ChaChaRng { core(&mut self.buffer, &self.state); self.index = 0; // update 128-bit counter - self.state[12] = self.state[12] + w(1); - if self.state[12] != w(0) { return }; - self.state[13] = self.state[13] + w(1); - if self.state[13] != w(0) { return }; - self.state[14] = self.state[14] + w(1); - if self.state[14] != w(0) { return }; - self.state[15] = self.state[15] + w(1); + self.state[12] = self.state[12].wrapping_add(1); + if self.state[12] != 0 { return }; + self.state[13] = self.state[13].wrapping_add(1); + if self.state[13] != 0 { return }; + self.state[14] = self.state[14].wrapping_add(1); + if self.state[14] != 0 { return }; + self.state[15] = self.state[15].wrapping_add(1); } } impl Rng for ChaChaRng { #[inline] fn next_u32(&mut self) -> u32 { - if self.index == STATE_WORDS { + // Using a local variable for `index`, and checking the size avoids a + // bounds check later on. + let mut index = self.index as usize; + if index >= STATE_WORDS { self.update(); + index = 0; } - let value = self.buffer[self.index % STATE_WORDS]; + let value = self.buffer[index]; self.index += 1; - value.0 + value } -} -impl<'a> SeedableRng<&'a [u32]> for ChaChaRng { + fn next_u64(&mut self) -> u64 { + impls::next_u64_via_u32(self) + } + + #[cfg(feature = "i128_support")] + fn next_u128(&mut self) -> u128 { + impls::next_u128_via_u64(self) + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + let mut read_len = 0; + while read_len < dest.len() { + if self.index >= self.buffer.len() { + self.update(); + } + + let (consumed_u32, filled_u8) = + impls::fill_via_u32_chunks(&mut self.buffer[self.index..], + &mut dest[read_len..]); - fn reseed(&mut self, seed: &'a [u32]) { - // reset state - self.init(&[0u32; KEY_WORDS]); - // set key in place - let key = &mut self.state[4 .. 4+KEY_WORDS]; - for (k, s) in key.iter_mut().zip(seed.iter()) { - *k = w(*s); + self.index += consumed_u32; + read_len += filled_u8; } } - /// Create a ChaCha generator from a seed, - /// obtained from a variable-length u32 array. - /// Only up to 8 words are used; if less than 8 - /// words are used, the remaining are set to zero. - fn from_seed(seed: &'a [u32]) -> ChaChaRng { - let mut rng = EMPTY; - rng.reseed(seed); - rng + fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { + Ok(self.fill_bytes(dest)) } } -impl Rand for ChaChaRng { - fn rand(other: &mut R) -> ChaChaRng { - let mut key : [u32; KEY_WORDS] = [0; KEY_WORDS]; - for word in key.iter_mut() { - *word = other.gen(); - } - SeedableRng::from_seed(&key[..]) +impl CryptoRng for ChaChaRng {} + +impl SeedableRng for ChaChaRng { + type Seed = [u8; SEED_WORDS*4]; + fn from_seed(seed: Self::Seed) -> Self { + let mut seed_u32 = [0u32; SEED_WORDS]; + le::read_u32_into(&seed, &mut seed_u32); + ChaChaRng::init(seed_u32) } } @@ -239,39 +241,27 @@ mod test { #[test] fn test_rng_rand_seeded() { - let s = ::test::rng().gen_iter::().take(8).collect::>(); - let mut ra: ChaChaRng = SeedableRng::from_seed(&s[..]); - let mut rb: ChaChaRng = SeedableRng::from_seed(&s[..]); - assert!(::test::iter_eq(ra.gen_ascii_chars().take(100), - rb.gen_ascii_chars().take(100))); - } - - #[test] - fn test_rng_seeded() { - let seed : &[_] = &[0,1,2,3,4,5,6,7]; - let mut ra: ChaChaRng = SeedableRng::from_seed(seed); - let mut rb: ChaChaRng = SeedableRng::from_seed(seed); - assert!(::test::iter_eq(ra.gen_ascii_chars().take(100), - rb.gen_ascii_chars().take(100))); - } - - #[test] - fn test_rng_reseed() { - let s = ::test::rng().gen_iter::().take(8).collect::>(); - let mut r: ChaChaRng = SeedableRng::from_seed(&s[..]); - let string1: String = r.gen_ascii_chars().take(100).collect(); - - r.reseed(&s); - - let string2: String = r.gen_ascii_chars().take(100).collect(); - assert_eq!(string1, string2); + // Test that various construction techniques produce a working RNG. + /* TODO: from_hashable + let mut ra = ChaChaRng::from_hashable("some weak seed"); + ra.next_u32(); + */ + let mut rb = ChaChaRng::from_rng(&mut ::test::rng()).unwrap(); + rb.next_u32(); + + let seed = [0,0,0,0,0,0,0,0, + 1,0,0,0,0,0,0,0, + 2,0,0,0,0,0,0,0, + 3,0,0,0,0,0,0,0]; + let mut rc = ChaChaRng::from_seed(seed); + rc.next_u32(); } #[test] fn test_rng_true_values() { // Test vectors 1 and 2 from // http://tools.ietf.org/html/draft-nir-cfrg-chacha20-poly1305-04 - let seed : &[_] = &[0u32; 8]; + let seed = [0u8; 32]; let mut ra: ChaChaRng = SeedableRng::from_seed(seed); let v = (0..16).map(|_| ra.next_u32()).collect::>(); @@ -289,7 +279,7 @@ mod test { 0x281fed31, 0x45fb0a51, 0x1f0ae1ac, 0x6f4d794b)); - let seed : &[_] = &[0,1,2,3,4,5,6,7]; + let seed = [0,0,0,0, 1,0,0,0, 2,0,0,0, 3,0,0,0, 4,0,0,0, 5,0,0,0, 6,0,0,0, 7,0,0,0]; let mut ra: ChaChaRng = SeedableRng::from_seed(seed); // Store the 17*i-th 32-bit word, @@ -309,9 +299,23 @@ mod test { 0x2c5bad8f, 0x898881dc, 0x5f1c86d9, 0xc1f8e7f4)); } + #[test] + fn test_rng_true_bytes() { + let seed = [0u8; 32]; + let mut ra: ChaChaRng = SeedableRng::from_seed(seed); + let mut buf = [0u8; 32]; + ra.fill_bytes(&mut buf); + // Same as first values in test_isaac_true_values as bytes in LE order + assert_eq!(buf, + [118, 184, 224, 173, 160, 241, 61, 144, + 64, 93, 106, 229, 83, 134, 189, 40, + 189, 210, 25, 184, 160, 141, 237, 26, + 168, 54, 239, 204, 139, 119, 13, 199]); + } + #[test] fn test_rng_clone() { - let seed : &[_] = &[0u32; 8]; + let seed = [0u8; 32]; let mut rng: ChaChaRng = SeedableRng::from_seed(seed); let mut clone = rng.clone(); for _ in 0..16 { diff --git a/src/prng/hc128.rs b/src/prng/hc128.rs new file mode 100644 index 00000000000..41a6362a3e8 --- /dev/null +++ b/src/prng/hc128.rs @@ -0,0 +1,529 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! The HC-128 random number generator. + +use core::{fmt, slice}; +use rand_core::{impls, le}; +use {Rng, CryptoRng, SeedableRng, Error}; + +const SEED_WORDS: usize = 8; // 128 bit key followed by 128 bit iv + +/// A cryptographically secure random number generator that uses the HC-128 +/// algorithm. +/// +/// HC-128 is a stream cipher designed by Hongjun Wu[1], that we use as an RNG. +/// It is selected as one of the "stream ciphers suitable for widespread +/// adoption" by eSTREAM[2]. +/// +/// HC-128 is an array based RNG. In this it is similar to RC-4 and ISAAC before +/// it, but those have never been proven cryptographically secure (or have even +/// been broken). +/// +/// Because HC-128 works with simple indexing into a large array and with a few +/// operations that parallelize well, it has very good performance. The size of +/// the array it needs, 4kb, can however be a disadvantage. +/// +/// This implementation is not based on the version of HC-128 submitted to the +/// eSTREAM contest, but on a later version by the author with a few small +/// improvements from December 15, 2009[3]. +/// +/// HC-128 has no known weaknesses that are easier to exploit than doing a +/// brute-force search of 2128. A very comprehensive analysis of the +/// current state of known attacks / weaknesses of HC-128 is given in [4]. +/// +/// ## References +/// [1]: Hongjun Wu (2008). ["The Stream Cipher HC-128"] +/// (http://www.ecrypt.eu.org/stream/p3ciphers/hc/hc128_p3.pdf). +/// *The eSTREAM Finalists*, LNCS 4986, pp. 39--47, Springer-Verlag. +/// +/// [2]: [eSTREAM: the ECRYPT Stream Cipher Project] +/// (http://www.ecrypt.eu.org/stream/) +/// +/// [3]: Hongjun Wu, [Stream Ciphers HC-128 and HC-256] +/// (http://www3.ntu.edu.sg/home/wuhj/research/hc/index.html) +/// +/// [4]: Shashwat Raizada (January 2015), +/// ["Some Results On Analysis And Implementation Of HC-128 Stream Cipher"] +/// (http://library.isical.ac.in:8080/jspui/bitstream/123456789/6636/1/TH431.pdf). +pub struct Hc128Rng { + state: Hc128, + results: [u32; 16], + index: usize, +} + +impl Clone for Hc128Rng { + fn clone(&self) -> Hc128Rng { + Hc128Rng { + state: self.state, + results: self.results, + index: self.index + } + } +} + +#[derive(Copy, Clone)] +struct Hc128 { + t: [u32; 1024], + counter1024: usize, +} + +// Custom Debug implementation that does not expose the internal state +impl fmt::Debug for Hc128Rng { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Hc128Rng {{}}") + } +} + +impl Hc128Rng { + fn init(seed: [u32; SEED_WORDS]) -> Self { + #[inline] + fn f1(x: u32) -> u32 { + x.rotate_right(7) ^ x.rotate_right(18) ^ (x >> 3) + } + + #[inline] + fn f2(x: u32) -> u32 { + x.rotate_right(17) ^ x.rotate_right(19) ^ (x >> 10) + } + + let mut t = [0u32; 1024]; + + // Expand the key and iv into P and Q + let (key, iv) = seed.split_at(4); + t[..4].copy_from_slice(key); + t[4..8].copy_from_slice(key); + t[8..12].copy_from_slice(iv); + t[12..16].copy_from_slice(iv); + + // Generate the 256 intermediate values W[16] ... W[256+16-1], and + // copy the last 16 generated values to the start op P. + for i in 16..256+16 { + t[i] = f2(t[i-2]).wrapping_add(t[i-7]).wrapping_add(f1(t[i-15])) + .wrapping_add(t[i-16]).wrapping_add(i as u32); + } + { + let (p1, p2) = t.split_at_mut(256); + p1[0..16].copy_from_slice(&p2[0..16]); + } + + // Generate both the P and Q tables + for i in 16..1024 { + t[i] = f2(t[i-2]).wrapping_add(t[i-7]).wrapping_add(f1(t[i-15])) + .wrapping_add(t[i-16]).wrapping_add(256 + i as u32); + } + + let mut state = Hc128Rng { + state: Hc128 { t: t, counter1024: 0 }, + results: [0; 16], + index: 16, // generate on first use + }; + + // run the cipher 1024 steps + for _ in 0..64 { state.state.sixteen_steps() }; + state.state.counter1024 = 0; + state + } +} + +impl Hc128 { + // One step of HC-128, update P and generate 32 bits keystream + #[inline(always)] + fn step_p(&mut self, i: usize, i511: usize, i3: usize, i10: usize, i12: usize) + -> u32 + { + let (p, q) = self.t.split_at_mut(512); + // FIXME: it would be great if we the bounds checks here could be + // optimized out, and we would not need unsafe. + // This improves performance by about 7%. + unsafe { + let temp0 = p.get_unchecked(i511).rotate_right(23); + let temp1 = p.get_unchecked(i3).rotate_right(10); + let temp2 = p.get_unchecked(i10).rotate_right(8); + *p.get_unchecked_mut(i) = p.get_unchecked(i) + .wrapping_add(temp2) + .wrapping_add(temp0 ^ temp1); + let temp3 = { + // The h1 function in HC-128 + let a = *p.get_unchecked(i12) as u8; + let c = (p.get_unchecked(i12) >> 16) as u8; + q[a as usize].wrapping_add(q[256 + c as usize]) + }; + temp3 ^ p.get_unchecked(i) + } + } + + // One step of HC-128, update Q and generate 32 bits keystream + // Similar to `step_p`, but `p` and `q` are swapped, and the rotates are to + // the left instead of to the right. + #[inline(always)] + fn step_q(&mut self, i: usize, i511: usize, i3: usize, i10: usize, i12: usize) + -> u32 + { + let (p, q) = self.t.split_at_mut(512); + unsafe { + let temp0 = q.get_unchecked(i511).rotate_left(23); + let temp1 = q.get_unchecked(i3).rotate_left(10); + let temp2 = q.get_unchecked(i10).rotate_left(8); + *q.get_unchecked_mut(i) = q.get_unchecked(i) + .wrapping_add(temp2) + .wrapping_add(temp0 ^ temp1); + let temp3 = { + // The h2 function in HC-128 + let a = *q.get_unchecked(i12) as u8; + let c = (q.get_unchecked(i12) >> 16) as u8; + p[a as usize].wrapping_add(p[256 + c as usize]) + }; + temp3 ^ q.get_unchecked(i) + } + } + + fn update(&mut self, results: &mut [u32]) { + assert!(self.counter1024 % 16 == 0); + + let cc = self.counter1024 % 512; + let dd = (cc + 16) % 512; + let ee = cc.wrapping_sub(16) % 512; + + if self.counter1024 & 512 == 0 { + // P block + results[0] = self.step_p(cc+0, cc+1, ee+13, ee+6, ee+4); + results[1] = self.step_p(cc+1, cc+2, ee+14, ee+7, ee+5); + results[2] = self.step_p(cc+2, cc+3, ee+15, ee+8, ee+6); + results[3] = self.step_p(cc+3, cc+4, cc+0, ee+9, ee+7); + results[4] = self.step_p(cc+4, cc+5, cc+1, ee+10, ee+8); + results[5] = self.step_p(cc+5, cc+6, cc+2, ee+11, ee+9); + results[6] = self.step_p(cc+6, cc+7, cc+3, ee+12, ee+10); + results[7] = self.step_p(cc+7, cc+8, cc+4, ee+13, ee+11); + results[8] = self.step_p(cc+8, cc+9, cc+5, ee+14, ee+12); + results[9] = self.step_p(cc+9, cc+10, cc+6, ee+15, ee+13); + results[10] = self.step_p(cc+10, cc+11, cc+7, cc+0, ee+14); + results[11] = self.step_p(cc+11, cc+12, cc+8, cc+1, ee+15); + results[12] = self.step_p(cc+12, cc+13, cc+9, cc+2, cc+0); + results[13] = self.step_p(cc+13, cc+14, cc+10, cc+3, cc+1); + results[14] = self.step_p(cc+14, cc+15, cc+11, cc+4, cc+2); + results[15] = self.step_p(cc+15, dd+0, cc+12, cc+5, cc+3); + } else { + // Q block + results[0] = self.step_q(cc+0, cc+1, ee+13, ee+6, ee+4); + results[1] = self.step_q(cc+1, cc+2, ee+14, ee+7, ee+5); + results[2] = self.step_q(cc+2, cc+3, ee+15, ee+8, ee+6); + results[3] = self.step_q(cc+3, cc+4, cc+0, ee+9, ee+7); + results[4] = self.step_q(cc+4, cc+5, cc+1, ee+10, ee+8); + results[5] = self.step_q(cc+5, cc+6, cc+2, ee+11, ee+9); + results[6] = self.step_q(cc+6, cc+7, cc+3, ee+12, ee+10); + results[7] = self.step_q(cc+7, cc+8, cc+4, ee+13, ee+11); + results[8] = self.step_q(cc+8, cc+9, cc+5, ee+14, ee+12); + results[9] = self.step_q(cc+9, cc+10, cc+6, ee+15, ee+13); + results[10] = self.step_q(cc+10, cc+11, cc+7, cc+0, ee+14); + results[11] = self.step_q(cc+11, cc+12, cc+8, cc+1, ee+15); + results[12] = self.step_q(cc+12, cc+13, cc+9, cc+2, cc+0); + results[13] = self.step_q(cc+13, cc+14, cc+10, cc+3, cc+1); + results[14] = self.step_q(cc+14, cc+15, cc+11, cc+4, cc+2); + results[15] = self.step_q(cc+15, dd+0, cc+12, cc+5, cc+3); + } + self.counter1024 = self.counter1024.wrapping_add(16); + } + + fn sixteen_steps(&mut self) { + assert!(self.counter1024 % 16 == 0); + + let cc = self.counter1024 % 512; + let dd = (cc + 16) % 512; + let ee = cc.wrapping_sub(16) % 512; + + if self.counter1024 < 512 { + // P block + self.t[cc+0] = self.step_p(cc+0, cc+1, ee+13, ee+6, ee+4); + self.t[cc+1] = self.step_p(cc+1, cc+2, ee+14, ee+7, ee+5); + self.t[cc+2] = self.step_p(cc+2, cc+3, ee+15, ee+8, ee+6); + self.t[cc+3] = self.step_p(cc+3, cc+4, cc+0, ee+9, ee+7); + self.t[cc+4] = self.step_p(cc+4, cc+5, cc+1, ee+10, ee+8); + self.t[cc+5] = self.step_p(cc+5, cc+6, cc+2, ee+11, ee+9); + self.t[cc+6] = self.step_p(cc+6, cc+7, cc+3, ee+12, ee+10); + self.t[cc+7] = self.step_p(cc+7, cc+8, cc+4, ee+13, ee+11); + self.t[cc+8] = self.step_p(cc+8, cc+9, cc+5, ee+14, ee+12); + self.t[cc+9] = self.step_p(cc+9, cc+10, cc+6, ee+15, ee+13); + self.t[cc+10] = self.step_p(cc+10, cc+11, cc+7, cc+0, ee+14); + self.t[cc+11] = self.step_p(cc+11, cc+12, cc+8, cc+1, ee+15); + self.t[cc+12] = self.step_p(cc+12, cc+13, cc+9, cc+2, cc+0); + self.t[cc+13] = self.step_p(cc+13, cc+14, cc+10, cc+3, cc+1); + self.t[cc+14] = self.step_p(cc+14, cc+15, cc+11, cc+4, cc+2); + self.t[cc+15] = self.step_p(cc+15, dd+0, cc+12, cc+5, cc+3); + } else { + // Q block + self.t[cc+512+0] = self.step_q(cc+0, cc+1, ee+13, ee+6, ee+4); + self.t[cc+512+1] = self.step_q(cc+1, cc+2, ee+14, ee+7, ee+5); + self.t[cc+512+2] = self.step_q(cc+2, cc+3, ee+15, ee+8, ee+6); + self.t[cc+512+3] = self.step_q(cc+3, cc+4, cc+0, ee+9, ee+7); + self.t[cc+512+4] = self.step_q(cc+4, cc+5, cc+1, ee+10, ee+8); + self.t[cc+512+5] = self.step_q(cc+5, cc+6, cc+2, ee+11, ee+9); + self.t[cc+512+6] = self.step_q(cc+6, cc+7, cc+3, ee+12, ee+10); + self.t[cc+512+7] = self.step_q(cc+7, cc+8, cc+4, ee+13, ee+11); + self.t[cc+512+8] = self.step_q(cc+8, cc+9, cc+5, ee+14, ee+12); + self.t[cc+512+9] = self.step_q(cc+9, cc+10, cc+6, ee+15, ee+13); + self.t[cc+512+10] = self.step_q(cc+10, cc+11, cc+7, cc+0, ee+14); + self.t[cc+512+11] = self.step_q(cc+11, cc+12, cc+8, cc+1, ee+15); + self.t[cc+512+12] = self.step_q(cc+12, cc+13, cc+9, cc+2, cc+0); + self.t[cc+512+13] = self.step_q(cc+13, cc+14, cc+10, cc+3, cc+1); + self.t[cc+512+14] = self.step_q(cc+14, cc+15, cc+11, cc+4, cc+2); + self.t[cc+512+15] = self.step_q(cc+15, dd+0, cc+12, cc+5, cc+3); + } + self.counter1024 += 16; + } +} + +impl Rng for Hc128Rng { + #[inline] + fn next_u32(&mut self) -> u32 { + if self.index >= 16 { + self.state.update(&mut self.results); + self.index = 0; + } + + let value = self.results[self.index]; + self.index += 1; + value + } + + #[inline] + fn next_u64(&mut self) -> u64 { + let index = self.index; + if index < 15 { + self.index += 2; + // Read an u64 from the current index + if cfg!(any(target_arch = "x86", target_arch = "x86_64")) { + unsafe { *(&self.results[index] as *const u32 as *const u64) } + } else { + let x = self.results[index] as u64; + let y = self.results[index + 1] as u64; + (y << 32) | x + } + } else if index >= 16 { + self.state.update(&mut self.results); + self.index = 2; + let x = self.results[0] as u64; + let y = self.results[1] as u64; + (y << 32) | x + } else { + let x = self.results[15] as u64; + self.state.update(&mut self.results); + self.index = 1; + let y = self.results[0] as u64; + (y << 32) | x + } + } + + #[cfg(feature = "i128_support")] + fn next_u128(&mut self) -> u128 { + impls::next_u128_via_u64(self) + } + + // As an optimization we try to write directly into the output buffer. + // This is only enabled for platforms where unaligned writes are known to + // be safe and fast. + // This improves performance by about 12%. + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + fn fill_bytes(&mut self, dest: &mut [u8]) { + let mut filled = 0; + + // Continue filling from the current set of results + if self.index < 16 { + let (consumed_u32, filled_u8) = + impls::fill_via_u32_chunks(&mut self.results[self.index..], + dest); + + self.index += consumed_u32; + filled += filled_u8; + } + + let len_remainder = (dest.len() - filled) % 16; + let len_direct = dest.len() - len_remainder; + + while filled < len_direct { + let dest_u32: &mut [u32] = unsafe { + slice::from_raw_parts_mut( + dest[filled..].as_mut_ptr() as *mut u8 as *mut u32, + 16) + }; + self.state.update(dest_u32); + filled += 16 * 4; + } + self.index = 16; + + if len_remainder > 0 { + self.state.update(&mut self.results); + + let (consumed_u32, _) = + impls::fill_via_u32_chunks(&mut self.results, + &mut dest[filled..]); + + self.index = consumed_u32; + } + } + + #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] + fn fill_bytes(&mut self, dest: &mut [u8]) { + let mut read_len = 0; + while read_len < dest.len() { + if self.index >= 16 { + self.state.update(&mut self.results); + self.index = 0; + } + + let (consumed_u32, filled_u8) = + impls::fill_via_u32_chunks(&mut self.results[self.index..], + &mut dest[read_len..]); + + self.index += consumed_u32; + read_len += filled_u8; + } + } + + fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { + Ok(self.fill_bytes(dest)) + } +} + +impl SeedableRng for Hc128Rng { + type Seed = [u8; SEED_WORDS*4]; + fn from_seed(seed: Self::Seed) -> Self { + let mut seed_u32 = [0u32; SEED_WORDS]; + le::read_u32_into(&seed, &mut seed_u32); + Hc128Rng::init(seed_u32) + } +} + +impl CryptoRng for Hc128Rng {} + +#[cfg(test)] +mod test { + use {Rng, SeedableRng}; + use super::Hc128Rng; + + #[test] + // Test vector 1 from the paper "The Stream Cipher HC-128" + fn test_hc128_true_values_a() { + let seed = [0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // key + 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; // iv + let mut rng = Hc128Rng::from_seed(seed); + + let v = (0..16).map(|_| rng.next_u32()).collect::>(); + assert_eq!(v, + vec!(0x73150082, 0x3bfd03a0, 0xfb2fd77f, 0xaa63af0e, + 0xde122fc6, 0xa7dc29b6, 0x62a68527, 0x8b75ec68, + 0x9036db1e, 0x81896005, 0x00ade078, 0x491fbf9a, + 0x1cdc3013, 0x6c3d6e24, 0x90f664b2, 0x9cd57102)); + } + + #[test] + // Test vector 2 from the paper "The Stream Cipher HC-128" + fn test_hc128_true_values_b() { + let seed = [0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // key + 1,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; // iv + let mut rng = Hc128Rng::from_seed(seed); + + let v = (0..16).map(|_| rng.next_u32()).collect::>(); + assert_eq!(v, + vec!(0xc01893d5, 0xb7dbe958, 0x8f65ec98, 0x64176604, + 0x36fc6724, 0xc82c6eec, 0x1b1c38a7, 0xc9b42a95, + 0x323ef123, 0x0a6a908b, 0xce757b68, 0x9f14f7bb, + 0xe4cde011, 0xaeb5173f, 0x89608c94, 0xb5cf46ca)); + } + + #[test] + // Test vector 3 from the paper "The Stream Cipher HC-128" + fn test_hc128_true_values_c() { + let seed = [0x55,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // key + 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; // iv + let mut rng = Hc128Rng::from_seed(seed); + + let v = (0..16).map(|_| rng.next_u32()).collect::>(); + assert_eq!(v, + vec!(0x518251a4, 0x04b4930a, 0xb02af931, 0x0639f032, + 0xbcb4a47a, 0x5722480b, 0x2bf99f72, 0xcdc0e566, + 0x310f0c56, 0xd3cc83e8, 0x663db8ef, 0x62dfe07f, + 0x593e1790, 0xc5ceaa9c, 0xab03806f, 0xc9a6e5a0)); + } + + #[test] + fn test_hc128_true_values_u64() { + let seed = [0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // key + 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; // iv + let mut rng = Hc128Rng::from_seed(seed); + + let v = (0..8).map(|_| rng.next_u64()).collect::>(); + assert_eq!(v, + vec!(0x3bfd03a073150082, 0xaa63af0efb2fd77f, + 0xa7dc29b6de122fc6, 0x8b75ec6862a68527, + 0x818960059036db1e, 0x491fbf9a00ade078, + 0x6c3d6e241cdc3013, 0x9cd5710290f664b2)); + + // The RNG operates in a P block of 512 results and next a Q block. + // After skipping 2*800 u32 results we end up somewhere in the Q block + // of the second round + for _ in 0..800 { rng.next_u64(); } + + let v = (0..8).map(|_| rng.next_u64()).collect::>(); + assert_eq!(v, + vec!(0xd8c4d6ca84d0fc10, 0xf16a5d91dc66e8e7, + 0xd800de5bc37a8653, 0x7bae1f88c0dfbb4c, + 0x3bfe1f374e6d4d14, 0x424b55676be3fa06, + 0xe3a1e8758cbff579, 0x417f7198c5652bcd)); + } + + #[test] + fn test_hc128_true_values_bytes() { + let seed = [0x55,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // key + 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; // iv + let mut rng = Hc128Rng::from_seed(seed); + let expected = + vec!(0x31, 0xf9, 0x2a, 0xb0, 0x32, 0xf0, 0x39, 0x06, + 0x7a, 0xa4, 0xb4, 0xbc, 0x0b, 0x48, 0x22, 0x57, + 0x72, 0x9f, 0xf9, 0x2b, 0x66, 0xe5, 0xc0, 0xcd, + 0x56, 0x0c, 0x0f, 0x31, 0xe8, 0x83, 0xcc, 0xd3, + 0xef, 0xb8, 0x3d, 0x66, 0x7f, 0xe0, 0xdf, 0x62, + 0x90, 0x17, 0x3e, 0x59, 0x9c, 0xaa, 0xce, 0xc5, + 0x6f, 0x80, 0x03, 0xab, 0xa0, 0xe5, 0xa6, 0xc9, + 0x60, 0x95, 0x84, 0x7a, 0xa5, 0x68, 0x5a, 0x84, + 0xea, 0xd5, 0xf3, 0xea, 0x73, 0xa9, 0xad, 0x01, + 0x79, 0x7d, 0xbe, 0x9f, 0xea, 0xe3, 0xf9, 0x74, + 0x0e, 0xda, 0x2f, 0xa0, 0xe4, 0x7b, 0x4b, 0x1b, + 0xdd, 0x17, 0x69, 0x4a, 0xfe, 0x9f, 0x56, 0x95, + 0xad, 0x83, 0x6b, 0x9d, 0x60, 0xa1, 0x99, 0x96, + 0x90, 0x00, 0x66, 0x7f, 0xfa, 0x7e, 0x65, 0xe9, + 0xac, 0x8b, 0x92, 0x34, 0x77, 0xb4, 0x23, 0xd0, + 0xb9, 0xab, 0xb1, 0x47, 0x7d, 0x4a, 0x13, 0x0a); + + // Pick a somewhat large buffer so we can test filling with the + // remainder from `state.results`, directly filling the buffer, and + // filling the remainder of the buffer. + let mut buffer = vec!(0u8; 16*4*2); + // Consume a value the we have a remainder. + let _ = rng.next_u64(); + rng.fill_bytes(&mut buffer); + + for i in buffer.iter() { + print!("0x{:02x}, ", i); + } + assert_eq!(buffer, expected); + } + + #[test] + fn test_hc128_clone() { + let seed = [0x55,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, // key + 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; // iv + let mut rng1 = Hc128Rng::from_seed(seed); + let mut rng2 = rng1.clone(); + for _ in 0..16 { + assert_eq!(rng1.next_u32(), rng2.next_u32()); + } + } +} diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index cf5eb679c08..f6985a95ef4 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -10,263 +10,361 @@ //! The ISAAC random number generator. -#![allow(non_camel_case_types)] - -use core::slice; -use core::iter::repeat; +use core::{fmt, slice}; use core::num::Wrapping as w; -use core::fmt; - -use {Rng, SeedableRng, Rand}; +use rand_core::{impls, le}; +use {Rng, SeedableRng, Error}; -#[allow(bad_style)] +#[allow(non_camel_case_types)] type w32 = w; const RAND_SIZE_LEN: usize = 8; -const RAND_SIZE: u32 = 1 << RAND_SIZE_LEN; -const RAND_SIZE_USIZE: usize = 1 << RAND_SIZE_LEN; +const RAND_SIZE: usize = 1 << RAND_SIZE_LEN; -/// A random number generator that uses the ISAAC algorithm[1]. +/// A random number generator that uses the ISAAC algorithm. +/// +/// ISAAC stands for "Indirection, Shift, Accumulate, Add, and Count" which are +/// the principal bitwise operations employed. It is the most advanced of a +/// series of array based random number generator designed by Robert Jenkins +/// in 1996[1][2]. +/// +/// Although ISAAC is designed to be cryptographically secure, its design is not +/// founded in cryptographic theory. Therefore it is _not recommended for_ +/// cryptographic purposes. It is however one of the strongest non-cryptograpic +/// RNGs, and that while still being reasonably fast. +/// +/// Where fast random numbers are needed which should still be secure, but where +/// speed is more important than absolute (cryptographic) security (e.g. to +/// initialise hashes in the std library), a generator like ISAAC may be a good +/// choice. +/// +/// In 2006 an improvement to ISAAC was suggested by Jean-Philippe Aumasson, +/// named ISAAC+[3]. But because the specification is not complete, there is no +/// good implementation, and because the suggested bias may not exist, it is not +/// implemented here. +/// +/// ## Overview of the ISAAC algorithm: +/// (in pseudo-code) +/// +/// ```text +/// Input: a, b, c, s[256] // state +/// Output: r[256] // results +/// +/// mix(a,i) = a ^ a << 13 if i = 0 mod 4 +/// a ^ a >> 6 if i = 1 mod 4 +/// a ^ a << 2 if i = 2 mod 4 +/// a ^ a >> 16 if i = 3 mod 4 +/// +/// c = c + 1 +/// b = b + c +/// +/// for i in 0..256 { +/// x = s_[i] +/// a = f(a,i) + s[i+128 mod 256] +/// y = a + b + s[x>>2 mod 256] +/// s[i] = y +/// b = x + s[y>>10 mod 256] +/// r[i] = b +/// } +/// ``` +/// +/// Numbers are generated in blocks of 256. This means the function above only +/// runs once every 256 times you ask for a next random number. In all other +/// circumstances the last element of the results array is returned. +/// +/// ISAAC therefore needs a lot of memory, relative to other non-vrypto RNGs. +/// 2 * 256 * 4 = 2 kb to hold the state and results. /// -/// The ISAAC algorithm is generally accepted as suitable for -/// cryptographic purposes, but this implementation has not be -/// verified as such. Prefer a generator like `OsRng` that defers to -/// the operating system for cases that need high security. +/// ## References +/// [1]: Bob Jenkins, [*ISAAC: A fast cryptographic random number generator*] +/// (http://burtleburtle.net/bob/rand/isaacafa.html) /// -/// [1]: Bob Jenkins, [*ISAAC: A fast cryptographic random number -/// generator*](http://www.burtleburtle.net/bob/rand/isaacafa.html) -#[derive(Copy)] +/// [2]: Bob Jenkins, [*ISAAC and RC4*] +/// (http://burtleburtle.net/bob/rand/isaac.html) +/// +/// [3]: Jean-Philippe Aumasson, [*On the pseudo-random generator ISAAC*] +/// (http://eprint.iacr.org/2006/438) pub struct IsaacRng { - cnt: u32, - rsl: [w32; RAND_SIZE_USIZE], - mem: [w32; RAND_SIZE_USIZE], + rsl: [u32; RAND_SIZE], + mem: [w32; RAND_SIZE], a: w32, b: w32, c: w32, + index: u32, } -static EMPTY: IsaacRng = IsaacRng { - cnt: 0, - rsl: [w(0); RAND_SIZE_USIZE], - mem: [w(0); RAND_SIZE_USIZE], - a: w(0), b: w(0), c: w(0), -}; +// Cannot be derived because [u32; 256] does not implement Clone +// FIXME: remove once RFC 2000 gets implemented +impl Clone for IsaacRng { + fn clone(&self) -> IsaacRng { + IsaacRng { + rsl: self.rsl, + mem: self.mem, + a: self.a, + b: self.b, + c: self.c, + index: self.index, + } + } +} -impl IsaacRng { +// Custom Debug implementation that does not expose the internal state +impl fmt::Debug for IsaacRng { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "IsaacRng {{}}") + } +} +impl IsaacRng { /// Create an ISAAC random number generator using the default /// fixed seed. pub fn new_unseeded() -> IsaacRng { - let mut rng = EMPTY; - rng.init(false); - rng + Self::new_from_u64(0) } - - /// Initialises `self`. If `use_rsl` is true, then use the current value - /// of `rsl` as a seed, otherwise construct one algorithmically (not - /// randomly). - fn init(&mut self, use_rsl: bool) { - let mut a = w(0x9e3779b9); - let mut b = a; - let mut c = a; - let mut d = a; - let mut e = a; - let mut f = a; - let mut g = a; - let mut h = a; - - macro_rules! mix { - () => {{ - a=a^(b<<11); d=d+a; b=b+c; - b=b^(c>>2); e=e+b; c=c+d; - c=c^(d<<8); f=f+c; d=d+e; - d=d^(e>>16); g=g+d; e=e+f; - e=e^(f<<10); h=h+e; f=f+g; - f=f^(g>>4); a=a+f; g=g+h; - g=g^(h<<8); b=b+g; h=h+a; - h=h^(a>>9); c=c+h; a=a+b; - }} - } - - for _ in 0..4 { - mix!(); - } - - if use_rsl { - macro_rules! memloop { - ($arr:expr) => {{ - for i in (0..RAND_SIZE_USIZE/8).map(|i| i * 8) { - a=a+$arr[i ]; b=b+$arr[i+1]; - c=c+$arr[i+2]; d=d+$arr[i+3]; - e=e+$arr[i+4]; f=f+$arr[i+5]; - g=g+$arr[i+6]; h=h+$arr[i+7]; - mix!(); - self.mem[i ]=a; self.mem[i+1]=b; - self.mem[i+2]=c; self.mem[i+3]=d; - self.mem[i+4]=e; self.mem[i+5]=f; - self.mem[i+6]=g; self.mem[i+7]=h; - } - }} - } - - memloop!(self.rsl); - memloop!(self.mem); - } else { - for i in (0..RAND_SIZE_USIZE/8).map(|i| i * 8) { - mix!(); - self.mem[i ]=a; self.mem[i+1]=b; - self.mem[i+2]=c; self.mem[i+3]=d; - self.mem[i+4]=e; self.mem[i+5]=f; - self.mem[i+6]=g; self.mem[i+7]=h; - } - } - - self.isaac(); + + /// Creates an ISAAC random number generator using an u64 as seed. + /// If `seed == 0` this will produce the same stream of random numbers as + /// the reference implementation when used unseeded. + pub fn new_from_u64(seed: u64) -> IsaacRng { + let mut key = [w(0); RAND_SIZE]; + key[0] = w(seed as u32); + key[1] = w((seed >> 32) as u32); + // Initialize with only one pass. + // A second pass does not improve the quality here, because all of + // the seed was already available in the first round. + // Not doing the second pass has the small advantage that if `seed == 0` + // this method produces exactly the same state as the reference + // implementation when used unseeded. + init(key, 1) } /// Refills the output buffer (`self.rsl`) - #[inline] + /// See also the pseudocode desciption of the algorithm at the top of this + /// file. + /// + /// Optimisations used (similar to the reference implementation): + /// - The loop is unrolled 4 times, once for every constant of mix(). + /// - The contents of the main loop are moved to a function `rngstep`, to + /// reduce code duplication. + /// - We use local variables for a and b, which helps with optimisations. + /// - We split the main loop in two, one that operates over 0..128 and one + /// over 128..256. This way we can optimise out the addition and modulus + /// from `s[i+128 mod 256]`. + /// - We maintain one index `i` and add `m` or `m2` as base (m2 for the + /// `s[i+128 mod 256]`), relying on the optimizer to turn it into pointer + /// arithmetic. + /// - We fill `rsl` backwards. The reference implementation reads values + /// from `rsl` in reverse. We read them in the normal direction, to make + /// `fill_bytes` a memcopy. To maintain compatibility we fill in reverse. fn isaac(&mut self) { - self.c = self.c + w(1); + self.c += w(1); // abbreviations let mut a = self.a; let mut b = self.b + self.c; + const MIDPOINT: usize = RAND_SIZE / 2; - const MIDPOINT: usize = RAND_SIZE_USIZE / 2; - - macro_rules! ind { - ($x:expr) => ( self.mem[($x >> 2usize).0 as usize & (RAND_SIZE_USIZE - 1)] ) + #[inline] + fn ind(mem:&[w32; RAND_SIZE], v: w32, amount: usize) -> w32 { + let index = (v >> amount).0 as usize % RAND_SIZE; + mem[index] } - let r = [(0, MIDPOINT), (MIDPOINT, 0)]; - for &(mr_offset, m2_offset) in r.iter() { - - macro_rules! rngstepp { - ($j:expr, $shift:expr) => {{ - let base = $j; - let mix = a << $shift; - - let x = self.mem[base + mr_offset]; - a = (a ^ mix) + self.mem[base + m2_offset]; - let y = ind!(x) + a + b; - self.mem[base + mr_offset] = y; - - b = ind!(y >> RAND_SIZE_LEN) + x; - self.rsl[base + mr_offset] = b; - }} - } - - macro_rules! rngstepn { - ($j:expr, $shift:expr) => {{ - let base = $j; - let mix = a >> $shift; - - let x = self.mem[base + mr_offset]; - a = (a ^ mix) + self.mem[base + m2_offset]; - let y = ind!(x) + a + b; - self.mem[base + mr_offset] = y; + #[inline] + fn rngstep(ctx: &mut IsaacRng, + mix: w32, + a: &mut w32, + b: &mut w32, + base: usize, + m: usize, + m2: usize) { + let x = ctx.mem[base + m]; + *a = mix + ctx.mem[base + m2]; + let y = *a + *b + ind(&ctx.mem, x, 2); + ctx.mem[base + m] = y; + *b = x + ind(&ctx.mem, y, 2 + RAND_SIZE_LEN); + ctx.rsl[RAND_SIZE - 1 - base - m] = (*b).0; + } - b = ind!(y >> RAND_SIZE_LEN) + x; - self.rsl[base + mr_offset] = b; - }} - } + let mut m = 0; + let mut m2 = MIDPOINT; + for i in (0..MIDPOINT/4).map(|i| i * 4) { + rngstep(self, a ^ (a << 13), &mut a, &mut b, i + 0, m, m2); + rngstep(self, a ^ (a >> 6 ), &mut a, &mut b, i + 1, m, m2); + rngstep(self, a ^ (a << 2 ), &mut a, &mut b, i + 2, m, m2); + rngstep(self, a ^ (a >> 16), &mut a, &mut b, i + 3, m, m2); + } - for i in (0..MIDPOINT/4).map(|i| i * 4) { - rngstepp!(i + 0, 13); - rngstepn!(i + 1, 6); - rngstepp!(i + 2, 2); - rngstepn!(i + 3, 16); - } + m = MIDPOINT; + m2 = 0; + for i in (0..MIDPOINT/4).map(|i| i * 4) { + rngstep(self, a ^ (a << 13), &mut a, &mut b, i + 0, m, m2); + rngstep(self, a ^ (a >> 6 ), &mut a, &mut b, i + 1, m, m2); + rngstep(self, a ^ (a << 2 ), &mut a, &mut b, i + 2, m, m2); + rngstep(self, a ^ (a >> 16), &mut a, &mut b, i + 3, m, m2); } self.a = a; self.b = b; - self.cnt = RAND_SIZE; - } -} - -// Cannot be derived because [u32; 256] does not implement Clone -impl Clone for IsaacRng { - fn clone(&self) -> IsaacRng { - *self + self.index = 0; } } impl Rng for IsaacRng { #[inline] fn next_u32(&mut self) -> u32 { - if self.cnt == 0 { - // make some more numbers + // Using a local variable for `index`, and checking the size avoids a + // bounds check later on. + let mut index = self.index as usize; + if index >= RAND_SIZE { self.isaac(); + index = 0; } - self.cnt -= 1; - - // self.cnt is at most RAND_SIZE, but that is before the - // subtraction above. We want to index without bounds - // checking, but this could lead to incorrect code if someone - // misrefactors, so we check, sometimes. - // - // (Changes here should be reflected in Isaac64Rng.next_u64.) - debug_assert!(self.cnt < RAND_SIZE); - - // (the % is cheaply telling the optimiser that we're always - // in bounds, without unsafe. NB. this is a power of two, so - // it optimises to a bitwise mask). - self.rsl[(self.cnt % RAND_SIZE) as usize].0 + + let value = self.rsl[index]; + self.index += 1; + value } -} -impl<'a> SeedableRng<&'a [u32]> for IsaacRng { - fn reseed(&mut self, seed: &'a [u32]) { - // make the seed into [seed[0], seed[1], ..., seed[seed.len() - // - 1], 0, 0, ...], to fill rng.rsl. - let seed_iter = seed.iter().map(|&x| x).chain(repeat(0u32)); + #[inline] + fn next_u64(&mut self) -> u64 { + impls::next_u64_via_u32(self) + } + + #[cfg(feature = "i128_support")] + fn next_u128(&mut self) -> u128 { + impls::next_u128_via_u64(self) + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + let mut read_len = 0; + while read_len < dest.len() { + if self.index as usize >= RAND_SIZE { + self.isaac(); + } + + let (consumed_u32, filled_u8) = + impls::fill_via_u32_chunks(&mut self.rsl[(self.index as usize)..], + &mut dest[read_len..]); - for (rsl_elem, seed_elem) in self.rsl.iter_mut().zip(seed_iter) { - *rsl_elem = w(seed_elem); + self.index += consumed_u32 as u32; + read_len += filled_u8; } - self.cnt = 0; - self.a = w(0); - self.b = w(0); - self.c = w(0); + } - self.init(true); + fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { + Ok(self.fill_bytes(dest)) } +} - /// Create an ISAAC random number generator with a seed. This can - /// be any length, although the maximum number of elements used is - /// 256 and any more will be silently ignored. A generator - /// constructed with a given seed will generate the same sequence - /// of values as all other generators constructed with that seed. - fn from_seed(seed: &'a [u32]) -> IsaacRng { - let mut rng = EMPTY; - rng.reseed(seed); - rng +/// Creates a new ISAAC random number generator. +/// +/// The author Bob Jenkins describes how to best initialize ISAAC here: +/// https://rt.cpan.org/Public/Bug/Display.html?id=64324 +/// The answer is included here just in case: +/// +/// "No, you don't need a full 8192 bits of seed data. Normal key sizes will do +/// fine, and they should have their expected strength (eg a 40-bit key will +/// take as much time to brute force as 40-bit keys usually will). You could +/// fill the remainder with 0, but set the last array element to the length of +/// the key provided (to distinguish keys that differ only by different amounts +/// of 0 padding). You do still need to call randinit() to make sure the initial +/// state isn't uniform-looking." +/// "After publishing ISAAC, I wanted to limit the key to half the size of r[], +/// and repeat it twice. That would have made it hard to provide a key that sets +/// the whole internal state to anything convenient. But I'd already published +/// it." +/// +/// And his answer to the question "For my code, would repeating the key over +/// and over to fill 256 integers be a better solution than zero-filling, or +/// would they essentially be the same?": +/// "If the seed is under 32 bytes, they're essentially the same, otherwise +/// repeating the seed would be stronger. randinit() takes a chunk of 32 bytes, +/// mixes it, and combines that with the next 32 bytes, et cetera. Then loops +/// over all the elements the same way a second time." +#[inline] +fn init(mut mem: [w32; RAND_SIZE], rounds: u32) -> IsaacRng { + // These numbers are the result of initializing a...h with the + // fractional part of the golden ratio in binary (0x9e3779b9) + // and applying mix() 4 times. + let mut a = w(0x1367df5a); + let mut b = w(0x95d90059); + let mut c = w(0xc3163e4b); + let mut d = w(0x0f421ad8); + let mut e = w(0xd92a4a78); + let mut f = w(0xa51a3c49); + let mut g = w(0xc4efea1b); + let mut h = w(0x30609119); + + // Normally this should do two passes, to make all of the seed effect all + // of `mem` + for _ in 0..rounds { + for i in (0..RAND_SIZE/8).map(|i| i * 8) { + a += mem[i ]; b += mem[i+1]; + c += mem[i+2]; d += mem[i+3]; + e += mem[i+4]; f += mem[i+5]; + g += mem[i+6]; h += mem[i+7]; + mix(&mut a, &mut b, &mut c, &mut d, + &mut e, &mut f, &mut g, &mut h); + mem[i ] = a; mem[i+1] = b; + mem[i+2] = c; mem[i+3] = d; + mem[i+4] = e; mem[i+5] = f; + mem[i+6] = g; mem[i+7] = h; + } } + + let mut rng = IsaacRng { + rsl: [0; RAND_SIZE], + mem: mem, + a: w(0), + b: w(0), + c: w(0), + index: 0, + }; + + // Prepare the first set of results + rng.isaac(); + rng } -impl Rand for IsaacRng { - fn rand(other: &mut R) -> IsaacRng { - let mut ret = EMPTY; - unsafe { - let ptr = ret.rsl.as_mut_ptr() as *mut u8; +fn mix(a: &mut w32, b: &mut w32, c: &mut w32, d: &mut w32, + e: &mut w32, f: &mut w32, g: &mut w32, h: &mut w32) { + *a ^= *b << 11; *d += *a; *b += *c; + *b ^= *c >> 2; *e += *b; *c += *d; + *c ^= *d << 8; *f += *c; *d += *e; + *d ^= *e >> 16; *g += *d; *e += *f; + *e ^= *f << 10; *h += *e; *f += *g; + *f ^= *g >> 4; *a += *f; *g += *h; + *g ^= *h << 8; *b += *g; *h += *a; + *h ^= *a >> 9; *c += *h; *a += *b; +} - let slice = slice::from_raw_parts_mut(ptr, RAND_SIZE_USIZE * 4); - other.fill_bytes(slice); - } - ret.cnt = 0; - ret.a = w(0); - ret.b = w(0); - ret.c = w(0); +impl SeedableRng for IsaacRng { + type Seed = [u8; 32]; - ret.init(true); - return ret; + fn from_seed(seed: Self::Seed) -> Self { + let mut seed_u32 = [0u32; 8]; + le::read_u32_into(&seed, &mut seed_u32); + let mut seed_extended = [w(0); RAND_SIZE]; + for (x, y) in seed_extended.iter_mut().zip(seed_u32.iter()) { + *x = w(*y); + } + init(seed_extended, 2) } -} -impl fmt::Debug for IsaacRng { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "IsaacRng {{}}") + fn from_rng(mut other: R) -> Result { + // Custom `from_rng` implementations that fills the entire state + let mut seed = [w(0u32); RAND_SIZE]; + unsafe { + let ptr = seed.as_mut_ptr() as *mut u8; + + let slice = slice::from_raw_parts_mut(ptr, RAND_SIZE * 4); + other.try_fill(slice)?; + } + for i in seed.iter_mut() { + *i = w(i.0.to_le()); + } + + Ok(init(seed, 2)) } } @@ -276,53 +374,82 @@ mod test { use super::IsaacRng; #[test] - fn test_rng_32_rand_seeded() { - let s = ::test::rng().gen_iter::().take(256).collect::>(); - let mut ra: IsaacRng = SeedableRng::from_seed(&s[..]); - let mut rb: IsaacRng = SeedableRng::from_seed(&s[..]); - assert!(::test::iter_eq(ra.gen_ascii_chars().take(100), - rb.gen_ascii_chars().take(100))); - } - - #[test] - fn test_rng_32_seeded() { - let seed: &[_] = &[1, 23, 456, 7890, 12345]; - let mut ra: IsaacRng = SeedableRng::from_seed(seed); - let mut rb: IsaacRng = SeedableRng::from_seed(seed); - assert!(::test::iter_eq(ra.gen_ascii_chars().take(100), - rb.gen_ascii_chars().take(100))); + fn test_isaac_construction() { + // Test that various construction techniques produce a working RNG. + /* TODO: from_hashable + let mut rng1 = IsaacRng::from_hashable("some weak seed"); + rng1.next_u32(); + */ + + let seed = [1,0,0,0, 23,0,0,0, 200,1,0,0, 210,30,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; + let mut rng2 = IsaacRng::from_seed(seed); + assert_eq!(rng2.next_u32(), 2869442790); + + let mut rng3 = IsaacRng::from_rng(&mut rng2).unwrap(); + assert_eq!(rng3.next_u32(), 3094074039); } #[test] - fn test_rng_32_reseed() { - let s = ::test::rng().gen_iter::().take(256).collect::>(); - let mut r: IsaacRng = SeedableRng::from_seed(&s[..]); - let string1: String = r.gen_ascii_chars().take(100).collect(); - - r.reseed(&s[..]); - - let string2: String = r.gen_ascii_chars().take(100).collect(); - assert_eq!(string1, string2); - } - - #[test] - fn test_rng_32_true_values() { - let seed: &[_] = &[1, 23, 456, 7890, 12345]; - let mut ra: IsaacRng = SeedableRng::from_seed(seed); + fn test_isaac_true_values() { + let seed = [1,0,0,0, 23,0,0,0, 200,1,0,0, 210,30,0,0, 57,48,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; + let mut rng1 = IsaacRng::from_seed(seed); // Regression test that isaac is actually using the above vector - let v = (0..10).map(|_| ra.next_u32()).collect::>(); + let v = (0..10).map(|_| rng1.next_u32()).collect::>(); assert_eq!(v, - vec!(2558573138, 873787463, 263499565, 2103644246, 3595684709, - 4203127393, 264982119, 2765226902, 2737944514, 3900253796)); + vec!(2558573138, 873787463, 263499565, 2103644246, + 3595684709, 4203127393, 264982119, 2765226902, + 2737944514, 3900253796)); - let seed: &[_] = &[12345, 67890, 54321, 9876]; - let mut rb: IsaacRng = SeedableRng::from_seed(seed); + let seed = [57,48,0,0, 50,9,1,0, 49,212,0,0, 148,38,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; + let mut rng2 = IsaacRng::from_seed(seed); // skip forward to the 10000th number - for _ in 0..10000 { rb.next_u32(); } + for _ in 0..10000 { rng2.next_u32(); } - let v = (0..10).map(|_| rb.next_u32()).collect::>(); + let v = (0..10).map(|_| rng2.next_u32()).collect::>(); assert_eq!(v, - vec!(3676831399, 3183332890, 2834741178, 3854698763, 2717568474, - 1576568959, 3507990155, 179069555, 141456972, 2478885421)); + vec!(3676831399, 3183332890, 2834741178, 3854698763, + 2717568474, 1576568959, 3507990155, 179069555, + 141456972, 2478885421)); + } + + #[test] + fn test_isaac_true_bytes() { + let seed = [1,0,0,0, 23,0,0,0, 200,1,0,0, 210,30,0,0, 57,48,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; + let mut rng1 = IsaacRng::from_seed(seed); + let mut buf = [0u8; 32]; + rng1.fill_bytes(&mut buf); + // Same as first values in test_isaac_true_values as bytes in LE order + assert_eq!(buf, + [82, 186, 128, 152, 71, 240, 20, 52, + 45, 175, 180, 15, 86, 16, 99, 125, + 101, 203, 81, 214, 97, 162, 134, 250, + 103, 78, 203, 15, 150, 3, 210, 164]); + } + + #[test] + fn test_isaac_new_uninitialized() { + // Compare the results from initializing `IsaacRng` with + // `new_from_u64(0)`, to make sure it is the same as the reference + // implementation when used uninitialized. + // Note: We only test the first 16 integers, not the full 256 of the + // first block. + let mut rng = IsaacRng::new_from_u64(0); + let vec = (0..16).map(|_| rng.next_u32()).collect::>(); + let expected: [u32; 16] = [ + 0x71D71FD2, 0xB54ADAE7, 0xD4788559, 0xC36129FA, + 0x21DC1EA9, 0x3CB879CA, 0xD83B237F, 0xFA3CE5BD, + 0x8D048509, 0xD82E9489, 0xDB452848, 0xCA20E846, + 0x500F972E, 0x0EEFF940, 0x00D6B993, 0xBC12C17F]; + assert_eq!(vec, expected); + } + + #[test] + fn test_isaac_clone() { + let seed = [1,0,0,0, 23,0,0,0, 200,1,0,0, 210,30,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; + let mut rng1 = IsaacRng::from_seed(seed); + let mut rng2 = rng1.clone(); + for _ in 0..16 { + assert_eq!(rng1.next_u32(), rng2.next_u32()); + } } } diff --git a/src/prng/isaac64.rs b/src/prng/isaac64.rs index b98e3fec4f5..528883d6a65 100644 --- a/src/prng/isaac64.rs +++ b/src/prng/isaac64.rs @@ -10,261 +10,337 @@ //! The ISAAC-64 random number generator. -use core::slice; -use core::iter::repeat; +use core::{fmt, slice}; use core::num::Wrapping as w; -use core::fmt; +use rand_core::{impls, le}; +use {Rng, SeedableRng, Error}; -use {Rng, SeedableRng, Rand}; - -#[allow(bad_style)] +#[allow(non_camel_case_types)] type w64 = w; -const RAND_SIZE_64_LEN: usize = 8; -const RAND_SIZE_64: usize = 1 << RAND_SIZE_64_LEN; +const RAND_SIZE_LEN: usize = 8; +const RAND_SIZE: usize = 1 << RAND_SIZE_LEN; -/// A random number generator that uses ISAAC-64[1], the 64-bit -/// variant of the ISAAC algorithm. +/// A random number generator that uses ISAAC-64, the 64-bit variant of the +/// ISAAC algorithm. +/// +/// ISAAC stands for "Indirection, Shift, Accumulate, Add, and Count" which are +/// the principal bitwise operations employed. It is the most advanced of a +/// series of array based random number generator designed by Robert Jenkins +/// in 1996[1]. +/// +/// Although ISAAC is designed to be cryptographically secure, its design is not +/// founded in cryptographic theory. Therefore it is _not recommended for_ +/// cryptographic purposes. It is however one of the strongest non-cryptograpic +/// RNGs, and that while still being reasonably fast. +/// +/// ISAAC-64 is mostly similar to ISAAC. Because it operates on 64-bit integers +/// instead of 32-bit, it uses twice as much memory to hold its state and +/// results. Also it uses different constants for shifts and indirect indexing, +/// optimized to give good results for 64bit arithmetic. +/// +/// ## Overview of the ISAAC-64 algorithm: +/// (in pseudo-code) +/// +/// ```text +/// Input: a, b, c, s[256] // state +/// Output: r[256] // results /// -/// The ISAAC algorithm is generally accepted as suitable for -/// cryptographic purposes, but this implementation has not be -/// verified as such. Prefer a generator like `OsRng` that defers to -/// the operating system for cases that need high security. +/// mix(a,i) = !(a ^ a << 21) if i = 0 mod 4 +/// a ^ a >> 5 if i = 1 mod 4 +/// a ^ a << 12 if i = 2 mod 4 +/// a ^ a >> 33 if i = 3 mod 4 /// -/// [1]: Bob Jenkins, [*ISAAC: A fast cryptographic random number -/// generator*](http://www.burtleburtle.net/bob/rand/isaacafa.html) -#[derive(Copy)] +/// c = c + 1 +/// b = b + c +/// +/// for i in 0..256 { +/// x = s_[i] +/// a = mix(a,i) + s[i+128 mod 256] +/// y = a + b + s[x>>3 mod 256] +/// s[i] = y +/// b = x + s[y>>11 mod 256] +/// r[i] = b +/// } +/// ``` +/// +/// See for more information the description in rand::prng::IsaacRng. +/// +/// [1]: Bob Jenkins, [*ISAAC and RC4*] +/// (http://burtleburtle.net/bob/rand/isaac.html) pub struct Isaac64Rng { - cnt: usize, - rsl: [w64; RAND_SIZE_64], - mem: [w64; RAND_SIZE_64], + rsl: [u64; RAND_SIZE], + mem: [w64; RAND_SIZE], a: w64, b: w64, c: w64, + index: u32, + half_used: bool, // true if only half of the previous result is used } -static EMPTY_64: Isaac64Rng = Isaac64Rng { - cnt: 0, - rsl: [w(0); RAND_SIZE_64], - mem: [w(0); RAND_SIZE_64], - a: w(0), b: w(0), c: w(0), -}; +// Cannot be derived because [u64; 256] does not implement Clone +// FIXME: remove once RFC 2000 gets implemented +impl Clone for Isaac64Rng { + fn clone(&self) -> Isaac64Rng { + Isaac64Rng { + rsl: self.rsl, + mem: self.mem, + a: self.a, + b: self.b, + c: self.c, + index: self.index, + half_used: self.half_used, + } + } +} + +// Custom Debug implementation that does not expose the internal state +impl fmt::Debug for Isaac64Rng { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Isaac64Rng {{}}") + } +} impl Isaac64Rng { /// Create a 64-bit ISAAC random number generator using the /// default fixed seed. pub fn new_unseeded() -> Isaac64Rng { - let mut rng = EMPTY_64; - rng.init(false); - rng + Self::new_from_u64(0) } - - /// Initialises `self`. If `use_rsl` is true, then use the current value - /// of `rsl` as a seed, otherwise construct one algorithmically (not - /// randomly). - fn init(&mut self, use_rsl: bool) { - macro_rules! init { - ($var:ident) => ( - let mut $var = w(0x9e3779b97f4a7c13); - ) - } - init!(a); init!(b); init!(c); init!(d); - init!(e); init!(f); init!(g); init!(h); - - macro_rules! mix { - () => {{ - a=a-e; f=f^(h>>9); h=h+a; - b=b-f; g=g^(a<<9); a=a+b; - c=c-g; h=h^(b>>23); b=b+c; - d=d-h; a=a^(c<<15); c=c+d; - e=e-a; b=b^(d>>14); d=d+e; - f=f-b; c=c^(e<<20); e=e+f; - g=g-c; d=d^(f>>17); f=f+g; - h=h-d; e=e^(g<<14); g=g+h; - }} - } - - for _ in 0..4 { - mix!(); - } - - if use_rsl { - macro_rules! memloop { - ($arr:expr) => {{ - for i in (0..RAND_SIZE_64 / 8).map(|i| i * 8) { - a=a+$arr[i ]; b=b+$arr[i+1]; - c=c+$arr[i+2]; d=d+$arr[i+3]; - e=e+$arr[i+4]; f=f+$arr[i+5]; - g=g+$arr[i+6]; h=h+$arr[i+7]; - mix!(); - self.mem[i ]=a; self.mem[i+1]=b; - self.mem[i+2]=c; self.mem[i+3]=d; - self.mem[i+4]=e; self.mem[i+5]=f; - self.mem[i+6]=g; self.mem[i+7]=h; - } - }} - } - - memloop!(self.rsl); - memloop!(self.mem); - } else { - for i in (0..RAND_SIZE_64 / 8).map(|i| i * 8) { - mix!(); - self.mem[i ]=a; self.mem[i+1]=b; - self.mem[i+2]=c; self.mem[i+3]=d; - self.mem[i+4]=e; self.mem[i+5]=f; - self.mem[i+6]=g; self.mem[i+7]=h; - } - } - - self.isaac64(); + + /// Creates an ISAAC-64 random number generator using an u64 as seed. + /// If `seed == 0` this will produce the same stream of random numbers as + /// the reference implementation when used unseeded. + pub fn new_from_u64(seed: u64) -> Isaac64Rng { + let mut key = [w(0); RAND_SIZE]; + key[0] = w(seed); + // Initialize with only one pass. + // A second pass does not improve the quality here, because all of + // the seed was already available in the first round. + // Not doing the second pass has the small advantage that if `seed == 0` + // this method produces exactly the same state as the reference + // implementation when used unseeded. + init(key, 1) } /// Refills the output buffer (`self.rsl`) + /// See also the pseudocode desciption of the algorithm at the top of this + /// file. + /// + /// Optimisations used (similar to the reference implementation): + /// - The loop is unrolled 4 times, once for every constant of mix(). + /// - The contents of the main loop are moved to a function `rngstep`, to + /// reduce code duplication. + /// - We use local variables for a and b, which helps with optimisations. + /// - We split the main loop in two, one that operates over 0..128 and one + /// over 128..256. This way we can optimise out the addition and modulus + /// from `s[i+128 mod 256]`. + /// - We maintain one index `i` and add `m` or `m2` as base (m2 for the + /// `s[i+128 mod 256]`), relying on the optimizer to turn it into pointer + /// arithmetic. + /// - We fill `rsl` backwards. The reference implementation reads values + /// from `rsl` in reverse. We read them in the normal direction, to make + /// `fill_bytes` a memcopy. To maintain compatibility we fill in reverse. fn isaac64(&mut self) { - self.c = self.c + w(1); + self.c += w(1); // abbreviations let mut a = self.a; let mut b = self.b + self.c; - const MIDPOINT: usize = RAND_SIZE_64 / 2; - const MP_VEC: [(usize, usize); 2] = [(0,MIDPOINT), (MIDPOINT, 0)]; - macro_rules! ind { - ($x:expr) => { - *self.mem.get_unchecked((($x >> 3usize).0 as usize) & (RAND_SIZE_64 - 1)) - } + const MIDPOINT: usize = RAND_SIZE / 2; + + #[inline] + fn ind(mem:&[w64; RAND_SIZE], v: w64, amount: usize) -> w64 { + let index = (v >> amount).0 as usize % RAND_SIZE; + mem[index] } - for &(mr_offset, m2_offset) in MP_VEC.iter() { - for base in (0..MIDPOINT / 4).map(|i| i * 4) { - - macro_rules! rngstepp { - ($j:expr, $shift:expr) => {{ - let base = base + $j; - let mix = a ^ (a << $shift); - let mix = if $j == 0 {!mix} else {mix}; - - unsafe { - let x = *self.mem.get_unchecked(base + mr_offset); - a = mix + *self.mem.get_unchecked(base + m2_offset); - let y = ind!(x) + a + b; - *self.mem.get_unchecked_mut(base + mr_offset) = y; - - b = ind!(y >> RAND_SIZE_64_LEN) + x; - *self.rsl.get_unchecked_mut(base + mr_offset) = b; - } - }} - } - - macro_rules! rngstepn { - ($j:expr, $shift:expr) => {{ - let base = base + $j; - let mix = a ^ (a >> $shift); - let mix = if $j == 0 {!mix} else {mix}; - - unsafe { - let x = *self.mem.get_unchecked(base + mr_offset); - a = mix + *self.mem.get_unchecked(base + m2_offset); - let y = ind!(x) + a + b; - *self.mem.get_unchecked_mut(base + mr_offset) = y; - - b = ind!(y >> RAND_SIZE_64_LEN) + x; - *self.rsl.get_unchecked_mut(base + mr_offset) = b; - } - }} - } - - rngstepp!(0, 21); - rngstepn!(1, 5); - rngstepp!(2, 12); - rngstepn!(3, 33); - } + #[inline] + fn rngstep(ctx: &mut Isaac64Rng, + mix: w64, + a: &mut w64, + b: &mut w64, + base: usize, + m: usize, + m2: usize) { + let x = ctx.mem[base + m]; + *a = mix + ctx.mem[base + m2]; + let y = *a + *b + ind(&ctx.mem, x, 3); + ctx.mem[base + m] = y; + *b = x + ind(&ctx.mem, y, 3 + RAND_SIZE_LEN); + ctx.rsl[RAND_SIZE - 1 - base - m] = (*b).0; + } + + let mut m = 0; + let mut m2 = MIDPOINT; + for i in (0..MIDPOINT/4).map(|i| i * 4) { + rngstep(self, !(a ^ (a << 21)), &mut a, &mut b, i + 0, m, m2); + rngstep(self, a ^ (a >> 5 ), &mut a, &mut b, i + 1, m, m2); + rngstep(self, a ^ (a << 12), &mut a, &mut b, i + 2, m, m2); + rngstep(self, a ^ (a >> 33), &mut a, &mut b, i + 3, m, m2); + } + + m = MIDPOINT; + m2 = 0; + for i in (0..MIDPOINT/4).map(|i| i * 4) { + rngstep(self, !(a ^ (a << 21)), &mut a, &mut b, i + 0, m, m2); + rngstep(self, a ^ (a >> 5 ), &mut a, &mut b, i + 1, m, m2); + rngstep(self, a ^ (a << 12), &mut a, &mut b, i + 2, m, m2); + rngstep(self, a ^ (a >> 33), &mut a, &mut b, i + 3, m, m2); } self.a = a; self.b = b; - self.cnt = RAND_SIZE_64; - } -} - -// Cannot be derived because [u32; 256] does not implement Clone -impl Clone for Isaac64Rng { - fn clone(&self) -> Isaac64Rng { - *self + self.index = 0; + self.half_used = false; } } impl Rng for Isaac64Rng { #[inline] fn next_u32(&mut self) -> u32 { - self.next_u64() as u32 + // Using a local variable for `index`, and checking the size avoids a + // bounds check later on. + let mut index = self.index as usize * 2 - self.half_used as usize; + if index >= RAND_SIZE * 2 { + self.isaac64(); + index = 0; + } + + self.half_used = !self.half_used; + self.index += self.half_used as u32; + + // Index as if this is a u32 slice. + let rsl = unsafe { &*(&mut self.rsl as *mut [u64; RAND_SIZE] + as *mut [u32; RAND_SIZE * 2]) }; + + if cfg!(target_endian = "little") { + rsl[index] + } else { + rsl[index ^ 1] + } } #[inline] fn next_u64(&mut self) -> u64 { - if self.cnt == 0 { - // make some more numbers + let mut index = self.index as usize; + if index >= RAND_SIZE { self.isaac64(); + index = 0; } - self.cnt -= 1; - // See corresponding location in IsaacRng.next_u32 for - // explanation. - debug_assert!(self.cnt < RAND_SIZE_64); - self.rsl[(self.cnt % RAND_SIZE_64) as usize].0 + let value = self.rsl[index]; + self.index += 1; + self.half_used = false; + value } -} -impl<'a> SeedableRng<&'a [u64]> for Isaac64Rng { - fn reseed(&mut self, seed: &'a [u64]) { - // make the seed into [seed[0], seed[1], ..., seed[seed.len() - // - 1], 0, 0, ...], to fill rng.rsl. - let seed_iter = seed.iter().map(|&x| x).chain(repeat(0u64)); + #[cfg(feature = "i128_support")] + fn next_u128(&mut self) -> u128 { + impls::next_u128_via_u64(self) + } - for (rsl_elem, seed_elem) in self.rsl.iter_mut().zip(seed_iter) { - *rsl_elem = w(seed_elem); - } - self.cnt = 0; - self.a = w(0); - self.b = w(0); - self.c = w(0); + fn fill_bytes(&mut self, dest: &mut [u8]) { + let mut read_len = 0; + while read_len < dest.len() { + if self.index as usize >= RAND_SIZE { + self.isaac64(); + } - self.init(true); + let (consumed_u64, filled_u8) = + impls::fill_via_u64_chunks(&mut self.rsl[self.index as usize..], + &mut dest[read_len..]); + + self.index += consumed_u64 as u32; + read_len += filled_u8; + } } - /// Create an ISAAC random number generator with a seed. This can - /// be any length, although the maximum number of elements used is - /// 256 and any more will be silently ignored. A generator - /// constructed with a given seed will generate the same sequence - /// of values as all other generators constructed with that seed. - fn from_seed(seed: &'a [u64]) -> Isaac64Rng { - let mut rng = EMPTY_64; - rng.reseed(seed); - rng + fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { + Ok(self.fill_bytes(dest)) } } -impl Rand for Isaac64Rng { - fn rand(other: &mut R) -> Isaac64Rng { - let mut ret = EMPTY_64; - unsafe { - let ptr = ret.rsl.as_mut_ptr() as *mut u8; - - let slice = slice::from_raw_parts_mut(ptr, RAND_SIZE_64 * 8); - other.fill_bytes(slice); +/// Creates a new ISAAC-64 random number generator. +fn init(mut mem: [w64; RAND_SIZE], rounds: u32) -> Isaac64Rng { + // These numbers are the result of initializing a...h with the + // fractional part of the golden ratio in binary (0x9e3779b97f4a7c13) + // and applying mix() 4 times. + let mut a = w(0x647c4677a2884b7c); + let mut b = w(0xb9f8b322c73ac862); + let mut c = w(0x8c0ea5053d4712a0); + let mut d = w(0xb29b2e824a595524); + let mut e = w(0x82f053db8355e0ce); + let mut f = w(0x48fe4a0fa5a09315); + let mut g = w(0xae985bf2cbfc89ed); + let mut h = w(0x98f5704f6c44c0ab); + + // Normally this should do two passes, to make all of the seed effect all + // of `mem` + for _ in 0..rounds { + for i in (0..RAND_SIZE/8).map(|i| i * 8) { + a += mem[i ]; b += mem[i+1]; + c += mem[i+2]; d += mem[i+3]; + e += mem[i+4]; f += mem[i+5]; + g += mem[i+6]; h += mem[i+7]; + mix(&mut a, &mut b, &mut c, &mut d, + &mut e, &mut f, &mut g, &mut h); + mem[i ] = a; mem[i+1] = b; + mem[i+2] = c; mem[i+3] = d; + mem[i+4] = e; mem[i+5] = f; + mem[i+6] = g; mem[i+7] = h; } - ret.cnt = 0; - ret.a = w(0); - ret.b = w(0); - ret.c = w(0); + } - ret.init(true); - return ret; + Isaac64Rng { + rsl: [0; RAND_SIZE], + mem: mem, + a: w(0), + b: w(0), + c: w(0), + index: RAND_SIZE as u32, // generate on first use + half_used: false, } } -impl fmt::Debug for Isaac64Rng { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Isaac64Rng {{}}") +fn mix(a: &mut w64, b: &mut w64, c: &mut w64, d: &mut w64, + e: &mut w64, f: &mut w64, g: &mut w64, h: &mut w64) { + *a -= *e; *f ^= *h >> 9; *h += *a; + *b -= *f; *g ^= *a << 9; *a += *b; + *c -= *g; *h ^= *b >> 23; *b += *c; + *d -= *h; *a ^= *c << 15; *c += *d; + *e -= *a; *b ^= *d >> 14; *d += *e; + *f -= *b; *c ^= *e << 20; *e += *f; + *g -= *c; *d ^= *f >> 17; *f += *g; + *h -= *d; *e ^= *g << 14; *g += *h; +} + +impl SeedableRng for Isaac64Rng { + type Seed = [u8; 32]; + + fn from_seed(seed: Self::Seed) -> Self { + let mut seed_u64 = [0u64; 4]; + le::read_u64_into(&seed, &mut seed_u64); + let mut seed_extended = [w(0); RAND_SIZE]; + for (x, y) in seed_extended.iter_mut().zip(seed_u64.iter()) { + *x = w(*y); + } + init(seed_extended, 2) + } + + fn from_rng(mut other: R) -> Result { + // Custom `from_rng` implementations that fills the entire state + let mut seed = [w(0u64); RAND_SIZE]; + unsafe { + let ptr = seed.as_mut_ptr() as *mut u8; + + let slice = slice::from_raw_parts_mut(ptr, RAND_SIZE * 8); + other.try_fill(slice)?; + } + for i in seed.iter_mut() { + *i = w(i.0.to_le()); + } + + Ok(init(seed, 2)) } } @@ -274,67 +350,120 @@ mod test { use super::Isaac64Rng; #[test] - fn test_rng_64_rand_seeded() { - let s = ::test::rng().gen_iter::().take(256).collect::>(); - let mut ra: Isaac64Rng = SeedableRng::from_seed(&s[..]); - let mut rb: Isaac64Rng = SeedableRng::from_seed(&s[..]); - assert!(::test::iter_eq(ra.gen_ascii_chars().take(100), - rb.gen_ascii_chars().take(100))); - } - - #[test] - fn test_rng_64_seeded() { - let seed: &[_] = &[1, 23, 456, 7890, 12345]; - let mut ra: Isaac64Rng = SeedableRng::from_seed(seed); - let mut rb: Isaac64Rng = SeedableRng::from_seed(seed); - assert!(::test::iter_eq(ra.gen_ascii_chars().take(100), - rb.gen_ascii_chars().take(100))); + fn test_isaac64_construction() { + // Test that various construction techniques produce a working RNG. + /* TODO: from_hashable + let mut rng1 = Isaac64Rng::from_hashable("some weak seed"); + rng1.next_u64(); + */ + + let seed = [1,0,0,0, 23,0,0,0, 200,1,0,0, 210,30,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]; + let mut rng2 = Isaac64Rng::from_seed(seed); + assert_eq!(rng2.next_u64(), 14964555543728284049); + + let mut rng3 = Isaac64Rng::from_rng(&mut rng2).unwrap(); + assert_eq!(rng3.next_u64(), 919595328260451758); } - + #[test] - fn test_rng_64_reseed() { - let s = ::test::rng().gen_iter::().take(256).collect::>(); - let mut r: Isaac64Rng = SeedableRng::from_seed(&s[..]); - let string1: String = r.gen_ascii_chars().take(100).collect(); - - r.reseed(&s[..]); + fn test_isaac64_true_values() { + let seed = [1,0,0,0, 0,0,0,0, 23,0,0,0, 0,0,0,0, 200,1,0,0, 0,0,0,0, 210,30,0,0, 0,0,0,0]; + let mut rng1 = Isaac64Rng::from_seed(seed); + // Regression test that isaac is actually using the above vector + let v = (0..10).map(|_| rng1.next_u64()).collect::>(); + assert_eq!(v, + vec!(15071495833797886820, 7720185633435529318, + 10836773366498097981, 5414053799617603544, + 12890513357046278984, 17001051845652595546, + 9240803642279356310, 12558996012687158051, + 14673053937227185542, 1677046725350116783)); + + let seed = [57,48,0,0, 0,0,0,0, 50,9,1,0, 0,0,0,0, 49,212,0,0, 0,0,0,0, 148,38,0,0, 0,0,0,0]; + let mut rng2 = Isaac64Rng::from_seed(seed); + // skip forward to the 10000th number + for _ in 0..10000 { rng2.next_u64(); } - let string2: String = r.gen_ascii_chars().take(100).collect(); - assert_eq!(string1, string2); + let v = (0..10).map(|_| rng2.next_u64()).collect::>(); + assert_eq!(v, + vec!(18143823860592706164, 8491801882678285927, + 2699425367717515619, 17196852593171130876, + 2606123525235546165, 15790932315217671084, + 596345674630742204, 9947027391921273664, + 11788097613744130851, 10391409374914919106)); } #[test] - fn test_rng_64_true_values() { - let seed: &[_] = &[1, 23, 456, 7890, 12345]; - let mut ra: Isaac64Rng = SeedableRng::from_seed(seed); - // Regression test that isaac is actually using the above vector - let v = (0..10).map(|_| ra.next_u64()).collect::>(); + fn test_isaac64_true_values_32() { + let seed = [1,0,0,0, 0,0,0,0, 23,0,0,0, 0,0,0,0, 200,1,0,0, 0,0,0,0, 210,30,0,0, 0,0,0,0]; + let mut rng1 = Isaac64Rng::from_seed(seed); + let v = (0..12).map(|_| rng1.next_u32()).collect::>(); + // Subset of above values, as an LE u32 sequence assert_eq!(v, - vec!(547121783600835980, 14377643087320773276, 17351601304698403469, - 1238879483818134882, 11952566807690396487, 13970131091560099343, - 4469761996653280935, 15552757044682284409, 6860251611068737823, - 13722198873481261842)); + [3477963620, 3509106075, + 687845478, 1797495790, + 227048253, 2523132918, + 4044335064, 1260557630, + 4079741768, 3001306521, + 69157722, 3958365844]); + } - let seed: &[_] = &[12345, 67890, 54321, 9876]; - let mut rb: Isaac64Rng = SeedableRng::from_seed(seed); - // skip forward to the 10000th number - for _ in 0..10000 { rb.next_u64(); } + #[test] + fn test_isaac64_true_values_mixed() { + let seed = [1,0,0,0, 0,0,0,0, 23,0,0,0, 0,0,0,0, 200,1,0,0, 0,0,0,0, 210,30,0,0, 0,0,0,0]; + let mut rng = Isaac64Rng::from_seed(seed); + // Test alternating between `next_u64` and `next_u32` works as expected. + // Values are the same as `test_isaac64_true_values` and + // `test_isaac64_true_values_32`. + assert_eq!(rng.next_u64(), 15071495833797886820); + assert_eq!(rng.next_u32(), 687845478); + assert_eq!(rng.next_u32(), 1797495790); + assert_eq!(rng.next_u64(), 10836773366498097981); + assert_eq!(rng.next_u32(), 4044335064); + // Skip one u32 + assert_eq!(rng.next_u64(), 12890513357046278984); + assert_eq!(rng.next_u32(), 69157722); + } - let v = (0..10).map(|_| rb.next_u64()).collect::>(); - assert_eq!(v, - vec!(18143823860592706164, 8491801882678285927, 2699425367717515619, - 17196852593171130876, 2606123525235546165, 15790932315217671084, - 596345674630742204, 9947027391921273664, 11788097613744130851, - 10391409374914919106)); + #[test] + fn test_isaac64_true_bytes() { + let seed = [1,0,0,0, 0,0,0,0, 23,0,0,0, 0,0,0,0, 200,1,0,0, 0,0,0,0, 210,30,0,0, 0,0,0,0]; + let mut rng1 = Isaac64Rng::from_seed(seed); + let mut buf = [0u8; 32]; + rng1.fill_bytes(&mut buf); + // Same as first values in test_isaac64_true_values as bytes in LE order + assert_eq!(buf, + [100, 131, 77, 207, 155, 181, 40, 209, + 102, 176, 255, 40, 238, 155, 35, 107, + 61, 123, 136, 13, 246, 243, 99, 150, + 216, 167, 15, 241, 62, 149, 34, 75]); + } + + #[test] + fn test_isaac_new_uninitialized() { + // Compare the results from initializing `IsaacRng` with + // `new_from_u64(0)`, to make sure it is the same as the reference + // implementation when used uninitialized. + // Note: We only test the first 16 integers, not the full 256 of the + // first block. + let mut rng = Isaac64Rng::new_from_u64(0); + let vec = (0..16).map(|_| rng.next_u64()).collect::>(); + let expected: [u64; 16] = [ + 0xF67DFBA498E4937C, 0x84A5066A9204F380, 0xFEE34BD5F5514DBB, + 0x4D1664739B8F80D6, 0x8607459AB52A14AA, 0x0E78BC5A98529E49, + 0xFE5332822AD13777, 0x556C27525E33D01A, 0x08643CA615F3149F, + 0xD0771FAF3CB04714, 0x30E86F68A37B008D, 0x3074EBC0488A3ADF, + 0x270645EA7A2790BC, 0x5601A0A8D3763C6A, 0x2F83071F53F325DD, + 0xB9090F3D42D2D2EA]; + assert_eq!(vec, expected); } #[test] - fn test_rng_clone() { - let seed: &[_] = &[1, 23, 456, 7890, 12345]; - let mut rng: Isaac64Rng = SeedableRng::from_seed(seed); - let mut clone = rng.clone(); + fn test_isaac64_clone() { + let seed = [1,0,0,0, 0,0,0,0, 23,0,0,0, 0,0,0,0, 200,1,0,0, 0,0,0,0, 210,30,0,0, 0,0,0,0]; + let mut rng1 = Isaac64Rng::from_seed(seed); + let mut rng2 = rng1.clone(); for _ in 0..16 { - assert_eq!(rng.next_u64(), clone.next_u64()); + assert_eq!(rng1.next_u64(), rng2.next_u64()); } } } diff --git a/src/prng/isaac_word.rs b/src/prng/isaac_word.rs new file mode 100644 index 00000000000..1770244f048 --- /dev/null +++ b/src/prng/isaac_word.rs @@ -0,0 +1,68 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! The ISAAC random number generator. + +use {Rng, SeedableRng, Error}; + +#[cfg(target_pointer_width = "32")] +type WordRngType = super::isaac::IsaacRng; +#[cfg(target_pointer_width = "64")] +type WordRngType = super::isaac64::Isaac64Rng; + +/// A random number generator that uses the ISAAC or ISAAC-64 algorithm, +/// depending on the pointer size of the target architecture. +/// +/// In general a random number generator that internally uses the word size of +/// the target architecture is faster than one that doesn't. Choosing +/// `IsaacWordRng` is therefore often a better choice than `IsaacRng` or +/// `Isaac64Rng`. The others can be a good choice if reproducability across +/// different architectures is desired. `IsaacRng` can also be a better choice +/// if memory usage is an issue, as it uses 2kb of state instead of the 4kb +/// `Isaac64Rng` uses. +/// +/// See for an explanation of the algorithm `IsaacRng` and `Isaac64Rng`. +#[derive(Clone, Debug)] +pub struct IsaacWordRng(WordRngType); + +impl Rng for IsaacWordRng { + fn next_u32(&mut self) -> u32 { + self.0.next_u32() + } + + fn next_u64(&mut self) -> u64 { + self.0.next_u64() + } + + #[cfg(feature = "i128_support")] + fn next_u128(&mut self) -> u128 { + self.0.next_u128() + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + self.0.fill_bytes(dest) + } + + fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { + self.0.try_fill(dest) + } +} + +impl SeedableRng for IsaacWordRng { + type Seed = ::Seed; + + fn from_seed(seed: Self::Seed) -> Self { + IsaacWordRng(WordRngType::from_seed(seed)) + } + + fn from_rng(rng: R) -> Result { + WordRngType::from_rng(rng).map(|rng| IsaacWordRng(rng)) + } +} diff --git a/src/prng/mod.rs b/src/prng/mod.rs index ed3e0188649..25ffdc81c57 100644 --- a/src/prng/mod.rs +++ b/src/prng/mod.rs @@ -39,13 +39,20 @@ //! short periods for some seeds. If one PRNG is seeded from another using the //! same algorithm, it is possible that both will yield the same sequence of //! values (with some lag). +//! +//! TODO: add some guidance on selection of a PRNG: cryptographic approval, +//! statistical properties, performance. mod chacha; +mod hc128; mod isaac; mod isaac64; +mod isaac_word; mod xorshift; pub use self::chacha::ChaChaRng; +pub use self::hc128::Hc128Rng; pub use self::isaac::IsaacRng; pub use self::isaac64::Isaac64Rng; +pub use self::isaac_word::IsaacWordRng; pub use self::xorshift::XorShiftRng; diff --git a/src/prng/xorshift.rs b/src/prng/xorshift.rs index dd367e9bfe9..d2b237663b5 100644 --- a/src/prng/xorshift.rs +++ b/src/prng/xorshift.rs @@ -11,7 +11,9 @@ //! Xorshift generators use core::num::Wrapping as w; -use {Rng, SeedableRng, Rand}; +use core::{fmt, slice}; +use {Rng, SeedableRng, Error}; +use rand_core::le; /// An Xorshift[1] random number /// generator. @@ -23,8 +25,7 @@ use {Rng, SeedableRng, Rand}; /// [1]: Marsaglia, George (July 2003). ["Xorshift /// RNGs"](http://www.jstatsoft.org/v08/i14/paper). *Journal of /// Statistical Software*. Vol. 8 (Issue 14). -#[allow(missing_copy_implementations)] -#[derive(Clone, Debug)] +#[derive(Clone)] pub struct XorShiftRng { x: w, y: w, @@ -32,6 +33,13 @@ pub struct XorShiftRng { w: w, } +// Custom Debug implementation that does not expose the internal state +impl fmt::Debug for XorShiftRng { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "XorShiftRng {{}}") + } +} + impl XorShiftRng { /// Creates a new XorShiftRng instance which is not seeded. /// @@ -61,41 +69,60 @@ impl Rng for XorShiftRng { self.w = w_ ^ (w_ >> 19) ^ (t ^ (t >> 8)); self.w.0 } + + fn next_u64(&mut self) -> u64 { + ::rand_core::impls::next_u64_via_u32(self) + } + #[cfg(feature = "i128_support")] + fn next_u128(&mut self) -> u128 { + ::rand_core::impls::next_u128_via_u64(self) + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + ::rand_core::impls::fill_bytes_via_u32(self, dest); + } + + fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { + Ok(self.fill_bytes(dest)) + } } -impl SeedableRng<[u32; 4]> for XorShiftRng { - /// Reseed an XorShiftRng. This will panic if `seed` is entirely 0. - fn reseed(&mut self, seed: [u32; 4]) { - assert!(!seed.iter().all(|&x| x == 0), - "XorShiftRng.reseed called with an all zero seed."); +impl SeedableRng for XorShiftRng { + type Seed = [u8; 16]; - self.x = w(seed[0]); - self.y = w(seed[1]); - self.z = w(seed[2]); - self.w = w(seed[3]); - } + fn from_seed(seed: Self::Seed) -> Self { + let mut seed_u32 = [0u32; 4]; + le::read_u32_into(&seed, &mut seed_u32); - /// Create a new XorShiftRng. This will panic if `seed` is entirely 0. - fn from_seed(seed: [u32; 4]) -> XorShiftRng { - assert!(!seed.iter().all(|&x| x == 0), - "XorShiftRng::from_seed called with an all zero seed."); + if seed_u32.iter().all(|&x| x == 0) { + seed_u32 = [0xBAD_5EED, 0xBAD_5EED, 0xBAD_5EED, 0xBAD_5EED]; + } XorShiftRng { - x: w(seed[0]), - y: w(seed[1]), - z: w(seed[2]), - w: w(seed[3]), + x: w(seed_u32[0]), + y: w(seed_u32[1]), + z: w(seed_u32[2]), + w: w(seed_u32[3]), } } -} -impl Rand for XorShiftRng { - fn rand(rng: &mut R) -> XorShiftRng { - let mut tuple: (u32, u32, u32, u32) = rng.gen(); - while tuple == (0, 0, 0, 0) { - tuple = rng.gen(); + fn from_rng(mut rng: R) -> Result { + let mut seed_u32 = [0u32; 4]; + loop { + unsafe { + let ptr = seed_u32.as_mut_ptr() as *mut u8; + + let slice = slice::from_raw_parts_mut(ptr, 4 * 4); + rng.try_fill(slice)?; + } + if !seed_u32.iter().all(|&x| x == 0) { break; } } - let (x, y, z, w_) = tuple; - XorShiftRng { x: w(x), y: w(y), z: w(z), w: w(w_) } + + Ok(XorShiftRng { + x: w(seed_u32[0]), + y: w(seed_u32[1]), + z: w(seed_u32[2]), + w: w(seed_u32[3]), + }) } } diff --git a/src/rand_impls.rs b/src/rand_impls.rs deleted file mode 100644 index a865bb69582..00000000000 --- a/src/rand_impls.rs +++ /dev/null @@ -1,299 +0,0 @@ -// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! The implementations of `Rand` for the built-in types. - -use core::{char, mem}; - -use {Rand,Rng}; - -impl Rand for isize { - #[inline] - fn rand(rng: &mut R) -> isize { - if mem::size_of::() == 4 { - rng.gen::() as isize - } else { - rng.gen::() as isize - } - } -} - -impl Rand for i8 { - #[inline] - fn rand(rng: &mut R) -> i8 { - rng.next_u32() as i8 - } -} - -impl Rand for i16 { - #[inline] - fn rand(rng: &mut R) -> i16 { - rng.next_u32() as i16 - } -} - -impl Rand for i32 { - #[inline] - fn rand(rng: &mut R) -> i32 { - rng.next_u32() as i32 - } -} - -impl Rand for i64 { - #[inline] - fn rand(rng: &mut R) -> i64 { - rng.next_u64() as i64 - } -} - -#[cfg(feature = "i128_support")] -impl Rand for i128 { - #[inline] - fn rand(rng: &mut R) -> i128 { - rng.gen::() as i128 - } -} - -impl Rand for usize { - #[inline] - fn rand(rng: &mut R) -> usize { - if mem::size_of::() == 4 { - rng.gen::() as usize - } else { - rng.gen::() as usize - } - } -} - -impl Rand for u8 { - #[inline] - fn rand(rng: &mut R) -> u8 { - rng.next_u32() as u8 - } -} - -impl Rand for u16 { - #[inline] - fn rand(rng: &mut R) -> u16 { - rng.next_u32() as u16 - } -} - -impl Rand for u32 { - #[inline] - fn rand(rng: &mut R) -> u32 { - rng.next_u32() - } -} - -impl Rand for u64 { - #[inline] - fn rand(rng: &mut R) -> u64 { - rng.next_u64() - } -} - -#[cfg(feature = "i128_support")] -impl Rand for u128 { - #[inline] - fn rand(rng: &mut R) -> u128 { - ((rng.next_u64() as u128) << 64) | (rng.next_u64() as u128) - } -} - - -macro_rules! float_impls { - ($mod_name:ident, $ty:ty, $mantissa_bits:expr, $method_name:ident) => { - mod $mod_name { - use {Rand, Rng, Open01, Closed01}; - - const SCALE: $ty = (1u64 << $mantissa_bits) as $ty; - - impl Rand for $ty { - /// Generate a floating point number in the half-open - /// interval `[0,1)`. - /// - /// See `Closed01` for the closed interval `[0,1]`, - /// and `Open01` for the open interval `(0,1)`. - #[inline] - fn rand(rng: &mut R) -> $ty { - rng.$method_name() - } - } - impl Rand for Open01<$ty> { - #[inline] - fn rand(rng: &mut R) -> Open01<$ty> { - // add a small amount (specifically 2 bits below - // the precision of f64/f32 at 1.0), so that small - // numbers are larger than 0, but large numbers - // aren't pushed to/above 1. - Open01(rng.$method_name() + 0.25 / SCALE) - } - } - impl Rand for Closed01<$ty> { - #[inline] - fn rand(rng: &mut R) -> Closed01<$ty> { - // rescale so that 1.0 - epsilon becomes 1.0 - // precisely. - Closed01(rng.$method_name() * SCALE / (SCALE - 1.0)) - } - } - } - } -} -float_impls! { f64_rand_impls, f64, 53, next_f64 } -float_impls! { f32_rand_impls, f32, 24, next_f32 } - -impl Rand for char { - #[inline] - fn rand(rng: &mut R) -> char { - // a char is 21 bits - const CHAR_MASK: u32 = 0x001f_ffff; - loop { - // Rejection sampling. About 0.2% of numbers with at most - // 21-bits are invalid codepoints (surrogates), so this - // will succeed first go almost every time. - match char::from_u32(rng.next_u32() & CHAR_MASK) { - Some(c) => return c, - None => {} - } - } - } -} - -impl Rand for bool { - #[inline] - fn rand(rng: &mut R) -> bool { - rng.gen::() & 1 == 1 - } -} - -macro_rules! tuple_impl { - // use variables to indicate the arity of the tuple - ($($tyvar:ident),* ) => { - // the trailing commas are for the 1 tuple - impl< - $( $tyvar : Rand ),* - > Rand for ( $( $tyvar ),* , ) { - - #[inline] - fn rand(_rng: &mut R) -> ( $( $tyvar ),* , ) { - ( - // use the $tyvar's to get the appropriate number of - // repeats (they're not actually needed) - $( - _rng.gen::<$tyvar>() - ),* - , - ) - } - } - } -} - -impl Rand for () { - #[inline] - fn rand(_: &mut R) -> () { () } -} -tuple_impl!{A} -tuple_impl!{A, B} -tuple_impl!{A, B, C} -tuple_impl!{A, B, C, D} -tuple_impl!{A, B, C, D, E} -tuple_impl!{A, B, C, D, E, F} -tuple_impl!{A, B, C, D, E, F, G} -tuple_impl!{A, B, C, D, E, F, G, H} -tuple_impl!{A, B, C, D, E, F, G, H, I} -tuple_impl!{A, B, C, D, E, F, G, H, I, J} -tuple_impl!{A, B, C, D, E, F, G, H, I, J, K} -tuple_impl!{A, B, C, D, E, F, G, H, I, J, K, L} - -macro_rules! array_impl { - {$n:expr, $t:ident, $($ts:ident,)*} => { - array_impl!{($n - 1), $($ts,)*} - - impl Rand for [T; $n] where T: Rand { - #[inline] - fn rand(_rng: &mut R) -> [T; $n] { - [_rng.gen::<$t>(), $(_rng.gen::<$ts>()),*] - } - } - }; - {$n:expr,} => { - impl Rand for [T; $n] { - fn rand(_rng: &mut R) -> [T; $n] { [] } - } - }; -} - -array_impl!{32, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,} - -impl Rand for Option { - #[inline] - fn rand(rng: &mut R) -> Option { - if rng.gen() { - Some(rng.gen()) - } else { - None - } - } -} - -#[cfg(test)] -mod tests { - use {Rng, thread_rng, Open01, Closed01}; - - struct ConstantRng(u64); - impl Rng for ConstantRng { - fn next_u32(&mut self) -> u32 { - let ConstantRng(v) = *self; - v as u32 - } - fn next_u64(&mut self) -> u64 { - let ConstantRng(v) = *self; - v - } - } - - #[test] - fn floating_point_edge_cases() { - // the test for exact equality is correct here. - assert!(ConstantRng(0xffff_ffff).gen::() != 1.0); - assert!(ConstantRng(0xffff_ffff_ffff_ffff).gen::() != 1.0); - } - - #[test] - fn rand_open() { - // this is unlikely to catch an incorrect implementation that - // generates exactly 0 or 1, but it keeps it sane. - let mut rng = thread_rng(); - for _ in 0..1_000 { - // strict inequalities - let Open01(f) = rng.gen::>(); - assert!(0.0 < f && f < 1.0); - - let Open01(f) = rng.gen::>(); - assert!(0.0 < f && f < 1.0); - } - } - - #[test] - fn rand_closed() { - let mut rng = thread_rng(); - for _ in 0..1_000 { - // strict inequalities - let Closed01(f) = rng.gen::>(); - assert!(0.0 <= f && f <= 1.0); - - let Closed01(f) = rng.gen::>(); - assert!(0.0 <= f && f <= 1.0); - } - } -} diff --git a/src/read.rs b/src/read.rs index c7351b75937..c4a41bbbd12 100644 --- a/src/read.rs +++ b/src/read.rs @@ -10,27 +10,30 @@ //! A wrapper around any Read to treat it as an RNG. -use std::io::{self, Read}; -use std::mem; -use Rng; +use std::io::Read; + +use {Rng, Error, ErrorKind}; /// An RNG that reads random bytes straight from a `Read`. This will /// work best with an infinite reader, but this is not required. /// /// # Panics /// -/// It will panic if it there is insufficient data to fulfill a request. +/// Only the `try_fill` method will report errors. All other methods will panic +/// if the underlying reader encounters an error. They will also panic if there +/// is insufficient data to fulfill a request. /// /// # Example /// /// ```rust -/// use rand::{read, Rng}; +/// use rand::{Rng, ReadRng, distributions}; /// /// let data = vec![1, 2, 3, 4, 5, 6, 7, 8]; -/// let mut rng = read::ReadRng::new(&data[..]); -/// println!("{:x}", rng.gen::()); +/// let mut rng = ReadRng::new(&data[..]); +/// println!("{:x}", rng.next_u32()); /// ``` #[derive(Debug)] +// Do not derive Clone, because it could share the underlying reader pub struct ReadRng { reader: R } @@ -46,40 +49,35 @@ impl ReadRng { impl Rng for ReadRng { fn next_u32(&mut self) -> u32 { - // This is designed for speed: reading a LE integer on a LE - // platform just involves blitting the bytes into the memory - // of the u32, similarly for BE on BE; avoiding byteswapping. - let mut buf = [0; 4]; - fill(&mut self.reader, &mut buf).unwrap(); - unsafe { *(buf.as_ptr() as *const u32) } + ::rand_core::impls::next_u32_via_fill(self) } + fn next_u64(&mut self) -> u64 { - // see above for explanation. - let mut buf = [0; 8]; - fill(&mut self.reader, &mut buf).unwrap(); - unsafe { *(buf.as_ptr() as *const u64) } + ::rand_core::impls::next_u64_via_fill(self) } - fn fill_bytes(&mut self, v: &mut [u8]) { - if v.len() == 0 { return } - fill(&mut self.reader, v).unwrap(); + + #[cfg(feature = "i128_support")] + fn next_u128(&mut self) -> u128 { + ::rand_core::impls::next_u128_via_fill(self) } -} -fn fill(r: &mut Read, mut buf: &mut [u8]) -> io::Result<()> { - while buf.len() > 0 { - match try!(r.read(buf)) { - 0 => return Err(io::Error::new(io::ErrorKind::Other, - "end of file reached")), - n => buf = &mut mem::replace(&mut buf, &mut [])[n..], - } + fn fill_bytes(&mut self, dest: &mut [u8]) { + ::rand_core::impls::fill_via_try_fill(self, dest) + } + + fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { + if dest.len() == 0 { return Ok(()); } + // Use `std::io::read_exact`, which retries on `ErrorKind::Interrupted`. + self.reader.read_exact(dest).map_err(|err| { + Error::with_cause(ErrorKind::Unavailable, "ReadRng: read error", err) + }) } - Ok(()) } #[cfg(test)] mod test { use super::ReadRng; - use Rng; + use {Rng, ErrorKind}; #[test] fn test_reader_rng_u64() { @@ -114,10 +112,12 @@ mod test { } #[test] - #[should_panic] fn test_reader_rng_insufficient_bytes() { - let mut rng = ReadRng::new(&[][..]); - let mut v = [0u8; 3]; - rng.fill_bytes(&mut v); + let v = [1u8, 2, 3, 4, 5, 6, 7, 8]; + let mut w = [0u8; 9]; + + let mut rng = ReadRng::new(&v[..]); + + assert!(rng.try_fill(&mut w).err().unwrap().kind == ErrorKind::Unavailable); } } diff --git a/src/reseeding.rs b/src/reseeding.rs index 1f24e2006fd..8af604f697e 100644 --- a/src/reseeding.rs +++ b/src/reseeding.rs @@ -11,21 +11,28 @@ //! A wrapper around another RNG that reseeds it after it //! generates a certain number of random bytes. -use core::default::Default; - -use {Rng, SeedableRng}; +use {Rng, SeedableRng, Error, ErrorKind}; +#[cfg(feature="std")] +use NewSeeded; /// How many bytes of entropy the underling RNG is allowed to generate /// before it is reseeded -const DEFAULT_GENERATION_THRESHOLD: u64 = 32 * 1024; +const DEFAULT_RESEEDING_THRESHOLD: i64 = 32 * 1024; /// A wrapper around any RNG which reseeds the underlying RNG after it /// has generated a certain number of random bytes. -#[derive(Debug)] -pub struct ReseedingRng { +/// +/// Note that reseeding is considered advisory only. If reseeding fails, the +/// generator may delay reseeding or not reseed at all. +/// +/// This derives `Clone` if both the inner RNG `R` and the reseeder `Rsdr` do. +/// Note that reseeders using external entropy should deliberately not +/// implement `Clone`. +#[derive(Debug, Clone)] +pub struct ReseedingRng> { rng: R, - generation_threshold: u64, - bytes_generated: u64, + threshold: i64, + bytes_until_reseed: i64, /// Controls the behaviour when reseeding the RNG. pub reseeder: Rsdr, } @@ -36,151 +43,173 @@ impl> ReseedingRng { /// # Arguments /// /// * `rng`: the random number generator to use. - /// * `generation_threshold`: the number of bytes of entropy at which to reseed the RNG. + /// * `threshold`: the number of generated bytes after which to reseed the RNG. /// * `reseeder`: the reseeding object to use. - pub fn new(rng: R, generation_threshold: u64, reseeder: Rsdr) -> ReseedingRng { + pub fn new(rng: R, threshold: u64, reseeder: Rsdr) -> ReseedingRng { + assert!(threshold <= ::core::i64::MAX as u64); ReseedingRng { rng: rng, - generation_threshold: generation_threshold, - bytes_generated: 0, + threshold: threshold as i64, + bytes_until_reseed: threshold as i64, reseeder: reseeder } } + /// Reseed the internal RNG. + /// On error, this may delay reseeding or not reseed at all. + #[inline(never)] + pub fn reseed(&mut self) { + self.bytes_until_reseed = self.threshold; + let mut err_count = 0; + loop { + if let Err(e) = self.reseeder.reseed(&mut self.rng) { + // TODO: log? + if e.kind.should_wait() { + // Delay reseeding + self.bytes_until_reseed = self.threshold >> 8; + } else if e.kind.should_retry() { + err_count += 1; + if err_count <= 5 { // arbitrary limit + continue; // retry immediately + } + } + // give up trying to reseed + } + break; // successfully reseeded, delayed, or given up. + } + } + /// Reseed the internal RNG if the number of bytes that have been /// generated exceed the threshold. - pub fn reseed_if_necessary(&mut self) { - if self.bytes_generated >= self.generation_threshold { - self.reseeder.reseed(&mut self.rng); - self.bytes_generated = 0; + /// + /// If reseeding fails, return an error with the original cause. Note that + /// if the cause has a permanent failure, we report a transient error and + /// skip reseeding. + #[inline(never)] + pub fn try_reseed(&mut self) -> Result<(), Error> { + if let Err(err) = self.reseeder.reseed(&mut self.rng) { + let newkind = match err.kind { + a @ ErrorKind::NotReady => a, + b @ ErrorKind::Transient => b, + _ => { + self.bytes_until_reseed = self.threshold; // skip reseeding + ErrorKind::Transient + } + }; + return Err(Error::with_cause(newkind, "reseeding failed", err)); } + self.bytes_until_reseed = self.threshold; + Ok(()) } } impl> Rng for ReseedingRng { fn next_u32(&mut self) -> u32 { - self.reseed_if_necessary(); - self.bytes_generated += 4; - self.rng.next_u32() + let value = self.rng.next_u32(); + self.bytes_until_reseed -= 4; + if self.bytes_until_reseed <= 0 { + self.reseed(); + } + value } fn next_u64(&mut self) -> u64 { - self.reseed_if_necessary(); - self.bytes_generated += 8; - self.rng.next_u64() + let value = self.rng.next_u64(); + self.bytes_until_reseed -= 8; + if self.bytes_until_reseed <= 0 { + self.reseed(); + } + value + } + + #[cfg(feature = "i128_support")] + fn next_u128(&mut self) -> u128 { + let value = self.rng.next_u128(); + self.bytes_until_reseed -= 16; + if self.bytes_until_reseed <= 0 { + self.reseed(); + } + value } fn fill_bytes(&mut self, dest: &mut [u8]) { - self.reseed_if_necessary(); - self.bytes_generated += dest.len() as u64; - self.rng.fill_bytes(dest) + self.rng.fill_bytes(dest); + self.bytes_until_reseed -= dest.len() as i64; + if self.bytes_until_reseed <= 0 { + self.reseed(); + } } -} -impl, Rsdr: Reseeder + Default> - SeedableRng<(Rsdr, S)> for ReseedingRng { - fn reseed(&mut self, (rsdr, seed): (Rsdr, S)) { - self.rng.reseed(seed); - self.reseeder = rsdr; - self.bytes_generated = 0; + fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> { + self.rng.try_fill(dest)?; + self.bytes_until_reseed -= dest.len() as i64; + if self.bytes_until_reseed <= 0 { + self.try_reseed()?; + } + Ok(()) } +} +impl> ReseedingRng { /// Create a new `ReseedingRng` from the given reseeder and - /// seed. This uses a default value for `generation_threshold`. - fn from_seed((rsdr, seed): (Rsdr, S)) -> ReseedingRng { + /// seed. This uses a default value for `threshold`. + pub fn from_reseeder(rsdr: Rsdr, seed: ::Seed) -> ReseedingRng { ReseedingRng { rng: SeedableRng::from_seed(seed), - generation_threshold: DEFAULT_GENERATION_THRESHOLD, - bytes_generated: 0, + threshold: DEFAULT_RESEEDING_THRESHOLD, + bytes_until_reseed: DEFAULT_RESEEDING_THRESHOLD, reseeder: rsdr } } } /// Something that can be used to reseed an RNG via `ReseedingRng`. -/// -/// # Example -/// -/// ```rust -/// use rand::{Rng, SeedableRng, StdRng}; -/// use rand::reseeding::{Reseeder, ReseedingRng}; -/// -/// struct TickTockReseeder { tick: bool } -/// impl Reseeder for TickTockReseeder { -/// fn reseed(&mut self, rng: &mut StdRng) { -/// let val = if self.tick {0} else {1}; -/// rng.reseed(&[val]); -/// self.tick = !self.tick; -/// } -/// } -/// fn main() { -/// let rsdr = TickTockReseeder { tick: true }; -/// -/// let inner = StdRng::new().unwrap(); -/// let mut rng = ReseedingRng::new(inner, 10, rsdr); -/// -/// // this will repeat, because it gets reseeded very regularly. -/// let s: String = rng.gen_ascii_chars().take(100).collect(); -/// println!("{}", s); -/// } -/// -/// ``` -pub trait Reseeder { +/// +/// Note that implementations should support `Clone` only if reseeding is +/// deterministic (no external entropy source). This is so that a `ReseedingRng` +/// only supports `Clone` if fully deterministic. +pub trait Reseeder { /// Reseed the given RNG. - fn reseed(&mut self, rng: &mut R); + /// + /// On error, this should just forward the source error; errors are handled + /// by the caller. + fn reseed(&mut self, rng: &mut R) -> Result<(), Error>; } -/// Reseed an RNG using a `Default` instance. This reseeds by -/// replacing the RNG with the result of a `Default::default` call. -#[derive(Clone, Copy, Debug)] -pub struct ReseedWithDefault; +/// Reseed an RNG using `NewSeeded` to replace the current instance. +#[cfg(feature="std")] +#[derive(Debug)] +pub struct ReseedWithNew; -impl Reseeder for ReseedWithDefault { - fn reseed(&mut self, rng: &mut R) { - *rng = Default::default(); +#[cfg(feature="std")] +impl Reseeder for ReseedWithNew { + fn reseed(&mut self, rng: &mut R) -> Result<(), Error> { + R::new().map(|result| *rng = result) } } -impl Default for ReseedWithDefault { - fn default() -> ReseedWithDefault { ReseedWithDefault } -} #[cfg(test)] mod test { - use std::default::Default; use std::iter::repeat; - use super::{ReseedingRng, ReseedWithDefault}; - use {SeedableRng, Rng}; - - struct Counter { - i: u32 - } - - impl Rng for Counter { - fn next_u32(&mut self) -> u32 { - self.i += 1; - // very random - self.i - 1 + use mock::MockAddRng; + use {SeedableRng, Rng, iter, Error}; + use super::{ReseedingRng, Reseeder}; + + #[derive(Debug, Clone)] + struct ReseedMock; + impl Reseeder for ReseedMock { + fn reseed(&mut self, rng: &mut MockAddRng) -> Result<(), Error> { + *rng = MockAddRng::new(0, 1); + Ok(()) } } - impl Default for Counter { - fn default() -> Counter { - Counter { i: 0 } - } - } - impl SeedableRng for Counter { - fn reseed(&mut self, seed: u32) { - self.i = seed; - } - fn from_seed(seed: u32) -> Counter { - Counter { i: seed } - } - } - type MyRng = ReseedingRng; + + type MyRng = ReseedingRng; #[test] fn test_reseeding() { - let mut rs = ReseedingRng::new(Counter {i:0}, 400, ReseedWithDefault); + let mut rs = ReseedingRng::new(MockAddRng::new(0, 1), 400, ReseedMock); let mut i = 0; for _ in 0..1000 { @@ -191,21 +220,12 @@ mod test { #[test] fn test_rng_seeded() { - let mut ra: MyRng = SeedableRng::from_seed((ReseedWithDefault, 2)); - let mut rb: MyRng = SeedableRng::from_seed((ReseedWithDefault, 2)); - assert!(::test::iter_eq(ra.gen_ascii_chars().take(100), - rb.gen_ascii_chars().take(100))); - } - - #[test] - fn test_rng_reseed() { - let mut r: MyRng = SeedableRng::from_seed((ReseedWithDefault, 3)); - let string1: String = r.gen_ascii_chars().take(100).collect(); - - r.reseed((ReseedWithDefault, 3)); - - let string2: String = r.gen_ascii_chars().take(100).collect(); - assert_eq!(string1, string2); + // Default seed threshold is way beyond what we use here + let seed = [0, 1, 2, 3, 4, 5, 6, 7]; + let mut ra: MyRng = ReseedingRng::from_reseeder(ReseedMock, seed); + let mut rb = MockAddRng::from_seed(seed); + assert!(::test::iter_eq(iter(&mut ra).map(|rng| rng.next_u32()).take(100), + iter(&mut rb).map(|rng| rng.next_u32()).take(100))); } const FILL_BYTES_V_LEN: usize = 13579; diff --git a/src/seq.rs b/src/seq.rs index a7889fe34b2..cd320fc1894 100644 --- a/src/seq.rs +++ b/src/seq.rs @@ -10,7 +10,7 @@ //! Functions for randomly accessing and sampling sequences. -use super::Rng; +use super::{Rng, Sample}; // This crate is only enabled when either std or alloc is available. // BTreeMap is not as fast in tests, but better than nothing. @@ -38,7 +38,7 @@ use super::Rng; /// let sample = seq::sample_iter(&mut rng, 1..100, 5).unwrap(); /// println!("{:?}", sample); /// ``` -pub fn sample_iter(rng: &mut R, iterable: I, amount: usize) -> Result, Vec> +pub fn sample_iter(mut rng: R, iterable: I, amount: usize) -> Result, Vec> where I: IntoIterator, R: Rng, { @@ -83,7 +83,7 @@ pub fn sample_iter(rng: &mut R, iterable: I, amount: usize) -> Result(rng: &mut R, slice: &[T], amount: usize) -> Vec +pub fn sample_slice(rng: R, slice: &[T], amount: usize) -> Vec where R: Rng, T: Clone { @@ -111,7 +111,7 @@ pub fn sample_slice(rng: &mut R, slice: &[T], amount: usize) -> Vec /// let values = vec![5, 6, 1, 3, 4, 6, 7]; /// println!("{:?}", seq::sample_slice_ref(&mut rng, &values, 3)); /// ``` -pub fn sample_slice_ref<'a, R, T>(rng: &mut R, slice: &'a [T], amount: usize) -> Vec<&'a T> +pub fn sample_slice_ref<'a, R, T>(rng: R, slice: &'a [T], amount: usize) -> Vec<&'a T> where R: Rng { let indices = sample_indices(rng, slice.len(), amount); @@ -131,7 +131,7 @@ pub fn sample_slice_ref<'a, R, T>(rng: &mut R, slice: &'a [T], amount: usize) -> /// have the indices themselves so this is provided as an alternative. /// /// Panics if `amount > length` -pub fn sample_indices(rng: &mut R, length: usize, amount: usize) -> Vec +pub fn sample_indices(rng: R, length: usize, amount: usize) -> Vec where R: Rng, { if amount > length { @@ -164,7 +164,7 @@ pub fn sample_indices(rng: &mut R, length: usize, amount: usize) -> Vec= length / 2` since it does not /// require allocating an extra cache and is much faster. -fn sample_indices_inplace(rng: &mut R, length: usize, amount: usize) -> Vec +fn sample_indices_inplace(mut rng: R, length: usize, amount: usize) -> Vec where R: Rng, { debug_assert!(amount <= length); @@ -188,7 +188,7 @@ fn sample_indices_inplace(rng: &mut R, length: usize, amount: usize) -> Vec( - rng: &mut R, + mut rng: R, length: usize, amount: usize, ) -> Vec @@ -226,7 +226,8 @@ fn sample_indices_cache( #[cfg(test)] mod test { use super::*; - use {thread_rng, XorShiftRng, SeedableRng}; + use {thread_rng, SeedableRng}; + use prng::XorShiftRng; #[test] fn test_sample_iter() { @@ -300,10 +301,9 @@ mod test { for length in 1usize..max_range { let amount = r.gen_range(0, length); - let seed: [u32; 4] = [ - r.next_u32(), r.next_u32(), r.next_u32(), r.next_u32() - ]; - + let mut seed = [0u8; 16]; + r.fill_bytes(&mut seed); + println!("Selecting indices: len={}, amount={}, seed={:?}", length, amount, seed); // assert that the two index methods give exactly the same result diff --git a/src/sequences/mod.rs b/src/sequences/mod.rs new file mode 100644 index 00000000000..af977859179 --- /dev/null +++ b/src/sequences/mod.rs @@ -0,0 +1,160 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Random operations on sequences + +use {Rng, Sample}; + +#[cfg(feature="std")] +pub use self::weighted::{Weighted, WeightedChoice}; + +#[cfg(feature="std")] +mod weighted; + +/// This trait implements a `choose` operations on slices and sequences. +pub trait Choose { + /// Return one element from a sequence. + /// + /// Returns `None` only if the sequence is empty. + /// + /// # Example + /// + /// ``` + /// use rand::thread_rng; + /// use rand::sequences::Choose; + /// + /// let choices = [1, 2, 4, 8, 16, 32]; + /// let mut rng = thread_rng(); + /// println!("{:?}", choices[..].choose(&mut rng)); + /// assert_eq!(choices[..0].choose(&mut rng), None); + /// ``` + fn choose(self, rng: &mut R) -> Option; +} + +impl<'a, T> Choose<&'a T> for &'a [T] { + fn choose(self, rng: &mut R) -> Option<&'a T> { + if self.is_empty() { + None + } else { + Some(&self[rng.gen_range(0, self.len())]) + } + } +} + +impl<'a, T> Choose<&'a mut T> for &'a mut [T] { + fn choose(self, rng: &mut R) -> Option<&'a mut T> { + if self.is_empty() { + None + } else { + let len = self.len(); + Some(&mut self[rng.gen_range(0, len)]) + } + } +} + +#[cfg(feature="std")] +impl Choose for Vec { + fn choose(mut self, rng: &mut R) -> Option { + if self.is_empty() { + None + } else { + let index = rng.gen_range(0, self.len()); + self.drain(index..).next() + } + } +} + +/// This trait introduces a `shuffle` operations on slices. +pub trait Shuffle { + /// Shuffle a mutable sequence in place. + /// + /// This applies Durstenfeld's algorithm for the [Fisher–Yates shuffle](https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_modern_algorithm) + /// which produces an unbiased permutation. + /// + /// # Example + /// + /// ```rust + /// use rand::thread_rng; + /// use rand::sequences::Shuffle; + /// + /// let mut rng = thread_rng(); + /// let mut y = [1, 2, 3]; + /// y[..].shuffle(&mut rng); + /// println!("{:?}", y); + /// y[..].shuffle(&mut rng); + /// println!("{:?}", y); + /// ``` + fn shuffle(self, rng: &mut R); +} + +impl<'a, T> Shuffle for &'a mut [T] { + fn shuffle(self, rng: &mut R) { + let mut i = self.len(); + while i >= 2 { + // invariant: elements with index >= i have been locked in place. + i -= 1; + // lock element i in place. + self.swap(i, rng.gen_range(0, i + 1)); + } + } +} + +#[cfg(feature="std")] +impl<'a, T> Shuffle for &'a mut Vec { + fn shuffle(self, rng: &mut R) { + (self[..]).shuffle(rng) + } +} + +#[cfg(test)] +mod test { + use {Rng, thread_rng}; + use super::{Choose, Shuffle}; + + #[test] + fn test_choose() { + let mut r = thread_rng(); + assert_eq!([1, 1, 1][..].choose(&mut r).map(|&x|x), Some(1)); + + let v: &[isize] = &[]; + assert_eq!(v.choose(&mut r), None); + } + + #[test] + fn test_shuffle() { + let mut r = thread_rng(); + let empty: &mut [isize] = &mut []; + empty.shuffle(&mut r); + let mut one = [1]; + one[..].shuffle(&mut r); + let b: &[_] = &[1]; + assert_eq!(one, b); + + let mut two = [1, 2]; + two[..].shuffle(&mut r); + assert!(two == [1, 2] || two == [2, 1]); + + let mut x = [1, 1, 1]; + x[..].shuffle(&mut r); + let b: &[_] = &[1, 1, 1]; + assert_eq!(x, b); + } + + #[test] + fn dyn_dispatch() { + let r: &mut Rng = &mut thread_rng(); + + assert_eq!([7, 7][..].choose(r), Some(&7)); + + let mut x = [6, 2]; + x[..].shuffle(r); + assert!(x == [6, 2] || x == [2, 6]); + } +} diff --git a/src/sequences/weighted.rs b/src/sequences/weighted.rs new file mode 100644 index 00000000000..37d88c0e610 --- /dev/null +++ b/src/sequences/weighted.rs @@ -0,0 +1,242 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Weighted choices +//! +//! TODO: evaluate whether this functionality should be stabilised as-is, +//! adapted, or removed entirely. + +use Rng; +use distributions::Distribution; +use distributions::range::{Range, RangeInt}; + +/// A value with a particular weight for use with `WeightedChoice`. +#[derive(Copy, Clone, Debug)] +pub struct Weighted { + /// The numerical weight of this item + pub weight: u32, + /// The actual item which is being weighted + pub item: T, +} + +/// A distribution that selects from a finite collection of weighted items. +/// +/// Each item has an associated weight that influences how likely it +/// is to be chosen: higher weight is more likely. +/// +/// # Example +/// +/// ```rust +/// use rand::distributions::Distribution; +/// use rand::sequences::{Weighted, WeightedChoice}; +/// +/// let items = vec!(Weighted { weight: 2, item: 'a' }, +/// Weighted { weight: 4, item: 'b' }, +/// Weighted { weight: 1, item: 'c' }); +/// let wc = WeightedChoice::new(items); +/// let mut rng = rand::thread_rng(); +/// for _ in 0..16 { +/// // on average prints 'a' 4 times, 'b' 8 and 'c' twice. +/// println!("{}", wc.sample(&mut rng)); +/// } +/// ``` +#[derive(Clone, Debug)] +pub struct WeightedChoice { + items: Vec>, + weight_range: Range>, +} + +impl WeightedChoice { + /// Create a new `WeightedChoice`. + /// + /// Panics if: + /// - `v` is empty + /// - the total weight is 0 + /// - the total weight is larger than a `u32` can contain. + pub fn new(mut items: Vec>) -> WeightedChoice { + // strictly speaking, this is subsumed by the total weight == 0 case + assert!(!items.is_empty(), "WeightedChoice::new called with no items"); + + let mut running_total: u32 = 0; + + // we convert the list from individual weights to cumulative + // weights so we can binary search. This *could* drop elements + // with weight == 0 as an optimisation. + for ref mut item in items.iter_mut() { + running_total = match running_total.checked_add(item.weight) { + Some(n) => n, + None => panic!("WeightedChoice::new called with a total weight \ + larger than a u32 can contain") + }; + + item.weight = running_total; + } + assert!(running_total != 0, "WeightedChoice::new called with a total weight of 0"); + + WeightedChoice { + items: items, + // we're likely to be generating numbers in this range + // relatively often, so might as well cache it + weight_range: Range::new(0, running_total) + } + } +} + +impl Distribution for WeightedChoice { + fn sample(&self, rng: &mut R) -> T { + // we want to find the first element that has cumulative + // weight > sample_weight, which we do by binary since the + // cumulative weights of self.items are sorted. + + // choose a weight in [0, total_weight) + let sample_weight = self.weight_range.sample(rng); + + // short circuit when it's the first item + if sample_weight < self.items[0].weight { + return self.items[0].item.clone(); + } + + let mut idx = 0; + let mut modifier = self.items.len(); + + // now we know that every possibility has an element to the + // left, so we can just search for the last element that has + // cumulative weight <= sample_weight, then the next one will + // be "it". (Note that this greatest element will never be the + // last element of the vector, since sample_weight is chosen + // in [0, total_weight) and the cumulative weight of the last + // one is exactly the total weight.) + while modifier > 1 { + let i = idx + modifier / 2; + if self.items[i].weight <= sample_weight { + // we're small, so look to the right, but allow this + // exact element still. + idx = i; + // we need the `/ 2` to round up otherwise we'll drop + // the trailing elements when `modifier` is odd. + modifier += 1; + } else { + // otherwise we're too big, so go left. (i.e. do + // nothing) + } + modifier /= 2; + } + return self.items[idx + 1].item.clone(); + } +} + +#[cfg(test)] +mod tests { + use mock::MockAddRng; + use distributions::Distribution; + use super::{WeightedChoice, Weighted}; + + #[test] + fn test_weighted_choice() { + // this makes assumptions about the internal implementation of + // WeightedChoice. It may fail when the implementation in + // `distributions::range::RangeInt changes. + + macro_rules! t { + ($items:expr, $expected:expr) => {{ + let items = $items; + let mut total_weight = 0; + for item in &items { total_weight += item.weight; } + + let wc = WeightedChoice::new(items); + let expected = $expected; + + // Use extremely large steps between the random numbers, because + // we test with small ranges and RangeInt is designed to prefer + // the most significant bits. + let mut rng = MockAddRng::new(0, !0 / (total_weight as u64)); + + for &val in expected.iter() { + assert_eq!(wc.sample(&mut rng), val) + } + }} + } + + t!(vec!(Weighted { weight: 1, item: 10}), [10]); + + // skip some + t!(vec!(Weighted { weight: 0, item: 20}, + Weighted { weight: 2, item: 21}, + Weighted { weight: 0, item: 22}, + Weighted { weight: 1, item: 23}), + [21, 21, 23]); + + // different weights + t!(vec!(Weighted { weight: 4, item: 30}, + Weighted { weight: 3, item: 31}), + [30, 31, 30, 31, 30, 31, 30]); + + // check that we're binary searching + // correctly with some vectors of odd + // length. + t!(vec!(Weighted { weight: 1, item: 40}, + Weighted { weight: 1, item: 41}, + Weighted { weight: 1, item: 42}, + Weighted { weight: 1, item: 43}, + Weighted { weight: 1, item: 44}), + [40, 41, 42, 43, 44]); + t!(vec!(Weighted { weight: 1, item: 50}, + Weighted { weight: 1, item: 51}, + Weighted { weight: 1, item: 52}, + Weighted { weight: 1, item: 53}, + Weighted { weight: 1, item: 54}, + Weighted { weight: 1, item: 55}, + Weighted { weight: 1, item: 56}), + [50, 54, 51, 55, 52, 56, 53]); + } + + #[test] + fn test_weighted_clone_initialization() { + let initial : Weighted = Weighted {weight: 1, item: 1}; + let clone = initial.clone(); + assert_eq!(initial.weight, clone.weight); + assert_eq!(initial.item, clone.item); + } + + #[test] #[should_panic] + fn test_weighted_clone_change_weight() { + let initial : Weighted = Weighted {weight: 1, item: 1}; + let mut clone = initial.clone(); + clone.weight = 5; + assert_eq!(initial.weight, clone.weight); + } + + #[test] #[should_panic] + fn test_weighted_clone_change_item() { + let initial : Weighted = Weighted {weight: 1, item: 1}; + let mut clone = initial.clone(); + clone.item = 5; + assert_eq!(initial.item, clone.item); + + } + + #[test] #[should_panic] + fn test_weighted_choice_no_items() { + WeightedChoice::::new(vec![]); + } + #[test] #[should_panic] + fn test_weighted_choice_zero_weight() { + WeightedChoice::new(vec![Weighted { weight: 0, item: 0}, + Weighted { weight: 0, item: 1}]); + } + #[test] #[should_panic] + fn test_weighted_choice_weight_overflows() { + let x = ::std::u32::MAX / 2; // x + x + 2 is the overflow + WeightedChoice::new(vec![Weighted { weight: x, item: 0 }, + Weighted { weight: 1, item: 1 }, + Weighted { weight: x, item: 2 }, + Weighted { weight: 1, item: 3 }]); + } +} diff --git a/src/thread_local.rs b/src/thread_local.rs new file mode 100644 index 00000000000..fe5b9e46191 --- /dev/null +++ b/src/thread_local.rs @@ -0,0 +1,155 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Thread-local handle to a random number generator + +use std::cell::RefCell; +use std::rc::Rc; + +use {Rng, StdRng, NewSeeded, Distribution, Default, Sample, Error}; + +use reseeding::{ReseedingRng, ReseedWithNew}; + +const THREAD_RNG_RESEED_THRESHOLD: u64 = 32_768; +type ReseedingStdRng = ReseedingRng; + +/// The thread-local RNG. +#[derive(Clone, Debug)] +pub struct ThreadRng { + rng: Rc>, +} + +impl Rng for ThreadRng { + fn next_u32(&mut self) -> u32 { + self.rng.borrow_mut().next_u32() + } + + fn next_u64(&mut self) -> u64 { + self.rng.borrow_mut().next_u64() + } + + #[cfg(feature = "i128_support")] + fn next_u128(&mut self) -> u128 { + self.rng.borrow_mut().next_u128() + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + self.rng.borrow_mut().fill_bytes(dest); + } + + fn try_fill(&mut self, bytes: &mut [u8]) -> Result<(), Error> { + self.rng.borrow_mut().try_fill(bytes) + } +} + +thread_local!( + static THREAD_RNG_KEY: Rc> = { + let r = match StdRng::new() { + Ok(r) => r, + Err(e) => panic!("could not initialize thread_rng: {:?}", e) + }; + let rng = ReseedingRng::new(r, + THREAD_RNG_RESEED_THRESHOLD, + ReseedWithNew); + Rc::new(RefCell::new(rng)) + } +); + +/// Retrieve the lazily-initialized thread-local random number +/// generator, seeded by the system. This is used by `random` and +/// `random_with` to generate new values, and may be used directly with other +/// distributions: `Range::new(0, 10).sample(&mut thread_rng())`. +/// +/// Uses `StdRng` internally, set to reseed from `OsRng` periodically. +/// This should provide a reasonable compromise between speed and security; +/// while the generator is not approved for crytographic usage its output should +/// be hard to guess, and performance should be similar to non-cryptographic +/// generators. +pub fn thread_rng() -> ThreadRng { + ThreadRng { rng: THREAD_RNG_KEY.with(|t| t.clone()) } +} + +/// Generates a random value using the thread-local random number generator. +/// +/// `random()` can generate various types of random things, and so may require +/// type hinting to generate the specific type you want. It uses +/// `distributions::SampleDefault` to determine *how* values are generated for each type. +/// +/// This function uses the thread local random number generator. This means +/// that if you're calling `random()` in a loop, caching the generator can +/// increase performance. An example is shown below. +/// +/// # Examples +/// +/// ``` +/// let x = rand::random::(); +/// println!("{}", x); +/// +/// if rand::random() { // generates a boolean +/// println!("Better lucky than good!"); +/// } +/// ``` +/// +/// Caching the thread local random number generator: +/// +/// ``` +/// use rand::{Sample}; +/// +/// let mut v = vec![1, 2, 3]; +/// +/// for x in v.iter_mut() { +/// *x = rand::random() +/// } +/// +/// // would be faster as +/// +/// let mut rng = rand::thread_rng(); +/// +/// for x in v.iter_mut() { +/// *x = rng.gen() +/// } +/// ``` +#[inline] +pub fn random() -> T where Default: Distribution { + thread_rng().sample(Default) +} + +/// Generates a random value using the thread-local random number generator. +/// +/// This is a more flexible variant of `random()`, supporting selection of the +/// distribution used. For example: +/// +/// ``` +/// use rand::{Sample, random_with}; +/// use rand::distributions::{Default, Uniform01, Closed01, Range}; +/// +/// // identical to calling `random()`: +/// let x: f64 = random_with(Default); +/// +/// // same distribution, since Default uses Uniform01 for floats: +/// let y: f64 = random_with(Uniform01); +/// +/// // use the closed range [0, 1] inseat of half-open [0, 1): +/// let z: f64 = random_with(Closed01); +/// +/// // use the half-open range [0, 2): +/// let w: f64 = random_with(Range::new(0.0, 2.0)); +/// +/// // Note that constructing a `Range` is non-trivial, so for the last example +/// // it might be better to do this if sampling a lot: +/// let mut rng = rand::thread_rng(); +/// let range = Range::new(0.0, 2.0); +/// // Do this bit many times: +/// let v = rng.sample(range); +/// ``` +#[inline] +pub fn random_with(distribution: D) -> T where D: Distribution { + thread_rng().sample(distribution) +} diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 00000000000..e6ed5ecbf17 --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,374 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! A collection of utility functions +//! +//! This module exposes some utility functions used internally by the `rand` +//! crate. They are not the intended or most ergonomic way to use the library. +//! +//! The `FloatConversions` trait provides the building blocks to convert a +//! random integer to a IEEE floating point number uniformly distributed over a +//! some range. + +use core::mem; + +trait FloatBitOps { + type F; + + /// Helper function to combine the fraction, exponent and sign into a float. + /// `self` includes the bits from the fraction and sign. + fn binor_exp(self, exponent: i32) -> Self::F; +} + +impl FloatBitOps for u32 { + type F = f32; + #[inline(always)] + fn binor_exp(self, exponent: i32) -> f32 { + // The exponent is encoded using an offset-binary representation, + // with the zero offset being 127 + let exponent_bits = ((127 + exponent) as u32) << 23; + unsafe { mem::transmute(self | exponent_bits) } + } +} + +impl FloatBitOps for u64 { + type F = f64; + #[inline(always)] + fn binor_exp(self, exponent: i32) -> f64 { + // The exponent is encoded using an offset-binary representation, + // with the zero offset being 1023 + let exponent_bits = ((1023 + exponent) as u64) << 52; + unsafe { mem::transmute(self | exponent_bits) } + } +} + +/// Convert a random integer to a uniformly distributed floating point number. +pub trait FloatConversions { + type F; + + /// Convert a random integer to a floating point number sampled from the + /// uniformly distributed half-open range [0,1). + /// The precision gets higher closer to zero, up to 2^-32 for f32 + /// and 2^-64 for f64. + fn closed_open01(self) -> Self::F; + + /// Convert a random integer to a floating point number sampled from the + /// uniformly distributed half-open range (0,1]. + /// The precision gets higher closer to zero, up to 2^-32 for f32 + /// and 2^-64 for f64. + fn open_closed01(self) -> Self::F; + + /// Convert a random integer to a floating point number sampled from the + /// uniformly distributed half-open range [-1,1). + /// The precision gets higher closer to zero, up to 2^-32 for f32 + /// and 2^-64 for f64. + fn closed_open11(self) -> Self::F; + + /// Convert a random integer to a floating point number sampled from the + /// uniformly distributed half-open range [0,1). + /// The precision is fixed, with 2^-23 for f32 and 2^-52 for f64. + /// This leaves a couple of the random bits available for other purposes + /// (the 9 least significant bits for f32, and 12 for f64). + fn closed_open01_fixed(self) -> Self::F; + + /// Convert a random integer to a floating point number sampled from the + /// uniformly distributed half-open range (0,1]. + /// The precision is fixed, with 2^-23 for f32 and 2^-52 for f64. + /// This leaves a couple of the random bits available for other purposes + /// (the 9 least significant bits for f32, and 12 for f64). + fn open_closed01_fixed(self) -> Self::F; + + /// Convert a random integer to a floating point number sampled from the + /// uniformly distributed half-open range [-1,1). + /// The precision is fixed, with 2^-22 for f32 and 2^-51 for f64. + /// This leaves a couple of the random bits available for other purposes + /// (the 9 least significant bits for f32, and 12 for f64). + fn closed_open11_fixed(self) -> Self::F; +} + +macro_rules! float_impls { + ($ty:ty, $uty:ty, $FLOAT_SIZE:expr, $FRACTION_BITS:expr, + $FRACTION_MASK:expr, $EXPONENT_BIAS:expr, $next_u:path) => { + + impl FloatConversions for $uty { + type F = $ty; + + /// Convert a random number to a floating point number sampled from + /// the uniformly distributed half-open range [0,1). + /// + /// We fill the fractional part of the f32 with 23 random bits. + /// The trick to generate a uniform distribution over [0,1) is to + /// fill the exponent with the -log2 of the remaining random bits. A + /// simpler alternative to -log2 is to count the number of trailing + /// zero's of the random bits, add 1, and negate the result. + /// + /// Each exponent is responsible for a piece of the distribution + /// between [0,1). The exponent -1 fills the part [0.5,1). -2 fills + /// [0.25,0.5). The lowest exponent we can get is -10. So a problem + /// with this method is that we can not fill the part between zero + /// and the part from -10. The solution is to treat numbers with an + /// exponent of -10 as if they have -9 as exponent, and substract + /// 2^-9. + #[inline(always)] + fn closed_open01(self) -> $ty { + const MIN_EXPONENT: i32 = $FRACTION_BITS - $FLOAT_SIZE; + #[allow(non_snake_case)] + let ADJUST: $ty = (0 as $uty).binor_exp(MIN_EXPONENT); + // 2^MIN_EXPONENT + + // Use the last 23 bits to fill the fraction + let fraction = self & $FRACTION_MASK; + // Use the remaing (first) 9 bits for the exponent + let exp = $FRACTION_BITS - 1 + - ((self & !$FRACTION_MASK).trailing_zeros() as i32); + if exp < MIN_EXPONENT { + return fraction.binor_exp(MIN_EXPONENT) - ADJUST; + } + fraction.binor_exp(exp) + } + + /// Convert a random number to a floating point number sampled from + /// the uniformly distributed chalf-open range (0,1]. + /// + /// This method is similar to `closed_open01`, with one extra step: + /// If the fractional part ends up as zero, add 1 to the exponent in + /// 50% of the cases. This changes 0.5 to 1.0, 0.25 to 0.5, etc. + /// + /// The chance to select these numbers that staddle the boundery + /// between exponents is 33% to high in `closed_open01` + /// (e.g. 33%*2^-23 for 0.5f32). The reason is that with 0.5 as + /// example the distance to the next number is 2^-23, but to the + /// previous number 2^-24. With the adjustment they have exactly the + /// right chance of getting sampled. + #[inline(always)] + fn open_closed01(self) -> $ty { + const MIN_EXPONENT: i32 = $FRACTION_BITS - $FLOAT_SIZE; + #[allow(non_snake_case)] + let ADJUST: $ty = (0 as $uty).binor_exp(MIN_EXPONENT); + // 2^MIN_EXPONENT + + // Use the last 23 bits to fill the fraction + let fraction = self & $FRACTION_MASK; + // Use the remaing (first) 9 bits for the exponent + let mut exp = $FRACTION_BITS - 1 + - ((self & !$FRACTION_MASK).trailing_zeros() as i32); + if fraction == 0 { + // Shift the exponent in about 50% of the samples, based on + // one of the unused bits for the exponent. + // Shift uncondinionally for the lowest exponents, because + // there are no more random bits left. + if (exp <= MIN_EXPONENT) || + (self & 1 << ($FLOAT_SIZE - 1) != 0) { + exp += 1; + } + } else if exp < MIN_EXPONENT { + return fraction.binor_exp(MIN_EXPONENT) - ADJUST; + } + fraction.binor_exp(exp) + } + + /// Convert a random number to a floating point number sampled from + /// the uniformly distributed half-open range [-1,1). + /// + /// This uses the same method as `closed_open01`. One of the random + /// bits that it uses for the exponent, is now used as a sign bit. + /// + /// This samples numbers from the range (-1,-0] and [0,1). Ideally + /// we would not sample -0.0, but include -1.0. That is why for + /// negative numbers we use the extra step from `open01`. + #[inline(always)] + fn closed_open11(self) -> $ty { + const MIN_EXPONENT: i32 = $FRACTION_BITS - $FLOAT_SIZE + 1; + const SIGN_BIT: $uty = 1 << ($FLOAT_SIZE - 1); + #[allow(non_snake_case)] + let ADJUST: $ty = (0 as $uty).binor_exp(MIN_EXPONENT); + // 2^MIN_EXPONENT + + // Use the last 23 bits to fill the fraction, and the first bit + // as sign + let fraction = self & ($FRACTION_MASK | SIGN_BIT); + // Use the remaing 8 bits for the exponent + let mut exp = $FRACTION_BITS - + (((self & !$FRACTION_MASK) << 1).trailing_zeros() as i32); + if fraction == SIGN_BIT { + // Shift the exponent in about 50% of the samples, based on + // one of the unused bits for the exponent. + // Shift uncondinionally for the lowest exponents, because + // there are no more random bits left. + if (exp <= MIN_EXPONENT) || + (self & 1 << ($FLOAT_SIZE - 2) != 0) { + exp += 1; + } + } else if exp < MIN_EXPONENT { + let result: $ty = fraction.binor_exp(MIN_EXPONENT); + match fraction & SIGN_BIT == SIGN_BIT { + false => return result - ADJUST, + true => return result + ADJUST, + }; + } + fraction.binor_exp(exp) + } + + /// This uses a technique described by Saito and Matsumoto at + /// MCQMC'08. Given that the IEEE floating point numbers are + /// uniformly distributed over [1,2), we generate a number in this + /// range and then offset it onto the range [0,1). Our choice of + /// bits (masking v. shifting) is arbitrary and should be immaterial + /// for high quality generators. For low quality generators + /// (ex. LCG), prefer bitshifting due to correlation between + /// sequential low order bits. + /// + /// See: + /// A PRNG specialized in double precision floating point numbers + /// using an affine transition + /// http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/ARTICLES/dSFMT.pdf + /// http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/dSFMT-slide-e.pdf + #[inline(always)] + fn closed_open01_fixed(self) -> $ty { + const EXPONENT: i32 = 0; + let fraction = self >> ($FLOAT_SIZE - $FRACTION_BITS); + fraction.binor_exp(EXPONENT) - 1.0 + } + + // Similar to `closed_open01_fixed`, with one modification. After + // converting to a float but before substracting -1.0, we want to + // shift every number one place to the next floating point number + // (e.g. +1 ulp). Thanks to how floating points a represented, this + // is as simple as adding 1 to the integer representation. + // Because we inline the `binor_exp` function, this looks more + // different than it really is. + #[inline(always)] + fn open_closed01_fixed(self) -> $ty { + let exponent_bits: $uty = $EXPONENT_BIAS << $FRACTION_BITS + 0; + let fraction = self >> ($FLOAT_SIZE - $FRACTION_BITS); + let bits = fraction | exponent_bits; + let float: $ty = unsafe { mem::transmute(bits + 1) }; + float - 1.0 + } + + /// Like `closed_open01_fixed`, but generate a number in the range + /// [2,4) and offset it onto the range [-1,1). + #[inline(always)] + fn closed_open11_fixed(self) -> $ty { + const EXPONENT: i32 = 1; + let fraction = self >> ($FLOAT_SIZE - $FRACTION_BITS); + fraction.binor_exp(EXPONENT) - 3.0 + } + } + } +} +float_impls! { f32, u32, 32, 23, 0x7f_ffff, 127, Rng::next_u32 } +float_impls! { f64, u64, 64, 52, 0xf_ffff_ffff_ffff, 1023, Rng::next_u64 } + + +#[cfg(test)] +mod tests { + use super::FloatConversions; + use Sample; + use distributions::Uniform; + + #[test] + fn closed_open01_edge_cases() { + // Test that the distribution is a half-open range over [0,1). + // These constants happen to generate the lowest and highest floats in + // the range. + assert!(0u32.closed_open01() == 0.0); + assert!(0xffff_ffffu32.closed_open01() == 0.99999994); + + assert!(0u64.closed_open01() == 0.0); + assert!(0xffff_ffff_ffff_ffffu64.closed_open01() == 0.9999999999999999); + } + + #[test] + fn open_closed01_edge_cases() { + // Test that the distribution is a half-open range over (0,1]. + // These constants happen to generate the lowest and highest floats in + // the range. + assert!(1u32.open_closed01() == 2.3283064e-10); // 2^-32 + assert!(0xff80_0000u32.open_closed01() == 1.0); + + assert!(1u64.open_closed01() == 5.421010862427522e-20); // 2^-64 + assert!(0xfff0_0000_0000_0000u64.open_closed01() == 1.0); + } + + #[test] + fn closed_open11_edge_cases() { + // Test that the distribution is a half-open range over [-1,1). + // These constants happen to generate the lowest and highest floats in + // the range. + assert!(0xff80_0000u32.closed_open11() == -1.0); + assert!(0x7fff_ffffu32.closed_open11() == 0.99999994); // 1 - 2^-24 + + assert!(0xfff0_0000_0000_0000u64.closed_open11() == -1.0); + assert!(0x7fff_ffff_ffff_ffffu64.closed_open11() == 0.9999999999999999); + } + + #[test] + fn closed_open01_fixed_edge_cases() { + // Test that the distribution is a half-open range over [0,1). + // These constants happen to generate the lowest and highest floats in + // the range. + let mut rng = ::test::rng(); + let mut bits: u32 = rng.sample(Uniform); + bits = bits & 0x1ff; // 9 bits with no influence + + assert!((bits | 0).closed_open01_fixed() == 0.0); + assert!((bits | 0xfffffe00).closed_open01_fixed() + == 0.9999999); // 1 - 2^-23 + + let mut bits: u64 = rng.sample(Uniform); + bits = bits & 0xfff; // 12 bits with no influence + + assert!((bits | 0).closed_open01_fixed() == 0.0); + assert!((bits | 0xffff_ffff_ffff_f000).closed_open01_fixed() == + 0.9999999999999998); // 1 - 2^-52 + } + + #[test] + fn open_closed01_fixed_edge_cases() { + // Test that the distribution is a half-open range over [0,1). + // These constants happen to generate the lowest and highest floats in + // the range. + let mut rng = ::test::rng(); + let mut bits: u32 = rng.sample(Uniform); + bits = bits & 0x1ff; // 9 bits with no influence + + assert!((bits | 0).open_closed01_fixed() == 1.1920929e-7); // 2^-23 + assert!((bits | 0xfffffe00).open_closed01_fixed() == 1.0); + + let mut bits: u64 = rng.sample(Uniform); + bits = bits & 0xfff; // 12 bits with no influence + + assert!((bits | 0).open_closed01_fixed() == + 2.220446049250313e-16); // 2^-52 + assert!((bits | 0xffff_ffff_ffff_f000).open_closed01_fixed() == 1.0); + } + + #[test] + fn closed_open11_fixed_edge_cases() { + // Test that the distribution is a half-open range over [0,1). + // These constants happen to generate the lowest and highest floats in + // the range. + let mut rng = ::test::rng(); + let mut bits: u32 = rng.sample(Uniform); + bits = bits & 0x1ff; // 9 bits with no influence + + assert!((bits | 0).closed_open11_fixed() == -1.0); + assert!((bits | 0xfffffe00).closed_open11_fixed() + == 0.99999976); // 1 - 2^-22 + + let mut bits: u64 = rng.sample(Uniform); + bits = bits & 0xfff; // 12 bits with no influence + + assert!((bits | 0).closed_open11_fixed() == -1.0); + assert!((bits | 0xffff_ffff_ffff_f000).closed_open11_fixed() == + 0.9999999999999996); // 1 - 2^-51 + } +} diff --git a/utils/ci/install.sh b/utils/ci/install.sh new file mode 100644 index 00000000000..80e18e47208 --- /dev/null +++ b/utils/ci/install.sh @@ -0,0 +1,47 @@ +set -ex + +main() { + local target= + if [ $TRAVIS_OS_NAME = linux ]; then + target=x86_64-unknown-linux-musl + sort=sort + else + target=x86_64-apple-darwin + sort=gsort # for `sort --sort-version`, from brew's coreutils. + fi + + # Builds for iOS are done on OSX, but require the specific target to be + # installed. + case $TARGET in + aarch64-apple-ios) + rustup target install aarch64-apple-ios + ;; + armv7-apple-ios) + rustup target install armv7-apple-ios + ;; + armv7s-apple-ios) + rustup target install armv7s-apple-ios + ;; + i386-apple-ios) + rustup target install i386-apple-ios + ;; + x86_64-apple-ios) + rustup target install x86_64-apple-ios + ;; + esac + + # This fetches latest stable release + local tag=$(git ls-remote --tags --refs --exit-code https://github.com/japaric/cross \ + | cut -d/ -f3 \ + | grep -E '^v[0.1.0-9.]+$' \ + | $sort --version-sort \ + | tail -n1) + curl -LSfs https://japaric.github.io/trust/install.sh | \ + sh -s -- \ + --force \ + --git japaric/cross \ + --tag $tag \ + --target $target +} + +main diff --git a/utils/ci/script.sh b/utils/ci/script.sh new file mode 100644 index 00000000000..c41788e168e --- /dev/null +++ b/utils/ci/script.sh @@ -0,0 +1,32 @@ +# This script takes care of testing your crate + +set -ex + +# TODO This is the "test phase", tweak it as you see fit +main() { + cross build --all --no-default-features --target $TARGET --release + if [ ! -z $DISABLE_STD ]; then + return + fi + + cross build --target $TARGET + if [ ! -z $NIGHTLY ]; then + cross doc --no-deps --features nightly + fi + + if [ ! -z $DISABLE_TESTS ]; then + return + fi + + cross test --all --target $TARGET + + if [ ! -z $NIGHTLY ]; then + cross test --all --features nightly --target $TARGET + cross test --all --benches --target $TARGET + fi +} + +# we don't run the "test phase" when doing deploys +if [ -z $TRAVIS_TAG ]; then + main +fi