1#[cfg(target_arch = "x86")]
18use core::arch::x86 as arch;
19#[cfg(target_arch = "x86_64")]
20use core::arch::x86_64 as arch;
21
22#[derive(Clone, Copy)]
24enum Kind {
25 Sse,
27 #[cfg(stable_vpclmulqdq)]
29 Avx2,
30 #[cfg(stable_vpclmulqdq)]
32 Avx512,
33}
34
35#[derive(Clone)]
36pub struct State {
37 state: u32,
38 kind: Kind,
39}
40
41impl State {
42 #[cfg(not(feature = "std"))]
43 fn detect() -> Option<Kind> {
44 if cfg!(target_feature = "pclmulqdq")
45 && cfg!(target_feature = "sse2")
46 && cfg!(target_feature = "sse4.1")
47 && cfg!(target_feature = "ssse3")
48 {
49 #[cfg(stable_vpclmulqdq)]
50 {
51 if cfg!(target_feature = "avx512f") && cfg!(target_feature = "vpclmulqdq") {
52 return Some(Kind::Avx512);
53 }
54 if cfg!(target_feature = "avx2") && cfg!(target_feature = "vpclmulqdq") {
55 return Some(Kind::Avx2);
56 }
57 }
58
59 return Some(Kind::Sse);
60 }
61
62 None
63 }
64
65 #[cfg(feature = "std")]
66 fn detect() -> Option<Kind> {
67 if is_x86_feature_detected!("pclmulqdq")
68 && is_x86_feature_detected!("sse2")
69 && is_x86_feature_detected!("sse4.1")
70 && is_x86_feature_detected!("ssse3")
71 {
72 #[cfg(stable_vpclmulqdq)]
73 {
74 if is_x86_feature_detected!("avx512f") && is_x86_feature_detected!("vpclmulqdq") {
75 return Some(Kind::Avx512);
76 }
77 if is_x86_feature_detected!("avx2") && is_x86_feature_detected!("vpclmulqdq") {
78 return Some(Kind::Avx2);
79 }
80 }
81
82 return Some(Kind::Sse);
83 }
84
85 None
86 }
87
88 pub fn new(state: u32) -> Option<Self> {
89 Self::detect().map(|kind| Self { state, kind })
91 }
92
93 pub fn update(&mut self, buf: &[u8]) {
94 self.state = unsafe {
96 match self.kind {
97 Kind::Sse => calculate(self.state, buf),
98 #[cfg(stable_vpclmulqdq)]
99 Kind::Avx2 => calculate_avx2(self.state, buf),
100 #[cfg(stable_vpclmulqdq)]
101 Kind::Avx512 => calculate_avx512(self.state, buf),
102 }
103 }
104 }
105
106 pub fn finalize(self) -> u32 {
107 self.state
108 }
109
110 pub fn reset(&mut self) {
111 self.state = 0;
112 }
113
114 pub fn combine(&mut self, other: u32, amount: u64) {
115 self.state = crate::combine::combine(self.state, other, amount);
116 }
117}
118
119const K1: i64 = 0x154442bd4;
120const K2: i64 = 0x1c6e41596;
121const K3: i64 = 0x1751997d0;
122const K4: i64 = 0x0ccaa009e;
123const K5: i64 = 0x163cd6124;
124
125const P_X: i64 = 0x1DB710641;
126const U_PRIME: i64 = 0x1F7011641;
127
128const MIN_FOLD_BY_4_BYTES: usize = 128;
131#[cfg(stable_vpclmulqdq)]
132const MIN_AVX512_BYTES: usize = 2 * 1024;
133
134#[cfg(stable_vpclmulqdq)]
138const K_1024_LOW: i64 = 0x1e88ef372;
139#[cfg(stable_vpclmulqdq)]
140const K_1024_HIGH: i64 = 0x14a7fe880;
141#[cfg(stable_vpclmulqdq)]
142const K_2048_LOW: i64 = 0x11542778a;
143#[cfg(stable_vpclmulqdq)]
144const K_2048_HIGH: i64 = 0x1322d1430;
145
146#[target_feature(
147 enable = "pclmulqdq",
148 enable = "sse2",
149 enable = "sse4.1",
150 enable = "ssse3"
151)]
152unsafe fn calculate(crc: u32, mut data: &[u8]) -> u32 {
153 if data.len() < 16 {
155 return crate::baseline::update_fast_16(crc, data);
156 }
157
158 if data.len() < MIN_FOLD_BY_4_BYTES {
161 let mut x = get(&mut data);
162 x = arch::_mm_xor_si128(x, arch::_mm_cvtsi32_si128(!crc as i32));
163 return reduce_128_to_crc(x, data);
164 }
165
166 let mut x3 = get(&mut data);
168 let mut x2 = get(&mut data);
169 let mut x1 = get(&mut data);
170 let mut x0 = get(&mut data);
171
172 x3 = arch::_mm_xor_si128(x3, arch::_mm_cvtsi32_si128(!crc as i32));
174
175 let k1k2 = arch::_mm_set_epi64x(K2, K1);
176 while data.len() >= 64 {
177 x3 = reduce128(x3, get(&mut data), k1k2);
178 x2 = reduce128(x2, get(&mut data), k1k2);
179 x1 = reduce128(x1, get(&mut data), k1k2);
180 x0 = reduce128(x0, get(&mut data), k1k2);
181 }
182
183 let k3k4 = arch::_mm_set_epi64x(K4, K3);
184 let mut x = reduce128(x3, x2, k3k4);
185 x = reduce128(x, x1, k3k4);
186 x = reduce128(x, x0, k3k4);
187
188 reduce_128_to_crc(x, data)
189}
190
191#[cfg(stable_vpclmulqdq)]
194#[allow(clippy::incompatible_msrv)] #[target_feature(
196 enable = "pclmulqdq",
197 enable = "sse2",
198 enable = "sse4.1",
199 enable = "ssse3",
200 enable = "avx",
201 enable = "avx2",
202 enable = "vpclmulqdq"
203)]
204unsafe fn calculate_avx2(crc: u32, mut data: &[u8]) -> u32 {
205 if data.len() < 256 {
207 return calculate(crc, data);
208 }
209
210 let mut v0 = get256(&mut data);
212 let mut v1 = get256(&mut data);
213 let mut v2 = get256(&mut data);
214 let mut v3 = get256(&mut data);
215
216 v0 = arch::_mm256_xor_si256(
218 v0,
219 arch::_mm256_castsi128_si256(arch::_mm_cvtsi32_si128(!crc as i32)),
220 );
221
222 let k = arch::_mm256_set_epi64x(K_1024_HIGH, K_1024_LOW, K_1024_HIGH, K_1024_LOW);
223 while data.len() >= 128 {
224 v0 = reduce256(v0, get256(&mut data), k);
225 v1 = reduce256(v1, get256(&mut data), k);
226 v2 = reduce256(v2, get256(&mut data), k);
227 v3 = reduce256(v3, get256(&mut data), k);
228 }
229
230 let k3k4 = arch::_mm_set_epi64x(K4, K3);
232 let mut x = arch::_mm256_castsi256_si128(v0);
233 x = reduce128(x, arch::_mm256_extracti128_si256(v0, 1), k3k4);
234 x = reduce128(x, arch::_mm256_castsi256_si128(v1), k3k4);
235 x = reduce128(x, arch::_mm256_extracti128_si256(v1, 1), k3k4);
236 x = reduce128(x, arch::_mm256_castsi256_si128(v2), k3k4);
237 x = reduce128(x, arch::_mm256_extracti128_si256(v2, 1), k3k4);
238 x = reduce128(x, arch::_mm256_castsi256_si128(v3), k3k4);
239 x = reduce128(x, arch::_mm256_extracti128_si256(v3, 1), k3k4);
240
241 reduce_128_to_crc(x, data)
242}
243
244#[cfg(stable_vpclmulqdq)]
247#[allow(clippy::incompatible_msrv)] #[target_feature(
249 enable = "pclmulqdq",
250 enable = "sse2",
251 enable = "sse4.1",
252 enable = "ssse3",
253 enable = "avx",
254 enable = "avx2",
255 enable = "vpclmulqdq",
256 enable = "avx512f"
257)]
258unsafe fn calculate_avx512(crc: u32, mut data: &[u8]) -> u32 {
259 if data.len() < MIN_AVX512_BYTES {
261 return calculate_avx2(crc, data);
262 }
263
264 let mut v0 = get512(&mut data);
266 let mut v1 = get512(&mut data);
267 let mut v2 = get512(&mut data);
268 let mut v3 = get512(&mut data);
269
270 v0 = arch::_mm512_xor_si512(
272 v0,
273 arch::_mm512_castsi128_si512(arch::_mm_cvtsi32_si128(!crc as i32)),
274 );
275
276 let k = arch::_mm512_set_epi64(
277 K_2048_HIGH,
278 K_2048_LOW,
279 K_2048_HIGH,
280 K_2048_LOW,
281 K_2048_HIGH,
282 K_2048_LOW,
283 K_2048_HIGH,
284 K_2048_LOW,
285 );
286 while data.len() >= 256 {
287 v0 = reduce512(v0, get512(&mut data), k);
288 v1 = reduce512(v1, get512(&mut data), k);
289 v2 = reduce512(v2, get512(&mut data), k);
290 v3 = reduce512(v3, get512(&mut data), k);
291 }
292
293 let k3k4 = arch::_mm_set_epi64x(K4, K3);
295 let mut x = arch::_mm512_castsi512_si128(v0);
296 x = reduce128(x, arch::_mm512_extracti32x4_epi32(v0, 1), k3k4);
297 x = reduce128(x, arch::_mm512_extracti32x4_epi32(v0, 2), k3k4);
298 x = reduce128(x, arch::_mm512_extracti32x4_epi32(v0, 3), k3k4);
299 x = reduce128(x, arch::_mm512_castsi512_si128(v1), k3k4);
300 x = reduce128(x, arch::_mm512_extracti32x4_epi32(v1, 1), k3k4);
301 x = reduce128(x, arch::_mm512_extracti32x4_epi32(v1, 2), k3k4);
302 x = reduce128(x, arch::_mm512_extracti32x4_epi32(v1, 3), k3k4);
303 x = reduce128(x, arch::_mm512_castsi512_si128(v2), k3k4);
304 x = reduce128(x, arch::_mm512_extracti32x4_epi32(v2, 1), k3k4);
305 x = reduce128(x, arch::_mm512_extracti32x4_epi32(v2, 2), k3k4);
306 x = reduce128(x, arch::_mm512_extracti32x4_epi32(v2, 3), k3k4);
307 x = reduce128(x, arch::_mm512_castsi512_si128(v3), k3k4);
308 x = reduce128(x, arch::_mm512_extracti32x4_epi32(v3, 1), k3k4);
309 x = reduce128(x, arch::_mm512_extracti32x4_epi32(v3, 2), k3k4);
310 x = reduce128(x, arch::_mm512_extracti32x4_epi32(v3, 3), k3k4);
311
312 if data.len() >= MIN_FOLD_BY_4_BYTES {
315 let crc = reduce_128_to_crc(x, &[]);
316 return calculate(crc, data);
317 }
318
319 reduce_128_to_crc(x, data)
320}
321
322#[target_feature(
326 enable = "pclmulqdq",
327 enable = "sse2",
328 enable = "sse4.1",
329 enable = "ssse3"
330)]
331unsafe fn reduce_128_to_crc(mut x: arch::__m128i, mut data: &[u8]) -> u32 {
332 let k3k4 = arch::_mm_set_epi64x(K4, K3);
333
334 while data.len() >= 16 {
336 x = reduce128(x, get(&mut data), k3k4);
337 }
338
339 let n = data.len();
344 if n > 0 {
345 let seq = arch::_mm_setr_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
346 let shl = arch::_mm_add_epi8(seq, arch::_mm_set1_epi8(n as i8 - 16));
347 let shr = arch::_mm_xor_si128(shl, arch::_mm_set1_epi8(-128));
348
349 let overflow = arch::_mm_shuffle_epi8(x, shl);
350 x = arch::_mm_shuffle_epi8(x, shr);
351
352 let mut part = [0u8; 16];
353 part[..n].copy_from_slice(data);
354 let part = arch::_mm_loadu_si128(part.as_ptr() as *const arch::__m128i);
355 x = arch::_mm_xor_si128(x, arch::_mm_shuffle_epi8(part, shl));
356
357 x = reduce128(overflow, x, k3k4);
358 }
359
360 let x = arch::_mm_xor_si128(
379 arch::_mm_clmulepi64_si128(x, k3k4, 0x10),
380 arch::_mm_srli_si128(x, 8),
381 );
382 let x = arch::_mm_xor_si128(
383 arch::_mm_clmulepi64_si128(
384 arch::_mm_and_si128(x, arch::_mm_set_epi32(0, 0, 0, !0)),
385 arch::_mm_set_epi64x(0, K5),
386 0x00,
387 ),
388 arch::_mm_srli_si128(x, 4),
389 );
390
391 let pu = arch::_mm_set_epi64x(U_PRIME, P_X);
395
396 let t1 = arch::_mm_clmulepi64_si128(
398 arch::_mm_and_si128(x, arch::_mm_set_epi32(0, 0, 0, !0)),
399 pu,
400 0x10,
401 );
402 let t2 = arch::_mm_clmulepi64_si128(
404 arch::_mm_and_si128(t1, arch::_mm_set_epi32(0, 0, 0, !0)),
405 pu,
406 0x00,
407 );
408 !(arch::_mm_extract_epi32(arch::_mm_xor_si128(x, t2), 1) as u32)
413}
414
415#[inline]
416unsafe fn reduce128(a: arch::__m128i, b: arch::__m128i, keys: arch::__m128i) -> arch::__m128i {
417 let t1 = arch::_mm_clmulepi64_si128(a, keys, 0x00);
418 let t2 = arch::_mm_clmulepi64_si128(a, keys, 0x11);
419 arch::_mm_xor_si128(arch::_mm_xor_si128(b, t1), t2)
420}
421
422#[cfg(stable_vpclmulqdq)]
423#[allow(clippy::incompatible_msrv)] #[target_feature(enable = "avx2", enable = "vpclmulqdq")]
425#[inline]
426unsafe fn reduce256(a: arch::__m256i, b: arch::__m256i, keys: arch::__m256i) -> arch::__m256i {
427 let t1 = arch::_mm256_clmulepi64_epi128(a, keys, 0x00);
428 let t2 = arch::_mm256_clmulepi64_epi128(a, keys, 0x11);
429 arch::_mm256_xor_si256(arch::_mm256_xor_si256(b, t1), t2)
430}
431
432#[cfg(stable_vpclmulqdq)]
433#[allow(clippy::incompatible_msrv)] #[target_feature(enable = "avx512f", enable = "vpclmulqdq")]
435#[inline]
436unsafe fn reduce512(a: arch::__m512i, b: arch::__m512i, keys: arch::__m512i) -> arch::__m512i {
437 let t1 = arch::_mm512_clmulepi64_epi128(a, keys, 0x00);
438 let t2 = arch::_mm512_clmulepi64_epi128(a, keys, 0x11);
439 arch::_mm512_xor_si512(arch::_mm512_xor_si512(b, t1), t2)
440}
441
442unsafe fn get(a: &mut &[u8]) -> arch::__m128i {
443 debug_assert!(a.len() >= 16);
444 let r = arch::_mm_loadu_si128(a.as_ptr() as *const arch::__m128i);
445 *a = &a[16..];
446 r
447}
448
449#[cfg(stable_vpclmulqdq)]
450#[target_feature(enable = "avx")]
451#[inline]
452unsafe fn get256(a: &mut &[u8]) -> arch::__m256i {
453 debug_assert!(a.len() >= 32);
454 let r = arch::_mm256_loadu_si256(a.as_ptr() as *const arch::__m256i);
455 *a = &a[32..];
456 r
457}
458
459#[cfg(stable_vpclmulqdq)]
460#[allow(clippy::incompatible_msrv)] #[target_feature(enable = "avx512f")]
462#[inline]
463unsafe fn get512(a: &mut &[u8]) -> arch::__m512i {
464 debug_assert!(a.len() >= 64);
465 let r = arch::_mm512_loadu_si512(a.as_ptr() as *const _);
466 *a = &a[64..];
467 r
468}
469
470#[cfg(test)]
471mod test {
472 quickcheck::quickcheck! {
473 fn check_against_baseline(init: u32, chunks: Vec<(Vec<u8>, usize)>) -> bool {
474 let mut baseline = super::super::super::baseline::State::new(init);
475 let mut pclmulqdq = super::State::new(init).expect("not supported");
476 for (chunk, mut offset) in chunks {
477 offset &= 0xF;
479 if chunk.len() <= offset {
480 baseline.update(&chunk);
481 pclmulqdq.update(&chunk);
482 } else {
483 baseline.update(&chunk[offset..]);
484 pclmulqdq.update(&chunk[offset..]);
485 }
486 }
487 pclmulqdq.finalize() == baseline.finalize()
488 }
489 }
490
491 #[test]
493 fn check_large_inputs_against_baseline() {
494 let mut data = vec![0u8; 8200];
495 let mut s: u32 = 0x1234_5678;
496 for b in data.iter_mut() {
497 s = s.wrapping_mul(1_664_525).wrapping_add(1_013_904_223);
498 *b = (s >> 24) as u8;
499 }
500
501 for &len in &[
502 0usize, 1, 15, 16, 17, 63, 64, 127, 128, 129, 255, 256, 257, 511, 512, 513, 1023, 1024,
503 1025, 2047, 2048, 2049, 2175, 2176, 2303, 2304, 4096, 8192, 8199,
504 ] {
505 for &offset in &[0usize, 1, 3, 7, 8, 15] {
506 if offset + len > data.len() {
507 continue;
508 }
509 let slice = &data[offset..offset + len];
510 let mut baseline = super::super::super::baseline::State::new(0);
511 baseline.update(slice);
512 let mut specialized = super::State::new(0).expect("not supported");
513 specialized.update(slice);
514 assert_eq!(
515 specialized.finalize(),
516 baseline.finalize(),
517 "mismatch for len={len} offset={offset}",
518 );
519 }
520 }
521 }
522}