bacnet_encoding/
segmentation.rs1use bacnet_types::error::Error;
8use bytes::Bytes;
9use std::collections::HashMap;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum SegmentedPduType {
16 ConfirmedRequest,
18 ComplexAck,
20}
21
22pub fn max_segment_payload(max_apdu_length: u16, pdu_type: SegmentedPduType) -> usize {
26 let overhead = match pdu_type {
27 SegmentedPduType::ConfirmedRequest => 6,
28 SegmentedPduType::ComplexAck => 5,
29 };
30 (max_apdu_length as usize).saturating_sub(overhead)
31}
32
33pub fn split_payload(payload: &[u8], max_segment_size: usize) -> Result<Vec<Bytes>, Error> {
37 if payload.is_empty() {
38 return Ok(vec![Bytes::new()]);
39 }
40 if max_segment_size == 0 {
41 return Err(Error::Segmentation(
42 "non-empty payload cannot be segmented with max segment size 0".into(),
43 ));
44 }
45 let segments: Vec<Bytes> = payload
46 .chunks(max_segment_size)
47 .map(Bytes::copy_from_slice)
48 .collect();
49 if segments.len() > 256 {
50 return Err(Error::Segmentation(format!(
51 "payload requires {} segments, maximum is 256",
52 segments.len()
53 )));
54 }
55 Ok(segments)
56}
57
58pub struct SegmentReceiver {
63 segments: HashMap<u8, Bytes>,
64}
65
66impl Default for SegmentReceiver {
67 fn default() -> Self {
68 Self::new()
69 }
70}
71
72impl SegmentReceiver {
73 pub fn new() -> Self {
75 Self {
76 segments: HashMap::new(),
77 }
78 }
79
80 const MAX_SEGMENT_SIZE: usize = 1476;
82
83 pub fn receive(&mut self, sequence_number: u8, data: Bytes) -> Result<(), Error> {
87 if data.len() > Self::MAX_SEGMENT_SIZE {
88 return Err(Error::Segmentation(format!(
89 "segment size {} exceeds maximum {}",
90 data.len(),
91 Self::MAX_SEGMENT_SIZE
92 )));
93 }
94 self.segments.insert(sequence_number, data);
95 Ok(())
96 }
97
98 pub fn has_segment(&self, sequence_number: u8) -> bool {
100 self.segments.contains_key(&sequence_number)
101 }
102
103 pub fn received_count(&self) -> usize {
105 self.segments.len()
106 }
107
108 pub fn reassemble(&self, total_segments: usize) -> Result<Vec<u8>, Error> {
113 if total_segments > 256 {
114 return Err(Error::Segmentation(format!(
115 "total_segments {total_segments} exceeds maximum BACnet value (256)"
116 )));
117 }
118 let mut result = Vec::with_capacity(total_segments * 480);
119 for i in 0..total_segments {
120 let seq = i as u8;
121 match self.segments.get(&seq) {
122 Some(data) => result.extend_from_slice(data),
123 None => {
124 return Err(Error::Segmentation(format!(
125 "missing segment {} of {}",
126 i, total_segments
127 )));
128 }
129 }
130 }
131 Ok(result)
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138
139 #[test]
140 fn max_segment_payload_confirmed_request() {
141 assert_eq!(
142 max_segment_payload(480, SegmentedPduType::ConfirmedRequest),
143 474
144 );
145 assert_eq!(
146 max_segment_payload(1476, SegmentedPduType::ConfirmedRequest),
147 1470
148 );
149 }
150
151 #[test]
152 fn max_segment_payload_complex_ack() {
153 assert_eq!(max_segment_payload(480, SegmentedPduType::ComplexAck), 475);
154 assert_eq!(
155 max_segment_payload(1476, SegmentedPduType::ComplexAck),
156 1471
157 );
158 }
159
160 #[test]
161 fn split_payload_fits_single_segment() {
162 let payload = vec![0u8; 100];
163 let segments = split_payload(&payload, 200).unwrap();
164 assert_eq!(segments.len(), 1);
165 assert_eq!(segments[0], payload);
166 }
167
168 #[test]
169 fn split_payload_exact_fit() {
170 let payload = vec![0u8; 200];
171 let segments = split_payload(&payload, 100).unwrap();
172 assert_eq!(segments.len(), 2);
173 assert_eq!(segments[0].len(), 100);
174 assert_eq!(segments[1].len(), 100);
175 }
176
177 #[test]
178 fn split_payload_remainder() {
179 let payload = vec![0u8; 250];
180 let segments = split_payload(&payload, 100).unwrap();
181 assert_eq!(segments.len(), 3);
182 assert_eq!(segments[0].len(), 100);
183 assert_eq!(segments[1].len(), 100);
184 assert_eq!(segments[2].len(), 50);
185 }
186
187 #[test]
188 fn split_empty_payload() {
189 let segments = split_payload(&[], 100).unwrap();
190 assert_eq!(segments.len(), 1);
191 assert!(segments[0].is_empty());
192 }
193
194 #[test]
195 fn reassemble_ordered_segments() {
196 let original = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
197 let segments = split_payload(&original, 3).unwrap();
198 assert_eq!(segments.len(), 4); let mut receiver = SegmentReceiver::new();
201 for (i, seg) in segments.iter().enumerate() {
202 receiver.receive(i as u8, seg.clone()).unwrap();
203 }
204 let reassembled = receiver.reassemble(segments.len()).unwrap();
205 assert_eq!(reassembled, original);
206 }
207
208 #[test]
209 fn reassemble_out_of_order() {
210 let mut receiver = SegmentReceiver::new();
211 receiver.receive(2, Bytes::from_static(&[5, 6])).unwrap();
212 receiver.receive(0, Bytes::from_static(&[1, 2])).unwrap();
213 receiver.receive(1, Bytes::from_static(&[3, 4])).unwrap();
214 let reassembled = receiver.reassemble(3).unwrap();
215 assert_eq!(reassembled, vec![1, 2, 3, 4, 5, 6]);
216 }
217
218 #[test]
219 fn reassemble_missing_segment_fails() {
220 let mut receiver = SegmentReceiver::new();
221 receiver.receive(0, Bytes::from_static(&[1, 2])).unwrap();
222 receiver.receive(2, Bytes::from_static(&[5, 6])).unwrap();
224 assert!(receiver.reassemble(3).is_err());
225 }
226
227 #[test]
228 fn split_payload_zero_segment_size() {
229 assert!(split_payload(&[1, 2, 3], 0).is_err());
230 }
231
232 #[test]
233 fn split_payload_over_256_segments_errors() {
234 let payload = vec![0u8; 257];
235 assert!(split_payload(&payload, 1).is_err());
236 }
237}