Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented XoShiro128 psuedo random number generator. #428

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion benches/generators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use test::{black_box, Bencher};

use rand::{RngCore, Rng, SeedableRng, FromEntropy};
use rand::{StdRng, SmallRng, OsRng, EntropyRng, ReseedingRng};
use rand::prng::{XorShiftRng, Hc128Rng, IsaacRng, Isaac64Rng, ChaChaRng};
use rand::prng::{XorShiftRng, Hc128Rng, IsaacRng, Isaac64Rng, XoShiro128, ChaChaRng};
use rand::prng::hc128::Hc128Core;
use rand::jitter::JitterRng;
use rand::thread_rng;
Expand Down Expand Up @@ -41,6 +41,7 @@ gen_bytes!(gen_bytes_isaac64, Isaac64Rng::from_entropy());
gen_bytes!(gen_bytes_std, StdRng::from_entropy());
gen_bytes!(gen_bytes_small, SmallRng::from_entropy());
gen_bytes!(gen_bytes_os, OsRng::new().unwrap());
gen_bytes!(gen_bytes_xoshiro128, XoShiro128::from_entropy());

macro_rules! gen_uint {
($fnn:ident, $ty:ty, $gen:expr) => {
Expand All @@ -67,6 +68,7 @@ gen_uint!(gen_u32_isaac64, u32, Isaac64Rng::from_entropy());
gen_uint!(gen_u32_std, u32, StdRng::from_entropy());
gen_uint!(gen_u32_small, u32, SmallRng::from_entropy());
gen_uint!(gen_u32_os, u32, OsRng::new().unwrap());
gen_bytes!(gen_u32_xoshiro128, XoShiro128::from_entropy());

gen_uint!(gen_u64_xorshift, u64, XorShiftRng::from_entropy());
gen_uint!(gen_u64_chacha20, u64, ChaChaRng::from_entropy());
Expand All @@ -76,6 +78,7 @@ gen_uint!(gen_u64_isaac64, u64, Isaac64Rng::from_entropy());
gen_uint!(gen_u64_std, u64, StdRng::from_entropy());
gen_uint!(gen_u64_small, u64, SmallRng::from_entropy());
gen_uint!(gen_u64_os, u64, OsRng::new().unwrap());
gen_bytes!(gen_u64_xoshiro128, XoShiro128::from_entropy());

// Do not test JitterRng like the others by running it RAND_BENCH_N times per,
// measurement, because it is way too slow. Only run it once.
Expand Down
2 changes: 2 additions & 0 deletions src/prng/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ pub mod chacha;
pub mod hc128;
pub mod isaac;
pub mod isaac64;
mod xoshiro128;
mod xorshift;

mod isaac_array;
Expand All @@ -53,3 +54,4 @@ pub use self::hc128::Hc128Rng;
pub use self::isaac::IsaacRng;
pub use self::isaac64::Isaac64Rng;
pub use self::xorshift::XorShiftRng;
pub use self::xoshiro128::XoShiro128;
98 changes: 98 additions & 0 deletions src/prng/xoshiro128.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// Copyright 2017 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.

//! XoShiro128 generator

use core::num::Wrapping as w;
use core::{fmt, slice};
use rand_core::{RngCore, SeedableRng, Error, impls, le};

#[inline(always)]
fn rotl(x: w<u64>, k: usize) -> w<u64> {
(x << k)| (x >> (64 - k))
}

/// An XoroShiro256 random number generator.
pub struct XoShiro128 {
s: [w<u64>; 2]
}

// Custom Debug implementation that does not expose the internal state
impl fmt::Debug for XoShiro128 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "XoShiro128 {{}}")
}
}

impl RngCore for XoShiro128 {
#[inline]
fn next_u32(&mut self) -> u32 {
self.next_u64() as u32
}

#[inline]
fn next_u64(&mut self) -> u64 {
let s0 = self.s[0];
let mut s1 = self.s[1];
let result = rotl(s0 * w(5), 7) * w(9);

s1 ^= s0;
self.s[0] = rotl(s0, 24) ^ s1 ^ (s1 << 16);
self.s[1] = rotl(s1, 37);

result.0
}

#[inline]
fn fill_bytes(&mut self, dest: &mut [u8]) {
impls::fill_bytes_via_next(self, dest)
}

fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
Ok(self.fill_bytes(dest))
}
}

impl SeedableRng for XoShiro128 {
type Seed = [u8; 16];

fn from_seed(seed: Self::Seed) -> Self {
let mut seed_u64 = [0u64; 2];
le::read_u64_into(&seed, &mut seed_u64);

// XoShiro128 cannot be seeded with 0 and we cannot return an Error, but
// also do not wish to panic (because a random seed can legitimately be
// 0); our only option is therefore to use a preset value.
if seed_u64.iter().all(|&x| x == 0) {
seed_u64 = [0xBAD_5EED, 0xBAD_5EED];
}

XoShiro128 {
s: [w(seed_u64[0]), w(seed_u64[0])]
}
}

fn from_rng<R: RngCore>(mut rng: R) -> Result<Self, Error> {
let mut seed_u64 = [0u64; 2];
loop {
unsafe {
let ptr = seed_u64.as_mut_ptr() as *mut u8;

let slice = slice::from_raw_parts_mut(ptr, 4 * 4);
rng.try_fill_bytes(slice)?;
}
if !seed_u64.iter().all(|&x| x == 0) { break; }
}

Ok(XoShiro128 {
s: [w(seed_u64[0]), w(seed_u64[0])]
})
}
}