use std::hash::{BuildHasher, Hasher};
use xxhash_rust::xxh3::{xxh3_64, xxh3_64_with_seed};
pub const GXHASH_MIN_SIZE: usize = 16;
const FNV_OFFSET: u64 = 0xcbf29ce484222325;
const FNV_PRIME: u64 = 0x100000001b3;
#[inline]
pub fn safe_hash(bytes: &[u8]) -> u64 {
if bytes.len() >= GXHASH_MIN_SIZE {
gxhash::gxhash64(bytes, 0)
} else {
xxh3_64(bytes)
}
}
#[inline]
pub fn safe_hash_with_seed(bytes: &[u8], seed: u64) -> u64 {
if bytes.len() >= GXHASH_MIN_SIZE {
gxhash::gxhash64(bytes, seed as i64)
} else {
xxh3_64_with_seed(bytes, seed)
}
}
#[inline]
pub fn fnv1a(bytes: &[u8]) -> u64 {
let mut hash = FNV_OFFSET;
for &byte in bytes {
hash ^= byte as u64;
hash = hash.wrapping_mul(FNV_PRIME);
}
hash
}
#[inline]
pub fn fnv1a_with_seed(bytes: &[u8], seed: u64) -> u64 {
let mut hash = FNV_OFFSET ^ seed.wrapping_mul(FNV_PRIME);
for &byte in bytes {
hash ^= byte as u64;
hash = hash.wrapping_mul(FNV_PRIME);
}
hash
}
#[derive(Clone, Default)]
pub struct SafeGxBuildHasher;
impl BuildHasher for SafeGxBuildHasher {
type Hasher = SafeGxHasher;
fn build_hasher(&self) -> Self::Hasher {
SafeGxHasher {
buffer: Vec::with_capacity(64),
}
}
}
pub struct SafeGxHasher {
buffer: Vec<u8>,
}
impl Hasher for SafeGxHasher {
fn write(&mut self, bytes: &[u8]) {
self.buffer.extend_from_slice(bytes);
}
fn finish(&self) -> u64 {
safe_hash(&self.buffer)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_safe_hash_short() {
let hash1 = safe_hash(b"hello");
let hash2 = safe_hash(b"hello");
assert_eq!(hash1, hash2);
let hash3 = safe_hash(b"world");
assert_ne!(hash1, hash3);
}
#[test]
fn test_safe_hash_long() {
let long_input = b"this is a longer string that exceeds 16 bytes";
let hash1 = safe_hash(long_input);
let hash2 = safe_hash(long_input);
assert_eq!(hash1, hash2);
}
#[test]
fn test_safe_hash_boundary() {
let exact = b"0123456789abcdef";
assert_eq!(exact.len(), 16);
let hash1 = safe_hash(exact);
let hash2 = safe_hash(exact);
assert_eq!(hash1, hash2);
let short = b"0123456789abcde";
assert_eq!(short.len(), 15);
let hash3 = safe_hash(short);
let hash4 = safe_hash(short);
assert_eq!(hash3, hash4);
}
#[test]
fn test_safe_hash_with_seed() {
let bytes = b"test";
let hash1 = safe_hash_with_seed(bytes, 0);
let hash2 = safe_hash_with_seed(bytes, 1);
assert_ne!(hash1, hash2);
}
#[test]
fn test_safe_gx_hasher() {
use std::hash::Hash;
let mut hasher1 = SafeGxBuildHasher.build_hasher();
"hello".hash(&mut hasher1);
let hash1 = hasher1.finish();
let mut hasher2 = SafeGxBuildHasher.build_hasher();
"hello".hash(&mut hasher2);
let hash2 = hasher2.finish();
assert_eq!(hash1, hash2);
}
}