compress_lib/checksum/
crc32.rs1const POLY: u32 = 0xEDB8_8320;
5
6static TABLES: [[u32; 256]; 16] = build_tables();
10
11const fn build_tables() -> [[u32; 256]; 16] {
12 let mut tables = [[0u32; 256]; 16];
13 let mut i = 0;
14 while i < 256 {
15 let mut crc = i as u32;
16 let mut bit = 0;
17 while bit < 8 {
18 crc = if crc & 1 != 0 {
19 (crc >> 1) ^ POLY
20 } else {
21 crc >> 1
22 };
23 bit += 1;
24 }
25 tables[0][i] = crc;
26 i += 1;
27 }
28 let mut k = 1;
29 while k < 16 {
30 let mut i = 0;
31 while i < 256 {
32 let prev = tables[k - 1][i];
33 tables[k][i] = (prev >> 8) ^ tables[0][(prev & 0xFF) as usize];
34 i += 1;
35 }
36 k += 1;
37 }
38 tables
39}
40
41fn update(mut crc: u32, data: &[u8]) -> u32 {
43 let t = &TABLES;
44 let mut chunks = data.chunks_exact(16);
45 for c in &mut chunks {
46 let a = crc ^ u32::from_le_bytes([c[0], c[1], c[2], c[3]]);
47 crc = t[15][(a & 0xFF) as usize]
48 ^ t[14][((a >> 8) & 0xFF) as usize]
49 ^ t[13][((a >> 16) & 0xFF) as usize]
50 ^ t[12][(a >> 24) as usize]
51 ^ t[11][c[4] as usize]
52 ^ t[10][c[5] as usize]
53 ^ t[9][c[6] as usize]
54 ^ t[8][c[7] as usize]
55 ^ t[7][c[8] as usize]
56 ^ t[6][c[9] as usize]
57 ^ t[5][c[10] as usize]
58 ^ t[4][c[11] as usize]
59 ^ t[3][c[12] as usize]
60 ^ t[2][c[13] as usize]
61 ^ t[1][c[14] as usize]
62 ^ t[0][c[15] as usize];
63 }
64 for &b in chunks.remainder() {
65 crc = (crc >> 8) ^ t[0][((crc ^ u32::from(b)) & 0xFF) as usize];
66 }
67 crc
68}
69
70#[must_use]
84pub fn crc32(data: &[u8]) -> u32 {
85 !update(!0, data)
86}
87
88#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
103pub struct Crc32 {
104 state: u32,
106 len: u64,
108}
109
110impl Crc32 {
111 #[must_use]
119 pub const fn new() -> Self {
120 Self { state: !0, len: 0 }
121 }
122
123 #[must_use]
136 pub const fn from_value(crc: u32, len: u64) -> Self {
137 Self { state: !crc, len }
138 }
139
140 pub fn update(&mut self, data: &[u8]) {
150 self.state = update(self.state, data);
151 self.len = self.len.wrapping_add(data.len() as u64);
152 }
153
154 #[must_use]
165 pub const fn finish(&self) -> u32 {
166 !self.state
167 }
168
169 #[must_use]
179 pub const fn len(&self) -> u64 {
180 self.len
181 }
182
183 #[must_use]
191 pub const fn is_empty(&self) -> bool {
192 self.len == 0
193 }
194
195 pub fn combine(&mut self, other: &Crc32) {
211 let combined = crc32_combine(self.finish(), other.finish(), other.len);
212 self.state = !combined;
213 self.len = self.len.wrapping_add(other.len);
214 }
215}
216
217impl Default for Crc32 {
218 fn default() -> Self {
219 Self::new()
220 }
221}
222
223const fn multiply_mod(a: u32, mut b: u32) -> u32 {
226 let mut product = 0;
227 let mut mask: u32 = 1 << 31;
228 while mask != 0 {
229 if a & mask != 0 {
230 product ^= b;
231 }
232 mask >>= 1;
233 b = if b & 1 != 0 { (b >> 1) ^ POLY } else { b >> 1 };
234 }
235 product
236}
237
238static POWERS: [u32; 67] = build_powers();
240
241const fn build_powers() -> [u32; 67] {
242 let mut powers = [0u32; 67];
243 let mut p: u32 = 1 << 30; powers[0] = p;
245 let mut k = 1;
246 while k < 67 {
247 p = multiply_mod(p, p);
248 powers[k] = p;
249 k += 1;
250 }
251 powers
252}
253
254fn shift_bytes(bytes: u64) -> u32 {
256 let mut p: u32 = 1 << 31; let mut n = bytes;
258 let mut k = 3;
261 while n != 0 && k < POWERS.len() {
262 if n & 1 != 0 {
263 p = multiply_mod(POWERS[k], p);
264 }
265 n >>= 1;
266 k += 1;
267 }
268 p
269}
270
271#[must_use]
283pub fn crc32_combine(crc1: u32, crc2: u32, len2: u64) -> u32 {
284 multiply_mod(shift_bytes(len2), crc1) ^ crc2
285}
286
287#[cfg(test)]
288mod tests {
289 use alloc::vec::Vec;
290
291 use proptest::prelude::*;
292
293 use super::*;
294
295 fn reference(data: &[u8]) -> u32 {
297 let mut crc = !0u32;
298 for &b in data {
299 crc ^= u32::from(b);
300 for _ in 0..8 {
301 crc = if crc & 1 != 0 {
302 (crc >> 1) ^ POLY
303 } else {
304 crc >> 1
305 };
306 }
307 }
308 !crc
309 }
310
311 #[test]
312 fn test_crc32_check_value_matches_catalogue() {
313 assert_eq!(crc32(b"123456789"), 0xCBF4_3926);
315 }
316
317 #[test]
318 fn test_crc32_known_strings_match() {
319 assert_eq!(crc32(b""), 0);
320 assert_eq!(crc32(b"a"), 0xE8B7_BE43);
321 assert_eq!(crc32(b"abc"), 0x3524_41C2);
322 assert_eq!(
323 crc32(b"The quick brown fox jumps over the lazy dog"),
324 0x414F_A339
325 );
326 assert_eq!(crc32(&[0u8; 32]), 0x190A_55AD);
327 assert_eq!(crc32(&[0xFFu8; 32]), 0xFF6C_AB0B);
328 }
329
330 #[test]
331 fn test_crc32_every_length_to_100_matches_reference() {
332 let data: Vec<u8> = (0..100u32).map(|i| (i * 31 + 7) as u8).collect();
333 for n in 0..=data.len() {
334 assert_eq!(crc32(&data[..n]), reference(&data[..n]), "length {n}");
335 }
336 }
337
338 #[test]
339 fn test_crc32_from_value_continues() {
340 let mut crc = Crc32::from_value(crc32(b"12345"), 5);
341 crc.update(b"6789");
342 assert_eq!(crc.finish(), 0xCBF4_3926);
343 assert_eq!(crc.len(), 9);
344 assert!(!crc.is_empty());
345 assert_eq!(Crc32::default(), Crc32::new());
346 }
347
348 #[test]
349 fn test_crc32_combine_empty_second_is_identity() {
350 assert_eq!(crc32_combine(0x1234_5678, 0, 0), 0x1234_5678);
351 let mut a = Crc32::new();
352 a.update(b"x");
353 a.combine(&Crc32::new());
354 assert_eq!(a.finish(), crc32(b"x"));
355 }
356
357 #[test]
358 fn test_crc32_combine_huge_length_terminates() {
359 let a = crc32_combine(1, 2, u64::MAX);
362 assert_eq!(a, crc32_combine(1, 2, u64::MAX));
363 }
364
365 proptest! {
366 #[test]
367 fn prop_crc32_matches_reference(data in proptest::collection::vec(any::<u8>(), 0..2048)) {
368 prop_assert_eq!(crc32(&data), reference(&data));
369 }
370
371 #[test]
372 fn prop_crc32_split_invariant(data in proptest::collection::vec(any::<u8>(), 0..2048), cut in any::<usize>()) {
373 let cut = if data.is_empty() { 0 } else { cut % (data.len() + 1) };
374 let mut crc = Crc32::new();
375 crc.update(&data[..cut]);
376 crc.update(&data[cut..]);
377 prop_assert_eq!(crc.finish(), crc32(&data));
378 }
379
380 #[test]
381 fn prop_crc32_combine_matches_concatenation(a in proptest::collection::vec(any::<u8>(), 0..512), b in proptest::collection::vec(any::<u8>(), 0..512)) {
382 let mut whole = a.clone();
383 whole.extend_from_slice(&b);
384 prop_assert_eq!(crc32_combine(crc32(&a), crc32(&b), b.len() as u64), crc32(&whole));
385 }
386 }
387}