Skip to content

Commit

Permalink
Replace SeedableRng impl for SmallRng with inherent fns (#1368)
Browse files Browse the repository at this point in the history
* Simplify redundant doc-link

* Remove impl of SeedableRng for SmallRng

* Fix SeedableRng::Seed type for StdRng

* SmallRng: do not expect thread_rng

* Make SmallRng::from_thread_rng infallible

* Fix benchmarks (SmallRng does not have from_entropy)
  • Loading branch information
dhardy committed Jan 5, 2024
1 parent e9a27a8 commit 1db3aa4
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 60 deletions.
6 changes: 3 additions & 3 deletions benches/generators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ gen_bytes!(gen_bytes_chacha12, ChaCha12Rng::from_entropy());
gen_bytes!(gen_bytes_chacha20, ChaCha20Rng::from_entropy());
gen_bytes!(gen_bytes_std, StdRng::from_entropy());
#[cfg(feature = "small_rng")]
gen_bytes!(gen_bytes_small, SmallRng::from_entropy());
gen_bytes!(gen_bytes_small, SmallRng::from_thread_rng());
gen_bytes!(gen_bytes_os, OsRng);

macro_rules! gen_uint {
Expand Down Expand Up @@ -80,7 +80,7 @@ gen_uint!(gen_u32_chacha12, u32, ChaCha12Rng::from_entropy());
gen_uint!(gen_u32_chacha20, u32, ChaCha20Rng::from_entropy());
gen_uint!(gen_u32_std, u32, StdRng::from_entropy());
#[cfg(feature = "small_rng")]
gen_uint!(gen_u32_small, u32, SmallRng::from_entropy());
gen_uint!(gen_u32_small, u32, SmallRng::from_thread_rng());
gen_uint!(gen_u32_os, u32, OsRng);

gen_uint!(gen_u64_step, u64, StepRng::new(0, 1));
Expand All @@ -93,7 +93,7 @@ gen_uint!(gen_u64_chacha12, u64, ChaCha12Rng::from_entropy());
gen_uint!(gen_u64_chacha20, u64, ChaCha20Rng::from_entropy());
gen_uint!(gen_u64_std, u64, StdRng::from_entropy());
#[cfg(feature = "small_rng")]
gen_uint!(gen_u64_small, u64, SmallRng::from_entropy());
gen_uint!(gen_u64_small, u64, SmallRng::from_thread_rng());
gen_uint!(gen_u64_os, u64, OsRng);

macro_rules! init_gen {
Expand Down
4 changes: 2 additions & 2 deletions benches/uniform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const N_RESAMPLES: usize = 10_000;
macro_rules! sample {
($R:ty, $T:ty, $U:ty, $g:expr) => {
$g.bench_function(BenchmarkId::new(stringify!($R), "single"), |b| {
let mut rng = <$R>::from_entropy();
let mut rng = <$R>::from_rng(thread_rng()).unwrap();
let x = rng.gen::<$U>();
let bits = (<$T>::BITS / 2);
let mask = (1 as $U).wrapping_neg() >> bits;
Expand All @@ -35,7 +35,7 @@ macro_rules! sample {
});

$g.bench_function(BenchmarkId::new(stringify!($R), "distr"), |b| {
let mut rng = <$R>::from_entropy();
let mut rng = <$R>::from_rng(thread_rng()).unwrap();
let x = rng.gen::<$U>();
let bits = (<$T>::BITS / 2);
let mask = (1 as $U).wrapping_neg() >> bits;
Expand Down
4 changes: 2 additions & 2 deletions benches/uniform_float.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const N_RESAMPLES: usize = 10_000;
macro_rules! single_random {
($R:ty, $T:ty, $g:expr) => {
$g.bench_function(BenchmarkId::new(stringify!($T), stringify!($R)), |b| {
let mut rng = <$R>::from_entropy();
let mut rng = <$R>::from_rng(thread_rng()).unwrap();
let (mut low, mut high);
loop {
low = <$T>::from_bits(rng.gen());
Expand Down Expand Up @@ -63,7 +63,7 @@ fn single_random(c: &mut Criterion) {
macro_rules! distr_random {
($R:ty, $T:ty, $g:expr) => {
$g.bench_function(BenchmarkId::new(stringify!($T), stringify!($R)), |b| {
let mut rng = <$R>::from_entropy();
let mut rng = <$R>::from_rng(thread_rng()).unwrap();
let dist = loop {
let low = <$T>::from_bits(rng.gen());
let high = <$T>::from_bits(rng.gen());
Expand Down
2 changes: 1 addition & 1 deletion rand_core/src/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ pub trait BlockRngCore {
/// A marker trait used to indicate that an [`RngCore`] implementation is
/// supposed to be cryptographically secure.
///
/// See [`CryptoRng`][crate::CryptoRng] docs for more information.
/// See [`CryptoRng`] docs for more information.
pub trait CryptoBlockRng: BlockRngCore { }

/// A wrapper type implementing [`RngCore`] for some type implementing
Expand Down
96 changes: 45 additions & 51 deletions src/rngs/small.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,58 +22,20 @@ type Rng = super::xoshiro128plusplus::Xoshiro128PlusPlus;
/// Note that depending on the application, [`StdRng`] may be faster on many
/// modern platforms while providing higher-quality randomness. Furthermore,
/// `SmallRng` is **not** a good choice when:
/// - Security against prediction is important. Use [`StdRng`] instead.
/// - Seeds with many zeros are provided. In such cases, it takes `SmallRng`
/// about 10 samples to produce 0 and 1 bits with equal probability. Either
/// provide seeds with an approximately equal number of 0 and 1 (for example
/// by using [`SeedableRng::from_entropy`] or [`SeedableRng::seed_from_u64`]),
/// or use [`StdRng`] instead.
///
/// The algorithm is deterministic but should not be considered reproducible
/// due to dependence on platform and possible replacement in future
/// library versions. For a reproducible generator, use a named PRNG from an
/// external crate, e.g. [rand_xoshiro] or [rand_chacha].
/// Refer also to [The Book](https://rust-random.github.io/book/guide-rngs.html).
/// - Portability is required. Its implementation is not fixed. Use a named
/// generator from an external crate instead, for example [rand_xoshiro] or
/// [rand_chacha]. Refer also to
/// [The Book](https://rust-random.github.io/book/guide-rngs.html).
/// - Security against prediction is important. Use [`StdRng`] instead.
///
/// The PRNG algorithm in `SmallRng` is chosen to be efficient on the current
/// platform, without consideration for cryptography or security. The size of
/// its state is much smaller than [`StdRng`]. The current algorithm is
/// `Xoshiro256PlusPlus` on 64-bit platforms and `Xoshiro128PlusPlus` on 32-bit
/// platforms. Both are also implemented by the [rand_xoshiro] crate.
///
/// # Examples
///
/// Initializing `SmallRng` with a random seed can be done using [`SeedableRng::from_entropy`]:
///
/// ```
/// use rand::{Rng, SeedableRng};
/// use rand::rngs::SmallRng;
///
/// // Create small, cheap to initialize and fast RNG with a random seed.
/// // The randomness is supplied by the operating system.
/// let mut small_rng = SmallRng::from_entropy();
/// # let v: u32 = small_rng.gen();
/// ```
///
/// When initializing a lot of `SmallRng`'s, using [`thread_rng`] can be more
/// efficient:
///
/// ```
/// use rand::{SeedableRng, thread_rng};
/// use rand::rngs::SmallRng;
///
/// // Create a big, expensive to initialize and slower, but unpredictable RNG.
/// // This is cached and done only once per thread.
/// let mut thread_rng = thread_rng();
/// // Create small, cheap to initialize and fast RNGs with random seeds.
/// // One can generally assume this won't fail.
/// let rngs: Vec<SmallRng> = (0..10)
/// .map(|_| SmallRng::from_rng(&mut thread_rng).unwrap())
/// .collect();
/// ```
///
/// [`StdRng`]: crate::rngs::StdRng
/// [`thread_rng`]: crate::thread_rng
/// [rand_chacha]: https://crates.io/crates/rand_chacha
/// [rand_xoshiro]: https://crates.io/crates/rand_xoshiro
#[cfg_attr(doc_cfg, doc(cfg(feature = "small_rng")))]
Expand Down Expand Up @@ -102,21 +64,53 @@ impl RngCore for SmallRng {
}
}

impl SeedableRng for SmallRng {
type Seed = <Rng as SeedableRng>::Seed;

impl SmallRng {
/// Construct an instance seeded from another `Rng`
///
/// We recommend that the source (master) RNG uses a different algorithm
/// (i.e. is not `SmallRng`) to avoid correlations between the child PRNGs.
///
/// # Example
/// ```
/// # use rand::rngs::SmallRng;
/// let rng = SmallRng::from_rng(rand::thread_rng());
/// ```
#[inline(always)]
fn from_seed(seed: Self::Seed) -> Self {
SmallRng(Rng::from_seed(seed))
pub fn from_rng<R: RngCore>(rng: R) -> Result<Self, Error> {
Rng::from_rng(rng).map(SmallRng)
}

/// Construct an instance seeded from the thread-local RNG
///
/// # Panics
///
/// This method panics only if [`thread_rng`](crate::thread_rng) fails to
/// initialize.
#[cfg(all(feature = "std", feature = "std_rng", feature = "getrandom"))]
#[inline(always)]
fn from_rng<R: RngCore>(rng: R) -> Result<Self, Error> {
Rng::from_rng(rng).map(SmallRng)
pub fn from_thread_rng() -> Self {
let mut seed = <Rng as SeedableRng>::Seed::default();
crate::thread_rng().fill_bytes(seed.as_mut());
SmallRng(Rng::from_seed(seed))
}

/// Construct an instance from a `u64` seed
///
/// This provides a convenient method of seeding a `SmallRng` from a simple
/// number by use of another algorithm to mutate and expand the input.
/// This is suitable for use with low Hamming Weight numbers like 0 and 1.
///
/// **Warning:** the implementation is deterministic but not portable:
/// output values may differ according to platform and may be changed by a
/// future version of the library.
///
/// # Example
/// ```
/// # use rand::rngs::SmallRng;
/// let rng = SmallRng::seed_from_u64(1);
/// ```
#[inline(always)]
fn seed_from_u64(state: u64) -> Self {
pub fn seed_from_u64(state: u64) -> Self {
SmallRng(Rng::seed_from_u64(state))
}
}
3 changes: 2 additions & 1 deletion src/rngs/std.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ impl RngCore for StdRng {
}

impl SeedableRng for StdRng {
type Seed = <Rng as SeedableRng>::Seed;
// Fix to 256 bits. Changing this is a breaking change!
type Seed = [u8; 32];

#[inline(always)]
fn from_seed(seed: Self::Seed) -> Self {
Expand Down

0 comments on commit 1db3aa4

Please sign in to comment.