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}