pub trait Checksum {
const LEN: usize;
fn compute(&self, data: &[u8]) -> u64;
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
pub struct Crc16CcittFalse;
impl Checksum for Crc16CcittFalse {
const LEN: usize = 2;
#[inline]
fn compute(&self, data: &[u8]) -> u64 {
u64::from(crc16_ccitt_false(data))
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
pub struct Crc32;
impl Checksum for Crc32 {
const LEN: usize = 4;
#[inline]
fn compute(&self, data: &[u8]) -> u64 {
u64::from(crc32(data))
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
pub struct Crc32c;
impl Checksum for Crc32c {
const LEN: usize = 4;
#[inline]
fn compute(&self, data: &[u8]) -> u64 {
u64::from(crc32c(data))
}
}
#[inline]
#[must_use]
pub fn crc16_ccitt_false(data: &[u8]) -> u16 {
data.iter().fold(0xFFFF_u16, |crc, &byte| {
let index = usize::from((crc >> 8) as u8 ^ byte);
(crc << 8) ^ CRC16_CCITT_TABLE[index]
})
}
#[inline]
#[must_use]
pub fn crc32(data: &[u8]) -> u32 {
reflected_crc32(&CRC32_TABLE, data)
}
#[inline]
#[must_use]
pub fn crc32c(data: &[u8]) -> u32 {
reflected_crc32(&CRC32C_TABLE, data)
}
#[inline]
fn reflected_crc32(tables: &[[u32; 256]; 8], data: &[u8]) -> u32 {
let mut crc = !0u32;
let mut chunks = data.chunks_exact(8);
for chunk in &mut chunks {
let mut word = [0u8; 8];
word.copy_from_slice(chunk);
let lo = u32::from_le_bytes([word[0], word[1], word[2], word[3]]) ^ crc;
let hi = u32::from_le_bytes([word[4], word[5], word[6], word[7]]);
crc = tables[7][usize::from(lo as u8)]
^ tables[6][usize::from((lo >> 8) as u8)]
^ tables[5][usize::from((lo >> 16) as u8)]
^ tables[4][usize::from((lo >> 24) as u8)]
^ tables[3][usize::from(hi as u8)]
^ tables[2][usize::from((hi >> 8) as u8)]
^ tables[1][usize::from((hi >> 16) as u8)]
^ tables[0][usize::from((hi >> 24) as u8)];
}
for &byte in chunks.remainder() {
crc = (crc >> 8) ^ tables[0][usize::from(crc as u8 ^ byte)];
}
!crc
}
static CRC16_CCITT_TABLE: [u16; 256] = crc16_table(0x1021);
static CRC32_TABLE: [[u32; 256]; 8] = slice_by_8(reflected_crc32_table(0xEDB8_8320));
static CRC32C_TABLE: [[u32; 256]; 8] = slice_by_8(reflected_crc32_table(0x82F6_3B78));
const fn crc16_table(poly: u16) -> [u16; 256] {
let mut table = [0u16; 256];
let mut i = 0;
while i < 256 {
let mut crc = (i as u16) << 8;
let mut bit = 0;
while bit < 8 {
crc = if crc & 0x8000 != 0 {
(crc << 1) ^ poly
} else {
crc << 1
};
bit += 1;
}
table[i] = crc;
i += 1;
}
table
}
const fn slice_by_8(base: [u32; 256]) -> [[u32; 256]; 8] {
let mut tables = [[0u32; 256]; 8];
tables[0] = base;
let mut k = 1;
while k < 8 {
let mut i = 0;
while i < 256 {
let prev = tables[k - 1][i];
tables[k][i] = (prev >> 8) ^ base[(prev & 0xFF) as usize];
i += 1;
}
k += 1;
}
tables
}
const fn reflected_crc32_table(poly: u32) -> [u32; 256] {
let mut table = [0u32; 256];
let mut i = 0;
while i < 256 {
let mut crc = i as u32;
let mut bit = 0;
while bit < 8 {
crc = if crc & 1 != 0 {
(crc >> 1) ^ poly
} else {
crc >> 1
};
bit += 1;
}
table[i] = crc;
i += 1;
}
table
}
#[cfg(test)]
mod tests {
use super::*;
fn crc16_reference(data: &[u8]) -> u16 {
let mut crc = 0xFFFFu16;
for &byte in data {
crc ^= u16::from(byte) << 8;
for _ in 0..8 {
crc = if crc & 0x8000 != 0 {
(crc << 1) ^ 0x1021
} else {
crc << 1
};
}
}
crc
}
fn crc32_reference(poly: u32, data: &[u8]) -> u32 {
let mut crc = !0u32;
for &byte in data {
crc ^= u32::from(byte);
for _ in 0..8 {
crc = if crc & 1 != 0 {
(crc >> 1) ^ poly
} else {
crc >> 1
};
}
}
!crc
}
#[test]
fn catalogue_check_values() {
assert_eq!(crc16_ccitt_false(b"123456789"), 0x29B1);
assert_eq!(crc32(b"123456789"), 0xCBF4_3926);
assert_eq!(crc32c(b"123456789"), 0xE306_9283);
}
#[test]
fn tables_agree_with_bitwise_reference() {
let data: Vec<u8> = (0..=255u8).chain((0..=255u8).rev()).collect();
for len in [0, 1, 2, 7, 64, data.len()] {
let slice = &data[..len];
assert_eq!(crc16_ccitt_false(slice), crc16_reference(slice));
assert_eq!(crc32(slice), crc32_reference(0xEDB8_8320, slice));
assert_eq!(crc32c(slice), crc32_reference(0x82F6_3B78, slice));
}
}
#[test]
fn tables_rebuilt_at_runtime_match_the_statics() {
assert_eq!(crc16_table(0x1021), CRC16_CCITT_TABLE);
assert_eq!(slice_by_8(reflected_crc32_table(0xEDB8_8320)), CRC32_TABLE);
assert_eq!(slice_by_8(reflected_crc32_table(0x82F6_3B78)), CRC32C_TABLE);
assert_eq!(CRC32_TABLE[0][1], 0x7707_3096);
}
#[test]
fn trait_impls_report_width() {
assert_eq!(Crc16CcittFalse::LEN, 2);
assert_eq!(Crc32::LEN, 4);
assert_eq!(Crc32c::LEN, 4);
assert_eq!(
Crc16CcittFalse.compute(b"a"),
u64::from(crc16_ccitt_false(b"a"))
);
assert_eq!(Crc32.compute(b"a"), u64::from(crc32(b"a")));
assert_eq!(Crc32c.compute(b"a"), u64::from(crc32c(b"a")));
}
}