use crate::rand::u64 as random_u64;
use std::hash::{BuildHasher, Hasher};
pub(crate) const WYP: [u64; 5] = [
0xa0761d6478bd642f,
0xe7037ed1a0b428db,
0x8ebc6af09c88c6e3,
0x589965cc75374cc3,
0x1d8e4e27c47d124f,
];
include!(concat!(env!("OUT_DIR"), "/wyhash_secret.rs"));
const ROUND_LEN: usize = 48;
#[inline(always)]
fn r8(data: &[u8]) -> u64 {
u64::from(data[7]) << 56
| u64::from(data[6]) << 48
| u64::from(data[5]) << 40
| u64::from(data[4]) << 32
| u64::from(data[3]) << 24
| u64::from(data[2]) << 16
| u64::from(data[1]) << 8
| u64::from(data[0])
}
#[inline(always)]
fn r4(data: &[u8]) -> u64 {
u64::from(data[3]) << 24
| u64::from(data[2]) << 16
| u64::from(data[1]) << 8
| u64::from(data[0])
}
#[inline(always)]
fn wymix(a: u64, b: u64) -> u64 {
let v = (a as u128).wrapping_mul(b as u128);
((v >> 64) ^ v) as u64
}
#[inline(always)]
fn wymum(a: u64, b: u64) -> (u64, u64) {
let r = (a as u128).wrapping_mul(b as u128);
(r as u64, (r >> 64) as u64)
}
#[inline]
#[allow(unused)]
fn wyhash_once(data: &[u8], seed: u64) -> u64 {
let mut a: u64;
let mut b: u64;
let mut seed = seed ^ wymix(seed ^ SECRETS[0], SECRETS[1]); let length = data.len();
if length <= 16 {
(a, b) = wyhash_a_b_0_16(data, length)
} else {
let mut see1 = seed;
let mut see2 = seed;
let mut p = 0usize;
let mut i = data.len();
if i > ROUND_LEN {
while i > ROUND_LEN {
wyhash_round(&mut seed, &mut see1, &mut see2, data, p);
p += ROUND_LEN;
i -= ROUND_LEN;
}
seed ^= see1 ^ see2;
}
while i > 16 {
seed = wymix(r8(&data[p..]) ^ SECRETS[1], r8(&data[p + 8..]) ^ seed);
p += 16;
i -= 16;
}
(a, b) = wyhash_a_b_0_16(&data[p..], i)
}
a ^= SECRETS[1];
b ^= seed;
(a, b) = wymum(a, b);
wymix(a ^ SECRETS[0] ^ (length as u64), b ^ SECRETS[1])
}
#[inline]
#[allow(unused)]
fn wyhash_once_fv4(data: &[u8], seed: u64) -> u64 {
let mut a: u64;
let mut b: u64;
let mut seed = seed ^ wymix(seed ^ SECRETS[0], SECRETS[1]); let length = data.len();
if length <= 16 {
(a, b) = wyhash_a_b_0_16(data, length)
} else {
let mut see1 = seed;
let mut see2 = seed;
let mut p = 0usize;
let mut i = data.len();
if i > ROUND_LEN {
while i > ROUND_LEN {
wyhash_round(&mut seed, &mut see1, &mut see2, data, p);
p += ROUND_LEN;
i -= ROUND_LEN;
}
seed ^= see1 ^ see2;
}
while i > 16 {
seed = wymix(r8(&data[p..]) ^ SECRETS[1], r8(&data[p + 8..]) ^ seed);
p += 16;
i -= 16;
}
a = r8(&data[p + i - 16..]);
b = r8(&data[p + i - 8..]);
}
a ^= SECRETS[1];
b ^= seed;
(a, b) = wymum(a, b);
wymix(a ^ SECRETS[0] ^ (length as u64), b ^ SECRETS[1])
}
#[inline(always)]
fn wyhash_round(seed: &mut u64, see1: &mut u64, see2: &mut u64, data: &[u8], p: usize) {
*seed = wymix(r8(&data[p..]) ^ SECRETS[1], r8(&data[p + 8..]) ^ *seed);
*see1 = wymix(
r8(&data[p + 16..]) ^ SECRETS[2],
r8(&data[p + 24..]) ^ *see1,
);
*see2 = wymix(
r8(&data[p + 32..]) ^ SECRETS[3],
r8(&data[p + 40..]) ^ *see2,
);
}
#[inline(always)]
fn wyhash_a_b_0_16(data: &[u8], length: usize) -> (u64, u64) {
debug_assert!(length <= 16);
if length >= 4 {
let a = r4(&data[..]) << 32 | r4(&data[((length >> 3) << 2)..]);
let b = r4(&data[length - 4..]) << 32 | r4(&data[length - 4 - ((length >> 3) << 2)..]);
(a, b)
} else if length > 0 {
let a = u64::from(data[0]) << 16
| u64::from(data[length >> 1]) << 8
| u64::from(data[length - 1]);
(a, 0)
} else {
(0, 0)
}
}
#[derive(Clone, Debug)]
#[repr(align(8))]
struct Align64<T>(T);
#[derive(Clone, Debug)]
pub struct WyHasher {
seed: u64,
see1: u64,
see2: u64,
buffer: Align64<[u8; ROUND_LEN]>,
buffer_len: usize,
input_len: usize,
}
impl WyHasher {
pub fn new() -> Self {
WyHasher::with_seed(random_u64(..))
}
fn with_seed(seed: u64) -> Self {
let new_seed = seed ^ wymix(seed ^ SECRETS[0], SECRETS[1]);
WyHasher {
seed: new_seed,
see1: new_seed,
see2: new_seed,
buffer: Align64([0; ROUND_LEN]),
buffer_len: 0,
input_len: 0,
}
}
fn write(&mut self, bytes: &[u8]) {
self.input_len += bytes.len();
if bytes.len() + self.buffer_len <= ROUND_LEN {
self.buffer.0[self.buffer_len..bytes.len() + self.buffer_len].copy_from_slice(bytes);
self.buffer_len += bytes.len();
} else {
self.buffer.0[self.buffer_len..].copy_from_slice(&bytes[..ROUND_LEN - self.buffer_len]);
let mut seed = self.seed;
let mut see1 = self.see1;
let mut see2 = self.see2;
wyhash_round(&mut seed, &mut see1, &mut see2, &self.buffer.0, 0);
let mut bytes_consumed = ROUND_LEN - self.buffer_len;
let mut new_buffer_len = bytes.len() + self.buffer_len - ROUND_LEN;
while new_buffer_len > ROUND_LEN {
wyhash_round(
&mut seed,
&mut see1,
&mut see2,
&bytes[bytes_consumed..bytes_consumed + ROUND_LEN],
0,
);
bytes_consumed += ROUND_LEN;
new_buffer_len -= ROUND_LEN;
}
self.buffer_len = new_buffer_len;
if new_buffer_len > 0 {
self.buffer.0[..new_buffer_len].copy_from_slice(&bytes[bytes_consumed..]);
}
self.seed = seed;
self.see1 = see1;
self.see2 = see2;
}
}
fn finish(&self) -> u64 {
let mut buffer_len = self.buffer_len; let input_len = self.input_len;
let data = self.buffer.0;
let mut a: u64;
let mut b: u64;
let mut seed = self.seed;
if input_len < 16 {
(a, b) = wyhash_a_b_0_16(&data, input_len);
} else {
if input_len > ROUND_LEN {
seed ^= self.see1 ^ self.see2;
}
let mut p = 0;
while buffer_len > 16 {
seed = wymix(r8(&data[p..]) ^ SECRETS[1], r8(&data[p + 8..]) ^ seed);
p += 16;
buffer_len -= 16;
}
(a, b) = wyhash_a_b_0_16(&data[p..], buffer_len)
}
a ^= SECRETS[1];
b ^= seed;
(a, b) = wymum(a, b);
wymix(a ^ SECRETS[0] ^ (input_len as u64), b ^ SECRETS[1])
}
}
impl Hasher for WyHasher {
fn write(&mut self, bytes: &[u8]) {
self.write(bytes)
}
fn finish(&self) -> u64 {
self.finish()
}
}
impl BuildHasher for WyHasher {
type Hasher = WyHasher;
#[inline(always)]
fn build_hasher(&self) -> Self::Hasher {
WyHasher::new()
}
}
impl Default for WyHasher {
fn default() -> Self {
WyHasher::new()
}
}
#[cfg(test)]
mod tests {
use std::collections::HashMap;
use super::*;
const SEED: u64 = 12345678910;
#[test]
fn simple_test() {
fn wyhash_once_t(b: &[u8]) -> u64 {
wyhash_once(b, SEED)
}
fn wyhash_digest_t(b: &[u8]) -> u64 {
let mut hasher = WyHasher::with_seed(SEED);
hasher.write(b);
hasher.finish()
}
let mut x: [u8; 1000] = [0; 1000];
for i in 0..x.len() {
x[i] = i as u8;
}
for i in 0..x.len() {
let b = &x[..i];
let v1 = wyhash_once_t(b);
let v2 = wyhash_digest_t(b);
assert_eq!(v1, v2, "index: {}", i);
}
}
#[test]
fn hashmap_test() {
let mut m = HashMap::with_hasher(WyHasher::new());
for i in 0..100 {
m.insert(i, i);
}
}
}