Skip to main content

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