Merge branch 'master' into seedable_rng

This commit is contained in:
Diggory Hardy 2018-01-23 11:18:49 +00:00
commit 71fa685021
12 changed files with 258 additions and 35 deletions

View File

@ -28,6 +28,7 @@ matrix:
script:
- cargo test
- cargo test --tests --no-default-features
- cargo test --features serde-1
- cargo test --manifest-path rand-derive/Cargo.toml
env:

View File

@ -17,16 +17,29 @@ categories = ["algorithms"]
default = ["std"]
nightly = ["i128_support"] # enables all features requiring nightly rust
std = ["libc"] # default feature; without this rand uses libcore
std = ["libc", "winapi"] # default feature; without this rand uses libcore
alloc = [] # enables Vec and Box support without std
i128_support = [] # enables i128 and u128 support
serde-1 = ["serde", "serde_derive"]
[target.'cfg(unix)'.dependencies]
libc = { version = "0.2", optional = true }
[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", features = ["minwindef", "ntsecapi", "profileapi", "winnt"] }
winapi = { version = "0.3", features = ["minwindef", "ntsecapi", "profileapi", "winnt"], optional = true }
[dependencies]
serde = {version="1",optional=true}
serde_derive = {version="1", optional=true}
[dev-dependencies]
# This is for testing serde, unfortunately
# we can't specify feature-gated dev deps yet,
# see: https://github.com/rust-lang/cargo/issues/1596
bincode = "0.9"
[workspace]
members = ["rand-derive"]

View File

@ -34,6 +34,7 @@ build: false
test_script:
- cargo test --benches
- cargo test
- cargo test --features serde-1
- cargo test --features nightly
- cargo test --tests --no-default-features --features=alloc
- cargo test --manifest-path rand-derive/Cargo.toml

View File

@ -23,6 +23,7 @@
#![allow(unused)]
use core::intrinsics::transmute;
use core::ptr::copy_nonoverlapping;
use core::slice;
use core::cmp::min;
use core::mem::size_of;
@ -82,21 +83,28 @@ macro_rules! impl_uint_from_fill {
}
macro_rules! fill_via_chunks {
($src:expr, $dest:expr, $N:expr) => ({
let chunk_size_u8 = min($src.len() * $N, $dest.len());
let chunk_size = (chunk_size_u8 + $N - 1) / $N;
// Convert to little-endian:
for ref mut x in $src[0..chunk_size].iter_mut() {
**x = (*x).to_le();
($src:expr, $dst:expr, $ty:ty, $size:expr) => ({
let chunk_size_u8 = min($src.len() * $size, $dst.len());
let chunk_size = (chunk_size_u8 + $size - 1) / $size;
if cfg!(target_endian="little") {
unsafe {
copy_nonoverlapping(
$src.as_ptr() as *const u8,
$dst.as_mut_ptr(),
chunk_size_u8);
}
} else {
for (&n, chunk) in $src.iter().zip($dst.chunks_mut($size)) {
let tmp = n.to_le();
let src_ptr = &tmp as *const $ty as *const u8;
unsafe {
copy_nonoverlapping(src_ptr,
chunk.as_mut_ptr(),
chunk.len());
}
}
}
let bytes = unsafe { slice::from_raw_parts($src.as_ptr() as *const u8,
$src.len() * $N) };
let dest_chunk = &mut $dest[0..chunk_size_u8];
dest_chunk.copy_from_slice(&bytes[0..chunk_size_u8]);
(chunk_size, chunk_size_u8)
});
}
@ -111,10 +119,6 @@ macro_rules! fill_via_chunks {
/// `consumed_u32` is the number of words consumed from `src`, which is the same
/// as `filled_u8 / 4` rounded up.
///
/// Note that on big-endian systems values in the output buffer `src` are
/// mutated. `src[0..consumed_u32]` get converted to little-endian before
/// copying.
///
/// # Example
/// (from `IsaacRng`)
///
@ -135,8 +139,8 @@ macro_rules! fill_via_chunks {
/// }
/// }
/// ```
pub fn fill_via_u32_chunks(src: &mut [u32], dest: &mut [u8]) -> (usize, usize) {
fill_via_chunks!(src, dest, 4)
pub fn fill_via_u32_chunks(src: &[u32], dest: &mut [u8]) -> (usize, usize) {
fill_via_chunks!(src, dest, u32, 4)
}
/// Implement `fill_bytes` by reading chunks from the output buffer of a block
@ -148,13 +152,9 @@ pub fn fill_via_u32_chunks(src: &mut [u32], dest: &mut [u8]) -> (usize, usize) {
/// `consumed_u64` is the number of words consumed from `src`, which is the same
/// as `filled_u8 / 8` rounded up.
///
/// Note that on big-endian systems values in the output buffer `src` are
/// mutated. `src[0..consumed_u64]` get converted to little-endian before
/// copying.
///
/// See `fill_via_u32_chunks` for an example.
pub fn fill_via_u64_chunks(src: &mut [u64], dest: &mut [u8]) -> (usize, usize) {
fill_via_chunks!(src, dest, 8)
pub fn fill_via_u64_chunks(src: &[u64], dest: &mut [u8]) -> (usize, usize) {
fill_via_chunks!(src, dest, u64, 8)
}
/// Implement `next_u32` via `fill_bytes`, little-endian order.

View File

@ -249,6 +249,9 @@
#[cfg(feature="std")] extern crate std as core;
#[cfg(all(feature = "alloc", not(feature="std")))] extern crate alloc;
#[cfg(test)] #[cfg(feature="serde-1")] extern crate bincode;
#[cfg(feature="serde-1")] extern crate serde;
#[cfg(feature="serde-1")] #[macro_use] extern crate serde_derive;
use core::{marker, mem};
#[cfg(feature="std")] use std::cell::RefCell;

View File

@ -195,8 +195,20 @@ impl Rng for ChaChaRng {
}
fn fill_bytes(&mut self, bytes: &mut [u8]) {
impls::fill_bytes_via_u32(self, bytes)
fn fill_bytes(&mut self, dest: &mut [u8]) {
let mut read_len = 0;
while read_len < dest.len() {
if self.index >= self.buffer.len() {
self.update();
}
let (consumed_u32, filled_u8) =
impls::fill_via_u32_chunks(&self.buffer[self.index..],
&mut dest[read_len..]);
self.index += consumed_u32;
read_len += filled_u8;
}
}
}

View File

@ -342,7 +342,7 @@ impl Rng for Hc128Rng {
// Continue filling from the current set of results
if self.index < 16 {
let (consumed_u32, filled_u8) =
impls::fill_via_u32_chunks(&mut self.results[self.index..],
impls::fill_via_u32_chunks(&self.results[self.index..],
dest);
self.index += consumed_u32;
@ -367,7 +367,7 @@ impl Rng for Hc128Rng {
self.state.update(&mut self.results);
let (consumed_u32, _) =
impls::fill_via_u32_chunks(&mut self.results,
impls::fill_via_u32_chunks(&self.results,
&mut dest[filled..]);
self.index = consumed_u32;
@ -384,7 +384,7 @@ impl Rng for Hc128Rng {
}
let (consumed_u32, filled_u8) =
impls::fill_via_u32_chunks(&mut self.results[self.index..],
impls::fill_via_u32_chunks(&self.results[self.index..],
&mut dest[read_len..]);
self.index += consumed_u32;

View File

@ -85,8 +85,11 @@ const RAND_SIZE: usize = 1 << RAND_SIZE_LEN;
///
/// [3]: Jean-Philippe Aumasson, [*On the pseudo-random generator ISAAC*](
/// https://eprint.iacr.org/2006/438)
#[cfg_attr(feature="serde-1", derive(Serialize,Deserialize))]
pub struct IsaacRng {
#[cfg_attr(feature="serde-1",serde(with="super::isaac_serde::rand_size_serde"))]
rsl: [u32; RAND_SIZE],
#[cfg_attr(feature="serde-1",serde(with="super::isaac_serde::rand_size_serde"))]
mem: [w32; RAND_SIZE],
a: w32,
b: w32,
@ -240,7 +243,7 @@ impl Rng for IsaacRng {
}
let (consumed_u32, filled_u8) =
impls::fill_via_u32_chunks(&mut self.rsl[(self.index as usize)..],
impls::fill_via_u32_chunks(&self.rsl[(self.index as usize)..],
&mut dest[read_len..]);
self.index += consumed_u32 as u32;
@ -461,4 +464,38 @@ mod test {
assert_eq!(rng1.next_u32(), rng2.next_u32());
}
}
#[test]
#[cfg(feature="serde-1")]
fn test_rng_serde() {
use bincode;
use std::io::{BufWriter, BufReader};
let seed: &[_] = &[1, 23, 456, 7890, 12345];
let mut rng: IsaacRng = SeedableRng::from_seed(seed);
let buf: Vec<u8> = Vec::new();
let mut buf = BufWriter::new(buf);
bincode::serialize_into(&mut buf, &rng, bincode::Infinite).expect("Could not serialize");
let buf = buf.into_inner().unwrap();
let mut read = BufReader::new(&buf[..]);
let mut deserialized: IsaacRng = bincode::deserialize_from(&mut read, bincode::Infinite).expect("Could not deserialize");
assert_eq!(rng.index, deserialized.index);
/* Can't assert directly because of the array size */
for (orig,deser) in rng.rsl.iter().zip(deserialized.rsl.iter()) {
assert_eq!(orig, deser);
}
for (orig,deser) in rng.mem.iter().zip(deserialized.mem.iter()) {
assert_eq!(orig, deser);
}
assert_eq!(rng.a, deserialized.a);
assert_eq!(rng.b, deserialized.b);
assert_eq!(rng.c, deserialized.c);
for _ in 0..16 {
assert_eq!(rng.next_u64(), deserialized.next_u64());
}
}
}

View File

@ -69,8 +69,11 @@ const RAND_SIZE: usize = 1 << RAND_SIZE_LEN;
///
/// [1]: Bob Jenkins, [*ISAAC and RC4*](
/// http://burtleburtle.net/bob/rand/isaac.html)
#[cfg_attr(feature="serde-1", derive(Serialize,Deserialize))]
pub struct Isaac64Rng {
#[cfg_attr(feature="serde-1",serde(with="super::isaac_serde::rand_size_serde"))]
rsl: [u64; RAND_SIZE],
#[cfg_attr(feature="serde-1",serde(with="super::isaac_serde::rand_size_serde"))]
mem: [w64; RAND_SIZE],
a: w64,
b: w64,
@ -243,7 +246,7 @@ impl Rng for Isaac64Rng {
}
let (consumed_u64, filled_u8) =
impls::fill_via_u64_chunks(&mut self.rsl[self.index as usize..],
impls::fill_via_u64_chunks(&self.rsl[self.index as usize..],
&mut dest[read_len..]);
self.index += consumed_u64 as u32;
@ -465,4 +468,39 @@ mod test {
assert_eq!(rng1.next_u64(), rng2.next_u64());
}
}
#[test]
#[cfg(feature="serde-1")]
fn test_rng_serde() {
use bincode;
use std::io::{BufWriter, BufReader};
let seed: &[_] = &[1, 23, 456, 7890, 12345];
let mut rng: Isaac64Rng = SeedableRng::from_seed(seed);
let buf: Vec<u8> = Vec::new();
let mut buf = BufWriter::new(buf);
bincode::serialize_into(&mut buf, &rng, bincode::Infinite).expect("Could not serialize");
let buf = buf.into_inner().unwrap();
let mut read = BufReader::new(&buf[..]);
let mut deserialized: Isaac64Rng = bincode::deserialize_from(&mut read, bincode::Infinite).expect("Could not deserialize");
assert_eq!(rng.index, deserialized.index);
assert_eq!(rng.half_used, deserialized.half_used);
/* Can't assert directly because of the array size */
for (orig,deser) in rng.rsl.iter().zip(deserialized.rsl.iter()) {
assert_eq!(orig, deser);
}
for (orig,deser) in rng.mem.iter().zip(deserialized.mem.iter()) {
assert_eq!(orig, deser);
}
assert_eq!(rng.a, deserialized.a);
assert_eq!(rng.b, deserialized.b);
assert_eq!(rng.c, deserialized.c);
for _ in 0..16 {
assert_eq!(rng.next_u64(), deserialized.next_u64());
}
}
}

79
src/prng/isaac_serde.rs Normal file
View File

@ -0,0 +1,79 @@
// Copyright 2017-2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// https://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! ISAAC serde helper functions.
pub(super) mod rand_size_serde {
const RAND_SIZE_LEN: usize = 8;
const RAND_SIZE: usize = 1 << RAND_SIZE_LEN;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde::de::{Visitor,SeqAccess};
use serde::de;
use std::fmt;
pub fn serialize<T, S>(arr: &[T;RAND_SIZE], ser: S) -> Result<S::Ok, S::Error>
where
T: Serialize,
S: Serializer
{
use serde::ser::SerializeTuple;
let mut seq = ser.serialize_tuple(RAND_SIZE)?;
for e in arr.iter() {
seq.serialize_element(&e)?;
}
seq.end()
}
#[inline]
pub fn deserialize<'de, T, D>(de: D) -> Result<[T;RAND_SIZE], D::Error>
where
T: Deserialize<'de>+Default+Copy,
D: Deserializer<'de>,
{
use std::marker::PhantomData;
struct ArrayVisitor<T> {
_pd: PhantomData<T>,
};
impl<'de,T> Visitor<'de> for ArrayVisitor<T>
where
T: Deserialize<'de>+Default+Copy
{
type Value = [T; RAND_SIZE];
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("Isaac state array")
}
#[inline]
fn visit_seq<A>(self, mut seq: A) -> Result<[T; RAND_SIZE], A::Error>
where
A: SeqAccess<'de>,
{
let mut out = [Default::default();RAND_SIZE];
for i in 0..RAND_SIZE {
match seq.next_element()? {
Some(val) => out[i] = val,
None => return Err(de::Error::invalid_length(i, &self)),
};
}
Ok(out)
}
}
de.deserialize_tuple(RAND_SIZE, ArrayVisitor{_pd: PhantomData})
}
}

View File

@ -46,8 +46,11 @@ mod isaac;
mod isaac64;
mod xorshift;
#[cfg(feature="serde-1")]
mod isaac_serde;
pub use self::chacha::ChaChaRng;
pub use self::hc128::Hc128Rng;
pub use self::isaac::IsaacRng;
pub use self::isaac64::Isaac64Rng;
pub use self::xorshift::XorShiftRng;
pub use self::xorshift::XorShiftRng;

View File

@ -26,6 +26,7 @@ use {impls, le};
/// RNGs"](https://www.jstatsoft.org/v08/i14/paper). *Journal of
/// Statistical Software*. Vol. 8 (Issue 14).
#[derive(Clone)]
#[cfg_attr(feature="serde-1", derive(Serialize,Deserialize))]
pub struct XorShiftRng {
x: w<u32>,
y: w<u32>,
@ -121,3 +122,38 @@ impl SeedableRng for XorShiftRng {
})
}
}
#[cfg(test)]
mod tests {
#[cfg(feature="serde-1")]
use {Rng, SeedableRng};
#[cfg(feature="serde-1")]
#[test]
fn test_serde() {
use super::XorShiftRng;
use thread_rng;
use bincode;
use std::io::{BufWriter, BufReader};
let seed: [u32; 4] = thread_rng().gen();
let mut rng: XorShiftRng = SeedableRng::from_seed(seed);
let buf: Vec<u8> = Vec::new();
let mut buf = BufWriter::new(buf);
bincode::serialize_into(&mut buf, &rng, bincode::Infinite).expect("Could not serialize");
let buf = buf.into_inner().unwrap();
let mut read = BufReader::new(&buf[..]);
let mut deserialized: XorShiftRng = bincode::deserialize_from(&mut read, bincode::Infinite).expect("Could not deserialize");
assert_eq!(rng.x, deserialized.x);
assert_eq!(rng.y, deserialized.y);
assert_eq!(rng.z, deserialized.z);
assert_eq!(rng.w, deserialized.w);
for _ in 0..16 {
assert_eq!(rng.next_u64(), deserialized.next_u64());
}
}
}