use crate::int_overflow::{DebugStrictAdd, DebugStrictSub};
use crate::ExtendedHasher;
use std::hash::Hasher;
use std::mem::{self, MaybeUninit};
use std::ptr;
#[cfg(test)]
mod tests;
const ELEM_SIZE: usize = mem::size_of::<u64>();
const BUFFER_CAPACITY: usize = 8;
const BUFFER_SIZE: usize = BUFFER_CAPACITY * ELEM_SIZE;
const BUFFER_WITH_SPILL_CAPACITY: usize = BUFFER_CAPACITY + 1;
const BUFFER_WITH_SPILL_SIZE: usize = BUFFER_WITH_SPILL_CAPACITY * ELEM_SIZE;
const BUFFER_SPILL_INDEX: usize = BUFFER_WITH_SPILL_CAPACITY - 1;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SipHasher128Hash(pub [u64; 2]);
#[derive(Debug, Clone)]
#[repr(C)]
pub struct SipHasher128 {
nbuf: usize, buf: [MaybeUninit<u64>; BUFFER_WITH_SPILL_CAPACITY], state: State, processed: usize, }
#[derive(Debug, Clone, Copy)]
#[repr(C)]
struct State {
v0: u64,
v2: u64,
v1: u64,
v3: u64,
}
macro_rules! compress {
($state:expr) => {{
compress!($state.v0, $state.v1, $state.v2, $state.v3)
}};
($v0:expr, $v1:expr, $v2:expr, $v3:expr) => {{
$v0 = $v0.wrapping_add($v1);
$v2 = $v2.wrapping_add($v3);
$v1 = $v1.rotate_left(13);
$v1 ^= $v0;
$v3 = $v3.rotate_left(16);
$v3 ^= $v2;
$v0 = $v0.rotate_left(32);
$v2 = $v2.wrapping_add($v1);
$v0 = $v0.wrapping_add($v3);
$v1 = $v1.rotate_left(17);
$v1 ^= $v2;
$v3 = $v3.rotate_left(21);
$v3 ^= $v0;
$v2 = $v2.rotate_left(32);
}};
}
#[inline]
unsafe fn copy_nonoverlapping_small(src: *const u8, dst: *mut u8, count: usize) {
debug_assert!(count <= 8);
unsafe {
if count == 8 {
ptr::copy_nonoverlapping(src, dst, 8);
return;
}
let mut i = 0;
if i.debug_strict_add(3) < count {
ptr::copy_nonoverlapping(src.add(i), dst.add(i), 4);
i = i.debug_strict_add(4);
}
if i.debug_strict_add(1) < count {
ptr::copy_nonoverlapping(src.add(i), dst.add(i), 2);
i = i.debug_strict_add(2)
}
if i < count {
*dst.add(i) = *src.add(i);
i = i.debug_strict_add(1);
}
debug_assert_eq!(i, count);
}
}
impl SipHasher128 {
#[inline]
pub fn new_with_keys(key0: u64, key1: u64) -> SipHasher128 {
let mut hasher = SipHasher128 {
nbuf: 0,
buf: unsafe {
MaybeUninit::<[MaybeUninit<_>; BUFFER_WITH_SPILL_CAPACITY]>::uninit().assume_init()
},
state: State {
v0: key0 ^ 0x736f6d6570736575,
v1: key1 ^ (0x646f72616e646f6d ^ 0xee),
v2: key0 ^ 0x6c7967656e657261,
v3: key1 ^ 0x7465646279746573,
},
processed: 0,
};
unsafe {
*hasher.buf.get_unchecked_mut(BUFFER_SPILL_INDEX) = MaybeUninit::zeroed();
}
hasher
}
#[inline(never)]
unsafe fn short_write_process_buffer<const LEN: usize>(&mut self, bytes: [u8; LEN]) {
unsafe {
let nbuf = self.nbuf;
debug_assert!(LEN <= 8);
debug_assert!(nbuf < BUFFER_SIZE);
debug_assert!(nbuf + LEN >= BUFFER_SIZE);
debug_assert!(nbuf + LEN < BUFFER_WITH_SPILL_SIZE);
let dst = (self.buf.as_mut_ptr() as *mut u8).add(nbuf);
ptr::copy_nonoverlapping(bytes.as_ptr(), dst, LEN);
for i in 0..BUFFER_CAPACITY {
let elem = self.buf.get_unchecked(i).assume_init().to_le();
self.state.v3 ^= elem;
Sip13Rounds::c_rounds(&mut self.state);
self.state.v0 ^= elem;
}
let dst = self.buf.as_mut_ptr() as *mut u8;
let src = self.buf.get_unchecked(BUFFER_SPILL_INDEX) as *const _ as *const u8;
ptr::copy_nonoverlapping(src, dst, LEN - 1);
self.nbuf = if LEN == 1 {
0
} else {
nbuf.debug_strict_add(LEN).debug_strict_sub(BUFFER_SIZE)
};
self.processed = self.processed.debug_strict_add(BUFFER_SIZE);
}
}
#[inline]
fn slice_write(&mut self, msg: &[u8]) {
let length = msg.len();
let nbuf = self.nbuf;
debug_assert!(nbuf < BUFFER_SIZE);
if nbuf.debug_strict_add(length) < BUFFER_SIZE {
unsafe {
let dst = (self.buf.as_mut_ptr() as *mut u8).add(nbuf);
if length <= 8 {
copy_nonoverlapping_small(msg.as_ptr(), dst, length);
} else {
ptr::copy_nonoverlapping(msg.as_ptr(), dst, length);
}
}
self.nbuf = nbuf.debug_strict_add(length);
return;
}
unsafe { self.slice_write_process_buffer(msg) }
}
#[inline(never)]
unsafe fn slice_write_process_buffer(&mut self, msg: &[u8]) {
unsafe {
let length = msg.len();
let nbuf = self.nbuf;
debug_assert!(nbuf < BUFFER_SIZE);
debug_assert!(nbuf + length >= BUFFER_SIZE);
let valid_in_elem = nbuf % ELEM_SIZE;
let needed_in_elem = ELEM_SIZE.debug_strict_sub(valid_in_elem);
let src = msg.as_ptr();
let dst = (self.buf.as_mut_ptr() as *mut u8).add(nbuf);
copy_nonoverlapping_small(src, dst, needed_in_elem);
let last = (nbuf / ELEM_SIZE).debug_strict_add(1);
for i in 0..last {
let elem = self.buf.get_unchecked(i).assume_init().to_le();
self.state.v3 ^= elem;
Sip13Rounds::c_rounds(&mut self.state);
self.state.v0 ^= elem;
}
let mut processed = needed_in_elem;
let input_left = length.debug_strict_sub(processed);
let elems_left = input_left / ELEM_SIZE;
let extra_bytes_left = input_left % ELEM_SIZE;
for _ in 0..elems_left {
let elem = (msg.as_ptr().add(processed) as *const u64)
.read_unaligned()
.to_le();
self.state.v3 ^= elem;
Sip13Rounds::c_rounds(&mut self.state);
self.state.v0 ^= elem;
processed = processed.debug_strict_add(ELEM_SIZE);
}
let src = msg.as_ptr().add(processed);
let dst = self.buf.as_mut_ptr() as *mut u8;
copy_nonoverlapping_small(src, dst, extra_bytes_left);
self.nbuf = extra_bytes_left;
self.processed = self
.processed
.debug_strict_add(nbuf.debug_strict_add(processed));
}
}
#[inline]
unsafe fn finish128_inner(
nbuf: usize,
buf: &mut [MaybeUninit<u64>; BUFFER_WITH_SPILL_CAPACITY],
mut state: State,
processed: usize,
) -> [u64; 2] {
debug_assert!(nbuf < BUFFER_SIZE);
let last = nbuf / ELEM_SIZE;
for i in 0..last {
let elem = unsafe { buf.get_unchecked(i).assume_init().to_le() };
state.v3 ^= elem;
Sip13Rounds::c_rounds(&mut state);
state.v0 ^= elem;
}
let elem = if nbuf % ELEM_SIZE != 0 {
unsafe {
let dst = (buf.as_mut_ptr() as *mut u8).add(nbuf);
ptr::write_bytes(dst, 0, ELEM_SIZE - 1);
buf.get_unchecked(last).assume_init().to_le()
}
} else {
0
};
let length = processed.debug_strict_add(nbuf);
let b: u64 = ((length as u64 & 0xff) << 56) | elem;
state.v3 ^= b;
Sip13Rounds::c_rounds(&mut state);
state.v0 ^= b;
state.v2 ^= 0xee;
Sip13Rounds::d_rounds(&mut state);
let l = state.v0 ^ state.v1 ^ state.v2 ^ state.v3;
state.v1 ^= 0xdd;
Sip13Rounds::d_rounds(&mut state);
let h = state.v0 ^ state.v1 ^ state.v2 ^ state.v3;
[l, h]
}
}
impl Default for SipHasher128 {
fn default() -> SipHasher128 {
SipHasher128::new_with_keys(0, 0)
}
}
impl ExtendedHasher for SipHasher128 {
type Hash = SipHasher128Hash;
#[inline]
fn short_write<const LEN: usize>(&mut self, bytes: [u8; LEN]) {
let nbuf = self.nbuf;
debug_assert!(LEN <= 8);
debug_assert!(nbuf < BUFFER_SIZE);
debug_assert!(nbuf + LEN < BUFFER_WITH_SPILL_SIZE);
if nbuf.debug_strict_add(LEN) < BUFFER_SIZE {
unsafe {
let dst = (self.buf.as_mut_ptr() as *mut u8).add(nbuf);
ptr::copy_nonoverlapping(bytes.as_ptr(), dst, LEN);
}
self.nbuf = nbuf.debug_strict_add(LEN);
return;
}
unsafe { self.short_write_process_buffer(bytes) }
}
#[inline(always)]
fn finish(mut self) -> SipHasher128Hash {
SipHasher128Hash(unsafe {
SipHasher128::finish128_inner(self.nbuf, &mut self.buf, self.state, self.processed)
})
}
}
impl Hasher for SipHasher128 {
#[inline]
fn write_u8(&mut self, i: u8) {
self.short_write(i.to_ne_bytes());
}
#[inline]
fn write_u16(&mut self, i: u16) {
self.short_write(i.to_ne_bytes());
}
#[inline]
fn write_u32(&mut self, i: u32) {
self.short_write(i.to_ne_bytes());
}
#[inline]
fn write_u64(&mut self, i: u64) {
self.short_write(i.to_ne_bytes());
}
#[inline]
fn write_usize(&mut self, i: usize) {
self.short_write(i.to_ne_bytes());
}
#[inline]
fn write_i8(&mut self, i: i8) {
self.short_write((i as u8).to_ne_bytes());
}
#[inline]
fn write_i16(&mut self, i: i16) {
self.short_write((i as u16).to_ne_bytes());
}
#[inline]
fn write_i32(&mut self, i: i32) {
self.short_write((i as u32).to_ne_bytes());
}
#[inline]
fn write_i64(&mut self, i: i64) {
self.short_write((i as u64).to_ne_bytes());
}
#[inline]
fn write_isize(&mut self, i: isize) {
self.short_write((i as usize).to_ne_bytes());
}
#[inline]
fn write(&mut self, msg: &[u8]) {
self.slice_write(msg);
}
#[cfg(feature = "nightly")]
#[inline]
fn write_str(&mut self, s: &str) {
self.write(s.as_bytes());
self.write_u8(0xFF);
}
fn finish(&self) -> u64 {
let mut buf = self.buf;
let [a, b] = unsafe {
SipHasher128::finish128_inner(self.nbuf, &mut buf, self.state, self.processed)
};
a.wrapping_mul(3).wrapping_add(b)
}
}
#[derive(Debug, Clone, Default)]
struct Sip13Rounds;
impl Sip13Rounds {
#[inline]
fn c_rounds(state: &mut State) {
compress!(state);
}
#[inline]
fn d_rounds(state: &mut State) {
compress!(state);
compress!(state);
compress!(state);
}
}