Skip to main content

automotive_wire_codec/
decode.rs

1//! RX-side traits: [`Decode`] (zero-copy decode borrowing from the buffer),
2//! [`DecodeIter`] (repeated same-typed elements), and the [`DecodeIterator`] adapter.
3
4use core::marker::PhantomData;
5
6use crate::error::{Incomplete, TrailingBytes};
7
8/// RX-side: zero-copy decode borrowing from `buf`. The value is valid only as long as
9/// `buf` lives.
10pub trait Decode<'a>: Sized {
11    /// Per-implementation error; constructible from [`Incomplete`] and [`TrailingBytes`]
12    /// so the fixed-width leaf read helpers (`read_u8`, `read_u16_be`,
13    /// `read_array`, …) and the `decode_exact` default lift through `?`.
14    ///
15    /// The variable-width helpers [`read_be_uint`](crate::read_be_uint) and
16    /// [`read_be_uint_into`](crate::read_be_uint_into) return
17    /// [`ReadUintError`](crate::ReadUintError) instead; to call them inside
18    /// `decode` with `?`, additionally implement
19    /// `From<ReadUintError>` for your error (match both arms — see the
20    /// error pattern in `MIGRATION.md`).
21    type Error: From<Incomplete> + From<TrailingBytes>;
22
23    /// Decode from the FRONT of `buf`; return `(value, unconsumed_remainder)`.
24    ///
25    /// # Errors
26    /// `Self::Error` if the input is malformed or too short.
27    fn decode(buf: &'a [u8]) -> Result<(Self, &'a [u8]), Self::Error>;
28
29    /// Decode requiring the ENTIRE buffer to be consumed. Use this at a message
30    /// boundary where the length is already known.
31    ///
32    /// Do NOT use it where a buffer may legally hold more than one message
33    /// (e.g. a multi-message datagram): there, trailing bytes are the *next*
34    /// message, not an error — use [`decode`](Decode::decode) and thread the
35    /// remainder.
36    ///
37    /// # Errors
38    /// [`TrailingBytes`] (via `Self::Error`) if bytes remain, plus anything
39    /// [`decode`](Decode::decode) can return.
40    fn decode_exact(buf: &'a [u8]) -> Result<Self, Self::Error> {
41        let (value, rest) = Self::decode(buf)?;
42        if rest.is_empty() {
43            Ok(value)
44        } else {
45            Err(TrailingBytes(rest.len()).into())
46        }
47    }
48}
49
50/// RX-side: decode a sequence of same-typed elements from a buffer. Implement this only
51/// for protocols that have repeated elements (e.g. a UDS DTC record list).
52pub trait DecodeIter<'a>: Sized {
53    /// Per-implementation error; constructible from [`Incomplete`] so the
54    /// fixed-width leaf read helpers lift through `?`. As with
55    /// [`Decode::Error`], the variable-width helpers return
56    /// [`ReadUintError`](crate::ReadUintError) and need their own `From` impl.
57    type Error: From<Incomplete>;
58
59    /// Wire size of one element, when fixed at compile time (must be non-zero
60    /// if `Some`). Enables [`DecodeIterator::remaining_len`] for fixed-stride
61    /// record streams. Default: `None` (variable-width).
62    const WIRE_SIZE: Option<usize> = None;
63
64    /// Decode the next element from the front of `buf`.
65    ///
66    /// Returns `Ok(Some((value, rest)))` for an element, `Ok(None)` for a clean end
67    /// (buffer empty / no more elements), or `Err(_)` for malformed input.
68    ///
69    /// **Convention — check for the clean end BEFORE attempting to decode an
70    /// element.** Exhaustion must be `Ok(None)`, never
71    /// `Err(Incomplete { available: 0, .. })`: a `decode_next` that delegates
72    /// straight to a [`Decode`] impl will wrongly turn an empty buffer into an
73    /// error. The reference shape is:
74    ///
75    /// ```text
76    /// if buf.is_empty() { return Ok(None); }
77    /// Decode::decode(buf).map(Some)
78    /// ```
79    ///
80    /// A *partial* element after a good start IS a real error — surfacing it
81    /// (rather than silently stopping) is deliberate; the adapter fuses after
82    /// the first `Err`. Consumers migrating from silent-truncation iterators
83    /// should treat the newly surfaced error as the correct behavior and keep
84    /// any pre-validated fast path on a separate infallible iterator.
85    ///
86    /// # Errors
87    /// `Self::Error` if the next element is malformed.
88    fn decode_next(buf: &'a [u8]) -> Result<Option<(Self, &'a [u8])>, Self::Error>;
89
90    /// Adapter: iterate all elements, yielding `Result<Self, Self::Error>`. Stops at the
91    /// first `Ok(None)` or `Err(_)`.
92    #[must_use]
93    fn iter(buf: &'a [u8]) -> DecodeIterator<'a, Self> {
94        DecodeIterator::new(buf)
95    }
96}
97
98/// Iterator produced by [`DecodeIter::iter`]. Threads the remaining buffer between calls
99/// to [`DecodeIter::decode_next`]; terminates on `Ok(None)` or the first error.
100pub struct DecodeIterator<'a, T> {
101    buf: &'a [u8],
102    done: bool,
103    _marker: PhantomData<fn() -> T>,
104}
105
106impl<'a, T> DecodeIterator<'a, T> {
107    fn new(buf: &'a [u8]) -> Self {
108        Self {
109            buf,
110            done: false,
111            _marker: PhantomData,
112        }
113    }
114}
115
116impl<'a, T: DecodeIter<'a>> Iterator for DecodeIterator<'a, T> {
117    type Item = Result<T, T::Error>;
118
119    fn next(&mut self) -> Option<Self::Item> {
120        if self.done {
121            return None;
122        }
123        match T::decode_next(self.buf) {
124            Ok(Some((value, rest))) => {
125                self.buf = rest;
126                Some(Ok(value))
127            }
128            Ok(None) => {
129                self.done = true;
130                None
131            }
132            Err(e) => {
133                self.done = true;
134                Some(Err(e))
135            }
136        }
137    }
138}
139
140impl<'a, T: DecodeIter<'a>> DecodeIterator<'a, T> {
141    /// Remaining item count, when [`T::WIRE_SIZE`](DecodeIter::WIRE_SIZE)
142    /// is fixed. `None` for variable-width elements (or a zero `WIRE_SIZE`);
143    /// `Some(0)` once the iterator has terminated.
144    ///
145    /// This counts *items* [`next`](Iterator::next) will yield, not complete
146    /// elements: a partial trailing element (buffer length not a multiple of
147    /// the stride) counts as one remaining item, because `next` will surface
148    /// it as `Err(Incomplete)`. `Some(0)` therefore always means `next`
149    /// returns `None` — it never hides a truncated tail.
150    #[must_use]
151    pub fn remaining_len(&self) -> Option<usize> {
152        let w = T::WIRE_SIZE?;
153        if w == 0 {
154            return None;
155        }
156        if self.done {
157            return Some(0);
158        }
159        Some(self.buf.len().div_ceil(w))
160    }
161}
162
163#[cfg(test)]
164mod tests {
165    use super::*;
166    use crate::error::{Incomplete, TrailingBytes};
167    use crate::read::read_u8;
168
169    #[derive(Debug, PartialEq)]
170    enum TestErr {
171        Incomplete(Incomplete),
172        Trailing(TrailingBytes),
173    }
174    impl From<Incomplete> for TestErr {
175        fn from(e: Incomplete) -> Self {
176            TestErr::Incomplete(e)
177        }
178    }
179    impl From<TrailingBytes> for TestErr {
180        fn from(e: TrailingBytes) -> Self {
181            TestErr::Trailing(e)
182        }
183    }
184
185    // A one-byte value for the Decode contract.
186    #[derive(Debug, PartialEq)]
187    struct One(u8);
188    impl<'a> Decode<'a> for One {
189        type Error = TestErr;
190        fn decode(buf: &'a [u8]) -> Result<(Self, &'a [u8]), TestErr> {
191            let (b, rest) = read_u8(buf)?;
192            Ok((One(b), rest))
193        }
194    }
195
196    #[test]
197    fn decode_exact_consumes_whole_buffer() {
198        assert_eq!(One::decode_exact(&[7]).unwrap(), One(7));
199    }
200
201    #[test]
202    fn decode_exact_reports_trailing_bytes() {
203        let err = One::decode_exact(&[1, 2]).unwrap_err();
204        assert_eq!(err, TestErr::Trailing(TrailingBytes(1)));
205    }
206
207    // A one-byte element for the DecodeIter contract; byte 0xFF simulates malformed.
208    #[derive(Debug, PartialEq)]
209    struct Elem(u8);
210    impl<'a> DecodeIter<'a> for Elem {
211        type Error = TestErr;
212        fn decode_next(buf: &'a [u8]) -> Result<Option<(Self, &'a [u8])>, TestErr> {
213            match buf.first() {
214                None => Ok(None),
215                Some(&0xFF) => Err(Incomplete {
216                    needed: 2,
217                    available: 1,
218                }
219                .into()),
220                Some(&b) => Ok(Some((Elem(b), &buf[1..]))),
221            }
222        }
223    }
224
225    #[test]
226    fn iter_yields_all_then_none() {
227        let mut it = Elem::iter(&[1, 2, 3]);
228        assert!(matches!(it.next(), Some(Ok(Elem(1)))));
229        assert!(matches!(it.next(), Some(Ok(Elem(2)))));
230        assert!(matches!(it.next(), Some(Ok(Elem(3)))));
231        assert!(it.next().is_none());
232    }
233
234    #[test]
235    fn iter_stops_after_first_error() {
236        let mut it = Elem::iter(&[1, 0xFF, 3]);
237        assert!(matches!(it.next(), Some(Ok(Elem(1)))));
238        assert!(matches!(it.next(), Some(Err(TestErr::Incomplete(_)))));
239        assert!(it.next().is_none());
240    }
241
242    // A 3-byte fixed-stride element advertising WIRE_SIZE (SE-2 shape: DTC record).
243    #[derive(Debug, PartialEq)]
244    struct Fixed3([u8; 3]);
245    impl<'a> DecodeIter<'a> for Fixed3 {
246        type Error = TestErr;
247        const WIRE_SIZE: Option<usize> = Some(3);
248        fn decode_next(buf: &'a [u8]) -> Result<Option<(Self, &'a [u8])>, TestErr> {
249            if buf.is_empty() {
250                return Ok(None);
251            }
252            let (b, rest) = crate::read::read_array::<3>(buf)?;
253            Ok(Some((Fixed3(b), rest)))
254        }
255    }
256
257    #[test]
258    fn remaining_len_reports_count_for_fixed_stride() {
259        let buf = [0u8; 12];
260        let mut it = Fixed3::iter(&buf);
261        assert_eq!(it.remaining_len(), Some(4));
262        it.next();
263        assert_eq!(it.remaining_len(), Some(3));
264    }
265
266    #[test]
267    fn remaining_len_is_none_for_variable_width() {
268        // Elem (above) keeps the default WIRE_SIZE = None.
269        let it = Elem::iter(&[1, 2, 3]);
270        assert_eq!(it.remaining_len(), None);
271    }
272
273    #[test]
274    fn remaining_len_counts_partial_tail_as_one_item() {
275        // A partial trailing element still produces one more item from the
276        // iterator (an Err(Incomplete)), so it must count as 1, not round
277        // down to 0 — Some(0) is reserved for "next() returns None".
278        let truncated = [0u8; 2]; // less than one 3-byte record
279        let it = Fixed3::iter(&truncated);
280        assert_eq!(it.remaining_len(), Some(1));
281
282        // One full record plus a 2-byte partial tail: 2 items remain
283        // (1 Ok + 1 Err), not 1.
284        let partial = [0u8; 5];
285        let mut it = Fixed3::iter(&partial);
286        assert_eq!(it.remaining_len(), Some(2));
287        assert!(matches!(it.next(), Some(Ok(Fixed3(_)))));
288        assert_eq!(it.remaining_len(), Some(1));
289        assert!(matches!(it.next(), Some(Err(TestErr::Incomplete(_)))));
290        assert_eq!(it.remaining_len(), Some(0));
291    }
292
293    #[test]
294    fn remaining_len_is_zero_after_exhaustion_or_error() {
295        let buf = [0u8; 3];
296        let mut it = Fixed3::iter(&buf);
297        it.next(); // consumes the only element
298        it.next(); // Ok(None) -> done
299        assert_eq!(it.remaining_len(), Some(0));
300
301        // Error path: a 5-byte buffer holds one full 3-byte record plus a
302        // 2-byte partial one. The first `next()` consumes the full record;
303        // the second hits the partial tail and `read_array::<3>` reports
304        // `Err(Incomplete)`. `remaining_len()` must still report `Some(0)`
305        // once the iterator has fused on the error.
306        let partial = [0u8; 5];
307        let mut it = Fixed3::iter(&partial);
308        assert!(matches!(it.next(), Some(Ok(Fixed3(_)))));
309        assert!(matches!(it.next(), Some(Err(TestErr::Incomplete(_)))));
310        assert_eq!(it.remaining_len(), Some(0));
311    }
312
313    // A zero-WIRE_SIZE element: the contract requires WIRE_SIZE to be
314    // non-zero if `Some`, but `remaining_len` defends against a violation
315    // anyway (a zero stride would otherwise divide-by-zero / loop forever).
316    #[derive(Debug, PartialEq)]
317    struct ZeroWidth;
318    impl<'a> DecodeIter<'a> for ZeroWidth {
319        type Error = TestErr;
320        const WIRE_SIZE: Option<usize> = Some(0);
321        fn decode_next(_buf: &'a [u8]) -> Result<Option<(Self, &'a [u8])>, TestErr> {
322            Ok(None)
323        }
324    }
325
326    #[test]
327    fn remaining_len_is_none_for_zero_wire_size_guard() {
328        let it = ZeroWidth::iter(&[]);
329        assert_eq!(it.remaining_len(), None);
330    }
331}