From 69a43e83d03f1c382e1944878e376a7030734cab Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Mon, 11 Sep 2017 21:01:04 +0200 Subject: [PATCH 01/18] Refactor isaac.rs This removes the unsafe blocks from the main isaac and isaac64 routines. Some macro's are replaced by functions. Over time the implementation of ISAAC and ISAAC-64 have diverged a bit, now they are as similar as possible (for easier comparison). The code is tested against the previous implementation, and the reference implementation. IsaacRng now does 34% better in the benchmark. [Cherry-picked from 4747665d228] --- src/prng/isaac.rs | 150 +++++++++++++++++------------------ src/prng/isaac64.rs | 185 ++++++++++++++++++++++---------------------- 2 files changed, 161 insertions(+), 174 deletions(-) diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index cf5eb679c08..ba3e210646b 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -10,8 +10,6 @@ //! The ISAAC random number generator. -#![allow(non_camel_case_types)] - use core::slice; use core::iter::repeat; use core::num::Wrapping as w; @@ -19,12 +17,11 @@ use core::fmt; use {Rng, SeedableRng, Rand}; -#[allow(bad_style)] +#[allow(non_camel_case_types)] type w32 = w; const RAND_SIZE_LEN: usize = 8; -const RAND_SIZE: u32 = 1 << RAND_SIZE_LEN; -const RAND_SIZE_USIZE: usize = 1 << RAND_SIZE_LEN; +const RAND_SIZE: usize = 1 << RAND_SIZE_LEN; /// A random number generator that uses the ISAAC algorithm[1]. /// @@ -37,18 +34,18 @@ const RAND_SIZE_USIZE: usize = 1 << RAND_SIZE_LEN; /// generator*](http://www.burtleburtle.net/bob/rand/isaacafa.html) #[derive(Copy)] pub struct IsaacRng { - cnt: u32, - rsl: [w32; RAND_SIZE_USIZE], - mem: [w32; RAND_SIZE_USIZE], + rsl: [w32; RAND_SIZE], + mem: [w32; RAND_SIZE], a: w32, b: w32, c: w32, + cnt: u32, } static EMPTY: IsaacRng = IsaacRng { cnt: 0, - rsl: [w(0); RAND_SIZE_USIZE], - mem: [w(0); RAND_SIZE_USIZE], + rsl: [w(0); RAND_SIZE], + mem: [w(0); RAND_SIZE], a: w(0), b: w(0), c: w(0), }; @@ -66,7 +63,7 @@ impl IsaacRng { /// of `rsl` as a seed, otherwise construct one algorithmically (not /// randomly). fn init(&mut self, use_rsl: bool) { - let mut a = w(0x9e3779b9); + let mut a = w(0x9e3779b9); // golden ratio let mut b = a; let mut c = a; let mut d = a; @@ -77,14 +74,14 @@ impl IsaacRng { macro_rules! mix { () => {{ - a=a^(b<<11); d=d+a; b=b+c; - b=b^(c>>2); e=e+b; c=c+d; - c=c^(d<<8); f=f+c; d=d+e; - d=d^(e>>16); g=g+d; e=e+f; - e=e^(f<<10); h=h+e; f=f+g; - f=f^(g>>4); a=a+f; g=g+h; - g=g^(h<<8); b=b+g; h=h+a; - h=h^(a>>9); c=c+h; a=a+b; + 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; }} } @@ -95,16 +92,16 @@ impl IsaacRng { if use_rsl { macro_rules! memloop { ($arr:expr) => {{ - for i in (0..RAND_SIZE_USIZE/8).map(|i| i * 8) { - a=a+$arr[i ]; b=b+$arr[i+1]; - c=c+$arr[i+2]; d=d+$arr[i+3]; - e=e+$arr[i+4]; f=f+$arr[i+5]; - g=g+$arr[i+6]; h=h+$arr[i+7]; + for i in (0..RAND_SIZE/8).map(|i| i * 8) { + a += $arr[i ]; b += $arr[i+1]; + c += $arr[i+2]; d += $arr[i+3]; + e += $arr[i+4]; f += $arr[i+5]; + g += $arr[i+6]; h += $arr[i+7]; mix!(); - self.mem[i ]=a; self.mem[i+1]=b; - self.mem[i+2]=c; self.mem[i+3]=d; - self.mem[i+4]=e; self.mem[i+5]=f; - self.mem[i+6]=g; self.mem[i+7]=h; + self.mem[i ] = a; self.mem[i+1] = b; + self.mem[i+2] = c; self.mem[i+3] = d; + self.mem[i+4] = e; self.mem[i+5] = f; + self.mem[i+6] = g; self.mem[i+7] = h; } }} } @@ -112,12 +109,12 @@ impl IsaacRng { memloop!(self.rsl); memloop!(self.mem); } else { - for i in (0..RAND_SIZE_USIZE/8).map(|i| i * 8) { + for i in (0..RAND_SIZE/8).map(|i| i * 8) { mix!(); - self.mem[i ]=a; self.mem[i+1]=b; - self.mem[i+2]=c; self.mem[i+3]=d; - self.mem[i+4]=e; self.mem[i+5]=f; - self.mem[i+6]=g; self.mem[i+7]=h; + self.mem[i ] = a; self.mem[i+1] = b; + self.mem[i+2] = c; self.mem[i+3] = d; + self.mem[i+4] = e; self.mem[i+5] = f; + self.mem[i+6] = g; self.mem[i+7] = h; } } @@ -125,63 +122,56 @@ impl IsaacRng { } /// Refills the output buffer (`self.rsl`) - #[inline] fn isaac(&mut self) { - self.c = self.c + w(1); + self.c += w(1); // abbreviations let mut a = self.a; let mut b = self.b + self.c; + const MIDPOINT: usize = RAND_SIZE / 2; - const MIDPOINT: usize = RAND_SIZE_USIZE / 2; - - macro_rules! ind { - ($x:expr) => ( self.mem[($x >> 2usize).0 as usize & (RAND_SIZE_USIZE - 1)] ) + #[inline(always)] + fn ind(mem:&[w32; RAND_SIZE], v: w32, amount: usize) -> w32 { + let index = (v >> amount).0 as usize % RAND_SIZE; + mem[index] } - let r = [(0, MIDPOINT), (MIDPOINT, 0)]; - for &(mr_offset, m2_offset) in r.iter() { - - macro_rules! rngstepp { - ($j:expr, $shift:expr) => {{ - let base = $j; - let mix = a << $shift; - - let x = self.mem[base + mr_offset]; - a = (a ^ mix) + self.mem[base + m2_offset]; - let y = ind!(x) + a + b; - self.mem[base + mr_offset] = y; - - b = ind!(y >> RAND_SIZE_LEN) + x; - self.rsl[base + mr_offset] = b; - }} - } - - macro_rules! rngstepn { - ($j:expr, $shift:expr) => {{ - let base = $j; - let mix = a >> $shift; - - let x = self.mem[base + mr_offset]; - a = (a ^ mix) + self.mem[base + m2_offset]; - let y = ind!(x) + a + b; - self.mem[base + mr_offset] = y; + #[inline(always)] + fn rngstep(ctx: &mut IsaacRng, + mix: w32, + a: &mut w32, + b: &mut w32, + base: usize, + m: usize, + m2: usize) { + let x = ctx.mem[base + m]; + *a = mix + ctx.mem[base + m2]; + let y = *a + *b + ind(&ctx.mem, x, 2); + ctx.mem[base + m] = y; + *b = x + ind(&ctx.mem, y, 2 + RAND_SIZE_LEN); + ctx.rsl[base + m] = *b; + } - b = ind!(y >> RAND_SIZE_LEN) + x; - self.rsl[base + mr_offset] = b; - }} - } + let mut m = 0; + let mut m2 = MIDPOINT; + for i in (0..MIDPOINT/4).map(|i| i * 4) { + rngstep(self, a ^ (a << 13), &mut a, &mut b, i + 0, m, m2); + rngstep(self, a ^ (a >> 6 ), &mut a, &mut b, i + 1, m, m2); + rngstep(self, a ^ (a << 2 ), &mut a, &mut b, i + 2, m, m2); + rngstep(self, a ^ (a >> 16), &mut a, &mut b, i + 3, m, m2); + } - for i in (0..MIDPOINT/4).map(|i| i * 4) { - rngstepp!(i + 0, 13); - rngstepn!(i + 1, 6); - rngstepp!(i + 2, 2); - rngstepn!(i + 3, 16); - } + m = MIDPOINT; + m2 = 0; + for i in (0..MIDPOINT/4).map(|i| i * 4) { + rngstep(self, a ^ (a << 13), &mut a, &mut b, i + 0, m, m2); + rngstep(self, a ^ (a >> 6 ), &mut a, &mut b, i + 1, m, m2); + rngstep(self, a ^ (a << 2 ), &mut a, &mut b, i + 2, m, m2); + rngstep(self, a ^ (a >> 16), &mut a, &mut b, i + 3, m, m2); } self.a = a; self.b = b; - self.cnt = RAND_SIZE; + self.cnt = RAND_SIZE as u32; } } @@ -207,12 +197,12 @@ impl Rng for IsaacRng { // misrefactors, so we check, sometimes. // // (Changes here should be reflected in Isaac64Rng.next_u64.) - debug_assert!(self.cnt < RAND_SIZE); + debug_assert!((self.cnt as usize) < RAND_SIZE); // (the % is cheaply telling the optimiser that we're always // in bounds, without unsafe. NB. this is a power of two, so // it optimises to a bitwise mask). - self.rsl[(self.cnt % RAND_SIZE) as usize].0 + self.rsl[self.cnt as usize % RAND_SIZE].0 } } @@ -251,7 +241,7 @@ impl Rand for IsaacRng { unsafe { let ptr = ret.rsl.as_mut_ptr() as *mut u8; - let slice = slice::from_raw_parts_mut(ptr, RAND_SIZE_USIZE * 4); + let slice = slice::from_raw_parts_mut(ptr, RAND_SIZE * 4); other.fill_bytes(slice); } ret.cnt = 0; diff --git a/src/prng/isaac64.rs b/src/prng/isaac64.rs index b98e3fec4f5..521c9d3242f 100644 --- a/src/prng/isaac64.rs +++ b/src/prng/isaac64.rs @@ -17,11 +17,11 @@ use core::fmt; use {Rng, SeedableRng, Rand}; -#[allow(bad_style)] +#[allow(non_camel_case_types)] type w64 = w; -const RAND_SIZE_64_LEN: usize = 8; -const RAND_SIZE_64: usize = 1 << RAND_SIZE_64_LEN; +const RAND_SIZE_LEN: usize = 8; +const RAND_SIZE: usize = 1 << RAND_SIZE_LEN; /// A random number generator that uses ISAAC-64[1], the 64-bit /// variant of the ISAAC algorithm. @@ -35,18 +35,18 @@ const RAND_SIZE_64: usize = 1 << RAND_SIZE_64_LEN; /// generator*](http://www.burtleburtle.net/bob/rand/isaacafa.html) #[derive(Copy)] pub struct Isaac64Rng { - cnt: usize, - rsl: [w64; RAND_SIZE_64], - mem: [w64; RAND_SIZE_64], + rsl: [w64; RAND_SIZE], + mem: [w64; RAND_SIZE], a: w64, b: w64, c: w64, + cnt: u32, } static EMPTY_64: Isaac64Rng = Isaac64Rng { cnt: 0, - rsl: [w(0); RAND_SIZE_64], - mem: [w(0); RAND_SIZE_64], + rsl: [w(0); RAND_SIZE], + mem: [w(0); RAND_SIZE], a: w(0), b: w(0), c: w(0), }; @@ -63,24 +63,25 @@ impl Isaac64Rng { /// of `rsl` as a seed, otherwise construct one algorithmically (not /// randomly). fn init(&mut self, use_rsl: bool) { - macro_rules! init { - ($var:ident) => ( - let mut $var = w(0x9e3779b97f4a7c13); - ) - } - init!(a); init!(b); init!(c); init!(d); - init!(e); init!(f); init!(g); init!(h); + let mut a = w(0x9e3779b97f4a7c13); // golden ratio + let mut b = a; + let mut c = a; + let mut d = a; + let mut e = a; + let mut f = a; + let mut g = a; + let mut h = a; macro_rules! mix { () => {{ - a=a-e; f=f^(h>>9); h=h+a; - b=b-f; g=g^(a<<9); a=a+b; - c=c-g; h=h^(b>>23); b=b+c; - d=d-h; a=a^(c<<15); c=c+d; - e=e-a; b=b^(d>>14); d=d+e; - f=f-b; c=c^(e<<20); e=e+f; - g=g-c; d=d^(f>>17); f=f+g; - h=h-d; e=e^(g<<14); g=g+h; + 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; }} } @@ -91,16 +92,16 @@ impl Isaac64Rng { if use_rsl { macro_rules! memloop { ($arr:expr) => {{ - for i in (0..RAND_SIZE_64 / 8).map(|i| i * 8) { - a=a+$arr[i ]; b=b+$arr[i+1]; - c=c+$arr[i+2]; d=d+$arr[i+3]; - e=e+$arr[i+4]; f=f+$arr[i+5]; - g=g+$arr[i+6]; h=h+$arr[i+7]; + for i in (0..RAND_SIZE/8).map(|i| i * 8) { + a += $arr[i ]; b += $arr[i+1]; + c += $arr[i+2]; d += $arr[i+3]; + e += $arr[i+4]; f += $arr[i+5]; + g += $arr[i+6]; h += $arr[i+7]; mix!(); - self.mem[i ]=a; self.mem[i+1]=b; - self.mem[i+2]=c; self.mem[i+3]=d; - self.mem[i+4]=e; self.mem[i+5]=f; - self.mem[i+6]=g; self.mem[i+7]=h; + self.mem[i ] = a; self.mem[i+1] = b; + self.mem[i+2] = c; self.mem[i+3] = d; + self.mem[i+4] = e; self.mem[i+5] = f; + self.mem[i+6] = g; self.mem[i+7] = h; } }} } @@ -108,12 +109,12 @@ impl Isaac64Rng { memloop!(self.rsl); memloop!(self.mem); } else { - for i in (0..RAND_SIZE_64 / 8).map(|i| i * 8) { + for i in (0..RAND_SIZE/8).map(|i| i * 8) { mix!(); - self.mem[i ]=a; self.mem[i+1]=b; - self.mem[i+2]=c; self.mem[i+3]=d; - self.mem[i+4]=e; self.mem[i+5]=f; - self.mem[i+6]=g; self.mem[i+7]=h; + self.mem[i ] = a; self.mem[i+1] = b; + self.mem[i+2] = c; self.mem[i+3] = d; + self.mem[i+4] = e; self.mem[i+5] = f; + self.mem[i+6] = g; self.mem[i+7] = h; } } @@ -122,67 +123,55 @@ impl Isaac64Rng { /// Refills the output buffer (`self.rsl`) fn isaac64(&mut self) { - self.c = self.c + w(1); + self.c += w(1); // abbreviations let mut a = self.a; let mut b = self.b + self.c; - const MIDPOINT: usize = RAND_SIZE_64 / 2; - const MP_VEC: [(usize, usize); 2] = [(0,MIDPOINT), (MIDPOINT, 0)]; - macro_rules! ind { - ($x:expr) => { - *self.mem.get_unchecked((($x >> 3usize).0 as usize) & (RAND_SIZE_64 - 1)) - } + const MIDPOINT: usize = RAND_SIZE / 2; + + #[inline(always)] + fn ind(mem:&[w64; RAND_SIZE], v: w64, amount: usize) -> w64 { + let index = (v >> amount).0 as usize % RAND_SIZE; + mem[index] } - for &(mr_offset, m2_offset) in MP_VEC.iter() { - for base in (0..MIDPOINT / 4).map(|i| i * 4) { - - macro_rules! rngstepp { - ($j:expr, $shift:expr) => {{ - let base = base + $j; - let mix = a ^ (a << $shift); - let mix = if $j == 0 {!mix} else {mix}; - - unsafe { - let x = *self.mem.get_unchecked(base + mr_offset); - a = mix + *self.mem.get_unchecked(base + m2_offset); - let y = ind!(x) + a + b; - *self.mem.get_unchecked_mut(base + mr_offset) = y; - - b = ind!(y >> RAND_SIZE_64_LEN) + x; - *self.rsl.get_unchecked_mut(base + mr_offset) = b; - } - }} - } - - macro_rules! rngstepn { - ($j:expr, $shift:expr) => {{ - let base = base + $j; - let mix = a ^ (a >> $shift); - let mix = if $j == 0 {!mix} else {mix}; - - unsafe { - let x = *self.mem.get_unchecked(base + mr_offset); - a = mix + *self.mem.get_unchecked(base + m2_offset); - let y = ind!(x) + a + b; - *self.mem.get_unchecked_mut(base + mr_offset) = y; - - b = ind!(y >> RAND_SIZE_64_LEN) + x; - *self.rsl.get_unchecked_mut(base + mr_offset) = b; - } - }} - } - - rngstepp!(0, 21); - rngstepn!(1, 5); - rngstepp!(2, 12); - rngstepn!(3, 33); - } + #[inline(always)] + fn rngstep(ctx: &mut Isaac64Rng, + mix: w64, + a: &mut w64, + b: &mut w64, + base: usize, + m: usize, + m2: usize) { + let x = ctx.mem[base + m]; + *a = mix + ctx.mem[base + m2]; + let y = *a + *b + ind(&ctx.mem, x, 3); + ctx.mem[base + m] = y; + *b = x + ind(&ctx.mem, y, 3 + RAND_SIZE_LEN); + ctx.rsl[base + m] = *b; + } + + let mut m = 0; + let mut m2 = MIDPOINT; + for i in (0..MIDPOINT/4).map(|i| i * 4) { + rngstep(self, !(a ^ (a << 21)), &mut a, &mut b, i + 0, m, m2); + rngstep(self, a ^ (a >> 5 ), &mut a, &mut b, i + 1, m, m2); + rngstep(self, a ^ (a << 12), &mut a, &mut b, i + 2, m, m2); + rngstep(self, a ^ (a >> 33), &mut a, &mut b, i + 3, m, m2); + } + + m = MIDPOINT; + m2 = 0; + for i in (0..MIDPOINT/4).map(|i| i * 4) { + rngstep(self, !(a ^ (a << 21)), &mut a, &mut b, i + 0, m, m2); + rngstep(self, a ^ (a >> 5 ), &mut a, &mut b, i + 1, m, m2); + rngstep(self, a ^ (a << 12), &mut a, &mut b, i + 2, m, m2); + rngstep(self, a ^ (a >> 33), &mut a, &mut b, i + 3, m, m2); } self.a = a; self.b = b; - self.cnt = RAND_SIZE_64; + self.cnt = RAND_SIZE as u32; } } @@ -207,10 +196,18 @@ impl Rng for Isaac64Rng { } self.cnt -= 1; - // See corresponding location in IsaacRng.next_u32 for - // explanation. - debug_assert!(self.cnt < RAND_SIZE_64); - self.rsl[(self.cnt % RAND_SIZE_64) as usize].0 + // self.cnt is at most RAND_SIZE, but that is before the + // subtraction above. We want to index without bounds + // checking, but this could lead to incorrect code if someone + // misrefactors, so we check, sometimes. + // + // (Changes here should be reflected in IsaacRng.next_u32.) + debug_assert!((self.cnt as usize) < RAND_SIZE); + + // (the % is cheaply telling the optimiser that we're always + // in bounds, without unsafe. NB. this is a power of two, so + // it optimises to a bitwise mask). + self.rsl[self.cnt as usize % RAND_SIZE].0 } } @@ -249,7 +246,7 @@ impl Rand for Isaac64Rng { unsafe { let ptr = ret.rsl.as_mut_ptr() as *mut u8; - let slice = slice::from_raw_parts_mut(ptr, RAND_SIZE_64 * 8); + let slice = slice::from_raw_parts_mut(ptr, RAND_SIZE * 8); other.fill_bytes(slice); } ret.cnt = 0; From 9c999338caa3bcab3087302860dbed1821fadf7c Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Fri, 15 Dec 2017 11:19:40 +0000 Subject: [PATCH 02/18] Add impls module; replace custom impls; remove default impls for next_u64 and fill_bytes This is based on dd1241a256f2 but heavily modified --- src/distributions/mod.rs | 6 +- src/impls.rs | 170 +++++++++++++++++++++++++++++++++++++++ src/jitter.rs | 19 +---- src/lib.rs | 67 +++++---------- src/prng/chacha.rs | 9 +++ src/prng/isaac.rs | 9 +++ src/prng/isaac64.rs | 5 ++ src/prng/xorshift.rs | 9 +++ src/rand_impls.rs | 5 ++ src/reseeding.rs | 8 ++ 10 files changed, 241 insertions(+), 66 deletions(-) create mode 100644 src/impls.rs diff --git a/src/distributions/mod.rs b/src/distributions/mod.rs index 5de8efb9c47..7c652363ef7 100644 --- a/src/distributions/mod.rs +++ b/src/distributions/mod.rs @@ -279,8 +279,8 @@ fn ziggurat( #[cfg(test)] mod tests { - use {Rng, Rand}; + use impls; use super::{RandSample, WeightedChoice, Weighted, Sample, IndependentSample}; #[derive(PartialEq, Debug)] @@ -301,6 +301,10 @@ mod tests { fn next_u64(&mut self) -> u64 { self.next_u32() as u64 } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + impls::fill_bytes_via_u32(self, dest) + } } #[test] diff --git a/src/impls.rs b/src/impls.rs new file mode 100644 index 00000000000..12fb2ff4234 --- /dev/null +++ b/src/impls.rs @@ -0,0 +1,170 @@ +// Copyright 2013-2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Helper functions for implementing `Rng` functions. +//! +//! For cross-platform reproducibility, these functions all use Little Endian: +//! least-significant part first. For example, `next_u64_via_u32` takes `u32` +//! values `x, y`, then outputs `(y << 32) | x`. To implement `next_u32` +//! from `next_u64` in little-endian order, one should use `next_u64() as u32`. +//! +//! Byte-swapping (like the std `to_le` functions) is only needed to convert +//! to/from byte sequences, and since its purpose is reproducibility, +//! non-reproducible sources (e.g. `OsRng`) need not bother with it. + +// TODO: eventually these should be exported somehow +#![allow(unused)] + +use core::intrinsics::transmute; +use core::slice; +use core::cmp::min; +use core::mem::size_of; +use Rng; + +/// Implement `next_u64` via `next_u32`, little-endian order. +pub fn next_u64_via_u32(rng: &mut R) -> u64 { + // Use LE; we explicitly generate one value before the next. + let x = rng.next_u32() as u64; + let y = rng.next_u32() as u64; + (y << 32) | x +} + +macro_rules! fill_bytes_via { + ($rng:ident, $next_u:ident, $BYTES:expr, $dest:ident) => {{ + let mut left = $dest; + while left.len() >= $BYTES { + let (l, r) = {left}.split_at_mut($BYTES); + left = r; + let chunk: [u8; $BYTES] = unsafe { + transmute($rng.$next_u().to_le()) + }; + l.copy_from_slice(&chunk); + } + let n = left.len(); + if n > 0 { + let chunk: [u8; $BYTES] = unsafe { + transmute($rng.$next_u().to_le()) + }; + left.copy_from_slice(&chunk[..n]); + } + }} +} + +/// Implement `fill_bytes` via `next_u32`, little-endian order. +pub fn fill_bytes_via_u32(rng: &mut R, dest: &mut [u8]) { + fill_bytes_via!(rng, next_u32, 4, dest) +} + +/// Implement `fill_bytes` via `next_u64`, little-endian order. +pub fn fill_bytes_via_u64(rng: &mut R, dest: &mut [u8]) { + fill_bytes_via!(rng, next_u64, 8, dest) +} + +macro_rules! impl_uint_from_fill { + ($self:expr, $ty:ty, $N:expr) => ({ + debug_assert!($N == size_of::<$ty>()); + + let mut int: $ty = 0; + unsafe { + let ptr = &mut int as *mut $ty as *mut u8; + let slice = slice::from_raw_parts_mut(ptr, $N); + $self.fill_bytes(slice); + } + int + }); +} + +macro_rules! fill_via_chunks { + ($src:expr, $dest:expr, $N:expr) => ({ + let chunk_size_u8 = min($src.len() * $N, $dest.len()); + let chunk_size = (chunk_size_u8 + $N - 1) / $N; + + // Convert to little-endian: + for ref mut x in $src[0..chunk_size].iter_mut() { + **x = (*x).to_le(); + } + + let bytes = unsafe { slice::from_raw_parts($src.as_ptr() as *const u8, + $src.len() * $N) }; + + let dest_chunk = &mut $dest[0..chunk_size_u8]; + dest_chunk.copy_from_slice(&bytes[0..chunk_size_u8]); + + (chunk_size, chunk_size_u8) + }); +} + +/// Implement `fill_bytes` by reading chunks from the output buffer of a block +/// based RNG. +/// +/// The return values are `(consumed_u32, filled_u8)`. +/// +/// `filled_u8` is the number of filled bytes in `dest`, which may be less than +/// the length of `dest`. +/// `consumed_u32` is the number of words consumed from `src`, which is the same +/// as `filled_u8 / 4` rounded up. +/// +/// Note that on big-endian systems values in the output buffer `src` are +/// mutated. `src[0..consumed_u32]` get converted to little-endian before +/// copying. +/// +/// # Example +/// (from `IsaacRng`) +/// +/// ```rust,ignore +/// fn fill_bytes(&mut self, dest: &mut [u8]) { +/// let mut read_len = 0; +/// while read_len < dest.len() { +/// if self.index >= self.rsl.len() { +/// self.isaac(); +/// } +/// +/// let (consumed_u32, filled_u8) = +/// impls::fill_via_u32_chunks(&mut self.rsl[self.index..], +/// &mut dest[read_len..]); +/// +/// self.index += consumed_u32; +/// read_len += filled_u8; +/// } +/// } +/// ``` +pub fn fill_via_u32_chunks(src: &mut [u32], dest: &mut [u8]) -> (usize, usize) { + fill_via_chunks!(src, dest, 4) +} + +/// Implement `fill_bytes` by reading chunks from the output buffer of a block +/// based RNG. +/// +/// The return values are `(consumed_u64, filled_u8)`. +/// `filled_u8` is the number of filled bytes in `dest`, which may be less than +/// the length of `dest`. +/// `consumed_u64` is the number of words consumed from `src`, which is the same +/// as `filled_u8 / 8` rounded up. +/// +/// Note that on big-endian systems values in the output buffer `src` are +/// mutated. `src[0..consumed_u64]` get converted to little-endian before +/// copying. +/// +/// See `fill_via_u32_chunks` for an example. +pub fn fill_via_u64_chunks(src: &mut [u64], dest: &mut [u8]) -> (usize, usize) { + fill_via_chunks!(src, dest, 8) +} + +/// Implement `next_u32` via `fill_bytes`, little-endian order. +pub fn next_u32_via_fill(rng: &mut R) -> u32 { + impl_uint_from_fill!(rng, u32, 4) +} + +/// Implement `next_u64` via `fill_bytes`, little-endian order. +pub fn next_u64_via_fill(rng: &mut R) -> u64 { + impl_uint_from_fill!(rng, u64, 8) +} + +// TODO: implement tests for the above diff --git a/src/jitter.rs b/src/jitter.rs index 942b0d0c9fe..162782db1a1 100644 --- a/src/jitter.rs +++ b/src/jitter.rs @@ -16,7 +16,7 @@ //! Non-physical true random number generator based on timing jitter. -use Rng; +use {Rng, impls}; use core::{fmt, mem, ptr}; #[cfg(feature="std")] @@ -731,22 +731,7 @@ impl Rng for JitterRng { } fn fill_bytes(&mut self, dest: &mut [u8]) { - let mut left = dest; - while left.len() >= 8 { - let (l, r) = {left}.split_at_mut(8); - left = r; - let chunk: [u8; 8] = unsafe { - mem::transmute(self.next_u64().to_le()) - }; - l.copy_from_slice(&chunk); - } - let n = left.len(); - if n > 0 { - let chunk: [u8; 8] = unsafe { - mem::transmute(self.next_u64().to_le()) - }; - left.copy_from_slice(&chunk[..n]); - } + impls::fill_bytes_via_u64(self, dest) } } diff --git a/src/lib.rs b/src/lib.rs index 996c12b7a2f..a63d210b633 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -277,6 +277,7 @@ use distributions::range::SampleRange; // public modules pub mod distributions; +mod impls; pub mod jitter; #[cfg(feature="std")] pub mod os; #[cfg(feature="std")] pub mod read; @@ -339,21 +340,10 @@ pub trait Rand : Sized { /// A random number generator. pub trait Rng { /// Return the next random u32. - /// - /// This rarely needs to be called directly, prefer `r.gen()` to - /// `r.next_u32()`. - // FIXME #rust-lang/rfcs#628: Should be implemented in terms of next_u64 fn next_u32(&mut self) -> u32; /// Return the next random u64. - /// - /// By default this is implemented in terms of `next_u32`. An - /// implementation of this trait must provide at least one of - /// these two methods. Similarly to `next_u32`, this rarely needs - /// to be called directly, prefer `r.gen()` to `r.next_u64()`. - fn next_u64(&mut self) -> u64 { - ((self.next_u32() as u64) << 32) | (self.next_u32() as u64) - } + fn next_u64(&mut self) -> u64; /// Return the next random f32 selected from the half-open /// interval `[0, 1)`. @@ -409,11 +399,6 @@ pub trait Rng { /// Fill `dest` with random data. /// - /// This has a default implementation in terms of `next_u64` and - /// `next_u32`, but should be overridden by implementations that - /// offer a more efficient solution than just calling those - /// methods repeatedly. - /// /// This method does *not* have a requirement to bear any fixed /// relationship to the other methods, for example, it does *not* /// have to result in the same output as progressively filling @@ -434,29 +419,7 @@ pub trait Rng { /// thread_rng().fill_bytes(&mut v); /// println!("{:?}", &v[..]); /// ``` - fn fill_bytes(&mut self, dest: &mut [u8]) { - // this could, in theory, be done by transmuting dest to a - // [u64], but this is (1) likely to be undefined behaviour for - // LLVM, (2) has to be very careful about alignment concerns, - // (3) adds more `unsafe` that needs to be checked, (4) - // probably doesn't give much performance gain if - // optimisations are on. - let mut count = 0; - let mut num = 0; - for byte in dest.iter_mut() { - if count == 0 { - // we could micro-optimise here by generating a u32 if - // we only need a few more bytes to fill the vector - // (i.e. at most 4). - num = self.next_u64(); - count = 8; - } - - *byte = (num & 0xff) as u8; - num >>= 8; - count -= 1; - } - } + fn fill_bytes(&mut self, dest: &mut [u8]); /// Return a random value of a `Rand` type. /// @@ -802,15 +765,17 @@ impl StdRng { } impl Rng for StdRng { - #[inline] fn next_u32(&mut self) -> u32 { self.rng.next_u32() } - #[inline] fn next_u64(&mut self) -> u64 { self.rng.next_u64() } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + self.rng.fill_bytes(dest) + } } impl<'a> SeedableRng<&'a [usize]> for StdRng { @@ -985,6 +950,7 @@ pub fn sample(rng: &mut R, iterable: I, amount: usize) -> Vec #[cfg(test)] mod test { + use impls; use super::{Rng, thread_rng, random, SeedableRng, StdRng, weak_rng}; use std::iter::repeat; @@ -992,10 +958,13 @@ mod test { impl Rng for MyRng { fn next_u32(&mut self) -> u32 { - fn next(t: &mut T) -> u32 { - t.next_u32() - } - next(&mut self.inner) + self.inner.next_u32() + } + fn next_u64(&mut self) -> u64 { + self.inner.next_u64() + } + fn fill_bytes(&mut self, dest: &mut [u8]) { + self.inner.fill_bytes(dest) } } @@ -1007,8 +976,10 @@ mod test { impl Rng for ConstRng { fn next_u32(&mut self) -> u32 { self.i as u32 } fn next_u64(&mut self) -> u64 { self.i } - - // no fill_bytes on purpose + + fn fill_bytes(&mut self, dest: &mut [u8]) { + impls::fill_bytes_via_u64(self, dest) + } } pub fn iter_eq(i: I, j: J) -> bool diff --git a/src/prng/chacha.rs b/src/prng/chacha.rs index a73e8e78f46..9cc0217e987 100644 --- a/src/prng/chacha.rs +++ b/src/prng/chacha.rs @@ -12,6 +12,7 @@ use core::num::Wrapping as w; use {Rng, SeedableRng, Rand}; +use impls; #[allow(bad_style)] type w32 = w; @@ -196,6 +197,14 @@ impl Rng for ChaChaRng { self.index += 1; value.0 } + + fn next_u64(&mut self) -> u64 { + impls::next_u64_via_u32(self) + } + + fn fill_bytes(&mut self, bytes: &mut [u8]) { + impls::fill_bytes_via_u32(self, bytes) + } } impl<'a> SeedableRng<&'a [u32]> for ChaChaRng { diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index ba3e210646b..85e9d4e3085 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -16,6 +16,7 @@ use core::num::Wrapping as w; use core::fmt; use {Rng, SeedableRng, Rand}; +use impls; #[allow(non_camel_case_types)] type w32 = w; @@ -204,6 +205,14 @@ impl Rng for IsaacRng { // it optimises to a bitwise mask). self.rsl[self.cnt as usize % RAND_SIZE].0 } + + fn next_u64(&mut self) -> u64 { + impls::next_u64_via_u32(self) + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + impls::fill_bytes_via_u32(self, dest) + } } impl<'a> SeedableRng<&'a [u32]> for IsaacRng { diff --git a/src/prng/isaac64.rs b/src/prng/isaac64.rs index 521c9d3242f..540fa23892b 100644 --- a/src/prng/isaac64.rs +++ b/src/prng/isaac64.rs @@ -16,6 +16,7 @@ use core::num::Wrapping as w; use core::fmt; use {Rng, SeedableRng, Rand}; +use impls; #[allow(non_camel_case_types)] type w64 = w; @@ -209,6 +210,10 @@ impl Rng for Isaac64Rng { // it optimises to a bitwise mask). self.rsl[self.cnt as usize % RAND_SIZE].0 } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + impls::fill_bytes_via_u64(self, dest) + } } impl<'a> SeedableRng<&'a [u64]> for Isaac64Rng { diff --git a/src/prng/xorshift.rs b/src/prng/xorshift.rs index dd367e9bfe9..0f5ba076fc1 100644 --- a/src/prng/xorshift.rs +++ b/src/prng/xorshift.rs @@ -12,6 +12,7 @@ use core::num::Wrapping as w; use {Rng, SeedableRng, Rand}; +use impls; /// An Xorshift[1] random number /// generator. @@ -61,6 +62,14 @@ impl Rng for XorShiftRng { self.w = w_ ^ (w_ >> 19) ^ (t ^ (t >> 8)); self.w.0 } + + fn next_u64(&mut self) -> u64 { + impls::next_u64_via_u32(self) + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + impls::fill_bytes_via_u32(self, dest) + } } impl SeedableRng<[u32; 4]> for XorShiftRng { diff --git a/src/rand_impls.rs b/src/rand_impls.rs index a865bb69582..13ff1d6f4eb 100644 --- a/src/rand_impls.rs +++ b/src/rand_impls.rs @@ -248,6 +248,7 @@ impl Rand for Option { #[cfg(test)] mod tests { + use impls; use {Rng, thread_rng, Open01, Closed01}; struct ConstantRng(u64); @@ -260,6 +261,10 @@ mod tests { let ConstantRng(v) = *self; v } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + impls::fill_bytes_via_u64(self, dest) + } } #[test] diff --git a/src/reseeding.rs b/src/reseeding.rs index 1f24e2006fd..455f90e3373 100644 --- a/src/reseeding.rs +++ b/src/reseeding.rs @@ -147,6 +147,7 @@ impl Default for ReseedWithDefault { #[cfg(test)] mod test { + use impls; use std::default::Default; use std::iter::repeat; use super::{ReseedingRng, ReseedWithDefault}; @@ -162,6 +163,13 @@ mod test { // very random self.i - 1 } + fn next_u64(&mut self) -> u64 { + impls::next_u64_via_u32(self) + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + impls::fill_bytes_via_u64(self, dest) + } } impl Default for Counter { fn default() -> Counter { From 78912da35aa560506f62707925e36fe5402975e5 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Fri, 15 Dec 2017 12:36:34 +0000 Subject: [PATCH 03/18] Fix: allow compiling with Rust 1.15 --- src/impls.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/impls.rs b/src/impls.rs index 12fb2ff4234..981399baa8c 100644 --- a/src/impls.rs +++ b/src/impls.rs @@ -68,14 +68,14 @@ pub fn fill_bytes_via_u64(rng: &mut R, dest: &mut [u8]) { } macro_rules! impl_uint_from_fill { - ($self:expr, $ty:ty, $N:expr) => ({ + ($rng:expr, $ty:ty, $N:expr) => ({ debug_assert!($N == size_of::<$ty>()); let mut int: $ty = 0; unsafe { let ptr = &mut int as *mut $ty as *mut u8; let slice = slice::from_raw_parts_mut(ptr, $N); - $self.fill_bytes(slice); + $rng.fill_bytes(slice); } int }); From 54851997552ac1ca9325eda9732d7c7576142cba Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Mon, 11 Sep 2017 22:33:07 +0200 Subject: [PATCH 04/18] Add some extra documentation [Cherry-picked from 14d05616b8] --- src/prng/isaac.rs | 83 +++++++++++++++++++++++++++++++++++++++++---- src/prng/isaac64.rs | 68 ++++++++++++++++++++++++++++++++----- 2 files changed, 136 insertions(+), 15 deletions(-) diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index 85e9d4e3085..a1157b2714d 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -24,15 +24,70 @@ type w32 = w; const RAND_SIZE_LEN: usize = 8; const RAND_SIZE: usize = 1 << RAND_SIZE_LEN; -/// A random number generator that uses the ISAAC algorithm[1]. +/// A random number generator that uses the ISAAC algorithm. /// -/// The ISAAC algorithm is generally accepted as suitable for -/// cryptographic purposes, but this implementation has not be -/// verified as such. Prefer a generator like `OsRng` that defers to -/// the operating system for cases that need high security. +/// ISAAC stands for "Indirection, Shift, Accumulate, Add, and Count" which are +/// the principal bitwise operations employed. It is the most advanced of a +/// series of array based random number generator designed by Robert Jenkins +/// in 1996[1][2]. /// -/// [1]: Bob Jenkins, [*ISAAC: A fast cryptographic random number -/// generator*](http://www.burtleburtle.net/bob/rand/isaacafa.html) +/// Although ISAAC is designed to be cryptographically secure, its design is not +/// founded in cryptographic theory. Therefore it is _not recommended for_ +/// cryptographic purposes. It is however one of the strongest non-cryptograpic +/// RNGs, and that while still being reasonably fast. +/// +/// Where fast random numbers are needed which should still be secure, but where +/// speed is more important than absolute (cryptographic) security (e.g. to +/// initialise hashes in the std library), a generator like ISAAC may be a good +/// choice. +/// +/// In 2006 an improvement to ISAAC was suggested by Jean-Philippe Aumasson, +/// named ISAAC+[3]. But because the specification is not complete, there is no +/// good implementation, and because the suggested bias may not exist, it is not +/// implemented here. +/// +/// ## Overview of the ISAAC algorithm: +/// (in pseudo-code) +/// +/// ```text +/// Input: a, b, c, s[256] // state +/// Output: r[256] // results +/// +/// mix(a,i) = a ^ a << 13 if i = 0 mod 4 +/// a ^ a >> 6 if i = 1 mod 4 +/// a ^ a << 2 if i = 2 mod 4 +/// a ^ a >> 16 if i = 3 mod 4 +/// +/// c = c + 1 +/// b = b + c +/// +/// for i in 0..256 { +/// x = s_[i] +/// a = f(a,i) + s[i+128 mod 256] +/// y = a + b + s[x>>2 mod 256] +/// s[i] = y +/// b = x + s[y>>10 mod 256] +/// r[i] = b +/// } +/// ``` +/// +/// Numbers are generated in blocks of 256. This means the function above only +/// runs once every 256 times you ask for a next random number. In all other +/// circumstances the last element of the results array is returned. +/// +/// ISAAC therefore needs a lot of memory, relative to other non-vrypto RNGs. +/// 2 * 256 * 4 = 2 kb to hold the state and results. +/// +/// ## References +/// [1]: Bob Jenkins, [*ISAAC: A fast cryptographic random number generator*] +/// (http://burtleburtle.net/bob/rand/isaacafa.html) +/// +/// [2]: Bob Jenkins, [*ISAAC and RC4*] +/// (http://burtleburtle.net/bob/rand/isaac.html) +/// +/// [3]: Jean-Philippe Aumasson, [*On the pseudo-random generator ISAAC*] +/// (http://eprint.iacr.org/2006/438) + #[derive(Copy)] pub struct IsaacRng { rsl: [w32; RAND_SIZE], @@ -123,6 +178,20 @@ impl IsaacRng { } /// Refills the output buffer (`self.rsl`) + /// See also the pseudocode desciption of the algorithm at the top of this + /// file. + /// + /// Optimisations used (similar to the reference implementation): + /// - The loop is unrolled 4 times, once for every constant of mix(). + /// - The contents of the main loop are moved to a function `rngstep`, to + /// reduce code duplication. + /// - We use local variables for a and b, which helps with optimisations. + /// - We split the main loop in two, one that operates over 0..128 and one + /// over 128..256. This way we can optimise out the addition and modulus + /// from `s[i+128 mod 256]`. + /// - We maintain one index `i` and add `m` or `m2` as base (m2 for the + /// `s[i+128 mod 256]`), relying on the optimizer to turn it into pointer + /// arithmetic. fn isaac(&mut self) { self.c += w(1); // abbreviations diff --git a/src/prng/isaac64.rs b/src/prng/isaac64.rs index 540fa23892b..2107e85e55b 100644 --- a/src/prng/isaac64.rs +++ b/src/prng/isaac64.rs @@ -24,16 +24,54 @@ type w64 = w; const RAND_SIZE_LEN: usize = 8; const RAND_SIZE: usize = 1 << RAND_SIZE_LEN; -/// A random number generator that uses ISAAC-64[1], the 64-bit -/// variant of the ISAAC algorithm. +/// A random number generator that uses ISAAC-64, the 64-bit variant of the +/// ISAAC algorithm. /// -/// The ISAAC algorithm is generally accepted as suitable for -/// cryptographic purposes, but this implementation has not be -/// verified as such. Prefer a generator like `OsRng` that defers to -/// the operating system for cases that need high security. +/// ISAAC stands for "Indirection, Shift, Accumulate, Add, and Count" which are +/// the principal bitwise operations employed. It is the most advanced of a +/// series of array based random number generator designed by Robert Jenkins +/// in 1996[1]. /// -/// [1]: Bob Jenkins, [*ISAAC: A fast cryptographic random number -/// generator*](http://www.burtleburtle.net/bob/rand/isaacafa.html) +/// Although ISAAC is designed to be cryptographically secure, its design is not +/// founded in cryptographic theory. Therefore it is _not recommended for_ +/// cryptographic purposes. It is however one of the strongest non-cryptograpic +/// RNGs, and that while still being reasonably fast. +/// +/// ISAAC-64 is mostly similar to ISAAC. Because it operates on 64-bit integers +/// instead of 32-bit, it uses twice as much memory to hold its state and +/// results. Also it uses different constants for shifts and indirect indexing, +/// optimized to give good results for 64bit arithmetic. +/// +/// ## Overview of the ISAAC-64 algorithm: +/// (in pseudo-code) +/// +/// ```text +/// Input: a, b, c, s[256] // state +/// Output: r[256] // results +/// +/// mix(a,i) = !(a ^ a << 21) if i = 0 mod 4 +/// a ^ a >> 5 if i = 1 mod 4 +/// a ^ a << 12 if i = 2 mod 4 +/// a ^ a >> 33 if i = 3 mod 4 +/// +/// c = c + 1 +/// b = b + c +/// +/// for i in 0..256 { +/// x = s_[i] +/// a = mix(a,i) + s[i+128 mod 256] +/// y = a + b + s[x>>3 mod 256] +/// s[i] = y +/// b = x + s[y>>11 mod 256] +/// r[i] = b +/// } +/// ``` +/// +/// See for more information the description in rand::prng::IsaacRng. +/// +/// [1]: Bob Jenkins, [*ISAAC and RC4*] +/// (http://burtleburtle.net/bob/rand/isaac.html) + #[derive(Copy)] pub struct Isaac64Rng { rsl: [w64; RAND_SIZE], @@ -123,6 +161,20 @@ impl Isaac64Rng { } /// Refills the output buffer (`self.rsl`) + /// See also the pseudocode desciption of the algorithm at the top of this + /// file. + /// + /// Optimisations used (similar to the reference implementation): + /// - The loop is unrolled 4 times, once for every constant of mix(). + /// - The contents of the main loop are moved to a function `rngstep`, to + /// reduce code duplication. + /// - We use local variables for a and b, which helps with optimisations. + /// - We split the main loop in two, one that operates over 0..128 and one + /// over 128..256. This way we can optimise out the addition and modulus + /// from `s[i+128 mod 256]`. + /// - We maintain one index `i` and add `m` or `m2` as base (m2 for the + /// `s[i+128 mod 256]`), relying on the optimizer to turn it into pointer + /// arithmetic. fn isaac64(&mut self) { self.c += w(1); // abbreviations From 73803b9dab6c8884b21629de4d4d79437aab60e9 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Fri, 15 Dec 2017 11:54:12 +0000 Subject: [PATCH 05/18] Add `new_from_u64` to `IsaacRng` and `Isaac64Rng` Cherry-picked from adcd8e56595a6 [by Paul Dicker, 19th October] I have implemented it as a function instead of a trait, as that makes it easy to add it to every RNG ont at a time. I split the `init` function in two instead of the current version that uses a bool to select between two paths. This makes it more clear how the seed is used. The current `mix` macro has to be defined in the function, and would have to be duplicated. Therefore I converted it to a seperate function. I precalculated the values a...h, but am not sure this is a good idea. It makes the resulting code smaller, and gives a small performance win. Because it are 'magic' values anyway, I thought why not? --- src/lib.rs | 2 +- src/prng/isaac.rs | 306 +++++++++++++++++++++++++++----------------- src/prng/isaac64.rs | 278 +++++++++++++++++++++++----------------- 3 files changed, 349 insertions(+), 237 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index a63d210b633..2c0a620f18b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -731,7 +731,7 @@ pub struct Closed01(pub F); /// The standard RNG. This is designed to be efficient on the current /// platform. -#[derive(Copy, Clone, Debug)] +#[derive(Clone, Debug)] pub struct StdRng { rng: IsaacWordRng, } diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index a1157b2714d..90ec98d746d 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -87,8 +87,6 @@ const RAND_SIZE: usize = 1 << RAND_SIZE_LEN; /// /// [3]: Jean-Philippe Aumasson, [*On the pseudo-random generator ISAAC*] /// (http://eprint.iacr.org/2006/438) - -#[derive(Copy)] pub struct IsaacRng { rsl: [w32; RAND_SIZE], mem: [w32; RAND_SIZE], @@ -98,83 +96,82 @@ pub struct IsaacRng { cnt: u32, } -static EMPTY: IsaacRng = IsaacRng { - cnt: 0, - rsl: [w(0); RAND_SIZE], - mem: [w(0); RAND_SIZE], - a: w(0), b: w(0), c: w(0), -}; - -impl IsaacRng { - - /// Create an ISAAC random number generator using the default - /// fixed seed. - pub fn new_unseeded() -> IsaacRng { - let mut rng = EMPTY; - rng.init(false); - rng - } - - /// Initialises `self`. If `use_rsl` is true, then use the current value - /// of `rsl` as a seed, otherwise construct one algorithmically (not - /// randomly). - fn init(&mut self, use_rsl: bool) { - let mut a = w(0x9e3779b9); // golden ratio - let mut b = a; - let mut c = a; - let mut d = a; - let mut e = a; - let mut f = a; - let mut g = a; - let mut h = a; - - macro_rules! mix { - () => {{ - a ^= 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; - }} +// Cannot be derived because [u32; 256] does not implement Clone +impl Clone for IsaacRng { + fn clone(&self) -> IsaacRng { + IsaacRng { + rsl: self.rsl, + mem: self.mem, + a: self.a, + b: self.b, + c: self.c, + cnt: self.cnt, } + } +} - for _ in 0..4 { - mix!(); - } +impl fmt::Debug for IsaacRng { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "IsaacRng {{}}") + } +} - if use_rsl { - macro_rules! memloop { - ($arr:expr) => {{ - for i in (0..RAND_SIZE/8).map(|i| i * 8) { - a += $arr[i ]; b += $arr[i+1]; - c += $arr[i+2]; d += $arr[i+3]; - e += $arr[i+4]; f += $arr[i+5]; - g += $arr[i+6]; h += $arr[i+7]; - mix!(); - self.mem[i ] = a; self.mem[i+1] = b; - self.mem[i+2] = c; self.mem[i+3] = d; - self.mem[i+4] = e; self.mem[i+5] = f; - self.mem[i+6] = g; self.mem[i+7] = h; - } - }} - } +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; +} - memloop!(self.rsl); - memloop!(self.mem); - } else { - for i in (0..RAND_SIZE/8).map(|i| i * 8) { - mix!(); - self.mem[i ] = a; self.mem[i+1] = b; - self.mem[i+2] = c; self.mem[i+3] = d; - self.mem[i+4] = e; self.mem[i+5] = f; - self.mem[i+6] = g; self.mem[i+7] = h; - } +impl IsaacRng { + /// Creates an ISAAC random number generator using an u64 as seed. + /// If `seed == 0` this will produce the same stream of random numbers as + /// the reference implementation when used unseeded. + pub fn new_from_u64(seed: u64) -> IsaacRng { + let mut 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); + + let mut mem = [w(0); RAND_SIZE]; + + a += w(seed as u32); + b += w((seed >> 32) as u32); + + for i in (0..RAND_SIZE/8).map(|i| i * 8) { + 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.isaac(); + // 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. + + let mut rng = IsaacRng { + rsl: [w(0); RAND_SIZE], + mem: mem, + a: w(0), + b: w(0), + c: w(0), + cnt: 0, + }; + + // Prepare the first set of results + rng.isaac(); + rng } /// Refills the output buffer (`self.rsl`) @@ -245,13 +242,6 @@ impl IsaacRng { } } -// Cannot be derived because [u32; 256] does not implement Clone -impl Clone for IsaacRng { - fn clone(&self) -> IsaacRng { - *self - } -} - impl Rng for IsaacRng { #[inline] fn next_u32(&mut self) -> u32 { @@ -274,67 +264,128 @@ impl Rng for IsaacRng { // it optimises to a bitwise mask). self.rsl[self.cnt as usize % RAND_SIZE].0 } - + fn next_u64(&mut self) -> u64 { impls::next_u64_via_u32(self) } - + + fn fill_bytes(&mut self, dest: &mut [u8]) { impls::fill_bytes_via_u32(self, dest) } } -impl<'a> SeedableRng<&'a [u32]> for IsaacRng { - fn reseed(&mut self, seed: &'a [u32]) { - // make the seed into [seed[0], seed[1], ..., seed[seed.len() - // - 1], 0, 0, ...], to fill rng.rsl. - let seed_iter = seed.iter().map(|&x| x).chain(repeat(0u32)); - - for (rsl_elem, seed_elem) in self.rsl.iter_mut().zip(seed_iter) { - *rsl_elem = w(seed_elem); - } - self.cnt = 0; - self.a = w(0); - self.b = w(0); - self.c = w(0); - - self.init(true); +/// Creates a new ISAAC-64 random number generator. +/// +/// The author Bob Jenkins describes how to best initialize ISAAC here: +/// https://rt.cpan.org/Public/Bug/Display.html?id=64324 +/// The answer is included here just in case: +/// +/// "No, you don't need a full 8192 bits of seed data. Normal key sizes will do +/// fine, and they should have their expected strength (eg a 40-bit key will +/// take as much time to brute force as 40-bit keys usually will). You could +/// fill the remainder with 0, but set the last array element to the length of +/// the key provided (to distinguish keys that differ only by different amounts +/// of 0 padding). You do still need to call randinit() to make sure the initial +/// state isn't uniform-looking." +/// "After publishing ISAAC, I wanted to limit the key to half the size of r[], +/// and repeat it twice. That would have made it hard to provide a key that sets +/// the whole internal state to anything convenient. But I'd already published +/// it." +/// +/// And his answer to the question "For my code, would repeating the key over +/// and over to fill 256 integers be a better solution than zero-filling, or +/// would they essentially be the same?": +/// "If the seed is under 32 bytes, they're essentially the same, otherwise +/// repeating the seed would be stronger. randinit() takes a chunk of 32 bytes, +/// mixes it, and combines that with the next 32 bytes, et cetera. Then loops +/// over all the elements the same way a second time." +#[inline] +fn init(key: [w32; RAND_SIZE]) -> 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); + + let mut mem = [w(0); RAND_SIZE]; + + macro_rules! memloop { + ($arr:expr) => {{ + for i in (0..RAND_SIZE/8).map(|i| i * 8) { + a += $arr[i ]; b += $arr[i+1]; + c += $arr[i+2]; d += $arr[i+3]; + e += $arr[i+4]; f += $arr[i+5]; + g += $arr[i+6]; h += $arr[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 with a seed. This can - /// be any length, although the maximum number of elements used is - /// 256 and any more will be silently ignored. A generator - /// constructed with a given seed will generate the same sequence - /// of values as all other generators constructed with that seed. - fn from_seed(seed: &'a [u32]) -> IsaacRng { - let mut rng = EMPTY; - rng.reseed(seed); - rng - } + memloop!(key); + // Do a second pass to make all of the seed affect all of `mem` + memloop!(mem); + + let mut rng = IsaacRng { + rsl: [w(0); RAND_SIZE], + mem: mem, + a: w(0), + b: w(0), + c: w(0), + cnt: 0, + }; + + // Prepare the first set of results + rng.isaac(); + rng } impl Rand for IsaacRng { fn rand(other: &mut R) -> IsaacRng { - let mut ret = EMPTY; + let mut key = [w(0); RAND_SIZE]; unsafe { - let ptr = ret.rsl.as_mut_ptr() as *mut u8; + let ptr = key.as_mut_ptr() as *mut u8; let slice = slice::from_raw_parts_mut(ptr, RAND_SIZE * 4); other.fill_bytes(slice); } - ret.cnt = 0; - ret.a = w(0); - ret.b = w(0); - ret.c = w(0); - ret.init(true); - return ret; + init(key) } } -impl fmt::Debug for IsaacRng { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "IsaacRng {{}}") +impl<'a> SeedableRng<&'a [u32]> for IsaacRng { + fn reseed(&mut self, seed: &'a [u32]) { + *self = Self::from_seed(seed); + } + /// Create an ISAAC random number generator with a seed. This can + /// be any length, although the maximum number of elements used is + /// 256 and any more will be silently ignored. A generator + /// constructed with a given seed will generate the same sequence + /// of values as all other generators constructed with that seed. + fn from_seed(seed: &'a [u32]) -> IsaacRng { + let mut key = [w(0); RAND_SIZE]; + + // make the seed into [seed[0], seed[1], ..., seed[seed.len() + // - 1], 0, 0, ...], to fill `key`. + let seed_iter = seed.iter().map(|&x| x).chain(repeat(0u32)); + + for (rsl_elem, seed_elem) in key.iter_mut().zip(seed_iter) { + *rsl_elem = w(seed_elem); + } + + init(key) } } @@ -393,4 +444,21 @@ mod test { vec!(3676831399, 3183332890, 2834741178, 3854698763, 2717568474, 1576568959, 3507990155, 179069555, 141456972, 2478885421)); } + + #[test] + fn test_isaac_new_uninitialized() { + // Compare the results from initializing `IsaacRng` with + // `new_from_u64(0)`, to make sure it is the same as the reference + // implementation when used uninitialized. + // Note: We only test the first 16 integers, not the full 256 of the + // first block. + let mut rng = IsaacRng::new_from_u64(0); + let vec = (0..16).map(|_| rng.next_u32()).collect::>(); + let expected: [u32; 16] = [ + 0x71D71FD2, 0xB54ADAE7, 0xD4788559, 0xC36129FA, + 0x21DC1EA9, 0x3CB879CA, 0xD83B237F, 0xFA3CE5BD, + 0x8D048509, 0xD82E9489, 0xDB452848, 0xCA20E846, + 0x500F972E, 0x0EEFF940, 0x00D6B993, 0xBC12C17F]; + assert_eq!(vec, expected); + } } diff --git a/src/prng/isaac64.rs b/src/prng/isaac64.rs index 2107e85e55b..f8ac4538bee 100644 --- a/src/prng/isaac64.rs +++ b/src/prng/isaac64.rs @@ -71,8 +71,6 @@ const RAND_SIZE: usize = 1 << RAND_SIZE_LEN; /// /// [1]: Bob Jenkins, [*ISAAC and RC4*] /// (http://burtleburtle.net/bob/rand/isaac.html) - -#[derive(Copy)] pub struct Isaac64Rng { rsl: [w64; RAND_SIZE], mem: [w64; RAND_SIZE], @@ -82,82 +80,81 @@ pub struct Isaac64Rng { cnt: u32, } -static EMPTY_64: Isaac64Rng = Isaac64Rng { - cnt: 0, - rsl: [w(0); RAND_SIZE], - mem: [w(0); RAND_SIZE], - a: w(0), b: w(0), c: w(0), -}; - -impl Isaac64Rng { - /// Create a 64-bit ISAAC random number generator using the - /// default fixed seed. - pub fn new_unseeded() -> Isaac64Rng { - let mut rng = EMPTY_64; - rng.init(false); - rng - } - - /// Initialises `self`. If `use_rsl` is true, then use the current value - /// of `rsl` as a seed, otherwise construct one algorithmically (not - /// randomly). - fn init(&mut self, use_rsl: bool) { - let mut a = w(0x9e3779b97f4a7c13); // golden ratio - let mut b = a; - let mut c = a; - let mut d = a; - let mut e = a; - let mut f = a; - let mut g = a; - let mut h = a; - - macro_rules! mix { - () => {{ - a -= 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; - }} +// Cannot be derived because [u64; 256] does not implement Clone +impl Clone for Isaac64Rng { + fn clone(&self) -> Isaac64Rng { + Isaac64Rng { + rsl: self.rsl, + mem: self.mem, + a: self.a, + b: self.b, + c: self.c, + cnt: self.cnt, } + } +} - for _ in 0..4 { - mix!(); - } +impl fmt::Debug for Isaac64Rng { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Isaac64Rng {{}}") + } +} - if use_rsl { - macro_rules! memloop { - ($arr:expr) => {{ - for i in (0..RAND_SIZE/8).map(|i| i * 8) { - a += $arr[i ]; b += $arr[i+1]; - c += $arr[i+2]; d += $arr[i+3]; - e += $arr[i+4]; f += $arr[i+5]; - g += $arr[i+6]; h += $arr[i+7]; - mix!(); - self.mem[i ] = a; self.mem[i+1] = b; - self.mem[i+2] = c; self.mem[i+3] = d; - self.mem[i+4] = e; self.mem[i+5] = f; - self.mem[i+6] = g; self.mem[i+7] = h; - } - }} - } +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; +} - memloop!(self.rsl); - memloop!(self.mem); - } else { - for i in (0..RAND_SIZE/8).map(|i| i * 8) { - mix!(); - self.mem[i ] = a; self.mem[i+1] = b; - self.mem[i+2] = c; self.mem[i+3] = d; - self.mem[i+4] = e; self.mem[i+5] = f; - self.mem[i+6] = g; self.mem[i+7] = h; - } +impl Isaac64Rng { + /// Creates an ISAAC-64 random number generator using an u64 as seed. + /// If `seed == 0` this will produce the same stream of random numbers as + /// the reference implementation when used unseeded. + pub fn new_from_u64(seed: u64) -> Isaac64Rng { + let mut 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); + + let mut mem = [w(0); RAND_SIZE]; + + a += w(seed); + + for i in (0..RAND_SIZE/8).map(|i| i * 8) { + 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.isaac64(); + // 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. + + let mut rng = Isaac64Rng { + rsl: [w(0); RAND_SIZE], + mem: mem, + a: w(0), + b: w(0), + c: w(0), + cnt: 0, + }; + + // Prepare the first set of results + rng.isaac64(); + rng } /// Refills the output buffer (`self.rsl`) @@ -228,13 +225,6 @@ impl Isaac64Rng { } } -// Cannot be derived because [u32; 256] does not implement Clone -impl Clone for Isaac64Rng { - fn clone(&self) -> Isaac64Rng { - *self - } -} - impl Rng for Isaac64Rng { #[inline] fn next_u32(&mut self) -> u32 { @@ -262,27 +252,79 @@ impl Rng for Isaac64Rng { // it optimises to a bitwise mask). self.rsl[self.cnt as usize % RAND_SIZE].0 } - + fn fill_bytes(&mut self, dest: &mut [u8]) { impls::fill_bytes_via_u64(self, dest) } } -impl<'a> SeedableRng<&'a [u64]> for Isaac64Rng { - fn reseed(&mut self, seed: &'a [u64]) { - // make the seed into [seed[0], seed[1], ..., seed[seed.len() - // - 1], 0, 0, ...], to fill rng.rsl. - let seed_iter = seed.iter().map(|&x| x).chain(repeat(0u64)); +/// Creates a new ISAAC-64 random number generator. +fn init(key: [w64; RAND_SIZE]) -> 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); + + let mut mem = [w(0); RAND_SIZE]; + + macro_rules! memloop { + ($arr:expr) => {{ + for i in (0..RAND_SIZE/8).map(|i| i * 8) { + a += $arr[i ]; b += $arr[i+1]; + c += $arr[i+2]; d += $arr[i+3]; + e += $arr[i+4]; f += $arr[i+5]; + g += $arr[i+6]; h += $arr[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; + } + }} + } - for (rsl_elem, seed_elem) in self.rsl.iter_mut().zip(seed_iter) { - *rsl_elem = w(seed_elem); + memloop!(key); + // Do a second pass to make all of the seed affect all of `mem` + memloop!(mem); + + let mut rng = Isaac64Rng { + rsl: [w(0); RAND_SIZE], + mem: mem, + a: w(0), + b: w(0), + c: w(0), + cnt: 0, + }; + + // Prepare the first set of results + rng.isaac64(); + rng +} + +impl Rand for Isaac64Rng { + fn rand(other: &mut R) -> Isaac64Rng { + let mut key = [w(0); RAND_SIZE]; + unsafe { + let ptr = key.as_mut_ptr() as *mut u8; + + let slice = slice::from_raw_parts_mut(ptr, RAND_SIZE * 8); + other.fill_bytes(slice); } - self.cnt = 0; - self.a = w(0); - self.b = w(0); - self.c = w(0); + init(key) + } +} - self.init(true); +impl<'a> SeedableRng<&'a [u64]> for Isaac64Rng { + fn reseed(&mut self, seed: &'a [u64]) { + *self = Self::from_seed(seed); } /// Create an ISAAC random number generator with a seed. This can @@ -291,34 +333,17 @@ impl<'a> SeedableRng<&'a [u64]> for Isaac64Rng { /// constructed with a given seed will generate the same sequence /// of values as all other generators constructed with that seed. fn from_seed(seed: &'a [u64]) -> Isaac64Rng { - let mut rng = EMPTY_64; - rng.reseed(seed); - rng - } -} + let mut key = [w(0); RAND_SIZE]; -impl Rand for Isaac64Rng { - fn rand(other: &mut R) -> Isaac64Rng { - let mut ret = EMPTY_64; - unsafe { - let ptr = ret.rsl.as_mut_ptr() as *mut u8; + // make the seed into [seed[0], seed[1], ..., seed[seed.len() + // - 1], 0, 0, ...], to fill `key`. + let seed_iter = seed.iter().map(|&x| x).chain(repeat(0u64)); - let slice = slice::from_raw_parts_mut(ptr, RAND_SIZE * 8); - other.fill_bytes(slice); + for (rsl_elem, seed_elem) in key.iter_mut().zip(seed_iter) { + *rsl_elem = w(seed_elem); } - ret.cnt = 0; - ret.a = w(0); - ret.b = w(0); - ret.c = w(0); - ret.init(true); - return ret; - } -} - -impl fmt::Debug for Isaac64Rng { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Isaac64Rng {{}}") + init(key) } } @@ -382,6 +407,25 @@ mod test { 10391409374914919106)); } + #[test] + fn test_isaac_new_uninitialized() { + // Compare the results from initializing `IsaacRng` with + // `new_from_u64(0)`, to make sure it is the same as the reference + // implementation when used uninitialized. + // Note: We only test the first 16 integers, not the full 256 of the + // first block. + let mut rng = Isaac64Rng::new_from_u64(0); + let vec = (0..16).map(|_| rng.next_u64()).collect::>(); + let expected: [u64; 16] = [ + 0xF67DFBA498E4937C, 0x84A5066A9204F380, 0xFEE34BD5F5514DBB, + 0x4D1664739B8F80D6, 0x8607459AB52A14AA, 0x0E78BC5A98529E49, + 0xFE5332822AD13777, 0x556C27525E33D01A, 0x08643CA615F3149F, + 0xD0771FAF3CB04714, 0x30E86F68A37B008D, 0x3074EBC0488A3ADF, + 0x270645EA7A2790BC, 0x5601A0A8D3763C6A, 0x2F83071F53F325DD, + 0xB9090F3D42D2D2EA]; + assert_eq!(vec, expected); + } + #[test] fn test_rng_clone() { let seed: &[_] = &[1, 23, 456, 7890, 12345]; From e8dbf2e1625c01874fd622d61d3210f06318779d Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Fri, 20 Oct 2017 18:20:47 +0200 Subject: [PATCH 06/18] Simplify isaac init code [Cherry-picked from 130b64c00fe94] --- src/prng/isaac.rs | 108 +++++++++++++++----------------------------- src/prng/isaac64.rs | 104 ++++++++++++++---------------------------- 2 files changed, 72 insertions(+), 140 deletions(-) diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index 90ec98d746d..673bf63982a 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -97,6 +97,7 @@ pub struct IsaacRng { } // 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 { @@ -116,62 +117,21 @@ impl fmt::Debug for IsaacRng { } } -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 IsaacRng { /// Creates an ISAAC random number generator using an u64 as seed. /// If `seed == 0` this will produce the same stream of random numbers as /// the reference implementation when used unseeded. pub fn new_from_u64(seed: u64) -> IsaacRng { - let mut 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); - - let mut mem = [w(0); RAND_SIZE]; - - a += w(seed as u32); - b += w((seed >> 32) as u32); - - for i in (0..RAND_SIZE/8).map(|i| i * 8) { - mix(&mut a, &mut b, &mut c, &mut d, &mut e, &mut f, &mut g, &mut h); - mem[i ] = a; mem[i+1] = b; - mem[i+2] = c; mem[i+3] = d; - mem[i+4] = e; mem[i+5] = f; - mem[i+6] = g; mem[i+7] = h; - } + let mut 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. - - let mut rng = IsaacRng { - rsl: [w(0); RAND_SIZE], - mem: mem, - a: w(0), - b: w(0), - c: w(0), - cnt: 0, - }; - - // Prepare the first set of results - rng.isaac(); - rng + init(key, 1) } /// Refills the output buffer (`self.rsl`) @@ -275,7 +235,7 @@ impl Rng for IsaacRng { } } -/// Creates a new ISAAC-64 random number generator. +/// Creates a new ISAAC random number generator. /// /// The author Bob Jenkins describes how to best initialize ISAAC here: /// https://rt.cpan.org/Public/Bug/Display.html?id=64324 @@ -301,7 +261,7 @@ impl Rng for IsaacRng { /// 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(key: [w32; RAND_SIZE]) -> IsaacRng { +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. @@ -314,29 +274,23 @@ fn init(key: [w32; RAND_SIZE]) -> IsaacRng { let mut g = w(0xc4efea1b); let mut h = w(0x30609119); - let mut mem = [w(0); RAND_SIZE]; - - macro_rules! memloop { - ($arr:expr) => {{ - for i in (0..RAND_SIZE/8).map(|i| i * 8) { - a += $arr[i ]; b += $arr[i+1]; - c += $arr[i+2]; d += $arr[i+3]; - e += $arr[i+4]; f += $arr[i+5]; - g += $arr[i+6]; h += $arr[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; - } - }} + // 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; + } } - memloop!(key); - // Do a second pass to make all of the seed affect all of `mem` - memloop!(mem); - let mut rng = IsaacRng { rsl: [w(0); RAND_SIZE], mem: mem, @@ -351,6 +305,18 @@ fn init(key: [w32; RAND_SIZE]) -> IsaacRng { 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 Rand for IsaacRng { fn rand(other: &mut R) -> IsaacRng { let mut key = [w(0); RAND_SIZE]; @@ -361,7 +327,7 @@ impl Rand for IsaacRng { other.fill_bytes(slice); } - init(key) + init(key, 2) } } @@ -385,7 +351,7 @@ impl<'a> SeedableRng<&'a [u32]> for IsaacRng { *rsl_elem = w(seed_elem); } - init(key) + init(key, 2) } } diff --git a/src/prng/isaac64.rs b/src/prng/isaac64.rs index f8ac4538bee..82de34991ce 100644 --- a/src/prng/isaac64.rs +++ b/src/prng/isaac64.rs @@ -81,6 +81,7 @@ pub struct Isaac64Rng { } // 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 { @@ -100,61 +101,20 @@ impl fmt::Debug for Isaac64Rng { } } -fn mix(a: &mut w64, b: &mut w64, c: &mut w64, d: &mut w64, - e: &mut w64, f: &mut w64, g: &mut w64, h: &mut w64) { - *a -= *e; *f ^= *h >> 9; *h += *a; - *b -= *f; *g ^= *a << 9; *a += *b; - *c -= *g; *h ^= *b >> 23; *b += *c; - *d -= *h; *a ^= *c << 15; *c += *d; - *e -= *a; *b ^= *d >> 14; *d += *e; - *f -= *b; *c ^= *e << 20; *e += *f; - *g -= *c; *d ^= *f >> 17; *f += *g; - *h -= *d; *e ^= *g << 14; *g += *h; -} - impl Isaac64Rng { /// Creates an ISAAC-64 random number generator using an u64 as seed. /// If `seed == 0` this will produce the same stream of random numbers as /// the reference implementation when used unseeded. pub fn new_from_u64(seed: u64) -> Isaac64Rng { - let mut 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); - - let mut mem = [w(0); RAND_SIZE]; - - a += w(seed); - - for i in (0..RAND_SIZE/8).map(|i| i * 8) { - mix(&mut a, &mut b, &mut c, &mut d, &mut e, &mut f, &mut g, &mut h); - mem[i ] = a; mem[i+1] = b; - mem[i+2] = c; mem[i+3] = d; - mem[i+4] = e; mem[i+5] = f; - mem[i+6] = g; mem[i+7] = h; - } + let mut 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. - - let mut rng = Isaac64Rng { - rsl: [w(0); RAND_SIZE], - mem: mem, - a: w(0), - b: w(0), - c: w(0), - cnt: 0, - }; - - // Prepare the first set of results - rng.isaac64(); - rng + init(key, 1) } /// Refills the output buffer (`self.rsl`) @@ -259,7 +219,7 @@ impl Rng for Isaac64Rng { } /// Creates a new ISAAC-64 random number generator. -fn init(key: [w64; RAND_SIZE]) -> Isaac64Rng { +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. @@ -272,29 +232,23 @@ fn init(key: [w64; RAND_SIZE]) -> Isaac64Rng { let mut g = w(0xae985bf2cbfc89ed); let mut h = w(0x98f5704f6c44c0ab); - let mut mem = [w(0); RAND_SIZE]; - - macro_rules! memloop { - ($arr:expr) => {{ - for i in (0..RAND_SIZE/8).map(|i| i * 8) { - a += $arr[i ]; b += $arr[i+1]; - c += $arr[i+2]; d += $arr[i+3]; - e += $arr[i+4]; f += $arr[i+5]; - g += $arr[i+6]; h += $arr[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; - } - }} + // 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; + } } - memloop!(key); - // Do a second pass to make all of the seed affect all of `mem` - memloop!(mem); - let mut rng = Isaac64Rng { rsl: [w(0); RAND_SIZE], mem: mem, @@ -309,6 +263,18 @@ fn init(key: [w64; RAND_SIZE]) -> Isaac64Rng { rng } +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 Rand for Isaac64Rng { fn rand(other: &mut R) -> Isaac64Rng { let mut key = [w(0); RAND_SIZE]; @@ -318,7 +284,7 @@ impl Rand for Isaac64Rng { let slice = slice::from_raw_parts_mut(ptr, RAND_SIZE * 8); other.fill_bytes(slice); } - init(key) + init(key, 2) } } @@ -343,7 +309,7 @@ impl<'a> SeedableRng<&'a [u64]> for Isaac64Rng { *rsl_elem = w(seed_elem); } - init(key) + init(key, 2) } } From 5c300f655057724d695b55166bbba929fd4eeeb1 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Sat, 21 Oct 2017 17:02:09 +0200 Subject: [PATCH 07/18] Remove `Copy` trait from ChaCha --- src/prng/chacha.rs | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/src/prng/chacha.rs b/src/prng/chacha.rs index 9cc0217e987..39f89325976 100644 --- a/src/prng/chacha.rs +++ b/src/prng/chacha.rs @@ -30,20 +30,13 @@ const CHACHA_ROUNDS: u32 = 20; // Cryptographically secure from 8 upwards as of /// /// [1]: D. J. Bernstein, [*ChaCha, a variant of /// Salsa20*](http://cr.yp.to/chacha.html) -#[derive(Copy, Clone, Debug)] +#[derive(Clone, Debug)] pub struct ChaChaRng { buffer: [w32; STATE_WORDS], // Internal buffer of output state: [w32; STATE_WORDS], // Initial state index: usize, // Index into state } -static EMPTY: ChaChaRng = ChaChaRng { - buffer: [w(0); STATE_WORDS], - state: [w(0); STATE_WORDS], - index: STATE_WORDS -}; - - macro_rules! quarter_round{ ($a: expr, $b: expr, $c: expr, $d: expr) => {{ $a = $a + $b; $d = $d ^ $a; $d = w($d.0.rotate_left(16)); @@ -103,7 +96,11 @@ impl ChaChaRng { /// - 2917185654 /// - 2419978656 pub fn new_unseeded() -> ChaChaRng { - let mut rng = EMPTY; + let mut rng = ChaChaRng { + buffer: [w(0); STATE_WORDS], + state: [w(0); STATE_WORDS], + index: STATE_WORDS + }; rng.init(&[0; KEY_WORDS]); rng } @@ -210,13 +207,7 @@ impl Rng for ChaChaRng { impl<'a> SeedableRng<&'a [u32]> for ChaChaRng { fn reseed(&mut self, seed: &'a [u32]) { - // reset state - self.init(&[0u32; KEY_WORDS]); - // set key in place - let key = &mut self.state[4 .. 4+KEY_WORDS]; - for (k, s) in key.iter_mut().zip(seed.iter()) { - *k = w(*s); - } + *self = Self::from_seed(seed); } /// Create a ChaCha generator from a seed, @@ -224,8 +215,19 @@ impl<'a> SeedableRng<&'a [u32]> for ChaChaRng { /// Only up to 8 words are used; if less than 8 /// words are used, the remaining are set to zero. fn from_seed(seed: &'a [u32]) -> ChaChaRng { - let mut rng = EMPTY; - rng.reseed(seed); + let mut rng = ChaChaRng { + buffer: [w(0); STATE_WORDS], + state: [w(0); STATE_WORDS], + index: STATE_WORDS + }; + rng.init(&[0u32; KEY_WORDS]); + // set key in place + { + let key = &mut rng.state[4 .. 4+KEY_WORDS]; + for (k, s) in key.iter_mut().zip(seed.iter()) { + *k = w(*s); + } + } rng } } From d16b9cb02dbea5b3a040f7397f23e0981181a6fe Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Sat, 21 Oct 2017 18:56:15 +0200 Subject: [PATCH 08/18] Custom Debug implementation for ChaCha and Xorshift So the internal state is never exposed (may be security-sensitive) [Cherry-picked from e513aaa8e2d933] --- src/prng/chacha.rs | 10 +++++++++- src/prng/isaac.rs | 1 + src/prng/isaac64.rs | 1 + src/prng/xorshift.rs | 11 +++++++++-- 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/prng/chacha.rs b/src/prng/chacha.rs index 39f89325976..5950802be8a 100644 --- a/src/prng/chacha.rs +++ b/src/prng/chacha.rs @@ -11,6 +11,7 @@ //! The ChaCha random number generator. use core::num::Wrapping as w; +use core::fmt; use {Rng, SeedableRng, Rand}; use impls; @@ -30,13 +31,20 @@ const CHACHA_ROUNDS: u32 = 20; // Cryptographically secure from 8 upwards as of /// /// [1]: D. J. Bernstein, [*ChaCha, a variant of /// Salsa20*](http://cr.yp.to/chacha.html) -#[derive(Clone, Debug)] +#[derive(Clone)] pub struct ChaChaRng { buffer: [w32; STATE_WORDS], // Internal buffer of output state: [w32; STATE_WORDS], // Initial state index: usize, // Index into state } +// Custom Debug implementation that does not expose the internal state +impl fmt::Debug for ChaChaRng { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "ChaChaRng {{}}") + } +} + macro_rules! quarter_round{ ($a: expr, $b: expr, $c: expr, $d: expr) => {{ $a = $a + $b; $d = $d ^ $a; $d = w($d.0.rotate_left(16)); diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index 673bf63982a..23f8c3d9a3c 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -111,6 +111,7 @@ impl Clone for IsaacRng { } } +// Custom Debug implementation that does not expose the internal state impl fmt::Debug for IsaacRng { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "IsaacRng {{}}") diff --git a/src/prng/isaac64.rs b/src/prng/isaac64.rs index 82de34991ce..08949765ca8 100644 --- a/src/prng/isaac64.rs +++ b/src/prng/isaac64.rs @@ -95,6 +95,7 @@ impl Clone for Isaac64Rng { } } +// 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 {{}}") diff --git a/src/prng/xorshift.rs b/src/prng/xorshift.rs index 0f5ba076fc1..19e92aefe62 100644 --- a/src/prng/xorshift.rs +++ b/src/prng/xorshift.rs @@ -11,6 +11,7 @@ //! Xorshift generators use core::num::Wrapping as w; +use core::fmt; use {Rng, SeedableRng, Rand}; use impls; @@ -24,8 +25,7 @@ use impls; /// [1]: Marsaglia, George (July 2003). ["Xorshift /// RNGs"](http://www.jstatsoft.org/v08/i14/paper). *Journal of /// Statistical Software*. Vol. 8 (Issue 14). -#[allow(missing_copy_implementations)] -#[derive(Clone, Debug)] +#[derive(Clone)] pub struct XorShiftRng { x: w, y: w, @@ -33,6 +33,13 @@ pub struct XorShiftRng { w: w, } +// Custom Debug implementation that does not expose the internal state +impl fmt::Debug for XorShiftRng { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "XorShiftRng {{}}") + } +} + impl XorShiftRng { /// Creates a new XorShiftRng instance which is not seeded. /// From 164f2898818815afcad6cd59ba66d91f8534b25b Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Fri, 10 Nov 2017 17:21:53 +0000 Subject: [PATCH 09/18] Add true_bytes tests for ChaCha and Isaac; fix 2 bugs in fill_bytes impls [Cherry-picked from ae365ef352eeb0a] --- src/prng/chacha.rs | 14 ++++++++++++++ src/prng/isaac.rs | 14 ++++++++++++++ src/prng/isaac64.rs | 16 +++++++++++++++- 3 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/prng/chacha.rs b/src/prng/chacha.rs index 5950802be8a..35ddcecf587 100644 --- a/src/prng/chacha.rs +++ b/src/prng/chacha.rs @@ -328,6 +328,20 @@ mod test { 0x2c5bad8f, 0x898881dc, 0x5f1c86d9, 0xc1f8e7f4)); } + #[test] + fn test_rng_true_bytes() { + let seed : &[_] = &[0u32; 8]; + let mut ra: ChaChaRng = SeedableRng::from_seed(seed); + let mut buf = [0u8; 32]; + ra.fill_bytes(&mut buf); + // Same as first values in test_isaac_true_values as bytes in LE order + assert_eq!(buf, + [118, 184, 224, 173, 160, 241, 61, 144, + 64, 93, 106, 229, 83, 134, 189, 40, + 189, 210, 25, 184, 160, 141, 237, 26, + 168, 54, 239, 204, 139, 119, 13, 199]); + } + #[test] fn test_rng_clone() { let seed : &[_] = &[0u32; 8]; diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index 23f8c3d9a3c..600a2a33bc4 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -412,6 +412,20 @@ mod test { 1576568959, 3507990155, 179069555, 141456972, 2478885421)); } + #[test] + fn test_isaac_true_bytes() { + let seed: &[_] = &[1, 23, 456, 7890, 12345]; + let mut rng1 = IsaacRng::from_seed(seed); + let mut buf = [0u8; 32]; + rng1.fill_bytes(&mut buf); + // Same as first values in test_isaac_true_values as bytes in LE order + assert_eq!(buf, + [82, 186, 128, 152, 71, 240, 20, 52, + 45, 175, 180, 15, 86, 16, 99, 125, + 101, 203, 81, 214, 97, 162, 134, 250, + 103, 78, 203, 15, 150, 3, 210, 164]); + } + #[test] fn test_isaac_new_uninitialized() { // Compare the results from initializing `IsaacRng` with diff --git a/src/prng/isaac64.rs b/src/prng/isaac64.rs index 08949765ca8..7df5882897a 100644 --- a/src/prng/isaac64.rs +++ b/src/prng/isaac64.rs @@ -373,7 +373,21 @@ mod test { 596345674630742204, 9947027391921273664, 11788097613744130851, 10391409374914919106)); } - + + #[test] + fn test_isaac64_true_bytes() { + let seed: &[_] = &[1, 23, 456, 7890, 12345]; + let mut rng1 = Isaac64Rng::from_seed(seed); + let mut buf = [0u8; 32]; + rng1.fill_bytes(&mut buf); + // Same as first values in test_isaac64_true_values as bytes in LE order + assert_eq!(buf, + [140, 237, 103, 8, 93, 196, 151, 7, + 156, 242, 26, 63, 54, 166, 135, 199, + 141, 186, 192, 50, 116, 69, 205, 240, + 98, 205, 127, 160, 83, 98, 49, 17]); + } + #[test] fn test_isaac_new_uninitialized() { // Compare the results from initializing `IsaacRng` with From a7e9d77cd30e13734fff0ce5420284692915d1e9 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Fri, 10 Nov 2017 17:34:55 +0000 Subject: [PATCH 10/18] Isaac64: add test for true 32-bit values Includes both the values output now and the values which should be output by #36. [Cherry-picked from fd2660b54272653] --- src/prng/isaac64.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/prng/isaac64.rs b/src/prng/isaac64.rs index 7df5882897a..7afffbae76f 100644 --- a/src/prng/isaac64.rs +++ b/src/prng/isaac64.rs @@ -374,6 +374,28 @@ mod test { 10391409374914919106)); } + #[test] + fn test_isaac64_true_values_32() { + let seed: &[_] = &[1, 23, 456, 7890, 12345]; + let mut rng1 = Isaac64Rng::from_seed(seed); + let v = (0..10).map(|_| rng1.next_u32()).collect::>(); + // Subset of above values, as an LE u32 sequence + // TODO: switch to this sequence? +// assert_eq!(v, +// [141028748, 127386717, +// 1058730652, 3347555894, +// 851491469, 4039984500, +// 2692730210, 288449107, +// 646103879, 2782923823]); + // Subset of above values, using only low-half of each u64 + assert_eq!(v, + [141028748, 1058730652, + 851491469, 2692730210, + 646103879, 4195642895, + 2836348583, 1312677241, + 999139615, 253604626]); + } + #[test] fn test_isaac64_true_bytes() { let seed: &[_] = &[1, 23, 456, 7890, 12345]; From 362d4aa6d498161a9f1086404ee7ba099b1c4f52 Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Sat, 11 Nov 2017 07:43:26 +0100 Subject: [PATCH 11/18] Convert ChaCha to use `fill_via_u32_chunks` This also replaces `core::num::Wrapping` with a few `wrapping_add`'s. There were about 30 conversions to and from `Wrapping`, while there are only 9 wrapping operations. Because `fill_via_u32_chunks` expects a `[u32]`, converting away was just easier. [Cherry-picked from d7b014c143e2a3] --- src/prng/chacha.rs | 89 +++++++++++++++++++++++----------------------- 1 file changed, 45 insertions(+), 44 deletions(-) diff --git a/src/prng/chacha.rs b/src/prng/chacha.rs index 35ddcecf587..6468f1624a8 100644 --- a/src/prng/chacha.rs +++ b/src/prng/chacha.rs @@ -10,14 +10,10 @@ //! The ChaCha random number generator. -use core::num::Wrapping as w; use core::fmt; use {Rng, SeedableRng, Rand}; use impls; -#[allow(bad_style)] -type w32 = w; - const KEY_WORDS : usize = 8; // 8 words for the 256-bit key const STATE_WORDS : usize = 16; const CHACHA_ROUNDS: u32 = 20; // Cryptographically secure from 8 upwards as of this writing @@ -33,9 +29,9 @@ const CHACHA_ROUNDS: u32 = 20; // Cryptographically secure from 8 upwards as of /// Salsa20*](http://cr.yp.to/chacha.html) #[derive(Clone)] pub struct ChaChaRng { - buffer: [w32; STATE_WORDS], // Internal buffer of output - state: [w32; STATE_WORDS], // Initial state - index: usize, // Index into state + buffer: [u32; STATE_WORDS], // Internal buffer of output + state: [u32; STATE_WORDS], // Initial state + index: usize, // Index into state } // Custom Debug implementation that does not expose the internal state @@ -47,10 +43,10 @@ impl fmt::Debug for ChaChaRng { macro_rules! quarter_round{ ($a: expr, $b: expr, $c: expr, $d: expr) => {{ - $a = $a + $b; $d = $d ^ $a; $d = w($d.0.rotate_left(16)); - $c = $c + $d; $b = $b ^ $c; $b = w($b.0.rotate_left(12)); - $a = $a + $b; $d = $d ^ $a; $d = w($d.0.rotate_left( 8)); - $c = $c + $d; $b = $b ^ $c; $b = w($b.0.rotate_left( 7)); + $a = $a.wrapping_add($b); $d ^= $a; $d = $d.rotate_left(16); + $c = $c.wrapping_add($d); $b ^= $c; $b = $b.rotate_left(12); + $a = $a.wrapping_add($b); $d ^= $a; $d = $d.rotate_left( 8); + $c = $c.wrapping_add($d); $b ^= $c; $b = $b.rotate_left( 7); }} } @@ -70,15 +66,15 @@ macro_rules! double_round{ } #[inline] -fn core(output: &mut [w32; STATE_WORDS], input: &[w32; STATE_WORDS]) { - *output = *input; +fn core(new: &mut [u32; STATE_WORDS], input: &[u32; STATE_WORDS]) { + *new = *input; for _ in 0..CHACHA_ROUNDS / 2 { - double_round!(output); + double_round!(new); } for i in 0..STATE_WORDS { - output[i] = output[i] + input[i]; + new[i] = new[i].wrapping_add(input[i]); } } @@ -105,8 +101,8 @@ impl ChaChaRng { /// - 2419978656 pub fn new_unseeded() -> ChaChaRng { let mut rng = ChaChaRng { - buffer: [w(0); STATE_WORDS], - state: [w(0); STATE_WORDS], + buffer: [0; STATE_WORDS], + state: [0; STATE_WORDS], index: STATE_WORDS }; rng.init(&[0; KEY_WORDS]); @@ -133,10 +129,10 @@ impl ChaChaRng { /// println!("{:?}", ra.next_u32()); /// ``` pub fn set_counter(&mut self, counter_low: u64, counter_high: u64) { - self.state[12] = w((counter_low >> 0) as u32); - self.state[13] = w((counter_low >> 32) as u32); - self.state[14] = w((counter_high >> 0) as u32); - self.state[15] = w((counter_high >> 32) as u32); + self.state[12] = (counter_low >> 0) as u32; + self.state[13] = (counter_low >> 32) as u32; + self.state[14] = (counter_high >> 0) as u32; + self.state[15] = (counter_high >> 32) as u32; self.index = STATE_WORDS; // force recomputation } @@ -159,19 +155,19 @@ impl ChaChaRng { /// [1]: Daniel J. Bernstein. [*Extending the Salsa20 /// nonce.*](http://cr.yp.to/papers.html#xsalsa) fn init(&mut self, key: &[u32; KEY_WORDS]) { - self.state[0] = w(0x61707865); - self.state[1] = w(0x3320646E); - self.state[2] = w(0x79622D32); - self.state[3] = w(0x6B206574); + self.state[0] = 0x61707865; + self.state[1] = 0x3320646E; + self.state[2] = 0x79622D32; + self.state[3] = 0x6B206574; for i in 0..KEY_WORDS { - self.state[4+i] = w(key[i]); + self.state[4+i] = key[i]; } - self.state[12] = w(0); - self.state[13] = w(0); - self.state[14] = w(0); - self.state[15] = w(0); + self.state[12] = 0; + self.state[13] = 0; + self.state[14] = 0; + self.state[15] = 0; self.index = STATE_WORDS; } @@ -181,31 +177,36 @@ impl ChaChaRng { core(&mut self.buffer, &self.state); self.index = 0; // update 128-bit counter - self.state[12] = self.state[12] + w(1); - if self.state[12] != w(0) { return }; - self.state[13] = self.state[13] + w(1); - if self.state[13] != w(0) { return }; - self.state[14] = self.state[14] + w(1); - if self.state[14] != w(0) { return }; - self.state[15] = self.state[15] + w(1); + self.state[12] = self.state[12].wrapping_add(1); + if self.state[12] != 0 { return }; + self.state[13] = self.state[13].wrapping_add(1); + if self.state[13] != 0 { return }; + self.state[14] = self.state[14].wrapping_add(1); + if self.state[14] != 0 { return }; + self.state[15] = self.state[15].wrapping_add(1); } } impl Rng for ChaChaRng { #[inline] fn next_u32(&mut self) -> u32 { - if self.index == STATE_WORDS { + // Using a local variable for `index`, and checking the size avoids a + // bounds check later on. + let mut index = self.index as usize; + if index >= STATE_WORDS { self.update(); + index = 0; } - let value = self.buffer[self.index % STATE_WORDS]; + let value = self.buffer[index]; self.index += 1; - value.0 + value } - + fn next_u64(&mut self) -> u64 { impls::next_u64_via_u32(self) } + fn fill_bytes(&mut self, bytes: &mut [u8]) { impls::fill_bytes_via_u32(self, bytes) @@ -224,8 +225,8 @@ impl<'a> SeedableRng<&'a [u32]> for ChaChaRng { /// words are used, the remaining are set to zero. fn from_seed(seed: &'a [u32]) -> ChaChaRng { let mut rng = ChaChaRng { - buffer: [w(0); STATE_WORDS], - state: [w(0); STATE_WORDS], + buffer: [0; STATE_WORDS], + state: [0; STATE_WORDS], index: STATE_WORDS }; rng.init(&[0u32; KEY_WORDS]); @@ -233,7 +234,7 @@ impl<'a> SeedableRng<&'a [u32]> for ChaChaRng { { let key = &mut rng.state[4 .. 4+KEY_WORDS]; for (k, s) in key.iter_mut().zip(seed.iter()) { - *k = w(*s); + *k = *s; } } rng From 74660c2e2e639604b44e2a1a7a06263d1c2389fc Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Sat, 11 Nov 2017 07:46:17 +0100 Subject: [PATCH 12/18] Fill `isaac` backwards, and use `fill_via_u32_chunks` Also uses a different solution to index without bounds checking, to recover a very little bit of lost performance. [Cherry-picked from f92a3476c730d] --- src/prng/isaac.rs | 61 +++++++++++++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index 600a2a33bc4..5aa0e69046c 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -88,12 +88,12 @@ const RAND_SIZE: usize = 1 << RAND_SIZE_LEN; /// [3]: Jean-Philippe Aumasson, [*On the pseudo-random generator ISAAC*] /// (http://eprint.iacr.org/2006/438) pub struct IsaacRng { - rsl: [w32; RAND_SIZE], + rsl: [u32; RAND_SIZE], mem: [w32; RAND_SIZE], a: w32, b: w32, c: w32, - cnt: u32, + index: u32, } // Cannot be derived because [u32; 256] does not implement Clone @@ -106,7 +106,7 @@ impl Clone for IsaacRng { a: self.a, b: self.b, c: self.c, - cnt: self.cnt, + index: self.index, } } } @@ -150,6 +150,9 @@ 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) { self.c += w(1); // abbreviations @@ -157,13 +160,13 @@ impl IsaacRng { let mut b = self.b + self.c; const MIDPOINT: usize = RAND_SIZE / 2; - #[inline(always)] + #[inline] fn ind(mem:&[w32; RAND_SIZE], v: w32, amount: usize) -> w32 { let index = (v >> amount).0 as usize % RAND_SIZE; mem[index] } - #[inline(always)] + #[inline] fn rngstep(ctx: &mut IsaacRng, mix: w32, a: &mut w32, @@ -176,7 +179,7 @@ impl IsaacRng { 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[base + m] = *b; + ctx.rsl[RAND_SIZE - 1 - base - m] = (*b).0; } let mut m = 0; @@ -199,40 +202,46 @@ impl IsaacRng { self.a = a; self.b = b; - self.cnt = RAND_SIZE as u32; + self.index = 0; } } impl Rng for IsaacRng { #[inline] fn next_u32(&mut self) -> u32 { - if self.cnt == 0 { - // make some more numbers + // Using a local variable for `index`, and checking the size avoids a + // bounds check later on. + let mut index = self.index as usize; + if index >= RAND_SIZE { self.isaac(); + index = 0; } - self.cnt -= 1; - - // self.cnt is at most RAND_SIZE, but that is before the - // subtraction above. We want to index without bounds - // checking, but this could lead to incorrect code if someone - // misrefactors, so we check, sometimes. - // - // (Changes here should be reflected in Isaac64Rng.next_u64.) - debug_assert!((self.cnt as usize) < RAND_SIZE); - - // (the % is cheaply telling the optimiser that we're always - // in bounds, without unsafe. NB. this is a power of two, so - // it optimises to a bitwise mask). - self.rsl[self.cnt as usize % RAND_SIZE].0 + + 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]) { - impls::fill_bytes_via_u32(self, dest) + let mut read_len = 0; + while read_len < dest.len() { + if self.index as usize >= RAND_SIZE { + self.isaac(); + } + + let (consumed_u32, filled_u8) = + impls::fill_via_u32_chunks(&mut self.rsl[(self.index as usize)..], + &mut dest[read_len..]); + + self.index += consumed_u32 as u32; + read_len += filled_u8; + } } } @@ -293,12 +302,12 @@ fn init(mut mem: [w32; RAND_SIZE], rounds: u32) -> IsaacRng { } let mut rng = IsaacRng { - rsl: [w(0); RAND_SIZE], + rsl: [0; RAND_SIZE], mem: mem, a: w(0), b: w(0), c: w(0), - cnt: 0, + index: 0, }; // Prepare the first set of results From 62b5722bf7c794e73285070c02e5cc25f710fdaa Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Sat, 11 Nov 2017 07:52:59 +0100 Subject: [PATCH 13/18] Fill `isaac64` backwards, and use `fill_via_u32_chunks` [Cherry-picked from 707c3e109209962] --- src/prng/isaac64.rs | 58 +++++++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/src/prng/isaac64.rs b/src/prng/isaac64.rs index 7afffbae76f..0d3cf8e2625 100644 --- a/src/prng/isaac64.rs +++ b/src/prng/isaac64.rs @@ -72,12 +72,12 @@ const RAND_SIZE: usize = 1 << RAND_SIZE_LEN; /// [1]: Bob Jenkins, [*ISAAC and RC4*] /// (http://burtleburtle.net/bob/rand/isaac.html) pub struct Isaac64Rng { - rsl: [w64; RAND_SIZE], + rsl: [u64; RAND_SIZE], mem: [w64; RAND_SIZE], a: w64, b: w64, c: w64, - cnt: u32, + index: u32, } // Cannot be derived because [u64; 256] does not implement Clone @@ -90,7 +90,7 @@ impl Clone for Isaac64Rng { a: self.a, b: self.b, c: self.c, - cnt: self.cnt, + index: self.index, } } } @@ -133,6 +133,9 @@ 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) { self.c += w(1); // abbreviations @@ -140,13 +143,13 @@ impl Isaac64Rng { let mut b = self.b + self.c; const MIDPOINT: usize = RAND_SIZE / 2; - #[inline(always)] + #[inline] fn ind(mem:&[w64; RAND_SIZE], v: w64, amount: usize) -> w64 { let index = (v >> amount).0 as usize % RAND_SIZE; mem[index] } - #[inline(always)] + #[inline] fn rngstep(ctx: &mut Isaac64Rng, mix: w64, a: &mut w64, @@ -159,7 +162,7 @@ impl Isaac64Rng { 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[base + m] = *b; + ctx.rsl[RAND_SIZE - 1 - base - m] = (*b).0; } let mut m = 0; @@ -182,7 +185,7 @@ impl Isaac64Rng { self.a = a; self.b = b; - self.cnt = RAND_SIZE as u32; + self.index = 0; } } @@ -194,28 +197,31 @@ impl Rng for Isaac64Rng { #[inline] fn next_u64(&mut self) -> u64 { - if self.cnt == 0 { - // make some more numbers + let mut index = self.index as usize; + if index >= RAND_SIZE { self.isaac64(); + index = 0; } - self.cnt -= 1; - - // self.cnt is at most RAND_SIZE, but that is before the - // subtraction above. We want to index without bounds - // checking, but this could lead to incorrect code if someone - // misrefactors, so we check, sometimes. - // - // (Changes here should be reflected in IsaacRng.next_u32.) - debug_assert!((self.cnt as usize) < RAND_SIZE); - - // (the % is cheaply telling the optimiser that we're always - // in bounds, without unsafe. NB. this is a power of two, so - // it optimises to a bitwise mask). - self.rsl[self.cnt as usize % RAND_SIZE].0 + + let value = self.rsl[index]; + self.index += 1; + value } fn fill_bytes(&mut self, dest: &mut [u8]) { - impls::fill_bytes_via_u64(self, dest) + let mut read_len = 0; + while read_len < dest.len() { + if self.index as usize >= RAND_SIZE { + self.isaac64(); + } + + let (consumed_u64, filled_u8) = + impls::fill_via_u64_chunks(&mut self.rsl[(self.index as usize)..], + &mut dest[read_len..]); + + self.index += consumed_u64 as u32; + read_len += filled_u8; + } } } @@ -251,12 +257,12 @@ fn init(mut mem: [w64; RAND_SIZE], rounds: u32) -> Isaac64Rng { } let mut rng = Isaac64Rng { - rsl: [w(0); RAND_SIZE], + rsl: [0; RAND_SIZE], mem: mem, a: w(0), b: w(0), c: w(0), - cnt: 0, + index: 0, }; // Prepare the first set of results From 865ed542b4ca275ca5c84c5dee1363321480002c Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Sat, 11 Nov 2017 08:04:20 +0100 Subject: [PATCH 14/18] Improve performance of `isaac64::next_u32`. This is 45% faster. We are no longer throwing away half of the results. [Cherry-picked from 415ef6f440cce and 0bdb1c3926] --- src/prng/isaac64.rs | 46 ++++++++++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/src/prng/isaac64.rs b/src/prng/isaac64.rs index 0d3cf8e2625..d0498348be5 100644 --- a/src/prng/isaac64.rs +++ b/src/prng/isaac64.rs @@ -78,6 +78,7 @@ pub struct Isaac64Rng { b: w64, c: w64, index: u32, + half_used: bool, // true if only half of the previous result is used } // Cannot be derived because [u64; 256] does not implement Clone @@ -91,6 +92,7 @@ impl Clone for Isaac64Rng { b: self.b, c: self.c, index: self.index, + half_used: self.half_used, } } } @@ -186,13 +188,33 @@ impl Isaac64Rng { self.a = a; self.b = b; self.index = 0; + self.half_used = false; } } impl Rng for Isaac64Rng { #[inline] fn next_u32(&mut self) -> u32 { - self.next_u64() as u32 + // Using a local variable for `index`, and checking the size avoids a + // bounds check later on. + let mut index = self.index as usize * 2 - self.half_used as usize; + if index >= RAND_SIZE * 2 { + self.isaac64(); + index = 0; + } + + self.half_used = !self.half_used; + self.index += self.half_used as u32; + + // Index as if this is a u32 slice. + let rsl = unsafe { &*(&mut self.rsl as *mut [u64; RAND_SIZE] + as *mut [u32; RAND_SIZE * 2]) }; + + if cfg!(target_endian = "little") { + rsl[index] + } else { + rsl[index ^ 1] + } } #[inline] @@ -205,6 +227,7 @@ impl Rng for Isaac64Rng { let value = self.rsl[index]; self.index += 1; + self.half_used = false; value } @@ -216,7 +239,7 @@ impl Rng for Isaac64Rng { } let (consumed_u64, filled_u8) = - impls::fill_via_u64_chunks(&mut self.rsl[(self.index as usize)..], + impls::fill_via_u64_chunks(&mut self.rsl[self.index as usize..], &mut dest[read_len..]); self.index += consumed_u64 as u32; @@ -263,6 +286,7 @@ fn init(mut mem: [w64; RAND_SIZE], rounds: u32) -> Isaac64Rng { b: w(0), c: w(0), index: 0, + half_used: false, }; // Prepare the first set of results @@ -386,20 +410,12 @@ mod test { let mut rng1 = Isaac64Rng::from_seed(seed); let v = (0..10).map(|_| rng1.next_u32()).collect::>(); // Subset of above values, as an LE u32 sequence - // TODO: switch to this sequence? -// assert_eq!(v, -// [141028748, 127386717, -// 1058730652, 3347555894, -// 851491469, 4039984500, -// 2692730210, 288449107, -// 646103879, 2782923823]); - // Subset of above values, using only low-half of each u64 assert_eq!(v, - [141028748, 1058730652, - 851491469, 2692730210, - 646103879, 4195642895, - 2836348583, 1312677241, - 999139615, 253604626]); + [141028748, 127386717, + 1058730652, 3347555894, + 851491469, 4039984500, + 2692730210, 288449107, + 646103879, 2782923823]); } #[test] From 31dd85d3040b51dfbe39bed7a53db044ef6466bc Mon Sep 17 00:00:00 2001 From: Paul Dicker Date: Sat, 11 Nov 2017 14:03:42 +0100 Subject: [PATCH 15/18] Add test for alternating between `next_u64` and `next_u32` [Cherry-picked from 5f4bedf78c] --- src/prng/isaac64.rs | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/src/prng/isaac64.rs b/src/prng/isaac64.rs index d0498348be5..8cabca38a87 100644 --- a/src/prng/isaac64.rs +++ b/src/prng/isaac64.rs @@ -403,21 +403,39 @@ mod test { 596345674630742204, 9947027391921273664, 11788097613744130851, 10391409374914919106)); } - + #[test] fn test_isaac64_true_values_32() { let seed: &[_] = &[1, 23, 456, 7890, 12345]; let mut rng1 = Isaac64Rng::from_seed(seed); - let v = (0..10).map(|_| rng1.next_u32()).collect::>(); + let v = (0..12).map(|_| rng1.next_u32()).collect::>(); // Subset of above values, as an LE u32 sequence assert_eq!(v, [141028748, 127386717, 1058730652, 3347555894, 851491469, 4039984500, 2692730210, 288449107, - 646103879, 2782923823]); + 646103879, 2782923823, + 4195642895, 3252674613]); } - + + #[test] + fn test_isaac64_true_values_mixed() { + let seed: &[_] = &[1, 23, 456, 7890, 12345]; + let mut rng = Isaac64Rng::from_seed(seed); + // Test alternating between `next_u64` and `next_u32` works as expected. + // Values are the same as `test_isaac64_true_values` and + // `test_isaac64_true_values_32`. + assert_eq!(rng.next_u64(), 547121783600835980); + assert_eq!(rng.next_u32(), 1058730652); + assert_eq!(rng.next_u32(), 3347555894); + assert_eq!(rng.next_u64(), 17351601304698403469); + assert_eq!(rng.next_u32(), 2692730210); + // Skip one u32 + assert_eq!(rng.next_u64(), 11952566807690396487); + assert_eq!(rng.next_u32(), 4195642895); + } + #[test] fn test_isaac64_true_bytes() { let seed: &[_] = &[1, 23, 456, 7890, 12345]; @@ -431,7 +449,7 @@ mod test { 141, 186, 192, 50, 116, 69, 205, 240, 98, 205, 127, 160, 83, 98, 49, 17]); } - + #[test] fn test_isaac_new_uninitialized() { // Compare the results from initializing `IsaacRng` with From bf9f9059a5485e309617d465ee915f432ac67993 Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Wed, 20 Dec 2017 16:21:15 +0000 Subject: [PATCH 16/18] Test next_u64 on 32-bit generator to check LE conversion --- src/prng/isaac.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index 5aa0e69046c..16b6f6b74a5 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -421,6 +421,17 @@ mod test { 1576568959, 3507990155, 179069555, 141456972, 2478885421)); } + #[test] + fn test_rng_64_true_values() { + // As above, using little-endian versions of above values + let seed: &[_] = &[1, 23, 456, 7890, 12345]; + let mut ra: IsaacRng = SeedableRng::from_seed(seed); + // Regression test that isaac is actually using the above vector + let v = (0..5).map(|_| ra.next_u64()).collect::>(); + assert_eq!(v, + vec!(3752888579798383186, 9035083239252078381, 18052294697452424037, 11876559110374379111, 16751462502657800130)); + } + #[test] fn test_isaac_true_bytes() { let seed: &[_] = &[1, 23, 456, 7890, 12345]; From 8ad6e2ac7ac3945bbc16c5af186391d56f77426f Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Wed, 27 Dec 2017 11:58:57 +0000 Subject: [PATCH 17/18] Revert some breaking changes Restore default implementations of next_u64 and fill_bytes Restore new_unseeded functions (as wrappers) --- src/lib.rs | 20 ++++++++++++++++++-- src/prng/isaac.rs | 6 ++++++ src/prng/isaac64.rs | 6 ++++++ 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 2c0a620f18b..9bbe904e00a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -343,7 +343,15 @@ pub trait Rng { fn next_u32(&mut self) -> u32; /// Return the next random u64. - fn next_u64(&mut self) -> u64; + /// + /// This function has a default implementation of `next_u32`. The + /// default implementation should not be used in wrapper types since the + /// wrapped RNG may have its own implementation which may be more efficient + /// or even produce different results. + fn next_u64(&mut self) -> u64 { + // TODO: remove default implementation once impls module is exposed + impls::next_u64_via_u32(self) + } /// Return the next random f32 selected from the half-open /// interval `[0, 1)`. @@ -398,6 +406,11 @@ pub trait Rng { } /// Fill `dest` with random data. + /// + /// This function has a default implementation in terms of `next_u64`. The + /// default implementation should not be used in wrapper types since the + /// wrapped RNG may have its own implementation which may be more efficient + /// or even produce different results. /// /// This method does *not* have a requirement to bear any fixed /// relationship to the other methods, for example, it does *not* @@ -419,7 +432,10 @@ pub trait Rng { /// thread_rng().fill_bytes(&mut v); /// println!("{:?}", &v[..]); /// ``` - fn fill_bytes(&mut self, dest: &mut [u8]); + fn fill_bytes(&mut self, dest: &mut [u8]) { + // TODO: remove default implementation once impls module is exposed + impls::fill_bytes_via_u64(self, dest) + } /// Return a random value of a `Rand` type. /// diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index 16b6f6b74a5..57e44ddca73 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -119,6 +119,12 @@ impl fmt::Debug for IsaacRng { } impl IsaacRng { + /// Create an ISAAC random number generator using the default + /// fixed seed. + pub fn new_unseeded() -> IsaacRng { + Self::new_from_u64(0) + } + /// Creates an ISAAC random number generator using an u64 as seed. /// If `seed == 0` this will produce the same stream of random numbers as /// the reference implementation when used unseeded. diff --git a/src/prng/isaac64.rs b/src/prng/isaac64.rs index 8cabca38a87..a8523d8248d 100644 --- a/src/prng/isaac64.rs +++ b/src/prng/isaac64.rs @@ -105,6 +105,12 @@ impl fmt::Debug for Isaac64Rng { } impl Isaac64Rng { + /// Create a 64-bit ISAAC random number generator using the + /// default fixed seed. + pub fn new_unseeded() -> Isaac64Rng { + Self::new_from_u64(0) + } + /// Creates an ISAAC-64 random number generator using an u64 as seed. /// If `seed == 0` this will produce the same stream of random numbers as /// the reference implementation when used unseeded. From 99a3b7c446ef01a8dd669f6097e86c4f20bc1fcd Mon Sep 17 00:00:00 2001 From: Diggory Hardy Date: Tue, 2 Jan 2018 13:58:27 +0000 Subject: [PATCH 18/18] Improve Rng method documentation --- src/lib.rs | 51 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 9bbe904e00a..b7ffc46337e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -339,17 +339,27 @@ pub trait Rand : Sized { /// A random number generator. pub trait Rng { - /// Return the next random u32. + /// Return the next random `u32`. + /// + /// Implementations of this trait must implement at least one of + /// `next_u32`, `next_u64` and `fill_bytes` directly. In the case this + /// function is not implemented directly, it can be implemented using + /// `self.next_u64() as u32` or via `fill_bytes` (TODO: expose helper + /// function). fn next_u32(&mut self) -> u32; - /// Return the next random u64. + /// Return the next random `u64`. + /// + /// Implementations of this trait must implement at least one of + /// `next_u32`, `next_u64` and `fill_bytes` directly. In the case this + /// function is not implemented directly, the default implementation will + /// generate values via `next_u32` in little-endian fashion, or this + /// function can be implemented via `fill_bytes` (TODO: expose helper + /// function). /// - /// This function has a default implementation of `next_u32`. The - /// default implementation should not be used in wrapper types since the - /// wrapped RNG may have its own implementation which may be more efficient - /// or even produce different results. + /// Types wrapping an inner RNG must not use the default implementation, + /// since the inner RNG's implementation may produce different values. fn next_u64(&mut self) -> u64 { - // TODO: remove default implementation once impls module is exposed impls::next_u64_via_u32(self) } @@ -407,17 +417,21 @@ pub trait Rng { /// Fill `dest` with random data. /// - /// This function has a default implementation in terms of `next_u64`. The - /// default implementation should not be used in wrapper types since the - /// wrapped RNG may have its own implementation which may be more efficient - /// or even produce different results. - /// - /// This method does *not* have a requirement to bear any fixed - /// relationship to the other methods, for example, it does *not* - /// have to result in the same output as progressively filling - /// `dest` with `self.gen::()`, and any such behaviour should - /// not be relied upon. - /// + /// Implementations of this trait must implement at least one of + /// `next_u32`, `next_u64` and `fill_bytes` directly. In the case this + /// function is not implemented directly, the default implementation will + /// generate values via `next_u64` in little-endian fashion. + /// (TODO: expose helper function to allow implementation via `next_u32`.) + /// + /// There is no requirement on how this method generates values relative to + /// `next_u32` or `next_u64`; e.g. a `u64` cast to bytes is not required to + /// have the same value as eight bytes filled via this function. There *is* + /// a requirement of portability for reproducible generators which implies + /// that any seedable generator must fix endianness when generating bytes. + /// + /// Types wrapping an inner RNG must not use the default implementation, + /// since the inner RNG's implementation may produce different values. + /// /// This method should guarantee that `dest` is entirely filled /// with new data, and may panic if this is impossible /// (e.g. reading past the end of a file that is being used as the @@ -433,7 +447,6 @@ pub trait Rng { /// println!("{:?}", &v[..]); /// ``` fn fill_bytes(&mut self, dest: &mut [u8]) { - // TODO: remove default implementation once impls module is exposed impls::fill_bytes_via_u64(self, dest) }