diff --git a/src/block.rs b/src/block.rs index a18219b4..4e525ce7 100644 --- a/src/block.rs +++ b/src/block.rs @@ -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 //! } //! } //! @@ -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; @@ -129,13 +148,14 @@ where } } +/// Calls [`Generator::drop`] on the output buffer impl Drop for BlockRng { fn drop(&mut self) { self.core.drop(&mut self.results); } } -impl> BlockRng { +impl> BlockRng { /// Create a new `BlockRng` from an existing RNG implementing /// `Generator`. Results will be generated on first use. #[inline] @@ -144,27 +164,9 @@ impl> 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 { - 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> BlockRng { +impl> BlockRng { /// Get the index into the result buffer. /// /// If this is equal to or larger than the size of the result buffer then @@ -198,9 +200,10 @@ impl> BlockRng { 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 @@ -213,36 +216,23 @@ impl> BlockRng { 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 `::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> BlockRng { +impl> BlockRng { /// Generate a `u64` from two `u32` words #[inline] pub fn next_u64_from_u32(&mut self) -> u64 { @@ -253,23 +243,26 @@ impl> BlockRng { 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> BlockRng { +impl> BlockRng { /// Fill `dest` #[inline] pub fn fill_bytes(&mut self, dest: &mut [u8]) { @@ -277,8 +270,8 @@ impl> BlockRng { 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::(); @@ -303,6 +296,8 @@ impl> BlockRng { break; } } + + G::erase(&mut self.results[0..index]); self.set_index(index); } } diff --git a/tests/block.rs b/tests/block.rs index ef818183..85130ef8 100644 --- a/tests/block.rs +++ b/tests/block.rs @@ -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 } }