Skip to main content

matter_codec/
reader.rs

1//! Streaming TLV decoder.
2//!
3//! [`TlvReader::next`] walks the input one element at a time. Scalars are
4//! returned as [`Element::Scalar`]; containers emit a [`Element::ContainerStart`]
5//! immediately followed by the children's elements and then a matching
6//! [`Element::ContainerEnd`]. Use [`TlvReader::read_value`] to materialise an
7//! entire element tree in one call.
8
9use crate::error::{Error, Result};
10use crate::tag::Tag;
11use crate::value::Value;
12use crate::{element_type as et, tag_control as tc};
13
14/// Which kind of TLV container a [`ContainerStart`](Element::ContainerStart)
15/// announces.
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17#[non_exhaustive]
18pub enum ContainerKind {
19    /// `Value::Structure` — children carry their own (typically context-tagged) tags.
20    Structure,
21    /// `Value::Array` — children carry anonymous tags only.
22    Array,
23    /// `Value::List` — children may carry any tag form.
24    List,
25}
26
27/// One step of the streaming reader.
28#[derive(Debug, Clone, PartialEq)]
29#[non_exhaustive]
30pub enum Element {
31    /// A complete scalar (or string/bytes) element.
32    Scalar {
33        /// The tag that identifies this element within its enclosing context.
34        tag: Tag,
35        /// The decoded scalar value.
36        value: Value,
37    },
38
39    /// A container has just been opened. Subsequent `next()` calls
40    /// return the container's children until a matching
41    /// [`ContainerEnd`](Element::ContainerEnd).
42    ContainerStart {
43        /// The tag that identifies this container within its enclosing context.
44        tag: Tag,
45        /// Which container kind was opened.
46        kind: ContainerKind,
47    },
48
49    /// The most recently-opened container has been closed.
50    ContainerEnd,
51}
52
53/// Maximum container nesting depth the reader accepts. The Matter spec
54/// recommends a 32-level limit to prevent stack blow-up on adversarial
55/// input.
56pub const MAX_DEPTH: usize = 32;
57
58/// Default ceiling on the total number of [`Value`] elements a single
59/// [`TlvReader::read_value`] call may materialise.
60///
61/// Tree-builder decoding allocates one `Value` (and, inside containers, one
62/// `(Tag, Value)` pair) per element. A tiny scalar such as a boolean costs a
63/// single wire byte but expands to a heap-resident `Value`, so an input packed
64/// with millions of one-byte scalars can amplify into a large allocation. This
65/// budget bounds that amplification: a decode that would produce more than this
66/// many elements fails with [`Error::ElementBudgetExceeded`].
67///
68/// The default is deliberately generous (1,048,576 elements) — far above any
69/// legitimate Matter payload, which is itself bounded by the protocol's
70/// message-size limits — so it only ever trips on adversarial input. Callers
71/// that need a tighter or looser bound can set their own via
72/// [`TlvReader::with_element_budget`].
73pub const DEFAULT_ELEMENT_BUDGET: usize = 1 << 20;
74
75/// A streaming TLV decoder over a borrowed byte slice.
76pub struct TlvReader<'a> {
77    bytes: &'a [u8],
78    pos: usize,
79    depth: usize,
80    /// Remaining element budget for a tree-builder decode. Decremented once
81    /// per materialised [`Value`] in [`Self::read_container_body`] /
82    /// [`Self::read_value`] — but only on *charged* decodes, i.e. when the
83    /// remaining input is larger than this budget; a smaller input provably
84    /// cannot exceed it (each element consumes ≥ 1 input byte), so the fast
85    /// path skips accounting without weakening the bound (see
86    /// [`Self::read_value`]). The streaming [`Self::next`] path does not
87    /// touch it because it allocates nothing per element.
88    element_budget: usize,
89}
90
91impl<'a> TlvReader<'a> {
92    /// Construct a reader that walks `bytes` from the start, using the
93    /// [`DEFAULT_ELEMENT_BUDGET`] for tree-builder decodes.
94    pub fn new(bytes: &'a [u8]) -> Self {
95        Self {
96            bytes,
97            pos: 0,
98            depth: 0,
99            element_budget: DEFAULT_ELEMENT_BUDGET,
100        }
101    }
102
103    /// Construct a reader with a custom total-element budget for tree-builder
104    /// decoding (see [`DEFAULT_ELEMENT_BUDGET`]).
105    ///
106    /// A [`Self::read_value`] call that would materialise more than `budget`
107    /// [`Value`] elements fails with [`Error::ElementBudgetExceeded`]. The
108    /// budget only affects the tree-builder path; the streaming [`Self::next`]
109    /// API is unaffected because it allocates nothing per element.
110    pub fn with_element_budget(bytes: &'a [u8], budget: usize) -> Self {
111        Self {
112            bytes,
113            pos: 0,
114            depth: 0,
115            element_budget: budget,
116        }
117    }
118
119    /// Whether there is no more input to consume.
120    pub fn is_empty(&self) -> bool {
121        self.pos >= self.bytes.len()
122    }
123
124    /// Advance one TLV element. Returns `Ok(None)` at end of input.
125    ///
126    /// # Errors
127    ///
128    /// Returns `Err` if the input is malformed:
129    ///
130    /// - [`Error::InvalidTagControl`] — unrecognised tag-control byte form.
131    /// - [`Error::InvalidElementType`] — unknown element-type code.
132    /// - [`Error::UnexpectedEof`] — truncated payload bytes.
133    /// - [`Error::UnexpectedEndOfContainer`] — end-of-container marker (`0x18`)
134    ///   at the top level, with no container open.
135    /// - [`Error::ContainerTooDeep`] — a container open would exceed
136    ///   [`MAX_DEPTH`] nesting levels.
137    ///
138    /// # Note on naming
139    ///
140    /// This method is deliberately named `next` to match the streaming-reader
141    /// idiom established by e.g. `serde`'s `Deserializer`. It returns
142    /// `Result<Option<T>>` rather than `Option<Result<T>>` so that callers
143    /// use `?` naturally. Implementing `std::iter::Iterator` is deferred to
144    /// a later phase when a fallible-iterator adapter is available.
145    #[allow(clippy::should_implement_trait)] // See note above; Iterator requires Option<Item>, not Result<Option<Item>>.
146    pub fn next(&mut self) -> Result<Option<Element>> {
147        if self.is_empty() {
148            return Ok(None);
149        }
150        let control = self.next_byte()?;
151        let elem_type = control & et::ELEMENT_TYPE_MASK;
152
153        // End-of-container is always emitted as anonymous tag form.
154        if elem_type == et::END_OF_CONTAINER {
155            if control & tc::TAG_CONTROL_MASK != tc::ANONYMOUS {
156                return Err(Error::InvalidTagControl(control & tc::TAG_CONTROL_MASK));
157            }
158            if self.depth == 0 {
159                return Err(Error::UnexpectedEndOfContainer);
160            }
161            self.depth -= 1;
162            return Ok(Some(Element::ContainerEnd));
163        }
164
165        let tag = self.read_tag(control)?;
166
167        // Container opens — record the kind and bump depth.
168        let kind = match elem_type {
169            et::STRUCTURE => Some(ContainerKind::Structure),
170            et::ARRAY => Some(ContainerKind::Array),
171            et::LIST => Some(ContainerKind::List),
172            _ => None,
173        };
174        if let Some(kind) = kind {
175            if self.depth >= MAX_DEPTH {
176                return Err(Error::ContainerTooDeep);
177            }
178            self.depth += 1;
179            return Ok(Some(Element::ContainerStart { tag, kind }));
180        }
181
182        let value = self.read_value_body(elem_type)?;
183        Ok(Some(Element::Scalar { tag, value }))
184    }
185
186    /// Skip the remaining body of the container whose
187    /// [`ContainerStart`](Element::ContainerStart) was just returned by
188    /// [`Self::next`], consuming through its matching
189    /// [`ContainerEnd`](Element::ContainerEnd).
190    ///
191    /// Call this immediately after `next()` yields a `ContainerStart` you
192    /// want to discard — for example an unknown field carried by a struct
193    /// from a newer Matter revision. On return the reader is positioned at
194    /// the first element *after* the skipped container. Scalars inside the
195    /// container are walked but not materialised, so cost is bounded by the
196    /// input size and nesting by [`MAX_DEPTH`] (both enforced by `next()`).
197    ///
198    /// # Errors
199    ///
200    /// - [`Error::UnclosedContainer`] — end of input before the container's
201    ///   closing marker.
202    /// - Any error returned by [`Self::next`] (malformed body, over-deep
203    ///   nesting, or element-budget exhaustion).
204    ///
205    /// # Examples
206    ///
207    /// ```
208    /// use matter_codec::{ContainerKind, Element, Tag, TlvReader, TlvWriter};
209    /// let mut buf = Vec::new();
210    /// let mut w = TlvWriter::new(&mut buf);
211    /// w.start_structure(Tag::Anonymous)?;
212    /// w.start_structure(Tag::Context(9))?; // an unknown nested field
213    /// w.end_container()?;
214    /// w.put_uint(Tag::Context(1), 42)?;
215    /// w.end_container()?;
216    ///
217    /// let mut r = TlvReader::new(&buf);
218    /// r.next()?; // open the outer struct
219    /// // next() returns the nested ctx9 ContainerStart we want to discard:
220    /// assert!(matches!(
221    ///     r.next()?,
222    ///     Some(Element::ContainerStart { kind: ContainerKind::Structure, .. })
223    /// ));
224    /// r.skip_container()?; // drain the nested struct
225    /// // the field after the unknown container is still readable:
226    /// assert!(matches!(r.next()?, Some(Element::Scalar { tag: Tag::Context(1), .. })));
227    /// # Ok::<(), matter_codec::Error>(())
228    /// ```
229    pub fn skip_container(&mut self) -> Result<()> {
230        let mut depth = 1usize;
231        while depth > 0 {
232            match self.next()? {
233                Some(Element::ContainerStart { .. }) => depth += 1,
234                Some(Element::ContainerEnd) => depth -= 1,
235                Some(Element::Scalar { .. }) => {}
236                None => return Err(Error::UnclosedContainer),
237            }
238        }
239        Ok(())
240    }
241
242    /// Materialise one full TLV element as a `(Tag, Value)`. Scalars are
243    /// returned directly; containers are read recursively up to
244    /// [`MAX_DEPTH`] levels (enforced by [`Self::next`]'s depth counter).
245    ///
246    /// # Errors
247    ///
248    /// - [`Error::UnexpectedEof`] — the input is empty.
249    /// - [`Error::UnexpectedEndOfContainer`] — the first element is a stray
250    ///   end-of-container marker.
251    /// - [`Error::UnclosedContainer`] — end of input was reached before the
252    ///   container's closing marker.
253    /// - [`Error::NonAnonymousArrayTag`] — an array child carried a
254    ///   non-anonymous tag (the spec requires array elements to be anonymous).
255    /// - [`Error::ElementBudgetExceeded`] — the decode would materialise more
256    ///   than the configured element budget (see [`DEFAULT_ELEMENT_BUDGET`]).
257    /// - Any error returned by [`Self::next`].
258    pub fn read_value(&mut self) -> Result<(Tag, Value)> {
259        // Budget fast path: every materialised element consumes at least one
260        // input byte (a null is one control byte; a container is a control
261        // byte plus its end marker), so when the remaining input is no larger
262        // than the remaining budget this decode CANNOT exceed it — decode
263        // with per-element accounting compiled out entirely. Real Matter
264        // payloads (≲ 1.5 KiB) against the 2^20 default budget always take
265        // this path; the charged path serves oversized or custom-budget
266        // inputs.
267        //
268        // This is observably equivalent to charging every element: a call is
269        // only admitted uncharged when the byte bound proves it cannot fail,
270        // and from such a call onward every future element from this reader
271        // is covered by the same bound (the remaining input only shrinks), so
272        // sequences of `read_value` calls fail on exactly the same element —
273        // with the same error — as the always-charged implementation. The
274        // lifetime invariant is unchanged: one reader never materialises more
275        // than its configured budget's worth of live `Value` elements.
276        let remaining_input = self.bytes.len().saturating_sub(self.pos);
277        if remaining_input <= self.element_budget {
278            self.read_value_inner::<false>()
279        } else {
280            self.read_value_inner::<true>()
281        }
282    }
283
284    /// Tree-builder body of [`Self::read_value`], monomorphised over whether
285    /// per-element budget accounting is active (see the fast-path comment
286    /// there — `CHARGE == false` is only reachable when the byte bound proves
287    /// the budget cannot be exceeded).
288    fn read_value_inner<const CHARGE: bool>(&mut self) -> Result<(Tag, Value)> {
289        match self.next()? {
290            Some(Element::Scalar { tag, value }) => {
291                if CHARGE {
292                    self.charge_element()?;
293                }
294                Ok((tag, value))
295            }
296            Some(Element::ContainerStart { tag, kind }) => {
297                if CHARGE {
298                    self.charge_element()?;
299                }
300                let value = self.read_container_body::<CHARGE>(kind)?;
301                Ok((tag, value))
302            }
303            Some(Element::ContainerEnd) => Err(Error::UnexpectedEndOfContainer),
304            None => Err(Error::UnexpectedEof),
305        }
306    }
307
308    /// Charge one element against the tree-builder budget. Returns
309    /// [`Error::ElementBudgetExceeded`] once the budget is exhausted. Only
310    /// called on charged decodes ([`Self::read_value`]'s slow path); the
311    /// container loops mirror the same arithmetic in a local for speed (see
312    /// [`Self::read_container_body`]).
313    fn charge_element(&mut self) -> Result<()> {
314        self.element_budget = self
315            .element_budget
316            .checked_sub(1)
317            .ok_or(Error::ElementBudgetExceeded)?;
318        Ok(())
319    }
320
321    /// Decode a container's children into a [`Value`] tree.
322    ///
323    /// # Element-budget accounting (denial-of-service bound)
324    ///
325    /// When `CHARGE` is false (the [`Self::read_value`] fast path: the byte
326    /// bound already proves the budget cannot be exceeded) no accounting code
327    /// is compiled into this copy at all.
328    ///
329    /// When `CHARGE` is true, the check is the hot path of tree-builder
330    /// decoding, so instead of calling [`Self::charge_element`] (a
331    /// read-modify-write of `self.element_budget` that the compiler cannot
332    /// keep in a register across the `self.next()` calls) each loop mirrors
333    /// the remaining budget in a local, charges the local per element, and
334    /// syncs it back to `self.element_budget` around recursion and at
335    /// container close.
336    ///
337    /// The preserved invariant: **a charged decode never materialises more
338    /// than the configured element budget's worth of [`Value`] elements, and
339    /// it fails (`Error::ElementBudgetExceeded`) on exactly the same element
340    /// as a per-element field update would** — every element is still
341    /// individually charged before it is pushed, and the field is up to date
342    /// whenever recursion (the only other consumer) runs. The one observable
343    /// difference is on *error* returns: charges made since the last sync are
344    /// not written back — which cannot weaken the bound, because the failed
345    /// call's partially built tree is dropped with it (the budget bounds live
346    /// memory amplification, and a subsequent `read_value` on the same reader
347    /// starts from a tree of zero live elements).
348    fn read_container_body<const CHARGE: bool>(&mut self, kind: ContainerKind) -> Result<Value> {
349        // Branch on the container kind once, before the loop, so arrays decode
350        // straight into a `Vec<Value>` without first building a `Vec<(Tag,
351        // Value)>` and re-collecting. Structures and lists keep their members'
352        // tags.
353        match kind {
354            ContainerKind::Array => {
355                let mut elements: Vec<Value> = Vec::new();
356                let mut budget = self.element_budget;
357                loop {
358                    match self.next()? {
359                        None => return Err(Error::UnclosedContainer),
360                        Some(Element::ContainerEnd) => break,
361                        Some(Element::Scalar { tag, value }) => {
362                            // Spec: every array element must be anonymous. Fail
363                            // closed on any other tag rather than discarding it.
364                            if tag != Tag::Anonymous {
365                                return Err(Error::NonAnonymousArrayTag);
366                            }
367                            if CHARGE {
368                                budget =
369                                    budget.checked_sub(1).ok_or(Error::ElementBudgetExceeded)?;
370                            }
371                            elements.push(value);
372                        }
373                        Some(Element::ContainerStart {
374                            tag,
375                            kind: inner_kind,
376                        }) => {
377                            if tag != Tag::Anonymous {
378                                return Err(Error::NonAnonymousArrayTag);
379                            }
380                            if CHARGE {
381                                budget =
382                                    budget.checked_sub(1).ok_or(Error::ElementBudgetExceeded)?;
383                                self.element_budget = budget;
384                            }
385                            elements.push(self.read_container_body::<CHARGE>(inner_kind)?);
386                            if CHARGE {
387                                budget = self.element_budget;
388                            }
389                        }
390                    }
391                }
392                if CHARGE {
393                    self.element_budget = budget;
394                }
395                Ok(Value::Array(elements))
396            }
397            ContainerKind::Structure | ContainerKind::List => {
398                let mut members: Vec<(Tag, Value)> = Vec::new();
399                let mut budget = self.element_budget;
400                loop {
401                    match self.next()? {
402                        None => return Err(Error::UnclosedContainer),
403                        Some(Element::ContainerEnd) => break,
404                        Some(Element::Scalar { tag, value }) => {
405                            if CHARGE {
406                                budget =
407                                    budget.checked_sub(1).ok_or(Error::ElementBudgetExceeded)?;
408                            }
409                            members.push((tag, value));
410                        }
411                        Some(Element::ContainerStart {
412                            tag,
413                            kind: inner_kind,
414                        }) => {
415                            if CHARGE {
416                                budget =
417                                    budget.checked_sub(1).ok_or(Error::ElementBudgetExceeded)?;
418                                self.element_budget = budget;
419                            }
420                            let inner = self.read_container_body::<CHARGE>(inner_kind)?;
421                            members.push((tag, inner));
422                            if CHARGE {
423                                budget = self.element_budget;
424                            }
425                        }
426                    }
427                }
428                if CHARGE {
429                    self.element_budget = budget;
430                }
431                Ok(match kind {
432                    ContainerKind::List => Value::List(members),
433                    // The outer match guarantees this arm is Structure.
434                    _ => Value::Structure(members),
435                })
436            }
437        }
438    }
439
440    fn next_byte(&mut self) -> Result<u8> {
441        let b = *self.bytes.get(self.pos).ok_or(Error::UnexpectedEof)?;
442        self.pos += 1;
443        Ok(b)
444    }
445
446    fn next_bytes(&mut self, n: usize) -> Result<&'a [u8]> {
447        let end = self.pos.checked_add(n).ok_or(Error::LengthOverflow)?;
448        let slice = self.bytes.get(self.pos..end).ok_or(Error::UnexpectedEof)?;
449        self.pos = end;
450        Ok(slice)
451    }
452
453    fn read_tag(&mut self, control: u8) -> Result<Tag> {
454        match control & tc::TAG_CONTROL_MASK {
455            tc::ANONYMOUS => Ok(Tag::Anonymous),
456            tc::CONTEXT => {
457                let n = self.next_byte()?;
458                Ok(Tag::Context(n))
459            }
460            tc::COMMON_PROFILE_2 => {
461                let raw: [u8; 2] = self
462                    .next_bytes(2)?
463                    .try_into()
464                    .map_err(|_| Error::InternalSliceConversion)?;
465                Ok(Tag::CommonProfile(u32::from(u16::from_le_bytes(raw))))
466            }
467            tc::COMMON_PROFILE_4 => {
468                let raw: [u8; 4] = self
469                    .next_bytes(4)?
470                    .try_into()
471                    .map_err(|_| Error::InternalSliceConversion)?;
472                Ok(Tag::CommonProfile(u32::from_le_bytes(raw)))
473            }
474            tc::IMPLICIT_PROFILE_2 => {
475                let raw: [u8; 2] = self
476                    .next_bytes(2)?
477                    .try_into()
478                    .map_err(|_| Error::InternalSliceConversion)?;
479                Ok(Tag::ImplicitProfile(u32::from(u16::from_le_bytes(raw))))
480            }
481            tc::IMPLICIT_PROFILE_4 => {
482                let raw: [u8; 4] = self
483                    .next_bytes(4)?
484                    .try_into()
485                    .map_err(|_| Error::InternalSliceConversion)?;
486                Ok(Tag::ImplicitProfile(u32::from_le_bytes(raw)))
487            }
488            tc::FULLY_QUALIFIED_6 => {
489                let vendor = self.read_u16_le()?;
490                let profile = self.read_u16_le()?;
491                let tag = u32::from(self.read_u16_le()?);
492                Ok(Tag::FullyQualified {
493                    vendor,
494                    profile,
495                    tag,
496                })
497            }
498            tc::FULLY_QUALIFIED_8 => {
499                let vendor = self.read_u16_le()?;
500                let profile = self.read_u16_le()?;
501                let tag = self.read_u32_le()?;
502                Ok(Tag::FullyQualified {
503                    vendor,
504                    profile,
505                    tag,
506                })
507            }
508            // The 3-bit tag-control field has only 8 possible values, and
509            // we have arms for all 8. This arm is unreachable in practice
510            // but rustc cannot prove that statically.
511            other => Err(Error::InvalidTagControl(other)),
512        }
513    }
514
515    fn read_u16_le(&mut self) -> Result<u16> {
516        let raw: [u8; 2] = self
517            .next_bytes(2)?
518            .try_into()
519            .map_err(|_| Error::InternalSliceConversion)?;
520        Ok(u16::from_le_bytes(raw))
521    }
522
523    fn read_u32_le(&mut self) -> Result<u32> {
524        let raw: [u8; 4] = self
525            .next_bytes(4)?
526            .try_into()
527            .map_err(|_| Error::InternalSliceConversion)?;
528        Ok(u32::from_le_bytes(raw))
529    }
530
531    #[allow(clippy::cast_possible_wrap)] // `b as i8`: reinterprets the byte pattern as signed, not truncation.
532    fn read_value_body(&mut self, elem_type: u8) -> Result<Value> {
533        match elem_type {
534            et::BOOL_FALSE => Ok(Value::Bool(false)),
535            et::BOOL_TRUE => Ok(Value::Bool(true)),
536            et::NULL => Ok(Value::Null),
537            et::UINT8 => Ok(Value::Uint(u64::from(self.next_byte()?))),
538            et::UINT16 => {
539                let raw: [u8; 2] = self
540                    .next_bytes(2)?
541                    .try_into()
542                    .map_err(|_| Error::InternalSliceConversion)?;
543                Ok(Value::Uint(u64::from(u16::from_le_bytes(raw))))
544            }
545            et::UINT32 => {
546                let raw: [u8; 4] = self
547                    .next_bytes(4)?
548                    .try_into()
549                    .map_err(|_| Error::InternalSliceConversion)?;
550                Ok(Value::Uint(u64::from(u32::from_le_bytes(raw))))
551            }
552            et::UINT64 => {
553                let raw: [u8; 8] = self
554                    .next_bytes(8)?
555                    .try_into()
556                    .map_err(|_| Error::InternalSliceConversion)?;
557                Ok(Value::Uint(u64::from_le_bytes(raw)))
558            }
559            et::INT8 => {
560                let b = self.next_byte()?;
561                Ok(Value::Int(i64::from(b as i8)))
562            }
563            et::INT16 => {
564                let raw: [u8; 2] = self
565                    .next_bytes(2)?
566                    .try_into()
567                    .map_err(|_| Error::InternalSliceConversion)?;
568                Ok(Value::Int(i64::from(i16::from_le_bytes(raw))))
569            }
570            et::INT32 => {
571                let raw: [u8; 4] = self
572                    .next_bytes(4)?
573                    .try_into()
574                    .map_err(|_| Error::InternalSliceConversion)?;
575                Ok(Value::Int(i64::from(i32::from_le_bytes(raw))))
576            }
577            et::INT64 => {
578                let raw: [u8; 8] = self
579                    .next_bytes(8)?
580                    .try_into()
581                    .map_err(|_| Error::InternalSliceConversion)?;
582                Ok(Value::Int(i64::from_le_bytes(raw)))
583            }
584            et::FLOAT32 => {
585                let raw: [u8; 4] = self
586                    .next_bytes(4)?
587                    .try_into()
588                    .map_err(|_| Error::InternalSliceConversion)?;
589                Ok(Value::Float(f32::from_le_bytes(raw)))
590            }
591            et::FLOAT64 => {
592                let raw: [u8; 8] = self
593                    .next_bytes(8)?
594                    .try_into()
595                    .map_err(|_| Error::InternalSliceConversion)?;
596                Ok(Value::Double(f64::from_le_bytes(raw)))
597            }
598            et::UTF8_LEN8 | et::UTF8_LEN16 | et::UTF8_LEN32 | et::UTF8_LEN64 => {
599                let len = self.read_payload_len(elem_type)?;
600                self.read_utf8(len)
601            }
602            et::BYTES_LEN8 | et::BYTES_LEN16 | et::BYTES_LEN32 | et::BYTES_LEN64 => {
603                let len = self.read_payload_len(elem_type)?;
604                self.read_bytes(len)
605            }
606            other => Err(Error::InvalidElementType(other)),
607        }
608    }
609
610    /// Read the variable-width length field that precedes utf8 and bytes
611    /// payloads. The two low bits of the element type encode the width:
612    /// `0b00` = 1 byte, `0b01` = 2 bytes, `0b10` = 4 bytes, `0b11` = 8 bytes.
613    fn read_payload_len(&mut self, elem_type: u8) -> Result<usize> {
614        match elem_type & 0b11 {
615            0b00 => Ok(usize::from(self.next_byte()?)),
616            0b01 => Ok(usize::from(self.read_u16_le()?)),
617            0b10 => usize::try_from(self.read_u32_le()?).map_err(|_| Error::LengthOverflow),
618            _ => usize::try_from(self.read_u64_le()?).map_err(|_| Error::LengthOverflow),
619        }
620    }
621
622    fn read_u64_le(&mut self) -> Result<u64> {
623        let raw: [u8; 8] = self
624            .next_bytes(8)?
625            .try_into()
626            .map_err(|_| Error::InternalSliceConversion)?;
627        Ok(u64::from_le_bytes(raw))
628    }
629
630    fn read_utf8(&mut self, len: usize) -> Result<Value> {
631        let bytes = self.next_bytes(len)?;
632        let s = core::str::from_utf8(bytes)?;
633        Ok(Value::Utf8(String::from(s)))
634    }
635
636    fn read_bytes(&mut self, len: usize) -> Result<Value> {
637        let bytes = self.next_bytes(len)?;
638        Ok(Value::Bytes(bytes.to_vec()))
639    }
640}
641
642#[cfg(test)]
643#[allow(clippy::unwrap_used)] // Test code: CLAUDE.md allows unwrap with a documented justification.
644mod tests {
645    use super::*;
646
647    #[test]
648    fn next_returns_none_on_empty_input() {
649        let mut r = TlvReader::new(&[]);
650        assert!(r.is_empty());
651        assert_eq!(r.next().unwrap(), None);
652    }
653
654    #[test]
655    fn next_decodes_bool_true_anonymous_vector_0001() {
656        let mut r = TlvReader::new(&[0x09]);
657        let el = r.next().unwrap().unwrap();
658        assert_eq!(
659            el,
660            Element::Scalar {
661                tag: Tag::Anonymous,
662                value: Value::Bool(true)
663            }
664        );
665        assert!(r.is_empty());
666    }
667
668    #[test]
669    fn next_decodes_bool_false() {
670        let mut r = TlvReader::new(&[0x08]);
671        let el = r.next().unwrap().unwrap();
672        assert_eq!(
673            el,
674            Element::Scalar {
675                tag: Tag::Anonymous,
676                value: Value::Bool(false)
677            }
678        );
679    }
680
681    #[test]
682    fn next_decodes_null_vector_implied() {
683        let mut r = TlvReader::new(&[0x14]);
684        let el = r.next().unwrap().unwrap();
685        assert_eq!(
686            el,
687            Element::Scalar {
688                tag: Tag::Anonymous,
689                value: Value::Null
690            }
691        );
692    }
693
694    #[test]
695    fn next_decodes_uint8_42_vector_0003() {
696        let mut r = TlvReader::new(&[0x04, 0x2A]);
697        let el = r.next().unwrap().unwrap();
698        assert_eq!(
699            el,
700            Element::Scalar {
701                tag: Tag::Anonymous,
702                value: Value::Uint(42)
703            }
704        );
705    }
706
707    #[test]
708    fn next_decodes_uint16_0x1234() {
709        let mut r = TlvReader::new(&[0x05, 0x34, 0x12]);
710        let el = r.next().unwrap().unwrap();
711        assert_eq!(
712            el,
713            Element::Scalar {
714                tag: Tag::Anonymous,
715                value: Value::Uint(0x1234)
716            }
717        );
718    }
719
720    #[test]
721    fn next_decodes_uint32_0xcafebabe() {
722        let mut r = TlvReader::new(&[0x06, 0xBE, 0xBA, 0xFE, 0xCA]);
723        let el = r.next().unwrap().unwrap();
724        assert_eq!(
725            el,
726            Element::Scalar {
727                tag: Tag::Anonymous,
728                value: Value::Uint(0xCAFE_BABE)
729            }
730        );
731    }
732
733    #[test]
734    fn next_decodes_uint64_big() {
735        let bytes = [0x07, 0xEF, 0xCD, 0xAB, 0x89, 0x67, 0x45, 0x23, 0x01];
736        let mut r = TlvReader::new(&bytes);
737        let el = r.next().unwrap().unwrap();
738        assert_eq!(
739            el,
740            Element::Scalar {
741                tag: Tag::Anonymous,
742                value: Value::Uint(0x0123_4567_89AB_CDEF),
743            }
744        );
745    }
746
747    #[test]
748    fn next_decodes_int8_neg17_vector_0008() {
749        let mut r = TlvReader::new(&[0x00, 0xEF]);
750        let el = r.next().unwrap().unwrap();
751        assert_eq!(
752            el,
753            Element::Scalar {
754                tag: Tag::Anonymous,
755                value: Value::Int(-17)
756            }
757        );
758    }
759
760    #[test]
761    fn next_decodes_int16_neg129() {
762        let mut r = TlvReader::new(&[0x01, 0x7F, 0xFF]);
763        let el = r.next().unwrap().unwrap();
764        assert_eq!(
765            el,
766            Element::Scalar {
767                tag: Tag::Anonymous,
768                value: Value::Int(-129)
769            }
770        );
771    }
772
773    #[test]
774    fn next_decodes_int32_min() {
775        let mut r = TlvReader::new(&[0x02, 0x00, 0x00, 0x00, 0x80]);
776        let el = r.next().unwrap().unwrap();
777        assert_eq!(
778            el,
779            Element::Scalar {
780                tag: Tag::Anonymous,
781                value: Value::Int(i64::from(i32::MIN))
782            }
783        );
784    }
785
786    #[test]
787    fn next_decodes_int64_min() {
788        let bytes = [0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80];
789        let mut r = TlvReader::new(&bytes);
790        let el = r.next().unwrap().unwrap();
791        assert_eq!(
792            el,
793            Element::Scalar {
794                tag: Tag::Anonymous,
795                value: Value::Int(i64::MIN)
796            }
797        );
798    }
799
800    #[test]
801    fn next_decodes_float32_zero_vector_0013() {
802        let mut r = TlvReader::new(&[0x0A, 0x00, 0x00, 0x00, 0x00]);
803        let el = r.next().unwrap().unwrap();
804        assert_eq!(
805            el,
806            Element::Scalar {
807                tag: Tag::Anonymous,
808                value: Value::Float(0.0)
809            }
810        );
811    }
812
813    #[test]
814    fn next_decodes_float64_zero_vector_0014() {
815        let bytes = [0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
816        let mut r = TlvReader::new(&bytes);
817        let el = r.next().unwrap().unwrap();
818        assert_eq!(
819            el,
820            Element::Scalar {
821                tag: Tag::Anonymous,
822                value: Value::Double(0.0)
823            }
824        );
825    }
826
827    #[test]
828    fn next_decodes_uint_with_context_tag_5() {
829        let mut r = TlvReader::new(&[0x24, 0x05, 0x2A]);
830        let el = r.next().unwrap().unwrap();
831        assert_eq!(
832            el,
833            Element::Scalar {
834                tag: Tag::Context(5),
835                value: Value::Uint(42)
836            }
837        );
838    }
839
840    #[test]
841    fn next_errors_on_unexpected_eof_in_payload() {
842        let mut r = TlvReader::new(&[0x05]); // uint16 with no payload
843        assert!(matches!(r.next(), Err(Error::UnexpectedEof)));
844    }
845
846    #[test]
847    fn next_decodes_uint_with_common_profile_2_byte_tag() {
848        let mut r = TlvReader::new(&[0x44, 0x07, 0x00, 0x2A]);
849        let el = r.next().unwrap().unwrap();
850        assert_eq!(
851            el,
852            Element::Scalar {
853                tag: Tag::CommonProfile(7),
854                value: Value::Uint(42)
855            }
856        );
857    }
858
859    #[test]
860    fn next_decodes_uint_with_common_profile_4_byte_tag() {
861        let mut r = TlvReader::new(&[0x64, 0x45, 0x23, 0x01, 0x00, 0x2A]);
862        let el = r.next().unwrap().unwrap();
863        assert_eq!(
864            el,
865            Element::Scalar {
866                tag: Tag::CommonProfile(0x0001_2345),
867                value: Value::Uint(42)
868            }
869        );
870    }
871
872    #[test]
873    fn next_decodes_uint_with_implicit_profile_2_byte_tag() {
874        let mut r = TlvReader::new(&[0x84, 0x07, 0x00, 0x2A]);
875        let el = r.next().unwrap().unwrap();
876        assert_eq!(
877            el,
878            Element::Scalar {
879                tag: Tag::ImplicitProfile(7),
880                value: Value::Uint(42)
881            }
882        );
883    }
884
885    #[test]
886    fn next_decodes_uint_with_implicit_profile_4_byte_tag() {
887        let mut r = TlvReader::new(&[0xA4, 0x45, 0x23, 0x01, 0x00, 0x2A]);
888        let el = r.next().unwrap().unwrap();
889        assert_eq!(
890            el,
891            Element::Scalar {
892                tag: Tag::ImplicitProfile(0x0001_2345),
893                value: Value::Uint(42)
894            }
895        );
896    }
897
898    #[test]
899    fn next_decodes_uint_with_fully_qualified_6_byte() {
900        let mut r = TlvReader::new(&[0xC4, 0xF1, 0xFF, 0x06, 0x00, 0x05, 0x00, 0x2A]);
901        let el = r.next().unwrap().unwrap();
902        assert_eq!(
903            el,
904            Element::Scalar {
905                tag: Tag::FullyQualified {
906                    vendor: 0xFFF1,
907                    profile: 0x0006,
908                    tag: 5
909                },
910                value: Value::Uint(42),
911            }
912        );
913    }
914
915    #[test]
916    fn next_decodes_uint_with_fully_qualified_8_byte() {
917        let mut r = TlvReader::new(&[0xE4, 0xF1, 0xFF, 0x06, 0x00, 0x45, 0x23, 0x01, 0x00, 0x2A]);
918        let el = r.next().unwrap().unwrap();
919        assert_eq!(
920            el,
921            Element::Scalar {
922                tag: Tag::FullyQualified {
923                    vendor: 0xFFF1,
924                    profile: 0x0006,
925                    tag: 0x0001_2345
926                },
927                value: Value::Uint(42),
928            }
929        );
930    }
931
932    #[test]
933    fn read_value_returns_tag_and_value_for_scalar() {
934        let mut r = TlvReader::new(&[0x24, 0x05, 0x2A]);
935        let (tag, value) = r.read_value().unwrap();
936        assert_eq!(tag, Tag::Context(5));
937        assert_eq!(value, Value::Uint(42));
938    }
939
940    #[test]
941    fn read_value_errors_on_empty_input() {
942        let mut r = TlvReader::new(&[]);
943        assert!(matches!(r.read_value(), Err(Error::UnexpectedEof)));
944    }
945
946    #[test]
947    fn next_decodes_utf8_hello_vector_0015() {
948        let bytes = [0x0C, 0x06, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x21];
949        let mut r = TlvReader::new(&bytes);
950        let el = r.next().unwrap().unwrap();
951        assert_eq!(
952            el,
953            Element::Scalar {
954                tag: Tag::Anonymous,
955                value: Value::Utf8(String::from("Hello!")),
956            }
957        );
958    }
959
960    #[test]
961    fn next_decodes_utf8_empty_vector_0016() {
962        let bytes = [0x0C, 0x00];
963        let mut r = TlvReader::new(&bytes);
964        let el = r.next().unwrap().unwrap();
965        assert_eq!(
966            el,
967            Element::Scalar {
968                tag: Tag::Anonymous,
969                value: Value::Utf8(String::new()),
970            }
971        );
972    }
973
974    #[test]
975    fn next_decodes_utf8_len16_path() {
976        let mut bytes = vec![0x0D, 0x00, 0x01]; // UTF8_LEN16, length 256 LE
977        bytes.extend(std::iter::repeat_n(b'a', 256));
978        let mut r = TlvReader::new(&bytes);
979        let el = r.next().unwrap().unwrap();
980        let Element::Scalar {
981            value: Value::Utf8(s),
982            ..
983        } = el
984        else {
985            panic!("wrong variant")
986        };
987        assert_eq!(s.len(), 256);
988        assert!(s.bytes().all(|b| b == b'a'));
989    }
990
991    #[test]
992    fn next_decodes_bytes_five_bytes_vector_0017() {
993        let bytes = [0x10, 0x05, 0x00, 0x01, 0x02, 0x03, 0x04];
994        let mut r = TlvReader::new(&bytes);
995        let el = r.next().unwrap().unwrap();
996        assert_eq!(
997            el,
998            Element::Scalar {
999                tag: Tag::Anonymous,
1000                value: Value::Bytes(vec![0x00, 0x01, 0x02, 0x03, 0x04]),
1001            }
1002        );
1003    }
1004
1005    #[test]
1006    fn next_decodes_bytes_empty_vector_0018() {
1007        let bytes = [0x10, 0x00];
1008        let mut r = TlvReader::new(&bytes);
1009        let el = r.next().unwrap().unwrap();
1010        assert_eq!(
1011            el,
1012            Element::Scalar {
1013                tag: Tag::Anonymous,
1014                value: Value::Bytes(Vec::new()),
1015            }
1016        );
1017    }
1018
1019    #[test]
1020    fn next_errors_on_invalid_utf8() {
1021        let bytes = [0x0C, 0x01, 0xFF];
1022        let mut r = TlvReader::new(&bytes);
1023        assert!(matches!(r.next(), Err(Error::InvalidUtf8(_))));
1024    }
1025
1026    #[test]
1027    fn next_errors_on_truncated_utf8_payload() {
1028        let bytes = [0x0C, 0x05, b'H', b'i']; // claims 5 bytes, has only 2
1029        let mut r = TlvReader::new(&bytes);
1030        assert!(matches!(r.next(), Err(Error::UnexpectedEof)));
1031    }
1032
1033    // --- Task 4: container event tests ---
1034
1035    #[test]
1036    fn next_decodes_structure_start_and_end_vector_0019() {
1037        let mut r = TlvReader::new(&[0x15, 0x18]);
1038        let el = r.next().unwrap().unwrap();
1039        assert_eq!(
1040            el,
1041            Element::ContainerStart {
1042                tag: Tag::Anonymous,
1043                kind: ContainerKind::Structure,
1044            }
1045        );
1046        let el = r.next().unwrap().unwrap();
1047        assert_eq!(el, Element::ContainerEnd);
1048        assert!(r.next().unwrap().is_none());
1049    }
1050
1051    #[test]
1052    fn next_decodes_array_start_and_end_vector_0020() {
1053        let mut r = TlvReader::new(&[0x16, 0x18]);
1054        assert_eq!(
1055            r.next().unwrap().unwrap(),
1056            Element::ContainerStart {
1057                tag: Tag::Anonymous,
1058                kind: ContainerKind::Array,
1059            }
1060        );
1061        assert_eq!(r.next().unwrap().unwrap(), Element::ContainerEnd);
1062    }
1063
1064    #[test]
1065    fn next_decodes_list_start_and_end() {
1066        let mut r = TlvReader::new(&[0x17, 0x18]);
1067        assert_eq!(
1068            r.next().unwrap().unwrap(),
1069            Element::ContainerStart {
1070                tag: Tag::Anonymous,
1071                kind: ContainerKind::List,
1072            }
1073        );
1074        assert_eq!(r.next().unwrap().unwrap(), Element::ContainerEnd);
1075    }
1076
1077    #[test]
1078    fn next_decodes_structure_with_child_streaming() {
1079        let mut r = TlvReader::new(&[0x15, 0x24, 0x00, 0x2A, 0x18]);
1080        assert_eq!(
1081            r.next().unwrap().unwrap(),
1082            Element::ContainerStart {
1083                tag: Tag::Anonymous,
1084                kind: ContainerKind::Structure,
1085            }
1086        );
1087        assert_eq!(
1088            r.next().unwrap().unwrap(),
1089            Element::Scalar {
1090                tag: Tag::Context(0),
1091                value: Value::Uint(42),
1092            }
1093        );
1094        assert_eq!(r.next().unwrap().unwrap(), Element::ContainerEnd);
1095        assert!(r.next().unwrap().is_none());
1096    }
1097
1098    #[test]
1099    fn next_errors_on_end_of_container_at_top_level() {
1100        let mut r = TlvReader::new(&[0x18]);
1101        assert!(matches!(r.next(), Err(Error::UnexpectedEndOfContainer)));
1102    }
1103
1104    #[test]
1105    fn next_errors_on_end_of_container_with_non_anonymous_tag_form() {
1106        // 0x38 = context-tag form (0b001) | END_OF_CONTAINER (0x18).
1107        let mut r = TlvReader::new(&[0x38, 0x05]);
1108        assert!(matches!(r.next(), Err(Error::InvalidTagControl(_))));
1109    }
1110
1111    #[test]
1112    fn next_errors_on_excessive_nesting() {
1113        let bytes: Vec<u8> = std::iter::repeat_n(0x15u8, 33).collect();
1114        let mut r = TlvReader::new(&bytes);
1115        for _ in 0..32 {
1116            assert!(matches!(
1117                r.next().unwrap().unwrap(),
1118                Element::ContainerStart {
1119                    kind: ContainerKind::Structure,
1120                    ..
1121                },
1122            ));
1123        }
1124        assert!(matches!(r.next(), Err(Error::ContainerTooDeep)));
1125    }
1126
1127    #[test]
1128    fn depth_returns_to_zero_after_balanced_close() {
1129        // Two readers — one balanced (depth returns to 0), then check that
1130        // a fresh reader sees 0x18 at top level as UnexpectedEndOfContainer.
1131        {
1132            let mut r = TlvReader::new(&[0x15, 0x18]);
1133            let _ = r.next(); // ContainerStart → depth = 1
1134            let _ = r.next(); // ContainerEnd → depth = 0
1135        }
1136        let mut r2 = TlvReader::new(&[0x18]);
1137        assert!(matches!(r2.next(), Err(Error::UnexpectedEndOfContainer)));
1138    }
1139
1140    // --- Task 5: read_value tree builder tests ---
1141
1142    #[test]
1143    fn read_value_returns_empty_structure_vector_0019() {
1144        let mut r = TlvReader::new(&[0x15, 0x18]);
1145        let (tag, value) = r.read_value().unwrap();
1146        assert_eq!(tag, Tag::Anonymous);
1147        assert_eq!(value, Value::Structure(Vec::new()));
1148    }
1149
1150    #[test]
1151    fn read_value_returns_empty_array_vector_0020() {
1152        let mut r = TlvReader::new(&[0x16, 0x18]);
1153        let (tag, value) = r.read_value().unwrap();
1154        assert_eq!(tag, Tag::Anonymous);
1155        assert_eq!(value, Value::Array(Vec::new()));
1156    }
1157
1158    #[test]
1159    fn read_value_returns_structure_with_ctx_member_vector_0021() {
1160        let mut r = TlvReader::new(&[0x15, 0x24, 0x00, 0x2A, 0x18]);
1161        let (tag, value) = r.read_value().unwrap();
1162        assert_eq!(tag, Tag::Anonymous);
1163        assert_eq!(
1164            value,
1165            Value::Structure(vec![(Tag::Context(0), Value::Uint(42))])
1166        );
1167    }
1168
1169    #[test]
1170    fn read_value_returns_array_of_three_uint8_vector_0022() {
1171        let mut r = TlvReader::new(&[0x16, 0x04, 0x01, 0x04, 0x02, 0x04, 0x03, 0x18]);
1172        let (tag, value) = r.read_value().unwrap();
1173        assert_eq!(tag, Tag::Anonymous);
1174        assert_eq!(
1175            value,
1176            Value::Array(vec![Value::Uint(1), Value::Uint(2), Value::Uint(3)])
1177        );
1178    }
1179
1180    #[test]
1181    fn read_value_returns_structure_with_bool_at_ctx7_vector_0023() {
1182        let mut r = TlvReader::new(&[0x15, 0x29, 0x07, 0x18]);
1183        let (tag, value) = r.read_value().unwrap();
1184        assert_eq!(tag, Tag::Anonymous);
1185        assert_eq!(
1186            value,
1187            Value::Structure(vec![(Tag::Context(7), Value::Bool(true))])
1188        );
1189    }
1190
1191    #[test]
1192    fn read_value_returns_empty_list() {
1193        let mut r = TlvReader::new(&[0x17, 0x18]);
1194        let (tag, value) = r.read_value().unwrap();
1195        assert_eq!(tag, Tag::Anonymous);
1196        assert_eq!(value, Value::List(Vec::new()));
1197    }
1198
1199    #[test]
1200    fn read_value_handles_nested_structure() {
1201        let mut r = TlvReader::new(&[0x15, 0x35, 0x00, 0x24, 0x00, 0x2A, 0x18, 0x18]);
1202        let (tag, value) = r.read_value().unwrap();
1203        assert_eq!(tag, Tag::Anonymous);
1204        let inner = Value::Structure(vec![(Tag::Context(0), Value::Uint(42))]);
1205        let outer = Value::Structure(vec![(Tag::Context(0), inner)]);
1206        assert_eq!(value, outer);
1207    }
1208
1209    #[test]
1210    fn read_value_errors_on_unclosed_container() {
1211        let mut r = TlvReader::new(&[0x15]);
1212        assert!(matches!(r.read_value(), Err(Error::UnclosedContainer)));
1213    }
1214
1215    #[test]
1216    fn read_value_errors_on_dangling_end_of_container() {
1217        let mut r = TlvReader::new(&[0x18]);
1218        assert!(matches!(
1219            r.read_value(),
1220            Err(Error::UnexpectedEndOfContainer)
1221        ));
1222    }
1223
1224    // --- Task 19: fail-closed array tags, budget, conversion error ---
1225
1226    #[test]
1227    fn read_value_rejects_array_with_context_tagged_child() {
1228        // 0x16 array-start, 0x24 0x00 0x2A = ctx(0) uint8=42, 0x18 end.
1229        // The child carries a context tag, which the spec forbids inside an
1230        // array. Pre-fix the decoder silently dropped the tag; now it errors.
1231        let mut r = TlvReader::new(&[0x16, 0x24, 0x00, 0x2A, 0x18]);
1232        assert!(matches!(r.read_value(), Err(Error::NonAnonymousArrayTag)));
1233    }
1234
1235    #[test]
1236    fn read_value_rejects_array_with_context_tagged_container_child() {
1237        // 0x16 array-start, 0x35 0x00 = ctx(0) struct-start, 0x18 inner end,
1238        // 0x18 outer end. The nested container child is context-tagged.
1239        let mut r = TlvReader::new(&[0x16, 0x35, 0x00, 0x18, 0x18]);
1240        assert!(matches!(r.read_value(), Err(Error::NonAnonymousArrayTag)));
1241    }
1242
1243    #[test]
1244    fn read_value_accepts_array_with_anonymous_children() {
1245        // Sanity: a well-formed array still decodes (regression guard for the
1246        // fail-closed change).
1247        let mut r = TlvReader::new(&[0x16, 0x04, 0x01, 0x04, 0x02, 0x18]);
1248        let (tag, value) = r.read_value().unwrap();
1249        assert_eq!(tag, Tag::Anonymous);
1250        assert_eq!(value, Value::Array(vec![Value::Uint(1), Value::Uint(2)]));
1251    }
1252
1253    #[test]
1254    fn read_value_errors_when_element_budget_is_exceeded() {
1255        // Array of three uint8 children = 4 elements total (array + 3 scalars).
1256        // A budget of 3 cannot fit them. The 8-byte input is larger than the
1257        // budget, so this takes the CHARGED path (the fast path is provably
1258        // unreachable for a violating input: more elements than budget implies
1259        // more input bytes than budget).
1260        let bytes = [0x16, 0x04, 0x01, 0x04, 0x02, 0x04, 0x03, 0x18];
1261        let mut r = TlvReader::with_element_budget(&bytes, 3);
1262        assert!(matches!(r.read_value(), Err(Error::ElementBudgetExceeded)));
1263    }
1264
1265    #[test]
1266    fn read_value_fast_path_at_budget_equal_to_input_len() {
1267        // Boundary of the uncharged fast path: remaining input (8 bytes) equal
1268        // to the budget — the byte bound proves the 4 materialised elements
1269        // cannot exceed it, so the decode succeeds without accounting.
1270        let bytes = [0x16, 0x04, 0x01, 0x04, 0x02, 0x04, 0x03, 0x18];
1271        let mut r = TlvReader::with_element_budget(&bytes, bytes.len());
1272        let (_, value) = r.read_value().unwrap();
1273        assert_eq!(
1274            value,
1275            Value::Array(vec![Value::Uint(1), Value::Uint(2), Value::Uint(3)])
1276        );
1277    }
1278
1279    #[test]
1280    fn read_value_charged_path_at_budget_one_below_input_len() {
1281        // One below the fast-path boundary: input (8 bytes) > budget (7) takes
1282        // the charged path, which still admits the 4-element tree.
1283        let bytes = [0x16, 0x04, 0x01, 0x04, 0x02, 0x04, 0x03, 0x18];
1284        let mut r = TlvReader::with_element_budget(&bytes, 7);
1285        let (_, value) = r.read_value().unwrap();
1286        assert_eq!(
1287            value,
1288            Value::Array(vec![Value::Uint(1), Value::Uint(2), Value::Uint(3)])
1289        );
1290    }
1291
1292    #[test]
1293    fn read_value_succeeds_at_exactly_the_element_budget() {
1294        // Same input, budget of exactly 4, decodes fine.
1295        let bytes = [0x16, 0x04, 0x01, 0x04, 0x02, 0x04, 0x03, 0x18];
1296        let mut r = TlvReader::with_element_budget(&bytes, 4);
1297        let (_, value) = r.read_value().unwrap();
1298        assert_eq!(
1299            value,
1300            Value::Array(vec![Value::Uint(1), Value::Uint(2), Value::Uint(3)])
1301        );
1302    }
1303
1304    #[test]
1305    fn read_value_budget_counts_a_single_scalar() {
1306        // A lone scalar costs one element; a zero budget rejects it.
1307        let mut r = TlvReader::with_element_budget(&[0x04, 0x2A], 0);
1308        assert!(matches!(r.read_value(), Err(Error::ElementBudgetExceeded)));
1309        let mut r = TlvReader::with_element_budget(&[0x04, 0x2A], 1);
1310        assert_eq!(r.read_value().unwrap(), (Tag::Anonymous, Value::Uint(42)));
1311    }
1312
1313    #[test]
1314    fn fixed_width_int_decode_still_works() {
1315        // Guard that the InternalSliceConversion relabel did not change the
1316        // happy path for a fixed-width int. The conversion branch itself is
1317        // unreachable: `next_bytes(N)` returns exactly N bytes or `UnexpectedEof`
1318        // first, so the `try_into::<[u8; N]>` can never fail.
1319        let mut r = TlvReader::new(&[0x05, 0x34, 0x12]);
1320        assert_eq!(
1321            r.next().unwrap().unwrap(),
1322            Element::Scalar {
1323                tag: Tag::Anonymous,
1324                value: Value::Uint(0x1234),
1325            }
1326        );
1327    }
1328
1329    // --- skip_container ----------------------------------------------------
1330
1331    /// Build an anonymous structure: ctx0=u(7), then a nested ctx9 struct
1332    /// {ctx0=u(1)}, then ctx1=u(42). Returns the encoded bytes.
1333    fn struct_with_nested() -> Vec<u8> {
1334        let mut buf = Vec::new();
1335        let mut w = crate::writer::TlvWriter::new(&mut buf);
1336        w.start_structure(Tag::Anonymous).unwrap();
1337        w.put_uint(Tag::Context(0), 7).unwrap();
1338        w.start_structure(Tag::Context(9)).unwrap();
1339        w.put_uint(Tag::Context(0), 1).unwrap();
1340        w.end_container().unwrap();
1341        w.put_uint(Tag::Context(1), 42).unwrap();
1342        w.end_container().unwrap();
1343        buf
1344    }
1345
1346    #[test]
1347    fn skip_container_drains_nested_struct_and_positions_after() {
1348        let buf = struct_with_nested();
1349        let mut r = TlvReader::new(&buf);
1350        // open the outer struct
1351        assert!(matches!(
1352            r.next().unwrap(),
1353            Some(Element::ContainerStart {
1354                kind: ContainerKind::Structure,
1355                ..
1356            })
1357        ));
1358        // consume ctx0=7
1359        assert!(matches!(r.next().unwrap(), Some(Element::Scalar { .. })));
1360        // the next element is the nested ctx9 struct — open then skip it
1361        assert!(matches!(
1362            r.next().unwrap(),
1363            Some(Element::ContainerStart {
1364                kind: ContainerKind::Structure,
1365                ..
1366            })
1367        ));
1368        r.skip_container().unwrap();
1369        // reader must now be positioned at ctx1=42, NOT at the outer end
1370        match r.next().unwrap() {
1371            Some(Element::Scalar {
1372                tag: Tag::Context(1),
1373                value: Value::Uint(v),
1374            }) => {
1375                assert_eq!(v, 42);
1376            }
1377            other => panic!("expected ctx1=42 after skip, got {other:?}"),
1378        }
1379        // then the outer ContainerEnd, then None
1380        assert!(matches!(r.next().unwrap(), Some(Element::ContainerEnd)));
1381        assert!(r.next().unwrap().is_none());
1382    }
1383
1384    #[test]
1385    fn skip_container_handles_array_and_list_and_empty() {
1386        for kind_byte in ["array", "list", "empty"] {
1387            let mut buf = Vec::new();
1388            let mut w = crate::writer::TlvWriter::new(&mut buf);
1389            w.start_structure(Tag::Anonymous).unwrap();
1390            match kind_byte {
1391                "array" => {
1392                    w.start_array(Tag::Context(0)).unwrap();
1393                    w.put_uint(Tag::Anonymous, 1).unwrap();
1394                    w.put_uint(Tag::Anonymous, 2).unwrap();
1395                    w.end_container().unwrap();
1396                }
1397                "list" => {
1398                    w.start_list(Tag::Context(0)).unwrap();
1399                    w.put_uint(Tag::Context(5), 9).unwrap();
1400                    w.end_container().unwrap();
1401                }
1402                _ => {
1403                    w.start_structure(Tag::Context(0)).unwrap();
1404                    w.end_container().unwrap();
1405                }
1406            }
1407            w.put_uint(Tag::Context(1), 99).unwrap();
1408            w.end_container().unwrap();
1409
1410            let mut r = TlvReader::new(&buf);
1411            assert!(matches!(
1412                r.next().unwrap(),
1413                Some(Element::ContainerStart { .. })
1414            ));
1415            assert!(matches!(
1416                r.next().unwrap(),
1417                Some(Element::ContainerStart { .. })
1418            ));
1419            r.skip_container().unwrap();
1420            match r.next().unwrap() {
1421                Some(Element::Scalar {
1422                    tag: Tag::Context(1),
1423                    value: Value::Uint(v),
1424                }) => {
1425                    assert_eq!(v, 99, "kind {kind_byte}");
1426                }
1427                other => panic!("kind {kind_byte}: expected ctx1=99, got {other:?}"),
1428            }
1429        }
1430    }
1431
1432    #[test]
1433    fn skip_container_unclosed_is_error() {
1434        // outer struct opened, nested struct opened but never closed (truncated)
1435        let mut buf = Vec::new();
1436        {
1437            let mut w = crate::writer::TlvWriter::new(&mut buf);
1438            w.start_structure(Tag::Anonymous).unwrap();
1439            w.start_structure(Tag::Context(0)).unwrap();
1440            w.put_uint(Tag::Anonymous, 1).unwrap();
1441            // deliberately do NOT close either container
1442        }
1443        let mut r = TlvReader::new(&buf);
1444        assert!(matches!(
1445            r.next().unwrap(),
1446            Some(Element::ContainerStart { .. })
1447        ));
1448        assert!(matches!(
1449            r.next().unwrap(),
1450            Some(Element::ContainerStart { .. })
1451        ));
1452        assert!(matches!(r.skip_container(), Err(Error::UnclosedContainer)));
1453    }
1454}