Skip to content

Commit 1818b99

Browse files
committed
Implement SeedableRng::seed_from_u64
1 parent ad14217 commit 1818b99

File tree

2 files changed

+82
-16
lines changed

2 files changed

+82
-16
lines changed

rand_core/src/lib.rs

+81-1
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252

5353
use core::default::Default;
5454
use core::convert::AsMut;
55+
use core::ptr::copy_nonoverlapping;
5556

5657
#[cfg(all(feature="alloc", not(feature="std")))] use alloc::boxed::Box;
5758

@@ -297,7 +298,46 @@ pub trait SeedableRng: Sized {
297298
/// for example `0xBAD5EEDu32` or `0x0DDB1A5E5BAD5EEDu64` ("odd biases? bad
298299
/// seed"). This is assuming only a small number of values must be rejected.
299300
fn from_seed(seed: Self::Seed) -> Self;
300-
301+
302+
/// Create a new PRNG using a `u64` seed.
303+
///
304+
/// This is a convenience-wrapper around `from_seed` to allow construction
305+
/// of any `SeedableRng` from a simple `u64` value. It is designed such that
306+
/// low Hamming Weight numbers like 0 and 1 can be used and should still
307+
/// result in good, independent seeds to the PRNG which is returned.
308+
///
309+
/// This **is not suitable for cryptography**, as should be clear given that
310+
/// the input size is only 64 bits.
311+
///
312+
/// Implementations for PRNGs *may* provide their own implementations of
313+
/// this function, but the default implementation should be good enough for
314+
/// all purposes. *Changing* the implementation of this function should be
315+
/// considered a value-breaking change.
316+
fn seed_from_u64(mut state: u64) -> Self {
317+
// We use PCG32 to generate a u32 sequence, and copy to the seed
318+
const MUL: u64 = 6364136223846793005;
319+
const INC: u64 = 11634580027462260723;
320+
321+
let mut seed = Self::Seed::default();
322+
for chunk in seed.as_mut().chunks_mut(4) {
323+
// We advance the state first (to get away from input, which may
324+
// have low Hamming Weight).
325+
state = state.wrapping_mul(MUL).wrapping_add(INC);
326+
327+
// Use PCG output function with to_le to generate x:
328+
let xorshifted = (((state >> 18) ^ state) >> 27) as u32;
329+
let rot = (state >> 59) as u32;
330+
let x = xorshifted.rotate_right(rot).to_le();
331+
332+
unsafe {
333+
let p = &x as *const u32 as *const u8;
334+
copy_nonoverlapping(p, chunk.as_mut_ptr(), chunk.len());
335+
}
336+
}
337+
338+
Self::from_seed(seed)
339+
}
340+
301341
/// Create a new PRNG seeded from another `Rng`.
302342
///
303343
/// This is the recommended way to initialize PRNGs with fresh entropy. The
@@ -402,3 +442,43 @@ impl<'a, R: CryptoRng + ?Sized> CryptoRng for &'a mut R {}
402442
// Implement `CryptoRng` for boxed references to an `CryptoRng`.
403443
#[cfg(feature="alloc")]
404444
impl<R: CryptoRng + ?Sized> CryptoRng for Box<R> {}
445+
446+
#[cfg(test)]
447+
mod test {
448+
use super::*;
449+
450+
#[test]
451+
fn test_seed_from_u64() {
452+
struct SeedableNum(u64);
453+
impl SeedableRng for SeedableNum {
454+
type Seed = [u8; 8];
455+
fn from_seed(seed: Self::Seed) -> Self {
456+
let mut x = [0u64; 1];
457+
le::read_u64_into(&seed, &mut x);
458+
SeedableNum(x[0])
459+
}
460+
}
461+
462+
const N: usize = 8;
463+
const SEEDS: [u64; N] = [0u64, 1, 2, 3, 4, 8, 16, -1i64 as u64];
464+
let mut results = [0u64; N];
465+
for (i, seed) in SEEDS.iter().enumerate() {
466+
let SeedableNum(x) = SeedableNum::seed_from_u64(*seed);
467+
results[i] = x;
468+
}
469+
470+
for (i1, r1) in results.iter().enumerate() {
471+
let weight = r1.count_ones();
472+
assert!(weight >= 20);
473+
474+
for (i2, r2) in results.iter().enumerate() {
475+
if i1 == i2 { continue; }
476+
let diff_weight = (r1 ^ r2).count_ones();
477+
assert!(diff_weight >= 20);
478+
}
479+
}
480+
481+
// value-breakage test:
482+
assert_eq!(results[0], 5029875928683246316);
483+
}
484+
}

src/lib.rs

+1-15
Original file line numberDiff line numberDiff line change
@@ -1060,21 +1060,7 @@ mod test {
10601060
}
10611061

10621062
pub fn rng(seed: u64) -> TestRng<StdRng> {
1063-
// TODO: use from_hashable
1064-
let mut state = seed;
1065-
let mut seed = <StdRng as SeedableRng>::Seed::default();
1066-
for x in seed.iter_mut() {
1067-
// PCG algorithm
1068-
const MUL: u64 = 6364136223846793005;
1069-
const INC: u64 = 11634580027462260723;
1070-
let oldstate = state;
1071-
state = oldstate.wrapping_mul(MUL).wrapping_add(INC);
1072-
1073-
let xorshifted = (((oldstate >> 18) ^ oldstate) >> 27) as u32;
1074-
let rot = (oldstate >> 59) as u32;
1075-
*x = xorshifted.rotate_right(rot) as u8;
1076-
}
1077-
TestRng { inner: StdRng::from_seed(seed) }
1063+
TestRng { inner: StdRng::seed_from_u64(seed) }
10781064
}
10791065

10801066
#[test]

0 commit comments

Comments
 (0)