Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace SeedableRng impl for SmallRng with inherent fns #1368

Merged
merged 6 commits into from
Jan 5, 2024
Merged
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
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