#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(all(
feature = "panic-handler",
not(feature = "std"),
not(test),
not(doctest)
))]
#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
loop {}
}
#[cfg(all(
feature = "panic-handler",
feature = "alloc",
not(feature = "std"),
not(test),
not(doctest)
))]
#[global_allocator]
static ALLOCATOR: StubAllocator = StubAllocator;
#[cfg(all(
feature = "panic-handler",
feature = "alloc",
not(feature = "std"),
not(test),
not(doctest)
))]
struct StubAllocator;
#[cfg(all(
feature = "panic-handler",
feature = "alloc",
not(feature = "std"),
not(test),
not(doctest)
))]
unsafe impl core::alloc::GlobalAlloc for StubAllocator {
unsafe fn alloc(&self, _layout: core::alloc::Layout) -> *mut u8 {
core::ptr::null_mut()
}
unsafe fn dealloc(&self, _ptr: *mut u8, _layout: core::alloc::Layout) {}
}
use crate::crc16::consts::{
CRC16_ARC, CRC16_CDMA2000, CRC16_CMS, CRC16_DDS_110, CRC16_DECT_R, CRC16_DECT_X, CRC16_DNP,
CRC16_EN_13757, CRC16_GENIBUS, CRC16_GSM, CRC16_IBM_3740, CRC16_IBM_SDLC,
CRC16_ISO_IEC_14443_3_A, CRC16_KERMIT, CRC16_LJ1200, CRC16_M17, CRC16_MAXIM_DOW, CRC16_MCRF4XX,
CRC16_MODBUS, CRC16_NRSC_5, CRC16_OPENSAFETY_A, CRC16_OPENSAFETY_B, CRC16_PROFIBUS,
CRC16_RIELLO, CRC16_SPI_FUJITSU, CRC16_T10_DIF, CRC16_TELEDISK, CRC16_TMS37157, CRC16_UMTS,
CRC16_USB, CRC16_XMODEM,
};
use crate::crc32::consts::{
CRC32_AIXM, CRC32_AUTOSAR, CRC32_BASE91_D, CRC32_BZIP2, CRC32_CD_ROM_EDC, CRC32_CKSUM,
CRC32_ISCSI, CRC32_ISO_HDLC, CRC32_JAMCRC, CRC32_MEF, CRC32_MPEG_2, CRC32_XFER,
};
#[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))]
#[cfg(feature = "std")]
use crate::crc32::fusion;
use crate::crc64::consts::{
CRC64_ECMA_182, CRC64_GO_ISO, CRC64_MS, CRC64_NVME, CRC64_REDIS, CRC64_WE, CRC64_XZ,
};
use crate::structs::Calculator;
use crate::traits::CrcCalculator;
#[cfg(feature = "alloc")]
use digest::DynDigest;
#[cfg(feature = "alloc")]
use digest::InvalidBufferSize;
#[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))]
use crate::feature_detection::get_arch_ops;
#[cfg(feature = "std")]
use std::fs::File;
#[cfg(feature = "std")]
use std::io::{Read, Write};
#[cfg(all(feature = "alloc", not(feature = "std")))]
extern crate alloc;
#[cfg(all(feature = "alloc", not(feature = "std")))]
use alloc::boxed::Box;
#[cfg(all(feature = "alloc", not(feature = "std")))]
use alloc::string::String;
mod algorithm;
pub mod arch;
mod cache;
mod combine;
mod consts;
mod crc16;
mod crc32;
mod crc64;
mod enums;
mod feature_detection;
#[cfg(feature = "ffi")]
mod ffi;
mod generate;
mod structs;
mod tables;
mod test;
mod traits;
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum CrcAlgorithm {
CrcCustom,
Crc16Arc,
Crc16Cdma2000,
Crc16Cms,
Crc16Dds110,
Crc16DectR,
Crc16DectX,
Crc16Dnp,
Crc16En13757,
Crc16Genibus,
Crc16Gsm,
Crc16Ibm3740,
Crc16IbmSdlc,
Crc16IsoIec144433A,
Crc16Kermit,
Crc16Lj1200,
Crc16M17,
Crc16MaximDow,
Crc16Mcrf4xx,
Crc16Modbus,
Crc16Nrsc5,
Crc16OpensafetyA,
Crc16OpensafetyB,
Crc16Profibus,
Crc16Riello,
Crc16SpiFujitsu,
Crc16T10Dif,
Crc16Teledisk,
Crc16Tms37157,
Crc16Umts,
Crc16Usb,
Crc16Xmodem,
Crc32Aixm,
Crc32Autosar,
Crc32Base91D,
Crc32Bzip2,
Crc32CdRomEdc,
Crc32Cksum,
#[deprecated(
since = "1.9.0",
note = "Use CrcCustom instead, which works with any supported width (16, 32, 64)"
)]
Crc32Custom, Crc32Iscsi,
Crc32IsoHdlc,
Crc32Jamcrc,
Crc32Mef,
Crc32Mpeg2,
Crc32Xfer,
#[deprecated(
since = "1.9.0",
note = "Use CrcCustom instead, which works with any supported width (16, 32, 64)"
)]
Crc64Custom, Crc64Ecma182,
Crc64GoIso,
Crc64Ms,
Crc64Nvme,
Crc64Redis,
Crc64We,
Crc64Xz,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum CrcKeysStorage {
KeysFold256([u64; 23]),
KeysFutureTest([u64; 25]),
}
impl CrcKeysStorage {
#[inline(always)]
const fn get_key(self, index: usize) -> u64 {
match self {
CrcKeysStorage::KeysFold256(keys) => {
if index < 23 {
keys[index]
} else {
0
}
}
CrcKeysStorage::KeysFutureTest(keys) => {
if index < 25 {
keys[index]
} else {
0
}
}
}
}
#[inline(always)]
const fn key_count(self) -> usize {
match self {
CrcKeysStorage::KeysFold256(_) => 23,
CrcKeysStorage::KeysFutureTest(_) => 25,
}
}
#[inline(always)]
const fn from_keys_fold_256(keys: [u64; 23]) -> Self {
CrcKeysStorage::KeysFold256(keys)
}
#[inline(always)]
#[allow(dead_code)] const fn from_keys_fold_future_test(keys: [u64; 25]) -> Self {
CrcKeysStorage::KeysFutureTest(keys)
}
#[inline(always)]
pub fn to_keys_array_23(self) -> [u64; 23] {
match self {
CrcKeysStorage::KeysFold256(keys) => keys,
CrcKeysStorage::KeysFutureTest(keys) => {
let mut result = [0u64; 23];
result.copy_from_slice(&keys[..23]);
result
}
}
}
}
impl PartialEq<[u64; 23]> for CrcKeysStorage {
fn eq(&self, other: &[u64; 23]) -> bool {
self.to_keys_array_23() == *other
}
}
impl PartialEq<CrcKeysStorage> for [u64; 23] {
fn eq(&self, other: &CrcKeysStorage) -> bool {
*self == other.to_keys_array_23()
}
}
#[derive(Clone, Copy, Debug)]
pub struct CrcParams {
pub algorithm: CrcAlgorithm,
pub name: &'static str,
pub width: u8,
pub poly: u64,
pub init: u64,
pub init_algorithm: u64,
pub refin: bool,
pub refout: bool,
pub xorout: u64,
pub check: u64,
pub keys: CrcKeysStorage,
}
type CalculatorFn = fn(
u64, &[u8], &CrcParams, ) -> u64;
#[derive(Copy, Clone, Debug)]
pub struct Digest {
state: u64,
amount: u64,
params: CrcParams,
calculator: CalculatorFn,
}
#[cfg(feature = "alloc")]
impl DynDigest for Digest {
#[inline(always)]
fn update(&mut self, data: &[u8]) {
self.update(data);
}
#[inline(always)]
fn finalize_into(self, buf: &mut [u8]) -> Result<(), InvalidBufferSize> {
if buf.len() != self.output_size() {
return Err(InvalidBufferSize);
}
let result = self.finalize();
let be_bytes = result.to_be_bytes();
let start = 8 - self.output_size();
buf.copy_from_slice(&be_bytes[start..]);
Ok(())
}
#[inline(always)]
fn finalize_into_reset(&mut self, out: &mut [u8]) -> Result<(), InvalidBufferSize> {
if out.len() != self.output_size() {
return Err(InvalidBufferSize);
}
let result = self.finalize();
self.reset();
let be_bytes = result.to_be_bytes();
let start = 8 - self.output_size();
out.copy_from_slice(&be_bytes[start..]);
Ok(())
}
#[inline(always)]
fn reset(&mut self) {
self.reset();
}
#[inline(always)]
fn output_size(&self) -> usize {
self.params.width as usize / 8
}
fn box_clone(&self) -> Box<dyn DynDigest> {
Box::new(*self)
}
}
impl Digest {
#[inline(always)]
pub fn new(algorithm: CrcAlgorithm) -> Self {
let (calculator, params) = get_calculator_params(algorithm);
Self {
state: params.init_algorithm,
amount: 0,
params,
calculator,
}
}
#[inline(always)]
pub fn new_with_init_state(algorithm: CrcAlgorithm, init_state: u64) -> Self {
let (calculator, params) = get_calculator_params(algorithm);
Self {
state: init_state,
amount: 0,
params,
calculator,
}
}
#[inline(always)]
pub fn new_with_params(params: CrcParams) -> Self {
let calculator = Calculator::calculate as CalculatorFn;
Self {
state: params.init_algorithm,
amount: 0,
params,
calculator,
}
}
#[inline(always)]
pub fn update(&mut self, data: &[u8]) {
self.state = (self.calculator)(self.state, data, &self.params);
self.amount += data.len() as u64;
}
#[inline(always)]
pub fn finalize(&self) -> u64 {
self.state ^ self.params.xorout
}
#[inline(always)]
pub fn finalize_reset(&mut self) -> u64 {
let result = self.finalize();
self.reset();
result
}
#[inline(always)]
pub fn reset(&mut self) {
self.state = self.params.init_algorithm;
self.amount = 0;
}
#[inline(always)]
pub fn combine(&mut self, other: &Self) {
self.amount += other.amount;
let other_crc = other.finalize();
self.state = combine::checksums(
self.state ^ self.params.xorout,
other_crc,
other.amount,
&self.params,
) ^ self.params.xorout;
}
#[inline(always)]
pub fn get_amount(&self) -> u64 {
self.amount
}
#[inline(always)]
pub fn get_state(&self) -> u64 {
self.state
}
}
#[cfg(feature = "std")]
impl Write for Digest {
#[inline(always)]
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.update(buf);
Ok(buf.len())
}
#[inline(always)]
fn write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result<usize> {
let len: usize = bufs
.iter()
.map(|buf| {
self.update(buf);
buf.len()
})
.sum();
Ok(len)
}
#[inline(always)]
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
#[inline(always)]
fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> {
self.update(buf);
Ok(())
}
}
#[inline]
#[allow(deprecated)]
pub fn checksum(algorithm: CrcAlgorithm, buf: &[u8]) -> u64 {
match algorithm {
CrcAlgorithm::Crc16Arc => {
Calculator::calculate(CRC16_ARC.init, buf, &CRC16_ARC) ^ CRC16_ARC.xorout
}
CrcAlgorithm::Crc16Cdma2000 => {
Calculator::calculate(CRC16_CDMA2000.init, buf, &CRC16_CDMA2000) ^ CRC16_CDMA2000.xorout
}
CrcAlgorithm::Crc16Cms => {
Calculator::calculate(CRC16_CMS.init, buf, &CRC16_CMS) ^ CRC16_CMS.xorout
}
CrcAlgorithm::Crc16Dds110 => {
Calculator::calculate(CRC16_DDS_110.init, buf, &CRC16_DDS_110) ^ CRC16_DDS_110.xorout
}
CrcAlgorithm::Crc16DectR => {
Calculator::calculate(CRC16_DECT_R.init, buf, &CRC16_DECT_R) ^ CRC16_DECT_R.xorout
}
CrcAlgorithm::Crc16DectX => {
Calculator::calculate(CRC16_DECT_X.init, buf, &CRC16_DECT_X) ^ CRC16_DECT_X.xorout
}
CrcAlgorithm::Crc16Dnp => {
Calculator::calculate(CRC16_DNP.init, buf, &CRC16_DNP) ^ CRC16_DNP.xorout
}
CrcAlgorithm::Crc16En13757 => {
Calculator::calculate(CRC16_EN_13757.init, buf, &CRC16_EN_13757) ^ CRC16_EN_13757.xorout
}
CrcAlgorithm::Crc16Genibus => {
Calculator::calculate(CRC16_GENIBUS.init, buf, &CRC16_GENIBUS) ^ CRC16_GENIBUS.xorout
}
CrcAlgorithm::Crc16Gsm => {
Calculator::calculate(CRC16_GSM.init, buf, &CRC16_GSM) ^ CRC16_GSM.xorout
}
CrcAlgorithm::Crc16Ibm3740 => {
Calculator::calculate(CRC16_IBM_3740.init, buf, &CRC16_IBM_3740) ^ CRC16_IBM_3740.xorout
}
CrcAlgorithm::Crc16IbmSdlc => {
Calculator::calculate(CRC16_IBM_SDLC.init, buf, &CRC16_IBM_SDLC) ^ CRC16_IBM_SDLC.xorout
}
CrcAlgorithm::Crc16IsoIec144433A => {
Calculator::calculate(
CRC16_ISO_IEC_14443_3_A.init_algorithm,
buf,
&CRC16_ISO_IEC_14443_3_A,
) ^ CRC16_ISO_IEC_14443_3_A.xorout
}
CrcAlgorithm::Crc16Kermit => {
Calculator::calculate(CRC16_KERMIT.init, buf, &CRC16_KERMIT) ^ CRC16_KERMIT.xorout
}
CrcAlgorithm::Crc16Lj1200 => {
Calculator::calculate(CRC16_LJ1200.init, buf, &CRC16_LJ1200) ^ CRC16_LJ1200.xorout
}
CrcAlgorithm::Crc16M17 => {
Calculator::calculate(CRC16_M17.init, buf, &CRC16_M17) ^ CRC16_M17.xorout
}
CrcAlgorithm::Crc16MaximDow => {
Calculator::calculate(CRC16_MAXIM_DOW.init, buf, &CRC16_MAXIM_DOW)
^ CRC16_MAXIM_DOW.xorout
}
CrcAlgorithm::Crc16Mcrf4xx => {
Calculator::calculate(CRC16_MCRF4XX.init, buf, &CRC16_MCRF4XX) ^ CRC16_MCRF4XX.xorout
}
CrcAlgorithm::Crc16Modbus => {
Calculator::calculate(CRC16_MODBUS.init, buf, &CRC16_MODBUS) ^ CRC16_MODBUS.xorout
}
CrcAlgorithm::Crc16Nrsc5 => {
Calculator::calculate(CRC16_NRSC_5.init, buf, &CRC16_NRSC_5) ^ CRC16_NRSC_5.xorout
}
CrcAlgorithm::Crc16OpensafetyA => {
Calculator::calculate(CRC16_OPENSAFETY_A.init, buf, &CRC16_OPENSAFETY_A)
^ CRC16_OPENSAFETY_A.xorout
}
CrcAlgorithm::Crc16OpensafetyB => {
Calculator::calculate(CRC16_OPENSAFETY_B.init, buf, &CRC16_OPENSAFETY_B)
^ CRC16_OPENSAFETY_B.xorout
}
CrcAlgorithm::Crc16Profibus => {
Calculator::calculate(CRC16_PROFIBUS.init, buf, &CRC16_PROFIBUS) ^ CRC16_PROFIBUS.xorout
}
CrcAlgorithm::Crc16Riello => {
Calculator::calculate(CRC16_RIELLO.init_algorithm, buf, &CRC16_RIELLO)
^ CRC16_RIELLO.xorout
}
CrcAlgorithm::Crc16SpiFujitsu => {
Calculator::calculate(CRC16_SPI_FUJITSU.init, buf, &CRC16_SPI_FUJITSU)
^ CRC16_SPI_FUJITSU.xorout
}
CrcAlgorithm::Crc16T10Dif => {
Calculator::calculate(CRC16_T10_DIF.init, buf, &CRC16_T10_DIF) ^ CRC16_T10_DIF.xorout
}
CrcAlgorithm::Crc16Teledisk => {
Calculator::calculate(CRC16_TELEDISK.init, buf, &CRC16_TELEDISK) ^ CRC16_TELEDISK.xorout
}
CrcAlgorithm::Crc16Tms37157 => {
Calculator::calculate(CRC16_TMS37157.init_algorithm, buf, &CRC16_TMS37157)
^ CRC16_TMS37157.xorout
}
CrcAlgorithm::Crc16Umts => {
Calculator::calculate(CRC16_UMTS.init, buf, &CRC16_UMTS) ^ CRC16_UMTS.xorout
}
CrcAlgorithm::Crc16Usb => {
Calculator::calculate(CRC16_USB.init, buf, &CRC16_USB) ^ CRC16_USB.xorout
}
CrcAlgorithm::Crc16Xmodem => {
Calculator::calculate(CRC16_XMODEM.init, buf, &CRC16_XMODEM) ^ CRC16_XMODEM.xorout
}
CrcAlgorithm::Crc32Aixm => {
Calculator::calculate(CRC32_AIXM.init, buf, &CRC32_AIXM) ^ CRC32_AIXM.xorout
}
CrcAlgorithm::Crc32Autosar => {
Calculator::calculate(CRC32_AUTOSAR.init, buf, &CRC32_AUTOSAR) ^ CRC32_AUTOSAR.xorout
}
CrcAlgorithm::Crc32Base91D => {
Calculator::calculate(CRC32_BASE91_D.init, buf, &CRC32_BASE91_D) ^ CRC32_BASE91_D.xorout
}
CrcAlgorithm::Crc32Bzip2 => {
Calculator::calculate(CRC32_BZIP2.init, buf, &CRC32_BZIP2) ^ CRC32_BZIP2.xorout
}
CrcAlgorithm::Crc32CdRomEdc => {
Calculator::calculate(CRC32_CD_ROM_EDC.init, buf, &CRC32_CD_ROM_EDC)
^ CRC32_CD_ROM_EDC.xorout
}
CrcAlgorithm::Crc32Cksum => {
Calculator::calculate(CRC32_CKSUM.init, buf, &CRC32_CKSUM) ^ CRC32_CKSUM.xorout
}
CrcAlgorithm::Crc32Custom => {
panic!("Custom CRC-32 requires parameters via CrcParams::new()")
}
CrcAlgorithm::Crc32Iscsi => {
crc32_iscsi_calculator(CRC32_ISCSI.init, buf, &CRC32_ISCSI) ^ CRC32_ISCSI.xorout
}
CrcAlgorithm::Crc32IsoHdlc => {
crc32_iso_hdlc_calculator(CRC32_ISO_HDLC.init, buf, &CRC32_ISO_HDLC)
^ CRC32_ISO_HDLC.xorout
}
CrcAlgorithm::Crc32Jamcrc => {
Calculator::calculate(CRC32_JAMCRC.init, buf, &CRC32_JAMCRC) ^ CRC32_JAMCRC.xorout
}
CrcAlgorithm::Crc32Mef => {
Calculator::calculate(CRC32_MEF.init, buf, &CRC32_MEF) ^ CRC32_MEF.xorout
}
CrcAlgorithm::Crc32Mpeg2 => {
Calculator::calculate(CRC32_MPEG_2.init, buf, &CRC32_MPEG_2) ^ CRC32_MPEG_2.xorout
}
CrcAlgorithm::Crc32Xfer => {
Calculator::calculate(CRC32_XFER.init, buf, &CRC32_XFER) ^ CRC32_XFER.xorout
}
CrcAlgorithm::CrcCustom => {
panic!("Custom CRC requires parameters via CrcParams::new()")
}
CrcAlgorithm::Crc64Custom => {
panic!("Custom CRC-64 requires parameters via CrcParams::new()")
}
CrcAlgorithm::Crc64Ecma182 => {
Calculator::calculate(CRC64_ECMA_182.init, buf, &CRC64_ECMA_182) ^ CRC64_ECMA_182.xorout
}
CrcAlgorithm::Crc64GoIso => {
Calculator::calculate(CRC64_GO_ISO.init, buf, &CRC64_GO_ISO) ^ CRC64_GO_ISO.xorout
}
CrcAlgorithm::Crc64Ms => {
Calculator::calculate(CRC64_MS.init, buf, &CRC64_MS) ^ CRC64_MS.xorout
}
CrcAlgorithm::Crc64Nvme => {
Calculator::calculate(CRC64_NVME.init, buf, &CRC64_NVME) ^ CRC64_NVME.xorout
}
CrcAlgorithm::Crc64Redis => {
Calculator::calculate(CRC64_REDIS.init, buf, &CRC64_REDIS) ^ CRC64_REDIS.xorout
}
CrcAlgorithm::Crc64We => {
Calculator::calculate(CRC64_WE.init, buf, &CRC64_WE) ^ CRC64_WE.xorout
}
CrcAlgorithm::Crc64Xz => {
Calculator::calculate(CRC64_XZ.init, buf, &CRC64_XZ) ^ CRC64_XZ.xorout
}
}
}
pub fn checksum_with_params(params: CrcParams, buf: &[u8]) -> u64 {
let calculator = Calculator::calculate as CalculatorFn;
calculator(params.init_algorithm, buf, ¶ms) ^ params.xorout
}
#[cfg(feature = "std")]
#[inline(always)]
pub fn checksum_file(
algorithm: CrcAlgorithm,
path: &str,
chunk_size: Option<usize>,
) -> Result<u64, std::io::Error> {
checksum_file_with_digest(Digest::new(algorithm), path, chunk_size)
}
#[cfg(feature = "std")]
pub fn checksum_file_with_params(
params: CrcParams,
path: &str,
chunk_size: Option<usize>,
) -> Result<u64, std::io::Error> {
checksum_file_with_digest(Digest::new_with_params(params), path, chunk_size)
}
#[cfg(feature = "std")]
fn checksum_file_with_digest(
mut digest: Digest,
path: &str,
chunk_size: Option<usize>,
) -> Result<u64, std::io::Error> {
let mut file = File::open(path)?;
let chunk_size = chunk_size.unwrap_or(524288);
let mut buf = vec![0; chunk_size];
while let Ok(n) = file.read(&mut buf) {
if n == 0 {
break;
}
digest.update(&buf[..n]);
}
Ok(digest.finalize())
}
#[inline(always)]
pub fn checksum_combine(
algorithm: CrcAlgorithm,
checksum1: u64,
checksum2: u64,
checksum2_len: u64,
) -> u64 {
let params = get_calculator_params(algorithm).1;
combine::checksums(checksum1, checksum2, checksum2_len, ¶ms)
}
pub fn checksum_combine_with_params(
params: CrcParams,
checksum1: u64,
checksum2: u64,
checksum2_len: u64,
) -> u64 {
combine::checksums(checksum1, checksum2, checksum2_len, ¶ms)
}
#[cfg(all(
feature = "alloc",
any(target_arch = "aarch64", target_arch = "x86", target_arch = "x86_64")
))]
pub fn get_calculator_target(_algorithm: CrcAlgorithm) -> String {
let arch_ops = get_arch_ops();
arch_ops.get_target_string()
}
#[inline(always)]
pub fn crc32_iscsi(data: &[u8]) -> u32 {
crc32_iscsi_calculator(CRC32_ISCSI.init, data, &CRC32_ISCSI) as u32 ^ CRC32_ISCSI.xorout as u32
}
#[inline(always)]
pub fn crc32_iso_hdlc(data: &[u8]) -> u32 {
crc32_iso_hdlc_calculator(CRC32_ISO_HDLC.init, data, &CRC32_ISO_HDLC) as u32
^ CRC32_ISO_HDLC.xorout as u32
}
#[inline(always)]
pub fn crc64_nvme(data: &[u8]) -> u64 {
Calculator::calculate(CRC64_NVME.init, data, &CRC64_NVME) ^ CRC64_NVME.xorout
}
#[cfg(all(
feature = "alloc",
not(any(target_arch = "aarch64", target_arch = "x86", target_arch = "x86_64"))
))]
pub fn get_calculator_target(_algorithm: CrcAlgorithm) -> String {
extern crate alloc;
use alloc::string::ToString;
"software-fallback-tables".to_string()
}
#[inline(always)]
#[allow(deprecated)]
fn get_calculator_params(algorithm: CrcAlgorithm) -> (CalculatorFn, CrcParams) {
match algorithm {
CrcAlgorithm::Crc16Arc => (Calculator::calculate as CalculatorFn, CRC16_ARC),
CrcAlgorithm::Crc16Cdma2000 => (Calculator::calculate as CalculatorFn, CRC16_CDMA2000),
CrcAlgorithm::Crc16Cms => (Calculator::calculate as CalculatorFn, CRC16_CMS),
CrcAlgorithm::Crc16Dds110 => (Calculator::calculate as CalculatorFn, CRC16_DDS_110),
CrcAlgorithm::Crc16DectR => (Calculator::calculate as CalculatorFn, CRC16_DECT_R),
CrcAlgorithm::Crc16DectX => (Calculator::calculate as CalculatorFn, CRC16_DECT_X),
CrcAlgorithm::Crc16Dnp => (Calculator::calculate as CalculatorFn, CRC16_DNP),
CrcAlgorithm::Crc16En13757 => (Calculator::calculate as CalculatorFn, CRC16_EN_13757),
CrcAlgorithm::Crc16Genibus => (Calculator::calculate as CalculatorFn, CRC16_GENIBUS),
CrcAlgorithm::Crc16Gsm => (Calculator::calculate as CalculatorFn, CRC16_GSM),
CrcAlgorithm::Crc16Ibm3740 => (Calculator::calculate as CalculatorFn, CRC16_IBM_3740),
CrcAlgorithm::Crc16IbmSdlc => (Calculator::calculate as CalculatorFn, CRC16_IBM_SDLC),
CrcAlgorithm::Crc16IsoIec144433A => (
Calculator::calculate as CalculatorFn,
CRC16_ISO_IEC_14443_3_A,
),
CrcAlgorithm::Crc16Kermit => (Calculator::calculate as CalculatorFn, CRC16_KERMIT),
CrcAlgorithm::Crc16Lj1200 => (Calculator::calculate as CalculatorFn, CRC16_LJ1200),
CrcAlgorithm::Crc16M17 => (Calculator::calculate as CalculatorFn, CRC16_M17),
CrcAlgorithm::Crc16MaximDow => (Calculator::calculate as CalculatorFn, CRC16_MAXIM_DOW),
CrcAlgorithm::Crc16Mcrf4xx => (Calculator::calculate as CalculatorFn, CRC16_MCRF4XX),
CrcAlgorithm::Crc16Modbus => (Calculator::calculate as CalculatorFn, CRC16_MODBUS),
CrcAlgorithm::Crc16Nrsc5 => (Calculator::calculate as CalculatorFn, CRC16_NRSC_5),
CrcAlgorithm::Crc16OpensafetyA => {
(Calculator::calculate as CalculatorFn, CRC16_OPENSAFETY_A)
}
CrcAlgorithm::Crc16OpensafetyB => {
(Calculator::calculate as CalculatorFn, CRC16_OPENSAFETY_B)
}
CrcAlgorithm::Crc16Profibus => (Calculator::calculate as CalculatorFn, CRC16_PROFIBUS),
CrcAlgorithm::Crc16Riello => (Calculator::calculate as CalculatorFn, CRC16_RIELLO),
CrcAlgorithm::Crc16SpiFujitsu => (Calculator::calculate as CalculatorFn, CRC16_SPI_FUJITSU),
CrcAlgorithm::Crc16T10Dif => (Calculator::calculate as CalculatorFn, CRC16_T10_DIF),
CrcAlgorithm::Crc16Teledisk => (Calculator::calculate as CalculatorFn, CRC16_TELEDISK),
CrcAlgorithm::Crc16Tms37157 => (Calculator::calculate as CalculatorFn, CRC16_TMS37157),
CrcAlgorithm::Crc16Umts => (Calculator::calculate as CalculatorFn, CRC16_UMTS),
CrcAlgorithm::Crc16Usb => (Calculator::calculate as CalculatorFn, CRC16_USB),
CrcAlgorithm::Crc16Xmodem => (Calculator::calculate as CalculatorFn, CRC16_XMODEM),
CrcAlgorithm::Crc32Aixm => (Calculator::calculate as CalculatorFn, CRC32_AIXM),
CrcAlgorithm::Crc32Autosar => (Calculator::calculate as CalculatorFn, CRC32_AUTOSAR),
CrcAlgorithm::Crc32Base91D => (Calculator::calculate as CalculatorFn, CRC32_BASE91_D),
CrcAlgorithm::Crc32Bzip2 => (Calculator::calculate as CalculatorFn, CRC32_BZIP2),
CrcAlgorithm::Crc32CdRomEdc => (Calculator::calculate as CalculatorFn, CRC32_CD_ROM_EDC),
CrcAlgorithm::Crc32Cksum => (Calculator::calculate as CalculatorFn, CRC32_CKSUM),
CrcAlgorithm::Crc32Custom => {
panic!("Custom CRC-32 requires parameters via CrcParams::new()")
}
CrcAlgorithm::Crc32Iscsi => (crc32_iscsi_calculator as CalculatorFn, CRC32_ISCSI),
CrcAlgorithm::Crc32IsoHdlc => (crc32_iso_hdlc_calculator as CalculatorFn, CRC32_ISO_HDLC),
CrcAlgorithm::Crc32Jamcrc => (Calculator::calculate as CalculatorFn, CRC32_JAMCRC),
CrcAlgorithm::Crc32Mef => (Calculator::calculate as CalculatorFn, CRC32_MEF),
CrcAlgorithm::Crc32Mpeg2 => (Calculator::calculate as CalculatorFn, CRC32_MPEG_2),
CrcAlgorithm::Crc32Xfer => (Calculator::calculate as CalculatorFn, CRC32_XFER),
CrcAlgorithm::CrcCustom => {
panic!("Custom CRC requires parameters via CrcParams::new()")
}
CrcAlgorithm::Crc64Custom => {
panic!("Custom CRC-64 requires parameters via CrcParams::new()")
}
CrcAlgorithm::Crc64Ecma182 => (Calculator::calculate as CalculatorFn, CRC64_ECMA_182),
CrcAlgorithm::Crc64GoIso => (Calculator::calculate as CalculatorFn, CRC64_GO_ISO),
CrcAlgorithm::Crc64Ms => (Calculator::calculate as CalculatorFn, CRC64_MS),
CrcAlgorithm::Crc64Nvme => (Calculator::calculate as CalculatorFn, CRC64_NVME),
CrcAlgorithm::Crc64Redis => (Calculator::calculate as CalculatorFn, CRC64_REDIS),
CrcAlgorithm::Crc64We => (Calculator::calculate as CalculatorFn, CRC64_WE),
CrcAlgorithm::Crc64Xz => (Calculator::calculate as CalculatorFn, CRC64_XZ),
}
}
#[inline(always)]
fn crc32_iscsi_calculator(state: u64, data: &[u8], _params: &CrcParams) -> u64 {
#[cfg(all(target_arch = "aarch64", feature = "std"))]
{
use crate::feature_detection::PerformanceTier;
let arch_ops = get_arch_ops();
match arch_ops.get_tier() {
PerformanceTier::AArch64AesSha3 | PerformanceTier::AArch64Aes => {
return fusion::crc32_iscsi(state as u32, data) as u64;
}
_ => {}
}
}
#[cfg(all(any(target_arch = "x86_64", target_arch = "x86"), feature = "std"))]
{
use crate::feature_detection::PerformanceTier;
let arch_ops = get_arch_ops();
match arch_ops.get_tier() {
PerformanceTier::X86_64Avx512Vpclmulqdq
| PerformanceTier::X86_64Avx512Pclmulqdq
| PerformanceTier::X86_64SsePclmulqdq
| PerformanceTier::X86SsePclmulqdq => {
if is_x86_feature_detected!("sse4.2") {
return fusion::crc32_iscsi(state as u32, data) as u64;
}
}
_ => {}
}
}
Calculator::calculate(state, data, _params)
}
#[inline(always)]
fn crc32_iso_hdlc_calculator(state: u64, data: &[u8], _params: &CrcParams) -> u64 {
#[cfg(all(target_arch = "aarch64", feature = "std"))]
{
use crate::feature_detection::{get_arch_ops, PerformanceTier};
let arch_ops = get_arch_ops();
match arch_ops.get_tier() {
PerformanceTier::AArch64AesSha3 | PerformanceTier::AArch64Aes => {
return fusion::crc32_iso_hdlc(state as u32, data) as u64;
}
_ => {}
}
}
Calculator::calculate(state, data, _params)
}
#[cfg(test)]
mod lib {
#![allow(unused)]
use super::*;
use crate::test::consts::{TEST_ALL_CONFIGS, TEST_CHECK_STRING};
use crate::test::enums::AnyCrcTestConfig;
use cbindgen::Language::C;
use cbindgen::Style::Both;
use rand::{rng, Rng};
use std::fs::{read, write};
#[test]
fn test_checksum_check() {
for config in TEST_ALL_CONFIGS {
assert_eq!(
checksum(config.get_algorithm(), TEST_CHECK_STRING),
config.get_check()
);
}
}
#[test]
fn test_checksum_reference() {
for config in TEST_ALL_CONFIGS {
assert_eq!(
checksum(config.get_algorithm(), TEST_CHECK_STRING),
config.checksum_with_reference(TEST_CHECK_STRING)
);
}
}
#[test]
fn test_checksum_with_custom_params() {
crate::cache::clear_cache();
assert_eq!(
checksum_with_params(get_custom_crc32_reflected(), TEST_CHECK_STRING),
CRC32_ISCSI.check,
);
assert_eq!(
checksum_with_params(get_custom_crc32_forward(), TEST_CHECK_STRING),
CRC32_BZIP2.check,
);
assert_eq!(
checksum_with_params(get_custom_crc64_reflected(), TEST_CHECK_STRING),
CRC64_NVME.check,
);
assert_eq!(
checksum_with_params(get_custom_crc64_forward(), TEST_CHECK_STRING),
CRC64_ECMA_182.check,
);
}
#[test]
fn test_get_custom_params() {
crate::cache::clear_cache();
assert_eq!(
checksum_with_params(get_custom_crc32_reflected(), TEST_CHECK_STRING),
CRC32_ISCSI.check,
);
assert_eq!(
checksum_with_params(get_custom_crc32_forward(), TEST_CHECK_STRING),
CRC32_BZIP2.check,
);
assert_eq!(
checksum_with_params(get_custom_crc64_reflected(), TEST_CHECK_STRING),
CRC64_NVME.check,
);
assert_eq!(
checksum_with_params(get_custom_crc64_forward(), TEST_CHECK_STRING),
CRC64_ECMA_182.check,
);
}
#[test]
fn test_get_calculator_target_format() {
let target = get_calculator_target(CrcAlgorithm::Crc32IsoHdlc);
assert!(!target.is_empty());
let valid_prefixes = ["aarch64-", "x86_64-", "x86-", "software-"];
assert!(
valid_prefixes
.iter()
.any(|prefix| target.starts_with(prefix)),
"Target '{}' should start with a valid architecture prefix",
target
);
let parts: Vec<&str> = target.split('-').collect();
assert!(
parts.len() >= 3,
"Target '{}' should have at least 3 parts: architecture-family-features",
target
);
}
#[test]
fn test_get_calculator_target_consistency() {
let target1 = get_calculator_target(CrcAlgorithm::Crc32IsoHdlc);
let target2 = get_calculator_target(CrcAlgorithm::Crc32Iscsi);
let target3 = get_calculator_target(CrcAlgorithm::Crc64Nvme);
assert_eq!(
target1, target2,
"Target should be consistent across different CRC-32 algorithms"
);
assert_eq!(
target1, target3,
"Target should be consistent across CRC-32 and CRC-64 algorithms"
);
}
#[test]
fn test_get_calculator_target_uses_cached_detection() {
let target1 = get_calculator_target(CrcAlgorithm::Crc32IsoHdlc);
let target2 = get_calculator_target(CrcAlgorithm::Crc32IsoHdlc);
assert_eq!(
target1, target2,
"Cached detection should return identical results"
);
}
#[test]
fn test_digest_updates_check() {
for config in TEST_ALL_CONFIGS {
check_digest(Digest::new(config.get_algorithm()), config.get_check());
}
}
#[test]
fn test_digest_updates_check_with_custom_params() {
crate::cache::clear_cache();
check_digest(
Digest::new_with_params(get_custom_crc32_reflected()),
CRC32_ISCSI.check,
);
check_digest(
Digest::new_with_params(get_custom_crc32_forward()),
CRC32_BZIP2.check,
);
check_digest(
Digest::new_with_params(get_custom_crc64_reflected()),
CRC64_NVME.check,
);
check_digest(
Digest::new_with_params(get_custom_crc64_forward()),
CRC64_ECMA_182.check,
);
}
fn check_digest(mut digest: Digest, check: u64) {
digest.update(b"123");
digest.update(b"456");
digest.update(b"789");
assert_eq!(digest.finalize(), check,);
}
#[test]
fn test_1024_length() {
for config in TEST_ALL_CONFIGS {
test_length(1024, config);
}
}
#[test]
#[cfg_attr(miri, ignore)]
fn test_small_all_lengths() {
for config in TEST_ALL_CONFIGS {
for len in 1..=255 {
test_length(len, config);
}
}
}
#[test]
#[cfg_attr(miri, ignore)]
fn test_medium_lengths() {
for config in TEST_ALL_CONFIGS {
for len in 256..=1024 {
test_length(len, config);
}
}
}
#[test]
#[cfg_attr(miri, ignore)]
fn test_large_lengths() {
for config in TEST_ALL_CONFIGS {
for len in 1048575..1048577 {
test_length(len, config);
}
}
}
fn test_length(length: usize, config: &AnyCrcTestConfig) {
let mut data = vec![0u8; length];
rng().fill(&mut data[..]);
let expected = config.checksum_with_reference(&data);
let result = checksum(config.get_algorithm(), &data);
assert_eq!(
result,
expected,
"Failed for algorithm: {:?}, length: {}, expected: {:#x}, got: {:#x}",
config.get_algorithm(),
length,
expected,
result
);
}
#[test]
fn test_combine() {
for config in TEST_ALL_CONFIGS {
let algorithm = config.get_algorithm();
let check = config.get_check();
let checksum1 = checksum(algorithm, "1234".as_ref());
let checksum2 = checksum(algorithm, "56789".as_ref());
assert_eq!(checksum_combine(algorithm, checksum1, checksum2, 5), check);
let mut digest1 = Digest::new(algorithm);
digest1.update("1234".as_ref());
let mut digest2 = Digest::new(algorithm);
digest2.update("56789".as_ref());
digest1.combine(&digest2);
assert_eq!(digest1.finalize(), check)
}
}
#[test]
fn test_combine_with_custom_params() {
crate::cache::clear_cache();
let crc32_params = get_custom_crc32_reflected();
let checksum1 = checksum_with_params(crc32_params, "1234".as_ref());
let checksum2 = checksum_with_params(crc32_params, "56789".as_ref());
assert_eq!(
checksum_combine_with_params(crc32_params, checksum1, checksum2, 5),
CRC32_ISCSI.check,
);
let crc32_params = get_custom_crc32_forward();
let checksum1 = checksum_with_params(crc32_params, "1234".as_ref());
let checksum2 = checksum_with_params(crc32_params, "56789".as_ref());
assert_eq!(
checksum_combine_with_params(crc32_params, checksum1, checksum2, 5),
CRC32_BZIP2.check,
);
let crc64_params = get_custom_crc64_reflected();
let checksum1 = checksum_with_params(crc64_params, "1234".as_ref());
let checksum2 = checksum_with_params(crc64_params, "56789".as_ref());
assert_eq!(
checksum_combine_with_params(crc64_params, checksum1, checksum2, 5),
CRC64_NVME.check,
);
let crc64_params = get_custom_crc64_forward();
let checksum1 = checksum_with_params(crc64_params, "1234".as_ref());
let checksum2 = checksum_with_params(crc64_params, "56789".as_ref());
assert_eq!(
checksum_combine_with_params(crc64_params, checksum1, checksum2, 5),
CRC64_ECMA_182.check,
);
}
#[test]
#[cfg_attr(miri, ignore)]
fn test_checksum_file() {
let test_file_path = "test/test_crc32_hash_file.bin";
let data = vec![0u8; 1024 * 1024]; if let Err(e) = write(test_file_path, &data) {
eprintln!("Skipping test due to write error: {}", e);
return;
}
for config in TEST_ALL_CONFIGS {
let result = checksum_file(config.get_algorithm(), test_file_path, None).unwrap();
assert_eq!(result, config.checksum_with_reference(&data));
}
std::fs::remove_file(test_file_path).unwrap();
}
#[test]
#[cfg_attr(miri, ignore)]
fn test_checksum_file_with_custom_params() {
crate::cache::clear_cache();
let test_file_path = "test/test_crc32_hash_file_custom.bin";
let data = vec![0u8; 1024 * 1024]; if let Err(e) = write(test_file_path, &data) {
eprintln!("Skipping test due to write error: {}", e);
return;
}
check_file(
get_custom_crc32_reflected(),
test_file_path,
CRC32_ISCSI.check,
);
check_file(
get_custom_crc32_forward(),
test_file_path,
CRC32_BZIP2.check,
);
check_file(
get_custom_crc64_reflected(),
test_file_path,
CRC64_NVME.check,
);
check_file(
get_custom_crc64_forward(),
test_file_path,
CRC64_ECMA_182.check,
);
std::fs::remove_file(test_file_path).unwrap();
}
fn check_file(params: CrcParams, file_path: &str, check: u64) {
let result = checksum_file_with_params(params, file_path, None).unwrap();
assert_eq!(result, check);
}
#[test]
#[cfg_attr(miri, ignore)]
fn test_writer() {
let test_file_path = "test/test_crc32_writer_file.bin";
let data = vec![0u8; 1024 * 1024]; if let Err(e) = std::fs::write(test_file_path, &data) {
eprintln!("Skipping test due to write error: {}", e);
return;
}
for config in TEST_ALL_CONFIGS {
let mut digest = Digest::new(config.get_algorithm());
let mut file = File::open(test_file_path).unwrap();
std::io::copy(&mut file, &mut digest).unwrap();
assert_eq!(digest.finalize(), config.checksum_with_reference(&data));
}
std::fs::remove_file(test_file_path).unwrap();
}
#[test]
fn test_digest_reset() {
for config in TEST_ALL_CONFIGS {
let mut digest = Digest::new(config.get_algorithm());
digest.update(b"42");
digest.reset();
digest.update(TEST_CHECK_STRING);
assert_eq!(digest.finalize(), config.get_check());
}
}
#[test]
fn test_digest_finalize_reset() {
for config in TEST_ALL_CONFIGS {
let check = config.get_check();
let mut digest = Digest::new(config.get_algorithm());
digest.update(TEST_CHECK_STRING);
assert_eq!(digest.finalize_reset(), check);
digest.update(TEST_CHECK_STRING);
assert_eq!(digest.finalize(), check);
}
}
#[test]
fn test_digest_finalize_into() {
for config in TEST_ALL_CONFIGS {
let mut digest = Digest::new(config.get_algorithm());
digest.update(TEST_CHECK_STRING);
match digest.params.width {
16 => {
let mut output = [0u8; 2];
digest.finalize_into(&mut output).unwrap();
let result = u16::from_be_bytes(output) as u64;
assert_eq!(result, config.get_check());
}
32 => {
let mut output = [0u8; 4];
digest.finalize_into(&mut output).unwrap();
let result = u32::from_be_bytes(output) as u64;
assert_eq!(result, config.get_check());
}
64 => {
let mut output = [0u8; 8];
digest.finalize_into(&mut output).unwrap();
let result = u64::from_be_bytes(output);
assert_eq!(result, config.get_check());
}
_ => panic!("Unsupported CRC width"),
}
}
}
#[test]
fn test_digest_finalize_into_reset() {
for config in TEST_ALL_CONFIGS {
let mut digest = Digest::new(config.get_algorithm());
digest.update(TEST_CHECK_STRING);
let mut output: Vec<u8> = match digest.params.width {
16 => vec![0u8; 2],
32 => vec![0u8; 4],
64 => vec![0u8; 8],
_ => panic!("Unsupported CRC width"),
};
digest.finalize_into_reset(&mut output).unwrap();
let result = match output.len() {
2 => u16::from_be_bytes(output.try_into().unwrap()) as u64,
4 => u32::from_be_bytes(output.try_into().unwrap()) as u64,
8 => u64::from_be_bytes(output.try_into().unwrap()),
_ => panic!("Unsupported CRC width"),
};
assert_eq!(result, config.get_check());
digest.update(TEST_CHECK_STRING);
assert_eq!(digest.finalize(), config.get_check());
}
}
#[test]
#[cfg_attr(miri, ignore)]
fn test_ffi_header() -> Result<(), String> {
#[cfg(target_os = "windows")]
{
eprintln!("Skipping test on Windows");
return Ok(());
}
const HEADER: &str = "libcrc_fast.h";
let crate_dir = std::env::var("CARGO_MANIFEST_DIR").map_err(|error| error.to_string())?;
let mut expected = Vec::new();
cbindgen::Builder::new()
.with_crate(crate_dir)
.with_include_guard("CRC_FAST_H")
.with_header("/* crc_fast library C/C++ API - Copyright 2025 Don MacAskill */\n/* This header is auto-generated. Do not edit directly. */\n")
.exclude_item("crc32_iscsi_impl")
.exclude_item("crc32_iso_hdlc_impl")
.exclude_item("get_iscsi_target")
.exclude_item("get_iso_hdlc_target")
.exclude_item("ISO_HDLC_TARGET")
.exclude_item("ISCSI_TARGET")
.exclude_item("CrcParams")
.rename_item("Digest", "CrcFastDigest")
.with_style(Both)
.with_language(C)
.with_cpp_compat(true)
.generate()
.map_err(|error| error.to_string())?
.write(&mut expected);
let header_content = String::from_utf8(expected).map_err(|error| error.to_string())?;
let regex = regex::Regex::new(r"\n{3,}").map_err(|error| error.to_string())?;
let cleaned_content = regex.replace_all(&header_content, "\n\n").to_string();
expected = cleaned_content.into_bytes();
let actual = read(HEADER).map_err(|error| error.to_string())?;
if expected != actual {
write(HEADER, expected).map_err(|error| error.to_string())?;
return Err(format!(
"{HEADER} is not up-to-date, commit the generated file and try again"
));
}
Ok(())
}
fn get_custom_crc32_reflected() -> CrcParams {
CrcParams::new(
"Custom CRC-32/ISCSI",
32,
CRC32_ISCSI.poly,
CRC32_ISCSI.init,
CRC32_ISCSI.refin,
CRC32_ISCSI.xorout,
CRC32_ISCSI.check,
)
}
fn get_custom_crc32_forward() -> CrcParams {
CrcParams::new(
"Custom CRC-32/BZIP2",
32,
CRC32_BZIP2.poly,
CRC32_BZIP2.init,
CRC32_BZIP2.refin,
CRC32_BZIP2.xorout,
CRC32_BZIP2.check,
)
}
fn get_custom_crc64_reflected() -> CrcParams {
CrcParams::new(
"Custom CRC-64/NVME",
64,
CRC64_NVME.poly,
CRC64_NVME.init,
CRC64_NVME.refin,
CRC64_NVME.xorout,
CRC64_NVME.check,
)
}
fn get_custom_crc64_forward() -> CrcParams {
CrcParams::new(
"Custom CRC-64/ECMA-182",
64,
CRC64_ECMA_182.poly,
CRC64_ECMA_182.init,
CRC64_ECMA_182.refin,
CRC64_ECMA_182.xorout,
CRC64_ECMA_182.check,
)
}
#[test]
#[allow(clippy::needless_range_loop)] fn test_crc_keys_storage_fold_256() {
let test_keys = [
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
];
let storage = CrcKeysStorage::from_keys_fold_256(test_keys);
for i in 0..23 {
assert_eq!(storage.get_key(i), test_keys[i]);
}
assert_eq!(storage.get_key(23), 0);
assert_eq!(storage.get_key(24), 0);
assert_eq!(storage.get_key(100), 0);
assert_eq!(storage.key_count(), 23);
}
#[test]
#[allow(clippy::needless_range_loop)] fn test_crc_keys_storage_future_test() {
let test_keys = [
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25,
];
let storage = CrcKeysStorage::from_keys_fold_future_test(test_keys);
for i in 0..25 {
assert_eq!(storage.get_key(i), test_keys[i]);
}
assert_eq!(storage.get_key(25), 0);
assert_eq!(storage.get_key(26), 0);
assert_eq!(storage.get_key(100), 0);
assert_eq!(storage.key_count(), 25);
}
#[test]
#[allow(clippy::needless_range_loop)] fn test_crc_params_safe_accessors() {
let test_keys = [
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
];
let params = CrcParams {
algorithm: CrcAlgorithm::Crc32IsoHdlc,
name: "test",
width: 32,
poly: 0x04C11DB7,
init: 0xFFFFFFFF,
init_algorithm: 0xFFFFFFFF,
refin: true,
refout: true,
xorout: 0xFFFFFFFF,
check: 0xCBF43926,
keys: CrcKeysStorage::from_keys_fold_256(test_keys),
};
for i in 0..23 {
assert_eq!(params.get_key(i), test_keys[i]);
assert_eq!(params.get_key_checked(i), Some(test_keys[i]));
}
assert_eq!(params.get_key(23), 0);
assert_eq!(params.get_key(24), 0);
assert_eq!(params.get_key(100), 0);
assert_eq!(params.get_key_checked(23), None);
assert_eq!(params.get_key_checked(24), None);
assert_eq!(params.get_key_checked(100), None);
assert_eq!(params.key_count(), 23);
}
#[test]
fn test_crc_keys_storage_const_constructors() {
const TEST_KEYS_23: [u64; 23] = [1; 23];
const TEST_KEYS_25: [u64; 25] = [2; 25];
const STORAGE_256: CrcKeysStorage = CrcKeysStorage::from_keys_fold_256(TEST_KEYS_23);
const STORAGE_FUTURE: CrcKeysStorage =
CrcKeysStorage::from_keys_fold_future_test(TEST_KEYS_25);
assert_eq!(STORAGE_256.get_key(0), 1);
assert_eq!(STORAGE_256.key_count(), 23);
assert_eq!(STORAGE_FUTURE.get_key(0), 2);
assert_eq!(STORAGE_FUTURE.key_count(), 25);
}
#[test]
fn test_crc_keys_storage_bounds_safety() {
let storage_256 = CrcKeysStorage::from_keys_fold_256([42; 23]);
let storage_future = CrcKeysStorage::from_keys_fold_future_test([84; 25]);
assert_eq!(storage_256.get_key(22), 42); assert_eq!(storage_256.get_key(23), 0);
assert_eq!(storage_future.get_key(24), 84); assert_eq!(storage_future.get_key(25), 0);
assert_eq!(storage_256.get_key(usize::MAX), 0);
assert_eq!(storage_future.get_key(usize::MAX), 0);
}
}