aboutsummaryrefslogtreecommitdiff
path: root/rand/src/reseeding.rs
diff options
context:
space:
mode:
Diffstat (limited to 'rand/src/reseeding.rs')
-rw-r--r--rand/src/reseeding.rs229
1 files changed, 229 insertions, 0 deletions
diff --git a/rand/src/reseeding.rs b/rand/src/reseeding.rs
new file mode 100644
index 0000000..1f24e20
--- /dev/null
+++ b/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);
+ }
+}