crc/
crc8.rs

1use crate::table::crc8_table;
2use crate::util::crc8;
3use crate::*;
4use crc_catalog::Algorithm;
5
6impl<const L: usize> Crc<u8, Table<L>>
7where
8    Table<L>: private::Sealed,
9{
10    pub const fn new(algorithm: &'static Algorithm<u8>) -> Self {
11        Self {
12            algorithm,
13            data: crc8_table(algorithm.width, algorithm.poly, algorithm.refin),
14        }
15    }
16
17    pub const fn checksum(&self, bytes: &[u8]) -> u8 {
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: u8, bytes: &[u8]) -> u8 {
24        update_table(crc, self.algorithm, &self.data, bytes)
25    }
26
27    pub const fn digest(&self) -> Digest<u8, 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: u8) -> Digest<u8, 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<u8> {
42        &self.data
43    }
44}
45
46impl<'a, const L: usize> Digest<'a, u8, Table<L>>
47where
48    Table<L>: private::Sealed,
49{
50    const fn new(crc: &'a Crc<u8, Table<L>>, value: u8) -> 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) -> u8 {
59        finalize(self.crc.algorithm, self.value)
60    }
61}
62
63const fn init(algorithm: &Algorithm<u8>, initial: u8) -> u8 {
64    if algorithm.refin {
65        initial.reverse_bits() >> (8u8 - algorithm.width)
66    } else {
67        initial << (8u8 - algorithm.width)
68    }
69}
70
71const fn finalize(algorithm: &Algorithm<u8>, mut crc: u8) -> u8 {
72    if algorithm.refin ^ algorithm.refout {
73        crc = crc.reverse_bits();
74    }
75    if !algorithm.refout {
76        crc >>= 8u8 - algorithm.width;
77    }
78    crc ^ algorithm.xorout
79}
80
81const fn update_table<const L: usize>(
82    mut crc: u8,
83    algorithm: &Algorithm<u8>,
84    table: &[[u8; 256]; L],
85    bytes: &[u8],
86) -> u8 {
87    let len = bytes.len();
88    let mut i = 0;
89
90    // Process 16 bytes at a time when L=16
91    if L == 16 {
92        while i + 16 <= len {
93            crc = table[0][bytes[i + 15] as usize]
94                ^ table[1][bytes[i + 14] as usize]
95                ^ table[2][bytes[i + 13] as usize]
96                ^ table[3][bytes[i + 12] as usize]
97                ^ table[4][bytes[i + 11] as usize]
98                ^ table[5][bytes[i + 10] as usize]
99                ^ table[6][bytes[i + 9] as usize]
100                ^ table[7][bytes[i + 8] as usize]
101                ^ table[8][bytes[i + 7] as usize]
102                ^ table[9][bytes[i + 6] as usize]
103                ^ table[10][bytes[i + 5] as usize]
104                ^ table[11][bytes[i + 4] as usize]
105                ^ table[12][bytes[i + 3] as usize]
106                ^ table[13][bytes[i + 2] as usize]
107                ^ table[14][bytes[i + 1] as usize]
108                ^ table[15][(bytes[i] ^ crc) as usize];
109            i += 16;
110        }
111    }
112
113    // Process remaining bytes one at a time using the table (for L=1 and L=16)
114    if L > 0 {
115        while i < len {
116            crc = table[0][(crc ^ bytes[i]) as usize];
117            i += 1;
118        }
119    } else {
120        // This section is for NoTable case (L=0)
121        let poly = if algorithm.refin {
122            let poly = algorithm.poly.reverse_bits();
123            poly >> (8u8 - algorithm.width)
124        } else {
125            algorithm.poly << (8u8 - algorithm.width)
126        };
127
128        while i < len {
129            crc = crc8(poly, algorithm.refin, crc ^ bytes[i]);
130            i += 1;
131        }
132    }
133
134    crc
135}
136
137#[cfg(test)]
138mod test {
139    use crate::*;
140    use crc_catalog::{Algorithm, CRC_8_BLUETOOTH};
141
142    /// Test this optimized version against the well known implementation to ensure correctness
143    #[test]
144    fn correctness() {
145        let data: &[&str] = &[
146            "",
147            "1",
148            "1234",
149            "123456789",
150            "0123456789ABCDE",
151            "01234567890ABCDEFGHIJK",
152            "01234567890ABCDEFGHIJK01234567890ABCDEFGHIJK01234567890ABCDEFGHIJK01234567890ABCDEFGHIJK01234567890ABCDEFGHIJK01234567890ABCDEFGHIJK01234567890ABCDEFGHIJK01234567890ABCDEFGHIJK01234567890ABCDEFGHIJK01234567890ABCDEFGHIJK01234567890ABCDEFGHIJK01234567890ABCDEFGHIJK",
153        ];
154
155        pub const CRC_8_BLUETOOTH_NONREFLEX: Algorithm<u8> = Algorithm {
156            width: 8,
157            poly: 0xa7,
158            init: 0x00,
159            refin: false,
160            refout: true,
161            xorout: 0x00,
162            check: 0x26,
163            residue: 0x00,
164        };
165
166        let algs_to_test = [&CRC_8_BLUETOOTH, &CRC_8_BLUETOOTH_NONREFLEX];
167
168        for alg in algs_to_test {
169            for data in data {
170                let crc_slice16 = Crc::<u8, Table<16>>::new(alg);
171                // let crc_slice8 = Crc::<u8, Table<8>>::new(alg);
172                let crc_nolookup = Crc::<u8, NoTable>::new(alg);
173                let expected = Crc::<u8, Table<1>>::new(alg).checksum(data.as_bytes());
174
175                // Check that doing all at once works as expected
176                assert_eq!(crc_slice16.checksum(data.as_bytes()), expected);
177                assert_eq!(crc_nolookup.checksum(data.as_bytes()), expected);
178
179                let mut digest = crc_slice16.digest();
180                digest.update(data.as_bytes());
181                assert_eq!(digest.finalize(), expected);
182
183                let mut digest = crc_nolookup.digest();
184                digest.update(data.as_bytes());
185                assert_eq!(digest.finalize(), expected);
186
187                // Check that we didn't break updating from multiple sources
188                if data.len() > 2 {
189                    let data = data.as_bytes();
190                    let data1 = &data[..data.len() / 2];
191                    let data2 = &data[data.len() / 2..];
192                    let mut digest = crc_slice16.digest();
193                    digest.update(data1);
194                    digest.update(data2);
195                    assert_eq!(digest.finalize(), expected);
196                    let mut digest = crc_nolookup.digest();
197                    digest.update(data1);
198                    digest.update(data2);
199                    assert_eq!(digest.finalize(), expected);
200                }
201            }
202        }
203    }
204}