Skip to main content

leodos_protocols/transport/cfdp/
checksum.rs

1/// A synchronous, stateful checksum calculator.
2pub trait CfdpChecksum: Send {
3    /// Updates the internal state with a new chunk of data.
4    fn update(&mut self, data: &[u8]);
5
6    /// Consumes the calculator and returns the final 32-bit checksum.
7    fn finalize(self) -> u32;
8}
9
10/// CCSDS Modular Checksum implementation (sum of 32-bit words).
11pub mod modular {
12    use heapless::Vec;
13
14    use crate::transport::cfdp::checksum::CfdpChecksum;
15
16    /// A CCSDS Modular Checksum calculator (sum of big-endian 32-bit words).
17    pub struct ModularChecksum {
18        /// Running 32-bit checksum accumulator.
19        sum: u32,
20        /// Buffer for incomplete trailing bytes not yet forming a full word.
21        pending: Vec<u8, 4>,
22    }
23
24    impl ModularChecksum {
25        /// Creates a new modular checksum calculator with an initial sum of zero.
26        pub fn new() -> Self {
27            Self {
28                sum: 0,
29                pending: Vec::new(),
30            }
31        }
32    }
33
34    impl CfdpChecksum for ModularChecksum {
35        fn update(&mut self, data: &[u8]) {
36            let mut cursor = 0;
37
38            // 1. Fill pending buffer
39            while !self.pending.is_full() && cursor < data.len() {
40                let _ = self.pending.push(data[cursor]);
41                cursor += 1;
42            }
43
44            // 2. Process pending word
45            if self.pending.is_full() {
46                let word = u32::from_be_bytes(self.pending.as_slice().try_into().unwrap());
47                self.sum = self.sum.wrapping_add(word);
48                self.pending.clear();
49            }
50
51            // 3. Process main body
52            let remaining = &data[cursor..];
53            let mut chunks = remaining.chunks_exact(4);
54            for chunk in chunks.by_ref() {
55                let word = u32::from_be_bytes(chunk.try_into().unwrap());
56                self.sum = self.sum.wrapping_add(word);
57            }
58
59            // 4. Save remainder
60            for b in chunks.remainder() {
61                let _ = self.pending.push(*b);
62            }
63        }
64
65        fn finalize(self) -> u32 {
66            let mut final_sum = self.sum;
67            if !self.pending.is_empty() {
68                let mut padded = [0u8; 4];
69                padded[..self.pending.len()].copy_from_slice(&self.pending);
70                let word = u32::from_be_bytes(padded);
71                final_sum = final_sum.wrapping_add(word);
72            }
73            final_sum
74        }
75    }
76}
77
78/// CRC-based checksum implementations (CRC-32C and IEEE 802.3).
79pub mod crc {
80    use crc::CRC_32_ISCSI;
81    use crc::CRC_32_ISO_HDLC;
82    use crc::Crc;
83    use crc::Digest;
84
85    use crate::transport::cfdp::checksum::CfdpChecksum;
86
87    static CRC_CASTAGNOLI: Crc<u32> = Crc::<u32>::new(&CRC_32_ISCSI);
88    static CRC_IEEE: Crc<u32> = Crc::<u32>::new(&CRC_32_ISO_HDLC);
89
90    /// A CRC-32 checksum calculator supporting CRC-32C and IEEE variants.
91    pub struct CrcChecksum {
92        /// The underlying CRC digest state.
93        digest: Digest<'static, u32>,
94    }
95
96    impl CrcChecksum {
97        /// Creates a calculator for CRC-32C (Castagnoli).
98        pub fn crc32c() -> Self {
99            Self {
100                digest: CRC_CASTAGNOLI.digest(),
101            }
102        }
103
104        /// Creates a calculator for IEEE 802.3 CRC-32 (also used for Proximity-1).
105        pub fn ieee() -> Self {
106            Self {
107                digest: CRC_IEEE.digest(),
108            }
109        }
110    }
111    impl CfdpChecksum for CrcChecksum {
112        fn update(&mut self, data: &[u8]) {
113            self.digest.update(data);
114        }
115
116        fn finalize(self) -> u32 {
117            self.digest.finalize()
118        }
119    }
120}
121
122/// A no-op checksum that always returns zero.
123pub mod null {
124    use crate::transport::cfdp::checksum::CfdpChecksum;
125
126    /// A null checksum calculator that always produces zero.
127    pub struct NullChecksum;
128
129    impl CfdpChecksum for NullChecksum {
130        fn update(&mut self, _data: &[u8]) {}
131        fn finalize(self) -> u32 {
132            0
133        }
134    }
135}