Coverage Report

Created: 2026-08-31 07:42

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/crc32fast-1.5.1/src/specialized/pclmulqdq.rs
Line
Count
Source
1
//! Specialized checksum code for the x86 CPU architecture, based on the efficient algorithm described
2
//! in the following whitepaper:
3
//!
4
//! Gopal, V., Ozturk, E., Guilford, J., Wolrich, G., Feghali, W., Dixon, M., & Karakoyunlu, D. (2009).
5
//! _Fast CRC computation for generic polynomials using PCLMULQDQ instruction_. Intel.
6
//! (Mirror link: <https://fossies.org/linux/zlib-ng/doc/crc-pclmulqdq.pdf>, accessed 2024-05-20)
7
//!
8
//! Throughout the code, this work is referred to as "the paper".
9
//!
10
//! On top of the 128-bit `PCLMULQDQ` implementation, two wider variants use `VPCLMULQDQ` to fold
11
//! several independent 128-bit streams per instruction: an `AVX2` variant over 256-bit `YMM`
12
//! registers (8 streams) and an `AVX-512` variant over 512-bit `ZMM` registers (16 streams). Both
13
//! rely on `VPCLMULQDQ` intrinsics stabilized in Rust 1.89 and are only compiled when the
14
//! `stable_vpclmulqdq` cfg is set by `build.rs`, leaving the crate MSRV unchanged otherwise. The
15
//! best variant supported by the running CPU is chosen at runtime.
16
17
#[cfg(target_arch = "x86")]
18
use core::arch::x86 as arch;
19
#[cfg(target_arch = "x86_64")]
20
use core::arch::x86_64 as arch;
21
22
/// Which SIMD implementation to use, chosen once at construction from the CPU's features.
23
#[derive(Clone, Copy)]
24
enum Kind {
25
    /// 128-bit `PCLMULQDQ`, fold by 4.
26
    Sse,
27
    /// 256-bit `VPCLMULQDQ`, 8 streams.
28
    #[cfg(stable_vpclmulqdq)]
29
    Avx2,
30
    /// 512-bit `VPCLMULQDQ`, 16 streams.
31
    #[cfg(stable_vpclmulqdq)]
32
    Avx512,
33
}
34
35
#[derive(Clone)]
36
pub struct State {
37
    state: u32,
38
    kind: Kind,
39
}
40
41
impl 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
15.5k
    fn detect() -> Option<Kind> {
67
15.5k
        if is_x86_feature_detected!("pclmulqdq")
68
15.5k
            && is_x86_feature_detected!("sse2")
69
15.5k
            && is_x86_feature_detected!("sse4.1")
70
15.5k
            && is_x86_feature_detected!("ssse3")
71
        {
72
            #[cfg(stable_vpclmulqdq)]
73
            {
74
15.5k
                if is_x86_feature_detected!("avx512f") && is_x86_feature_detected!("vpclmulqdq") {
75
0
                    return Some(Kind::Avx512);
76
15.5k
                }
77
15.5k
                if is_x86_feature_detected!("avx2") && is_x86_feature_detected!("vpclmulqdq") {
78
0
                    return Some(Kind::Avx2);
79
15.5k
                }
80
            }
81
82
15.5k
            return Some(Kind::Sse);
83
0
        }
84
85
0
        None
86
15.5k
    }
87
88
15.5k
    pub fn new(state: u32) -> Option<Self> {
89
        // SAFETY: `detect` only returns a `Kind` whose instructions the CPU supports.
90
15.5k
        Self::detect().map(|kind| Self { state, kind })
91
15.5k
    }
92
93
1.10M
    pub fn update(&mut self, buf: &[u8]) {
94
        // SAFETY: `State::new` ensured the CPU supports the instructions for `self.kind`.
95
        self.state = unsafe {
96
1.10M
            match self.kind {
97
1.10M
                Kind::Sse => calculate(self.state, buf),
98
                #[cfg(stable_vpclmulqdq)]
99
0
                Kind::Avx2 => calculate_avx2(self.state, buf),
100
                #[cfg(stable_vpclmulqdq)]
101
0
                Kind::Avx512 => calculate_avx512(self.state, buf),
102
            }
103
        }
104
1.10M
    }
105
106
169k
    pub fn finalize(self) -> u32 {
107
169k
        self.state
108
169k
    }
109
110
179k
    pub fn reset(&mut self) {
111
179k
        self.state = 0;
112
179k
    }
113
114
0
    pub fn combine(&mut self, other: u32, amount: u64) {
115
0
        self.state = crate::combine::combine(self.state, other, amount);
116
0
    }
117
}
118
119
const K1: i64 = 0x154442bd4;
120
const K2: i64 = 0x1c6e41596;
121
const K3: i64 = 0x1751997d0;
122
const K4: i64 = 0x0ccaa009e;
123
const K5: i64 = 0x163cd6124;
124
125
const P_X: i64 = 0x1DB710641;
126
const U_PRIME: i64 = 0x1F7011641;
127
128
// The wider kernels have progressively more streams to initialize and collapse. Keep their
129
// crossovers conservative so short inputs do not pay that fixed cost.
130
const MIN_FOLD_BY_4_BYTES: usize = 128;
131
#[cfg(stable_vpclmulqdq)]
132
const MIN_AVX512_BYTES: usize = 2 * 1024;
133
134
// Fold constants for the wider strides. For a fold by `D` bits the pair is
135
// `(reflect(x^(D+32) mod P), reflect(x^(D-32) mod P))`, as for `K1`/`K2` (D = 512) and
136
// `K3`/`K4` (D = 128). AVX2 uses 8 streams (D = 1024), AVX-512 uses 16 streams (D = 2048).
137
#[cfg(stable_vpclmulqdq)]
138
const K_1024_LOW: i64 = 0x1e88ef372;
139
#[cfg(stable_vpclmulqdq)]
140
const K_1024_HIGH: i64 = 0x14a7fe880;
141
#[cfg(stable_vpclmulqdq)]
142
const K_2048_LOW: i64 = 0x11542778a;
143
#[cfg(stable_vpclmulqdq)]
144
const K_2048_HIGH: i64 = 0x1322d1430;
145
146
#[target_feature(
147
    enable = "pclmulqdq",
148
    enable = "sse2",
149
    enable = "sse4.1",
150
    enable = "ssse3"
151
)]
152
1.10M
unsafe fn calculate(crc: u32, mut data: &[u8]) -> u32 {
153
    // Below 16 bytes there isn't even a single full block to load, so use the scalar fallback.
154
1.10M
    if data.len() < 16 {
155
937k
        return crate::baseline::update_fast_16(crc, data);
156
162k
    }
157
158
    // For 16..127 bytes a single-accumulator fold-by-1 is enough; the fold-by-4 setup below only
159
    // pays off once there are several 64-byte groups.
160
162k
    if data.len() < MIN_FOLD_BY_4_BYTES {
161
78.7k
        let mut x = get(&mut data);
162
78.7k
        x = arch::_mm_xor_si128(x, arch::_mm_cvtsi32_si128(!crc as i32));
163
78.7k
        return reduce_128_to_crc(x, data);
164
83.4k
    }
165
166
    // Step 1: fold by 4 loop
167
83.4k
    let mut x3 = get(&mut data);
168
83.4k
    let mut x2 = get(&mut data);
169
83.4k
    let mut x1 = get(&mut data);
170
83.4k
    let mut x0 = get(&mut data);
171
172
    // fold in our initial value, part of the incremental crc checksum
173
83.4k
    x3 = arch::_mm_xor_si128(x3, arch::_mm_cvtsi32_si128(!crc as i32));
174
175
83.4k
    let k1k2 = arch::_mm_set_epi64x(K2, K1);
176
1.07M
    while data.len() >= 64 {
177
996k
        x3 = reduce128(x3, get(&mut data), k1k2);
178
996k
        x2 = reduce128(x2, get(&mut data), k1k2);
179
996k
        x1 = reduce128(x1, get(&mut data), k1k2);
180
996k
        x0 = reduce128(x0, get(&mut data), k1k2);
181
996k
    }
182
183
83.4k
    let k3k4 = arch::_mm_set_epi64x(K4, K3);
184
83.4k
    let mut x = reduce128(x3, x2, k3k4);
185
83.4k
    x = reduce128(x, x1, k3k4);
186
83.4k
    x = reduce128(x, x0, k3k4);
187
188
83.4k
    reduce_128_to_crc(x, data)
189
1.10M
}
190
191
/// 256-bit `VPCLMULQDQ` variant: 8 streams across four `YMM` registers (two lanes each), folding
192
/// two streams per carry-less multiply.
193
#[cfg(stable_vpclmulqdq)]
194
#[allow(clippy::incompatible_msrv)] // intrinsics are gated to rustc >= 1.89 by build.rs
195
#[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
)]
204
0
unsafe fn calculate_avx2(crc: u32, mut data: &[u8]) -> u32 {
205
    // Too small for the wide loop; use the 128-bit path.
206
0
    if data.len() < 256 {
207
0
        return calculate(crc, data);
208
0
    }
209
210
    // First 128 bytes as 8 streams (four YMM registers, two lanes each).
211
0
    let mut v0 = get256(&mut data);
212
0
    let mut v1 = get256(&mut data);
213
0
    let mut v2 = get256(&mut data);
214
0
    let mut v3 = get256(&mut data);
215
216
    // Fold the initial CRC into the lowest-offset stream.
217
0
    v0 = arch::_mm256_xor_si256(
218
0
        v0,
219
0
        arch::_mm256_castsi128_si256(arch::_mm_cvtsi32_si128(!crc as i32)),
220
0
    );
221
222
0
    let k = arch::_mm256_set_epi64x(K_1024_HIGH, K_1024_LOW, K_1024_HIGH, K_1024_LOW);
223
0
    while data.len() >= 128 {
224
0
        v0 = reduce256(v0, get256(&mut data), k);
225
0
        v1 = reduce256(v1, get256(&mut data), k);
226
0
        v2 = reduce256(v2, get256(&mut data), k);
227
0
        v3 = reduce256(v3, get256(&mut data), k);
228
0
    }
229
230
    // Collapse the 8 streams, in increasing byte offset, folding each into the next by 128 bits.
231
0
    let k3k4 = arch::_mm_set_epi64x(K4, K3);
232
0
    let mut x = arch::_mm256_castsi256_si128(v0);
233
0
    x = reduce128(x, arch::_mm256_extracti128_si256(v0, 1), k3k4);
234
0
    x = reduce128(x, arch::_mm256_castsi256_si128(v1), k3k4);
235
0
    x = reduce128(x, arch::_mm256_extracti128_si256(v1, 1), k3k4);
236
0
    x = reduce128(x, arch::_mm256_castsi256_si128(v2), k3k4);
237
0
    x = reduce128(x, arch::_mm256_extracti128_si256(v2, 1), k3k4);
238
0
    x = reduce128(x, arch::_mm256_castsi256_si128(v3), k3k4);
239
0
    x = reduce128(x, arch::_mm256_extracti128_si256(v3, 1), k3k4);
240
241
0
    reduce_128_to_crc(x, data)
242
0
}
243
244
/// 512-bit `VPCLMULQDQ` variant: 16 streams across four `ZMM` registers (four lanes each), folding
245
/// four streams per carry-less multiply.
246
#[cfg(stable_vpclmulqdq)]
247
#[allow(clippy::incompatible_msrv)] // intrinsics are gated to rustc >= 1.89 by build.rs
248
#[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
)]
258
0
unsafe fn calculate_avx512(crc: u32, mut data: &[u8]) -> u32 {
259
    // Use a conservative crossover because the 16-stream setup and collapse are costly near 1 KiB.
260
0
    if data.len() < MIN_AVX512_BYTES {
261
0
        return calculate_avx2(crc, data);
262
0
    }
263
264
    // First 256 bytes as 16 streams (four ZMM registers, four lanes each).
265
0
    let mut v0 = get512(&mut data);
266
0
    let mut v1 = get512(&mut data);
267
0
    let mut v2 = get512(&mut data);
268
0
    let mut v3 = get512(&mut data);
269
270
    // Fold the initial CRC into the lowest-offset stream.
271
0
    v0 = arch::_mm512_xor_si512(
272
0
        v0,
273
0
        arch::_mm512_castsi128_si512(arch::_mm_cvtsi32_si128(!crc as i32)),
274
0
    );
275
276
0
    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
0
    while data.len() >= 256 {
287
0
        v0 = reduce512(v0, get512(&mut data), k);
288
0
        v1 = reduce512(v1, get512(&mut data), k);
289
0
        v2 = reduce512(v2, get512(&mut data), k);
290
0
        v3 = reduce512(v3, get512(&mut data), k);
291
0
    }
292
293
    // Collapse the 16 streams, in increasing byte offset, folding each into the next by 128 bits.
294
0
    let k3k4 = arch::_mm_set_epi64x(K4, K3);
295
0
    let mut x = arch::_mm512_castsi512_si128(v0);
296
0
    x = reduce128(x, arch::_mm512_extracti32x4_epi32(v0, 1), k3k4);
297
0
    x = reduce128(x, arch::_mm512_extracti32x4_epi32(v0, 2), k3k4);
298
0
    x = reduce128(x, arch::_mm512_extracti32x4_epi32(v0, 3), k3k4);
299
0
    x = reduce128(x, arch::_mm512_castsi512_si128(v1), k3k4);
300
0
    x = reduce128(x, arch::_mm512_extracti32x4_epi32(v1, 1), k3k4);
301
0
    x = reduce128(x, arch::_mm512_extracti32x4_epi32(v1, 2), k3k4);
302
0
    x = reduce128(x, arch::_mm512_extracti32x4_epi32(v1, 3), k3k4);
303
0
    x = reduce128(x, arch::_mm512_castsi512_si128(v2), k3k4);
304
0
    x = reduce128(x, arch::_mm512_extracti32x4_epi32(v2, 1), k3k4);
305
0
    x = reduce128(x, arch::_mm512_extracti32x4_epi32(v2, 2), k3k4);
306
0
    x = reduce128(x, arch::_mm512_extracti32x4_epi32(v2, 3), k3k4);
307
0
    x = reduce128(x, arch::_mm512_castsi512_si128(v3), k3k4);
308
0
    x = reduce128(x, arch::_mm512_extracti32x4_epi32(v3, 1), k3k4);
309
0
    x = reduce128(x, arch::_mm512_extracti32x4_epi32(v3, 2), k3k4);
310
0
    x = reduce128(x, arch::_mm512_extracti32x4_epi32(v3, 3), k3k4);
311
312
    // A substantial remainder is faster through SSE's complete fold-by-4 kernel than through the
313
    // serial fold-by-1 tail below. Finalizing and restarting is valid because CRC updates compose.
314
0
    if data.len() >= MIN_FOLD_BY_4_BYTES {
315
0
        let crc = reduce_128_to_crc(x, &[]);
316
0
        return calculate(crc, data);
317
0
    }
318
319
0
    reduce_128_to_crc(x, data)
320
0
}
321
322
/// Folds any remaining 16-byte chunks into `x`, then folds a final partial (`< 16` byte) block
323
/// with a byte-shift, reduces from 128 to 32 bits with a Barrett reduction, and returns the CRC.
324
/// Shared by all of the fold implementations.
325
#[target_feature(
326
    enable = "pclmulqdq",
327
    enable = "sse2",
328
    enable = "sse4.1",
329
    enable = "ssse3"
330
)]
331
162k
unsafe fn reduce_128_to_crc(mut x: arch::__m128i, mut data: &[u8]) -> u32 {
332
162k
    let k3k4 = arch::_mm_set_epi64x(K4, K3);
333
334
    // Fold by 1 over any remaining whole 16-byte blocks.
335
383k
    while data.len() >= 16 {
336
220k
        x = reduce128(x, get(&mut data), k3k4);
337
220k
    }
338
339
    // Fold a final partial block of `n` (1..=15) bytes. The last `n` bytes of the accumulator are
340
    // shifted out (`overflow`) and folded back by 128 bits, while `x` is shifted down to make room
341
    // for the `n` new bytes, which are byte-aligned into the vacated high lanes. The shuffle masks
342
    // are built at runtime from the byte length, avoiding a lookup table.
343
162k
    let n = data.len();
344
162k
    if n > 0 {
345
130k
        let seq = arch::_mm_setr_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
346
130k
        let shl = arch::_mm_add_epi8(seq, arch::_mm_set1_epi8(n as i8 - 16));
347
130k
        let shr = arch::_mm_xor_si128(shl, arch::_mm_set1_epi8(-128));
348
130k
349
130k
        let overflow = arch::_mm_shuffle_epi8(x, shl);
350
130k
        x = arch::_mm_shuffle_epi8(x, shr);
351
130k
352
130k
        let mut part = [0u8; 16];
353
130k
        part[..n].copy_from_slice(data);
354
130k
        let part = arch::_mm_loadu_si128(part.as_ptr() as *const arch::__m128i);
355
130k
        x = arch::_mm_xor_si128(x, arch::_mm_shuffle_epi8(part, shl));
356
130k
357
130k
        x = reduce128(overflow, x, k3k4);
358
130k
    }
359
360
    // Perform step 3, reduction from 128 bits to 64 bits. This is
361
    // significantly different from the paper and basically doesn't follow it
362
    // at all. It's not really clear why, but implementations of this algorithm
363
    // in Chrome/Linux diverge in the same way. It is beyond me why this is
364
    // different than the paper, maybe the paper has like errata or something?
365
    // Unclear.
366
    //
367
    // It's also not clear to me what's actually happening here and/or why, but
368
    // algebraically what's happening is:
369
    //
370
    // x = (x[0:63] • K4) ^ x[64:127]           // 96 bit result
371
    // x = ((x[0:31] as u64) • K5) ^ x[32:95]   // 64 bit result
372
    //
373
    // It's... not clear to me what's going on here. The paper itself is pretty
374
    // vague on this part but definitely uses different constants at least.
375
    // It's not clear to me, reading the paper, where the xor operations are
376
    // happening or why things are shifting around. This implementation...
377
    // appears to work though!
378
162k
    let x = arch::_mm_xor_si128(
379
162k
        arch::_mm_clmulepi64_si128(x, k3k4, 0x10),
380
162k
        arch::_mm_srli_si128(x, 8),
381
    );
382
162k
    let x = arch::_mm_xor_si128(
383
162k
        arch::_mm_clmulepi64_si128(
384
162k
            arch::_mm_and_si128(x, arch::_mm_set_epi32(0, 0, 0, !0)),
385
162k
            arch::_mm_set_epi64x(0, K5),
386
            0x00,
387
        ),
388
162k
        arch::_mm_srli_si128(x, 4),
389
    );
390
391
    // Perform a Barrett reduction from our now 64 bits to 32 bits. The
392
    // algorithm for this is described at the end of the paper, and note that
393
    // this also implements the "bit reflected input" variant.
394
162k
    let pu = arch::_mm_set_epi64x(U_PRIME, P_X);
395
396
    // T1(x) = ⌊(R(x) % x^32)⌋ • μ
397
162k
    let t1 = arch::_mm_clmulepi64_si128(
398
162k
        arch::_mm_and_si128(x, arch::_mm_set_epi32(0, 0, 0, !0)),
399
162k
        pu,
400
        0x10,
401
    );
402
    // T2(x) = ⌊(T1(x) % x^32)⌋ • P(x)
403
162k
    let t2 = arch::_mm_clmulepi64_si128(
404
162k
        arch::_mm_and_si128(t1, arch::_mm_set_epi32(0, 0, 0, !0)),
405
162k
        pu,
406
        0x00,
407
    );
408
    // We're doing the bit-reflected variant, so get the upper 32-bits of the
409
    // 64-bit result instead of the lower 32-bits.
410
    //
411
    // C(x) = R(x) ^ T2(x) / x^32
412
162k
    !(arch::_mm_extract_epi32(arch::_mm_xor_si128(x, t2), 1) as u32)
413
162k
}
414
415
#[inline]
416
4.58M
unsafe fn reduce128(a: arch::__m128i, b: arch::__m128i, keys: arch::__m128i) -> arch::__m128i {
417
4.58M
    let t1 = arch::_mm_clmulepi64_si128(a, keys, 0x00);
418
4.58M
    let t2 = arch::_mm_clmulepi64_si128(a, keys, 0x11);
419
4.58M
    arch::_mm_xor_si128(arch::_mm_xor_si128(b, t1), t2)
420
4.58M
}
421
422
#[cfg(stable_vpclmulqdq)]
423
#[allow(clippy::incompatible_msrv)] // intrinsics are gated to rustc >= 1.89 by build.rs
424
#[target_feature(enable = "avx2", enable = "vpclmulqdq")]
425
#[inline]
426
0
unsafe fn reduce256(a: arch::__m256i, b: arch::__m256i, keys: arch::__m256i) -> arch::__m256i {
427
0
    let t1 = arch::_mm256_clmulepi64_epi128(a, keys, 0x00);
428
0
    let t2 = arch::_mm256_clmulepi64_epi128(a, keys, 0x11);
429
0
    arch::_mm256_xor_si256(arch::_mm256_xor_si256(b, t1), t2)
430
0
}
431
432
#[cfg(stable_vpclmulqdq)]
433
#[allow(clippy::incompatible_msrv)] // intrinsics are gated to rustc >= 1.89 by build.rs
434
#[target_feature(enable = "avx512f", enable = "vpclmulqdq")]
435
#[inline]
436
0
unsafe fn reduce512(a: arch::__m512i, b: arch::__m512i, keys: arch::__m512i) -> arch::__m512i {
437
0
    let t1 = arch::_mm512_clmulepi64_epi128(a, keys, 0x00);
438
0
    let t2 = arch::_mm512_clmulepi64_epi128(a, keys, 0x11);
439
0
    arch::_mm512_xor_si512(arch::_mm512_xor_si512(b, t1), t2)
440
0
}
441
442
4.61M
unsafe fn get(a: &mut &[u8]) -> arch::__m128i {
443
4.61M
    debug_assert!(a.len() >= 16);
444
4.61M
    let r = arch::_mm_loadu_si128(a.as_ptr() as *const arch::__m128i);
445
4.61M
    *a = &a[16..];
446
4.61M
    r
447
4.61M
}
448
449
#[cfg(stable_vpclmulqdq)]
450
#[target_feature(enable = "avx")]
451
#[inline]
452
0
unsafe fn get256(a: &mut &[u8]) -> arch::__m256i {
453
0
    debug_assert!(a.len() >= 32);
454
0
    let r = arch::_mm256_loadu_si256(a.as_ptr() as *const arch::__m256i);
455
0
    *a = &a[32..];
456
0
    r
457
0
}
458
459
#[cfg(stable_vpclmulqdq)]
460
#[allow(clippy::incompatible_msrv)] // intrinsics are gated to rustc >= 1.89 by build.rs
461
#[target_feature(enable = "avx512f")]
462
#[inline]
463
0
unsafe fn get512(a: &mut &[u8]) -> arch::__m512i {
464
0
    debug_assert!(a.len() >= 64);
465
0
    let r = arch::_mm512_loadu_si512(a.as_ptr() as *const _);
466
0
    *a = &a[64..];
467
0
    r
468
0
}
469
470
#[cfg(test)]
471
mod 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
                // simulate random alignments by offsetting the slice by up to 15 bytes
478
                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
    // Exercises the wide fold paths across the sizes and alignments where the strategy switches.
492
    #[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
}