1use core::fmt;
48
49#[cfg(feature = "alloc")]
50use alloc::{vec, vec::Vec};
51
52use super::field::{Address, Control, ControlError};
53
54#[derive(Clone, Debug, Eq, PartialEq)]
56pub struct LongFrame {
57 control: Control,
58 address: Address,
59 control_information: u8,
60 user_data: [u8; Self::MAX_USER_DATA_LEN],
61 user_data_len: u8,
62}
63
64impl LongFrame {
65 pub const START: u8 = 0x68;
67 pub const FIXED_DATA_LEN: usize = 3;
69 pub const MIN_USER_DATA_LEN: usize = 1;
71 pub const MAX_USER_DATA_LEN: usize = 252;
73 pub const STOP: u8 = 0x16;
75 pub const MIN_LEN: usize = 10;
77 pub const MAX_LEN: usize = 261;
79
80 pub fn new(
82 control: Control,
83 address: Address,
84 control_information: u8,
85 user_data: &[u8],
86 ) -> Result<Self, LongFrameError> {
87 control
88 .validate_variable_frame()
89 .map_err(LongFrameError::Control)?;
90 if !(Self::MIN_USER_DATA_LEN..=Self::MAX_USER_DATA_LEN).contains(&user_data.len()) {
91 return Err(LongFrameError::InvalidUserDataLength {
92 actual: user_data.len(),
93 });
94 }
95 let mut stored = [0; Self::MAX_USER_DATA_LEN];
96 stored[..user_data.len()].copy_from_slice(user_data);
97 Ok(Self {
98 control,
99 address,
100 control_information,
101 user_data: stored,
102 user_data_len: user_data.len() as u8,
103 })
104 }
105
106 #[must_use]
108 pub const fn control(&self) -> Control {
109 self.control
110 }
111
112 #[must_use]
114 pub const fn address(&self) -> Address {
115 self.address
116 }
117
118 #[must_use]
120 pub const fn control_information(&self) -> u8 {
121 self.control_information
122 }
123
124 #[must_use]
126 pub fn user_data(&self) -> &[u8] {
127 &self.user_data[..usize::from(self.user_data_len)]
128 }
129
130 pub fn decode(bytes: &[u8]) -> Result<Self, LongFrameError> {
132 if bytes.len() < 4 {
133 return Err(LongFrameError::IncompleteHeader {
134 actual: bytes.len(),
135 });
136 }
137 if bytes[0] != Self::START {
138 return Err(LongFrameError::InvalidStart {
139 index: 0,
140 actual: bytes[0],
141 });
142 }
143 if bytes[1] != bytes[2] {
144 return Err(LongFrameError::MismatchedDataLengths {
145 first: bytes[1],
146 second: bytes[2],
147 });
148 }
149 if usize::from(bytes[1]) < Self::FIXED_DATA_LEN + Self::MIN_USER_DATA_LEN {
150 return Err(LongFrameError::InvalidDataLength { actual: bytes[1] });
151 }
152 let expected_len = usize::from(bytes[1]) + 6;
153 if bytes.len() != expected_len {
154 return Err(LongFrameError::InvalidLength {
155 expected: expected_len,
156 actual: bytes.len(),
157 });
158 }
159 if bytes[3] != Self::START {
160 return Err(LongFrameError::InvalidStart {
161 index: 3,
162 actual: bytes[3],
163 });
164 }
165 if bytes[expected_len - 1] != Self::STOP {
166 return Err(LongFrameError::InvalidStop {
167 actual: bytes[expected_len - 1],
168 });
169 }
170 let expected_checksum = Self::checksum(&bytes[4..expected_len - 2]);
171 let actual_checksum = bytes[expected_len - 2];
172 if actual_checksum != expected_checksum {
173 return Err(LongFrameError::InvalidChecksum {
174 expected: expected_checksum,
175 actual: actual_checksum,
176 });
177 }
178 Self::new(
179 Control::new(bytes[4]),
180 Address::new(bytes[5]),
181 bytes[6],
182 &bytes[7..expected_len - 2],
183 )
184 }
185
186 pub fn encode_into<'a>(&self, output: &'a mut [u8]) -> Result<&'a [u8], LongFrameError> {
188 let required = self.required_len();
189 if output.len() < required {
190 return Err(LongFrameError::OutputTooSmall {
191 required,
192 actual: output.len(),
193 });
194 }
195 let data_len = (Self::FIXED_DATA_LEN + self.user_data().len()) as u8;
196 output[0] = Self::START;
197 output[1] = data_len;
198 output[2] = data_len;
199 output[3] = Self::START;
200 output[4] = self.control.value();
201 output[5] = self.address.value();
202 output[6] = self.control_information;
203 output[7..required - 2].copy_from_slice(self.user_data());
204 output[required - 2] = Self::checksum(&output[4..required - 2]);
205 output[required - 1] = Self::STOP;
206 Ok(&output[..required])
207 }
208
209 #[cfg(feature = "alloc")]
211 pub fn encode(&self) -> Vec<u8> {
212 let mut output = vec![0; self.required_len()];
213 let required = output.len();
214 let data_len = (Self::FIXED_DATA_LEN + self.user_data().len()) as u8;
215 output[..7].copy_from_slice(&[
216 Self::START,
217 data_len,
218 data_len,
219 Self::START,
220 self.control.value(),
221 self.address.value(),
222 self.control_information,
223 ]);
224 output[7..required - 2].copy_from_slice(self.user_data());
225 output[required - 2] = Self::checksum(&output[4..required - 2]);
226 output[required - 1] = Self::STOP;
227 output
228 }
229
230 fn required_len(&self) -> usize {
231 self.user_data().len() + 9
232 }
233
234 fn checksum(bytes: &[u8]) -> u8 {
235 bytes
236 .iter()
237 .fold(0, |checksum, byte| checksum.wrapping_add(*byte))
238 }
239}
240
241#[derive(Clone, Copy, Debug, Eq, PartialEq)]
243#[allow(missing_docs)]
244pub enum LongFrameError {
245 IncompleteHeader { actual: usize },
247 InvalidLength { expected: usize, actual: usize },
249 InvalidStart { index: usize, actual: u8 },
251 MismatchedDataLengths { first: u8, second: u8 },
253 InvalidDataLength { actual: u8 },
255 InvalidStop { actual: u8 },
257 InvalidChecksum { expected: u8, actual: u8 },
259 Control(ControlError),
261 InvalidUserDataLength { actual: usize },
263 OutputTooSmall { required: usize, actual: usize },
265}
266
267impl fmt::Display for LongFrameError {
268 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
269 match self {
270 Self::IncompleteHeader { actual } => write!(
271 formatter,
272 "incomplete long frame header: expected at least 4 bytes, got {actual}"
273 ),
274 Self::InvalidLength { expected, actual } => write!(
275 formatter,
276 "invalid long frame length: expected {expected}, got {actual}"
277 ),
278 Self::InvalidStart { index, actual } => write!(
279 formatter,
280 "invalid long frame start at {index}: expected 0x68, got 0x{actual:02x}"
281 ),
282 Self::MismatchedDataLengths { first, second } => write!(
283 formatter,
284 "mismatched long frame data lengths: {first} and {second}"
285 ),
286 Self::InvalidDataLength { actual } => write!(
287 formatter,
288 "invalid long frame data length: expected at least 4, got {actual}"
289 ),
290 Self::InvalidStop { actual } => write!(
291 formatter,
292 "invalid long frame stop: expected 0x16, got 0x{actual:02x}"
293 ),
294 Self::InvalidChecksum { expected, actual } => write!(
295 formatter,
296 "invalid long frame checksum: expected 0x{expected:02x}, got 0x{actual:02x}"
297 ),
298 Self::Control(error) => error.fmt(formatter),
299 Self::InvalidUserDataLength { actual } => write!(
300 formatter,
301 "invalid long frame user-data length: expected 1 through 252, got {actual}"
302 ),
303 Self::OutputTooSmall { required, actual } => write!(
304 formatter,
305 "long frame output buffer too small: expected {required}, got {actual}"
306 ),
307 }
308 }
309}
310
311impl core::error::Error for LongFrameError {
312 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
313 match self {
314 Self::Control(error) => Some(error),
315 _ => None,
316 }
317 }
318}
319
320#[cfg(test)]
321#[cfg_attr(coverage_nightly, coverage(off))]
322mod tests {
323 use super::*;
324 #[cfg(feature = "alloc")]
325 use alloc::string::ToString;
326
327 const FRAME: [u8; 10] = [0x68, 0x04, 0x04, 0x68, 0x53, 0xfe, 0x50, 0x10, 0xb1, 0x16];
328
329 #[test]
330 fn encodes_and_decodes_known_frame() {
331 let frame = LongFrame::new(Control::new(0x53), Address::new(254), 0x50, &[0x10]).unwrap();
332 let mut output = [0; LongFrame::MAX_LEN];
333 assert_eq!(frame.encode_into(&mut output).unwrap(), FRAME);
334 assert_eq!(LongFrame::decode(&FRAME), Ok(frame.clone()));
335 assert_eq!(frame.control(), Control::new(0x53));
336 assert_eq!(frame.address(), Address::new(254));
337 assert_eq!(frame.control_information(), 0x50);
338 assert_eq!(frame.user_data(), [0x10]);
339 }
340
341 #[cfg(feature = "alloc")]
342 #[test]
343 fn allocates_encoded_frame() {
344 assert_eq!(LongFrame::decode(&FRAME).unwrap().encode(), FRAME);
345 }
346
347 #[test]
348 fn owns_payload_and_validates_boundaries() {
349 let mut source = [1, 2];
350 let frame = LongFrame::new(Control::new(0x53), Address::new(252), 0, &source).unwrap();
351 source[0] = 9;
352 assert_eq!(source[0], 9);
353 assert_eq!(frame.user_data(), [1, 2]);
354 assert_eq!(
355 LongFrame::new(Control::new(0x53), Address::new(1), 0, &[]),
356 Err(LongFrameError::InvalidUserDataLength { actual: 0 })
357 );
358 let error = LongFrameError::Control(ControlError::InvalidForVariableFrame { value: 0x40 });
359 assert!(core::error::Error::source(&error).is_some());
360 assert!(
361 core::error::Error::source(&LongFrameError::IncompleteHeader { actual: 0 }).is_none()
362 );
363 assert_eq!(
364 LongFrame::new(Control::new(0x53), Address::new(1), 0, &[0; 253]),
365 Err(LongFrameError::InvalidUserDataLength { actual: 253 })
366 );
367 assert_eq!(
368 LongFrame::new(Control::new(0x40), Address::new(1), 0, &[1]),
369 Err(LongFrameError::Control(
370 ControlError::InvalidForVariableFrame { value: 0x40 }
371 ))
372 );
373 }
374
375 #[test]
376 fn supports_maximum_frame() {
377 let frame =
378 LongFrame::new(Control::new(0x53), Address::new(255), 255, &[0xff; 252]).unwrap();
379 let mut output = [0; LongFrame::MAX_LEN];
380 let encoded = frame.encode_into(&mut output).unwrap();
381 assert_eq!(encoded.len(), LongFrame::MAX_LEN);
382 assert_eq!(encoded[1], 0xff);
383 assert_eq!(LongFrame::decode(encoded), Ok(frame));
384 }
385
386 #[test]
387 fn rejects_header_and_length_errors() {
388 assert_eq!(
389 LongFrame::decode(&[0; 3]),
390 Err(LongFrameError::IncompleteHeader { actual: 3 })
391 );
392 let mut bytes = FRAME;
393 bytes[0] = 0;
394 assert_eq!(
395 LongFrame::decode(&bytes),
396 Err(LongFrameError::InvalidStart {
397 index: 0,
398 actual: 0
399 })
400 );
401 bytes = FRAME;
402 bytes[2] = 5;
403 assert_eq!(
404 LongFrame::decode(&bytes),
405 Err(LongFrameError::MismatchedDataLengths {
406 first: 4,
407 second: 5
408 })
409 );
410 bytes = FRAME;
411 bytes[1] = 3;
412 bytes[2] = 3;
413 assert_eq!(
414 LongFrame::decode(&bytes),
415 Err(LongFrameError::InvalidDataLength { actual: 3 })
416 );
417 assert_eq!(
418 LongFrame::decode(&FRAME[..9]),
419 Err(LongFrameError::InvalidLength {
420 expected: 10,
421 actual: 9
422 })
423 );
424 let mut trailing = [0; 11];
425 trailing[..10].copy_from_slice(&FRAME);
426 assert_eq!(
427 LongFrame::decode(&trailing),
428 Err(LongFrameError::InvalidLength {
429 expected: 10,
430 actual: 11
431 })
432 );
433 }
434
435 #[test]
436 fn rejects_body_errors() {
437 let mut bytes = FRAME;
438 bytes[3] = 0;
439 assert_eq!(
440 LongFrame::decode(&bytes),
441 Err(LongFrameError::InvalidStart {
442 index: 3,
443 actual: 0
444 })
445 );
446 bytes = FRAME;
447 bytes[9] = 0;
448 assert_eq!(
449 LongFrame::decode(&bytes),
450 Err(LongFrameError::InvalidStop { actual: 0 })
451 );
452 bytes = FRAME;
453 bytes[8] = 0;
454 assert_eq!(
455 LongFrame::decode(&bytes),
456 Err(LongFrameError::InvalidChecksum {
457 expected: 0xb1,
458 actual: 0
459 })
460 );
461 bytes = [0x68, 4, 4, 0x68, 0x40, 1, 0, 1, 0x42, 0x16];
462 assert_eq!(
463 LongFrame::decode(&bytes),
464 Err(LongFrameError::Control(
465 ControlError::InvalidForVariableFrame { value: 0x40 }
466 ))
467 );
468 }
469
470 #[test]
471 fn rejects_small_output() {
472 let frame = LongFrame::decode(&FRAME).unwrap();
473 assert_eq!(
474 frame.encode_into(&mut [0; 9]),
475 Err(LongFrameError::OutputTooSmall {
476 required: 10,
477 actual: 9
478 })
479 );
480 }
481
482 #[cfg(feature = "alloc")]
483 #[test]
484 fn formats_errors() {
485 let errors = [
486 (
487 LongFrameError::IncompleteHeader { actual: 3 }.to_string(),
488 "incomplete long frame header: expected at least 4 bytes, got 3",
489 ),
490 (
491 LongFrameError::InvalidLength {
492 expected: 10,
493 actual: 9,
494 }
495 .to_string(),
496 "invalid long frame length: expected 10, got 9",
497 ),
498 (
499 LongFrameError::InvalidStart {
500 index: 3,
501 actual: 0,
502 }
503 .to_string(),
504 "invalid long frame start at 3: expected 0x68, got 0x00",
505 ),
506 (
507 LongFrameError::MismatchedDataLengths {
508 first: 4,
509 second: 5,
510 }
511 .to_string(),
512 "mismatched long frame data lengths: 4 and 5",
513 ),
514 (
515 LongFrameError::InvalidDataLength { actual: 3 }.to_string(),
516 "invalid long frame data length: expected at least 4, got 3",
517 ),
518 (
519 LongFrameError::InvalidStop { actual: 0 }.to_string(),
520 "invalid long frame stop: expected 0x16, got 0x00",
521 ),
522 (
523 LongFrameError::InvalidChecksum {
524 expected: 1,
525 actual: 2,
526 }
527 .to_string(),
528 "invalid long frame checksum: expected 0x01, got 0x02",
529 ),
530 (
531 LongFrameError::Control(ControlError::InvalidForVariableFrame { value: 0x40 })
532 .to_string(),
533 "control value 0x40 is invalid for a variable-format frame",
534 ),
535 (
536 LongFrameError::InvalidUserDataLength { actual: 0 }.to_string(),
537 "invalid long frame user-data length: expected 1 through 252, got 0",
538 ),
539 (
540 LongFrameError::OutputTooSmall {
541 required: 10,
542 actual: 9,
543 }
544 .to_string(),
545 "long frame output buffer too small: expected 10, got 9",
546 ),
547 ];
548 for (actual, expected) in errors {
549 assert_eq!(actual, expected);
550 }
551 }
552}