1use crate::crc32::consts::{
134 CRC32_AIXM, CRC32_AUTOSAR, CRC32_BASE91_D, CRC32_BZIP2, CRC32_CD_ROM_EDC, CRC32_CKSUM,
135 CRC32_ISCSI, CRC32_ISO_HDLC, CRC32_JAMCRC, CRC32_MEF, CRC32_MPEG_2, CRC32_XFER,
136};
137
138#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
139use crate::crc32::fusion;
140
141use crate::crc64::consts::{
142 CRC64_ECMA_182, CRC64_GO_ISO, CRC64_MS, CRC64_NVME, CRC64_REDIS, CRC64_WE, CRC64_XZ,
143};
144use crate::structs::Calculator;
145use crate::traits::CrcCalculator;
146use digest::{DynDigest, InvalidBufferSize};
147use std::fs::File;
148use std::io::{Read, Write};
149
150mod algorithm;
151mod arch;
152mod cache;
153mod combine;
154mod consts;
155mod crc32;
156mod crc64;
157mod enums;
158mod ffi;
159mod generate;
160mod structs;
161mod test;
162mod traits;
163
164#[derive(Debug, Clone, Copy, PartialEq)]
166pub enum CrcAlgorithm {
167 Crc32Aixm,
168 Crc32Autosar,
169 Crc32Base91D,
170 Crc32Bzip2,
171 Crc32CdRomEdc,
172 Crc32Cksum,
173 Crc32Custom, Crc32Iscsi,
175 Crc32IsoHdlc,
176 Crc32Jamcrc,
177 Crc32Mef,
178 Crc32Mpeg2,
179 Crc32Xfer,
180 Crc64Custom, Crc64Ecma182,
182 Crc64GoIso,
183 Crc64Ms,
184 Crc64Nvme,
185 Crc64Redis,
186 Crc64We,
187 Crc64Xz,
188}
189
190#[derive(Clone, Copy, Debug, PartialEq)]
194pub enum CrcKeysStorage {
195 KeysFold256([u64; 23]),
197 KeysFutureTest([u64; 25]),
199}
200
201impl CrcKeysStorage {
202 #[inline(always)]
204 const fn get_key(self, index: usize) -> u64 {
205 match self {
206 CrcKeysStorage::KeysFold256(keys) => {
207 if index < 23 {
208 keys[index]
209 } else {
210 0
211 }
212 }
213 CrcKeysStorage::KeysFutureTest(keys) => {
214 if index < 25 {
215 keys[index]
216 } else {
217 0
218 }
219 }
220 }
221 }
222
223 #[inline(always)]
225 const fn key_count(self) -> usize {
226 match self {
227 CrcKeysStorage::KeysFold256(_) => 23,
228 CrcKeysStorage::KeysFutureTest(_) => 25,
229 }
230 }
231
232 #[inline(always)]
234 const fn from_keys_fold_256(keys: [u64; 23]) -> Self {
235 CrcKeysStorage::KeysFold256(keys)
236 }
237
238 #[inline(always)]
240 #[allow(dead_code)] const fn from_keys_fold_future_test(keys: [u64; 25]) -> Self {
242 CrcKeysStorage::KeysFutureTest(keys)
243 }
244
245 #[inline(always)]
249 pub fn to_keys_array_23(self) -> [u64; 23] {
250 match self {
251 CrcKeysStorage::KeysFold256(keys) => keys,
252 CrcKeysStorage::KeysFutureTest(keys) => {
253 let mut result = [0u64; 23];
254 result.copy_from_slice(&keys[..23]);
255 result
256 }
257 }
258 }
259}
260
261impl PartialEq<[u64; 23]> for CrcKeysStorage {
263 fn eq(&self, other: &[u64; 23]) -> bool {
264 self.to_keys_array_23() == *other
265 }
266}
267
268impl PartialEq<CrcKeysStorage> for [u64; 23] {
269 fn eq(&self, other: &CrcKeysStorage) -> bool {
270 *self == other.to_keys_array_23()
271 }
272}
273
274#[derive(Clone, Copy, Debug)]
276pub struct CrcParams {
277 pub algorithm: CrcAlgorithm,
278 pub name: &'static str,
279 pub width: u8,
280 pub poly: u64,
281 pub init: u64,
282 pub refin: bool,
283 pub refout: bool,
284 pub xorout: u64,
285 pub check: u64,
286 pub keys: CrcKeysStorage,
287}
288
289type CalculatorFn = fn(
298 u64, &[u8], CrcParams, ) -> u64;
302
303#[derive(Copy, Clone, Debug)]
309pub struct Digest {
310 state: u64,
312
313 amount: u64,
315
316 params: CrcParams,
318
319 calculator: CalculatorFn,
321}
322
323impl DynDigest for Digest {
324 #[inline(always)]
325 fn update(&mut self, data: &[u8]) {
326 self.update(data);
327 }
328
329 #[inline(always)]
330 fn finalize_into(self, buf: &mut [u8]) -> Result<(), InvalidBufferSize> {
331 if buf.len() != self.output_size() {
332 return Err(InvalidBufferSize);
333 }
334
335 let result = self.finalize();
336 let bytes = if self.output_size() == 4 {
337 result.to_be_bytes()[4..].to_vec() } else {
339 result.to_be_bytes().to_vec() };
341 buf.copy_from_slice(&bytes[..self.output_size()]);
342
343 Ok(())
344 }
345
346 #[inline(always)]
347 fn finalize_into_reset(&mut self, out: &mut [u8]) -> Result<(), InvalidBufferSize> {
348 if out.len() != self.output_size() {
349 return Err(InvalidBufferSize);
350 }
351 let result = self.finalize();
352 self.reset();
353 let bytes = if self.output_size() == 4 {
354 result.to_be_bytes()[4..].to_vec() } else {
356 result.to_be_bytes().to_vec() };
358 out.copy_from_slice(&bytes[..self.output_size()]);
359 Ok(())
360 }
361
362 #[inline(always)]
363 fn reset(&mut self) {
364 self.reset();
365 }
366
367 #[inline(always)]
368 fn output_size(&self) -> usize {
369 self.params.width as usize / 8
370 }
371
372 fn box_clone(&self) -> Box<dyn DynDigest> {
373 Box::new(*self)
374 }
375}
376
377impl Digest {
378 #[inline(always)]
380 pub fn new(algorithm: CrcAlgorithm) -> Self {
381 let (calculator, params) = get_calculator_params(algorithm);
382
383 Self {
384 state: params.init,
385 amount: 0,
386 params,
387 calculator,
388 }
389 }
390
391 #[inline(always)]
416 pub fn new_with_params(params: CrcParams) -> Self {
417 let calculator = Calculator::calculate as CalculatorFn;
418
419 Self {
420 state: params.init,
421 amount: 0,
422 params,
423 calculator,
424 }
425 }
426
427 #[inline(always)]
429 pub fn update(&mut self, data: &[u8]) {
430 self.state = (self.calculator)(self.state, data, self.params);
431 self.amount += data.len() as u64;
432 }
433
434 #[inline(always)]
436 pub fn finalize(&self) -> u64 {
437 self.state ^ self.params.xorout
438 }
439
440 #[inline(always)]
442 pub fn finalize_reset(&mut self) -> u64 {
443 let result = self.finalize();
444 self.reset();
445
446 result
447 }
448
449 #[inline(always)]
451 pub fn reset(&mut self) {
452 self.state = self.params.init;
453 self.amount = 0;
454 }
455
456 #[inline(always)]
458 pub fn combine(&mut self, other: &Self) {
459 self.amount += other.amount;
460 let other_crc = other.finalize();
461
462 self.state = combine::checksums(
465 self.state ^ self.params.xorout,
466 other_crc,
467 other.amount,
468 self.params,
469 ) ^ self.params.xorout;
470 }
471
472 #[inline(always)]
474 pub fn get_amount(&self) -> u64 {
475 self.amount
476 }
477}
478
479impl Write for Digest {
480 #[inline(always)]
481 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
482 self.update(buf);
483 Ok(buf.len())
484 }
485
486 #[inline(always)]
487 fn write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result<usize> {
488 let len: usize = bufs
489 .iter()
490 .map(|buf| {
491 self.update(buf);
492 buf.len()
493 })
494 .sum();
495
496 Ok(len)
497 }
498
499 #[inline(always)]
500 fn flush(&mut self) -> std::io::Result<()> {
501 Ok(())
502 }
503
504 #[inline(always)]
505 fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> {
506 self.update(buf);
507
508 Ok(())
509 }
510}
511
512#[inline(always)]
521pub fn checksum(algorithm: CrcAlgorithm, buf: &[u8]) -> u64 {
522 let (calculator, params) = get_calculator_params(algorithm);
523
524 calculator(params.init, buf, params) ^ params.xorout
525}
526
527pub fn checksum_with_params(params: CrcParams, buf: &[u8]) -> u64 {
550 let calculator = Calculator::calculate as CalculatorFn;
551
552 calculator(params.init, buf, params) ^ params.xorout
553}
554
555#[inline(always)]
578pub fn checksum_file(
579 algorithm: CrcAlgorithm,
580 path: &str,
581 chunk_size: Option<usize>,
582) -> Result<u64, std::io::Error> {
583 checksum_file_with_digest(Digest::new(algorithm), path, chunk_size)
584}
585
586pub fn checksum_file_with_params(
620 params: CrcParams,
621 path: &str,
622 chunk_size: Option<usize>,
623) -> Result<u64, std::io::Error> {
624 checksum_file_with_digest(Digest::new_with_params(params), path, chunk_size)
625}
626
627fn checksum_file_with_digest(
633 mut digest: Digest,
634 path: &str,
635 chunk_size: Option<usize>,
636) -> Result<u64, std::io::Error> {
637 let mut file = File::open(path)?;
638
639 let chunk_size = chunk_size.unwrap_or(524288);
645
646 let mut buf = vec![0; chunk_size];
647
648 while let Ok(n) = file.read(&mut buf) {
649 if n == 0 {
650 break;
651 }
652 digest.update(&buf[..n]);
653 }
654
655 Ok(digest.finalize())
656}
657
658#[inline(always)]
671pub fn checksum_combine(
672 algorithm: CrcAlgorithm,
673 checksum1: u64,
674 checksum2: u64,
675 checksum2_len: u64,
676) -> u64 {
677 let params = get_calculator_params(algorithm).1;
678
679 combine::checksums(checksum1, checksum2, checksum2_len, params)
680}
681
682pub fn checksum_combine_with_params(
707 params: CrcParams,
708 checksum1: u64,
709 checksum2: u64,
710 checksum2_len: u64,
711) -> u64 {
712 combine::checksums(checksum1, checksum2, checksum2_len, params)
713}
714
715pub fn get_calculator_target(_algorithm: CrcAlgorithm) -> String {
727 arch::get_target()
728}
729
730#[inline(always)]
732fn get_calculator_params(algorithm: CrcAlgorithm) -> (CalculatorFn, CrcParams) {
733 match algorithm {
734 CrcAlgorithm::Crc32Aixm => (Calculator::calculate as CalculatorFn, CRC32_AIXM),
735 CrcAlgorithm::Crc32Autosar => (Calculator::calculate as CalculatorFn, CRC32_AUTOSAR),
736 CrcAlgorithm::Crc32Base91D => (Calculator::calculate as CalculatorFn, CRC32_BASE91_D),
737 CrcAlgorithm::Crc32Bzip2 => (Calculator::calculate as CalculatorFn, CRC32_BZIP2),
738 CrcAlgorithm::Crc32CdRomEdc => (Calculator::calculate as CalculatorFn, CRC32_CD_ROM_EDC),
739 CrcAlgorithm::Crc32Cksum => (Calculator::calculate as CalculatorFn, CRC32_CKSUM),
740 CrcAlgorithm::Crc32Custom => {
741 panic!("Custom CRC-32 requires parameters via CrcParams::new()")
742 }
743 CrcAlgorithm::Crc32Iscsi => (crc32_iscsi_calculator as CalculatorFn, CRC32_ISCSI),
744 CrcAlgorithm::Crc32IsoHdlc => (crc32_iso_hdlc_calculator as CalculatorFn, CRC32_ISO_HDLC),
745 CrcAlgorithm::Crc32Jamcrc => (Calculator::calculate as CalculatorFn, CRC32_JAMCRC),
746 CrcAlgorithm::Crc32Mef => (Calculator::calculate as CalculatorFn, CRC32_MEF),
747 CrcAlgorithm::Crc32Mpeg2 => (Calculator::calculate as CalculatorFn, CRC32_MPEG_2),
748 CrcAlgorithm::Crc32Xfer => (Calculator::calculate as CalculatorFn, CRC32_XFER),
749 CrcAlgorithm::Crc64Custom => {
750 panic!("Custom CRC-64 requires parameters via CrcParams::new()")
751 }
752 CrcAlgorithm::Crc64Ecma182 => (Calculator::calculate as CalculatorFn, CRC64_ECMA_182),
753 CrcAlgorithm::Crc64GoIso => (Calculator::calculate as CalculatorFn, CRC64_GO_ISO),
754 CrcAlgorithm::Crc64Ms => (Calculator::calculate as CalculatorFn, CRC64_MS),
755 CrcAlgorithm::Crc64Nvme => (Calculator::calculate as CalculatorFn, CRC64_NVME),
756 CrcAlgorithm::Crc64Redis => (Calculator::calculate as CalculatorFn, CRC64_REDIS),
757 CrcAlgorithm::Crc64We => (Calculator::calculate as CalculatorFn, CRC64_WE),
758 CrcAlgorithm::Crc64Xz => (Calculator::calculate as CalculatorFn, CRC64_XZ),
759 }
760}
761
762#[inline(always)]
767fn crc32_iscsi_calculator(state: u64, data: &[u8], _params: CrcParams) -> u64 {
768 #[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))]
770 return fusion::crc32_iscsi(state as u32, data) as u64;
771
772 #[cfg(all(not(target_arch = "aarch64"), not(target_arch = "x86_64")))]
773 Calculator::calculate(state, data, _params)
775}
776
777#[inline(always)]
783fn crc32_iso_hdlc_calculator(state: u64, data: &[u8], _params: CrcParams) -> u64 {
784 #[cfg(target_arch = "aarch64")]
786 return fusion::crc32_iso_hdlc(state as u32, data) as u64;
787
788 #[cfg(not(target_arch = "aarch64"))]
791 Calculator::calculate(state, data, _params)
792}
793
794#[cfg(test)]
795mod lib {
796 #![allow(unused)]
797
798 use super::*;
799 use crate::test::consts::{TEST_ALL_CONFIGS, TEST_CHECK_STRING};
800 use crate::test::enums::AnyCrcTestConfig;
801 use cbindgen::Language::C;
802 use cbindgen::Style::Both;
803 use rand::{rng, Rng};
804 use std::fs::{read, write};
805
806 #[test]
807 fn test_checksum_check() {
808 for config in TEST_ALL_CONFIGS {
809 assert_eq!(
810 checksum(config.get_algorithm(), TEST_CHECK_STRING),
811 config.get_check()
812 );
813 }
814 }
815
816 #[test]
817 fn test_checksum_reference() {
818 for config in TEST_ALL_CONFIGS {
819 assert_eq!(
820 checksum(config.get_algorithm(), TEST_CHECK_STRING),
821 config.checksum_with_reference(TEST_CHECK_STRING)
822 );
823 }
824 }
825
826 #[test]
827 fn test_checksum_with_custom_params() {
828 crate::cache::clear_cache();
829
830 assert_eq!(
832 checksum_with_params(get_custom_crc32_reflected(), TEST_CHECK_STRING),
833 CRC32_ISCSI.check,
834 );
835
836 assert_eq!(
838 checksum_with_params(get_custom_crc32_forward(), TEST_CHECK_STRING),
839 CRC32_BZIP2.check,
840 );
841
842 assert_eq!(
844 checksum_with_params(get_custom_crc64_reflected(), TEST_CHECK_STRING),
845 CRC64_NVME.check,
846 );
847
848 assert_eq!(
850 checksum_with_params(get_custom_crc64_forward(), TEST_CHECK_STRING),
851 CRC64_ECMA_182.check,
852 );
853 }
854
855 #[test]
856 fn test_get_custom_params() {
857 crate::cache::clear_cache();
858
859 assert_eq!(
860 checksum_with_params(get_custom_crc32_reflected(), TEST_CHECK_STRING),
861 CRC32_ISCSI.check,
862 );
863
864 assert_eq!(
865 checksum_with_params(get_custom_crc32_forward(), TEST_CHECK_STRING),
866 CRC32_BZIP2.check,
867 );
868
869 assert_eq!(
870 checksum_with_params(get_custom_crc64_reflected(), TEST_CHECK_STRING),
871 CRC64_NVME.check,
872 );
873
874 assert_eq!(
875 checksum_with_params(get_custom_crc64_forward(), TEST_CHECK_STRING),
876 CRC64_ECMA_182.check,
877 );
878 }
879
880 #[test]
881 fn test_digest_updates_check() {
882 for config in TEST_ALL_CONFIGS {
883 check_digest(Digest::new(config.get_algorithm()), config.get_check());
884 }
885 }
886
887 #[test]
888 fn test_digest_updates_check_with_custom_params() {
889 crate::cache::clear_cache();
890
891 check_digest(
893 Digest::new_with_params(get_custom_crc32_reflected()),
894 CRC32_ISCSI.check,
895 );
896
897 check_digest(
899 Digest::new_with_params(get_custom_crc32_forward()),
900 CRC32_BZIP2.check,
901 );
902
903 check_digest(
905 Digest::new_with_params(get_custom_crc64_reflected()),
906 CRC64_NVME.check,
907 );
908
909 check_digest(
911 Digest::new_with_params(get_custom_crc64_forward()),
912 CRC64_ECMA_182.check,
913 );
914 }
915
916 fn check_digest(mut digest: Digest, check: u64) {
917 digest.update(b"123");
918 digest.update(b"456");
919 digest.update(b"789");
920 assert_eq!(digest.finalize(), check,);
921 }
922
923 #[test]
924 fn test_small_all_lengths() {
925 for config in TEST_ALL_CONFIGS {
926 for len in 1..=255 {
928 test_length(len, config);
929 }
930 }
931 }
932
933 #[test]
934 fn test_medium_lengths() {
935 for config in TEST_ALL_CONFIGS {
936 for len in 256..=1024 {
938 test_length(len, config);
939 }
940 }
941 }
942
943 #[test]
944 fn test_large_lengths() {
945 for config in TEST_ALL_CONFIGS {
946 for len in 1048575..1048577 {
948 test_length(len, config);
949 }
950 }
951 }
952
953 fn test_length(length: usize, config: &AnyCrcTestConfig) {
954 let mut data = vec![0u8; length];
955 rng().fill(&mut data[..]);
956
957 let expected = config.checksum_with_reference(&data);
959
960 let result = checksum(config.get_algorithm(), &data);
961
962 assert_eq!(
963 result,
964 expected,
965 "Failed for algorithm: {:?}, length: {}, expected: {:#x}, got: {:#x}",
966 config.get_algorithm(),
967 length,
968 expected,
969 result
970 );
971 }
972
973 #[test]
974 fn test_combine() {
975 for config in TEST_ALL_CONFIGS {
976 let algorithm = config.get_algorithm();
977 let check = config.get_check();
978
979 let checksum1 = checksum(algorithm, "1234".as_ref());
981 let checksum2 = checksum(algorithm, "56789".as_ref());
982
983 assert_eq!(checksum_combine(algorithm, checksum1, checksum2, 5), check,);
985
986 let mut digest1 = Digest::new(algorithm);
988 digest1.update("1234".as_ref());
989
990 let mut digest2 = Digest::new(algorithm);
991 digest2.update("56789".as_ref());
992
993 digest1.combine(&digest2);
994
995 assert_eq!(digest1.finalize(), check)
996 }
997 }
998
999 #[test]
1000 fn test_combine_with_custom_params() {
1001 crate::cache::clear_cache();
1002
1003 let crc32_params = get_custom_crc32_reflected();
1005 let checksum1 = checksum_with_params(crc32_params, "1234".as_ref());
1006 let checksum2 = checksum_with_params(crc32_params, "56789".as_ref());
1007 assert_eq!(
1008 checksum_combine_with_params(crc32_params, checksum1, checksum2, 5),
1009 CRC32_ISCSI.check,
1010 );
1011
1012 let crc32_params = get_custom_crc32_forward();
1014 let checksum1 = checksum_with_params(crc32_params, "1234".as_ref());
1015 let checksum2 = checksum_with_params(crc32_params, "56789".as_ref());
1016 assert_eq!(
1017 checksum_combine_with_params(crc32_params, checksum1, checksum2, 5),
1018 CRC32_BZIP2.check,
1019 );
1020
1021 let crc64_params = get_custom_crc64_reflected();
1023 let checksum1 = checksum_with_params(crc64_params, "1234".as_ref());
1024 let checksum2 = checksum_with_params(crc64_params, "56789".as_ref());
1025 assert_eq!(
1026 checksum_combine_with_params(crc64_params, checksum1, checksum2, 5),
1027 CRC64_NVME.check,
1028 );
1029
1030 let crc64_params = get_custom_crc64_forward();
1032 let checksum1 = checksum_with_params(crc64_params, "1234".as_ref());
1033 let checksum2 = checksum_with_params(crc64_params, "56789".as_ref());
1034 assert_eq!(
1035 checksum_combine_with_params(crc64_params, checksum1, checksum2, 5),
1036 CRC64_ECMA_182.check,
1037 );
1038 }
1039
1040 #[test]
1041 fn test_checksum_file() {
1042 let test_file_path = "test/test_crc32_hash_file.bin";
1044 let data = vec![0u8; 1024 * 1024]; if let Err(e) = write(test_file_path, &data) {
1046 eprintln!("Skipping test due to write error: {}", e);
1047 return;
1048 }
1049
1050 for config in TEST_ALL_CONFIGS {
1051 let result = checksum_file(config.get_algorithm(), test_file_path, None).unwrap();
1052 assert_eq!(result, config.checksum_with_reference(&data));
1053 }
1054
1055 std::fs::remove_file(test_file_path).unwrap();
1056 }
1057
1058 #[test]
1059 fn test_checksum_file_with_custom_params() {
1060 crate::cache::clear_cache();
1061
1062 let test_file_path = "test/test_crc32_hash_file_custom.bin";
1064 let data = vec![0u8; 1024 * 1024]; if let Err(e) = write(test_file_path, &data) {
1066 eprintln!("Skipping test due to write error: {}", e);
1067 return;
1068 }
1069
1070 check_file(
1072 get_custom_crc32_reflected(),
1073 test_file_path,
1074 CRC32_ISCSI.check,
1075 );
1076
1077 check_file(
1079 get_custom_crc32_forward(),
1080 test_file_path,
1081 CRC32_BZIP2.check,
1082 );
1083
1084 check_file(
1086 get_custom_crc64_reflected(),
1087 test_file_path,
1088 CRC64_NVME.check,
1089 );
1090
1091 check_file(
1093 get_custom_crc64_forward(),
1094 test_file_path,
1095 CRC64_ECMA_182.check,
1096 );
1097
1098 std::fs::remove_file(test_file_path).unwrap();
1099 }
1100
1101 fn check_file(params: CrcParams, file_path: &str, check: u64) {
1102 let result = checksum_file_with_params(params, file_path, None).unwrap();
1103 assert_eq!(result, check);
1104 }
1105
1106 #[test]
1107 fn test_writer() {
1108 let test_file_path = "test/test_crc32_writer_file.bin";
1110 let data = vec![0u8; 1024 * 1024]; if let Err(e) = std::fs::write(test_file_path, &data) {
1112 eprintln!("Skipping test due to write error: {}", e);
1113 return;
1114 }
1115
1116 for config in TEST_ALL_CONFIGS {
1117 let mut digest = Digest::new(config.get_algorithm());
1118 let mut file = File::open(test_file_path).unwrap();
1119 std::io::copy(&mut file, &mut digest).unwrap();
1120 assert_eq!(digest.finalize(), config.checksum_with_reference(&data));
1121 }
1122
1123 std::fs::remove_file(test_file_path).unwrap();
1124 }
1125 #[test]
1126 fn test_digest_reset() {
1127 for config in TEST_ALL_CONFIGS {
1128 let mut digest = Digest::new(config.get_algorithm());
1129 digest.update(b"42");
1130 digest.reset();
1131 digest.update(TEST_CHECK_STRING);
1132 assert_eq!(digest.finalize(), config.get_check());
1133 }
1134 }
1135
1136 #[test]
1137 fn test_digest_finalize_reset() {
1138 for config in TEST_ALL_CONFIGS {
1139 let check = config.get_check();
1140
1141 let mut digest = Digest::new(config.get_algorithm());
1142 digest.update(TEST_CHECK_STRING);
1143 assert_eq!(digest.finalize_reset(), check);
1144
1145 digest.update(TEST_CHECK_STRING);
1146 assert_eq!(digest.finalize(), check);
1147 }
1148 }
1149
1150 #[test]
1151 fn test_digest_finalize_into() {
1152 for config in TEST_ALL_CONFIGS {
1153 let mut digest = Digest::new(config.get_algorithm());
1154 digest.update(TEST_CHECK_STRING);
1155
1156 match digest.params.width {
1157 32 => {
1158 let mut output = [0u8; 4];
1159 digest.finalize_into(&mut output).unwrap();
1160 let result = u32::from_be_bytes(output) as u64;
1161 assert_eq!(result, config.get_check());
1162 }
1163 64 => {
1164 let mut output = [0u8; 8];
1165 digest.finalize_into(&mut output).unwrap();
1166 let result = u64::from_be_bytes(output);
1167 assert_eq!(result, config.get_check());
1168 }
1169 _ => panic!("Unsupported CRC width"),
1170 }
1171 }
1172 }
1173
1174 #[test]
1175 fn test_digest_finalize_into_reset() {
1176 for config in TEST_ALL_CONFIGS {
1177 let mut digest = Digest::new(config.get_algorithm());
1178 digest.update(TEST_CHECK_STRING);
1179
1180 let mut output: Vec<u8> = match digest.params.width {
1181 32 => vec![0u8; 4],
1182 64 => vec![0u8; 8],
1183 _ => panic!("Unsupported CRC width"),
1184 };
1185
1186 digest.finalize_into_reset(&mut output).unwrap();
1187 let result = match output.len() {
1188 4 => u32::from_be_bytes(output.try_into().unwrap()) as u64,
1189 8 => u64::from_be_bytes(output.try_into().unwrap()),
1190 _ => panic!("Unsupported CRC width"),
1191 };
1192 assert_eq!(result, config.get_check());
1193
1194 digest.update(TEST_CHECK_STRING);
1195 assert_eq!(digest.finalize(), config.get_check());
1196 }
1197 }
1198
1199 #[test]
1201 fn test_ffi_header() -> Result<(), String> {
1202 #[cfg(target_os = "windows")]
1203 {
1204 eprintln!("Skipping test on Windows");
1206
1207 return Ok(());
1208 }
1209
1210 const HEADER: &str = "libcrc_fast.h";
1211
1212 let crate_dir = std::env::var("CARGO_MANIFEST_DIR").map_err(|error| error.to_string())?;
1213
1214 let mut expected = Vec::new();
1215 cbindgen::Builder::new()
1216 .with_crate(crate_dir)
1217 .with_include_guard("CRC_FAST_H")
1218 .with_header("/* crc_fast library C/C++ API - Copyright 2025 Don MacAskill */\n/* This header is auto-generated. Do not edit directly. */\n")
1219 .exclude_item("crc32_iscsi_impl")
1221 .exclude_item("crc32_iso_hdlc_impl")
1222 .exclude_item("get_iscsi_target")
1223 .exclude_item("get_iso_hdlc_target")
1224 .exclude_item("ISO_HDLC_TARGET")
1225 .exclude_item("ISCSI_TARGET")
1226 .exclude_item("CrcParams")
1227 .rename_item("Digest", "CrcFastDigest")
1228 .with_style(Both)
1229 .with_language(C)
1231 .with_cpp_compat(true)
1233 .generate()
1234 .map_err(|error| error.to_string())?
1235 .write(&mut expected);
1236
1237 let header_content = String::from_utf8(expected).map_err(|error| error.to_string())?;
1240
1241 let regex = regex::Regex::new(r"\n{3,}").map_err(|error| error.to_string())?;
1243 let cleaned_content = regex.replace_all(&header_content, "\n\n").to_string();
1244
1245 expected = cleaned_content.into_bytes();
1247
1248 let actual = read(HEADER).map_err(|error| error.to_string())?;
1249
1250 if expected != actual {
1251 write(HEADER, expected).map_err(|error| error.to_string())?;
1252 return Err(format!(
1253 "{HEADER} is not up-to-date, commit the generated file and try again"
1254 ));
1255 }
1256
1257 Ok(())
1258 }
1259
1260 fn get_custom_crc32_reflected() -> CrcParams {
1261 CrcParams::new(
1262 "Custom CRC-32/ISCSI",
1263 32,
1264 CRC32_ISCSI.poly,
1265 CRC32_ISCSI.init,
1266 CRC32_ISCSI.refin,
1267 CRC32_ISCSI.xorout,
1268 CRC32_ISCSI.check,
1269 )
1270 }
1271
1272 fn get_custom_crc32_forward() -> CrcParams {
1273 CrcParams::new(
1274 "Custom CRC-32/BZIP2",
1275 32,
1276 CRC32_BZIP2.poly,
1277 CRC32_BZIP2.init,
1278 CRC32_BZIP2.refin,
1279 CRC32_BZIP2.xorout,
1280 CRC32_BZIP2.check,
1281 )
1282 }
1283
1284 fn get_custom_crc64_reflected() -> CrcParams {
1285 CrcParams::new(
1286 "Custom CRC-64/NVME",
1287 64,
1288 CRC64_NVME.poly,
1289 CRC64_NVME.init,
1290 CRC64_NVME.refin,
1291 CRC64_NVME.xorout,
1292 CRC64_NVME.check,
1293 )
1294 }
1295
1296 fn get_custom_crc64_forward() -> CrcParams {
1297 CrcParams::new(
1298 "Custom CRC-64/ECMA-182",
1299 64,
1300 CRC64_ECMA_182.poly,
1301 CRC64_ECMA_182.init,
1302 CRC64_ECMA_182.refin,
1303 CRC64_ECMA_182.xorout,
1304 CRC64_ECMA_182.check,
1305 )
1306 }
1307
1308 #[test]
1309 fn test_crc_keys_storage_fold_256() {
1310 let test_keys = [
1311 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
1312 ];
1313 let storage = CrcKeysStorage::from_keys_fold_256(test_keys);
1314
1315 for i in 0..23 {
1317 assert_eq!(storage.get_key(i), test_keys[i]);
1318 }
1319
1320 assert_eq!(storage.get_key(23), 0);
1322 assert_eq!(storage.get_key(24), 0);
1323 assert_eq!(storage.get_key(100), 0);
1324
1325 assert_eq!(storage.key_count(), 23);
1327 }
1328
1329 #[test]
1330 fn test_crc_keys_storage_future_test() {
1331 let test_keys = [
1332 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
1333 25,
1334 ];
1335 let storage = CrcKeysStorage::from_keys_fold_future_test(test_keys);
1336
1337 for i in 0..25 {
1339 assert_eq!(storage.get_key(i), test_keys[i]);
1340 }
1341
1342 assert_eq!(storage.get_key(25), 0);
1344 assert_eq!(storage.get_key(26), 0);
1345 assert_eq!(storage.get_key(100), 0);
1346
1347 assert_eq!(storage.key_count(), 25);
1349 }
1350
1351 #[test]
1352 fn test_crc_params_safe_accessors() {
1353 let test_keys = [
1355 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
1356 ];
1357 let params = CrcParams {
1358 algorithm: CrcAlgorithm::Crc32IsoHdlc,
1359 name: "test",
1360 width: 32,
1361 poly: 0x04C11DB7,
1362 init: 0xFFFFFFFF,
1363 refin: true,
1364 refout: true,
1365 xorout: 0xFFFFFFFF,
1366 check: 0xCBF43926,
1367 keys: CrcKeysStorage::from_keys_fold_256(test_keys),
1368 };
1369
1370 for i in 0..23 {
1372 assert_eq!(params.get_key(i), test_keys[i]);
1373 assert_eq!(params.get_key_checked(i), Some(test_keys[i]));
1374 }
1375
1376 assert_eq!(params.get_key(23), 0);
1378 assert_eq!(params.get_key(24), 0);
1379 assert_eq!(params.get_key(100), 0);
1380
1381 assert_eq!(params.get_key_checked(23), None);
1382 assert_eq!(params.get_key_checked(24), None);
1383 assert_eq!(params.get_key_checked(100), None);
1384
1385 assert_eq!(params.key_count(), 23);
1387 }
1388
1389 #[test]
1390 fn test_crc_keys_storage_const_constructors() {
1391 const TEST_KEYS_23: [u64; 23] = [1; 23];
1393 const TEST_KEYS_25: [u64; 25] = [2; 25];
1394
1395 const STORAGE_256: CrcKeysStorage = CrcKeysStorage::from_keys_fold_256(TEST_KEYS_23);
1396 const STORAGE_FUTURE: CrcKeysStorage =
1397 CrcKeysStorage::from_keys_fold_future_test(TEST_KEYS_25);
1398
1399 assert_eq!(STORAGE_256.get_key(0), 1);
1401 assert_eq!(STORAGE_256.key_count(), 23);
1402
1403 assert_eq!(STORAGE_FUTURE.get_key(0), 2);
1404 assert_eq!(STORAGE_FUTURE.key_count(), 25);
1405 }
1406
1407 #[test]
1408 fn test_crc_keys_storage_bounds_safety() {
1409 let storage_256 = CrcKeysStorage::from_keys_fold_256([42; 23]);
1410 let storage_future = CrcKeysStorage::from_keys_fold_future_test([84; 25]);
1411
1412 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);
1421 assert_eq!(storage_future.get_key(usize::MAX), 0);
1422 }
1423}