rs_matter/tlv/toiter.rs
1/*
2 *
3 * Copyright (c) 2024-2026 Project CHIP Authors
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18use core::iter::{Chain, Once};
19
20use crate::error::{Error, ErrorCode};
21
22use super::{OnceTLVIter, TLVTag, TLVValue, TLVValueType, TLV};
23
24type TLVResult<'a> = Result<TLV<'a>, Error>;
25type ChainedTLVIter<'a, C> = Chain<C, OnceTLVIter<'a>>;
26
27/// A decorator trait for serializing data as TLV in the form of an
28/// `Iterator` of `Result<TLV<'a>, Error>` bytes.
29///
30/// The trait provides additional combinators on top of the standard `Iterator`
31/// trait combinators (e.g. `map`, `filter`, `flat_map`, etc.) that allow for serializing TLV elements.
32///
33/// The trait is already implemented for any `Iterator` that yields items of type `Result<TLV<'a>, Error>`,
34/// so users are not expected to provide implementations of it.
35///
36/// Using an Iterator approach to TLV serialization is useful when the data is not serialized to its
37/// final location (be it in the storage or in an outgoing network packet) - but rather - is serialized
38/// so that it is afterwards consumed as a stream of bytes by another component - say - a hash signature
39/// algorithm that operates on the TLV representation of the data.
40///
41/// This way, the need for an interim buffer for the serialized TLV data might be avoided.
42///
43/// NOTE:
44/// Keep in mind that the resulting iterator might quickly become rather large if the serialized
45/// TLV data contains many small TLV elements, as each TLV element is represented as multiple compositions
46/// of the Rust `Iterator` combinators (e.g. `chain`, `map`, `flat_map`, etc.), and - moreover -
47/// the size of each `TLV` itself is rather large (~ 32 bytes on 32bit archs).
48///
49/// Therefore, the iterator TLV serialization is only useful when the serialized TLV data contains few but
50/// large non-container TLV elements, like octet strings or utf8 strings
51/// (which is typical for e.g. TLV-encoded certificates).
52///
53/// For other cases, allocating a temporary memory buffer and serializing into it with `TLVWrite` might result
54/// in less memory overhead (and better performance when reading the raw serialized TLV data) by the code that
55/// operates on it.
56pub trait TLVIter<'a>: Iterator<Item = TLVResult<'a>> + Sized {
57 fn flatten(value: Result<Self, Error>) -> EitherIter<Self, Once<TLVResult<'a>>> {
58 match value {
59 Ok(value) => EitherIter::First(value),
60 Err(err) => EitherIter::Second(core::iter::once(Err(err))),
61 }
62 }
63
64 /// Serialize a TLV tag and value.
65 fn tlv(self, tag: TLVTag, value: TLVValue<'a>) -> ChainedTLVIter<'a, Self>
66 where
67 Self: 'a,
68 {
69 self.chain(TLV::new(tag, value).into_tlv_iter())
70 }
71
72 /// Serialize the given tag and the provided value as an S8 TLV value.
73 fn i8(self, tag: TLVTag, data: i8) -> ChainedTLVIter<'a, Self>
74 where
75 Self: 'a,
76 {
77 self.chain(TLV::i8(tag, data).into_tlv_iter())
78 }
79
80 /// Serialize the given tag and the provided value as a U8 TLV value.
81 fn u8(self, tag: TLVTag, data: u8) -> ChainedTLVIter<'a, Self>
82 where
83 Self: 'a,
84 {
85 self.chain(TLV::u8(tag, data).into_tlv_iter())
86 }
87
88 /// Serialize the given tag and the provided value as an S16 TLV value,
89 /// or as an S8 TLV value if the provided data can fit in the S8 domain range.
90 fn i16(self, tag: TLVTag, data: i16) -> ChainedTLVIter<'a, Self>
91 where
92 Self: 'a,
93 {
94 self.chain(TLV::i16(tag, data).into_tlv_iter())
95 }
96
97 /// Serialize the given tag and the provided value as a U16 TLV value,
98 /// or as a U8 TLV value if the provided data can fit in the U8 domain range.
99 fn u16(self, tag: TLVTag, data: u16) -> ChainedTLVIter<'a, Self>
100 where
101 Self: 'a,
102 {
103 self.chain(TLV::u16(tag, data).into_tlv_iter())
104 }
105
106 /// Serialize the given tag and the provided value as an S32 TLV value,
107 /// or as an S16 / S8 TLV value if the provided data can fit in a smaller domain range.
108 fn i32(self, tag: TLVTag, data: i32) -> ChainedTLVIter<'a, Self>
109 where
110 Self: 'a,
111 {
112 self.chain(TLV::i32(tag, data).into_tlv_iter())
113 }
114
115 /// Serialize the given tag and the provided value as a U32 TLV value,
116 /// or as a U16 / U8 TLV value if the provided data can fit in a smaller domain range.
117 fn u32(self, tag: TLVTag, data: u32) -> ChainedTLVIter<'a, Self>
118 where
119 Self: 'a,
120 {
121 self.chain(TLV::u32(tag, data).into_tlv_iter())
122 }
123
124 /// Serialize the given tag and the provided value as an S64 TLV value,
125 /// or as an S32 / S16 / S8 TLV value if the provided data can fit in a smaller domain range.
126 fn i64(self, tag: TLVTag, data: i64) -> ChainedTLVIter<'a, Self>
127 where
128 Self: 'a,
129 {
130 self.chain(TLV::i64(tag, data).into_tlv_iter())
131 }
132
133 /// Serialize the given tag and the provided value as a U64 TLV value,
134 /// or as a U32 / U16 / U8 TLV value if the provided data can fit in a smaller domain range.
135 fn u64(self, tag: TLVTag, data: u64) -> ChainedTLVIter<'a, Self>
136 where
137 Self: 'a,
138 {
139 self.chain(TLV::u64(tag, data).into_tlv_iter())
140 }
141
142 /// Serialize the given tag and the provided value as an F32 TLV value.
143 fn f32(self, tag: TLVTag, data: f32) -> ChainedTLVIter<'a, Self>
144 where
145 Self: 'a,
146 {
147 self.chain(TLV::f32(tag, data).into_tlv_iter())
148 }
149
150 /// Serialize the given tag and the provided value as an F64 TLV value.
151 fn f64(self, tag: TLVTag, data: f64) -> ChainedTLVIter<'a, Self>
152 where
153 Self: 'a,
154 {
155 self.chain(TLV::f64(tag, data).into_tlv_iter())
156 }
157
158 /// Serialize the given tag and the provided value as a TLV Octet String.
159 ///
160 /// The exact octet string type (Str8l, Str16l, Str32l, or Str64l) is chosen based on the length of the data,
161 /// whereas the smallest type filling the provided data length is chosen.
162 fn str(self, tag: TLVTag, data: &'a [u8]) -> ChainedTLVIter<'a, Self>
163 where
164 Self: 'a,
165 {
166 self.chain(TLV::str(tag, data).into_tlv_iter())
167 }
168
169 /// Serialize the given tag and the provided value as a TLV UTF-8 String.
170 ///
171 /// The exact UTF-8 string type (Utf8l, Utf16l, Utf32l, or Utf64l) is chosen based on the length of the data,
172 /// whereas the smallest type filling the provided data length is chosen.
173 fn utf8(self, tag: TLVTag, data: &'a str) -> ChainedTLVIter<'a, Self>
174 where
175 Self: 'a,
176 {
177 self.chain(TLV::utf8(tag, data).into_tlv_iter())
178 }
179
180 /// Serialize the given tag and a value indicating the start of a Struct TLV container.
181 ///
182 /// NOTE: The user must call `end_container` after serializing all the Struct fields
183 /// to close the Struct container or else the generated TLV stream will be invalid.
184 fn start_struct(self, tag: TLVTag) -> ChainedTLVIter<'a, Self>
185 where
186 Self: 'a,
187 {
188 self.chain(TLV::structure(tag).into_tlv_iter())
189 }
190
191 /// Serialize the given tag and a value indicating the start of an Array TLV container.
192 ///
193 /// NOTE: The user must call `end_container` after serializing all the Array elements
194 /// to close the Array container or else the generated TLV stream will be invalid.
195 fn start_array(self, tag: TLVTag) -> ChainedTLVIter<'a, Self>
196 where
197 Self: 'a,
198 {
199 self.chain(TLV::array(tag).into_tlv_iter())
200 }
201
202 /// Serialize the given tag and a value indicating the start of a List TLV container.
203 ///
204 /// NOTE: The user must call `end_container` after serializing all the List elements
205 /// to close the List container or else the generated TLV stream will be invalid.
206 fn start_list(self, tag: TLVTag) -> ChainedTLVIter<'a, Self>
207 where
208 Self: 'a,
209 {
210 self.chain(TLV::list(tag).into_tlv_iter())
211 }
212
213 /// Serialize the given tag and a value indicating the start of a TLV container.
214 ///
215 /// NOTE: The user must call `end_container` after serializing all the container fields
216 /// to close the Struct container or else the generated TLV stream will be invalid.
217 fn start_container(self, tag: TLVTag, container_type: TLVValueType) -> ChainedTLVIter<'a, Self>
218 where
219 Self: 'a,
220 {
221 match container_type {
222 TLVValueType::Struct => self.start_struct(tag),
223 TLVValueType::Array => self.start_array(tag),
224 TLVValueType::List => self.start_list(tag),
225 _ => self.chain(core::iter::once(Err(ErrorCode::TLVTypeMismatch.into()))),
226 }
227 }
228
229 /// Serialize a value indicating the end of a Struct, Array, or List TLV container.
230 ///
231 /// NOTE: This method must be called only when the corresponding container has been opened
232 /// using `start_struct`, `start_array`, or `start_list`, or else the generated TLV stream will be invalid.
233 fn end_container(self) -> ChainedTLVIter<'a, Self>
234 where
235 Self: 'a,
236 {
237 self.chain(TLV::end_container().into_tlv_iter())
238 }
239
240 /// Serialize the given tag and a value indicating a Null TLV value.
241 fn null(self, tag: TLVTag) -> ChainedTLVIter<'a, Self>
242 where
243 Self: 'a,
244 {
245 self.chain(TLV::null(tag).into_tlv_iter())
246 }
247
248 /// Serialize the given tag and a value indicating a True or False TLV value.
249 fn bool(self, tag: TLVTag, data: bool) -> ChainedTLVIter<'a, Self>
250 where
251 Self: 'a,
252 {
253 self.chain(TLV::bool(tag, data).into_tlv_iter())
254 }
255}
256
257impl<'a, T> TLVIter<'a> for T where T: Iterator<Item = TLVResult<'a>> {}
258
259/// A decorator enum type wrapping two iterators and implementing
260/// the `Iterator` trait.
261///
262/// Useful when the "to-tlv-iter" implementation needs to return
263/// one of two iterators based on some condition.
264#[derive(Clone)]
265pub enum EitherIter<F, S> {
266 First(F),
267 Second(S),
268}
269
270impl<F, S> Iterator for EitherIter<F, S>
271where
272 F: Iterator,
273 S: Iterator<Item = F::Item>,
274{
275 type Item = <F as Iterator>::Item;
276
277 fn next(&mut self) -> Option<Self::Item> {
278 match self {
279 Self::First(i) => i.next(),
280 Self::Second(i) => i.next(),
281 }
282 }
283}
284
285/// A decorator enum type wrapping three iterators and implementing
286/// the `Iterator` trait.
287///
288/// Useful when the "to-tlv-iter" implementation needs to return
289/// one of three iterators based on some condition.
290#[derive(Clone)]
291pub enum Either3Iter<F, S, T> {
292 First(F),
293 Second(S),
294 Third(T),
295}
296
297impl<F, S, T> Iterator for Either3Iter<F, S, T>
298where
299 F: Iterator,
300 S: Iterator<Item = F::Item>,
301 T: Iterator<Item = F::Item>,
302{
303 type Item = <F as Iterator>::Item;
304
305 fn next(&mut self) -> Option<Self::Item> {
306 match self {
307 Self::First(i) => i.next(),
308 Self::Second(i) => i.next(),
309 Self::Third(i) => i.next(),
310 }
311 }
312}
313
314/// A decorator enum type wrapping four iterators and implementing
315/// the `Iterator` trait.
316///
317/// Useful when the "to-tlv-iter" implementation needs to return
318/// one of four iterators based on some condition.
319#[derive(Clone)]
320pub enum Either4Iter<F, S, T, U> {
321 First(F),
322 Second(S),
323 Third(T),
324 Fourth(U),
325}
326
327impl<F, S, T, U> Iterator for Either4Iter<F, S, T, U>
328where
329 F: Iterator,
330 S: Iterator<Item = F::Item>,
331 T: Iterator<Item = F::Item>,
332 U: Iterator<Item = F::Item>,
333{
334 type Item = <F as Iterator>::Item;
335
336 fn next(&mut self) -> Option<Self::Item> {
337 match self {
338 Self::First(i) => i.next(),
339 Self::Second(i) => i.next(),
340 Self::Third(i) => i.next(),
341 Self::Fourth(i) => i.next(),
342 }
343 }
344}
345
346/// A decorator enum type wrapping five iterators and implementing
347/// the `Iterator` trait.
348///
349/// Useful when the "to-tlv-iter" implementation needs to return
350/// one of five iterators based on some condition.
351#[derive(Clone)]
352pub enum Either5Iter<F, S, T, U, I> {
353 First(F),
354 Second(S),
355 Third(T),
356 Fourth(U),
357 Fifth(I),
358}
359
360impl<F, S, T, U, I> Iterator for Either5Iter<F, S, T, U, I>
361where
362 F: Iterator,
363 S: Iterator<Item = F::Item>,
364 T: Iterator<Item = F::Item>,
365 U: Iterator<Item = F::Item>,
366 I: Iterator<Item = F::Item>,
367{
368 type Item = <F as Iterator>::Item;
369
370 fn next(&mut self) -> Option<Self::Item> {
371 match self {
372 Self::First(i) => i.next(),
373 Self::Second(i) => i.next(),
374 Self::Third(i) => i.next(),
375 Self::Fourth(i) => i.next(),
376 Self::Fifth(i) => i.next(),
377 }
378 }
379}
380
381/// A decorator enum type wrapping six iterators and implementing
382/// the `Iterator` trait.
383///
384/// Useful when the "to-tlv-iter" implementation needs to return
385/// one of six iterators based on some condition.
386#[derive(Clone)]
387pub enum Either6Iter<F, S, T, U, I, X> {
388 First(F),
389 Second(S),
390 Third(T),
391 Fourth(U),
392 Fifth(I),
393 Sixth(X),
394}
395
396impl<F, S, T, U, I, X> Iterator for Either6Iter<F, S, T, U, I, X>
397where
398 F: Iterator,
399 S: Iterator<Item = F::Item>,
400 T: Iterator<Item = F::Item>,
401 U: Iterator<Item = F::Item>,
402 I: Iterator<Item = F::Item>,
403 X: Iterator<Item = F::Item>,
404{
405 type Item = <F as Iterator>::Item;
406
407 fn next(&mut self) -> Option<Self::Item> {
408 match self {
409 Self::First(i) => i.next(),
410 Self::Second(i) => i.next(),
411 Self::Third(i) => i.next(),
412 Self::Fourth(i) => i.next(),
413 Self::Fifth(i) => i.next(),
414 Self::Sixth(i) => i.next(),
415 }
416 }
417}
418
419#[cfg(test)]
420mod tests {
421 use core::{f32, iter::empty};
422
423 use crate::tlv::TLV;
424
425 use super::{TLVIter, TLVResult, TLVTag};
426
427 fn expect<'a, I>(iter: I, expected: &[u8])
428 where
429 I: Iterator<Item = TLVResult<'a>>,
430 {
431 let mut iter = iter.map(|r| r.unwrap()).flat_map(TLV::into_bytes_iter);
432 let mut expected = expected.iter().copied();
433
434 loop {
435 match (iter.next(), expected.next()) {
436 (Some(a), Some(b)) => assert_eq!(a, b),
437 (None, None) => break,
438 (Some(_), None) => panic!("Iterator has more bytes than expected"),
439 (None, Some(_)) => panic!("Iterator has fewer bytes than expected"),
440 }
441 }
442 }
443
444 #[test]
445 fn test_write_success() {
446 expect(
447 empty()
448 .start_struct(TLVTag::Anonymous)
449 .u8(TLVTag::Anonymous, 12)
450 .u8(TLVTag::Context(1), 13)
451 .u16(TLVTag::Anonymous, 0x1212)
452 .u16(TLVTag::Context(2), 0x1313)
453 .start_array(TLVTag::Context(3))
454 .bool(TLVTag::Anonymous, true)
455 .end_container()
456 .end_container(),
457 &[
458 21, 4, 12, 36, 1, 13, 5, 0x12, 0x012, 37, 2, 0x13, 0x13, 54, 3, 9, 24, 24,
459 ],
460 );
461 }
462
463 #[test]
464 fn test_put_str8() {
465 expect(
466 empty()
467 .u8(TLVTag::Context(1), 13)
468 .str(TLVTag::Anonymous, &[10, 11, 12, 13, 14])
469 .u16(TLVTag::Context(2), 0x1313)
470 .str(TLVTag::Context(3), &[20, 21, 22]),
471 &[
472 36, 1, 13, 16, 5, 10, 11, 12, 13, 14, 37, 2, 0x13, 0x13, 48, 3, 3, 20, 21, 22,
473 ],
474 );
475 }
476
477 #[test]
478 fn test_matter_spec_examples() {
479 // Boolean false
480
481 expect(empty().bool(TLVTag::Anonymous, false), &[0x08]);
482
483 // Boolean true
484
485 expect(empty().bool(TLVTag::Anonymous, true), &[0x09]);
486
487 // Signed Integer, 1-octet, value 42
488
489 expect(empty().i8(TLVTag::Anonymous, 42), &[0x00, 0x2a]);
490
491 // Signed Integer, 1-octet, value -17
492
493 expect(empty().i8(TLVTag::Anonymous, -17), &[0x00, 0xef]);
494
495 // Unsigned Integer, 1-octet, value 42U
496
497 expect(empty().u8(TLVTag::Anonymous, 42), &[0x04, 0x2a]);
498
499 // Signed Integer, 2-octet, value 422
500
501 expect(empty().i16(TLVTag::Anonymous, 422), &[0x01, 0xa6, 0x01]);
502
503 // Signed Integer, 4-octet, value -170000
504
505 expect(
506 empty().i64(TLVTag::Anonymous, -170000),
507 &[0x02, 0xf0, 0x67, 0xfd, 0xff],
508 );
509
510 // Signed Integer, 8-octet, value 40000000000
511
512 expect(
513 empty().i64(TLVTag::Anonymous, 40000000000),
514 &[0x03, 0x00, 0x90, 0x2f, 0x50, 0x09, 0x00, 0x00, 0x00],
515 );
516
517 // UTF-8 String, 1-octet length, "Hello!"
518
519 expect(
520 empty().utf8(TLVTag::Anonymous, "Hello!"),
521 &[0x0c, 0x06, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x21],
522 );
523
524 // UTF-8 String, 1-octet length, "Tschüs"
525
526 expect(
527 empty().utf8(TLVTag::Anonymous, "Tschüs"),
528 &[0x0c, 0x07, 0x54, 0x73, 0x63, 0x68, 0xc3, 0xbc, 0x73],
529 );
530
531 // Octet String, 1-octet length, octets 00 01 02 03 04
532
533 expect(
534 empty().str(TLVTag::Anonymous, &[0x00, 0x01, 0x02, 0x03, 0x04]),
535 &[0x10, 0x05, 0x00, 0x01, 0x02, 0x03, 0x04],
536 );
537
538 // Null
539
540 expect(empty().null(TLVTag::Anonymous), &[0x14]);
541
542 // Single precision floating point 0.0
543
544 expect(
545 empty().f32(TLVTag::Anonymous, 0.0),
546 &[0x0a, 0x00, 0x00, 0x00, 0x00],
547 );
548
549 // Single precision floating point (1.0 / 3.0)
550
551 expect(
552 empty().f32(TLVTag::Anonymous, 1.0 / 3.0),
553 &[0x0a, 0xab, 0xaa, 0xaa, 0x3e],
554 );
555
556 // Single precision floating point 17.9
557
558 expect(
559 empty().f32(TLVTag::Anonymous, 17.9),
560 &[0x0a, 0x33, 0x33, 0x8f, 0x41],
561 );
562
563 // Single precision floating point infinity
564
565 expect(
566 empty().f32(TLVTag::Anonymous, f32::INFINITY),
567 &[0x0a, 0x00, 0x00, 0x80, 0x7f],
568 );
569
570 // Single precision floating point negative infinity
571
572 expect(
573 empty().f32(TLVTag::Anonymous, f32::NEG_INFINITY),
574 &[0x0a, 0x00, 0x00, 0x80, 0xff],
575 );
576
577 // Double precision floating point 0.0
578
579 expect(
580 empty().f64(TLVTag::Anonymous, 0.0),
581 &[0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
582 );
583
584 // Double precision floating point (1.0 / 3.0)
585
586 expect(
587 empty().f64(TLVTag::Anonymous, 1.0 / 3.0),
588 &[0x0b, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xd5, 0x3f],
589 );
590
591 // Double precision floating point 17.9
592
593 expect(
594 empty().f64(TLVTag::Anonymous, 17.9),
595 &[0x0b, 0x66, 0x66, 0x66, 0x66, 0x66, 0xe6, 0x31, 0x40],
596 );
597
598 // Double precision floating point infinity (∞)
599
600 expect(
601 empty().f64(TLVTag::Anonymous, f64::INFINITY),
602 &[0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x7f],
603 );
604
605 // Double precision floating point negative infinity
606
607 expect(
608 empty().f64(TLVTag::Anonymous, f64::NEG_INFINITY),
609 &[0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xff],
610 );
611
612 // Empty Structure, {}
613
614 expect(
615 empty().start_struct(TLVTag::Anonymous).end_container(),
616 &[0x15, 0x18],
617 );
618
619 // Empty Array, []
620
621 expect(
622 empty().start_array(TLVTag::Anonymous).end_container(),
623 &[0x16, 0x18],
624 );
625
626 // Empty List, []
627
628 expect(
629 empty().start_list(TLVTag::Anonymous).end_container(),
630 &[0x17, 0x18],
631 );
632
633 // Structure, two context specific tags, Signed Integer, 1 octet values, {0 = 42, 1 = -17}
634
635 expect(
636 empty()
637 .start_struct(TLVTag::Anonymous)
638 .i8(TLVTag::Context(0), 42)
639 .i32(TLVTag::Context(1), -17)
640 .end_container(),
641 &[0x15, 0x20, 0x00, 0x2a, 0x20, 0x01, 0xef, 0x18],
642 );
643
644 // Array, Signed Integer, 1-octet values, [0, 1, 2, 3, 4]
645
646 expect(
647 empty()
648 .start_array(TLVTag::Anonymous)
649 .i8(TLVTag::Anonymous, 0)
650 .i8(TLVTag::Anonymous, 1)
651 .i8(TLVTag::Anonymous, 2)
652 .i8(TLVTag::Anonymous, 3)
653 .i8(TLVTag::Anonymous, 4)
654 .end_container(),
655 &[
656 0x16, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x18,
657 ],
658 );
659
660 // List, mix of anonymous and context tags, Signed Integer, 1 octet values, [[1, 0 = 42, 2, 3, 0 = -17]]
661
662 expect(
663 empty()
664 .start_list(TLVTag::Anonymous)
665 .i64(TLVTag::Anonymous, 1)
666 .i16(TLVTag::Context(0), 42)
667 .i8(TLVTag::Anonymous, 2)
668 .i8(TLVTag::Anonymous, 3)
669 .i32(TLVTag::Context(0), -17)
670 .end_container(),
671 &[
672 0x17, 0x00, 0x01, 0x20, 0x00, 0x2a, 0x00, 0x02, 0x00, 0x03, 0x20, 0x00, 0xef, 0x18,
673 ],
674 );
675
676 // Array, mix of element types, [42, -170000, {}, 17.9, "Hello!"]
677
678 expect(
679 empty()
680 .start_array(TLVTag::Anonymous)
681 .i64(TLVTag::Anonymous, 42)
682 .i64(TLVTag::Anonymous, -170000)
683 .start_struct(TLVTag::Anonymous)
684 .end_container()
685 .f32(TLVTag::Anonymous, 17.9)
686 .utf8(TLVTag::Anonymous, "Hello!")
687 .end_container(),
688 &[
689 0x16, 0x00, 0x2a, 0x02, 0xf0, 0x67, 0xfd, 0xff, 0x15, 0x18, 0x0a, 0x33, 0x33, 0x8f,
690 0x41, 0x0c, 0x06, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x21, 0x18,
691 ],
692 );
693
694 // Anonymous tag, Unsigned Integer, 1-octet value, 42U
695
696 expect(empty().u64(TLVTag::Anonymous, 42), &[0x04, 0x2a]);
697
698 // Context tag 1, Unsigned Integer, 1-octet value, 1 = 42U
699
700 expect(empty().u16(TLVTag::Context(1), 42), &[0x24, 0x01, 0x2a]);
701
702 // Common profile tag 1, Unsigned Integer, 1-octet value, Matter::1 = 42U
703
704 expect(
705 empty().u16(TLVTag::CommonPrf16(1), 42),
706 &[0x44, 0x01, 0x00, 0x2a],
707 );
708
709 // Common profile tag 100000, Unsigned Integer, 1-octet value, Matter::100000 = 42U
710
711 expect(
712 empty().u16(TLVTag::CommonPrf32(100000), 42),
713 &[0x64, 0xa0, 0x86, 0x01, 0x00, 0x2a],
714 );
715
716 // Fully qualified tag, Vendor ID 0xFFF1/65521, profile number 0xDEED/57069,
717 // 2-octet tag 1, Unsigned Integer, 1-octet value 42, 65521::57069:1 = 42U
718
719 expect(
720 empty().u16(
721 TLVTag::FullQual48 {
722 vendor_id: 65521,
723 profile: 57069,
724 tag: 1,
725 },
726 42,
727 ),
728 &[0xc4, 0xf1, 0xff, 0xed, 0xde, 0x01, 0x00, 0x2a],
729 );
730
731 // Fully qualified tag, Vendor ID 0xFFF1/65521, profile number 0xDEED/57069,
732 // 4-octet tag 0xAA55FEED/2857762541, Unsigned Integer, 1-octet value 42, 65521::57069:2857762541 = 42U
733
734 expect(
735 empty().u16(
736 TLVTag::FullQual64 {
737 vendor_id: 65521,
738 profile: 57069,
739 tag: 2857762541,
740 },
741 42,
742 ),
743 &[0xe4, 0xf1, 0xff, 0xed, 0xde, 0xed, 0xfe, 0x55, 0xaa, 0x2a],
744 );
745
746 // Structure with the fully qualified tag, Vendor ID 0xFFF1/65521, profile number 0xDEED/57069,
747 // 2-octet tag 1. The structure contains a single element labeled using a fully qualified tag under
748 // the same profile, with 2-octet tag 0xAA55/43605. 65521::57069:1 = {65521::57069:43605 = 42U}
749
750 expect(
751 empty()
752 .start_struct(TLVTag::FullQual48 {
753 vendor_id: 65521,
754 profile: 57069,
755 tag: 1,
756 })
757 .u64(
758 TLVTag::FullQual48 {
759 vendor_id: 65521,
760 profile: 57069,
761 tag: 43605,
762 },
763 42,
764 )
765 .end_container(),
766 &[
767 0xd5, 0xf1, 0xff, 0xed, 0xde, 0x01, 0x00, 0xc4, 0xf1, 0xff, 0xed, 0xde, 0x55, 0xaa,
768 0x2a, 0x18,
769 ],
770 );
771 }
772}