pub trait ClojureHash {
fn clojure_hash(&self) -> u32;
}
pub fn murmur3_mix(mut h: u32) -> u32 {
h ^= h >> 16;
h = h.wrapping_mul(0x85eb_ca6b);
h ^= h >> 13;
h = h.wrapping_mul(0xc2b2_ae35);
h ^= h >> 16;
h
}
pub fn hash_i64(n: i64) -> u32 {
let lo = n as u32;
let hi = (n >> 32) as u32;
murmur3_mix(lo ^ hi)
}
pub fn hash_u128(n: u128) -> u32 {
let a = n as u32;
let b = ((n >> 32) & 0xFFFFFFFF) as u32;
let c = ((n >> 64) & 0xFFFFFFFF) as u32;
let d = ((n >> 96) & 0xFFFFFFFF) as u32;
murmur3_mix(a ^ b ^ c ^ d)
}
pub fn hash_string(s: &str) -> u32 {
let mut h: i32 = 0;
for ch in s.chars() {
let mut buf = [0u16; 2];
for unit in ch.encode_utf16(&mut buf) {
h = h.wrapping_mul(31).wrapping_add(*unit as i32);
}
}
murmur3_mix(h as u32)
}
pub fn hash_combine_unordered(a: u32, b: u32) -> u32 {
a ^ b
}
pub fn hash_combine_ordered(acc: u32, h: u32) -> u32 {
murmur3_mix(acc.wrapping_mul(31).wrapping_add(h))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_hash_i64_zero() {
let h1 = hash_i64(0);
let h2 = hash_i64(0);
assert_eq!(h1, h2);
}
#[test]
fn test_hash_string_deterministic() {
let h1 = hash_string("hello");
let h2 = hash_string("hello");
assert_eq!(h1, h2);
assert_ne!(hash_string("hello"), hash_string("world"));
}
#[test]
fn test_murmur3_avalanche() {
let h1 = murmur3_mix(0);
let h2 = murmur3_mix(1);
assert_ne!(h1, h2);
}
}