pub fn hashlittle2(key: &[u8], pc: u32, pb: u32) -> (u32, u32) {
let mut a: u32;
let mut b: u32;
let mut c: u32;
a = 0xdeadbeef_u32
.wrapping_add(key.len() as u32)
.wrapping_add(pc);
b = a;
c = a.wrapping_add(pb);
let mut offset = 0;
let mut remaining = key.len();
while remaining > 12 {
a = a.wrapping_add(
key[offset] as u32
| (key[offset + 1] as u32) << 8
| (key[offset + 2] as u32) << 16
| (key[offset + 3] as u32) << 24,
);
b = b.wrapping_add(
key[offset + 4] as u32
| (key[offset + 5] as u32) << 8
| (key[offset + 6] as u32) << 16
| (key[offset + 7] as u32) << 24,
);
c = c.wrapping_add(
key[offset + 8] as u32
| (key[offset + 9] as u32) << 8
| (key[offset + 10] as u32) << 16
| (key[offset + 11] as u32) << 24,
);
a = a.wrapping_sub(c);
a ^= c.rotate_left(4);
c = c.wrapping_add(b);
b = b.wrapping_sub(a);
b ^= a.rotate_left(6);
a = a.wrapping_add(c);
c = c.wrapping_sub(b);
c ^= b.rotate_left(8);
b = b.wrapping_add(a);
a = a.wrapping_sub(c);
a ^= c.rotate_left(16);
c = c.wrapping_add(b);
b = b.wrapping_sub(a);
b ^= a.rotate_left(19);
a = a.wrapping_add(c);
c = c.wrapping_sub(b);
c ^= b.rotate_left(4);
b = b.wrapping_add(a);
offset += 12;
remaining -= 12;
}
match remaining {
12 => {
c = c.wrapping_add((key[offset + 11] as u32) << 24);
c = c.wrapping_add((key[offset + 10] as u32) << 16);
c = c.wrapping_add((key[offset + 9] as u32) << 8);
c = c.wrapping_add(key[offset + 8] as u32);
b = b.wrapping_add((key[offset + 7] as u32) << 24);
b = b.wrapping_add((key[offset + 6] as u32) << 16);
b = b.wrapping_add((key[offset + 5] as u32) << 8);
b = b.wrapping_add(key[offset + 4] as u32);
a = a.wrapping_add((key[offset + 3] as u32) << 24);
a = a.wrapping_add((key[offset + 2] as u32) << 16);
a = a.wrapping_add((key[offset + 1] as u32) << 8);
a = a.wrapping_add(key[offset] as u32);
}
11 => {
c = c.wrapping_add((key[offset + 10] as u32) << 16);
c = c.wrapping_add((key[offset + 9] as u32) << 8);
c = c.wrapping_add(key[offset + 8] as u32);
b = b.wrapping_add((key[offset + 7] as u32) << 24);
b = b.wrapping_add((key[offset + 6] as u32) << 16);
b = b.wrapping_add((key[offset + 5] as u32) << 8);
b = b.wrapping_add(key[offset + 4] as u32);
a = a.wrapping_add((key[offset + 3] as u32) << 24);
a = a.wrapping_add((key[offset + 2] as u32) << 16);
a = a.wrapping_add((key[offset + 1] as u32) << 8);
a = a.wrapping_add(key[offset] as u32);
}
10 => {
c = c.wrapping_add((key[offset + 9] as u32) << 8);
c = c.wrapping_add(key[offset + 8] as u32);
b = b.wrapping_add((key[offset + 7] as u32) << 24);
b = b.wrapping_add((key[offset + 6] as u32) << 16);
b = b.wrapping_add((key[offset + 5] as u32) << 8);
b = b.wrapping_add(key[offset + 4] as u32);
a = a.wrapping_add((key[offset + 3] as u32) << 24);
a = a.wrapping_add((key[offset + 2] as u32) << 16);
a = a.wrapping_add((key[offset + 1] as u32) << 8);
a = a.wrapping_add(key[offset] as u32);
}
9 => {
c = c.wrapping_add(key[offset + 8] as u32);
b = b.wrapping_add((key[offset + 7] as u32) << 24);
b = b.wrapping_add((key[offset + 6] as u32) << 16);
b = b.wrapping_add((key[offset + 5] as u32) << 8);
b = b.wrapping_add(key[offset + 4] as u32);
a = a.wrapping_add((key[offset + 3] as u32) << 24);
a = a.wrapping_add((key[offset + 2] as u32) << 16);
a = a.wrapping_add((key[offset + 1] as u32) << 8);
a = a.wrapping_add(key[offset] as u32);
}
8 => {
b = b.wrapping_add((key[offset + 7] as u32) << 24);
b = b.wrapping_add((key[offset + 6] as u32) << 16);
b = b.wrapping_add((key[offset + 5] as u32) << 8);
b = b.wrapping_add(key[offset + 4] as u32);
a = a.wrapping_add((key[offset + 3] as u32) << 24);
a = a.wrapping_add((key[offset + 2] as u32) << 16);
a = a.wrapping_add((key[offset + 1] as u32) << 8);
a = a.wrapping_add(key[offset] as u32);
}
7 => {
b = b.wrapping_add((key[offset + 6] as u32) << 16);
b = b.wrapping_add((key[offset + 5] as u32) << 8);
b = b.wrapping_add(key[offset + 4] as u32);
a = a.wrapping_add((key[offset + 3] as u32) << 24);
a = a.wrapping_add((key[offset + 2] as u32) << 16);
a = a.wrapping_add((key[offset + 1] as u32) << 8);
a = a.wrapping_add(key[offset] as u32);
}
6 => {
b = b.wrapping_add((key[offset + 5] as u32) << 8);
b = b.wrapping_add(key[offset + 4] as u32);
a = a.wrapping_add((key[offset + 3] as u32) << 24);
a = a.wrapping_add((key[offset + 2] as u32) << 16);
a = a.wrapping_add((key[offset + 1] as u32) << 8);
a = a.wrapping_add(key[offset] as u32);
}
5 => {
b = b.wrapping_add(key[offset + 4] as u32);
a = a.wrapping_add((key[offset + 3] as u32) << 24);
a = a.wrapping_add((key[offset + 2] as u32) << 16);
a = a.wrapping_add((key[offset + 1] as u32) << 8);
a = a.wrapping_add(key[offset] as u32);
}
4 => {
a = a.wrapping_add((key[offset + 3] as u32) << 24);
a = a.wrapping_add((key[offset + 2] as u32) << 16);
a = a.wrapping_add((key[offset + 1] as u32) << 8);
a = a.wrapping_add(key[offset] as u32);
}
3 => {
a = a.wrapping_add((key[offset + 2] as u32) << 16);
a = a.wrapping_add((key[offset + 1] as u32) << 8);
a = a.wrapping_add(key[offset] as u32);
}
2 => {
a = a.wrapping_add((key[offset + 1] as u32) << 8);
a = a.wrapping_add(key[offset] as u32);
}
1 => {
a = a.wrapping_add(key[offset] as u32);
}
0 => {
return (c, b);
}
_ => unreachable!(),
}
c ^= b;
c = c.wrapping_sub(b.rotate_left(14));
a ^= c;
a = a.wrapping_sub(c.rotate_left(11));
b ^= a;
b = b.wrapping_sub(a.rotate_left(25));
c ^= b;
c = c.wrapping_sub(b.rotate_left(16));
a ^= c;
a = a.wrapping_sub(c.rotate_left(4));
b ^= a;
b = b.wrapping_sub(a.rotate_left(14));
c ^= b;
c = c.wrapping_sub(b.rotate_left(24));
(c, b)
}
pub fn hashpath(path: &str) -> u64 {
let normalized = path.to_uppercase().replace('/', "\\");
let (pc, pb) = hashlittle2(normalized.as_bytes(), 0, 0);
((pb as u64) << 32) | (pc as u64)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn hashlittle2_empty() {
let (pc, pb) = hashlittle2(b"", 0, 0);
assert_eq!(pc, 0xdeadbeef);
assert_eq!(pb, 0xdeadbeef);
}
#[test]
fn hashlittle2_known_vectors() {
let (pc, pb) = hashlittle2(b"abc", 0, 0);
assert_ne!(pc, 0); assert_ne!(pb, 0);
}
#[test]
fn hashpath_normalizes_case_and_slashes() {
let h1 = hashpath("World/Maps/Azeroth/Azeroth.wdt");
let h2 = hashpath("world/maps/azeroth/azeroth.wdt");
let h3 = hashpath("WORLD\\MAPS\\AZEROTH\\AZEROTH.WDT");
assert_eq!(h1, h2);
assert_eq!(h2, h3);
}
#[test]
fn hashpath_deterministic() {
let h1 = hashpath("test/file.txt");
let h2 = hashpath("test/file.txt");
assert_eq!(h1, h2);
}
}