pub struct TlvReader<'a> { /* private fields */ }Expand description
A streaming TLV decoder over a borrowed byte slice.
Implementations§
Source§impl<'a> TlvReader<'a>
impl<'a> TlvReader<'a>
Sourcepub fn new(bytes: &'a [u8]) -> Self
pub fn new(bytes: &'a [u8]) -> Self
Construct a reader that walks bytes from the start, using the
DEFAULT_ELEMENT_BUDGET for tree-builder decodes.
Sourcepub fn with_element_budget(bytes: &'a [u8], budget: usize) -> Self
pub fn with_element_budget(bytes: &'a [u8], budget: usize) -> Self
Construct a reader with a custom total-element budget for tree-builder
decoding (see DEFAULT_ELEMENT_BUDGET).
A Self::read_value call that would materialise more than budget
Value elements fails with Error::ElementBudgetExceeded. The
budget only affects the tree-builder path; the streaming Self::next
API is unaffected because it allocates nothing per element.
Sourcepub fn next(&mut self) -> Result<Option<Element>>
pub fn next(&mut self) -> Result<Option<Element>>
Advance one TLV element, returning an Element that owns its
string/bytes payloads. Implemented over Self::next_ref — the
borrowed walk IS the decode core, so the two can never disagree.
Returns Ok(None) at end of input. See Self::next_ref for the
zero-copy variant, which borrows string/bytes payloads from the
input instead of allocating.
§Errors
Returns Err if the input is malformed:
Error::InvalidTagControl— unrecognised tag-control byte form.Error::InvalidElementType— unknown element-type code.Error::UnexpectedEof— truncated payload bytes.Error::UnexpectedEndOfContainer— end-of-container marker (0x18) at the top level, with no container open.Error::ContainerTooDeep— a container open would exceedMAX_DEPTHnesting levels.
§Note on naming
This method is deliberately named next to match the streaming-reader
idiom established by e.g. serde’s Deserializer. It returns
Result<Option<T>> rather than Option<Result<T>> so that callers
use ? naturally. Implementing std::iter::Iterator is deferred to
a later phase when a fallible-iterator adapter is available.
Sourcepub fn next_ref(&mut self) -> Result<Option<ElementRef<'a>>>
pub fn next_ref(&mut self) -> Result<Option<ElementRef<'a>>>
Advance one TLV element, returning an ElementRef whose
string/bytes payloads borrow directly from the reader’s input — the
zero-copy sibling of Self::next, and the single decode core both
methods share. Returns Ok(None) at end of input.
§Errors
Returns Err if the input is malformed:
Error::InvalidTagControl— unrecognised tag-control byte form.Error::InvalidElementType— unknown element-type code.Error::UnexpectedEof— truncated payload bytes.Error::UnexpectedEndOfContainer— end-of-container marker (0x18) at the top level, with no container open.Error::ContainerTooDeep— a container open would exceedMAX_DEPTHnesting levels.
Sourcepub fn skip_container(&mut self) -> Result<()>
pub fn skip_container(&mut self) -> Result<()>
Skip the remaining body of the container whose
ContainerStart was just returned by
Self::next, consuming through its matching
ContainerEnd.
Call this immediately after next() yields a ContainerStart you
want to discard — for example an unknown field carried by a struct
from a newer Matter revision. On return the reader is positioned at
the first element after the skipped container. The container body
is skipped as a raw byte walk: nothing is materialised, and string
payloads inside the skipped (unobserved) region are not UTF-8
validated. Cost is bounded by the input size and nesting by
MAX_DEPTH.
§Errors
Error::UnclosedContainer— end of input before the container’s closing marker.Error::InvalidTagControl— unrecognised tag-control byte form.Error::InvalidElementType— unknown element-type code.Error::UnexpectedEof— truncated payload bytes or a truncated length field.Error::ContainerTooDeep— a container open inside the skipped region would exceedMAX_DEPTHnesting levels.Error::UnexpectedEndOfContainer— called with no container open on this reader.
After a successful skip, Self::element_span reports the whole
skipped container (header through end-of-container marker) — same as
calling Self::skip_container_span and discarding the return value.
§Examples
use matter_codec::{ContainerKind, Element, Tag, TlvReader, TlvWriter};
let mut buf = Vec::new();
let mut w = TlvWriter::new(&mut buf);
w.start_structure(Tag::Anonymous)?;
w.start_structure(Tag::Context(9))?; // an unknown nested field
w.end_container()?;
w.put_uint(Tag::Context(1), 42)?;
w.end_container()?;
let mut r = TlvReader::new(&buf);
r.next()?; // open the outer struct
// next() returns the nested ctx9 ContainerStart we want to discard:
assert!(matches!(
r.next()?,
Some(Element::ContainerStart { kind: ContainerKind::Structure, .. })
));
r.skip_container()?; // drain the nested struct
// the field after the unknown container is still readable:
assert!(matches!(r.next()?, Some(Element::Scalar { tag: Tag::Context(1), .. })));After an Err from this method, the reader’s position and depth are
unspecified — discard the reader rather than continuing to iterate.
Sourcepub fn skip_container_span(&mut self) -> Result<ElementSpan>
pub fn skip_container_span(&mut self) -> Result<ElementSpan>
Like Self::skip_container, but returns the skipped container’s
ElementSpan (marked at the ContainerStart just returned by
next(); ended at the read position after the raw skip).
This method has a precondition: the immediately preceding
Self::next / Self::next_ref call must have returned a
ContainerStart. Anywhere else — after a scalar, after an
end-of-container, after a Self::read_value that walked a whole
tree, after another skip, or before any element at all — it consumes
nothing and returns Error::UnexpectedEndOfContainer, rather than
handing back a span over unrelated bytes that a retag caller would
re-emit as malformed TLV.
§Errors
Error::UnexpectedEndOfContainer if the precondition above does not
hold, plus every error Self::skip_container can return. Misuse is
always an error — never a panic, and never a meaningless span.
After an Err from this method, the reader’s position and depth are
unspecified — discard the reader rather than continuing to iterate.
Sourcepub fn element_span(&self) -> Option<ElementSpan>
pub fn element_span(&self) -> Option<ElementSpan>
Span of the element most recently returned by Self::next /
Self::next_ref (or the whole container after a
skip_container* call). None before the first element; unchanged
by calls that return Ok(None) or an error.
Tree-builder reads drive the same core: Self::read_value walks its
element via Self::next_ref internally, so afterwards the span
refers to the last interior element it consumed, not the tree as a
whole. Read the span only immediately after the next / next_ref
call whose element you care about.
Sourcepub fn span_bytes(&self, range: Range<usize>) -> &'a [u8] ⓘ
pub fn span_bytes(&self, range: Range<usize>) -> &'a [u8] ⓘ
Resolve a range produced by this reader’s span APIs against the reader’s input. Returns an empty slice for a range that does not lie within the input (only possible with a span from a different reader).
Sourcepub fn read_value(&mut self) -> Result<(Tag, Value)>
pub fn read_value(&mut self) -> Result<(Tag, Value)>
Materialise one full TLV element as a (Tag, Value). Scalars are
returned directly; containers are read recursively up to
MAX_DEPTH levels (enforced by Self::next’s depth counter).
§Errors
Error::UnexpectedEof— the input is empty.Error::UnexpectedEndOfContainer— the first element is a stray end-of-container marker.Error::UnclosedContainer— end of input was reached before the container’s closing marker.Error::NonAnonymousArrayTag— an array child carried a non-anonymous tag (the spec requires array elements to be anonymous).Error::ElementBudgetExceeded— the decode would materialise more than the configured element budget (seeDEFAULT_ELEMENT_BUDGET).- Any error returned by
Self::next.