use std::slice;
use helper;
#[derive(Clone)]
pub struct State {
a: u64,
b: u64,
c: u64,
d: u64,
written: u64,
}
impl State {
pub fn new(a: u64, b: u64, c: u64, d: u64) -> State {
State {
a: a,
b: b,
c: c,
d: d,
written: 0,
}
}
pub fn hash(buf: &[u8], (mut a, mut b, mut c, mut d): (u64, u64, u64, u64)) -> State {
unsafe {
let mut ptr = buf.as_ptr();
let end_ptr = buf.as_ptr().offset(buf.len() as isize & !0x1F);
while end_ptr > ptr {
a ^= helper::read_u64(ptr);
b ^= helper::read_u64(ptr.offset(8));
c ^= helper::read_u64(ptr.offset(16));
d ^= helper::read_u64(ptr.offset(24));
ptr = ptr.offset(32);
a = helper::diffuse(a);
b = helper::diffuse(b);
c = helper::diffuse(c);
d = helper::diffuse(d);
}
let mut excessive = buf.len() as usize + buf.as_ptr() as usize - end_ptr as usize;
match excessive {
0 => {}
1..=7 => {
a ^= helper::read_int(slice::from_raw_parts(ptr as *const u8, excessive));
a = helper::diffuse(a);
}
8 => {
a ^= helper::read_u64(ptr);
a = helper::diffuse(a);
}
9..=15 => {
a ^= helper::read_u64(ptr);
excessive = excessive - 8;
b ^= helper::read_int(slice::from_raw_parts(ptr.offset(8), excessive));
a = helper::diffuse(a);
b = helper::diffuse(b);
}
16 => {
a = helper::diffuse(a ^ helper::read_u64(ptr));
b = helper::diffuse(b ^ helper::read_u64(ptr.offset(8)));
}
17..=23 => {
a ^= helper::read_u64(ptr);
b ^= helper::read_u64(ptr.offset(8));
excessive = excessive - 16;
c ^= helper::read_int(slice::from_raw_parts(ptr.offset(16), excessive));
a = helper::diffuse(a);
b = helper::diffuse(b);
c = helper::diffuse(c);
}
24 => {
a ^= helper::read_u64(ptr);
b ^= helper::read_u64(ptr.offset(8));
c ^= helper::read_u64(ptr.offset(16));
a = helper::diffuse(a);
b = helper::diffuse(b);
c = helper::diffuse(c);
}
_ => {
a ^= helper::read_u64(ptr);
b ^= helper::read_u64(ptr.offset(8));
c ^= helper::read_u64(ptr.offset(16));
excessive = excessive - 24;
d ^= helper::read_int(slice::from_raw_parts(ptr.offset(24), excessive));
a = helper::diffuse(a);
b = helper::diffuse(b);
c = helper::diffuse(c);
d = helper::diffuse(d);
}
}
}
State {
a: a,
b: b,
c: c,
d: d,
written: buf.len() as u64,
}
}
pub fn push(&mut self, x: u64) {
let a = helper::diffuse(self.a ^ x);
self.a = self.b;
self.b = self.c;
self.c = self.d;
self.d = a;
self.written += 8;
}
pub fn pop(&mut self, last: u64) {
let d = helper::undiffuse(self.d) ^ last;
self.d = self.c;
self.c = self.b;
self.b = self.a;
self.a = d;
self.written -= 8;
}
#[inline]
pub fn finalize(self) -> u64 {
let State {
written,
mut a,
b,
mut c,
d,
} = self;
a ^= b;
c ^= d;
a ^= c;
a ^= written;
helper::diffuse(a)
}
}
pub fn hash(buf: &[u8]) -> u64 {
hash_seeded(
buf,
0x16f11fe89b0d677c,
0xb480a793d8e6c86c,
0x6fe2e5aaf078ebc9,
0x14f994a4c5259381,
)
}
pub fn hash_seeded(buf: &[u8], a: u64, b: u64, c: u64, d: u64) -> u64 {
State::hash(buf, (a, b, c, d)).finalize()
}
#[cfg(test)]
mod tests {
use super::*;
use reference;
fn hash_match(a: &[u8]) {
assert_eq!(hash(a), reference::hash(a));
assert_eq!(
hash_seeded(a, 1, 1, 1, 1),
reference::hash_seeded(a, 1, 1, 1, 1)
);
assert_eq!(
hash_seeded(a, 500, 2873, 2389, 9283),
reference::hash_seeded(a, 500, 2873, 2389, 9283)
);
assert_eq!(
hash_seeded(a, 238945723984, 872894734, 239478243, 28937498234),
reference::hash_seeded(a, 238945723984, 872894734, 239478243, 28937498234)
);
assert_eq!(
hash_seeded(a, !0, !0, !0, !0),
reference::hash_seeded(a, !0, !0, !0, !0)
);
assert_eq!(
hash_seeded(a, 0, 0, 0, 0),
reference::hash_seeded(a, 0, 0, 0, 0)
);
}
#[test]
#[cfg_attr(miri, ignore)] fn zero() {
let arr = [0; 4096];
for n in 0..4096 {
hash_match(&arr[0..n]);
}
}
#[test]
fn seq() {
let mut buf = [0; 4096];
for i in 0..4096 {
buf[i] = i as u8;
}
hash_match(&buf);
}
#[test]
fn position_depedent() {
let mut buf1 = [0; 4098];
for i in 0..4098 {
buf1[i] = i as u8;
}
let mut buf2 = [0; 4098];
for i in 0..4098 {
buf2[i] = i as u8 ^ 1;
}
assert!(hash(&buf1) != hash(&buf2));
}
#[test]
fn shakespear() {
hash_match(b"to be or not to be");
hash_match(b"love is a wonderful terrible thing");
}
#[test]
fn zero_senitive() {
assert_ne!(hash(&[1, 2, 3, 4]), hash(&[1, 0, 2, 3, 4]));
assert_ne!(hash(&[1, 2, 3, 4]), hash(&[1, 0, 0, 2, 3, 4]));
assert_ne!(hash(&[1, 2, 3, 4]), hash(&[1, 2, 3, 4, 0]));
assert_ne!(hash(&[1, 2, 3, 4]), hash(&[0, 1, 2, 3, 4]));
assert_ne!(hash(&[0, 0, 0]), hash(&[0, 0, 0, 0, 0]));
}
#[test]
fn not_equal() {
assert_ne!(hash(b"to be or not to be "), hash(b"to be or not to be"));
assert_ne!(hash(b"jkjke"), hash(b"jkjk"));
assert_ne!(hash(b"ijkjke"), hash(b"ijkjk"));
assert_ne!(hash(b"iijkjke"), hash(b"iijkjk"));
assert_ne!(hash(b"iiijkjke"), hash(b"iiijkjk"));
assert_ne!(hash(b"iiiijkjke"), hash(b"iiiijkjk"));
assert_ne!(hash(b"iiiiijkjke"), hash(b"iiiiijkjk"));
assert_ne!(hash(b"iiiiiijkjke"), hash(b"iiiiiijkjk"));
assert_ne!(hash(b"iiiiiiijkjke"), hash(b"iiiiiiijkjk"));
assert_ne!(hash(b"iiiiiiiijkjke"), hash(b"iiiiiiiijkjk"));
assert_ne!(hash(b"ab"), hash(b"bb"));
}
#[test]
fn push() {
let mut state = State::new(1, 2, 3, 4);
state.push(!0);
state.push(0);
assert_eq!(
hash_seeded(
&[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0],
1,
2,
3,
4
),
state.finalize()
);
}
#[test]
fn pop() {
let mut state = State::new(1, 2, 3, 4);
state.push(!0);
state.push(0);
state.pop(0);
assert_eq!(
hash_seeded(
&[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF],
1,
2,
3,
4
),
state.finalize()
);
}
}