Skip to main content

pamoja_mesh/
crc.rs

1//! CRC-16/CCITT-FALSE, the integrity check the mesh frame carries.
2
3/// An incremental CRC-16/CCITT-FALSE accumulator.
4///
5/// CCITT-FALSE is the long-standing checksum of short radio frames: polynomial `0x1021`,
6/// initial value `0xFFFF`, no reflection, and no final inversion. The accumulator lets a
7/// checksum span more than one slice, which the mesh frame needs because it sums its
8/// header and its payload while skipping the mutable hop-limit byte between them. For a
9/// single contiguous slice, [`crc16`] is the one-shot form.
10///
11/// # Examples
12///
13/// ```
14/// use pamoja_mesh::Crc16;
15///
16/// // Summing in two parts matches summing the whole in one go.
17/// let mut crc = Crc16::new();
18/// crc.update(b"1234");
19/// crc.update(b"56789");
20/// assert_eq!(crc.finish(), 0x29B1);
21/// ```
22#[derive(Clone, Copy, Debug, PartialEq, Eq)]
23pub struct Crc16 {
24    state: u16,
25}
26
27impl Crc16 {
28    /// Creates an accumulator primed with the CCITT-FALSE initial value.
29    ///
30    /// # Returns
31    ///
32    /// A fresh accumulator, ready for [`update`](Crc16::update).
33    pub const fn new() -> Self {
34        Crc16 { state: 0xFFFF }
35    }
36
37    /// Folds a slice of bytes into the running checksum.
38    ///
39    /// # Arguments
40    ///
41    /// * `data` - the bytes to add to the checksum.
42    pub fn update(&mut self, data: &[u8]) {
43        for &byte in data {
44            self.state ^= u16::from(byte) << 8;
45            for _ in 0..8 {
46                if self.state & 0x8000 != 0 {
47                    self.state = (self.state << 1) ^ 0x1021;
48                } else {
49                    self.state <<= 1;
50                }
51            }
52        }
53    }
54
55    /// Returns the checksum of everything folded in so far.
56    ///
57    /// # Returns
58    ///
59    /// The 16-bit CRC.
60    pub fn finish(&self) -> u16 {
61        self.state
62    }
63}
64
65impl Default for Crc16 {
66    fn default() -> Self {
67        Self::new()
68    }
69}
70
71/// Computes the CRC-16/CCITT-FALSE of a single byte slice.
72///
73/// # Arguments
74///
75/// * `data` - the bytes to check.
76///
77/// # Returns
78///
79/// The 16-bit CRC.
80///
81/// # Examples
82///
83/// ```
84/// use pamoja_mesh::crc16;
85///
86/// // The standard CRC-16/CCITT-FALSE check value over the ASCII digits "123456789".
87/// assert_eq!(crc16(b"123456789"), 0x29B1);
88/// ```
89pub fn crc16(data: &[u8]) -> u16 {
90    let mut crc = Crc16::new();
91    crc.update(data);
92    crc.finish()
93}
94
95#[cfg(test)]
96mod tests {
97    use super::*;
98
99    #[test]
100    fn matches_the_standard_check_value() {
101        assert_eq!(crc16(b"123456789"), 0x29B1);
102    }
103
104    #[test]
105    fn an_empty_slice_is_the_initial_value() {
106        assert_eq!(crc16(&[]), 0xFFFF);
107    }
108
109    #[test]
110    fn updating_in_parts_matches_one_shot() {
111        let mut split = Crc16::new();
112        split.update(b"123");
113        split.update(b"456789");
114        assert_eq!(split.finish(), crc16(b"123456789"));
115    }
116}