1#![allow(unsafe_code)]
2
3use crate::key::Key;
4use crate::traits::HighwayHash;
5use core::{default::Default, fmt::Debug, mem::ManuallyDrop};
6
7#[cfg(target_arch = "aarch64")]
8use crate::aarch64::NeonHash;
9#[cfg(not(any(
10 all(target_family = "wasm", target_feature = "simd128"),
11 target_arch = "aarch64"
12)))]
13use crate::portable::PortableHash;
14#[cfg(all(target_family = "wasm", target_feature = "simd128"))]
15use crate::wasm::WasmHash;
16#[cfg(target_arch = "x86_64")]
17use crate::{AvxHash, SseHash};
18
19union HighwayChoices {
23 #[cfg(not(any(
24 all(target_family = "wasm", target_feature = "simd128"),
25 target_arch = "aarch64"
26 )))]
27 portable: ManuallyDrop<PortableHash>,
28 #[cfg(target_arch = "x86_64")]
29 avx: ManuallyDrop<AvxHash>,
30 #[cfg(target_arch = "x86_64")]
31 sse: ManuallyDrop<SseHash>,
32 #[cfg(target_arch = "aarch64")]
33 neon: ManuallyDrop<NeonHash>,
34 #[cfg(all(target_family = "wasm", target_feature = "simd128"))]
35 wasm: ManuallyDrop<WasmHash>,
36}
37
38pub struct HighwayHasher {
40 tag: u8,
41 inner: HighwayChoices,
42}
43
44impl Debug for HighwayHasher {
45 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
46 let mut debug = f.debug_struct("HighwayHasher");
47 debug.field("tag", &self.tag);
48
49 match self.tag {
50 #[cfg(not(any(
51 all(target_family = "wasm", target_feature = "simd128"),
52 target_arch = "aarch64"
53 )))]
54 0 => debug.field("hasher", unsafe { &self.inner.portable }),
55 #[cfg(target_arch = "x86_64")]
56 1 => debug.field("hasher", unsafe { &self.inner.avx }),
57 #[cfg(target_arch = "x86_64")]
58 2 => debug.field("hasher", unsafe { &self.inner.sse }),
59 #[cfg(target_arch = "aarch64")]
60 3 => debug.field("hasher", unsafe { &self.inner.neon }),
61 #[cfg(all(target_family = "wasm", target_feature = "simd128"))]
62 4 => debug.field("hasher", unsafe { &self.inner.wasm }),
63 _ => unsafe { core::hint::unreachable_unchecked() },
64 };
65
66 debug.finish()
67 }
68}
69
70impl Clone for HighwayHasher {
71 fn clone(&self) -> Self {
72 let tag = self.tag;
73 match tag {
74 #[cfg(not(any(
75 all(target_family = "wasm", target_feature = "simd128"),
76 target_arch = "aarch64"
77 )))]
78 0 => HighwayHasher {
79 tag,
80 inner: HighwayChoices {
81 portable: unsafe { self.inner.portable.clone() },
82 },
83 },
84 #[cfg(target_arch = "x86_64")]
85 1 => HighwayHasher {
86 tag,
87 inner: HighwayChoices {
88 avx: unsafe { self.inner.avx.clone() },
89 },
90 },
91 #[cfg(target_arch = "x86_64")]
92 2 => HighwayHasher {
93 tag,
94 inner: HighwayChoices {
95 sse: unsafe { self.inner.sse.clone() },
96 },
97 },
98 #[cfg(target_arch = "aarch64")]
99 3 => HighwayHasher {
100 tag,
101 inner: HighwayChoices {
102 neon: unsafe { self.inner.neon.clone() },
103 },
104 },
105 #[cfg(all(target_family = "wasm", target_feature = "simd128"))]
106 4 => HighwayHasher {
107 tag,
108 inner: HighwayChoices {
109 wasm: unsafe { self.inner.wasm.clone() },
110 },
111 },
112 _ => unsafe { core::hint::unreachable_unchecked() },
113 }
114 }
115}
116
117impl HighwayHash for HighwayHasher {
118 #[inline]
119 fn append(&mut self, data: &[u8]) {
120 self.append(data);
121 }
122
123 #[inline]
124 fn finalize64(mut self) -> u64 {
125 Self::finalize64(&mut self)
126 }
127
128 #[inline]
129 fn finalize128(mut self) -> [u64; 2] {
130 Self::finalize128(&mut self)
131 }
132
133 #[inline]
134 fn finalize256(mut self) -> [u64; 4] {
135 Self::finalize256(&mut self)
136 }
137
138 #[inline]
139 fn checkpoint(&self) -> [u8; 164] {
140 Self::checkpoint(self)
141 }
142}
143
144impl HighwayHasher {
145 #[must_use]
147 pub fn new(key: Key) -> Self {
148 #[cfg(target_arch = "x86_64")]
149 {
150 if cfg!(target_feature = "avx2") {
151 let avx = ManuallyDrop::new(unsafe { AvxHash::force_new(key) });
152 return HighwayHasher {
153 tag: 1,
154 inner: HighwayChoices { avx },
155 };
156 } else if cfg!(target_feature = "sse4.1") {
157 let sse = ManuallyDrop::new(unsafe { SseHash::force_new(key) });
158 return HighwayHasher {
159 tag: 2,
160 inner: HighwayChoices { sse },
161 };
162 } else {
163 #[cfg(feature = "std")]
166 if is_x86_feature_detected!("avx2") {
167 let avx = ManuallyDrop::new(unsafe { AvxHash::force_new(key) });
168 return HighwayHasher {
169 tag: 1,
170 inner: HighwayChoices { avx },
171 };
172 }
173
174 #[cfg(feature = "std")]
175 if is_x86_feature_detected!("sse4.1") {
176 let sse = ManuallyDrop::new(unsafe { SseHash::force_new(key) });
177 return HighwayHasher {
178 tag: 2,
179 inner: HighwayChoices { sse },
180 };
181 }
182 }
183 }
184
185 #[cfg(target_arch = "aarch64")]
186 {
187 let neon = ManuallyDrop::new(unsafe { NeonHash::force_new(key) });
193 HighwayHasher {
194 tag: 3,
195 inner: HighwayChoices { neon },
196 }
197 }
198
199 #[cfg(all(target_family = "wasm", target_feature = "simd128"))]
200 {
201 let wasm = ManuallyDrop::new(WasmHash::new(key));
202 HighwayHasher {
203 tag: 4,
204 inner: HighwayChoices { wasm },
205 }
206 }
207
208 #[cfg(not(any(
209 all(target_family = "wasm", target_feature = "simd128"),
210 target_arch = "aarch64"
211 )))]
212 {
213 let portable = ManuallyDrop::new(PortableHash::new(key));
214 HighwayHasher {
215 tag: 0,
216 inner: HighwayChoices { portable },
217 }
218 }
219 }
220
221 #[must_use]
223 pub fn from_checkpoint(data: [u8; 164]) -> Self {
224 #[cfg(target_arch = "x86_64")]
225 {
226 if cfg!(target_feature = "avx2") {
227 let avx = ManuallyDrop::new(unsafe { AvxHash::force_from_checkpoint(data) });
228 return HighwayHasher {
229 tag: 1,
230 inner: HighwayChoices { avx },
231 };
232 } else if cfg!(target_feature = "sse4.1") {
233 let sse = ManuallyDrop::new(unsafe { SseHash::force_from_checkpoint(data) });
234 return HighwayHasher {
235 tag: 2,
236 inner: HighwayChoices { sse },
237 };
238 } else {
239 #[cfg(feature = "std")]
242 if is_x86_feature_detected!("avx2") {
243 let avx = ManuallyDrop::new(unsafe { AvxHash::force_from_checkpoint(data) });
244 return HighwayHasher {
245 tag: 1,
246 inner: HighwayChoices { avx },
247 };
248 }
249
250 #[cfg(feature = "std")]
251 if is_x86_feature_detected!("sse4.1") {
252 let sse = ManuallyDrop::new(unsafe { SseHash::force_from_checkpoint(data) });
253 return HighwayHasher {
254 tag: 2,
255 inner: HighwayChoices { sse },
256 };
257 }
258 }
259 }
260
261 #[cfg(target_arch = "aarch64")]
262 {
263 let neon = ManuallyDrop::new(unsafe { NeonHash::force_from_checkpoint(data) });
269 HighwayHasher {
270 tag: 3,
271 inner: HighwayChoices { neon },
272 }
273 }
274
275 #[cfg(all(target_family = "wasm", target_feature = "simd128"))]
276 {
277 let wasm = ManuallyDrop::new(WasmHash::from_checkpoint(data));
278 HighwayHasher {
279 tag: 4,
280 inner: HighwayChoices { wasm },
281 }
282 }
283
284 #[cfg(not(any(
285 all(target_family = "wasm", target_feature = "simd128"),
286 target_arch = "aarch64"
287 )))]
288 {
289 let portable = ManuallyDrop::new(PortableHash::from_checkpoint(data));
290 HighwayHasher {
291 tag: 0,
292 inner: HighwayChoices { portable },
293 }
294 }
295 }
296
297 fn append(&mut self, data: &[u8]) {
298 match self.tag {
299 #[cfg(not(any(
300 all(target_family = "wasm", target_feature = "simd128"),
301 target_arch = "aarch64"
302 )))]
303 0 => unsafe { &mut self.inner.portable }.append(data),
304 #[cfg(target_arch = "x86_64")]
305 1 => unsafe { &mut self.inner.avx }.append(data),
306 #[cfg(target_arch = "x86_64")]
307 2 => unsafe { &mut self.inner.sse }.append(data),
308 #[cfg(target_arch = "aarch64")]
309 3 => unsafe { &mut self.inner.neon }.append(data),
310 #[cfg(all(target_family = "wasm", target_feature = "simd128"))]
311 4 => unsafe { &mut self.inner.wasm }.append(data),
312 _ => unsafe { core::hint::unreachable_unchecked() },
313 }
314 }
315
316 fn finalize64(&mut self) -> u64 {
317 match self.tag {
318 #[cfg(not(any(
319 all(target_family = "wasm", target_feature = "simd128"),
320 target_arch = "aarch64"
321 )))]
322 0 => unsafe { PortableHash::finalize64(&mut self.inner.portable) },
323 #[cfg(target_arch = "x86_64")]
324 1 => unsafe { AvxHash::finalize64(&mut self.inner.avx) },
325 #[cfg(target_arch = "x86_64")]
326 2 => unsafe { SseHash::finalize64(&mut self.inner.sse) },
327 #[cfg(target_arch = "aarch64")]
328 3 => unsafe { NeonHash::finalize64(&mut self.inner.neon) },
329 #[cfg(all(target_family = "wasm", target_feature = "simd128"))]
330 4 => unsafe { WasmHash::finalize64(&mut self.inner.wasm) },
331 _ => unsafe { core::hint::unreachable_unchecked() },
332 }
333 }
334
335 fn finalize128(&mut self) -> [u64; 2] {
336 match self.tag {
337 #[cfg(not(any(
338 all(target_family = "wasm", target_feature = "simd128"),
339 target_arch = "aarch64"
340 )))]
341 0 => unsafe { PortableHash::finalize128(&mut self.inner.portable) },
342 #[cfg(target_arch = "x86_64")]
343 1 => unsafe { AvxHash::finalize128(&mut self.inner.avx) },
344 #[cfg(target_arch = "x86_64")]
345 2 => unsafe { SseHash::finalize128(&mut self.inner.sse) },
346 #[cfg(target_arch = "aarch64")]
347 3 => unsafe { NeonHash::finalize128(&mut self.inner.neon) },
348 #[cfg(all(target_family = "wasm", target_feature = "simd128"))]
349 4 => unsafe { WasmHash::finalize128(&mut self.inner.wasm) },
350 _ => unsafe { core::hint::unreachable_unchecked() },
351 }
352 }
353
354 fn finalize256(&mut self) -> [u64; 4] {
355 match self.tag {
356 #[cfg(not(any(
357 all(target_family = "wasm", target_feature = "simd128"),
358 target_arch = "aarch64"
359 )))]
360 0 => unsafe { PortableHash::finalize256(&mut self.inner.portable) },
361 #[cfg(target_arch = "x86_64")]
362 1 => unsafe { AvxHash::finalize256(&mut self.inner.avx) },
363 #[cfg(target_arch = "x86_64")]
364 2 => unsafe { SseHash::finalize256(&mut self.inner.sse) },
365 #[cfg(target_arch = "aarch64")]
366 3 => unsafe { NeonHash::finalize256(&mut self.inner.neon) },
367 #[cfg(all(target_family = "wasm", target_feature = "simd128"))]
368 4 => unsafe { WasmHash::finalize256(&mut self.inner.wasm) },
369 _ => unsafe { core::hint::unreachable_unchecked() },
370 }
371 }
372
373 fn checkpoint(&self) -> [u8; 164] {
374 match self.tag {
375 #[cfg(not(any(
376 all(target_family = "wasm", target_feature = "simd128"),
377 target_arch = "aarch64"
378 )))]
379 0 => unsafe { PortableHash::checkpoint(&self.inner.portable) },
380 #[cfg(target_arch = "x86_64")]
381 1 => unsafe { AvxHash::checkpoint(&self.inner.avx) },
382 #[cfg(target_arch = "x86_64")]
383 2 => unsafe { SseHash::checkpoint(&self.inner.sse) },
384 #[cfg(target_arch = "aarch64")]
385 3 => unsafe { NeonHash::checkpoint(&self.inner.neon) },
386 #[cfg(all(target_family = "wasm", target_feature = "simd128"))]
387 4 => unsafe { WasmHash::checkpoint(&self.inner.wasm) },
388 _ => unsafe { core::hint::unreachable_unchecked() },
389 }
390 }
391}
392
393impl Default for HighwayHasher {
394 fn default() -> Self {
395 HighwayHasher::new(Key::default())
396 }
397}
398
399impl_write!(HighwayHasher);
400impl_hasher!(HighwayHasher);
401
402#[cfg(test)]
403mod tests {
404 use super::*;
405
406 #[test]
407 fn test_has_debug_representation_with_data() {
408 let hasher = HighwayHasher::new(Key::default());
409 let output = format!("{:?}", &hasher);
410 assert!(output.contains("hasher: "));
411 }
412}