#![no_std]
use core::mem::size_of;
pub struct CrcAlgo<T> {
poly: T,
offset: usize,
init: T,
xorout: T,
reflect: bool,
table: [T; 256],
}
pub struct Crc<T> {
crc: T,
algo: CrcAlgo<T>,
}
macro_rules! crc_impl {
($($t:tt)*) => ($(
impl CrcAlgo<$t> {
const fn make_table(poly: $t, reflect: bool)-> [$t; 256] {
const fn table_value_reflect(index: usize, poly: $t) -> $t {
let mut v = index as $t;
let mut i = 0;
while i < 8 {
if v.trailing_zeros() == 0 {
v = v >> 1 ^ poly;
} else {
v >>= 1;
}
i += 1;
}
v
}
const fn table_value(index: usize, poly: $t) -> $t {
let mut v = (index as $t) << size_of::<$t>() * 8 - 8;
let mut i = 0;
while i < 8 {
if v.leading_zeros() == 0 {
v = v << 1 ^ poly;
} else {
v <<= 1;
}
i += 1;
}
v
}
if reflect {
crc_all_macros::make_crc_table!(table_value_reflect, poly)
} else {
crc_all_macros::make_crc_table!(table_value, poly)
}
}
pub const fn new(poly: $t, width: usize, init: $t, xorout: $t, reflect: bool) -> Self {
let offset = size_of::<$t>() * 8 - width;
let poly = if reflect { (poly << offset).reverse_bits() } else { poly << offset };
let init = if reflect { init.reverse_bits() >> offset } else { init };
Self {
poly,
offset,
init,
xorout,
reflect,
table: Self::make_table(poly, reflect),
}
}
pub fn update_crc(&self, crc: &mut $t, data: &[u8]) -> $t {
macro_rules! crc_update {
(u8) => {
if !self.reflect {
*crc <<= self.offset;
}
for b in data {
*crc = self.table[(*crc ^ b) as usize];
}
};
($_:ty) => {
if self.reflect {
for b in data {
*crc = *crc >> 8 ^ self.table[(crc.to_le_bytes()[0] ^ b) as usize];
}
} else {
*crc <<= self.offset;
for b in data {
*crc = *crc << 8 ^ self.table[(crc.to_be_bytes()[0] ^ b) as usize];
}
}
};
}
crc_update!($t);
self.finish_crc(crc)
}
pub fn update_bits_crc(&self, crc: &mut $t, bits: u8, offset: usize) -> $t {
assert!(!self.reflect);
assert!(offset < 8);
*crc ^= ((bits & 0xff << offset) as $t) << ((size_of::<$t>() - 1) * 8);
for _ in offset..8 {
if crc.leading_zeros() == 0 {
*crc = *crc << 1 ^ self.poly;
} else {
*crc <<= 1;
}
}
self.finish_crc(crc)
}
pub fn finish_crc(&self, crc: &$t) -> $t {
if self.reflect {
crc ^ self.xorout
} else {
crc >> self.offset ^ self.xorout
}
}
pub fn init_crc(&self, crc: &mut $t) {
*crc = self.init;
}
}
impl Crc<$t> {
pub const fn new(poly: $t, width: usize, init: $t, xorout: $t, reflect: bool) -> Self {
let algo: CrcAlgo<$t> = CrcAlgo::<$t>::new(poly, width, init, xorout, reflect);
Self {
crc: algo.init,
algo
}
}
pub fn update(&mut self, data: &[u8]) -> $t {
self.algo.update_crc(&mut self.crc, data)
}
pub fn update_bits(&mut self, bits: u8, offset: usize) -> $t {
self.algo.update_bits_crc(&mut self.crc, bits, offset)
}
pub fn finish(&self) -> $t {
self.algo.finish_crc(&self.crc)
}
pub fn init(&mut self) {
self.crc = self.algo.init;
}
}
)*)
}
crc_impl!(u8 u16 u32 u64 u128);