From 72bf201d61ac36f058cdea6a3487a27029fb65e6 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Sat, 21 Sep 2013 21:32:57 +1000 Subject: [PATCH 01/22] std::rand: move the Isaac implementation to its own file. --- src/libstd/rand/isaac.rs | 198 +++++++++++++++++++++++++++++++++++++++ src/libstd/rand/mod.rs | 193 ++------------------------------------ 2 files changed, 207 insertions(+), 184 deletions(-) create mode 100644 src/libstd/rand/isaac.rs diff --git a/src/libstd/rand/isaac.rs b/src/libstd/rand/isaac.rs new file mode 100644 index 0000000000000..d20b05db485c1 --- /dev/null +++ b/src/libstd/rand/isaac.rs @@ -0,0 +1,198 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! The ISAAC random number generator. + +use rand::{seed, Rng}; +use iter::{Iterator, range, range_step}; +use option::{None, Some}; + +use cast; +use cmp; +use sys; +use vec; + +static RAND_SIZE_LEN: u32 = 8; +static RAND_SIZE: u32 = 1 << RAND_SIZE_LEN; + +/// A random number generator that uses the [ISAAC +/// algorithm](http://en.wikipedia.org/wiki/ISAAC_%28cipher%29). +/// +/// The ISAAC algorithm is suitable for cryptographic purposes. +pub struct IsaacRng { + priv cnt: u32, + priv rsl: [u32, .. RAND_SIZE], + priv mem: [u32, .. RAND_SIZE], + priv a: u32, + priv b: u32, + priv c: u32 +} + +impl IsaacRng { + /// Create an ISAAC random number generator with a random seed. + pub fn new() -> IsaacRng { + IsaacRng::new_seeded(seed(RAND_SIZE as uint * 4)) + } + + /// Create an ISAAC random number generator with a seed. This can be any + /// length, although the maximum number of bytes used is 1024 and any more + /// will be silently ignored. A generator constructed with a given seed + /// will generate the same sequence of values as all other generators + /// constructed with the same seed. + pub fn new_seeded(seed: &[u8]) -> IsaacRng { + let mut rng = IsaacRng { + cnt: 0, + rsl: [0, .. RAND_SIZE], + mem: [0, .. RAND_SIZE], + a: 0, b: 0, c: 0 + }; + + let array_size = sys::size_of_val(&rng.rsl); + let copy_length = cmp::min(array_size, seed.len()); + + // manually create a &mut [u8] slice of randrsl to copy into. + let dest = unsafe { cast::transmute((&mut rng.rsl, array_size)) }; + vec::bytes::copy_memory(dest, seed, copy_length); + rng.init(true); + rng + } + + /// Create an ISAAC random number generator using the default + /// fixed seed. + pub fn new_unseeded() -> IsaacRng { + let mut rng = IsaacRng { + cnt: 0, + rsl: [0, .. RAND_SIZE], + mem: [0, .. RAND_SIZE], + a: 0, b: 0, c: 0 + }; + rng.init(false); + rng + } + + /// Initialises `self`. If `use_rsl` is true, then use the current value + /// of `rsl` as a seed, otherwise construct one algorithmically (not + /// randomly). + fn init(&mut self, use_rsl: bool) { + let mut a = 0x9e3779b9; + let mut b = a; + let mut c = a; + let mut d = a; + let mut e = a; + let mut f = a; + let mut g = a; + let mut h = a; + + macro_rules! mix( + () => {{ + a^=b<<11; d+=a; b+=c; + b^=c>>2; e+=b; c+=d; + c^=d<<8; f+=c; d+=e; + d^=e>>16; g+=d; e+=f; + e^=f<<10; h+=e; f+=g; + f^=g>>4; a+=f; g+=h; + g^=h<<8; b+=g; h+=a; + h^=a>>9; c+=h; a+=b; + }} + ); + + do 4.times { mix!(); } + + if use_rsl { + macro_rules! memloop ( + ($arr:expr) => {{ + for i in range_step(0u32, RAND_SIZE, 8) { + a+=$arr[i ]; b+=$arr[i+1]; + c+=$arr[i+2]; d+=$arr[i+3]; + e+=$arr[i+4]; f+=$arr[i+5]; + g+=$arr[i+6]; h+=$arr[i+7]; + mix!(); + self.mem[i ]=a; self.mem[i+1]=b; + self.mem[i+2]=c; self.mem[i+3]=d; + self.mem[i+4]=e; self.mem[i+5]=f; + self.mem[i+6]=g; self.mem[i+7]=h; + } + }} + ); + + memloop!(self.rsl); + memloop!(self.mem); + } else { + for i in range_step(0u32, RAND_SIZE, 8) { + mix!(); + self.mem[i ]=a; self.mem[i+1]=b; + self.mem[i+2]=c; self.mem[i+3]=d; + self.mem[i+4]=e; self.mem[i+5]=f; + self.mem[i+6]=g; self.mem[i+7]=h; + } + } + + self.isaac(); + } + + /// Refills the output buffer (`self.rsl`) + #[inline] + fn isaac(&mut self) { + self.c += 1; + // abbreviations + let mut a = self.a; + let mut b = self.b + self.c; + + static MIDPOINT: uint = RAND_SIZE as uint / 2; + + macro_rules! ind (($x:expr) => { + self.mem[($x >> 2) & (RAND_SIZE - 1)] + }); + macro_rules! rngstep( + ($j:expr, $shift:expr) => {{ + let base = $j; + let mix = if $shift < 0 { + a >> -$shift as uint + } else { + a << $shift as uint + }; + + let x = self.mem[base + mr_offset]; + a = (a ^ mix) + self.mem[base + m2_offset]; + let y = ind!(x) + a + b; + self.mem[base + mr_offset] = y; + + b = ind!(y >> RAND_SIZE_LEN) + x; + self.rsl[base + mr_offset] = b; + }} + ); + + let r = [(0, MIDPOINT), (MIDPOINT, 0)]; + for &(mr_offset, m2_offset) in r.iter() { + for i in range_step(0u, MIDPOINT, 4) { + rngstep!(i + 0, 13); + rngstep!(i + 1, -6); + rngstep!(i + 2, 2); + rngstep!(i + 3, -16); + } + } + + self.a = a; + self.b = b; + self.cnt = RAND_SIZE; + } +} + +impl Rng for IsaacRng { + #[inline] + fn next(&mut self) -> u32 { + if self.cnt == 0 { + // make some more numbers + self.isaac(); + } + self.cnt -= 1; + self.rsl[self.cnt] + } +} diff --git a/src/libstd/rand/mod.rs b/src/libstd/rand/mod.rs index 70bcb0f717075..3348f5ee7f741 100644 --- a/src/libstd/rand/mod.rs +++ b/src/libstd/rand/mod.rs @@ -58,7 +58,10 @@ use uint; use vec; use libc::size_t; +pub use self::isaac::IsaacRng; + pub mod distributions; +pub mod isaac; /// A type that can be randomly generated using an Rng pub trait Rand { @@ -576,184 +579,6 @@ pub fn weak_rng() -> XorShiftRng { XorShiftRng::new() } -static RAND_SIZE_LEN: u32 = 8; -static RAND_SIZE: u32 = 1 << RAND_SIZE_LEN; - -/// A random number generator that uses the [ISAAC -/// algorithm](http://en.wikipedia.org/wiki/ISAAC_%28cipher%29). -/// -/// The ISAAC algorithm is suitable for cryptographic purposes. -pub struct IsaacRng { - priv cnt: u32, - priv rsl: [u32, .. RAND_SIZE], - priv mem: [u32, .. RAND_SIZE], - priv a: u32, - priv b: u32, - priv c: u32 -} - -impl IsaacRng { - /// Create an ISAAC random number generator with a random seed. - pub fn new() -> IsaacRng { - IsaacRng::new_seeded(seed()) - } - - /// Create an ISAAC random number generator with a seed. This can be any - /// length, although the maximum number of bytes used is 1024 and any more - /// will be silently ignored. A generator constructed with a given seed - /// will generate the same sequence of values as all other generators - /// constructed with the same seed. - pub fn new_seeded(seed: &[u8]) -> IsaacRng { - let mut rng = IsaacRng { - cnt: 0, - rsl: [0, .. RAND_SIZE], - mem: [0, .. RAND_SIZE], - a: 0, b: 0, c: 0 - }; - - let array_size = sys::size_of_val(&rng.rsl); - let copy_length = cmp::min(array_size, seed.len()); - - // manually create a &mut [u8] slice of randrsl to copy into. - let dest = unsafe { cast::transmute((&mut rng.rsl, array_size)) }; - vec::bytes::copy_memory(dest, seed, copy_length); - rng.init(true); - rng - } - - /// Create an ISAAC random number generator using the default - /// fixed seed. - pub fn new_unseeded() -> IsaacRng { - let mut rng = IsaacRng { - cnt: 0, - rsl: [0, .. RAND_SIZE], - mem: [0, .. RAND_SIZE], - a: 0, b: 0, c: 0 - }; - rng.init(false); - rng - } - - /// Initialises `self`. If `use_rsl` is true, then use the current value - /// of `rsl` as a seed, otherwise construct one algorithmically (not - /// randomly). - fn init(&mut self, use_rsl: bool) { - let mut a = 0x9e3779b9; - let mut b = a; - let mut c = a; - let mut d = a; - let mut e = a; - let mut f = a; - let mut g = a; - let mut h = a; - - macro_rules! mix( - () => {{ - a^=b<<11; d+=a; b+=c; - b^=c>>2; e+=b; c+=d; - c^=d<<8; f+=c; d+=e; - d^=e>>16; g+=d; e+=f; - e^=f<<10; h+=e; f+=g; - f^=g>>4; a+=f; g+=h; - g^=h<<8; b+=g; h+=a; - h^=a>>9; c+=h; a+=b; - }} - ); - - do 4.times { mix!(); } - - if use_rsl { - macro_rules! memloop ( - ($arr:expr) => {{ - for i in range_step(0u32, RAND_SIZE, 8) { - a+=$arr[i ]; b+=$arr[i+1]; - c+=$arr[i+2]; d+=$arr[i+3]; - e+=$arr[i+4]; f+=$arr[i+5]; - g+=$arr[i+6]; h+=$arr[i+7]; - mix!(); - self.mem[i ]=a; self.mem[i+1]=b; - self.mem[i+2]=c; self.mem[i+3]=d; - self.mem[i+4]=e; self.mem[i+5]=f; - self.mem[i+6]=g; self.mem[i+7]=h; - } - }} - ); - - memloop!(self.rsl); - memloop!(self.mem); - } else { - for i in range_step(0u32, RAND_SIZE, 8) { - mix!(); - self.mem[i ]=a; self.mem[i+1]=b; - self.mem[i+2]=c; self.mem[i+3]=d; - self.mem[i+4]=e; self.mem[i+5]=f; - self.mem[i+6]=g; self.mem[i+7]=h; - } - } - - self.isaac(); - } - - /// Refills the output buffer (`self.rsl`) - #[inline] - fn isaac(&mut self) { - self.c += 1; - // abbreviations - let mut a = self.a; - let mut b = self.b + self.c; - - static MIDPOINT: uint = RAND_SIZE as uint / 2; - - macro_rules! ind (($x:expr) => { - self.mem[($x >> 2) & (RAND_SIZE - 1)] - }); - macro_rules! rngstep( - ($j:expr, $shift:expr) => {{ - let base = $j; - let mix = if $shift < 0 { - a >> -$shift as uint - } else { - a << $shift as uint - }; - - let x = self.mem[base + mr_offset]; - a = (a ^ mix) + self.mem[base + m2_offset]; - let y = ind!(x) + a + b; - self.mem[base + mr_offset] = y; - - b = ind!(y >> RAND_SIZE_LEN) + x; - self.rsl[base + mr_offset] = b; - }} - ); - - let r = [(0, MIDPOINT), (MIDPOINT, 0)]; - for &(mr_offset, m2_offset) in r.iter() { - for i in range_step(0u, MIDPOINT, 4) { - rngstep!(i + 0, 13); - rngstep!(i + 1, -6); - rngstep!(i + 2, 2); - rngstep!(i + 3, -16); - } - } - - self.a = a; - self.b = b; - self.cnt = RAND_SIZE; - } -} - -impl Rng for IsaacRng { - #[inline] - fn next(&mut self) -> u32 { - if self.cnt == 0 { - // make some more numbers - self.isaac(); - } - self.cnt -= 1; - self.rsl[self.cnt] - } -} - /// An [Xorshift random number /// generator](http://en.wikipedia.org/wiki/Xorshift). /// @@ -786,7 +611,8 @@ impl XorShiftRng { pub fn new() -> XorShiftRng { #[fixed_stack_segment]; #[inline(never)]; - // generate seeds the same way as seed(), except we have a spceific size + // generate seeds the same way as seed(), except we have a + // specific size, so we can just use a fixed buffer. let mut s = [0u8, ..16]; loop { do s.as_mut_buf |p, sz| { @@ -817,12 +643,11 @@ impl XorShiftRng { } } -/// Create a new random seed. -pub fn seed() -> ~[u8] { +/// Create a new random seed of length `n`. +pub fn seed(n: uint) -> ~[u8] { #[fixed_stack_segment]; #[inline(never)]; unsafe { - let n = RAND_SIZE * 4; let mut s = vec::from_elem(n as uint, 0_u8); do s.as_mut_buf |p, sz| { rustrt::rand_gen_seed(p, sz as size_t) @@ -844,7 +669,7 @@ pub fn task_rng() -> @mut IsaacRng { let r = local_data::get(tls_rng_state, |k| k.map(|&k| *k)); match r { None => { - let rng = @@mut IsaacRng::new_seeded(seed()); + let rng = @@mut IsaacRng::new(); local_data::set(tls_rng_state, rng); *rng } @@ -877,7 +702,7 @@ mod test { #[test] fn test_rng_seeded() { - let seed = seed(); + let seed = seed(400); let mut ra = IsaacRng::new_seeded(seed); let mut rb = IsaacRng::new_seeded(seed); assert_eq!(ra.gen_ascii_str(100u), rb.gen_ascii_str(100u)); From a2b509656ac9c0f98d89fe4ea9d2f64a6ec7047a Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Sat, 21 Sep 2013 22:06:50 +1000 Subject: [PATCH 02/22] std::rand: Add an implementation of ISAAC64. This is 2x faster on 64-bit computers at generating anything larger than 32-bits. It has been verified against the canonical C implementation from the website of the creator of ISAAC64. Also, move `Rng.next` to `Rng.next_u32` and add `Rng.next_u64` to take full advantage of the wider word width; otherwise Isaac64 will always be squeezed down into a u32 wasting half the entropy and offering no advantage over the 32-bit variant. --- src/libextra/bitv.rs | 16 +-- src/libstd/rand/isaac.rs | 233 ++++++++++++++++++++++++++++++- src/libstd/rand/mod.rs | 96 ++++++------- src/libstd/rt/task.rs | 2 +- src/test/bench/core-map.rs | 2 +- src/test/bench/core-set.rs | 4 +- src/test/bench/shootout-fasta.rs | 2 +- src/test/run-pass/morestack6.rs | 4 +- 8 files changed, 295 insertions(+), 64 deletions(-) diff --git a/src/libextra/bitv.rs b/src/libextra/bitv.rs index 49a38a00b1003..98f69425aee37 100644 --- a/src/libextra/bitv.rs +++ b/src/libextra/bitv.rs @@ -1533,7 +1533,7 @@ mod tests { let mut r = rng(); let mut bitv = 0 as uint; do b.iter { - bitv |= (1 << ((r.next() as uint) % uint::bits)); + bitv |= (1 << ((r.next_u32() as uint) % uint::bits)); } } @@ -1542,7 +1542,7 @@ mod tests { let mut r = rng(); let mut bitv = SmallBitv::new(uint::bits); do b.iter { - bitv.set((r.next() as uint) % uint::bits, true); + bitv.set((r.next_u32() as uint) % uint::bits, true); } } @@ -1551,7 +1551,7 @@ mod tests { let mut r = rng(); let mut bitv = BigBitv::new(~[0]); do b.iter { - bitv.set((r.next() as uint) % uint::bits, true); + bitv.set((r.next_u32() as uint) % uint::bits, true); } } @@ -1562,7 +1562,7 @@ mod tests { storage.grow(BENCH_BITS / uint::bits, &0u); let mut bitv = BigBitv::new(storage); do b.iter { - bitv.set((r.next() as uint) % BENCH_BITS, true); + bitv.set((r.next_u32() as uint) % BENCH_BITS, true); } } @@ -1571,7 +1571,7 @@ mod tests { let mut r = rng(); let mut bitv = Bitv::new(BENCH_BITS, false); do b.iter { - bitv.set((r.next() as uint) % BENCH_BITS, true); + bitv.set((r.next_u32() as uint) % BENCH_BITS, true); } } @@ -1580,7 +1580,7 @@ mod tests { let mut r = rng(); let mut bitv = Bitv::new(uint::bits, false); do b.iter { - bitv.set((r.next() as uint) % uint::bits, true); + bitv.set((r.next_u32() as uint) % uint::bits, true); } } @@ -1589,7 +1589,7 @@ mod tests { let mut r = rng(); let mut bitv = BitvSet::new(); do b.iter { - bitv.insert((r.next() as uint) % uint::bits); + bitv.insert((r.next_u32() as uint) % uint::bits); } } @@ -1598,7 +1598,7 @@ mod tests { let mut r = rng(); let mut bitv = BitvSet::new(); do b.iter { - bitv.insert((r.next() as uint) % BENCH_BITS); + bitv.insert((r.next_u32() as uint) % BENCH_BITS); } } diff --git a/src/libstd/rand/isaac.rs b/src/libstd/rand/isaac.rs index d20b05db485c1..534ebfb473b94 100644 --- a/src/libstd/rand/isaac.rs +++ b/src/libstd/rand/isaac.rs @@ -187,7 +187,7 @@ impl IsaacRng { impl Rng for IsaacRng { #[inline] - fn next(&mut self) -> u32 { + fn next_u32(&mut self) -> u32 { if self.cnt == 0 { // make some more numbers self.isaac(); @@ -196,3 +196,234 @@ impl Rng for IsaacRng { self.rsl[self.cnt] } } + +static RAND_SIZE_64_LEN: uint = 8; +static RAND_SIZE_64: uint = 1 << RAND_SIZE_64_LEN; + +/// A random number generator that uses the 64-bit variant of the +/// [ISAAC +/// algorithm](http://en.wikipedia.org/wiki/ISAAC_%28cipher%29). +/// +/// The ISAAC algorithm is suitable for cryptographic purposes. +pub struct Isaac64Rng { + priv cnt: uint, + priv rsl: [u64, .. RAND_SIZE_64], + priv mem: [u64, .. RAND_SIZE_64], + priv a: u64, + priv b: u64, + priv c: u64, +} + +impl Isaac64Rng { + /// Create a 64-bit ISAAC random number generator with a random + /// seed. + pub fn new() -> Isaac64Rng { + Isaac64Rng::new_seeded(seed(RAND_SIZE_64 as uint * 8)) + } + + /// Create a 64-bit ISAAC random number generator with a + /// seed. This can be any length, although the maximum number of + /// bytes used is 2048 and any more will be silently ignored. A + /// generator constructed with a given seed will generate the same + /// sequence of values as all other generators constructed with + /// the same seed. + pub fn new_seeded(seed: &[u8]) -> Isaac64Rng { + let mut rng = Isaac64Rng { + cnt: 0, + rsl: [0, .. RAND_SIZE_64], + mem: [0, .. RAND_SIZE_64], + a: 0, b: 0, c: 0, + }; + + let array_size = sys::size_of_val(&rng.rsl); + let copy_length = cmp::min(array_size, seed.len()); + + // manually create a &mut [u8] slice of randrsl to copy into. + let dest = unsafe { cast::transmute((&mut rng.rsl, array_size)) }; + vec::bytes::copy_memory(dest, seed, copy_length); + rng.init(true); + rng + } + + /// Create a 64-bit ISAAC random number generator using the + /// default fixed seed. + pub fn new_unseeded() -> Isaac64Rng { + let mut rng = Isaac64Rng { + cnt: 0, + rsl: [0, .. RAND_SIZE_64], + mem: [0, .. RAND_SIZE_64], + a: 0, b: 0, c: 0, + }; + rng.init(false); + rng + } + + /// Initialises `self`. If `use_rsl` is true, then use the current value + /// of `rsl` as a seed, otherwise construct one algorithmically (not + /// randomly). + fn init(&mut self, use_rsl: bool) { + macro_rules! init ( + ($var:ident) => ( + let mut $var = 0x9e3779b97f4a7c13; + ) + ); + init!(a); init!(b); init!(c); init!(d); + init!(e); init!(f); init!(g); init!(h); + + macro_rules! mix( + () => {{ + a-=e; f^=h>>9; h+=a; + b-=f; g^=a<<9; a+=b; + c-=g; h^=b>>23; b+=c; + d-=h; a^=c<<15; c+=d; + e-=a; b^=d>>14; d+=e; + f-=b; c^=e<<20; e+=f; + g-=c; d^=f>>17; f+=g; + h-=d; e^=g<<14; g+=h; + }} + ); + + for _ in range(0, 4) { mix!(); } + if use_rsl { + macro_rules! memloop ( + ($arr:expr) => {{ + for i in range(0, RAND_SIZE_64 / 8).map(|i| i * 8) { + a+=$arr[i ]; b+=$arr[i+1]; + c+=$arr[i+2]; d+=$arr[i+3]; + e+=$arr[i+4]; f+=$arr[i+5]; + g+=$arr[i+6]; h+=$arr[i+7]; + mix!(); + self.mem[i ]=a; self.mem[i+1]=b; + self.mem[i+2]=c; self.mem[i+3]=d; + self.mem[i+4]=e; self.mem[i+5]=f; + self.mem[i+6]=g; self.mem[i+7]=h; + } + }} + ); + + memloop!(self.rsl); + memloop!(self.mem); + } else { + for i in range(0, RAND_SIZE_64 / 8).map(|i| i * 8) { + mix!(); + self.mem[i ]=a; self.mem[i+1]=b; + self.mem[i+2]=c; self.mem[i+3]=d; + self.mem[i+4]=e; self.mem[i+5]=f; + self.mem[i+6]=g; self.mem[i+7]=h; + } + } + + self.isaac64(); + } + + /// Refills the output buffer (`self.rsl`) + fn isaac64(&mut self) { + self.c += 1; + // abbreviations + let mut a = self.a; + let mut b = self.b + self.c; + static MIDPOINT: uint = RAND_SIZE_64 / 2; + static MP_VEC: [(uint, uint), .. 2] = [(0,MIDPOINT), (MIDPOINT, 0)]; + macro_rules! ind ( + ($x:expr) => { + self.mem.unsafe_get(($x as uint >> 3) & (RAND_SIZE_64 - 1)) + } + ); + macro_rules! rngstep( + ($j:expr, $shift:expr) => {{ + let base = base + $j; + let mix = a ^ (if $shift < 0 { + a >> -$shift as uint + } else { + a << $shift as uint + }); + let mix = if $j == 0 {!mix} else {mix}; + + unsafe { + let x = self.mem.unsafe_get(base + mr_offset); + a = mix + self.mem.unsafe_get(base + m2_offset); + let y = ind!(x) + a + b; + self.mem.unsafe_set(base + mr_offset, y); + + b = ind!(y >> RAND_SIZE_64_LEN) + x; + self.rsl.unsafe_set(base + mr_offset, b); + } + }} + ); + + for &(mr_offset, m2_offset) in MP_VEC.iter() { + for base in range(0, MIDPOINT / 4).map(|i| i * 4) { + rngstep!(0, 21); + rngstep!(1, -5); + rngstep!(2, 12); + rngstep!(3, -33); + } + } + + self.a = a; + self.b = b; + self.cnt = RAND_SIZE_64; + } +} + +impl Rng for Isaac64Rng { + #[inline] + fn next_u64(&mut self) -> u64 { + if self.cnt == 0 { + // make some more numbers + self.isaac64(); + } + self.cnt -= 1; + unsafe { self.rsl.unsafe_get(self.cnt) } + } +} + +#[cfg(test)] +mod test { + use super::*; + use rand::{Rng, seed}; + use option::{Option, Some}; + + #[test] + fn test_rng_seeded() { + let seed = seed(1024); + let mut ra = IsaacRng::new_seeded(seed); + let mut rb = IsaacRng::new_seeded(seed); + assert_eq!(ra.gen_ascii_str(100u), rb.gen_ascii_str(100u)); + + let seed = seed(2048); + let mut ra = Isaac64Rng::new_seeded(seed); + let mut rb = Isaac64Rng::new_seeded(seed); + assert_eq!(ra.gen_ascii_str(100u), rb.gen_ascii_str(100u)); + } + + #[test] + fn test_rng_seeded_custom_seed() { + // much shorter than generated seeds which are 1024 & 2048 + // bytes resp. + let seed = [2u8, 32u8, 4u8, 32u8, 51u8]; + let mut ra = IsaacRng::new_seeded(seed); + let mut rb = IsaacRng::new_seeded(seed); + assert_eq!(ra.gen_ascii_str(100u), rb.gen_ascii_str(100u)); + + let mut ra = Isaac64Rng::new_seeded(seed); + let mut rb = Isaac64Rng::new_seeded(seed); + assert_eq!(ra.gen_ascii_str(100u), rb.gen_ascii_str(100u)); + } + + #[test] + fn test_rng_seeded_custom_seed2() { + let seed = [2u8, 32u8, 4u8, 32u8, 51u8]; + let mut ra = IsaacRng::new_seeded(seed); + // Regression test that isaac is actually using the above vector + let r = ra.next_u32(); + error2!("{:?}", r); + assert_eq!(r, 2935188040u32); + + let mut ra = Isaac64Rng::new_seeded(seed); + // Regression test that isaac is actually using the above vector + let r = ra.next_u64(); + error2!("{:?}", r); + assert!(r == 0 && r == 1); // FIXME: find true value + } +} diff --git a/src/libstd/rand/mod.rs b/src/libstd/rand/mod.rs index 3348f5ee7f741..237ffb0e9adcc 100644 --- a/src/libstd/rand/mod.rs +++ b/src/libstd/rand/mod.rs @@ -58,7 +58,7 @@ use uint; use vec; use libc::size_t; -pub use self::isaac::IsaacRng; +pub use self::isaac::{IsaacRng, Isaac64Rng}; pub mod distributions; pub mod isaac; @@ -74,7 +74,7 @@ impl Rand for int { #[inline] fn rand(rng: &mut R) -> int { if int::bits == 32 { - rng.next() as int + rng.gen::() as int } else { rng.gen::() as int } @@ -84,28 +84,28 @@ impl Rand for int { impl Rand for i8 { #[inline] fn rand(rng: &mut R) -> i8 { - rng.next() as i8 + rng.next_u32() as i8 } } impl Rand for i16 { #[inline] fn rand(rng: &mut R) -> i16 { - rng.next() as i16 + rng.next_u32() as i16 } } impl Rand for i32 { #[inline] fn rand(rng: &mut R) -> i32 { - rng.next() as i32 + rng.next_u32() as i32 } } impl Rand for i64 { #[inline] fn rand(rng: &mut R) -> i64 { - (rng.next() as i64 << 32) | rng.next() as i64 + rng.next_u64() as i64 } } @@ -113,7 +113,7 @@ impl Rand for uint { #[inline] fn rand(rng: &mut R) -> uint { if uint::bits == 32 { - rng.next() as uint + rng.gen::() as uint } else { rng.gen::() as uint } @@ -123,28 +123,28 @@ impl Rand for uint { impl Rand for u8 { #[inline] fn rand(rng: &mut R) -> u8 { - rng.next() as u8 + rng.next_u32() as u8 } } impl Rand for u16 { #[inline] fn rand(rng: &mut R) -> u16 { - rng.next() as u16 + rng.next_u32() as u16 } } impl Rand for u32 { #[inline] fn rand(rng: &mut R) -> u32 { - rng.next() + rng.next_u32() } } impl Rand for u64 { #[inline] fn rand(rng: &mut R) -> u64 { - (rng.next() as u64 << 32) | rng.next() as u64 + rng.next_u64() } } @@ -159,9 +159,9 @@ static SCALE : f64 = (u32::max_value as f64) + 1.0f64; impl Rand for f64 { #[inline] fn rand(rng: &mut R) -> f64 { - let u1 = rng.next() as f64; - let u2 = rng.next() as f64; - let u3 = rng.next() as f64; + let u1 = rng.next_u32() as f64; + let u2 = rng.next_u32() as f64; + let u3 = rng.next_u32() as f64; ((u1 / SCALE + u2) / SCALE + u3) / SCALE } @@ -170,7 +170,7 @@ impl Rand for f64 { impl Rand for bool { #[inline] fn rand(rng: &mut R) -> bool { - rng.next() & 1u32 == 1u32 + rng.gen::() & 1 == 1 } } @@ -252,8 +252,23 @@ pub struct Weighted { /// A random number generator pub trait Rng { - /// Return the next random integer - fn next(&mut self) -> u32; + /// Return the next random u32. + /// + /// By default this is implemented in terms of `next_u64`. An + /// implementation of this trait must provide at least one of + /// these two methods. + fn next_u32(&mut self) -> u32 { + self.next_u64() as u32 + } + + /// Return the next random u64. + /// + /// By default this is implemented in terms of `next_u32`. An + /// implementation of this trait must provide at least one of + /// these two methods. + fn next_u64(&mut self) -> u64 { + (self.next_u32() as u64 << 32) | (self.next_u32() as u64) + } /// Return a random value of a Rand type. @@ -594,7 +609,7 @@ pub struct XorShiftRng { impl Rng for XorShiftRng { #[inline] - fn next(&mut self) -> u32 { + fn next_u32(&mut self) -> u32 { let x = self.x; let t = x ^ (x << 11); self.x = self.y; @@ -680,8 +695,12 @@ pub fn task_rng() -> @mut IsaacRng { // Allow direct chaining with `task_rng` impl Rng for @mut R { #[inline] - fn next(&mut self) -> u32 { - (**self).next() + fn next_u32(&mut self) -> u32 { + (**self).next_u32() + } + #[inline] + fn next_u64(&mut self) -> u64 { + (**self).next_u64() } } @@ -700,34 +719,6 @@ mod test { use option::{Option, Some}; use super::*; - #[test] - fn test_rng_seeded() { - let seed = seed(400); - let mut ra = IsaacRng::new_seeded(seed); - let mut rb = IsaacRng::new_seeded(seed); - assert_eq!(ra.gen_ascii_str(100u), rb.gen_ascii_str(100u)); - } - - #[test] - fn test_rng_seeded_custom_seed() { - // much shorter than generated seeds which are 1024 bytes - let seed = [2u8, 32u8, 4u8, 32u8, 51u8]; - let mut ra = IsaacRng::new_seeded(seed); - let mut rb = IsaacRng::new_seeded(seed); - assert_eq!(ra.gen_ascii_str(100u), rb.gen_ascii_str(100u)); - } - - #[test] - fn test_rng_seeded_custom_seed2() { - let seed = [2u8, 32u8, 4u8, 32u8, 51u8]; - let mut ra = IsaacRng::new_seeded(seed); - // Regression test that isaac is actually using the above vector - let r = ra.next(); - debug2!("{:?}", r); - assert!(r == 890007737u32 // on x86_64 - || r == 2935188040u32); // on x86 - } - #[test] fn test_gen_integer_range() { let mut r = rng(); @@ -921,6 +912,15 @@ mod bench { bh.bytes = size_of::() as u64; } + #[bench] + fn rand_isaac64(bh: &mut BenchHarness) { + let mut rng = Isaac64Rng::new(); + do bh.iter { + rng.gen::(); + } + bh.bytes = size_of::() as u64; + } + #[bench] fn rand_shuffle_100(bh: &mut BenchHarness) { let mut rng = XorShiftRng::new(); diff --git a/src/libstd/rt/task.rs b/src/libstd/rt/task.rs index 2d1b57cebf5a9..48b894f51e095 100644 --- a/src/libstd/rt/task.rs +++ b/src/libstd/rt/task.rs @@ -509,7 +509,7 @@ mod test { do run_in_newsched_task() { use rand::{rng, Rng}; let mut r = rng(); - let _ = r.next(); + let _ = r.next_u32(); } } diff --git a/src/test/bench/core-map.rs b/src/test/bench/core-map.rs index 3a3466eebff12..bec11ff3a7d1d 100644 --- a/src/test/bench/core-map.rs +++ b/src/test/bench/core-map.rs @@ -109,7 +109,7 @@ fn main() { let mut rng = std::rand::IsaacRng::new_seeded([1, 1, 1, 1, 1, 1, 1]); let mut set = HashSet::new(); while set.len() != n_keys { - let next = rng.next() as uint; + let next = rng.gen(); if set.insert(next) { rand.push(next); } diff --git a/src/test/bench/core-set.rs b/src/test/bench/core-set.rs index 138d5795ae188..36ebdc549227a 100644 --- a/src/test/bench/core-set.rs +++ b/src/test/bench/core-set.rs @@ -60,7 +60,7 @@ impl Results { let mut set = f(); do timed(&mut self.random_ints) { for _ in range(0, num_keys) { - set.insert((rng.next() as uint) % rand_cap); + set.insert(rng.gen::() % rand_cap); } } } @@ -102,7 +102,7 @@ impl Results { let mut set = f(); do timed(&mut self.random_strings) { for _ in range(0, num_keys) { - let s = (rng.next() as uint).to_str(); + let s = rng.gen::().to_str(); set.insert(s); } } diff --git a/src/test/bench/shootout-fasta.rs b/src/test/bench/shootout-fasta.rs index 402b3be9542f8..53da752fdc4fa 100644 --- a/src/test/bench/shootout-fasta.rs +++ b/src/test/bench/shootout-fasta.rs @@ -76,7 +76,7 @@ fn make_random_fasta(wr: @io::Writer, wr.write_line(~">" + id + " " + desc); let mut rng = rand::rng(); let rng = @mut MyRandom { - last: rng.next() + last: rng.gen() }; let mut op: ~str = ~""; for _ in range(0u, n as uint) { diff --git a/src/test/run-pass/morestack6.rs b/src/test/run-pass/morestack6.rs index 45473193de93e..a5a67ede98c0d 100644 --- a/src/test/run-pass/morestack6.rs +++ b/src/test/run-pass/morestack6.rs @@ -69,8 +69,8 @@ pub fn main() { let mut rng = rand::rng(); for f in fns.iter() { let f = *f; - let sz = rng.next() % 256u32 + 256u32; - let frame_backoff = rng.next() % 10u32 + 1u32; + let sz = rng.gen::() % 256u32 + 256u32; + let frame_backoff = rng.gen::() % 10u32 + 1u32; task::try(|| runtest(f, frame_backoff) ); } } From 39a69d323da95ce642ea7fe8d40eb8bdd6a277c8 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Sun, 22 Sep 2013 20:51:57 +1000 Subject: [PATCH 03/22] std::rand: Add OSRng, ReaderRng wrappers around the OS RNG & generic Readers respectively. The former reads from e.g. /dev/urandom, the latter just wraps any std::rt::io::Reader into an interface that implements Rng. This also adds Rng.fill_bytes for efficient implementations of the above (reading 8 bytes at a time is inefficient when you can read 1000), and removes the dependence on src/rt (i.e. rand_gen_seed) although this last one requires implementing hand-seeding of the XorShiftRng used in the scheduler on Linux/unixes, since OSRng relies on a scheduler existing to be able to read from /dev/urandom. --- src/libstd/rand/mod.rs | 122 ++++++++++++++++++------ src/libstd/rand/os.rs | 193 ++++++++++++++++++++++++++++++++++++++ src/libstd/rand/reader.rs | 94 +++++++++++++++++++ src/libstd/rt/sched.rs | 56 ++++++++++- 4 files changed, 435 insertions(+), 30 deletions(-) create mode 100644 src/libstd/rand/os.rs create mode 100644 src/libstd/rand/reader.rs diff --git a/src/libstd/rand/mod.rs b/src/libstd/rand/mod.rs index 237ffb0e9adcc..e6bf42a5aedcd 100644 --- a/src/libstd/rand/mod.rs +++ b/src/libstd/rand/mod.rs @@ -44,24 +44,24 @@ fn main () { */ use cast; -use cmp; use container::Container; use int; -use iter::{Iterator, range, range_step}; +use iter::{Iterator, range}; use local_data; use prelude::*; use str; -use sys; use u32; use u64; use uint; use vec; -use libc::size_t; pub use self::isaac::{IsaacRng, Isaac64Rng}; +pub use self::os::OSRng; pub mod distributions; pub mod isaac; +pub mod os; +pub mod reader; /// A type that can be randomly generated using an Rng pub trait Rand { @@ -233,15 +233,6 @@ impl Rand for @T { fn rand(rng: &mut R) -> @T { @rng.gen() } } -#[abi = "cdecl"] -pub mod rustrt { - use libc::size_t; - - extern { - pub fn rand_gen_seed(buf: *mut u8, sz: size_t); - } -} - /// A value with a particular weight compared to other values pub struct Weighted { /// The numerical weight of this item @@ -252,7 +243,8 @@ pub struct Weighted { /// A random number generator pub trait Rng { - /// Return the next random u32. + /// Return the next random u32. This rarely needs to be called + /// directly, prefer `r.gen()` to `r.next_u32()`. /// /// By default this is implemented in terms of `next_u64`. An /// implementation of this trait must provide at least one of @@ -261,7 +253,8 @@ pub trait Rng { self.next_u64() as u32 } - /// Return the next random u64. + /// Return the next random u64. This rarely needs to be called + /// directly, prefer `r.gen()` to `r.next_u64()`. /// /// By default this is implemented in terms of `next_u32`. An /// implementation of this trait must provide at least one of @@ -270,6 +263,76 @@ pub trait Rng { (self.next_u32() as u64 << 32) | (self.next_u32() as u64) } + /// Fill `dest` with random data. + /// + /// This has a default implementation in terms of `next_u64` and + /// `next_u32`, but should be overriden by implementations that + /// offer a more efficient solution than just calling those + /// methods repeatedly. + /// + /// This method does *not* have a requirement to bear any fixed + /// relationship to the other methods, for example, it does *not* + /// have to result in the same output as progressively filling + /// `dest` with `self.gen::()`, and any such behaviour should + /// not be relied upon. + /// + /// This method should guarantee that `dest` is entirely filled + /// with new data, and may fail if this is impossible + /// (e.g. reading past the end of a file that is being used as the + /// source of randomness). + /// + /// # Example + /// + /// ~~~{.rust} + /// use std::rand::{task_rng, Rng}; + /// + /// fn main() { + /// let mut v = [0u8, .. 13579]; + /// 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. + let as_u64: &mut &mut [u64] = unsafe { cast::transmute(&mut dest) }; + for dest in as_u64.mut_iter() { + *dest = self.next_u64(); + } + + // the above will have filled up the vector as much as + // possible in multiples of 8 bytes. + let mut remaining = dest.len() % 8; + + // space for a u32 + if remaining >= 4 { + let as_u32: &mut &mut [u32] = unsafe { cast::transmute(&mut dest) }; + as_u32[as_u32.len() - 1] = self.next_u32(); + remaining -= 4; + } + // exactly filled + if remaining == 0 { return } + + // now we know we've either got 1, 2 or 3 spots to go, + // i.e. exactly one u32 is enough. + let rand = self.next_u32(); + let remaining_index = dest.len() - remaining; + match dest.mut_slice_from(remaining_index) { + [ref mut a] => { + *a = rand as u8; + } + [ref mut a, ref mut b] => { + *a = rand as u8; + *b = (rand >> 8) as u8; + } + [ref mut a, ref mut b, ref mut c] => { + *a = rand as u8; + *b = (rand >> 8) as u8; + *c = (rand >> 16) as u8; + } + _ => fail2!("Rng.fill_bytes: the impossible occurred: remaining != 1, 2 or 3") + } + } /// Return a random value of a Rand type. /// @@ -630,11 +693,9 @@ impl XorShiftRng { // specific size, so we can just use a fixed buffer. let mut s = [0u8, ..16]; loop { - do s.as_mut_buf |p, sz| { - unsafe { - rustrt::rand_gen_seed(p, sz as size_t); - } - } + let mut r = OSRng::new(); + r.fill_bytes(s); + if !s.iter().all(|x| *x == 0) { break; } @@ -660,15 +721,10 @@ impl XorShiftRng { /// Create a new random seed of length `n`. pub fn seed(n: uint) -> ~[u8] { - #[fixed_stack_segment]; #[inline(never)]; - - unsafe { - let mut s = vec::from_elem(n as uint, 0_u8); - do s.as_mut_buf |p, sz| { - rustrt::rand_gen_seed(p, sz as size_t) - } - s - } + let mut s = vec::from_elem(n as uint, 0_u8); + let mut r = OSRng::new(); + r.fill_bytes(s); + s } // used to make space in TLS for a random number generator @@ -719,6 +775,14 @@ mod test { use option::{Option, Some}; use super::*; + #[test] + fn test_fill_bytes_default() { + let mut r = weak_rng(); + + let mut v = [0u8, .. 100]; + r.fill_bytes(v); + } + #[test] fn test_gen_integer_range() { let mut r = rng(); diff --git a/src/libstd/rand/os.rs b/src/libstd/rand/os.rs new file mode 100644 index 0000000000000..0e6472b4d37fd --- /dev/null +++ b/src/libstd/rand/os.rs @@ -0,0 +1,193 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Interfaces to the operating system provided random number +//! generators. + +use rand::Rng; +use ops::Drop; + +#[cfg(unix)] +use rand::reader::ReaderRng; +#[cfg(unix)] +use rt::io::{file, Open, Read}; + +#[cfg(windows)] +use ptr; +#[cfg(windows)] +use cast; +#[cfg(windows)] +use libc::{GetLastError, FALSE}; + +/// A random number generator that retrieves randomness straight from +/// the operating system. On Unix-like systems this reads from +/// `/dev/urandom`, on Windows this uses `CryptGenRandom`. +/// +/// This does not block. +#[cfg(unix)] +pub struct OSRng { + priv inner: ReaderRng +} +/// A random number generator that retrieves randomness straight from +/// the operating system. On Unix-like systems this reads from +/// `/dev/urandom`, on Windows this uses `CryptGenRandom`. +/// +/// This does not block. +/// +/// XXX: it is unlikely that this is threadsafe with the use of +/// GetLastError. +#[cfg(windows)] +pub struct OSRng { + priv hcryptprov: raw::HCRYPTPROV +} + +impl OSRng { + /// Create a new `OSRng`. + #[cfg(unix)] + pub fn new() -> OSRng { + let reader = file::open(& &"/dev/urandom", Open, Read).expect("Error opening /dev/urandom"); + let reader_rng = ReaderRng::new(reader); + + OSRng { inner: reader_rng } + } + + /// Create a new `OSRng`. + #[cfg(windows)] + pub fn new() -> OSRng { + let hcp = ptr::mut_null(); + // TODO these two 0 constants are incorrect! + if unsafe { raw::CryptAcquireContext(hcp, ptr::null(), ptr::null(), 0, 0); } == FALSE { + fail!("CryptAcquireContext failed with error %u", unsafe {GetLastError()}) + } + + OSRng { hcryptprov: hcp } + } +} + +#[cfg(unix)] +impl Rng for OSRng { + fn next_u32(&mut self) -> u32 { + self.inner.next_u32() + } + fn next_u64(&mut self) -> u64 { + self.inner.next_u64() + } + fn fill_bytes(&mut self, v: &mut [u8]) { + self.inner.fill_bytes(v) + } +} + +#[cfg(windows)] +impl Rng for OSRng { + fn next_u32(&mut self) -> u32 { + let mut v = [0u8, .. 4]; + self.fill_bytes(v); + unsafe { cast::transmute(v) } + } + fn next_u64(&mut self) -> u64 { + let mut v = [0u8, .. 8]; + self.fill_bytes(v); + unsafe { cast::transmute(v) } + } + fn fill_bytes(&mut self, v: &mut [u8]) { + if unsafe { raw::CryptGenRandom(self.hcryptprov, v.len(), v.unsafe_mut_ref(0)) } == FALSE { + fail!("CryptGenRandom failed with error %u", unsafe {GetLastError()}) + } + } +} + +impl Drop for OSRng { + #[cfg(unix)] + fn drop(&mut self) { + // ensure that OSRng is not implicitly copyable on all + // platforms, for consistency. + } + + #[cfg(windows)] + fn drop(&mut self) { + // TODO this 0 means? + if unsafe { raw::CryptReleaseContext(self.hcryptprov, 0)} == FALSE { + fail!("CryptReleaseContext failed with error %u", unsafe {GetLastError()}) + } + } +} + +#[abi = "cdecl"] +#[cfg(windows)] +mod raw { + use libc::{LPCTSTR, DWORD, BOOL, BYTE}; + + enum HCRYPTPROV_opaque {} + pub type HCRYPTPROV = *CRYPTPROV; + extern { + pub fn CryptAcquireContext(phProv: *mut HCRYPTPROV, + pszContainer: LPCTSTR, pszProvider: LPCTSTR, + dwProvType: DWORD, dwFlags: DWORD) -> BOOL; + pub fn CryptGenRandom(hProv: HCRYPTPROV, dwLen: DWORD, pbBuffer: *mut BYTE) -> BOOL; + pub fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlags: DWORD) -> BOOL; + } +} + +#[cfg(test)] +mod test { + use super::*; + use rand::Rng; + + #[test] + fn test_os_rng() { + let mut r = OSRng::new(); + + r.next_u32(); + r.next_u64(); + + let mut v = [0u8, .. 1000]; + r.fill_bytes(v); + } + + #[test] + fn test_os_rng_tasks() { + use task; + use comm; + use comm::{GenericChan, GenericPort}; + use option::{None, Some}; + use iter::{Iterator, range}; + use vec::{ImmutableVector, OwnedVector}; + + let mut chans = ~[]; + for _ in range(0, 20) { + let (p, c) = comm::stream(); + chans.push(c); + do task::spawn_with(p) |p| { + // wait until all the tasks are ready to go. + p.recv(); + + // deschedule to attempt to interleave things as much + // as possible (XXX: is this a good test?) + let mut r = OSRng::new(); + task::deschedule(); + let mut v = [0u8, .. 1000]; + + for _ in range(0, 100) { + r.next_u32(); + task::deschedule(); + r.next_u64(); + task::deschedule(); + r.fill_bytes(v); + task::deschedule(); + } + } + } + + // start all the tasks + for c in chans.iter() { + c.send(()) + } + } +} diff --git a/src/libstd/rand/reader.rs b/src/libstd/rand/reader.rs new file mode 100644 index 0000000000000..f1164346fe606 --- /dev/null +++ b/src/libstd/rand/reader.rs @@ -0,0 +1,94 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rt::io::Reader; +use rt::io::ReaderByteConversions; + +use rand::Rng; + +/// An RNG that reads random bytes straight from a `Reader`. This will +/// work best with an infinite reader, but this is not required. The +/// semantics of reading past the end of the reader are the same as +/// those of the `read` method of the inner `Reader`. +pub struct ReaderRng { + priv reader: R +} + +impl ReaderRng { + /// Create a new `ReaderRng` from a `Reader`. + pub fn new(r: R) -> ReaderRng { + ReaderRng { + reader: r + } + } +} + +impl Rng for ReaderRng { + fn next_u32(&mut self) -> u32 { + // XXX which is better: consistency between big/little-endian + // platforms, or speed. + if cfg!(target_endian="little") { + self.reader.read_le_u32_() + } else { + self.reader.read_be_u32_() + } + } + fn next_u64(&mut self) -> u64 { + if cfg!(target_endian="little") { + self.reader.read_le_u64_() + } else { + self.reader.read_be_u64_() + } + } + fn fill_bytes(&mut self, v: &mut [u8]) { + // XXX: check that we filled `v`` + let _n = self.reader.read(v); + } +} + +#[cfg(test)] +mod test { + use super::*; + use rt::io::mem::MemReader; + use cast; + + #[test] + fn test_reader_rng_u64() { + // transmute from the target to avoid endianness concerns. + let v = ~[1u64, 2u64, 3u64]; + let bytes: ~[u8] = unsafe {cast::transmute(v)}; + let mut rng = ReaderRng::new(MemReader::new(bytes)); + + assert_eq!(rng.next_u64(), 1); + assert_eq!(rng.next_u64(), 2); + assert_eq!(rng.next_u64(), 3); + } + #[test] + fn test_reader_rng_u32() { + // transmute from the target to avoid endianness concerns. + let v = ~[1u32, 2u32, 3u32]; + let bytes: ~[u8] = unsafe {cast::transmute(v)}; + let mut rng = ReaderRng::new(MemReader::new(bytes)); + + assert_eq!(rng.next_u32(), 1); + assert_eq!(rng.next_u32(), 2); + assert_eq!(rng.next_u32(), 3); + } + #[test] + fn test_reader_rng_fill_bytes() { + let v = [1u8, 2, 3, 4, 5, 6, 7, 8]; + let mut w = [0u8, .. 8]; + + let mut rng = ReaderRng::new(MemReader::new(v.to_owned())); + rng.fill_bytes(w); + + assert_eq!(v, w); + } +} diff --git a/src/libstd/rt/sched.rs b/src/libstd/rt/sched.rs index cbffec51cc969..004dab8d73a23 100644 --- a/src/libstd/rt/sched.rs +++ b/src/libstd/rt/sched.rs @@ -140,7 +140,7 @@ impl Scheduler { cleanup_job: None, run_anything: run_anything, friend_handle: friend, - rng: XorShiftRng::new(), + rng: new_sched_rng(), idle_callback: None, yield_check_count: 0, steal_for_yield: false @@ -844,6 +844,60 @@ impl ClosureConverter for UnsafeTaskReceiver { fn to_fn(self) -> &fn(&mut Scheduler, ~Task) { unsafe { transmute(self) } } } +// On unix, we read randomness straight from /dev/urandom, but the +// default constructor of an XorShiftRng does this via io::file, which +// relies on the scheduler existing, so we have to manually load +// randomness. Windows has its own C API for this, so we don't need to +// worry there. +#[cfg(windows)] +fn new_sched_rng() -> XorShiftRng { + XorShiftRng::new() +} +#[cfg(unix)] +#[fixed_stack_segment] #[inline(never)] +fn new_sched_rng() -> XorShiftRng { + use libc; + use sys; + use c_str::ToCStr; + use ptr::RawPtr; + use vec::MutableVector; + use iter::Iterator; + + // XXX: this could use io::native::file, when it works. + let file = do "/dev/urandom".with_c_str |name| { + do "r".with_c_str |mode| { + unsafe { libc::fopen(name, mode) } + } + }; + if file.is_null() { + rtabort!("could not open /dev/urandom for reading.") + } + + let mut seeds = [0u32, .. 4]; + loop { + let nbytes = do seeds.as_mut_buf |buf, len| { + unsafe { + libc::fread(buf as *mut libc::c_void, + sys::size_of::() as libc::size_t, + len as libc::size_t, + file) + } + }; + rtassert!(nbytes == seeds.len() as libc::size_t); + + if !seeds.iter().all(|x| *x == 0) { + break; + } + } + + // XXX: do we need to guarantee that this is closed with a finally + // block (is that even possible without a scheduler?), or do we + // know that the only way that we can fail here is `abort`ing? + unsafe {libc::fclose(file);} + + XorShiftRng::new_seeded(seeds[0], seeds[1], seeds[2], seeds[3]) +} + #[cfg(test)] mod test { extern mod extra; From f39a215f270bc8c958a19cc9f693720232340cbc Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Thu, 26 Sep 2013 19:08:44 +1000 Subject: [PATCH 04/22] std::rand: add the StdRng wrapper for a blessed RNG. This is implemented as a wrapper around another RNG. It is designed to allow the actual implementation to be changed without changing the external API (e.g. it currently uses a 64-bit generator on 64- bit platforms, and a 32-bit one on 32-bit platforms; but one could imagine that the IsaacRng may be deprecated later, and having this ability to switch algorithms without having to update the points of use is convenient.) This is the recommended general use RNG. --- src/libstd/rand/mod.rs | 61 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 7 deletions(-) diff --git a/src/libstd/rand/mod.rs b/src/libstd/rand/mod.rs index e6bf42a5aedcd..4528174a212ba 100644 --- a/src/libstd/rand/mod.rs +++ b/src/libstd/rand/mod.rs @@ -643,8 +643,46 @@ pub trait Rng { /// available in Rust. If you require a specifically seeded `Rng` for /// consistency over time you should pick one algorithm and create the /// `Rng` yourself. -pub fn rng() -> IsaacRng { - IsaacRng::new() +/// +/// This is a very expensive operation as it has to read randomness +/// from the operating system and use this in an expensive seeding +/// operation. If one does not require high performance, `task_rng` +/// and/or `random` may be more appropriate. +pub fn rng() -> StdRng { + StdRng::new() +} + +/// The standard RNG. This is designed to be efficient on the current +/// platform. +#[cfg(not(target_word_size="64"))] +pub struct StdRng { priv rng: IsaacRng } + +/// The standard RNG. This is designed to be efficient on the current +/// platform. +#[cfg(target_word_size="64")] +pub struct StdRng { priv rng: Isaac64Rng } + +impl StdRng { + #[cfg(not(target_word_size="64"))] + fn new() -> StdRng { + StdRng { rng: IsaacRng::new() } + } + #[cfg(target_word_size="64")] + fn new() -> StdRng { + StdRng { rng: Isaac64Rng::new() } + } +} + +impl Rng for StdRng { + #[inline] + fn next_u32(&mut self) -> u32 { + self.rng.next_u32() + } + + #[inline] + fn next_u64(&mut self) -> u64 { + self.rng.next_u64() + } } /// Create a weak random number generator with a default algorithm and seed. @@ -728,7 +766,7 @@ pub fn seed(n: uint) -> ~[u8] { } // used to make space in TLS for a random number generator -local_data_key!(tls_rng_state: @@mut IsaacRng) +local_data_key!(tls_rng_state: @mut StdRng) /** * Gives back a lazily initialized task-local random number generator, @@ -736,15 +774,15 @@ local_data_key!(tls_rng_state: @@mut IsaacRng) * `task_rng().gen::()`. */ #[inline] -pub fn task_rng() -> @mut IsaacRng { +pub fn task_rng() -> @mut StdRng { let r = local_data::get(tls_rng_state, |k| k.map(|&k| *k)); match r { None => { - let rng = @@mut IsaacRng::new(); + let rng = @mut StdRng::new(); local_data::set(tls_rng_state, rng); - *rng + rng } - Some(rng) => *rng + Some(rng) => rng } } @@ -985,6 +1023,15 @@ mod bench { bh.bytes = size_of::() as u64; } + #[bench] + fn rand_std(bh: &mut BenchHarness) { + let mut rng = StdRng::new(); + do bh.iter { + rng.gen::(); + } + bh.bytes = size_of::() as u64; + } + #[bench] fn rand_shuffle_100(bh: &mut BenchHarness) { let mut rng = XorShiftRng::new(); From 0223cf65e41cc1046b306dcd0e3959a20729e0cf Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Sun, 29 Sep 2013 16:49:11 +1000 Subject: [PATCH 05/22] std::rand: Add ReseedingRng, which will reseed an RNG after it generates a certain number of bytes. It is an "RNG adaptor" and so any RNG can be wrapped to have this behaviour. --- src/libstd/rand/mod.rs | 1 + src/libstd/rand/reseeding.rs | 128 +++++++++++++++++++++++++++++++++++ 2 files changed, 129 insertions(+) create mode 100644 src/libstd/rand/reseeding.rs diff --git a/src/libstd/rand/mod.rs b/src/libstd/rand/mod.rs index 4528174a212ba..9fb6863692b43 100644 --- a/src/libstd/rand/mod.rs +++ b/src/libstd/rand/mod.rs @@ -62,6 +62,7 @@ pub mod distributions; pub mod isaac; pub mod os; pub mod reader; +pub mod reseeding; /// A type that can be randomly generated using an Rng pub trait Rand { diff --git a/src/libstd/rand/reseeding.rs b/src/libstd/rand/reseeding.rs new file mode 100644 index 0000000000000..d471a5e4e10eb --- /dev/null +++ b/src/libstd/rand/reseeding.rs @@ -0,0 +1,128 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! A wrapper around another RNG that reseeds it after it +//! generates a certain number of random bytes. + +use rand::Rng; +use default::Default; + +/// How many bytes of entropy the underling RNG is allowed to generate +/// before it is reseeded. +static DEFAULT_GENERATION_THRESHOLD: uint = 32 * 1024; + +/// A wrapper around any RNG which reseeds the underlying RNG after it +/// has generated a certain number of random bytes. +pub struct ReseedingRng { + priv rng: R, + priv generation_threshold: uint, + priv bytes_generated: uint, + /// Controls the behaviour when reseeding the RNG. + reseeder: Rsdr +} + +impl> ReseedingRng { + /// Create a new `ReseedingRng` with the given parameters. + /// + /// # Arguments + /// + /// * `rng`: the random number generator to use. + /// * `generation_threshold`: the number of bytes of entropy at which to reseed the RNG. + /// * `reseeder`: the reseeding object to use. + pub fn new(rng: R, generation_threshold: uint, reseeder: Rsdr) -> ReseedingRng { + ReseedingRng { + rng: rng, + generation_threshold: generation_threshold, + bytes_generated: 0, + reseeder: reseeder + } + } + + /// Reseed the internal RNG if the number of bytes that have been + /// generated exceed the threshold. + pub fn reseed_if_necessary(&mut self) { + if self.bytes_generated >= self.generation_threshold { + self.reseeder.reseed(&mut self.rng); + self.bytes_generated = 0; + } + } +} + + +impl> Rng for ReseedingRng { + fn next_u32(&mut self) -> u32 { + self.reseed_if_necessary(); + self.bytes_generated += 4; + self.rng.next_u32() + } + + fn next_u64(&mut self) -> u64 { + self.reseed_if_necessary(); + self.bytes_generated += 8; + self.rng.next_u64() + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + self.reseed_if_necessary(); + self.bytes_generated += dest.len(); + self.fill_bytes(dest) + } +} + +/// Something that can be used to reseed an RNG via `ReseedingRng`. +pub trait Reseeder { + /// Reseed the given RNG. + fn reseed(&mut self, rng: &mut R); +} + +/// Reseed an RNG using a `Default` instance. This reseeds by +/// replacing the RNG with the result of a `Default::default` call. +pub struct ReseedWithDefault; + +impl Reseeder for ReseedWithDefault { + fn reseed(&mut self, rng: &mut R) { + *rng = Default::default(); + } +} + +#[cfg(test)] +mod test { + use super::*; + use rand::Rng; + use default::Default; + + struct Counter { + i: u32 + } + + impl Rng for Counter { + fn next_u32(&mut self) -> u32 { + self.i += 1; + // very random + self.i - 1 + } + } + impl Default for Counter { + fn default() -> Counter { + Counter { i: 0 } + } + } + + #[test] + fn test_reseeding() { + let mut rs = ReseedingRng::from_options(Counter {i:0}, 100, ReseedWithDefault); + + let mut i = 0; + for _ in range(0, 1000) { + assert_eq!(rs.next_u32(), i % 100); + i += 1; + } + } +} From 92725ae765a75b139d5d5fc27305f4436dbb456e Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Mon, 30 Sep 2013 01:29:28 +1000 Subject: [PATCH 06/22] std::rand: Add a trait for seeding RNGs: SeedableRng. This provides 2 methods: .reseed() and ::from_seed that modify and create respecitively. Implement this trait for the RNGs in the stdlib for which this makes sense. --- src/libextra/bitv.rs | 4 +- src/libextra/treemap.rs | 2 +- src/libstd/rand/isaac.rs | 213 ++++++++++++++++++++++------------- src/libstd/rand/mod.rs | 106 +++++++++++++---- src/libstd/rand/reseeding.rs | 4 +- src/libstd/rt/sched.rs | 4 +- src/test/bench/core-map.rs | 4 +- src/test/bench/core-set.rs | 8 +- 8 files changed, 232 insertions(+), 113 deletions(-) diff --git a/src/libextra/bitv.rs b/src/libextra/bitv.rs index 98f69425aee37..4f8cd4d330883 100644 --- a/src/libextra/bitv.rs +++ b/src/libextra/bitv.rs @@ -1524,8 +1524,8 @@ mod tests { } fn rng() -> rand::IsaacRng { - let seed = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]; - rand::IsaacRng::new_seeded(seed) + let seed = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]; + rand::SeedableRng::from_seed(seed) } #[bench] diff --git a/src/libextra/treemap.rs b/src/libextra/treemap.rs index ee7ba4a888b86..66e1fd0c2c3dc 100644 --- a/src/libextra/treemap.rs +++ b/src/libextra/treemap.rs @@ -1013,7 +1013,7 @@ mod test_treemap { check_equal(ctrl, &map); assert!(map.find(&5).is_none()); - let mut rng = rand::IsaacRng::new_seeded(&[42]); + let mut rng: rand::IsaacRng = rand::SeedableRng::from_seed(&[42]); do 3.times { do 90.times { diff --git a/src/libstd/rand/isaac.rs b/src/libstd/rand/isaac.rs index 534ebfb473b94..6d338a922beca 100644 --- a/src/libstd/rand/isaac.rs +++ b/src/libstd/rand/isaac.rs @@ -10,15 +10,10 @@ //! The ISAAC random number generator. -use rand::{seed, Rng}; -use iter::{Iterator, range, range_step}; +use rand::{seed, Rng, SeedableRng}; +use iter::{Iterator, range, range_step, Repeat}; use option::{None, Some}; -use cast; -use cmp; -use sys; -use vec; - static RAND_SIZE_LEN: u32 = 8; static RAND_SIZE: u32 = 1 << RAND_SIZE_LEN; @@ -38,30 +33,8 @@ pub struct IsaacRng { impl IsaacRng { /// Create an ISAAC random number generator with a random seed. pub fn new() -> IsaacRng { - IsaacRng::new_seeded(seed(RAND_SIZE as uint * 4)) - } - - /// Create an ISAAC random number generator with a seed. This can be any - /// length, although the maximum number of bytes used is 1024 and any more - /// will be silently ignored. A generator constructed with a given seed - /// will generate the same sequence of values as all other generators - /// constructed with the same seed. - pub fn new_seeded(seed: &[u8]) -> IsaacRng { - let mut rng = IsaacRng { - cnt: 0, - rsl: [0, .. RAND_SIZE], - mem: [0, .. RAND_SIZE], - a: 0, b: 0, c: 0 - }; - - let array_size = sys::size_of_val(&rng.rsl); - let copy_length = cmp::min(array_size, seed.len()); - - // manually create a &mut [u8] slice of randrsl to copy into. - let dest = unsafe { cast::transmute((&mut rng.rsl, array_size)) }; - vec::bytes::copy_memory(dest, seed, copy_length); - rng.init(true); - rng + let s = unsafe {seed::(RAND_SIZE as uint)}; + SeedableRng::from_seed(s.as_slice()) } /// Create an ISAAC random number generator using the default @@ -197,6 +170,43 @@ impl Rng for IsaacRng { } } +impl<'self> SeedableRng<&'self [u32]> for IsaacRng { + fn reseed(&mut self, seed: &'self [u32]) { + // make the seed into [seed[0], seed[1], ..., seed[seed.len() + // - 1], 0, 0, ...], to fill rng.rsl. + let seed_iter = seed.iter().map(|&x| x).chain(Repeat::new(0u32)); + + for (rsl_elem, seed_elem) in self.rsl.mut_iter().zip(seed_iter) { + *rsl_elem = seed_elem; + } + self.cnt = 0; + self.a = 0; + self.b = 0; + self.c = 0; + + self.init(true); + } + + /// Create an ISAAC random number generator with a seed. This can + /// be any length, although the maximum number of elements used is + /// 256 and any more will be silently ignored. A generator + /// constructed with a given seed will generate the same sequence + /// of values as all other generators constructed with that seed. + fn from_seed(seed: &'self [u32]) -> IsaacRng { + let mut rng = IsaacRng { + cnt: 0, + rsl: [0, .. RAND_SIZE], + mem: [0, .. RAND_SIZE], + a: 0, b: 0, c: 0 + }; + + rng.reseed(seed); + + rng + } +} + + static RAND_SIZE_64_LEN: uint = 8; static RAND_SIZE_64: uint = 1 << RAND_SIZE_64_LEN; @@ -218,31 +228,8 @@ impl Isaac64Rng { /// Create a 64-bit ISAAC random number generator with a random /// seed. pub fn new() -> Isaac64Rng { - Isaac64Rng::new_seeded(seed(RAND_SIZE_64 as uint * 8)) - } - - /// Create a 64-bit ISAAC random number generator with a - /// seed. This can be any length, although the maximum number of - /// bytes used is 2048 and any more will be silently ignored. A - /// generator constructed with a given seed will generate the same - /// sequence of values as all other generators constructed with - /// the same seed. - pub fn new_seeded(seed: &[u8]) -> Isaac64Rng { - let mut rng = Isaac64Rng { - cnt: 0, - rsl: [0, .. RAND_SIZE_64], - mem: [0, .. RAND_SIZE_64], - a: 0, b: 0, c: 0, - }; - - let array_size = sys::size_of_val(&rng.rsl); - let copy_length = cmp::min(array_size, seed.len()); - - // manually create a &mut [u8] slice of randrsl to copy into. - let dest = unsafe { cast::transmute((&mut rng.rsl, array_size)) }; - vec::bytes::copy_memory(dest, seed, copy_length); - rng.init(true); - rng + let s = unsafe {seed::(RAND_SIZE_64)}; + SeedableRng::from_seed(s.as_slice()) } /// Create a 64-bit ISAAC random number generator using the @@ -378,22 +365,58 @@ impl Rng for Isaac64Rng { } } +impl<'self> SeedableRng<&'self [u64]> for Isaac64Rng { + fn reseed(&mut self, seed: &'self [u64]) { + // make the seed into [seed[0], seed[1], ..., seed[seed.len() + // - 1], 0, 0, ...], to fill rng.rsl. + let seed_iter = seed.iter().map(|&x| x).chain(Repeat::new(0u64)); + + for (rsl_elem, seed_elem) in self.rsl.mut_iter().zip(seed_iter) { + *rsl_elem = seed_elem; + } + self.cnt = 0; + self.a = 0; + self.b = 0; + self.c = 0; + + self.init(true); + } + + /// Create an ISAAC random number generator with a seed. This can + /// be any length, although the maximum number of elements used is + /// 256 and any more will be silently ignored. A generator + /// constructed with a given seed will generate the same sequence + /// of values as all other generators constructed with that seed. + fn from_seed(seed: &'self [u64]) -> Isaac64Rng { + let mut rng = Isaac64Rng { + cnt: 0, + rsl: [0, .. RAND_SIZE_64], + mem: [0, .. RAND_SIZE_64], + a: 0, b: 0, c: 0, + }; + rng.reseed(seed); + rng + } +} + #[cfg(test)] mod test { use super::*; - use rand::{Rng, seed}; - use option::{Option, Some}; + use rand::{Rng, SeedableRng, seed}; + use option::Some; + use iter::range; + use vec; #[test] fn test_rng_seeded() { - let seed = seed(1024); - let mut ra = IsaacRng::new_seeded(seed); - let mut rb = IsaacRng::new_seeded(seed); + let s = unsafe {seed::(256)}; + let mut ra: IsaacRng = SeedableRng::from_seed(s.as_slice()); + let mut rb: IsaacRng = SeedableRng::from_seed(s.as_slice()); assert_eq!(ra.gen_ascii_str(100u), rb.gen_ascii_str(100u)); - let seed = seed(2048); - let mut ra = Isaac64Rng::new_seeded(seed); - let mut rb = Isaac64Rng::new_seeded(seed); + let s = unsafe {seed::(256)}; + let mut ra: Isaac64Rng = SeedableRng::from_seed(s.as_slice()); + let mut rb: Isaac64Rng = SeedableRng::from_seed(s.as_slice()); assert_eq!(ra.gen_ascii_str(100u), rb.gen_ascii_str(100u)); } @@ -401,29 +424,59 @@ mod test { fn test_rng_seeded_custom_seed() { // much shorter than generated seeds which are 1024 & 2048 // bytes resp. - let seed = [2u8, 32u8, 4u8, 32u8, 51u8]; - let mut ra = IsaacRng::new_seeded(seed); - let mut rb = IsaacRng::new_seeded(seed); + let seed = &[2, 32, 4, 32, 51]; + let mut ra: IsaacRng = SeedableRng::from_seed(seed); + let mut rb: IsaacRng = SeedableRng::from_seed(seed); assert_eq!(ra.gen_ascii_str(100u), rb.gen_ascii_str(100u)); - let mut ra = Isaac64Rng::new_seeded(seed); - let mut rb = Isaac64Rng::new_seeded(seed); + let seed = &[2, 32, 4, 32, 51]; + let mut ra: Isaac64Rng = SeedableRng::from_seed(seed); + let mut rb: Isaac64Rng = SeedableRng::from_seed(seed); assert_eq!(ra.gen_ascii_str(100u), rb.gen_ascii_str(100u)); } #[test] - fn test_rng_seeded_custom_seed2() { - let seed = [2u8, 32u8, 4u8, 32u8, 51u8]; - let mut ra = IsaacRng::new_seeded(seed); + fn test_rng_32_true_values() { + let seed = &[2, 32, 4, 32, 51]; + let mut ra: IsaacRng = SeedableRng::from_seed(seed); // Regression test that isaac is actually using the above vector - let r = ra.next_u32(); - error2!("{:?}", r); - assert_eq!(r, 2935188040u32); - - let mut ra = Isaac64Rng::new_seeded(seed); + let v = vec::from_fn(10, |_| ra.next_u32()); + assert_eq!(v, + ~[447462228, 2081944040, 3163797308, 2379916134, 2377489184, + 1132373754, 536342443, 2995223415, 1265094839, 345325140]); + + let seed = &[500, -4000, 123456, 9876543, 1, 1, 1, 1, 1]; + let mut rb: IsaacRng = SeedableRng::from_seed(seed); + // skip forward to the 10000th number + for _ in range(0, 10000) { rb.next_u32(); } + + let v = vec::from_fn(10, |_| rb.next_u32()); + assert_eq!(v, + ~[612373032, 292987903, 1819311337, 3141271980, 422447569, + 310096395, 1083172510, 867909094, 2478664230, 2073577855]); + } + #[test] + fn test_rng_64_true_values() { + let seed = &[2, 32, 4, 32, 51]; + let mut ra: Isaac64Rng = SeedableRng::from_seed(seed); // Regression test that isaac is actually using the above vector - let r = ra.next_u64(); - error2!("{:?}", r); - assert!(r == 0 && r == 1); // FIXME: find true value + let v = vec::from_fn(10, |_| ra.next_u64()); + assert_eq!(v, + ~[15015576812873463115, 12461067598045625862, 14818626436142668771, + 5562406406765984441, 11813289907965514161, 13443797187798420053, + 6935026941854944442, 7750800609318664042, 14428747036317928637, + 14028894460301215947]); + + let seed = &[500, -4000, 123456, 9876543, 1, 1, 1, 1, 1]; + let mut rb: Isaac64Rng = SeedableRng::from_seed(seed); + // skip forward to the 10000th number + for _ in range(0, 10000) { rb.next_u64(); } + + let v = vec::from_fn(10, |_| rb.next_u64()); + assert_eq!(v, + ~[13557216323596688637, 17060829581390442094, 4927582063811333743, + 2699639759356482270, 4819341314392384881, 6047100822963614452, + 11086255989965979163, 11901890363215659856, 5370800226050011580, + 16496463556025356451]); } } diff --git a/src/libstd/rand/mod.rs b/src/libstd/rand/mod.rs index 9fb6863692b43..64f266d00452b 100644 --- a/src/libstd/rand/mod.rs +++ b/src/libstd/rand/mod.rs @@ -638,6 +638,42 @@ pub trait Rng { } } +/// A random number generator that can be explicitly seeded to produce +/// the same stream of randomness multiple times. +pub trait SeedableRng: Rng { + /// Reseed an RNG with the given seed. + /// + /// # Example + /// + /// ```rust + /// use std::rand; + /// use std::rand::Rng; + /// + /// fn main() { + /// let mut rng: rand::XorShiftRng = rand::SeedableRng::from_seed(&[1, 2, 3, 4]); + /// println!("{}", rng.gen::()); + /// rng.reseed([5, 6, 7, 8]); + /// println!("{}", rng.gen::()); + /// } + /// ``` + fn reseed(&mut self, Seed); + + /// Create a new RNG with the given seed. + /// + /// # Example + /// + /// ```rust + /// use std::rand; + /// use std::rand::Rng; + /// + /// fn main() { + /// let mut rng: rand::XorShiftRng = rand::SeedableRng::from_seed(&[1, 2, 3, 4]); + /// println!("{}", rng.gen::()); + /// } + /// ``` + fn from_seed(seed: Seed) -> Self; +} + /// Create a random number generator with a default algorithm and seed. /// /// It returns the cryptographically-safest `Rng` algorithm currently @@ -686,6 +722,18 @@ impl Rng for StdRng { } } +impl<'self> SeedableRng<&'self [uint]> for StdRng { + fn reseed(&mut self, seed: &'self [uint]) { + // the internal RNG can just be seeded from the above + // randomness. + self.rng.reseed(unsafe {cast::transmute(seed)}) + } + + fn from_seed(seed: &'self [uint]) -> StdRng { + StdRng { rng: SeedableRng::from_seed(unsafe {cast::transmute(seed)}) } + } +} + /// Create a weak random number generator with a default algorithm and seed. /// /// It returns the fastest `Rng` algorithm currently available in Rust without @@ -723,11 +771,35 @@ impl Rng for XorShiftRng { } } +impl SeedableRng<[u32, .. 4]> for XorShiftRng { + /// Reseed an XorShiftRng. This will fail if `seed` is entirely 0. + fn reseed(&mut self, seed: [u32, .. 4]) { + assert!(!seed.iter().all(|&x| x == 0), + "XorShiftRng.reseed called with an all zero seed."); + + self.x = seed[0]; + self.y = seed[1]; + self.z = seed[2]; + self.w = seed[3]; + } + + /// Create a new XorShiftRng. This will fail if `seed` is entirely 0. + fn from_seed(seed: [u32, .. 4]) -> XorShiftRng { + assert!(!seed.iter().all(|&x| x == 0), + "XorShiftRng::from_seed called with an all zero seed."); + + XorShiftRng { + x: seed[0], + y: seed[1], + z: seed[2], + w: seed[3] + } + } +} + impl XorShiftRng { /// Create an xor shift random number generator with a random seed. pub fn new() -> XorShiftRng { - #[fixed_stack_segment]; #[inline(never)]; - // generate seeds the same way as seed(), except we have a // specific size, so we can just use a fixed buffer. let mut s = [0u8, ..16]; @@ -740,29 +812,21 @@ impl XorShiftRng { } } let s: &[u32, ..4] = unsafe { cast::transmute(&s) }; - XorShiftRng::new_seeded(s[0], s[1], s[2], s[3]) - } - - /** - * Create a random number generator using the specified seed. A generator - * constructed with a given seed will generate the same sequence of values - * as all other generators constructed with the same seed. - */ - pub fn new_seeded(x: u32, y: u32, z: u32, w: u32) -> XorShiftRng { - XorShiftRng { - x: x, - y: y, - z: z, - w: w, - } + SeedableRng::from_seed(*s) } } -/// Create a new random seed of length `n`. -pub fn seed(n: uint) -> ~[u8] { - let mut s = vec::from_elem(n as uint, 0_u8); +/// Create a new random seed of length `n`. This should only be used +/// to create types for which *any* bit pattern is valid. +pub unsafe fn seed(n: uint) -> ~[T] { + use unstable::intrinsics; + let mut s = vec::from_elem(n, intrinsics::init()); let mut r = OSRng::new(); - r.fill_bytes(s); + + { + let s_u8 = cast::transmute::<&mut [T], &mut [u8]>(s); + r.fill_bytes(s_u8); + } s } diff --git a/src/libstd/rand/reseeding.rs b/src/libstd/rand/reseeding.rs index d471a5e4e10eb..727efff0101f0 100644 --- a/src/libstd/rand/reseeding.rs +++ b/src/libstd/rand/reseeding.rs @@ -97,6 +97,8 @@ mod test { use super::*; use rand::Rng; use default::Default; + use iter::range; + use option::{None, Some}; struct Counter { i: u32 @@ -117,7 +119,7 @@ mod test { #[test] fn test_reseeding() { - let mut rs = ReseedingRng::from_options(Counter {i:0}, 100, ReseedWithDefault); + let mut rs = ReseedingRng::new(Counter {i:0}, 400, ReseedWithDefault); let mut i = 0; for _ in range(0, 1000) { diff --git a/src/libstd/rt/sched.rs b/src/libstd/rt/sched.rs index 004dab8d73a23..c1ca484754d20 100644 --- a/src/libstd/rt/sched.rs +++ b/src/libstd/rt/sched.rs @@ -26,7 +26,7 @@ use rt::local::Local; use rt::rtio::{RemoteCallback, PausibleIdleCallback}; use borrow::{to_uint}; use cell::Cell; -use rand::{XorShiftRng, Rng, Rand}; +use rand::{SeedableRng, XorShiftRng, Rng, Rand}; use iter::range; use vec::{OwnedVector}; @@ -895,7 +895,7 @@ fn new_sched_rng() -> XorShiftRng { // know that the only way that we can fail here is `abort`ing? unsafe {libc::fclose(file);} - XorShiftRng::new_seeded(seeds[0], seeds[1], seeds[2], seeds[3]) + SeedableRng::from_seed(seeds) } #[cfg(test)] diff --git a/src/test/bench/core-map.rs b/src/test/bench/core-map.rs index bec11ff3a7d1d..2d209e40e4249 100644 --- a/src/test/bench/core-map.rs +++ b/src/test/bench/core-map.rs @@ -15,7 +15,7 @@ use extra::treemap::TreeMap; use std::hashmap::{HashMap, HashSet}; use std::io; use std::os; -use std::rand::Rng; +use std::rand::{Rng, IsaacRng, SeedableRng}; use std::trie::TrieMap; use std::uint; use std::vec; @@ -106,7 +106,7 @@ fn main() { let mut rand = vec::with_capacity(n_keys); { - let mut rng = std::rand::IsaacRng::new_seeded([1, 1, 1, 1, 1, 1, 1]); + let mut rng: IsaacRng = SeedableRng::from_seed(&[1, 1, 1, 1, 1, 1, 1]); let mut set = HashSet::new(); while set.len() != n_keys { let next = rng.gen(); diff --git a/src/test/bench/core-set.rs b/src/test/bench/core-set.rs index 36ebdc549227a..b9a8e74668fa9 100644 --- a/src/test/bench/core-set.rs +++ b/src/test/bench/core-set.rs @@ -163,11 +163,11 @@ fn main() { } }; - let seed = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + let seed = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; let max = 200000; { - let mut rng = rand::IsaacRng::new_seeded(seed); + let mut rng: rand::IsaacRng = rand::SeedableRng::from_seed(seed); let mut results = empty_results(); results.bench_int(&mut rng, num_keys, max, || { let s: HashSet = HashSet::new(); @@ -181,7 +181,7 @@ fn main() { } { - let mut rng = rand::IsaacRng::new_seeded(seed); + let mut rng: rand::IsaacRng = rand::SeedableRng::from_seed(seed); let mut results = empty_results(); results.bench_int(&mut rng, num_keys, max, || { let s: TreeSet = TreeSet::new(); @@ -195,7 +195,7 @@ fn main() { } { - let mut rng = rand::IsaacRng::new_seeded(seed); + let mut rng: rand::IsaacRng = rand::SeedableRng::from_seed(seed); let mut results = empty_results(); results.bench_int(&mut rng, num_keys, max, || BitvSet::new()); write_results("extra::bitv::BitvSet", &results); From fb9706338d56599ea3073b5f8e93c2e769431a48 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Tue, 1 Oct 2013 01:32:12 +1000 Subject: [PATCH 07/22] 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 --- src/libstd/rand/mod.rs | 135 ++++++++++++++++++++++++++++++----- src/libstd/rand/reseeding.rs | 19 ++++- 2 files changed, 134 insertions(+), 20 deletions(-) diff --git a/src/libstd/rand/mod.rs b/src/libstd/rand/mod.rs index 64f266d00452b..7f37077d5bb70 100644 --- a/src/libstd/rand/mod.rs +++ b/src/libstd/rand/mod.rs @@ -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(n: uint) -> ~[T] { s } +/// 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 +} + +impl Default for TaskRngReseeder { + fn default() -> TaskRngReseeder { WithNew } +} + +impl reseeding::Reseeder 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; + // used to make space in TLS for a random number generator -local_data_key!(tls_rng_state: @mut StdRng) +local_data_key!(TASK_RNG_KEY: @mut TaskRng) -/** - * 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::()`. - */ -#[inline] -pub fn task_rng() -> @mut StdRng { - let r = local_data::get(tls_rng_state, |k| k.map(|&k| *k)); +/// 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::()`. +/// +/// 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::(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::()); +/// +/// rand::seed_task_rng(&[1u, 2, 3, 4, 5, 6, 7, 8]); +/// printfln!("Same every time: %f", rand::random::()); +/// } +/// ``` +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 Rng for @mut R { #[inline] @@ -863,10 +937,23 @@ impl 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::()); +/// } +/// } +/// ``` #[inline] pub fn random() -> 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::(); + + seed_task_rng([1]); + let second = random::(); + assert_eq!(first, second); + } } #[cfg(test)] diff --git a/src/libstd/rand/reseeding.rs b/src/libstd/rand/reseeding.rs index 727efff0101f0..b6c880841a147 100644 --- a/src/libstd/rand/reseeding.rs +++ b/src/libstd/rand/reseeding.rs @@ -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> Rng for ReseedingRng { } } +impl, Rsdr: Reseeder + Default> SeedableRng for ReseedingRng { + 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 { + 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 { /// Reseed the given RNG. From 29e3b33a0954f2494278d74344792f4b84a44120 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Wed, 2 Oct 2013 02:18:57 +1000 Subject: [PATCH 08/22] std::rand: make the windows OSRng more correct, remove some C++. This lets the C++ code in the rt handle the (slightly) tricky parts of random number generation: e.g. error detection/handling, and using the values of the `#define`d options to the various functions. --- mk/rt.mk | 1 - src/libstd/rand/os.rs | 45 +++++++++------------ src/libstd/rand/reader.rs | 38 ++++++++++++++---- src/libstd/rt/sched.rs | 3 +- src/rt/rust_builtin.cpp | 62 ++++++++++++++++++++++++++--- src/rt/rust_rng.cpp | 83 --------------------------------------- src/rt/rust_rng.h | 26 ------------ src/rt/rustrt.def.in | 4 +- 8 files changed, 110 insertions(+), 152 deletions(-) delete mode 100644 src/rt/rust_rng.cpp delete mode 100644 src/rt/rust_rng.h diff --git a/mk/rt.mk b/mk/rt.mk index 5d75b9615108d..412e25bed4de2 100644 --- a/mk/rt.mk +++ b/mk/rt.mk @@ -86,7 +86,6 @@ RUNTIME_CXXS_$(1)_$(2) := \ rt/sync/lock_and_signal.cpp \ rt/sync/rust_thread.cpp \ rt/rust_builtin.cpp \ - rt/rust_rng.cpp \ rt/rust_upcall.cpp \ rt/rust_uv.cpp \ rt/miniz.cpp \ diff --git a/src/libstd/rand/os.rs b/src/libstd/rand/os.rs index 0e6472b4d37fd..7a4e0aa95c6db 100644 --- a/src/libstd/rand/os.rs +++ b/src/libstd/rand/os.rs @@ -19,12 +19,8 @@ use rand::reader::ReaderRng; #[cfg(unix)] use rt::io::{file, Open, Read}; -#[cfg(windows)] -use ptr; #[cfg(windows)] use cast; -#[cfg(windows)] -use libc::{GetLastError, FALSE}; /// A random number generator that retrieves randomness straight from /// the operating system. On Unix-like systems this reads from @@ -40,9 +36,6 @@ pub struct OSRng { /// `/dev/urandom`, on Windows this uses `CryptGenRandom`. /// /// This does not block. -/// -/// XXX: it is unlikely that this is threadsafe with the use of -/// GetLastError. #[cfg(windows)] pub struct OSRng { priv hcryptprov: raw::HCRYPTPROV @@ -60,12 +53,10 @@ impl OSRng { /// Create a new `OSRng`. #[cfg(windows)] + #[fixed_stack_segment] #[inline(never)] pub fn new() -> OSRng { - let hcp = ptr::mut_null(); - // TODO these two 0 constants are incorrect! - if unsafe { raw::CryptAcquireContext(hcp, ptr::null(), ptr::null(), 0, 0); } == FALSE { - fail!("CryptAcquireContext failed with error %u", unsafe {GetLastError()}) - } + let mut hcp = 0; + unsafe {raw::rust_win32_rand_acquire(&mut hcp)}; OSRng { hcryptprov: hcp } } @@ -96,9 +87,12 @@ impl Rng for OSRng { self.fill_bytes(v); unsafe { cast::transmute(v) } } + #[fixed_stack_segment] #[inline(never)] fn fill_bytes(&mut self, v: &mut [u8]) { - if unsafe { raw::CryptGenRandom(self.hcryptprov, v.len(), v.unsafe_mut_ref(0)) } == FALSE { - fail!("CryptGenRandom failed with error %u", unsafe {GetLastError()}) + use libc::DWORD; + + do v.as_mut_buf |ptr, len| { + unsafe {raw::rust_win32_rand_gen(self.hcryptprov, len as DWORD, ptr)} } } } @@ -111,27 +105,24 @@ impl Drop for OSRng { } #[cfg(windows)] + #[fixed_stack_segment] #[inline(never)] fn drop(&mut self) { - // TODO this 0 means? - if unsafe { raw::CryptReleaseContext(self.hcryptprov, 0)} == FALSE { - fail!("CryptReleaseContext failed with error %u", unsafe {GetLastError()}) - } + unsafe {raw::rust_win32_rand_release(self.hcryptprov)} } } -#[abi = "cdecl"] #[cfg(windows)] mod raw { - use libc::{LPCTSTR, DWORD, BOOL, BYTE}; + use libc::{c_long, DWORD, BYTE}; + + pub type HCRYPTPROV = c_long; - enum HCRYPTPROV_opaque {} - pub type HCRYPTPROV = *CRYPTPROV; + // these functions are implemented so that they either succeed or + // abort(), so we can just assume they work when we call them. extern { - pub fn CryptAcquireContext(phProv: *mut HCRYPTPROV, - pszContainer: LPCTSTR, pszProvider: LPCTSTR, - dwProvType: DWORD, dwFlags: DWORD) -> BOOL; - pub fn CryptGenRandom(hProv: HCRYPTPROV, dwLen: DWORD, pbBuffer: *mut BYTE) -> BOOL; - pub fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlags: DWORD) -> BOOL; + pub fn rust_win32_rand_acquire(phProv: *mut HCRYPTPROV); + pub fn rust_win32_rand_gen(hProv: HCRYPTPROV, dwLen: DWORD, pbBuffer: *mut BYTE); + pub fn rust_win32_rand_release(hProv: HCRYPTPROV); } } diff --git a/src/libstd/rand/reader.rs b/src/libstd/rand/reader.rs index f1164346fe606..32a97100fa1f6 100644 --- a/src/libstd/rand/reader.rs +++ b/src/libstd/rand/reader.rs @@ -8,15 +8,28 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use option::{Some, None}; use rt::io::Reader; use rt::io::ReaderByteConversions; use rand::Rng; /// An RNG that reads random bytes straight from a `Reader`. This will -/// work best with an infinite reader, but this is not required. The -/// semantics of reading past the end of the reader are the same as -/// those of the `read` method of the inner `Reader`. +/// work best with an infinite reader, but this is not required. +/// +/// It will fail if it there is insufficient data to fulfill a request. +/// +/// # Example +/// +/// ```rust +/// use std::rand::reader; +/// use std::rt::io::mem; +/// +/// fn main() { +/// let mut rng = reader::ReaderRng::new(mem::MemReader::new(~[1,2,3,4,5,6,7,8])); +/// println!("{}", rng.gen::()); +/// } +/// ``` pub struct ReaderRng { priv reader: R } @@ -32,8 +45,6 @@ impl ReaderRng { impl Rng for ReaderRng { fn next_u32(&mut self) -> u32 { - // XXX which is better: consistency between big/little-endian - // platforms, or speed. if cfg!(target_endian="little") { self.reader.read_le_u32_() } else { @@ -48,8 +59,13 @@ impl Rng for ReaderRng { } } fn fill_bytes(&mut self, v: &mut [u8]) { - // XXX: check that we filled `v`` - let _n = self.reader.read(v); + if v.len() == 0 { return } + match self.reader.read(v) { + Some(n) if n == v.len() => return, + Some(n) => fail2!("ReaderRng.fill_bytes could not fill buffer: \ + read {} out of {} bytes.", n, v.len()), + None => fail2!("ReaderRng.fill_bytes reached eof.") + } } } @@ -91,4 +107,12 @@ mod test { assert_eq!(v, w); } + + #[test] + #[should_fail] + fn test_reader_rng_insufficient_bytes() { + let mut rng = ReaderRng::new(MemReader::new(~[])); + let mut v = [0u8, .. 3]; + rng.fill_bytes(v); + } } diff --git a/src/libstd/rt/sched.rs b/src/libstd/rt/sched.rs index c1ca484754d20..618921f8e080b 100644 --- a/src/libstd/rt/sched.rs +++ b/src/libstd/rt/sched.rs @@ -26,7 +26,7 @@ use rt::local::Local; use rt::rtio::{RemoteCallback, PausibleIdleCallback}; use borrow::{to_uint}; use cell::Cell; -use rand::{SeedableRng, XorShiftRng, Rng, Rand}; +use rand::{XorShiftRng, Rng, Rand}; use iter::range; use vec::{OwnedVector}; @@ -862,6 +862,7 @@ fn new_sched_rng() -> XorShiftRng { use ptr::RawPtr; use vec::MutableVector; use iter::Iterator; + use rand::SeedableRng; // XXX: this could use io::native::file, when it works. let file = do "/dev/urandom".with_c_str |name| { diff --git a/src/rt/rust_builtin.cpp b/src/rt/rust_builtin.cpp index 34d1efd577d48..d1d4286a7c629 100644 --- a/src/rt/rust_builtin.cpp +++ b/src/rt/rust_builtin.cpp @@ -15,7 +15,6 @@ #include "sync/lock_and_signal.h" #include "memory_region.h" #include "boxed_region.h" -#include "rust_rng.h" #include "vg/valgrind.h" #include "sp.h" @@ -69,11 +68,6 @@ rust_env_pairs() { } #endif -extern "C" CDECL void -rand_gen_seed(uint8_t* dest, size_t size) { - rng_gen_seed(dest, size); -} - extern "C" CDECL char* #if defined(__WIN32__) rust_list_dir_val(WIN32_FIND_DATA* entry_ptr) { @@ -654,6 +648,62 @@ rust_unset_sigprocmask() { #endif +#if defined(__WIN32__) +void +win32_require(LPCTSTR fn, BOOL ok) { + if (!ok) { + LPTSTR buf; + DWORD err = GetLastError(); + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, err, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &buf, 0, NULL ); + fprintf(stderr, "%s failed with error %ld: %s", fn, err, buf); + LocalFree((HLOCAL)buf); + abort(); + } +} + +extern "C" CDECL void +rust_win32_rand_acquire(HCRYPTPROV* phProv) { + win32_require + (_T("CryptAcquireContext"), + CryptAcquireContext(phProv, NULL, NULL, PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT|CRYPT_SILENT)); + +} +extern "C" CDECL void +rust_win32_rand_gen(HCRYPTPROV hProv, DWORD dwLen, BYTE* pbBuffer) { + win32_require + (_T("CryptGenRandom"), CryptGenRandom(hProv, dwLen, pbBuffer)); +} +extern "C" CDECL void +rust_win32_rand_release(HCRYPTPROV hProv) { + win32_require + (_T("CryptReleaseContext"), CryptReleaseContext(hProv, 0)); +} + +#else + +// these symbols are listed in rustrt.def.in, so they need to exist; but they +// should never be called. + +extern "C" CDECL void +rust_win32_rand_acquire() { + abort(); +} +extern "C" CDECL void +rust_win32_rand_gen() { + abort(); +} +extern "C" CDECL void +rust_win32_rand_release() { + abort(); +} + +#endif // // Local Variables: // mode: C++ diff --git a/src/rt/rust_rng.cpp b/src/rt/rust_rng.cpp deleted file mode 100644 index 89754f94b7096..0000000000000 --- a/src/rt/rust_rng.cpp +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#include "rust_globals.h" -#include "rust_rng.h" -#include "rust_util.h" - - -#ifdef __WIN32__ -void -win32_require(LPCTSTR fn, BOOL ok) { - if (!ok) { - LPTSTR buf; - DWORD err = GetLastError(); - FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, err, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPTSTR) &buf, 0, NULL ); - fprintf(stderr, "%s failed with error %ld: %s", fn, err, buf); - LocalFree((HLOCAL)buf); - abort(); - } -} -#endif - -void -rng_gen_seed(uint8_t* dest, size_t size) { -#ifdef __WIN32__ - HCRYPTPROV hProv; - win32_require - (_T("CryptAcquireContext"), - CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, - CRYPT_VERIFYCONTEXT|CRYPT_SILENT)); - win32_require - (_T("CryptGenRandom"), CryptGenRandom(hProv, size, (BYTE*) dest)); - win32_require - (_T("CryptReleaseContext"), CryptReleaseContext(hProv, 0)); -#else - int fd = open("/dev/urandom", O_RDONLY); - if (fd == -1) { - fprintf(stderr, "error opening /dev/urandom: %s", strerror(errno)); - abort(); - } - size_t amount = 0; - do { - ssize_t ret = read(fd, dest+amount, size-amount); - if (ret < 0) { - fprintf(stderr, "error reading /dev/urandom: %s", strerror(errno)); - abort(); - } - else if (ret == 0) { - fprintf(stderr, "somehow hit eof reading from /dev/urandom"); - abort(); - } - amount += (size_t)ret; - } while (amount < size); - int ret = close(fd); - if (ret != 0) { - fprintf(stderr, "error closing /dev/urandom: %s", strerror(errno)); - // FIXME #3697: Why does this fail sometimes? - // abort(); - } -#endif -} - -// -// Local Variables: -// mode: C++ -// fill-column: 78; -// indent-tabs-mode: nil -// c-basic-offset: 4 -// buffer-file-coding-system: utf-8-unix -// End: -// diff --git a/src/rt/rust_rng.h b/src/rt/rust_rng.h deleted file mode 100644 index 08335a6f73f79..0000000000000 --- a/src/rt/rust_rng.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#ifndef RUST_RNG_H -#define RUST_RNG_H - -void rng_gen_seed(uint8_t* dest, size_t size); - -// -// Local Variables: -// mode: C++ -// fill-column: 78; -// indent-tabs-mode: nil -// c-basic-offset: 4 -// buffer-file-coding-system: utf-8-unix -// End: -// - -#endif diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in index d0491a9c059bb..30f60c662e991 100644 --- a/src/rt/rustrt.def.in +++ b/src/rt/rustrt.def.in @@ -9,7 +9,6 @@ rust_localtime rust_timegm rust_mktime precise_time_ns -rand_gen_seed rust_path_is_dir rust_path_exists rust_get_stdin @@ -23,6 +22,9 @@ rust_log_console_off rust_should_log_console rust_unset_sigprocmask rust_env_pairs +rust_win32_rand_acquire +rust_win32_rand_gen +rust_win32_rand_release upcall_rust_personality upcall_call_shim_on_c_stack upcall_call_shim_on_rust_stack From 6f4ec72362c2cf223b85d4fae9a1592a02e80317 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Wed, 2 Oct 2013 02:23:22 +1000 Subject: [PATCH 09/22] std::rand: add & split some tests. --- src/libstd/rand/isaac.rs | 37 ++++++++++++++++++++++++++++++------ src/libstd/rand/mod.rs | 20 +++++++++++++++++++ src/libstd/rand/reseeding.rs | 32 ++++++++++++++++++++++++++++++- 3 files changed, 82 insertions(+), 7 deletions(-) diff --git a/src/libstd/rand/isaac.rs b/src/libstd/rand/isaac.rs index 6d338a922beca..0166f83f8c468 100644 --- a/src/libstd/rand/isaac.rs +++ b/src/libstd/rand/isaac.rs @@ -408,12 +408,14 @@ mod test { use vec; #[test] - fn test_rng_seeded() { + fn test_rng_32_rand_seeded() { let s = unsafe {seed::(256)}; let mut ra: IsaacRng = SeedableRng::from_seed(s.as_slice()); let mut rb: IsaacRng = SeedableRng::from_seed(s.as_slice()); assert_eq!(ra.gen_ascii_str(100u), rb.gen_ascii_str(100u)); - + } + #[test] + fn test_rng_64_rand_seeded() { let s = unsafe {seed::(256)}; let mut ra: Isaac64Rng = SeedableRng::from_seed(s.as_slice()); let mut rb: Isaac64Rng = SeedableRng::from_seed(s.as_slice()); @@ -421,20 +423,43 @@ mod test { } #[test] - fn test_rng_seeded_custom_seed() { - // much shorter than generated seeds which are 1024 & 2048 - // bytes resp. + fn test_rng_32_seeded() { let seed = &[2, 32, 4, 32, 51]; let mut ra: IsaacRng = SeedableRng::from_seed(seed); let mut rb: IsaacRng = SeedableRng::from_seed(seed); assert_eq!(ra.gen_ascii_str(100u), rb.gen_ascii_str(100u)); - + } + #[test] + fn test_rng_64_seeded() { let seed = &[2, 32, 4, 32, 51]; let mut ra: Isaac64Rng = SeedableRng::from_seed(seed); let mut rb: Isaac64Rng = SeedableRng::from_seed(seed); assert_eq!(ra.gen_ascii_str(100u), rb.gen_ascii_str(100u)); } + #[test] + fn test_rng_32_reseed() { + let s = unsafe {seed::(256)}; + let mut r: IsaacRng = SeedableRng::from_seed(s.as_slice()); + let string1 = r.gen_ascii_str(100); + + r.reseed(s); + + let string2 = r.gen_ascii_str(100); + assert_eq!(string1, string2); + } + #[test] + fn test_rng_64_reseed() { + let s = unsafe {seed::(256)}; + let mut r: Isaac64Rng = SeedableRng::from_seed(s.as_slice()); + let string1 = r.gen_ascii_str(100); + + r.reseed(s); + + let string2 = r.gen_ascii_str(100); + assert_eq!(string1, string2); + } + #[test] fn test_rng_32_true_values() { let seed = &[2, 32, 4, 32, 51]; diff --git a/src/libstd/rand/mod.rs b/src/libstd/rand/mod.rs index 7f37077d5bb70..97d723bc7a0b6 100644 --- a/src/libstd/rand/mod.rs +++ b/src/libstd/rand/mod.rs @@ -1141,6 +1141,26 @@ mod test { })); } + #[test] + fn test_std_rng_seeded() { + let s = unsafe {seed::(256)}; + let mut ra: StdRng = SeedableRng::from_seed(s.as_slice()); + let mut rb: StdRng = SeedableRng::from_seed(s.as_slice()); + assert_eq!(ra.gen_ascii_str(100u), rb.gen_ascii_str(100u)); + } + + #[test] + fn test_std_rng_reseed() { + let s = unsafe {seed::(256)}; + let mut r: StdRng = SeedableRng::from_seed(s.as_slice()); + let string1 = r.gen_ascii_str(100); + + r.reseed(s); + + let string2 = r.gen_ascii_str(100); + assert_eq!(string1, string2); + } + #[test] fn test_seed_task_rng() { seed_task_rng([1]); diff --git a/src/libstd/rand/reseeding.rs b/src/libstd/rand/reseeding.rs index b6c880841a147..c3ceb1b8aa57a 100644 --- a/src/libstd/rand/reseeding.rs +++ b/src/libstd/rand/reseeding.rs @@ -108,11 +108,14 @@ impl Reseeder for ReseedWithDefault { *rng = Default::default(); } } +impl Default for ReseedWithDefault { + fn default() -> ReseedWithDefault { ReseedWithDefault } +} #[cfg(test)] mod test { use super::*; - use rand::Rng; + use rand::{SeedableRng, Rng}; use default::Default; use iter::range; use option::{None, Some}; @@ -133,6 +136,15 @@ mod test { Counter { i: 0 } } } + impl SeedableRng for Counter { + fn reseed(&mut self, seed: u32) { + self.i = seed; + } + fn from_seed(seed: u32) -> Counter { + Counter { i: seed } + } + } + type MyRng = ReseedingRng; #[test] fn test_reseeding() { @@ -144,4 +156,22 @@ mod test { i += 1; } } + + #[test] + fn test_rng_seeded() { + let mut ra: MyRng = SeedableRng::from_seed(2); + let mut rb: MyRng = SeedableRng::from_seed(2); + assert_eq!(ra.gen_ascii_str(100u), rb.gen_ascii_str(100u)); + } + + #[test] + fn test_rng_reseed() { + let mut r: MyRng = SeedableRng::from_seed(3); + let string1 = r.gen_ascii_str(100); + + r.reseed(3); + + let string2 = r.gen_ascii_str(100); + assert_eq!(string1, string2); + } } From 0b1a0d01a8d0a769cc2c0bd9b11bfb71864d2f36 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Wed, 2 Oct 2013 03:16:22 +1000 Subject: [PATCH 10/22] std::rand: move the Rand impls into a separate file for neatness. --- src/libstd/rand/mod.rs | 169 +---------------------------- src/libstd/rand/rand_impls.rs | 199 ++++++++++++++++++++++++++++++++++ 2 files changed, 201 insertions(+), 167 deletions(-) create mode 100644 src/libstd/rand/rand_impls.rs diff --git a/src/libstd/rand/mod.rs b/src/libstd/rand/mod.rs index 97d723bc7a0b6..510bb0dbde387 100644 --- a/src/libstd/rand/mod.rs +++ b/src/libstd/rand/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -45,14 +45,11 @@ fn main () { use cast; use container::Container; -use int; use iter::{Iterator, range}; use local_data; use prelude::*; use str; -use u32; use u64; -use uint; use vec; use os::getenv; @@ -64,6 +61,7 @@ pub mod isaac; pub mod os; pub mod reader; pub mod reseeding; +mod rand_impls; /// A type that can be randomly generated using an Rng pub trait Rand { @@ -72,169 +70,6 @@ pub trait Rand { fn rand(rng: &mut R) -> Self; } -impl Rand for int { - #[inline] - fn rand(rng: &mut R) -> int { - if int::bits == 32 { - rng.gen::() as int - } else { - rng.gen::() as int - } - } -} - -impl Rand for i8 { - #[inline] - fn rand(rng: &mut R) -> i8 { - rng.next_u32() as i8 - } -} - -impl Rand for i16 { - #[inline] - fn rand(rng: &mut R) -> i16 { - rng.next_u32() as i16 - } -} - -impl Rand for i32 { - #[inline] - fn rand(rng: &mut R) -> i32 { - rng.next_u32() as i32 - } -} - -impl Rand for i64 { - #[inline] - fn rand(rng: &mut R) -> i64 { - rng.next_u64() as i64 - } -} - -impl Rand for uint { - #[inline] - fn rand(rng: &mut R) -> uint { - if uint::bits == 32 { - rng.gen::() as uint - } else { - rng.gen::() as uint - } - } -} - -impl Rand for u8 { - #[inline] - fn rand(rng: &mut R) -> u8 { - rng.next_u32() as u8 - } -} - -impl Rand for u16 { - #[inline] - fn rand(rng: &mut R) -> u16 { - rng.next_u32() as u16 - } -} - -impl Rand for u32 { - #[inline] - fn rand(rng: &mut R) -> u32 { - rng.next_u32() - } -} - -impl Rand for u64 { - #[inline] - fn rand(rng: &mut R) -> u64 { - rng.next_u64() - } -} - -impl Rand for f32 { - #[inline] - fn rand(rng: &mut R) -> f32 { - rng.gen::() as f32 - } -} - -static SCALE : f64 = (u32::max_value as f64) + 1.0f64; -impl Rand for f64 { - #[inline] - fn rand(rng: &mut R) -> f64 { - let u1 = rng.next_u32() as f64; - let u2 = rng.next_u32() as f64; - let u3 = rng.next_u32() as f64; - - ((u1 / SCALE + u2) / SCALE + u3) / SCALE - } -} - -impl Rand for bool { - #[inline] - fn rand(rng: &mut R) -> bool { - rng.gen::() & 1 == 1 - } -} - -macro_rules! tuple_impl { - // use variables to indicate the arity of the tuple - ($($tyvar:ident),* ) => { - // the trailing commas are for the 1 tuple - impl< - $( $tyvar : Rand ),* - > Rand for ( $( $tyvar ),* , ) { - - #[inline] - fn rand(_rng: &mut R) -> ( $( $tyvar ),* , ) { - ( - // use the $tyvar's to get the appropriate number of - // repeats (they're not actually needed) - $( - _rng.gen::<$tyvar>() - ),* - , - ) - } - } - } -} - -impl Rand for () { - #[inline] - fn rand(_: &mut R) -> () { () } -} -tuple_impl!{A} -tuple_impl!{A, B} -tuple_impl!{A, B, C} -tuple_impl!{A, B, C, D} -tuple_impl!{A, B, C, D, E} -tuple_impl!{A, B, C, D, E, F} -tuple_impl!{A, B, C, D, E, F, G} -tuple_impl!{A, B, C, D, E, F, G, H} -tuple_impl!{A, B, C, D, E, F, G, H, I} -tuple_impl!{A, B, C, D, E, F, G, H, I, J} - -impl Rand for Option { - #[inline] - fn rand(rng: &mut R) -> Option { - if rng.gen() { - Some(rng.gen()) - } else { - None - } - } -} - -impl Rand for ~T { - #[inline] - fn rand(rng: &mut R) -> ~T { ~rng.gen() } -} - -impl Rand for @T { - #[inline] - fn rand(rng: &mut R) -> @T { @rng.gen() } -} - /// A value with a particular weight compared to other values pub struct Weighted { /// The numerical weight of this item diff --git a/src/libstd/rand/rand_impls.rs b/src/libstd/rand/rand_impls.rs new file mode 100644 index 0000000000000..22980a3e1c4c3 --- /dev/null +++ b/src/libstd/rand/rand_impls.rs @@ -0,0 +1,199 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! The implementations of `Rand` for the built-in types. + +use char; +use int; +use option::{Option, Some, None}; +use rand::{Rand,Rng}; +use u32; +use uint; + +impl Rand for int { + #[inline] + fn rand(rng: &mut R) -> int { + if int::bits == 32 { + rng.gen::() as int + } else { + rng.gen::() as int + } + } +} + +impl Rand for i8 { + #[inline] + fn rand(rng: &mut R) -> i8 { + rng.next_u32() as i8 + } +} + +impl Rand for i16 { + #[inline] + fn rand(rng: &mut R) -> i16 { + rng.next_u32() as i16 + } +} + +impl Rand for i32 { + #[inline] + fn rand(rng: &mut R) -> i32 { + rng.next_u32() as i32 + } +} + +impl Rand for i64 { + #[inline] + fn rand(rng: &mut R) -> i64 { + rng.next_u64() as i64 + } +} + +impl Rand for uint { + #[inline] + fn rand(rng: &mut R) -> uint { + if uint::bits == 32 { + rng.gen::() as uint + } else { + rng.gen::() as uint + } + } +} + +impl Rand for u8 { + #[inline] + fn rand(rng: &mut R) -> u8 { + rng.next_u32() as u8 + } +} + +impl Rand for u16 { + #[inline] + fn rand(rng: &mut R) -> u16 { + rng.next_u32() as u16 + } +} + +impl Rand for u32 { + #[inline] + fn rand(rng: &mut R) -> u32 { + rng.next_u32() + } +} + +impl Rand for u64 { + #[inline] + fn rand(rng: &mut R) -> u64 { + rng.next_u64() + } +} + +impl Rand for f32 { + #[inline] + fn rand(rng: &mut R) -> f32 { + rng.gen::() as f32 + } +} + +static SCALE : f64 = (u32::max_value as f64) + 1.0f64; +impl Rand for f64 { + #[inline] + fn rand(rng: &mut R) -> f64 { + let u1 = rng.next_u32() as f64; + let u2 = rng.next_u32() as f64; + let u3 = rng.next_u32() as f64; + + ((u1 / SCALE + u2) / SCALE + u3) / SCALE + } +} + + +impl Rand for char { + #[inline] + fn rand(rng: &mut R) -> char { + // a char is 21 bits + static CHAR_MASK: u32 = 0x001f_ffff; + loop { + // Rejection sampling. About 0.2% of numbers with at most + // 21-bits are invalid codepoints (surrogates), so this + // will succeed first go almost every time. + match char::from_u32(rng.next_u32() & CHAR_MASK) { + Some(c) => return c, + None => {} + } + } + } +} + +impl Rand for bool { + #[inline] + fn rand(rng: &mut R) -> bool { + rng.gen::() & 1 == 1 + } +} + +macro_rules! tuple_impl { + // use variables to indicate the arity of the tuple + ($($tyvar:ident),* ) => { + // the trailing commas are for the 1 tuple + impl< + $( $tyvar : Rand ),* + > Rand for ( $( $tyvar ),* , ) { + + #[inline] + fn rand(_rng: &mut R) -> ( $( $tyvar ),* , ) { + ( + // use the $tyvar's to get the appropriate number of + // repeats (they're not actually needed) + $( + _rng.gen::<$tyvar>() + ),* + , + ) + } + } + } +} + +impl Rand for () { + #[inline] + fn rand(_: &mut R) -> () { () } +} +tuple_impl!{A} +tuple_impl!{A, B} +tuple_impl!{A, B, C} +tuple_impl!{A, B, C, D} +tuple_impl!{A, B, C, D, E} +tuple_impl!{A, B, C, D, E, F} +tuple_impl!{A, B, C, D, E, F, G} +tuple_impl!{A, B, C, D, E, F, G, H} +tuple_impl!{A, B, C, D, E, F, G, H, I} +tuple_impl!{A, B, C, D, E, F, G, H, I, J} + +impl Rand for Option { + #[inline] + fn rand(rng: &mut R) -> Option { + if rng.gen() { + Some(rng.gen()) + } else { + None + } + } +} + +impl Rand for ~T { + #[inline] + fn rand(rng: &mut R) -> ~T { ~rng.gen() } +} + +impl Rand for @T { + #[inline] + fn rand(rng: &mut R) -> @T { @rng.gen() } +} From 98869799eb2604ecd7c947db117794df10890a2c Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Wed, 2 Oct 2013 03:17:57 +1000 Subject: [PATCH 11/22] std::rand: documentation additions & fixes. --- src/libstd/rand/mod.rs | 30 +++++++++++++++++++++++------- src/libstd/rand/reader.rs | 6 ++++-- src/libstd/rand/reseeding.rs | 25 +++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 9 deletions(-) diff --git a/src/libstd/rand/mod.rs b/src/libstd/rand/mod.rs index 510bb0dbde387..5394d384e6360 100644 --- a/src/libstd/rand/mod.rs +++ b/src/libstd/rand/mod.rs @@ -19,6 +19,22 @@ suffice, but sometimes an annotation is required, e.g. `rand::random::()`. See the `distributions` submodule for sampling random numbers from distributions like normal and exponential. +# Task-local RNG + +There is built-in support for a RNG associated with each task stored +in task-local storage. This RNG can be accessed via `task_rng`, or +used implicitly via `random`. This RNG is normally randomly seeded +from an operating-system source of randomness, e.g. `/dev/urandom` on +Unix systems, and will automatically reseed itself from this source +after generating 32 KiB of random data. + +It can be explicitly seeded on a per-task basis with `seed_task_rng`; +this only affects the task-local generator in the task in which it is +called. It can be seeded globally using the `RUST_SEED` environment +variable, which should be an integer. Setting `RUST_SEED` will seed +every task-local RNG with the same seed. Using either of these will +disable the automatic reseeding. + # Examples ```rust @@ -126,7 +142,7 @@ pub trait Rng { /// fn main() { /// let mut v = [0u8, .. 13579]; /// task_rng().fill_bytes(v); - /// printfln!(v); + /// println!("{:?}", v); /// } /// ``` fn fill_bytes(&mut self, mut dest: &mut [u8]) { @@ -486,7 +502,7 @@ pub trait SeedableRng: Rng { /// use std::rand::Rng; /// /// fn main() { - /// let mut rng: rand::XorShiftRng = rand::SeedableRng::from_seed(&[1, 2, 3, 4]); + /// let mut rng: rand::StdRng = rand::SeedableRng::from_seed(&[1, 2, 3, 4]); /// println!("{}", rng.gen::()); /// rng.reseed([5, 6, 7, 8]); /// println!("{}", rng.gen::()); @@ -503,7 +519,7 @@ pub trait SeedableRng: Rng { /// use std::rand::Rng; /// /// fn main() { - /// let mut rng: rand::XorShiftRng = rand::SeedableRng::from_seed(&[1, 2, 3, 4]); + /// let mut rng: rand::StdRng = rand::SeedableRng::from_seed(&[1, 2, 3, 4]); /// println!("{}", rng.gen::()); /// } /// ``` @@ -748,10 +764,10 @@ pub fn task_rng() -> @mut TaskRng { /// /// fn main() { /// rand::seed_task_rng(&[10u]); -/// printfln!("Same every time: %u", rand::random::()); +/// println!("Same every time: {}", rand::random::()); /// /// rand::seed_task_rng(&[1u, 2, 3, 4, 5, 6, 7, 8]); -/// printfln!("Same every time: %f", rand::random::()); +/// println!("Same every time: {}", rand::random::()); /// } /// ``` pub fn seed_task_rng(seed: &[uint]) { @@ -783,9 +799,9 @@ impl Rng for @mut R { /// fn main() { /// if random() { /// let x = random(); -/// printfln!(2u * x); +/// println!("{}", 2u * x); /// } else { -/// printfln!(random::()); +/// println!("{}", random::()); /// } /// } /// ``` diff --git a/src/libstd/rand/reader.rs b/src/libstd/rand/reader.rs index 32a97100fa1f6..78dad30117da3 100644 --- a/src/libstd/rand/reader.rs +++ b/src/libstd/rand/reader.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +//! A wrapper around any Reader to treat it as an RNG. + use option::{Some, None}; use rt::io::Reader; use rt::io::ReaderByteConversions; @@ -22,12 +24,12 @@ use rand::Rng; /// # Example /// /// ```rust -/// use std::rand::reader; +/// use std::rand::{reader, Rng}; /// use std::rt::io::mem; /// /// fn main() { /// let mut rng = reader::ReaderRng::new(mem::MemReader::new(~[1,2,3,4,5,6,7,8])); -/// println!("{}", rng.gen::()); +/// println!("{:x}", rng.gen::()); /// } /// ``` pub struct ReaderRng { diff --git a/src/libstd/rand/reseeding.rs b/src/libstd/rand/reseeding.rs index c3ceb1b8aa57a..b3eab2bbc4cba 100644 --- a/src/libstd/rand/reseeding.rs +++ b/src/libstd/rand/reseeding.rs @@ -94,6 +94,31 @@ impl, Rsdr: Reseeder + Default> SeedableRng for Resee } /// Something that can be used to reseed an RNG via `ReseedingRng`. +/// +/// # Example +/// +/// ```rust +/// use std::rand; +/// use std::rand::{Rng, SeedableRng}; +/// use std::rand::reseeding::{Reseeder, ReseedingRng}; +/// +/// struct TickTockReseeder { tick: bool } +/// impl Reseeder for TickTockReseeder { +/// fn reseed(&mut self, rng: &mut rand::StdRng) { +/// let val = if self.tick {0} else {1}; +/// rng.reseed(&[val]); +/// self.tick = !self.tick; +/// } +/// } +/// fn main() { +/// let rsdr = TickTockReseeder { tick: true }; +/// let mut rng = ReseedingRng::new(rand::StdRng::new(), 10, rsdr); +/// +/// // this will repeat, because it gets reseeded very regularly. +/// println(rng.gen_ascii_str(100)); +/// } +/// +/// ``` pub trait Reseeder { /// Reseed the given RNG. fn reseed(&mut self, rng: &mut R); From 9db32a2f1d2cfafc519941475f5e660a9ae076f0 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Thu, 3 Oct 2013 13:10:34 +1000 Subject: [PATCH 12/22] std::rand: adjust the f32 & f64 Rand instances. The f32 generator now just uses a single u32, and the f64 uses a single u64. This will make both significantly faster, especially on 64-bit platforms. --- src/libstd/rand/rand_impls.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/libstd/rand/rand_impls.rs b/src/libstd/rand/rand_impls.rs index 22980a3e1c4c3..976eea7191a50 100644 --- a/src/libstd/rand/rand_impls.rs +++ b/src/libstd/rand/rand_impls.rs @@ -14,7 +14,6 @@ use char; use int; use option::{Option, Some, None}; use rand::{Rand,Rng}; -use u32; use uint; impl Rand for int { @@ -96,21 +95,23 @@ impl Rand for u64 { } impl Rand for f32 { + /// A random `f32` in the range `[0, 1)`. #[inline] fn rand(rng: &mut R) -> f32 { - rng.gen::() as f32 + // weird, but this is the easiest way to get 2**32 + static SCALE: f32 = 2.0 * (1u32 << 31) as f32; + rng.next_u32() as f32 / SCALE } } -static SCALE : f64 = (u32::max_value as f64) + 1.0f64; impl Rand for f64 { + /// A random `f64` in the range `[0, 1)`. #[inline] fn rand(rng: &mut R) -> f64 { - let u1 = rng.next_u32() as f64; - let u2 = rng.next_u32() as f64; - let u3 = rng.next_u32() as f64; + // weird, but this is the easiest way to get 2**64 + static SCALE: f64 = 2.0 * (1u64 << 63) as f64; - ((u1 / SCALE + u2) / SCALE + u3) / SCALE + rng.next_u64() as f64 / SCALE } } From 5bb5f7678530c32caa11a1bc31e27187ff74bc19 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Tue, 8 Oct 2013 23:19:20 +1100 Subject: [PATCH 13/22] Convert rt::sched::new_sched_rng to use open/read/close rather than f*. --- src/libstd/rt/sched.rs | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/src/libstd/rt/sched.rs b/src/libstd/rt/sched.rs index 618921f8e080b..e5cbadda23d73 100644 --- a/src/libstd/rt/sched.rs +++ b/src/libstd/rt/sched.rs @@ -859,42 +859,36 @@ fn new_sched_rng() -> XorShiftRng { use libc; use sys; use c_str::ToCStr; - use ptr::RawPtr; use vec::MutableVector; use iter::Iterator; use rand::SeedableRng; // XXX: this could use io::native::file, when it works. - let file = do "/dev/urandom".with_c_str |name| { - do "r".with_c_str |mode| { - unsafe { libc::fopen(name, mode) } - } + let fd = do "/dev/urandom".with_c_str |name| { + unsafe { libc::open(name, libc::O_RDONLY, 0) } }; - if file.is_null() { + if fd == -1 { rtabort!("could not open /dev/urandom for reading.") } let mut seeds = [0u32, .. 4]; + let size = sys::size_of_val(&seeds); loop { - let nbytes = do seeds.as_mut_buf |buf, len| { + let nbytes = do seeds.as_mut_buf |buf, _| { unsafe { - libc::fread(buf as *mut libc::c_void, - sys::size_of::() as libc::size_t, - len as libc::size_t, - file) + libc::read(fd, + buf as *mut libc::c_void, + size as libc::size_t) } }; - rtassert!(nbytes == seeds.len() as libc::size_t); + rtassert!(nbytes as uint == size); if !seeds.iter().all(|x| *x == 0) { break; } } - // XXX: do we need to guarantee that this is closed with a finally - // block (is that even possible without a scheduler?), or do we - // know that the only way that we can fail here is `abort`ing? - unsafe {libc::fclose(file);} + unsafe {libc::close(fd);} SeedableRng::from_seed(seeds) } From 71addded64548ff845d9ef3852a2c1d2592ae39f Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Wed, 9 Oct 2013 00:21:26 +1100 Subject: [PATCH 14/22] std::rand: remove `seed`. This much better handled by directly calling out to `OSRng` where appropriate. --- src/libstd/rand/isaac.rs | 76 +++++++++++++++++++++------------------- src/libstd/rand/mod.rs | 18 ++-------- 2 files changed, 42 insertions(+), 52 deletions(-) diff --git a/src/libstd/rand/isaac.rs b/src/libstd/rand/isaac.rs index 0166f83f8c468..b785189ef9374 100644 --- a/src/libstd/rand/isaac.rs +++ b/src/libstd/rand/isaac.rs @@ -10,7 +10,8 @@ //! The ISAAC random number generator. -use rand::{seed, Rng, SeedableRng}; +use cast; +use rand::{Rng, SeedableRng, OSRng}; use iter::{Iterator, range, range_step, Repeat}; use option::{None, Some}; @@ -29,23 +30,31 @@ pub struct IsaacRng { priv b: u32, priv c: u32 } +static EMPTY: IsaacRng = IsaacRng { + cnt: 0, + rsl: [0, .. RAND_SIZE], + mem: [0, .. RAND_SIZE], + a: 0, b: 0, c: 0 +}; impl IsaacRng { /// Create an ISAAC random number generator with a random seed. pub fn new() -> IsaacRng { - let s = unsafe {seed::(RAND_SIZE as uint)}; - SeedableRng::from_seed(s.as_slice()) + let mut rng = EMPTY; + + { + let bytes = unsafe {cast::transmute::<&mut [u32], &mut [u8]>(rng.rsl)}; + OSRng::new().fill_bytes(bytes); + } + + rng.init(true); + rng } /// Create an ISAAC random number generator using the default /// fixed seed. pub fn new_unseeded() -> IsaacRng { - let mut rng = IsaacRng { - cnt: 0, - rsl: [0, .. RAND_SIZE], - mem: [0, .. RAND_SIZE], - a: 0, b: 0, c: 0 - }; + let mut rng = EMPTY; rng.init(false); rng } @@ -193,15 +202,8 @@ impl<'self> SeedableRng<&'self [u32]> for IsaacRng { /// constructed with a given seed will generate the same sequence /// of values as all other generators constructed with that seed. fn from_seed(seed: &'self [u32]) -> IsaacRng { - let mut rng = IsaacRng { - cnt: 0, - rsl: [0, .. RAND_SIZE], - mem: [0, .. RAND_SIZE], - a: 0, b: 0, c: 0 - }; - + let mut rng = EMPTY; rng.reseed(seed); - rng } } @@ -224,23 +226,30 @@ pub struct Isaac64Rng { priv c: u64, } +static EMPTY_64: Isaac64Rng = Isaac64Rng { + cnt: 0, + rsl: [0, .. RAND_SIZE_64], + mem: [0, .. RAND_SIZE_64], + a: 0, b: 0, c: 0, +}; + impl Isaac64Rng { /// Create a 64-bit ISAAC random number generator with a random /// seed. pub fn new() -> Isaac64Rng { - let s = unsafe {seed::(RAND_SIZE_64)}; - SeedableRng::from_seed(s.as_slice()) + let mut rng = EMPTY_64; + { + let bytes = unsafe {cast::transmute::<&mut [u64], &mut [u8]>(rng.rsl)}; + OSRng::new().fill_bytes(bytes); + } + rng.init(true); + rng } /// Create a 64-bit ISAAC random number generator using the /// default fixed seed. pub fn new_unseeded() -> Isaac64Rng { - let mut rng = Isaac64Rng { - cnt: 0, - rsl: [0, .. RAND_SIZE_64], - mem: [0, .. RAND_SIZE_64], - a: 0, b: 0, c: 0, - }; + let mut rng = EMPTY_64; rng.init(false); rng } @@ -388,12 +397,7 @@ impl<'self> SeedableRng<&'self [u64]> for Isaac64Rng { /// constructed with a given seed will generate the same sequence /// of values as all other generators constructed with that seed. fn from_seed(seed: &'self [u64]) -> Isaac64Rng { - let mut rng = Isaac64Rng { - cnt: 0, - rsl: [0, .. RAND_SIZE_64], - mem: [0, .. RAND_SIZE_64], - a: 0, b: 0, c: 0, - }; + let mut rng = EMPTY_64; rng.reseed(seed); rng } @@ -402,21 +406,21 @@ impl<'self> SeedableRng<&'self [u64]> for Isaac64Rng { #[cfg(test)] mod test { use super::*; - use rand::{Rng, SeedableRng, seed}; + use rand::{Rng, SeedableRng, OSRng}; use option::Some; use iter::range; use vec; #[test] fn test_rng_32_rand_seeded() { - let s = unsafe {seed::(256)}; + let s = OSRng::new().gen_vec::(256); let mut ra: IsaacRng = SeedableRng::from_seed(s.as_slice()); let mut rb: IsaacRng = SeedableRng::from_seed(s.as_slice()); assert_eq!(ra.gen_ascii_str(100u), rb.gen_ascii_str(100u)); } #[test] fn test_rng_64_rand_seeded() { - let s = unsafe {seed::(256)}; + let s = OSRng::new().gen_vec::(256); let mut ra: Isaac64Rng = SeedableRng::from_seed(s.as_slice()); let mut rb: Isaac64Rng = SeedableRng::from_seed(s.as_slice()); assert_eq!(ra.gen_ascii_str(100u), rb.gen_ascii_str(100u)); @@ -439,7 +443,7 @@ mod test { #[test] fn test_rng_32_reseed() { - let s = unsafe {seed::(256)}; + let s = OSRng::new().gen_vec::(256); let mut r: IsaacRng = SeedableRng::from_seed(s.as_slice()); let string1 = r.gen_ascii_str(100); @@ -450,7 +454,7 @@ mod test { } #[test] fn test_rng_64_reseed() { - let s = unsafe {seed::(256)}; + let s = OSRng::new().gen_vec::(256); let mut r: Isaac64Rng = SeedableRng::from_seed(s.as_slice()); let string1 = r.gen_ascii_str(100); diff --git a/src/libstd/rand/mod.rs b/src/libstd/rand/mod.rs index 5394d384e6360..b07d00f2236ab 100644 --- a/src/libstd/rand/mod.rs +++ b/src/libstd/rand/mod.rs @@ -672,20 +672,6 @@ impl XorShiftRng { } } -/// Create a new random seed of length `n`. This should only be used -/// to create types for which *any* bit pattern is valid. -pub unsafe fn seed(n: uint) -> ~[T] { - use unstable::intrinsics; - let mut s = vec::from_elem(n, intrinsics::init()); - let mut r = OSRng::new(); - - { - let s_u8 = cast::transmute::<&mut [T], &mut [u8]>(s); - r.fill_bytes(s_u8); - } - s -} - /// Controls how the task-local RNG is reseeded. enum TaskRngReseeder { /// Reseed using the StdRng::new() function, i.e. reading new @@ -994,7 +980,7 @@ mod test { #[test] fn test_std_rng_seeded() { - let s = unsafe {seed::(256)}; + let s = OSRng::new().gen_vec::(256); let mut ra: StdRng = SeedableRng::from_seed(s.as_slice()); let mut rb: StdRng = SeedableRng::from_seed(s.as_slice()); assert_eq!(ra.gen_ascii_str(100u), rb.gen_ascii_str(100u)); @@ -1002,7 +988,7 @@ mod test { #[test] fn test_std_rng_reseed() { - let s = unsafe {seed::(256)}; + let s = OSRng::new().gen_vec::(256); let mut r: StdRng = SeedableRng::from_seed(s.as_slice()); let string1 = r.gen_ascii_str(100); From a836f13dc0c2c636fd00b77d05f50a00cc3a7c55 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Wed, 9 Oct 2013 01:54:21 +1100 Subject: [PATCH 15/22] Documentation & address minor point. --- src/libstd/rand/mod.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/libstd/rand/mod.rs b/src/libstd/rand/mod.rs index b07d00f2236ab..1465e59422749 100644 --- a/src/libstd/rand/mod.rs +++ b/src/libstd/rand/mod.rs @@ -535,8 +535,9 @@ pub trait SeedableRng: Rng { /// /// This is a very expensive operation as it has to read randomness /// from the operating system and use this in an expensive seeding -/// operation. If one does not require high performance, `task_rng` -/// and/or `random` may be more appropriate. +/// operation. If one does not require high performance generation of +/// random numbers, `task_rng` and/or `random` may be more +/// appropriate. pub fn rng() -> StdRng { StdRng::new() } @@ -596,6 +597,9 @@ impl<'self> SeedableRng<&'self [uint]> for StdRng { /// consideration for cryptography or security. If you require a specifically /// seeded `Rng` for consistency over time you should pick one algorithm and /// create the `Rng` yourself. +/// +/// This will read randomness from the operating system to seed the +/// generator. pub fn weak_rng() -> XorShiftRng { XorShiftRng::new() } @@ -667,8 +671,8 @@ impl XorShiftRng { break; } } - let s: &[u32, ..4] = unsafe { cast::transmute(&s) }; - SeedableRng::from_seed(*s) + let s: [u32, ..4] = unsafe { cast::transmute(s) }; + SeedableRng::from_seed(s) } } From 38732c4b5cf778cb1b441bfc4290b3e3524b80c2 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Wed, 9 Oct 2013 02:09:33 +1100 Subject: [PATCH 16/22] std::rand: Correct the implementation of Rand for f32 & f64. --- src/libstd/rand/rand_impls.rs | 37 +++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/src/libstd/rand/rand_impls.rs b/src/libstd/rand/rand_impls.rs index 976eea7191a50..8ad0bd9e29755 100644 --- a/src/libstd/rand/rand_impls.rs +++ b/src/libstd/rand/rand_impls.rs @@ -95,23 +95,28 @@ impl Rand for u64 { } impl Rand for f32 { - /// A random `f32` in the range `[0, 1)`. + /// A random `f32` in the range `[0, 1)`, using 24 bits of + /// precision. #[inline] fn rand(rng: &mut R) -> f32 { - // weird, but this is the easiest way to get 2**32 - static SCALE: f32 = 2.0 * (1u32 << 31) as f32; - rng.next_u32() as f32 / SCALE + // using any more than 24 bits will cause (e.g.) 0xffff_ffff + // to correspond to 1 exactly, so we need to drop 8 to + // guarantee the open end. + + static SCALE: f32 = (1u32 << 24) as f32; + (rng.next_u32() >> 8) as f32 / SCALE } } impl Rand for f64 { - /// A random `f64` in the range `[0, 1)`. + /// A random `f64` in the range `[0, 1)`, using 53 bits of + /// precision. #[inline] fn rand(rng: &mut R) -> f64 { - // weird, but this is the easiest way to get 2**64 - static SCALE: f64 = 2.0 * (1u64 << 63) as f64; + // as for f32, but using more bits. - rng.next_u64() as f64 / SCALE + static SCALE: f64 = (1u64 << 53) as f64; + (rng.next_u64() >> 11) as f64 / SCALE } } @@ -198,3 +203,19 @@ impl Rand for @T { #[inline] fn rand(rng: &mut R) -> @T { @rng.gen() } } + +#[cfg(test)] +mod tests { + use rand::Rng; + struct ConstantRng(u64); + impl Rng for ConstantRng { + fn next_u64(&mut self) -> u64 { + **self + } + } + fn floating_point_edge_cases() { + // the test for exact equality is correct here. + assert!(ConstantRng(0xffff_ffff).gen::() != 1.0) + assert!(ConstantRng(0xffff_ffff_ffff_ffff).gen::() != 1.0) + } +} From 649c1759e8b56f0ac31dd31e46eae81b7ffd1e2c Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Wed, 9 Oct 2013 02:13:34 +1100 Subject: [PATCH 17/22] std::rand::reader: describe cfg!(endianness). --- src/libstd/rand/reader.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libstd/rand/reader.rs b/src/libstd/rand/reader.rs index 78dad30117da3..961a5b2cd2869 100644 --- a/src/libstd/rand/reader.rs +++ b/src/libstd/rand/reader.rs @@ -47,6 +47,9 @@ impl ReaderRng { impl Rng for ReaderRng { fn next_u32(&mut self) -> u32 { + // This is designed for speed: reading a LE integer on a LE + // platform just involves blitting the bytes into the memory + // of the u32, similarly for BE on BE; avoiding byteswapping. if cfg!(target_endian="little") { self.reader.read_le_u32_() } else { @@ -54,6 +57,7 @@ impl Rng for ReaderRng { } } fn next_u64(&mut self) -> u64 { + // see above for explanation. if cfg!(target_endian="little") { self.reader.read_le_u64_() } else { From d86de18b6198547f0a39e217123d8ec9ef4a9988 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Wed, 9 Oct 2013 02:22:37 +1100 Subject: [PATCH 18/22] std::rand::reseeding: seed the reseeder in the SeedableRng impl. This stops us relying on Default here. --- src/libstd/rand/mod.rs | 3 +-- src/libstd/rand/reseeding.rs | 22 ++++++++++++---------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/libstd/rand/mod.rs b/src/libstd/rand/mod.rs index 1465e59422749..f68bf71ba7f5d 100644 --- a/src/libstd/rand/mod.rs +++ b/src/libstd/rand/mod.rs @@ -762,8 +762,7 @@ pub fn task_rng() -> @mut TaskRng { /// ``` pub fn seed_task_rng(seed: &[uint]) { let t_r = task_rng(); - (*t_r).reseed(seed); - t_r.reseeder = DontReseed; + (*t_r).reseed((DontReseed, seed)); } // Allow direct chaining with `task_rng` diff --git a/src/libstd/rand/reseeding.rs b/src/libstd/rand/reseeding.rs index b3eab2bbc4cba..3b4919392fc29 100644 --- a/src/libstd/rand/reseeding.rs +++ b/src/libstd/rand/reseeding.rs @@ -76,19 +76,21 @@ impl> Rng for ReseedingRng { } } -impl, Rsdr: Reseeder + Default> SeedableRng for ReseedingRng { - fn reseed(&mut self, seed: S) { +impl, Rsdr: Reseeder> + SeedableRng<(Rsdr, S)> for ReseedingRng { + fn reseed(&mut self, (rsdr, seed): (Rsdr, S)) { self.rng.reseed(seed); + self.reseeder = rsdr; 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 { + /// Create a new `ReseedingRng` from the given reseeder and + /// seed. This uses a default value for `generation_threshold`. + fn from_seed((rsdr, seed): (Rsdr, S)) -> ReseedingRng { ReseedingRng { rng: SeedableRng::from_seed(seed), generation_threshold: DEFAULT_GENERATION_THRESHOLD, bytes_generated: 0, - reseeder: Default::default() + reseeder: rsdr } } } @@ -184,17 +186,17 @@ mod test { #[test] fn test_rng_seeded() { - let mut ra: MyRng = SeedableRng::from_seed(2); - let mut rb: MyRng = SeedableRng::from_seed(2); + let mut ra: MyRng = SeedableRng::from_seed((ReseedWithDefault, 2)); + let mut rb: MyRng = SeedableRng::from_seed((ReseedWithDefault, 2)); assert_eq!(ra.gen_ascii_str(100u), rb.gen_ascii_str(100u)); } #[test] fn test_rng_reseed() { - let mut r: MyRng = SeedableRng::from_seed(3); + let mut r: MyRng = SeedableRng::from_seed((ReseedWithDefault, 3)); let string1 = r.gen_ascii_str(100); - r.reseed(3); + r.reseed((ReseedWithDefault, 3)); let string2 = r.gen_ascii_str(100); assert_eq!(string1, string2); From 618c6afe3232d0cc3750178d4f301352684ddde6 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Wed, 9 Oct 2013 09:45:38 +1100 Subject: [PATCH 19/22] std::rand::os: use the externfn! macro for the Windows RNG. --- src/libstd/rand/os.rs | 38 ++++++++++++++++---------------------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/src/libstd/rand/os.rs b/src/libstd/rand/os.rs index 7a4e0aa95c6db..4c8cf06c55e87 100644 --- a/src/libstd/rand/os.rs +++ b/src/libstd/rand/os.rs @@ -21,6 +21,13 @@ use rt::io::{file, Open, Read}; #[cfg(windows)] use cast; +#[cfg(windows)] +use libc::{c_long, DWORD, BYTE}; +#[cfg(windows)] +type HCRYPTPROV = c_long; +// the extern functions imported from the runtime on Windows are +// implemented so that they either succeed or abort(), so we can just +// assume they work when we call them. /// A random number generator that retrieves randomness straight from /// the operating system. On Unix-like systems this reads from @@ -38,7 +45,7 @@ pub struct OSRng { /// This does not block. #[cfg(windows)] pub struct OSRng { - priv hcryptprov: raw::HCRYPTPROV + priv hcryptprov: HCRYPTPROV } impl OSRng { @@ -53,10 +60,11 @@ impl OSRng { /// Create a new `OSRng`. #[cfg(windows)] - #[fixed_stack_segment] #[inline(never)] pub fn new() -> OSRng { + externfn!(fn rust_win32_rand_acquire(phProv: *mut HCRYPTPROV)) + let mut hcp = 0; - unsafe {raw::rust_win32_rand_acquire(&mut hcp)}; + unsafe {rust_win32_rand_acquire(&mut hcp)}; OSRng { hcryptprov: hcp } } @@ -87,12 +95,11 @@ impl Rng for OSRng { self.fill_bytes(v); unsafe { cast::transmute(v) } } - #[fixed_stack_segment] #[inline(never)] fn fill_bytes(&mut self, v: &mut [u8]) { - use libc::DWORD; + externfn!(fn rust_win32_rand_gen(hProv: HCRYPTPROV, dwLen: DWORD, pbBuffer: *mut BYTE)) do v.as_mut_buf |ptr, len| { - unsafe {raw::rust_win32_rand_gen(self.hcryptprov, len as DWORD, ptr)} + unsafe {rust_win32_rand_gen(self.hcryptprov, len as DWORD, ptr)} } } } @@ -105,27 +112,14 @@ impl Drop for OSRng { } #[cfg(windows)] - #[fixed_stack_segment] #[inline(never)] fn drop(&mut self) { - unsafe {raw::rust_win32_rand_release(self.hcryptprov)} - } -} + externfn!(fn rust_win32_rand_release(hProv: HCRYPTPROV)) -#[cfg(windows)] -mod raw { - use libc::{c_long, DWORD, BYTE}; - - pub type HCRYPTPROV = c_long; - - // these functions are implemented so that they either succeed or - // abort(), so we can just assume they work when we call them. - extern { - pub fn rust_win32_rand_acquire(phProv: *mut HCRYPTPROV); - pub fn rust_win32_rand_gen(hProv: HCRYPTPROV, dwLen: DWORD, pbBuffer: *mut BYTE); - pub fn rust_win32_rand_release(hProv: HCRYPTPROV); + unsafe {rust_win32_rand_release(self.hcryptprov)} } } + #[cfg(test)] mod test { use super::*; From 62fededd8e9cfd85197f224123a057df43a6feab Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Wed, 9 Oct 2013 09:56:57 +1100 Subject: [PATCH 20/22] std::rand: Make Rng.next_u32 non-default, waiting for #7771. --- src/libstd/rand/isaac.rs | 6 ++++++ src/libstd/rand/mod.rs | 8 ++------ src/libstd/rand/rand_impls.rs | 3 +++ 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/libstd/rand/isaac.rs b/src/libstd/rand/isaac.rs index b785189ef9374..0068b60cfa51b 100644 --- a/src/libstd/rand/isaac.rs +++ b/src/libstd/rand/isaac.rs @@ -363,6 +363,12 @@ impl Isaac64Rng { } impl Rng for Isaac64Rng { + // FIXME #7771: having next_u32 like this should be unnecessary + #[inline] + fn next_u32(&mut self) -> u32 { + self.next_u64() as u32 + } + #[inline] fn next_u64(&mut self) -> u64 { if self.cnt == 0 { diff --git a/src/libstd/rand/mod.rs b/src/libstd/rand/mod.rs index f68bf71ba7f5d..a6ffbdd7b17d7 100644 --- a/src/libstd/rand/mod.rs +++ b/src/libstd/rand/mod.rs @@ -99,12 +99,8 @@ pub trait Rng { /// Return the next random u32. This rarely needs to be called /// directly, prefer `r.gen()` to `r.next_u32()`. /// - /// By default this is implemented in terms of `next_u64`. An - /// implementation of this trait must provide at least one of - /// these two methods. - fn next_u32(&mut self) -> u32 { - self.next_u64() as u32 - } + // FIXME #7771: Should be implemented in terms of next_u64 + fn next_u32(&mut self) -> u32; /// Return the next random u64. This rarely needs to be called /// directly, prefer `r.gen()` to `r.next_u64()`. diff --git a/src/libstd/rand/rand_impls.rs b/src/libstd/rand/rand_impls.rs index 8ad0bd9e29755..aad0d4e861c58 100644 --- a/src/libstd/rand/rand_impls.rs +++ b/src/libstd/rand/rand_impls.rs @@ -209,6 +209,9 @@ mod tests { use rand::Rng; struct ConstantRng(u64); impl Rng for ConstantRng { + fn next_u32(&mut self) -> u32 { + (**self) as u32 + } fn next_u64(&mut self) -> u64 { **self } From 5442a473624f9d463a21508a5dbdd5a5ec326c15 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Wed, 9 Oct 2013 17:36:31 +1100 Subject: [PATCH 21/22] std::rand: remove seed_task_rng and RUST_SEED. --- src/libstd/rand/mod.rs | 85 +++++------------------------------------- 1 file changed, 9 insertions(+), 76 deletions(-) diff --git a/src/libstd/rand/mod.rs b/src/libstd/rand/mod.rs index a6ffbdd7b17d7..aa4b496338d94 100644 --- a/src/libstd/rand/mod.rs +++ b/src/libstd/rand/mod.rs @@ -28,13 +28,6 @@ from an operating-system source of randomness, e.g. `/dev/urandom` on Unix systems, and will automatically reseed itself from this source after generating 32 KiB of random data. -It can be explicitly seeded on a per-task basis with `seed_task_rng`; -this only affects the task-local generator in the task in which it is -called. It can be seeded globally using the `RUST_SEED` environment -variable, which should be an integer. Setting `RUST_SEED` will seed -every task-local RNG with the same seed. Using either of these will -disable the automatic reseeding. - # Examples ```rust @@ -67,7 +60,6 @@ use prelude::*; use str; use u64; use vec; -use os::getenv; pub use self::isaac::{IsaacRng, Isaac64Rng}; pub use self::os::OSRng; @@ -673,25 +665,11 @@ impl XorShiftRng { } /// 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 -} - -impl Default for TaskRngReseeder { - fn default() -> TaskRngReseeder { WithNew } -} +struct TaskRngReseeder; impl reseeding::Reseeder for TaskRngReseeder { fn reseed(&mut self, rng: &mut StdRng) { - match *self { - WithNew => *rng = StdRng::new(), - DontReseed => {} - } + *rng = StdRng::new(); } } static TASK_RNG_RESEED_THRESHOLD: uint = 32_768; @@ -706,33 +684,19 @@ local_data_key!(TASK_RNG_KEY: @mut TaskRng) /// chaining style, e.g. `task_rng().gen::()`. /// /// 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. +/// after generating a certain amount of randomness. /// -/// 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`. +/// The internal RNG used is platform and architecture dependent, even +/// if the operating system random number generator is rigged to give +/// the same sequence always. 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 => { - // check the environment - let (sub_rng, reseeder) = match getenv("RUST_SEED") { - None => (StdRng::new(), WithNew), - - Some(s) => match from_str::(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, + let rng = @mut reseeding::ReseedingRng::new(StdRng::new(), TASK_RNG_RESEED_THRESHOLD, - reseeder); + TaskRngReseeder); local_data::set(TASK_RNG_KEY, rng); rng } @@ -740,27 +704,6 @@ pub fn task_rng() -> @mut TaskRng { } } -/// 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]); -/// println!("Same every time: {}", rand::random::()); -/// -/// rand::seed_task_rng(&[1u, 2, 3, 4, 5, 6, 7, 8]); -/// println!("Same every time: {}", rand::random::()); -/// } -/// ``` -pub fn seed_task_rng(seed: &[uint]) { - let t_r = task_rng(); - (*t_r).reseed((DontReseed, seed)); -} - // Allow direct chaining with `task_rng` impl Rng for @mut R { #[inline] @@ -996,16 +939,6 @@ mod test { let string2 = r.gen_ascii_str(100); assert_eq!(string1, string2); } - - #[test] - fn test_seed_task_rng() { - seed_task_rng([1]); - let first = random::(); - - seed_task_rng([1]); - let second = random::(); - assert_eq!(first, second); - } } #[cfg(test)] From e678435cab9cc8ddd668d4cb58ab82194c220382 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Wed, 9 Oct 2013 17:39:37 +1100 Subject: [PATCH 22/22] std::rand: Minor clean-up of comments & add a missing default method. --- src/libstd/rand/mod.rs | 7 +++++-- src/libstd/rt/sched.rs | 1 - 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/libstd/rand/mod.rs b/src/libstd/rand/mod.rs index aa4b496338d94..9b757092266bf 100644 --- a/src/libstd/rand/mod.rs +++ b/src/libstd/rand/mod.rs @@ -648,8 +648,6 @@ impl SeedableRng<[u32, .. 4]> for XorShiftRng { impl XorShiftRng { /// Create an xor shift random number generator with a random seed. pub fn new() -> XorShiftRng { - // generate seeds the same way as seed(), except we have a - // specific size, so we can just use a fixed buffer. let mut s = [0u8, ..16]; loop { let mut r = OSRng::new(); @@ -714,6 +712,11 @@ impl Rng for @mut R { fn next_u64(&mut self) -> u64 { (**self).next_u64() } + + #[inline] + fn fill_bytes(&mut self, bytes: &mut [u8]) { + (**self).fill_bytes(bytes); + } } /// Generate a random value using the task-local random number diff --git a/src/libstd/rt/sched.rs b/src/libstd/rt/sched.rs index e5cbadda23d73..ee43ced44ab25 100644 --- a/src/libstd/rt/sched.rs +++ b/src/libstd/rt/sched.rs @@ -863,7 +863,6 @@ fn new_sched_rng() -> XorShiftRng { use iter::Iterator; use rand::SeedableRng; - // XXX: this could use io::native::file, when it works. let fd = do "/dev/urandom".with_c_str |name| { unsafe { libc::open(name, libc::O_RDONLY, 0) } };