1#[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))]
9use crate::algorithm;
10
11use crate::structs::CrcParams;
12
13#[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))]
14use crate::structs::{Width32, Width64};
15
16#[cfg(target_arch = "aarch64")]
17use crate::arch::aarch64::AArch64Ops;
18
19#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
20use crate::arch::x86::X86Ops;
21
22#[cfg(all(target_arch = "x86_64", feature = "vpclmulqdq"))]
23use crate::arch::vpclmulqdq::VpclmulqdqOps;
24
25pub(crate) mod aarch64;
26mod software;
27mod vpclmulqdq;
28pub(crate) mod x86;
29
30#[inline]
36pub(crate) unsafe fn update(state: u64, bytes: &[u8], params: CrcParams) -> u64 {
37    #[cfg(target_arch = "aarch64")]
38    {
39        let ops = AArch64Ops;
40
41        match params.width {
42            64 => algorithm::update::<AArch64Ops, Width64>(state, bytes, params, &ops),
43            32 => {
44                algorithm::update::<AArch64Ops, Width32>(state as u32, bytes, params, &ops) as u64
45            }
46            _ => panic!("Unsupported CRC width: {}", params.width),
47        }
48    }
49
50    #[cfg(all(target_arch = "x86_64", feature = "vpclmulqdq"))]
51    {
52        use std::arch::is_x86_feature_detected;
53
54        if bytes.len() >= 256 && is_x86_feature_detected!("vpclmulqdq") {
55            let ops = vpclmulqdq::VpclmulqdqOps::new();
56
57            return match params.width {
58                64 => algorithm::update::<VpclmulqdqOps, Width64>(state, bytes, params, &ops),
59                32 => {
60                    algorithm::update::<VpclmulqdqOps, Width32>(state as u32, bytes, params, &ops)
61                        as u64
62                }
63                _ => panic!("Unsupported CRC width: {}", params.width),
64            };
65        }
66    }
67
68    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
69    {
70        let ops = X86Ops;
71
72        match params.width {
73            64 => algorithm::update::<X86Ops, Width64>(state, bytes, params, &ops),
74            32 => algorithm::update::<X86Ops, Width32>(state as u32, bytes, params, &ops) as u64,
75            _ => panic!("Unsupported CRC width: {}", params.width),
76        }
77    }
78
79    #[cfg(not(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")))]
80    return software::update(state, bytes, params);
81}
82
83pub fn get_target() -> String {
84    #[cfg(target_arch = "aarch64")]
85    return "internal-aarch64-neon".to_string();
86
87    #[cfg(all(target_arch = "x86_64", feature = "vpclmulqdq"))]
88    return "internal-x86_64-avx512-vpclmulqdq".to_string();
89
90    #[allow(unreachable_code)]
91    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
92    return "internal-x86-sse-pclmulqdq".to_string();
93
94    #[cfg(not(any(target_arch = "aarch64", target_arch = "x86", target_arch = "x86_64")))]
95    return "software-fallback".to_string();
96}
97
98#[cfg(test)]
99mod tests {
100    use super::*;
101    use crate::crc32::consts::CRC32_BZIP2;
102    use crate::crc64::consts::CRC64_NVME;
103    use crate::test::consts::{TEST_256_BYTES_STRING, TEST_ALL_CONFIGS, TEST_CHECK_STRING};
104    use rand::{rng, Rng};
105
106    #[test]
107    fn test_check_value() {
108        for config in TEST_ALL_CONFIGS {
109            let actual = unsafe {
111                update(config.get_init(), TEST_CHECK_STRING, *config.get_params())
112                    ^ config.get_xorout()
113            };
114
115            assert_eq!(
116                actual,
117                config.get_check(),
118                "Mismatch CRC, {}, expected {:#x}, got {:#x}",
119                config.get_name(),
120                config.get_check(),
121                actual
122            );
123        }
124    }
125
126    #[test]
129    fn test_crc64_nvme_standard_vectors() {
130        static CASES: &[(&[u8], u64)] = &[
131            (b"123456789", 0xae8b14860a799888),
135
136            (&[0; 4096], 0x6482d367eb22b64e),
142            (&[255; 4096], 0xc0ddba7302eca3ac),
143
144            (TEST_256_BYTES_STRING, 0xabdb9e6c30937916),
146            (b"", 0),
147            (b"@", 0x2808afa9582aa47),
148            (b"1\x97", 0xb4af0ae0feb08e0f),
149            (b"M\"\xdf", 0x85d7cd041a2a8a5d),
150            (b"l\xcd\x13\xd7", 0x1860820ea79b0fa3),
151            (&[0; 32], 0xcf3473434d4ecf3b),
152            (&[255; 32], 0xa0a06974c34d63c4),
153            (b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F", 0xb9d9d4a8492cbd7f),
154            (&[0; 1024], 0x691bb2b09be5498a),
155            (b"hello, world!", 0xf8046e40c403f1d0),
156        ];
157
158        for (input, expected) in CASES {
159            unsafe {
160                let actual = update(CRC64_NVME.init, input, CRC64_NVME) ^ CRC64_NVME.xorout;
161
162                assert_eq!(
163                    actual, *expected,
164                    "Mismatch CRC, expected {:#x}, got {:#x}, input: {:?}",
165                    expected, actual, input
166                );
167            }
168        }
169    }
170
171    #[test]
177    fn test_crc32_php_standard_vectors() {
178        static CASES: &[(&[u8], u64)] = &[
179            (b"123456789", 0x181989fc),
180            (&[0; 4096], 0xe3380088),
181            (&[255; 4096], 0x8f2ae650),
182            (b"hello, world!", 0x5eacce7),
183        ];
184
185        for (input, expected) in CASES {
186            let bzip2_crc = unsafe {
187                (update(CRC32_BZIP2.init, input, CRC32_BZIP2) ^ CRC32_BZIP2.xorout) as u32
188            };
189
190            let actual = bzip2_crc.swap_bytes();
192
193            assert_eq!(
194                actual, *expected as u32,
195                "Mismatch CRC, expected {:#x}, got {:#x}, input: {:?}",
196                expected, actual, input
197            );
198        }
199    }
200
201    #[test]
202    fn test_small_lengths_all() {
203        let mut rng = rng();
204
205        for config in TEST_ALL_CONFIGS {
207            for len in 0..=255 {
209                let mut data = vec![0u8; len];
211                rng.fill(&mut data[..]);
212
213                let expected = config.checksum_with_reference(&data);
215
216                let actual = unsafe {
218                    update(config.get_init(), &data, *config.get_params()) ^ config.get_xorout()
219                };
220
221                assert_eq!(
222                    actual,
223                    expected,
224                    "\nFailed for {} with length {}\nGot: {:016x}\nExpected: {:016x}",
225                    config.get_name(),
226                    len,
227                    actual,
228                    expected
229                );
230            }
231        }
232    }
233
234    #[test]
235    fn test_medium_lengths() {
236        let mut rng = rng();
237
238        for config in TEST_ALL_CONFIGS {
240            for len in 256..=1024 {
242                let mut data = vec![0u8; len];
244                rng.fill(&mut data[..]);
245
246                let expected = config.checksum_with_reference(&data);
248
249                let actual = unsafe {
251                    update(config.get_init(), &data, *config.get_params()) ^ config.get_xorout()
252                };
253
254                assert_eq!(
255                    actual,
256                    expected,
257                    "\nFailed for {} with length {}\nGot: {:016x}\nExpected: {:016x}",
258                    config.get_name(),
259                    len,
260                    actual,
261                    expected
262                );
263            }
264        }
265    }
266
267    #[test]
268    fn test_large_lengths() {
269        let mut rng = rng();
270
271        for config in TEST_ALL_CONFIGS {
273            for len in 1048575..=1048577 {
275                let mut data = vec![0u8; len];
277                rng.fill(&mut data[..]);
278
279                let expected = config.checksum_with_reference(&data);
281
282                let actual = unsafe {
284                    update(config.get_init(), &data, *config.get_params()) ^ config.get_xorout()
285                };
286
287                assert_eq!(
288                    actual,
289                    expected,
290                    "\nFailed for {} with length {}\\nGot: {:016x}\nExpected: {:016x}",
291                    config.get_name(),
292                    len,
293                    actual,
294                    expected
295                );
296            }
297        }
298    }
299}