1pub fn hashlittle2(key: &[u8], pc: u32, pb: u32) -> (u32, u32) {
12 let mut a: u32;
13 let mut b: u32;
14 let mut c: u32;
15
16 a = 0xdeadbeef_u32
17 .wrapping_add(key.len() as u32)
18 .wrapping_add(pc);
19 b = a;
20 c = a.wrapping_add(pb);
21
22 let mut offset = 0;
23 let mut remaining = key.len();
24
25 while remaining > 12 {
27 a = a.wrapping_add(
28 key[offset] as u32
29 | (key[offset + 1] as u32) << 8
30 | (key[offset + 2] as u32) << 16
31 | (key[offset + 3] as u32) << 24,
32 );
33 b = b.wrapping_add(
34 key[offset + 4] as u32
35 | (key[offset + 5] as u32) << 8
36 | (key[offset + 6] as u32) << 16
37 | (key[offset + 7] as u32) << 24,
38 );
39 c = c.wrapping_add(
40 key[offset + 8] as u32
41 | (key[offset + 9] as u32) << 8
42 | (key[offset + 10] as u32) << 16
43 | (key[offset + 11] as u32) << 24,
44 );
45
46 a = a.wrapping_sub(c);
48 a ^= c.rotate_left(4);
49 c = c.wrapping_add(b);
50 b = b.wrapping_sub(a);
51 b ^= a.rotate_left(6);
52 a = a.wrapping_add(c);
53 c = c.wrapping_sub(b);
54 c ^= b.rotate_left(8);
55 b = b.wrapping_add(a);
56 a = a.wrapping_sub(c);
57 a ^= c.rotate_left(16);
58 c = c.wrapping_add(b);
59 b = b.wrapping_sub(a);
60 b ^= a.rotate_left(19);
61 a = a.wrapping_add(c);
62 c = c.wrapping_sub(b);
63 c ^= b.rotate_left(4);
64 b = b.wrapping_add(a);
65
66 offset += 12;
67 remaining -= 12;
68 }
69
70 match remaining {
72 12 => {
73 c = c.wrapping_add((key[offset + 11] as u32) << 24);
74 c = c.wrapping_add((key[offset + 10] as u32) << 16);
75 c = c.wrapping_add((key[offset + 9] as u32) << 8);
76 c = c.wrapping_add(key[offset + 8] as u32);
77 b = b.wrapping_add((key[offset + 7] as u32) << 24);
78 b = b.wrapping_add((key[offset + 6] as u32) << 16);
79 b = b.wrapping_add((key[offset + 5] as u32) << 8);
80 b = b.wrapping_add(key[offset + 4] as u32);
81 a = a.wrapping_add((key[offset + 3] as u32) << 24);
82 a = a.wrapping_add((key[offset + 2] as u32) << 16);
83 a = a.wrapping_add((key[offset + 1] as u32) << 8);
84 a = a.wrapping_add(key[offset] as u32);
85 }
86 11 => {
87 c = c.wrapping_add((key[offset + 10] as u32) << 16);
88 c = c.wrapping_add((key[offset + 9] as u32) << 8);
89 c = c.wrapping_add(key[offset + 8] as u32);
90 b = b.wrapping_add((key[offset + 7] as u32) << 24);
91 b = b.wrapping_add((key[offset + 6] as u32) << 16);
92 b = b.wrapping_add((key[offset + 5] as u32) << 8);
93 b = b.wrapping_add(key[offset + 4] as u32);
94 a = a.wrapping_add((key[offset + 3] as u32) << 24);
95 a = a.wrapping_add((key[offset + 2] as u32) << 16);
96 a = a.wrapping_add((key[offset + 1] as u32) << 8);
97 a = a.wrapping_add(key[offset] as u32);
98 }
99 10 => {
100 c = c.wrapping_add((key[offset + 9] as u32) << 8);
101 c = c.wrapping_add(key[offset + 8] as u32);
102 b = b.wrapping_add((key[offset + 7] as u32) << 24);
103 b = b.wrapping_add((key[offset + 6] as u32) << 16);
104 b = b.wrapping_add((key[offset + 5] as u32) << 8);
105 b = b.wrapping_add(key[offset + 4] as u32);
106 a = a.wrapping_add((key[offset + 3] as u32) << 24);
107 a = a.wrapping_add((key[offset + 2] as u32) << 16);
108 a = a.wrapping_add((key[offset + 1] as u32) << 8);
109 a = a.wrapping_add(key[offset] as u32);
110 }
111 9 => {
112 c = c.wrapping_add(key[offset + 8] as u32);
113 b = b.wrapping_add((key[offset + 7] as u32) << 24);
114 b = b.wrapping_add((key[offset + 6] as u32) << 16);
115 b = b.wrapping_add((key[offset + 5] as u32) << 8);
116 b = b.wrapping_add(key[offset + 4] as u32);
117 a = a.wrapping_add((key[offset + 3] as u32) << 24);
118 a = a.wrapping_add((key[offset + 2] as u32) << 16);
119 a = a.wrapping_add((key[offset + 1] as u32) << 8);
120 a = a.wrapping_add(key[offset] as u32);
121 }
122 8 => {
123 b = b.wrapping_add((key[offset + 7] as u32) << 24);
124 b = b.wrapping_add((key[offset + 6] as u32) << 16);
125 b = b.wrapping_add((key[offset + 5] as u32) << 8);
126 b = b.wrapping_add(key[offset + 4] as u32);
127 a = a.wrapping_add((key[offset + 3] as u32) << 24);
128 a = a.wrapping_add((key[offset + 2] as u32) << 16);
129 a = a.wrapping_add((key[offset + 1] as u32) << 8);
130 a = a.wrapping_add(key[offset] as u32);
131 }
132 7 => {
133 b = b.wrapping_add((key[offset + 6] as u32) << 16);
134 b = b.wrapping_add((key[offset + 5] as u32) << 8);
135 b = b.wrapping_add(key[offset + 4] as u32);
136 a = a.wrapping_add((key[offset + 3] as u32) << 24);
137 a = a.wrapping_add((key[offset + 2] as u32) << 16);
138 a = a.wrapping_add((key[offset + 1] as u32) << 8);
139 a = a.wrapping_add(key[offset] as u32);
140 }
141 6 => {
142 b = b.wrapping_add((key[offset + 5] as u32) << 8);
143 b = b.wrapping_add(key[offset + 4] as u32);
144 a = a.wrapping_add((key[offset + 3] as u32) << 24);
145 a = a.wrapping_add((key[offset + 2] as u32) << 16);
146 a = a.wrapping_add((key[offset + 1] as u32) << 8);
147 a = a.wrapping_add(key[offset] as u32);
148 }
149 5 => {
150 b = b.wrapping_add(key[offset + 4] as u32);
151 a = a.wrapping_add((key[offset + 3] as u32) << 24);
152 a = a.wrapping_add((key[offset + 2] as u32) << 16);
153 a = a.wrapping_add((key[offset + 1] as u32) << 8);
154 a = a.wrapping_add(key[offset] as u32);
155 }
156 4 => {
157 a = a.wrapping_add((key[offset + 3] as u32) << 24);
158 a = a.wrapping_add((key[offset + 2] as u32) << 16);
159 a = a.wrapping_add((key[offset + 1] as u32) << 8);
160 a = a.wrapping_add(key[offset] as u32);
161 }
162 3 => {
163 a = a.wrapping_add((key[offset + 2] as u32) << 16);
164 a = a.wrapping_add((key[offset + 1] as u32) << 8);
165 a = a.wrapping_add(key[offset] as u32);
166 }
167 2 => {
168 a = a.wrapping_add((key[offset + 1] as u32) << 8);
169 a = a.wrapping_add(key[offset] as u32);
170 }
171 1 => {
172 a = a.wrapping_add(key[offset] as u32);
173 }
174 0 => {
175 return (c, b);
176 }
177 _ => unreachable!(),
178 }
179
180 c ^= b;
182 c = c.wrapping_sub(b.rotate_left(14));
183 a ^= c;
184 a = a.wrapping_sub(c.rotate_left(11));
185 b ^= a;
186 b = b.wrapping_sub(a.rotate_left(25));
187 c ^= b;
188 c = c.wrapping_sub(b.rotate_left(16));
189 a ^= c;
190 a = a.wrapping_sub(c.rotate_left(4));
191 b ^= a;
192 b = b.wrapping_sub(a.rotate_left(14));
193 c ^= b;
194 c = c.wrapping_sub(b.rotate_left(24));
195
196 (c, b)
197}
198
199pub fn hashpath(path: &str) -> u64 {
201 let normalized = path.to_uppercase().replace('/', "\\");
202 let (pc, pb) = hashlittle2(normalized.as_bytes(), 0, 0);
203 ((pb as u64) << 32) | (pc as u64)
204}
205
206#[cfg(test)]
207mod tests {
208 use super::*;
209
210 #[test]
211 fn hashlittle2_empty() {
212 let (pc, pb) = hashlittle2(b"", 0, 0);
213 assert_eq!(pc, 0xdeadbeef);
216 assert_eq!(pb, 0xdeadbeef);
217 }
218
219 #[test]
220 fn hashlittle2_known_vectors() {
221 let (pc, pb) = hashlittle2(b"abc", 0, 0);
223 assert_ne!(pc, 0); assert_ne!(pb, 0);
226 }
227
228 #[test]
229 fn hashpath_normalizes_case_and_slashes() {
230 let h1 = hashpath("World/Maps/Azeroth/Azeroth.wdt");
231 let h2 = hashpath("world/maps/azeroth/azeroth.wdt");
232 let h3 = hashpath("WORLD\\MAPS\\AZEROTH\\AZEROTH.WDT");
233 assert_eq!(h1, h2);
234 assert_eq!(h2, h3);
235 }
236
237 #[test]
238 fn hashpath_deterministic() {
239 let h1 = hashpath("test/file.txt");
240 let h2 = hashpath("test/file.txt");
241 assert_eq!(h1, h2);
242 }
243}