minidsp_protocol/
packet.rs1use bytes::{BufMut, Bytes, BytesMut};
3#[cfg(feature = "debug")]
4use thiserror::Error;
5
6#[derive(Clone)]
7#[cfg_attr(feature = "debug", derive(Debug, Error))]
8pub enum ParseError {
9 #[cfg_attr(feature = "debug", error("packet was empty"))]
10 EmptyPacket,
11
12 #[cfg_attr(feature = "debug", error("expected length: {expected}, got: {actual}"))]
13 UnexpectedLength {
14 expected: usize,
15 actual: usize,
16 data: Bytes,
17 },
18}
19
20pub fn frame<T: AsRef<[u8]>>(packet: T) -> Bytes {
22 let mut buf = BytesMut::with_capacity(65);
23
24 buf.put_u8((packet.as_ref().len() + 1) as u8);
26
27 buf.extend_from_slice(packet.as_ref());
29
30 buf.put_u8(checksum(&buf));
32
33 buf.freeze()
34}
35
36pub fn checksum<T: AsRef<[u8]>>(data: T) -> u8 {
38 (data.as_ref().iter().map(|&x| x as u32).sum::<u32>() & 0xFF) as u8
39}
40
41pub fn unframe(response: Bytes) -> Result<Bytes, ParseError> {
43 if response.is_empty() {
44 return Err(ParseError::EmptyPacket);
45 }
46 let len = response[0] as usize;
47 if response.len() < len {
48 Err(ParseError::UnexpectedLength {
49 expected: len,
50 actual: response.len(),
51 data: response,
52 })
53 } else if len == 0 {
54 Err(ParseError::EmptyPacket)
55 } else {
56 Ok(response.slice(1..len))
57 }
58}
59
60#[cfg(test)]
61mod test {
62 use alloc::vec;
63
64 use super::*;
65
66 #[test]
67 fn frame_test() {
68 let packet = Bytes::from_static(&[0x05, 0xFF, 0xDA, 0x02]);
69 let framed = frame(packet.clone());
70
71 assert_eq!(
72 framed.len(),
73 packet.len() + 2,
74 "length should be len(data) + 1 (len) + 1 (checksum)"
75 );
76 assert_eq!(
77 framed[0], 5,
78 "the first byte should indicate the length of the packet including the checksum byte"
79 );
80 assert!(
81 framed[1..5].iter().eq(packet.iter()),
82 "the packet data should be there verbatim"
83 );
84 assert_eq!(framed[5], 229, "The checksum should be accurate");
85 }
86
87 #[test]
88 fn unframe_test() {
89 let response = Bytes::from_static(&[0x3, 0x1, 0x2, 0xFF, 0xFF, 0xFF]);
90 let frame = unframe(response).ok().unwrap();
91 assert_eq!(
92 frame,
93 vec![0x1, 0x2],
94 "should remove the length header and return the right data"
95 );
96 }
97}