use crate::extensions::ext_slice_u8::ExtSliceU8;
use crate::utils::rot64;
const SC_NUM_VARS: u8 = 12;
const SC_BLOCK_SIZE: u8 = SC_NUM_VARS * 8;
const SC_BUF_SIZE: u8 = 2 * SC_BLOCK_SIZE;
const SC_CONST: u64 = 0xdeadbeefdeadbeef;
pub struct Spooky {}
const ALLOW_UNALIGNED_READS: bool = true;
impl Spooky {
#[inline]
#[rustfmt::skip]
const fn mix(data: [u64; 12], mut states: [u64; 12]) -> [u64; 12] {
states[00] = states[00].wrapping_add(data[00]); states[02] ^= states[10]; states[11] ^= states[00]; states[00] = rot64(states[00],11); states[11] = states[11].wrapping_add(states[01]);
states[01] = states[01].wrapping_add(data[01]); states[03] ^= states[11]; states[00] ^= states[01]; states[01] = rot64(states[01],32); states[00] = states[00].wrapping_add(states[02]);
states[02] = states[02].wrapping_add(data[02]); states[04] ^= states[00]; states[01] ^= states[02]; states[02] = rot64(states[02],43); states[01] = states[01].wrapping_add(states[03]);
states[03] = states[03].wrapping_add(data[03]); states[05] ^= states[01]; states[02] ^= states[03]; states[03] = rot64(states[03],31); states[02] = states[02].wrapping_add(states[04]);
states[04] = states[04].wrapping_add(data[04]); states[06] ^= states[02]; states[03] ^= states[04]; states[04] = rot64(states[04],17); states[03] = states[03].wrapping_add(states[05]);
states[05] = states[05].wrapping_add(data[05]); states[07] ^= states[03]; states[04] ^= states[05]; states[05] = rot64(states[05],28); states[04] = states[04].wrapping_add(states[06]);
states[06] = states[06].wrapping_add(data[06]); states[08] ^= states[04]; states[05] ^= states[06]; states[06] = rot64(states[06],39); states[05] = states[05].wrapping_add(states[07]);
states[07] = states[07].wrapping_add(data[07]); states[09] ^= states[05]; states[06] ^= states[07]; states[07] = rot64(states[07],57); states[06] = states[06].wrapping_add(states[08]);
states[08] = states[08].wrapping_add(data[08]); states[10] ^= states[06]; states[07] ^= states[08]; states[08] = rot64(states[08],55); states[07] = states[07].wrapping_add(states[09]);
states[09] = states[09].wrapping_add(data[09]); states[11] ^= states[07]; states[08] ^= states[09]; states[09] = rot64(states[09],54); states[08] = states[08].wrapping_add(states[10]);
states[10] = states[10].wrapping_add(data[10]); states[00] ^= states[08]; states[09] ^= states[10]; states[10] = rot64(states[10],22); states[09] = states[09].wrapping_add(states[11]);
states[11] = states[11].wrapping_add(data[11]); states[01] ^= states[09]; states[10] ^= states[11]; states[11] = rot64(states[11],46); states[10] = states[10].wrapping_add(states[00]);
states
}
#[inline]
#[rustfmt::skip]
const fn end_partial(mut states: [u64; 12]) -> [u64; 12] {
states[11] = states[11].wrapping_add(states[01]); states[02] ^= states[11]; states[01] = rot64(states[01], 44);
states[00] = states[00].wrapping_add(states[02]); states[03] ^= states[00]; states[02] = rot64(states[02], 15);
states[01] = states[01].wrapping_add(states[03]); states[04] ^= states[01]; states[03] = rot64(states[03], 34);
states[02] = states[02].wrapping_add(states[04]); states[05] ^= states[02]; states[04] = rot64(states[04], 21);
states[03] = states[03].wrapping_add(states[05]); states[06] ^= states[03]; states[05] = rot64(states[05], 38);
states[04] = states[04].wrapping_add(states[06]); states[07] ^= states[04]; states[06] = rot64(states[06], 33);
states[05] = states[05].wrapping_add(states[07]); states[08] ^= states[05]; states[07] = rot64(states[07], 10);
states[06] = states[06].wrapping_add(states[08]); states[09] ^= states[06]; states[08] = rot64(states[08], 13);
states[07] = states[07].wrapping_add(states[09]); states[10] ^= states[07]; states[09] = rot64(states[09], 38);
states[08] = states[08].wrapping_add(states[10]); states[11] ^= states[08]; states[10] = rot64(states[10], 53);
states[09] = states[09].wrapping_add(states[11]); states[00] ^= states[09]; states[11] = rot64(states[11], 42);
states[10] = states[10].wrapping_add(states[00]); states[01] ^= states[10]; states[00] = rot64(states[00], 54);
states
}
#[inline]
const fn end(mut states: [u64; 12]) -> [u64; 12] {
states = Self::end_partial(states);
states = Self::end_partial(states);
states = Self::end_partial(states);
states
}
#[inline]
#[rustfmt::skip]
const fn short_mix(mut states: [u64; 4]) -> [u64; 4] {
states[2] = rot64(states[2], 50); states[2] = states[2].wrapping_add(states[3]); states[0] ^= states[2];
states[3] = rot64(states[3], 52); states[3] = states[3].wrapping_add(states[0]); states[1] ^= states[3];
states[0] = rot64(states[0], 30); states[0] = states[0].wrapping_add(states[1]); states[2] ^= states[0];
states[1] = rot64(states[1], 41); states[1] = states[1].wrapping_add(states[2]); states[3] ^= states[1];
states[2] = rot64(states[2], 54); states[2] = states[2].wrapping_add(states[3]); states[0] ^= states[2];
states[3] = rot64(states[3], 48); states[3] = states[3].wrapping_add(states[0]); states[1] ^= states[3];
states[0] = rot64(states[0], 38); states[0] = states[0].wrapping_add(states[1]); states[2] ^= states[0];
states[1] = rot64(states[1], 37); states[1] = states[1].wrapping_add(states[2]); states[3] ^= states[1];
states[2] = rot64(states[2], 62); states[2] = states[2].wrapping_add(states[3]); states[0] ^= states[2];
states[3] = rot64(states[3], 34); states[3] = states[3].wrapping_add(states[0]); states[1] ^= states[3];
states[0] = rot64(states[0], 05); states[0] = states[0].wrapping_add(states[1]); states[2] ^= states[0];
states[1] = rot64(states[1], 36); states[1] = states[1].wrapping_add(states[2]); states[3] ^= states[1];
states
}
#[inline]
#[rustfmt::skip]
const fn short_end(mut states: [u64; 4]) -> [u64; 4] {
states[3] ^= states[2]; states[2] = rot64(states[2], 15); states[3] = states[3].wrapping_add(states[2]);
states[0] ^= states[3]; states[3] = rot64(states[3], 52); states[0] = states[0].wrapping_add(states[3]);
states[1] ^= states[0]; states[0] = rot64(states[0], 26); states[1] = states[1].wrapping_add(states[0]);
states[2] ^= states[1]; states[1] = rot64(states[1], 51); states[2] = states[2].wrapping_add(states[1]);
states[3] ^= states[2]; states[2] = rot64(states[2], 28); states[3] = states[3].wrapping_add(states[2]);
states[0] ^= states[3]; states[3] = rot64(states[3], 09); states[0] = states[0].wrapping_add(states[3]);
states[1] ^= states[0]; states[0] = rot64(states[0], 47); states[1] = states[1].wrapping_add(states[0]);
states[2] ^= states[1]; states[1] = rot64(states[1], 54); states[2] = states[2].wrapping_add(states[1]);
states[3] ^= states[2]; states[2] = rot64(states[2], 32); states[3] = states[3].wrapping_add(states[2]);
states[0] ^= states[3]; states[3] = rot64(states[3], 25); states[0] = states[0].wrapping_add(states[3]);
states[1] ^= states[0]; states[0] = rot64(states[0], 63); states[1] = states[1].wrapping_add(states[0]);
states
}
pub fn short(mut data: &[u8], mut seed1: u64, mut seed2: u64) -> (u64, u64) {
let starting_length = data.len();
let mut remainder: usize = data.len() % 32;
let mut states = [seed1, seed2, SC_CONST, SC_CONST];
if starting_length > 15 {
let chunks = data.chunks_exact(32);
for chunk in chunks {
states[2] = states[2].wrapping_add(chunk[8 * 0..].read_u64_le());
states[3] = states[3].wrapping_add(chunk[8 * 1..].read_u64_le());
states = Self::short_mix(states);
states[0] = states[0].wrapping_add(chunk[8 * 2..].read_u64_le());
states[1] = states[1].wrapping_add(chunk[8 * 3..].read_u64_le());
data = &data[8 * 4..]
}
if remainder >= 16 {
states[2] = states[2].wrapping_add(data[8 * 0..].read_u64_le());
states[3] = states[3].wrapping_add(data[8 * 1..].read_u64_le());
states = Self::short_mix(states);
data = &data[8 * 2..];
remainder = remainder.wrapping_sub(16);
}
}
states[3] = (starting_length as u64) << 56;
if remainder == 15 {
states[3] = states[3].wrapping_add((data[14] as u64) << 48)
}
if (14..=15).contains(&remainder) {
states[3] = states[3].wrapping_add((data[13] as u64) << 40)
}
if (13..=15).contains(&remainder) {
states[3] = states[3].wrapping_add((data[12] as u64) << 32)
}
if (12..=15).contains(&remainder) {
states[3] = states[3].wrapping_add(data[4 * 2..].read_u32_le() as u64);
states[2] = states[2].wrapping_add(data[8 * 0..].read_u64_le());
}
if remainder == 11 {
states[3] = states[3].wrapping_add((data[10] as u64) << 16);
}
if (10..=11).contains(&remainder) {
states[3] = states[3].wrapping_add((data[09] as u64) << 8);
}
if (09..=11).contains(&remainder) {
states[3] = states[3].wrapping_add((data[08] as u64));
}
if (08..=11).contains(&remainder) {
states[2] = states[2].wrapping_add(data[8 * 0..].read_u64_le());
}
if remainder == 07 {
states[2] = states[2].wrapping_add((data[06] as u64) << 48)
}
if (06..=07).contains(&remainder) {
states[2] = states[2].wrapping_add((data[05] as u64) << 40)
}
if (05..=07).contains(&remainder) {
states[2] = states[2].wrapping_add((data[04] as u64) << 32)
}
if (04..=07).contains(&remainder) {
states[2] = states[2].wrapping_add(data[4 * 0..].read_u32_le() as u64);
}
if remainder == 03 {
states[2] = states[2].wrapping_add((data[02] as u64) << 16)
}
if (02..=03).contains(&remainder) {
states[2] = states[2].wrapping_add((data[01] as u64) << 8)
}
if (01..=03).contains(&remainder) {
states[2] = states[2].wrapping_add((data[00] as u64))
}
if remainder == 00 {
states[2] = states[2].wrapping_add(SC_CONST);
states[3] = states[3].wrapping_add(SC_CONST);
}
states = Self::short_end(states);
(states[0], states[1])
}
#[rustfmt::skip]
pub fn hash128(mut input: &[u8], mut seed1: u64, mut seed2: u64) -> (u64, u64) {
if input.len() < SC_BUF_SIZE as usize {
return Self::short(input, seed1, seed2);
}
let mut states = [seed1, seed2, SC_CONST, seed1, seed2, SC_CONST, seed1, seed2, SC_CONST, seed1, seed2, SC_CONST];
let chunks = input.chunks_exact((SC_BLOCK_SIZE) as usize);
let remaining_data = chunks.remainder();
if ALLOW_UNALIGNED_READS || (input.as_ptr() as usize & 7 == 0) {
for chunk in chunks {
states = Self::mix([
chunk[8 * 00..].read_u64_le(), chunk[8 * 01..].read_u64_le(),
chunk[8 * 02..].read_u64_le(), chunk[8 * 03..].read_u64_le(),
chunk[8 * 04..].read_u64_le(), chunk[8 * 05..].read_u64_le(),
chunk[8 * 06..].read_u64_le(), chunk[8 * 07..].read_u64_le(),
chunk[8 * 08..].read_u64_le(), chunk[8 * 09..].read_u64_le(),
chunk[8 * 10..].read_u64_le(), chunk[8 * 11..].read_u64_le(),
], states);
}
} else {todo!()}
let mut remainder = [0u8; SC_NUM_VARS as usize * size_of::<u64>()];
remainder[..remaining_data.len()].copy_from_slice(remaining_data);
remainder[(SC_BLOCK_SIZE as usize)-1] = remaining_data.len() as u8;
states = Self::mix([
remainder[8 * 00..].read_u64_le(), remainder[8 * 01..].read_u64_le(),
remainder[8 * 02..].read_u64_le(), remainder[8 * 03..].read_u64_le(),
remainder[8 * 04..].read_u64_le(), remainder[8 * 05..].read_u64_le(),
remainder[8 * 06..].read_u64_le(), remainder[8 * 07..].read_u64_le(),
remainder[8 * 08..].read_u64_le(), remainder[8 * 09..].read_u64_le(),
remainder[8 * 10..].read_u64_le(), remainder[8 * 11..].read_u64_le(),
], states);
states = Self::end(states);
(states[0], states[1])
}
pub fn hash64(input: &[u8], seed1: u64) -> u64 {
let (seed, _) = Self::hash128(input, seed1, seed1);
seed
}
pub fn hash32(input: &[u8], seed1: u32) -> u32 {
let (seed, _) =Self::hash128(input, seed1 as u64, seed1 as u64);
seed as u32
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{DEFAULT_TEST_STRING };
#[test]
fn test_short_short() {
let str = DEFAULT_TEST_STRING;
let seed1 = 0;
let seed2 = 0;
let (seed1, seed2) = Spooky::short(str, seed1, seed2);
assert_eq!((seed1, seed2), (0x3E4A0F8311D417BC, 0xEE491890D39F45EB))
}
#[test]
fn test_short_long() {
let str = DEFAULT_TEST_STRING.repeat(6);
let seed1 = 0;
let seed2 = 0;
let (seed1, seed2) = Spooky::short(&*str, seed1, seed2);
assert_eq!((seed1, seed2), (0x9120BB631D547D7E, 0x27791CE5610C03CC))
}
#[test]
fn test_hash128_long() {
let str = DEFAULT_TEST_STRING.repeat(6);
let seed1 = 0;
let seed2 = 0;
let (seed1, seed2) = Spooky::hash128(&*str, seed1, seed2);
assert_eq!((seed1, seed2), (0x4F120CC64DAE2E36, 0xBAE002B3C64ED326))
}
#[test]
fn test_hash128_short() {
let str = DEFAULT_TEST_STRING;
let seed1 = 0;
let seed2 = 0;
let (seed1, seed2) = Spooky::hash128(&*str, seed1, seed2);
assert_eq!((seed1, seed2), (0x3E4A0F8311D417BC, 0xEE491890D39F45EB))
}
#[test]
fn test_hash32() {
let str = DEFAULT_TEST_STRING;
let seed = Spooky::hash32(&*str, 0);
assert_eq!(seed, 0x11D417BC)
}
}