diff --git a/.travis.yml b/.travis.yml index 7466e29e365..84d7c9deeb7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -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: diff --git a/Cargo.toml b/Cargo.toml index 13d76debd68..f3df538adb7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,12 +22,25 @@ 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"], 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"] diff --git a/appveyor.yml b/appveyor.yml index e5800fa7a07..14c572f5f93 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -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 diff --git a/src/lib.rs b/src/lib.rs index db3d930e8f5..9b8af89c5c0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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; use core::mem; diff --git a/src/prng/isaac.rs b/src/prng/isaac.rs index 4aa03d6c50f..c6f6f732598 100644 --- a/src/prng/isaac.rs +++ b/src/prng/isaac.rs @@ -87,8 +87,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, @@ -465,4 +468,38 @@ mod test { assert_eq!(rng.next_u64(), clone.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: IsaacRng = SeedableRng::from_seed(seed); + + let buf: Vec = 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()); + } + } } diff --git a/src/prng/isaac64.rs b/src/prng/isaac64.rs index 2ee3c3bbf74..9ddf5eae786 100644 --- a/src/prng/isaac64.rs +++ b/src/prng/isaac64.rs @@ -71,8 +71,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, @@ -473,4 +476,39 @@ mod test { assert_eq!(rng.next_u64(), clone.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 = 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()); + } + } } diff --git a/src/prng/isaac_serde.rs b/src/prng/isaac_serde.rs new file mode 100644 index 00000000000..4cb7c49d266 --- /dev/null +++ b/src/prng/isaac_serde.rs @@ -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 or the MIT license +// , 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(arr: &[T;RAND_SIZE], ser: S) -> Result + 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 { + _pd: PhantomData, + }; + impl<'de,T> Visitor<'de> for ArrayVisitor + 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(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}) + } +} diff --git a/src/prng/mod.rs b/src/prng/mod.rs index ac9d185ade1..7eb1dac8e34 100644 --- a/src/prng/mod.rs +++ b/src/prng/mod.rs @@ -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; \ No newline at end of file diff --git a/src/prng/xorshift.rs b/src/prng/xorshift.rs index ea83697996d..aabea468ca0 100644 --- a/src/prng/xorshift.rs +++ b/src/prng/xorshift.rs @@ -26,6 +26,7 @@ use impls; /// 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, y: w, @@ -115,3 +116,38 @@ impl Rand for XorShiftRng { XorShiftRng { x: w(x), y: w(y), z: w(z), w: w(w_) } } } + +#[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 = 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()); + } + } +} \ No newline at end of file