Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 48 additions & 53 deletions src/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@
//! }
//!
//! impl Generator for MyRngCore {
//! type Word = u32;
//! type Output = [u32; 8];
//!
//! fn generate(&mut self, output: &mut Self::Output) {
//! fn generate(&mut self, output: &mut Self::Output) -> usize {
//! // Write a new block to output...
//! # *output = self.state;
//! # 0
//! }
//! }
//!
Expand Down Expand Up @@ -79,20 +81,37 @@ use core::fmt;

/// A random (block) generator
pub trait Generator {
/// The word type.
type Word: Word;

/// The output type.
///
/// For use with [`rand_core::block`](crate::block) code this must be `[u32; _]` or `[u64; _]`.
/// For use with [`rand_core::block`](crate::block) code this must be `[Self::Word; _]`.
type Output;

/// Generate a new block of `output`.
///
/// This must fill `output` with random data.
fn generate(&mut self, output: &mut Self::Output);
/// This must fill `output` with random data and return the first usable
/// index. The return value must be less than the length.
#[must_use]
fn generate(&mut self, output: &mut Self::Output) -> usize;

/// Erase results from the output buffer
///
/// This method is called after values from the buffer are consumed and
/// before the buffer is deconstructed.
/// The default implementation does nothing; an overriding implementation
/// might be used for fast erasure of consumed values.
#[inline]
fn erase(slice: &mut [Self::Word]) {
let _ = slice;
}

/// Destruct the output buffer
///
/// This method is called on [`Drop`] of the [`Self::Output`] buffer.
/// The default implementation does nothing.
/// The default implementation does nothing; an overriding implementation
/// might be used to (securely) erase results.
#[inline]
fn drop(&mut self, output: &mut Self::Output) {
let _ = output;
Expand Down Expand Up @@ -129,13 +148,14 @@ where
}
}

/// Calls [`Generator::drop`] on the output buffer
impl<G: Generator> Drop for BlockRng<G> {
fn drop(&mut self) {
self.core.drop(&mut self.results);
}
}

impl<W: Word + Default, const N: usize, G: Generator<Output = [W; N]>> BlockRng<G> {
impl<W: Word + Default, const N: usize, G: Generator<Word = W, Output = [W; N]>> BlockRng<G> {
/// Create a new `BlockRng` from an existing RNG implementing
/// `Generator`. Results will be generated on first use.
#[inline]
Expand All @@ -144,27 +164,9 @@ impl<W: Word + Default, const N: usize, G: Generator<Output = [W; N]>> BlockRng<
results[0] = W::from_usize(N);
BlockRng { core, results }
}

/// Reconstruct from a core and a remaining-results buffer.
///
/// This may be used to deserialize using a `core` and the output of
/// [`Self::remaining_results`].
///
/// Returns `None` if `remaining_results` is too long.
pub fn reconstruct(core: G, remaining_results: &[W]) -> Option<Self> {
let mut results = [W::default(); N];
if remaining_results.len() < N {
let index = N - remaining_results.len();
results[index..].copy_from_slice(remaining_results);
results[0] = W::from_usize(index);
Some(BlockRng { results, core })
} else {
None
}
}
}

impl<W: Word, const N: usize, G: Generator<Output = [W; N]>> BlockRng<G> {
impl<W: Word, const N: usize, G: Generator<Word = W, Output = [W; N]>> BlockRng<G> {
/// Get the index into the result buffer.
///
/// If this is equal to or larger than the size of the result buffer then
Expand Down Expand Up @@ -198,9 +200,10 @@ impl<W: Word, const N: usize, G: Generator<Output = [W; N]>> BlockRng<G> {
return;
}

assert!(n < N);
self.core.generate(&mut self.results);
self.set_index(n);
let index = self.core.generate(&mut self.results);
let index = index.max(n);
assert!(index < N);
self.set_index(index);
}

/// Get the number of words consumed since the start of the block
Expand All @@ -213,36 +216,23 @@ impl<W: Word, const N: usize, G: Generator<Output = [W; N]>> BlockRng<G> {
if index >= N { 0 } else { index }
}

/// Access the unused part of the results buffer
///
/// The length of the returned slice is guaranteed to be less than the
/// length of `<Self as Generator>::Output` (i.e. less than `N` where
/// `Output = [W; N]`).
///
/// This is a low-level interface intended for serialization.
/// Results are not marked as consumed.
#[inline]
pub fn remaining_results(&self) -> &[W] {
let index = self.index();
&self.results[index..]
}

/// Generate the next word (e.g. `u32`)
#[inline]
pub fn next_word(&mut self) -> W {
let mut index = self.index();
if index >= N {
self.core.generate(&mut self.results);
index = 0;
index = self.core.generate(&mut self.results);
assert!(index < N);
}

let value = self.results[index];
G::erase(&mut self.results[index..index + 1]);
self.set_index(index + 1);
value
}
}

impl<const N: usize, G: Generator<Output = [u32; N]>> BlockRng<G> {
impl<const N: usize, G: Generator<Word = u32, Output = [u32; N]>> BlockRng<G> {
/// Generate a `u64` from two `u32` words
#[inline]
pub fn next_u64_from_u32(&mut self) -> u64 {
Expand All @@ -253,32 +243,35 @@ impl<const N: usize, G: Generator<Output = [u32; N]>> BlockRng<G> {
lo = self.results[index];
hi = self.results[index + 1];
new_index = index + 2;
G::erase(&mut self.results[index..new_index]);
} else {
lo = self.results[N - 1];
self.core.generate(&mut self.results);
hi = self.results[0];
new_index = 1;
new_index = self.core.generate(&mut self.results);
assert!(new_index + 1 < N);
hi = self.results[new_index];
new_index += 1;
if index >= N {
lo = hi;
hi = self.results[1];
new_index = 2;
hi = self.results[new_index];
new_index += 1;
}
G::erase(&mut self.results[0..new_index]);
}
self.set_index(new_index);
(u64::from(hi) << 32) | u64::from(lo)
}
}

impl<W: Word, const N: usize, G: Generator<Output = [W; N]>> BlockRng<G> {
impl<W: Word, const N: usize, G: Generator<Word = W, Output = [W; N]>> BlockRng<G> {
/// Fill `dest`
#[inline]
pub fn fill_bytes(&mut self, dest: &mut [u8]) {
let mut read_len = 0;
let mut index = self.index();
while read_len < dest.len() {
if index >= N {
self.core.generate(&mut self.results);
index = 0;
index = self.core.generate(&mut self.results);
assert!(index < N);
}

let size = core::mem::size_of::<W>();
Expand All @@ -303,6 +296,8 @@ impl<W: Word, const N: usize, G: Generator<Output = [W; N]>> BlockRng<G> {
break;
}
}

G::erase(&mut self.results[0..index]);
self.set_index(index);
}
}
4 changes: 3 additions & 1 deletion tests/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ struct DummyRng {
}

impl Generator for DummyRng {
type Word = u32;
type Output = [u32; RESULTS_LEN];

fn generate(&mut self, output: &mut Self::Output) {
fn generate(&mut self, output: &mut Self::Output) -> usize {
for item in output {
*item = self.counter;
self.counter = self.counter.wrapping_add(3511615421);
}
0
}
}

Expand Down