1#![cfg_attr(not(feature = "std"), no_std)]
4
5#[cfg(all(
146 feature = "panic-handler",
147 not(feature = "std"),
148 not(test),
149 not(doctest)
150))]
151#[panic_handler]
152fn panic(_info: &core::panic::PanicInfo) -> ! {
153 loop {}
154}
155
156#[cfg(all(
159 feature = "panic-handler",
160 feature = "alloc",
161 not(feature = "std"),
162 not(test),
163 not(doctest)
164))]
165#[global_allocator]
166static ALLOCATOR: StubAllocator = StubAllocator;
167
168#[cfg(all(
169 feature = "panic-handler",
170 feature = "alloc",
171 not(feature = "std"),
172 not(test),
173 not(doctest)
174))]
175struct StubAllocator;
176
177#[cfg(all(
178 feature = "panic-handler",
179 feature = "alloc",
180 not(feature = "std"),
181 not(test),
182 not(doctest)
183))]
184unsafe impl core::alloc::GlobalAlloc for StubAllocator {
185 unsafe fn alloc(&self, _layout: core::alloc::Layout) -> *mut u8 {
186 core::ptr::null_mut()
187 }
188 unsafe fn dealloc(&self, _ptr: *mut u8, _layout: core::alloc::Layout) {}
189}
190
191use crate::crc16::consts::{
192 CRC16_ARC, CRC16_CDMA2000, CRC16_CMS, CRC16_DDS_110, CRC16_DECT_R, CRC16_DECT_X, CRC16_DNP,
193 CRC16_EN_13757, CRC16_GENIBUS, CRC16_GSM, CRC16_IBM_3740, CRC16_IBM_SDLC,
194 CRC16_ISO_IEC_14443_3_A, CRC16_KERMIT, CRC16_LJ1200, CRC16_M17, CRC16_MAXIM_DOW, CRC16_MCRF4XX,
195 CRC16_MODBUS, CRC16_NRSC_5, CRC16_OPENSAFETY_A, CRC16_OPENSAFETY_B, CRC16_PROFIBUS,
196 CRC16_RIELLO, CRC16_SPI_FUJITSU, CRC16_T10_DIF, CRC16_TELEDISK, CRC16_TMS37157, CRC16_UMTS,
197 CRC16_USB, CRC16_XMODEM,
198};
199
200use crate::crc32::consts::{
201 CRC32_AIXM, CRC32_AUTOSAR, CRC32_BASE91_D, CRC32_BZIP2, CRC32_CD_ROM_EDC, CRC32_CKSUM,
202 CRC32_ISCSI, CRC32_ISO_HDLC, CRC32_JAMCRC, CRC32_MEF, CRC32_MPEG_2, CRC32_XFER,
203};
204
205#[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))]
206#[cfg(feature = "std")]
207use crate::crc32::fusion;
208
209use crate::crc64::consts::{
210 CRC64_ECMA_182, CRC64_GO_ISO, CRC64_MS, CRC64_NVME, CRC64_REDIS, CRC64_WE, CRC64_XZ,
211};
212use crate::structs::Calculator;
213use crate::traits::CrcCalculator;
214#[cfg(feature = "alloc")]
215use digest::DynDigest;
216#[cfg(feature = "alloc")]
217use digest::InvalidBufferSize;
218
219#[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))]
220use crate::feature_detection::get_arch_ops;
221#[cfg(feature = "std")]
222use std::fs::File;
223#[cfg(feature = "std")]
224use std::io::{Read, Write};
225
226#[cfg(all(feature = "alloc", not(feature = "std")))]
227extern crate alloc;
228#[cfg(all(feature = "alloc", not(feature = "std")))]
229use alloc::boxed::Box;
230#[cfg(all(feature = "alloc", not(feature = "std")))]
231use alloc::string::String;
232
233mod algorithm;
234mod arch;
235mod cache;
236mod combine;
237mod consts;
238mod crc16;
239mod crc32;
240mod crc64;
241mod enums;
242mod feature_detection;
243#[cfg(feature = "ffi")]
244mod ffi;
245mod generate;
246mod structs;
247mod test;
248mod traits;
249
250#[derive(Debug, Clone, Copy, PartialEq)]
252pub enum CrcAlgorithm {
253 CrcCustom,
256 Crc16Arc,
257 Crc16Cdma2000,
258 Crc16Cms,
259 Crc16Dds110,
260 Crc16DectR,
261 Crc16DectX,
262 Crc16Dnp,
263 Crc16En13757,
264 Crc16Genibus,
265 Crc16Gsm,
266 Crc16Ibm3740,
267 Crc16IbmSdlc,
268 Crc16IsoIec144433A,
269 Crc16Kermit,
270 Crc16Lj1200,
271 Crc16M17,
272 Crc16MaximDow,
273 Crc16Mcrf4xx,
274 Crc16Modbus,
275 Crc16Nrsc5,
276 Crc16OpensafetyA,
277 Crc16OpensafetyB,
278 Crc16Profibus,
279 Crc16Riello,
280 Crc16SpiFujitsu,
281 Crc16T10Dif,
282 Crc16Teledisk,
283 Crc16Tms37157,
284 Crc16Umts,
285 Crc16Usb,
286 Crc16Xmodem,
287 Crc32Aixm,
288 Crc32Autosar,
289 Crc32Base91D,
290 Crc32Bzip2,
291 Crc32CdRomEdc,
292 Crc32Cksum,
293 #[deprecated(
294 since = "1.9.0",
295 note = "Use CrcCustom instead, which works with any supported width (16, 32, 64)"
296 )]
297 Crc32Custom, Crc32Iscsi,
299 Crc32IsoHdlc,
300 Crc32Jamcrc,
301 Crc32Mef,
302 Crc32Mpeg2,
303 Crc32Xfer,
304 #[deprecated(
305 since = "1.9.0",
306 note = "Use CrcCustom instead, which works with any supported width (16, 32, 64)"
307 )]
308 Crc64Custom, Crc64Ecma182,
310 Crc64GoIso,
311 Crc64Ms,
312 Crc64Nvme,
313 Crc64Redis,
314 Crc64We,
315 Crc64Xz,
316}
317
318#[derive(Clone, Copy, Debug, PartialEq)]
322pub enum CrcKeysStorage {
323 KeysFold256([u64; 23]),
325 KeysFutureTest([u64; 25]),
327}
328
329impl CrcKeysStorage {
330 #[inline(always)]
332 const fn get_key(self, index: usize) -> u64 {
333 match self {
334 CrcKeysStorage::KeysFold256(keys) => {
335 if index < 23 {
336 keys[index]
337 } else {
338 0
339 }
340 }
341 CrcKeysStorage::KeysFutureTest(keys) => {
342 if index < 25 {
343 keys[index]
344 } else {
345 0
346 }
347 }
348 }
349 }
350
351 #[inline(always)]
353 const fn key_count(self) -> usize {
354 match self {
355 CrcKeysStorage::KeysFold256(_) => 23,
356 CrcKeysStorage::KeysFutureTest(_) => 25,
357 }
358 }
359
360 #[inline(always)]
362 const fn from_keys_fold_256(keys: [u64; 23]) -> Self {
363 CrcKeysStorage::KeysFold256(keys)
364 }
365
366 #[inline(always)]
368 #[allow(dead_code)] const fn from_keys_fold_future_test(keys: [u64; 25]) -> Self {
370 CrcKeysStorage::KeysFutureTest(keys)
371 }
372
373 #[inline(always)]
377 pub fn to_keys_array_23(self) -> [u64; 23] {
378 match self {
379 CrcKeysStorage::KeysFold256(keys) => keys,
380 CrcKeysStorage::KeysFutureTest(keys) => {
381 let mut result = [0u64; 23];
382 result.copy_from_slice(&keys[..23]);
383 result
384 }
385 }
386 }
387}
388
389impl PartialEq<[u64; 23]> for CrcKeysStorage {
391 fn eq(&self, other: &[u64; 23]) -> bool {
392 self.to_keys_array_23() == *other
393 }
394}
395
396impl PartialEq<CrcKeysStorage> for [u64; 23] {
397 fn eq(&self, other: &CrcKeysStorage) -> bool {
398 *self == other.to_keys_array_23()
399 }
400}
401
402#[derive(Clone, Copy, Debug)]
404pub struct CrcParams {
405 pub algorithm: CrcAlgorithm,
406 pub name: &'static str,
407 pub width: u8,
408 pub poly: u64,
409 pub init: u64,
410 pub init_algorithm: u64,
422 pub refin: bool,
423 pub refout: bool,
424 pub xorout: u64,
425 pub check: u64,
426 pub keys: CrcKeysStorage,
427}
428
429type CalculatorFn = fn(
441 u64, &[u8], &CrcParams, ) -> u64;
445
446#[derive(Copy, Clone, Debug)]
452pub struct Digest {
453 state: u64,
455
456 amount: u64,
458
459 params: CrcParams,
461
462 calculator: CalculatorFn,
464}
465
466#[cfg(feature = "alloc")]
467impl DynDigest for Digest {
468 #[inline(always)]
469 fn update(&mut self, data: &[u8]) {
470 self.update(data);
471 }
472
473 #[inline(always)]
474 fn finalize_into(self, buf: &mut [u8]) -> Result<(), InvalidBufferSize> {
475 if buf.len() != self.output_size() {
476 return Err(InvalidBufferSize);
477 }
478
479 let result = self.finalize();
480 let be_bytes = result.to_be_bytes();
481 let start = 8 - self.output_size();
482 buf.copy_from_slice(&be_bytes[start..]);
483
484 Ok(())
485 }
486
487 #[inline(always)]
488 fn finalize_into_reset(&mut self, out: &mut [u8]) -> Result<(), InvalidBufferSize> {
489 if out.len() != self.output_size() {
490 return Err(InvalidBufferSize);
491 }
492 let result = self.finalize();
493 self.reset();
494 let be_bytes = result.to_be_bytes();
495 let start = 8 - self.output_size();
496 out.copy_from_slice(&be_bytes[start..]);
497 Ok(())
498 }
499
500 #[inline(always)]
501 fn reset(&mut self) {
502 self.reset();
503 }
504
505 #[inline(always)]
506 fn output_size(&self) -> usize {
507 self.params.width as usize / 8
508 }
509
510 fn box_clone(&self) -> Box<dyn DynDigest> {
511 Box::new(*self)
512 }
513}
514
515impl Digest {
516 #[inline(always)]
530 pub fn new(algorithm: CrcAlgorithm) -> Self {
531 let (calculator, params) = get_calculator_params(algorithm);
532
533 Self {
534 state: params.init_algorithm,
535 amount: 0,
536 params,
537 calculator,
538 }
539 }
540
541 #[inline(always)]
565 pub fn new_with_init_state(algorithm: CrcAlgorithm, init_state: u64) -> Self {
566 let (calculator, params) = get_calculator_params(algorithm);
567
568 Self {
569 state: init_state,
570 amount: 0,
571 params,
572 calculator,
573 }
574 }
575
576 #[inline(always)]
601 pub fn new_with_params(params: CrcParams) -> Self {
602 let calculator = Calculator::calculate as CalculatorFn;
603
604 Self {
605 state: params.init_algorithm,
606 amount: 0,
607 params,
608 calculator,
609 }
610 }
611
612 #[inline(always)]
614 pub fn update(&mut self, data: &[u8]) {
615 self.state = (self.calculator)(self.state, data, &self.params);
616 self.amount += data.len() as u64;
617 }
618
619 #[inline(always)]
621 pub fn finalize(&self) -> u64 {
622 self.state ^ self.params.xorout
623 }
624
625 #[inline(always)]
627 pub fn finalize_reset(&mut self) -> u64 {
628 let result = self.finalize();
629 self.reset();
630
631 result
632 }
633
634 #[inline(always)]
636 pub fn reset(&mut self) {
637 self.state = self.params.init_algorithm;
638 self.amount = 0;
639 }
640
641 #[inline(always)]
643 pub fn combine(&mut self, other: &Self) {
644 self.amount += other.amount;
645 let other_crc = other.finalize();
646
647 self.state = combine::checksums(
650 self.state ^ self.params.xorout,
651 other_crc,
652 other.amount,
653 &self.params,
654 ) ^ self.params.xorout;
655 }
656
657 #[inline(always)]
659 pub fn get_amount(&self) -> u64 {
660 self.amount
661 }
662
663 #[inline(always)]
680 pub fn get_state(&self) -> u64 {
681 self.state
682 }
683}
684
685#[cfg(feature = "std")]
686impl Write for Digest {
687 #[inline(always)]
688 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
689 self.update(buf);
690 Ok(buf.len())
691 }
692
693 #[inline(always)]
694 fn write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result<usize> {
695 let len: usize = bufs
696 .iter()
697 .map(|buf| {
698 self.update(buf);
699 buf.len()
700 })
701 .sum();
702
703 Ok(len)
704 }
705
706 #[inline(always)]
707 fn flush(&mut self) -> std::io::Result<()> {
708 Ok(())
709 }
710
711 #[inline(always)]
712 fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> {
713 self.update(buf);
714
715 Ok(())
716 }
717}
718
719#[inline]
728#[allow(deprecated)]
729pub fn checksum(algorithm: CrcAlgorithm, buf: &[u8]) -> u64 {
730 match algorithm {
732 CrcAlgorithm::Crc16Arc => {
733 Calculator::calculate(CRC16_ARC.init, buf, &CRC16_ARC) ^ CRC16_ARC.xorout
734 }
735 CrcAlgorithm::Crc16Cdma2000 => {
736 Calculator::calculate(CRC16_CDMA2000.init, buf, &CRC16_CDMA2000) ^ CRC16_CDMA2000.xorout
737 }
738 CrcAlgorithm::Crc16Cms => {
739 Calculator::calculate(CRC16_CMS.init, buf, &CRC16_CMS) ^ CRC16_CMS.xorout
740 }
741 CrcAlgorithm::Crc16Dds110 => {
742 Calculator::calculate(CRC16_DDS_110.init, buf, &CRC16_DDS_110) ^ CRC16_DDS_110.xorout
743 }
744 CrcAlgorithm::Crc16DectR => {
745 Calculator::calculate(CRC16_DECT_R.init, buf, &CRC16_DECT_R) ^ CRC16_DECT_R.xorout
746 }
747 CrcAlgorithm::Crc16DectX => {
748 Calculator::calculate(CRC16_DECT_X.init, buf, &CRC16_DECT_X) ^ CRC16_DECT_X.xorout
749 }
750 CrcAlgorithm::Crc16Dnp => {
751 Calculator::calculate(CRC16_DNP.init, buf, &CRC16_DNP) ^ CRC16_DNP.xorout
752 }
753 CrcAlgorithm::Crc16En13757 => {
754 Calculator::calculate(CRC16_EN_13757.init, buf, &CRC16_EN_13757) ^ CRC16_EN_13757.xorout
755 }
756 CrcAlgorithm::Crc16Genibus => {
757 Calculator::calculate(CRC16_GENIBUS.init, buf, &CRC16_GENIBUS) ^ CRC16_GENIBUS.xorout
758 }
759 CrcAlgorithm::Crc16Gsm => {
760 Calculator::calculate(CRC16_GSM.init, buf, &CRC16_GSM) ^ CRC16_GSM.xorout
761 }
762 CrcAlgorithm::Crc16Ibm3740 => {
763 Calculator::calculate(CRC16_IBM_3740.init, buf, &CRC16_IBM_3740) ^ CRC16_IBM_3740.xorout
764 }
765 CrcAlgorithm::Crc16IbmSdlc => {
766 Calculator::calculate(CRC16_IBM_SDLC.init, buf, &CRC16_IBM_SDLC) ^ CRC16_IBM_SDLC.xorout
767 }
768 CrcAlgorithm::Crc16IsoIec144433A => {
769 Calculator::calculate(
770 CRC16_ISO_IEC_14443_3_A.init_algorithm,
771 buf,
772 &CRC16_ISO_IEC_14443_3_A,
773 ) ^ CRC16_ISO_IEC_14443_3_A.xorout
774 }
775 CrcAlgorithm::Crc16Kermit => {
776 Calculator::calculate(CRC16_KERMIT.init, buf, &CRC16_KERMIT) ^ CRC16_KERMIT.xorout
777 }
778 CrcAlgorithm::Crc16Lj1200 => {
779 Calculator::calculate(CRC16_LJ1200.init, buf, &CRC16_LJ1200) ^ CRC16_LJ1200.xorout
780 }
781 CrcAlgorithm::Crc16M17 => {
782 Calculator::calculate(CRC16_M17.init, buf, &CRC16_M17) ^ CRC16_M17.xorout
783 }
784 CrcAlgorithm::Crc16MaximDow => {
785 Calculator::calculate(CRC16_MAXIM_DOW.init, buf, &CRC16_MAXIM_DOW)
786 ^ CRC16_MAXIM_DOW.xorout
787 }
788 CrcAlgorithm::Crc16Mcrf4xx => {
789 Calculator::calculate(CRC16_MCRF4XX.init, buf, &CRC16_MCRF4XX) ^ CRC16_MCRF4XX.xorout
790 }
791 CrcAlgorithm::Crc16Modbus => {
792 Calculator::calculate(CRC16_MODBUS.init, buf, &CRC16_MODBUS) ^ CRC16_MODBUS.xorout
793 }
794 CrcAlgorithm::Crc16Nrsc5 => {
795 Calculator::calculate(CRC16_NRSC_5.init, buf, &CRC16_NRSC_5) ^ CRC16_NRSC_5.xorout
796 }
797 CrcAlgorithm::Crc16OpensafetyA => {
798 Calculator::calculate(CRC16_OPENSAFETY_A.init, buf, &CRC16_OPENSAFETY_A)
799 ^ CRC16_OPENSAFETY_A.xorout
800 }
801 CrcAlgorithm::Crc16OpensafetyB => {
802 Calculator::calculate(CRC16_OPENSAFETY_B.init, buf, &CRC16_OPENSAFETY_B)
803 ^ CRC16_OPENSAFETY_B.xorout
804 }
805 CrcAlgorithm::Crc16Profibus => {
806 Calculator::calculate(CRC16_PROFIBUS.init, buf, &CRC16_PROFIBUS) ^ CRC16_PROFIBUS.xorout
807 }
808 CrcAlgorithm::Crc16Riello => {
809 Calculator::calculate(CRC16_RIELLO.init_algorithm, buf, &CRC16_RIELLO)
810 ^ CRC16_RIELLO.xorout
811 }
812 CrcAlgorithm::Crc16SpiFujitsu => {
813 Calculator::calculate(CRC16_SPI_FUJITSU.init, buf, &CRC16_SPI_FUJITSU)
814 ^ CRC16_SPI_FUJITSU.xorout
815 }
816 CrcAlgorithm::Crc16T10Dif => {
817 Calculator::calculate(CRC16_T10_DIF.init, buf, &CRC16_T10_DIF) ^ CRC16_T10_DIF.xorout
818 }
819 CrcAlgorithm::Crc16Teledisk => {
820 Calculator::calculate(CRC16_TELEDISK.init, buf, &CRC16_TELEDISK) ^ CRC16_TELEDISK.xorout
821 }
822 CrcAlgorithm::Crc16Tms37157 => {
823 Calculator::calculate(CRC16_TMS37157.init_algorithm, buf, &CRC16_TMS37157)
824 ^ CRC16_TMS37157.xorout
825 }
826 CrcAlgorithm::Crc16Umts => {
827 Calculator::calculate(CRC16_UMTS.init, buf, &CRC16_UMTS) ^ CRC16_UMTS.xorout
828 }
829 CrcAlgorithm::Crc16Usb => {
830 Calculator::calculate(CRC16_USB.init, buf, &CRC16_USB) ^ CRC16_USB.xorout
831 }
832 CrcAlgorithm::Crc16Xmodem => {
833 Calculator::calculate(CRC16_XMODEM.init, buf, &CRC16_XMODEM) ^ CRC16_XMODEM.xorout
834 }
835 CrcAlgorithm::Crc32Aixm => {
836 Calculator::calculate(CRC32_AIXM.init, buf, &CRC32_AIXM) ^ CRC32_AIXM.xorout
837 }
838 CrcAlgorithm::Crc32Autosar => {
839 Calculator::calculate(CRC32_AUTOSAR.init, buf, &CRC32_AUTOSAR) ^ CRC32_AUTOSAR.xorout
840 }
841 CrcAlgorithm::Crc32Base91D => {
842 Calculator::calculate(CRC32_BASE91_D.init, buf, &CRC32_BASE91_D) ^ CRC32_BASE91_D.xorout
843 }
844 CrcAlgorithm::Crc32Bzip2 => {
845 Calculator::calculate(CRC32_BZIP2.init, buf, &CRC32_BZIP2) ^ CRC32_BZIP2.xorout
846 }
847 CrcAlgorithm::Crc32CdRomEdc => {
848 Calculator::calculate(CRC32_CD_ROM_EDC.init, buf, &CRC32_CD_ROM_EDC)
849 ^ CRC32_CD_ROM_EDC.xorout
850 }
851 CrcAlgorithm::Crc32Cksum => {
852 Calculator::calculate(CRC32_CKSUM.init, buf, &CRC32_CKSUM) ^ CRC32_CKSUM.xorout
853 }
854 CrcAlgorithm::Crc32Custom => {
855 panic!("Custom CRC-32 requires parameters via CrcParams::new()")
856 }
857 CrcAlgorithm::Crc32Iscsi => {
858 crc32_iscsi_calculator(CRC32_ISCSI.init, buf, &CRC32_ISCSI) ^ CRC32_ISCSI.xorout
859 }
860 CrcAlgorithm::Crc32IsoHdlc => {
861 crc32_iso_hdlc_calculator(CRC32_ISO_HDLC.init, buf, &CRC32_ISO_HDLC)
862 ^ CRC32_ISO_HDLC.xorout
863 }
864 CrcAlgorithm::Crc32Jamcrc => {
865 Calculator::calculate(CRC32_JAMCRC.init, buf, &CRC32_JAMCRC) ^ CRC32_JAMCRC.xorout
866 }
867 CrcAlgorithm::Crc32Mef => {
868 Calculator::calculate(CRC32_MEF.init, buf, &CRC32_MEF) ^ CRC32_MEF.xorout
869 }
870 CrcAlgorithm::Crc32Mpeg2 => {
871 Calculator::calculate(CRC32_MPEG_2.init, buf, &CRC32_MPEG_2) ^ CRC32_MPEG_2.xorout
872 }
873 CrcAlgorithm::Crc32Xfer => {
874 Calculator::calculate(CRC32_XFER.init, buf, &CRC32_XFER) ^ CRC32_XFER.xorout
875 }
876 CrcAlgorithm::CrcCustom => {
877 panic!("Custom CRC requires parameters via CrcParams::new()")
878 }
879 CrcAlgorithm::Crc64Custom => {
880 panic!("Custom CRC-64 requires parameters via CrcParams::new()")
881 }
882 CrcAlgorithm::Crc64Ecma182 => {
883 Calculator::calculate(CRC64_ECMA_182.init, buf, &CRC64_ECMA_182) ^ CRC64_ECMA_182.xorout
884 }
885 CrcAlgorithm::Crc64GoIso => {
886 Calculator::calculate(CRC64_GO_ISO.init, buf, &CRC64_GO_ISO) ^ CRC64_GO_ISO.xorout
887 }
888 CrcAlgorithm::Crc64Ms => {
889 Calculator::calculate(CRC64_MS.init, buf, &CRC64_MS) ^ CRC64_MS.xorout
890 }
891 CrcAlgorithm::Crc64Nvme => {
892 Calculator::calculate(CRC64_NVME.init, buf, &CRC64_NVME) ^ CRC64_NVME.xorout
893 }
894 CrcAlgorithm::Crc64Redis => {
895 Calculator::calculate(CRC64_REDIS.init, buf, &CRC64_REDIS) ^ CRC64_REDIS.xorout
896 }
897 CrcAlgorithm::Crc64We => {
898 Calculator::calculate(CRC64_WE.init, buf, &CRC64_WE) ^ CRC64_WE.xorout
899 }
900 CrcAlgorithm::Crc64Xz => {
901 Calculator::calculate(CRC64_XZ.init, buf, &CRC64_XZ) ^ CRC64_XZ.xorout
902 }
903 }
904}
905
906pub fn checksum_with_params(params: CrcParams, buf: &[u8]) -> u64 {
929 let calculator = Calculator::calculate as CalculatorFn;
930
931 calculator(params.init, buf, ¶ms) ^ params.xorout
932}
933
934#[cfg(feature = "std")]
957#[inline(always)]
958pub fn checksum_file(
959 algorithm: CrcAlgorithm,
960 path: &str,
961 chunk_size: Option<usize>,
962) -> Result<u64, std::io::Error> {
963 checksum_file_with_digest(Digest::new(algorithm), path, chunk_size)
964}
965
966#[cfg(feature = "std")]
1000pub fn checksum_file_with_params(
1001 params: CrcParams,
1002 path: &str,
1003 chunk_size: Option<usize>,
1004) -> Result<u64, std::io::Error> {
1005 checksum_file_with_digest(Digest::new_with_params(params), path, chunk_size)
1006}
1007
1008#[cfg(feature = "std")]
1014fn checksum_file_with_digest(
1015 mut digest: Digest,
1016 path: &str,
1017 chunk_size: Option<usize>,
1018) -> Result<u64, std::io::Error> {
1019 let mut file = File::open(path)?;
1020
1021 let chunk_size = chunk_size.unwrap_or(524288);
1027
1028 let mut buf = vec![0; chunk_size];
1029
1030 while let Ok(n) = file.read(&mut buf) {
1031 if n == 0 {
1032 break;
1033 }
1034 digest.update(&buf[..n]);
1035 }
1036
1037 Ok(digest.finalize())
1038}
1039
1040#[inline(always)]
1053pub fn checksum_combine(
1054 algorithm: CrcAlgorithm,
1055 checksum1: u64,
1056 checksum2: u64,
1057 checksum2_len: u64,
1058) -> u64 {
1059 let params = get_calculator_params(algorithm).1;
1060
1061 combine::checksums(checksum1, checksum2, checksum2_len, ¶ms)
1062}
1063
1064pub fn checksum_combine_with_params(
1089 params: CrcParams,
1090 checksum1: u64,
1091 checksum2: u64,
1092 checksum2_len: u64,
1093) -> u64 {
1094 combine::checksums(checksum1, checksum2, checksum2_len, ¶ms)
1095}
1096
1097#[cfg(all(
1127 feature = "alloc",
1128 any(target_arch = "aarch64", target_arch = "x86", target_arch = "x86_64")
1129))]
1130pub fn get_calculator_target(_algorithm: CrcAlgorithm) -> String {
1131 let arch_ops = get_arch_ops();
1132 arch_ops.get_target_string()
1133}
1134
1135#[inline(always)]
1151pub fn crc32_iscsi(data: &[u8]) -> u32 {
1152 crc32_iscsi_calculator(CRC32_ISCSI.init, data, &CRC32_ISCSI) as u32 ^ CRC32_ISCSI.xorout as u32
1153}
1154
1155#[inline(always)]
1171pub fn crc32_iso_hdlc(data: &[u8]) -> u32 {
1172 crc32_iso_hdlc_calculator(CRC32_ISO_HDLC.init, data, &CRC32_ISO_HDLC) as u32
1173 ^ CRC32_ISO_HDLC.xorout as u32
1174}
1175
1176#[inline(always)]
1191pub fn crc64_nvme(data: &[u8]) -> u64 {
1192 Calculator::calculate(CRC64_NVME.init, data, &CRC64_NVME) ^ CRC64_NVME.xorout
1193}
1194
1195#[cfg(all(
1197 feature = "alloc",
1198 not(any(target_arch = "aarch64", target_arch = "x86", target_arch = "x86_64"))
1199))]
1200pub fn get_calculator_target(_algorithm: CrcAlgorithm) -> String {
1201 extern crate alloc;
1202 use alloc::string::ToString;
1203 "software-fallback-tables".to_string()
1204}
1205
1206#[inline(always)]
1208#[allow(deprecated)]
1209fn get_calculator_params(algorithm: CrcAlgorithm) -> (CalculatorFn, CrcParams) {
1210 match algorithm {
1211 CrcAlgorithm::Crc16Arc => (Calculator::calculate as CalculatorFn, CRC16_ARC),
1212 CrcAlgorithm::Crc16Cdma2000 => (Calculator::calculate as CalculatorFn, CRC16_CDMA2000),
1213 CrcAlgorithm::Crc16Cms => (Calculator::calculate as CalculatorFn, CRC16_CMS),
1214 CrcAlgorithm::Crc16Dds110 => (Calculator::calculate as CalculatorFn, CRC16_DDS_110),
1215 CrcAlgorithm::Crc16DectR => (Calculator::calculate as CalculatorFn, CRC16_DECT_R),
1216 CrcAlgorithm::Crc16DectX => (Calculator::calculate as CalculatorFn, CRC16_DECT_X),
1217 CrcAlgorithm::Crc16Dnp => (Calculator::calculate as CalculatorFn, CRC16_DNP),
1218 CrcAlgorithm::Crc16En13757 => (Calculator::calculate as CalculatorFn, CRC16_EN_13757),
1219 CrcAlgorithm::Crc16Genibus => (Calculator::calculate as CalculatorFn, CRC16_GENIBUS),
1220 CrcAlgorithm::Crc16Gsm => (Calculator::calculate as CalculatorFn, CRC16_GSM),
1221 CrcAlgorithm::Crc16Ibm3740 => (Calculator::calculate as CalculatorFn, CRC16_IBM_3740),
1222 CrcAlgorithm::Crc16IbmSdlc => (Calculator::calculate as CalculatorFn, CRC16_IBM_SDLC),
1223 CrcAlgorithm::Crc16IsoIec144433A => (
1224 Calculator::calculate as CalculatorFn,
1225 CRC16_ISO_IEC_14443_3_A,
1226 ),
1227 CrcAlgorithm::Crc16Kermit => (Calculator::calculate as CalculatorFn, CRC16_KERMIT),
1228 CrcAlgorithm::Crc16Lj1200 => (Calculator::calculate as CalculatorFn, CRC16_LJ1200),
1229 CrcAlgorithm::Crc16M17 => (Calculator::calculate as CalculatorFn, CRC16_M17),
1230 CrcAlgorithm::Crc16MaximDow => (Calculator::calculate as CalculatorFn, CRC16_MAXIM_DOW),
1231 CrcAlgorithm::Crc16Mcrf4xx => (Calculator::calculate as CalculatorFn, CRC16_MCRF4XX),
1232 CrcAlgorithm::Crc16Modbus => (Calculator::calculate as CalculatorFn, CRC16_MODBUS),
1233 CrcAlgorithm::Crc16Nrsc5 => (Calculator::calculate as CalculatorFn, CRC16_NRSC_5),
1234 CrcAlgorithm::Crc16OpensafetyA => {
1235 (Calculator::calculate as CalculatorFn, CRC16_OPENSAFETY_A)
1236 }
1237 CrcAlgorithm::Crc16OpensafetyB => {
1238 (Calculator::calculate as CalculatorFn, CRC16_OPENSAFETY_B)
1239 }
1240 CrcAlgorithm::Crc16Profibus => (Calculator::calculate as CalculatorFn, CRC16_PROFIBUS),
1241 CrcAlgorithm::Crc16Riello => (Calculator::calculate as CalculatorFn, CRC16_RIELLO),
1242 CrcAlgorithm::Crc16SpiFujitsu => (Calculator::calculate as CalculatorFn, CRC16_SPI_FUJITSU),
1243 CrcAlgorithm::Crc16T10Dif => (Calculator::calculate as CalculatorFn, CRC16_T10_DIF),
1244 CrcAlgorithm::Crc16Teledisk => (Calculator::calculate as CalculatorFn, CRC16_TELEDISK),
1245 CrcAlgorithm::Crc16Tms37157 => (Calculator::calculate as CalculatorFn, CRC16_TMS37157),
1246 CrcAlgorithm::Crc16Umts => (Calculator::calculate as CalculatorFn, CRC16_UMTS),
1247 CrcAlgorithm::Crc16Usb => (Calculator::calculate as CalculatorFn, CRC16_USB),
1248 CrcAlgorithm::Crc16Xmodem => (Calculator::calculate as CalculatorFn, CRC16_XMODEM),
1249 CrcAlgorithm::Crc32Aixm => (Calculator::calculate as CalculatorFn, CRC32_AIXM),
1250 CrcAlgorithm::Crc32Autosar => (Calculator::calculate as CalculatorFn, CRC32_AUTOSAR),
1251 CrcAlgorithm::Crc32Base91D => (Calculator::calculate as CalculatorFn, CRC32_BASE91_D),
1252 CrcAlgorithm::Crc32Bzip2 => (Calculator::calculate as CalculatorFn, CRC32_BZIP2),
1253 CrcAlgorithm::Crc32CdRomEdc => (Calculator::calculate as CalculatorFn, CRC32_CD_ROM_EDC),
1254 CrcAlgorithm::Crc32Cksum => (Calculator::calculate as CalculatorFn, CRC32_CKSUM),
1255 CrcAlgorithm::Crc32Custom => {
1256 panic!("Custom CRC-32 requires parameters via CrcParams::new()")
1257 }
1258 CrcAlgorithm::Crc32Iscsi => (crc32_iscsi_calculator as CalculatorFn, CRC32_ISCSI),
1259 CrcAlgorithm::Crc32IsoHdlc => (crc32_iso_hdlc_calculator as CalculatorFn, CRC32_ISO_HDLC),
1260 CrcAlgorithm::Crc32Jamcrc => (Calculator::calculate as CalculatorFn, CRC32_JAMCRC),
1261 CrcAlgorithm::Crc32Mef => (Calculator::calculate as CalculatorFn, CRC32_MEF),
1262 CrcAlgorithm::Crc32Mpeg2 => (Calculator::calculate as CalculatorFn, CRC32_MPEG_2),
1263 CrcAlgorithm::Crc32Xfer => (Calculator::calculate as CalculatorFn, CRC32_XFER),
1264 CrcAlgorithm::CrcCustom => {
1265 panic!("Custom CRC requires parameters via CrcParams::new()")
1266 }
1267 CrcAlgorithm::Crc64Custom => {
1268 panic!("Custom CRC-64 requires parameters via CrcParams::new()")
1269 }
1270 CrcAlgorithm::Crc64Ecma182 => (Calculator::calculate as CalculatorFn, CRC64_ECMA_182),
1271 CrcAlgorithm::Crc64GoIso => (Calculator::calculate as CalculatorFn, CRC64_GO_ISO),
1272 CrcAlgorithm::Crc64Ms => (Calculator::calculate as CalculatorFn, CRC64_MS),
1273 CrcAlgorithm::Crc64Nvme => (Calculator::calculate as CalculatorFn, CRC64_NVME),
1274 CrcAlgorithm::Crc64Redis => (Calculator::calculate as CalculatorFn, CRC64_REDIS),
1275 CrcAlgorithm::Crc64We => (Calculator::calculate as CalculatorFn, CRC64_WE),
1276 CrcAlgorithm::Crc64Xz => (Calculator::calculate as CalculatorFn, CRC64_XZ),
1277 }
1278}
1279
1280#[inline(always)]
1286fn crc32_iscsi_calculator(state: u64, data: &[u8], _params: &CrcParams) -> u64 {
1287 #[cfg(all(target_arch = "aarch64", feature = "std"))]
1288 {
1289 use crate::feature_detection::PerformanceTier;
1290
1291 let arch_ops = get_arch_ops();
1292 match arch_ops.get_tier() {
1293 PerformanceTier::AArch64AesSha3 | PerformanceTier::AArch64Aes => {
1294 return fusion::crc32_iscsi(state as u32, data) as u64;
1295 }
1296 _ => {}
1297 }
1298 }
1299
1300 #[cfg(all(any(target_arch = "x86_64", target_arch = "x86"), feature = "std"))]
1301 {
1302 use crate::feature_detection::PerformanceTier;
1303
1304 let arch_ops = get_arch_ops();
1305 match arch_ops.get_tier() {
1306 PerformanceTier::X86_64Avx512Vpclmulqdq
1307 | PerformanceTier::X86_64Avx512Pclmulqdq
1308 | PerformanceTier::X86_64SsePclmulqdq
1309 | PerformanceTier::X86SsePclmulqdq => {
1310 if is_x86_feature_detected!("sse4.2") {
1312 return fusion::crc32_iscsi(state as u32, data) as u64;
1313 }
1314 }
1315 _ => {}
1316 }
1317 }
1318
1319 Calculator::calculate(state, data, _params)
1320}
1321
1322#[inline(always)]
1329fn crc32_iso_hdlc_calculator(state: u64, data: &[u8], _params: &CrcParams) -> u64 {
1330 #[cfg(all(target_arch = "aarch64", feature = "std"))]
1331 {
1332 use crate::feature_detection::{get_arch_ops, PerformanceTier};
1333 let arch_ops = get_arch_ops();
1334
1335 match arch_ops.get_tier() {
1336 PerformanceTier::AArch64AesSha3 | PerformanceTier::AArch64Aes => {
1337 return fusion::crc32_iso_hdlc(state as u32, data) as u64;
1338 }
1339 _ => {}
1340 }
1341 }
1342
1343 Calculator::calculate(state, data, _params)
1344}
1345
1346#[cfg(test)]
1347mod lib {
1348 #![allow(unused)]
1349
1350 use super::*;
1351 use crate::test::consts::{TEST_ALL_CONFIGS, TEST_CHECK_STRING};
1352 use crate::test::enums::AnyCrcTestConfig;
1353 use cbindgen::Language::C;
1354 use cbindgen::Style::Both;
1355 use rand::{rng, Rng};
1356 use std::fs::{read, write};
1357
1358 #[test]
1359 fn test_checksum_check() {
1360 for config in TEST_ALL_CONFIGS {
1361 assert_eq!(
1362 checksum(config.get_algorithm(), TEST_CHECK_STRING),
1363 config.get_check()
1364 );
1365 }
1366 }
1367
1368 #[test]
1369 fn test_checksum_reference() {
1370 for config in TEST_ALL_CONFIGS {
1371 assert_eq!(
1372 checksum(config.get_algorithm(), TEST_CHECK_STRING),
1373 config.checksum_with_reference(TEST_CHECK_STRING)
1374 );
1375 }
1376 }
1377
1378 #[test]
1379 fn test_checksum_with_custom_params() {
1380 crate::cache::clear_cache();
1381
1382 assert_eq!(
1384 checksum_with_params(get_custom_crc32_reflected(), TEST_CHECK_STRING),
1385 CRC32_ISCSI.check,
1386 );
1387
1388 assert_eq!(
1390 checksum_with_params(get_custom_crc32_forward(), TEST_CHECK_STRING),
1391 CRC32_BZIP2.check,
1392 );
1393
1394 assert_eq!(
1396 checksum_with_params(get_custom_crc64_reflected(), TEST_CHECK_STRING),
1397 CRC64_NVME.check,
1398 );
1399
1400 assert_eq!(
1402 checksum_with_params(get_custom_crc64_forward(), TEST_CHECK_STRING),
1403 CRC64_ECMA_182.check,
1404 );
1405 }
1406
1407 #[test]
1408 fn test_get_custom_params() {
1409 crate::cache::clear_cache();
1410
1411 assert_eq!(
1412 checksum_with_params(get_custom_crc32_reflected(), TEST_CHECK_STRING),
1413 CRC32_ISCSI.check,
1414 );
1415
1416 assert_eq!(
1417 checksum_with_params(get_custom_crc32_forward(), TEST_CHECK_STRING),
1418 CRC32_BZIP2.check,
1419 );
1420
1421 assert_eq!(
1422 checksum_with_params(get_custom_crc64_reflected(), TEST_CHECK_STRING),
1423 CRC64_NVME.check,
1424 );
1425
1426 assert_eq!(
1427 checksum_with_params(get_custom_crc64_forward(), TEST_CHECK_STRING),
1428 CRC64_ECMA_182.check,
1429 );
1430 }
1431
1432 #[test]
1433 fn test_get_calculator_target_format() {
1434 let target = get_calculator_target(CrcAlgorithm::Crc32IsoHdlc);
1435
1436 assert!(!target.is_empty());
1438
1439 let valid_prefixes = ["aarch64-", "x86_64-", "x86-", "software-"];
1441 assert!(
1442 valid_prefixes
1443 .iter()
1444 .any(|prefix| target.starts_with(prefix)),
1445 "Target '{}' should start with a valid architecture prefix",
1446 target
1447 );
1448
1449 let parts: Vec<&str> = target.split('-').collect();
1451 assert!(
1452 parts.len() >= 3,
1453 "Target '{}' should have at least 3 parts: architecture-family-features",
1454 target
1455 );
1456 }
1457
1458 #[test]
1459 fn test_get_calculator_target_consistency() {
1460 let target1 = get_calculator_target(CrcAlgorithm::Crc32IsoHdlc);
1462 let target2 = get_calculator_target(CrcAlgorithm::Crc32Iscsi);
1463 let target3 = get_calculator_target(CrcAlgorithm::Crc64Nvme);
1464
1465 assert_eq!(
1466 target1, target2,
1467 "Target should be consistent across different CRC-32 algorithms"
1468 );
1469 assert_eq!(
1470 target1, target3,
1471 "Target should be consistent across CRC-32 and CRC-64 algorithms"
1472 );
1473 }
1474
1475 #[test]
1476 fn test_get_calculator_target_uses_cached_detection() {
1477 let target1 = get_calculator_target(CrcAlgorithm::Crc32IsoHdlc);
1482 let target2 = get_calculator_target(CrcAlgorithm::Crc32IsoHdlc);
1483
1484 assert_eq!(
1485 target1, target2,
1486 "Cached detection should return identical results"
1487 );
1488 }
1489
1490 #[test]
1491 fn test_digest_updates_check() {
1492 for config in TEST_ALL_CONFIGS {
1493 check_digest(Digest::new(config.get_algorithm()), config.get_check());
1494 }
1495 }
1496
1497 #[test]
1498 fn test_digest_updates_check_with_custom_params() {
1499 crate::cache::clear_cache();
1500
1501 check_digest(
1503 Digest::new_with_params(get_custom_crc32_reflected()),
1504 CRC32_ISCSI.check,
1505 );
1506
1507 check_digest(
1509 Digest::new_with_params(get_custom_crc32_forward()),
1510 CRC32_BZIP2.check,
1511 );
1512
1513 check_digest(
1515 Digest::new_with_params(get_custom_crc64_reflected()),
1516 CRC64_NVME.check,
1517 );
1518
1519 check_digest(
1521 Digest::new_with_params(get_custom_crc64_forward()),
1522 CRC64_ECMA_182.check,
1523 );
1524 }
1525
1526 fn check_digest(mut digest: Digest, check: u64) {
1527 digest.update(b"123");
1528 digest.update(b"456");
1529 digest.update(b"789");
1530 assert_eq!(digest.finalize(), check,);
1531 }
1532
1533 #[test]
1534 fn test_1024_length() {
1535 for config in TEST_ALL_CONFIGS {
1536 test_length(1024, config);
1537 }
1538 }
1539
1540 #[test]
1543 #[cfg_attr(miri, ignore)]
1544 fn test_small_all_lengths() {
1545 for config in TEST_ALL_CONFIGS {
1546 for len in 1..=255 {
1548 test_length(len, config);
1549 }
1550 }
1551 }
1552
1553 #[test]
1556 #[cfg_attr(miri, ignore)]
1557 fn test_medium_lengths() {
1558 for config in TEST_ALL_CONFIGS {
1559 for len in 256..=1024 {
1561 test_length(len, config);
1562 }
1563 }
1564 }
1565
1566 #[test]
1569 #[cfg_attr(miri, ignore)]
1570 fn test_large_lengths() {
1571 for config in TEST_ALL_CONFIGS {
1572 for len in 1048575..1048577 {
1574 test_length(len, config);
1575 }
1576 }
1577 }
1578
1579 fn test_length(length: usize, config: &AnyCrcTestConfig) {
1580 let mut data = vec![0u8; length];
1581 rng().fill(&mut data[..]);
1582
1583 let expected = config.checksum_with_reference(&data);
1585
1586 let result = checksum(config.get_algorithm(), &data);
1587
1588 assert_eq!(
1589 result,
1590 expected,
1591 "Failed for algorithm: {:?}, length: {}, expected: {:#x}, got: {:#x}",
1592 config.get_algorithm(),
1593 length,
1594 expected,
1595 result
1596 );
1597 }
1598
1599 #[test]
1600 fn test_combine() {
1601 for config in TEST_ALL_CONFIGS {
1602 let algorithm = config.get_algorithm();
1603 let check = config.get_check();
1604
1605 let checksum1 = checksum(algorithm, "1234".as_ref());
1607 let checksum2 = checksum(algorithm, "56789".as_ref());
1608
1609 assert_eq!(checksum_combine(algorithm, checksum1, checksum2, 5), check);
1611
1612 let mut digest1 = Digest::new(algorithm);
1614 digest1.update("1234".as_ref());
1615
1616 let mut digest2 = Digest::new(algorithm);
1617 digest2.update("56789".as_ref());
1618
1619 digest1.combine(&digest2);
1620
1621 assert_eq!(digest1.finalize(), check)
1622 }
1623 }
1624
1625 #[test]
1626 fn test_combine_with_custom_params() {
1627 crate::cache::clear_cache();
1628
1629 let crc32_params = get_custom_crc32_reflected();
1631 let checksum1 = checksum_with_params(crc32_params, "1234".as_ref());
1632 let checksum2 = checksum_with_params(crc32_params, "56789".as_ref());
1633 assert_eq!(
1634 checksum_combine_with_params(crc32_params, checksum1, checksum2, 5),
1635 CRC32_ISCSI.check,
1636 );
1637
1638 let crc32_params = get_custom_crc32_forward();
1640 let checksum1 = checksum_with_params(crc32_params, "1234".as_ref());
1641 let checksum2 = checksum_with_params(crc32_params, "56789".as_ref());
1642 assert_eq!(
1643 checksum_combine_with_params(crc32_params, checksum1, checksum2, 5),
1644 CRC32_BZIP2.check,
1645 );
1646
1647 let crc64_params = get_custom_crc64_reflected();
1649 let checksum1 = checksum_with_params(crc64_params, "1234".as_ref());
1650 let checksum2 = checksum_with_params(crc64_params, "56789".as_ref());
1651 assert_eq!(
1652 checksum_combine_with_params(crc64_params, checksum1, checksum2, 5),
1653 CRC64_NVME.check,
1654 );
1655
1656 let crc64_params = get_custom_crc64_forward();
1658 let checksum1 = checksum_with_params(crc64_params, "1234".as_ref());
1659 let checksum2 = checksum_with_params(crc64_params, "56789".as_ref());
1660 assert_eq!(
1661 checksum_combine_with_params(crc64_params, checksum1, checksum2, 5),
1662 CRC64_ECMA_182.check,
1663 );
1664 }
1665
1666 #[test]
1669 #[cfg_attr(miri, ignore)]
1670 fn test_checksum_file() {
1671 let test_file_path = "test/test_crc32_hash_file.bin";
1673 let data = vec![0u8; 1024 * 1024]; if let Err(e) = write(test_file_path, &data) {
1675 eprintln!("Skipping test due to write error: {}", e);
1676 return;
1677 }
1678
1679 for config in TEST_ALL_CONFIGS {
1680 let result = checksum_file(config.get_algorithm(), test_file_path, None).unwrap();
1681 assert_eq!(result, config.checksum_with_reference(&data));
1682 }
1683
1684 std::fs::remove_file(test_file_path).unwrap();
1685 }
1686
1687 #[test]
1690 #[cfg_attr(miri, ignore)]
1691 fn test_checksum_file_with_custom_params() {
1692 crate::cache::clear_cache();
1693
1694 let test_file_path = "test/test_crc32_hash_file_custom.bin";
1696 let data = vec![0u8; 1024 * 1024]; if let Err(e) = write(test_file_path, &data) {
1698 eprintln!("Skipping test due to write error: {}", e);
1699 return;
1700 }
1701
1702 check_file(
1704 get_custom_crc32_reflected(),
1705 test_file_path,
1706 CRC32_ISCSI.check,
1707 );
1708
1709 check_file(
1711 get_custom_crc32_forward(),
1712 test_file_path,
1713 CRC32_BZIP2.check,
1714 );
1715
1716 check_file(
1718 get_custom_crc64_reflected(),
1719 test_file_path,
1720 CRC64_NVME.check,
1721 );
1722
1723 check_file(
1725 get_custom_crc64_forward(),
1726 test_file_path,
1727 CRC64_ECMA_182.check,
1728 );
1729
1730 std::fs::remove_file(test_file_path).unwrap();
1731 }
1732
1733 fn check_file(params: CrcParams, file_path: &str, check: u64) {
1734 let result = checksum_file_with_params(params, file_path, None).unwrap();
1735 assert_eq!(result, check);
1736 }
1737
1738 #[test]
1741 #[cfg_attr(miri, ignore)]
1742 fn test_writer() {
1743 let test_file_path = "test/test_crc32_writer_file.bin";
1745 let data = vec![0u8; 1024 * 1024]; if let Err(e) = std::fs::write(test_file_path, &data) {
1747 eprintln!("Skipping test due to write error: {}", e);
1748 return;
1749 }
1750
1751 for config in TEST_ALL_CONFIGS {
1752 let mut digest = Digest::new(config.get_algorithm());
1753 let mut file = File::open(test_file_path).unwrap();
1754 std::io::copy(&mut file, &mut digest).unwrap();
1755 assert_eq!(digest.finalize(), config.checksum_with_reference(&data));
1756 }
1757
1758 std::fs::remove_file(test_file_path).unwrap();
1759 }
1760 #[test]
1761 fn test_digest_reset() {
1762 for config in TEST_ALL_CONFIGS {
1763 let mut digest = Digest::new(config.get_algorithm());
1764 digest.update(b"42");
1765 digest.reset();
1766 digest.update(TEST_CHECK_STRING);
1767 assert_eq!(digest.finalize(), config.get_check());
1768 }
1769 }
1770
1771 #[test]
1772 fn test_digest_finalize_reset() {
1773 for config in TEST_ALL_CONFIGS {
1774 let check = config.get_check();
1775
1776 let mut digest = Digest::new(config.get_algorithm());
1777 digest.update(TEST_CHECK_STRING);
1778 assert_eq!(digest.finalize_reset(), check);
1779
1780 digest.update(TEST_CHECK_STRING);
1781 assert_eq!(digest.finalize(), check);
1782 }
1783 }
1784
1785 #[test]
1786 fn test_digest_finalize_into() {
1787 for config in TEST_ALL_CONFIGS {
1788 let mut digest = Digest::new(config.get_algorithm());
1789 digest.update(TEST_CHECK_STRING);
1790
1791 match digest.params.width {
1792 16 => {
1793 let mut output = [0u8; 2];
1794 digest.finalize_into(&mut output).unwrap();
1795 let result = u16::from_be_bytes(output) as u64;
1796 assert_eq!(result, config.get_check());
1797 }
1798 32 => {
1799 let mut output = [0u8; 4];
1800 digest.finalize_into(&mut output).unwrap();
1801 let result = u32::from_be_bytes(output) as u64;
1802 assert_eq!(result, config.get_check());
1803 }
1804 64 => {
1805 let mut output = [0u8; 8];
1806 digest.finalize_into(&mut output).unwrap();
1807 let result = u64::from_be_bytes(output);
1808 assert_eq!(result, config.get_check());
1809 }
1810 _ => panic!("Unsupported CRC width"),
1811 }
1812 }
1813 }
1814
1815 #[test]
1816 fn test_digest_finalize_into_reset() {
1817 for config in TEST_ALL_CONFIGS {
1818 let mut digest = Digest::new(config.get_algorithm());
1819 digest.update(TEST_CHECK_STRING);
1820
1821 let mut output: Vec<u8> = match digest.params.width {
1822 16 => vec![0u8; 2],
1823 32 => vec![0u8; 4],
1824 64 => vec![0u8; 8],
1825 _ => panic!("Unsupported CRC width"),
1826 };
1827
1828 digest.finalize_into_reset(&mut output).unwrap();
1829 let result = match output.len() {
1830 2 => u16::from_be_bytes(output.try_into().unwrap()) as u64,
1831 4 => u32::from_be_bytes(output.try_into().unwrap()) as u64,
1832 8 => u64::from_be_bytes(output.try_into().unwrap()),
1833 _ => panic!("Unsupported CRC width"),
1834 };
1835 assert_eq!(result, config.get_check());
1836
1837 digest.update(TEST_CHECK_STRING);
1838 assert_eq!(digest.finalize(), config.get_check());
1839 }
1840 }
1841
1842 #[test]
1844 #[cfg_attr(miri, ignore)]
1845 fn test_ffi_header() -> Result<(), String> {
1846 #[cfg(target_os = "windows")]
1847 {
1848 eprintln!("Skipping test on Windows");
1850
1851 return Ok(());
1852 }
1853
1854 const HEADER: &str = "libcrc_fast.h";
1855
1856 let crate_dir = std::env::var("CARGO_MANIFEST_DIR").map_err(|error| error.to_string())?;
1857
1858 let mut expected = Vec::new();
1859 cbindgen::Builder::new()
1860 .with_crate(crate_dir)
1861 .with_include_guard("CRC_FAST_H")
1862 .with_header("/* crc_fast library C/C++ API - Copyright 2025 Don MacAskill */\n/* This header is auto-generated. Do not edit directly. */\n")
1863 .exclude_item("crc32_iscsi_impl")
1865 .exclude_item("crc32_iso_hdlc_impl")
1866 .exclude_item("get_iscsi_target")
1867 .exclude_item("get_iso_hdlc_target")
1868 .exclude_item("ISO_HDLC_TARGET")
1869 .exclude_item("ISCSI_TARGET")
1870 .exclude_item("CrcParams")
1871 .rename_item("Digest", "CrcFastDigest")
1872 .with_style(Both)
1873 .with_language(C)
1875 .with_cpp_compat(true)
1877 .generate()
1878 .map_err(|error| error.to_string())?
1879 .write(&mut expected);
1880
1881 let header_content = String::from_utf8(expected).map_err(|error| error.to_string())?;
1884
1885 let regex = regex::Regex::new(r"\n{3,}").map_err(|error| error.to_string())?;
1887 let cleaned_content = regex.replace_all(&header_content, "\n\n").to_string();
1888
1889 expected = cleaned_content.into_bytes();
1891
1892 let actual = read(HEADER).map_err(|error| error.to_string())?;
1893
1894 if expected != actual {
1895 write(HEADER, expected).map_err(|error| error.to_string())?;
1896 return Err(format!(
1897 "{HEADER} is not up-to-date, commit the generated file and try again"
1898 ));
1899 }
1900
1901 Ok(())
1902 }
1903
1904 fn get_custom_crc32_reflected() -> CrcParams {
1905 CrcParams::new(
1906 "Custom CRC-32/ISCSI",
1907 32,
1908 CRC32_ISCSI.poly,
1909 CRC32_ISCSI.init,
1910 CRC32_ISCSI.refin,
1911 CRC32_ISCSI.xorout,
1912 CRC32_ISCSI.check,
1913 )
1914 }
1915
1916 fn get_custom_crc32_forward() -> CrcParams {
1917 CrcParams::new(
1918 "Custom CRC-32/BZIP2",
1919 32,
1920 CRC32_BZIP2.poly,
1921 CRC32_BZIP2.init,
1922 CRC32_BZIP2.refin,
1923 CRC32_BZIP2.xorout,
1924 CRC32_BZIP2.check,
1925 )
1926 }
1927
1928 fn get_custom_crc64_reflected() -> CrcParams {
1929 CrcParams::new(
1930 "Custom CRC-64/NVME",
1931 64,
1932 CRC64_NVME.poly,
1933 CRC64_NVME.init,
1934 CRC64_NVME.refin,
1935 CRC64_NVME.xorout,
1936 CRC64_NVME.check,
1937 )
1938 }
1939
1940 fn get_custom_crc64_forward() -> CrcParams {
1941 CrcParams::new(
1942 "Custom CRC-64/ECMA-182",
1943 64,
1944 CRC64_ECMA_182.poly,
1945 CRC64_ECMA_182.init,
1946 CRC64_ECMA_182.refin,
1947 CRC64_ECMA_182.xorout,
1948 CRC64_ECMA_182.check,
1949 )
1950 }
1951
1952 #[test]
1953 #[allow(clippy::needless_range_loop)] fn test_crc_keys_storage_fold_256() {
1955 let test_keys = [
1956 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
1957 ];
1958 let storage = CrcKeysStorage::from_keys_fold_256(test_keys);
1959
1960 for i in 0..23 {
1962 assert_eq!(storage.get_key(i), test_keys[i]);
1963 }
1964
1965 assert_eq!(storage.get_key(23), 0);
1967 assert_eq!(storage.get_key(24), 0);
1968 assert_eq!(storage.get_key(100), 0);
1969
1970 assert_eq!(storage.key_count(), 23);
1972 }
1973
1974 #[test]
1975 #[allow(clippy::needless_range_loop)] fn test_crc_keys_storage_future_test() {
1977 let test_keys = [
1978 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
1979 25,
1980 ];
1981 let storage = CrcKeysStorage::from_keys_fold_future_test(test_keys);
1982
1983 for i in 0..25 {
1985 assert_eq!(storage.get_key(i), test_keys[i]);
1986 }
1987
1988 assert_eq!(storage.get_key(25), 0);
1990 assert_eq!(storage.get_key(26), 0);
1991 assert_eq!(storage.get_key(100), 0);
1992
1993 assert_eq!(storage.key_count(), 25);
1995 }
1996
1997 #[test]
1998 #[allow(clippy::needless_range_loop)] fn test_crc_params_safe_accessors() {
2000 let test_keys = [
2002 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
2003 ];
2004 let params = CrcParams {
2005 algorithm: CrcAlgorithm::Crc32IsoHdlc,
2006 name: "test",
2007 width: 32,
2008 poly: 0x04C11DB7,
2009 init: 0xFFFFFFFF,
2010 init_algorithm: 0xFFFFFFFF,
2011 refin: true,
2012 refout: true,
2013 xorout: 0xFFFFFFFF,
2014 check: 0xCBF43926,
2015 keys: CrcKeysStorage::from_keys_fold_256(test_keys),
2016 };
2017
2018 for i in 0..23 {
2020 assert_eq!(params.get_key(i), test_keys[i]);
2021 assert_eq!(params.get_key_checked(i), Some(test_keys[i]));
2022 }
2023
2024 assert_eq!(params.get_key(23), 0);
2026 assert_eq!(params.get_key(24), 0);
2027 assert_eq!(params.get_key(100), 0);
2028
2029 assert_eq!(params.get_key_checked(23), None);
2030 assert_eq!(params.get_key_checked(24), None);
2031 assert_eq!(params.get_key_checked(100), None);
2032
2033 assert_eq!(params.key_count(), 23);
2035 }
2036
2037 #[test]
2038 fn test_crc_keys_storage_const_constructors() {
2039 const TEST_KEYS_23: [u64; 23] = [1; 23];
2041 const TEST_KEYS_25: [u64; 25] = [2; 25];
2042
2043 const STORAGE_256: CrcKeysStorage = CrcKeysStorage::from_keys_fold_256(TEST_KEYS_23);
2044 const STORAGE_FUTURE: CrcKeysStorage =
2045 CrcKeysStorage::from_keys_fold_future_test(TEST_KEYS_25);
2046
2047 assert_eq!(STORAGE_256.get_key(0), 1);
2049 assert_eq!(STORAGE_256.key_count(), 23);
2050
2051 assert_eq!(STORAGE_FUTURE.get_key(0), 2);
2052 assert_eq!(STORAGE_FUTURE.key_count(), 25);
2053 }
2054
2055 #[test]
2056 fn test_crc_keys_storage_bounds_safety() {
2057 let storage_256 = CrcKeysStorage::from_keys_fold_256([42; 23]);
2058 let storage_future = CrcKeysStorage::from_keys_fold_future_test([84; 25]);
2059
2060 assert_eq!(storage_256.get_key(22), 42); assert_eq!(storage_256.get_key(23), 0); assert_eq!(storage_future.get_key(24), 84); assert_eq!(storage_future.get_key(25), 0); assert_eq!(storage_256.get_key(usize::MAX), 0);
2069 assert_eq!(storage_future.get_key(usize::MAX), 0);
2070 }
2071}