const CRC32C_POLY: u32 = 0x82F6_3B78;
const CRC32C_TABLE: [u32; 256] = build_crc32c_table();
const fn build_crc32c_table() -> [u32; 256] {
let mut table = [0u32; 256];
let mut i = 0usize;
while i < 256 {
let mut crc = i as u32;
let mut bit = 0;
while bit < 8 {
crc = if crc & 1 != 0 {
(crc >> 1) ^ CRC32C_POLY
} else {
crc >> 1
};
bit += 1;
}
table[i] = crc;
i += 1;
}
table
}
pub fn crc32c(data: &[u8]) -> u32 {
let mut crc = 0xFFFF_FFFFu32;
for &byte in data {
let index = ((crc ^ byte as u32) & 0xFF) as usize;
crc = (crc >> 8) ^ CRC32C_TABLE[index];
}
!crc
}
pub fn checksum64(data: &[u8]) -> u64 {
let body = crc32c(data) as u64;
let len = crc32c(&(data.len() as u64).to_le_bytes()) as u64;
(len << 32) | body
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn crc32c_matches_known_vectors() {
assert_eq!(crc32c(b""), 0x0000_0000);
assert_eq!(crc32c(b"a"), 0xC1D0_4330);
assert_eq!(crc32c(b"123456789"), 0xE306_9283);
}
#[test]
fn crc32c_detects_reordering() {
assert_ne!(crc32c(b"ab"), crc32c(b"ba"));
}
#[test]
fn checksum64_detects_truncation() {
let full = b"pandrs storage payload";
assert_ne!(checksum64(full), checksum64(&full[..full.len() - 1]));
}
}