Merge branch 'master' into seedable_rng
This commit is contained in:
commit
71fa685021
@ -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:
|
||||
|
17
Cargo.toml
17
Cargo.toml
@ -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"]
|
||||
|
@ -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
|
||||
|
50
src/impls.rs
50
src/impls.rs
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
79
src/prng/isaac_serde.rs
Normal 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})
|
||||
}
|
||||
}
|
@ -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;
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user