diff --git a/CHANGELOG.md b/CHANGELOG.md index 325ad7cb..cf8a3714 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Removed optional dependency `serde` ([#28]) - Add `SeedableRng::fork` methods ([#17]) - Rename trait `block::BlockRngCore` to `block::Generator` and associated type `Results` to `Output`; remove assoc. `type Item` and remove type bounds ([#26]) -- Add `fn drop` to trait `block::Generator` (#35) +- Add `fn drop` to trait `block::Generator` ([#35]) +- Remove `BlockRng64` ([#34]) +- Remove impl of `RngCore` for `BlockRng`, making the latter more generic ([#34]) +- Add trait `le::Word` ([#34]) ### Other - Changed repository from [rust-random/rand] to [rust-random/core]. @@ -31,6 +34,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [#17]: https://github.com/rust-random/rand-core/pull/17 [#26]: https://github.com/rust-random/rand-core/pull/28 [#28]: https://github.com/rust-random/rand-core/pull/28 +[#34]: https://github.com/rust-random/rand-core/pull/34 [#35]: https://github.com/rust-random/rand-core/pull/35 [rust-random/rand]: https://github.com/rust-random/rand diff --git a/src/block.rs b/src/block.rs index db9a5ca4..0889bcd8 100644 --- a/src/block.rs +++ b/src/block.rs @@ -1,51 +1,91 @@ -//! The `Generator` trait and implementation helpers +//! The [`Generator`] trait and [`BlockRng`] //! -//! The [`Generator`] trait exists to assist in the implementation of RNGs -//! which generate a block of data in a cache instead of returning generated -//! values directly. +//! Trait [`Generator`] and marker trait [`CryptoGenerator`] may be implemented +//! by block-generators; that is PRNGs whose output is a *block* of words, such +//! as `[u32; 16]`. //! -//! Usage of this trait is optional, but provides two advantages: -//! implementations only need to concern themselves with generation of the -//! block, not the various [`RngCore`] methods (especially [`fill_bytes`], where -//! the optimal implementations are not trivial), and this allows -//! `ReseedingRng` (see [`rand`](https://docs.rs/rand) crate) perform periodic -//! reseeding with very low overhead. +//! The struct [`BlockRng`] wraps such a [`Generator`] together with an output +//! buffer and implements several methods (e.g. [`BlockRng::next_word`]) to +//! assist in the implementation of [`RngCore`]. Note that (unlike in earlier +//! versions of `rand_core`) [`BlockRng`] itself does not implement [`RngCore`] +//! since in practice we found it was always beneficial to use a wrapper type +//! over [`BlockRng`]. //! //! # Example //! -//! ```no_run +//! ``` //! use rand_core::{RngCore, SeedableRng}; //! use rand_core::block::{Generator, BlockRng}; //! -//! struct MyRngCore; +//! struct MyRngCore { +//! // Generator state ... +//! # state: [u32; 8], +//! } //! //! impl Generator for MyRngCore { -//! type Output = [u32; 16]; +//! type Output = [u32; 8]; //! //! fn generate(&mut self, output: &mut Self::Output) { -//! unimplemented!() +//! // Write a new block to output... +//! # *output = self.state; //! } //! } //! -//! impl SeedableRng for MyRngCore { +//! // Our RNG is a wrapper over BlockRng +//! pub struct MyRng(BlockRng); +//! +//! impl SeedableRng for MyRng { //! type Seed = [u8; 32]; //! fn from_seed(seed: Self::Seed) -> Self { -//! unimplemented!() +//! let core = MyRngCore { +//! // ... +//! # state: { +//! # let mut buf = [0u32; 8]; +//! # rand_core::le::read_u32_into(&seed, &mut buf); +//! # buf +//! # } +//! }; +//! MyRng(BlockRng::new(core)) //! } //! } //! -//! // optionally, also implement CryptoGenerator for MyRngCore +//! impl RngCore for MyRng { +//! #[inline] +//! fn next_u32(&mut self) -> u32 { +//! self.0.next_word() +//! } +//! +//! #[inline] +//! fn next_u64(&mut self) -> u64 { +//! self.0.next_u64_from_u32() +//! } //! -//! // Final RNG. -//! let mut rng = BlockRng::::seed_from_u64(0); +//! #[inline] +//! fn fill_bytes(&mut self, bytes: &mut [u8]) { +//! self.0.fill_bytes(bytes) +//! } +//! } +//! +//! // And if applicable: impl CryptoRng for MyRng {} +//! +//! let mut rng = MyRng::seed_from_u64(0); //! println!("First value: {}", rng.next_u32()); +//! # assert_eq!(rng.next_u32(), 1171109249); //! ``` //! +//! # ReseedingRng +//! +//! The [`Generator`] trait supports usage of [`rand::rngs::ReseedingRng`]. +//! This requires that [`SeedableRng`] be implemented on the "core" generator. +//! Additionally, it may be useful to implement [`CryptoGenerator`]. +//! (This is in addition to any implementations on an [`RngCore`] type.) +//! //! [`Generator`]: crate::block::Generator -//! [`fill_bytes`]: RngCore::fill_bytes +//! [`RngCore`]: crate::RngCore +//! [`SeedableRng`]: crate::SeedableRng +//! [`rand::rngs::ReseedingRng`]: https://docs.rs/rand/latest/rand/rngs/struct.ReseedingRng.html -use crate::le::fill_via_chunks; -use crate::{CryptoRng, RngCore, SeedableRng, TryRngCore}; +use crate::le::{Word, fill_via_chunks}; use core::fmt; /// A random (block) generator @@ -79,42 +119,20 @@ pub trait Generator { /// `#[cfg(test)]` attribute to ensure that mock "crypto" generators cannot be /// used in production. /// -/// See [`CryptoRng`] docs for more information. +/// See [`CryptoRng`](crate::CryptoRng) docs for more information. pub trait CryptoGenerator: Generator {} -/// A wrapper type implementing [`RngCore`] for some type implementing -/// [`Generator`] with `u32` array buffer; i.e. this can be used to implement -/// a full RNG from just a `generate` function. -/// -/// The `core` field may be accessed directly but the results buffer may not. -/// PRNG implementations can simply use a type alias -/// (`pub type MyRng = BlockRng;`) but might prefer to use a -/// wrapper type (`pub struct MyRng(BlockRng);`); the latter must -/// re-implement `RngCore` but hides the implementation details and allows -/// extra functionality to be defined on the RNG -/// (e.g. `impl MyRng { fn set_stream(...){...} }`). -/// -/// `BlockRng` has heavily optimized implementations of the [`RngCore`] methods -/// reading values from the results buffer, as well as -/// calling [`Generator::generate`] directly on the output array when -/// [`fill_bytes`] is called on a large array. These methods also handle -/// the bookkeeping of when to generate a new batch of values. +/// RNG functionality for a block [`Generator`] /// -/// No whole generated `u32` values are thrown away and all values are consumed -/// in-order. [`next_u32`] simply takes the next available `u32` value. -/// [`next_u64`] is implemented by combining two `u32` values, least -/// significant first. [`fill_bytes`] consume a whole number of `u32` values, -/// converting each `u32` to a byte slice in little-endian order. If the requested byte -/// length is not a multiple of 4, some bytes will be discarded. +/// This type encompasses a [`Generator`] [`core`](Self::core) and a buffer. +/// It provides optimized implementations of methods required by an [`RngCore`]. /// -/// See also [`BlockRng64`] which uses `u64` array buffers. Currently there is -/// no direct support for other buffer types. +/// All values are consumed in-order of generation. No whole words (e.g. `u32` +/// or `u64`) are discarded, though where a word is partially used (e.g. for a +/// byte-fill whose length is not a multiple of the word size) the rest of the +/// word is discarded. /// -/// For easy initialization `BlockRng` also implements [`SeedableRng`]. -/// -/// [`next_u32`]: RngCore::next_u32 -/// [`next_u64`]: RngCore::next_u64 -/// [`fill_bytes`]: RngCore::fill_bytes +/// [`RngCore`]: crate::RngCore #[derive(Clone)] pub struct BlockRng { results: G::Output, @@ -139,7 +157,7 @@ impl Drop for BlockRng { } } -impl> BlockRng { +impl> BlockRng { /// Create a new `BlockRng` from an existing RNG implementing /// `Generator`. Results will be generated on first use. #[inline] @@ -147,10 +165,12 @@ impl> BlockRng { BlockRng { core, index: N, - results: [0; N], + results: [W::default(); N], } } +} +impl> BlockRng { /// Get the index into the result buffer. /// /// If this is equal to or larger than the size of the result buffer then @@ -176,22 +196,24 @@ impl> BlockRng { self.core.generate(&mut self.results); self.index = index; } -} -impl> RngCore for BlockRng { + /// Generate the next word (e.g. `u32`) #[inline] - fn next_u32(&mut self) -> u32 { + pub fn next_word(&mut self) -> W { if self.index >= N { self.generate_and_set(0); } - let value = self.results[self.index]; + let value = self.results[self.index].clone(); self.index += 1; value } +} +impl> BlockRng { + /// Generate a `u64` from two `u32` words #[inline] - fn next_u64(&mut self) -> u64 { + pub fn next_u64_from_u32(&mut self) -> u64 { let read_u64 = |results: &[u32], index| { let data = &results[index..=index + 1]; (u64::from(data[1]) << 32) | u64::from(data[0]) @@ -212,9 +234,12 @@ impl> RngCore for BlockRng { (y << 32) | x } } +} +impl> BlockRng { + /// Fill `dest` #[inline] - fn fill_bytes(&mut self, dest: &mut [u8]) { + pub fn fill_bytes(&mut self, dest: &mut [u8]) { let mut read_len = 0; while read_len < dest.len() { if self.index >= N { @@ -229,196 +254,10 @@ impl> RngCore for BlockRng { } } -impl + SeedableRng> SeedableRng for BlockRng { - type Seed = G::Seed; - - #[inline(always)] - fn from_seed(seed: Self::Seed) -> Self { - Self::new(G::from_seed(seed)) - } - - #[inline(always)] - fn seed_from_u64(seed: u64) -> Self { - Self::new(G::seed_from_u64(seed)) - } - - #[inline(always)] - fn from_rng(rng: &mut S) -> Self { - Self::new(G::from_rng(rng)) - } - - #[inline(always)] - fn try_from_rng(rng: &mut S) -> Result { - G::try_from_rng(rng).map(Self::new) - } -} - -impl> CryptoRng for BlockRng {} - -/// A wrapper type implementing [`RngCore`] for some type implementing -/// [`Generator`] with `u64` array buffer; i.e. this can be used to implement -/// a full RNG from just a `generate` function. -/// -/// This is similar to [`BlockRng`], but specialized for algorithms that operate -/// on `u64` values. -/// -/// No whole generated `u64` values are thrown away and all values are consumed -/// in-order. [`next_u64`] simply takes the next available `u64` value. -/// [`next_u32`] is however a bit special: half of a `u64` is consumed, leaving -/// the other half in the buffer. If the next function called is [`next_u32`] -/// then the other half is then consumed, however both [`next_u64`] and -/// [`fill_bytes`] discard the rest of any half-consumed `u64`s when called. -/// -/// [`fill_bytes`] consumes a whole number of `u64` values. If the requested length -/// is not a multiple of 8, some bytes will be discarded. -/// -/// [`next_u32`]: RngCore::next_u32 -/// [`next_u64`]: RngCore::next_u64 -/// [`fill_bytes`]: RngCore::fill_bytes -#[derive(Clone)] -pub struct BlockRng64 { - results: G::Output, - index: usize, - half_used: bool, // true if only half of the previous result is used - /// The *core* part of the RNG, implementing the `generate` function. - pub core: G, -} - -// Custom Debug implementation that does not expose the contents of `results`. -impl fmt::Debug for BlockRng64 { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.debug_struct("BlockRng64") - .field("core", &self.core) - .field("index", &self.index) - .field("half_used", &self.half_used) - .finish() - } -} - -impl> BlockRng64 { - /// Create a new `BlockRng` from an existing RNG implementing - /// `Generator`. Results will be generated on first use. - #[inline] - pub fn new(core: G) -> BlockRng64 { - BlockRng64 { - core, - index: N, - half_used: false, - results: [0; N], - } - } - - /// Get the index into the result buffer. - /// - /// If this is equal to or larger than the size of the result buffer then - /// the buffer is "empty" and `generate()` must be called to produce new - /// results. - #[inline(always)] - pub fn index(&self) -> usize { - self.index - } - - /// Reset the number of available results. - /// This will force a new set of results to be generated on next use. - #[inline] - pub fn reset(&mut self) { - self.index = N; - self.half_used = false; - } - - /// Generate a new set of results immediately, setting the index to the - /// given value. - #[inline] - pub fn generate_and_set(&mut self, index: usize) { - assert!(index < N); - self.core.generate(&mut self.results); - self.index = index; - self.half_used = false; - } -} - -impl> RngCore for BlockRng64 { - #[inline] - fn next_u32(&mut self) -> u32 { - let mut index = self.index - self.half_used as usize; - if index >= N { - self.core.generate(&mut self.results); - self.index = 0; - index = 0; - // `self.half_used` is by definition `false` - self.half_used = false; - } - - let shift = 32 * (self.half_used as usize); - - self.half_used = !self.half_used; - self.index += self.half_used as usize; - - (self.results[index] >> shift) as u32 - } - - #[inline] - fn next_u64(&mut self) -> u64 { - if self.index >= N { - self.core.generate(&mut self.results); - self.index = 0; - } - - let value = self.results[self.index]; - self.index += 1; - self.half_used = false; - value - } - - #[inline] - fn fill_bytes(&mut self, dest: &mut [u8]) { - let mut read_len = 0; - self.half_used = false; - while read_len < dest.len() { - if self.index >= N { - self.core.generate(&mut self.results); - self.index = 0; - } - - let (consumed_u64, filled_u8) = - fill_via_chunks(&self.results[self.index..], &mut dest[read_len..]); - - self.index += consumed_u64; - read_len += filled_u8; - } - } -} - -impl + SeedableRng> SeedableRng for BlockRng64 { - type Seed = G::Seed; - - #[inline(always)] - fn from_seed(seed: Self::Seed) -> Self { - Self::new(G::from_seed(seed)) - } - - #[inline(always)] - fn seed_from_u64(seed: u64) -> Self { - Self::new(G::seed_from_u64(seed)) - } - - #[inline(always)] - fn from_rng(rng: &mut S) -> Self { - Self::new(G::from_rng(rng)) - } - - #[inline(always)] - fn try_from_rng(rng: &mut S) -> Result { - G::try_from_rng(rng).map(Self::new) - } -} - -impl> CryptoRng for BlockRng64 {} - #[cfg(test)] mod test { - use crate::block::{BlockRng, BlockRng64, Generator}; - use crate::{RngCore, SeedableRng}; + use crate::SeedableRng; + use crate::block::{BlockRng, Generator}; #[derive(Debug, Clone)] struct DummyRng { @@ -448,106 +287,38 @@ mod test { #[test] fn blockrng_next_u32_vs_next_u64() { - let mut rng1 = BlockRng::::from_seed([1, 2, 3, 4]); + let mut rng1 = BlockRng::new(DummyRng::from_seed([1, 2, 3, 4])); let mut rng2 = rng1.clone(); let mut rng3 = rng1.clone(); let mut a = [0; 16]; - a[..4].copy_from_slice(&rng1.next_u32().to_le_bytes()); - a[4..12].copy_from_slice(&rng1.next_u64().to_le_bytes()); - a[12..].copy_from_slice(&rng1.next_u32().to_le_bytes()); + a[..4].copy_from_slice(&rng1.next_word().to_le_bytes()); + a[4..12].copy_from_slice(&rng1.next_u64_from_u32().to_le_bytes()); + a[12..].copy_from_slice(&rng1.next_word().to_le_bytes()); let mut b = [0; 16]; - b[..4].copy_from_slice(&rng2.next_u32().to_le_bytes()); - b[4..8].copy_from_slice(&rng2.next_u32().to_le_bytes()); - b[8..].copy_from_slice(&rng2.next_u64().to_le_bytes()); + b[..4].copy_from_slice(&rng2.next_word().to_le_bytes()); + b[4..8].copy_from_slice(&rng2.next_word().to_le_bytes()); + b[8..].copy_from_slice(&rng2.next_u64_from_u32().to_le_bytes()); assert_eq!(a, b); let mut c = [0; 16]; - c[..8].copy_from_slice(&rng3.next_u64().to_le_bytes()); - c[8..12].copy_from_slice(&rng3.next_u32().to_le_bytes()); - c[12..].copy_from_slice(&rng3.next_u32().to_le_bytes()); + c[..8].copy_from_slice(&rng3.next_u64_from_u32().to_le_bytes()); + c[8..12].copy_from_slice(&rng3.next_word().to_le_bytes()); + c[12..].copy_from_slice(&rng3.next_word().to_le_bytes()); assert_eq!(a, c); } - #[derive(Debug, Clone)] - struct DummyRng64 { - counter: u64, - } - - impl Generator for DummyRng64 { - type Output = [u64; 8]; - - fn generate(&mut self, output: &mut Self::Output) { - for item in output { - *item = self.counter; - self.counter = self.counter.wrapping_add(2781463553396133981); - } - } - } - - impl SeedableRng for DummyRng64 { - type Seed = [u8; 8]; - - fn from_seed(seed: Self::Seed) -> Self { - DummyRng64 { - counter: u64::from_le_bytes(seed), - } - } - } - - #[test] - fn blockrng64_next_u32_vs_next_u64() { - let mut rng1 = BlockRng64::::from_seed([1, 2, 3, 4, 5, 6, 7, 8]); - let mut rng2 = rng1.clone(); - let mut rng3 = rng1.clone(); - - let mut a = [0; 16]; - a[..4].copy_from_slice(&rng1.next_u32().to_le_bytes()); - a[4..12].copy_from_slice(&rng1.next_u64().to_le_bytes()); - a[12..].copy_from_slice(&rng1.next_u32().to_le_bytes()); - - let mut b = [0; 16]; - b[..4].copy_from_slice(&rng2.next_u32().to_le_bytes()); - b[4..8].copy_from_slice(&rng2.next_u32().to_le_bytes()); - b[8..].copy_from_slice(&rng2.next_u64().to_le_bytes()); - assert_ne!(a, b); - assert_eq!(&a[..4], &b[..4]); - assert_eq!(&a[4..12], &b[8..]); - - let mut c = [0; 16]; - c[..8].copy_from_slice(&rng3.next_u64().to_le_bytes()); - c[8..12].copy_from_slice(&rng3.next_u32().to_le_bytes()); - c[12..].copy_from_slice(&rng3.next_u32().to_le_bytes()); - assert_eq!(b, c); - } - - #[test] - fn blockrng64_generate_and_set() { - let mut rng = BlockRng64::::from_seed([1, 2, 3, 4, 5, 6, 7, 8]); - assert_eq!(rng.index(), rng.results.len()); - - rng.generate_and_set(5); - assert_eq!(rng.index(), 5); - } - - #[test] - #[should_panic(expected = "index < N")] - fn blockrng64_generate_and_set_panic() { - let mut rng = BlockRng64::::from_seed([1, 2, 3, 4, 5, 6, 7, 8]); - rng.generate_and_set(rng.results.len()); - } - #[test] fn blockrng_next_u64() { - let mut rng = BlockRng::::from_seed([1, 2, 3, 4]); + let mut rng = BlockRng::new(DummyRng::from_seed([1, 2, 3, 4])); let result_size = rng.results.len(); for _i in 0..result_size / 2 - 1 { - rng.next_u64(); + rng.next_u64_from_u32(); } - rng.next_u32(); + rng.next_word(); - let _ = rng.next_u64(); + let _ = rng.next_u64_from_u32(); assert_eq!(rng.index(), 1); } } diff --git a/src/le.rs b/src/le.rs index e42c572d..b8508ff1 100644 --- a/src/le.rs +++ b/src/le.rs @@ -71,31 +71,39 @@ pub fn fill_bytes_via_next(rng: &mut R, dest: &mut [u8]) { } } -pub(crate) trait Observable: Copy { - type Bytes: Sized + AsRef<[u8]>; - fn to_le_bytes(self) -> Self::Bytes; -} -impl Observable for u32 { - type Bytes = [u8; 4]; +mod word { + pub trait Sealed: Copy { + type Bytes: Sized + AsRef<[u8]>; + fn to_le_bytes(self) -> Self::Bytes; + } + impl Sealed for u32 { + type Bytes = [u8; 4]; - fn to_le_bytes(self) -> Self::Bytes { - Self::to_le_bytes(self) + fn to_le_bytes(self) -> Self::Bytes { + Self::to_le_bytes(self) + } } -} -impl Observable for u64 { - type Bytes = [u8; 8]; + impl Sealed for u64 { + type Bytes = [u8; 8]; - fn to_le_bytes(self) -> Self::Bytes { - Self::to_le_bytes(self) + fn to_le_bytes(self) -> Self::Bytes { + Self::to_le_bytes(self) + } } } +/// A marker trait for supported word types +/// +/// This is implemented for: `u32`, `u64`. +pub trait Word: word::Sealed {} +impl Word for W {} + /// Fill dest from src /// /// Returns `(n, byte_len)`. `src[..n]` is consumed, /// `dest[..byte_len]` is filled. `src[n..]` and `dest[byte_len..]` are left /// unaltered. -pub(crate) fn fill_via_chunks(src: &[T], dest: &mut [u8]) -> (usize, usize) { +pub(crate) fn fill_via_chunks(src: &[T], dest: &mut [u8]) -> (usize, usize) { let size = core::mem::size_of::(); // Always use little endian for portability of results.