Skip to content

Commit 9432876

Browse files
committed
Add table-based software fallback
For unsupported architectures, there’s now a software fallback using relatively fast table lookups via the ‘crc’ crate.
1 parent 62cc3fb commit 9432876

File tree

13 files changed

+258
-33
lines changed

13 files changed

+258
-33
lines changed

src/algorithm.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
//! The main entry point is the `update` function, which takes the current CRC state,
1414
//! the input data, CRC parameters, and architecture-specific operations.
1515
16+
#![cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))]
17+
1618
use crate::consts::CRC_CHUNK_SIZE;
1719
use crate::enums::{DataChunkProcessor, Reflector};
1820
use crate::structs::{CrcParams, CrcState};

src/arch/mod.rs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,13 @@
55
//! It dispatches to the appropriate architecture-specific implementation
66
//! based on the target architecture.
77
8+
#[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))]
89
use crate::algorithm;
9-
use crate::structs::{CrcParams, Width32, Width64};
10+
11+
use crate::structs::CrcParams;
12+
13+
#[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))]
14+
use crate::structs::{Width32, Width64};
1015

1116
#[cfg(target_arch = "aarch64")]
1217
use crate::arch::aarch64::AArch64Ops;
@@ -18,6 +23,7 @@ use crate::arch::x86::X86Ops;
1823
use crate::arch::vpclmulqdq::VpclmulqdqOps;
1924

2025
pub(crate) mod aarch64;
26+
mod software;
2127
mod vpclmulqdq;
2228
pub(crate) mod x86;
2329

@@ -70,10 +76,8 @@ pub(crate) unsafe fn update(state: u64, bytes: &[u8], params: CrcParams) -> u64
7076
}
7177
}
7278

73-
#[cfg(not(any(target_arch = "aarch64", target_arch = "x86", target_arch = "x86_64")))]
74-
{
75-
compile_error!("Unsupported architecture for SIMD CRC calculation");
76-
}
79+
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")))]
80+
return software::update(state, bytes, params);
7781
}
7882

7983
pub fn get_target() -> String {
@@ -88,7 +92,7 @@ pub fn get_target() -> String {
8892
return "internal-x86-sse-pclmulqdq".to_string();
8993

9094
#[cfg(not(any(target_arch = "aarch64", target_arch = "x86", target_arch = "x86_64")))]
91-
compile_error!("Unsupported architecture for SIMD CRC calculation");
95+
return "software-fallback".to_string();
9296
}
9397

9498
#[cfg(test)]

src/arch/software.rs

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
// Copyright 2025 Don MacAskill. Licensed under MIT or Apache-2.0.
2+
3+
//! This module contains a software fallback for unsupported architectures.
4+
5+
#![cfg(not(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")))]
6+
7+
use crate::consts::CRC_64_NVME;
8+
use crate::structs::CrcParams;
9+
use crate::CrcAlgorithm;
10+
use crc::Table;
11+
12+
const RUST_CRC32_AIXM: crc::Crc<u32, Table<16>> =
13+
crc::Crc::<u32, Table<16>>::new(&crc::CRC_32_AIXM);
14+
15+
const RUST_CRC32_AUTOSAR: crc::Crc<u32, Table<16>> =
16+
crc::Crc::<u32, Table<16>>::new(&crc::CRC_32_AUTOSAR);
17+
18+
const RUST_CRC32_BASE91_D: crc::Crc<u32, Table<16>> =
19+
crc::Crc::<u32, Table<16>>::new(&crc::CRC_32_BASE91_D);
20+
21+
const RUST_CRC32_BZIP2: crc::Crc<u32, Table<16>> =
22+
crc::Crc::<u32, Table<16>>::new(&crc::CRC_32_BZIP2);
23+
24+
const RUST_CRC32_CD_ROM_EDC: crc::Crc<u32, Table<16>> =
25+
crc::Crc::<u32, Table<16>>::new(&crc::CRC_32_CD_ROM_EDC);
26+
27+
const RUST_CRC32_CKSUM: crc::Crc<u32, Table<16>> =
28+
crc::Crc::<u32, Table<16>>::new(&crc::CRC_32_CKSUM);
29+
30+
const RUST_CRC32_ISCSI: crc::Crc<u32, Table<16>> =
31+
crc::Crc::<u32, Table<16>>::new(&crc::CRC_32_ISCSI);
32+
33+
const RUST_CRC32_ISO_HDLC: crc::Crc<u32, Table<16>> =
34+
crc::Crc::<u32, Table<16>>::new(&crc::CRC_32_ISO_HDLC);
35+
36+
const RUST_CRC32_JAMCRC: crc::Crc<u32, Table<16>> =
37+
crc::Crc::<u32, Table<16>>::new(&crc::CRC_32_JAMCRC);
38+
39+
const RUST_CRC32_MEF: crc::Crc<u32, Table<16>> = crc::Crc::<u32, Table<16>>::new(&crc::CRC_32_MEF);
40+
41+
const RUST_CRC32_MPEG_2: crc::Crc<u32, Table<16>> =
42+
crc::Crc::<u32, Table<16>>::new(&crc::CRC_32_MPEG_2);
43+
44+
const RUST_CRC32_XFER: crc::Crc<u32, Table<16>> =
45+
crc::Crc::<u32, Table<16>>::new(&crc::CRC_32_XFER);
46+
47+
const RUST_CRC64_ECMA_182: crc::Crc<u64, Table<16>> =
48+
crc::Crc::<u64, Table<16>>::new(&crc::CRC_64_ECMA_182);
49+
50+
const RUST_CRC64_GO_ISO: crc::Crc<u64, Table<16>> =
51+
crc::Crc::<u64, Table<16>>::new(&crc::CRC_64_GO_ISO);
52+
53+
const RUST_CRC64_MS: crc::Crc<u64, Table<16>> = crc::Crc::<u64, Table<16>>::new(&crc::CRC_64_MS);
54+
55+
const RUST_CRC64_NVME: crc::Crc<u64, Table<16>> = crc::Crc::<u64, Table<16>>::new(&CRC_64_NVME);
56+
57+
const RUST_CRC64_REDIS: crc::Crc<u64, Table<16>> =
58+
crc::Crc::<u64, Table<16>>::new(&crc::CRC_64_REDIS);
59+
60+
const RUST_CRC64_WE: crc::Crc<u64, Table<16>> = crc::Crc::<u64, Table<16>>::new(&crc::CRC_64_WE);
61+
62+
const RUST_CRC64_XZ: crc::Crc<u64, Table<16>> = crc::Crc::<u64, Table<16>>::new(&crc::CRC_64_XZ);
63+
64+
// Dispatch function that handles the generic case
65+
pub(crate) fn update(state: u64, data: &[u8], params: CrcParams) -> u64 {
66+
match params.width {
67+
32 => {
68+
let params = match params.algorithm {
69+
CrcAlgorithm::Crc32Aixm => RUST_CRC32_AIXM,
70+
CrcAlgorithm::Crc32Autosar => RUST_CRC32_AUTOSAR,
71+
CrcAlgorithm::Crc32Base91D => RUST_CRC32_BASE91_D,
72+
CrcAlgorithm::Crc32Bzip2 => RUST_CRC32_BZIP2,
73+
CrcAlgorithm::Crc32CdRomEdc => RUST_CRC32_CD_ROM_EDC,
74+
CrcAlgorithm::Crc32Cksum => RUST_CRC32_CKSUM,
75+
CrcAlgorithm::Crc32Iscsi => RUST_CRC32_ISCSI,
76+
CrcAlgorithm::Crc32IsoHdlc => RUST_CRC32_ISO_HDLC,
77+
CrcAlgorithm::Crc32Jamcrc => RUST_CRC32_JAMCRC,
78+
CrcAlgorithm::Crc32Mef => RUST_CRC32_MEF,
79+
CrcAlgorithm::Crc32Mpeg2 => RUST_CRC32_MPEG_2,
80+
CrcAlgorithm::Crc32Xfer => RUST_CRC32_XFER,
81+
_ => panic!("Invalid algorithm for u32 CRC"),
82+
};
83+
update_u32(state as u32, data, params) as u64
84+
}
85+
64 => {
86+
let params = match params.algorithm {
87+
CrcAlgorithm::Crc64Ecma182 => RUST_CRC64_ECMA_182,
88+
CrcAlgorithm::Crc64GoIso => RUST_CRC64_GO_ISO,
89+
CrcAlgorithm::Crc64Ms => RUST_CRC64_MS,
90+
CrcAlgorithm::Crc64Nvme => RUST_CRC64_NVME,
91+
CrcAlgorithm::Crc64Redis => RUST_CRC64_REDIS,
92+
CrcAlgorithm::Crc64We => RUST_CRC64_WE,
93+
CrcAlgorithm::Crc64Xz => RUST_CRC64_XZ,
94+
_ => panic!("Invalid algorithm for u64 CRC"),
95+
};
96+
update_u64(state, data, params)
97+
}
98+
_ => panic!("Unsupported CRC width: {}", params.width),
99+
}
100+
}
101+
102+
// Specific implementation for u32
103+
fn update_u32(state: u32, data: &[u8], params: crc::Crc<u32, Table<16>>) -> u32 {
104+
// apply REFIN if necessary
105+
let initial = if params.algorithm.refin {
106+
state.reverse_bits()
107+
} else {
108+
state
109+
};
110+
111+
let mut digest = params.digest_with_initial(initial);
112+
digest.update(data);
113+
114+
let checksum = digest.finalize();
115+
116+
println!(
117+
"finalized checksum {:#16x}, xor'd {:#16x}",
118+
checksum,
119+
checksum ^ params.algorithm.xorout
120+
);
121+
122+
// remove XOR since this will be applied in the library Digest::finalize() step instead
123+
checksum ^ params.algorithm.xorout
124+
}
125+
126+
// Specific implementation for u64
127+
fn update_u64(state: u64, data: &[u8], params: crc::Crc<u64, Table<16>>) -> u64 {
128+
// apply REFIN if necessary
129+
let initial = if params.algorithm.refin {
130+
state.reverse_bits()
131+
} else {
132+
state
133+
};
134+
135+
let mut digest = params.digest_with_initial(initial);
136+
digest.update(data);
137+
138+
// remove XOR since this will be applied in the library Digest::finalize() step instead
139+
digest.finalize() ^ params.algorithm.xorout
140+
}

src/consts.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,43 @@ pub(crate) const CRC64_EXPONENTS: [u64; 21] = [
9090
64 * 4,
9191
64 * 5,
9292
];
93+
94+
// for software fallbacks and testing
95+
pub(crate) const RUST_CRC32_AIXM: crc::Crc<u32> = crc::Crc::<u32>::new(&crc::CRC_32_AIXM);
96+
97+
pub(crate) const RUST_CRC32_AUTOSAR: crc::Crc<u32> = crc::Crc::<u32>::new(&crc::CRC_32_AUTOSAR);
98+
99+
pub(crate) const RUST_CRC32_BASE91_D: crc::Crc<u32> = crc::Crc::<u32>::new(&crc::CRC_32_BASE91_D);
100+
101+
pub(crate) const RUST_CRC32_BZIP2: crc::Crc<u32> = crc::Crc::<u32>::new(&crc::CRC_32_BZIP2);
102+
103+
pub(crate) const RUST_CRC32_CD_ROM_EDC: crc::Crc<u32> =
104+
crc::Crc::<u32>::new(&crc::CRC_32_CD_ROM_EDC);
105+
106+
pub(crate) const RUST_CRC32_CKSUM: crc::Crc<u32> = crc::Crc::<u32>::new(&crc::CRC_32_CKSUM);
107+
108+
pub(crate) const RUST_CRC32_ISCSI: crc::Crc<u32> = crc::Crc::<u32>::new(&crc::CRC_32_ISCSI);
109+
110+
pub(crate) const RUST_CRC32_ISO_HDLC: crc::Crc<u32> = crc::Crc::<u32>::new(&crc::CRC_32_ISO_HDLC);
111+
112+
pub(crate) const RUST_CRC32_JAMCRC: crc::Crc<u32> = crc::Crc::<u32>::new(&crc::CRC_32_JAMCRC);
113+
114+
pub(crate) const RUST_CRC32_MEF: crc::Crc<u32> = crc::Crc::<u32>::new(&crc::CRC_32_MEF);
115+
116+
pub(crate) const RUST_CRC32_MPEG_2: crc::Crc<u32> = crc::Crc::<u32>::new(&crc::CRC_32_MPEG_2);
117+
118+
pub(crate) const RUST_CRC32_XFER: crc::Crc<u32> = crc::Crc::<u32>::new(&crc::CRC_32_XFER);
119+
120+
pub(crate) const RUST_CRC64_ECMA_182: crc::Crc<u64> = crc::Crc::<u64>::new(&crc::CRC_64_ECMA_182);
121+
122+
pub(crate) const RUST_CRC64_GO_ISO: crc::Crc<u64> = crc::Crc::<u64>::new(&crc::CRC_64_GO_ISO);
123+
124+
pub(crate) const RUST_CRC64_MS: crc::Crc<u64> = crc::Crc::<u64>::new(&crc::CRC_64_MS);
125+
126+
pub(crate) const RUST_CRC64_NVME: crc::Crc<u64> = crc::Crc::<u64>::new(&CRC_64_NVME);
127+
128+
pub(crate) const RUST_CRC64_REDIS: crc::Crc<u64> = crc::Crc::<u64>::new(&crc::CRC_64_REDIS);
129+
130+
pub(crate) const RUST_CRC64_WE: crc::Crc<u64> = crc::Crc::<u64>::new(&crc::CRC_64_WE);
131+
132+
pub(crate) const RUST_CRC64_XZ: crc::Crc<u64> = crc::Crc::<u64>::new(&crc::CRC_64_XZ);

src/crc32/algorithm.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
//! This module provides the CRC-32 algorithm implementations for areas where it differs from
44
//! CRC-64.
55
6+
#![cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))]
7+
68
use crate::algorithm;
79
use crate::consts::CRC_CHUNK_SIZE;
810
use crate::crc32::consts::{PSHUFB_SHF_TABLE_FORWARD, PSHUFB_SHF_TABLE_REVERSE, SIMD_CONSTANTS};

src/crc64/algorithm.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
//! This module provides the CRC-64 implementation for areas where it differs from CRC-32.
44
5+
#![cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))]
6+
57
use crate::algorithm;
68
use crate::consts::{CRC_CHUNK_SIZE, CRC_HALF_CHUNK_SIZE};
79
use crate::crc64::consts::SIMD_CONSTANTS;

src/crc64/utils.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
// Copyright 2025 Don MacAskill. Licensed under MIT or Apache-2.0.
22

3+
#![cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))]
4+
35
#[cfg(target_arch = "aarch64")]
46
use std::arch::aarch64::*;
57

68
#[cfg(target_arch = "x86")]
79
use std::arch::x86::*;
810

911
use crate::traits::ArchOps;
12+
1013
#[cfg(target_arch = "x86_64")]
1114
use std::arch::x86_64::*;
1215

src/enums.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,14 @@ impl Display for CrcAlgorithm {
6060
}
6161
}
6262

63+
#[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))]
6364
#[derive(Debug, Copy, Clone)]
6465
pub(crate) enum Reflector<T> {
6566
NoReflector,
6667
ForwardReflector { smask: T },
6768
}
6869

70+
#[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))]
6971
/// Different processing strategies based on data length
7072
pub(crate) enum DataChunkProcessor {
7173
From0To15, // 0-15 bytes
@@ -74,6 +76,7 @@ pub(crate) enum DataChunkProcessor {
7476
From32To255, // 32-255 bytes
7577
}
7678

79+
#[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))]
7780
impl DataChunkProcessor {
7881
/// Select the appropriate processor based on data length
7982
pub fn for_length(len: usize) -> Self {

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -859,12 +859,14 @@ mod lib {
859859

860860
/// Tests whether the CRC-32/ISO-HDLC bindings are up-to-date
861861
#[test]
862+
#[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))]
862863
fn test_crc32_iso_hdlc_bindings() -> Result<(), String> {
863864
build_bindgen("crc32_iso_hdlc", "src/bindings/crc32_iso_hdlc.rs")
864865
}
865866

866867
/// Tests whether the CRC-32/ISCSI bindings are up-to-date
867868
#[test]
869+
#[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))]
868870
fn test_crc32_iscsi_bindings() -> Result<(), String> {
869871
build_bindgen("crc32_iscsi", "src/bindings/crc32_iscsi.rs")
870872
}

0 commit comments

Comments
 (0)