Skip to content

Commit cdee06e

Browse files
committed
Add support for custom CRC parameters
Enables anyone to BYOP (bring your own parameters). See: awesomized#5
1 parent 4ea82ea commit cdee06e

File tree

1 file changed

+160
-10
lines changed

1 file changed

+160
-10
lines changed

src/lib.rs

Lines changed: 160 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454
//! Implements the [std::io::Write](https://doc.rust-lang.org/std/io/trait.Write.html) trait for
5555
//! easier integration with existing code.
5656
//!
57-
//! ```no_run
57+
//! ```rust
5858
//! use std::env;
5959
//! use std::fs::File;
6060
//! use crc_fast::{Digest, CrcAlgorithm::Crc32IsoHdlc};
@@ -261,6 +261,19 @@ impl Digest {
261261
}
262262
}
263263

264+
/// Creates a new `Digest` instance with custom parameters.
265+
#[inline(always)]
266+
pub fn new_with_params(params: CrcParams) -> Self {
267+
let calculator = Calculator::calculate as CalculatorFn;
268+
269+
Self {
270+
state: params.init,
271+
amount: 0,
272+
params,
273+
calculator,
274+
}
275+
}
276+
264277
/// Updates the CRC state with the given data.
265278
#[inline(always)]
266279
pub fn update(&mut self, data: &[u8]) {
@@ -361,6 +374,13 @@ pub fn checksum(algorithm: CrcAlgorithm, buf: &[u8]) -> u64 {
361374
calculator(params.init, buf, params) ^ params.xorout
362375
}
363376

377+
/// Computes the CRC checksum for the given data using the custom specified parameters.
378+
pub fn checksum_with_params(params: CrcParams, buf: &[u8]) -> u64 {
379+
let calculator = Calculator::calculate as CalculatorFn;
380+
381+
calculator(params.init, buf, params) ^ params.xorout
382+
}
383+
364384
/// Computes the CRC checksum for the given file using the specified algorithm.
365385
///
366386
/// Appears to be much faster (~2X) than using Writer and io::*, at least on Apple M2 Ultra
@@ -371,7 +391,7 @@ pub fn checksum(algorithm: CrcAlgorithm, buf: &[u8]) -> u64 {
371391
///
372392
/// # Examples
373393
/// ### checksum_file
374-
///```no_run
394+
///```rust
375395
/// use std::env;
376396
/// use crc_fast::{checksum_file, CrcAlgorithm::Crc32IsoHdlc};
377397
///
@@ -389,7 +409,32 @@ pub fn checksum_file(
389409
path: &str,
390410
chunk_size: Option<usize>,
391411
) -> Result<u64, std::io::Error> {
392-
let mut digest = Digest::new(algorithm);
412+
checksum_file_with_digest(Digest::new(algorithm), path, chunk_size)
413+
}
414+
415+
/// Computes the CRC checksum for the given file using the custom specified parameters.
416+
///
417+
/// # Errors
418+
///
419+
/// This function will return an error if the file cannot be read.
420+
pub fn checksum_file_with_params(
421+
params: CrcParams,
422+
path: &str,
423+
chunk_size: Option<usize>,
424+
) -> Result<u64, std::io::Error> {
425+
checksum_file_with_digest(Digest::new_with_params(params), path, chunk_size)
426+
}
427+
428+
/// Computes the CRC checksum for the given file using the specified Digest.
429+
///
430+
/// # Errors
431+
///
432+
/// This function will return an error if the file cannot be read.
433+
fn checksum_file_with_digest(
434+
mut digest: Digest,
435+
path: &str,
436+
chunk_size: Option<usize>,
437+
) -> Result<u64, std::io::Error> {
393438
let mut file = File::open(path)?;
394439

395440
// 512KiB KiB was fastest in my benchmarks on an Apple M2 Ultra
@@ -540,19 +585,78 @@ mod lib {
540585
}
541586
}
542587

588+
#[test]
589+
fn test_checksum_with_custom_params() {
590+
// CRC-32 reflected
591+
assert_eq!(
592+
checksum_with_params(CRC32_ISCSI, TEST_CHECK_STRING),
593+
CRC32_ISCSI.check,
594+
);
595+
596+
// CRC-32 forward
597+
assert_eq!(
598+
checksum_with_params(CRC32_BZIP2, TEST_CHECK_STRING),
599+
CRC32_BZIP2.check,
600+
);
601+
602+
// CRC-64 reflected
603+
assert_eq!(
604+
checksum_with_params(CRC64_NVME, TEST_CHECK_STRING),
605+
CRC64_NVME.check,
606+
);
607+
608+
// CRC-64 forward
609+
assert_eq!(
610+
checksum_with_params(CRC64_ECMA_182, TEST_CHECK_STRING),
611+
CRC64_ECMA_182.check,
612+
);
613+
}
614+
543615
#[test]
544616
fn test_digest_updates_check() {
545617
for config in TEST_ALL_CONFIGS {
546618
let mut digest = Digest::new(config.get_algorithm());
547-
digest.update(b"123");
548-
digest.update(b"456");
549-
digest.update(b"789");
550-
let result = digest.finalize();
551-
552-
assert_eq!(result, config.get_check());
619+
check_digest(Digest::new(config.get_algorithm()), config.get_check());
553620
}
554621
}
555622

623+
#[test]
624+
fn test_digest_updates_check_with_custom_params() {
625+
// CRC-32 reflected
626+
check_digest(
627+
Digest::new_with_params(CRC32_ISCSI),
628+
CRC32_ISCSI.check,
629+
);
630+
631+
// CRC-32 forward
632+
check_digest(
633+
Digest::new_with_params(CRC32_BZIP2),
634+
CRC32_BZIP2.check,
635+
);
636+
637+
// CRC-64 reflected
638+
check_digest(
639+
Digest::new_with_params(CRC64_NVME),
640+
CRC64_NVME.check,
641+
);
642+
643+
// CRC-64 forward
644+
check_digest(
645+
Digest::new_with_params(CRC64_ECMA_182),
646+
CRC64_ECMA_182.check,
647+
);
648+
}
649+
650+
fn check_digest(mut digest: Digest, check: u64) {
651+
digest.update(b"123");
652+
digest.update(b"456");
653+
digest.update(b"789");
654+
assert_eq!(
655+
digest.finalize(),
656+
check,
657+
);
658+
}
659+
556660
#[test]
557661
fn test_small_all_lengths() {
558662
for config in TEST_ALL_CONFIGS {
@@ -634,7 +738,7 @@ mod lib {
634738
// Create a test file with repeating zeros
635739
let test_file_path = "test/test_crc32_hash_file.bin";
636740
let data = vec![0u8; 1024 * 1024]; // 1 MiB of zeros
637-
if let Err(e) = std::fs::write(test_file_path, &data) {
741+
if let Err(e) = write(test_file_path, &data) {
638742
eprintln!("Skipping test due to write error: {}", e);
639743
return;
640744
}
@@ -647,6 +751,52 @@ mod lib {
647751
std::fs::remove_file(test_file_path).unwrap();
648752
}
649753

754+
#[test]
755+
fn test_checksum_file_with_custom_params() {
756+
// Create a test file with repeating zeros
757+
let test_file_path = "test/test_crc32_hash_file_custom.bin";
758+
let data = vec![0u8; 1024 * 1024]; // 1 MiB of zeros
759+
if let Err(e) = write(test_file_path, &data) {
760+
eprintln!("Skipping test due to write error: {}", e);
761+
return;
762+
}
763+
764+
// CRC-32 reflected
765+
check_file(
766+
CRC32_ISCSI,
767+
test_file_path,
768+
CRC32_ISCSI.check,
769+
);
770+
771+
// CRC-32 forward
772+
check_file(
773+
CRC32_BZIP2,
774+
test_file_path,
775+
CRC32_BZIP2.check,
776+
);
777+
778+
// CRC-64 reflected
779+
check_file(
780+
CRC64_NVME,
781+
test_file_path,
782+
CRC64_NVME.check,
783+
);
784+
785+
// CRC-64 forward
786+
check_file(
787+
CRC64_ECMA_182,
788+
test_file_path,
789+
CRC64_ECMA_182.check,
790+
);
791+
792+
std::fs::remove_file(test_file_path).unwrap();
793+
}
794+
795+
fn check_file(params: CrcParams, file_path: &str, check: u64) {
796+
let result = checksum_file_with_params(params, file_path, None).unwrap();
797+
assert_eq!(result, check);
798+
}
799+
650800
#[test]
651801
fn test_writer() {
652802
// Create a test file with repeating zeros

0 commit comments

Comments
 (0)