Skip to content
Open
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
117 changes: 41 additions & 76 deletions src/block.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,20 @@
//! The `BlockRngCore` trait and implementation helpers
//! Support for block generators
//!
//! The [`BlockRngCore`] trait exists to assist in the implementation of RNGs
//! which generate a block of data in a cache instead of returning generated
//! values directly.
//!
//! 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 [`BlockRng`] and [`BlockRng64`] types may be used to provide an
//! implementation of [`RngCore`] over a block [`Generator`].
//!
//! # Example
//!
//! ```no_run
//! use rand_core::{RngCore, SeedableRng};
//! use rand_core::block::{BlockRngCore, BlockRng};
//! use rand_core::{Generator, RngCore, SeedableRng};
//! use rand_core::block::BlockRng;
//!
//! struct MyRngCore;
//!
//! impl BlockRngCore for MyRngCore {
//! type Item = u32;
//! type Results = [u32; 16];
//! impl Generator for MyRngCore {
//! type Result = [u32; 16];
//!
//! fn generate(&mut self, results: &mut Self::Results) {
//! fn generate(&mut self, result: &mut Self::Result) {
//! unimplemented!()
//! }
//! }
Expand All @@ -35,47 +26,24 @@
//! }
//! }
//!
//! // optionally, also implement CryptoBlockRng for MyRngCore
//! // optionally, also implement CryptoGenerator for MyRngCore
//!
//! // Final RNG.
//! let mut rng = BlockRng::<MyRngCore>::seed_from_u64(0);
//! println!("First value: {}", rng.next_u32());
//! ```
//!
//! [`BlockRngCore`]: crate::block::BlockRngCore
//! [`Generator`]: crate::block::Generator
//! [`fill_bytes`]: RngCore::fill_bytes

use crate::le::fill_via_chunks;
use crate::{CryptoRng, RngCore, SeedableRng, TryRngCore};
use crate::*;
use core::fmt;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

/// A trait for RNGs which do not generate random numbers individually, but in
/// blocks (typically `[u32; N]`). This technique is commonly used by
/// cryptographic RNGs to improve performance.
///
/// See the [module][crate::block] documentation for details.
pub trait BlockRngCore {
/// Results element type, e.g. `u32`.
type Item;

/// Results type. This is the 'block' an RNG implementing `BlockRngCore`
/// generates, which will usually be an array like `[u32; 16]`.
type Results: AsRef<[Self::Item]> + AsMut<[Self::Item]> + Default;

/// Generate a new block of results.
fn generate(&mut self, results: &mut Self::Results);
}

/// A marker trait used to indicate that an [`RngCore`] implementation is
/// supposed to be cryptographically secure.
///
/// See [`CryptoRng`] docs for more information.
pub trait CryptoBlockRng: BlockRngCore {}

/// A wrapper type implementing [`RngCore`] for some type implementing
/// [`BlockRngCore`] with `u32` array buffer; i.e. this can be used to implement
/// [`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.
Expand All @@ -88,7 +56,7 @@ pub trait CryptoBlockRng: BlockRngCore {}
///
/// `BlockRng` has heavily optimized implementations of the [`RngCore`] methods
/// reading values from the results buffer, as well as
/// calling [`BlockRngCore::generate`] directly on the output array when
/// 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.
///
Expand All @@ -112,18 +80,18 @@ pub trait CryptoBlockRng: BlockRngCore {}
#[cfg_attr(
feature = "serde",
serde(
bound = "for<'x> R: Serialize + Deserialize<'x>, for<'x> R::Results: Serialize + Deserialize<'x>"
bound = "for<'x> R: Serialize + Deserialize<'x>, for<'x> R::Result: Serialize + Deserialize<'x>"
)
)]
pub struct BlockRng<R: BlockRngCore> {
results: R::Results,
pub struct BlockRng<R: Generator> {
results: R::Result,
index: usize,
/// The *core* part of the RNG, implementing the `generate` function.
pub core: R,
}

// Custom Debug implementation that does not expose the contents of `results`.
impl<R: BlockRngCore + fmt::Debug> fmt::Debug for BlockRng<R> {
impl<const N: usize, R: Generator<Result = [u32; N]> + fmt::Debug> fmt::Debug for BlockRng<R> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("BlockRng")
.field("core", &self.core)
Expand All @@ -133,16 +101,15 @@ impl<R: BlockRngCore + fmt::Debug> fmt::Debug for BlockRng<R> {
}
}

impl<R: BlockRngCore> BlockRng<R> {
impl<const N: usize, R: Generator<Result = [u32; N]>> BlockRng<R> {
/// Create a new `BlockRng` from an existing RNG implementing
/// `BlockRngCore`. Results will be generated on first use.
/// `Generator`. Result will be generated on first use.
#[inline]
pub fn new(core: R) -> BlockRng<R> {
let results_empty = R::Results::default();
BlockRng {
core,
index: results_empty.as_ref().len(),
results: results_empty,
index: N,
results: [0; N],
}
}

Expand Down Expand Up @@ -173,7 +140,7 @@ impl<R: BlockRngCore> BlockRng<R> {
}
}

impl<R: BlockRngCore<Item = u32>> RngCore for BlockRng<R> {
impl<const N: usize, R: Generator<Result = [u32; N]>> RngCore for BlockRng<R> {
#[inline]
fn next_u32(&mut self) -> u32 {
if self.index >= self.results.as_ref().len() {
Expand Down Expand Up @@ -226,7 +193,7 @@ impl<R: BlockRngCore<Item = u32>> RngCore for BlockRng<R> {
}
}

impl<R: BlockRngCore + SeedableRng> SeedableRng for BlockRng<R> {
impl<const N: usize, R: Generator<Result = [u32; N]> + SeedableRng> SeedableRng for BlockRng<R> {
type Seed = R::Seed;

#[inline(always)]
Expand All @@ -250,10 +217,10 @@ impl<R: BlockRngCore + SeedableRng> SeedableRng for BlockRng<R> {
}
}

impl<R: CryptoBlockRng + BlockRngCore<Item = u32>> CryptoRng for BlockRng<R> {}
impl<const N: usize, R: CryptoGenerator<Result = [u32; N]>> CryptoRng for BlockRng<R> {}

/// A wrapper type implementing [`RngCore`] for some type implementing
/// [`BlockRngCore`] with `u64` array buffer; i.e. this can be used to implement
/// [`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
Expand All @@ -274,16 +241,16 @@ impl<R: CryptoBlockRng + BlockRngCore<Item = u32>> CryptoRng for BlockRng<R> {}
/// [`fill_bytes`]: RngCore::fill_bytes
#[derive(Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct BlockRng64<R: BlockRngCore + ?Sized> {
results: R::Results,
pub struct BlockRng64<R: Generator + ?Sized> {
results: R::Result,
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: R,
}

// Custom Debug implementation that does not expose the contents of `results`.
impl<R: BlockRngCore + fmt::Debug> fmt::Debug for BlockRng64<R> {
impl<const N: usize, R: Generator<Result = [u64; N]> + fmt::Debug> fmt::Debug for BlockRng64<R> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("BlockRng64")
.field("core", &self.core)
Expand All @@ -294,12 +261,12 @@ impl<R: BlockRngCore + fmt::Debug> fmt::Debug for BlockRng64<R> {
}
}

impl<R: BlockRngCore> BlockRng64<R> {
impl<const N: usize, R: Generator<Result = [u64; N]>> BlockRng64<R> {
/// Create a new `BlockRng` from an existing RNG implementing
/// `BlockRngCore`. Results will be generated on first use.
/// `Generator`. Results will be generated on first use.
#[inline]
pub fn new(core: R) -> BlockRng64<R> {
let results_empty = R::Results::default();
let results_empty = [0; N];
BlockRng64 {
core,
index: results_empty.as_ref().len(),
Expand Down Expand Up @@ -337,7 +304,7 @@ impl<R: BlockRngCore> BlockRng64<R> {
}
}

impl<R: BlockRngCore<Item = u64>> RngCore for BlockRng64<R> {
impl<const N: usize, R: Generator<Result = [u64; N]>> RngCore for BlockRng64<R> {
#[inline]
fn next_u32(&mut self) -> u32 {
let mut index = self.index - self.half_used as usize;
Expand Down Expand Up @@ -389,7 +356,7 @@ impl<R: BlockRngCore<Item = u64>> RngCore for BlockRng64<R> {
}
}

impl<R: BlockRngCore + SeedableRng> SeedableRng for BlockRng64<R> {
impl<const N: usize, R: Generator<Result = [u64; N]> + SeedableRng> SeedableRng for BlockRng64<R> {
type Seed = R::Seed;

#[inline(always)]
Expand All @@ -413,23 +380,22 @@ impl<R: BlockRngCore + SeedableRng> SeedableRng for BlockRng64<R> {
}
}

impl<R: CryptoBlockRng + BlockRngCore<Item = u64>> CryptoRng for BlockRng64<R> {}
impl<const N: usize, R: CryptoGenerator<Result = [u64; N]>> CryptoRng for BlockRng64<R> {}

#[cfg(test)]
mod test {
use crate::block::{BlockRng, BlockRng64, BlockRngCore};
use crate::block::{BlockRng, BlockRng64, Generator};
use crate::{RngCore, SeedableRng};

#[derive(Debug, Clone)]
struct DummyRng {
counter: u32,
}

impl BlockRngCore for DummyRng {
type Item = u32;
type Results = [u32; 16];
impl Generator for DummyRng {
type Result = [u32; 16];

fn generate(&mut self, results: &mut Self::Results) {
fn generate(&mut self, results: &mut Self::Result) {
for r in results {
*r = self.counter;
self.counter = self.counter.wrapping_add(3511615421);
Expand Down Expand Up @@ -476,11 +442,10 @@ mod test {
counter: u64,
}

impl BlockRngCore for DummyRng64 {
type Item = u64;
type Results = [u64; 8];
impl Generator for DummyRng64 {
type Result = [u64; 8];

fn generate(&mut self, results: &mut Self::Results) {
fn generate(&mut self, results: &mut Self::Result) {
for r in results {
*r = self.counter;
self.counter = self.counter.wrapping_add(2781463553396133981);
Expand Down
48 changes: 47 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
#![deny(missing_docs)]
#![deny(missing_debug_implementations)]
#![deny(clippy::undocumented_unsafe_blocks)]
#![doc(test(attr(allow(unused_variables), deny(warnings))))]
#![doc(test(attr(allow(unused), deny(warnings))))]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![no_std]

Expand All @@ -32,6 +32,52 @@ use core::{fmt, ops::DerefMut};
pub mod block;
pub mod le;

/// A random generator
pub trait Generator {
/// The result type.
///
/// This could be a simple word like `u64` or an array like `[u32; 16]`.
type Result;
Comment on lines +37 to +40
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be renamed? Most of the std::ops traits have an Output associated type, so that may be the more logical name.


/// Generate a new result.
///
/// Since [`Self::Result`] may be large, the output is passed by reference.
/// Word generators should likely implement this as a shim over another
/// method:
/// ```
/// pub struct CountingGenerator(u64);
/// impl CountingGenerator {
/// fn next(&mut self) -> u64 {
/// let x = self.0;
/// self.0 = self.0.wrapping_add(1);
/// x
/// }
/// }
///
/// impl rand_core::Generator for CountingGenerator {
/// type Result = u64;
///
/// #[inline]
/// fn generate(&mut self, result: &mut Self::Result) {
/// *result = self.next();
/// }
/// }
/// ```
fn generate(&mut self, result: &mut Self::Result);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generating by-reference has two limitations:

  • Using it requires first constructing a "default" instance. Since we don't even require Result: Default, this is only usable when the Result type is known or otherwise bounded. In practice this seems fine for our use-cases, but it could be an issue.
  • Using it to generate small, Copy types is unergonomic and dependent on inlining for good performance.

Alternative 1: return Self::Result by value. This is easier to use. For chacha20 (uses 2048-bit output blocks) it may be dependent on inlining optimisations for performance. (This type of optimisation is commonly relied on by constructors in Rust.)

Alternative 2: have both fns. Over-complex.

}

/// A cryptographically secure generator
///
/// This is a marker trait used to indicate that a [`Generator`] implementation
/// is supposed to be cryptographically secure.
///
/// Mock generators should not implement this trait *except* under a
/// `#[cfg(test)]` attribute to ensure that mock "crypto" generators cannot be
/// used in production.
///
/// See [`CryptoRng`] docs for more information.
pub trait CryptoGenerator: Generator {}

/// Implementation-level interface for RNGs
///
/// This trait encapsulates the low-level functionality common to all
Expand Down