#[cfg(feature = "alloc")]
use alloc::fmt::{self, Debug, Display, Formatter};
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
#[cfg(feature = "heapless")]
use heapless::Vec as HeaplessVec;
use crate::constants::crc_u32::*;
use crate::lookup_table::LookUpTable;
#[allow(clippy::upper_case_acronyms)]
pub struct CRCu32 {
by_table: bool,
poly: u32,
lookup_table: LookUpTable<u32>,
sum: u32,
pub(crate) bits: u8,
high_bit: u32,
mask: u32,
initial: u32,
final_xor: u32,
reflect: bool,
reorder: bool,
}
#[cfg(feature = "alloc")]
impl Debug for CRCu32 {
#[inline]
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
if self.by_table {
debug_helper::impl_debug_for_struct!(CRCu64, f, self, let .lookup_table = self.lookup_table.as_ref(), (.sum, "0x{:08X}", self.sum), .bits, (.initial, "0x{:08X}", self.initial), (.final_xor, "0x{:08X}", self.final_xor), .reflect, .reorder);
} else {
debug_helper::impl_debug_for_struct!(CRCu64, f, self, (.poly, "0x{:08X}", self.poly), (.sum, "0x{:08X}", self.sum), .bits, (.initial, "0x{:08X}", self.initial), (.final_xor, "0x{:08X}", self.final_xor), .reflect, .reorder);
}
}
}
#[cfg(feature = "alloc")]
impl Display for CRCu32 {
#[inline]
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
f.write_fmt(format_args!("0x{:01$X}", self.get_crc(), (self.bits as usize + 3) >> 2))
}
}
impl CRCu32 {
pub fn create_crc(poly: u32, bits: u8, initial: u32, final_xor: u32, reflect: bool) -> CRCu32 {
debug_assert!(bits <= 32 && bits > 0);
if bits % 8 == 0 {
let lookup_table = if reflect {
LookUpTable::Dynamic(Self::crc_reflect_table(poly))
} else {
LookUpTable::Dynamic(Self::crc_table(poly, bits))
};
Self::create_crc_with_exists_lookup_table(
lookup_table,
bits,
initial,
final_xor,
reflect,
)
} else {
Self::create(
false,
LookUpTable::Static(&[0u32; 256]),
poly,
bits,
initial,
final_xor,
reflect,
)
}
}
#[inline]
pub(crate) fn create_crc_with_exists_lookup_table(
lookup_table: LookUpTable<u32>,
bits: u8,
initial: u32,
final_xor: u32,
reflect: bool,
) -> CRCu32 {
debug_assert!(bits % 8 == 0);
Self::create(true, lookup_table, 0, bits, initial, final_xor, reflect)
}
#[inline]
fn create(
by_table: bool,
lookup_table: LookUpTable<u32>,
mut poly: u32,
bits: u8,
initial: u32,
final_xor: u32,
reflect: bool,
) -> CRCu32 {
let high_bit = 1 << u32::from(bits - 1);
let mask = ((high_bit - 1) << 1) | 1;
let sum = if reflect {
Self::reflect_function(high_bit, initial)
} else {
initial
};
if !by_table && reflect {
poly = Self::reflect_function(high_bit, poly);
}
CRCu32 {
by_table,
poly,
lookup_table,
sum,
bits,
high_bit,
mask,
initial,
final_xor,
reflect,
reorder: false,
}
}
#[inline]
fn reflect_function(high_bit: u32, n: u32) -> u32 {
let mut i = high_bit;
let mut j = 1;
let mut out = 0;
while i != 0 {
if n & i != 0 {
out |= j;
}
j <<= 1;
i >>= 1;
}
out
}
#[inline]
fn reflect_method(&self, n: u32) -> u32 {
Self::reflect_function(self.high_bit, n)
}
pub fn digest<T: ?Sized + AsRef<[u8]>>(&mut self, data: &T) {
if self.by_table {
if self.bits == 8 {
for n in data.as_ref().iter().copied() {
let index = (self.sum as u8 ^ n) as usize;
self.sum = self.lookup_table[index];
}
} else if self.reflect {
for n in data.as_ref().iter().copied() {
let index = ((self.sum as u8) ^ n) as usize;
self.sum = (self.sum >> 8) ^ self.lookup_table[index];
}
} else {
for n in data.as_ref().iter().copied() {
let index = ((self.sum >> u32::from(self.bits - 8)) as u8 ^ n) as usize;
self.sum = (self.sum << 8) ^ self.lookup_table[index];
}
}
} else if self.reflect {
for n in data.as_ref().iter().copied() {
let n = super::crc_u8::CRCu8::reflect_function(0x80, n);
let mut i = 0x80;
while i != 0 {
let mut bit = self.sum & self.high_bit;
self.sum <<= 1;
if n & i != 0 {
bit ^= self.high_bit;
}
if bit != 0 {
self.sum ^= self.poly;
}
i >>= 1;
}
}
} else {
for n in data.as_ref().iter().copied() {
let mut i = 0x80;
while i != 0 {
let mut bit = self.sum & self.high_bit;
self.sum <<= 1;
if n & i != 0 {
bit ^= self.high_bit;
}
if bit != 0 {
self.sum ^= self.poly;
}
i >>= 1;
}
}
}
}
pub fn reset(&mut self) {
self.sum = self.initial;
}
pub fn get_crc(&self) -> u32 {
let sum = if self.by_table || !self.reflect {
(self.sum ^ self.final_xor) & self.mask
} else {
(self.reflect_method(self.sum) ^ self.final_xor) & self.mask
};
if self.reorder {
let mut new_sum = 0;
let e = (self.bits as u32 + 7) >> 3;
let e_dec = e - 1;
for i in 0..e {
new_sum |= ((sum >> ((e_dec - i) * 8)) & 0xFF) << (i * 8);
}
new_sum
} else {
sum
}
}
fn crc_reflect_table(poly_rev: u32) -> [u32; 256] {
let mut lookup_table = [0u32; 256];
for (i, e) in lookup_table.iter_mut().enumerate() {
let mut v = i as u32;
#[allow(clippy::branches_sharing_code)]
for _ in 0..8u8 {
if v & 1 != 0 {
v >>= 1;
v ^= poly_rev;
} else {
v >>= 1;
}
}
*e = v;
}
lookup_table
}
fn crc_table(poly: u32, bits: u8) -> [u32; 256] {
let mut lookup_table = [0u32; 256];
let mask1 = 1u32 << u32::from(bits - 1);
let mask2 = ((mask1 - 1) << 1) | 1;
for (i, e) in lookup_table.iter_mut().enumerate() {
let mut v = i as u32;
#[allow(clippy::branches_sharing_code)]
for _ in 0..bits {
if v & mask1 == 0 {
v <<= 1;
} else {
v <<= 1;
v ^= poly;
}
}
*e = v & mask2;
}
lookup_table
}
}
#[cfg(feature = "alloc")]
impl CRCu32 {
#[inline]
pub fn get_crc_vec_le(&mut self) -> Vec<u8> {
let crc = self.get_crc();
let e = (self.bits as usize + 7) >> 3;
crc.to_le_bytes()[..e].to_vec()
}
#[inline]
pub fn get_crc_vec_be(&mut self) -> Vec<u8> {
let crc = self.get_crc();
let e = (self.bits as usize + 7) >> 3;
crc.to_be_bytes()[(4 - e)..].to_vec()
}
}
#[cfg(feature = "heapless")]
impl CRCu32 {
#[inline]
pub fn get_crc_heapless_vec_le(&mut self) -> HeaplessVec<u8, 4> {
let crc = self.get_crc();
let e = (self.bits as usize + 7) >> 3;
let mut vec = HeaplessVec::new();
vec.extend_from_slice(&crc.to_le_bytes()[..e]).unwrap();
vec
}
#[inline]
pub fn get_crc_heapless_vec_be(&mut self) -> HeaplessVec<u8, 4> {
let crc = self.get_crc();
let e = (self.bits as usize + 7) >> 3;
let mut vec = HeaplessVec::new();
vec.extend_from_slice(&crc.to_be_bytes()[(4 - e)..]).unwrap();
vec
}
}
impl CRCu32 {
#[cfg_attr(feature = "alloc", doc = "assert_eq!(\"0x04F03\", &crc.to_string());")]
pub fn crc17can() -> CRCu32 {
Self::create_crc(0x0001685B, 17, 0x00000000, 0x00000000, false)
}
#[cfg_attr(feature = "alloc", doc = "assert_eq!(\"0x0ED841\", &crc.to_string());")]
pub fn crc21can() -> CRCu32 {
Self::create_crc(0x00102899, 21, 0x00000000, 0x00000000, false)
}
#[cfg_attr(feature = "alloc", doc = "assert_eq!(\"0x21CF02\", &crc.to_string());")]
pub fn crc24() -> CRCu32 {
let lookup_table = LookUpTable::Static(&NO_REF_24_00864CFB);
Self::create_crc_with_exists_lookup_table(lookup_table, 24, 0x00B704CE, 0x00000000, false)
}
#[cfg_attr(feature = "alloc", doc = "assert_eq!(\"0xC25A56\", &crc.to_string());")]
pub fn crc24ble() -> CRCu32 {
let lookup_table = LookUpTable::Static(&REF_24_00DA6000);
Self::create_crc_with_exists_lookup_table(lookup_table, 24, 0x00555555, 0x00000000, true)
}
#[cfg_attr(feature = "alloc", doc = "assert_eq!(\"0x7979BD\", &crc.to_string());")]
pub fn crc24flexray_a() -> CRCu32 {
let lookup_table = LookUpTable::Static(&NO_REF_24_005D6DCB);
Self::create_crc_with_exists_lookup_table(lookup_table, 24, 0x00FEDCBA, 0x00000000, false)
}
#[cfg_attr(feature = "alloc", doc = "assert_eq!(\"0x1F23B8\", &crc.to_string());")]
pub fn crc24flexray_b() -> CRCu32 {
let lookup_table = LookUpTable::Static(&NO_REF_24_005D6DCB);
Self::create_crc_with_exists_lookup_table(lookup_table, 24, 0x00ABCDEF, 0x00000000, false)
}
#[cfg_attr(feature = "alloc", doc = "assert_eq!(\"0xCDE703\", &crc.to_string());")]
pub fn crc24lte_a() -> CRCu32 {
let lookup_table = LookUpTable::Static(&NO_REF_24_00864CFB);
Self::create_crc_with_exists_lookup_table(lookup_table, 24, 0x00000000, 0x00000000, false)
}
#[cfg_attr(feature = "alloc", doc = "assert_eq!(\"0x23EF52\", &crc.to_string());")]
pub fn crc24lte_b() -> CRCu32 {
let lookup_table = LookUpTable::Static(&NO_REF_24_00800063);
Self::create_crc_with_exists_lookup_table(lookup_table, 24, 0x00000000, 0x00000000, false)
}
#[cfg_attr(feature = "alloc", doc = "assert_eq!(\"0x200FA5\", &crc.to_string());")]
pub fn crc24os9() -> CRCu32 {
let lookup_table = LookUpTable::Static(&NO_REF_24_00800063);
Self::create_crc_with_exists_lookup_table(lookup_table, 24, 0x00FFFFFF, 0x00FFFFFF, false)
}
#[cfg_attr(feature = "alloc", doc = "assert_eq!(\"0x04C34ABF\", &crc.to_string());")]
pub fn crc30cdma() -> CRCu32 {
Self::create_crc(0x2030B9C7, 30, 0x3FFFFFFF, 0x3FFFFFFF, false)
}
#[cfg_attr(feature = "alloc", doc = "assert_eq!(\"0xCBF43926\", &crc.to_string());")]
pub fn crc32() -> CRCu32 {
let lookup_table = LookUpTable::Static(&REF_32_EDB88320);
Self::create_crc_with_exists_lookup_table(lookup_table, 32, 0xFFFFFFFF, 0xFFFFFFFF, true)
}
#[cfg_attr(feature = "alloc", doc = "assert_eq!(\"0x181989FC\", &crc.to_string());")]
pub fn crc32mhash() -> CRCu32 {
let lookup_table = LookUpTable::Static(&NO_REF_32_04C11DB7);
let mut crc = Self::create_crc_with_exists_lookup_table(
lookup_table,
32,
0xFFFFFFFF,
0xFFFFFFFF,
false,
);
crc.reorder = true;
crc
}
#[cfg_attr(feature = "alloc", doc = "assert_eq!(\"0xFC891918\", &crc.to_string());")]
pub fn crc32bzip2() -> CRCu32 {
let lookup_table = LookUpTable::Static(&NO_REF_32_04C11DB7);
Self::create_crc_with_exists_lookup_table(lookup_table, 32, 0xFFFFFFFF, 0xFFFFFFFF, false)
}
#[cfg_attr(feature = "alloc", doc = "assert_eq!(\"0xE3069283\", &crc.to_string());")]
pub fn crc32c() -> CRCu32 {
let lookup_table = LookUpTable::Static(&REF_32_82F63B78);
Self::create_crc_with_exists_lookup_table(lookup_table, 32, 0xFFFFFFFF, 0xFFFFFFFF, true)
}
#[cfg_attr(feature = "alloc", doc = "assert_eq!(\"0x87315576\", &crc.to_string());")]
pub fn crc32d() -> CRCu32 {
let lookup_table = LookUpTable::Static(&REF_32_D419CC15);
Self::create_crc_with_exists_lookup_table(lookup_table, 32, 0xFFFFFFFF, 0xFFFFFFFF, true)
}
#[cfg_attr(feature = "alloc", doc = "assert_eq!(\"0x0376E6E7\", &crc.to_string());")]
pub fn crc32mpeg2() -> CRCu32 {
let lookup_table = LookUpTable::Static(&NO_REF_32_04C11DB7);
Self::create_crc_with_exists_lookup_table(lookup_table, 32, 0xFFFFFFFF, 0x00000000, false)
}
#[cfg_attr(feature = "alloc", doc = "assert_eq!(\"0x765E7680\", &crc.to_string());")]
pub fn crc32posix() -> CRCu32 {
let lookup_table = LookUpTable::Static(&NO_REF_32_04C11DB7);
Self::create_crc_with_exists_lookup_table(lookup_table, 32, 0x00000000, 0xFFFFFFFF, false)
}
#[cfg_attr(feature = "alloc", doc = "assert_eq!(\"0x3010BF7F\", &crc.to_string());")]
pub fn crc32q() -> CRCu32 {
let lookup_table = LookUpTable::Static(&NO_REF_32_814141AB);
Self::create_crc_with_exists_lookup_table(lookup_table, 32, 0x00000000, 0x00000000, false)
}
#[cfg_attr(feature = "alloc", doc = "assert_eq!(\"0x340BC6D9\", &crc.to_string());")]
pub fn crc32jamcrc() -> CRCu32 {
let lookup_table = LookUpTable::Static(&REF_32_EDB88320);
Self::create_crc_with_exists_lookup_table(lookup_table, 32, 0xFFFFFFFF, 0x00000000, true)
}
#[cfg_attr(feature = "alloc", doc = "assert_eq!(\"0xBD0BE338\", &crc.to_string());")]
pub fn crc32xfer() -> CRCu32 {
let lookup_table = LookUpTable::Static(&NO_REF_32_000000AF);
Self::create_crc_with_exists_lookup_table(lookup_table, 32, 0x00000000, 0x00000000, false)
}
}
#[cfg(all(feature = "development", test))]
mod tests {
use super::CRCu32;
use alloc::fmt::Write;
use alloc::string::String;
#[test]
fn print_lookup_table() {
let crc = CRCu32::crc24ble();
let mut s = String::new();
for n in crc.lookup_table.iter().take(255) {
s.write_fmt(format_args!("{}u32, ", n)).unwrap();
}
s.write_fmt(format_args!("{}u32", crc.lookup_table[255])).unwrap();
println!("let lookup_table = [{}];", s);
}
}