From 602ff6f9bb7e3ca10820043637202b71980cf078 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Thu, 22 Mar 2018 12:08:28 +0100 Subject: [PATCH 1/9] Implement BlockRngCore for ISAAC --- src/prng/isaac.rs | 356 +++++++++++++++++++++++----------------------- 1 file changed, 180 insertions(+), 176 deletions(-) diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index 6193277db15..56029cbf999 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -12,8 +12,8 @@ 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; #[allow(non_camel_case_types)] type w32 = w; @@ -85,37 +85,38 @@ const RAND_SIZE: usize = 1 << RAND_SIZE_LEN; /// 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, -} +#[derive(Clone, Debug)] +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)) } } @@ -129,23 +130,75 @@ impl IsaacRng { 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) + IsaacRng(BlockRng { + // 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. + core: IsaacCore::init(key, 1), + results: IsaacArray([0; RAND_SIZE]), + index: RAND_SIZE, // generate on first use + }) + } +} + +#[derive(Clone)] +pub struct IsaacCore { + 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 {{}}") } +} - /// Refills the output buffer (`self.rsl`) +// Terrible workaround because arrays with more than 32 elements do not +// implement `AsRef` (or any other traits for that matter) +#[derive(Copy, Clone)] +#[allow(missing_debug_implementations)] +pub struct IsaacArray([u32; RAND_SIZE]); +impl ::core::convert::AsRef<[u32]> for IsaacArray { + #[inline(always)] + fn as_ref(&self) -> &[u32] { + &self.0[..] + } +} +impl ::core::ops::Deref for IsaacArray { + type Target = [u32; RAND_SIZE]; + #[inline(always)] + fn deref(&self) -> &Self::Target { + &self.0 + } +} +impl ::core::ops::DerefMut for IsaacArray { + #[inline(always)] + fn deref_mut(&mut self) -> &mut [u32; RAND_SIZE] { + &mut self.0 + } +} +impl ::core::default::Default for IsaacArray { + fn default() -> IsaacArray { IsaacArray([0u32; RAND_SIZE]) } +} + +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 +213,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 +231,130 @@ 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)) - } -} -/// 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; - } + Self { mem, a: w(0), b: w(0), c: w(0) } } - - 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 +371,7 @@ impl SeedableRng for IsaacRng { *i = w(i.0.to_le()); } - Ok(init(seed, 2)) + Ok(Self::init(seed, 2)) } } From dc001927ce523317d7d7f116848f9bc14098cddd Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Thu, 22 Mar 2018 12:41:05 +0100 Subject: [PATCH 2/9] Add BlockRng64 wrapper --- rand_core/src/impls.rs | 161 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 161 insertions(+) diff --git a/rand_core/src/impls.rs b/rand_core/src/impls.rs index 393d998667a..43c7aab0943 100644 --- a/rand_core/src/impls.rs +++ b/rand_core/src/impls.rs @@ -335,6 +335,167 @@ impl SeedableRng for BlockRng { } } + + +/// 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)] +pub struct BlockRng64 { + pub results: R::Results, + pub index: usize, + pub half_used: bool, // true if only half of the previous result is used + pub 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> 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; + + // 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; + while read_len < dest.len() { + if self.index as usize >= self.results.as_ref().len() { + self.core.generate(&mut self.results); + self.index = 0; + self.half_used = false; + } + + 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 { + let results_empty = R::Results::default(); + Self { + core: R::from_seed(seed), + index: results_empty.as_ref().len(), // generate on first use + half_used: false, + results: results_empty, + } + } + + 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 + half_used: false, + results: results_empty, + }) + } +} + impl CryptoRng for BlockRng {} // TODO: implement tests for the above From 84f6ae9a32706eeb7039599ab2df33f105a443f5 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Thu, 22 Mar 2018 15:36:26 +0100 Subject: [PATCH 3/9] Implement BlockRngCore for ISAAC-64 --- src/prng/isaac64.rs | 328 +++++++++++++++++++++----------------------- 1 file changed, 157 insertions(+), 171 deletions(-) diff --git a/src/prng/isaac64.rs b/src/prng/isaac64.rs index 65dedac3894..02576f2c0f8 100644 --- a/src/prng/isaac64.rs +++ b/src/prng/isaac64.rs @@ -12,8 +12,8 @@ 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; #[allow(non_camel_case_types)] type w64 = w; @@ -75,39 +75,38 @@ const RAND_SIZE: usize = 1 << RAND_SIZE_LEN; /// /// [`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 -} +#[derive(Clone, Debug)] +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)) } } @@ -121,22 +120,75 @@ impl Isaac64Rng { 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) + Isaac64Rng(BlockRng64 { + // 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. + core: Isaac64Core::init(key, 1), + results: Isaac64Array([0; RAND_SIZE]), + index: RAND_SIZE, // generate on first use + half_used: false, + }) + } +} + +#[derive(Clone)] +pub struct Isaac64Core { + 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 {{}}") } +} - /// Refills the output buffer (`self.rsl`) +// Terrible workaround because arrays with more than 32 elements do not +// implement `AsRef` (or any other traits for that matter) +#[derive(Copy, Clone)] +#[allow(missing_debug_implementations)] +pub struct Isaac64Array([u64; RAND_SIZE]); +impl ::core::convert::AsRef<[u64]> for Isaac64Array { + #[inline(always)] + fn as_ref(&self) -> &[u64] { + &self.0[..] + } +} +impl ::core::ops::Deref for Isaac64Array { + type Target = [u64; RAND_SIZE]; + #[inline(always)] + fn deref(&self) -> &Self::Target { + &self.0 + } +} +impl ::core::ops::DerefMut for Isaac64Array { + #[inline(always)] + fn deref_mut(&mut self) -> &mut [u64; RAND_SIZE] { + &mut self.0 + } +} +impl ::core::default::Default for Isaac64Array { + fn default() -> Isaac64Array { Isaac64Array([0u64; RAND_SIZE]) } +} + +impl BlockRngCore for Isaac64Core { + type Item = u64; + type Results = Isaac64Array; + + /// Refills the output buffer (`results`) /// See also the pseudocode desciption of the algorithm at the top of this /// file. /// @@ -151,10 +203,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 Isaac64Array) { self.c += w(1); // abbreviations let mut a = self.a; @@ -168,171 +221,105 @@ 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] - } - } - - #[inline] - fn next_u64(&mut self) -> u64 { - let mut index = self.index as usize; - if index >= RAND_SIZE { - self.isaac64(); - index = 0; +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; } - 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; } - } - 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, + Self { mem, a: w(0), b: w(0), c: w(0) } } } -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 +328,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 +335,7 @@ impl SeedableRng for Isaac64Rng { *i = w(i.0.to_le()); } - Ok(init(seed, 2)) + Ok(Self::init(seed, 2)) } } From 610e975fa9bbcadcda401446b38821ab2ea13c21 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Sat, 24 Mar 2018 07:39:41 +0100 Subject: [PATCH 4/9] Add serde to rand_core::impl::{BlockRng, BlockRng64} --- Cargo.toml | 2 +- rand_core/Cargo.toml | 5 +++++ rand_core/src/impls.rs | 10 ++++++++++ rand_core/src/lib.rs | 2 ++ 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 479c2adb2fa..97adda71608 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 +serde-1 = ["serde", "serde_derive", "rand_core/serde-1"] # enables serialization for PRNGs [workspace] members = ["rand_core"] diff --git a/rand_core/Cargo.toml b/rand_core/Cargo.toml index 59742bc1b2f..dda81bcfa3c 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 +serde-1 = ["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 43c7aab0943..c02fb618b17 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="serde-1")] 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,7 +186,11 @@ pub fn next_u64_via_fill(rng: &mut R) -> u64 { /// [`RngCore`]: ../RngCore.t.html /// [`SeedableRng`]: ../SeedableRng.t.html #[derive(Clone)] +#[cfg_attr(feature="serde-1", derive(Serialize, Deserialize))] pub struct BlockRng { + #[cfg_attr(feature="serde-1", serde(bound( + serialize = "R::Results: Serialize", + deserialize = "R::Results: Deserialize<'de>")))] pub results: R::Results, pub index: usize, pub core: R, @@ -347,7 +353,11 @@ impl SeedableRng for BlockRng { /// [`RngCore`]: ../RngCore.t.html /// [`BlockRng`]: struct.BlockRng.html #[derive(Clone)] +#[cfg_attr(feature="serde-1", derive(Serialize, Deserialize))] pub struct BlockRng64 { + #[cfg_attr(feature="serde-1", serde(bound( + serialize = "R::Results: Serialize", + deserialize = "R::Results: Deserialize<'de>")))] pub results: R::Results, pub index: usize, pub half_used: bool, // true if only half of the previous result is used diff --git a/rand_core/src/lib.rs b/rand_core/src/lib.rs index 5eb1e174b20..4cf8daea22c 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="serde-1")] extern crate serde; +#[cfg(feature="serde-1")] #[macro_use] extern crate serde_derive; use core::default::Default; From 94fbf762b5fa18342b9611166f0b13839dbe7506 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Tue, 3 Apr 2018 08:08:46 +0200 Subject: [PATCH 5/9] Add back serde to IsaacRng and Isaac64Rng --- src/prng/isaac.rs | 50 +++++------------ src/prng/isaac64.rs | 52 +++++------------- src/prng/{isaac_serde.rs => isaac_array.rs} | 59 +++++++++++++++++++-- src/prng/mod.rs | 3 +- 4 files changed, 83 insertions(+), 81 deletions(-) rename src/prng/{isaac_serde.rs => isaac_array.rs} (58%) diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index 56029cbf999..ef321994ce8 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -14,6 +14,7 @@ use core::{fmt, slice}; use core::num::Wrapping as w; 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; @@ -86,6 +87,7 @@ const RAND_SIZE: usize = 1 << RAND_SIZE_LEN; /// /// [`Hc128Rng`]: hc128/struct.Hc128Rng.html #[derive(Clone, Debug)] +#[cfg_attr(feature="serde-1", derive(Serialize, Deserialize))] pub struct IsaacRng(BlockRng); impl RngCore for IsaacRng { @@ -145,14 +147,16 @@ impl IsaacRng { // `seed == 0` this method produces exactly the same state as the // reference implementation when used unseeded. core: IsaacCore::init(key, 1), - results: IsaacArray([0; RAND_SIZE]), + results: IsaacArray::default(), index: RAND_SIZE, // generate on first use }) } } #[derive(Clone)] +#[cfg_attr(feature="serde-1", derive(Serialize, Deserialize))] pub struct IsaacCore { + #[cfg_attr(feature="serde-1",serde(with="super::isaac_array::isaac_array_serde"))] mem: [w32; RAND_SIZE], a: w32, b: w32, @@ -166,37 +170,9 @@ impl fmt::Debug for IsaacCore { } } -// Terrible workaround because arrays with more than 32 elements do not -// implement `AsRef` (or any other traits for that matter) -#[derive(Copy, Clone)] -#[allow(missing_debug_implementations)] -pub struct IsaacArray([u32; RAND_SIZE]); -impl ::core::convert::AsRef<[u32]> for IsaacArray { - #[inline(always)] - fn as_ref(&self) -> &[u32] { - &self.0[..] - } -} -impl ::core::ops::Deref for IsaacArray { - type Target = [u32; RAND_SIZE]; - #[inline(always)] - fn deref(&self) -> &Self::Target { - &self.0 - } -} -impl ::core::ops::DerefMut for IsaacArray { - #[inline(always)] - fn deref_mut(&mut self) -> &mut [u32; RAND_SIZE] { - &mut self.0 - } -} -impl ::core::default::Default for IsaacArray { - fn default() -> IsaacArray { IsaacArray([0u32; RAND_SIZE]) } -} - impl BlockRngCore for IsaacCore { type Item = u32; - type Results = IsaacArray; + type Results = IsaacArray; /// Refills the output buffer (`results`) /// See also the pseudocode desciption of the algorithm at the top of this @@ -217,7 +193,7 @@ impl BlockRngCore for IsaacCore { /// 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) { + fn generate(&mut self, results: &mut IsaacArray) { self.c += w(1); // abbreviations let mut a = self.a; @@ -493,17 +469,17 @@ 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); + assert_eq!(rng.0.index, deserialized.0.index); /* Can't assert directly because of the array size */ - for (orig,deser) in rng.rsl.iter().zip(deserialized.rsl.iter()) { + for (orig,deser) in rng.0.results.iter().zip(deserialized.0.results.iter()) { assert_eq!(orig, deser); } - for (orig,deser) in rng.mem.iter().zip(deserialized.mem.iter()) { + for (orig,deser) in rng.0.core.mem.iter().zip(deserialized.0.core.mem.iter()) { assert_eq!(orig, deser); } - assert_eq!(rng.a, deserialized.a); - assert_eq!(rng.b, deserialized.b); - assert_eq!(rng.c, deserialized.c); + assert_eq!(rng.0.core.a, deserialized.0.core.a); + assert_eq!(rng.0.core.b, deserialized.0.core.b); + assert_eq!(rng.0.core.c, deserialized.0.core.c); for _ in 0..16 { assert_eq!(rng.next_u64(), deserialized.next_u64()); diff --git a/src/prng/isaac64.rs b/src/prng/isaac64.rs index 02576f2c0f8..40c14b7a092 100644 --- a/src/prng/isaac64.rs +++ b/src/prng/isaac64.rs @@ -14,6 +14,7 @@ use core::{fmt, slice}; use core::num::Wrapping as w; 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; @@ -76,6 +77,7 @@ const RAND_SIZE: usize = 1 << RAND_SIZE_LEN; /// [`IsaacRng`]: ../isaac/struct.IsaacRng.html /// [`Hc128Rng`]: hc128/struct.Hc128Rng.html #[derive(Clone, Debug)] +#[cfg_attr(feature="serde-1", derive(Serialize, Deserialize))] pub struct Isaac64Rng(BlockRng64); impl RngCore for Isaac64Rng { @@ -134,7 +136,7 @@ impl Isaac64Rng { // `seed == 0` this method produces exactly the same state as the // reference implementation when used unseeded. core: Isaac64Core::init(key, 1), - results: Isaac64Array([0; RAND_SIZE]), + results: IsaacArray::default(), index: RAND_SIZE, // generate on first use half_used: false, }) @@ -142,7 +144,9 @@ impl Isaac64Rng { } #[derive(Clone)] +#[cfg_attr(feature="serde-1", derive(Serialize, Deserialize))] pub struct Isaac64Core { + #[cfg_attr(feature="serde-1",serde(with="super::isaac_array::isaac_array_serde"))] mem: [w64; RAND_SIZE], a: w64, b: w64, @@ -156,37 +160,9 @@ impl fmt::Debug for Isaac64Core { } } -// Terrible workaround because arrays with more than 32 elements do not -// implement `AsRef` (or any other traits for that matter) -#[derive(Copy, Clone)] -#[allow(missing_debug_implementations)] -pub struct Isaac64Array([u64; RAND_SIZE]); -impl ::core::convert::AsRef<[u64]> for Isaac64Array { - #[inline(always)] - fn as_ref(&self) -> &[u64] { - &self.0[..] - } -} -impl ::core::ops::Deref for Isaac64Array { - type Target = [u64; RAND_SIZE]; - #[inline(always)] - fn deref(&self) -> &Self::Target { - &self.0 - } -} -impl ::core::ops::DerefMut for Isaac64Array { - #[inline(always)] - fn deref_mut(&mut self) -> &mut [u64; RAND_SIZE] { - &mut self.0 - } -} -impl ::core::default::Default for Isaac64Array { - fn default() -> Isaac64Array { Isaac64Array([0u64; RAND_SIZE]) } -} - impl BlockRngCore for Isaac64Core { type Item = u64; - type Results = Isaac64Array; + type Results = IsaacArray; /// Refills the output buffer (`results`) /// See also the pseudocode desciption of the algorithm at the top of this @@ -207,7 +183,7 @@ impl BlockRngCore for Isaac64Core { /// 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 Isaac64Array) { + fn generate(&mut self, results: &mut IsaacArray) { self.c += w(1); // abbreviations let mut a = self.a; @@ -486,18 +462,18 @@ 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); + assert_eq!(rng.0.index, deserialized.0.index); + assert_eq!(rng.0.half_used, deserialized.0.half_used); /* Can't assert directly because of the array size */ - for (orig,deser) in rng.rsl.iter().zip(deserialized.rsl.iter()) { + for (orig,deser) in rng.0.results.iter().zip(deserialized.0.results.iter()) { assert_eq!(orig, deser); } - for (orig,deser) in rng.mem.iter().zip(deserialized.mem.iter()) { + for (orig,deser) in rng.0.core.mem.iter().zip(deserialized.0.core.mem.iter()) { assert_eq!(orig, deser); } - assert_eq!(rng.a, deserialized.a); - assert_eq!(rng.b, deserialized.b); - assert_eq!(rng.c, deserialized.c); + assert_eq!(rng.0.core.a, deserialized.0.core.a); + assert_eq!(rng.0.core.b, deserialized.0.core.b); + assert_eq!(rng.0.core.c, deserialized.0.core.c); for _ in 0..16 { assert_eq!(rng.next_u64(), deserialized.next_u64()); diff --git a/src/prng/isaac_serde.rs b/src/prng/isaac_array.rs similarity index 58% rename from src/prng/isaac_serde.rs rename to src/prng/isaac_array.rs index 459b9287fd6..39716df79a8 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="serde-1")] 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="serde-1", derive(Serialize, Deserialize))] +pub struct IsaacArray where T: Default + Copy { + #[cfg_attr(feature="serde-1",serde(with="isaac_array_serde"))] + #[cfg_attr(feature="serde-1", serde(bound( + serialize = "T: Serialize", + deserialize = "T: Deserialize<'de>")))] + inner: [T; RAND_SIZE] +} + +impl ::core::convert::AsRef<[T]> for IsaacArray where T: Default + Copy { + #[inline(always)] + fn as_ref(&self) -> &[T] { + &self.inner[..] + } +} + +impl ::core::ops::Deref for IsaacArray where T: Default + Copy { + type Target = [T; RAND_SIZE]; + #[inline(always)] + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl ::core::ops::DerefMut for IsaacArray where T: Default + Copy { + #[inline(always)] + fn deref_mut(&mut self) -> &mut [T; RAND_SIZE] { + &mut self.inner + } +} + +impl ::core::default::Default for IsaacArray where T: Default + Copy { + fn default() -> IsaacArray { + IsaacArray { inner: [T::default(); RAND_SIZE] } + } +} + + +#[cfg(feature="serde-1")] +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..b7c64ab281e 100644 --- a/src/prng/mod.rs +++ b/src/prng/mod.rs @@ -46,8 +46,7 @@ mod isaac; 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; From bbd1b384d47fa3f2889294c3fb604e547b31839f Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Tue, 3 Apr 2018 09:40:04 +0200 Subject: [PATCH 6/9] Make `isaac` and `isaac64` modules public (and relative links are really inconsistent) --- src/prng/isaac.rs | 3 ++- src/prng/isaac64.rs | 3 ++- src/prng/mod.rs | 8 ++++---- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index ef321994ce8..22d4d59b54b 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -85,7 +85,7 @@ 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 +/// [`Hc128Rng`]: ../hc128/struct.Hc128Rng.html #[derive(Clone, Debug)] #[cfg_attr(feature="serde-1", derive(Serialize, Deserialize))] pub struct IsaacRng(BlockRng); @@ -153,6 +153,7 @@ impl IsaacRng { } } +/// The core of `IsaacRng`, used with `BlockRng`. #[derive(Clone)] #[cfg_attr(feature="serde-1", derive(Serialize, Deserialize))] pub struct IsaacCore { diff --git a/src/prng/isaac64.rs b/src/prng/isaac64.rs index 40c14b7a092..bd04fdf5550 100644 --- a/src/prng/isaac64.rs +++ b/src/prng/isaac64.rs @@ -75,7 +75,7 @@ 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 +/// [`Hc128Rng`]: ../hc128/struct.Hc128Rng.html #[derive(Clone, Debug)] #[cfg_attr(feature="serde-1", derive(Serialize, Deserialize))] pub struct Isaac64Rng(BlockRng64); @@ -143,6 +143,7 @@ impl Isaac64Rng { } } +/// The core of `Isaac64Rng`, used with `BlockRng`. #[derive(Clone)] #[cfg_attr(feature="serde-1", derive(Serialize, Deserialize))] pub struct Isaac64Core { diff --git a/src/prng/mod.rs b/src/prng/mod.rs index b7c64ab281e..c4bd0032052 100644 --- a/src/prng/mod.rs +++ b/src/prng/mod.rs @@ -42,14 +42,14 @@ pub mod chacha; pub mod hc128; -mod isaac; -mod isaac64; +pub mod isaac; +pub mod isaac64; mod xorshift; 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; From c5ffc61fc91b61e6cb47f85c65120c5a2e912dcd Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Tue, 3 Apr 2018 13:10:57 +0200 Subject: [PATCH 7/9] Don't put bounds on a data structure --- src/prng/isaac_array.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/prng/isaac_array.rs b/src/prng/isaac_array.rs index 39716df79a8..c263dc9714f 100644 --- a/src/prng/isaac_array.rs +++ b/src/prng/isaac_array.rs @@ -23,22 +23,22 @@ const RAND_SIZE: usize = 1 << RAND_SIZE_LEN; #[derive(Copy, Clone)] #[allow(missing_debug_implementations)] #[cfg_attr(feature="serde-1", derive(Serialize, Deserialize))] -pub struct IsaacArray where T: Default + Copy { +pub struct IsaacArray { #[cfg_attr(feature="serde-1",serde(with="isaac_array_serde"))] #[cfg_attr(feature="serde-1", serde(bound( serialize = "T: Serialize", - deserialize = "T: Deserialize<'de>")))] + deserialize = "T: Deserialize<'de> + Copy + Default")))] inner: [T; RAND_SIZE] } -impl ::core::convert::AsRef<[T]> for IsaacArray where T: Default + Copy { +impl ::core::convert::AsRef<[T]> for IsaacArray { #[inline(always)] fn as_ref(&self) -> &[T] { &self.inner[..] } } -impl ::core::ops::Deref for IsaacArray where T: Default + Copy { +impl ::core::ops::Deref for IsaacArray { type Target = [T; RAND_SIZE]; #[inline(always)] fn deref(&self) -> &Self::Target { @@ -46,14 +46,14 @@ impl ::core::ops::Deref for IsaacArray where T: Default + Copy { } } -impl ::core::ops::DerefMut for IsaacArray where T: Default + Copy { +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: Default + Copy { +impl ::core::default::Default for IsaacArray where T: Copy + Default { fn default() -> IsaacArray { IsaacArray { inner: [T::default(); RAND_SIZE] } } From 28238e00dba77bcc0009f37617477a5394065ff6 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Wed, 4 Apr 2018 17:26:04 +0200 Subject: [PATCH 8/9] Make BlockRng{64} fields private --- rand_core/src/impls.rs | 101 +++++++++++++++++++++++++++-------------- src/prng/chacha.rs | 8 ++-- src/prng/isaac.rs | 53 +++++++++------------ src/prng/isaac64.rs | 51 ++++++++------------- src/reseeding.rs | 38 ++++++++-------- 5 files changed, 133 insertions(+), 118 deletions(-) diff --git a/rand_core/src/impls.rs b/rand_core/src/impls.rs index c02fb618b17..9b2478fa0f3 100644 --- a/rand_core/src/impls.rs +++ b/rand_core/src/impls.rs @@ -191,9 +191,9 @@ pub struct BlockRng { #[cfg_attr(feature="serde-1", serde(bound( serialize = "R::Results: Serialize", deserialize = "R::Results: Deserialize<'de>")))] - pub results: R::Results, - pub index: usize, - pub core: R, + results: R::Results, + index: usize, + core: R, } // Custom Debug implementation that does not expose the contents of `results`. @@ -207,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]> { @@ -323,21 +352,11 @@ impl SeedableRng for BlockRng { type Seed = R::Seed; fn from_seed(seed: Self::Seed) -> Self { - let results_empty = R::Results::default(); - Self { - core: R::from_seed(seed), - index: results_empty.as_ref().len(), // generate on first use - results: results_empty, - } + 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)?)) } } @@ -358,10 +377,10 @@ pub struct BlockRng64 { #[cfg_attr(feature="serde-1", serde(bound( serialize = "R::Results: Serialize", deserialize = "R::Results: Deserialize<'de>")))] - pub results: R::Results, - pub index: usize, - pub half_used: bool, // true if only half of the previous result is used - pub core: R, + 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`. @@ -376,6 +395,31 @@ impl fmt::Debug for BlockRng64 { } } +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(); + 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]> { @@ -424,6 +468,7 @@ where ::Results: AsRef<[u64]> #[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() { @@ -461,11 +506,11 @@ where ::Results: AsRef<[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; - self.half_used = false; } let (consumed_u64, filled_u8) = @@ -486,23 +531,11 @@ impl SeedableRng for BlockRng64 { type Seed = R::Seed; fn from_seed(seed: Self::Seed) -> Self { - let results_empty = R::Results::default(); - Self { - core: R::from_seed(seed), - index: results_empty.as_ref().len(), // generate on first use - half_used: false, - results: results_empty, - } + 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 - half_used: false, - results: results_empty, - }) + Ok(Self::new(R::from_rng(rng)?)) } } 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 22d4d59b54b..77575a1d5d5 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -128,28 +128,15 @@ 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) } - /// Create 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); - IsaacRng(BlockRng { - // 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. - core: IsaacCore::init(key, 1), - results: IsaacArray::default(), - index: RAND_SIZE, // generate on first use - }) + pub fn new_from_u64(seed: u64) -> Self { + IsaacRng(BlockRng::new(IsaacCore::new_from_u64(seed))) } } @@ -318,6 +305,22 @@ impl IsaacCore { Self { mem, a: w(0), b: w(0), c: w(0) } } + + /// 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) + } } impl SeedableRng for IsaacCore { @@ -470,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.0.index, deserialized.0.index); - /* Can't assert directly because of the array size */ - for (orig,deser) in rng.0.results.iter().zip(deserialized.0.results.iter()) { - assert_eq!(orig, deser); - } - for (orig,deser) in rng.0.core.mem.iter().zip(deserialized.0.core.mem.iter()) { - assert_eq!(orig, deser); - } - assert_eq!(rng.0.core.a, deserialized.0.core.a); - assert_eq!(rng.0.core.b, deserialized.0.core.b); - assert_eq!(rng.0.core.c, deserialized.0.core.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 bd04fdf5550..131c50f1dee 100644 --- a/src/prng/isaac64.rs +++ b/src/prng/isaac64.rs @@ -118,28 +118,15 @@ 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) } - /// Create 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); - Isaac64Rng(BlockRng64 { - // 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. - core: Isaac64Core::init(key, 1), - results: IsaacArray::default(), - index: RAND_SIZE, // generate on first use - half_used: false, - }) + pub fn new_from_u64(seed: u64) -> Self { + Isaac64Rng(BlockRng64::new(Isaac64Core::new_from_u64(seed))) } } @@ -283,6 +270,21 @@ impl Isaac64Core { Self { mem, a: w(0), b: w(0), c: w(0) } } + + /// 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) + } } impl SeedableRng for Isaac64Core { @@ -463,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.0.index, deserialized.0.index); - assert_eq!(rng.0.half_used, deserialized.0.half_used); - /* Can't assert directly because of the array size */ - for (orig,deser) in rng.0.results.iter().zip(deserialized.0.results.iter()) { - assert_eq!(orig, deser); - } - for (orig,deser) in rng.0.core.mem.iter().zip(deserialized.0.core.mem.iter()) { - assert_eq!(orig, deser); - } - assert_eq!(rng.0.core.a, deserialized.0.core.a); - assert_eq!(rng.0.core.b, deserialized.0.core.b); - assert_eq!(rng.0.core.c, deserialized.0.core.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/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| { From f9f0ea637e9efe9eaa80d39ac17024327bef8553 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Tue, 3 Apr 2018 13:17:41 +0200 Subject: [PATCH 9/9] Rename `serde-1` to `serde1` --- .travis.yml | 8 ++++---- CHANGELOG.md | 4 ++-- Cargo.toml | 2 +- README.md | 4 ++-- UPDATING.md | 2 +- appveyor.yml | 4 ++-- rand_core/Cargo.toml | 2 +- rand_core/src/impls.rs | 10 +++++----- rand_core/src/lib.rs | 4 ++-- src/lib.rs | 6 +++--- src/prng/isaac.rs | 8 ++++---- src/prng/isaac64.rs | 8 ++++---- src/prng/isaac_array.rs | 10 +++++----- src/prng/xorshift.rs | 4 ++-- utils/ci/script.sh | 6 +++--- 15 files changed, 41 insertions(+), 41 deletions(-) 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 97adda71608..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", "rand_core/serde-1"] # enables serialization 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 dda81bcfa3c..e75d927fe89 100644 --- a/rand_core/Cargo.toml +++ b/rand_core/Cargo.toml @@ -22,7 +22,7 @@ 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 -serde-1 = ["serde", "serde_derive"] # enables serde for BlockRng wrapper +serde1 = ["serde", "serde_derive"] # enables serde for BlockRng wrapper [dependencies] serde = { version = "1", optional = true } diff --git a/rand_core/src/impls.rs b/rand_core/src/impls.rs index 9b2478fa0f3..645dc8f990b 100644 --- a/rand_core/src/impls.rs +++ b/rand_core/src/impls.rs @@ -27,7 +27,7 @@ use core::cmp::min; use core::mem::size_of; use {RngCore, BlockRngCore, CryptoRng, SeedableRng, Error}; -#[cfg(feature="serde-1")] use serde::{Serialize, Deserialize}; +#[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 { @@ -186,9 +186,9 @@ pub fn next_u64_via_fill(rng: &mut R) -> u64 { /// [`RngCore`]: ../RngCore.t.html /// [`SeedableRng`]: ../SeedableRng.t.html #[derive(Clone)] -#[cfg_attr(feature="serde-1", derive(Serialize, Deserialize))] +#[cfg_attr(feature="serde1", derive(Serialize, Deserialize))] pub struct BlockRng { - #[cfg_attr(feature="serde-1", serde(bound( + #[cfg_attr(feature="serde1", serde(bound( serialize = "R::Results: Serialize", deserialize = "R::Results: Deserialize<'de>")))] results: R::Results, @@ -372,9 +372,9 @@ impl SeedableRng for BlockRng { /// [`RngCore`]: ../RngCore.t.html /// [`BlockRng`]: struct.BlockRng.html #[derive(Clone)] -#[cfg_attr(feature="serde-1", derive(Serialize, Deserialize))] +#[cfg_attr(feature="serde1", derive(Serialize, Deserialize))] pub struct BlockRng64 { - #[cfg_attr(feature="serde-1", serde(bound( + #[cfg_attr(feature="serde1", serde(bound( serialize = "R::Results: Serialize", deserialize = "R::Results: Deserialize<'de>")))] results: R::Results, diff --git a/rand_core/src/lib.rs b/rand_core/src/lib.rs index 4cf8daea22c..483bc4bbf73 100644 --- a/rand_core/src/lib.rs +++ b/rand_core/src/lib.rs @@ -44,8 +44,8 @@ #[cfg(feature="std")] extern crate core; #[cfg(all(feature = "alloc", not(feature="std")))] extern crate alloc; -#[cfg(feature="serde-1")] extern crate serde; -#[cfg(feature="serde-1")] #[macro_use] extern crate serde_derive; +#[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/isaac.rs b/src/prng/isaac.rs index 77575a1d5d5..5701650890b 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -87,7 +87,7 @@ const RAND_SIZE: usize = 1 << RAND_SIZE_LEN; /// /// [`Hc128Rng`]: ../hc128/struct.Hc128Rng.html #[derive(Clone, Debug)] -#[cfg_attr(feature="serde-1", derive(Serialize, Deserialize))] +#[cfg_attr(feature="serde1", derive(Serialize, Deserialize))] pub struct IsaacRng(BlockRng); impl RngCore for IsaacRng { @@ -142,9 +142,9 @@ impl IsaacRng { /// The core of `IsaacRng`, used with `BlockRng`. #[derive(Clone)] -#[cfg_attr(feature="serde-1", derive(Serialize, Deserialize))] +#[cfg_attr(feature="serde1", derive(Serialize, Deserialize))] pub struct IsaacCore { - #[cfg_attr(feature="serde-1",serde(with="super::isaac_array::isaac_array_serde"))] + #[cfg_attr(feature="serde1",serde(with="super::isaac_array::isaac_array_serde"))] mem: [w32; RAND_SIZE], a: w32, b: w32, @@ -456,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}; diff --git a/src/prng/isaac64.rs b/src/prng/isaac64.rs index 131c50f1dee..05468fc2d84 100644 --- a/src/prng/isaac64.rs +++ b/src/prng/isaac64.rs @@ -77,7 +77,7 @@ const RAND_SIZE: usize = 1 << RAND_SIZE_LEN; /// [`IsaacRng`]: ../isaac/struct.IsaacRng.html /// [`Hc128Rng`]: ../hc128/struct.Hc128Rng.html #[derive(Clone, Debug)] -#[cfg_attr(feature="serde-1", derive(Serialize, Deserialize))] +#[cfg_attr(feature="serde1", derive(Serialize, Deserialize))] pub struct Isaac64Rng(BlockRng64); impl RngCore for Isaac64Rng { @@ -132,9 +132,9 @@ impl Isaac64Rng { /// The core of `Isaac64Rng`, used with `BlockRng`. #[derive(Clone)] -#[cfg_attr(feature="serde-1", derive(Serialize, Deserialize))] +#[cfg_attr(feature="serde1", derive(Serialize, Deserialize))] pub struct Isaac64Core { - #[cfg_attr(feature="serde-1",serde(with="super::isaac_array::isaac_array_serde"))] + #[cfg_attr(feature="serde1",serde(with="super::isaac_array::isaac_array_serde"))] mem: [w64; RAND_SIZE], a: w64, b: w64, @@ -448,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}; diff --git a/src/prng/isaac_array.rs b/src/prng/isaac_array.rs index c263dc9714f..327cfbf5bcf 100644 --- a/src/prng/isaac_array.rs +++ b/src/prng/isaac_array.rs @@ -14,7 +14,7 @@ // implement `AsRef`, `Default`, `Serialize`, `Deserialize`, or any other // traits for that matter. -#[cfg(feature="serde-1")] use serde::{Serialize, Deserialize}; +#[cfg(feature="serde1")] use serde::{Serialize, Deserialize}; const RAND_SIZE_LEN: usize = 8; const RAND_SIZE: usize = 1 << RAND_SIZE_LEN; @@ -22,10 +22,10 @@ const RAND_SIZE: usize = 1 << RAND_SIZE_LEN; #[derive(Copy, Clone)] #[allow(missing_debug_implementations)] -#[cfg_attr(feature="serde-1", derive(Serialize, Deserialize))] +#[cfg_attr(feature="serde1", derive(Serialize, Deserialize))] pub struct IsaacArray { - #[cfg_attr(feature="serde-1",serde(with="isaac_array_serde"))] - #[cfg_attr(feature="serde-1", serde(bound( + #[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] @@ -60,7 +60,7 @@ impl ::core::default::Default for IsaacArray where T: Copy + Default { } -#[cfg(feature="serde-1")] +#[cfg(feature="serde1")] pub(super) mod isaac_array_serde { const RAND_SIZE_LEN: usize = 8; const RAND_SIZE: usize = 1 << RAND_SIZE_LEN; 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/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 }