#![cfg(target_arch = "aarch64")]
mod iscsi;
mod iso_hdlc;
use core::arch::aarch64::*;
#[cfg(feature = "std")]
use std::arch::is_aarch64_feature_detected;
use iscsi::crc_pmull::crc32_iscsi_v12e_v1;
use iscsi::crc_pmull_sha3::crc32_iscsi_eor3_v9s3x2e_s3;
use iso_hdlc::crc_pmull::crc32_iso_hdlc_v12e_v1;
use iso_hdlc::crc_pmull_sha3::crc32_iso_hdlc_eor3_v9s3x2e_s3;
#[inline(always)]
pub fn crc32_iscsi(crc: u32, data: &[u8]) -> u32 {
let data_len = data.len();
if data_len < 128 {
return unsafe { crc32_iscsi_small_fast(crc, data) };
}
#[cfg(feature = "std")]
let has_sha3 = is_aarch64_feature_detected!("sha3");
#[cfg(not(feature = "std"))]
let has_sha3 = cfg!(target_feature = "sha3");
if has_sha3 {
unsafe { crc32_iscsi_aes_sha3(crc, data, data_len) }
} else {
unsafe { crc32_iscsi_aes(crc, data, data_len) }
}
}
#[inline(always)]
pub fn crc32_iso_hdlc(crc: u32, data: &[u8]) -> u32 {
let data_len = data.len();
if data_len < 128 {
return unsafe { crc32_iso_hdlc_small_fast(crc, data) };
}
#[cfg(feature = "std")]
let has_sha3 = is_aarch64_feature_detected!("sha3");
#[cfg(not(feature = "std"))]
let has_sha3 = cfg!(target_feature = "sha3");
if has_sha3 {
unsafe { crc32_iso_hdlc_aes_sha3(crc, data, data_len) }
} else {
unsafe { crc32_iso_hdlc_aes(crc, data, data_len) }
}
}
#[inline]
#[target_feature(enable = "crc,aes,sha3")]
unsafe fn crc32_iscsi_aes_sha3(crc: u32, data: &[u8], data_len: usize) -> u32 {
unsafe {
const LARGE_BUFFER_THRESHOLD: usize = 1024;
if data_len <= LARGE_BUFFER_THRESHOLD {
crc32_iscsi_v12e_v1(crc, data.as_ptr(), data_len)
} else {
crc32_iscsi_eor3_v9s3x2e_s3(crc, data.as_ptr(), data_len)
}
}
}
#[inline]
#[target_feature(enable = "crc,aes")]
unsafe fn crc32_iscsi_aes(crc: u32, data: &[u8], data_len: usize) -> u32 {
unsafe { crc32_iscsi_v12e_v1(crc, data.as_ptr(), data_len) }
}
#[inline]
#[target_feature(enable = "crc,aes,sha3")]
unsafe fn crc32_iso_hdlc_aes_sha3(crc: u32, data: &[u8], data_len: usize) -> u32 {
unsafe {
const LARGE_BUFFER_THRESHOLD: usize = 1024;
if data_len <= LARGE_BUFFER_THRESHOLD {
crc32_iso_hdlc_v12e_v1(crc, data.as_ptr(), data_len)
} else {
crc32_iso_hdlc_eor3_v9s3x2e_s3(crc, data.as_ptr(), data_len)
}
}
}
#[inline]
#[target_feature(enable = "crc,aes")]
unsafe fn crc32_iso_hdlc_aes(crc: u32, data: &[u8], data_len: usize) -> u32 {
unsafe { crc32_iso_hdlc_v12e_v1(crc, data.as_ptr(), data_len) }
}
#[inline]
#[target_feature(enable = "aes")]
unsafe fn clmul_lo(a: uint64x2_t, b: uint64x2_t) -> uint64x2_t {
let result = vmull_p64(vgetq_lane_u64(a, 0), vgetq_lane_u64(b, 0));
vreinterpretq_u64_p128(result)
}
#[inline]
#[target_feature(enable = "aes")]
unsafe fn clmul_hi(a: uint64x2_t, b: uint64x2_t) -> uint64x2_t {
let result = vmull_p64(vgetq_lane_u64(a, 1), vgetq_lane_u64(b, 1));
vreinterpretq_u64_p128(result)
}
#[inline]
#[target_feature(enable = "aes")]
unsafe fn clmul_scalar(a: u32, b: u32) -> uint64x2_t {
let result = vmull_p64(a as u64, b as u64);
vreinterpretq_u64_p128(result)
}
#[inline]
#[target_feature(enable = "aes")]
unsafe fn clmul_lo_and_xor(a: uint64x2_t, b: uint64x2_t, c: uint64x2_t) -> uint64x2_t {
veorq_u64(clmul_lo(a, b), c)
}
#[inline]
#[target_feature(enable = "aes")]
unsafe fn clmul_hi_and_xor(a: uint64x2_t, b: uint64x2_t, c: uint64x2_t) -> uint64x2_t {
veorq_u64(clmul_hi(a, b), c)
}
#[inline]
#[target_feature(enable = "crc")]
pub unsafe fn crc32_iscsi_small_fast(mut crc: u32, data: &[u8]) -> u32 {
let (prefix, aligned, suffix) = data.align_to::<u64>();
for &byte in prefix {
crc = __crc32cb(crc, byte);
}
let mut chunks = aligned.chunks_exact(8);
for chunk in &mut chunks {
crc = __crc32cd(crc, chunk[0]);
crc = __crc32cd(crc, chunk[1]);
crc = __crc32cd(crc, chunk[2]);
crc = __crc32cd(crc, chunk[3]);
crc = __crc32cd(crc, chunk[4]);
crc = __crc32cd(crc, chunk[5]);
crc = __crc32cd(crc, chunk[6]);
crc = __crc32cd(crc, chunk[7]);
}
for &val in chunks.remainder() {
crc = __crc32cd(crc, val);
}
for &byte in suffix {
crc = __crc32cb(crc, byte);
}
crc
}
#[inline]
#[target_feature(enable = "crc")]
pub unsafe fn crc32_iso_hdlc_small_fast(mut crc: u32, data: &[u8]) -> u32 {
let (prefix, aligned, suffix) = data.align_to::<u64>();
for &byte in prefix {
crc = __crc32b(crc, byte);
}
let mut chunks = aligned.chunks_exact(8);
for chunk in &mut chunks {
crc = __crc32d(crc, chunk[0]);
crc = __crc32d(crc, chunk[1]);
crc = __crc32d(crc, chunk[2]);
crc = __crc32d(crc, chunk[3]);
crc = __crc32d(crc, chunk[4]);
crc = __crc32d(crc, chunk[5]);
crc = __crc32d(crc, chunk[6]);
crc = __crc32d(crc, chunk[7]);
}
for &val in chunks.remainder() {
crc = __crc32d(crc, val);
}
for &byte in suffix {
crc = __crc32b(crc, byte);
}
crc
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test::consts::TEST_CHECK_STRING;
use crc::{Crc, Table};
use rand::{rng, Rng};
const RUST_CRC32_ISO_HDLC: Crc<u32, Table<16>> =
Crc::<u32, Table<16>>::new(&crc::CRC_32_ISO_HDLC);
const RUST_CRC32_ISCSI: Crc<u32, Table<16>> = Crc::<u32, Table<16>>::new(&crc::CRC_32_ISCSI);
#[test]
fn test_crc32_iso_hdlc_check() {
assert_eq!(
crc32_iso_hdlc(0xffffffff, TEST_CHECK_STRING) ^ 0xffffffff,
0xcbf43926
);
}
#[test]
fn test_crc32_iso_hdlc_small_all_lengths() {
for len in 1..=255 {
crc32_iso_hdlc_random(len)
}
}
#[test]
fn test_crc32_iso_hdlc_medium_lengths() {
for len in 256..=1024 {
crc32_iso_hdlc_random(len)
}
}
#[test]
fn test_crc32_iso_hdlc_large_lengths() {
for len in 1048575..1048577 {
crc32_iso_hdlc_random(len)
}
}
#[test]
fn test_crc32_iscsi_check() {
assert_eq!(
crc32_iscsi(0xffffffff, TEST_CHECK_STRING) ^ 0xffffffff,
0xe3069283
);
}
#[test]
fn test_crc32_iscsi_small_all_lengths() {
for len in 1..=255 {
crc32_iscsi_random(len);
}
}
#[test]
fn test_crc32_iscsi_medium_lengths() {
for len in 256..=1024 {
crc32_iscsi_random(len);
}
}
#[test]
fn test_crc32_iscsi_large_lengths() {
for len in 1048575..1048577 {
crc32_iscsi_random(len);
}
}
#[cfg(target_feature = "sha3")]
fn crc32_iso_hdlc_random(len: usize) {
let mut data = vec![0u8; len];
rng().fill(&mut data[..]);
let checksum = RUST_CRC32_ISO_HDLC.checksum(&data);
assert_eq!(crc32_iso_hdlc(0xffffffff, &data) ^ 0xffffffff, checksum);
unsafe {
assert_eq!(
crc32_iso_hdlc_eor3_v9s3x2e_s3(0xffffffff, data.as_ptr(), data.len()) ^ 0xffffffff,
checksum
);
assert_eq!(
crc32_iso_hdlc_v12e_v1(0xffffffff, data.as_ptr(), data.len()) ^ 0xffffffff,
checksum
);
}
}
#[cfg(not(target_feature = "sha3"))]
fn crc32_iso_hdlc_random(len: usize) {
let mut data = vec![0u8; len];
rng().fill(&mut data[..]);
let checksum = RUST_CRC32_ISO_HDLC.checksum(&data);
assert_eq!(crc32_iso_hdlc(0xffffffff, &data) ^ 0xffffffff, checksum);
unsafe {
assert_eq!(
crc32_iso_hdlc_v12e_v1(0xffffffff, data.as_ptr(), data.len()) ^ 0xffffffff,
checksum
);
}
}
#[cfg(target_feature = "sha3")]
fn crc32_iscsi_random(len: usize) {
let mut data = vec![0u8; len];
rng().fill(&mut data[..]);
let checksum = RUST_CRC32_ISCSI.checksum(&data);
assert_eq!(crc32_iscsi(0xffffffff, &data) ^ 0xffffffff, checksum);
unsafe {
assert_eq!(
crc32_iscsi_eor3_v9s3x2e_s3(0xffffffff, data.as_ptr(), data.len()) ^ 0xffffffff,
checksum
);
assert_eq!(
crc32_iscsi_v12e_v1(0xffffffff, data.as_ptr(), data.len()) ^ 0xffffffff,
checksum
);
}
}
#[cfg(not(target_feature = "sha3"))]
fn crc32_iscsi_random(len: usize) {
let mut data = vec![0u8; len];
rng().fill(&mut data[..]);
let checksum = RUST_CRC32_ISCSI.checksum(&data);
assert_eq!(crc32_iscsi(0xffffffff, &data) ^ 0xffffffff, checksum);
unsafe {
assert_eq!(
crc32_iscsi_v12e_v1(0xffffffff, data.as_ptr(), data.len()) ^ 0xffffffff,
checksum
);
}
}
}