diff --git a/Cargo.toml b/Cargo.toml index 61813b8..096b0a6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wyrand" -version = "0.2.1" +version = "0.3.0" edition = "2021" authors = ["Gonçalo Rica Pais da Silva "] description = "A fast & portable non-cryptographic pseudorandom number generator and hashing algorithm" @@ -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" diff --git a/README.md b/README.md index e3ff394..75664b0 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/benches/rand_bench.rs b/benches/rand_bench.rs index 4fdcdcb..10990d3 100644 --- a/benches/rand_bench.rs +++ b/benches/rand_bench.rs @@ -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; @@ -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()))) }); } diff --git a/src/final_v4_2/builder.rs b/src/final_v4_2/builder.rs index dfb14e6..6c48520 100644 --- a/src/final_v4_2/builder.rs +++ b/src/final_v4_2/builder.rs @@ -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 = 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::(); - - 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)] @@ -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); diff --git a/src/final_v4_2/wyrand.rs b/src/final_v4_2/wyrand.rs index e736dc7..69a277b 100644 --- a/src/final_v4_2/wyrand.rs +++ b/src/final_v4_2/wyrand.rs @@ -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")] @@ -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")] @@ -98,8 +92,13 @@ impl SeedableRng for WyRand { } #[inline] - fn from_rng(mut rng: R) -> Result { - Ok(Self::new(rng.next_u64())) + fn from_rng(rng: &mut impl RngCore) -> Self { + Self::new(rng.next_u64()) + } + + #[inline] + fn try_from_rng(rng: &mut R) -> Result { + Ok(Self::new(rng.try_next_u64()?)) } } @@ -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); } diff --git a/src/legacy_final_v4/builder.rs b/src/legacy_final_v4/builder.rs index 4e3ce46..8786676 100644 --- a/src/legacy_final_v4/builder.rs +++ b/src/legacy_final_v4/builder.rs @@ -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 = 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::(); - - 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)] @@ -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); diff --git a/src/legacy_final_v4/wyrand.rs b/src/legacy_final_v4/wyrand.rs index 8213f62..2c19333 100644 --- a/src/legacy_final_v4/wyrand.rs +++ b/src/legacy_final_v4/wyrand.rs @@ -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")] @@ -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")] @@ -98,8 +92,13 @@ impl SeedableRng for WyRandLegacy { } #[inline] - fn from_rng(mut rng: R) -> Result { - Ok(Self::new(rng.next_u64())) + fn from_rng(rng: &mut impl RngCore) -> Self { + Self::new(rng.next_u64()) + } + + #[inline] + fn try_from_rng(rng: &mut R) -> Result { + Ok(Self::new(rng.try_next_u64()?)) } } @@ -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); } diff --git a/src/utils.rs b/src/utils.rs index 76d5946..06b9285 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -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() + } +}