use core::hash::{BuildHasher, Hasher};
use foldhash::quality::{FixedState, FoldHasher};
#[derive(Clone)]
pub struct SeededState<B> {
integers: FixedState,
bytes: B,
}
impl<B> SeededState<B> {
#[must_use]
pub const fn new(integer_seed: u64, bytes: B) -> Self {
Self {
integers: FixedState::with_seed(integer_seed),
bytes,
}
}
}
impl<B: BuildHasher + Clone> BuildHasher for SeededState<B> {
type Hasher = SeededHasher<B>;
#[inline]
fn build_hasher(&self) -> Self::Hasher {
SeededHasher {
integers: self.integers.build_hasher(),
builder: self.bytes.clone(),
bytes: None,
}
}
}
#[cfg(feature = "std")]
pub type RandomState = SeededState<std::hash::RandomState>;
#[cfg(feature = "std")]
impl Default for RandomState {
fn default() -> Self {
let bytes = std::hash::RandomState::new();
let seed = bytes.hash_one(0x6861_7368_7365_6564_u64);
Self::new(seed, bytes)
}
}
pub struct SeededHasher<B: BuildHasher> {
integers: FoldHasher<'static>,
builder: B,
bytes: Option<B::Hasher>,
}
macro_rules! integer_write {
($method:ident($value:ident: $ty:ty) => $integer:ident($mapped:expr)) => {
#[inline]
fn $method(&mut self, $value: $ty) {
match &mut self.bytes {
Some(bytes) => bytes.$method($value),
None => self.integers.$integer($mapped),
}
}
};
}
#[inline(never)]
fn finish_bytes<H: Hasher>(bytes: &H) -> u64 {
bytes.finish()
}
impl<B: BuildHasher> Hasher for SeededHasher<B> {
#[inline]
fn write(&mut self, input: &[u8]) {
let (builder, integers) = (&self.builder, &self.integers);
self.bytes
.get_or_insert_with(|| {
let mut bytes = builder.build_hasher();
bytes.write_u64(integers.finish());
bytes
})
.write(input);
}
integer_write!(write_u8(value: u8) => write_u8(value));
integer_write!(write_u16(value: u16) => write_u16(value));
integer_write!(write_u32(value: u32) => write_u32(value));
integer_write!(write_u64(value: u64) => write_u64(value));
integer_write!(write_u128(value: u128) => write_u128(value));
integer_write!(write_usize(value: usize) => write_usize(value));
integer_write!(write_i8(value: i8) => write_u8(value.cast_unsigned()));
integer_write!(write_i16(value: i16) => write_u16(value.cast_unsigned()));
integer_write!(write_i32(value: i32) => write_u32(value.cast_unsigned()));
integer_write!(write_i64(value: i64) => write_u64(value.cast_unsigned()));
integer_write!(write_i128(value: i128) => write_u128(value.cast_unsigned()));
integer_write!(write_isize(value: isize) => write_usize(value.cast_unsigned()));
#[inline]
fn finish(&self) -> u64 {
match &self.bytes {
Some(bytes) => finish_bytes(bytes),
None => self.integers.finish(),
}
}
}