OXYGENHUB
# dab94366a4f0 oxygen / vendor / crc-fast / src / crc32 / fusion / x86 / mod.rs
10.0 KiB · Rust · last changed Strata: the cloud stores send through ureq, so every release target links 1 month ago source blame history raw
1
//! Provides CRC-32/ISCSI calculations using a fusion of native CLMUL
2
//! instructions and native CRC calculation instructions on x86 / x86_64.
3
//!
4
//! https://www.corsix.org/content/fast-crc32c-4k
5
//! https://www.corsix.org/content/alternative-exposition-crc32_4k_pclmulqdq
6
//!
7
//! Converted to Rust from the original C code generated by https://github.com/corsix/fast-crc32/
8
//! with the help of Claude.ai.
9
//!
10
//! Modified as necessary for this Rust implementation.
11
//!
12
//! MIT licensed.
13
14
#![cfg(any(target_arch = "x86", target_arch = "x86_64"))]
15
16
mod iscsi;
17
18
use iscsi::sse_pclmulqdq::crc32_iscsi_sse_v4s3x3;
19
20
#[cfg(target_arch = "x86")]
21
use core::arch::x86::*;
22
#[cfg(target_arch = "x86_64")]
23
use core::arch::x86_64::*;
24
#[cfg(target_arch = "x86_64")]
25
use iscsi::avx512_pclmulqdq::crc32_iscsi_avx512_v4s3x3;
26
#[cfg(target_arch = "x86_64")]
27
use iscsi::avx512_vpclmulqdq::crc32_iscsi_avx512_vpclmulqdq_v3x2;
28
29
/// CRC32 iSCSI calculation using the highest available instruction set (post-AVX-512 support)
30
///
31
/// This function is called by the wrapper layer after feature detection has been performed.
32
/// The wrapper layer ensures that only the appropriate implementation is called based on
33
/// cached feature detection results, removing runtime checks from the hot path.
34
#[inline(always)]
35
pub fn crc32_iscsi(crc: u32, data: &[u8]) -> u32 {
36
    let data_len = data.len();
37
38
    // SSE4.2 is required for native CRC32 instructions used in small buffer path
39
    if data_len <= 256 && is_x86_feature_detected!("sse4.2") {
40
        unsafe {
41
            return crc32_iscsi_small_fast(crc, data);
42
        }
43
    }
44
45
    #[cfg(target_arch = "x86_64")]
46
    {
47
        // AVX512 + VPCLMULQDQ
48
49
        if is_x86_feature_detected!("avx512vl") && is_x86_feature_detected!("vpclmulqdq") {
50
            unsafe {
51
                return crc32_iscsi_avx512_vpclmulqdq_v3x2(crc, data.as_ptr(), data_len);
52
            }
53
        }
54
55
        // AVX512
56
        if is_x86_feature_detected!("avx512vl") {
57
            unsafe {
58
                return crc32_iscsi_avx512_v4s3x3(crc, data.as_ptr(), data_len);
59
            }
60
        }
61
    }
62
63
    // Fallback to SSE implementation
64
    unsafe { crc32_iscsi_sse_v4s3x3(crc, data.as_ptr(), data_len) }
65
}
66
67
#[cfg(target_arch = "x86_64")]
68
#[inline]
69
#[target_feature(enable = "avx512vl,vpclmulqdq")]
70
unsafe fn clmul_lo_avx512_vpclmulqdq(a: __m512i, b: __m512i) -> __m512i {
71
    _mm512_clmulepi64_epi128(a, b, 0)
72
}
73
74
#[cfg(target_arch = "x86_64")]
75
#[inline]
76
#[target_feature(enable = "avx512vl,vpclmulqdq")]
77
unsafe fn clmul_hi_avx512_vpclmulqdq(a: __m512i, b: __m512i) -> __m512i {
78
    _mm512_clmulepi64_epi128(a, b, 17)
79
}
80
81
#[inline]
82
#[target_feature(enable = "pclmulqdq")]
83
unsafe fn clmul_lo_sse(a: __m128i, b: __m128i) -> __m128i {
84
    _mm_clmulepi64_si128(a, b, 0)
85
}
86
87
#[inline]
88
#[target_feature(enable = "pclmulqdq")]
89
unsafe fn clmul_hi_sse(a: __m128i, b: __m128i) -> __m128i {
90
    _mm_clmulepi64_si128(a, b, 17)
91
}
92
93
#[inline]
94
#[target_feature(enable = "pclmulqdq")]
95
unsafe fn clmul_scalar_sse(a: u32, b: u32) -> __m128i {
96
    _mm_clmulepi64_si128(_mm_cvtsi32_si128(a as i32), _mm_cvtsi32_si128(b as i32), 0)
97
}
98
99
// x^n mod P, in log(n) time
100
#[target_feature(enable = "sse4.2,pclmulqdq")]
101
unsafe fn xnmodp_iscsi_sse(mut n: u64) -> u32 {
102
    let mut stack = !1u64;
103
    let mut acc: u32;
104
    let mut low: u32;
105
106
    while n > 191 {
107
        stack = (stack << 1) + (n & 1);
108
        n = (n >> 1) - 16;
109
    }
110
    stack = !stack;
111
    acc = 0x80000000u32 >> (n & 31);
112
    n >>= 5;
113
114
    while n > 0 {
115
        // Use hardware CRC32C instruction
116
        acc = _mm_crc32_u32(acc, 0);
117
        n -= 1;
118
    }
119
120
    while {
121
        low = (stack & 1) as u32;
122
        stack >>= 1;
123
        stack != 0
124
    } {
125
        let x = _mm_cvtsi32_si128(acc as i32);
126
        let clmul_result = _mm_clmulepi64_si128(x, x, 0);
127
        let y = mm_extract_epi64(clmul_result, 0);
128
        acc = mm_crc32_u64(0, y << low);
129
    }
130
    acc
131
}
132
133
#[inline]
134
#[target_feature(enable = "pclmulqdq")]
135
unsafe fn crc_shift_iscsi_sse(crc: u32, nbytes: usize) -> __m128i {
136
    clmul_scalar_sse(crc, xnmodp_iscsi_sse((nbytes * 8 - 33) as u64))
137
}
138
139
#[inline]
140
#[target_feature(enable = "sse4.1")]
141
unsafe fn mm_extract_epi64(val: __m128i, idx: i32) -> u64 {
142
    #[cfg(target_arch = "x86_64")]
143
    {
144
        if idx == 0 {
145
            _mm_cvtsi128_si64(val) as u64
146
        } else {
147
            _mm_cvtsi128_si64(_mm_srli_si128(val, 8)) as u64
148
        }
149
    }
150
    #[cfg(target_arch = "x86")]
151
    {
152
        // On 32-bit x86, extract two 32-bit values and combine them
153
        let shifted = if idx == 0 {
154
            val
155
        } else {
156
            _mm_srli_si128(val, 8)
157
        };
158
        let low = _mm_cvtsi128_si32(shifted) as u32;
159
        let high = _mm_cvtsi128_si32(_mm_srli_si128(shifted, 4)) as u32;
160
        (low as u64) | ((high as u64) << 32)
161
    }
162
}
163
164
#[inline]
165
#[target_feature(enable = "sse4.2")]
166
unsafe fn mm_crc32_u64(crc: u32, val: u64) -> u32 {
167
    #[cfg(target_arch = "x86_64")]
168
    {
169
        _mm_crc32_u64(crc.into(), val) as u32
170
    }
171
    #[cfg(target_arch = "x86")]
172
    {
173
        // On 32-bit x86, process 64-bit value as two 32-bit CRC operations
174
        let low = val as u32;
175
        let high = (val >> 32) as u32;
176
        let crc = _mm_crc32_u32(crc, low);
177
        _mm_crc32_u32(crc, high)
178
    }
179
}
180
181
/// CRC-32/ISCSI calculation for small buffers (<= 256 bytes) using unrolled native CRC instructions
182
#[inline]
183
#[target_feature(enable = "sse4.2")]
184
pub unsafe fn crc32_iscsi_small_fast(mut crc: u32, data: &[u8]) -> u32 {
185
    let (prefix, aligned, suffix) = data.align_to::<u64>();
186
187
    for &byte in prefix {
188
        crc = _mm_crc32_u8(crc, byte);
189
    }
190
191
    let mut chunks = aligned.chunks_exact(8);
192
    for chunk in &mut chunks {
193
        crc = mm_crc32_u64(crc, chunk[0]);
194
        crc = mm_crc32_u64(crc, chunk[1]);
195
        crc = mm_crc32_u64(crc, chunk[2]);
196
        crc = mm_crc32_u64(crc, chunk[3]);
197
        crc = mm_crc32_u64(crc, chunk[4]);
198
        crc = mm_crc32_u64(crc, chunk[5]);
199
        crc = mm_crc32_u64(crc, chunk[6]);
200
        crc = mm_crc32_u64(crc, chunk[7]);
201
    }
202
203
    for &val in chunks.remainder() {
204
        crc = mm_crc32_u64(crc, val);
205
    }
206
207
    for &byte in suffix {
208
        crc = _mm_crc32_u8(crc, byte);
209
    }
210
211
    crc
212
}
213
214
#[cfg(test)]
215
mod tests {
216
    use super::*;
217
    use crate::test::consts::TEST_CHECK_STRING;
218
    use crc::{Crc, Table};
219
    use rand::{rng, Rng};
220
221
    const RUST_CRC32_ISCSI: Crc<u32, Table<16>> = Crc::<u32, Table<16>>::new(&crc::CRC_32_ISCSI);
222
223
    #[test]
224
    fn test_crc32_iscsi_check() {
225
        if is_x86_feature_detected!("sse4.2") && is_x86_feature_detected!("pclmulqdq") {
226
            assert_eq!(
227
                crc32_iscsi(0xffffffff, TEST_CHECK_STRING) ^ 0xffffffff,
228
                0xe3069283
229
            );
230
        }
231
    }
232
233
    #[test]
234
    fn test_crc32_iscsi_small_fast_check() {
235
        if is_x86_feature_detected!("sse4.2") {
236
            unsafe {
237
                assert_eq!(
238
                    crc32_iscsi_small_fast(0xffffffff, TEST_CHECK_STRING) ^ 0xffffffff,
239
                    0xe3069283
240
                );
241
            }
242
        }
243
    }
244
245
    #[test]
246
    fn test_crc32_iscsi_small_fast_all_lengths() {
247
        if is_x86_feature_detected!("sse4.2") {
248
            for len in 1..=255 {
249
                test_crc32_iscsi_small_fast_random(len);
250
            }
251
        }
252
    }
253
254
    fn test_crc32_iscsi_small_fast_random(len: usize) {
255
        let mut data = vec![0u8; len];
256
        rng().fill(&mut data[..]);
257
258
        let checksum = RUST_CRC32_ISCSI.checksum(&data);
259
260
        if is_x86_feature_detected!("sse4.2") {
261
            unsafe {
262
                assert_eq!(
263
                    crc32_iscsi_small_fast(0xffffffff, &data) ^ 0xffffffff,
264
                    checksum
265
                );
266
            }
267
        }
268
    }
269
270
    #[test]
271
    fn test_crc32_iscsi_small_all_lengths() {
272
        if is_x86_feature_detected!("sse4.2") && is_x86_feature_detected!("pclmulqdq") {
273
            for len in 1..=255 {
274
                test_crc32_iscsi_random(len);
275
            }
276
        }
277
    }
278
279
    #[test]
280
    fn test_crc32_iscsi_medium_lengths() {
281
        if is_x86_feature_detected!("sse4.2") && is_x86_feature_detected!("pclmulqdq") {
282
            // Test each length from 256 to 1024, which should fold and include handling remainders
283
            for len in 256..=1024 {
284
                test_crc32_iscsi_random(len);
285
            }
286
        }
287
    }
288
289
    #[test]
290
    fn test_crc32_iscsi_large_lengths() {
291
        if is_x86_feature_detected!("sse4.2") && is_x86_feature_detected!("pclmulqdq") {
292
            // Test 1 MiB just before, at, and just after the folding boundaries
293
            for len in 1048575..1048577 {
294
                test_crc32_iscsi_random(len);
295
            }
296
        }
297
    }
298
299
    fn test_crc32_iscsi_random(len: usize) {
300
        let mut data = vec![0u8; len];
301
        rng().fill(&mut data[..]);
302
303
        let checksum = RUST_CRC32_ISCSI.checksum(&data);
304
305
        if is_x86_feature_detected!("sse4.2") && is_x86_feature_detected!("pclmulqdq") {
306
            assert_eq!(crc32_iscsi(0xffffffff, &data) ^ 0xffffffff, checksum);
307
        }
308
309
        unsafe {
310
            #[cfg(target_arch = "x86_64")]
311
            {
312
                if is_x86_feature_detected!("vpclmulqdq")
313
                    && is_x86_feature_detected!("avx512vl")
314
                    && is_x86_feature_detected!("avx512f")
315
                {
316
                    assert_eq!(
317
                        crc32_iscsi_avx512_vpclmulqdq_v3x2(0xffffffff, data.as_ptr(), data.len())
318
                            ^ 0xffffffff,
319
                        checksum
320
                    );
321
                }
322
323
                if is_x86_feature_detected!("avx512vl")
324
                    && is_x86_feature_detected!("avx512f")
325
                    && is_x86_feature_detected!("pclmulqdq")
326
                {
327
                    assert_eq!(
328
                        crc32_iscsi_avx512_v4s3x3(0xffffffff, data.as_ptr(), data.len())
329
                            ^ 0xffffffff,
330
                        checksum
331
                    );
332
                }
333
            }
334
335
            assert_eq!(
336
                crc32_iscsi_sse_v4s3x3(0xffffffff, data.as_ptr(), data.len()) ^ 0xffffffff,
337
                checksum
338
            );
339
        }
340
    }
341
}