1pub struct Tlv8Writer<'a> {
13 out: &'a mut Vec<u8>,
14}
15
16impl<'a> Tlv8Writer<'a> {
17 pub fn new(out: &'a mut Vec<u8>) -> Self {
19 Self { out }
20 }
21
22 pub fn push(&mut self, ty: u8, value: &[u8]) {
30 if value.is_empty() {
31 self.out.push(ty);
32 self.out.push(0);
33 return;
34 }
35 let mut last_chunk_len = 0usize;
36 for chunk in value.chunks(255) {
37 self.out.push(ty);
38 #[allow(clippy::cast_possible_truncation)]
40 self.out.push(chunk.len() as u8);
41 self.out.extend_from_slice(chunk);
42 last_chunk_len = chunk.len();
43 }
44 if last_chunk_len == 255 {
47 self.out.push(ty);
48 self.out.push(0);
49 }
50 }
51
52 pub fn push_u8(&mut self, ty: u8, v: u8) {
54 self.push(ty, &v.to_le_bytes());
55 }
56
57 pub fn push_u16(&mut self, ty: u8, v: u16) {
59 self.push(ty, &v.to_le_bytes());
60 }
61
62 pub fn push_u32(&mut self, ty: u8, v: u32) {
64 self.push(ty, &v.to_le_bytes());
65 }
66
67 pub fn push_u64(&mut self, ty: u8, v: u64) {
69 self.push(ty, &v.to_le_bytes());
70 }
71
72 pub fn push_str(&mut self, ty: u8, v: &str) {
74 self.push(ty, v.as_bytes());
75 }
76
77 pub fn push_separator(&mut self) {
80 self.out.push(crate::SEPARATOR);
81 self.out.push(0);
82 }
83}
84
85#[cfg(test)]
86#[allow(clippy::unwrap_used, clippy::expect_used)]
88mod tests {
89 use super::*;
90
91 #[test]
92 fn push_short_value_emits_type_len_value() {
93 let mut buf = Vec::new();
94 let mut w = Tlv8Writer::new(&mut buf);
95 w.push(0x01, &[0xAB, 0xCD]);
96 assert_eq!(buf, [0x01, 0x02, 0xAB, 0xCD]);
97 }
98
99 #[test]
100 fn push_empty_value_emits_zero_length_item() {
101 let mut buf = Vec::new();
102 let mut w = Tlv8Writer::new(&mut buf);
103 w.push(0x06, &[]);
104 assert_eq!(buf, [0x06, 0x00]);
105 }
106
107 #[test]
108 fn push_256_bytes_fragments_255_then_1() {
109 let mut buf = Vec::new();
110 let mut w = Tlv8Writer::new(&mut buf);
111 let value: Vec<u8> = (0..=u8::MAX).collect();
112 w.push(0x09, &value);
114 assert_eq!(&buf[0..2], &[0x09, 0xFF]);
116 assert_eq!(&buf[2..257], &(0u8..=254).collect::<Vec<u8>>()[..]);
118 assert_eq!(&buf[257..259], &[0x09, 0x01]);
120 assert_eq!(buf[259], 0xFF);
122 assert_eq!(buf.len(), 260);
123 }
124
125 #[test]
126 fn push_exactly_255_appends_terminating_zero_length_item() {
127 let mut buf = Vec::new();
128 let mut w = Tlv8Writer::new(&mut buf);
129 let value = vec![0x42_u8; 255];
130 w.push(0x09, &value);
131 assert_eq!(&buf[0..2], &[0x09, 0xFF]);
132 assert!(buf[2..257].iter().all(|&b| b == 0x42));
133 assert_eq!(&buf[257..259], &[0x09, 0x00]);
135 assert_eq!(buf.len(), 259);
136 }
137
138 #[test]
139 fn push_510_bytes_two_full_fragments_then_terminator() {
140 let mut buf = Vec::new();
141 let mut w = Tlv8Writer::new(&mut buf);
142 let value = vec![0xAB_u8; 510];
143 w.push(0x09, &value);
144 assert_eq!(&buf[0..2], &[0x09, 0xFF]);
145 assert_eq!(&buf[257..259], &[0x09, 0xFF]);
146 assert_eq!(&buf[514..516], &[0x09, 0x00]);
147 assert_eq!(buf.len(), 516);
148 }
149
150 #[test]
151 fn push_300_bytes_fragments_255_then_45() {
152 let mut buf = Vec::new();
153 let mut w = Tlv8Writer::new(&mut buf);
154 let value = vec![0x01_u8; 300];
155 w.push(0x09, &value);
156 assert_eq!(&buf[0..2], &[0x09, 0xFF]);
157 assert_eq!(&buf[257..259], &[0x09, 0x2D]);
159 assert_eq!(buf.len(), 2 + 255 + 2 + 45);
160 }
161
162 #[test]
163 fn push_u8_emits_one_le_byte() {
164 let mut buf = Vec::new();
165 let mut w = Tlv8Writer::new(&mut buf);
166 w.push_u8(0x02, 0x2A);
167 assert_eq!(buf, [0x02, 0x01, 0x2A]);
168 }
169
170 #[test]
171 fn push_u16_emits_two_le_bytes() {
172 let mut buf = Vec::new();
173 let mut w = Tlv8Writer::new(&mut buf);
174 w.push_u16(0x03, 0x1234);
175 assert_eq!(buf, [0x03, 0x02, 0x34, 0x12]);
176 }
177
178 #[test]
179 fn push_u32_emits_four_le_bytes() {
180 let mut buf = Vec::new();
181 let mut w = Tlv8Writer::new(&mut buf);
182 w.push_u32(0x04, 0xCAFE_BABE);
183 assert_eq!(buf, [0x04, 0x04, 0xBE, 0xBA, 0xFE, 0xCA]);
184 }
185
186 #[test]
187 fn push_u64_emits_eight_le_bytes() {
188 let mut buf = Vec::new();
189 let mut w = Tlv8Writer::new(&mut buf);
190 w.push_u64(0x05, 0x0123_4567_89AB_CDEF);
191 assert_eq!(
192 buf,
193 [0x05, 0x08, 0xEF, 0xCD, 0xAB, 0x89, 0x67, 0x45, 0x23, 0x01]
194 );
195 }
196
197 #[test]
198 fn push_str_emits_utf8_bytes() {
199 let mut buf = Vec::new();
200 let mut w = Tlv8Writer::new(&mut buf);
201 w.push_str(0x07, "Pair");
202 assert_eq!(buf, [0x07, 0x04, 0x50, 0x61, 0x69, 0x72]);
204 }
205
206 #[test]
207 fn push_separator_emits_ff_zero() {
208 let mut buf = Vec::new();
209 let mut w = Tlv8Writer::new(&mut buf);
210 w.push_separator();
211 assert_eq!(buf, [0xFF, 0x00]);
212 }
213
214 #[test]
215 fn write_then_parse_round_trips_with_separator_and_fragment() {
216 use crate::Tlv8Reader;
217 let big = vec![0x07_u8; 300];
218 let mut buf = Vec::new();
219 {
220 let mut w = Tlv8Writer::new(&mut buf);
221 w.push(0x01, &[0xAA]);
222 w.push_separator();
223 w.push(0x01, &big);
224 }
225 let items = Tlv8Reader::parse(&buf).unwrap();
226 assert_eq!(items, vec![(0x01, vec![0xAA]), (0xFF, vec![]), (0x01, big)]);
227 }
228}