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}