crc/
crc128.rs

1use crate::table::crc128_table;
2use crate::util::crc128;
3use crate::*;
4use crc_catalog::Algorithm;
5
6impl<const L: usize> Crc<u128, Table<L>>
7where
8    Table<L>: private::Sealed,
9{
10    pub const fn new(algorithm: &'static Algorithm<u128>) -> Self {
11        Self {
12            algorithm,
13            data: crc128_table(algorithm.width, algorithm.poly, algorithm.refin),
14        }
15    }
16
17    pub const fn checksum(&self, bytes: &[u8]) -> u128 {
18        let mut crc = init(self.algorithm, self.algorithm.init);
19        crc = self.update(crc, bytes);
20        finalize(self.algorithm, crc)
21    }
22
23    const fn update(&self, crc: u128, bytes: &[u8]) -> u128 {
24        update_table(crc, self.algorithm, &self.data, bytes)
25    }
26
27    pub const fn digest(&self) -> Digest<u128, Table<L>> {
28        self.digest_with_initial(self.algorithm.init)
29    }
30
31    /// Construct a `Digest` with a given initial value.
32    ///
33    /// This overrides the initial value specified by the algorithm.
34    /// The effects of the algorithm's properties `refin` and `width`
35    /// are applied to the custom initial value.
36    pub const fn digest_with_initial(&self, initial: u128) -> Digest<u128, Table<L>> {
37        let value = init(self.algorithm, initial);
38        Digest::new(self, value)
39    }
40
41    pub const fn table(&self) -> &<Table<L> as Implementation>::Data<u128> {
42        &self.data
43    }
44}
45
46impl<'a, const L: usize> Digest<'a, u128, Table<L>>
47where
48    Table<L>: private::Sealed,
49{
50    const fn new(crc: &'a Crc<u128, Table<L>>, value: u128) -> Self {
51        Digest { crc, value }
52    }
53
54    pub fn update(&mut self, bytes: &[u8]) {
55        self.value = self.crc.update(self.value, bytes);
56    }
57
58    pub const fn finalize(self) -> u128 {
59        finalize(self.crc.algorithm, self.value)
60    }
61}
62
63const fn init(algorithm: &Algorithm<u128>, initial: u128) -> u128 {
64    if algorithm.refin {
65        initial.reverse_bits() >> (128u8 - algorithm.width)
66    } else {
67        initial << (128u8 - algorithm.width)
68    }
69}
70
71const fn finalize(algorithm: &Algorithm<u128>, mut crc: u128) -> u128 {
72    if algorithm.refin ^ algorithm.refout {
73        crc = crc.reverse_bits();
74    }
75    if !algorithm.refout {
76        crc >>= 128u8 - algorithm.width;
77    }
78    crc ^ algorithm.xorout
79}
80
81const fn update_table<const L: usize>(
82    mut crc: u128,
83    algorithm: &Algorithm<u128>,
84    table: &[[u128; 256]; L],
85    bytes: &[u8],
86) -> u128 {
87    let len = bytes.len();
88    let mut i = 0;
89    let reflect = algorithm.refin;
90
91    // Process 16 bytes at a time when L=16
92    if L == 16 {
93        while i + 16 <= len {
94            if reflect {
95                // XOR the first 16 bytes with the current CRC value
96                let current0 = bytes[i] ^ (crc as u8);
97                let current1 = bytes[i + 1] ^ ((crc >> 8) as u8);
98                let current2 = bytes[i + 2] ^ ((crc >> 16) as u8);
99                let current3 = bytes[i + 3] ^ ((crc >> 24) as u8);
100                let current4 = bytes[i + 4] ^ ((crc >> 32) as u8);
101                let current5 = bytes[i + 5] ^ ((crc >> 40) as u8);
102                let current6 = bytes[i + 6] ^ ((crc >> 48) as u8);
103                let current7 = bytes[i + 7] ^ ((crc >> 56) as u8);
104                let current8 = bytes[i + 8] ^ ((crc >> 64) as u8);
105                let current9 = bytes[i + 9] ^ ((crc >> 72) as u8);
106                let current10 = bytes[i + 10] ^ ((crc >> 80) as u8);
107                let current11 = bytes[i + 11] ^ ((crc >> 88) as u8);
108                let current12 = bytes[i + 12] ^ ((crc >> 96) as u8);
109                let current13 = bytes[i + 13] ^ ((crc >> 104) as u8);
110                let current14 = bytes[i + 14] ^ ((crc >> 112) as u8);
111                let current15 = bytes[i + 15] ^ ((crc >> 120) as u8);
112
113                crc = table[0][current15 as usize]
114                    ^ table[1][current14 as usize]
115                    ^ table[2][current13 as usize]
116                    ^ table[3][current12 as usize]
117                    ^ table[4][current11 as usize]
118                    ^ table[5][current10 as usize]
119                    ^ table[6][current9 as usize]
120                    ^ table[7][current8 as usize]
121                    ^ table[8][current7 as usize]
122                    ^ table[9][current6 as usize]
123                    ^ table[10][current5 as usize]
124                    ^ table[11][current4 as usize]
125                    ^ table[12][current3 as usize]
126                    ^ table[13][current2 as usize]
127                    ^ table[14][current1 as usize]
128                    ^ table[15][current0 as usize];
129            } else {
130                // For non-reflected CRC128
131                let current0 = bytes[i] ^ ((crc >> 120) as u8);
132                let current1 = bytes[i + 1] ^ ((crc >> 112) as u8);
133                let current2 = bytes[i + 2] ^ ((crc >> 104) as u8);
134                let current3 = bytes[i + 3] ^ ((crc >> 96) as u8);
135                let current4 = bytes[i + 4] ^ ((crc >> 88) as u8);
136                let current5 = bytes[i + 5] ^ ((crc >> 80) as u8);
137                let current6 = bytes[i + 6] ^ ((crc >> 72) as u8);
138                let current7 = bytes[i + 7] ^ ((crc >> 64) as u8);
139                let current8 = bytes[i + 8] ^ ((crc >> 56) as u8);
140                let current9 = bytes[i + 9] ^ ((crc >> 48) as u8);
141                let current10 = bytes[i + 10] ^ ((crc >> 40) as u8);
142                let current11 = bytes[i + 11] ^ ((crc >> 32) as u8);
143                let current12 = bytes[i + 12] ^ ((crc >> 24) as u8);
144                let current13 = bytes[i + 13] ^ ((crc >> 16) as u8);
145                let current14 = bytes[i + 14] ^ ((crc >> 8) as u8);
146                let current15 = bytes[i + 15] ^ (crc as u8);
147
148                crc = table[0][current15 as usize]
149                    ^ table[1][current14 as usize]
150                    ^ table[2][current13 as usize]
151                    ^ table[3][current12 as usize]
152                    ^ table[4][current11 as usize]
153                    ^ table[5][current10 as usize]
154                    ^ table[6][current9 as usize]
155                    ^ table[7][current8 as usize]
156                    ^ table[8][current7 as usize]
157                    ^ table[9][current6 as usize]
158                    ^ table[10][current5 as usize]
159                    ^ table[11][current4 as usize]
160                    ^ table[12][current3 as usize]
161                    ^ table[13][current2 as usize]
162                    ^ table[14][current1 as usize]
163                    ^ table[15][current0 as usize];
164            }
165            i += 16;
166        }
167    }
168
169    // Process remaining bytes one at a time using the table (for L=1 and L=16)
170    if L > 0 {
171        if reflect {
172            while i < len {
173                let table_index = ((crc ^ bytes[i] as u128) & 0xFF) as usize;
174                crc = table[0][table_index] ^ (crc >> 8);
175                i += 1;
176            }
177        } else {
178            while i < len {
179                let table_index = (((crc >> 120) ^ bytes[i] as u128) & 0xFF) as usize;
180                crc = table[0][table_index] ^ (crc << 8);
181                i += 1;
182            }
183        }
184    } else {
185        // This section is for NoTable case (L=0)
186        let poly = if reflect {
187            let poly = algorithm.poly.reverse_bits();
188            poly >> (128u8 - algorithm.width)
189        } else {
190            algorithm.poly << (128u8 - algorithm.width)
191        };
192
193        if reflect {
194            while i < len {
195                let to_crc = (crc ^ bytes[i] as u128) & 0xFF;
196                crc = crc128(poly, reflect, to_crc) ^ (crc >> 8);
197                i += 1;
198            }
199        } else {
200            while i < len {
201                let to_crc = ((crc >> 120) ^ bytes[i] as u128) & 0xFF;
202                crc = crc128(poly, reflect, to_crc) ^ (crc << 8);
203                i += 1;
204            }
205        }
206    }
207
208    crc
209}
210
211#[cfg(test)]
212mod test {
213    use crate::*;
214    use crc_catalog::{Algorithm, CRC_82_DARC};
215
216    /// Test this optimized version against the well known implementation to ensure correctness
217    #[test]
218    fn correctness() {
219        let data: &[&str] = &[
220            "",
221            "1",
222            "1234",
223            "123456789",
224            "0123456789ABCDE",
225            "01234567890ABCDEFGHIJK",
226            "01234567890ABCDEFGHIJK01234567890ABCDEFGHIJK01234567890ABCDEFGHIJK01234567890ABCDEFGHIJK01234567890ABCDEFGHIJK01234567890ABCDEFGHIJK01234567890ABCDEFGHIJK01234567890ABCDEFGHIJK01234567890ABCDEFGHIJK01234567890ABCDEFGHIJK01234567890ABCDEFGHIJK01234567890ABCDEFGHIJK",
227        ];
228
229        pub const CRC_82_DARC_NONREFLEX: Algorithm<u128> = Algorithm {
230            width: 82,
231            poly: 0x0308c0111011401440411,
232            init: 0x000000000000000000000,
233            refin: false,
234            refout: true,
235            xorout: 0x000000000000000000000,
236            check: 0x09ea83f625023801fd612,
237            residue: 0x000000000000000000000,
238        };
239
240        let algs_to_test = [&CRC_82_DARC, &CRC_82_DARC_NONREFLEX];
241
242        for alg in algs_to_test {
243            for data in data {
244                let crc_slice16 = Crc::<u128, Table<16>>::new(alg);
245                let crc_nolookup = Crc::<u128, NoTable>::new(alg);
246                let expected = Crc::<u128, Table<1>>::new(alg).checksum(data.as_bytes());
247
248                // Check that doing all at once works as expected
249                assert_eq!(crc_slice16.checksum(data.as_bytes()), expected);
250                assert_eq!(crc_nolookup.checksum(data.as_bytes()), expected);
251
252                let mut digest = crc_slice16.digest();
253                digest.update(data.as_bytes());
254                assert_eq!(digest.finalize(), expected);
255
256                let mut digest = crc_nolookup.digest();
257                digest.update(data.as_bytes());
258                assert_eq!(digest.finalize(), expected);
259
260                // Check that we didn't break updating from multiple sources
261                if data.len() > 2 {
262                    let data = data.as_bytes();
263                    let data1 = &data[..data.len() / 2];
264                    let data2 = &data[data.len() / 2..];
265                    let mut digest = crc_slice16.digest();
266                    digest.update(data1);
267                    digest.update(data2);
268                    assert_eq!(digest.finalize(), expected);
269                    let mut digest = crc_nolookup.digest();
270                    digest.update(data1);
271                    digest.update(data2);
272                    assert_eq!(digest.finalize(), expected);
273                }
274            }
275        }
276    }
277}