#![allow(clippy::module_name_repetitions)]
use smallvec::SmallVec;
use std::hash::Hash;
pub(crate) struct HashWriter {
buf: SmallVec<[u8; 32]>,
}
impl HashWriter {
#[inline]
pub(crate) fn new() -> Self {
Self {
buf: SmallVec::new(),
}
}
#[inline]
pub(crate) fn into_bytes(self) -> SmallVec<[u8; 32]> {
self.buf
}
}
impl std::hash::Hasher for HashWriter {
#[inline(always)]
fn write(&mut self, bytes: &[u8]) {
self.buf.extend_from_slice(bytes);
}
#[inline(always)]
fn finish(&self) -> u64 {
0
}
}
pub trait BloomHasher: Send + Sync {
fn hash_bytes(&self, bytes: &[u8]) -> u64;
fn hash_bytes_with_seed(&self, bytes: &[u8], seed: u64) -> u64 {
self.hash_bytes(bytes) ^ seed
}
fn hash_bytes_pair(&self, bytes: &[u8]) -> (u64, u64) {
let h1 = self.hash_bytes(bytes);
let h2 = self.hash_bytes_with_seed(bytes, 0x517c_c1b7_2722_0a95);
(h1, h2.rotate_left(31) ^ 0xa021_282d_c0b9_ed54)
}
fn hash_bytes_triple(&self, bytes: &[u8]) -> (u64, u64, u64) {
let h1 = self.hash_bytes(bytes);
let h2 = self.hash_bytes_with_seed(bytes, 0x517c_c1b7_2722_0a95);
let h3 = self.hash_bytes_with_seed(bytes, 0x9e37_79b9_7f4a_7c15);
(h1, h2, h3)
}
#[inline]
fn hash_item<T: Hash>(&self, item: &T) -> (u64, u64) {
let mut writer = HashWriter::new();
item.hash(&mut writer);
let bytes = writer.into_bytes();
self.hash_bytes_pair(&bytes)
}
fn name(&self) -> &'static str;
fn instance_token(&self) -> u64 {
0
}
}
#[derive(Debug, Clone, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct DeterministicHasher {
state: u64,
}
impl DeterministicHasher {
const FNV_OFFSET: u64 = 0xcbf2_9ce4_8422_2325;
const FNV_PRIME: u64 = 0x0000_0100_0000_01b3;
#[inline]
pub(crate) fn new() -> Self {
Self {
state: Self::FNV_OFFSET,
}
}
}
impl std::hash::Hasher for DeterministicHasher {
#[inline(always)]
fn write(&mut self, bytes: &[u8]) {
for &byte in bytes {
self.state ^= byte as u64;
self.state = self.state.wrapping_mul(Self::FNV_PRIME);
}
}
#[inline(always)]
fn finish(&self) -> u64 {
self.state
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct StdHasher {
seed: u64,
}
impl StdHasher {
const DEFAULT_SEED: u64 = 0x517c_c1b7_2722_0a95;
#[must_use]
#[inline]
pub fn new() -> Self {
Self {
seed: Self::DEFAULT_SEED,
}
}
#[must_use]
#[inline]
pub fn with_seed(seed: u64) -> Self {
Self { seed }
}
}
impl Default for StdHasher {
fn default() -> Self {
Self::new()
}
}
impl BloomHasher for StdHasher {
#[inline]
fn hash_bytes(&self, bytes: &[u8]) -> u64 {
use std::hash::Hasher;
let mut h = DeterministicHasher::new();
h.write_u64(self.seed);
h.write(bytes);
h.finish()
}
#[inline]
fn hash_bytes_with_seed(&self, bytes: &[u8], seed: u64) -> u64 {
use std::hash::Hasher;
let mut h = DeterministicHasher::new();
h.write_u64(self.seed ^ seed);
h.write(bytes);
h.finish()
}
#[inline]
fn hash_item<T: Hash>(&self, item: &T) -> (u64, u64) {
use std::hash::Hasher;
let mut h1 = DeterministicHasher::new();
h1.write_u64(self.seed);
item.hash(&mut h1);
let h1 = h1.finish();
let mut h2 = DeterministicHasher::new();
h2.write_u64(self.seed ^ 0x9e37_79b9_7f4a_7c15);
item.hash(&mut h2);
let h2 = h2.finish();
(h1, h2.rotate_left(31) ^ 0xa021_282d_c0b9_ed54)
}
#[inline]
fn hash_bytes_pair(&self, bytes: &[u8]) -> (u64, u64) {
let h1 = self.hash_bytes(bytes);
let h2 = self.hash_bytes_with_seed(bytes, 0x9e37_79b9_7f4a_7c15);
(h1, h2.rotate_left(31) ^ 0xa021_282d_c0b9_ed54)
}
#[inline]
fn name(&self) -> &'static str {
"StdHasher"
}
#[inline]
fn instance_token(&self) -> u64 {
self.seed
}
}
impl std::hash::BuildHasher for StdHasher {
type Hasher = DeterministicHasher;
fn build_hasher(&self) -> Self::Hasher {
use std::hash::Hasher;
let mut h = DeterministicHasher::new();
h.write_u64(self.seed);
h
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_determinism() {
let hasher = StdHasher::new();
let data = b"test string";
assert_eq!(hasher.hash_bytes(data), hasher.hash_bytes(data));
}
#[test]
fn test_different_inputs_differ() {
let hasher = StdHasher::new();
assert_ne!(hasher.hash_bytes(b"input1"), hasher.hash_bytes(b"input2"));
}
#[test]
fn test_empty_input_nonzero() {
assert_ne!(StdHasher::new().hash_bytes(b""), 0);
}
#[test]
fn test_single_byte_sensitivity() {
let hasher = StdHasher::new();
assert_ne!(hasher.hash_bytes(b"a"), hasher.hash_bytes(b"b"));
}
#[test]
fn test_different_seeds_produce_different_hashes() {
let data = b"test";
assert_ne!(
StdHasher::with_seed(123).hash_bytes(data),
StdHasher::with_seed(456).hash_bytes(data),
);
}
#[test]
fn test_same_seed_same_results_cross_instance() {
let h1 = StdHasher::with_seed(42).hash_bytes(b"reproducibility");
let h2 = StdHasher::with_seed(42).hash_bytes(b"reproducibility");
assert_eq!(h1, h2);
}
#[test]
fn test_hash_bytes_with_seed_produces_independent_values() {
let hasher = StdHasher::new();
let data = b"test";
let h0 = hasher.hash_bytes_with_seed(data, 0);
let h1 = hasher.hash_bytes_with_seed(data, 42);
let h2 = hasher.hash_bytes_with_seed(data, 999);
assert_ne!(h0, h1);
assert_ne!(h1, h2);
assert_ne!(h0, h2);
}
#[test]
fn test_pair_values_are_distinct() {
let (h1, h2) = StdHasher::new().hash_bytes_pair(b"test");
assert_ne!(h1, h2);
}
#[test]
fn test_pair_is_deterministic() {
let hasher = StdHasher::new();
let (a1, a2) = hasher.hash_bytes_pair(b"test");
let (b1, b2) = hasher.hash_bytes_pair(b"test");
assert_eq!(a1, b1);
assert_eq!(a2, b2);
}
#[test]
fn test_triple_values_are_mutually_distinct() {
let (h1, h2, h3) = StdHasher::new().hash_bytes_triple(b"test");
assert_ne!(h1, h2);
assert_ne!(h2, h3);
assert_ne!(h1, h3);
}
#[test]
fn test_triple_is_deterministic() {
let hasher = StdHasher::new();
let (a1, a2, a3) = hasher.hash_bytes_triple(b"test");
let (b1, b2, b3) = hasher.hash_bytes_triple(b"test");
assert_eq!(a1, b1);
assert_eq!(a2, b2);
assert_eq!(a3, b3);
}
#[test]
fn test_hash_item_string_is_deterministic() {
let hasher = StdHasher::new();
let (a1, a2) = hasher.hash_item(&"hello world".to_string());
let (b1, b2) = hasher.hash_item(&"hello world".to_string());
assert_eq!(a1, b1);
assert_eq!(a2, b2);
}
#[test]
fn test_hash_item_integer_is_deterministic() {
let hasher = StdHasher::new();
let (a1, a2) = hasher.hash_item(&42u64);
let (b1, b2) = hasher.hash_item(&42u64);
assert_eq!(a1, b1);
assert_eq!(a2, b2);
}
#[test]
fn test_hash_item_pair_values_are_distinct() {
let hasher = StdHasher::new();
let (h1, h2) = hasher.hash_item(&"test".to_string());
assert_ne!(h1, h2);
}
#[test]
fn test_hash_item_different_items_differ() {
let hasher = StdHasher::new();
let (a1, _) = hasher.hash_item(&"foo".to_string());
let (b1, _) = hasher.hash_item(&"bar".to_string());
assert_ne!(a1, b1);
}
#[test]
fn test_hash_item_integer_differs_by_value() {
let hasher = StdHasher::new();
let (h1, _) = hasher.hash_item(&1u64);
let (h2, _) = hasher.hash_item(&2u64);
assert_ne!(h1, h2);
}
#[test]
fn test_hash_item_large_key_no_panic() {
let hasher = StdHasher::new();
let large_key = "x".repeat(256);
let (h1, h2) = hasher.hash_item(&large_key);
assert_ne!(h1, h2);
let (h3, h4) = hasher.hash_item(&large_key);
assert_eq!(h1, h3);
assert_eq!(h2, h4);
}
#[test]
fn test_hash_item_consistent_with_hash_bytes_pair() {
let hasher = StdHasher::new();
let item = "determinism check".to_string();
let (item_h1, item_h2) = hasher.hash_item(&item);
let mut writer = HashWriter::new();
item.hash(&mut writer);
let bytes = writer.into_bytes();
let (bytes_h1, bytes_h2) = hasher.hash_bytes_pair(&bytes);
assert_eq!(item_h1, bytes_h1);
assert_eq!(item_h2, bytes_h2);
}
#[test]
fn test_pair_statistical_independence() {
let hasher = StdHasher::new();
let (xor_h1, xor_h2) = (0..200u64).fold((0u64, 0u64), |(x1, x2), i| {
let data = format!("item_{}", i);
let (h1, h2) = hasher.hash_bytes_pair(data.as_bytes());
(x1 ^ h1, x2 ^ h2)
});
assert_ne!(xor_h1, xor_h2, "h1 and h2 streams must be independent");
}
#[test]
fn test_avalanche_single_bit_flip() {
let hasher = StdHasher::new();
let data1 = b"test data!";
let mut data2 = *b"test data!";
data2[0] ^= 1;
let h1 = hasher.hash_bytes(data1);
let h2 = hasher.hash_bytes(&data2);
let changed_bits = (h1 ^ h2).count_ones();
assert!(
(16..=48).contains(&changed_bits),
"Avalanche: {} bits changed (expected 16–48)",
changed_bits
);
}
#[test]
fn test_name() {
assert_eq!(StdHasher::new().name(), "StdHasher");
}
#[test]
fn test_clone_produces_identical_results() {
let h1 = StdHasher::with_seed(999);
let h2 = h1.clone();
assert_eq!(h1.hash_bytes(b"clone test"), h2.hash_bytes(b"clone test"));
}
#[test]
fn test_send_sync_bounds() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<StdHasher>();
}
#[test]
fn test_large_byte_slice() {
let hasher = StdHasher::new();
let large = vec![0xabu8; 65_536];
let hash = hasher.hash_bytes(&large);
assert_ne!(hash, 0);
}
#[test]
fn test_unicode_bytes() {
let hasher = StdHasher::new();
let s = "こんにちは 🦀";
let h1 = hasher.hash_bytes(s.as_bytes());
let h2 = hasher.hash_bytes(s.as_bytes());
assert_eq!(h1, h2);
}
#[test]
fn test_consecutive_inputs_differ() {
let hasher = StdHasher::new();
assert_ne!(hasher.hash_bytes(b"aaaa"), hasher.hash_bytes(b"aaab"));
}
#[test]
fn test_build_hasher_produces_consistent_result() {
use std::hash::{BuildHasher, Hasher};
let builder = StdHasher::new();
let mut h1 = builder.build_hasher();
let mut h2 = builder.build_hasher();
h1.write(b"test");
h2.write(b"test");
assert_eq!(h1.finish(), h2.finish());
}
#[test]
fn test_integration_with_double_hashing() {
use crate::hash::strategies::{DoubleHashing, HashStrategy};
let hasher = StdHasher::new();
let (h1, h2) = hasher.hash_bytes_pair(b"test");
let indices = DoubleHashing.generate_indices(h1, h2, 0, 7, 1000);
assert_eq!(indices.len(), 7);
assert!(indices.iter().all(|&i| i < 1000));
}
#[test]
fn test_integration_with_enhanced_double_hashing() {
use crate::hash::strategies::{EnhancedDoubleHashing, HashStrategy};
let hasher = StdHasher::new();
let (h1, h2) = hasher.hash_bytes_pair(b"test");
let indices = EnhancedDoubleHashing.generate_indices(h1, h2, 0, 7, 1000);
assert_eq!(indices.len(), 7);
assert!(indices.iter().all(|&i| i < 1000));
}
#[test]
fn test_integration_with_triple_hashing() {
use crate::hash::strategies::{HashStrategy, TripleHashing};
let hasher = StdHasher::new();
let (h1, h2, h3) = hasher.hash_bytes_triple(b"test");
let indices = TripleHashing.generate_indices(h1, h2, h3, 7, 1000);
assert_eq!(indices.len(), 7);
assert!(indices.iter().all(|&i| i < 1000));
}
#[test]
fn test_hash_item_with_strategy() {
use crate::hash::strategies::{EnhancedDoubleHashing, HashStrategy};
let hasher = StdHasher::new();
let (h1, h2) = hasher.hash_item(&"integration test".to_string());
let indices = EnhancedDoubleHashing.generate_indices(h1, h2, 0, 7, 10_000);
assert_eq!(indices.len(), 7);
assert!(indices.iter().all(|&i| i < 10_000));
}
#[test]
fn instance_token_differs_for_different_seeds() {
let h1 = StdHasher::with_seed(1);
let h2 = StdHasher::with_seed(2);
assert_ne!(
h1.instance_token(),
h2.instance_token(),
"different seeds must produce different instance tokens"
);
}
#[test]
fn instance_token_identical_for_same_seed() {
let h1 = StdHasher::with_seed(42);
let h2 = StdHasher::with_seed(42);
assert_eq!(
h1.instance_token(),
h2.instance_token(),
"identical seeds must produce identical instance tokens"
);
}
#[test]
fn instance_token_default_seed_is_consistent() {
assert_eq!(
StdHasher::new().instance_token(),
StdHasher::new().instance_token()
);
}
#[test]
fn instance_token_reflects_seed_value() {
let seed = 0xDEAD_BEEF_CAFE_1234_u64;
assert_eq!(StdHasher::with_seed(seed).instance_token(), seed);
}
}