use {
crate::ADDRESS_BYTES,
core::{
cell::Cell,
hash::{BuildHasher, Hasher},
mem,
},
rand::{rng, Rng},
};
#[derive(Default)]
pub struct AddressHasher {
offset: usize,
state: u64,
}
impl Hasher for AddressHasher {
#[inline]
fn finish(&self) -> u64 {
self.state
}
#[inline]
fn write(&mut self, bytes: &[u8]) {
debug_assert_eq!(
bytes.len(),
ADDRESS_BYTES,
"This hasher is intended to be used with addresses and nothing else"
);
let chunk: &[u8; mem::size_of::<u64>()] = bytes
[self.offset..self.offset + mem::size_of::<u64>()]
.try_into()
.unwrap();
self.state = u64::from_ne_bytes(*chunk);
}
}
#[derive(Clone)]
pub struct AddressHasherBuilder {
offset: usize,
}
impl AddressHasherBuilder {
pub fn with_offset(offset: usize) -> Self {
AddressHasherBuilder { offset }
}
pub fn offset(&self) -> usize {
self.offset
}
}
impl Default for AddressHasherBuilder {
fn default() -> Self {
std::thread_local!(static OFFSET: Cell<usize> = {
let mut rng = rng();
Cell::new(rng.random_range(0..ADDRESS_BYTES - mem::size_of::<u64>()))
});
let offset = OFFSET.with(|offset| {
let mut next_offset = offset.get() + 1;
if next_offset > ADDRESS_BYTES - mem::size_of::<u64>() {
next_offset = 0;
}
offset.set(next_offset);
next_offset
});
AddressHasherBuilder { offset }
}
}
impl BuildHasher for AddressHasherBuilder {
type Hasher = AddressHasher;
#[inline]
fn build_hasher(&self) -> Self::Hasher {
AddressHasher {
offset: self.offset,
state: 0,
}
}
}
#[cfg(test)]
mod tests {
use {
super::AddressHasherBuilder,
crate::Address,
core::hash::{BuildHasher, Hasher},
};
#[test]
fn test_address_hasher_builder() {
let key = Address::new_unique();
let builder = AddressHasherBuilder::default();
let mut hasher1 = builder.build_hasher();
let mut hasher2 = builder.build_hasher();
hasher1.write(key.as_array());
hasher2.write(key.as_array());
assert_eq!(
hasher1.finish(),
hasher2.finish(),
"Hashers made with same builder should be identical"
);
let builder2 = AddressHasherBuilder::default();
for _ in 0..64 {
let mut hasher3 = builder2.build_hasher();
hasher3.write(key.as_array());
std::dbg!(hasher1.finish());
std::dbg!(hasher3.finish());
if hasher1.finish() != hasher3.finish() {
return;
}
}
panic!("Hashers built with different builder should be different due to random offset");
}
#[test]
fn test_address_hasher() {
let key1 = Address::new_unique();
let key2 = Address::new_unique();
let builder = AddressHasherBuilder::default();
let mut hasher1 = builder.build_hasher();
let mut hasher2 = builder.build_hasher();
hasher1.write(key1.as_array());
hasher2.write(key2.as_array());
assert_ne!(hasher1.finish(), hasher2.finish());
}
#[test]
fn test_builder_with_offset() {
let builder1 = AddressHasherBuilder::default();
let offset1 = builder1.offset();
let builder2 = AddressHasherBuilder::with_offset(offset1);
let offset2 = builder2.offset();
assert_eq!(offset1, offset2);
let key = Address::new_unique();
assert_eq!(builder1.hash_one(key), builder2.hash_one(key));
}
}