use crc::{Crc, CRC_32_ISCSI};
const CRC32C: Crc<u32> = Crc::<u32>::new(&CRC_32_ISCSI);
pub(crate) fn crc32c(data: &[u8]) -> u32 {
CRC32C.checksum(data)
}
pub(crate) fn crc32c_with_seed(seed: u32, data: &[u8]) -> u32 {
let mut crc = seed;
for &b in data {
crc = NAME_HASH_TABLE[((crc ^ u32::from(b)) & 0xFF) as usize] ^ (crc >> 8);
}
crc
}
const NAME_HASH_TABLE: [u32; 256] = generate_name_hash_table();
const fn generate_name_hash_table() -> [u32; 256] {
let mut table = [0u32; 256];
let mut n = 0u32;
while n < 256 {
let mut c = n;
let mut k = 0;
while k < 8 {
if c & 1 != 0 {
c = (c >> 1) ^ 0x82F6_3B78;
} else {
c >>= 1;
}
k += 1;
}
table[n as usize] = c;
n += 1;
}
table
}
pub(crate) fn verify_crc32c(body: &[u8], stored: &[u8]) -> bool {
if stored.len() < 4 {
return false;
}
let expected = u32::from_le_bytes([stored[0], stored[1], stored[2], stored[3]]);
crc32c(body) == expected
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn known_crc32c_value() {
assert_eq!(crc32c(&[]), 0);
assert_eq!(crc32c(b"123456789"), 0xE306_9283);
}
#[test]
fn name_hash_matches_btrfs_kernel_output() {
assert_eq!(crc32c_with_seed(0xFFFF_FFFE, b"hello.txt"), 0x415F_EB59);
assert_eq!(crc32c_with_seed(0xFFFF_FFFE, b"dir-a"), 0x2C54_9827);
assert_eq!(crc32c_with_seed(0xFFFF_FFFE, b"nested.txt"), 0x86F5_F6F8);
}
#[test]
fn name_hash_uses_seed() {
let a = crc32c(b"default");
let b = crc32c_with_seed(0xFFFF_FFFE, b"default");
assert_ne!(a, b, "name-hash seed must change the result");
}
#[test]
fn verify_csum_round_trip() {
let body = b"hello, btrfs";
let mut stored = [0u8; 32];
let csum = crc32c(body);
stored[0..4].copy_from_slice(&csum.to_le_bytes());
assert!(verify_crc32c(body, &stored));
stored[0] ^= 0x01;
assert!(!verify_crc32c(body, &stored));
}
}