From e9f4e841c292f7e81d38710de31dc7973b3d3037 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Wed, 19 Nov 2025 19:27:39 +0300 Subject: [PATCH 01/18] Migrate to new `rand_core` utility functions --- Cargo.toml | 17 ++-- rand_pcg/src/pcg128.rs | 16 ++-- rand_pcg/src/pcg128cm.rs | 9 +- rand_pcg/src/pcg64.rs | 8 +- src/lib.rs | 2 +- src/rngs/reseeding.rs | 167 +++++++++++---------------------- src/rngs/xoshiro128plusplus.rs | 5 +- src/rngs/xoshiro256plusplus.rs | 5 +- 8 files changed, 85 insertions(+), 144 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 13aea84ed7..f5522b5211 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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. @@ -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. @@ -67,7 +67,7 @@ log = ["dep:log"] [workspace] members = [ - "rand_chacha", + # "rand_chacha", "rand_pcg", ] exclude = ["benches", "distr_test"] @@ -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] @@ -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" } diff --git a/rand_pcg/src/pcg128.rs b/rand_pcg/src/pcg128.rs index f7dbada8cb..65767fd1de 100644 --- a/rand_pcg/src/pcg128.rs +++ b/rand_pcg/src/pcg128.rs @@ -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) @@ -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()) } } @@ -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) } } @@ -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()) } } diff --git a/rand_pcg/src/pcg128cm.rs b/rand_pcg/src/pcg128cm.rs index 681ca639b4..be8c7e700a 100644 --- a/rand_pcg/src/pcg128cm.rs +++ b/rand_pcg/src/pcg128cm.rs @@ -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) @@ -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()) } } diff --git a/rand_pcg/src/pcg64.rs b/rand_pcg/src/pcg64.rs index 967299fdcb..571d2ef573 100644 --- a/rand_pcg/src/pcg64.rs +++ b/rand_pcg/src/pcg64.rs @@ -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) } } @@ -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()) } } diff --git a/src/lib.rs b/src/lib.rs index 3ae859eed4..28278173b5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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()) } } diff --git a/src/rngs/reseeding.rs b/src/rngs/reseeding.rs index 4a9f107edb..390da39abc 100644 --- a/src/rngs/reseeding.rs +++ b/src/rngs/reseeding.rs @@ -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 @@ -67,14 +65,20 @@ use rand_core::{CryptoRng, RngCore, SeedableRng, TryCryptoRng, TryRngCore}; /// [`ReseedingRng::new`]: ReseedingRng::new /// [`reseed()`]: ReseedingRng::reseed #[derive(Debug)] -pub struct ReseedingRng(BlockRng>) +pub struct ReseedingRng where - R: BlockRngCore + SeedableRng, - Rsdr: TryRngCore; + R: RngCore + SeedableRng, + Rsdr: TryRngCore, +{ + prng: R, + reseed_rng: Rsdr, + threshold: i64, + bytes_until_reseed: i64, +} impl ReseedingRng where - R: BlockRngCore + SeedableRng, + R: RngCore + SeedableRng, Rsdr: TryRngCore, { /// Create a new `ReseedingRng` from an existing PRNG, combined with a RNG @@ -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 { - 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 RngCore for ReseedingRng -where - R: BlockRngCore + 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 CryptoRng for ReseedingRng -where - R: BlockRngCore + SeedableRng + CryptoBlockRng, - Rsdr: TryCryptoRng, -{ -} - -#[derive(Debug)] -struct ReseedingCore { - inner: R, - reseeder: Rsdr, - threshold: i64, - bytes_until_reseed: i64, -} - -impl BlockRngCore for ReseedingCore -where - R: BlockRngCore + SeedableRng, - Rsdr: TryRngCore, -{ - type Item = ::Item; - type Results = ::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 ReseedingCore -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 { + pub fn new(threshold: u64, mut reseed_rng: Rsdr) -> Result { // 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 @@ -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 ::Results) { - trace!("Reseeding RNG (periodic reseed)"); +impl RngCore for ReseedingRng +where + R: RngCore + SeedableRng, + Rsdr: TryRngCore, +{ + #[inline(always)] + fn next_u32(&mut self) -> u32 { + self.reseed_check(size_of::()); + 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::()); + 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 CryptoBlockRng for ReseedingCore +impl CryptoRng for ReseedingRng where - R: BlockRngCore + SeedableRng + CryptoBlockRng, + R: CryptoRng + SeedableRng, Rsdr: TryCryptoRng, { } diff --git a/src/rngs/xoshiro128plusplus.rs b/src/rngs/xoshiro128plusplus.rs index 5d6b9318c5..58f15885db 100644 --- a/src/rngs/xoshiro128plusplus.rs +++ b/src/rngs/xoshiro128plusplus.rs @@ -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) { @@ -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()) } } diff --git a/src/rngs/xoshiro256plusplus.rs b/src/rngs/xoshiro256plusplus.rs index 4ef433abe1..af0b9cb4e6 100644 --- a/src/rngs/xoshiro256plusplus.rs +++ b/src/rngs/xoshiro256plusplus.rs @@ -31,8 +31,7 @@ impl SeedableRng for Xoshiro256PlusPlus { /// mapped to a different seed. #[inline] fn from_seed(seed: [u8; 32]) -> Xoshiro256PlusPlus { - let mut state = [0; 4]; - le::read_u64_into(&seed, &mut state); + let state: [u64; 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) { @@ -95,7 +94,7 @@ impl RngCore for Xoshiro256PlusPlus { #[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_u64()) } } From f5cce31f1b0c2bda47e66e19a48c5157f071921a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Wed, 19 Nov 2025 20:25:38 +0300 Subject: [PATCH 02/18] port rand_chacha --- Cargo.toml | 2 +- rand_chacha/src/chacha.rs | 80 ++++++++++++++++++++++----------------- 2 files changed, 46 insertions(+), 36 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f5522b5211..8d2edd312d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -67,7 +67,7 @@ log = ["dep:log"] [workspace] members = [ - # "rand_chacha", + "rand_chacha", "rand_pcg", ] exclude = ["benches", "distr_test"] diff --git a/rand_chacha/src/chacha.rs b/rand_chacha/src/chacha.rs index f42232f757..889ab5fa04 100644 --- a/rand_chacha/src/chacha.rs +++ b/rand_chacha/src/chacha.rs @@ -10,8 +10,7 @@ use crate::guts::ChaCha; use core::fmt; -use rand_core::block::{BlockRng, BlockRngCore, CryptoBlockRng}; -use rand_core::{CryptoRng, RngCore, SeedableRng}; +use rand_core::{CryptoRng, RngCore, SeedableRng, le}; #[cfg(feature = "serde")] use serde::{Deserialize, Deserializer, Serialize, Serializer}; @@ -82,28 +81,19 @@ macro_rules! chacha_impl { } } - impl BlockRngCore for $ChaChaXCore { - type Item = u32; - type Results = Array64; - - #[inline] - fn generate(&mut self, r: &mut Self::Results) { - self.state.refill4($rounds, &mut r.0); - } - } - - impl SeedableRng for $ChaChaXCore { - type Seed = [u8; 32]; - + impl $ChaChaXCore { #[inline] - fn from_seed(seed: Self::Seed) -> Self { + fn from_seed(seed: [u8; 32]) -> Self { $ChaChaXCore { state: ChaCha::new(&seed, &[0u8; 8]), } } - } - impl CryptoBlockRng for $ChaChaXCore {} + #[inline] + fn next_block(&mut self, r: &mut [u32; 64]) { + self.state.refill4($rounds, r); + } + } /// A cryptographically secure random number generator that uses the ChaCha algorithm. /// @@ -145,7 +135,24 @@ macro_rules! chacha_impl { /// http://www.ecrypt.eu.org/stream/) #[derive(Clone, Debug)] pub struct $ChaChaXRng { - rng: BlockRng<$ChaChaXCore>, + core: $ChaChaXCore, + buffer: [u32; 64], + } + + impl $ChaChaXRng { + fn buffer_index(&self) -> u32 { + self.buffer[0] + } + + fn generate_and_set(&mut self, index: usize) { + assert!(index < self.buffer.len()); + self.buffer[0] = if index != 0 { + self.core.next_block(&mut self.buffer); + index as u32 + } else { + self.buffer.len() as u32 + } + } } impl SeedableRng for $ChaChaXRng { @@ -153,9 +160,9 @@ macro_rules! chacha_impl { #[inline] fn from_seed(seed: Self::Seed) -> Self { - let core = $ChaChaXCore::from_seed(seed); Self { - rng: BlockRng::new(core), + core: $ChaChaXCore::from_seed(seed), + buffer: le::new_buffer(), } } } @@ -163,17 +170,20 @@ macro_rules! chacha_impl { impl RngCore for $ChaChaXRng { #[inline] fn next_u32(&mut self) -> u32 { - self.rng.next_u32() + let Self { core, buffer } = self; + le::next_word_via_gen_block(buffer, |block| core.next_block(block)) } #[inline] fn next_u64(&mut self) -> u64 { - self.rng.next_u64() + let Self { core, buffer } = self; + le::next_u64_via_gen_block(buffer, |block| core.next_block(block)) } #[inline] - fn fill_bytes(&mut self, bytes: &mut [u8]) { - self.rng.fill_bytes(bytes) + fn fill_bytes(&mut self, dst: &mut [u8]) { + let Self { core, buffer } = self; + le::fill_bytes_via_gen_block(dst, buffer, |block| core.next_block(block)); } } @@ -190,11 +200,11 @@ macro_rules! chacha_impl { #[inline] pub fn get_word_pos(&self) -> u128 { let buf_start_block = { - let buf_end_block = self.rng.core.state.get_block_pos(); + let buf_end_block = self.core.state.get_block_pos(); u64::wrapping_sub(buf_end_block, BUF_BLOCKS.into()) }; let (buf_offset_blocks, block_offset_words) = { - let buf_offset_words = self.rng.index() as u64; + let buf_offset_words = self.buffer_index() as u64; let blocks_part = buf_offset_words / u64::from(BLOCK_WORDS); let words_part = buf_offset_words % u64::from(BLOCK_WORDS); (blocks_part, words_part) @@ -212,9 +222,8 @@ macro_rules! chacha_impl { #[inline] pub fn set_word_pos(&mut self, word_offset: u128) { let block = (word_offset / u128::from(BLOCK_WORDS)) as u64; - self.rng.core.state.set_block_pos(block); - self.rng - .generate_and_set((word_offset % u128::from(BLOCK_WORDS)) as usize); + self.core.state.set_block_pos(block); + self.generate_and_set((word_offset % u128::from(BLOCK_WORDS)) as usize); } /// Set the stream number. @@ -230,8 +239,8 @@ macro_rules! chacha_impl { /// indirectly via `set_word_pos`), but this is not directly supported. #[inline] pub fn set_stream(&mut self, stream: u64) { - self.rng.core.state.set_nonce(stream); - if self.rng.index() != 64 { + self.core.state.set_nonce(stream); + if self.buffer_index() != 64 { let wp = self.get_word_pos(); self.set_word_pos(wp); } @@ -240,13 +249,13 @@ macro_rules! chacha_impl { /// Get the stream number. #[inline] pub fn get_stream(&self) -> u64 { - self.rng.core.state.get_nonce() + self.core.state.get_nonce() } /// Get the seed. #[inline] pub fn get_seed(&self) -> [u8; 32] { - self.rng.core.state.get_seed() + self.core.state.get_seed() } } @@ -255,7 +264,8 @@ macro_rules! chacha_impl { impl From<$ChaChaXCore> for $ChaChaXRng { fn from(core: $ChaChaXCore) -> Self { $ChaChaXRng { - rng: BlockRng::new(core), + core, + buffer: le::new_buffer(), } } } From 90b9fee9a1b1bb0e7760f1a63ae1bb29200f82b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Wed, 19 Nov 2025 20:29:24 +0300 Subject: [PATCH 03/18] patch rand_core for benches --- benches/Cargo.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/benches/Cargo.toml b/benches/Cargo.toml index 699186b26f..54d2b6b989 100644 --- a/benches/Cargo.toml +++ b/benches/Cargo.toml @@ -56,3 +56,6 @@ harness = false [[bench]] name = "weighted" harness = false + +[patch.crates-io] +rand_core = { git = "https://github.com/rust-random/rand_core", branch = "block_buffer" } From 0baca571e904dbabc4ad7f1db5fa43bc9a6716e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Wed, 19 Nov 2025 20:29:31 +0300 Subject: [PATCH 04/18] fix some warnings --- rand_chacha/src/chacha.rs | 46 --------------------------------------- src/distr/uniform_int.rs | 2 +- 2 files changed, 1 insertion(+), 47 deletions(-) diff --git a/rand_chacha/src/chacha.rs b/rand_chacha/src/chacha.rs index 889ab5fa04..b18b6822f3 100644 --- a/rand_chacha/src/chacha.rs +++ b/rand_chacha/src/chacha.rs @@ -20,52 +20,6 @@ const BUF_BLOCKS: u8 = 4; // number of 32-bit words per ChaCha block (fixed by algorithm definition) const BLOCK_WORDS: u8 = 16; -#[repr(transparent)] -pub struct Array64([T; 64]); -impl Default for Array64 -where - T: Default, -{ - #[rustfmt::skip] - fn default() -> Self { - Self([ - T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), - T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), - T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), - T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), - T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), - T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), - T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), - T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), T::default(), - ]) - } -} -impl AsRef<[T]> for Array64 { - fn as_ref(&self) -> &[T] { - &self.0 - } -} -impl AsMut<[T]> for Array64 { - fn as_mut(&mut self) -> &mut [T] { - &mut self.0 - } -} -impl Clone for Array64 -where - T: Copy + Default, -{ - fn clone(&self) -> Self { - let mut new = Self::default(); - new.0.copy_from_slice(&self.0); - new - } -} -impl fmt::Debug for Array64 { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Array64 {{}}") - } -} - macro_rules! chacha_impl { ($ChaChaXCore:ident, $ChaChaXRng:ident, $rounds:expr, $doc:expr, $abst:ident,) => { #[doc=$doc] diff --git a/src/distr/uniform_int.rs b/src/distr/uniform_int.rs index e4f4cfa3f8..53f3a6eb6f 100644 --- a/src/distr/uniform_int.rs +++ b/src/distr/uniform_int.rs @@ -882,7 +882,7 @@ mod tests { use serde_json; let serialized_on_32bit = r#"{"low":10,"range":91,"thresh":74}"#; let deserialized: UniformUsize = - serde_json::from_str(&serialized_on_32bit).expect("deserialization"); + serde_json::from_str(serialized_on_32bit).expect("deserialization"); assert_eq!( deserialized, UniformUsize::new_inclusive(10, 100).expect("creation") From 9ba4cadcfe65ef6d1510216e0cbab58578e52083 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Wed, 19 Nov 2025 20:36:19 +0300 Subject: [PATCH 05/18] use rand_chacha instead of chacha20 --- Cargo.toml | 7 ++++--- src/rngs/reseeding.rs | 8 ++++---- src/rngs/std.rs | 3 --- src/rngs/thread.rs | 6 +++--- 4 files changed, 11 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8d2edd312d..73e54ad84c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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. @@ -77,6 +77,7 @@ 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 = { path = "rand_chacha", optional = true, package = "rand_chacha" } getrandom = { version = "0.3.0", optional = true } [dev-dependencies] diff --git a/src/rngs/reseeding.rs b/src/rngs/reseeding.rs index 390da39abc..2c1f64da31 100644 --- a/src/rngs/reseeding.rs +++ b/src/rngs/reseeding.rs @@ -50,13 +50,13 @@ use rand_core::{CryptoRng, RngCore, SeedableRng, TryCryptoRng, TryRngCore}; /// # Example /// /// ``` -/// use chacha20::ChaCha20Core; // Internal part of ChaChaRng that +/// use chacha20::ChaCha20Rng; // Internal part of ChaChaRng that /// // implements BlockRngCore /// use rand::prelude::*; /// use rand::rngs::OsRng; /// use rand::rngs::ReseedingRng; /// -/// let mut reseeding_rng = ReseedingRng::::new(0, OsRng).unwrap(); +/// let mut reseeding_rng = ReseedingRng::::new(0, OsRng).unwrap(); /// /// println!("{}", reseeding_rng.random::()); /// ``` @@ -168,7 +168,7 @@ where #[cfg(test)] mod test { use crate::Rng; - use crate::rngs::std::Core; + use crate::rngs::std::StdRng; use crate::test::const_rng; use super::ReseedingRng; @@ -177,7 +177,7 @@ mod test { fn test_reseeding() { let zero = const_rng(0); let thresh = 1; // reseed every time the buffer is exhausted - let mut reseeding = ReseedingRng::::new(thresh, zero).unwrap(); + let mut reseeding = ReseedingRng::::new(thresh, zero).unwrap(); // RNG buffer size is [u32; 64] // Debug is only implemented up to length 32 so use two arrays diff --git a/src/rngs/std.rs b/src/rngs/std.rs index 3fc3e491e1..b62b62c0c8 100644 --- a/src/rngs/std.rs +++ b/src/rngs/std.rs @@ -10,9 +10,6 @@ use rand_core::{CryptoRng, RngCore, SeedableRng}; -#[cfg(any(test, feature = "os_rng"))] -pub(crate) use chacha20::ChaCha12Core as Core; - use chacha20::ChaCha12Rng as Rng; /// A strong, fast (amortized), non-portable RNG diff --git a/src/rngs/thread.rs b/src/rngs/thread.rs index 5acb90df0d..55914eca47 100644 --- a/src/rngs/thread.rs +++ b/src/rngs/thread.rs @@ -13,7 +13,7 @@ use std::fmt; use std::rc::Rc; use std::thread_local; -use super::{OsError, OsRng, ReseedingRng, std::Core}; +use super::{OsError, OsRng, ReseedingRng, std::StdRng}; use rand_core::{CryptoRng, RngCore}; // Rationale for using `UnsafeCell` in `ThreadRng`: @@ -90,7 +90,7 @@ const THREAD_RNG_RESEED_THRESHOLD: u64 = 1024 * 64; #[derive(Clone)] pub struct ThreadRng { // Rc is explicitly !Send and !Sync - rng: Rc>>, + rng: Rc>>, } impl ThreadRng { @@ -115,7 +115,7 @@ impl fmt::Debug for ThreadRng { thread_local!( // We require Rc<..> to avoid premature freeing when ThreadRng is used // within thread-local destructors. See #968. - static THREAD_RNG_KEY: Rc>> = { + static THREAD_RNG_KEY: Rc>> = { let rng = ReseedingRng::new(THREAD_RNG_RESEED_THRESHOLD, OsRng).unwrap_or_else(|err| panic!("could not initialize ThreadRng: {}", err)); From 569262ae22e09c474586fb22dc0b57f48efede66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Wed, 19 Nov 2025 20:39:42 +0300 Subject: [PATCH 06/18] fix benches --- benches/Cargo.toml | 2 +- benches/benches/generators.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/benches/Cargo.toml b/benches/Cargo.toml index 54d2b6b989..724207708a 100644 --- a/benches/Cargo.toml +++ b/benches/Cargo.toml @@ -11,7 +11,7 @@ simd_support = ["rand/simd_support"] [dependencies] [dev-dependencies] -rand = { path = "..", features = ["small_rng", "nightly"] } +rand = { path = "..", features = ["small_rng", "nightly", "thread_rng"] } rand_pcg = { path = "../rand_pcg" } rand_chacha = { path = "../rand_chacha" } criterion = "0.5" diff --git a/benches/benches/generators.rs b/benches/benches/generators.rs index 1d22b1aa6c..1f1d427358 100644 --- a/benches/benches/generators.rs +++ b/benches/benches/generators.rs @@ -13,7 +13,7 @@ use rand::prelude::*; use rand::rngs::OsRng; use rand::rngs::ReseedingRng; use rand_chacha::rand_core::UnwrapErr; -use rand_chacha::{ChaCha8Rng, ChaCha12Rng, ChaCha20Core, ChaCha20Rng}; +use rand_chacha::{ChaCha8Rng, ChaCha12Rng, ChaCha20Rng}; use rand_pcg::{Pcg32, Pcg64, Pcg64Dxsm, Pcg64Mcg}; criterion_group!( @@ -198,7 +198,7 @@ pub fn reseeding_bytes(c: &mut Criterion) { fn bench(g: &mut BenchmarkGroup, thresh: u64) { let name = format!("chacha20_{thresh}k"); g.bench_function(name.as_str(), |b| { - let mut rng = ReseedingRng::::new(thresh * 1024, OsRng).unwrap(); + let mut rng = ReseedingRng::::new(thresh * 1024, OsRng).unwrap(); let mut buf = [0u8; 1024 * 1024]; b.iter(|| { rng.fill_bytes(&mut buf); From 3f4c4c869ceffaa5d247ca1d543c8d93bd312449 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Wed, 19 Nov 2025 20:50:32 +0300 Subject: [PATCH 07/18] fix warning --- src/rngs/reseeding.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rngs/reseeding.rs b/src/rngs/reseeding.rs index 2c1f64da31..82c90eaea1 100644 --- a/src/rngs/reseeding.rs +++ b/src/rngs/reseeding.rs @@ -119,8 +119,8 @@ where 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}"); + if let Err(_e) = res { + warn!("Reseeding RNG failed: {_e}"); } self.bytes_until_reseed = self.threshold; } From 47676619b9f5e8d2e423ef78b331e1527f3da7a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Wed, 19 Nov 2025 20:56:00 +0300 Subject: [PATCH 08/18] fix doc --- src/rngs/reseeding.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/rngs/reseeding.rs b/src/rngs/reseeding.rs index 82c90eaea1..d0a237cb86 100644 --- a/src/rngs/reseeding.rs +++ b/src/rngs/reseeding.rs @@ -13,7 +13,7 @@ use core::mem::size_of_val; use rand_core::{CryptoRng, RngCore, SeedableRng, TryCryptoRng, TryRngCore}; -/// A wrapper around any PRNG that implements [`BlockRngCore`], that adds the +/// A wrapper around any PRNG that implements [`RngCore`], that adds the /// ability to reseed it. /// /// `ReseedingRng` reseeds the underlying PRNG in the following cases: @@ -61,7 +61,6 @@ use rand_core::{CryptoRng, RngCore, SeedableRng, TryCryptoRng, TryRngCore}; /// println!("{}", reseeding_rng.random::()); /// ``` /// -/// [`BlockRngCore`]: rand_core::block::BlockRngCore /// [`ReseedingRng::new`]: ReseedingRng::new /// [`reseed()`]: ReseedingRng::reseed #[derive(Debug)] From 60baaba3484878257e9dec918992cadbc13ef6f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Wed, 19 Nov 2025 21:07:23 +0300 Subject: [PATCH 09/18] uncomment default features --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 73e54ad84c..28e83158a1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,7 @@ 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"] From 2154e9fd74baf0de485f07342e40f57c8a4d586e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Wed, 19 Nov 2025 21:10:19 +0300 Subject: [PATCH 10/18] fix rand_chacha docs --- rand_chacha/src/chacha.rs | 2 +- rand_chacha/src/lib.rs | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/rand_chacha/src/chacha.rs b/rand_chacha/src/chacha.rs index b18b6822f3..4a2b04aa44 100644 --- a/rand_chacha/src/chacha.rs +++ b/rand_chacha/src/chacha.rs @@ -80,7 +80,7 @@ macro_rules! chacha_impl { /// ``` /// /// This implementation uses an output buffer of sixteen `u32` words, and uses - /// [`BlockRng`] to implement the [`RngCore`] methods. + /// them to implement the [`RngCore`] methods. /// /// [^1]: D. J. Bernstein, [*ChaCha, a variant of Salsa20*]( /// https://cr.yp.to/chacha.html) diff --git a/rand_chacha/src/lib.rs b/rand_chacha/src/lib.rs index 112ffe6f40..ce1ad76e7e 100644 --- a/rand_chacha/src/lib.rs +++ b/rand_chacha/src/lib.rs @@ -13,9 +13,8 @@ //! //! ## Generators //! -//! This crate provides 8-, 12- and 20-round variants of generators via a "core" -//! implementation (of [`BlockRngCore`]), each with an associated "RNG" type -//! (implementing [`RngCore`]). +//! This crate provides 8-, 12- and 20-round variants of generators +//! implementing [`RngCore`]. //! //! These generators are all deterministic and portable (see [Reproducibility] //! in the book), with testing against reference vectors. @@ -73,7 +72,6 @@ //! [Seeding RNGs]: https://rust-random.github.io/book/guide-seeding.html //! [Security]: https://rust-random.github.io/book/guide-rngs.html#security //! [Random Values]: https://rust-random.github.io/book/guide-values.html -//! [`BlockRngCore`]: rand_core::block::BlockRngCore //! [`RngCore`]: rand_core::RngCore //! [`SeedableRng`]: rand_core::SeedableRng //! [`OsRng`]: https://docs.rs/rand/latest/rand/rngs/struct.OsRng.html From 911038772dc5108de849067005aa10e07ae80e07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Wed, 19 Nov 2025 21:23:19 +0300 Subject: [PATCH 11/18] add inline attributes --- src/rngs/reseeding.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/rngs/reseeding.rs b/src/rngs/reseeding.rs index d0a237cb86..5e71ca6675 100644 --- a/src/rngs/reseeding.rs +++ b/src/rngs/reseeding.rs @@ -86,6 +86,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. + #[inline] pub fn new(threshold: u64, mut reseed_rng: Rsdr) -> Result { // Because generating more values than `i64::MAX` takes centuries on // current hardware, we just clamp to that value. @@ -110,11 +111,13 @@ where /// Immediately reseed the generator. /// /// This discards any remaining random data in the cache. + #[inline] pub fn reseed(&mut self) -> Result<(), Rsdr::Error> { self.prng = R::try_from_rng(&mut self.reseed_rng)?; Ok(()) } + #[inline] fn reseed_check(&mut self, bytes: usize) { if self.bytes_until_reseed <= 0 { let res = self.reseed(); @@ -144,7 +147,7 @@ where self.prng.next_u64() } - #[inline] + #[inline(always)] 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. From e338cd2661f6f1b6daabca2980d9c2c1e083806a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Fri, 21 Nov 2025 16:00:48 +0300 Subject: [PATCH 12/18] remove `criterion-cycles-per-byte` dependency --- benches/Cargo.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/benches/Cargo.toml b/benches/Cargo.toml index 724207708a..bde03de878 100644 --- a/benches/Cargo.toml +++ b/benches/Cargo.toml @@ -11,11 +11,10 @@ simd_support = ["rand/simd_support"] [dependencies] [dev-dependencies] -rand = { path = "..", features = ["small_rng", "nightly", "thread_rng"] } +rand = { path = "..", features = ["small_rng", "nightly"] } rand_pcg = { path = "../rand_pcg" } rand_chacha = { path = "../rand_chacha" } criterion = "0.5" -criterion-cycles-per-byte = "0.6" [[bench]] name = "array" From de706e4131d2e00d5413c73b4f960fb9f55dc8a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Mon, 8 Dec 2025 15:06:35 +0300 Subject: [PATCH 13/18] Update to `BlockBuffer` --- rand_chacha/src/chacha.rs | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/rand_chacha/src/chacha.rs b/rand_chacha/src/chacha.rs index 4a2b04aa44..39ea0a733c 100644 --- a/rand_chacha/src/chacha.rs +++ b/rand_chacha/src/chacha.rs @@ -10,7 +10,7 @@ use crate::guts::ChaCha; use core::fmt; -use rand_core::{CryptoRng, RngCore, SeedableRng, le}; +use rand_core::{CryptoRng, RngCore, SeedableRng, le::BlockBuffer}; #[cfg(feature = "serde")] use serde::{Deserialize, Deserializer, Serialize, Serializer}; @@ -90,21 +90,20 @@ macro_rules! chacha_impl { #[derive(Clone, Debug)] pub struct $ChaChaXRng { core: $ChaChaXCore, - buffer: [u32; 64], + buffer: BlockBuffer, } impl $ChaChaXRng { fn buffer_index(&self) -> u32 { - self.buffer[0] + self.buffer.index() } fn generate_and_set(&mut self, index: usize) { - assert!(index < self.buffer.len()); - self.buffer[0] = if index != 0 { - self.core.next_block(&mut self.buffer); - index as u32 + if index == 0 { + self.buffer.reset(); } else { - self.buffer.len() as u32 + self.buffer + .generate_and_set(index, |block| self.core.next_block(block)); } } } @@ -116,7 +115,7 @@ macro_rules! chacha_impl { fn from_seed(seed: Self::Seed) -> Self { Self { core: $ChaChaXCore::from_seed(seed), - buffer: le::new_buffer(), + buffer: BlockBuffer::default(), } } } @@ -124,20 +123,18 @@ macro_rules! chacha_impl { impl RngCore for $ChaChaXRng { #[inline] fn next_u32(&mut self) -> u32 { - let Self { core, buffer } = self; - le::next_word_via_gen_block(buffer, |block| core.next_block(block)) + self.buffer.next_word(|block| self.core.next_block(block)) } #[inline] fn next_u64(&mut self) -> u64 { - let Self { core, buffer } = self; - le::next_u64_via_gen_block(buffer, |block| core.next_block(block)) + self.buffer.next_u64(|block| self.core.next_block(block)) } #[inline] fn fill_bytes(&mut self, dst: &mut [u8]) { - let Self { core, buffer } = self; - le::fill_bytes_via_gen_block(dst, buffer, |block| core.next_block(block)); + self.buffer + .fill_bytes(dst, |block| self.core.next_block(block)); } } @@ -219,7 +216,7 @@ macro_rules! chacha_impl { fn from(core: $ChaChaXCore) -> Self { $ChaChaXRng { core, - buffer: le::new_buffer(), + buffer: BlockBuffer::default(), } } } From 0045925f9d47a20bc125d39da6cc853be36f9849 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Mon, 8 Dec 2025 15:46:33 +0300 Subject: [PATCH 14/18] new `ThreadRng` impl --- rand_chacha/src/chacha.rs | 11 ++++-- src/distr/utils.rs | 1 + src/rngs/thread.rs | 71 ++++++++++++++++++++++++++++----------- 3 files changed, 62 insertions(+), 21 deletions(-) diff --git a/rand_chacha/src/chacha.rs b/rand_chacha/src/chacha.rs index 39ea0a733c..edf41beea1 100644 --- a/rand_chacha/src/chacha.rs +++ b/rand_chacha/src/chacha.rs @@ -36,17 +36,24 @@ macro_rules! chacha_impl { } impl $ChaChaXCore { + /// Create new core state from seed. #[inline] - fn from_seed(seed: [u8; 32]) -> Self { + pub fn from_seed(seed: [u8; 32]) -> Self { $ChaChaXCore { state: ChaCha::new(&seed, &[0u8; 8]), } } + /// Generate next block. #[inline] - fn next_block(&mut self, r: &mut [u32; 64]) { + pub fn next_block(&mut self, r: &mut [u32; 64]) { self.state.refill4($rounds, r); } + + /// Get current block position. + pub fn block_pos(&self) -> u64 { + self.state.get_block_pos() + } } /// A cryptographically secure random number generator that uses the ChaCha algorithm. diff --git a/src/distr/utils.rs b/src/distr/utils.rs index 784534f48b..bb9e7006c1 100644 --- a/src/distr/utils.rs +++ b/src/distr/utils.rs @@ -179,6 +179,7 @@ mod simd_wmul { type Output = ($ty, $ty); #[inline(always)] + #[allow(clippy::undocumented_unsafe_blocks)] fn wmul(self, x: $ty) -> Self::Output { let hi = unsafe { $mulhi(self.into(), x.into()) }.into(); let lo = unsafe { $mullo(self.into(), x.into()) }.into(); diff --git a/src/rngs/thread.rs b/src/rngs/thread.rs index 55914eca47..d5dc194d61 100644 --- a/src/rngs/thread.rs +++ b/src/rngs/thread.rs @@ -13,8 +13,9 @@ use std::fmt; use std::rc::Rc; use std::thread_local; -use super::{OsError, OsRng, ReseedingRng, std::StdRng}; -use rand_core::{CryptoRng, RngCore}; +use super::{OsError, OsRng}; +use chacha20::ChaCha12Core; +use rand_core::{CryptoRng, RngCore, TryRngCore, le::BlockBuffer}; // Rationale for using `UnsafeCell` in `ThreadRng`: // @@ -30,10 +31,41 @@ use rand_core::{CryptoRng, RngCore}; // `ThreadRng` internally, which is nonsensical anyway. We should also never run // `ThreadRng` in destructors of its implementation, which is also nonsensical. -// Number of generated bytes after which to reseed `ThreadRng`. +// Number of generated ChaCha blocks (64 bytes) after which to reseed `ThreadRng`. // According to benchmarks, reseeding has a noticeable impact with thresholds -// of 32 kB and less. We choose 64 kB to avoid significant overhead. -const THREAD_RNG_RESEED_THRESHOLD: u64 = 1024 * 64; +// of 32 KiB and less. We choose 64 KiB to avoid significant overhead. +const THREAD_RNG_RESEED_THRESHOLD: u64 = 1024; + +pub struct ThreadRngInner { + core: ChaCha12Core, +} + +impl ThreadRngInner { + fn new() -> Result { + let mut seed = [0u8; 32]; + OsRng.try_fill_bytes(&mut seed)?; + Ok(Self { + core: ChaCha12Core::from_seed(seed), + }) + } + + fn reseed(&mut self) -> Result<(), OsError> { + let mut seed = [0u8; 32]; + OsRng.try_fill_bytes(&mut seed)?; + self.core = ChaCha12Core::from_seed(seed); + Ok(()) + } + + fn next_block(&mut self, block: &mut [u32; 64]) { + if self.core.block_pos() > THREAD_RNG_RESEED_THRESHOLD { + let res = self.reseed(); + if let Err(_e) = res { + warn!("Reseeding RNG failed: {_e}"); + } + } + self.core.next_block(block); + } +} /// A reference to the thread-local generator /// @@ -90,7 +122,7 @@ const THREAD_RNG_RESEED_THRESHOLD: u64 = 1024 * 64; #[derive(Clone)] pub struct ThreadRng { // Rc is explicitly !Send and !Sync - rng: Rc>>, + rng: Rc)>>, } impl ThreadRng { @@ -100,8 +132,9 @@ impl ThreadRng { pub fn reseed(&mut self) -> Result<(), OsError> { // SAFETY: We must make sure to stop using `rng` before anyone else // creates another mutable reference - let rng = unsafe { &mut *self.rng.get() }; - rng.reseed() + let (rng_core, buffer) = unsafe { &mut *self.rng.get() }; + buffer.reset(); + rng_core.reseed() } } @@ -115,11 +148,11 @@ impl fmt::Debug for ThreadRng { thread_local!( // We require Rc<..> to avoid premature freeing when ThreadRng is used // within thread-local destructors. See #968. - static THREAD_RNG_KEY: Rc>> = { - let rng = ReseedingRng::new(THREAD_RNG_RESEED_THRESHOLD, - OsRng).unwrap_or_else(|err| + static THREAD_RNG_KEY: Rc)>> = { + let rng_core = ThreadRngInner::new().unwrap_or_else(|err| panic!("could not initialize ThreadRng: {}", err)); - Rc::new(UnsafeCell::new(rng)) + let buffer = BlockBuffer::default(); + Rc::new(UnsafeCell::new((rng_core, buffer))) } ); @@ -167,24 +200,24 @@ impl RngCore for ThreadRng { fn next_u32(&mut self) -> u32 { // SAFETY: We must make sure to stop using `rng` before anyone else // creates another mutable reference - let rng = unsafe { &mut *self.rng.get() }; - rng.next_u32() + let (rng_core, buffer) = unsafe { &mut *self.rng.get() }; + buffer.next_word(|block| rng_core.next_block(block)) } #[inline(always)] fn next_u64(&mut self) -> u64 { // SAFETY: We must make sure to stop using `rng` before anyone else // creates another mutable reference - let rng = unsafe { &mut *self.rng.get() }; - rng.next_u64() + let (rng_core, buffer) = unsafe { &mut *self.rng.get() }; + buffer.next_u64(|block| rng_core.next_block(block)) } #[inline(always)] - fn fill_bytes(&mut self, dest: &mut [u8]) { + fn fill_bytes(&mut self, dst: &mut [u8]) { // SAFETY: We must make sure to stop using `rng` before anyone else // creates another mutable reference - let rng = unsafe { &mut *self.rng.get() }; - rng.fill_bytes(dest) + let (rng_core, buffer) = unsafe { &mut *self.rng.get() }; + buffer.fill_bytes(dst, |block| rng_core.next_block(block)); } } From 8827d156d0248a3f4ce8714b8185df9ae78ba0c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Mon, 8 Dec 2025 16:05:15 +0300 Subject: [PATCH 15/18] Add `#[inline]` to `ThreadRng` methods --- src/rngs/thread.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/rngs/thread.rs b/src/rngs/thread.rs index d5dc194d61..dfd19782b7 100644 --- a/src/rngs/thread.rs +++ b/src/rngs/thread.rs @@ -41,6 +41,7 @@ pub struct ThreadRngInner { } impl ThreadRngInner { + #[inline] fn new() -> Result { let mut seed = [0u8; 32]; OsRng.try_fill_bytes(&mut seed)?; @@ -49,6 +50,7 @@ impl ThreadRngInner { }) } + #[inline] fn reseed(&mut self) -> Result<(), OsError> { let mut seed = [0u8; 32]; OsRng.try_fill_bytes(&mut seed)?; @@ -56,6 +58,7 @@ impl ThreadRngInner { Ok(()) } + #[inline] fn next_block(&mut self, block: &mut [u32; 64]) { if self.core.block_pos() > THREAD_RNG_RESEED_THRESHOLD { let res = self.reseed(); @@ -129,6 +132,7 @@ impl ThreadRng { /// Immediately reseed the generator /// /// This discards any remaining random data in the cache. + #[inline] pub fn reseed(&mut self) -> Result<(), OsError> { // SAFETY: We must make sure to stop using `rng` before anyone else // creates another mutable reference @@ -184,12 +188,14 @@ thread_local!( /// # Security /// /// Refer to [`ThreadRng#Security`]. +#[inline] pub fn rng() -> ThreadRng { let rng = THREAD_RNG_KEY.with(|t| t.clone()); ThreadRng { rng } } impl Default for ThreadRng { + #[inline] fn default() -> ThreadRng { rng() } From f75c6d76a6cf6c7cfe2507b5e146958feed01abb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Mon, 8 Dec 2025 16:06:42 +0300 Subject: [PATCH 16/18] Add `#[inline]` to `rand_chacha` --- rand_chacha/src/chacha.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/rand_chacha/src/chacha.rs b/rand_chacha/src/chacha.rs index edf41beea1..c692a279e0 100644 --- a/rand_chacha/src/chacha.rs +++ b/rand_chacha/src/chacha.rs @@ -51,6 +51,7 @@ macro_rules! chacha_impl { } /// Get current block position. + #[inline] pub fn block_pos(&self) -> u64 { self.state.get_block_pos() } @@ -101,10 +102,12 @@ macro_rules! chacha_impl { } impl $ChaChaXRng { + #[inline] fn buffer_index(&self) -> u32 { self.buffer.index() } + #[inline] fn generate_and_set(&mut self, index: usize) { if index == 0 { self.buffer.reset(); From a6d513399f82c2fba4805ebc9432f4cc0c45b0d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Mon, 8 Dec 2025 16:32:23 +0300 Subject: [PATCH 17/18] tweak code --- src/rngs/thread.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/rngs/thread.rs b/src/rngs/thread.rs index dfd19782b7..df839f5dcd 100644 --- a/src/rngs/thread.rs +++ b/src/rngs/thread.rs @@ -45,9 +45,8 @@ impl ThreadRngInner { fn new() -> Result { let mut seed = [0u8; 32]; OsRng.try_fill_bytes(&mut seed)?; - Ok(Self { - core: ChaCha12Core::from_seed(seed), - }) + let core = ChaCha12Core::from_seed(seed); + Ok(Self { core }) } #[inline] @@ -125,7 +124,7 @@ impl ThreadRngInner { #[derive(Clone)] pub struct ThreadRng { // Rc is explicitly !Send and !Sync - rng: Rc)>>, + rng: Rc, ThreadRngInner)>>, } impl ThreadRng { @@ -136,9 +135,10 @@ impl ThreadRng { pub fn reseed(&mut self) -> Result<(), OsError> { // SAFETY: We must make sure to stop using `rng` before anyone else // creates another mutable reference - let (rng_core, buffer) = unsafe { &mut *self.rng.get() }; + let (buffer, rng_core) = unsafe { &mut *self.rng.get() }; + rng_core.reseed()?; buffer.reset(); - rng_core.reseed() + Ok(()) } } @@ -152,11 +152,11 @@ impl fmt::Debug for ThreadRng { thread_local!( // We require Rc<..> to avoid premature freeing when ThreadRng is used // within thread-local destructors. See #968. - static THREAD_RNG_KEY: Rc)>> = { + static THREAD_RNG_KEY: Rc, ThreadRngInner)>> = { let rng_core = ThreadRngInner::new().unwrap_or_else(|err| panic!("could not initialize ThreadRng: {}", err)); let buffer = BlockBuffer::default(); - Rc::new(UnsafeCell::new((rng_core, buffer))) + Rc::new(UnsafeCell::new((buffer, rng_core))) } ); @@ -206,7 +206,7 @@ impl RngCore for ThreadRng { fn next_u32(&mut self) -> u32 { // SAFETY: We must make sure to stop using `rng` before anyone else // creates another mutable reference - let (rng_core, buffer) = unsafe { &mut *self.rng.get() }; + let (buffer, rng_core) = unsafe { &mut *self.rng.get() }; buffer.next_word(|block| rng_core.next_block(block)) } @@ -214,7 +214,7 @@ impl RngCore for ThreadRng { fn next_u64(&mut self) -> u64 { // SAFETY: We must make sure to stop using `rng` before anyone else // creates another mutable reference - let (rng_core, buffer) = unsafe { &mut *self.rng.get() }; + let (buffer, rng_core) = unsafe { &mut *self.rng.get() }; buffer.next_u64(|block| rng_core.next_block(block)) } @@ -222,7 +222,7 @@ impl RngCore for ThreadRng { fn fill_bytes(&mut self, dst: &mut [u8]) { // SAFETY: We must make sure to stop using `rng` before anyone else // creates another mutable reference - let (rng_core, buffer) = unsafe { &mut *self.rng.get() }; + let (buffer, rng_core) = unsafe { &mut *self.rng.get() }; buffer.fill_bytes(dst, |block| rng_core.next_block(block)); } } From 363d4880b6ccbef2fcd5a33ce44d4cb39c24b90a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Mon, 8 Dec 2025 16:42:31 +0300 Subject: [PATCH 18/18] remove ThreadRngInner --- src/rngs/thread.rs | 85 +++++++++++++++++++++------------------------- 1 file changed, 38 insertions(+), 47 deletions(-) diff --git a/src/rngs/thread.rs b/src/rngs/thread.rs index df839f5dcd..a2c423b785 100644 --- a/src/rngs/thread.rs +++ b/src/rngs/thread.rs @@ -31,44 +31,12 @@ use rand_core::{CryptoRng, RngCore, TryRngCore, le::BlockBuffer}; // `ThreadRng` internally, which is nonsensical anyway. We should also never run // `ThreadRng` in destructors of its implementation, which is also nonsensical. -// Number of generated ChaCha blocks (64 bytes) after which to reseed `ThreadRng`. -// According to benchmarks, reseeding has a noticeable impact with thresholds -// of 32 KiB and less. We choose 64 KiB to avoid significant overhead. +/// Number of generated ChaCha blocks (64 bytes) after which we reseed `ThreadRng`. +/// +/// According to benchmarks, reseeding has a noticeable impact with thresholds +/// of 32 KiB and less. We choose 64 KiB (64 * 1024) to avoid significant overhead. const THREAD_RNG_RESEED_THRESHOLD: u64 = 1024; -pub struct ThreadRngInner { - core: ChaCha12Core, -} - -impl ThreadRngInner { - #[inline] - fn new() -> Result { - let mut seed = [0u8; 32]; - OsRng.try_fill_bytes(&mut seed)?; - let core = ChaCha12Core::from_seed(seed); - Ok(Self { core }) - } - - #[inline] - fn reseed(&mut self) -> Result<(), OsError> { - let mut seed = [0u8; 32]; - OsRng.try_fill_bytes(&mut seed)?; - self.core = ChaCha12Core::from_seed(seed); - Ok(()) - } - - #[inline] - fn next_block(&mut self, block: &mut [u32; 64]) { - if self.core.block_pos() > THREAD_RNG_RESEED_THRESHOLD { - let res = self.reseed(); - if let Err(_e) = res { - warn!("Reseeding RNG failed: {_e}"); - } - } - self.core.next_block(block); - } -} - /// A reference to the thread-local generator /// /// This type is a reference to a lazily-initialized thread-local generator. @@ -124,7 +92,7 @@ impl ThreadRngInner { #[derive(Clone)] pub struct ThreadRng { // Rc is explicitly !Send and !Sync - rng: Rc, ThreadRngInner)>>, + rng: Rc, ChaCha12Core)>>, } impl ThreadRng { @@ -136,7 +104,7 @@ impl ThreadRng { // SAFETY: We must make sure to stop using `rng` before anyone else // creates another mutable reference let (buffer, rng_core) = unsafe { &mut *self.rng.get() }; - rng_core.reseed()?; + reseed(rng_core)?; buffer.reset(); Ok(()) } @@ -152,9 +120,13 @@ impl fmt::Debug for ThreadRng { thread_local!( // We require Rc<..> to avoid premature freeing when ThreadRng is used // within thread-local destructors. See #968. - static THREAD_RNG_KEY: Rc, ThreadRngInner)>> = { - let rng_core = ThreadRngInner::new().unwrap_or_else(|err| - panic!("could not initialize ThreadRng: {}", err)); + static THREAD_RNG_KEY: Rc, ChaCha12Core)>> = { + let mut seed = [0u8; 32]; + let res = OsRng.try_fill_bytes(&mut seed); + if let Err(err) = res { + panic!("could not initialize ThreadRng: {}", err) + } + let rng_core = ChaCha12Core::from_seed(seed); let buffer = BlockBuffer::default(); Rc::new(UnsafeCell::new((buffer, rng_core))) } @@ -202,33 +174,52 @@ impl Default for ThreadRng { } impl RngCore for ThreadRng { - #[inline(always)] + #[inline] fn next_u32(&mut self) -> u32 { // SAFETY: We must make sure to stop using `rng` before anyone else // creates another mutable reference let (buffer, rng_core) = unsafe { &mut *self.rng.get() }; - buffer.next_word(|block| rng_core.next_block(block)) + buffer.next_word(|block| next_block(rng_core, block)) } - #[inline(always)] + #[inline] fn next_u64(&mut self) -> u64 { // SAFETY: We must make sure to stop using `rng` before anyone else // creates another mutable reference let (buffer, rng_core) = unsafe { &mut *self.rng.get() }; - buffer.next_u64(|block| rng_core.next_block(block)) + buffer.next_u64(|block| next_block(rng_core, block)) } - #[inline(always)] + #[inline] fn fill_bytes(&mut self, dst: &mut [u8]) { // SAFETY: We must make sure to stop using `rng` before anyone else // creates another mutable reference let (buffer, rng_core) = unsafe { &mut *self.rng.get() }; - buffer.fill_bytes(dst, |block| rng_core.next_block(block)); + buffer.fill_bytes(dst, |block| next_block(rng_core, block)); } } impl CryptoRng for ThreadRng {} +#[inline(always)] +fn reseed(rng_core: &mut ChaCha12Core) -> Result<(), OsError> { + let mut seed = [0u8; 32]; + OsRng.try_fill_bytes(&mut seed)?; + *rng_core = ChaCha12Core::from_seed(seed); + Ok(()) +} + +#[inline(always)] +fn next_block(rng_core: &mut ChaCha12Core, block: &mut [u32; 64]) { + if rng_core.block_pos() >= THREAD_RNG_RESEED_THRESHOLD { + let res = reseed(rng_core); + if let Err(_e) = res { + warn!("Reseeding RNG failed: {_e}"); + } + } + rng_core.next_block(block); +} + #[cfg(test)] mod test { #[test]