use std::hash::Hasher;
use crate::hash::DEFAULT_UPDATE_SEED;
const C1: u64 = 0x87c37b91114253d5;
const C2: u64 = 0x4cf5ad432745937f;
#[derive(Debug)]
pub struct MurmurHash3X64128 {
h1: u64,
h2: u64,
total: u64,
buf: [u8; 16],
buf_len: usize,
}
impl MurmurHash3X64128 {
pub fn with_seed(seed: u64) -> Self {
MurmurHash3X64128 {
h1: seed,
h2: seed,
total: 0,
buf: [0; 16],
buf_len: 0,
}
}
pub fn finish128(&self) -> (u64, u64) {
let mut h1 = self.h1;
let mut h2 = self.h2;
let total = self.total + self.buf_len as u64;
let rem = self.buf_len;
if rem > 0 {
if rem > 8 {
let mut k2 = super::read_u64_le(&self.buf[8..rem]);
k2 = k2.wrapping_mul(C2);
k2 = k2.rotate_left(33);
k2 = k2.wrapping_mul(C1);
h2 ^= k2;
}
let k1_len = rem.min(8);
let mut k1 = super::read_u64_le(&self.buf[..k1_len]);
k1 = k1.wrapping_mul(C1);
k1 = k1.rotate_left(31);
k1 = k1.wrapping_mul(C2);
h1 ^= k1;
}
h1 ^= total;
h2 ^= total;
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);
(h1, h2)
}
#[inline]
fn update(&mut self, mut k1: u64, mut k2: u64) {
k1 = k1.wrapping_mul(C1);
k1 = k1.rotate_left(31);
k1 = k1.wrapping_mul(C2);
self.h1 ^= k1;
self.h1 = self.h1.rotate_left(27);
self.h1 = self.h1.wrapping_add(self.h2);
self.h1 = self.h1.wrapping_mul(5).wrapping_add(0x52dce729);
k2 = k2.wrapping_mul(C2);
k2 = k2.rotate_left(33);
k2 = k2.wrapping_mul(C1);
self.h2 ^= k2;
self.h2 = self.h2.rotate_left(31);
self.h2 = self.h2.wrapping_add(self.h1);
self.h2 = self.h2.wrapping_mul(5).wrapping_add(0x38495ab5);
self.total += 16;
}
}
impl Default for MurmurHash3X64128 {
fn default() -> Self {
Self::with_seed(DEFAULT_UPDATE_SEED)
}
}
impl Hasher for MurmurHash3X64128 {
fn finish(&self) -> u64 {
self.finish128().0
}
fn write(&mut self, mut bytes: &[u8]) {
if self.buf_len + bytes.len() < 16 {
self.buf[self.buf_len..self.buf_len + bytes.len()].copy_from_slice(bytes);
self.buf_len += bytes.len();
return;
}
if self.buf_len != 0 {
let wanted = 16 - self.buf_len;
self.buf[self.buf_len..].copy_from_slice(&bytes[..wanted]);
let k1 = super::read_u64_le(&self.buf[0..8]);
let k2 = super::read_u64_le(&self.buf[8..16]);
self.update(k1, k2);
bytes = &bytes[wanted..];
self.buf_len = 0;
}
let blocks = bytes.len() >> 4;
for i in 0..blocks {
let lo = i << 4;
let mi = lo + 8;
let hi = mi + 8;
let k1 = super::read_u64_le(&bytes[lo..mi]);
let k2 = super::read_u64_le(&bytes[mi..hi]);
self.update(k1, k2);
}
let len = bytes.len() % 16;
if len > 0 {
self.buf[0..len].copy_from_slice(&bytes[blocks << 4..]);
self.buf_len = len;
}
}
}
#[inline]
fn fmix64(mut k: u64) -> u64 {
k ^= k >> 33;
k = k.wrapping_mul(0xff51afd7ed558ccd);
k ^= k >> 33;
k = k.wrapping_mul(0xc4ceb9fe1a85ec53);
k ^ (k >> 33)
}
#[cfg(test)]
mod tests {
use super::*;
fn murmurhash3_x64_128(key: &[u8], seed: u64) -> (u64, u64) {
let mut hasher = MurmurHash3X64128::with_seed(seed);
hasher.write(key);
hasher.finish128()
}
#[test]
fn test_remainder() {
let key = "The quick brown fox jumps over the lazy dog";
let (h1, h2) = murmurhash3_x64_128(key.as_bytes(), 0);
assert_eq!(h1, 0xe34bbc7bbc071b6c);
assert_eq!(h2, 0x7a433ca9c49a9347);
let key = "The quick brown fox jumps over the lazy eog";
let (h1, h2) = murmurhash3_x64_128(key.as_bytes(), 0);
assert_eq!(h1, 0x362108102c62d1c9);
assert_eq!(h2, 0x3285cd100292b305);
let key = "The quick brown fox jumps over the lazy dogdogdog";
let (h1, h2) = murmurhash3_x64_128(key.as_bytes(), 0);
assert_eq!(h1, 0x9c8205300e612fc4);
assert_eq!(h2, 0xcbc0af6136aa3df9);
let key = "The quick brown fox jumps over the lazy1";
let (h1, h2) = murmurhash3_x64_128(key.as_bytes(), 0);
assert_eq!(h1, 0xe3301a827e5cdfe3);
assert_eq!(h2, 0xbdbf05f8da0f0392);
let key = "The quick brown fox jumps over t";
let (h1, h2) = murmurhash3_x64_128(key.as_bytes(), 0);
assert_eq!(h1, 0xdf6af91bb29bdacf);
assert_eq!(h2, 0x91a341c58df1f3a6);
let key = [
0x54, 0x68, 0x65, 0x20, 0x71, 0x75, 0x69, 0x63, 0x6b, 0x20, 0x62, 0x72, 0x6f, 0x77,
0x6e, 0x20, 0x66, 0x6f, 0x78, 0x20, 0x6a, 0x75, 0x6d, 0x70, 0x73, 0x20, 0x6f, 0x76,
0x65, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x61, 0x7a, 0x79, 0x20, 0x64, 0x6f,
0x67, 0xff, 0x64, 0x6f, 0x67, 0x00,
];
let (h1, h2) = murmurhash3_x64_128(&key, 0);
assert_eq!(h1, 0xe88abda785929c9e);
assert_eq!(h2, 0x96b98587cacc83d6);
}
}