ucpack/
lib.rs

1#![doc = include_str!("../README.md")]
2#![cfg_attr(not(feature = "std"), no_std)]
3
4pub mod buffer;
5pub mod de;
6mod macros;
7pub mod ser;
8
9use core::fmt::Display;
10
11use buffer::{SliceCursor, WriteBuffer};
12use serde::Deserialize;
13
14#[derive(Debug)]
15/// Error returned by the ucpack crate
16pub enum UcPackError {
17    /// Tried to serialize a variant index bigger than `255`.
18    BadVariant,
19    /// The cursor does not have any more data to deserialize from.
20    Eof,
21    /// Serialization / Deserialization of this type is not supported by the ucpack protocol.
22    /// If you think this is a mistake, please open an issue.
23    NoSupport(&'static str),
24    /// Tried to serialize more than 256 bytes of payload data. This is a restriction
25    /// imposed by the protocol.
26    TooLong,
27    /// Tried to serialize more bytes than the buffer could possible handle.
28    BufferFull,
29    /// There was a serde error during serialization.
30    #[cfg(not(feature = "std"))]
31    SerError,
32    #[cfg(feature = "std")]
33    SerError(String),
34    /// There was a serde error during deserialization.
35    #[cfg(not(feature = "std"))]
36    DeError,
37    #[cfg(feature = "std")]
38    DeError(String),
39    /// Input data for deserialization has problems finding a representation in a given data format
40    ///
41    /// For example: a serialized boolean value ∉ {0, 1}
42    InvalidData,
43    /// Received a message with a wrong/faulty crc. Probably indicates data corruption.
44    WrongCrc,
45    /// Received a message containing wrong index/indices for the start and stop bytes.
46    WrongIndex,
47}
48
49impl Display for UcPackError {
50    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
51        let msg = match self {
52            Self::NoSupport(typename) => {
53                return write!(f, "there's no support for type {typename}")
54            }
55            Self::Eof => "not enough data to deserialize",
56            Self::InvalidData => "invalid data for data type",
57            Self::BadVariant => "tried to serialize a variant index bigger than 255",
58            Self::TooLong => "tried to serialize more than 256 bytes",
59            Self::BufferFull => "tried to write but buffer reached capacity",
60
61            Self::WrongCrc => "crc verification failed",
62            Self::WrongIndex => "invalid start and/or stop indices",
63
64            #[cfg(not(feature = "std"))]
65            Self::SerError => "serde encountered an error serializing",
66            #[cfg(feature = "std")]
67            Self::SerError(err) => {
68                return write!(f, "serde encountered an error while serializing: {err}");
69            }
70
71            #[cfg(not(feature = "std"))]
72            Self::DeError => "serde encountered an error deserializing",
73            #[cfg(feature = "std")]
74            Self::DeError(err) => {
75                return write!(f, "serde encountered an error while deserializing: {err}");
76            }
77        };
78
79        f.write_str(msg)
80    }
81}
82
83#[cfg(feature = "std")]
84impl std::error::Error for UcPackError {}
85
86impl serde::ser::Error for UcPackError {
87    fn custom<T>(_msg: T) -> Self
88    where
89        T: Display,
90    {
91        #[cfg(not(feature = "std"))]
92        {
93            Self::SerError
94        }
95
96        #[cfg(feature = "std")]
97        {
98            Self::SerError(_msg.to_string())
99        }
100    }
101}
102
103impl serde::de::Error for UcPackError {
104    fn custom<T>(_msg: T) -> Self
105    where
106        T: Display,
107    {
108        #[cfg(not(feature = "std"))]
109        {
110            Self::DeError
111        }
112
113        #[cfg(feature = "std")]
114        {
115            Self::DeError(_msg.to_string())
116        }
117    }
118}
119// impl core for UcPackError {}
120
121/// UcPack structure
122pub struct UcPack {
123    start_index: u8,
124    end_index: u8,
125}
126
127impl Default for UcPack {
128    fn default() -> Self {
129        Self::new(b'A', b'#')
130    }
131}
132
133impl UcPack {
134    pub const fn new(start_index: u8, end_index: u8) -> Self {
135        Self {
136            start_index,
137            end_index,
138        }
139    }
140
141    #[cfg(feature = "std")]
142    pub fn serialize_vec(
143        &self,
144        payload: &impl serde::ser::Serialize,
145    ) -> Result<Vec<u8>, UcPackError> {
146        let mut buffer = vec![self.start_index, 0];
147
148        let mut serializer = ser::Serializer::new(&mut buffer);
149        payload.serialize(&mut serializer)?;
150
151        let data_end = buffer.len();
152        buffer[1] = u8::try_from(data_end - 2).map_err(|_| UcPackError::TooLong)?;
153
154        buffer.push(self.end_index);
155        buffer.push(crc8_slice(&buffer[2..data_end]));
156
157        Ok(buffer)
158    }
159
160    pub fn serialize_slice(
161        &self,
162        payload: &impl serde::ser::Serialize,
163        buffer: &mut [u8],
164    ) -> Result<usize, UcPackError> {
165        let mut cursor = SliceCursor::from_slice(&mut *buffer);
166        cursor.push_slice(&[self.start_index, 0])?; // start_index + placeholder for length
167
168        let mut serializer = ser::Serializer::new(&mut cursor);
169        payload.serialize(&mut serializer)?;
170
171        let data_end = cursor.index();
172        let crc = crc8_slice(&cursor.inner()[2..data_end]);
173        cursor.push_slice(&[self.end_index, crc])?;
174
175        let total_size = cursor.index();
176
177        buffer[1] = u8::try_from(data_end - 2).map_err(|_| UcPackError::TooLong)?;
178        Ok(total_size)
179    }
180
181    pub fn deserialize_slice<'d, 'b, T>(&self, buffer: &'b [u8]) -> Result<T, UcPackError>
182    where
183        T: Deserialize<'d>,
184        'b: 'd,
185    {
186        let packet = is_complete_message(buffer).ok_or(UcPackError::Eof)?;
187        let [index, _, payload @ .., end_index, crc] = packet else {
188            return Err(UcPackError::Eof);
189        };
190
191        if cfg!(feature = "strict") && (*index != self.start_index || *end_index != self.end_index)
192        {
193            return Err(UcPackError::WrongIndex);
194        }
195
196        let expected_crc = crc8_slice(payload);
197        if expected_crc != *crc {
198            return Err(UcPackError::WrongCrc);
199        }
200
201        let mut cursor = SliceCursor::from_slice(payload);
202        let mut de = de::Deserializer::new(&mut cursor);
203        T::deserialize(&mut de)
204    }
205}
206
207/// Check a buffer for a message. This method is useful during hardware interrupts,
208/// to check whether the received data is a readble message or more data has yet to arrive
209///
210/// Arguments:
211/// - `buffer`: this argument is NOT for the whole buffer to be passed in but
212/// rather the slice of the buffer containing the currently received information
213///
214/// Returns:
215/// - `Some`: a slice guaranteed to contain a message
216/// - `None`: a full message hasn't yet been received
217pub fn is_complete_message(buffer: &[u8]) -> Option<&[u8]> {
218    let length: usize = buffer.get(1).map(|&length| length.into())?;
219    buffer.get(..(length + 4))
220}
221
222/// Helper function to calculate crc8 over byte slices
223#[inline]
224pub fn crc8_slice(input: &[u8]) -> u8 {
225    crc8(input.into_iter().copied())
226}
227
228/// Calculates a CRC8 checksum over any `u8` iterator
229pub fn crc8(input: impl IntoIterator<Item = u8>) -> u8 {
230    let input = input.into_iter();
231
232    input
233        .into_iter()
234        .flat_map(|byte| (0u8..8u8).map(move |j| (byte, j)))
235        .fold(0, |mut crc, (byte, j)| {
236            let sum = (crc ^ (byte >> j)) & 0x01;
237            crc >>= 1;
238            crc ^ (sum != 0).then_some(0x8C).unwrap_or(0) // more explicit than unwrap_or_default
239        })
240}