const POLY: u32 = 0x82F6_3B78;
const TABLES: [[u32; 256]; 8] = generate_tables();
const fn generate_tables() -> [[u32; 256]; 8] {
let mut tables = [[0u32; 256]; 8];
let mut n = 0;
while n < 256 {
let mut crc = n as u32;
let mut bit = 0;
while bit < 8 {
crc = if crc & 1 != 0 {
(crc >> 1) ^ POLY
} else {
crc >> 1
};
bit += 1;
}
tables[0][n] = crc;
n += 1;
}
let mut slice = 1;
while slice < 8 {
let mut i = 0;
while i < 256 {
let prev = tables[slice - 1][i];
tables[slice][i] = (prev >> 8) ^ tables[0][(prev & 0xff) as usize];
i += 1;
}
slice += 1;
}
tables
}
#[inline]
fn process(mut crc: u32, data: &[u8]) -> u32 {
let mut chunks = data.chunks_exact(8);
for chunk in &mut chunks {
let lo = u32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]);
let hi = u32::from_le_bytes([chunk[4], chunk[5], chunk[6], chunk[7]]);
crc ^= lo;
crc = TABLES[7][(crc & 0xff) as usize]
^ TABLES[6][((crc >> 8) & 0xff) as usize]
^ TABLES[5][((crc >> 16) & 0xff) as usize]
^ TABLES[4][((crc >> 24) & 0xff) as usize]
^ TABLES[3][(hi & 0xff) as usize]
^ TABLES[2][((hi >> 8) & 0xff) as usize]
^ TABLES[1][((hi >> 16) & 0xff) as usize]
^ TABLES[0][((hi >> 24) & 0xff) as usize];
}
for &byte in chunks.remainder() {
crc = (crc >> 8) ^ TABLES[0][((crc ^ byte as u32) & 0xff) as usize];
}
crc
}
#[derive(Debug, Clone)]
pub struct Crc32c {
state: u32,
}
impl Default for Crc32c {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl Crc32c {
#[inline]
#[must_use]
pub const fn new() -> Self {
Self { state: u32::MAX }
}
#[inline]
pub fn update(&mut self, data: &[u8]) {
self.state = process(self.state, data);
}
#[inline]
#[must_use]
pub fn finalize(self) -> u32 {
self.state ^ u32::MAX
}
}
#[inline]
#[must_use]
pub fn crc32c(data: &[u8]) -> u32 {
let mut h = Crc32c::new();
h.update(data);
h.finalize()
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used, clippy::expect_used)]
use super::*;
#[test]
fn test_crc32c_empty_is_zero() {
assert_eq!(crc32c(b""), 0);
}
#[test]
fn test_crc32c_check_vector_matches_standard() {
assert_eq!(crc32c(b"123456789"), 0xE306_9283);
}
#[test]
fn test_crc32c_streaming_equals_one_shot() {
let data = b"the quick brown fox jumps over the lazy dog";
for split in 0..=data.len() {
let (a, b) = data.split_at(split);
let mut h = Crc32c::new();
h.update(a);
h.update(b);
assert_eq!(h.finalize(), crc32c(data), "split at {split}");
}
}
#[test]
fn test_crc32c_single_bit_flip_changes_value() {
let mut data = vec![0u8; 4096];
for (i, b) in data.iter_mut().enumerate() {
*b = i as u8;
}
let base = crc32c(&data);
let mut flipped = data.clone();
flipped[2048] ^= 0x01;
assert_ne!(crc32c(&flipped), base);
}
}