1#[cfg(not(feature = "std"))]
2use alloc::vec::Vec;
3
4use bytes::{BufMut, BytesMut};
5
6use crate::varu64_size;
7
8pub struct TlvWriter {
10 buf: BytesMut,
11}
12
13impl TlvWriter {
14 pub fn new() -> Self {
15 Self {
16 buf: BytesMut::new(),
17 }
18 }
19
20 pub fn with_capacity(cap: usize) -> Self {
21 Self {
22 buf: BytesMut::with_capacity(cap),
23 }
24 }
25
26 fn write_varu64_inner(&mut self, value: u64) {
27 let mut tmp = [0u8; 9];
28 let n = crate::write_varu64(&mut tmp, value);
29 self.buf.put_slice(&tmp[..n]);
30 }
31
32 pub fn write_tlv(&mut self, typ: u64, value: &[u8]) {
34 self.write_varu64_inner(typ);
35 self.write_varu64_inner(value.len() as u64);
36 self.buf.put_slice(value);
37 }
38
39 pub fn write_nested<F>(&mut self, typ: u64, f: F)
45 where
46 F: FnOnce(&mut TlvWriter),
47 {
48 let mut inner = TlvWriter::new();
49 f(&mut inner);
50 let inner_bytes = inner.buf;
51
52 self.write_varu64_inner(typ);
53 self.write_varu64_inner(inner_bytes.len() as u64);
54 self.buf.put_slice(&inner_bytes);
55 }
56
57 pub fn write_varu64(&mut self, value: u64) {
59 self.write_varu64_inner(value);
60 }
61
62 pub fn write_raw(&mut self, data: &[u8]) {
66 self.buf.put_slice(data);
67 }
68
69 pub fn slice_from(&self, start: usize) -> &[u8] {
74 &self.buf[start..]
75 }
76
77 #[deprecated(note = "use slice_from for a zero-copy &[u8]")]
81 pub fn snapshot(&self, start: usize) -> Vec<u8> {
82 self.buf[start..].to_vec()
83 }
84
85 pub fn finish(self) -> bytes::Bytes {
87 self.buf.freeze()
88 }
89
90 pub fn len(&self) -> usize {
92 self.buf.len()
93 }
94
95 pub fn is_empty(&self) -> bool {
96 self.buf.is_empty()
97 }
98}
99
100impl Default for TlvWriter {
101 fn default() -> Self {
102 Self::new()
103 }
104}
105
106pub fn tlv_size(typ: u64, value_len: usize) -> usize {
108 varu64_size(typ) + varu64_size(value_len as u64) + value_len
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114 use crate::TlvReader;
115
116 #[test]
119 fn write_tlv_empty_value() {
120 let mut w = TlvWriter::new();
121 w.write_tlv(0x21, &[]);
122 let bytes = w.finish();
123 assert_eq!(bytes.as_ref(), &[0x21, 0x00]);
124 }
125
126 #[test]
127 fn write_tlv_with_value() {
128 let mut w = TlvWriter::new();
129 w.write_tlv(0x08, b"ndn");
130 let bytes = w.finish();
131 assert_eq!(bytes.as_ref(), &[0x08, 0x03, b'n', b'd', b'n']);
132 }
133
134 #[test]
135 fn write_tlv_3byte_type() {
136 let mut w = TlvWriter::new();
137 w.write_tlv(0x0320, &[0xAB]);
138 let bytes = w.finish();
139 assert_eq!(bytes.as_ref(), &[0xFD, 0x03, 0x20, 0x01, 0xAB]);
141 }
142
143 #[test]
144 fn write_tlv_roundtrip() {
145 let payload = b"hello world";
146 let mut w = TlvWriter::new();
147 w.write_tlv(0x15, payload);
148 let bytes = w.finish();
149
150 let mut r = TlvReader::new(bytes);
151 let (typ, val) = r.read_tlv().unwrap();
152 assert_eq!(typ, 0x15);
153 assert_eq!(val.as_ref(), payload);
154 assert!(r.is_empty());
155 }
156
157 #[test]
158 fn write_multiple_tlvs() {
159 let mut w = TlvWriter::new();
160 w.write_tlv(0x07, b"name");
161 w.write_tlv(0x15, b"content");
162 let bytes = w.finish();
163
164 let mut r = TlvReader::new(bytes);
165 let (t1, v1) = r.read_tlv().unwrap();
166 let (t2, v2) = r.read_tlv().unwrap();
167 assert_eq!(t1, 0x07);
168 assert_eq!(v1.as_ref(), b"name");
169 assert_eq!(t2, 0x15);
170 assert_eq!(v2.as_ref(), b"content");
171 assert!(r.is_empty());
172 }
173
174 #[test]
177 fn write_nested_empty_inner() {
178 let mut w = TlvWriter::new();
179 w.write_nested(0x07, |_| {});
180 let bytes = w.finish();
181
182 let mut r = TlvReader::new(bytes);
184 let (typ, val) = r.read_tlv().unwrap();
185 assert_eq!(typ, 0x07);
186 assert_eq!(val.len(), 0);
187 }
188
189 #[test]
190 fn write_nested_with_inner_tlvs() {
191 let mut w = TlvWriter::new();
192 w.write_nested(0x07, |inner| {
193 inner.write_tlv(0x08, b"foo");
194 inner.write_tlv(0x08, b"bar");
195 });
196 let bytes = w.finish();
197
198 let mut r = TlvReader::new(bytes);
199 let (typ, val) = r.read_tlv().unwrap();
200 assert_eq!(typ, 0x07);
201
202 let mut inner = TlvReader::new(val);
203 let (t1, v1) = inner.read_tlv().unwrap();
204 let (t2, v2) = inner.read_tlv().unwrap();
205 assert_eq!(t1, 0x08);
206 assert_eq!(v1.as_ref(), b"foo");
207 assert_eq!(t2, 0x08);
208 assert_eq!(v2.as_ref(), b"bar");
209 assert!(inner.is_empty());
210 }
211
212 #[test]
213 fn write_nested_three_levels() {
214 let mut w = TlvWriter::new();
215 w.write_nested(0x05, |outer| {
216 outer.write_nested(0x07, |name| {
217 name.write_tlv(0x08, b"test");
218 });
219 });
220 let bytes = w.finish();
221
222 let mut r = TlvReader::new(bytes);
223 let (t0, v0) = r.read_tlv().unwrap();
224 assert_eq!(t0, 0x05);
225 let mut r1 = TlvReader::new(v0);
226 let (t1, v1) = r1.read_tlv().unwrap();
227 assert_eq!(t1, 0x07);
228 let mut r2 = TlvReader::new(v1);
229 let (t2, v2) = r2.read_tlv().unwrap();
230 assert_eq!(t2, 0x08);
231 assert_eq!(v2.as_ref(), b"test");
232 }
233
234 #[test]
237 fn tlv_size_matches_write_tlv_output() {
238 let cases: &[(u64, &[u8])] = &[(0x08, b"hello"), (0x0320, &[0xAB, 0xCD]), (0x21, &[])];
239 for &(typ, value) in cases {
240 let mut w = TlvWriter::new();
241 w.write_tlv(typ, value);
242 let expected_size = tlv_size(typ, value.len());
243 assert_eq!(
244 w.len(),
245 expected_size,
246 "typ={typ:#x} value_len={}",
247 value.len()
248 );
249 }
250 }
251
252 #[test]
255 fn writer_starts_empty() {
256 let w = TlvWriter::new();
257 assert!(w.is_empty());
258 assert_eq!(w.len(), 0);
259 }
260
261 #[test]
262 fn with_capacity_works_same_as_new() {
263 let mut w = TlvWriter::with_capacity(64);
264 w.write_tlv(0x08, b"hi");
265 assert_eq!(w.len(), 4);
266 }
267}