use core::slice;
use diffuse;
#[inline(always)]
fn read_int(buf: &[u8]) -> u64 {
let ptr = buf.as_ptr();
unsafe {
match buf.len() {
1 => *ptr as u64,
2 => (*(ptr as *const u16)).to_le() as u64,
3 => {
let a = (*(ptr as *const u16)).to_le() as u64;
let b = *ptr.offset(2) as u64;
a | (b << 16)
},
4 => (*(ptr as *const u32)).to_le() as u64,
5 => {
let a = (*(ptr as *const u32)).to_le() as u64;
let b = *ptr.offset(4) as u64;
a | (b << 32)
},
6 => {
let a = (*(ptr as *const u32)).to_le() as u64;
let b = (*(ptr.offset(4) as *const u16)).to_le() as u64;
a | (b << 32)
},
7 => {
let a = (*(ptr as *const u32)).to_le() as u64;
let b = (*(ptr.offset(4) as *const u16)).to_le() as u64;
let c = *ptr.offset(6) as u64;
a | (b << 32) | (c << 48)
},
_ => 0,
}
}
}
#[inline(always)]
unsafe fn read_u64(ptr: *const u8) -> u64 {
#[cfg(target_pointer_width = "32")]
{
(*(ptr as *const u32)).to_le() as u64 | ((*(ptr as *const u32)).to_le() as u64) << 32
}
#[cfg(target_pointer_width = "64")]
{
(*(ptr as *const u64)).to_le()
}
}
pub fn hash(buf: &[u8]) -> u64 {
unsafe {
let mut a = 0x16f11fe89b0d677c;
let mut b = 0xb480a793d8e6c86c;
let mut c = 0x6fe2e5aaf078ebc9;
let mut d = 0x14f994a4c5259381;
let mut ptr = buf.as_ptr();
let end_ptr = buf.as_ptr().offset(buf.len() as isize & !0x1F) as usize;
while end_ptr > ptr as usize {
a = a ^ read_u64(ptr);
ptr = ptr.offset(8);
b = b ^ read_u64(ptr);
ptr = ptr.offset(8);
c = c ^ read_u64(ptr);
ptr = ptr.offset(8);
d = d ^ read_u64(ptr);
ptr = ptr.offset(8);
a = diffuse(a);
b = diffuse(b);
c = diffuse(c);
d = diffuse(d);
}
let mut excessive = end_ptr;
excessive = buf.len() as usize + buf.as_ptr() as usize - excessive as usize;
match excessive {
0 => {},
1...7 => {
a = a ^ read_int(slice::from_raw_parts(ptr as *const u8, excessive));
a = diffuse(a);
},
8 => {
a = a ^ read_u64(ptr);
a = diffuse(a);
},
9...15 => {
a = a ^ read_u64(ptr);
ptr = ptr.offset(8);
excessive = excessive - 8;
b = b ^ read_int(slice::from_raw_parts(ptr as *const u8, excessive));
a = diffuse(a);
b = diffuse(b);
},
16 => {
a = a ^ read_u64(ptr);
ptr = ptr.offset(8);
b = b ^ read_u64(ptr);
a = diffuse(a);
b = diffuse(b);
},
17...23 => {
a = a ^ read_u64(ptr);
ptr = ptr.offset(8);
b = b ^ read_u64(ptr);
ptr = ptr.offset(8);
excessive = excessive - 16;
c = c ^ read_int(slice::from_raw_parts(ptr as *const u8, excessive));
a = diffuse(a);
b = diffuse(b);
c = diffuse(c);
},
24 => {
a = a ^ read_u64(ptr);
ptr = ptr.offset(8);
b = b ^ read_u64(ptr);
ptr = ptr.offset(8);
c = c ^ read_u64(ptr);
a = diffuse(a);
b = diffuse(b);
c = diffuse(c);
},
_ => {
a = a ^ read_u64(ptr);
ptr = ptr.offset(8);
b = b ^ read_u64(ptr);
ptr = ptr.offset(8);
c = c ^ read_u64(ptr);
ptr = ptr.offset(8);
excessive = excessive - 24;
d = d ^ read_int(slice::from_raw_parts(ptr as *const u8, excessive));
a = diffuse(a);
b = diffuse(b);
c = diffuse(c);
d = diffuse(d);
}
}
a = a ^ b;
c = c ^ d;
a = a ^ c;
a = a ^ buf.len() as u64;
diffuse(a)
}
}
#[cfg(test)]
mod tests {
use super::*;
use reference;
fn hash_match(a: &[u8]) {
assert_eq!(hash(a), reference::hash(a));
}
#[test]
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"));
}
}