use crate::error::{Result, Tlv8Error};
pub struct Tlv8Reader;
impl Tlv8Reader {
pub fn parse(bytes: &[u8]) -> Result<Vec<(u8, Vec<u8>)>> {
let mut items: Vec<(u8, Vec<u8>)> = Vec::new();
let mut open_run: Option<(u8, bool)> = None;
let mut pos = 0;
while pos < bytes.len() {
let ty = bytes[pos];
let len = *bytes.get(pos + 1).ok_or(Tlv8Error::UnexpectedEof)? as usize;
let start = pos + 2;
let end = start.checked_add(len).ok_or(Tlv8Error::UnexpectedEof)?;
let value = bytes.get(start..end).ok_or(Tlv8Error::UnexpectedEof)?;
pos = end;
if ty == crate::SEPARATOR {
open_run = None;
items.push((ty, value.to_vec()));
continue;
}
let continues = matches!(open_run, Some((run_ty, true)) if run_ty == ty);
if continues {
if let Some(last) = items.last_mut() {
last.1.extend_from_slice(value);
}
} else {
items.push((ty, value.to_vec()));
}
open_run = Some((ty, len == 255));
}
Ok(items)
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used)]
mod tests {
use super::*;
#[test]
fn parse_empty_input_yields_no_items() {
assert_eq!(Tlv8Reader::parse(&[]).unwrap(), vec![]);
}
#[test]
fn parse_single_short_item() {
let items = Tlv8Reader::parse(&[0x01, 0x02, 0xAB, 0xCD]).unwrap();
assert_eq!(items, vec![(0x01, vec![0xAB, 0xCD])]);
}
#[test]
fn parse_zero_length_item() {
let items = Tlv8Reader::parse(&[0x06, 0x00]).unwrap();
assert_eq!(items, vec![(0x06, vec![])]);
}
#[test]
fn parse_two_distinct_types() {
let items = Tlv8Reader::parse(&[0x01, 0x01, 0xAA, 0x02, 0x01, 0xBB]).unwrap();
assert_eq!(items, vec![(0x01, vec![0xAA]), (0x02, vec![0xBB])]);
}
#[test]
fn parse_truncated_value_errors() {
let err = Tlv8Reader::parse(&[0x01, 0x02, 0xAA]).unwrap_err();
assert_eq!(err, Tlv8Error::UnexpectedEof);
}
#[test]
fn parse_missing_length_byte_errors() {
let err = Tlv8Reader::parse(&[0x01]).unwrap_err();
assert_eq!(err, Tlv8Error::UnexpectedEof);
}
#[test]
fn parse_reassembles_256_byte_value() {
let mut stream = vec![0x09, 0xFF];
stream.extend((0u8..=254).collect::<Vec<u8>>()); stream.extend([0x09, 0x01, 0xFF]); let items = Tlv8Reader::parse(&stream).unwrap();
assert_eq!(items.len(), 1);
assert_eq!(items[0].0, 0x09);
assert_eq!(items[0].1.len(), 256);
assert_eq!(items[0].1, (0..=u8::MAX).collect::<Vec<u8>>());
}
#[test]
fn parse_reassembles_exact_255_with_terminator() {
let mut stream = vec![0x09, 0xFF];
stream.extend(vec![0x42_u8; 255]);
stream.extend([0x09, 0x00]); let items = Tlv8Reader::parse(&stream).unwrap();
assert_eq!(items.len(), 1);
assert_eq!(items[0].0, 0x09);
assert_eq!(items[0].1, vec![0x42; 255]);
}
#[test]
fn parse_reassembles_510_byte_value() {
let mut stream = vec![0x09, 0xFF];
stream.extend(vec![0xAB_u8; 255]);
stream.extend([0x09, 0xFF]);
stream.extend(vec![0xAB_u8; 255]);
stream.extend([0x09, 0x00]); let items = Tlv8Reader::parse(&stream).unwrap();
assert_eq!(items.len(), 1);
assert_eq!(items[0].1, vec![0xAB; 510]);
}
#[test]
fn parse_does_not_merge_two_short_items_of_same_type() {
let items = Tlv8Reader::parse(&[0x09, 0x01, 0xAA, 0x09, 0x01, 0xBB]).unwrap();
assert_eq!(items, vec![(0x09, vec![0xAA]), (0x09, vec![0xBB])]);
}
#[test]
fn parse_keeps_separator_as_distinct_item() {
let stream = [0x01, 0x01, 0xAA, 0xFF, 0x00, 0x01, 0x01, 0xBB];
let items = Tlv8Reader::parse(&stream).unwrap();
assert_eq!(
items,
vec![(0x01, vec![0xAA]), (0xFF, vec![]), (0x01, vec![0xBB])]
);
}
}