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)]
392 pub fn new(algorithm: CrcAlgorithm) -> Self {
393 let (calculator, params) = get_calculator_params(algorithm);
394
395 Self {
396 state: params.init,
397 amount: 0,
398 params,
399 calculator,
400 }
401 }
402
403 #[inline(always)]
427 pub fn new_with_init_state(algorithm: CrcAlgorithm, init_state: u64) -> Self {
428 let (calculator, params) = get_calculator_params(algorithm);
429
430 Self {
431 state: init_state,
432 amount: 0,
433 params,
434 calculator,
435 }
436 }
437
438 #[inline(always)]
463 pub fn new_with_params(params: CrcParams) -> Self {
464 let calculator = Calculator::calculate as CalculatorFn;
465
466 Self {
467 state: params.init,
468 amount: 0,
469 params,
470 calculator,
471 }
472 }
473
474 #[inline(always)]
476 pub fn update(&mut self, data: &[u8]) {
477 self.state = (self.calculator)(self.state, data, self.params);
478 self.amount += data.len() as u64;
479 }
480
481 #[inline(always)]
483 pub fn finalize(&self) -> u64 {
484 self.state ^ self.params.xorout
485 }
486
487 #[inline(always)]
489 pub fn finalize_reset(&mut self) -> u64 {
490 let result = self.finalize();
491 self.reset();
492
493 result
494 }
495
496 #[inline(always)]
498 pub fn reset(&mut self) {
499 self.state = self.params.init;
500 self.amount = 0;
501 }
502
503 #[inline(always)]
505 pub fn combine(&mut self, other: &Self) {
506 self.amount += other.amount;
507 let other_crc = other.finalize();
508
509 self.state = combine::checksums(
512 self.state ^ self.params.xorout,
513 other_crc,
514 other.amount,
515 self.params,
516 ) ^ self.params.xorout;
517 }
518
519 #[inline(always)]
521 pub fn get_amount(&self) -> u64 {
522 self.amount
523 }
524
525 #[inline(always)]
542 pub fn get_state(&self) -> u64 {
543 self.state
544 }
545}
546
547impl Write for Digest {
548 #[inline(always)]
549 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
550 self.update(buf);
551 Ok(buf.len())
552 }
553
554 #[inline(always)]
555 fn write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result<usize> {
556 let len: usize = bufs
557 .iter()
558 .map(|buf| {
559 self.update(buf);
560 buf.len()
561 })
562 .sum();
563
564 Ok(len)
565 }
566
567 #[inline(always)]
568 fn flush(&mut self) -> std::io::Result<()> {
569 Ok(())
570 }
571
572 #[inline(always)]
573 fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> {
574 self.update(buf);
575
576 Ok(())
577 }
578}
579
580#[inline(always)]
589pub fn checksum(algorithm: CrcAlgorithm, buf: &[u8]) -> u64 {
590 let (calculator, params) = get_calculator_params(algorithm);
591
592 calculator(params.init, buf, params) ^ params.xorout
593}
594
595pub fn checksum_with_params(params: CrcParams, buf: &[u8]) -> u64 {
618 let calculator = Calculator::calculate as CalculatorFn;
619
620 calculator(params.init, buf, params) ^ params.xorout
621}
622
623#[inline(always)]
646pub fn checksum_file(
647 algorithm: CrcAlgorithm,
648 path: &str,
649 chunk_size: Option<usize>,
650) -> Result<u64, std::io::Error> {
651 checksum_file_with_digest(Digest::new(algorithm), path, chunk_size)
652}
653
654pub fn checksum_file_with_params(
688 params: CrcParams,
689 path: &str,
690 chunk_size: Option<usize>,
691) -> Result<u64, std::io::Error> {
692 checksum_file_with_digest(Digest::new_with_params(params), path, chunk_size)
693}
694
695fn checksum_file_with_digest(
701 mut digest: Digest,
702 path: &str,
703 chunk_size: Option<usize>,
704) -> Result<u64, std::io::Error> {
705 let mut file = File::open(path)?;
706
707 let chunk_size = chunk_size.unwrap_or(524288);
713
714 let mut buf = vec![0; chunk_size];
715
716 while let Ok(n) = file.read(&mut buf) {
717 if n == 0 {
718 break;
719 }
720 digest.update(&buf[..n]);
721 }
722
723 Ok(digest.finalize())
724}
725
726#[inline(always)]
739pub fn checksum_combine(
740 algorithm: CrcAlgorithm,
741 checksum1: u64,
742 checksum2: u64,
743 checksum2_len: u64,
744) -> u64 {
745 let params = get_calculator_params(algorithm).1;
746
747 combine::checksums(checksum1, checksum2, checksum2_len, params)
748}
749
750pub fn checksum_combine_with_params(
775 params: CrcParams,
776 checksum1: u64,
777 checksum2: u64,
778 checksum2_len: u64,
779) -> u64 {
780 combine::checksums(checksum1, checksum2, checksum2_len, params)
781}
782
783pub fn get_calculator_target(_algorithm: CrcAlgorithm) -> String {
795 arch::get_target()
796}
797
798#[inline(always)]
800fn get_calculator_params(algorithm: CrcAlgorithm) -> (CalculatorFn, CrcParams) {
801 match algorithm {
802 CrcAlgorithm::Crc32Aixm => (Calculator::calculate as CalculatorFn, CRC32_AIXM),
803 CrcAlgorithm::Crc32Autosar => (Calculator::calculate as CalculatorFn, CRC32_AUTOSAR),
804 CrcAlgorithm::Crc32Base91D => (Calculator::calculate as CalculatorFn, CRC32_BASE91_D),
805 CrcAlgorithm::Crc32Bzip2 => (Calculator::calculate as CalculatorFn, CRC32_BZIP2),
806 CrcAlgorithm::Crc32CdRomEdc => (Calculator::calculate as CalculatorFn, CRC32_CD_ROM_EDC),
807 CrcAlgorithm::Crc32Cksum => (Calculator::calculate as CalculatorFn, CRC32_CKSUM),
808 CrcAlgorithm::Crc32Custom => {
809 panic!("Custom CRC-32 requires parameters via CrcParams::new()")
810 }
811 CrcAlgorithm::Crc32Iscsi => (crc32_iscsi_calculator as CalculatorFn, CRC32_ISCSI),
812 CrcAlgorithm::Crc32IsoHdlc => (crc32_iso_hdlc_calculator as CalculatorFn, CRC32_ISO_HDLC),
813 CrcAlgorithm::Crc32Jamcrc => (Calculator::calculate as CalculatorFn, CRC32_JAMCRC),
814 CrcAlgorithm::Crc32Mef => (Calculator::calculate as CalculatorFn, CRC32_MEF),
815 CrcAlgorithm::Crc32Mpeg2 => (Calculator::calculate as CalculatorFn, CRC32_MPEG_2),
816 CrcAlgorithm::Crc32Xfer => (Calculator::calculate as CalculatorFn, CRC32_XFER),
817 CrcAlgorithm::Crc64Custom => {
818 panic!("Custom CRC-64 requires parameters via CrcParams::new()")
819 }
820 CrcAlgorithm::Crc64Ecma182 => (Calculator::calculate as CalculatorFn, CRC64_ECMA_182),
821 CrcAlgorithm::Crc64GoIso => (Calculator::calculate as CalculatorFn, CRC64_GO_ISO),
822 CrcAlgorithm::Crc64Ms => (Calculator::calculate as CalculatorFn, CRC64_MS),
823 CrcAlgorithm::Crc64Nvme => (Calculator::calculate as CalculatorFn, CRC64_NVME),
824 CrcAlgorithm::Crc64Redis => (Calculator::calculate as CalculatorFn, CRC64_REDIS),
825 CrcAlgorithm::Crc64We => (Calculator::calculate as CalculatorFn, CRC64_WE),
826 CrcAlgorithm::Crc64Xz => (Calculator::calculate as CalculatorFn, CRC64_XZ),
827 }
828}
829
830#[inline(always)]
835fn crc32_iscsi_calculator(state: u64, data: &[u8], _params: CrcParams) -> u64 {
836 #[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))]
838 return fusion::crc32_iscsi(state as u32, data) as u64;
839
840 #[cfg(all(not(target_arch = "aarch64"), not(target_arch = "x86_64")))]
841 Calculator::calculate(state, data, _params)
843}
844
845#[inline(always)]
851fn crc32_iso_hdlc_calculator(state: u64, data: &[u8], _params: CrcParams) -> u64 {
852 #[cfg(target_arch = "aarch64")]
854 return fusion::crc32_iso_hdlc(state as u32, data) as u64;
855
856 #[cfg(not(target_arch = "aarch64"))]
859 Calculator::calculate(state, data, _params)
860}
861
862#[cfg(test)]
863mod lib {
864 #![allow(unused)]
865
866 use super::*;
867 use crate::test::consts::{TEST_ALL_CONFIGS, TEST_CHECK_STRING};
868 use crate::test::enums::AnyCrcTestConfig;
869 use cbindgen::Language::C;
870 use cbindgen::Style::Both;
871 use rand::{rng, Rng};
872 use std::fs::{read, write};
873
874 #[test]
875 fn test_checksum_check() {
876 for config in TEST_ALL_CONFIGS {
877 assert_eq!(
878 checksum(config.get_algorithm(), TEST_CHECK_STRING),
879 config.get_check()
880 );
881 }
882 }
883
884 #[test]
885 fn test_checksum_reference() {
886 for config in TEST_ALL_CONFIGS {
887 assert_eq!(
888 checksum(config.get_algorithm(), TEST_CHECK_STRING),
889 config.checksum_with_reference(TEST_CHECK_STRING)
890 );
891 }
892 }
893
894 #[test]
895 fn test_checksum_with_custom_params() {
896 crate::cache::clear_cache();
897
898 assert_eq!(
900 checksum_with_params(get_custom_crc32_reflected(), TEST_CHECK_STRING),
901 CRC32_ISCSI.check,
902 );
903
904 assert_eq!(
906 checksum_with_params(get_custom_crc32_forward(), TEST_CHECK_STRING),
907 CRC32_BZIP2.check,
908 );
909
910 assert_eq!(
912 checksum_with_params(get_custom_crc64_reflected(), TEST_CHECK_STRING),
913 CRC64_NVME.check,
914 );
915
916 assert_eq!(
918 checksum_with_params(get_custom_crc64_forward(), TEST_CHECK_STRING),
919 CRC64_ECMA_182.check,
920 );
921 }
922
923 #[test]
924 fn test_get_custom_params() {
925 crate::cache::clear_cache();
926
927 assert_eq!(
928 checksum_with_params(get_custom_crc32_reflected(), TEST_CHECK_STRING),
929 CRC32_ISCSI.check,
930 );
931
932 assert_eq!(
933 checksum_with_params(get_custom_crc32_forward(), TEST_CHECK_STRING),
934 CRC32_BZIP2.check,
935 );
936
937 assert_eq!(
938 checksum_with_params(get_custom_crc64_reflected(), TEST_CHECK_STRING),
939 CRC64_NVME.check,
940 );
941
942 assert_eq!(
943 checksum_with_params(get_custom_crc64_forward(), TEST_CHECK_STRING),
944 CRC64_ECMA_182.check,
945 );
946 }
947
948 #[test]
949 fn test_digest_updates_check() {
950 for config in TEST_ALL_CONFIGS {
951 check_digest(Digest::new(config.get_algorithm()), config.get_check());
952 }
953 }
954
955 #[test]
956 fn test_digest_updates_check_with_custom_params() {
957 crate::cache::clear_cache();
958
959 check_digest(
961 Digest::new_with_params(get_custom_crc32_reflected()),
962 CRC32_ISCSI.check,
963 );
964
965 check_digest(
967 Digest::new_with_params(get_custom_crc32_forward()),
968 CRC32_BZIP2.check,
969 );
970
971 check_digest(
973 Digest::new_with_params(get_custom_crc64_reflected()),
974 CRC64_NVME.check,
975 );
976
977 check_digest(
979 Digest::new_with_params(get_custom_crc64_forward()),
980 CRC64_ECMA_182.check,
981 );
982 }
983
984 fn check_digest(mut digest: Digest, check: u64) {
985 digest.update(b"123");
986 digest.update(b"456");
987 digest.update(b"789");
988 assert_eq!(digest.finalize(), check,);
989 }
990
991 #[test]
992 fn test_small_all_lengths() {
993 for config in TEST_ALL_CONFIGS {
994 for len in 1..=255 {
996 test_length(len, config);
997 }
998 }
999 }
1000
1001 #[test]
1002 fn test_medium_lengths() {
1003 for config in TEST_ALL_CONFIGS {
1004 for len in 256..=1024 {
1006 test_length(len, config);
1007 }
1008 }
1009 }
1010
1011 #[test]
1012 fn test_large_lengths() {
1013 for config in TEST_ALL_CONFIGS {
1014 for len in 1048575..1048577 {
1016 test_length(len, config);
1017 }
1018 }
1019 }
1020
1021 fn test_length(length: usize, config: &AnyCrcTestConfig) {
1022 let mut data = vec![0u8; length];
1023 rng().fill(&mut data[..]);
1024
1025 let expected = config.checksum_with_reference(&data);
1027
1028 let result = checksum(config.get_algorithm(), &data);
1029
1030 assert_eq!(
1031 result,
1032 expected,
1033 "Failed for algorithm: {:?}, length: {}, expected: {:#x}, got: {:#x}",
1034 config.get_algorithm(),
1035 length,
1036 expected,
1037 result
1038 );
1039 }
1040
1041 #[test]
1042 fn test_combine() {
1043 for config in TEST_ALL_CONFIGS {
1044 let algorithm = config.get_algorithm();
1045 let check = config.get_check();
1046
1047 let checksum1 = checksum(algorithm, "1234".as_ref());
1049 let checksum2 = checksum(algorithm, "56789".as_ref());
1050
1051 assert_eq!(checksum_combine(algorithm, checksum1, checksum2, 5), check,);
1053
1054 let mut digest1 = Digest::new(algorithm);
1056 digest1.update("1234".as_ref());
1057
1058 let mut digest2 = Digest::new(algorithm);
1059 digest2.update("56789".as_ref());
1060
1061 digest1.combine(&digest2);
1062
1063 assert_eq!(digest1.finalize(), check)
1064 }
1065 }
1066
1067 #[test]
1068 fn test_combine_with_custom_params() {
1069 crate::cache::clear_cache();
1070
1071 let crc32_params = get_custom_crc32_reflected();
1073 let checksum1 = checksum_with_params(crc32_params, "1234".as_ref());
1074 let checksum2 = checksum_with_params(crc32_params, "56789".as_ref());
1075 assert_eq!(
1076 checksum_combine_with_params(crc32_params, checksum1, checksum2, 5),
1077 CRC32_ISCSI.check,
1078 );
1079
1080 let crc32_params = get_custom_crc32_forward();
1082 let checksum1 = checksum_with_params(crc32_params, "1234".as_ref());
1083 let checksum2 = checksum_with_params(crc32_params, "56789".as_ref());
1084 assert_eq!(
1085 checksum_combine_with_params(crc32_params, checksum1, checksum2, 5),
1086 CRC32_BZIP2.check,
1087 );
1088
1089 let crc64_params = get_custom_crc64_reflected();
1091 let checksum1 = checksum_with_params(crc64_params, "1234".as_ref());
1092 let checksum2 = checksum_with_params(crc64_params, "56789".as_ref());
1093 assert_eq!(
1094 checksum_combine_with_params(crc64_params, checksum1, checksum2, 5),
1095 CRC64_NVME.check,
1096 );
1097
1098 let crc64_params = get_custom_crc64_forward();
1100 let checksum1 = checksum_with_params(crc64_params, "1234".as_ref());
1101 let checksum2 = checksum_with_params(crc64_params, "56789".as_ref());
1102 assert_eq!(
1103 checksum_combine_with_params(crc64_params, checksum1, checksum2, 5),
1104 CRC64_ECMA_182.check,
1105 );
1106 }
1107
1108 #[test]
1109 fn test_checksum_file() {
1110 let test_file_path = "test/test_crc32_hash_file.bin";
1112 let data = vec![0u8; 1024 * 1024]; if let Err(e) = write(test_file_path, &data) {
1114 eprintln!("Skipping test due to write error: {}", e);
1115 return;
1116 }
1117
1118 for config in TEST_ALL_CONFIGS {
1119 let result = checksum_file(config.get_algorithm(), test_file_path, None).unwrap();
1120 assert_eq!(result, config.checksum_with_reference(&data));
1121 }
1122
1123 std::fs::remove_file(test_file_path).unwrap();
1124 }
1125
1126 #[test]
1127 fn test_checksum_file_with_custom_params() {
1128 crate::cache::clear_cache();
1129
1130 let test_file_path = "test/test_crc32_hash_file_custom.bin";
1132 let data = vec![0u8; 1024 * 1024]; if let Err(e) = write(test_file_path, &data) {
1134 eprintln!("Skipping test due to write error: {}", e);
1135 return;
1136 }
1137
1138 check_file(
1140 get_custom_crc32_reflected(),
1141 test_file_path,
1142 CRC32_ISCSI.check,
1143 );
1144
1145 check_file(
1147 get_custom_crc32_forward(),
1148 test_file_path,
1149 CRC32_BZIP2.check,
1150 );
1151
1152 check_file(
1154 get_custom_crc64_reflected(),
1155 test_file_path,
1156 CRC64_NVME.check,
1157 );
1158
1159 check_file(
1161 get_custom_crc64_forward(),
1162 test_file_path,
1163 CRC64_ECMA_182.check,
1164 );
1165
1166 std::fs::remove_file(test_file_path).unwrap();
1167 }
1168
1169 fn check_file(params: CrcParams, file_path: &str, check: u64) {
1170 let result = checksum_file_with_params(params, file_path, None).unwrap();
1171 assert_eq!(result, check);
1172 }
1173
1174 #[test]
1175 fn test_writer() {
1176 let test_file_path = "test/test_crc32_writer_file.bin";
1178 let data = vec![0u8; 1024 * 1024]; if let Err(e) = std::fs::write(test_file_path, &data) {
1180 eprintln!("Skipping test due to write error: {}", e);
1181 return;
1182 }
1183
1184 for config in TEST_ALL_CONFIGS {
1185 let mut digest = Digest::new(config.get_algorithm());
1186 let mut file = File::open(test_file_path).unwrap();
1187 std::io::copy(&mut file, &mut digest).unwrap();
1188 assert_eq!(digest.finalize(), config.checksum_with_reference(&data));
1189 }
1190
1191 std::fs::remove_file(test_file_path).unwrap();
1192 }
1193 #[test]
1194 fn test_digest_reset() {
1195 for config in TEST_ALL_CONFIGS {
1196 let mut digest = Digest::new(config.get_algorithm());
1197 digest.update(b"42");
1198 digest.reset();
1199 digest.update(TEST_CHECK_STRING);
1200 assert_eq!(digest.finalize(), config.get_check());
1201 }
1202 }
1203
1204 #[test]
1205 fn test_digest_finalize_reset() {
1206 for config in TEST_ALL_CONFIGS {
1207 let check = config.get_check();
1208
1209 let mut digest = Digest::new(config.get_algorithm());
1210 digest.update(TEST_CHECK_STRING);
1211 assert_eq!(digest.finalize_reset(), check);
1212
1213 digest.update(TEST_CHECK_STRING);
1214 assert_eq!(digest.finalize(), check);
1215 }
1216 }
1217
1218 #[test]
1219 fn test_digest_finalize_into() {
1220 for config in TEST_ALL_CONFIGS {
1221 let mut digest = Digest::new(config.get_algorithm());
1222 digest.update(TEST_CHECK_STRING);
1223
1224 match digest.params.width {
1225 32 => {
1226 let mut output = [0u8; 4];
1227 digest.finalize_into(&mut output).unwrap();
1228 let result = u32::from_be_bytes(output) as u64;
1229 assert_eq!(result, config.get_check());
1230 }
1231 64 => {
1232 let mut output = [0u8; 8];
1233 digest.finalize_into(&mut output).unwrap();
1234 let result = u64::from_be_bytes(output);
1235 assert_eq!(result, config.get_check());
1236 }
1237 _ => panic!("Unsupported CRC width"),
1238 }
1239 }
1240 }
1241
1242 #[test]
1243 fn test_digest_finalize_into_reset() {
1244 for config in TEST_ALL_CONFIGS {
1245 let mut digest = Digest::new(config.get_algorithm());
1246 digest.update(TEST_CHECK_STRING);
1247
1248 let mut output: Vec<u8> = match digest.params.width {
1249 32 => vec![0u8; 4],
1250 64 => vec![0u8; 8],
1251 _ => panic!("Unsupported CRC width"),
1252 };
1253
1254 digest.finalize_into_reset(&mut output).unwrap();
1255 let result = match output.len() {
1256 4 => u32::from_be_bytes(output.try_into().unwrap()) as u64,
1257 8 => u64::from_be_bytes(output.try_into().unwrap()),
1258 _ => panic!("Unsupported CRC width"),
1259 };
1260 assert_eq!(result, config.get_check());
1261
1262 digest.update(TEST_CHECK_STRING);
1263 assert_eq!(digest.finalize(), config.get_check());
1264 }
1265 }
1266
1267 #[test]
1269 fn test_ffi_header() -> Result<(), String> {
1270 #[cfg(target_os = "windows")]
1271 {
1272 eprintln!("Skipping test on Windows");
1274
1275 return Ok(());
1276 }
1277
1278 const HEADER: &str = "libcrc_fast.h";
1279
1280 let crate_dir = std::env::var("CARGO_MANIFEST_DIR").map_err(|error| error.to_string())?;
1281
1282 let mut expected = Vec::new();
1283 cbindgen::Builder::new()
1284 .with_crate(crate_dir)
1285 .with_include_guard("CRC_FAST_H")
1286 .with_header("/* crc_fast library C/C++ API - Copyright 2025 Don MacAskill */\n/* This header is auto-generated. Do not edit directly. */\n")
1287 .exclude_item("crc32_iscsi_impl")
1289 .exclude_item("crc32_iso_hdlc_impl")
1290 .exclude_item("get_iscsi_target")
1291 .exclude_item("get_iso_hdlc_target")
1292 .exclude_item("ISO_HDLC_TARGET")
1293 .exclude_item("ISCSI_TARGET")
1294 .exclude_item("CrcParams")
1295 .rename_item("Digest", "CrcFastDigest")
1296 .with_style(Both)
1297 .with_language(C)
1299 .with_cpp_compat(true)
1301 .generate()
1302 .map_err(|error| error.to_string())?
1303 .write(&mut expected);
1304
1305 let header_content = String::from_utf8(expected).map_err(|error| error.to_string())?;
1308
1309 let regex = regex::Regex::new(r"\n{3,}").map_err(|error| error.to_string())?;
1311 let cleaned_content = regex.replace_all(&header_content, "\n\n").to_string();
1312
1313 expected = cleaned_content.into_bytes();
1315
1316 let actual = read(HEADER).map_err(|error| error.to_string())?;
1317
1318 if expected != actual {
1319 write(HEADER, expected).map_err(|error| error.to_string())?;
1320 return Err(format!(
1321 "{HEADER} is not up-to-date, commit the generated file and try again"
1322 ));
1323 }
1324
1325 Ok(())
1326 }
1327
1328 fn get_custom_crc32_reflected() -> CrcParams {
1329 CrcParams::new(
1330 "Custom CRC-32/ISCSI",
1331 32,
1332 CRC32_ISCSI.poly,
1333 CRC32_ISCSI.init,
1334 CRC32_ISCSI.refin,
1335 CRC32_ISCSI.xorout,
1336 CRC32_ISCSI.check,
1337 )
1338 }
1339
1340 fn get_custom_crc32_forward() -> CrcParams {
1341 CrcParams::new(
1342 "Custom CRC-32/BZIP2",
1343 32,
1344 CRC32_BZIP2.poly,
1345 CRC32_BZIP2.init,
1346 CRC32_BZIP2.refin,
1347 CRC32_BZIP2.xorout,
1348 CRC32_BZIP2.check,
1349 )
1350 }
1351
1352 fn get_custom_crc64_reflected() -> CrcParams {
1353 CrcParams::new(
1354 "Custom CRC-64/NVME",
1355 64,
1356 CRC64_NVME.poly,
1357 CRC64_NVME.init,
1358 CRC64_NVME.refin,
1359 CRC64_NVME.xorout,
1360 CRC64_NVME.check,
1361 )
1362 }
1363
1364 fn get_custom_crc64_forward() -> CrcParams {
1365 CrcParams::new(
1366 "Custom CRC-64/ECMA-182",
1367 64,
1368 CRC64_ECMA_182.poly,
1369 CRC64_ECMA_182.init,
1370 CRC64_ECMA_182.refin,
1371 CRC64_ECMA_182.xorout,
1372 CRC64_ECMA_182.check,
1373 )
1374 }
1375
1376 #[test]
1377 fn test_crc_keys_storage_fold_256() {
1378 let test_keys = [
1379 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
1380 ];
1381 let storage = CrcKeysStorage::from_keys_fold_256(test_keys);
1382
1383 for i in 0..23 {
1385 assert_eq!(storage.get_key(i), test_keys[i]);
1386 }
1387
1388 assert_eq!(storage.get_key(23), 0);
1390 assert_eq!(storage.get_key(24), 0);
1391 assert_eq!(storage.get_key(100), 0);
1392
1393 assert_eq!(storage.key_count(), 23);
1395 }
1396
1397 #[test]
1398 fn test_crc_keys_storage_future_test() {
1399 let test_keys = [
1400 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
1401 25,
1402 ];
1403 let storage = CrcKeysStorage::from_keys_fold_future_test(test_keys);
1404
1405 for i in 0..25 {
1407 assert_eq!(storage.get_key(i), test_keys[i]);
1408 }
1409
1410 assert_eq!(storage.get_key(25), 0);
1412 assert_eq!(storage.get_key(26), 0);
1413 assert_eq!(storage.get_key(100), 0);
1414
1415 assert_eq!(storage.key_count(), 25);
1417 }
1418
1419 #[test]
1420 fn test_crc_params_safe_accessors() {
1421 let test_keys = [
1423 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
1424 ];
1425 let params = CrcParams {
1426 algorithm: CrcAlgorithm::Crc32IsoHdlc,
1427 name: "test",
1428 width: 32,
1429 poly: 0x04C11DB7,
1430 init: 0xFFFFFFFF,
1431 refin: true,
1432 refout: true,
1433 xorout: 0xFFFFFFFF,
1434 check: 0xCBF43926,
1435 keys: CrcKeysStorage::from_keys_fold_256(test_keys),
1436 };
1437
1438 for i in 0..23 {
1440 assert_eq!(params.get_key(i), test_keys[i]);
1441 assert_eq!(params.get_key_checked(i), Some(test_keys[i]));
1442 }
1443
1444 assert_eq!(params.get_key(23), 0);
1446 assert_eq!(params.get_key(24), 0);
1447 assert_eq!(params.get_key(100), 0);
1448
1449 assert_eq!(params.get_key_checked(23), None);
1450 assert_eq!(params.get_key_checked(24), None);
1451 assert_eq!(params.get_key_checked(100), None);
1452
1453 assert_eq!(params.key_count(), 23);
1455 }
1456
1457 #[test]
1458 fn test_crc_keys_storage_const_constructors() {
1459 const TEST_KEYS_23: [u64; 23] = [1; 23];
1461 const TEST_KEYS_25: [u64; 25] = [2; 25];
1462
1463 const STORAGE_256: CrcKeysStorage = CrcKeysStorage::from_keys_fold_256(TEST_KEYS_23);
1464 const STORAGE_FUTURE: CrcKeysStorage =
1465 CrcKeysStorage::from_keys_fold_future_test(TEST_KEYS_25);
1466
1467 assert_eq!(STORAGE_256.get_key(0), 1);
1469 assert_eq!(STORAGE_256.key_count(), 23);
1470
1471 assert_eq!(STORAGE_FUTURE.get_key(0), 2);
1472 assert_eq!(STORAGE_FUTURE.key_count(), 25);
1473 }
1474
1475 #[test]
1476 fn test_crc_keys_storage_bounds_safety() {
1477 let storage_256 = CrcKeysStorage::from_keys_fold_256([42; 23]);
1478 let storage_future = CrcKeysStorage::from_keys_fold_future_test([84; 25]);
1479
1480 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);
1489 assert_eq!(storage_future.get_key(usize::MAX), 0);
1490 }
1491}