@@ -47,18 +47,25 @@ mod crc32c_constants {
4747/// Constants from crc32fast (MIT licensed, same as Intel paper constants):
4848/// - K1/K2: 64-byte (512-bit) folding
4949/// - K3/K4: 16-byte (128-bit) folding
50- /// - MU/POLY: Barrett reduction
50+ /// - K5: 64→32 bit reduction
51+ /// - P_X/U_PRIME: Barrett reduction
5152///
5253/// These are 33-bit values with bit 32 set, as required by the Intel algorithm.
5354mod crc32_ieee_constants {
5455 /// 64-byte block folding: (K1, K2) for 512-bit fold.
55- pub const FOLD_64 : ( u64 , u64 ) = ( 0x0000_0001_5444_2bd4 , 0x0000_0001_c6e4_1596 ) ;
56+ pub const K1 : u64 = 0x0000_0001_5444_2bd4 ;
57+ pub const K2 : u64 = 0x0000_0001_c6e4_1596 ;
5658
5759 /// 16-byte lane folding: (K3, K4) for 128-bit fold.
58- pub const FOLD_16 : ( u64 , u64 ) = ( 0x0000_0001_7519_97d0 , 0x0000_0000_ccaa_009e ) ;
60+ pub const K3 : u64 = 0x0000_0001_7519_97d0 ;
61+ pub const K4 : u64 = 0x0000_0000_ccaa_009e ;
5962
60- /// Barrett reduction: (µ, P').
61- pub const MU_POLY : ( u64 , u64 ) = ( 0x0000_0001_F701_1641 , 0x0000_0001_DB71_0641 ) ;
63+ /// 64→32 bit reduction constant.
64+ pub const K5 : u64 = 0x0000_0001_63cd_6124 ;
65+
66+ /// Barrett reduction: P_X (polynomial) and U_PRIME (µ).
67+ pub const P_X : u64 = 0x0000_0001_DB71_0641 ;
68+ pub const U_PRIME : u64 = 0x0000_0001_F701_1641 ;
6269}
6370
6471// ─────────────────────────────────────────────────────────────────────────────
@@ -402,37 +409,37 @@ pub fn crc32c_pclmul_safe(crc: u32, data: &[u8]) -> u32 {
402409// CRC32-IEEE: Pure PCLMUL + Barrett (no hardware instruction)
403410// ─────────────────────────────────────────────────────────────────────────────
404411
405- /// Barrett reduction from 128 bits to 32 bits for CRC32-IEEE.
412+ /// Reduce 128 bits to 32 bits for CRC32-IEEE.
406413///
407- /// Algorithm based on Intel's "Fast CRC Computation" paper.
408- /// Uses K3/K4 for 128→64→32 reduction , then Barrett for final step .
414+ /// Algorithm from crc32fast / Intel's "Fast CRC Computation" paper.
415+ /// Stages: 128→64 using K3, 64 →32 using K5 , then Barrett with P_X/U_PRIME .
409416#[ target_feature( enable = "pclmulqdq" , enable = "sse4.1" ) ]
410- unsafe fn barrett_reduce_ieee ( acc : __m128i ) -> u32 {
417+ unsafe fn barrett_reduce_ieee ( mut x : __m128i , k3k4 : __m128i ) -> u32 {
411418 // SAFETY: target_feature ensures PCLMULQDQ and SSE4.1 availability
412- let k3_k4 = _mm_set_epi64x (
413- crc32_ieee_constants:: FOLD_16 . 1 as i64 , // K4
414- crc32_ieee_constants:: FOLD_16 . 0 as i64 , // K3
415- ) ;
416- let mu_poly = _mm_set_epi64x (
417- crc32_ieee_constants:: MU_POLY . 1 as i64 , // POLY
418- crc32_ieee_constants:: MU_POLY . 0 as i64 , // MU
419+
420+ // Stage 1: 128→64 bits using K3 (selector 0x10 = x.hi × k3k4.lo = x.hi × K3)
421+ x = _mm_xor_si128 ( _mm_clmulepi64_si128 ( x, k3k4, 0x10 ) , _mm_srli_si128 ( x, 8 ) ) ;
422+
423+ // Stage 2: 64→32 bits using K5
424+ // Mask to get lower 32 bits, multiply by K5, XOR with upper 32 bits
425+ let k5 = _mm_set_epi64x ( 0 , crc32_ieee_constants:: K5 as i64 ) ;
426+ x = _mm_xor_si128 (
427+ _mm_clmulepi64_si128 ( _mm_and_si128 ( x, _mm_set_epi32 ( 0 , 0 , 0 , !0 ) ) , k5, 0x00 ) ,
428+ _mm_srli_si128 ( x, 4 ) ,
419429 ) ;
420430
421- // Fold 128→64 bits: multiply high 64 bits by K3, XOR with low 64 bits
422- let t1 = _mm_clmulepi64_si128 ( acc, k3_k4, 0x10 ) ; // acc.hi × K3
423- let t2 = _mm_xor_si128 ( t1, _mm_srli_si128 ( acc, 8 ) ) ;
424- let t3 = _mm_xor_si128 ( t2, _mm_slli_si128 ( acc, 8 ) ) ;
431+ // Stage 3: Barrett reduction
432+ // pu = [U_PRIME, P_X] - note the order for selector usage
433+ let pu = _mm_set_epi64x ( crc32_ieee_constants:: U_PRIME as i64 , crc32_ieee_constants:: P_X as i64 ) ;
425434
426- // Fold 64→32 bits: multiply bits [63:32] by K4
427- let t4 = _mm_clmulepi64_si128 ( t3, k3_k4, 0x01 ) ; // t3.lo × K4
428- let t5 = _mm_xor_si128 ( t4, t3) ;
435+ // t1 = (x & 0xFFFFFFFF) × U_PRIME (selector 0x10 = x.lo × pu.hi)
436+ let t1 = _mm_clmulepi64_si128 ( _mm_and_si128 ( x, _mm_set_epi32 ( 0 , 0 , 0 , !0 ) ) , pu, 0x10 ) ;
429437
430- // Barrett reduction: 64→32 bits
431- let t6 = _mm_clmulepi64_si128 ( _mm_and_si128 ( t5, _mm_set_epi32 ( 0 , 0 , !0 , !0 ) ) , mu_poly, 0x00 ) ;
432- let t7 = _mm_clmulepi64_si128 ( t6, mu_poly, 0x10 ) ;
433- let result = _mm_xor_si128 ( t5, t7) ;
438+ // t2 = (t1 & 0xFFFFFFFF) × P_X (selector 0x00 = t1.lo × pu.lo)
439+ let t2 = _mm_clmulepi64_si128 ( _mm_and_si128 ( t1, _mm_set_epi32 ( 0 , 0 , 0 , !0 ) ) , pu, 0x00 ) ;
434440
435- _mm_extract_epi32 ( result, 1 ) as u32
441+ // Result is in bits [63:32] after XOR
442+ _mm_extract_epi32 ( _mm_xor_si128 ( x, t2) , 1 ) as u32
436443}
437444
438445/// CRC32-IEEE using PCLMULQDQ folding + Barrett reduction.
@@ -453,14 +460,9 @@ pub unsafe fn crc32_ieee_pclmul(mut crc: u32, data: &[u8]) -> u32 {
453460 let mut ptr = data. as_ptr ( ) ;
454461 let end = ptr. add ( data. len ( ) ) ;
455462
456- let fold_k = _mm_set_epi64x (
457- crc32_ieee_constants:: FOLD_16 . 1 as i64 ,
458- crc32_ieee_constants:: FOLD_16 . 0 as i64 ,
459- ) ;
460- let fold_64_k = _mm_set_epi64x (
461- crc32_ieee_constants:: FOLD_64 . 1 as i64 ,
462- crc32_ieee_constants:: FOLD_64 . 0 as i64 ,
463- ) ;
463+ // K1K2 for 64-byte fold, K3K4 for 16-byte fold
464+ let k1k2 = _mm_set_epi64x ( crc32_ieee_constants:: K2 as i64 , crc32_ieee_constants:: K1 as i64 ) ;
465+ let k3k4 = _mm_set_epi64x ( crc32_ieee_constants:: K4 as i64 , crc32_ieee_constants:: K3 as i64 ) ;
464466
465467 // Initialize 4 lanes with first 64 bytes
466468 let crc_vec = _mm_set_epi32 ( 0 , 0 , 0 , crc as i32 ) ;
@@ -472,45 +474,45 @@ pub unsafe fn crc32_ieee_pclmul(mut crc: u32, data: &[u8]) -> u32 {
472474 ] ;
473475 ptr = ptr. add ( FOLD_BYTES ) ;
474476
475- // Process 64-byte blocks
477+ // Process 64-byte blocks using K1K2
476478 while ptr. add ( FOLD_BYTES ) <= end {
477479 for ( i, lane) in lanes. iter_mut ( ) . enumerate ( ) {
478480 let new_data = _mm_loadu_si128 ( ptr. add ( i * 16 ) as * const __m128i ) ;
479- let lo = clmul_lo ( * lane, fold_64_k ) ;
480- let hi = clmul_hi ( * lane, fold_64_k ) ;
481+ let lo = clmul_lo ( * lane, k1k2 ) ;
482+ let hi = clmul_hi ( * lane, k1k2 ) ;
481483 * lane = _mm_xor_si128 ( _mm_xor_si128 ( lo, hi) , new_data) ;
482484 }
483485 ptr = ptr. add ( FOLD_BYTES ) ;
484486 }
485487
486- // Reduce 4 lanes to 1
488+ // Reduce 4 lanes to 1 using K3K4
487489 let fold_48 = {
488- let lo = clmul_lo ( lanes[ 0 ] , fold_k ) ;
489- let hi = clmul_hi ( lanes[ 0 ] , fold_k ) ;
490+ let lo = clmul_lo ( lanes[ 0 ] , k3k4 ) ;
491+ let hi = clmul_hi ( lanes[ 0 ] , k3k4 ) ;
490492 _mm_xor_si128 ( _mm_xor_si128 ( lo, hi) , lanes[ 1 ] )
491493 } ;
492494 let fold_32 = {
493- let lo = clmul_lo ( fold_48, fold_k ) ;
494- let hi = clmul_hi ( fold_48, fold_k ) ;
495+ let lo = clmul_lo ( fold_48, k3k4 ) ;
496+ let hi = clmul_hi ( fold_48, k3k4 ) ;
495497 _mm_xor_si128 ( _mm_xor_si128 ( lo, hi) , lanes[ 2 ] )
496498 } ;
497499 let mut acc = {
498- let lo = clmul_lo ( fold_32, fold_k ) ;
499- let hi = clmul_hi ( fold_32, fold_k ) ;
500+ let lo = clmul_lo ( fold_32, k3k4 ) ;
501+ let hi = clmul_hi ( fold_32, k3k4 ) ;
500502 _mm_xor_si128 ( _mm_xor_si128 ( lo, hi) , lanes[ 3 ] )
501503 } ;
502504
503- // Process remaining 16-byte blocks
505+ // Process remaining 16-byte blocks using K3K4
504506 while ptr. add ( 16 ) <= end {
505507 let block = _mm_loadu_si128 ( ptr as * const __m128i ) ;
506- let lo = clmul_lo ( acc, fold_k ) ;
507- let hi = clmul_hi ( acc, fold_k ) ;
508+ let lo = clmul_lo ( acc, k3k4 ) ;
509+ let hi = clmul_hi ( acc, k3k4 ) ;
508510 acc = _mm_xor_si128 ( _mm_xor_si128 ( lo, hi) , block) ;
509511 ptr = ptr. add ( 16 ) ;
510512 }
511513
512- // Barrett reduction
513- crc = barrett_reduce_ieee ( acc) ;
514+ // Reduce 128→32 bits
515+ crc = barrett_reduce_ieee ( acc, k3k4 ) ;
514516
515517 // Handle remainder with portable
516518 let remainder_len = end. offset_from ( ptr) as usize ;
0 commit comments