2cd40ea1 Andy McCall 1 month ago ↶
1
# Design Document: CRC Width-32 Shared Base
2
3
## Overview
4
5
This design refactors the CRC-16 and CRC-32 algorithm implementations to share a common base module. Since CRC-16 operates in 32-bit space (scaling 16-bit values up for processing, then down for results), both widths use nearly identical SIMD operations. By extracting this shared logic into ` src/crc32/width32_ops.rs` , we eliminate ~200 lines of duplicated code and prepare the codebase for future CRC width additions.
6
7
## Architecture
8
9
### Current Structure
10
11
```
12
src/
13
├── crc16/
14
│ ├── algorithm.rs # Width16 impl + process_0_to_15 (duplicated)
15
│ ├── consts.rs
16
│ └── mod.rs
17
├── crc32/
18
│ ├── algorithm.rs # Width32 impl + process_0_to_15 (duplicated)
19
│ ├── consts.rs
20
│ └── mod.rs
21
├── crc64/
22
│ ├── algorithm.rs # Width64 impl + process_0_to_15 (unique)
23
│ ├── consts.rs
24
│ └── mod.rs
25
└── algorithm.rs # Shared algorithm infrastructure
26
```
27
28
### Proposed Structure
29
30
```
31
src/
32
├── crc16/
33
│ ├── algorithm.rs # Width16 impl (delegates to width32_ops)
34
│ ├── consts.rs
35
│ └── mod.rs
36
├── crc32/
37
│ ├── algorithm.rs # Width32 impl (delegates to width32_ops)
38
│ ├── consts.rs
39
│ ├── mod.rs
40
│ └── width32_ops.rs # NEW: Shared 32-bit-space operations
41
├── crc64/
42
│ ├── algorithm.rs # Width64 impl + process_0_to_15 (unchanged)
43
│ ├── consts.rs
44
│ └── mod.rs
45
└── algorithm.rs # Shared algorithm infrastructure
46
```
47
48
## Components and Interfaces
49
50
### 1. New Module: ` src/crc32/width32_ops.rs`
51
52
This module contains all shared 32-bit-space operations:
53
54
``` rust
55
//! Shared operations for CRC widths that operate in 32-bit space (CRC-16, CRC-32).
56
//!
57
//! CRC-16 computation scales 16-bit values to 32-bit space, uses these operations,
58
//! then scales results back to 16 bits. CRC-32 uses these operations directly.
59
60
use crate::algorithm;
61
use crate::consts::CRC_CHUNK_SIZE;
62
use crate::crc32::consts::{PSHUFB_SHF_TABLE_FORWARD, PSHUFB_SHF_TABLE_REVERSE, SIMD_CONSTANTS};
63
use crate::enums::Reflector;
64
use crate::structs::CrcState;
65
use crate::traits::{ArchOps, EnhancedCrcWidth};
66
67
/// SIMD constants shared by CRC-16 and CRC-32
68
pub const WIDTH32_CONSTANTS_REFLECTED: [[u64; 2]; 4] = [
69
[0x08090a0b0c0d0e0f, 0x0001020304050607], // smask
70
[0x8080808080808080, 0x8080808080808080], // mask1
71
[0xFFFFFFFF00000000, 0xFFFFFFFFFFFFFFFF], // mask2 reverse
72
[0x0000000000000000, 0x0000000000000000], // unused
73
];
74
75
pub const WIDTH32_CONSTANTS_FORWARD: [[u64; 2]; 4] = [
76
[0x08090a0b0c0d0e0f, 0x0001020304050607], // smask
77
[0x8080808080808080, 0x8080808080808080], // mask1
78
[0xffffffffffffffff, 0x00000000ffffffff], // mask2 forward
79
[0x0000000000000000, 0x0000000000000000], // unused
80
];
81
82
/// Load constants for 32-bit-space CRC operations
83
#[inline(always)]
84
pub fn load_constants(reflected: bool) -> [[u64; 2]; 4] {
85
if reflected {
86
WIDTH32_CONSTANTS_REFLECTED
87
} else {
88
WIDTH32_CONSTANTS_FORWARD
89
}
90
}
91
92
/// Fold 16 bytes for 32-bit-space CRC operations
93
#[inline(always)]
94
pub unsafe fn fold_16<T: ArchOps>(
95
state: &mut CrcState<T::Vector>,
96
coeff: T::Vector,
97
data_to_xor: T::Vector,
98
ops: &T,
99
) where
100
T::Vector: Copy,
101
{
102
let (h, l) = if state.reflected {
103
(
104
ops.carryless_mul_10(state.value, coeff),
105
ops.carryless_mul_01(state.value, coeff),
106
)
107
} else {
108
(
109
ops.carryless_mul_00(state.value, coeff),
110
ops.carryless_mul_11(state.value, coeff),
111
)
112
};
113
state.value = ops.xor3_vectors(h, l, data_to_xor);
114
}
115
116
/// Fold to width for 32-bit-space CRC operations
117
#[inline(always)]
118
pub unsafe fn fold_width<T: ArchOps>(
119
state: &mut CrcState<T::Vector>,
120
high: u64,
121
low: u64,
122
ops: &T,
123
) where
124
T::Vector: Copy,
125
{
126
let coeff_vector_low = ops.create_vector_from_u64_pair_non_reflected(0, low);
127
let coeff_vector_high = ops.create_vector_from_u64_pair_non_reflected(high, 0);
128
129
state.value = if state.reflected {
130
ops.xor_vectors(
131
ops.carryless_mul_00(state.value, coeff_vector_low),
132
ops.shift_right_8(state.value),
133
)
134
} else {
135
ops.xor_vectors(
136
ops.carryless_mul_01(state.value, coeff_vector_low),
137
ops.shift_left_8(state.value),
138
)
139
};
140
141
let (clmul, masked) = if state.reflected {
142
let mask2 = ops.load_aligned(&[0xFFFFFFFF00000000, 0xFFFFFFFFFFFFFFFF]);
143
let masked = ops.and_vectors(state.value, mask2);
144
let shifted = ops.shift_left_12(state.value);
145
let clmul = ops.carryless_mul_11(shifted, coeff_vector_high);
146
(clmul, masked)
147
} else {
148
let mask2 = ops.load_aligned(&[0xFFFFFFFFFFFFFFFF, 0x00000000FFFFFFFF]);
149
let masked = ops.and_vectors(state.value, mask2);
150
let shifted = ops.shift_right_12(state.value);
151
let clmul = ops.carryless_mul_10(shifted, coeff_vector_high);
152
(clmul, masked)
153
};
154
155
state.value = ops.xor_vectors(clmul, masked);
156
}
157
158
/// Barrett reduction for 32-bit-space CRC operations
159
/// Returns the full u64 result; caller extracts appropriate bits
160
#[inline(always)]
161
pub unsafe fn barrett_reduction<T: ArchOps>(
162
state: &CrcState<T::Vector>,
163
poly: u64,
164
mu: u64,
165
ops: &T,
166
) -> [u64; 2]
167
where
168
T::Vector: Copy,
169
{
170
let x = state.value;
171
let mu_poly = ops.create_vector_from_u64_pair_non_reflected(poly, mu);
172
173
if state.reflected {
174
let clmul1 = ops.carryless_mul_00(x, mu_poly);
175
let clmul2 = ops.carryless_mul_10(clmul1, mu_poly);
176
let xorred = ops.xor_vectors(x, clmul2);
177
ops.extract_u64s(xorred)
178
} else {
179
let clmul1 = ops.shift_left_4(ops.carryless_mul_01(x, mu_poly));
180
let clmul2_shifted = ops.shift_left_4(ops.carryless_mul_11(clmul1, mu_poly));
181
let final_xor = ops.xor_vectors(clmul2_shifted, x);
182
ops.extract_u64s(final_xor)
183
}
184
}
185
186
/// Create coefficient vector for 32-bit-space CRC operations
187
#[inline(always)]
188
pub unsafe fn create_coefficient<T: ArchOps>(
189
high: u64,
190
low: u64,
191
ops: &T,
192
) -> T::Vector
193
where
194
T::Vector: Copy,
195
{
196
ops.create_vector_from_u64_pair_non_reflected(high, low)
197
}
198
199
/// Get shuffle table pointer for 32-bit-space CRC operations
200
#[inline(always)]
201
pub fn get_last_bytes_table_ptr(reflected: bool, remaining_len: usize) -> (*const u8, usize) {
202
if reflected {
203
let base_ptr = &PSHUFB_SHF_TABLE_REVERSE as *const _ as *const u8;
204
(base_ptr, remaining_len)
205
} else {
206
let base_ptr = &PSHUFB_SHF_TABLE_FORWARD as *const _ as *const u8;
207
(base_ptr, 16 - remaining_len)
208
}
209
}
210
211
/// Process inputs smaller than 16 bytes for 32-bit-space CRC operations
212
#[inline]
213
#[cfg_attr(
214
any(target_arch = "x86", target_arch = "x86_64"),
215
target_feature(enable = "ssse3,sse4.1,pclmulqdq")
216
)]
217
#[cfg_attr(target_arch = "aarch64", target_feature(enable = "aes"))]
218
pub unsafe fn process_0_to_15<T: ArchOps, W: EnhancedCrcWidth>(
219
data: &[u8],
220
state: &mut CrcState<T::Vector>,
221
reflector: &Reflector<T::Vector>,
222
keys: [u64; 23],
223
ops: &T,
224
) -> W::Value
225
where
226
T::Vector: Copy,
227
{
228
// Implementation shared by CRC-16 and CRC-32
229
// ... (full implementation)
230
}
231
```
232
233
### 2. Updated ` src/crc16/algorithm.rs`
234
235
The Width16 implementation delegates to shared operations:
236
237
``` rust
238
impl EnhancedCrcWidth for Width16 {
239
#[inline(always)]
240
fn load_constants(reflected: bool) -> [[u64; 2]; 4] {
241
crate::crc32::width32_ops::load_constants(reflected)
242
}
243
244
#[inline(always)]
245
unsafe fn fold_16<T: ArchOps>(...) {
246
crate::crc32::width32_ops::fold_16(state, coeff, data_to_xor, ops)
247
}
248
249
#[inline(always)]
250
unsafe fn barrett_reduction<T: ArchOps>(...) -> Self::Value {
251
let u64s = crate::crc32::width32_ops::barrett_reduction(state, poly, mu, ops);
252
// Extract 16-bit result
253
if state.reflected {
254
u64s[1] as u16
255
} else {
256
((u64s[0] >> 32) >> 16) as u16
257
}
258
}
259
260
// ... other delegating methods
261
}
262
263
// process_0_to_15 delegates to shared implementation
264
pub(crate) unsafe fn process_0_to_15<T: ArchOps, W: EnhancedCrcWidth>(...) -> W::Value {
265
crate::crc32::width32_ops::process_0_to_15::<T, W>(data, state, reflector, keys, ops)
266
}
267
```
268
269
### 3. Updated ` src/crc32/algorithm.rs`
270
271
The Width32 implementation also delegates to shared operations:
272
273
``` rust
274
impl EnhancedCrcWidth for Width32 {
275
#[inline(always)]
276
fn load_constants(reflected: bool) -> [[u64; 2]; 4] {
277
crate::crc32::width32_ops::load_constants(reflected)
278
}
279
280
#[inline(always)]
281
unsafe fn barrett_reduction<T: ArchOps>(...) -> Self::Value {
282
let u64s = crate::crc32::width32_ops::barrett_reduction(state, poly, mu, ops);
283
// Extract 32-bit result
284
if state.reflected {
285
u64s[1] as u32
286
} else {
287
(u64s[0] >> 32) as u32
288
}
289
}
290
291
// ... other delegating methods
292
}
293
294
// process_0_to_15 delegates to shared implementation
295
pub(crate) unsafe fn process_0_to_15<T: ArchOps, W: EnhancedCrcWidth>(...) -> W::Value {
296
crate::crc32::width32_ops::process_0_to_15::<T, W>(data, state, reflector, keys, ops)
297
}
298
```
299
300
### 4. Updated ` src/crc32/mod.rs`
301
302
Export the new module:
303
304
``` rust
305
pub mod algorithm;
306
pub mod consts;
307
pub(crate) mod width32_ops; // NEW
308
```
309
310
## Data Models
311
312
No changes to data models. The refactoring only affects internal implementation.
313
314
## Correctness Properties
315
316
* A property is a characteristic or behavior that should hold true across all valid executions of a system.*
317
318
### Property 1: CRC-16 Computation Unchanged
319
320
* For any* input byte sequence and any CRC-16 parameters, the computed checksum after refactoring SHALL equal the checksum computed before refactoring.
321
322
** Validates: Requirements 4.1**
323
324
### Property 2: CRC-32 Computation Unchanged
325
326
* For any* input byte sequence and any CRC-32 parameters, the computed checksum after refactoring SHALL equal the checksum computed before refactoring.
327
328
** Validates: Requirements 4.2**
329
330
### Property 3: CRC-64 Computation Unchanged
331
332
* For any* input byte sequence and any CRC-64 parameters, the computed checksum after refactoring SHALL equal the checksum computed before refactoring.
333
334
** Validates: Requirements 4.3**
335
336
## Error Handling
337
338
No changes to error handling. The refactoring is purely internal.
339
340
## Testing Strategy
341
342
### Existing Tests
343
344
All existing tests serve as regression tests:
345
- CRC-16 check value tests (0x906E, 0xD0DB)
346
- CRC-32 check value tests
347
- CRC-64 check value tests
348
- Property-based tests comparing against ` crc` crate
349
- Length-based tests for various input sizes
350
351
### Verification Approach
352
353
1. Run ` cargo test` before refactoring to establish baseline
354
2. Perform refactoring
355
3. Run ` cargo test` after refactoring to verify no regressions
356
4. Run ` cargo fmt --check` and ` cargo clippy -- -D warnings`
357
358
No new tests are needed since this is a pure refactoring with no behavior changes.