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