const fn tables(poly: u32) -> [[u32; 256]; 8] {
let mut t = [[0u32; 256]; 8];
let mut i = 0;
while i < 256 {
let mut c = i as u32;
let mut k = 0;
while k < 8 {
c = (c >> 1) ^ ((c & 1).wrapping_neg() & poly);
k += 1;
}
t[0][i] = c;
i += 1;
}
let mut n = 1;
while n < 8 {
let mut i = 0;
while i < 256 {
let prev = t[n - 1][i];
t[n][i] = (prev >> 8) ^ t[0][(prev & 0xFF) as usize];
i += 1;
}
n += 1;
}
t
}
const IEEE: [[u32; 256]; 8] = tables(0xEDB8_8320);
const CASTAGNOLI: [[u32; 256]; 8] = tables(0x82F6_3B78);
#[inline]
fn fold(t: &[[u32; 256]; 8], mut s: u32, data: &[u8]) -> u32 {
let (chunks, tail) = data.as_chunks::<8>();
for c in chunks {
let lo = u32::from_le_bytes([c[0], c[1], c[2], c[3]]) ^ s;
let hi = u32::from_le_bytes([c[4], c[5], c[6], c[7]]);
s = t[7][(lo & 0xFF) as usize]
^ t[6][((lo >> 8) & 0xFF) as usize]
^ t[5][((lo >> 16) & 0xFF) as usize]
^ t[4][(lo >> 24) as usize]
^ t[3][(hi & 0xFF) as usize]
^ t[2][((hi >> 8) & 0xFF) as usize]
^ t[1][((hi >> 16) & 0xFF) as usize]
^ t[0][(hi >> 24) as usize];
}
for &b in tail {
s = (s >> 8) ^ t[0][((s ^ b as u32) & 0xFF) as usize];
}
s
}
pub fn crc32c(data: &[u8]) -> u32 {
crc32c_append(0, data)
}
pub fn crc32c_append(crc: u32, data: &[u8]) -> u32 {
!fold(&CASTAGNOLI, !crc, data)
}
pub fn crc32(data: &[u8]) -> u32 {
crc32_append(0, data)
}
pub fn crc32_append(crc: u32, data: &[u8]) -> u32 {
!fold(&IEEE, !crc, data)
}
pub fn crc32_ieee_raw(state: u32, data: &[u8]) -> u32 {
fold(&IEEE, state, data)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn matches_the_catalogue_check_values() {
assert_eq!(crc32c(b"123456789"), 0xE306_9283, "CRC-32C");
assert_eq!(crc32(b"123456789"), 0xCBF4_3926, "CRC-32");
assert_eq!(!crc32_ieee_raw(!0, b"123456789"), 0xCBF4_3926, "raw CRC-32");
}
#[test]
#[cfg(feature = "gzip")]
fn raw_ieee_agrees_with_compcol() {
for data in [
&b""[..],
b"123456789",
b"the quick brown fox jumps over the lazy dog",
&[0xFFu8; 1024][..],
] {
let mut c = compcol::checksum::Crc32::new();
c.update(data);
assert_eq!(crc32(data), c.finalize(), "{} bytes", data.len());
assert_eq!(
!crc32_ieee_raw(!0, data),
c.finalize(),
"{} bytes",
data.len()
);
}
}
#[test]
fn appending_matches_one_shot() {
let data: Vec<u8> = (0..=255u8).cycle().take(1000).collect();
for cut in [0, 1, 7, 8, 9, 63, 64, 512, 999, 1000] {
let (a, b) = data.split_at(cut);
assert_eq!(
crc32c_append(crc32c(a), b),
crc32c(&data),
"crc32c split at {cut}"
);
assert_eq!(
crc32_append(crc32(a), b),
crc32(&data),
"crc32 split at {cut}"
);
assert_eq!(
crc32_ieee_raw(crc32_ieee_raw(!0, a), b),
crc32_ieee_raw(!0, &data),
"raw ieee split at {cut}"
);
}
}
#[test]
fn the_polynomials_differ() {
assert_ne!(crc32c(b"fstool"), !crc32_ieee_raw(!0, b"fstool"));
}
#[test]
fn slice_by_eight_matches_the_naive_loop() {
fn naive(poly: u32, mut s: u32, data: &[u8]) -> u32 {
for &b in data {
s ^= b as u32;
for _ in 0..8 {
s = (s >> 1) ^ ((s & 1).wrapping_neg() & poly);
}
}
s
}
let data: Vec<u8> = (0..40u8).map(|i| i.wrapping_mul(37)).collect();
for n in 0..data.len() {
assert_eq!(
crc32c(&data[..n]),
!naive(0x82F6_3B78, !0, &data[..n]),
"crc32c len {n}"
);
assert_eq!(
crc32_ieee_raw(!0, &data[..n]),
naive(0xEDB8_8320, !0, &data[..n]),
"raw ieee len {n}"
);
}
}
}