const CRC_TABLE: [u16; 16] = [
0x0000, 0xCC01, 0xD801, 0x1400, 0xF001, 0x3C00, 0x2800, 0xE401, 0xA001, 0x6C00, 0x7800, 0xB401,
0x5000, 0x9C01, 0x8801, 0x4400,
];
#[inline]
pub fn update(mut crc: u16, byte: u8) -> u16 {
let mut tmp = CRC_TABLE[(crc & 0xF) as usize];
crc = (crc >> 4) & 0x0FFF;
crc = crc ^ tmp ^ CRC_TABLE[(byte & 0xF) as usize];
tmp = CRC_TABLE[(crc & 0xF) as usize];
crc = (crc >> 4) & 0x0FFF;
crc = crc ^ tmp ^ CRC_TABLE[((byte >> 4) & 0xF) as usize];
crc
}
#[inline]
pub fn calculate(data: &[u8]) -> u16 {
data.iter().fold(0u16, |crc, &byte| update(crc, byte))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_input_is_zero() {
assert_eq!(calculate(&[]), 0);
}
#[test]
fn single_zero_byte_stays_zero() {
assert_eq!(update(0, 0), 0);
assert_eq!(calculate(&[0]), 0);
}
#[test]
fn calculate_matches_iterative_update() {
let data: &[u8] = &[0xDE, 0xAD, 0xBE, 0xEF, 0x01, 0x02, 0x03];
let one_shot = calculate(data);
let stepwise = data.iter().fold(0u16, |c, &b| update(c, b));
assert_eq!(one_shot, stepwise);
}
#[test]
fn split_and_combine_is_associative() {
let data: Vec<u8> = (0u8..=255).collect();
let full = calculate(&data);
let mid = data.len() / 2;
let prefix_crc = calculate(&data[..mid]);
let combined = data[mid..].iter().fold(prefix_crc, |c, &b| update(c, b));
assert_eq!(full, combined);
}
#[test]
fn fixed_known_value_after_one_byte() {
assert_eq!(update(0, 0x01), 0xC0C1);
}
}