Skip to main content

ace_core/
codec.rs

1use crate::DiagError;
2
3// region: FrameRead
4
5/// Decodes `Self` from a zero-copy buffer cursor.
6///
7/// The cursor `buf` is a `&mut &'a [u8]` - a mutable reference to a shared
8/// slice. Each call to `decode` advances the cursor by consuming bytes from
9/// the front. The lifetime `'a` ties any borrowed data in `Self` back to
10/// the original buffer, ensuring zero allocation for slice fields.
11///
12/// # Implementing for custom types
13///
14/// Most types should use `#[derive(FrameRead)]` from `ace-macros`.
15/// Manual implementation is required when field lengths depend on context
16/// from a parent struct - use [`FrameReadWithContext`] in those cases.
17///
18/// # Blanket impls provided
19///
20/// - `u8`, `u16`, `u32` - big-endian
21/// - `[u8; N]` - fixed-size arrays
22/// - `&'a [u8]` - consumes all remaining bytes
23/// - `Option<T: FrameRead>` - `None` if buffer empty, `Some(T::decode(...))` otherwise
24/// - `FrameIter<'a, T: FrameRead>` - lazy iterator consuming all remaining bytes
25pub trait FrameRead<'a>: Sized {
26    type Error: core::fmt::Debug;
27    fn decode(buf: &mut &'a [u8]) -> Result<Self, Self::Error>;
28}
29
30// endregion: FrameRead
31
32// region: Writer
33
34mod sealed {
35    pub trait Sealed {}
36}
37
38/// Abstraction over alloc and no-alloc write targets.
39///
40/// Implemented for:
41/// - `&mut [u8]` - no-alloc, fixed-size buffer, advances cursor on write
42/// - `bytes::BytesMut` - alloc, growable buffer (feature = "alloc")
43///
44/// Sealed to prevent downstream implementations.
45pub trait Writer: sealed::Sealed {
46    fn write_bytes(&mut self, data: &[u8]) -> Result<(), DiagError>;
47}
48
49impl sealed::Sealed for &mut [u8] {}
50
51impl Writer for &mut [u8] {
52    fn write_bytes(&mut self, data: &[u8]) -> Result<(), DiagError> {
53        if self.len() < data.len() {
54            return Err(DiagError::BufferOverflow);
55        }
56        self[..data.len()].copy_from_slice(data);
57        let tmp = core::mem::take(self);
58        *self = &mut tmp[data.len()..];
59        Ok(())
60    }
61}
62
63#[cfg(feature = "alloc")]
64impl sealed::Sealed for bytes::BytesMut {}
65
66#[cfg(feature = "alloc")]
67impl Writer for bytes::BytesMut {
68    fn write_bytes(&mut self, data: &[u8]) -> Result<(), DiagError> {
69        self.extend_from_slice(data);
70        Ok(())
71    }
72}
73
74// endregion: Writer
75
76// region: FrameWrite
77
78/// Encodes `Self` into a [`Writer`].
79///
80/// A single generic method covers both alloc (`BytesMut`) and no-alloc
81/// (`&mut [u8]`) targets - no cfg splits required in implementations.
82///
83/// Most types should use `#[derive(FrameWrite)]` from `ace-macros`.
84pub trait FrameWrite {
85    type Error: core::fmt::Debug;
86    fn encode<W: Writer>(&self, buf: &mut W) -> Result<(), Self::Error>;
87}
88
89// endregion: FrameWrite
90
91// region: FrameCodec
92
93/// Composite trait for types that implement both [`FrameRead`] and [`FrameWrite`]
94/// with the same error type.
95///
96/// A blanket impl covers all types satisfying both bounds - there is no need
97/// to implement this trait manually.
98pub trait FrameCodec<'a>: FrameRead<'a, Error = <Self as FrameWrite>::Error> + FrameWrite {}
99
100impl<'a, T> FrameCodec<'a> for T
101where
102    T: FrameRead<'a> + FrameWrite,
103    T: FrameRead<'a, Error = <T as FrameWrite>::Error>,
104{
105}
106
107// endregion: FrameCodec
108
109// region: Primitive FrameRead impls
110
111impl<'a> FrameRead<'a> for u8 {
112    type Error = DiagError;
113
114    fn decode(buf: &mut &'a [u8]) -> Result<Self, Self::Error> {
115        let val = buf.first().copied().ok_or(DiagError::LengthMismatch {
116            expected: 1,
117            actual: 0,
118        })?;
119        *buf = &buf[1..];
120        Ok(val)
121    }
122}
123
124impl<'a> FrameRead<'a> for u16 {
125    type Error = DiagError;
126
127    fn decode(buf: &mut &'a [u8]) -> Result<Self, Self::Error> {
128        if buf.len() < 2 {
129            return Err(DiagError::LengthMismatch {
130                expected: 2,
131                actual: buf.len(),
132            });
133        }
134        let val = u16::from_be_bytes([buf[0], buf[1]]);
135        *buf = &buf[2..];
136        Ok(val)
137    }
138}
139
140impl<'a> FrameRead<'a> for u32 {
141    type Error = DiagError;
142
143    fn decode(buf: &mut &'a [u8]) -> Result<Self, Self::Error> {
144        if buf.len() < 4 {
145            return Err(DiagError::LengthMismatch {
146                expected: 4,
147                actual: buf.len(),
148            });
149        }
150        let val = u32::from_be_bytes([buf[0], buf[1], buf[2], buf[3]]);
151        *buf = &buf[4..];
152        Ok(val)
153    }
154}
155
156impl<'a, const N: usize> FrameRead<'a> for [u8; N] {
157    type Error = DiagError;
158
159    fn decode(buf: &mut &'a [u8]) -> Result<Self, Self::Error> {
160        if buf.len() < N {
161            return Err(DiagError::LengthMismatch {
162                expected: N,
163                actual: buf.len(),
164            });
165        }
166        let mut arr = [0u8; N];
167        arr.copy_from_slice(&buf[..N]);
168        *buf = &buf[N..];
169        Ok(arr)
170    }
171}
172
173/// Consumes all remaining bytes - only valid as a trailing field.
174impl<'a> FrameRead<'a> for &'a [u8] {
175    type Error = DiagError;
176
177    fn decode(buf: &mut &'a [u8]) -> Result<Self, Self::Error> {
178        let slice = *buf;
179        *buf = &buf[buf.len()..];
180        Ok(slice)
181    }
182}
183
184/// `None` if buffer is empty, `Some(T::decode(...))` otherwise.
185impl<'a, T> FrameRead<'a> for Option<T>
186where
187    T: FrameRead<'a>,
188{
189    type Error = T::Error;
190
191    fn decode(buf: &mut &'a [u8]) -> Result<Self, Self::Error> {
192        if buf.is_empty() {
193            Ok(None)
194        } else {
195            Ok(Some(T::decode(buf)?))
196        }
197    }
198}
199
200// endregion: Primitive FrameRead impls
201
202// region: Primitive FrameWrite impls
203
204impl FrameWrite for u8 {
205    type Error = DiagError;
206
207    fn encode<W: Writer>(&self, buf: &mut W) -> Result<(), Self::Error> {
208        buf.write_bytes(&[*self])
209    }
210}
211
212impl FrameWrite for u16 {
213    type Error = DiagError;
214
215    fn encode<W: Writer>(&self, buf: &mut W) -> Result<(), Self::Error> {
216        buf.write_bytes(&self.to_be_bytes())
217    }
218}
219
220impl FrameWrite for u32 {
221    type Error = DiagError;
222
223    fn encode<W: Writer>(&self, buf: &mut W) -> Result<(), Self::Error> {
224        buf.write_bytes(&self.to_be_bytes())
225    }
226}
227
228impl<const N: usize> FrameWrite for [u8; N] {
229    type Error = DiagError;
230
231    fn encode<W: Writer>(&self, buf: &mut W) -> Result<(), Self::Error> {
232        buf.write_bytes(self.as_ref())
233    }
234}
235
236impl FrameWrite for &[u8] {
237    type Error = DiagError;
238
239    fn encode<W: Writer>(&self, buf: &mut W) -> Result<(), Self::Error> {
240        buf.write_bytes(self)
241    }
242}
243
244impl<T: FrameWrite> FrameWrite for Option<T> {
245    type Error = T::Error;
246
247    fn encode<W: Writer>(&self, buf: &mut W) -> Result<(), Self::Error> {
248        if let Some(inner) = self {
249            inner.encode(buf)?;
250        }
251        Ok(())
252    }
253}
254
255// endregion: Primitive FrameWrite impls
256
257// region: Free functions
258
259/// Consumes exactly `n` bytes from the cursor, returning a zero-copy slice.
260pub fn take_n<'a>(buf: &mut &'a [u8], n: usize) -> Result<&'a [u8], DiagError> {
261    if buf.len() < n {
262        return Err(DiagError::LengthMismatch {
263            expected: n,
264            actual: buf.len(),
265        });
266    }
267    let slice = &buf[..n];
268    *buf = &buf[n..];
269    Ok(slice)
270}
271
272// endregion: Free functions