diff --git a/Cargo.lock b/Cargo.lock index 6c334b1..e3534d2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -281,9 +281,9 @@ dependencies = [ [[package]] name = "hashcrew" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd3730de410a9d5f2ca41072653e79059e2c6fd9cd87300de4becacb1a93154" +checksum = "f73782af6df9e45939f4e6206b646f3cbab68c2cc5c0c375dab045800cafb018" [[package]] name = "heck" diff --git a/Cargo.toml b/Cargo.toml index 9542fd2..644b5ae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,7 @@ asyncband = { version = "0.7.1", features = ["barrier", "semaphore", "watch"] } cargo_metadata = { version = "0.23.1" } clap = { version = "4.6.5", features = ["derive"] } crc-fast = { version = "1.10.0", default-features = false, features = ["std"] } -hashcrew = { version = "0.2.0", features = ["std", "xxhash"] } +hashcrew = { version = "0.3.0", features = ["std", "crc", "xxhash"] } io-uring = { version = "0.7.14" } libc = { version = "0.2.189" } log = { version = "0.4.31", features = ["kv"] } diff --git a/cache2/Cargo.toml b/cache2/Cargo.toml index 6245cfe..bcbbbad 100644 --- a/cache2/Cargo.toml +++ b/cache2/Cargo.toml @@ -32,12 +32,12 @@ rustdoc-args = ["--cfg", "docsrs"] [dependencies] asyncband = { workspace = true } -crc-fast = { workspace = true } hashcrew = { workspace = true } log = { workspace = true } tokio = { workspace = true } [dev-dependencies] +crc-fast = { workspace = true } quickcheck = { workspace = true } tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } diff --git a/cache2/src/checksum.rs b/cache2/src/checksum.rs index 1bbb1d0..bd2ae88 100644 --- a/cache2/src/checksum.rs +++ b/cache2/src/checksum.rs @@ -18,9 +18,8 @@ //! retains a portable software fallback. This wrapper keeps the cache's codec //! API and checksum values independent of that implementation detail. -use crc_fast::CrcAlgorithm; -use crc_fast::Digest; -use crc_fast::crc32_iscsi; +use hashcrew::crc::Crc32Iscsi; +use hashcrew::crc::crc32_iscsi; /// Computes the standard CRC32C checksum of `bytes`. pub fn crc32c(bytes: &[u8]) -> u32 { @@ -30,13 +29,13 @@ pub fn crc32c(bytes: &[u8]) -> u32 { /// Incremental CRC32C state, useful for checksum a key and value without first joining them in a /// temporary allocation. pub struct Crc32c { - digest: Digest, + digest: Crc32Iscsi, } impl Crc32c { pub fn new() -> Self { Self { - digest: Digest::new(CrcAlgorithm::Crc32Iscsi), + digest: Crc32Iscsi::new(), } } @@ -45,7 +44,7 @@ impl Crc32c { } pub fn finish(self) -> u32 { - self.digest.finalize() as u32 + self.digest.digest() } } @@ -57,6 +56,7 @@ impl Default for Crc32c { #[cfg(test)] mod tests { + use crate::checksum::Crc32c; use crate::checksum::crc32c; #[test] @@ -64,4 +64,35 @@ mod tests { assert_eq!(crc32c(b"123456789"), 0xe306_9283); assert_eq!(crc32c(b""), 0); } + + #[test] + fn fragmented_checksums_match_the_previous_implementation() { + let bytes: Vec<_> = (0..65_544).map(|index| (index * 37) as u8).collect(); + for offset in [0, 1, 7] { + for len in [0, 1, 44, 48, 4092, 4096, 65_537] { + let input = &bytes[offset..offset + len]; + let expected = crc_fast::crc32_iscsi(input); + assert_eq!(crc32c(input), expected); + for split in [0, len.min(44), len.min(56), len / 2, len] { + let mut checksum = Crc32c::new(); + checksum.update(&input[..split]); + checksum.update(&[]); + checksum.update(&input[split..]); + assert_eq!(checksum.finish(), expected, "len={len}, split={split}"); + } + } + } + + // Record headers, index pages, and recovery pages zero their checksum + // field without concatenating the surrounding slices. + for (len, checksum_offset) in [(48, 44), (4096, 56), (4096, 4092)] { + let mut page = bytes[..len].to_vec(); + page[checksum_offset..checksum_offset + 4].fill(0); + let mut checksum = Crc32c::new(); + checksum.update(&page[..checksum_offset]); + checksum.update(&[0; 4]); + checksum.update(&page[checksum_offset + 4..]); + assert_eq!(checksum.finish(), crc_fast::crc32_iscsi(&page)); + } + } }