use std::hash::Hasher;
use std::num::Wrapping;
use std::{mem, ptr};
pub mod spooky_hash;
pub struct OAATHasher(Wrapping<u64>);
impl Hasher for OAATHasher {
#[inline]
fn finish(&self) -> u64 {
let mut hash = self.0;
hash += hash << 3;
hash ^= hash >> 11;
hash += hash << 15;
hash.0
}
#[inline]
fn write(&mut self, bytes: &[u8]) {
for byte in bytes.iter() {
self.0 += Wrapping(*byte as u64);
self.0 += self.0 << 10;
self.0 ^= self.0 >> 6;
}
}
}
default_for_constant!(OAATHasher, Wrapping(0));
hasher_to_fcn!(
oaat,
OAATHasher
);
#[cfg(test)]
mod oaat_tests {
use super::*;
#[test]
fn basic() {
assert_eq!(oaat(b""), 0);
assert_eq!(oaat(b"a"), 29161854018);
assert_eq!(oaat(b"b"), 30079156635);
assert_eq!(oaat(b"ab"), 30087418617432);
assert_eq!(oaat(b"abcdefg"), 3103867595652801641);
}
}
pub struct Lookup3Hasher {
pc: Wrapping<u32>, pb: Wrapping<u32>, }
impl Default for Lookup3Hasher {
fn default() -> Lookup3Hasher {
Lookup3Hasher {
pc: Wrapping(0),
pb: Wrapping(0),
}
}
}
#[inline]
fn rot(x: Wrapping<u32>, k: usize) -> Wrapping<u32> {
x << k | x >> (32 - k)
}
#[inline]
fn mix(a: &mut Wrapping<u32>, b: &mut Wrapping<u32>, c: &mut Wrapping<u32>) {
*a -= *c;
*a ^= rot(*c, 4);
*c += *b;
*b -= *a;
*b ^= rot(*a, 6);
*a += *c;
*c -= *b;
*c ^= rot(*b, 8);
*b += *a;
*a -= *c;
*a ^= rot(*c, 16);
*c += *b;
*b -= *a;
*b ^= rot(*a, 19);
*a += *c;
*c -= *b;
*c ^= rot(*b, 4);
*b += *a;
}
#[inline]
fn final_mix(a: &mut Wrapping<u32>, b: &mut Wrapping<u32>, c: &mut Wrapping<u32>) {
*c ^= *b;
*c -= rot(*b, 14);
*a ^= *c;
*a -= rot(*c, 11);
*b ^= *a;
*b -= rot(*a, 25);
*c ^= *b;
*c -= rot(*b, 16);
*a ^= *c;
*a -= rot(*c, 4);
*b ^= *a;
*b -= rot(*a, 14);
*c ^= *b;
*c -= rot(*b, 24);
}
#[inline]
fn shift_add(s: &[u8]) -> Wrapping<u32> {
Wrapping(match s.len() {
1 => s[0] as u32,
2 => (s[0] as u32) + ((s[1] as u32) << 8),
3 => (s[0] as u32) + ((s[1] as u32) << 8) + ((s[2] as u32) << 16),
4 => (s[0] as u32) + ((s[1] as u32) << 8) + ((s[2] as u32) << 16) + ((s[3] as u32) << 24),
_ => 0 as u32,
})
}
#[inline]
fn offset_to_align<T>(ptr: *const T, align: usize) -> usize {
align - (ptr as usize & (align - 1))
}
impl Hasher for Lookup3Hasher {
#[inline]
fn finish(&self) -> u64 {
(self.pc.0 as u64) + ((self.pb.0 as u64) << 32)
}
#[inline]
fn write(&mut self, bytes: &[u8]) {
if bytes.len() == 0 {
return;
}
let initial = Wrapping(0xdeadbeefu32) + Wrapping(bytes.len() as u32) + self.pc;
let mut a: Wrapping<u32> = initial;
let mut b: Wrapping<u32> = initial;
let mut c: Wrapping<u32> = initial;
c += self.pb;
if cfg!(target_endian = "little") && offset_to_align(bytes.as_ptr(), 4) == 0 {
for chunk in bytes.chunks(12) {
if chunk.len() == 12 {
a += Wrapping(load_int_le!(chunk, 0, u32));
b += Wrapping(load_int_le!(chunk, 4, u32));
c += Wrapping(load_int_le!(chunk, 8, u32));
mix(&mut a, &mut b, &mut c);
} else if chunk.len() >= 8 {
a += Wrapping(load_int_le!(chunk, 0, u32));
b += Wrapping(load_int_le!(chunk, 4, u32));
c += shift_add(&chunk[8..]);
} else if chunk.len() >= 4 {
a += Wrapping(load_int_le!(chunk, 0, u32));
b += shift_add(&chunk[4..]);
} else {
a += shift_add(chunk);
}
}
} else if cfg!(target_endian = "little") && offset_to_align(bytes.as_ptr(), 2) == 0 {
for chunk in bytes.chunks(12) {
if chunk.len() == 12 {
a += Wrapping(load_int_le!(chunk, 0, u16) as u32)
+ Wrapping(load_int_le!(chunk, 0, u16) as u32)
<< 16;
b += Wrapping(load_int_le!(chunk, 4, u16) as u32)
+ Wrapping(load_int_le!(chunk, 6, u16) as u32)
<< 16;
c += Wrapping(load_int_le!(chunk, 8, u16) as u32)
+ Wrapping(load_int_le!(chunk, 10, u16) as u32)
<< 16;
mix(&mut a, &mut b, &mut c);
} else if chunk.len() >= 8 {
a += Wrapping(load_int_le!(chunk, 0, u16) as u32)
+ Wrapping(load_int_le!(chunk, 0, u16) as u32)
<< 16;
b += Wrapping(load_int_le!(chunk, 4, u16) as u32)
+ Wrapping(load_int_le!(chunk, 6, u16) as u32)
<< 16;
c += shift_add(&chunk[8..]);
} else if chunk.len() >= 4 {
a += Wrapping(load_int_le!(chunk, 0, u16) as u32)
+ Wrapping(load_int_le!(chunk, 0, u16) as u32)
<< 16;
b += shift_add(&chunk[4..]);
} else {
a += shift_add(chunk);
}
}
} else {
for chunk in bytes.chunks(12) {
if chunk.len() == 12 {
a += shift_add(&chunk[..4]);
b += shift_add(&chunk[4..8]);
c += shift_add(&chunk[8..]);
mix(&mut a, &mut b, &mut c);
} else if chunk.len() >= 8 {
a += shift_add(&chunk[..4]);
b += shift_add(&chunk[4..8]);
c += shift_add(&chunk[8..]);
} else if chunk.len() >= 4 {
a += shift_add(&chunk[..4]);
b += shift_add(&chunk[4..]);
} else {
a += shift_add(chunk);
}
}
}
final_mix(&mut a, &mut b, &mut c);
self.pb = b;
self.pc = c;
}
}
hasher_to_fcn!(
lookup3,
Lookup3Hasher
);
#[cfg(test)]
mod lookup3_tests {
use super::*;
#[test]
fn basic() {
assert_eq!(lookup3(b""), 0);
assert_eq!(lookup3(b"a"), 6351843130003064584);
assert_eq!(lookup3(b"b"), 5351957087540069269);
assert_eq!(lookup3(b"ab"), 7744397999705663711);
assert_eq!(lookup3(b"abcd"), 16288908501016938652);
assert_eq!(lookup3(b"abcdefg"), 6461572128488215717);
}
}