const POLY: u32 = 0x82F63B78;
const TABLE: [u32; 256] = {
let mut table = [0u32; 256];
let mut i = 0u32;
while i < 256 {
let mut crc = i;
let mut j = 0;
while j < 8 {
crc = if crc & 1 != 0 {
(crc >> 1) ^ POLY
} else {
crc >> 1
};
j += 1;
}
table[i as usize] = crc;
i += 1;
}
table
};
#[derive(Debug, Clone, Copy)]
pub(crate) struct Crc32cHasher {
state: u32,
}
impl Crc32cHasher {
pub(crate) const fn new() -> Self {
Self {
state: 0xFFFF_FFFFu32,
}
}
pub(crate) fn update(&mut self, data: &[u8]) {
let mut crc = self.state;
for &b in data {
let idx = ((crc ^ u32::from(b)) & 0xFF) as usize;
crc = (crc >> 8) ^ TABLE[idx];
}
self.state = crc;
}
pub(crate) fn finalize(self) -> u32 {
self.state ^ 0xFFFF_FFFF
}
}
#[cfg(test)]
mod tests {
use super::*;
fn hash(data: &[u8]) -> u32 {
let mut h = Crc32cHasher::new();
h.update(data);
h.finalize()
}
#[test]
fn known_vectors() {
assert_eq!(hash(b""), 0);
assert_eq!(hash(b"a"), 0xC1D04330);
assert_eq!(hash(&[0u8; 32]), 0x8A9136AA);
assert_eq!(hash(&[0xFFu8; 32]), 0x62A8AB43);
let mut buf = [0u8; 32];
for (i, b) in buf.iter_mut().enumerate() {
*b = i as u8;
}
assert_eq!(hash(&buf), 0x46DD794E);
}
#[test]
fn hasher_splits_match_oneshot() {
let data = b"the quick brown fox jumps over the lazy dog";
let oneshot = hash(data);
for split in 0..=data.len() {
let mut h = Crc32cHasher::new();
h.update(&data[..split]);
h.update(&data[split..]);
assert_eq!(h.finalize(), oneshot, "split at {split}");
}
}
}