graven/
crc.rs

1//! CRC32C (Castagnoli) used by the envelope trailer.
2
3/// Reflected Castagnoli polynomial.
4const POLY: u32 = 0x82F6_3B78;
5
6/// Slicing-by-8 lookup tables, computed at compile time.
7static TABLES: [[u32; 256]; 8] = build_tables();
8
9const fn build_tables() -> [[u32; 256]; 8] {
10    let mut t = [[0u32; 256]; 8];
11    let mut i = 0;
12    while i < 256 {
13        let mut c = i as u32;
14        let mut k = 0;
15        while k < 8 {
16            c = if c & 1 != 0 { (c >> 1) ^ POLY } else { c >> 1 };
17            k += 1;
18        }
19        t[0][i] = c;
20        i += 1;
21    }
22    let mut i = 0;
23    while i < 256 {
24        let mut s = 1;
25        while s < 8 {
26            let prev = t[s - 1][i];
27            t[s][i] = (prev >> 8) ^ t[0][(prev & 0xFF) as usize];
28            s += 1;
29        }
30        i += 1;
31    }
32    t
33}
34
35/// Portable software CRC32C (slicing-by-8). Always available, including `no_std`.
36#[doc(hidden)]
37pub fn crc32c_sw(data: &[u8]) -> u32 {
38    let t = &TABLES;
39    let mut crc = !0u32;
40    let mut chunks = data.chunks_exact(8);
41    for c in &mut chunks {
42        let lo = u32::from_le_bytes([c[0], c[1], c[2], c[3]]) ^ crc;
43        let hi = u32::from_le_bytes([c[4], c[5], c[6], c[7]]);
44        crc = t[7][(lo & 0xFF) as usize]
45            ^ t[6][((lo >> 8) & 0xFF) as usize]
46            ^ t[5][((lo >> 16) & 0xFF) as usize]
47            ^ t[4][(lo >> 24) as usize]
48            ^ t[3][(hi & 0xFF) as usize]
49            ^ t[2][((hi >> 8) & 0xFF) as usize]
50            ^ t[1][((hi >> 16) & 0xFF) as usize]
51            ^ t[0][(hi >> 24) as usize];
52    }
53    for &b in chunks.remainder() {
54        crc = (crc >> 8) ^ t[0][((crc ^ b as u32) & 0xFF) as usize];
55    }
56    !crc
57}
58
59/// CRC32C of `data`. Hardware-accelerated with `std`, software otherwise.
60#[cfg(feature = "std")]
61pub fn crc32c(data: &[u8]) -> u32 {
62    ::crc32c::crc32c(data)
63}
64
65/// CRC32C of `data`. Hardware-accelerated with `std`, software otherwise.
66#[cfg(not(feature = "std"))]
67pub fn crc32c(data: &[u8]) -> u32 {
68    crc32c_sw(data)
69}
70
71#[cfg(test)]
72mod tests {
73    use super::*;
74    use proptest::prelude::*;
75
76    #[test]
77    fn check_value() {
78        assert_eq!(crc32c(b"123456789"), 0xE306_9283);
79        assert_eq!(crc32c_sw(b"123456789"), 0xE306_9283);
80    }
81
82    #[test]
83    fn empty_is_zero() {
84        assert_eq!(crc32c(b""), 0);
85        assert_eq!(crc32c_sw(b""), 0);
86    }
87
88    // Without `std`, `crc32c` *is* `crc32c_sw`: the comparison only makes sense with the accelerated one.
89    #[cfg(feature = "std")]
90    proptest! {
91        #[test]
92        fn sw_matches_accel(v in proptest::collection::vec(any::<u8>(), 0..5000)) {
93            prop_assert_eq!(crc32c_sw(&v), crc32c(&v));
94        }
95    }
96}