Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
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
17 changes: 10 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ features = ["small_rng", "serde"]

[features]
# Meta-features:
default = ["std", "std_rng", "os_rng", "small_rng", "thread_rng"]
# default = ["std", "std_rng", "os_rng", "small_rng", "thread_rng"]
nightly = [] # some additions requiring nightly Rust
serde = ["dep:serde", "rand_core/serde"]
serde = ["dep:serde"]

# Option (enabled by default): without "std" rand uses libcore; this option
# enables functionality expected to be available on a standard platform.
Expand All @@ -46,16 +46,16 @@ os_rng = ["dep:getrandom"]
simd_support = []

# Option (enabled by default): enable StdRng
std_rng = ["dep:chacha20"]
# std_rng = ["dep:chacha20"]

# Option: enable SmallRng
small_rng = []

# Option: enable ThreadRng and rng()
thread_rng = ["std", "std_rng", "os_rng"]
# thread_rng = ["std", "std_rng", "os_rng"]

# Option: enable rand::rngs::ChaCha*Rng
chacha = ["dep:chacha20"]
# chacha = ["dep:chacha20"]

# Option: use unbiased sampling for algorithms supporting this option: Uniform distribution.
# By default, bias affecting no more than one in 2^48 samples is accepted.
Expand All @@ -67,7 +67,7 @@ log = ["dep:log"]

[workspace]
members = [
"rand_chacha",
# "rand_chacha",
"rand_pcg",
]
exclude = ["benches", "distr_test"]
Expand All @@ -76,7 +76,7 @@ exclude = ["benches", "distr_test"]
rand_core = { version = "0.10.0-rc-2", default-features = false }
log = { version = "0.4.4", optional = true }
serde = { version = "1.0.103", features = ["derive"], optional = true }
chacha20 = { version = "=0.10.0-rc.5", default-features = false, features = ["rng"], optional = true }
# chacha20 = { version = "=0.10.0-rc.5", default-features = false, features = ["rng"], optional = true }
getrandom = { version = "0.3.0", optional = true }

[dev-dependencies]
Expand All @@ -85,3 +85,6 @@ rand_pcg = { path = "rand_pcg", version = "0.10.0-rc.1" }
bincode = "1.2.1"
rayon = "1.7"
serde_json = "1.0.140"

[patch.crates-io]
rand_core = { git = "https://github.com/rust-random/rand_core", branch = "block_buffer" }
16 changes: 7 additions & 9 deletions rand_pcg/src/pcg128.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,10 +126,9 @@ impl SeedableRng for Lcg128Xsl64 {
/// We use a single 255-bit seed to initialise the state and select a stream.
/// One `seed` bit (lowest bit of `seed[8]`) is ignored.
fn from_seed(seed: Self::Seed) -> Self {
let mut seed_u64 = [0u64; 4];
le::read_u64_into(&seed, &mut seed_u64);
let state = u128::from(seed_u64[0]) | (u128::from(seed_u64[1]) << 64);
let incr = u128::from(seed_u64[2]) | (u128::from(seed_u64[3]) << 64);
let seed: [u64; 4] = le::read_words(&seed);
let state = u128::from(seed[0]) | (u128::from(seed[1]) << 64);
let incr = u128::from(seed[2]) | (u128::from(seed[3]) << 64);

// The increment must be odd, hence we discard one bit:
Lcg128Xsl64::from_state_incr(state, incr | 1)
Expand All @@ -150,7 +149,7 @@ impl RngCore for Lcg128Xsl64 {

#[inline]
fn fill_bytes(&mut self, dest: &mut [u8]) {
le::fill_bytes_via_next(self, dest)
le::fill_bytes_via_next_word(dest, || self.next_u64())
}
}

Expand Down Expand Up @@ -232,9 +231,8 @@ impl SeedableRng for Mcg128Xsl64 {

fn from_seed(seed: Self::Seed) -> Self {
// Read as if a little-endian u128 value:
let mut seed_u64 = [0u64; 2];
le::read_u64_into(&seed, &mut seed_u64);
let state = u128::from(seed_u64[0]) | (u128::from(seed_u64[1]) << 64);
let seed: [u64; 2] = le::read_words(&seed);
let state = u128::from(seed[0]) | (u128::from(seed[1]) << 64);
Mcg128Xsl64::new(state)
}
}
Expand All @@ -253,7 +251,7 @@ impl RngCore for Mcg128Xsl64 {

#[inline]
fn fill_bytes(&mut self, dest: &mut [u8]) {
le::fill_bytes_via_next(self, dest)
le::fill_bytes_via_next_word(dest, || self.next_u64())
}
}

Expand Down
9 changes: 4 additions & 5 deletions rand_pcg/src/pcg128cm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,10 +131,9 @@ impl SeedableRng for Lcg128CmDxsm64 {
/// We use a single 255-bit seed to initialise the state and select a stream.
/// One `seed` bit (lowest bit of `seed[8]`) is ignored.
fn from_seed(seed: Self::Seed) -> Self {
let mut seed_u64 = [0u64; 4];
le::read_u64_into(&seed, &mut seed_u64);
let state = u128::from(seed_u64[0]) | (u128::from(seed_u64[1]) << 64);
let incr = u128::from(seed_u64[2]) | (u128::from(seed_u64[3]) << 64);
let seed: [u64; 4] = le::read_words(&seed);
let state = u128::from(seed[0]) | (u128::from(seed[1]) << 64);
let incr = u128::from(seed[2]) | (u128::from(seed[3]) << 64);

// The increment must be odd, hence we discard one bit:
Self::from_state_incr(state, incr | 1)
Expand All @@ -156,7 +155,7 @@ impl RngCore for Lcg128CmDxsm64 {

#[inline]
fn fill_bytes(&mut self, dest: &mut [u8]) {
le::fill_bytes_via_next(self, dest)
le::fill_bytes_via_next_word(dest, || self.next_u64())
}
}

Expand Down
8 changes: 3 additions & 5 deletions rand_pcg/src/pcg64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,11 +127,9 @@ impl SeedableRng for Lcg64Xsh32 {
/// We use a single 127-bit seed to initialise the state and select a stream.
/// One `seed` bit (lowest bit of `seed[8]`) is ignored.
fn from_seed(seed: Self::Seed) -> Self {
let mut seed_u64 = [0u64; 2];
le::read_u64_into(&seed, &mut seed_u64);

let [state, increment] = le::read_words(&seed);
// The increment must be odd, hence we discard one bit:
Lcg64Xsh32::from_state_incr(seed_u64[0], seed_u64[1] | 1)
Lcg64Xsh32::from_state_incr(state, increment | 1)
}
}

Expand Down Expand Up @@ -159,6 +157,6 @@ impl RngCore for Lcg64Xsh32 {

#[inline]
fn fill_bytes(&mut self, dest: &mut [u8]) {
le::fill_bytes_via_next(self, dest)
le::fill_bytes_via_next_word(dest, || self.next_u32())
}
}
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@ mod test {
}

fn fill_bytes(&mut self, dst: &mut [u8]) {
rand_core::le::fill_bytes_via_next(self, dst)
rand_core::le::fill_bytes_via_next_word(dst, || self.next_u64())
}
}

Expand Down
167 changes: 56 additions & 111 deletions src/rngs/reseeding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@
//! generates a certain number of random bytes.

use core::mem::size_of_val;

use rand_core::block::{BlockRng, BlockRngCore, CryptoBlockRng};
use rand_core::{CryptoRng, RngCore, SeedableRng, TryCryptoRng, TryRngCore};

/// A wrapper around any PRNG that implements [`BlockRngCore`], that adds the
Expand Down Expand Up @@ -67,14 +65,20 @@ use rand_core::{CryptoRng, RngCore, SeedableRng, TryCryptoRng, TryRngCore};
/// [`ReseedingRng::new`]: ReseedingRng::new
/// [`reseed()`]: ReseedingRng::reseed
#[derive(Debug)]
pub struct ReseedingRng<R, Rsdr>(BlockRng<ReseedingCore<R, Rsdr>>)
pub struct ReseedingRng<R, Rsdr>
where
R: BlockRngCore + SeedableRng,
Rsdr: TryRngCore;
R: RngCore + SeedableRng,
Rsdr: TryRngCore,
{
prng: R,
reseed_rng: Rsdr,
threshold: i64,
bytes_until_reseed: i64,
}

impl<R, Rsdr> ReseedingRng<R, Rsdr>
where
R: BlockRngCore + SeedableRng,
R: RngCore + SeedableRng,
Rsdr: TryRngCore,
{
/// Create a new `ReseedingRng` from an existing PRNG, combined with a RNG
Expand All @@ -83,89 +87,7 @@ where
/// `threshold` sets the number of generated bytes after which to reseed the
/// PRNG. Set it to zero to never reseed based on the number of generated
/// values.
pub fn new(threshold: u64, reseeder: Rsdr) -> Result<Self, Rsdr::Error> {
Ok(ReseedingRng(BlockRng::new(ReseedingCore::new(
threshold, reseeder,
)?)))
}

/// Immediately reseed the generator
///
/// This discards any remaining random data in the cache.
pub fn reseed(&mut self) -> Result<(), Rsdr::Error> {
self.0.reset();
self.0.core.reseed()
}
}

// TODO: this should be implemented for any type where the inner type
// implements RngCore, but we can't specify that because ReseedingCore is private
impl<R, Rsdr> RngCore for ReseedingRng<R, Rsdr>
where
R: BlockRngCore<Item = u32> + SeedableRng,
Rsdr: TryRngCore,
{
#[inline(always)]
fn next_u32(&mut self) -> u32 {
self.0.next_u32()
}

#[inline(always)]
fn next_u64(&mut self) -> u64 {
self.0.next_u64()
}

fn fill_bytes(&mut self, dest: &mut [u8]) {
self.0.fill_bytes(dest)
}
}

impl<R, Rsdr> CryptoRng for ReseedingRng<R, Rsdr>
where
R: BlockRngCore<Item = u32> + SeedableRng + CryptoBlockRng,
Rsdr: TryCryptoRng,
{
}

#[derive(Debug)]
struct ReseedingCore<R, Rsdr> {
inner: R,
reseeder: Rsdr,
threshold: i64,
bytes_until_reseed: i64,
}

impl<R, Rsdr> BlockRngCore for ReseedingCore<R, Rsdr>
where
R: BlockRngCore + SeedableRng,
Rsdr: TryRngCore,
{
type Item = <R as BlockRngCore>::Item;
type Results = <R as BlockRngCore>::Results;

fn generate(&mut self, results: &mut Self::Results) {
if self.bytes_until_reseed <= 0 {
// We get better performance by not calling only `reseed` here
// and continuing with the rest of the function, but by directly
// returning from a non-inlined function.
return self.reseed_and_generate(results);
}
let num_bytes = size_of_val(results.as_ref());
self.bytes_until_reseed -= num_bytes as i64;
self.inner.generate(results);
}
}

impl<R, Rsdr> ReseedingCore<R, Rsdr>
where
R: BlockRngCore + SeedableRng,
Rsdr: TryRngCore,
{
/// Create a new `ReseedingCore`.
///
/// `threshold` is the maximum number of bytes produced by
/// [`BlockRngCore::generate`] before attempting reseeding.
fn new(threshold: u64, mut reseeder: Rsdr) -> Result<Self, Rsdr::Error> {
pub fn new(threshold: u64, mut reseed_rng: Rsdr) -> Result<Self, Rsdr::Error> {
// Because generating more values than `i64::MAX` takes centuries on
// current hardware, we just clamp to that value.
// Also we set a threshold of 0, which indicates no limit, to that
Expand All @@ -178,43 +100,66 @@ where
i64::MAX
};

let inner = R::try_from_rng(&mut reseeder)?;

Ok(ReseedingCore {
inner,
reseeder,
R::try_from_rng(&mut reseed_rng).map(|prng| Self {
prng,
reseed_rng,
threshold,
bytes_until_reseed: threshold,
})
}

/// Reseed the internal PRNG.
fn reseed(&mut self) -> Result<(), Rsdr::Error> {
R::try_from_rng(&mut self.reseeder).map(|result| {
/// Immediately reseed the generator.
///
/// This discards any remaining random data in the cache.
pub fn reseed(&mut self) -> Result<(), Rsdr::Error> {
self.prng = R::try_from_rng(&mut self.reseed_rng)?;
Ok(())
}

fn reseed_check(&mut self, bytes: usize) {
if self.bytes_until_reseed <= 0 {
let res = self.reseed();
if let Err(e) = res {
warn!("Reseeding RNG failed: {e}");
}
self.bytes_until_reseed = self.threshold;
self.inner = result
})
}
self.bytes_until_reseed -= bytes as i64;
}
}

#[inline(never)]
fn reseed_and_generate(&mut self, results: &mut <Self as BlockRngCore>::Results) {
trace!("Reseeding RNG (periodic reseed)");
impl<R, Rsdr> RngCore for ReseedingRng<R, Rsdr>
where
R: RngCore + SeedableRng,
Rsdr: TryRngCore,
{
#[inline(always)]
fn next_u32(&mut self) -> u32 {
self.reseed_check(size_of::<u32>());
self.prng.next_u32()
}

let num_bytes = size_of_val(results.as_ref());
#[inline(always)]
fn next_u64(&mut self) -> u64 {
self.reseed_check(size_of::<u64>());
self.prng.next_u64()
}

if let Err(e) = self.reseed() {
warn!("Reseeding RNG failed: {}", e);
let _ = e;
#[inline]
fn fill_bytes(&mut self, dst: &mut [u8]) {
// Do not perform `reseed_check` for an empty `dst` to allow the compiler
// to infer that we always generate PRNG data after reseeding.
if dst.is_empty() {
return;
}

self.bytes_until_reseed = self.threshold - num_bytes as i64;
self.inner.generate(results);
self.reseed_check(size_of_val(dst));
self.prng.fill_bytes(dst)
}
}

impl<R, Rsdr> CryptoBlockRng for ReseedingCore<R, Rsdr>
impl<R, Rsdr> CryptoRng for ReseedingRng<R, Rsdr>
where
R: BlockRngCore<Item = u32> + SeedableRng + CryptoBlockRng,
R: CryptoRng + SeedableRng,
Rsdr: TryCryptoRng,
{
}
Expand Down
5 changes: 2 additions & 3 deletions src/rngs/xoshiro128plusplus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@ impl SeedableRng for Xoshiro128PlusPlus {
/// mapped to a different seed.
#[inline]
fn from_seed(seed: [u8; 16]) -> Xoshiro128PlusPlus {
let mut state = [0; 4];
le::read_u32_into(&seed, &mut state);
let state: [u32; 4] = le::read_words(&seed);
// Check for zero on aligned integers for better code generation.
// Furtermore, seed_from_u64(0) will expand to a constant when optimized.
if state.iter().all(|&x| x == 0) {
Expand Down Expand Up @@ -93,7 +92,7 @@ impl RngCore for Xoshiro128PlusPlus {

#[inline]
fn fill_bytes(&mut self, dst: &mut [u8]) {
le::fill_bytes_via_next(self, dst)
le::fill_bytes_via_next_word(dst, || self.next_u32())
}
}

Expand Down
Loading
Loading