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}