#[inline(always)]
fn rot(x: u32, k: u32) -> u32 {
x.rotate_left(k)
}
#[inline(always)]
fn mix(a: &mut u32, b: &mut u32, c: &mut u32) {
*a = a.wrapping_sub(*c); *a ^= rot(*c, 4); *c = c.wrapping_add(*b);
*b = b.wrapping_sub(*a); *b ^= rot(*a, 6); *a = a.wrapping_add(*c);
*c = c.wrapping_sub(*b); *c ^= rot(*b, 8); *b = b.wrapping_add(*a);
*a = a.wrapping_sub(*c); *a ^= rot(*c, 16); *c = c.wrapping_add(*b);
*b = b.wrapping_sub(*a); *b ^= rot(*a, 19); *a = a.wrapping_add(*c);
*c = c.wrapping_sub(*b); *c ^= rot(*b, 4); *b = b.wrapping_add(*a);
}
#[inline(always)]
fn final_mix(a: &mut u32, b: &mut u32, c: &mut u32) {
*c ^= *b; *c = c.wrapping_sub(rot(*b, 14));
*a ^= *c; *a = a.wrapping_sub(rot(*c, 11));
*b ^= *a; *b = b.wrapping_sub(rot(*a, 25));
*c ^= *b; *c = c.wrapping_sub(rot(*b, 16));
*a ^= *c; *a = a.wrapping_sub(rot(*c, 4));
*b ^= *a; *b = b.wrapping_sub(rot(*a, 14));
*c ^= *b; *c = c.wrapping_sub(rot(*b, 24));
}
pub fn hashlittle2(key: &[u8], pc_init: u32, pb_init: u32) -> (u32, u32) {
let length = key.len();
let init = 0xdeadbeef_u32
.wrapping_add(length as u32)
.wrapping_add(pc_init);
let mut a = init;
let mut b = init;
let mut c = init.wrapping_add(pb_init);
let mut k = key;
#[cfg(target_endian = "little")]
{
if key.as_ptr() as usize & 0x3 == 0 {
while k.len() > 12 {
a = a.wrapping_add(u32::from_ne_bytes([k[0], k[1], k[2], k[3]]));
b = b.wrapping_add(u32::from_ne_bytes([k[4], k[5], k[6], k[7]]));
c = c.wrapping_add(u32::from_ne_bytes([k[8], k[9], k[10], k[11]]));
mix(&mut a, &mut b, &mut c);
k = &k[12..];
}
return hashlittle2_tail(k, a, b, c);
}
}
while k.len() > 12 {
a = a.wrapping_add(k[0] as u32);
a = a.wrapping_add((k[1] as u32) << 8);
a = a.wrapping_add((k[2] as u32) << 16);
a = a.wrapping_add((k[3] as u32) << 24);
b = b.wrapping_add(k[4] as u32);
b = b.wrapping_add((k[5] as u32) << 8);
b = b.wrapping_add((k[6] as u32) << 16);
b = b.wrapping_add((k[7] as u32) << 24);
c = c.wrapping_add(k[8] as u32);
c = c.wrapping_add((k[9] as u32) << 8);
c = c.wrapping_add((k[10] as u32) << 16);
c = c.wrapping_add((k[11] as u32) << 24);
mix(&mut a, &mut b, &mut c);
k = &k[12..];
}
hashlittle2_tail(k, a, b, c)
}
#[inline(always)]
fn hashlittle2_tail(k: &[u8], mut a: u32, mut b: u32, mut c: u32) -> (u32, u32) {
match k.len() {
12 => {
c = c.wrapping_add((k[11] as u32) << 24);
c = c.wrapping_add((k[10] as u32) << 16);
c = c.wrapping_add((k[9] as u32) << 8);
c = c.wrapping_add( k[8] as u32);
b = b.wrapping_add((k[7] as u32) << 24);
b = b.wrapping_add((k[6] as u32) << 16);
b = b.wrapping_add((k[5] as u32) << 8);
b = b.wrapping_add( k[4] as u32);
a = a.wrapping_add((k[3] as u32) << 24);
a = a.wrapping_add((k[2] as u32) << 16);
a = a.wrapping_add((k[1] as u32) << 8);
a = a.wrapping_add( k[0] as u32);
}
11 => {
c = c.wrapping_add((k[10] as u32) << 16);
c = c.wrapping_add((k[9] as u32) << 8);
c = c.wrapping_add( k[8] as u32);
b = b.wrapping_add((k[7] as u32) << 24);
b = b.wrapping_add((k[6] as u32) << 16);
b = b.wrapping_add((k[5] as u32) << 8);
b = b.wrapping_add( k[4] as u32);
a = a.wrapping_add((k[3] as u32) << 24);
a = a.wrapping_add((k[2] as u32) << 16);
a = a.wrapping_add((k[1] as u32) << 8);
a = a.wrapping_add( k[0] as u32);
}
10 => {
c = c.wrapping_add((k[9] as u32) << 8);
c = c.wrapping_add( k[8] as u32);
b = b.wrapping_add((k[7] as u32) << 24);
b = b.wrapping_add((k[6] as u32) << 16);
b = b.wrapping_add((k[5] as u32) << 8);
b = b.wrapping_add( k[4] as u32);
a = a.wrapping_add((k[3] as u32) << 24);
a = a.wrapping_add((k[2] as u32) << 16);
a = a.wrapping_add((k[1] as u32) << 8);
a = a.wrapping_add( k[0] as u32);
}
9 => {
c = c.wrapping_add( k[8] as u32);
b = b.wrapping_add((k[7] as u32) << 24);
b = b.wrapping_add((k[6] as u32) << 16);
b = b.wrapping_add((k[5] as u32) << 8);
b = b.wrapping_add( k[4] as u32);
a = a.wrapping_add((k[3] as u32) << 24);
a = a.wrapping_add((k[2] as u32) << 16);
a = a.wrapping_add((k[1] as u32) << 8);
a = a.wrapping_add( k[0] as u32);
}
8 => {
b = b.wrapping_add((k[7] as u32) << 24);
b = b.wrapping_add((k[6] as u32) << 16);
b = b.wrapping_add((k[5] as u32) << 8);
b = b.wrapping_add( k[4] as u32);
a = a.wrapping_add((k[3] as u32) << 24);
a = a.wrapping_add((k[2] as u32) << 16);
a = a.wrapping_add((k[1] as u32) << 8);
a = a.wrapping_add( k[0] as u32);
}
7 => {
b = b.wrapping_add((k[6] as u32) << 16);
b = b.wrapping_add((k[5] as u32) << 8);
b = b.wrapping_add( k[4] as u32);
a = a.wrapping_add((k[3] as u32) << 24);
a = a.wrapping_add((k[2] as u32) << 16);
a = a.wrapping_add((k[1] as u32) << 8);
a = a.wrapping_add( k[0] as u32);
}
6 => {
b = b.wrapping_add((k[5] as u32) << 8);
b = b.wrapping_add( k[4] as u32);
a = a.wrapping_add((k[3] as u32) << 24);
a = a.wrapping_add((k[2] as u32) << 16);
a = a.wrapping_add((k[1] as u32) << 8);
a = a.wrapping_add( k[0] as u32);
}
5 => {
b = b.wrapping_add( k[4] as u32);
a = a.wrapping_add((k[3] as u32) << 24);
a = a.wrapping_add((k[2] as u32) << 16);
a = a.wrapping_add((k[1] as u32) << 8);
a = a.wrapping_add( k[0] as u32);
}
4 => {
a = a.wrapping_add((k[3] as u32) << 24);
a = a.wrapping_add((k[2] as u32) << 16);
a = a.wrapping_add((k[1] as u32) << 8);
a = a.wrapping_add( k[0] as u32);
}
3 => {
a = a.wrapping_add((k[2] as u32) << 16);
a = a.wrapping_add((k[1] as u32) << 8);
a = a.wrapping_add( k[0] as u32);
}
2 => {
a = a.wrapping_add((k[1] as u32) << 8);
a = a.wrapping_add( k[0] as u32);
}
1 => {
a = a.wrapping_add( k[0] as u32);
}
0 => return (c, b),
_ => unreachable!(),
}
final_mix(&mut a, &mut b, &mut c);
(c, b)
}
pub fn hash64(data: &[u8]) -> u64 {
let (pc, pb) = hashlittle2(data, 0, 0);
((pc as u64) << 32) | (pb as u64)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_empty() {
let (pc, pb) = hashlittle2(b"", 0, 0);
assert_eq!(pc, 0xdeadbeef);
assert_eq!(pb, 0xdeadbeef);
}
#[test]
fn test_deterministic() {
let h1 = hash64(b"MESSAGE=Hello, world!");
let h2 = hash64(b"MESSAGE=Hello, world!");
assert_eq!(h1, h2);
}
#[test]
fn test_different_inputs_differ() {
assert_ne!(hash64(b"MESSAGE=a"), hash64(b"MESSAGE=b"));
assert_ne!(hash64(b"PRIORITY=6"), hash64(b"PRIORITY=7"));
}
#[test]
fn test_known_hash() {
let h = hash64(b"Four score and seven years ago");
assert_ne!(h, 0);
assert_eq!(h, hash64(b"Four score and seven years ago"));
}
#[test]
fn test_fast_path_matches_slow_path() {
fn hashlittle2_slow(key: &[u8], pc_init: u32, pb_init: u32) -> (u32, u32) {
let length = key.len();
let init = 0xdeadbeef_u32
.wrapping_add(length as u32)
.wrapping_add(pc_init);
let mut a = init;
let mut b = init;
let mut c = init.wrapping_add(pb_init);
let mut k = key;
while k.len() > 12 {
a = a.wrapping_add(k[0] as u32);
a = a.wrapping_add((k[1] as u32) << 8);
a = a.wrapping_add((k[2] as u32) << 16);
a = a.wrapping_add((k[3] as u32) << 24);
b = b.wrapping_add(k[4] as u32);
b = b.wrapping_add((k[5] as u32) << 8);
b = b.wrapping_add((k[6] as u32) << 16);
b = b.wrapping_add((k[7] as u32) << 24);
c = c.wrapping_add(k[8] as u32);
c = c.wrapping_add((k[9] as u32) << 8);
c = c.wrapping_add((k[10] as u32) << 16);
c = c.wrapping_add((k[11] as u32) << 24);
mix(&mut a, &mut b, &mut c);
k = &k[12..];
}
hashlittle2_tail(k, a, b, c)
}
let base_data: Vec<u8> = (0u8..=255).cycle().take(64).collect();
for len in 0..=50 {
let data = &base_data[..len];
let fast = hashlittle2(data, 0, 0);
let slow = hashlittle2_slow(data, 0, 0);
assert_eq!(
fast, slow,
"mismatch for aligned data of length {}",
len
);
let fast2 = hashlittle2(data, 42, 99);
let slow2 = hashlittle2_slow(data, 42, 99);
assert_eq!(
fast2, slow2,
"mismatch for aligned data of length {} with init (42, 99)",
len
);
}
let mut padded = vec![0u8; 68]; for i in 0..64 {
padded[i + 1] = base_data[i];
}
for offset in 1..=3 {
for len in 0..=50 {
let data = &padded[offset..offset + len];
let result = hashlittle2(data, 0, 0);
let slow = hashlittle2_slow(data, 0, 0);
assert_eq!(
result, slow,
"mismatch for unaligned (offset={}) data of length {}",
offset, len
);
}
}
}
}