diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index 00343044eee..f5df6967854 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -23,15 +23,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], @@ -121,6 +176,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 b4ba3fe2b05..32c9198f83f 100644 --- a/src/prng/isaac64.rs +++ b/src/prng/isaac64.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! The ISAAC random number generator. +//! The ISAAC-64 random number generator. use core::slice; use core::iter::repeat; @@ -23,16 +23,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], @@ -122,6 +160,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 diff --git a/src/prng/isaac_word.rs b/src/prng/isaac_word.rs index 58c884d9f26..c1ca12f0f12 100644 --- a/src/prng/isaac_word.rs +++ b/src/prng/isaac_word.rs @@ -10,8 +10,105 @@ //! The ISAAC random number generator. -/// Select 32- or 64-bit variant dependent on pointer size. +use core::fmt; +use {Rng, FromRng, SeedableRng}; + +#[cfg(target_pointer_width = "32")] +#[derive(Copy)] +/// A random number generator that uses the ISAAC or ISAAC-64 algorithm, +/// depending on the pointer size of the target architecture. +/// +/// In general a random number generator that internally uses the word size of +/// the target architecture is faster than one that doesn't. Choosing +/// `IsaacWordRng` is therefore often a better choice than `IsaacRng` or +/// `Isaac64Rng`. The others can be a good choice if reproducability across +/// different architectures is desired. `IsaacRng` can also be a better choice +/// if memory usage is an issue, as it uses 2kb of state instead of the 4kb +/// `Isaac64Rng` uses. +/// +/// See for an explanation of the algorithm `IsaacRng` and `Isaac64Rng`. +pub struct IsaacWordRng(super::isaac::IsaacRng); + +#[cfg(target_pointer_width = "64")] +#[derive(Copy)] +/// A random number generator that uses the ISAAC or ISAAC-64 algorithm, +/// depending on the pointer size of the target architecture. +/// +/// In general a random number generator that internally uses the word size of +/// the target architecture is faster than one that doesn't. Choosing +/// `IsaacWordRng` is therefore often a better choice than `IsaacRng` or +/// `Isaac64Rng`. The others can be a good choice if reproducability across +/// different architectures is desired. `IsaacRng` can also be a better choice +/// if memory usage is an issue, as it uses 2kb of state instead of the 4kb +/// `Isaac64Rng` uses. +/// +/// See for an explanation of the algorithm `IsaacRng` and `Isaac64Rng`. +pub struct IsaacWordRng(super::isaac64::Isaac64Rng); + +impl Clone for IsaacWordRng { + fn clone(&self) -> IsaacWordRng { + *self + } +} + +impl Rng for IsaacWordRng { + #[inline] + fn next_u32(&mut self) -> u32 { + self.0.next_u32() + } + + #[inline] + fn next_u64(&mut self) -> u64 { + self.0.next_u64() + } +} + +impl FromRng for IsaacWordRng { + #[cfg(target_pointer_width = "32")] + fn from_rng(other: &mut R) -> IsaacWordRng { + IsaacWordRng(super::isaac::IsaacRng::from_rng(other)) + } + + #[cfg(target_pointer_width = "64")] + fn from_rng(other: &mut R) -> IsaacWordRng { + IsaacWordRng(super::isaac64::Isaac64Rng::from_rng(other)) + } +} + #[cfg(target_pointer_width = "32")] -pub use prng::isaac::IsaacRng as IsaacWordRng; +impl<'a> SeedableRng<&'a [u32]> for IsaacWordRng { + fn reseed(&mut self, seed: &'a [u32]) { + self.0.reseed(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]) -> IsaacWordRng { + IsaacWordRng(super::isaac::IsaacRng::from_seed(seed)) + } +} + #[cfg(target_pointer_width = "64")] -pub use prng::isaac64::Isaac64Rng as IsaacWordRng; +impl<'a> SeedableRng<&'a [u64]> for IsaacWordRng { + fn reseed(&mut self, seed: &'a [u64]) { + self.0.reseed(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 [u64]) -> IsaacWordRng { + IsaacWordRng(super::isaac64::Isaac64Rng::from_seed(seed)) + } +} + +impl fmt::Debug for IsaacWordRng { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "IsaacWordRng {{}}") + } +}