const P1: u64 = 0x9E37_79B1_85EB_CA87;
const P2: u64 = 0xC2B2_AE3D_27D4_EB4F;
const P3: u64 = 0x1656_67B1_9E37_79F9;
const P4: u64 = 0x85EB_CA77_C2B2_AE63;
const P5: u64 = 0x27D4_EB2F_1656_67C5;
#[must_use]
pub fn xxh64(data: &[u8], seed: u64) -> u64 {
let len = data.len();
let mut chunks = data.chunks_exact(32);
let mut h = if len >= 32 {
let mut v1 = seed.wrapping_add(P1).wrapping_add(P2);
let mut v2 = seed.wrapping_add(P2);
let mut v3 = seed;
let mut v4 = seed.wrapping_sub(P1);
for block in chunks.by_ref() {
let block: [u8; 32] = block.try_into().expect("chunks_exact yields 32");
v1 = round(
v1,
u64::from_le_bytes(block[0..8].try_into().expect("8 bytes")),
);
v2 = round(
v2,
u64::from_le_bytes(block[8..16].try_into().expect("8 bytes")),
);
v3 = round(
v3,
u64::from_le_bytes(block[16..24].try_into().expect("8 bytes")),
);
v4 = round(
v4,
u64::from_le_bytes(block[24..32].try_into().expect("8 bytes")),
);
}
let mut h = v1
.rotate_left(1)
.wrapping_add(v2.rotate_left(7))
.wrapping_add(v3.rotate_left(12))
.wrapping_add(v4.rotate_left(18));
for v in [v1, v2, v3, v4] {
h ^= round(0, v);
h = h.wrapping_mul(P1).wrapping_add(P4);
}
h
} else {
seed.wrapping_add(P5)
};
h = h.wrapping_add(len as u64);
let rem = chunks.remainder();
let mut rem = rem;
while rem.len() >= 8 {
let lane = u64::from_le_bytes(rem[..8].try_into().expect("8 bytes"));
h ^= round(0, lane);
h = h.rotate_left(27).wrapping_mul(P1).wrapping_add(P4);
rem = &rem[8..];
}
if rem.len() >= 4 {
let lane = u32::from_le_bytes(rem[..4].try_into().expect("4 bytes"));
h ^= u64::from(lane).wrapping_mul(P1);
h = h.rotate_left(23).wrapping_mul(P2).wrapping_add(P3);
rem = &rem[4..];
}
for &byte in rem {
h ^= u64::from(byte).wrapping_mul(P5);
h = h.rotate_left(11).wrapping_mul(P1);
}
avalanche(h)
}
fn round(acc: u64, lane: u64) -> u64 {
acc.wrapping_add(lane.wrapping_mul(P2))
.rotate_left(31)
.wrapping_mul(P1)
}
fn avalanche(mut h: u64) -> u64 {
h ^= h >> 33;
h = h.wrapping_mul(P2);
h ^= h >> 29;
h = h.wrapping_mul(P3);
h ^= h >> 32;
h
}
#[cfg(test)]
mod tests {
use super::xxh64;
#[test]
fn reference_short_inputs() {
assert_eq!(xxh64(b"", 0), 0xEF46_DB37_51D8_E999);
assert_eq!(xxh64(b"", 1), 0xD5AF_BA13_36A3_BE4B);
assert_eq!(xxh64(b"a", 0), 0xD24E_C4F1_A98C_6E5B);
assert_eq!(xxh64(b"abc", 0), 0x44BC_2CF5_AD77_0999);
assert_eq!(xxh64(b"Hello, world!", 0), 0xF583_36A7_8B6F_9476);
}
#[test]
fn stripe_and_tail_lengths() {
assert_eq!(xxh64(&[b'p'; 40], 7), 0x7470_070D_6C48_70B7);
let bytes33: Vec<u8> = (0..33).collect();
assert_eq!(xxh64(&bytes33, 0), 0x0C53_5D1A_CAFB_8EAD);
let bytes15: Vec<u8> = (0..15).collect();
assert_eq!(xxh64(&bytes15, 1), 0xC60A_A959_76ED_0E4E);
}
}