std::rand: improve the task_rng code.
It now: - can be explicitly seeded from user code (`seed_task_rng`) or from the environment (`RUST_SEED`, a positive integer) - automatically reseeds itself from the OS *unless* it was seeded by either method above - has more documentation
This commit is contained in:
parent
92725ae765
commit
fb9706338d
@ -54,6 +54,7 @@ use u32;
|
||||
use u64;
|
||||
use uint;
|
||||
use vec;
|
||||
use os::getenv;
|
||||
|
||||
pub use self::isaac::{IsaacRng, Isaac64Rng};
|
||||
pub use self::os::OSRng;
|
||||
@ -284,7 +285,7 @@ pub trait Rng {
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ~~~{.rust}
|
||||
/// ```rust
|
||||
/// use std::rand::{task_rng, Rng};
|
||||
///
|
||||
/// fn main() {
|
||||
@ -292,7 +293,7 @@ pub trait Rng {
|
||||
/// task_rng().fill_bytes(v);
|
||||
/// printfln!(v);
|
||||
/// }
|
||||
/// ~~~
|
||||
/// ```
|
||||
fn fill_bytes(&mut self, mut dest: &mut [u8]) {
|
||||
// this relies on the lengths being transferred correctly when
|
||||
// transmuting between vectors like this.
|
||||
@ -700,12 +701,16 @@ pub struct StdRng { priv rng: IsaacRng }
|
||||
pub struct StdRng { priv rng: Isaac64Rng }
|
||||
|
||||
impl StdRng {
|
||||
/// Create a randomly seeded instance of `StdRng`. This reads
|
||||
/// randomness from the OS to seed the PRNG.
|
||||
#[cfg(not(target_word_size="64"))]
|
||||
fn new() -> StdRng {
|
||||
pub fn new() -> StdRng {
|
||||
StdRng { rng: IsaacRng::new() }
|
||||
}
|
||||
/// Create a randomly seeded instance of `StdRng`. This reads
|
||||
/// randomness from the OS to seed the PRNG.
|
||||
#[cfg(target_word_size="64")]
|
||||
fn new() -> StdRng {
|
||||
pub fn new() -> StdRng {
|
||||
StdRng { rng: Isaac64Rng::new() }
|
||||
}
|
||||
}
|
||||
@ -830,27 +835,96 @@ pub unsafe fn seed<T: Clone>(n: uint) -> ~[T] {
|
||||
s
|
||||
}
|
||||
|
||||
// used to make space in TLS for a random number generator
|
||||
local_data_key!(tls_rng_state: @mut StdRng)
|
||||
/// Controls how the task-local RNG is reseeded.
|
||||
enum TaskRngReseeder {
|
||||
/// Reseed using the StdRng::new() function, i.e. reading new
|
||||
/// randomness.
|
||||
WithNew,
|
||||
/// Don't reseed at all, e.g. when it has been explicitly seeded
|
||||
/// by the user.
|
||||
DontReseed
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives back a lazily initialized task-local random number generator,
|
||||
* seeded by the system. Intended to be used in method chaining style, ie
|
||||
* `task_rng().gen::<int>()`.
|
||||
*/
|
||||
#[inline]
|
||||
pub fn task_rng() -> @mut StdRng {
|
||||
let r = local_data::get(tls_rng_state, |k| k.map(|&k| *k));
|
||||
impl Default for TaskRngReseeder {
|
||||
fn default() -> TaskRngReseeder { WithNew }
|
||||
}
|
||||
|
||||
impl reseeding::Reseeder<StdRng> for TaskRngReseeder {
|
||||
fn reseed(&mut self, rng: &mut StdRng) {
|
||||
match *self {
|
||||
WithNew => *rng = StdRng::new(),
|
||||
DontReseed => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
static TASK_RNG_RESEED_THRESHOLD: uint = 32_768;
|
||||
/// The task-local RNG.
|
||||
pub type TaskRng = reseeding::ReseedingRng<StdRng, TaskRngReseeder>;
|
||||
|
||||
// used to make space in TLS for a random number generator
|
||||
local_data_key!(TASK_RNG_KEY: @mut TaskRng)
|
||||
|
||||
/// Retrieve the lazily-initialized task-local random number
|
||||
/// generator, seeded by the system. Intended to be used in method
|
||||
/// chaining style, e.g. `task_rng().gen::<int>()`.
|
||||
///
|
||||
/// The RNG provided will reseed itself from the operating system
|
||||
/// after generating a certain amount of randomness, unless it was
|
||||
/// explicitly seeded either by `seed_task_rng` or by setting the
|
||||
/// `RUST_SEED` environmental variable to some integer.
|
||||
///
|
||||
/// The internal RNG used is platform and architecture dependent, so
|
||||
/// may yield differing sequences on different computers, even when
|
||||
/// explicitly seeded with `seed_task_rng`. If absolute consistency is
|
||||
/// required, explicitly select an RNG, e.g. `IsaacRng` or
|
||||
/// `Isaac64Rng`.
|
||||
pub fn task_rng() -> @mut TaskRng {
|
||||
let r = local_data::get(TASK_RNG_KEY, |k| k.map(|&k| *k));
|
||||
match r {
|
||||
None => {
|
||||
let rng = @mut StdRng::new();
|
||||
local_data::set(tls_rng_state, rng);
|
||||
// check the environment
|
||||
let (sub_rng, reseeder) = match getenv("RUST_SEED") {
|
||||
None => (StdRng::new(), WithNew),
|
||||
|
||||
Some(s) => match from_str::<uint>(s) {
|
||||
None => fail2!("`RUST_SEED` is `{}`, should be a positive integer.", s),
|
||||
// explicitly seeded, so don't overwrite the seed later.
|
||||
Some(seed) => (SeedableRng::from_seed(&[seed]), DontReseed),
|
||||
}
|
||||
};
|
||||
|
||||
let rng = @mut reseeding::ReseedingRng::new(sub_rng,
|
||||
TASK_RNG_RESEED_THRESHOLD,
|
||||
reseeder);
|
||||
local_data::set(TASK_RNG_KEY, rng);
|
||||
rng
|
||||
}
|
||||
Some(rng) => rng
|
||||
}
|
||||
}
|
||||
|
||||
/// Explicitly seed (or reseed) the task-local random number
|
||||
/// generator. This stops the RNG from automatically reseeding itself.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use std::rand;
|
||||
///
|
||||
/// fn main() {
|
||||
/// rand::seed_task_rng(&[10u]);
|
||||
/// printfln!("Same every time: %u", rand::random::<uint>());
|
||||
///
|
||||
/// rand::seed_task_rng(&[1u, 2, 3, 4, 5, 6, 7, 8]);
|
||||
/// printfln!("Same every time: %f", rand::random::<float>());
|
||||
/// }
|
||||
/// ```
|
||||
pub fn seed_task_rng(seed: &[uint]) {
|
||||
let t_r = task_rng();
|
||||
(*t_r).reseed(seed);
|
||||
t_r.reseeder = DontReseed;
|
||||
}
|
||||
|
||||
// Allow direct chaining with `task_rng`
|
||||
impl<R: Rng> Rng for @mut R {
|
||||
#[inline]
|
||||
@ -863,10 +937,23 @@ impl<R: Rng> Rng for @mut R {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a random value of a Rand type, using the task's random number
|
||||
* generator.
|
||||
*/
|
||||
/// Generate a random value using the task-local random number
|
||||
/// generator.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use std::rand::random;
|
||||
///
|
||||
/// fn main() {
|
||||
/// if random() {
|
||||
/// let x = random();
|
||||
/// printfln!(2u * x);
|
||||
/// } else {
|
||||
/// printfln!(random::<float>());
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn random<T: Rand>() -> T {
|
||||
task_rng().gen()
|
||||
@ -1053,6 +1140,16 @@ mod test {
|
||||
**e >= MIN_VAL && **e <= MAX_VAL
|
||||
}));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_seed_task_rng() {
|
||||
seed_task_rng([1]);
|
||||
let first = random::<uint>();
|
||||
|
||||
seed_task_rng([1]);
|
||||
let second = random::<uint>();
|
||||
assert_eq!(first, second);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -11,7 +11,7 @@
|
||||
//! A wrapper around another RNG that reseeds it after it
|
||||
//! generates a certain number of random bytes.
|
||||
|
||||
use rand::Rng;
|
||||
use rand::{Rng, SeedableRng};
|
||||
use default::Default;
|
||||
|
||||
/// How many bytes of entropy the underling RNG is allowed to generate
|
||||
@ -76,6 +76,23 @@ impl<R: Rng, Rsdr: Reseeder<R>> Rng for ReseedingRng<R, Rsdr> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, R: SeedableRng<S>, Rsdr: Reseeder<R> + Default> SeedableRng<S> for ReseedingRng<R, Rsdr> {
|
||||
fn reseed(&mut self, seed: S) {
|
||||
self.rng.reseed(seed);
|
||||
self.bytes_generated = 0;
|
||||
}
|
||||
/// Create a new `ReseedingRng` from the given seed. This uses
|
||||
/// default values for both `generation_threshold` and `reseeder`.
|
||||
fn from_seed(seed: S) -> ReseedingRng<R, Rsdr> {
|
||||
ReseedingRng {
|
||||
rng: SeedableRng::from_seed(seed),
|
||||
generation_threshold: DEFAULT_GENERATION_THRESHOLD,
|
||||
bytes_generated: 0,
|
||||
reseeder: Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Something that can be used to reseed an RNG via `ReseedingRng`.
|
||||
pub trait Reseeder<R> {
|
||||
/// Reseed the given RNG.
|
||||
|
Loading…
x
Reference in New Issue
Block a user