Skip to content

Commit

Permalink
Add GlobalRng
Browse files Browse the repository at this point in the history
  • Loading branch information
dhardy committed Jan 30, 2024
1 parent d0f06ad commit 7691dd4
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 0 deletions.
4 changes: 4 additions & 0 deletions benches/generators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const BYTES_LEN: usize = 1024;
use core::mem::size_of;
use test::{black_box, Bencher};

use rand::global_rng;
use rand::prelude::*;
use rand::rngs::adapter::ReseedingRng;
use rand::rngs::{mock::StepRng, OsRng};
Expand Down Expand Up @@ -53,6 +54,7 @@ gen_bytes!(gen_bytes_std, StdRng::from_entropy());
gen_bytes!(gen_bytes_small, SmallRng::from_entropy());
gen_bytes!(gen_bytes_os, OsRng);
gen_bytes!(gen_bytes_thread, thread_rng());
gen_bytes!(gen_bytes_global, global_rng());

macro_rules! gen_uint {
($fnn:ident, $ty:ty, $gen:expr) => {
Expand Down Expand Up @@ -84,6 +86,7 @@ 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);
gen_uint!(gen_u32_thread, u32, thread_rng());
gen_uint!(gen_u32_global, u32, global_rng());

gen_uint!(gen_u64_step, u64, StepRng::new(0, 1));
gen_uint!(gen_u64_pcg32, u64, Pcg32::from_entropy());
Expand All @@ -98,6 +101,7 @@ 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);
gen_uint!(gen_u64_thread, u64, thread_rng());
gen_uint!(gen_u64_global, u64, global_rng());

macro_rules! init_gen {
($fnn:ident, $gen:ident) => {
Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ pub mod seq;
// Public exports
#[cfg(all(feature = "std", feature = "std_rng", feature = "getrandom"))]
pub use crate::rngs::thread::thread_rng;
#[cfg(all(feature = "std", feature = "std_rng", feature = "getrandom"))]
pub use crate::rngs::global::global_rng;
pub use rng::{Fill, Rng};

#[cfg(all(feature = "std", feature = "std_rng", feature = "getrandom"))]
Expand Down
110 changes: 110 additions & 0 deletions src/rngs/global.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// Copyright 2018 Developers of the Rand project.
//
// 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.

//! Global random number generator
use std::fmt;
use std::sync::{Mutex, MutexGuard};

use super::std::Core;
use crate::rngs::adapter::ReseedingRng;
use crate::rngs::OsRng;
use crate::{CryptoRng, Error, RngCore, SeedableRng};

// Number of generated bytes after which to reseed `GlobalRng`.
const GLOBAL_RNG_RESEED_THRESHOLD: u64 = 1024 * 64;

/// A reference to the global generator
///
/// This locks the global generator until dropped.
#[cfg_attr(doc_cfg, doc(cfg(all(feature = "std", feature = "std_rng", feature = "getrandom"))))]
pub struct GlobalRng<'a> {
lock: MutexGuard<'a, Option<ReseedingRng<Core, OsRng>>>,
}

/// Debug implementation does not leak internal state
impl<'a> fmt::Debug for GlobalRng<'a> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "GlobalRng {{ .. }}")
}
}

static GLOBAL_RNG_KEY: Mutex<Option<ReseedingRng<Core, OsRng>>> = Mutex::new(None);

/// Access the global generator
///
/// Locks until release
#[cfg_attr(doc_cfg, doc(cfg(all(feature = "std", feature = "std_rng", feature = "getrandom"))))]
pub fn global_rng<'a>() -> GlobalRng<'a> {
let mut lock = GLOBAL_RNG_KEY.lock().unwrap();

if lock.is_none() {
let r = Core::from_rng(OsRng).unwrap_or_else(|err|
panic!("could not initialize global_rng: {}", err));
let rng = ReseedingRng::new(r, GLOBAL_RNG_RESEED_THRESHOLD, OsRng);
*lock = Some(rng);
}

assert!(lock.is_some());
GlobalRng { lock }
}

// impl<'a> Default for GlobalRng<'a> {
// fn default() -> GlobalRng {
// global_rng()
// }
// }

impl<'a> RngCore for GlobalRng<'a> {
#[inline(always)]
fn next_u32(&mut self) -> u32 {
// SAFETY: We ensure lock.is_some() before constructing GlobalRng
let rng = unsafe { self.lock.as_mut().unwrap_unchecked() };
rng.next_u32()
}

#[inline(always)]
fn next_u64(&mut self) -> u64 {
// SAFETY: We ensure lock.is_some() before constructing GlobalRng
let rng = unsafe { self.lock.as_mut().unwrap_unchecked() };
rng.next_u64()
}

fn fill_bytes(&mut self, dest: &mut [u8]) {
// SAFETY: We ensure lock.is_some() before constructing GlobalRng
let rng = unsafe { self.lock.as_mut().unwrap_unchecked() };
rng.fill_bytes(dest)
}

fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
// SAFETY: We ensure lock.is_some() before constructing GlobalRng
let rng = unsafe { self.lock.as_mut().unwrap_unchecked() };
rng.try_fill_bytes(dest)
}
}

impl<'a> CryptoRng for GlobalRng<'a> {}


#[cfg(test)]
mod test {
#[test]
fn test_global_rng() {
use crate::Rng;
let mut r = crate::global_rng();
r.gen::<i32>();
assert_eq!(r.gen_range(0..1), 0);
}

#[test]
fn test_debug_output() {
// We don't care about the exact output here, but it must not include
// private CSPRNG state or the cache stored by BlockRng!
assert_eq!(std::format!("{:?}", crate::global_rng()), "GlobalRng { .. }");
}
}
2 changes: 2 additions & 0 deletions src/rngs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,12 @@ mod xoshiro128plusplus;

#[cfg(feature = "std_rng")] mod std;
#[cfg(all(feature = "std", feature = "std_rng", feature = "getrandom"))] pub(crate) mod thread;
#[cfg(all(feature = "std", feature = "std_rng", feature = "getrandom"))] pub(crate) mod global;

#[cfg(feature = "small_rng")] pub use self::small::SmallRng;
#[cfg(feature = "std_rng")] pub use self::std::StdRng;
#[cfg(all(feature = "std", feature = "std_rng", feature = "getrandom"))] pub use self::thread::ThreadRng;
#[cfg(all(feature = "std", feature = "std_rng", feature = "getrandom"))] pub use self::global::GlobalRng;

#[cfg_attr(doc_cfg, doc(cfg(feature = "getrandom")))]
#[cfg(feature = "getrandom")] pub use rand_core::OsRng;

0 comments on commit 7691dd4

Please sign in to comment.