Skip to content

Commit c32a85b

Browse files
committed
BlockRng: template over element type; add doc
1 parent 20b24f2 commit c32a85b

File tree

6 files changed

+100
-60
lines changed

6 files changed

+100
-60
lines changed

rand-core/src/impls.rs

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -167,30 +167,30 @@ pub fn next_u64_via_fill<R: RngCore + ?Sized>(rng: &mut R) -> u64 {
167167
/// Wrapper around PRNGs that implement [`BlockRngCore`] to keep a results
168168
/// buffer and offer the methods from [`RngCore`].
169169
///
170-
/// `BlockRng` has optimized methods to read from the output array that the
171-
/// algorithm of many cryptograpic RNGs generates natively. Also they handle the
172-
/// bookkeeping when to generate a new batch of values.
173-
///
174-
/// `next_u32` simply indexes the array. `next_u64` tries to read two `u32`
175-
/// values at a time if possible, and handles edge cases like when only one
176-
/// value is left. `try_fill_bytes` is optimized use the [`BlockRngCore`]
177-
/// implementation to write the results directly to the destination slice.
170+
/// `BlockRng` has heavily optimized implementations of the [`RngCore`] methods
171+
/// reading values from the results buffer, as well as
172+
/// calling `BlockRngCore::generate` directly on the output array when
173+
/// `fill_bytes` / `try_fill_bytes` is called on a large array. These methods
174+
/// also handle the bookkeeping of when to generate a new batch of values.
178175
/// No generated values are ever thown away.
179176
///
177+
/// Currently `BlockRng` only implements `RngCore` for buffers which are slices
178+
/// of `u32` elements; this may be extended to other types in the future.
179+
///
180180
/// For easy initialization `BlockRng` also implements [`SeedableRng`].
181181
///
182182
/// [`BlockRngCore`]: ../BlockRngCore.t.html
183183
/// [`RngCore`]: ../RngCore.t.html
184184
/// [`SeedableRng`]: ../SeedableRng.t.html
185185
#[derive(Clone)]
186-
pub struct BlockRng<R: BlockRngCore<u32>> {
186+
pub struct BlockRng<T, R: BlockRngCore<T>> {
187187
pub core: R,
188188
pub results: R::Results,
189189
pub index: usize,
190190
}
191191

192192
// Custom Debug implementation that does not expose the contents of `results`.
193-
impl<R: BlockRngCore<u32>+fmt::Debug> fmt::Debug for BlockRng<R> {
193+
impl<T, R: BlockRngCore<T> + fmt::Debug> fmt::Debug for BlockRng<T, R> {
194194
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
195195
fmt.debug_struct("BlockRng")
196196
.field("core", &self.core)
@@ -200,7 +200,7 @@ impl<R: BlockRngCore<u32>+fmt::Debug> fmt::Debug for BlockRng<R> {
200200
}
201201
}
202202

203-
impl<R: BlockRngCore<u32>> RngCore for BlockRng<R> {
203+
impl<R: BlockRngCore<u32>> RngCore for BlockRng<u32, R> {
204204
#[inline(always)]
205205
fn next_u32(&mut self) -> u32 {
206206
if self.index >= self.results.as_ref().len() {
@@ -308,7 +308,7 @@ impl<R: BlockRngCore<u32>> RngCore for BlockRng<R> {
308308
}
309309
}
310310

311-
impl<R: BlockRngCore<u32> + SeedableRng> SeedableRng for BlockRng<R> {
311+
impl<R: BlockRngCore<u32> + SeedableRng> SeedableRng for BlockRng<u32, R> {
312312
type Seed = R::Seed;
313313

314314
fn from_seed(seed: Self::Seed) -> Self {
@@ -330,6 +330,6 @@ impl<R: BlockRngCore<u32> + SeedableRng> SeedableRng for BlockRng<R> {
330330
}
331331
}
332332

333-
impl<R: BlockRngCore<u32>+CryptoRng> CryptoRng for BlockRng<R> {}
333+
impl<T, R: BlockRngCore<T> + CryptoRng> CryptoRng for BlockRng<T, R> {}
334334

335335
// TODO: implement tests for the above

rand-core/src/lib.rs

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -162,9 +162,44 @@ pub trait RngCore {
162162
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error>;
163163
}
164164

165-
/// Trait for RNGs that do not generate random numbers one at a time, but in
166-
/// blocks. Especially for cryptographic RNG's it is common to generate 16 or
167-
/// more results at a time.
165+
/// A trait for RNGs which do not generate random numbers individually, but in
166+
/// blocks (typically `[u32; N]`). This technique is commonly used by
167+
/// cryptographic RNGs to improve performance.
168+
///
169+
/// Usage of this trait is optional, but provides two advantages:
170+
/// implementations only need to concern themselves with generation of the
171+
/// block, not the various `RngCore` methods (especially `fill_bytes`, where the
172+
/// optimal implementations are not trivial), and this allows `ReseedingRng` to
173+
/// perform periodic reseeding with very low overhead.
174+
///
175+
/// # Example
176+
///
177+
/// ```norun
178+
/// use rand_core::BlockRngCore;
179+
/// use rand_core::impls::BlockRng;
180+
///
181+
/// struct MyRngCore;
182+
///
183+
/// impl BlockRngCore for MyRngCore {
184+
/// type Results = [u32; 16];
185+
///
186+
/// fn generate(&mut self, results: &mut Self::Results) {
187+
/// unimplemented!()
188+
/// }
189+
/// }
190+
///
191+
/// impl SeedableRng for MyRngCore {
192+
/// type Seed = unimplemented!();
193+
/// fn from_seed(seed: Self::Seed) -> Self {
194+
/// unimplemented!()
195+
/// }
196+
/// }
197+
///
198+
/// // optionally, also implement CryptoRng for MyRngCore
199+
///
200+
/// // Final RNG.
201+
/// type MyRng = BlockRng<u32, MyRngCore>;
202+
/// ```
168203
pub trait BlockRngCore<T>: Sized {
169204
/// Results type. This is the 'block' an RNG implementing `BlockRngCore`
170205
/// generates, which will usually be an array like `[u32; 16]`.
@@ -174,8 +209,8 @@ pub trait BlockRngCore<T>: Sized {
174209
fn generate(&mut self, results: &mut Self::Results);
175210
}
176211

177-
/// A marker trait for an `Rng` which may be considered for use in
178-
/// cryptography.
212+
/// A marker trait used to indicate that an `RngCore` or `BlockRngCore`
213+
/// implementation is supposed to be cryptographically secure.
179214
///
180215
/// *Cryptographically secure generators*, also known as *CSPRNGs*, should
181216
/// satisfy an additional properties over other generators: given the first

src/prng/chacha.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ const STATE_WORDS: usize = 16;
6363
///
6464
/// [`set_rounds`]: #method.set_counter
6565
#[derive(Clone, Debug)]
66-
pub struct ChaChaRng(BlockRng<ChaChaCore>);
66+
pub struct ChaChaRng(BlockRng<u32, ChaChaCore>);
6767

6868
impl RngCore for ChaChaRng {
6969
#[inline]
@@ -91,11 +91,11 @@ impl SeedableRng for ChaChaRng {
9191
type Seed = <ChaChaCore as SeedableRng>::Seed;
9292

9393
fn from_seed(seed: Self::Seed) -> Self {
94-
ChaChaRng(BlockRng::<ChaChaCore>::from_seed(seed))
94+
ChaChaRng(BlockRng::<u32, ChaChaCore>::from_seed(seed))
9595
}
9696

9797
fn from_rng<R: RngCore>(rng: &mut R) -> Result<Self, Error> {
98-
BlockRng::<ChaChaCore>::from_rng(rng).map(|rng| ChaChaRng(rng))
98+
BlockRng::<u32, ChaChaCore>::from_rng(rng).map(|rng| ChaChaRng(rng))
9999
}
100100
}
101101

src/prng/hc128.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ const SEED_WORDS: usize = 8; // 128 bit key followed by 128 bit iv
6262
/// [5]: Internet Engineering Task Force (Februari 2015),
6363
/// ["Prohibiting RC4 Cipher Suites"](https://tools.ietf.org/html/rfc7465).
6464
#[derive(Clone, Debug)]
65-
pub struct Hc128Rng(BlockRng<Hc128Core>);
65+
pub struct Hc128Rng(BlockRng<u32, Hc128Core>);
6666

6767
impl RngCore for Hc128Rng {
6868
#[inline(always)]
@@ -88,16 +88,17 @@ impl SeedableRng for Hc128Rng {
8888
type Seed = <Hc128Core as SeedableRng>::Seed;
8989

9090
fn from_seed(seed: Self::Seed) -> Self {
91-
Hc128Rng(BlockRng::<Hc128Core>::from_seed(seed))
91+
Hc128Rng(BlockRng::<u32, Hc128Core>::from_seed(seed))
9292
}
9393

9494
fn from_rng<R: RngCore>(rng: &mut R) -> Result<Self, Error> {
95-
BlockRng::<Hc128Core>::from_rng(rng).map(|rng| Hc128Rng(rng))
95+
BlockRng::<u32, Hc128Core>::from_rng(rng).map(|rng| Hc128Rng(rng))
9696
}
9797
}
9898

9999
impl CryptoRng for Hc128Rng {}
100100

101+
/// The core of `Hc128Rng`, used with `BlockRng`.
101102
#[derive(Clone)]
102103
pub struct Hc128Core {
103104
t: [u32; 1024],

src/reseeding.rs

Lines changed: 38 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111
//! A wrapper around another PRNG that reseeds it after it
1212
//! generates a certain number of random bytes.
1313
14+
use core::marker::PhantomData;
15+
use core::mem::size_of;
16+
1417
use rand_core::{RngCore, BlockRngCore, CryptoRng, SeedableRng, Error, ErrorKind};
1518
use rand_core::impls::BlockRng;
1619

@@ -51,12 +54,12 @@ use rand_core::impls::BlockRng;
5154
/// If handling the source error fails `ReseedingRng` will continue generating
5255
/// data from the wrapped PRNG without reseeding.
5356
#[derive(Debug)]
54-
pub struct ReseedingRng<R, Rsdr>(BlockRng<ReseedingCore<R, Rsdr>>)
55-
where R: BlockRngCore<u32> + SeedableRng,
57+
pub struct ReseedingRng<T, R, Rsdr>(BlockRng<T, ReseedingCore<T, R, Rsdr>>)
58+
where R: BlockRngCore<T> + SeedableRng,
5659
Rsdr: RngCore;
5760

58-
impl<R, Rsdr> ReseedingRng<R, Rsdr>
59-
where R: BlockRngCore<u32> + SeedableRng,
61+
impl<T, R, Rsdr> ReseedingRng<T, R, Rsdr>
62+
where R: BlockRngCore<T> + SeedableRng,
6063
Rsdr: RngCore
6164
{
6265
/// Create a new `ReseedingRng` with the given parameters.
@@ -67,13 +70,14 @@ where R: BlockRngCore<u32> + SeedableRng,
6770
/// * `threshold`: the number of generated bytes after which to reseed the RNG.
6871
/// * `reseeder`: the RNG to use for reseeding.
6972
pub fn new(rng: R, threshold: u64, reseeder: Rsdr)
70-
-> ReseedingRng<R, Rsdr>
73+
-> ReseedingRng<T, R, Rsdr>
7174
{
7275
assert!(threshold <= ::core::i64::MAX as u64);
7376
let results_empty = R::Results::default();
7477
ReseedingRng(
7578
BlockRng {
7679
core: ReseedingCore {
80+
phantom: PhantomData,
7781
inner: rng,
7882
reseeder: reseeder,
7983
threshold: threshold as i64,
@@ -91,7 +95,11 @@ where R: BlockRngCore<u32> + SeedableRng,
9195
}
9296
}
9397

94-
impl<R: BlockRngCore<u32> + SeedableRng, Rsdr: RngCore> RngCore for ReseedingRng<R, Rsdr> {
98+
// TODO: this should be implemented for any type where the inner type
99+
// implements RngCore, but we can't specify that because ReseedingCore is private
100+
impl<R, Rsdr: RngCore> RngCore for ReseedingRng<u32, R, Rsdr>
101+
where R: BlockRngCore<u32> + SeedableRng
102+
{
95103
#[inline(always)]
96104
fn next_u32(&mut self) -> u32 {
97105
self.0.next_u32()
@@ -111,23 +119,24 @@ impl<R: BlockRngCore<u32> + SeedableRng, Rsdr: RngCore> RngCore for ReseedingRng
111119
}
112120
}
113121

114-
impl<R, Rsdr> CryptoRng for ReseedingRng<R, Rsdr>
115-
where R: BlockRngCore<u32> + SeedableRng + CryptoRng,
122+
impl<T, R, Rsdr> CryptoRng for ReseedingRng<T, R, Rsdr>
123+
where R: BlockRngCore<T> + SeedableRng + CryptoRng,
116124
Rsdr: RngCore + CryptoRng {}
117125

118126
#[derive(Debug)]
119-
struct ReseedingCore<R, Rsdr> {
127+
struct ReseedingCore<T, R, Rsdr> {
128+
phantom: PhantomData<T>, // associated with R, but not a parameter; used below
120129
inner: R,
121130
reseeder: Rsdr,
122131
threshold: i64,
123132
bytes_until_reseed: i64,
124133
}
125134

126-
impl<R, Rsdr> BlockRngCore<u32> for ReseedingCore<R, Rsdr>
127-
where R: BlockRngCore<u32> + SeedableRng,
135+
impl<T, R, Rsdr> BlockRngCore<T> for ReseedingCore<T, R, Rsdr>
136+
where R: BlockRngCore<T> + SeedableRng,
128137
Rsdr: RngCore
129138
{
130-
type Results = <R as BlockRngCore<u32>>::Results;
139+
type Results = <R as BlockRngCore<T>>::Results;
131140

132141
fn generate(&mut self, results: &mut Self::Results) {
133142
if self.bytes_until_reseed <= 0 {
@@ -136,13 +145,14 @@ where R: BlockRngCore<u32> + SeedableRng,
136145
// returning from a non-inlined function.
137146
return self.reseed_and_generate(results);
138147
}
139-
self.bytes_until_reseed -= results.as_ref().len() as i64 * 4;
148+
let num_bytes = results.as_ref().len() * size_of::<T>();
149+
self.bytes_until_reseed -= num_bytes as i64;
140150
self.inner.generate(results);
141151
}
142152
}
143153

144-
impl<R, Rsdr> ReseedingCore<R, Rsdr>
145-
where R: BlockRngCore<u32> + SeedableRng,
154+
impl<T, R, Rsdr> ReseedingCore<T, R, Rsdr>
155+
where R: BlockRngCore<T> + SeedableRng,
146156
Rsdr: RngCore
147157
{
148158
/// Reseed the internal PRNG.
@@ -153,39 +163,33 @@ where R: BlockRngCore<u32> + SeedableRng,
153163
})
154164
}
155165

156-
/// Reseed the internal PRNG.
157-
///
158-
/// If reseeding fails, this will try to work around errors intelligently
159-
/// by adjusting the delay until automatic reseeding next occurs.
160-
fn auto_reseed(&mut self) {
166+
#[inline(never)]
167+
fn reseed_and_generate(&mut self,
168+
results: &mut <Self as BlockRngCore<T>>::Results)
169+
{
161170
trace!("Reseeding RNG after {} generated bytes",
162171
self.threshold - self.bytes_until_reseed);
163-
if let Err(e) = self.reseed() {
172+
let threshold = if let Err(e) = self.reseed() {
164173
let delay = match e.kind {
165174
ErrorKind::Transient => 0,
166175
kind @ _ if kind.should_retry() => self.threshold >> 8,
167176
_ => self.threshold,
168177
};
169178
warn!("Reseeding RNG delayed reseeding by {} bytes due to \
170179
error from source: {}", delay, e);
171-
self.bytes_until_reseed = delay;
180+
delay
172181
} else {
173-
self.bytes_until_reseed = self.threshold;
174-
}
175-
}
176-
177-
#[inline(never)]
178-
fn reseed_and_generate(&mut self,
179-
results: &mut <Self as BlockRngCore<u32>>::Results)
180-
{
181-
self.auto_reseed();
182-
self.bytes_until_reseed -= results.as_ref().len() as i64 * 4;
182+
self.threshold
183+
};
184+
185+
let num_bytes = results.as_ref().len() * size_of::<T>();
186+
self.bytes_until_reseed = threshold - num_bytes as i64;
183187
self.inner.generate(results);
184188
}
185189
}
186190

187-
impl<R, Rsdr> CryptoRng for ReseedingCore<R, Rsdr>
188-
where R: BlockRngCore<u32> + SeedableRng + CryptoRng,
191+
impl<T, R, Rsdr> CryptoRng for ReseedingCore<T, R, Rsdr>
192+
where R: BlockRngCore<T> + SeedableRng + CryptoRng,
189193
Rsdr: RngCore + CryptoRng {}
190194

191195
#[cfg(test)]

src/thread_rng.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,11 @@ const THREAD_RNG_RESEED_THRESHOLD: u64 = 32*1024*1024; // 32 MiB
3232
/// [`thread_rng`]: fn.thread_rng.html
3333
#[derive(Clone, Debug)]
3434
pub struct ThreadRng {
35-
rng: Rc<RefCell<ReseedingRng<Hc128Core, EntropyRng>>>,
35+
rng: Rc<RefCell<ReseedingRng<u32, Hc128Core, EntropyRng>>>,
3636
}
3737

3838
thread_local!(
39-
static THREAD_RNG_KEY: Rc<RefCell<ReseedingRng<Hc128Core, EntropyRng>>> = {
39+
static THREAD_RNG_KEY: Rc<RefCell<ReseedingRng<u32, Hc128Core, EntropyRng>>> = {
4040
let mut entropy_source = EntropyRng::new();
4141
let r = Hc128Core::from_rng(&mut entropy_source).unwrap_or_else(|err|
4242
panic!("could not initialize thread_rng: {}", err));

0 commit comments

Comments
 (0)