From a2b509656ac9c0f98d89fe4ea9d2f64a6ec7047a Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Sat, 21 Sep 2013 22:06:50 +1000 Subject: 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/libstd/rt/task.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/libstd/rt') diff --git a/src/libstd/rt/task.rs b/src/libstd/rt/task.rs index 2d1b57cebf5..48b894f51e0 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(); } } -- cgit 1.4.1-3-g733a5 From 39a69d323da95ce642ea7fe8d40eb8bdd6a277c8 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Sun, 22 Sep 2013 20:51:57 +1000 Subject: 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 (limited to 'src/libstd/rt') diff --git a/src/libstd/rand/mod.rs b/src/libstd/rand/mod.rs index 237ffb0e9ad..e6bf42a5aed 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 00000000000..0e6472b4d37 --- /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 00000000000..f1164346fe6 --- /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 cbffec51cc9..004dab8d73a 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; -- cgit 1.4.1-3-g733a5 From 92725ae765a75b139d5d5fc27305f4436dbb456e Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Mon, 30 Sep 2013 01:29:28 +1000 Subject: 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(-) (limited to 'src/libstd/rt') diff --git a/src/libextra/bitv.rs b/src/libextra/bitv.rs index 98f69425aee..4f8cd4d3308 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 ee7ba4a888b..66e1fd0c2c3 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 534ebfb473b..6d338a922be 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 9fb6863692b..64f266d0045 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 d471a5e4e10..727efff0101 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 004dab8d73a..c1ca484754d 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 bec11ff3a7d..2d209e40e42 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 36ebdc54922..b9a8e74668f 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); -- cgit 1.4.1-3-g733a5 From 29e3b33a0954f2494278d74344792f4b84a44120 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Wed, 2 Oct 2013 02:18:57 +1000 Subject: 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 (limited to 'src/libstd/rt') diff --git a/mk/rt.mk b/mk/rt.mk index 5d75b961510..412e25bed4d 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 0e6472b4d37..7a4e0aa95c6 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 f1164346fe6..32a97100fa1 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 c1ca484754d..618921f8e08 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 34d1efd577d..d1d4286a7c6 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 89754f94b70..00000000000 --- 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 08335a6f73f..00000000000 --- 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 d0491a9c059..30f60c662e9 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 -- cgit 1.4.1-3-g733a5 From 5bb5f7678530c32caa11a1bc31e27187ff74bc19 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Tue, 8 Oct 2013 23:19:20 +1100 Subject: 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(-) (limited to 'src/libstd/rt') diff --git a/src/libstd/rt/sched.rs b/src/libstd/rt/sched.rs index 618921f8e08..e5cbadda23d 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) } -- cgit 1.4.1-3-g733a5 From e678435cab9cc8ddd668d4cb58ab82194c220382 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Wed, 9 Oct 2013 17:39:37 +1100 Subject: 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(-) (limited to 'src/libstd/rt') diff --git a/src/libstd/rand/mod.rs b/src/libstd/rand/mod.rs index aa4b496338d..9b757092266 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 e5cbadda23d..ee43ced44ab 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) } }; -- cgit 1.4.1-3-g733a5