pub fn murmurhash3_seed(key: &str, seed: u32) -> u32 {
let key_bytes = key.as_bytes();
let mut h1: u32 = seed;
let c1: u32 = 0xcc9e_2d51;
let c2: u32 = 0x1b87_3593;
let len = key_bytes.len();
let blocks = len / 4;
for i in 0..blocks {
let mut k1: u32 = (key_bytes[i * 4] as u32)
| ((key_bytes[i * 4 + 1] as u32) << 8)
| ((key_bytes[i * 4 + 2] as u32) << 16)
| ((key_bytes[i * 4 + 3] as u32) << 24);
k1 = k1.wrapping_mul(c1);
k1 = (k1 << 15) | (k1 >> 17);
k1 = k1.wrapping_mul(c2);
h1 ^= k1;
h1 = (h1 << 13) | (h1 >> 19);
h1 = h1.wrapping_mul(5).wrapping_add(0xe654_6b64);
}
let mut k1: u32 = 0;
let remainder = len % 4;
let offset = blocks * 4;
if remainder == 3 {
k1 ^= (key_bytes[offset + 2] as u32) << 16;
}
if remainder >= 2 {
k1 ^= (key_bytes[offset + 1] as u32) << 8;
}
if remainder >= 1 {
k1 ^= key_bytes[offset] as u32;
k1 = k1.wrapping_mul(c1);
k1 = (k1 << 15) | (k1 >> 17);
k1 = k1.wrapping_mul(c2);
h1 ^= k1;
}
h1 ^= len as u32;
h1 ^= h1 >> 16;
h1 = h1.wrapping_mul(0x85eb_ca6b);
h1 ^= h1 >> 13;
h1 = h1.wrapping_mul(0xc2b2_ae35);
h1 ^= h1 >> 16;
h1
}
pub fn murmurhash3(key: &str) -> u32 {
murmurhash3_seed(key, 0)
}
pub fn bucket_of(seed: &str) -> u32 {
murmurhash3(seed) % 100
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn twelve_canonical_vectors() {
let vectors: &[(&str, u32, u32)] = &[
("", 0, 0),
("a", 1009084850, 50),
("ab", 2613040991, 91),
("abc", 3017643002, 2),
("abcd", 1139631978, 78),
("hello", 613153351, 51),
("feature_x.user-123", 1651817416, 16),
("new_checkout.user-42", 2069292701, 1),
("dark_mode.alice", 325027212, 12),
("beta.bob", 3544697655, 55),
("my_flag.00000000-0000-0000-0000-000000000000", 2457227110, 10),
(
"flag.user_with_a_fairly_long_identifier_string_1234567890",
1645539598,
98,
),
];
for (input, hash, mod100) in vectors {
assert_eq!(murmurhash3(input), *hash, "hash mismatch for {input:?}");
assert_eq!(bucket_of(input), *mod100, "bucket mismatch for {input:?}");
}
}
}