const CRC32C_TABLE: [u32; 256] = generate_crc32c_table();
const fn generate_crc32c_table() -> [u32; 256] {
let mut table = [0u32; 256];
let poly: u32 = 0x82F6_3B78; let mut i = 0;
while i < 256 {
let mut crc = i as u32;
let mut j = 0;
while j < 8 {
if crc & 1 != 0 {
crc = (crc >> 1) ^ poly;
} else {
crc >>= 1;
}
j += 1;
}
table[i] = crc;
i += 1;
}
table
}
fn crc32c_update(mut crc: u32, data: &[u8]) -> u32 {
for &byte in data {
let index = ((crc ^ byte as u32) & 0xFF) as usize;
crc = CRC32C_TABLE[index] ^ (crc >> 8);
}
crc
}
fn crc32c_scalar(data: &[u8]) -> u32 {
crc32c_update(0xFFFF_FFFF, data) ^ 0xFFFF_FFFF
}
#[cfg(target_arch = "x86_64")]
#[target_feature(enable = "sse4.2")]
unsafe fn crc32c_sse42(data: &[u8]) -> u32 {
use core::arch::x86_64::{_mm_crc32_u8, _mm_crc32_u32, _mm_crc32_u64};
let mut crc: u64 = !0u64; let mut ptr = data.as_ptr();
let mut len = data.len();
while len >= 8 {
let val = unsafe { ptr.cast::<u64>().read_unaligned() };
crc = _mm_crc32_u64(crc, val);
ptr = unsafe { ptr.add(8) };
len -= 8;
}
let mut crc32 = crc as u32;
if len >= 4 {
let val = unsafe { ptr.cast::<u32>().read_unaligned() };
crc32 = _mm_crc32_u32(crc32, val);
ptr = unsafe { ptr.add(4) };
len -= 4;
}
let tail = unsafe { core::slice::from_raw_parts(ptr, len) };
for &b in tail {
crc32 = _mm_crc32_u8(crc32, b);
}
!crc32
}
#[cfg(target_arch = "x86_64")]
static CRC32C_FN: std::sync::OnceLock<fn(&[u8]) -> u32> = std::sync::OnceLock::new();
#[cfg(target_arch = "x86_64")]
fn get_crc32c_fn() -> fn(&[u8]) -> u32 {
*CRC32C_FN.get_or_init(|| {
if is_x86_feature_detected!("sse4.2") {
|data| unsafe { crc32c_sse42(data) }
} else {
crc32c_scalar
}
})
}
pub fn crc32c(data: &[u8]) -> u32 {
#[cfg(target_arch = "x86_64")]
{
get_crc32c_fn()(data)
}
#[cfg(not(target_arch = "x86_64"))]
crc32c_scalar(data)
}
pub fn mask_checksum(crc: u32) -> u32 {
crc.rotate_right(15).wrapping_add(0xA282_EAD8)
}
pub fn unmask_checksum(masked: u32) -> u32 {
let rot = masked.wrapping_sub(0xA282_EAD8);
rot.rotate_left(15)
}
pub fn masked_crc32c(data: &[u8]) -> u32 {
mask_checksum(crc32c(data))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_crc32c_empty() {
assert_eq!(crc32c(b""), 0x0000_0000);
}
#[test]
fn test_crc32c_known_values() {
assert_eq!(crc32c(b"123456789"), 0xE306_9283);
}
#[test]
fn test_crc32c_single_bytes() {
let crc = crc32c(&[0x00]);
assert_eq!(crc, crc32c(&[0x00]));
}
#[test]
fn test_mask_unmask_roundtrip() {
let original = 0x12345678_u32;
let masked = mask_checksum(original);
let unmasked = unmask_checksum(masked);
assert_eq!(original, unmasked);
}
#[test]
fn test_mask_unmask_zero() {
let masked = mask_checksum(0);
let unmasked = unmask_checksum(masked);
assert_eq!(0, unmasked);
}
#[test]
fn test_masked_crc32c() {
let data = b"Hello, World!";
let raw = crc32c(data);
let masked = masked_crc32c(data);
assert_eq!(masked, mask_checksum(raw));
}
#[test]
fn test_crc32c_table_valid() {
let crc = crc32c(&[0x01]);
assert_ne!(crc, 0);
}
#[test]
fn test_crc32c_incremental_vs_oneshot() {
let data = b"The quick brown fox jumps over the lazy dog";
let oneshot = crc32c(data);
let mid = data.len() / 2;
let crc1 = crc32c_update(0xFFFF_FFFF, &data[..mid]);
let crc2 = crc32c_update(crc1, &data[mid..]) ^ 0xFFFF_FFFF;
assert_eq!(oneshot, crc2);
}
#[cfg(target_arch = "x86_64")]
mod simd_tests {
use super::*;
#[test]
fn test_crc32c_sse_matches_scalar_lengths() {
if !is_x86_feature_detected!("sse4.2") {
return;
}
let data = vec![0xABu8; 4097];
for len in (0..=4096).step_by(17) {
let scalar = crc32c_scalar(&data[..len]);
let simd = unsafe { crc32c_sse42(&data[..len]) };
assert_eq!(scalar, simd, "length {len} mismatch");
}
}
#[test]
fn test_crc32c_sse_random() {
if !is_x86_feature_detected!("sse4.2") {
return;
}
let mut state: u64 = 0xdead_beef_1234_5678;
for _ in 0..100 {
state = state
.wrapping_mul(6_364_136_223_846_793_005)
.wrapping_add(1_442_695_040_888_963_407);
let len = (state >> 48) as usize % 8193;
let data: Vec<u8> = (0..len)
.map(|i| ((state >> (i % 8)) & 0xFF) as u8)
.collect();
let scalar = crc32c_scalar(&data);
let simd = unsafe { crc32c_sse42(&data) };
assert_eq!(scalar, simd, "random len {len} mismatch");
}
}
#[test]
fn test_crc32c_known_vectors() {
assert_eq!(crc32c_scalar(&[]), 0x0000_0000);
assert_eq!(crc32c_scalar(&[0x00]), 0x527D_5351);
assert_eq!(crc32c_scalar(b"123456789"), 0xE306_9283);
if is_x86_feature_detected!("sse4.2") {
assert_eq!(unsafe { crc32c_sse42(&[]) }, 0x0000_0000);
assert_eq!(unsafe { crc32c_sse42(&[0x00]) }, 0x527D_5351);
assert_eq!(unsafe { crc32c_sse42(b"123456789") }, 0xE306_9283);
}
}
}
}