use core::hash::BuildHasher;
#[cfg(feature = "debug")]
use core::fmt::Debug;
#[cfg(feature = "fully_randomised_wyhash")]
use std::sync::LazyLock;
use crate::utils::get_random_u64;
use super::{secret::Secret, WyHash};
#[cfg(feature = "fully_randomised_wyhash")]
static SECRET: LazyLock<Secret> = LazyLock::new(|| {
use super::secret::make_secret;
make_secret(get_random_u64())
});
#[derive(Clone)]
#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
pub struct RandomWyHashState {
state: u64,
secret: Secret,
}
impl RandomWyHashState {
#[must_use]
#[inline]
pub fn new() -> Self {
#[cfg(not(feature = "fully_randomised_wyhash"))]
use super::constants::{WY0, WY1, WY2, WY3};
#[cfg(feature = "fully_randomised_wyhash")]
let secret = SECRET.clone();
#[cfg(not(feature = "fully_randomised_wyhash"))]
let secret = Secret::new(WY0, WY1, WY2, WY3);
Self::new_with_secret(secret)
}
#[must_use]
#[inline]
pub fn new_with_secret(secret: Secret) -> Self {
Self {
state: get_random_u64(),
secret,
}
}
}
impl BuildHasher for RandomWyHashState {
type Hasher = WyHash;
#[inline]
fn build_hasher(&self) -> Self::Hasher {
WyHash::new_with_secret(self.state, self.secret.clone())
}
}
impl Default for RandomWyHashState {
#[inline]
fn default() -> Self {
Self::new()
}
}
#[cfg(feature = "debug")]
impl Debug for RandomWyHashState {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("RandomisedWyHashState")
.finish_non_exhaustive()
}
}
#[cfg(test)]
mod tests {
extern crate alloc;
use super::*;
#[cfg(feature = "debug")]
#[test]
fn no_leaking_debug() {
use alloc::format;
let builder = RandomWyHashState::default();
assert_eq!(
format!("{builder:?}"),
"RandomisedWyHashState { .. }",
"Debug should not be leaking internal state"
);
}
#[test]
fn randomised_builder_states() {
let builder1 = RandomWyHashState::new();
let builder2 = RandomWyHashState::new();
assert_ne!(&builder1.state, &builder2.state);
assert_eq!(&builder1.secret, &builder2.secret);
#[cfg(feature = "fully_randomised_wyhash")]
{
use super::super::constants::{WY0, WY1, WY2, WY3};
let default_secret = Secret::new(WY0, WY1, WY2, WY3);
assert_ne!(&builder1.secret, &default_secret);
assert_ne!(&builder2.secret, &default_secret);
}
}
}