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: IntoOwned
110
111#[cfg(feature = "alloc")]
112pub trait IntoOwned {
113    type Owned;
114
115    fn into_owned(self) -> Self::Owned;
116}
117
118// endregion: IntoOwned
119
120// region: Primitive FrameRead impls
121
122#[cfg(feature = "alloc")]
123impl<'a> FrameRead<'a> for alloc::vec::Vec<u8> {
124    type Error = DiagError;
125
126    fn decode(buf: &mut &'a [u8]) -> Result<Self, Self::Error> {
127        let v = buf.to_vec();
128        *buf = &buf[buf.len()..];
129        Ok(v)
130    }
131}
132
133impl<'a> FrameRead<'a> for u8 {
134    type Error = DiagError;
135
136    fn decode(buf: &mut &'a [u8]) -> Result<Self, Self::Error> {
137        let val = buf.first().copied().ok_or(DiagError::LengthMismatch {
138            expected: 1,
139            actual: 0,
140        })?;
141        *buf = &buf[1..];
142        Ok(val)
143    }
144}
145
146impl<'a> FrameRead<'a> for u16 {
147    type Error = DiagError;
148
149    fn decode(buf: &mut &'a [u8]) -> Result<Self, Self::Error> {
150        if buf.len() < 2 {
151            return Err(DiagError::LengthMismatch {
152                expected: 2,
153                actual: buf.len(),
154            });
155        }
156        let val = u16::from_be_bytes([buf[0], buf[1]]);
157        *buf = &buf[2..];
158        Ok(val)
159    }
160}
161
162impl<'a> FrameRead<'a> for u32 {
163    type Error = DiagError;
164
165    fn decode(buf: &mut &'a [u8]) -> Result<Self, Self::Error> {
166        if buf.len() < 4 {
167            return Err(DiagError::LengthMismatch {
168                expected: 4,
169                actual: buf.len(),
170            });
171        }
172        let val = u32::from_be_bytes([buf[0], buf[1], buf[2], buf[3]]);
173        *buf = &buf[4..];
174        Ok(val)
175    }
176}
177
178impl<'a, const N: usize> FrameRead<'a> for [u8; N] {
179    type Error = DiagError;
180
181    fn decode(buf: &mut &'a [u8]) -> Result<Self, Self::Error> {
182        if buf.len() < N {
183            return Err(DiagError::LengthMismatch {
184                expected: N,
185                actual: buf.len(),
186            });
187        }
188        let mut arr = [0u8; N];
189        arr.copy_from_slice(&buf[..N]);
190        *buf = &buf[N..];
191        Ok(arr)
192    }
193}
194
195/// Consumes all remaining bytes - only valid as a trailing field.
196impl<'a> FrameRead<'a> for &'a [u8] {
197    type Error = DiagError;
198
199    fn decode(buf: &mut &'a [u8]) -> Result<Self, Self::Error> {
200        let slice = *buf;
201        *buf = &buf[buf.len()..];
202        Ok(slice)
203    }
204}
205
206/// `None` if buffer is empty, `Some(T::decode(...))` otherwise.
207impl<'a, T> FrameRead<'a> for Option<T>
208where
209    T: FrameRead<'a>,
210{
211    type Error = T::Error;
212
213    fn decode(buf: &mut &'a [u8]) -> Result<Self, Self::Error> {
214        if buf.is_empty() {
215            Ok(None)
216        } else {
217            Ok(Some(T::decode(buf)?))
218        }
219    }
220}
221
222// endregion: Primitive FrameRead impls
223
224// region: Primitive FrameWrite impls
225
226#[cfg(feature = "alloc")]
227impl FrameWrite for alloc::vec::Vec<u8> {
228    type Error = DiagError;
229
230    fn encode<W: Writer>(&self, buf: &mut W) -> Result<(), Self::Error> {
231        buf.write_bytes(self.as_ref())
232    }
233}
234
235impl FrameWrite for u8 {
236    type Error = DiagError;
237
238    fn encode<W: Writer>(&self, buf: &mut W) -> Result<(), Self::Error> {
239        buf.write_bytes(&[*self])
240    }
241}
242
243impl FrameWrite for u16 {
244    type Error = DiagError;
245
246    fn encode<W: Writer>(&self, buf: &mut W) -> Result<(), Self::Error> {
247        buf.write_bytes(&self.to_be_bytes())
248    }
249}
250
251impl FrameWrite for u32 {
252    type Error = DiagError;
253
254    fn encode<W: Writer>(&self, buf: &mut W) -> Result<(), Self::Error> {
255        buf.write_bytes(&self.to_be_bytes())
256    }
257}
258
259impl<const N: usize> FrameWrite for [u8; N] {
260    type Error = DiagError;
261
262    fn encode<W: Writer>(&self, buf: &mut W) -> Result<(), Self::Error> {
263        buf.write_bytes(self.as_ref())
264    }
265}
266
267impl FrameWrite for &[u8] {
268    type Error = DiagError;
269
270    fn encode<W: Writer>(&self, buf: &mut W) -> Result<(), Self::Error> {
271        buf.write_bytes(self)
272    }
273}
274
275impl<T: FrameWrite> FrameWrite for Option<T> {
276    type Error = T::Error;
277
278    fn encode<W: Writer>(&self, buf: &mut W) -> Result<(), Self::Error> {
279        if let Some(inner) = self {
280            inner.encode(buf)?;
281        }
282        Ok(())
283    }
284}
285
286// endregion: Primitive FrameWrite impls
287
288// region: Free functions
289
290/// Consumes exactly `n` bytes from the cursor, returning a zero-copy slice.
291pub fn take_n<'a>(buf: &mut &'a [u8], n: usize) -> Result<&'a [u8], DiagError> {
292    if buf.len() < n {
293        return Err(DiagError::LengthMismatch {
294            expected: n,
295            actual: buf.len(),
296        });
297    }
298    let slice = &buf[..n];
299    *buf = &buf[n..];
300    Ok(slice)
301}
302
303#[cfg(feature = "alloc")]
304pub fn take_n_owned(buf: &mut &[u8], n: usize) -> Result<alloc::vec::Vec<u8>, DiagError> {
305    take_n(buf, n).map(|s| s.to_vec())
306}
307
308/// Takes a mutable slice input and decodes it.
309pub fn decode_from_slice<'a, T>(mut input: &'a [u8]) -> Result<T, T::Error>
310where
311    T: FrameRead<'a>,
312{
313    T::decode(&mut input)
314}
315
316// endregion: Free functions