use crate::tables;
use crate::CrcAlgorithm;
use crate::CrcParams;
const fn crc16_single(poly: u16, reflect: bool, mut value: u16) -> u16 {
if reflect {
let mut i = 0;
while i < 8 {
value = (value >> 1) ^ ((value & 1) * poly);
i += 1;
}
} else {
value <<= 8;
let mut i = 0;
while i < 8 {
value = (value << 1) ^ (((value >> 15) & 1) * poly);
i += 1;
}
}
value
}
const fn crc32_single(poly: u32, reflect: bool, mut value: u32) -> u32 {
if reflect {
let mut i = 0;
while i < 8 {
value = (value >> 1) ^ ((value & 1) * poly);
i += 1;
}
} else {
value <<= 24;
let mut i = 0;
while i < 8 {
value = (value << 1) ^ (((value >> 31) & 1) * poly);
i += 1;
}
}
value
}
const fn crc64_single(poly: u64, reflect: bool, mut value: u64) -> u64 {
if reflect {
let mut i = 0;
while i < 8 {
value = (value >> 1) ^ ((value & 1) * poly);
i += 1;
}
} else {
value <<= 56;
let mut i = 0;
while i < 8 {
value = (value << 1) ^ (((value >> 63) & 1) * poly);
i += 1;
}
}
value
}
pub const fn generate_table_u16(width: u8, poly: u16, reflect: bool) -> [[u16; 256]; 16] {
let poly = if reflect {
let poly = poly.reverse_bits();
poly >> (16u8 - width)
} else {
poly << (16u8 - width)
};
let mut table = [[0u16; 256]; 16];
let mut i = 0;
while i < 256 {
table[0][i] = crc16_single(poly, reflect, i as u16);
i += 1;
}
let mut i = 0;
while i < 256 {
let mut e = 1;
while e < 16 {
let one_lower = table[e - 1][i];
if reflect {
table[e][i] = (one_lower >> 8) ^ table[0][(one_lower & 0xFF) as usize];
} else {
table[e][i] = (one_lower << 8) ^ table[0][((one_lower >> 8) & 0xFF) as usize];
}
e += 1;
}
i += 1;
}
table
}
pub const fn generate_table_u32(width: u8, poly: u32, reflect: bool) -> [[u32; 256]; 16] {
let poly = if reflect {
let poly = poly.reverse_bits();
poly >> (32u8 - width)
} else {
poly << (32u8 - width)
};
let mut table = [[0u32; 256]; 16];
let mut i = 0;
while i < 256 {
table[0][i] = crc32_single(poly, reflect, i as u32);
i += 1;
}
let mut i = 0;
while i < 256 {
let mut e = 1;
while e < 16 {
let one_lower = table[e - 1][i];
if reflect {
table[e][i] = (one_lower >> 8) ^ table[0][(one_lower & 0xFF) as usize];
} else {
table[e][i] = (one_lower << 8) ^ table[0][((one_lower >> 24) & 0xFF) as usize];
}
e += 1;
}
i += 1;
}
table
}
pub const fn generate_table_u64(width: u8, poly: u64, reflect: bool) -> [[u64; 256]; 16] {
let poly = if reflect {
let poly = poly.reverse_bits();
poly >> (64u8 - width)
} else {
poly << (64u8 - width)
};
let mut table = [[0u64; 256]; 16];
let mut i = 0;
while i < 256 {
table[0][i] = crc64_single(poly, reflect, i as u64);
i += 1;
}
let mut i = 0;
while i < 256 {
let mut e = 1;
while e < 16 {
let one_lower = table[e - 1][i];
if reflect {
table[e][i] = (one_lower >> 8) ^ table[0][(one_lower & 0xFF) as usize];
} else {
table[e][i] = (one_lower << 8) ^ table[0][((one_lower >> 56) & 0xFF) as usize];
}
e += 1;
}
i += 1;
}
table
}
#[cfg(feature = "alloc")]
#[cfg(feature = "std")]
use std::collections::HashMap;
#[cfg(feature = "alloc")]
#[cfg(feature = "std")]
use std::sync::{Mutex, OnceLock};
#[cfg(feature = "alloc")]
#[cfg(all(not(feature = "std"), feature = "cache"))]
use hashbrown::HashMap;
#[cfg(feature = "alloc")]
#[cfg(all(not(feature = "std"), feature = "cache"))]
use spin::{Mutex, Once};
#[cfg(feature = "alloc")]
#[cfg(any(feature = "std", feature = "cache"))]
type Crc16Key = (u16, u16, bool, bool, u16, u16);
#[cfg(feature = "alloc")]
#[cfg(any(feature = "std", feature = "cache"))]
type Crc32Key = (u32, u32, bool, bool, u32, u32);
#[cfg(feature = "alloc")]
#[cfg(any(feature = "std", feature = "cache"))]
type Crc64Key = (u64, u64, bool, bool, u64, u64);
#[cfg(feature = "alloc")]
#[cfg(any(feature = "std", feature = "cache"))]
type Crc16CacheValue = &'static [[u16; 256]; 16];
#[cfg(feature = "alloc")]
#[cfg(any(feature = "std", feature = "cache"))]
type Crc32CacheValue = &'static [[u32; 256]; 16];
#[cfg(feature = "alloc")]
#[cfg(any(feature = "std", feature = "cache"))]
type Crc64CacheValue = &'static [[u64; 256]; 16];
#[cfg(feature = "alloc")]
#[cfg(feature = "std")]
static CUSTOM_CRC16_CACHE: OnceLock<Mutex<HashMap<Crc16Key, Crc16CacheValue>>> = OnceLock::new();
#[cfg(feature = "alloc")]
#[cfg(feature = "std")]
static CUSTOM_CRC32_CACHE: OnceLock<Mutex<HashMap<Crc32Key, Crc32CacheValue>>> = OnceLock::new();
#[cfg(feature = "alloc")]
#[cfg(feature = "std")]
static CUSTOM_CRC64_CACHE: OnceLock<Mutex<HashMap<Crc64Key, Crc64CacheValue>>> = OnceLock::new();
#[cfg(feature = "alloc")]
#[cfg(all(not(feature = "std"), feature = "cache"))]
static CUSTOM_CRC16_CACHE: Once<Mutex<HashMap<Crc16Key, Crc16CacheValue>>> = Once::new();
#[cfg(feature = "alloc")]
#[cfg(all(not(feature = "std"), feature = "cache"))]
static CUSTOM_CRC32_CACHE: Once<Mutex<HashMap<Crc32Key, Crc32CacheValue>>> = Once::new();
#[cfg(feature = "alloc")]
#[cfg(all(not(feature = "std"), feature = "cache"))]
static CUSTOM_CRC64_CACHE: Once<Mutex<HashMap<Crc64Key, Crc64CacheValue>>> = Once::new();
#[allow(unused)]
#[allow(deprecated)]
pub(crate) fn update(state: u64, data: &[u8], params: &CrcParams) -> u64 {
match params.width {
16 => update_crc16(state as u16, data, params) as u64,
32 => update_crc32(state as u32, data, params) as u64,
64 => update_crc64(state, data, params),
_ => panic!("Unsupported CRC width: {}", params.width),
}
}
fn update_crc16(state: u16, data: &[u8], params: &CrcParams) -> u16 {
let (table, refin) = match params.algorithm {
CrcAlgorithm::Crc16Arc => (&tables::crc16::CRC16_ARC_TABLE, true),
CrcAlgorithm::Crc16Cdma2000 => (&tables::crc16::CRC16_CDMA2000_TABLE, false),
CrcAlgorithm::Crc16Cms => (&tables::crc16::CRC16_CMS_TABLE, false),
CrcAlgorithm::Crc16Dds110 => (&tables::crc16::CRC16_DDS_110_TABLE, false),
CrcAlgorithm::Crc16DectR => (&tables::crc16::CRC16_DECT_R_TABLE, false),
CrcAlgorithm::Crc16DectX => (&tables::crc16::CRC16_DECT_X_TABLE, false),
CrcAlgorithm::Crc16Dnp => (&tables::crc16::CRC16_DNP_TABLE, true),
CrcAlgorithm::Crc16En13757 => (&tables::crc16::CRC16_EN_13757_TABLE, false),
CrcAlgorithm::Crc16Genibus => (&tables::crc16::CRC16_GENIBUS_TABLE, false),
CrcAlgorithm::Crc16Gsm => (&tables::crc16::CRC16_GSM_TABLE, false),
CrcAlgorithm::Crc16Ibm3740 => (&tables::crc16::CRC16_IBM_3740_TABLE, false),
CrcAlgorithm::Crc16IbmSdlc => (&tables::crc16::CRC16_IBM_SDLC_TABLE, true),
CrcAlgorithm::Crc16IsoIec144433A => (&tables::crc16::CRC16_ISO_IEC_14443_3_A_TABLE, true),
CrcAlgorithm::Crc16Kermit => (&tables::crc16::CRC16_KERMIT_TABLE, true),
CrcAlgorithm::Crc16Lj1200 => (&tables::crc16::CRC16_LJ1200_TABLE, false),
CrcAlgorithm::Crc16M17 => (&tables::crc16::CRC16_M17_TABLE, false),
CrcAlgorithm::Crc16MaximDow => (&tables::crc16::CRC16_MAXIM_DOW_TABLE, true),
CrcAlgorithm::Crc16Mcrf4xx => (&tables::crc16::CRC16_MCRF4XX_TABLE, true),
CrcAlgorithm::Crc16Modbus => (&tables::crc16::CRC16_MODBUS_TABLE, true),
CrcAlgorithm::Crc16Nrsc5 => (&tables::crc16::CRC16_NRSC_5_TABLE, true),
CrcAlgorithm::Crc16OpensafetyA => (&tables::crc16::CRC16_OPENSAFETY_A_TABLE, false),
CrcAlgorithm::Crc16OpensafetyB => (&tables::crc16::CRC16_OPENSAFETY_B_TABLE, false),
CrcAlgorithm::Crc16Profibus => (&tables::crc16::CRC16_PROFIBUS_TABLE, false),
CrcAlgorithm::Crc16Riello => (&tables::crc16::CRC16_RIELLO_TABLE, true),
CrcAlgorithm::Crc16SpiFujitsu => (&tables::crc16::CRC16_SPI_FUJITSU_TABLE, false),
CrcAlgorithm::Crc16T10Dif => (&tables::crc16::CRC16_T10_DIF_TABLE, false),
CrcAlgorithm::Crc16Teledisk => (&tables::crc16::CRC16_TELEDISK_TABLE, false),
CrcAlgorithm::Crc16Tms37157 => (&tables::crc16::CRC16_TMS37157_TABLE, true),
CrcAlgorithm::Crc16Umts => (&tables::crc16::CRC16_UMTS_TABLE, false),
CrcAlgorithm::Crc16Usb => (&tables::crc16::CRC16_USB_TABLE, true),
CrcAlgorithm::Crc16Xmodem => (&tables::crc16::CRC16_XMODEM_TABLE, false),
CrcAlgorithm::CrcCustom => {
return update_crc16_custom(state, data, params);
}
_ => panic!("Invalid algorithm for u16 CRC"),
};
native_update_u16(state, table, refin, data)
}
#[cfg(feature = "alloc")]
fn update_crc16_custom(state: u16, data: &[u8], params: &CrcParams) -> u16 {
extern crate alloc;
use alloc::boxed::Box;
let refin = params.refin;
#[cfg(any(feature = "std", feature = "cache"))]
let table: &'static [[u16; 256]; 16] = {
let key: Crc16Key = (
params.poly as u16,
params.init as u16,
params.refin,
params.refout,
params.xorout as u16,
params.check as u16,
);
#[cfg(feature = "std")]
{
let cache = CUSTOM_CRC16_CACHE.get_or_init(|| Mutex::new(HashMap::new()));
let mut cache_guard = cache.lock().unwrap();
cache_guard.entry(key).or_insert_with(|| {
let table = generate_table_u16(params.width, params.poly as u16, refin);
Box::leak(Box::new(table))
})
}
#[cfg(all(not(feature = "std"), feature = "cache"))]
{
let cache = CUSTOM_CRC16_CACHE.call_once(|| Mutex::new(HashMap::new()));
let mut cache_guard = cache.lock();
cache_guard.entry(key).or_insert_with(|| {
let table = generate_table_u16(params.width, params.poly as u16, refin);
Box::leak(Box::new(table))
})
}
};
#[cfg(not(any(feature = "std", feature = "cache")))]
let table: &'static [[u16; 256]; 16] = {
let table = generate_table_u16(params.width, params.poly as u16, refin);
Box::leak(Box::new(table))
};
native_update_u16(state, table, refin, data)
}
#[cfg(not(feature = "alloc"))]
fn update_crc16_custom(_state: u16, _data: &[u8], _params: &CrcParams) -> u16 {
panic!("Custom CRC parameters require the 'alloc' feature")
}
fn update_crc32(state: u32, data: &[u8], params: &CrcParams) -> u32 {
let (table, refin) = match params.algorithm {
CrcAlgorithm::Crc32Aixm => (&tables::crc32::CRC32_AIXM_TABLE, false),
CrcAlgorithm::Crc32Autosar => (&tables::crc32::CRC32_AUTOSAR_TABLE, true),
CrcAlgorithm::Crc32Base91D => (&tables::crc32::CRC32_BASE91_D_TABLE, true),
CrcAlgorithm::Crc32Bzip2 => (&tables::crc32::CRC32_BZIP2_TABLE, false),
CrcAlgorithm::Crc32CdRomEdc => (&tables::crc32::CRC32_CD_ROM_EDC_TABLE, true),
CrcAlgorithm::Crc32Cksum => (&tables::crc32::CRC32_CKSUM_TABLE, false),
CrcAlgorithm::Crc32Iscsi => (&tables::crc32::CRC32_ISCSI_TABLE, true),
CrcAlgorithm::Crc32IsoHdlc => (&tables::crc32::CRC32_ISO_HDLC_TABLE, true),
CrcAlgorithm::Crc32Jamcrc => (&tables::crc32::CRC32_JAMCRC_TABLE, true),
CrcAlgorithm::Crc32Mef => (&tables::crc32::CRC32_MEF_TABLE, true),
CrcAlgorithm::Crc32Mpeg2 => (&tables::crc32::CRC32_MPEG_2_TABLE, false),
CrcAlgorithm::Crc32Xfer => (&tables::crc32::CRC32_XFER_TABLE, false),
#[allow(deprecated)]
CrcAlgorithm::Crc32Custom | CrcAlgorithm::CrcCustom => {
return update_crc32_custom(state, data, params);
}
_ => panic!("Invalid algorithm for u32 CRC"),
};
native_update_u32(state, table, refin, data)
}
#[cfg(feature = "alloc")]
fn update_crc32_custom(state: u32, data: &[u8], params: &CrcParams) -> u32 {
extern crate alloc;
use alloc::boxed::Box;
let refin = params.refin;
#[cfg(any(feature = "std", feature = "cache"))]
let table: &'static [[u32; 256]; 16] = {
let key: Crc32Key = (
params.poly as u32,
params.init as u32,
params.refin,
params.refout,
params.xorout as u32,
params.check as u32,
);
#[cfg(feature = "std")]
{
let cache = CUSTOM_CRC32_CACHE.get_or_init(|| Mutex::new(HashMap::new()));
let mut cache_guard = cache.lock().unwrap();
cache_guard.entry(key).or_insert_with(|| {
let table = generate_table_u32(params.width, params.poly as u32, refin);
Box::leak(Box::new(table))
})
}
#[cfg(all(not(feature = "std"), feature = "cache"))]
{
let cache = CUSTOM_CRC32_CACHE.call_once(|| Mutex::new(HashMap::new()));
let mut cache_guard = cache.lock();
cache_guard.entry(key).or_insert_with(|| {
let table = generate_table_u32(params.width, params.poly as u32, refin);
Box::leak(Box::new(table))
})
}
};
#[cfg(not(any(feature = "std", feature = "cache")))]
let table: &'static [[u32; 256]; 16] = {
let table = generate_table_u32(params.width, params.poly as u32, refin);
Box::leak(Box::new(table))
};
native_update_u32(state, table, refin, data)
}
#[cfg(not(feature = "alloc"))]
fn update_crc32_custom(_state: u32, _data: &[u8], _params: &CrcParams) -> u32 {
panic!("Custom CRC parameters require the 'alloc' feature")
}
fn update_crc64(state: u64, data: &[u8], params: &CrcParams) -> u64 {
let (table, refin) = match params.algorithm {
CrcAlgorithm::Crc64Ecma182 => (&tables::crc64::CRC64_ECMA_182_TABLE, false),
CrcAlgorithm::Crc64GoIso => (&tables::crc64::CRC64_GO_ISO_TABLE, true),
CrcAlgorithm::Crc64Ms => (&tables::crc64::CRC64_MS_TABLE, true),
CrcAlgorithm::Crc64Nvme => (&tables::crc64::CRC64_NVME_TABLE, true),
CrcAlgorithm::Crc64Redis => (&tables::crc64::CRC64_REDIS_TABLE, true),
CrcAlgorithm::Crc64We => (&tables::crc64::CRC64_WE_TABLE, false),
CrcAlgorithm::Crc64Xz => (&tables::crc64::CRC64_XZ_TABLE, true),
#[allow(deprecated)]
CrcAlgorithm::Crc64Custom | CrcAlgorithm::CrcCustom => {
return update_crc64_custom(state, data, params);
}
_ => panic!("Invalid algorithm for u64 CRC"),
};
native_update_u64(state, table, refin, data)
}
#[cfg(feature = "alloc")]
fn update_crc64_custom(state: u64, data: &[u8], params: &CrcParams) -> u64 {
extern crate alloc;
use alloc::boxed::Box;
let refin = params.refin;
#[cfg(any(feature = "std", feature = "cache"))]
let table: &'static [[u64; 256]; 16] = {
let key: Crc64Key = (
params.poly,
params.init,
params.refin,
params.refout,
params.xorout,
params.check,
);
#[cfg(feature = "std")]
{
let cache = CUSTOM_CRC64_CACHE.get_or_init(|| Mutex::new(HashMap::new()));
let mut cache_guard = cache.lock().unwrap();
cache_guard.entry(key).or_insert_with(|| {
let table = generate_table_u64(params.width, params.poly, refin);
Box::leak(Box::new(table))
})
}
#[cfg(all(not(feature = "std"), feature = "cache"))]
{
let cache = CUSTOM_CRC64_CACHE.call_once(|| Mutex::new(HashMap::new()));
let mut cache_guard = cache.lock();
cache_guard.entry(key).or_insert_with(|| {
let table = generate_table_u64(params.width, params.poly, refin);
Box::leak(Box::new(table))
})
}
};
#[cfg(not(any(feature = "std", feature = "cache")))]
let table: &'static [[u64; 256]; 16] = {
let table = generate_table_u64(params.width, params.poly, refin);
Box::leak(Box::new(table))
};
native_update_u64(state, table, refin, data)
}
#[cfg(not(feature = "alloc"))]
fn update_crc64_custom(_state: u64, _data: &[u8], _params: &CrcParams) -> u64 {
panic!("Custom CRC parameters require the 'alloc' feature")
}
#[allow(dead_code)]
pub(crate) fn native_update_u16(
mut crc: u16,
table: &[[u16; 256]; 16],
reflect: bool,
bytes: &[u8],
) -> u16 {
let len = bytes.len();
let mut i = 0;
while i + 16 <= len {
if reflect {
let current0 = bytes[i] ^ (crc as u8);
let current1 = bytes[i + 1] ^ ((crc >> 8) as u8);
crc = table[0][bytes[i + 15] as usize]
^ table[1][bytes[i + 14] as usize]
^ table[2][bytes[i + 13] as usize]
^ table[3][bytes[i + 12] as usize]
^ table[4][bytes[i + 11] as usize]
^ table[5][bytes[i + 10] as usize]
^ table[6][bytes[i + 9] as usize]
^ table[7][bytes[i + 8] as usize]
^ table[8][bytes[i + 7] as usize]
^ table[9][bytes[i + 6] as usize]
^ table[10][bytes[i + 5] as usize]
^ table[11][bytes[i + 4] as usize]
^ table[12][bytes[i + 3] as usize]
^ table[13][bytes[i + 2] as usize]
^ table[14][current1 as usize]
^ table[15][current0 as usize];
} else {
let current0 = bytes[i] ^ ((crc >> 8) as u8);
let current1 = bytes[i + 1] ^ (crc as u8);
crc = table[0][bytes[i + 15] as usize]
^ table[1][bytes[i + 14] as usize]
^ table[2][bytes[i + 13] as usize]
^ table[3][bytes[i + 12] as usize]
^ table[4][bytes[i + 11] as usize]
^ table[5][bytes[i + 10] as usize]
^ table[6][bytes[i + 9] as usize]
^ table[7][bytes[i + 8] as usize]
^ table[8][bytes[i + 7] as usize]
^ table[9][bytes[i + 6] as usize]
^ table[10][bytes[i + 5] as usize]
^ table[11][bytes[i + 4] as usize]
^ table[12][bytes[i + 3] as usize]
^ table[13][bytes[i + 2] as usize]
^ table[14][current1 as usize]
^ table[15][current0 as usize];
}
i += 16;
}
if reflect {
while i < len {
let table_index = ((crc ^ bytes[i] as u16) & 0xFF) as usize;
crc = table[0][table_index] ^ (crc >> 8);
i += 1;
}
} else {
while i < len {
let table_index = (((crc >> 8) ^ bytes[i] as u16) & 0xFF) as usize;
crc = table[0][table_index] ^ (crc << 8);
i += 1;
}
}
crc
}
#[allow(dead_code)]
pub(crate) fn native_update_u32(
mut crc: u32,
table: &[[u32; 256]; 16],
reflect: bool,
bytes: &[u8],
) -> u32 {
let len = bytes.len();
let mut i = 0;
while i + 16 <= len {
if reflect {
let current0 = bytes[i] ^ (crc as u8);
let current1 = bytes[i + 1] ^ ((crc >> 8) as u8);
let current2 = bytes[i + 2] ^ ((crc >> 16) as u8);
let current3 = bytes[i + 3] ^ ((crc >> 24) as u8);
crc = table[0][bytes[i + 15] as usize]
^ table[1][bytes[i + 14] as usize]
^ table[2][bytes[i + 13] as usize]
^ table[3][bytes[i + 12] as usize]
^ table[4][bytes[i + 11] as usize]
^ table[5][bytes[i + 10] as usize]
^ table[6][bytes[i + 9] as usize]
^ table[7][bytes[i + 8] as usize]
^ table[8][bytes[i + 7] as usize]
^ table[9][bytes[i + 6] as usize]
^ table[10][bytes[i + 5] as usize]
^ table[11][bytes[i + 4] as usize]
^ table[12][current3 as usize]
^ table[13][current2 as usize]
^ table[14][current1 as usize]
^ table[15][current0 as usize];
} else {
let current0 = bytes[i] ^ ((crc >> 24) as u8);
let current1 = bytes[i + 1] ^ ((crc >> 16) as u8);
let current2 = bytes[i + 2] ^ ((crc >> 8) as u8);
let current3 = bytes[i + 3] ^ (crc as u8);
crc = table[0][bytes[i + 15] as usize]
^ table[1][bytes[i + 14] as usize]
^ table[2][bytes[i + 13] as usize]
^ table[3][bytes[i + 12] as usize]
^ table[4][bytes[i + 11] as usize]
^ table[5][bytes[i + 10] as usize]
^ table[6][bytes[i + 9] as usize]
^ table[7][bytes[i + 8] as usize]
^ table[8][bytes[i + 7] as usize]
^ table[9][bytes[i + 6] as usize]
^ table[10][bytes[i + 5] as usize]
^ table[11][bytes[i + 4] as usize]
^ table[12][current3 as usize]
^ table[13][current2 as usize]
^ table[14][current1 as usize]
^ table[15][current0 as usize];
}
i += 16;
}
if reflect {
while i < len {
let table_index = ((crc ^ bytes[i] as u32) & 0xFF) as usize;
crc = table[0][table_index] ^ (crc >> 8);
i += 1;
}
} else {
while i < len {
let table_index = (((crc >> 24) ^ bytes[i] as u32) & 0xFF) as usize;
crc = table[0][table_index] ^ (crc << 8);
i += 1;
}
}
crc
}
#[allow(dead_code)]
pub(crate) fn native_update_u64(
mut crc: u64,
table: &[[u64; 256]; 16],
reflect: bool,
bytes: &[u8],
) -> u64 {
let len = bytes.len();
let mut i = 0;
while i + 16 <= len {
if reflect {
let current0 = bytes[i] ^ (crc as u8);
let current1 = bytes[i + 1] ^ ((crc >> 8) as u8);
let current2 = bytes[i + 2] ^ ((crc >> 16) as u8);
let current3 = bytes[i + 3] ^ ((crc >> 24) as u8);
let current4 = bytes[i + 4] ^ ((crc >> 32) as u8);
let current5 = bytes[i + 5] ^ ((crc >> 40) as u8);
let current6 = bytes[i + 6] ^ ((crc >> 48) as u8);
let current7 = bytes[i + 7] ^ ((crc >> 56) as u8);
crc = table[0][bytes[i + 15] as usize]
^ table[1][bytes[i + 14] as usize]
^ table[2][bytes[i + 13] as usize]
^ table[3][bytes[i + 12] as usize]
^ table[4][bytes[i + 11] as usize]
^ table[5][bytes[i + 10] as usize]
^ table[6][bytes[i + 9] as usize]
^ table[7][bytes[i + 8] as usize]
^ table[8][current7 as usize]
^ table[9][current6 as usize]
^ table[10][current5 as usize]
^ table[11][current4 as usize]
^ table[12][current3 as usize]
^ table[13][current2 as usize]
^ table[14][current1 as usize]
^ table[15][current0 as usize];
} else {
let current0 = bytes[i] ^ ((crc >> 56) as u8);
let current1 = bytes[i + 1] ^ ((crc >> 48) as u8);
let current2 = bytes[i + 2] ^ ((crc >> 40) as u8);
let current3 = bytes[i + 3] ^ ((crc >> 32) as u8);
let current4 = bytes[i + 4] ^ ((crc >> 24) as u8);
let current5 = bytes[i + 5] ^ ((crc >> 16) as u8);
let current6 = bytes[i + 6] ^ ((crc >> 8) as u8);
let current7 = bytes[i + 7] ^ (crc as u8);
crc = table[0][bytes[i + 15] as usize]
^ table[1][bytes[i + 14] as usize]
^ table[2][bytes[i + 13] as usize]
^ table[3][bytes[i + 12] as usize]
^ table[4][bytes[i + 11] as usize]
^ table[5][bytes[i + 10] as usize]
^ table[6][bytes[i + 9] as usize]
^ table[7][bytes[i + 8] as usize]
^ table[8][current7 as usize]
^ table[9][current6 as usize]
^ table[10][current5 as usize]
^ table[11][current4 as usize]
^ table[12][current3 as usize]
^ table[13][current2 as usize]
^ table[14][current1 as usize]
^ table[15][current0 as usize];
}
i += 16;
}
if reflect {
while i < len {
let table_index = ((crc ^ bytes[i] as u64) & 0xFF) as usize;
crc = table[0][table_index] ^ (crc >> 8);
i += 1;
}
} else {
while i < len {
let table_index = (((crc >> 56) ^ bytes[i] as u64) & 0xFF) as usize;
crc = table[0][table_index] ^ (crc << 8);
i += 1;
}
}
crc
}
#[cfg(test)]
mod property_tests {
use crate::test::consts::{
RUST_CRC16_ARC, RUST_CRC16_IBM_SDLC, RUST_CRC16_T10_DIF, RUST_CRC32_BZIP2,
RUST_CRC32_ISCSI, RUST_CRC32_ISO_HDLC, RUST_CRC64_ECMA_182, RUST_CRC64_NVME, RUST_CRC64_XZ,
TEST_CHECK_STRING,
};
use crate::test::miri_compatible_proptest_config;
use crate::{checksum, checksum_with_params, CrcAlgorithm, CrcParams, Digest};
use proptest::prelude::*;
proptest! {
#![proptest_config(miri_compatible_proptest_config())]
#[test]
fn prop_native_crc16_reflected_matches_reference(data in proptest::collection::vec(any::<u8>(), 0..1024)) {
let our_result = checksum(CrcAlgorithm::Crc16IbmSdlc, &data);
let reference_result = RUST_CRC16_IBM_SDLC.checksum(&data) as u64;
prop_assert_eq!(
our_result, reference_result,
"CRC-16/IBM-SDLC native mismatch for {} bytes: our=0x{:04X}, ref=0x{:04X}",
data.len(), our_result, reference_result
);
}
#[test]
fn prop_native_crc16_forward_matches_reference(data in proptest::collection::vec(any::<u8>(), 0..1024)) {
let our_result = checksum(CrcAlgorithm::Crc16T10Dif, &data);
let reference_result = RUST_CRC16_T10_DIF.checksum(&data) as u64;
prop_assert_eq!(
our_result, reference_result,
"CRC-16/T10-DIF native mismatch for {} bytes: our=0x{:04X}, ref=0x{:04X}",
data.len(), our_result, reference_result
);
}
#[test]
fn prop_native_crc32_reflected_matches_reference(data in proptest::collection::vec(any::<u8>(), 0..1024)) {
let our_result = checksum(CrcAlgorithm::Crc32IsoHdlc, &data);
let reference_result = RUST_CRC32_ISO_HDLC.checksum(&data) as u64;
prop_assert_eq!(
our_result, reference_result,
"CRC-32/ISO-HDLC native mismatch for {} bytes: our=0x{:08X}, ref=0x{:08X}",
data.len(), our_result, reference_result
);
}
#[test]
fn prop_native_crc32_forward_matches_reference(data in proptest::collection::vec(any::<u8>(), 0..1024)) {
let our_result = checksum(CrcAlgorithm::Crc32Bzip2, &data);
let reference_result = RUST_CRC32_BZIP2.checksum(&data) as u64;
prop_assert_eq!(
our_result, reference_result,
"CRC-32/BZIP2 native mismatch for {} bytes: our=0x{:08X}, ref=0x{:08X}",
data.len(), our_result, reference_result
);
}
#[test]
fn prop_native_crc64_reflected_matches_reference(data in proptest::collection::vec(any::<u8>(), 0..1024)) {
let our_result = checksum(CrcAlgorithm::Crc64Xz, &data);
let reference_result = RUST_CRC64_XZ.checksum(&data);
prop_assert_eq!(
our_result, reference_result,
"CRC-64/XZ native mismatch for {} bytes: our=0x{:016X}, ref=0x{:016X}",
data.len(), our_result, reference_result
);
}
#[test]
fn prop_native_crc64_forward_matches_reference(data in proptest::collection::vec(any::<u8>(), 0..1024)) {
let our_result = checksum(CrcAlgorithm::Crc64Ecma182, &data);
let reference_result = RUST_CRC64_ECMA_182.checksum(&data);
prop_assert_eq!(
our_result, reference_result,
"CRC-64/ECMA-182 native mismatch for {} bytes: our=0x{:016X}, ref=0x{:016X}",
data.len(), our_result, reference_result
);
}
#[test]
fn prop_incremental_update_crc32_equivalence(
data in proptest::collection::vec(any::<u8>(), 0..1024),
split_point in 0usize..=1024usize
) {
let split_point = split_point.min(data.len());
let (part1, part2) = data.split_at(split_point);
let mut digest = Digest::new(CrcAlgorithm::Crc32IsoHdlc);
digest.update(part1);
digest.update(part2);
let incremental_result = digest.finalize();
let single_result = checksum(CrcAlgorithm::Crc32IsoHdlc, &data);
prop_assert_eq!(
incremental_result, single_result,
"CRC-32/ISO-HDLC incremental mismatch: incremental=0x{:08X}, single=0x{:08X}, split_point={}",
incremental_result, single_result, split_point
);
}
#[test]
fn prop_incremental_update_crc64_equivalence(
data in proptest::collection::vec(any::<u8>(), 0..1024),
split_point in 0usize..=1024usize
) {
let split_point = split_point.min(data.len());
let (part1, part2) = data.split_at(split_point);
let mut digest = Digest::new(CrcAlgorithm::Crc64Nvme);
digest.update(part1);
digest.update(part2);
let incremental_result = digest.finalize();
let single_result = checksum(CrcAlgorithm::Crc64Nvme, &data);
prop_assert_eq!(
incremental_result, single_result,
"CRC-64/NVME incremental mismatch: incremental=0x{:016X}, single=0x{:016X}, split_point={}",
incremental_result, single_result, split_point
);
}
#[test]
fn prop_custom_params_crc16_matches_reference(data in proptest::collection::vec(any::<u8>(), 0..1024)) {
let custom_params = CrcParams::new(
"CRC-16/CUSTOM-ARC",
16,
0x8005, 0x0000, true, 0x0000, 0xBB3D, );
let our_result = checksum_with_params(custom_params, &data);
let reference_result = RUST_CRC16_ARC.checksum(&data) as u64;
prop_assert_eq!(
our_result, reference_result,
"CRC-16 custom params mismatch for {} bytes: our=0x{:04X}, ref=0x{:04X}",
data.len(), our_result, reference_result
);
}
#[test]
fn prop_custom_params_crc32_matches_reference(data in proptest::collection::vec(any::<u8>(), 0..1024)) {
let custom_params = CrcParams::new(
"CRC-32/CUSTOM-ISCSI",
32,
0x1EDC6F41, 0xFFFFFFFF, true, 0xFFFFFFFF, 0xE3069283, );
let our_result = checksum_with_params(custom_params, &data);
let reference_result = RUST_CRC32_ISCSI.checksum(&data) as u64;
prop_assert_eq!(
our_result, reference_result,
"CRC-32 custom params mismatch for {} bytes: our=0x{:08X}, ref=0x{:08X}",
data.len(), our_result, reference_result
);
}
#[test]
fn prop_custom_params_crc64_matches_reference(data in proptest::collection::vec(any::<u8>(), 0..1024)) {
let custom_params = CrcParams::new(
"CRC-64/CUSTOM-NVME",
64,
0xAD93D23594C93659, 0xFFFFFFFFFFFFFFFF, true, 0xFFFFFFFFFFFFFFFF, 0xAE8B14860A799888, );
let our_result = checksum_with_params(custom_params, &data);
let reference_result = RUST_CRC64_NVME.checksum(&data);
prop_assert_eq!(
our_result, reference_result,
"CRC-64 custom params mismatch for {} bytes: our=0x{:016X}, ref=0x{:016X}",
data.len(), our_result, reference_result
);
}
}
#[test]
fn test_check_values_crc16() {
use crate::crc16::consts::*;
let test_cases: &[(&str, CrcParams, u64)] = &[
("CRC-16/ARC", CRC16_ARC, 0xBB3D),
("CRC-16/CDMA2000", CRC16_CDMA2000, 0x4C06),
("CRC-16/CMS", CRC16_CMS, 0xAEE7),
("CRC-16/DDS-110", CRC16_DDS_110, 0x9ECF),
("CRC-16/DECT-R", CRC16_DECT_R, 0x007E),
("CRC-16/DECT-X", CRC16_DECT_X, 0x007F),
("CRC-16/DNP", CRC16_DNP, 0xEA82),
("CRC-16/EN-13757", CRC16_EN_13757, 0xC2B7),
("CRC-16/GENIBUS", CRC16_GENIBUS, 0xD64E),
("CRC-16/GSM", CRC16_GSM, 0xCE3C),
("CRC-16/IBM-3740", CRC16_IBM_3740, 0x29B1),
("CRC-16/IBM-SDLC", CRC16_IBM_SDLC, 0x906E),
("CRC-16/ISO-IEC-14443-3-A", CRC16_ISO_IEC_14443_3_A, 0xBF05),
("CRC-16/KERMIT", CRC16_KERMIT, 0x2189),
("CRC-16/LJ1200", CRC16_LJ1200, 0xBDF4),
("CRC-16/M17", CRC16_M17, 0x772B),
("CRC-16/MAXIM-DOW", CRC16_MAXIM_DOW, 0x44C2),
("CRC-16/MCRF4XX", CRC16_MCRF4XX, 0x6F91),
("CRC-16/MODBUS", CRC16_MODBUS, 0x4B37),
("CRC-16/NRSC-5", CRC16_NRSC_5, 0xA066),
("CRC-16/OPENSAFETY-A", CRC16_OPENSAFETY_A, 0x5D38),
("CRC-16/OPENSAFETY-B", CRC16_OPENSAFETY_B, 0x20FE),
("CRC-16/PROFIBUS", CRC16_PROFIBUS, 0xA819),
("CRC-16/RIELLO", CRC16_RIELLO, 0x63D0),
("CRC-16/SPI-FUJITSU", CRC16_SPI_FUJITSU, 0xE5CC),
("CRC-16/T10-DIF", CRC16_T10_DIF, 0xD0DB),
("CRC-16/TELEDISK", CRC16_TELEDISK, 0x0FB3),
("CRC-16/TMS37157", CRC16_TMS37157, 0x26B1),
("CRC-16/UMTS", CRC16_UMTS, 0xFEE8),
("CRC-16/USB", CRC16_USB, 0xB4C8),
("CRC-16/XMODEM", CRC16_XMODEM, 0x31C3),
];
for (name, params, expected_check) in test_cases {
let result = checksum_with_params(*params, TEST_CHECK_STRING);
assert_eq!(
result, *expected_check,
"{} check value mismatch: got 0x{:04X}, expected 0x{:04X}",
name, result, expected_check
);
}
}
#[test]
fn test_check_values_crc32() {
use crate::crc32::consts::*;
let test_cases: &[(&str, CrcParams, u64)] = &[
("CRC-32/AIXM", CRC32_AIXM, 0x3010BF7F),
("CRC-32/AUTOSAR", CRC32_AUTOSAR, 0x1697D06A),
("CRC-32/BASE91-D", CRC32_BASE91_D, 0x87315576),
("CRC-32/BZIP2", CRC32_BZIP2, 0xFC891918),
("CRC-32/CD-ROM-EDC", CRC32_CD_ROM_EDC, 0x6EC2EDC4),
("CRC-32/CKSUM", CRC32_CKSUM, 0x765E7680),
("CRC-32/ISCSI", CRC32_ISCSI, 0xE3069283),
("CRC-32/ISO-HDLC", CRC32_ISO_HDLC, 0xCBF43926),
("CRC-32/JAMCRC", CRC32_JAMCRC, 0x340BC6D9),
("CRC-32/MEF", CRC32_MEF, 0xD2C22F51),
("CRC-32/MPEG-2", CRC32_MPEG_2, 0x0376E6E7),
("CRC-32/XFER", CRC32_XFER, 0xBD0BE338),
];
for (name, params, expected_check) in test_cases {
let result = checksum_with_params(*params, TEST_CHECK_STRING);
assert_eq!(
result, *expected_check,
"{} check value mismatch: got 0x{:08X}, expected 0x{:08X}",
name, result, expected_check
);
}
}
#[test]
fn test_check_values_crc64() {
use crate::crc64::consts::*;
let test_cases: &[(&str, CrcParams, u64)] = &[
("CRC-64/ECMA-182", CRC64_ECMA_182, 0x6C40DF5F0B497347),
("CRC-64/GO-ISO", CRC64_GO_ISO, 0xB90956C775A41001),
("CRC-64/MS", CRC64_MS, 0x75D4B74F024ECEEA),
("CRC-64/NVME", CRC64_NVME, 0xAE8B14860A799888),
("CRC-64/REDIS", CRC64_REDIS, 0xE9C6D914C4B8D9CA),
("CRC-64/WE", CRC64_WE, 0x62EC59E3F1A4F00A),
("CRC-64/XZ", CRC64_XZ, 0x995DC9BBDF1939FA),
];
for (name, params, expected_check) in test_cases {
let result = checksum_with_params(*params, TEST_CHECK_STRING);
assert_eq!(
result, *expected_check,
"{} check value mismatch: got 0x{:016X}, expected 0x{:016X}",
name, result, expected_check
);
}
}
}