pub struct Tlv8Writer<'a> {
out: &'a mut Vec<u8>,
}
impl<'a> Tlv8Writer<'a> {
pub fn new(out: &'a mut Vec<u8>) -> Self {
Self { out }
}
pub fn push(&mut self, ty: u8, value: &[u8]) {
if value.is_empty() {
self.out.push(ty);
self.out.push(0);
return;
}
let mut last_chunk_len = 0usize;
for chunk in value.chunks(255) {
self.out.push(ty);
#[allow(clippy::cast_possible_truncation)]
self.out.push(chunk.len() as u8);
self.out.extend_from_slice(chunk);
last_chunk_len = chunk.len();
}
if last_chunk_len == 255 {
self.out.push(ty);
self.out.push(0);
}
}
pub fn push_u8(&mut self, ty: u8, v: u8) {
self.push(ty, &v.to_le_bytes());
}
pub fn push_u16(&mut self, ty: u8, v: u16) {
self.push(ty, &v.to_le_bytes());
}
pub fn push_u32(&mut self, ty: u8, v: u32) {
self.push(ty, &v.to_le_bytes());
}
pub fn push_u64(&mut self, ty: u8, v: u64) {
self.push(ty, &v.to_le_bytes());
}
pub fn push_str(&mut self, ty: u8, v: &str) {
self.push(ty, v.as_bytes());
}
pub fn push_separator(&mut self) {
self.out.push(crate::SEPARATOR);
self.out.push(0);
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used)]
mod tests {
use super::*;
#[test]
fn push_short_value_emits_type_len_value() {
let mut buf = Vec::new();
let mut w = Tlv8Writer::new(&mut buf);
w.push(0x01, &[0xAB, 0xCD]);
assert_eq!(buf, [0x01, 0x02, 0xAB, 0xCD]);
}
#[test]
fn push_empty_value_emits_zero_length_item() {
let mut buf = Vec::new();
let mut w = Tlv8Writer::new(&mut buf);
w.push(0x06, &[]);
assert_eq!(buf, [0x06, 0x00]);
}
#[test]
fn push_256_bytes_fragments_255_then_1() {
let mut buf = Vec::new();
let mut w = Tlv8Writer::new(&mut buf);
let value: Vec<u8> = (0..=u8::MAX).collect();
w.push(0x09, &value);
assert_eq!(&buf[0..2], &[0x09, 0xFF]);
assert_eq!(&buf[2..257], &(0u8..=254).collect::<Vec<u8>>()[..]);
assert_eq!(&buf[257..259], &[0x09, 0x01]);
assert_eq!(buf[259], 0xFF);
assert_eq!(buf.len(), 260);
}
#[test]
fn push_exactly_255_appends_terminating_zero_length_item() {
let mut buf = Vec::new();
let mut w = Tlv8Writer::new(&mut buf);
let value = vec![0x42_u8; 255];
w.push(0x09, &value);
assert_eq!(&buf[0..2], &[0x09, 0xFF]);
assert!(buf[2..257].iter().all(|&b| b == 0x42));
assert_eq!(&buf[257..259], &[0x09, 0x00]);
assert_eq!(buf.len(), 259);
}
#[test]
fn push_510_bytes_two_full_fragments_then_terminator() {
let mut buf = Vec::new();
let mut w = Tlv8Writer::new(&mut buf);
let value = vec![0xAB_u8; 510];
w.push(0x09, &value);
assert_eq!(&buf[0..2], &[0x09, 0xFF]);
assert_eq!(&buf[257..259], &[0x09, 0xFF]);
assert_eq!(&buf[514..516], &[0x09, 0x00]);
assert_eq!(buf.len(), 516);
}
#[test]
fn push_300_bytes_fragments_255_then_45() {
let mut buf = Vec::new();
let mut w = Tlv8Writer::new(&mut buf);
let value = vec![0x01_u8; 300];
w.push(0x09, &value);
assert_eq!(&buf[0..2], &[0x09, 0xFF]);
assert_eq!(&buf[257..259], &[0x09, 0x2D]);
assert_eq!(buf.len(), 2 + 255 + 2 + 45);
}
#[test]
fn push_u8_emits_one_le_byte() {
let mut buf = Vec::new();
let mut w = Tlv8Writer::new(&mut buf);
w.push_u8(0x02, 0x2A);
assert_eq!(buf, [0x02, 0x01, 0x2A]);
}
#[test]
fn push_u16_emits_two_le_bytes() {
let mut buf = Vec::new();
let mut w = Tlv8Writer::new(&mut buf);
w.push_u16(0x03, 0x1234);
assert_eq!(buf, [0x03, 0x02, 0x34, 0x12]);
}
#[test]
fn push_u32_emits_four_le_bytes() {
let mut buf = Vec::new();
let mut w = Tlv8Writer::new(&mut buf);
w.push_u32(0x04, 0xCAFE_BABE);
assert_eq!(buf, [0x04, 0x04, 0xBE, 0xBA, 0xFE, 0xCA]);
}
#[test]
fn push_u64_emits_eight_le_bytes() {
let mut buf = Vec::new();
let mut w = Tlv8Writer::new(&mut buf);
w.push_u64(0x05, 0x0123_4567_89AB_CDEF);
assert_eq!(
buf,
[0x05, 0x08, 0xEF, 0xCD, 0xAB, 0x89, 0x67, 0x45, 0x23, 0x01]
);
}
#[test]
fn push_str_emits_utf8_bytes() {
let mut buf = Vec::new();
let mut w = Tlv8Writer::new(&mut buf);
w.push_str(0x07, "Pair");
assert_eq!(buf, [0x07, 0x04, 0x50, 0x61, 0x69, 0x72]);
}
#[test]
fn push_separator_emits_ff_zero() {
let mut buf = Vec::new();
let mut w = Tlv8Writer::new(&mut buf);
w.push_separator();
assert_eq!(buf, [0xFF, 0x00]);
}
#[test]
fn write_then_parse_round_trips_with_separator_and_fragment() {
use crate::Tlv8Reader;
let big = vec![0x07_u8; 300];
let mut buf = Vec::new();
{
let mut w = Tlv8Writer::new(&mut buf);
w.push(0x01, &[0xAA]);
w.push_separator();
w.push(0x01, &big);
}
let items = Tlv8Reader::parse(&buf).unwrap();
assert_eq!(items, vec![(0x01, vec![0xAA]), (0xFF, vec![]), (0x01, big)]);
}
}