use core::{ptr, slice};
use crate::xxh64_common::*;
#[inline(always)]
fn read_32le_unaligned(data: *const u8) -> u32 {
debug_assert!(!data.is_null());
unsafe {
ptr::read_unaligned(data as *const u32).to_le()
}
}
#[inline(always)]
fn read_32le_aligned(data: *const u8) -> u32 {
debug_assert!(!data.is_null());
unsafe {
ptr::read(data as *const u32).to_le()
}
}
#[inline(always)]
fn read_32le_is_align(data: *const u8, is_aligned: bool) -> u32 {
match is_aligned {
true => read_32le_aligned(data),
false => read_32le_unaligned(data),
}
}
#[inline(always)]
fn read_64le_unaligned(data: *const u8) -> u64 {
debug_assert!(!data.is_null());
unsafe {
ptr::read_unaligned(data as *const u64).to_le()
}
}
#[inline(always)]
fn read_64le_aligned(data: *const u8) -> u64 {
debug_assert!(!data.is_null());
unsafe {
ptr::read(data as *const u64).to_le()
}
}
#[inline(always)]
fn read_64le_is_align(data: *const u8, is_aligned: bool) -> u64 {
match is_aligned {
true => read_64le_aligned(data),
false => read_64le_unaligned(data),
}
}
fn finalize(mut input: u64, mut data: &[u8], is_aligned: bool) -> u64 {
while data.len() >= 8 {
input ^= round(0, read_64le_is_align(data.as_ptr(), is_aligned));
data = &data[8..];
input = input.rotate_left(27).wrapping_mul(PRIME_1).wrapping_add(PRIME_4)
}
if data.len() >= 4 {
input ^= (read_32le_is_align(data.as_ptr(), is_aligned) as u64).wrapping_mul(PRIME_1);
data = &data[4..];
input = input.rotate_left(23).wrapping_mul(PRIME_2).wrapping_add(PRIME_3);
}
for byte in data.iter() {
input ^= (*byte as u64).wrapping_mul(PRIME_5);
input = input.rotate_left(11).wrapping_mul(PRIME_1);
}
avalanche(input)
}
pub fn xxh64(mut input: &[u8], seed: u64) -> u64 {
let input_len = input.len() as u64;
let mut result;
if input.len() >= CHUNK_SIZE {
let mut v1 = seed.wrapping_add(PRIME_1).wrapping_add(PRIME_2);
let mut v2 = seed.wrapping_add(PRIME_2);
let mut v3 = seed;
let mut v4 = seed.wrapping_sub(PRIME_1);
loop {
v1 = round(v1, read_64le_unaligned(input.as_ptr()));
input = &input[8..];
v2 = round(v2, read_64le_unaligned(input.as_ptr()));
input = &input[8..];
v3 = round(v3, read_64le_unaligned(input.as_ptr()));
input = &input[8..];
v4 = round(v4, read_64le_unaligned(input.as_ptr()));
input = &input[8..];
if input.len() < CHUNK_SIZE {
break;
}
}
result = v1.rotate_left(1).wrapping_add(v2.rotate_left(7))
.wrapping_add(v3.rotate_left(12))
.wrapping_add(v4.rotate_left(18));
result = merge_round(result, v1);
result = merge_round(result, v2);
result = merge_round(result, v3);
result = merge_round(result, v4);
} else {
result = seed.wrapping_add(PRIME_5)
}
result = result.wrapping_add(input_len);
finalize(result, input, false)
}
#[derive(Clone)]
pub struct Xxh64 {
total_len: u64,
v1: u64,
v2: u64,
v3: u64,
v4: u64,
mem: [u64; 4],
mem_size: u64,
}
impl Xxh64 {
#[inline]
pub const fn new(seed: u64) -> Self {
Self {
total_len: 0,
v1: seed.wrapping_add(PRIME_1).wrapping_add(PRIME_2),
v2: seed.wrapping_add(PRIME_2),
v3: seed,
v4: seed.wrapping_sub(PRIME_1),
mem: [0, 0, 0, 0],
mem_size: 0,
}
}
pub fn update(&mut self, mut input: &[u8]) {
self.total_len = self.total_len.wrapping_add(input.len() as u64);
if (self.mem_size as usize + input.len()) < CHUNK_SIZE {
unsafe {
ptr::copy_nonoverlapping(input.as_ptr(), (self.mem.as_mut_ptr() as *mut u8).add(self.mem_size as usize), input.len())
}
self.mem_size += input.len() as u64;
return
}
if self.mem_size > 0 {
let fill_len = CHUNK_SIZE - self.mem_size as usize;
unsafe {
ptr::copy_nonoverlapping(input.as_ptr(), (self.mem.as_mut_ptr() as *mut u8).add(self.mem_size as usize), fill_len)
}
self.v1 = round(self.v1, self.mem[0].to_le());
self.v2 = round(self.v2, self.mem[1].to_le());
self.v3 = round(self.v3, self.mem[2].to_le());
self.v4 = round(self.v4, self.mem[3].to_le());
input = &input[fill_len..];
self.mem_size = 0;
}
if input.len() >= CHUNK_SIZE {
loop {
self.v1 = round(self.v1, read_64le_unaligned(input.as_ptr()));
input = &input[8..];
self.v2 = round(self.v2, read_64le_unaligned(input.as_ptr()));
input = &input[8..];
self.v3 = round(self.v3, read_64le_unaligned(input.as_ptr()));
input = &input[8..];
self.v4 = round(self.v4, read_64le_unaligned(input.as_ptr()));
input = &input[8..];
if input.len() < CHUNK_SIZE {
break;
}
}
}
if input.len() > 0 {
unsafe {
ptr::copy_nonoverlapping(input.as_ptr(), self.mem.as_mut_ptr() as *mut u8, input.len())
}
self.mem_size = input.len() as u64;
}
}
pub fn digest(&self) -> u64 {
let mut result;
if self.total_len >= CHUNK_SIZE as u64 {
result = self.v1.rotate_left(1).wrapping_add(self.v2.rotate_left(7))
.wrapping_add(self.v3.rotate_left(12))
.wrapping_add(self.v4.rotate_left(18));
result = merge_round(result, self.v1);
result = merge_round(result, self.v2);
result = merge_round(result, self.v3);
result = merge_round(result, self.v4);
} else {
result = self.v3.wrapping_add(PRIME_5)
}
result = result.wrapping_add(self.total_len);
let input = unsafe {
slice::from_raw_parts(self.mem.as_ptr() as *const u8, self.mem_size as usize)
};
finalize(result, input, true)
}
#[inline]
pub fn reset(&mut self, seed: u64) {
self.total_len = 0;
self.v1 = seed.wrapping_add(PRIME_1).wrapping_add(PRIME_2);
self.v2 = seed.wrapping_add(PRIME_2);
self.v3 = seed;
self.v4 = seed.wrapping_sub(PRIME_1);
self.mem_size = 0;
}
}
impl core::hash::Hasher for Xxh64 {
#[inline(always)]
fn finish(&self) -> u64 {
self.digest()
}
#[inline(always)]
fn write(&mut self, input: &[u8]) {
self.update(input)
}
}
impl Default for Xxh64 {
#[inline(always)]
fn default() -> Self {
Xxh64Builder::new(0).build()
}
}
#[derive(Clone, Copy, Default)]
pub struct Xxh64Builder {
seed: u64
}
impl Xxh64Builder {
#[inline(always)]
pub const fn new(seed: u64) -> Self {
Self {
seed
}
}
#[inline(always)]
pub const fn build(self) -> Xxh64 {
Xxh64::new(self.seed)
}
}
impl core::hash::BuildHasher for Xxh64Builder {
type Hasher = Xxh64;
#[inline(always)]
fn build_hasher(&self) -> Self::Hasher {
self.build()
}
}