Skip to content

Commit de703be

Browse files
committed
Rework SeedableRng trait
1 parent 3a63497 commit de703be

File tree

9 files changed

+314
-427
lines changed

9 files changed

+314
-427
lines changed

src/lib.rs

Lines changed: 99 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -250,8 +250,7 @@
250250
#[cfg(feature="std")] extern crate std as core;
251251
#[cfg(all(feature = "alloc", not(feature="std")))] extern crate alloc;
252252

253-
use core::marker;
254-
use core::mem;
253+
use core::{marker, mem, slice};
255254
#[cfg(feature="std")] use std::cell::RefCell;
256255
#[cfg(feature="std")] use std::rc::Rc;
257256
#[cfg(all(feature="alloc", not(feature="std")))] use alloc::boxed::Box;
@@ -732,36 +731,66 @@ impl<'a, R: Rng> Iterator for AsciiGenerator<'a, R> {
732731
}
733732
}
734733

735-
/// A random number generator that can be explicitly seeded to produce
736-
/// the same stream of randomness multiple times.
737-
pub trait SeedableRng<Seed>: Rng {
738-
/// Reseed an RNG with the given seed.
734+
mod private {
735+
pub trait Sealed {}
736+
impl Sealed for [u8; 4] {}
737+
impl Sealed for [u8; 8] {}
738+
impl Sealed for [u8; 12] {}
739+
impl Sealed for [u8; 16] {}
740+
impl Sealed for [u8; 24] {}
741+
impl Sealed for [u8; 32] {}
742+
}
743+
744+
/// The seed type is restricted to these types. This trait is sealed to prevent
745+
/// user-extension.
746+
///
747+
/// Use of byte-arrays avoids endianness issues. We may extend this to allow
748+
/// byte arrays of other lengths in the future.
749+
pub trait SeedRestriction: private::Sealed + Default + AsMut<[u8]> {}
750+
impl<S> SeedRestriction for S where S: private::Sealed + Default + AsMut<[u8]> {}
751+
752+
/// A random number generator that can be explicitly seeded.
753+
///
754+
/// There are two subtle differences between `from_rng` and`from_seed` (beyond
755+
/// the obvious): first, that `from_rng` has no reproducibility requirement, and
756+
/// second, that `from_rng` may directly fill internal states larger than
757+
/// `SeedableRng::Seed`, where `from_seed` may need some extra step to expand
758+
/// the input.
759+
pub trait SeedableRng: Sized {
760+
/// Seed type.
761+
type Seed: SeedRestriction;
762+
763+
/// Create a new PRNG using the given seed.
739764
///
740-
/// # Example
765+
/// Each PRNG should implement this.
741766
///
742-
/// ```rust
743-
/// use rand::{Rng, SeedableRng, StdRng};
767+
/// Reproducibility is required; that is, a fixed PRNG seeded using this
768+
/// function with a fixed seed should produce the same sequence of output
769+
/// today, and in the future. PRNGs not able to satisfy this should make
770+
/// clear notes in their documentation. It is however not required that this
771+
/// function yield the same state as a reference implementation of the PRNG
772+
/// given equivalent seed; if necessary another constructor should be used.
744773
///
745-
/// let seed: &[_] = &[1, 2, 3, 4];
746-
/// let mut rng: StdRng = SeedableRng::from_seed(seed);
747-
/// println!("{}", rng.gen::<f64>());
748-
/// rng.reseed(&[5, 6, 7, 8]);
749-
/// println!("{}", rng.gen::<f64>());
750-
/// ```
751-
fn reseed(&mut self, Seed);
774+
/// It may be expected that bits in the seed are well distributed, i.e. that
775+
/// values like 0, 1 and (size - 1) are unlikely.
776+
fn from_seed(seed: Self::Seed) -> Self;
752777

753-
/// Create a new RNG with the given seed.
778+
/// Create a new PRNG seeded from another `Rng`.
754779
///
755-
/// # Example
756-
///
757-
/// ```rust
758-
/// use rand::{Rng, SeedableRng, StdRng};
759-
///
760-
/// let seed: &[_] = &[1, 2, 3, 4];
761-
/// let mut rng: StdRng = SeedableRng::from_seed(seed);
762-
/// println!("{}", rng.gen::<f64>());
763-
/// ```
764-
fn from_seed(seed: Seed) -> Self;
780+
/// Seeding from a cryptographic generator should be fine. On the other
781+
/// hand, seeding a simple numerical generator from another of the same
782+
/// type sometimes has serious side effects such as effectively cloning the
783+
/// generator.
784+
fn from_rng<R: Rng>(mut rng: R) -> Result<Self, Error> {
785+
let mut seed = Self::Seed::default();
786+
let size = mem::size_of::<Self::Seed>() as usize;
787+
unsafe {
788+
let ptr = seed.as_mut().as_mut_ptr() as *mut u8;
789+
let slice = slice::from_raw_parts_mut(ptr, size);
790+
rng.try_fill_bytes(slice)?;
791+
}
792+
Ok(Self::from_seed(seed))
793+
}
765794
}
766795

767796
/// A wrapper for generating floating point numbers uniformly in the
@@ -801,10 +830,11 @@ pub struct Closed01<F>(pub F);
801830

802831
/// The standard RNG. This is designed to be efficient on the current
803832
/// platform.
833+
///
834+
/// The underlying algorithm is not fixed, thus values from this generator
835+
/// cannot be guaranteed to be reproducible.
804836
#[derive(Clone, Debug)]
805-
pub struct StdRng {
806-
rng: IsaacWordRng,
807-
}
837+
pub struct StdRng(IsaacWordRng);
808838

809839
impl StdRng {
810840
/// Create a randomly seeded instance of `StdRng`.
@@ -821,10 +851,10 @@ impl StdRng {
821851
#[cfg(feature="std")]
822852
pub fn new() -> Result<StdRng, Error> {
823853
match OsRng::new() {
824-
Ok(mut r) => Ok(StdRng { rng: r.gen() }),
854+
Ok(mut r) => Ok(StdRng(r.gen())),
825855
Err(e1) => {
826856
match JitterRng::new() {
827-
Ok(mut r) => Ok(StdRng { rng: r.gen() }),
857+
Ok(mut r) => Ok(StdRng(r.gen())),
828858
Err(_) => {
829859
Err(e1)
830860
}
@@ -835,36 +865,32 @@ impl StdRng {
835865
}
836866

837867
impl Rng for StdRng {
838-
#[inline]
839868
fn next_u32(&mut self) -> u32 {
840-
self.rng.next_u32()
869+
self.0.next_u32()
841870
}
842871

843-
#[inline]
844872
fn next_u64(&mut self) -> u64 {
845-
self.rng.next_u64()
873+
self.0.next_u64()
846874
}
847875

848-
#[inline]
849876
fn fill_bytes(&mut self, dest: &mut [u8]) {
850-
self.rng.fill_bytes(dest)
877+
self.0.fill_bytes(dest);
851878
}
852-
853-
#[inline]
879+
854880
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
855-
self.rng.try_fill_bytes(dest)
881+
self.0.try_fill_bytes(dest)
856882
}
857883
}
858884

859-
impl<'a> SeedableRng<&'a [usize]> for StdRng {
860-
fn reseed(&mut self, seed: &'a [usize]) {
861-
// the internal RNG can just be seeded from the above
862-
// randomness.
863-
self.rng.reseed(unsafe {mem::transmute(seed)})
885+
impl SeedableRng for StdRng {
886+
type Seed = <IsaacWordRng as SeedableRng>::Seed;
887+
888+
fn from_seed(seed: Self::Seed) -> Self {
889+
StdRng(IsaacWordRng::from_seed(seed))
864890
}
865891

866-
fn from_seed(seed: &'a [usize]) -> StdRng {
867-
StdRng { rng: SeedableRng::from_seed(unsafe {mem::transmute(seed)}) }
892+
fn from_rng<R: Rng>(rng: R) -> Result<Self, Error> {
893+
IsaacWordRng::from_rng(rng).map(|rng| StdRng(rng))
868894
}
869895
}
870896

@@ -1037,7 +1063,7 @@ pub fn sample<T, I, R>(rng: &mut R, iterable: I, amount: usize) -> Vec<T>
10371063
mod test {
10381064
use impls;
10391065
#[cfg(feature="std")]
1040-
use super::{random, thread_rng, weak_rng};
1066+
use super::{random, thread_rng};
10411067
use super::{Rng, SeedableRng, StdRng};
10421068
#[cfg(feature="alloc")]
10431069
use alloc::boxed::Box;
@@ -1057,8 +1083,21 @@ mod test {
10571083
}
10581084

10591085
pub fn rng(seed: u64) -> TestRng<StdRng> {
1060-
let seed = [seed as usize];
1061-
TestRng { inner: StdRng::from_seed(&seed) }
1086+
// TODO: use from_hashable
1087+
let mut state = seed;
1088+
let mut seed = <StdRng as SeedableRng>::Seed::default();
1089+
for x in seed.iter_mut() {
1090+
// PCG algorithm
1091+
const MUL: u64 = 6364136223846793005;
1092+
const INC: u64 = 11634580027462260723;
1093+
let oldstate = state;
1094+
state = oldstate.wrapping_mul(MUL).wrapping_add(INC);
1095+
1096+
let xorshifted = (((oldstate >> 18) ^ oldstate) >> 27) as u32;
1097+
let rot = (oldstate >> 59) as u32;
1098+
*x = xorshifted.rotate_right(rot) as u8;
1099+
}
1100+
TestRng { inner: StdRng::from_seed(seed) }
10621101
}
10631102

10641103
struct ConstRng { i: u64 }
@@ -1071,23 +1110,6 @@ mod test {
10711110
}
10721111
}
10731112

1074-
pub fn iter_eq<I, J>(i: I, j: J) -> bool
1075-
where I: IntoIterator,
1076-
J: IntoIterator<Item=I::Item>,
1077-
I::Item: Eq
1078-
{
1079-
// make sure the iterators have equal length
1080-
let mut i = i.into_iter();
1081-
let mut j = j.into_iter();
1082-
loop {
1083-
match (i.next(), j.next()) {
1084-
(Some(ref ei), Some(ref ej)) if ei == ej => { }
1085-
(None, None) => return true,
1086-
_ => return false,
1087-
}
1088-
}
1089-
}
1090-
10911113
#[test]
10921114
fn test_fill_bytes_default() {
10931115
let mut r = ConstRng { i: 0x11_22_33_44_55_66_77_88 };
@@ -1250,35 +1272,23 @@ mod test {
12501272
#[test]
12511273
#[cfg(target_pointer_width = "32")]
12521274
fn test_stdrng_construction() {
1253-
let seed = [1, 23, 456, 7890, 0, 0, 0, 0];
1254-
let mut rng1 = StdRng::from_seed(&seed);
1275+
let seed = [1,0,0,0, 23,0,0,0, 200,1,0,0, 210,30,0,0,
1276+
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0];
1277+
let mut rng1 = StdRng::from_seed(seed);
12551278
assert_eq!(rng1.next_u32(), 2869442790);
12561279

1257-
/* FIXME: enable once `from_rng` has landed
12581280
let mut rng2 = StdRng::from_rng(&mut rng1).unwrap();
1259-
assert_eq!(rng1.next_u32(), 3094074039);
1260-
*/
1281+
assert_eq!(rng2.next_u32(), 3094074039);
12611282
}
12621283
#[test]
12631284
#[cfg(target_pointer_width = "64")]
12641285
fn test_stdrng_construction() {
1265-
let seed = [1, 23, 456, 7890, 0, 0, 0, 0];
1266-
let mut rng1 = StdRng::from_seed(&seed);
1267-
assert_eq!(rng1.next_u32(), 3477963620);
1286+
let seed = [1,0,0,0, 23,0,0,0, 200,1,0,0, 210,30,0,0,
1287+
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0];
1288+
let mut rng1 = StdRng::from_seed(seed);
1289+
assert_eq!(rng1.next_u64(), 14964555543728284049);
12681290

1269-
/* FIXME: enable once `from_rng` has landed
12701291
let mut rng2 = StdRng::from_rng(&mut rng1).unwrap();
1271-
assert_eq!(rng1.next_u32(), 3094074039);
1272-
*/
1273-
}
1274-
1275-
#[test]
1276-
#[cfg(feature="std")]
1277-
fn test_weak_rng() {
1278-
let s = weak_rng().gen_iter::<usize>().take(256).collect::<Vec<usize>>();
1279-
let mut ra: StdRng = SeedableRng::from_seed(&s[..]);
1280-
let mut rb: StdRng = SeedableRng::from_seed(&s[..]);
1281-
assert!(iter_eq(ra.gen_ascii_chars().take(100),
1282-
rb.gen_ascii_chars().take(100)));
1292+
assert_eq!(rng2.next_u64(), 919595328260451758);
12831293
}
12841294
}

0 commit comments

Comments
 (0)