diff --git a/.travis.yml b/.travis.yml index 92ff8ff4983..af57fd47bcd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,18 +12,18 @@ matrix: install: script: - cargo test --all --tests --no-default-features - - cargo test --features serde-1,log + - cargo test --features serde1,log - rust: stable os: osx install: script: - cargo test --all --tests --no-default-features - - cargo test --features serde-1,log + - cargo test --features serde1,log - rust: beta install: script: - cargo test --all --tests --no-default-features - - cargo test --tests --no-default-features --features=serde-1 + - cargo test --tests --no-default-features --features=serde1 - rust: nightly install: before_script: @@ -31,7 +31,7 @@ matrix: script: - cargo test --all --tests --no-default-features --features=alloc - cargo test --all --features=alloc - - cargo test --features serde-1,log,nightly + - cargo test --features serde1,log,nightly - cargo test --benches - cargo doc --no-deps --all --all-features - cargo --list | egrep "^\s*deadlinks$" -q || cargo install cargo-deadlinks diff --git a/CHANGELOG.md b/CHANGELOG.md index 12afe544d25..6a9a0a2d96a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,7 @@ You may also find the [Update Guide](UPDATING.md) useful. - Create a seperate `rand_core` crate. (#288) - Deprecate `rand_derive`. (#256) - Add `log` feature. Logging is now available in `JitterRng`, `OsRng`, `EntropyRng` and `ReseedingRng`. (#246) -- Add `serde-1` feature for some PRNGs. (#189) +- Add `serde1` feature for some PRNGs. (#189) - `stdweb` feature for `OsRng` support on WASM via stdweb. (#272, #336) ### `Rng` trait @@ -55,7 +55,7 @@ You may also find the [Update Guide](UPDATING.md) useful. - Change `thread_rng` reseeding threshold to 32 MiB. (#277) - PRNGs no longer implement `Copy`. (#209) - `Debug` implementations no longer show internals. (#209) -- Implement serialisation for `XorShiftRng`, `IsaacRng` and `Isaac64Rng` under the `serde-1` feature. (#189) +- Implement serialization for `XorShiftRng`, `IsaacRng` and `Isaac64Rng` under the `serde1` feature. (#189) - Implement `BlockRngCore` for `ChaChaCore` and `Hc128Core`. (#281) - All PRNGs are now portable across big- and little-endian architectures. (#209) - `Isaac64Rng::next_u32` no longer throws away half the results. (#209) diff --git a/Cargo.toml b/Cargo.toml index 479c2adb2fa..27b0aadc3d9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ nightly = ["i128_support"] # enables all features requiring nightly rust std = ["rand_core/std", "alloc", "libc", "winapi", "cloudabi", "fuchsia-zircon"] alloc = ["rand_core/alloc"] # enables Vec and Box support (without std) i128_support = [] # enables i128 and u128 support -serde-1 = ["serde", "serde_derive"] # enables serialisation for PRNGs +serde1 = ["serde", "serde_derive", "rand_core/serde1"] # enables serialization for PRNGs [workspace] members = ["rand_core"] diff --git a/README.md b/README.md index 6947494a261..1f75787e0ea 100644 --- a/README.md +++ b/README.md @@ -103,7 +103,7 @@ optional features are available: - `i128_support` enables support for generating `u128` and `i128` values - `log` enables some logging via the `log` crate - `nightly` enables all unstable features (`i128_support`) -- `serde-1` enables serialisation for some types, via Serde version 1 +- `serde1` enables serialization for some types, via Serde version 1 - `stdweb` enables support for `OsRng` on WASM via stdweb. - `std` enabled by default; by setting "default-features = false" `no_std` mode is activated; this removes features depending on `std` functionality: @@ -132,7 +132,7 @@ cargo test --tests --no-default-features cargo test --tests --no-default-features --features alloc # Test log and serde support -cargo test --features serde-1,log +cargo test --features serde1,log # Test 128-bit support (requires nightly) cargo test --all --features nightly diff --git a/UPDATING.md b/UPDATING.md index 2f3b0b3cc06..8a694b52400 100644 --- a/UPDATING.md +++ b/UPDATING.md @@ -27,7 +27,7 @@ deprecation of `Rand`. Several new Cargo feature flags have been added: - `alloc`, used without `std`, allows use of `Box` and `Vec` -- `serde-1` adds serialisation support to some PRNGs +- `serde1` adds serialization support to some PRNGs - `log` adds logging in a few places (primarily to `OsRng` and `JitterRng`) ### `Rng` and friends (core traits) diff --git a/appveyor.yml b/appveyor.yml index 823f5525e06..d9b613edaeb 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -34,6 +34,6 @@ build: false test_script: - cargo test --benches - cargo test --all - - cargo test --features serde-1,log,nightly + - cargo test --features serde1,log,nightly - cargo test --all --tests --no-default-features --features=alloc - - cargo test --tests --no-default-features --features=serde-1 + - cargo test --tests --no-default-features --features=serde1 diff --git a/rand_core/Cargo.toml b/rand_core/Cargo.toml index 59742bc1b2f..e75d927fe89 100644 --- a/rand_core/Cargo.toml +++ b/rand_core/Cargo.toml @@ -22,3 +22,8 @@ appveyor = { repository = "alexcrichton/rand" } # default = ["std"] std = ["alloc"] # use std library; should be default but for above bug alloc = [] # enables Vec and Box support without std +serde1 = ["serde", "serde_derive"] # enables serde for BlockRng wrapper + +[dependencies] +serde = { version = "1", optional = true } +serde_derive = { version = "1", optional = true } diff --git a/rand_core/src/impls.rs b/rand_core/src/impls.rs index 393d998667a..645dc8f990b 100644 --- a/rand_core/src/impls.rs +++ b/rand_core/src/impls.rs @@ -27,6 +27,8 @@ use core::cmp::min; use core::mem::size_of; use {RngCore, BlockRngCore, CryptoRng, SeedableRng, Error}; +#[cfg(feature="serde1")] use serde::{Serialize, Deserialize}; + /// 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. @@ -184,10 +186,14 @@ pub fn next_u64_via_fill(rng: &mut R) -> u64 { /// [`RngCore`]: ../RngCore.t.html /// [`SeedableRng`]: ../SeedableRng.t.html #[derive(Clone)] +#[cfg_attr(feature="serde1", derive(Serialize, Deserialize))] pub struct BlockRng { - pub results: R::Results, - pub index: usize, - pub core: R, + #[cfg_attr(feature="serde1", serde(bound( + serialize = "R::Results: Serialize", + deserialize = "R::Results: Deserialize<'de>")))] + results: R::Results, + index: usize, + core: R, } // Custom Debug implementation that does not expose the contents of `results`. @@ -201,6 +207,35 @@ impl fmt::Debug for BlockRng { } } +impl BlockRng { + /// Create a new `BlockRng` from an existing RNG implementing + /// `BlockRngCore`. Results will be generated on first use. + pub fn new(core: R) -> BlockRng{ + let results_empty = R::Results::default(); + BlockRng { + core, + index: results_empty.as_ref().len(), + results: results_empty, + } + } + + /// Return a reference the wrapped `BlockRngCore`. + pub fn inner(&self) -> &R { + &self.core + } + + /// Return a mutable reference the wrapped `BlockRngCore`. + pub fn inner_mut(&mut self) -> &mut R { + &mut self.core + } + + // Reset the number of available results. + // This will force a new set of results to be generated on next use. + pub fn reset(&mut self) { + self.index = self.results.as_ref().len(); + } +} + impl> RngCore for BlockRng where ::Results: AsRef<[u32]> { @@ -317,21 +352,190 @@ impl SeedableRng for BlockRng { type Seed = R::Seed; fn from_seed(seed: Self::Seed) -> Self { + Self::new(R::from_seed(seed)) + } + + fn from_rng(rng: S) -> Result { + Ok(Self::new(R::from_rng(rng)?)) + } +} + + + +/// Wrapper around PRNGs that implement [`BlockRngCore`] to keep a results +/// buffer and offer the methods from [`RngCore`]. +/// +/// This is similar to [`BlockRng`], but specialized for algorithms that operate +/// on `u64` values. +/// +/// [`BlockRngCore`]: ../BlockRngCore.t.html +/// [`RngCore`]: ../RngCore.t.html +/// [`BlockRng`]: struct.BlockRng.html +#[derive(Clone)] +#[cfg_attr(feature="serde1", derive(Serialize, Deserialize))] +pub struct BlockRng64 { + #[cfg_attr(feature="serde1", serde(bound( + serialize = "R::Results: Serialize", + deserialize = "R::Results: Deserialize<'de>")))] + results: R::Results, + index: usize, + half_used: bool, // true if only half of the previous result is used + core: R, +} + +// Custom Debug implementation that does not expose the contents of `results`. +impl fmt::Debug for BlockRng64 { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_struct("BlockRng64") + .field("core", &self.core) + .field("result_len", &self.results.as_ref().len()) + .field("index", &self.index) + .field("half_used", &self.half_used) + .finish() + } +} + +impl BlockRng64 { + /// Create a new `BlockRng` from an existing RNG implementing + /// `BlockRngCore`. Results will be generated on first use. + pub fn new(core: R) -> BlockRng64{ let results_empty = R::Results::default(); - Self { - core: R::from_seed(seed), - index: results_empty.as_ref().len(), // generate on first use + BlockRng64 { + core, + index: results_empty.as_ref().len(), + half_used: false, results: results_empty, } } + /// Return a mutable reference the wrapped `BlockRngCore`. + pub fn inner(&mut self) -> &mut R { + &mut self.core + } + + // Reset the number of available results. + // This will force a new set of results to be generated on next use. + pub fn reset(&mut self) { + self.index = self.results.as_ref().len(); + } +} + +impl> RngCore for BlockRng64 +where ::Results: AsRef<[u64]> +{ + #[inline(always)] + fn next_u32(&mut self) -> u32 { + let mut index = self.index * 2 - self.half_used as usize; + if index >= self.results.as_ref().len() * 2 { + self.core.generate(&mut self.results); + self.index = 0; + // `self.half_used` is by definition `false` + self.half_used = false; + index = 0; + } + + self.half_used = !self.half_used; + self.index += self.half_used as usize; + + // Index as if this is a u32 slice. + unsafe { + let results = + &*(self.results.as_ref() as *const [u64] as *const [u32]); + if cfg!(target_endian = "little") { + *results.get_unchecked(index) + } else { + *results.get_unchecked(index ^ 1) + } + } + } + + #[inline(always)] + fn next_u64(&mut self) -> u64 { + if self.index >= self.results.as_ref().len() { + self.core.generate(&mut self.results); + self.index = 0; + } + + let value = self.results.as_ref()[self.index]; + self.index += 1; + self.half_used = false; + value + } + + // As an optimization we try to write directly into the output buffer. + // This is only enabled for little-endian platforms where unaligned writes + // are known to be safe and fast. + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + fn fill_bytes(&mut self, dest: &mut [u8]) { + let mut filled = 0; + self.half_used = false; + + // Continue filling from the current set of results + if self.index < self.results.as_ref().len() { + let (consumed_u64, filled_u8) = + fill_via_u64_chunks(&self.results.as_ref()[self.index..], + dest); + + self.index += consumed_u64; + filled += filled_u8; + } + + let len_remainder = + (dest.len() - filled) % (self.results.as_ref().len() * 8); + let end_direct = dest.len() - len_remainder; + + while filled < end_direct { + let dest_u64: &mut R::Results = unsafe { + ::core::mem::transmute(dest[filled..].as_mut_ptr()) + }; + self.core.generate(dest_u64); + filled += self.results.as_ref().len() * 8; + } + self.index = self.results.as_ref().len(); + + if len_remainder > 0 { + self.core.generate(&mut self.results); + let (consumed_u64, _) = + fill_via_u64_chunks(&mut self.results.as_ref(), + &mut dest[filled..]); + + self.index = consumed_u64; + } + } + + #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] + fn fill_bytes(&mut self, dest: &mut [u8]) { + let mut read_len = 0; + self.half_used = false; + while read_len < dest.len() { + if self.index as usize >= self.results.as_ref().len() { + self.core.generate(&mut self.results); + self.index = 0; + } + + let (consumed_u64, filled_u8) = + fill_via_u64_chunks(&self.results.as_ref()[self.index as usize..], + &mut dest[read_len..]); + + self.index += consumed_u64; + read_len += filled_u8; + } + } + + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { + Ok(self.fill_bytes(dest)) + } +} + +impl SeedableRng for BlockRng64 { + type Seed = R::Seed; + + fn from_seed(seed: Self::Seed) -> Self { + Self::new(R::from_seed(seed)) + } + fn from_rng(rng: S) -> Result { - let results_empty = R::Results::default(); - Ok(Self { - core: R::from_rng(rng)?, - index: results_empty.as_ref().len(), // generate on first use - results: results_empty, - }) + Ok(Self::new(R::from_rng(rng)?)) } } diff --git a/rand_core/src/lib.rs b/rand_core/src/lib.rs index 5eb1e174b20..483bc4bbf73 100644 --- a/rand_core/src/lib.rs +++ b/rand_core/src/lib.rs @@ -44,6 +44,8 @@ #[cfg(feature="std")] extern crate core; #[cfg(all(feature = "alloc", not(feature="std")))] extern crate alloc; +#[cfg(feature="serde1")] extern crate serde; +#[cfg(feature="serde1")] #[macro_use] extern crate serde_derive; use core::default::Default; diff --git a/src/lib.rs b/src/lib.rs index 76ef632ff76..2bccf0d4ab1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -195,9 +195,9 @@ #[cfg(feature="std")] extern crate std as core; #[cfg(all(feature = "alloc", not(feature="std")))] extern crate alloc; -#[cfg(test)] #[cfg(feature="serde-1")] extern crate bincode; -#[cfg(feature="serde-1")] extern crate serde; -#[cfg(feature="serde-1")] #[macro_use] extern crate serde_derive; +#[cfg(test)] #[cfg(feature="serde1")] extern crate bincode; +#[cfg(feature="serde1")] extern crate serde; +#[cfg(feature="serde1")] #[macro_use] extern crate serde_derive; #[cfg(all(target_arch = "wasm32", feature = "stdweb"))] #[macro_use] diff --git a/src/prng/chacha.rs b/src/prng/chacha.rs index cf3926b5e4d..7d1748c91c5 100644 --- a/src/prng/chacha.rs +++ b/src/prng/chacha.rs @@ -156,8 +156,8 @@ impl ChaChaRng { /// assert_eq!(rng1.next_u32(), rng2.next_u32()); /// ``` pub fn set_counter(&mut self, counter_low: u64, counter_high: u64) { - self.0.core.set_counter(counter_low, counter_high); - self.0.index = STATE_WORDS; // force recomputation on next use + self.0.inner_mut().set_counter(counter_low, counter_high); + self.0.reset(); // force recomputation on next use } /// Sets the number of rounds to run the ChaCha core algorithm per block to @@ -179,8 +179,8 @@ impl ChaChaRng { /// assert_eq!(rng.next_u32(), 0x2fef003e); /// ``` pub fn set_rounds(&mut self, rounds: usize) { - self.0.core.set_rounds(rounds); - self.0.index = STATE_WORDS; // force recomputation on next use + self.0.inner_mut().set_rounds(rounds); + self.0.reset(); // force recomputation on next use } } diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index 6193277db15..5701650890b 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -12,8 +12,9 @@ use core::{fmt, slice}; use core::num::Wrapping as w; - -use rand_core::{RngCore, SeedableRng, Error, impls, le}; +use rand_core::{BlockRngCore, RngCore, SeedableRng, Error, le}; +use rand_core::impls::BlockRng; +use prng::isaac_array::IsaacArray; #[allow(non_camel_case_types)] type w32 = w; @@ -84,38 +85,40 @@ const RAND_SIZE: usize = 1 << RAND_SIZE_LEN; /// [3]: Jean-Philippe Aumasson, [*On the pseudo-random generator ISAAC*]( /// https://eprint.iacr.org/2006/438) /// -/// [`Hc128Rng`]: hc128/struct.Hc128Rng.html -#[cfg_attr(feature="serde-1", derive(Serialize,Deserialize))] -pub struct IsaacRng { - #[cfg_attr(feature="serde-1",serde(with="super::isaac_serde::rand_size_serde"))] - rsl: [u32; RAND_SIZE], - #[cfg_attr(feature="serde-1",serde(with="super::isaac_serde::rand_size_serde"))] - mem: [w32; RAND_SIZE], - a: w32, - b: w32, - c: w32, - index: u32, -} +/// [`Hc128Rng`]: ../hc128/struct.Hc128Rng.html +#[derive(Clone, Debug)] +#[cfg_attr(feature="serde1", derive(Serialize, Deserialize))] +pub struct IsaacRng(BlockRng); -// 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 RngCore for IsaacRng { + #[inline(always)] + fn next_u32(&mut self) -> u32 { + self.0.next_u32() + } + + #[inline(always)] + fn next_u64(&mut self) -> u64 { + self.0.next_u64() + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + self.0.fill_bytes(dest) + } + + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { + self.0.try_fill_bytes(dest) } } -// 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 SeedableRng for IsaacRng { + type Seed = ::Seed; + + fn from_seed(seed: Self::Seed) -> Self { + IsaacRng(BlockRng::::from_seed(seed)) + } + + fn from_rng(rng: S) -> Result { + BlockRng::::from_rng(rng).map(|rng| IsaacRng(rng)) } } @@ -125,27 +128,41 @@ impl IsaacRng { /// /// DEPRECATED. `IsaacRng::new_from_u64(0)` will produce identical results. #[deprecated(since="0.5.0", note="use the NewRng or SeedableRng trait")] - pub fn new_unseeded() -> IsaacRng { + pub fn new_unseeded() -> Self { Self::new_from_u64(0) } - /// Creates an ISAAC random number generator using an u64 as seed. + /// Create 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) + pub fn new_from_u64(seed: u64) -> Self { + IsaacRng(BlockRng::new(IsaacCore::new_from_u64(seed))) } +} - /// Refills the output buffer (`self.rsl`) +/// The core of `IsaacRng`, used with `BlockRng`. +#[derive(Clone)] +#[cfg_attr(feature="serde1", derive(Serialize, Deserialize))] +pub struct IsaacCore { + #[cfg_attr(feature="serde1",serde(with="super::isaac_array::isaac_array_serde"))] + mem: [w32; RAND_SIZE], + a: w32, + b: w32, + c: w32, +} + +// Custom Debug implementation that does not expose the internal state +impl fmt::Debug for IsaacCore { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "IsaacCore {{}}") + } +} + +impl BlockRngCore for IsaacCore { + type Item = u32; + type Results = IsaacArray; + + /// Refills the output buffer (`results`) /// See also the pseudocode desciption of the algorithm at the top of this /// file. /// @@ -160,10 +177,11 @@ impl IsaacRng { /// - 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) { + /// - We fill `results` backwards. The reference implementation reads values + /// from `results` in reverse. We read them in the normal direction, to + /// make `fill_bytes` a memcopy. To maintain compatibility we fill in + /// reverse. + fn generate(&mut self, results: &mut IsaacArray) { self.c += w(1); // abbreviations let mut a = self.a; @@ -177,180 +195,146 @@ impl IsaacRng { } #[inline] - fn rngstep(ctx: &mut IsaacRng, + fn rngstep(mem: &mut [w32; RAND_SIZE], + results: &mut [u32; RAND_SIZE], 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; + let x = mem[base + m]; + *a = mix + mem[base + m2]; + let y = *a + *b + ind(&mem, x, 2); + mem[base + m] = y; + *b = x + ind(&mem, y, 2 + RAND_SIZE_LEN); + results[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 << 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); + rngstep(&mut self.mem, results, a ^ (a << 13), &mut a, &mut b, i + 0, m, m2); + rngstep(&mut self.mem, results, a ^ (a >> 6 ), &mut a, &mut b, i + 1, m, m2); + rngstep(&mut self.mem, results, a ^ (a << 2 ), &mut a, &mut b, i + 2, m, m2); + rngstep(&mut self.mem, results, a ^ (a >> 16), &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 << 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); + rngstep(&mut self.mem, results, a ^ (a << 13), &mut a, &mut b, i + 0, m, m2); + rngstep(&mut self.mem, results, a ^ (a >> 6 ), &mut a, &mut b, i + 1, m, m2); + rngstep(&mut self.mem, results, a ^ (a << 2 ), &mut a, &mut b, i + 2, m, m2); + rngstep(&mut self.mem, results, a ^ (a >> 16), &mut a, &mut b, i + 3, m, m2); } self.a = a; self.b = b; - self.index = 0; } } -impl RngCore for IsaacRng { +impl IsaacCore { + /// Create a new ISAAC random number generator. + /// + /// The author Bob Jenkins describes how to best initialize ISAAC here: + /// + /// 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 next_u32(&mut self) -> u32 { - // 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; + fn init(mut mem: [w32; RAND_SIZE], rounds: u32) -> Self { + 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 value = self.rsl[index]; - self.index += 1; - value - } - - #[inline] - fn next_u64(&mut self) -> u64 { - impls::next_u64_via_u32(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(); + // 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 (consumed_u32, filled_u8) = - impls::fill_via_u32_chunks(&self.rsl[(self.index as usize)..], - &mut dest[read_len..]); - - self.index += consumed_u32 as u32; - read_len += filled_u8; } - } - fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { - Ok(self.fill_bytes(dest)) + Self { mem, a: w(0), b: w(0), c: w(0) } } -} -/// Creates a new ISAAC random number generator. -/// -/// The author Bob Jenkins describes how to best initialize ISAAC here: -/// -/// 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; - } + /// Create 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. + fn new_from_u64(seed: u64) -> Self { + 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. + Self::init(key, 1) } - - let mut rng = IsaacRng { - rsl: [0; RAND_SIZE], - mem, - a: w(0), - b: w(0), - c: w(0), - index: 0, - }; - - // Prepare the first set of results - rng.isaac(); - rng } -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; -} - -impl SeedableRng for IsaacRng { +impl SeedableRng for IsaacCore { type Seed = [u8; 32]; fn from_seed(seed: Self::Seed) -> Self { let mut seed_u32 = [0u32; 8]; le::read_u32_into(&seed, &mut seed_u32); + // Convert the seed to `Wrapping` and zero-extend to `RAND_SIZE`. 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) + Self::init(seed_extended, 2) } fn from_rng(mut rng: R) -> Result { @@ -367,7 +351,7 @@ impl SeedableRng for IsaacRng { *i = w(i.0.to_le()); } - Ok(init(seed, 2)) + Ok(Self::init(seed, 2)) } } @@ -472,7 +456,7 @@ mod test { } #[test] - #[cfg(all(feature="serde-1", feature="std"))] + #[cfg(all(feature="serde1", feature="std"))] fn test_isaac_serde() { use bincode; use std::io::{BufWriter, BufReader}; @@ -489,20 +473,8 @@ mod test { let mut read = BufReader::new(&buf[..]); let mut deserialized: IsaacRng = bincode::deserialize_from(&mut read).expect("Could not deserialize"); - assert_eq!(rng.index, deserialized.index); - /* Can't assert directly because of the array size */ - for (orig,deser) in rng.rsl.iter().zip(deserialized.rsl.iter()) { - assert_eq!(orig, deser); - } - for (orig,deser) in rng.mem.iter().zip(deserialized.mem.iter()) { - assert_eq!(orig, deser); - } - assert_eq!(rng.a, deserialized.a); - assert_eq!(rng.b, deserialized.b); - assert_eq!(rng.c, deserialized.c); - - for _ in 0..16 { - assert_eq!(rng.next_u64(), deserialized.next_u64()); + for _ in 0..300 { // more than the 256 buffered results + assert_eq!(rng.next_u32(), deserialized.next_u32()); } } } diff --git a/src/prng/isaac64.rs b/src/prng/isaac64.rs index 65dedac3894..05468fc2d84 100644 --- a/src/prng/isaac64.rs +++ b/src/prng/isaac64.rs @@ -12,8 +12,9 @@ use core::{fmt, slice}; use core::num::Wrapping as w; - -use rand_core::{RngCore, SeedableRng, Error, impls, le}; +use rand_core::{BlockRngCore, RngCore, SeedableRng, Error, le}; +use rand_core::impls::BlockRng64; +use prng::isaac_array::IsaacArray; #[allow(non_camel_case_types)] type w64 = w; @@ -74,40 +75,40 @@ const RAND_SIZE: usize = 1 << RAND_SIZE_LEN; /// http://burtleburtle.net/bob/rand/isaac.html) /// /// [`IsaacRng`]: ../isaac/struct.IsaacRng.html -/// [`Hc128Rng`]: hc128/struct.Hc128Rng.html -#[cfg_attr(feature="serde-1", derive(Serialize,Deserialize))] -pub struct Isaac64Rng { - #[cfg_attr(feature="serde-1",serde(with="super::isaac_serde::rand_size_serde"))] - rsl: [u64; RAND_SIZE], - #[cfg_attr(feature="serde-1",serde(with="super::isaac_serde::rand_size_serde"))] - 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 -} +/// [`Hc128Rng`]: ../hc128/struct.Hc128Rng.html +#[derive(Clone, Debug)] +#[cfg_attr(feature="serde1", derive(Serialize, Deserialize))] +pub struct Isaac64Rng(BlockRng64); -// 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, - } +impl RngCore for Isaac64Rng { + #[inline(always)] + fn next_u32(&mut self) -> u32 { + self.0.next_u32() + } + + #[inline(always)] + fn next_u64(&mut self) -> u64 { + self.0.next_u64() + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + self.0.fill_bytes(dest) + } + + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { + self.0.try_fill_bytes(dest) } } -// 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 SeedableRng for Isaac64Rng { + type Seed = ::Seed; + + fn from_seed(seed: Self::Seed) -> Self { + Isaac64Rng(BlockRng64::::from_seed(seed)) + } + + fn from_rng(rng: S) -> Result { + BlockRng64::::from_rng(rng).map(|rng| Isaac64Rng(rng)) } } @@ -117,26 +118,41 @@ impl Isaac64Rng { /// /// DEPRECATED. `Isaac64Rng::new_from_u64(0)` will produce identical results. #[deprecated(since="0.5.0", note="use the NewRng or SeedableRng trait")] - pub fn new_unseeded() -> Isaac64Rng { + pub fn new_unseeded() -> Self { Self::new_from_u64(0) } - /// Creates an ISAAC-64 random number generator using an u64 as seed. + /// Create 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) + pub fn new_from_u64(seed: u64) -> Self { + Isaac64Rng(BlockRng64::new(Isaac64Core::new_from_u64(seed))) } +} - /// Refills the output buffer (`self.rsl`) +/// The core of `Isaac64Rng`, used with `BlockRng`. +#[derive(Clone)] +#[cfg_attr(feature="serde1", derive(Serialize, Deserialize))] +pub struct Isaac64Core { + #[cfg_attr(feature="serde1",serde(with="super::isaac_array::isaac_array_serde"))] + mem: [w64; RAND_SIZE], + a: w64, + b: w64, + c: w64, +} + +// Custom Debug implementation that does not expose the internal state +impl fmt::Debug for Isaac64Core { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Isaac64Core {{}}") + } +} + +impl BlockRngCore for Isaac64Core { + type Item = u64; + type Results = IsaacArray; + + /// Refills the output buffer (`results`) /// See also the pseudocode desciption of the algorithm at the top of this /// file. /// @@ -151,10 +167,11 @@ impl Isaac64Rng { /// - 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) { + /// - We fill `results` backwards. The reference implementation reads values + /// from `results` in reverse. We read them in the normal direction, to + /// make `fill_bytes` a memcopy. To maintain compatibility we fill in + /// reverse. + fn generate(&mut self, results: &mut IsaacArray) { self.c += w(1); // abbreviations let mut a = self.a; @@ -168,171 +185,120 @@ impl Isaac64Rng { } #[inline] - fn rngstep(ctx: &mut Isaac64Rng, + fn rngstep(mem: &mut [w64; RAND_SIZE], + results: &mut [u64; RAND_SIZE], 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 x = mem[base + m]; + *a = mix + mem[base + m2]; + let y = *a + *b + ind(&mem, x, 3); + mem[base + m] = y; + *b = x + ind(&mem, y, 3 + RAND_SIZE_LEN); + results[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); + rngstep(&mut self.mem, results, !(a ^ (a << 21)), &mut a, &mut b, i + 0, m, m2); + rngstep(&mut self.mem, results, a ^ (a >> 5 ), &mut a, &mut b, i + 1, m, m2); + rngstep(&mut self.mem, results, a ^ (a << 12), &mut a, &mut b, i + 2, m, m2); + rngstep(&mut self.mem, results, 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); + rngstep(&mut self.mem, results, !(a ^ (a << 21)), &mut a, &mut b, i + 0, m, m2); + rngstep(&mut self.mem, results, a ^ (a >> 5 ), &mut a, &mut b, i + 1, m, m2); + rngstep(&mut self.mem, results, a ^ (a << 12), &mut a, &mut b, i + 2, m, m2); + rngstep(&mut self.mem, results, a ^ (a >> 33), &mut a, &mut b, i + 3, m, m2); } self.a = a; self.b = b; - self.index = 0; - self.half_used = false; } } -impl RngCore for Isaac64Rng { - #[inline] - fn next_u32(&mut self) -> 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] +impl Isaac64Core { + /// Create a new ISAAC-64 random number generator. + fn init(mut mem: [w64; RAND_SIZE], rounds: u32) -> Self { + 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; } - } - #[inline] - fn next_u64(&mut self) -> u64 { - let mut index = self.index as usize; - if index >= RAND_SIZE { - self.isaac64(); - index = 0; - } - - let value = self.rsl[index]; - self.index += 1; - self.half_used = false; - value - } - - 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(); + // 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; } - - let (consumed_u64, filled_u8) = - impls::fill_via_u64_chunks(&self.rsl[self.index as usize..], - &mut dest[read_len..]); - - self.index += consumed_u64 as u32; - read_len += filled_u8; } - } - fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { - Ok(self.fill_bytes(dest)) - } -} - -/// 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; - } + Self { mem, a: w(0), b: w(0), c: w(0) } } - Isaac64Rng { - rsl: [0; RAND_SIZE], - mem, - a: w(0), - b: w(0), - c: w(0), - index: RAND_SIZE as u32, // generate on first use - half_used: false, + /// Create 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) -> Self { + 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. + Self::init(key, 1) } } -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 { +impl SeedableRng for Isaac64Core { 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); + // Convert the seed to `Wrapping` and zero-extend to `RAND_SIZE`. 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) + Self::init(seed_extended, 2) } fn from_rng(mut rng: R) -> Result { @@ -341,7 +307,6 @@ impl SeedableRng for Isaac64Rng { 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); rng.try_fill_bytes(slice)?; } @@ -349,7 +314,7 @@ impl SeedableRng for Isaac64Rng { *i = w(i.0.to_le()); } - Ok(init(seed, 2)) + Ok(Self::init(seed, 2)) } } @@ -483,7 +448,7 @@ mod test { } #[test] - #[cfg(all(feature="serde-1", feature="std"))] + #[cfg(all(feature="serde1", feature="std"))] fn test_isaac64_serde() { use bincode; use std::io::{BufWriter, BufReader}; @@ -500,20 +465,7 @@ mod test { let mut read = BufReader::new(&buf[..]); let mut deserialized: Isaac64Rng = bincode::deserialize_from(&mut read).expect("Could not deserialize"); - assert_eq!(rng.index, deserialized.index); - assert_eq!(rng.half_used, deserialized.half_used); - /* Can't assert directly because of the array size */ - for (orig,deser) in rng.rsl.iter().zip(deserialized.rsl.iter()) { - assert_eq!(orig, deser); - } - for (orig,deser) in rng.mem.iter().zip(deserialized.mem.iter()) { - assert_eq!(orig, deser); - } - assert_eq!(rng.a, deserialized.a); - assert_eq!(rng.b, deserialized.b); - assert_eq!(rng.c, deserialized.c); - - for _ in 0..16 { + for _ in 0..300 { // more than the 256 buffered results assert_eq!(rng.next_u64(), deserialized.next_u64()); } } diff --git a/src/prng/isaac_serde.rs b/src/prng/isaac_array.rs similarity index 59% rename from src/prng/isaac_serde.rs rename to src/prng/isaac_array.rs index 459b9287fd6..327cfbf5bcf 100644 --- a/src/prng/isaac_serde.rs +++ b/src/prng/isaac_array.rs @@ -8,9 +8,60 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! ISAAC serde helper functions. +//! ISAAC helper functions for 256-element arrays. -pub(super) mod rand_size_serde { +// Terrible workaround because arrays with more than 32 elements do not +// implement `AsRef`, `Default`, `Serialize`, `Deserialize`, or any other +// traits for that matter. + +#[cfg(feature="serde1")] use serde::{Serialize, Deserialize}; + +const RAND_SIZE_LEN: usize = 8; +const RAND_SIZE: usize = 1 << RAND_SIZE_LEN; + + +#[derive(Copy, Clone)] +#[allow(missing_debug_implementations)] +#[cfg_attr(feature="serde1", derive(Serialize, Deserialize))] +pub struct IsaacArray { + #[cfg_attr(feature="serde1",serde(with="isaac_array_serde"))] + #[cfg_attr(feature="serde1", serde(bound( + serialize = "T: Serialize", + deserialize = "T: Deserialize<'de> + Copy + Default")))] + inner: [T; RAND_SIZE] +} + +impl ::core::convert::AsRef<[T]> for IsaacArray { + #[inline(always)] + fn as_ref(&self) -> &[T] { + &self.inner[..] + } +} + +impl ::core::ops::Deref for IsaacArray { + type Target = [T; RAND_SIZE]; + #[inline(always)] + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl ::core::ops::DerefMut for IsaacArray { + #[inline(always)] + fn deref_mut(&mut self) -> &mut [T; RAND_SIZE] { + &mut self.inner + } +} + +impl ::core::default::Default for IsaacArray where T: Copy + Default { + fn default() -> IsaacArray { + IsaacArray { inner: [T::default(); RAND_SIZE] } + } +} + + +#[cfg(feature="serde1")] +pub(super) mod isaac_array_serde { const RAND_SIZE_LEN: usize = 8; const RAND_SIZE: usize = 1 << RAND_SIZE_LEN; @@ -20,10 +71,10 @@ pub(super) mod rand_size_serde { use core::fmt; - pub fn serialize(arr: &[T;RAND_SIZE], ser: S) -> Result + pub fn serialize(arr: &[T;RAND_SIZE], ser: S) -> Result where T: Serialize, - S: Serializer + S: Serializer { use serde::ser::SerializeTuple; diff --git a/src/prng/mod.rs b/src/prng/mod.rs index edf8f5dffb2..c4bd0032052 100644 --- a/src/prng/mod.rs +++ b/src/prng/mod.rs @@ -42,15 +42,14 @@ pub mod chacha; pub mod hc128; -mod isaac; -mod isaac64; +pub mod isaac; +pub mod isaac64; mod xorshift; -#[cfg(feature="serde-1")] -mod isaac_serde; +mod isaac_array; -#[doc(inline)] pub use self::chacha::ChaChaRng; -#[doc(inline)] pub use self::hc128::Hc128Rng; +pub use self::chacha::ChaChaRng; +pub use self::hc128::Hc128Rng; pub use self::isaac::IsaacRng; pub use self::isaac64::Isaac64Rng; pub use self::xorshift::XorShiftRng; diff --git a/src/prng/xorshift.rs b/src/prng/xorshift.rs index 181fbf21363..9f7a3c88bb5 100644 --- a/src/prng/xorshift.rs +++ b/src/prng/xorshift.rs @@ -25,7 +25,7 @@ use rand_core::{RngCore, SeedableRng, Error, impls, le}; /// RNGs"](https://www.jstatsoft.org/v08/i14/paper). *Journal of /// Statistical Software*. Vol. 8 (Issue 14). #[derive(Clone)] -#[cfg_attr(feature="serde-1", derive(Serialize,Deserialize))] +#[cfg_attr(feature="serde1", derive(Serialize,Deserialize))] pub struct XorShiftRng { x: w, y: w, @@ -195,7 +195,7 @@ mod tests { } } - #[cfg(all(feature="serde-1", feature="std"))] + #[cfg(all(feature="serde1", feature="std"))] #[test] fn test_xorshift_serde() { use bincode; diff --git a/src/reseeding.rs b/src/reseeding.rs index af8bcba8f26..263a6dca096 100644 --- a/src/reseeding.rs +++ b/src/reseeding.rs @@ -68,28 +68,13 @@ where R: BlockRngCore + SeedableRng, /// * `rng`: the random number generator to use. /// * `threshold`: the number of generated bytes after which to reseed the RNG. /// * `reseeder`: the RNG to use for reseeding. - pub fn new(rng: R, threshold: u64, reseeder: Rsdr) - -> ReseedingRng - { - assert!(threshold <= ::core::i64::MAX as u64); - let results_empty = R::Results::default(); - ReseedingRng( - BlockRng { - core: ReseedingCore { - inner: rng, - reseeder, - threshold: threshold as i64, - bytes_until_reseed: threshold as i64, - }, - index: results_empty.as_ref().len(), // generate on first use - results: results_empty, - } - ) + pub fn new(rng: R, threshold: u64, reseeder: Rsdr) -> Self { + ReseedingRng(BlockRng::new(ReseedingCore::new(rng, threshold, reseeder))) } /// Reseed the internal PRNG. pub fn reseed(&mut self) -> Result<(), Error> { - self.0.core.reseed() + self.0.inner_mut().reseed() } } @@ -154,6 +139,23 @@ impl ReseedingCore where R: BlockRngCore + SeedableRng, Rsdr: RngCore { + /// Create a new `ReseedingCore` with the given parameters. + /// + /// # Arguments + /// + /// * `rng`: the random number generator to use. + /// * `threshold`: the number of generated bytes after which to reseed the RNG. + /// * `reseeder`: the RNG to use for reseeding. + pub fn new(rng: R, threshold: u64, reseeder: Rsdr) -> Self { + assert!(threshold <= ::core::i64::MAX as u64); + ReseedingCore { + inner: rng, + reseeder, + threshold: threshold as i64, + bytes_until_reseed: threshold as i64, + } + } + /// Reseed the internal PRNG. fn reseed(&mut self) -> Result<(), Error> { R::from_rng(&mut self.reseeder).map(|result| { diff --git a/utils/ci/script.sh b/utils/ci/script.sh index 26d694b1c4e..a34dc5f00b5 100644 --- a/utils/ci/script.sh +++ b/utils/ci/script.sh @@ -6,18 +6,18 @@ main() { if [ ! -z $DISABLE_TESTS ]; then cross build --all --no-default-features --target $TARGET --release if [ -z $DISABLE_STD ]; then - cross build --features log,serde-1 --target $TARGET + cross build --features log,serde1 --target $TARGET fi return fi if [ ! -z $NIGHTLY ]; then cross test --all --tests --no-default-features --features alloc --target $TARGET - cross test --features serde-1,log,nightly --target $TARGET + cross test --features serde1,log,nightly --target $TARGET cross test --all --benches --target $TARGET else cross test --all --tests --no-default-features --target $TARGET - cross test --features serde-1,log --target $TARGET + cross test --features serde1,log --target $TARGET fi }