const C1: u64 = 0x87c3_7b91_1142_53d5;
const C2: u64 = 0x4cf5_ad43_2745_937f;
#[must_use]
pub fn murmur3_x64_128(data: &[u8], seed: u32) -> crate::Digest<16> {
let mut h1 = u64::from(seed);
let mut h2 = u64::from(seed);
let body_len = data.len() - (data.len() & 15);
for block in data[..body_len].chunks_exact(16) {
let mut k1 = u64::from_le_bytes(block[0..8].try_into().expect("8 bytes"));
let mut k2 = u64::from_le_bytes(block[8..16].try_into().expect("8 bytes"));
k1 = k1.wrapping_mul(C1);
k1 = k1.rotate_left(31);
k1 = k1.wrapping_mul(C2);
h1 ^= k1;
h1 = h1.rotate_left(27);
h1 = h1.wrapping_add(h2);
h1 = h1.wrapping_mul(5).wrapping_add(0x52dc_e729);
k2 = k2.wrapping_mul(C2);
k2 = k2.rotate_left(33);
k2 = k2.wrapping_mul(C1);
h2 ^= k2;
h2 = h2.rotate_left(31);
h2 = h2.wrapping_add(h1);
h2 = h2.wrapping_mul(5).wrapping_add(0x3849_5ab5);
}
let tail = &data[body_len..];
let tail_len = tail.len() & 15;
let mut k1: u64 = 0;
let mut k2: u64 = 0;
for (i, &byte) in tail[..tail_len].iter().enumerate().rev() {
let place = u64::from(byte) << ((i % 8) * 8);
if i >= 8 {
k2 ^= place;
} else {
k1 ^= place;
}
}
if tail_len > 8 {
k2 = k2.wrapping_mul(C2);
k2 = k2.rotate_left(33);
k2 = k2.wrapping_mul(C1);
h2 ^= k2;
}
if tail_len > 0 {
k1 = k1.wrapping_mul(C1);
k1 = k1.rotate_left(31);
k1 = k1.wrapping_mul(C2);
h1 ^= k1;
}
h1 ^= data.len() as u64;
h2 ^= data.len() as u64;
h1 = h1.wrapping_add(h2);
h2 = h2.wrapping_add(h1);
h1 = fmix64(h1);
h2 = fmix64(h2);
h1 = h1.wrapping_add(h2);
h2 = h2.wrapping_add(h1);
let mut out = [0u8; 16];
out[..8].copy_from_slice(&h1.to_le_bytes());
out[8..].copy_from_slice(&h2.to_le_bytes());
crate::Digest::from_bytes(out)
}
fn fmix64(mut k: u64) -> u64 {
k ^= k >> 33;
k = k.wrapping_mul(0xff51_afd7_ed55_8ccd);
k ^= k >> 33;
k = k.wrapping_mul(0xc4ce_b9fe_1a85_ec53);
k ^= k >> 33;
k
}
#[cfg(test)]
mod tests {
use super::murmur3_x64_128;
#[test]
fn reference_vectors() {
assert_eq!(
murmur3_x64_128(b"", 0).to_string(),
"00000000000000000000000000000000"
);
assert_eq!(
murmur3_x64_128(b"hello", 0).to_string(),
"029bbd41b3a7d8cb191dae486a901e5b"
);
assert_eq!(
murmur3_x64_128(b"abc", 0).to_string(),
"6778ad3f3f3f96b4522dca264174a23b"
);
assert_eq!(
murmur3_x64_128(b"foo", 0).to_string(),
"6145f501578671e2877dba2be487af7e"
);
assert_eq!(
murmur3_x64_128(b"foobar", 0).to_string(),
"455ac81671aed2bdafd6f8bae055a274"
);
assert_eq!(
murmur3_x64_128(b"The quick brown fox jumps over the lazy dog", 0).to_string(),
"6c1b07bc7bbc4be347939ac4a93c437a"
);
let bytes33: Vec<u8> = (0..33).collect();
assert_eq!(
murmur3_x64_128(&bytes33, 1).to_string(),
"df0c60f0aaff3b9221e7844dad044ab3"
);
let bytes17: Vec<u8> = (0..17).collect();
assert_eq!(
murmur3_x64_128(&bytes17, 42).to_string(),
"fba12a339df8088f6cdf19cd98b42b59"
);
}
}