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

feat: Upgrade to rand_core v0.9.0 #14

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
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
10 changes: 5 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "wyrand"
version = "0.2.1"
version = "0.3.0"
edition = "2021"
authors = ["Gonçalo Rica Pais da Silva <[email protected]>"]
description = "A fast & portable non-cryptographic pseudorandom number generator and hashing algorithm"
Expand All @@ -27,15 +27,15 @@ threadrng_wyhash = ["dep:rand", "randomised_wyhash"]
legacy_v4 = []

[dependencies]
getrandom = { version = "0.2", optional = true }
rand = { version = "0.8", optional = true }
rand_core = { version = "0.6", default-features = false, optional = true }
getrandom = { version = "=0.3.0-rc.0", optional = true }
rand = { version = "=0.9.0-beta.1", optional = true }
rand_core = { version = "=0.9.0-beta.1", default-features = false, optional = true }
serde = { version = "1.0", features = ["derive"], optional = true }

[dev-dependencies]
criterion = "0.5"
serde_test = "1.0"
rand = "0.8"
rand = "=0.9.0-beta.1"

[[bench]]
name = "rand_bench"
Expand Down
13 changes: 8 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,20 @@ The crate will always export `WyRand` and will do so when set as `default-featur
- **`hash`** - Enables `core::hash::Hash` implementation for `WyRand`.
- **`wyhash`** - Enables `WyHash`, a fast & portable hashing algorithm. Based on the final v4 C implementation.
- **`randomised_wyhash`** - Enables `RandomWyHashState`, a means to source a randomised state for `WyHash` for use in collections like `HashMap`/`HashSet`. Enables `wyhash` feature if it is not already enabled.
- **`fully_randomised_wyhash`** - Randomises not just the seed for `RandomWyHashState`, but also the secret. The new secret is generated once per runtime, and then is used for every subsequent new `WyHash` (with each `WyHash` instance having its own unique seed). Enables `randomised_wyhash` if not already enabled, and requires `std` environments.
- **`threadrng_wyhash`** - Enables sourcing entropy from `rand`'s `thread_rng()` method. Much quicker than `getrandom`. Enables `randomised_wyhash` if not already enabled. Requires `std` environments.
- **`legacy_v4`** - Exposes the legacy PRNG/Hashing algorithms that use the final v4 implementation.

Below are **application only features**, meant only to be enabled by app/bin crates, **NOT** lib crates as this changes runtime behaviour and also can pull in crates that change whether this crate can compile for `no-std` environments or not:

- **`fully_randomised_wyhash`** - Randomises not just the seed for `RandomWyHashState`, but also the secret. The new secret is generated once per runtime, and then is used for every subsequent new `WyHash` (with each `WyHash` instance having its own unique seed). Enables `randomised_wyhash` if not already enabled, and requires `std` environments. ONLY FOR BIN CRATES.
- **`threadrng_wyhash`** - Enables sourcing entropy from `rand`'s `thread_rng()` method. Much quicker than `getrandom`. Enables `randomised_wyhash` if not already enabled. Requires `std` environments. ONLY FOR BIN CRATES.

## Building for WASM/Web

If you are using `WyRand` with `rand_core` and/or `WyHash` with `randomised_wyhash` then for building for the web/WASM, you'll need to configure `getrandom` to make use of the browser APIs in order to source entropy from. Add the following to your project `Cargo.toml` if your WASM builds target the web:
If you are using `WyRand` with `rand_core` and/or `WyHash` with `randomised_wyhash` then for building for the web/WASM, you'll need to configure `getrandom` and its backend to make use of the browser APIs in order to source entropy from. Add the following to your project `.cargo/config.toml` if your WASM builds target the web:

```toml
[target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dependencies]
getrandom = { version = "0.2", features = ["js"] }
[target.wasm32-unknown-unknown]
rustflags = ['--cfg', 'getrandom_backend="wasm_js"']
```

## License
Expand Down
4 changes: 2 additions & 2 deletions benches/rand_bench.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use criterion::{black_box, criterion_main, Criterion};

fn wyrand_benchmark(c: &mut Criterion) {
use rand::thread_rng;
use rand::rng;
use rand_core::{RngCore, SeedableRng};
use wyrand::WyRand;

Expand Down Expand Up @@ -32,7 +32,7 @@ fn wyrand_benchmark(c: &mut Criterion) {
});

c.bench_function("from_rng", |b| {
b.iter(|| black_box(WyRand::from_rng(thread_rng())))
b.iter(|| black_box(WyRand::from_rng(&mut rng())))
});
}

Expand Down
32 changes: 7 additions & 25 deletions src/final_v4_2/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,35 +6,19 @@ use core::fmt::Debug;
#[cfg(feature = "fully_randomised_wyhash")]
use std::sync::OnceLock;

use crate::utils::get_random_u64;

use super::{secret::Secret, WyHash};

#[cfg(feature = "fully_randomised_wyhash")]
static SECRET: OnceLock<Secret> = OnceLock::new();

#[cfg(feature = "fully_randomised_wyhash")]
#[inline]
fn get_random_u64() -> u64 {
#[cfg(not(feature = "threadrng_wyhash"))]
{
const SIZE: usize = core::mem::size_of::<u64>();

let mut state = [0; SIZE];

// Don't bother trying to handle the result. If we can't obtain
// entropy with getrandom, then there is no hope and we might as
// well panic. It is up to the user to ensure getrandom is configured
// correctly for their platform.
getrandom::getrandom(&mut state)
.expect("Failed to source entropy for WyHash randomised state");
fn gen_new_secret() -> Secret {
use super::secret::make_secret;

u64::from_ne_bytes(state)
}
#[cfg(feature = "threadrng_wyhash")]
{
use rand::RngCore;

// This is faster than doing `.fill_bytes()`. User-space entropy goes brrr.
rand::thread_rng().next_u64()
}
make_secret(get_random_u64())
}

#[derive(Clone)]
Expand Down Expand Up @@ -71,11 +55,9 @@ impl RandomWyHashState {
pub fn new() -> Self {
#[cfg(not(feature = "fully_randomised_wyhash"))]
use super::constants::{WY0, WY1, WY2, WY3};
#[cfg(feature = "fully_randomised_wyhash")]
use super::secret::make_secret;

#[cfg(feature = "fully_randomised_wyhash")]
let secret = SECRET.get_or_init(|| make_secret(get_random_u64())).clone();
let secret = SECRET.get_or_init(gen_new_secret).clone();
#[cfg(not(feature = "fully_randomised_wyhash"))]
let secret = Secret::new(WY0, WY1, WY2, WY3);

Expand Down
19 changes: 9 additions & 10 deletions src/final_v4_2/wyrand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use core::fmt::Debug;

use super::constants::{WY0, WY1};
#[cfg(feature = "rand_core")]
use rand_core::{impls::fill_bytes_via_next, RngCore, SeedableRng};
use rand_core::{impls::fill_bytes_via_next, RngCore, SeedableRng, TryRngCore};

use crate::utils::wymix;
#[cfg(feature = "serde1")]
Expand Down Expand Up @@ -80,12 +80,6 @@ impl RngCore for WyRand {
fn fill_bytes(&mut self, dest: &mut [u8]) {
fill_bytes_via_next(self, dest);
}

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

#[cfg(feature = "rand_core")]
Expand All @@ -98,8 +92,13 @@ impl SeedableRng for WyRand {
}

#[inline]
fn from_rng<R: RngCore>(mut rng: R) -> Result<Self, rand_core::Error> {
Ok(Self::new(rng.next_u64()))
fn from_rng(rng: &mut impl RngCore) -> Self {
Self::new(rng.next_u64())
}

#[inline]
fn try_from_rng<R: TryRngCore>(rng: &mut R) -> Result<Self, R::Error> {
Ok(Self::new(rng.try_next_u64()?))
}
}

Expand Down Expand Up @@ -166,7 +165,7 @@ mod tests {
fn rand_core_from_rng() {
let mut source = WyRand::from_seed(Default::default());

let mut rng = WyRand::from_rng(&mut source).expect("from_rng should never fail here");
let mut rng = WyRand::from_rng(&mut source);

assert_eq!(rng.next_u32(), 844672934);
}
Expand Down
36 changes: 7 additions & 29 deletions src/legacy_final_v4/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,37 +6,19 @@ use core::fmt::Debug;
#[cfg(feature = "fully_randomised_wyhash")]
use std::sync::OnceLock;

use super::WyHashLegacy;
use crate::utils::get_random_u64;

use super::secret::LegacySecret;
use super::{secret::LegacySecret, WyHashLegacy};

#[cfg(feature = "fully_randomised_wyhash")]
static SECRET: OnceLock<LegacySecret> = OnceLock::new();

#[cfg(feature = "fully_randomised_wyhash")]
#[inline]
fn get_random_u64() -> u64 {
#[cfg(not(feature = "threadrng_wyhash"))]
{
const SIZE: usize = core::mem::size_of::<u64>();

let mut state = [0; SIZE];
fn gen_new_secret() -> LegacySecret {
use super::secret::make_secret_legacy;

// Don't bother trying to handle the result. If we can't obtain
// entropy with getrandom, then there is no hope and we might as
// well panic. It is up to the user to ensure getrandom is configured
// correctly for their platform.
getrandom::getrandom(&mut state)
.expect("Failed to source entropy for WyHash randomised state");

u64::from_ne_bytes(state)
}
#[cfg(feature = "threadrng_wyhash")]
{
use rand::RngCore;

// This is faster than doing `.fill_bytes()`. User-space entropy goes brrr.
rand::thread_rng().next_u64()
}
make_secret_legacy(get_random_u64())
}

#[derive(Clone)]
Expand Down Expand Up @@ -73,13 +55,9 @@ impl RandomWyHashLegacyState {
pub fn new() -> Self {
#[cfg(not(feature = "fully_randomised_wyhash"))]
use super::constants::{WY0, WY1, WY2, WY3};
#[cfg(feature = "fully_randomised_wyhash")]
use super::secret::make_secret_legacy;

#[cfg(feature = "fully_randomised_wyhash")]
let secret = SECRET
.get_or_init(|| make_secret_legacy(get_random_u64()))
.clone();
let secret = SECRET.get_or_init(gen_new_secret).clone();
#[cfg(not(feature = "fully_randomised_wyhash"))]
let secret = LegacySecret::new(WY0, WY1, WY2, WY3);

Expand Down
19 changes: 9 additions & 10 deletions src/legacy_final_v4/wyrand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use core::fmt::Debug;

use super::constants::{WY0, WY1};
#[cfg(feature = "rand_core")]
use rand_core::{impls::fill_bytes_via_next, RngCore, SeedableRng};
use rand_core::{impls::fill_bytes_via_next, RngCore, SeedableRng, TryRngCore};

use crate::utils::wymix;
#[cfg(feature = "serde1")]
Expand Down Expand Up @@ -80,12 +80,6 @@ impl RngCore for WyRandLegacy {
fn fill_bytes(&mut self, dest: &mut [u8]) {
fill_bytes_via_next(self, dest);
}

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

#[cfg(feature = "rand_core")]
Expand All @@ -98,8 +92,13 @@ impl SeedableRng for WyRandLegacy {
}

#[inline]
fn from_rng<R: RngCore>(mut rng: R) -> Result<Self, rand_core::Error> {
Ok(Self::new(rng.next_u64()))
fn from_rng(rng: &mut impl RngCore) -> Self {
Self::new(rng.next_u64())
}

#[inline]
fn try_from_rng<R: TryRngCore>(rng: &mut R) -> Result<Self, R::Error> {
Ok(Self::new(rng.try_next_u64()?))
}
}

Expand Down Expand Up @@ -166,7 +165,7 @@ mod tests {
fn rand_core_from_rng() {
let mut source = WyRandLegacy::from_seed(Default::default());

let mut rng = WyRandLegacy::from_rng(&mut source).expect("from_rng should never fail here");
let mut rng = WyRandLegacy::from_rng(&mut source);

assert_eq!(rng.next_u32(), 4242651740);
}
Expand Down
20 changes: 20 additions & 0 deletions src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,23 @@ pub(crate) const fn check_for_valid_secret_value(current_value: usize, secret: &

true
}

#[cfg(feature = "randomised_wyhash")]
#[inline]
pub(crate) fn get_random_u64() -> u64 {
#[cfg(not(feature = "threadrng_wyhash"))]
{
// Don't bother trying to handle the result. If we can't obtain
// entropy with getrandom, then there is no hope and we might as
// well panic. It is up to the user to ensure getrandom is configured
// correctly for their platform.
getrandom::u64().expect("Failed to source entropy for WyHash randomised state")
}
#[cfg(feature = "threadrng_wyhash")]
{
use rand_core::RngCore;

// This is faster than doing `.fill_bytes()`. User-space entropy goes brrr.
rand::rng().next_u64()
}
}