]> git.proxmox.com Git - rustc.git/blame - src/librand/reseeding.rs
Imported Upstream version 1.3.0+dfsg1
[rustc.git] / src / librand / reseeding.rs
CommitLineData
1a4d82fc
JJ
1// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2// file at the top-level directory of this distribution and at
3// http://rust-lang.org/COPYRIGHT.
4//
5// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8// option. This file may not be copied, modified, or distributed
9// except according to those terms.
10
11//! A wrapper around another RNG that reseeds it after it
12//! generates a certain number of random bytes.
13
14use core::prelude::*;
15
16use {Rng, SeedableRng};
1a4d82fc
JJ
17
18/// How many bytes of entropy the underling RNG is allowed to generate
19/// before it is reseeded.
c34b1796 20const DEFAULT_GENERATION_THRESHOLD: usize = 32 * 1024;
1a4d82fc
JJ
21
22/// A wrapper around any RNG which reseeds the underlying RNG after it
23/// has generated a certain number of random bytes.
24pub struct ReseedingRng<R, Rsdr> {
25 rng: R,
c34b1796
AL
26 generation_threshold: usize,
27 bytes_generated: usize,
1a4d82fc
JJ
28 /// Controls the behaviour when reseeding the RNG.
29 pub reseeder: Rsdr,
30}
31
32impl<R: Rng, Rsdr: Reseeder<R>> ReseedingRng<R, Rsdr> {
33 /// Create a new `ReseedingRng` with the given parameters.
34 ///
35 /// # Arguments
36 ///
37 /// * `rng`: the random number generator to use.
38 /// * `generation_threshold`: the number of bytes of entropy at which to reseed the RNG.
39 /// * `reseeder`: the reseeding object to use.
c34b1796 40 pub fn new(rng: R, generation_threshold: usize, reseeder: Rsdr) -> ReseedingRng<R,Rsdr> {
1a4d82fc
JJ
41 ReseedingRng {
42 rng: rng,
43 generation_threshold: generation_threshold,
44 bytes_generated: 0,
45 reseeder: reseeder
46 }
47 }
48
49 /// Reseed the internal RNG if the number of bytes that have been
50 /// generated exceed the threshold.
51 pub fn reseed_if_necessary(&mut self) {
52 if self.bytes_generated >= self.generation_threshold {
53 self.reseeder.reseed(&mut self.rng);
54 self.bytes_generated = 0;
55 }
56 }
57}
58
59
60impl<R: Rng, Rsdr: Reseeder<R>> Rng for ReseedingRng<R, Rsdr> {
61 fn next_u32(&mut self) -> u32 {
62 self.reseed_if_necessary();
63 self.bytes_generated += 4;
64 self.rng.next_u32()
65 }
66
67 fn next_u64(&mut self) -> u64 {
68 self.reseed_if_necessary();
69 self.bytes_generated += 8;
70 self.rng.next_u64()
71 }
72
73 fn fill_bytes(&mut self, dest: &mut [u8]) {
74 self.reseed_if_necessary();
75 self.bytes_generated += dest.len();
76 self.rng.fill_bytes(dest)
77 }
78}
79
80impl<S, R: SeedableRng<S>, Rsdr: Reseeder<R> + Default>
81 SeedableRng<(Rsdr, S)> for ReseedingRng<R, Rsdr> {
82 fn reseed(&mut self, (rsdr, seed): (Rsdr, S)) {
83 self.rng.reseed(seed);
84 self.reseeder = rsdr;
85 self.bytes_generated = 0;
86 }
87
88 /// Create a new `ReseedingRng` from the given reseeder and
89 /// seed. This uses a default value for `generation_threshold`.
90 fn from_seed((rsdr, seed): (Rsdr, S)) -> ReseedingRng<R, Rsdr> {
91 ReseedingRng {
92 rng: SeedableRng::from_seed(seed),
93 generation_threshold: DEFAULT_GENERATION_THRESHOLD,
94 bytes_generated: 0,
95 reseeder: rsdr
96 }
97 }
98}
99
100/// Something that can be used to reseed an RNG via `ReseedingRng`.
1a4d82fc
JJ
101pub trait Reseeder<R> {
102 /// Reseed the given RNG.
103 fn reseed(&mut self, rng: &mut R);
104}
105
106/// Reseed an RNG using a `Default` instance. This reseeds by
107/// replacing the RNG with the result of a `Default::default` call.
c34b1796
AL
108#[derive(Copy, Clone)]
109pub struct ReseedWithDefault;
1a4d82fc
JJ
110
111impl<R: Rng + Default> Reseeder<R> for ReseedWithDefault {
112 fn reseed(&mut self, rng: &mut R) {
113 *rng = Default::default();
114 }
115}
85aaf69f 116#[stable(feature = "rust1", since = "1.0.0")]
1a4d82fc 117impl Default for ReseedWithDefault {
85aaf69f 118 #[stable(feature = "rust1", since = "1.0.0")]
1a4d82fc
JJ
119 fn default() -> ReseedWithDefault { ReseedWithDefault }
120}
121
122#[cfg(test)]
d9579d0f 123mod tests {
1a4d82fc
JJ
124 use std::prelude::v1::*;
125
126 use core::iter::{order, repeat};
127 use super::{ReseedingRng, ReseedWithDefault};
1a4d82fc
JJ
128 use {SeedableRng, Rng};
129
130 struct Counter {
131 i: u32
132 }
133
134 impl Rng for Counter {
135 fn next_u32(&mut self) -> u32 {
136 self.i += 1;
137 // very random
138 self.i - 1
139 }
140 }
141 impl Default for Counter {
142 fn default() -> Counter {
143 Counter { i: 0 }
144 }
145 }
146 impl SeedableRng<u32> for Counter {
147 fn reseed(&mut self, seed: u32) {
148 self.i = seed;
149 }
150 fn from_seed(seed: u32) -> Counter {
151 Counter { i: seed }
152 }
153 }
154 type MyRng = ReseedingRng<Counter, ReseedWithDefault>;
155
156 #[test]
157 fn test_reseeding() {
158 let mut rs = ReseedingRng::new(Counter {i:0}, 400, ReseedWithDefault);
159
160 let mut i = 0;
85aaf69f 161 for _ in 0..1000 {
1a4d82fc
JJ
162 assert_eq!(rs.next_u32(), i % 100);
163 i += 1;
164 }
165 }
166
167 #[test]
168 fn test_rng_seeded() {
169 let mut ra: MyRng = SeedableRng::from_seed((ReseedWithDefault, 2));
170 let mut rb: MyRng = SeedableRng::from_seed((ReseedWithDefault, 2));
171 assert!(order::equals(ra.gen_ascii_chars().take(100),
172 rb.gen_ascii_chars().take(100)));
173 }
174
175 #[test]
176 fn test_rng_reseed() {
177 let mut r: MyRng = SeedableRng::from_seed((ReseedWithDefault, 3));
178 let string1: String = r.gen_ascii_chars().take(100).collect();
179
180 r.reseed((ReseedWithDefault, 3));
181
182 let string2: String = r.gen_ascii_chars().take(100).collect();
183 assert_eq!(string1, string2);
184 }
185
c34b1796 186 const FILL_BYTES_V_LEN: usize = 13579;
1a4d82fc
JJ
187 #[test]
188 fn test_rng_fill_bytes() {
c1a9b12d 189 let mut v = vec![0; FILL_BYTES_V_LEN];
85aaf69f 190 ::test::rng().fill_bytes(&mut v);
1a4d82fc
JJ
191
192 // Sanity test: if we've gotten here, `fill_bytes` has not infinitely
193 // recursed.
194 assert_eq!(v.len(), FILL_BYTES_V_LEN);
195
196 // To test that `fill_bytes` actually did something, check that the
197 // average of `v` is not 0.
198 let mut sum = 0.0;
85aaf69f 199 for &x in &v {
1a4d82fc
JJ
200 sum += x as f64;
201 }
202 assert!(sum / v.len() as f64 != 0.0);
203 }
204}