1#![cfg(any(target_arch = "aarch64", target_arch = "x86_64", target_arch = "x86"))]
9
10use crate::CrcAlgorithm;
11use crate::CrcParams;
12use crate::{get_calculator_target, Digest};
13use std::collections::HashMap;
14use std::ffi::CStr;
15use std::os::raw::c_char;
16use std::slice;
17use std::sync::Mutex;
18use std::sync::OnceLock;
19
20static STABLE_KEY_STORAGE: OnceLock<Mutex<HashMap<u64, Box<[u64]>>>> = OnceLock::new();
22
23fn create_stable_key_pointer(keys: &crate::CrcKeysStorage) -> (*const u64, u32) {
26 let storage = STABLE_KEY_STORAGE.get_or_init(|| Mutex::new(HashMap::new()));
27
28 let key_hash = match keys {
30 crate::CrcKeysStorage::KeysFold256(keys) => {
31 let mut hasher = std::collections::hash_map::DefaultHasher::new();
32 use std::hash::{Hash, Hasher};
33 keys.hash(&mut hasher);
34 hasher.finish()
35 }
36 crate::CrcKeysStorage::KeysFutureTest(keys) => {
37 let mut hasher = std::collections::hash_map::DefaultHasher::new();
38 use std::hash::{Hash, Hasher};
39 keys.hash(&mut hasher);
40 hasher.finish()
41 }
42 };
43
44 let mut storage_map = storage.lock().unwrap();
45
46 if let Some(stored_keys) = storage_map.get(&key_hash) {
48 return (stored_keys.as_ptr(), stored_keys.len() as u32);
49 }
50
51 let key_vec: Vec<u64> = match keys {
53 crate::CrcKeysStorage::KeysFold256(keys) => keys.to_vec(),
54 crate::CrcKeysStorage::KeysFutureTest(keys) => keys.to_vec(),
55 };
56
57 let boxed_keys = key_vec.into_boxed_slice();
58 let ptr = boxed_keys.as_ptr();
59 let count = boxed_keys.len() as u32;
60
61 storage_map.insert(key_hash, boxed_keys);
62
63 (ptr, count)
64}
65
66#[repr(C)]
68pub struct CrcFastDigestHandle(*mut Digest);
69
70#[repr(C)]
72pub enum CrcFastAlgorithm {
73 Crc32Aixm,
74 Crc32Autosar,
75 Crc32Base91D,
76 Crc32Bzip2,
77 Crc32CdRomEdc,
78 Crc32Cksum,
79 Crc32Custom,
80 Crc32Iscsi,
81 Crc32IsoHdlc,
82 Crc32Jamcrc,
83 Crc32Mef,
84 Crc32Mpeg2,
85 Crc32Xfer,
86 Crc64Custom,
87 Crc64Ecma182,
88 Crc64GoIso,
89 Crc64Ms,
90 Crc64Nvme,
91 Crc64Redis,
92 Crc64We,
93 Crc64Xz,
94}
95
96impl From<CrcFastAlgorithm> for CrcAlgorithm {
98 fn from(value: CrcFastAlgorithm) -> Self {
99 match value {
100 CrcFastAlgorithm::Crc32Aixm => CrcAlgorithm::Crc32Aixm,
101 CrcFastAlgorithm::Crc32Autosar => CrcAlgorithm::Crc32Autosar,
102 CrcFastAlgorithm::Crc32Base91D => CrcAlgorithm::Crc32Base91D,
103 CrcFastAlgorithm::Crc32Bzip2 => CrcAlgorithm::Crc32Bzip2,
104 CrcFastAlgorithm::Crc32CdRomEdc => CrcAlgorithm::Crc32CdRomEdc,
105 CrcFastAlgorithm::Crc32Cksum => CrcAlgorithm::Crc32Cksum,
106 CrcFastAlgorithm::Crc32Custom => CrcAlgorithm::Crc32Custom,
107 CrcFastAlgorithm::Crc32Iscsi => CrcAlgorithm::Crc32Iscsi,
108 CrcFastAlgorithm::Crc32IsoHdlc => CrcAlgorithm::Crc32IsoHdlc,
109 CrcFastAlgorithm::Crc32Jamcrc => CrcAlgorithm::Crc32Jamcrc,
110 CrcFastAlgorithm::Crc32Mef => CrcAlgorithm::Crc32Mef,
111 CrcFastAlgorithm::Crc32Mpeg2 => CrcAlgorithm::Crc32Mpeg2,
112 CrcFastAlgorithm::Crc32Xfer => CrcAlgorithm::Crc32Xfer,
113 CrcFastAlgorithm::Crc64Custom => CrcAlgorithm::Crc64Custom,
114 CrcFastAlgorithm::Crc64Ecma182 => CrcAlgorithm::Crc64Ecma182,
115 CrcFastAlgorithm::Crc64GoIso => CrcAlgorithm::Crc64GoIso,
116 CrcFastAlgorithm::Crc64Ms => CrcAlgorithm::Crc64Ms,
117 CrcFastAlgorithm::Crc64Nvme => CrcAlgorithm::Crc64Nvme,
118 CrcFastAlgorithm::Crc64Redis => CrcAlgorithm::Crc64Redis,
119 CrcFastAlgorithm::Crc64We => CrcAlgorithm::Crc64We,
120 CrcFastAlgorithm::Crc64Xz => CrcAlgorithm::Crc64Xz,
121 }
122 }
123}
124
125#[repr(C)]
127pub struct CrcFastParams {
128 pub algorithm: CrcFastAlgorithm,
129 pub width: u8,
130 pub poly: u64,
131 pub init: u64,
132 pub refin: bool,
133 pub refout: bool,
134 pub xorout: u64,
135 pub check: u64,
136 pub key_count: u32,
137 pub keys: *const u64,
138}
139
140impl From<CrcFastParams> for CrcParams {
142 fn from(value: CrcFastParams) -> Self {
143 let keys = unsafe { std::slice::from_raw_parts(value.keys, value.key_count as usize) };
145
146 let storage = match value.key_count {
147 23 => crate::CrcKeysStorage::from_keys_fold_256(
148 keys.try_into().expect("Invalid key count for fold_256"),
149 ),
150 25 => crate::CrcKeysStorage::from_keys_fold_future_test(
151 keys.try_into().expect("Invalid key count for future_test"),
152 ),
153 _ => panic!("Unsupported key count: {}", value.key_count),
154 };
155
156 CrcParams {
157 algorithm: value.algorithm.into(),
158 name: "custom", width: value.width,
160 poly: value.poly,
161 init: value.init,
162 refin: value.refin,
163 refout: value.refout,
164 xorout: value.xorout,
165 check: value.check,
166 keys: storage,
167 }
168 }
169}
170
171impl From<CrcParams> for CrcFastParams {
173 fn from(params: CrcParams) -> Self {
174 let (keys_ptr, key_count) = create_stable_key_pointer(¶ms.keys);
176
177 CrcFastParams {
178 algorithm: match params.algorithm {
179 CrcAlgorithm::Crc32Aixm => CrcFastAlgorithm::Crc32Aixm,
180 CrcAlgorithm::Crc32Autosar => CrcFastAlgorithm::Crc32Autosar,
181 CrcAlgorithm::Crc32Base91D => CrcFastAlgorithm::Crc32Base91D,
182 CrcAlgorithm::Crc32Bzip2 => CrcFastAlgorithm::Crc32Bzip2,
183 CrcAlgorithm::Crc32CdRomEdc => CrcFastAlgorithm::Crc32CdRomEdc,
184 CrcAlgorithm::Crc32Cksum => CrcFastAlgorithm::Crc32Cksum,
185 CrcAlgorithm::Crc32Custom => CrcFastAlgorithm::Crc32Custom,
186 CrcAlgorithm::Crc32Iscsi => CrcFastAlgorithm::Crc32Iscsi,
187 CrcAlgorithm::Crc32IsoHdlc => CrcFastAlgorithm::Crc32IsoHdlc,
188 CrcAlgorithm::Crc32Jamcrc => CrcFastAlgorithm::Crc32Jamcrc,
189 CrcAlgorithm::Crc32Mef => CrcFastAlgorithm::Crc32Mef,
190 CrcAlgorithm::Crc32Mpeg2 => CrcFastAlgorithm::Crc32Mpeg2,
191 CrcAlgorithm::Crc32Xfer => CrcFastAlgorithm::Crc32Xfer,
192 CrcAlgorithm::Crc64Custom => CrcFastAlgorithm::Crc64Custom,
193 CrcAlgorithm::Crc64Ecma182 => CrcFastAlgorithm::Crc64Ecma182,
194 CrcAlgorithm::Crc64GoIso => CrcFastAlgorithm::Crc64GoIso,
195 CrcAlgorithm::Crc64Ms => CrcFastAlgorithm::Crc64Ms,
196 CrcAlgorithm::Crc64Nvme => CrcFastAlgorithm::Crc64Nvme,
197 CrcAlgorithm::Crc64Redis => CrcFastAlgorithm::Crc64Redis,
198 CrcAlgorithm::Crc64We => CrcFastAlgorithm::Crc64We,
199 CrcAlgorithm::Crc64Xz => CrcFastAlgorithm::Crc64Xz,
200 },
201 width: params.width,
202 poly: params.poly,
203 init: params.init,
204 refin: params.refin,
205 refout: params.refout,
206 xorout: params.xorout,
207 check: params.check,
208 key_count,
209 keys: keys_ptr,
210 }
211 }
212}
213
214#[no_mangle]
216pub extern "C" fn crc_fast_digest_new(algorithm: CrcFastAlgorithm) -> *mut CrcFastDigestHandle {
217 let digest = Box::new(Digest::new(algorithm.into()));
218 let handle = Box::new(CrcFastDigestHandle(Box::into_raw(digest)));
219 Box::into_raw(handle)
220}
221
222#[no_mangle]
224pub extern "C" fn crc_fast_digest_new_with_init_state(
225 algorithm: CrcFastAlgorithm,
226 init_state: u64,
227) -> *mut CrcFastDigestHandle {
228 let digest = Box::new(Digest::new_with_init_state(algorithm.into(), init_state));
229 let handle = Box::new(CrcFastDigestHandle(Box::into_raw(digest)));
230 Box::into_raw(handle)
231}
232
233#[no_mangle]
235pub extern "C" fn crc_fast_digest_new_with_params(
236 params: CrcFastParams,
237) -> *mut CrcFastDigestHandle {
238 let digest = Box::new(Digest::new_with_params(params.into()));
239 let handle = Box::new(CrcFastDigestHandle(Box::into_raw(digest)));
240 Box::into_raw(handle)
241}
242
243#[no_mangle]
245pub extern "C" fn crc_fast_digest_update(
246 handle: *mut CrcFastDigestHandle,
247 data: *const c_char,
248 len: usize,
249) {
250 if handle.is_null() || data.is_null() {
251 return;
252 }
253
254 unsafe {
255 let digest = &mut *(*handle).0;
256
257 #[allow(clippy::unnecessary_cast)]
258 let bytes = slice::from_raw_parts(data as *const u8, len);
259 digest.update(bytes);
260 }
261}
262
263#[no_mangle]
265pub extern "C" fn crc_fast_digest_finalize(handle: *mut CrcFastDigestHandle) -> u64 {
266 if handle.is_null() {
267 return 0;
268 }
269
270 unsafe {
271 let digest = &*(*handle).0;
272 digest.finalize()
273 }
274}
275
276#[no_mangle]
278pub extern "C" fn crc_fast_digest_free(handle: *mut CrcFastDigestHandle) {
279 if handle.is_null() {
280 return;
281 }
282
283 unsafe {
284 let handle = Box::from_raw(handle);
285 let _ = Box::from_raw(handle.0); }
287}
288
289#[no_mangle]
291pub extern "C" fn crc_fast_digest_reset(handle: *mut CrcFastDigestHandle) {
292 if handle.is_null() {
293 return;
294 }
295
296 unsafe {
297 let digest = &mut *(*handle).0;
298
299 digest.reset();
300 }
301}
302
303#[no_mangle]
305pub extern "C" fn crc_fast_digest_finalize_reset(handle: *mut CrcFastDigestHandle) -> u64 {
306 if handle.is_null() {
307 return 0;
308 }
309
310 unsafe {
311 let digest = &mut *(*handle).0;
312
313 digest.finalize_reset()
314 }
315}
316
317#[no_mangle]
319pub extern "C" fn crc_fast_digest_combine(
320 handle1: *mut CrcFastDigestHandle,
321 handle2: *mut CrcFastDigestHandle,
322) {
323 if handle1.is_null() || handle2.is_null() {
324 return;
325 }
326
327 unsafe {
328 let digest1 = &mut *(*handle1).0;
329 let digest2 = &*(*handle2).0;
330 digest1.combine(digest2);
331 }
332}
333
334#[no_mangle]
336pub extern "C" fn crc_fast_digest_get_amount(handle: *mut CrcFastDigestHandle) -> u64 {
337 if handle.is_null() {
338 return 0;
339 }
340
341 unsafe {
342 let digest = &*(*handle).0;
343 digest.get_amount()
344 }
345}
346
347#[no_mangle]
349pub extern "C" fn crc_fast_digest_get_state(handle: *mut CrcFastDigestHandle) -> u64 {
350 if handle.is_null() {
351 return 0;
352 }
353 unsafe {
354 let digest = &*(*handle).0;
355 digest.get_state()
356 }
357}
358
359#[no_mangle]
361pub extern "C" fn crc_fast_checksum(
362 algorithm: CrcFastAlgorithm,
363 data: *const c_char,
364 len: usize,
365) -> u64 {
366 if data.is_null() {
367 return 0;
368 }
369 unsafe {
370 #[allow(clippy::unnecessary_cast)]
371 let bytes = slice::from_raw_parts(data as *const u8, len);
372 crate::checksum(algorithm.into(), bytes)
373 }
374}
375
376#[no_mangle]
378pub extern "C" fn crc_fast_checksum_with_params(
379 params: CrcFastParams,
380 data: *const c_char,
381 len: usize,
382) -> u64 {
383 if data.is_null() {
384 return 0;
385 }
386 unsafe {
387 #[allow(clippy::unnecessary_cast)]
388 let bytes = slice::from_raw_parts(data as *const u8, len);
389 crate::checksum_with_params(params.into(), bytes)
390 }
391}
392
393#[no_mangle]
395pub extern "C" fn crc_fast_checksum_file(
396 algorithm: CrcFastAlgorithm,
397 path_ptr: *const u8,
398 path_len: usize,
399) -> u64 {
400 if path_ptr.is_null() {
401 return 0;
402 }
403
404 unsafe {
405 crate::checksum_file(
406 algorithm.into(),
407 &convert_to_string(path_ptr, path_len),
408 None,
409 )
410 .unwrap()
411 }
412}
413
414#[no_mangle]
416pub extern "C" fn crc_fast_checksum_file_with_params(
417 params: CrcFastParams,
418 path_ptr: *const u8,
419 path_len: usize,
420) -> u64 {
421 if path_ptr.is_null() {
422 return 0;
423 }
424
425 unsafe {
426 crate::checksum_file_with_params(
427 params.into(),
428 &convert_to_string(path_ptr, path_len),
429 None,
430 )
431 .unwrap_or(0) }
433}
434
435#[no_mangle]
437pub extern "C" fn crc_fast_checksum_combine(
438 algorithm: CrcFastAlgorithm,
439 checksum1: u64,
440 checksum2: u64,
441 checksum2_len: u64,
442) -> u64 {
443 crate::checksum_combine(algorithm.into(), checksum1, checksum2, checksum2_len)
444}
445
446#[no_mangle]
448pub extern "C" fn crc_fast_checksum_combine_with_params(
449 params: CrcFastParams,
450 checksum1: u64,
451 checksum2: u64,
452 checksum2_len: u64,
453) -> u64 {
454 crate::checksum_combine_with_params(params.into(), checksum1, checksum2, checksum2_len)
455}
456
457#[no_mangle]
459pub extern "C" fn crc_fast_get_custom_params(
460 name_ptr: *const c_char,
461 width: u8,
462 poly: u64,
463 init: u64,
464 reflected: bool,
465 xorout: u64,
466 check: u64,
467) -> CrcFastParams {
468 let name = if name_ptr.is_null() {
469 "custom"
470 } else {
471 unsafe { CStr::from_ptr(name_ptr).to_str().unwrap_or("custom") }
472 };
473
474 let params = CrcParams::new(
476 Box::leak(name.to_string().into_boxed_str()),
478 width,
479 poly,
480 init,
481 reflected,
482 xorout,
483 check,
484 );
485
486 let (keys_ptr, key_count) = create_stable_key_pointer(¶ms.keys);
488
489 CrcFastParams {
491 algorithm: match width {
492 32 => CrcFastAlgorithm::Crc32Custom,
493 64 => CrcFastAlgorithm::Crc64Custom,
494 _ => panic!("Unsupported width: {width}",),
495 },
496 width: params.width,
497 poly: params.poly,
498 init: params.init,
499 refin: params.refin,
500 refout: params.refout,
501 xorout: params.xorout,
502 check: params.check,
503 key_count,
504 keys: keys_ptr,
505 }
506}
507
508#[no_mangle]
510pub extern "C" fn crc_fast_get_calculator_target(algorithm: CrcFastAlgorithm) -> *const c_char {
511 let target = get_calculator_target(algorithm.into());
512
513 std::ffi::CString::new(target).unwrap().into_raw()
514}
515
516#[no_mangle]
518pub extern "C" fn crc_fast_get_version() -> *const libc::c_char {
519 const VERSION: &CStr =
520 match CStr::from_bytes_with_nul(concat!(env!("CARGO_PKG_VERSION"), "\0").as_bytes()) {
521 Ok(version) => version,
522 Err(_) => panic!("package version contains null bytes??"),
523 };
524
525 VERSION.as_ptr()
526}
527
528unsafe fn convert_to_string(data: *const u8, len: usize) -> String {
529 if data.is_null() {
530 return String::new();
531 }
532
533 match std::str::from_utf8(slice::from_raw_parts(data, len)) {
535 Ok(s) => s.to_string(),
536 Err(_) => panic!("Invalid UTF-8 string"),
537 }
538}