use core::hash::BuildHasher;
use core::hash::Hasher;
use crate::read_u32;
const PRIME1: u32 = 0x9e37_79b1;
const PRIME2: u32 = 0x85eb_ca77;
const PRIME3: u32 = 0xc2b2_ae3d;
const PRIME4: u32 = 0x27d4_eb2f;
const PRIME5: u32 = 0x1656_67b1;
#[derive(Clone, Debug)]
struct Accumulators([u32; 4]);
impl Accumulators {
const fn new(seed: u32) -> Self {
Self([
seed.wrapping_add(PRIME2),
seed.wrapping_add(PRIME1).wrapping_add(PRIME2),
seed.wrapping_sub(PRIME1),
seed,
])
}
#[inline]
fn consume(&mut self, input: [u32; 4]) {
let [acc1, acc0, acc3, acc2] = &mut self.0;
let [product0, product1, product2, product3] =
input.map(|lane| u32::from_le(lane).wrapping_mul(PRIME2));
*acc0 = round_product(*acc0, product0);
*acc1 = round_product(*acc1, product1);
*acc2 = round_product(*acc2, product2);
*acc3 = round_product(*acc3, product3);
}
const fn digest(&self) -> u32 {
let [acc1, acc0, acc3, acc2] = self.0;
acc0.rotate_left(1)
.wrapping_add(acc1.rotate_left(7))
.wrapping_add(acc2.rotate_left(12))
.wrapping_add(acc3.rotate_left(18))
}
}
#[inline]
const fn round(acc: u32, lane: u32) -> u32 {
round_product(acc, lane.wrapping_mul(PRIME2))
}
#[inline]
const fn round_product(acc: u32, product: u32) -> u32 {
let acc = acc.wrapping_add(product);
let acc = acc.rotate_left(13);
acc.wrapping_mul(PRIME1)
}
#[inline(always)]
fn avalanche(mut hash: u32) -> u32 {
hash ^= hash >> 15;
hash = hash.wrapping_mul(PRIME2);
hash ^= hash >> 13;
hash = hash.wrapping_mul(PRIME3);
hash ^ (hash >> 16)
}
#[inline(always)]
fn consume_tail(mut hash: u32, tail: &[u8]) -> u32 {
let mut offset = 0;
while offset + 4 <= tail.len() {
hash = hash.wrapping_add(read_u32(tail, offset).wrapping_mul(PRIME3));
hash = hash.rotate_left(17).wrapping_mul(PRIME4);
offset += 4;
}
for &byte in &tail[offset..] {
hash = hash.wrapping_add(u32::from(byte).wrapping_mul(PRIME5));
hash = hash.rotate_left(11).wrapping_mul(PRIME1);
}
avalanche(hash)
}
#[must_use]
#[inline]
pub fn xxh32(input: &[u8], seed: u32) -> u32 {
let mut offset = 0;
let mut lanes = [
seed.wrapping_add(PRIME1).wrapping_add(PRIME2),
seed.wrapping_add(PRIME2),
seed,
seed.wrapping_sub(PRIME1),
];
while offset + 16 <= input.len() {
lanes[0] = round(lanes[0], read_u32(input, offset));
lanes[1] = round(lanes[1], read_u32(input, offset + 4));
lanes[2] = round(lanes[2], read_u32(input, offset + 8));
lanes[3] = round(lanes[3], read_u32(input, offset + 12));
offset += 16;
}
let mut hash = if input.len() >= 16 {
lanes[0]
.rotate_left(1)
.wrapping_add(lanes[1].rotate_left(7))
.wrapping_add(lanes[2].rotate_left(12))
.wrapping_add(lanes[3].rotate_left(18))
} else {
seed.wrapping_add(PRIME5)
};
hash = hash.wrapping_add(input.len() as u32);
consume_tail(hash, &input[offset..])
}
#[derive(Clone, Debug)]
pub struct Xxh32 {
seed: u32,
lanes: Accumulators,
buffer: [u8; 16],
buffered: usize,
total_len: u64,
length_overflowed: bool,
}
impl Xxh32 {
#[must_use]
pub const fn new() -> Self {
Self::with_seed(0)
}
#[must_use]
pub const fn with_seed(seed: u32) -> Self {
Self {
seed,
lanes: Accumulators::new(seed),
buffer: [0; 16],
buffered: 0,
total_len: 0,
length_overflowed: false,
}
}
#[must_use]
pub const fn seed(&self) -> u32 {
self.seed
}
#[must_use]
pub const fn total_len(&self) -> u64 {
if self.length_overflowed {
u64::MAX
} else {
self.total_len
}
}
pub fn reset(&mut self) {
*self = Self::with_seed(self.seed);
}
#[inline]
pub fn update(&mut self, mut input: &[u8]) {
let (total_len, overflowed) = self.total_len.overflowing_add(input.len() as u64);
self.total_len = total_len;
self.length_overflowed |= overflowed;
if self.buffered != 0 {
let needed = 16 - self.buffered;
let copied = needed.min(input.len());
self.buffer[self.buffered..self.buffered + copied].copy_from_slice(&input[..copied]);
self.buffered += copied;
input = &input[copied..];
if self.buffered < 16 {
return;
}
consume_block(&mut self.lanes, &self.buffer);
self.buffered = 0;
}
while let Some((block, rest)) = input.split_first_chunk::<16>() {
consume_block(&mut self.lanes, block);
input = rest;
}
self.buffer[..input.len()].copy_from_slice(input);
self.buffered = input.len();
}
#[must_use]
#[inline]
pub fn digest(&self) -> u32 {
let mut hash = if self.length_overflowed || self.total_len >= 16 {
self.lanes.digest()
} else {
self.seed.wrapping_add(PRIME5)
};
hash = hash.wrapping_add(self.total_len as u32);
consume_tail(hash, &self.buffer[..self.buffered])
}
}
#[inline]
fn consume_block(lanes: &mut Accumulators, block: &[u8; 16]) {
lanes.consume(unsafe { block.as_ptr().cast::<[u32; 4]>().read_unaligned() });
}
impl Default for Xxh32 {
fn default() -> Self {
Self::new()
}
}
impl Hasher for Xxh32 {
#[inline]
fn finish(&self) -> u64 {
u64::from(self.digest())
}
#[inline]
fn write(&mut self, bytes: &[u8]) {
self.update(bytes);
}
}
#[cfg(feature = "std")]
impl std::io::Write for Xxh32 {
#[inline]
fn write(&mut self, input: &[u8]) -> std::io::Result<usize> {
self.update(input);
Ok(input.len())
}
#[inline]
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct Xxh32Builder {
seed: u32,
}
impl Xxh32Builder {
#[must_use]
pub const fn with_seed(seed: u32) -> Self {
Self { seed }
}
}
impl BuildHasher for Xxh32Builder {
type Hasher = Xxh32;
#[inline]
fn build_hasher(&self) -> Self::Hasher {
Xxh32::with_seed(self.seed)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn length_overflow_keeps_long_digest_mode() {
let mut hash = Xxh32::new();
hash.total_len = u64::MAX;
hash.update(&[0]);
assert!(hash.length_overflowed);
assert_eq!(hash.total_len(), u64::MAX);
let expected = hash.lanes.digest().wrapping_add(hash.total_len as u32);
assert_eq!(
hash.digest(),
consume_tail(expected, &hash.buffer[..hash.buffered])
);
}
}