minidsp_protocol/
packet.rs

1//! Functions for framing and unframing packets, and computing their checksums
2use 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
20/// Formats an hid command
21pub fn frame<T: AsRef<[u8]>>(packet: T) -> Bytes {
22    let mut buf = BytesMut::with_capacity(65);
23
24    // Packet len including length itself
25    buf.put_u8((packet.as_ref().len() + 1) as u8);
26
27    // Payload
28    buf.extend_from_slice(packet.as_ref());
29
30    // Checksum byte
31    buf.put_u8(checksum(&buf));
32
33    buf.freeze()
34}
35
36/// Computes a packet's checksums
37pub 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
41/// Extracts the packet's data by looking at its first byte for its length
42pub 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}