#![allow(clippy::indexing_slicing)]
#[cfg(feature = "crc16")]
#[must_use]
pub const fn crc16_bitwise(poly: u16, init: u16, data: &[u8]) -> u16 {
let mut crc = init;
let mut i: usize = 0;
while i < data.len() {
crc ^= data[i] as u16;
let mut bit: u32 = 0;
while bit < 8 {
crc = if crc & 1 != 0 { (crc >> 1) ^ poly } else { crc >> 1 };
bit = bit.strict_add(1);
}
i = i.strict_add(1);
}
crc
}
#[cfg(feature = "crc24")]
#[must_use]
pub const fn crc24_bitwise(poly: u32, init: u32, data: &[u8]) -> u32 {
let poly_expanded = poly.strict_shl(8);
let mut crc = (init & 0x00FF_FFFF).strict_shl(8);
let mut i: usize = 0;
while i < data.len() {
crc ^= (data[i] as u32).strict_shl(24);
let mut bit: u32 = 0;
while bit < 8 {
crc = if crc & 0x8000_0000 != 0 {
crc.strict_shl(1) ^ poly_expanded
} else {
crc.strict_shl(1)
};
bit = bit.strict_add(1);
}
i = i.strict_add(1);
}
(crc >> 8) & 0x00FF_FFFF
}
#[cfg(feature = "crc32")]
#[must_use]
pub const fn crc32_bitwise(poly: u32, init: u32, data: &[u8]) -> u32 {
let mut crc = init;
let mut i: usize = 0;
while i < data.len() {
crc ^= data[i] as u32;
let mut bit: u32 = 0;
while bit < 8 {
crc = if crc & 1 != 0 { (crc >> 1) ^ poly } else { crc >> 1 };
bit = bit.strict_add(1);
}
i = i.strict_add(1);
}
crc
}
#[cfg(feature = "crc64")]
#[must_use]
pub const fn crc64_bitwise(poly: u64, init: u64, data: &[u8]) -> u64 {
let mut crc = init;
let mut i: usize = 0;
while i < data.len() {
crc ^= data[i] as u64;
let mut bit: u32 = 0;
while bit < 8 {
crc = if crc & 1 != 0 { (crc >> 1) ^ poly } else { crc >> 1 };
bit = bit.strict_add(1);
}
i = i.strict_add(1);
}
crc
}
#[cfg(feature = "crc16")]
use super::tables::CRC16_CCITT_POLY;
#[cfg(feature = "crc24")]
use super::tables::CRC24_OPENPGP_POLY;
#[cfg(feature = "crc32")]
use super::tables::{CRC32_IEEE_POLY, CRC32C_POLY};
#[cfg(feature = "crc64")]
use super::tables::{CRC64_NVME_POLY, CRC64_XZ_POLY};
const CHECK_INPUT: &[u8] = b"123456789";
#[cfg(feature = "crc16")]
const _: () = {
let raw = crc16_bitwise(CRC16_CCITT_POLY, !0u16, CHECK_INPUT);
let check = raw ^ !0u16;
assert!(check == 0x906E);
};
#[cfg(feature = "crc24")]
const _: () = {
let check = crc24_bitwise(CRC24_OPENPGP_POLY, 0x00B7_04CE, CHECK_INPUT);
assert!(check == 0x0021_CF02);
};
#[cfg(feature = "crc32")]
const _: () = {
let raw = crc32_bitwise(CRC32_IEEE_POLY, !0u32, CHECK_INPUT);
let check = raw ^ !0u32;
assert!(check == 0xCBF4_3926);
};
#[cfg(feature = "crc32")]
const _: () = {
let raw = crc32_bitwise(CRC32C_POLY, !0u32, CHECK_INPUT);
let check = raw ^ !0u32;
assert!(check == 0xE306_9283);
};
#[cfg(feature = "crc64")]
const _: () = {
let raw = crc64_bitwise(CRC64_XZ_POLY, !0u64, CHECK_INPUT);
let check = raw ^ !0u64;
assert!(check == 0x995D_C9BB_DF19_39FA);
};
#[cfg(feature = "crc64")]
const _: () = {
let raw = crc64_bitwise(CRC64_NVME_POLY, !0u64, CHECK_INPUT);
let check = raw ^ !0u64;
assert!(check == 0xAE8B_1486_0A79_9888);
};
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[cfg(feature = "crc16")]
fn crc16_empty() {
let raw = crc16_bitwise(CRC16_CCITT_POLY, !0u16, &[]);
assert_eq!(raw ^ !0u16, 0);
}
#[test]
#[cfg(feature = "crc16")]
fn crc16_single_bytes() {
for byte in 0u8..=255 {
let crc = crc16_bitwise(CRC16_CCITT_POLY, !0u16, &[byte]);
let _ = crc;
}
}
#[test]
#[cfg(feature = "crc16")]
fn crc16_incremental() {
let data = b"The quick brown fox jumps over the lazy dog";
let oneshot = crc16_bitwise(CRC16_CCITT_POLY, !0u16, data);
for split in 1..data.len() {
let first = crc16_bitwise(CRC16_CCITT_POLY, !0u16, &data[..split]);
let second = crc16_bitwise(CRC16_CCITT_POLY, first, &data[split..]);
assert_eq!(second, oneshot, "Incremental mismatch at split {split}");
}
}
#[test]
#[cfg(feature = "crc24")]
fn crc24_empty() {
let crc = crc24_bitwise(CRC24_OPENPGP_POLY, 0x00B7_04CE, &[]);
assert_eq!(crc, 0x00B7_04CE);
}
#[test]
#[cfg(feature = "crc24")]
fn crc24_incremental() {
let data = b"The quick brown fox jumps over the lazy dog";
let oneshot = crc24_bitwise(CRC24_OPENPGP_POLY, 0x00B7_04CE, data);
for split in 1..data.len() {
let first = crc24_bitwise(CRC24_OPENPGP_POLY, 0x00B7_04CE, &data[..split]);
let second = crc24_bitwise(CRC24_OPENPGP_POLY, first, &data[split..]);
assert_eq!(second, oneshot, "Incremental mismatch at split {split}");
}
}
#[test]
#[cfg(feature = "crc32")]
fn crc32_empty() {
let raw = crc32_bitwise(CRC32_IEEE_POLY, !0u32, &[]);
assert_eq!(raw ^ !0u32, 0);
}
#[test]
#[cfg(feature = "crc32")]
fn crc32_single_bytes() {
for byte in 0u8..=255 {
let crc = crc32_bitwise(CRC32_IEEE_POLY, !0u32, &[byte]);
let _ = crc;
}
}
#[test]
#[cfg(feature = "crc32")]
fn crc32_incremental() {
let data = b"The quick brown fox jumps over the lazy dog";
let oneshot = crc32_bitwise(CRC32_IEEE_POLY, !0u32, data);
for split in 1..data.len() {
let first = crc32_bitwise(CRC32_IEEE_POLY, !0u32, &data[..split]);
let second = crc32_bitwise(CRC32_IEEE_POLY, first, &data[split..]);
assert_eq!(second, oneshot, "Incremental mismatch at split {split}");
}
}
#[test]
#[cfg(feature = "crc32")]
fn crc32c_check_value() {
let raw = crc32_bitwise(CRC32C_POLY, !0u32, CHECK_INPUT);
assert_eq!(raw ^ !0u32, 0xE306_9283);
}
#[test]
#[cfg(feature = "crc64")]
fn crc64_empty() {
let raw = crc64_bitwise(CRC64_XZ_POLY, !0u64, &[]);
assert_eq!(raw ^ !0u64, 0);
let raw = crc64_bitwise(CRC64_NVME_POLY, !0u64, &[]);
assert_eq!(raw ^ !0u64, 0);
}
#[test]
#[cfg(feature = "crc64")]
fn crc64_single_bytes() {
for byte in 0u8..=255 {
let crc = crc64_bitwise(CRC64_XZ_POLY, !0u64, &[byte]);
let _ = crc;
}
}
#[test]
#[cfg(feature = "crc64")]
fn crc64_incremental() {
let data = b"The quick brown fox jumps over the lazy dog";
let oneshot = crc64_bitwise(CRC64_XZ_POLY, !0u64, data);
for split in 1..data.len() {
let first = crc64_bitwise(CRC64_XZ_POLY, !0u64, &data[..split]);
let second = crc64_bitwise(CRC64_XZ_POLY, first, &data[split..]);
assert_eq!(second, oneshot, "Incremental mismatch at split {split}");
}
}
#[test]
#[cfg(feature = "crc64")]
fn crc64_nvme_check_value() {
let raw = crc64_bitwise(CRC64_NVME_POLY, !0u64, CHECK_INPUT);
assert_eq!(raw ^ !0u64, 0xAE8B_1486_0A79_9888);
}
#[test]
#[cfg(all(feature = "crc16", feature = "crc24", feature = "crc32", feature = "crc64"))]
fn all_widths_handle_large_input() {
let data: [u8; 1024] = core::array::from_fn(|i| (i as u8).wrapping_mul(17));
let _ = crc16_bitwise(CRC16_CCITT_POLY, !0u16, &data);
let _ = crc24_bitwise(CRC24_OPENPGP_POLY, 0x00B7_04CE, &data);
let _ = crc32_bitwise(CRC32_IEEE_POLY, !0u32, &data);
let _ = crc64_bitwise(CRC64_XZ_POLY, !0u64, &data);
}
}