|
52 | 52 |
|
53 | 53 | use core::default::Default;
|
54 | 54 | use core::convert::AsMut;
|
| 55 | +use core::ptr::copy_nonoverlapping; |
55 | 56 |
|
56 | 57 | #[cfg(all(feature="alloc", not(feature="std")))] use alloc::boxed::Box;
|
57 | 58 |
|
@@ -297,7 +298,46 @@ pub trait SeedableRng: Sized {
|
297 | 298 | /// for example `0xBAD5EEDu32` or `0x0DDB1A5E5BAD5EEDu64` ("odd biases? bad
|
298 | 299 | /// seed"). This is assuming only a small number of values must be rejected.
|
299 | 300 | 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 | + |
301 | 341 | /// Create a new PRNG seeded from another `Rng`.
|
302 | 342 | ///
|
303 | 343 | /// 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 {}
|
402 | 442 | // Implement `CryptoRng` for boxed references to an `CryptoRng`.
|
403 | 443 | #[cfg(feature="alloc")]
|
404 | 444 | 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 | +} |
0 commit comments