diff options
Diffstat (limited to 'vendor/rand/src/reseeding.rs')
-rw-r--r-- | vendor/rand/src/reseeding.rs | 229 |
1 files changed, 229 insertions, 0 deletions
diff --git a/vendor/rand/src/reseeding.rs b/vendor/rand/src/reseeding.rs new file mode 100644 index 0000000..1f24e20 --- /dev/null +++ b/vendor/rand/src/reseeding.rs @@ -0,0 +1,229 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! A wrapper around another RNG that reseeds it after it +//! generates a certain number of random bytes. + +use core::default::Default; + +use {Rng, SeedableRng}; + +/// How many bytes of entropy the underling RNG is allowed to generate +/// before it is reseeded +const DEFAULT_GENERATION_THRESHOLD: u64 = 32 * 1024; + +/// A wrapper around any RNG which reseeds the underlying RNG after it +/// has generated a certain number of random bytes. +#[derive(Debug)] +pub struct ReseedingRng<R, Rsdr> { + rng: R, + generation_threshold: u64, + bytes_generated: u64, + /// Controls the behaviour when reseeding the RNG. + pub reseeder: Rsdr, +} + +impl<R: Rng, Rsdr: Reseeder<R>> ReseedingRng<R, Rsdr> { + /// Create a new `ReseedingRng` with the given parameters. + /// + /// # Arguments + /// + /// * `rng`: the random number generator to use. + /// * `generation_threshold`: the number of bytes of entropy at which to reseed the RNG. + /// * `reseeder`: the reseeding object to use. + pub fn new(rng: R, generation_threshold: u64, reseeder: Rsdr) -> ReseedingRng<R,Rsdr> { + ReseedingRng { + rng: rng, + generation_threshold: generation_threshold, + bytes_generated: 0, + reseeder: reseeder + } + } + + /// Reseed the internal RNG if the number of bytes that have been + /// generated exceed the threshold. + pub fn reseed_if_necessary(&mut self) { + if self.bytes_generated >= self.generation_threshold { + self.reseeder.reseed(&mut self.rng); + self.bytes_generated = 0; + } + } +} + + +impl<R: Rng, Rsdr: Reseeder<R>> Rng for ReseedingRng<R, Rsdr> { + fn next_u32(&mut self) -> u32 { + self.reseed_if_necessary(); + self.bytes_generated += 4; + self.rng.next_u32() + } + + fn next_u64(&mut self) -> u64 { + self.reseed_if_necessary(); + self.bytes_generated += 8; + self.rng.next_u64() + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + self.reseed_if_necessary(); + self.bytes_generated += dest.len() as u64; + self.rng.fill_bytes(dest) + } +} + +impl<S, R: SeedableRng<S>, Rsdr: Reseeder<R> + Default> + SeedableRng<(Rsdr, S)> for ReseedingRng<R, Rsdr> { + fn reseed(&mut self, (rsdr, seed): (Rsdr, S)) { + self.rng.reseed(seed); + self.reseeder = rsdr; + self.bytes_generated = 0; + } + + /// Create a new `ReseedingRng` from the given reseeder and + /// seed. This uses a default value for `generation_threshold`. + fn from_seed((rsdr, seed): (Rsdr, S)) -> ReseedingRng<R, Rsdr> { + ReseedingRng { + rng: SeedableRng::from_seed(seed), + generation_threshold: DEFAULT_GENERATION_THRESHOLD, + bytes_generated: 0, + reseeder: rsdr + } + } +} + +/// Something that can be used to reseed an RNG via `ReseedingRng`. +/// +/// # Example +/// +/// ```rust +/// use rand::{Rng, SeedableRng, StdRng}; +/// use rand::reseeding::{Reseeder, ReseedingRng}; +/// +/// struct TickTockReseeder { tick: bool } +/// impl Reseeder<StdRng> for TickTockReseeder { +/// fn reseed(&mut self, rng: &mut StdRng) { +/// let val = if self.tick {0} else {1}; +/// rng.reseed(&[val]); +/// self.tick = !self.tick; +/// } +/// } +/// fn main() { +/// let rsdr = TickTockReseeder { tick: true }; +/// +/// let inner = StdRng::new().unwrap(); +/// let mut rng = ReseedingRng::new(inner, 10, rsdr); +/// +/// // this will repeat, because it gets reseeded very regularly. +/// let s: String = rng.gen_ascii_chars().take(100).collect(); +/// println!("{}", s); +/// } +/// +/// ``` +pub trait Reseeder<R> { + /// Reseed the given RNG. + fn reseed(&mut self, rng: &mut R); +} + +/// Reseed an RNG using a `Default` instance. This reseeds by +/// replacing the RNG with the result of a `Default::default` call. +#[derive(Clone, Copy, Debug)] +pub struct ReseedWithDefault; + +impl<R: Rng + Default> Reseeder<R> for ReseedWithDefault { + fn reseed(&mut self, rng: &mut R) { + *rng = Default::default(); + } +} +impl Default for ReseedWithDefault { + fn default() -> ReseedWithDefault { ReseedWithDefault } +} + +#[cfg(test)] +mod test { + use std::default::Default; + use std::iter::repeat; + use super::{ReseedingRng, ReseedWithDefault}; + use {SeedableRng, Rng}; + + struct Counter { + i: u32 + } + + impl Rng for Counter { + fn next_u32(&mut self) -> u32 { + self.i += 1; + // very random + self.i - 1 + } + } + impl Default for Counter { + fn default() -> Counter { + Counter { i: 0 } + } + } + impl SeedableRng<u32> for Counter { + fn reseed(&mut self, seed: u32) { + self.i = seed; + } + fn from_seed(seed: u32) -> Counter { + Counter { i: seed } + } + } + type MyRng = ReseedingRng<Counter, ReseedWithDefault>; + + #[test] + fn test_reseeding() { + let mut rs = ReseedingRng::new(Counter {i:0}, 400, ReseedWithDefault); + + let mut i = 0; + for _ in 0..1000 { + assert_eq!(rs.next_u32(), i % 100); + i += 1; + } + } + + #[test] + fn test_rng_seeded() { + let mut ra: MyRng = SeedableRng::from_seed((ReseedWithDefault, 2)); + let mut rb: MyRng = SeedableRng::from_seed((ReseedWithDefault, 2)); + assert!(::test::iter_eq(ra.gen_ascii_chars().take(100), + rb.gen_ascii_chars().take(100))); + } + + #[test] + fn test_rng_reseed() { + let mut r: MyRng = SeedableRng::from_seed((ReseedWithDefault, 3)); + let string1: String = r.gen_ascii_chars().take(100).collect(); + + r.reseed((ReseedWithDefault, 3)); + + let string2: String = r.gen_ascii_chars().take(100).collect(); + assert_eq!(string1, string2); + } + + const FILL_BYTES_V_LEN: usize = 13579; + #[test] + fn test_rng_fill_bytes() { + let mut v = repeat(0u8).take(FILL_BYTES_V_LEN).collect::<Vec<_>>(); + ::test::rng().fill_bytes(&mut v); + + // Sanity test: if we've gotten here, `fill_bytes` has not infinitely + // recursed. + assert_eq!(v.len(), FILL_BYTES_V_LEN); + + // To test that `fill_bytes` actually did something, check that the + // average of `v` is not 0. + let mut sum = 0.0; + for &x in v.iter() { + sum += x as f64; + } + assert!(sum / v.len() as f64 != 0.0); + } +} |