Skip to main content

rs_matter/tlv/
traits.rs

1/*
2 *
3 *    Copyright (c) 2022-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 crate::error::Error;
19use crate::utils::init;
20
21use super::{
22    EitherIter, TLVElement, TLVSequenceIter, TLVSequenceTLVIter, TLVTag, TLVValue, TLVValueType,
23    TLVWrite, TLV,
24};
25
26pub use builder::*;
27pub use container::*;
28pub use maybe::*;
29pub use octets::*;
30pub use skippable::*;
31pub use slice::*;
32pub use str::*;
33
34mod array;
35mod bitflags;
36mod builder;
37mod container;
38mod maybe;
39mod octets;
40mod primitive;
41mod skippable;
42mod slice;
43mod str;
44mod vec;
45
46/// A trait representing Rust types that can deserialize themselves from
47/// a TLV-encoded byte slice.
48pub trait FromTLV<'a>: Sized + 'a {
49    /// Deserialize the type from a TLV-encoded element.
50    fn from_tlv(element: &TLVElement<'a>) -> Result<Self, Error>;
51
52    /// Generate an in-place initializer for the type that initializes
53    /// the type from a TLV-encoded element.
54    fn init_from_tlv(element: TLVElement<'a>) -> impl init::Init<Self, Error> {
55        unsafe {
56            init::init_from_closure(move |slot| {
57                core::ptr::write(slot, Self::from_tlv(&element)?);
58
59                Ok(())
60            })
61        }
62    }
63
64    /// Deserialize the type from a TLV-encoded element.
65    ///
66    /// Called when the deserialized value will be placed in `Nullable`
67    /// so as to check whether the value falls in the nullable range of the type, which
68    /// might be shorter than the non-nullable range of the type.
69    fn nullable_from_tlv(element: &TLVElement<'a>) -> Result<Self, Error> {
70        Self::from_tlv(element)
71    }
72
73    /// Generate an in-place initializer for the type that initializes
74    /// the type from a TLV-encoded element.
75    ///
76    /// Called when the deserialized value will be placed in `Nullable`
77    /// so as to check whether the value falls in the nullable range of the type, which
78    /// might be shorter than the non-nullable range of the type.
79    fn init_nullable_from_tlv(element: TLVElement<'a>) -> impl init::Init<Self, Error> {
80        unsafe {
81            init::init_from_closure(move |slot| {
82                core::ptr::write(slot, Self::nullable_from_tlv(&element)?);
83
84                Ok(())
85            })
86        }
87    }
88}
89
90/// A trait representing Rust types that can serialize themselves to
91/// a TLV-encoded stream.
92pub trait ToTLV {
93    /// Serialize the type to a TLV-encoded stream.
94    fn to_tlv<W: TLVWrite>(&self, tag: &TLVTag, tw: W) -> Result<(), Error>;
95
96    /// Serialize the type as an iterator of `TLV` instances by potentially borrowing
97    /// data from the type.
98    fn tlv_iter(&self, tag: TLVTag) -> impl Iterator<Item = Result<TLV<'_>, Error>>;
99
100    /// Serialize the type to a TLV-encoded stream.
101    ///
102    /// Called when the serialized value is placed in `Nullable`
103    /// so as to check whether the value falls in the nullable range of the type, which
104    /// might be shorter than the non-nullable range of the type.
105    fn nullable_to_tlv<W: TLVWrite>(&self, tag: &TLVTag, tw: W) -> Result<(), Error> {
106        self.to_tlv(tag, tw)
107    }
108
109    /// Serialize the type as an iterator of `TLV` instances by potentially borrowing
110    /// data from the type.
111    ///
112    /// Called when the serialized value is placed in `Nullable`
113    /// so as to check whether the value falls in the nullable range of the type, which
114    /// might be shorter than the non-nullable range of the type.
115    fn nullable_tlv_iter(&self, tag: TLVTag) -> impl Iterator<Item = Result<TLV<'_>, Error>> {
116        self.tlv_iter(tag)
117    }
118}
119
120impl<T> ToTLV for &T
121where
122    T: ToTLV,
123{
124    fn to_tlv<W: TLVWrite>(&self, tag: &TLVTag, tw: W) -> Result<(), Error> {
125        (*self).to_tlv(tag, tw)
126    }
127
128    fn tlv_iter(&self, tag: TLVTag) -> impl Iterator<Item = Result<TLV<'_>, Error>> {
129        (*self).tlv_iter(tag)
130    }
131
132    fn nullable_to_tlv<W: TLVWrite>(&self, tag: &TLVTag, tw: W) -> Result<(), Error> {
133        (*self).nullable_to_tlv(tag, tw)
134    }
135
136    fn nullable_tlv_iter(&self, tag: TLVTag) -> impl Iterator<Item = Result<TLV<'_>, Error>> {
137        (*self).nullable_tlv_iter(tag)
138    }
139}
140
141impl<'a> FromTLV<'a> for TLVElement<'a> {
142    fn from_tlv(element: &TLVElement<'a>) -> Result<Self, Error> {
143        Ok(element.clone())
144    }
145}
146
147impl ToTLV for TLVElement<'_> {
148    fn to_tlv<W: TLVWrite>(&self, tag: &TLVTag, mut tw: W) -> Result<(), Error> {
149        if self.is_empty() {
150            // Special-case serializing empty TLV elements to nothing
151            // Useful in tests
152            Ok(())
153        } else {
154            let value_type = self.control()?.value_type;
155            let payload = self.raw_value()?;
156
157            // `TLVWrite::raw_value` emits its payload right after the
158            // control + tag bytes, but for variable-size (UTF-8 / octet
159            // string) values `TLVElement::raw_value` strips the length
160            // prefix, so it has to be re-emitted - with the same size class
161            // as the source element - or the written TLV is malformed.
162            let size_len = value_type.variable_size_len();
163            if size_len > 0 {
164                let len_bytes = (payload.len() as u64).to_le_bytes();
165                tw.raw_value(tag, value_type, &len_bytes[..size_len])?;
166                tw.write_raw_data(payload.iter().copied())
167            } else {
168                tw.raw_value(tag, value_type, payload)
169            }
170        }
171    }
172
173    fn tlv_iter(&self, tag: TLVTag) -> impl Iterator<Item = Result<TLV<'_>, Error>> {
174        TLVElementTLVIter::Start(tag, self.clone())
175    }
176}
177
178enum TLVElementTLVIter<'a> {
179    Start(TLVTag, TLVElement<'a>),
180    Seq(TLVSequenceTLVIter<'a>),
181    Finished,
182}
183
184impl<'a> Iterator for TLVElementTLVIter<'a> {
185    type Item = Result<TLV<'a>, Error>;
186
187    fn next(&mut self) -> Option<Self::Item> {
188        match core::mem::replace(self, Self::Finished) {
189            TLVElementTLVIter::Start(tag, elem) => {
190                if elem.is_empty() {
191                    // Special-case serializing empty TLV elements to nothing
192                    // Useful in tests
193                    None
194                } else {
195                    let value = elem.value().map(|value| TLV::new(tag, value));
196
197                    if let Ok(seq) = elem.container() {
198                        *self = Self::Seq(seq.tlv_iter());
199                    } else {
200                        *self = TLVElementTLVIter::Finished;
201                    }
202
203                    Some(value)
204                }
205            }
206            TLVElementTLVIter::Seq(mut iter) => {
207                if let Some(value) = iter.next() {
208                    *self = TLVElementTLVIter::Seq(iter);
209                    Some(value)
210                } else {
211                    Some(Ok(TLV::end_container()))
212                }
213            }
214            TLVElementTLVIter::Finished => None,
215        }
216    }
217}
218
219impl<'a> FromTLV<'a> for TLVValue<'a> {
220    fn from_tlv(element: &TLVElement<'a>) -> Result<Self, Error> {
221        element.value()
222    }
223}
224
225impl ToTLV for TLVValue<'_> {
226    fn to_tlv<W: TLVWrite>(&self, tag: &TLVTag, mut tw: W) -> Result<(), Error> {
227        tw.tlv(tag, self)
228    }
229
230    fn tlv_iter(&self, tag: TLVTag) -> impl Iterator<Item = Result<TLV<'_>, Error>> {
231        TLV::new(tag, self.clone()).into_tlv_iter()
232    }
233}
234
235#[cfg(test)]
236mod tests {
237    use core::fmt::Debug;
238    use core::mem::MaybeUninit;
239
240    use rs_matter_macros::{FromTLV, ToTLV};
241
242    use crate::tlv::{Octets, TLVElement, TLV};
243    use crate::utils::init::InitMaybeUninit;
244    use crate::utils::storage::WriteBuf;
245
246    use super::{FromTLV, OctetStr, TLVTag, ToTLV};
247
248    fn test_from_tlv<'a, T: FromTLV<'a> + PartialEq + Debug>(data: &'a [u8], expected: T) {
249        let root = TLVElement::new(data);
250        let test = T::from_tlv(&root).unwrap();
251        ::core::assert_eq!(test, expected);
252
253        let test_init = T::init_from_tlv(root);
254
255        let mut test = MaybeUninit::<T>::uninit();
256
257        let test = test.try_init_with(test_init).unwrap();
258
259        ::core::assert_eq!(*test, expected);
260    }
261
262    fn test_to_tlv<T: ToTLV>(t: T, expected: &[u8]) {
263        let mut buf = [0; 20];
264        let mut tw = WriteBuf::new(&mut buf);
265
266        t.to_tlv(&TLVTag::Anonymous, &mut tw).unwrap();
267
268        assert_eq!(tw.as_slice(), expected);
269
270        tw.reset();
271
272        let mut iter = t
273            .tlv_iter(TLVTag::Anonymous)
274            .flat_map(TLV::result_into_bytes_iter);
275        loop {
276            match iter.next() {
277                Some(Ok(byte)) => tw.append(&[byte]).unwrap(),
278                None => break,
279                _ => panic!("Error in iterator"),
280            }
281        }
282
283        ::core::assert_eq!(tw.as_slice(), expected);
284    }
285
286    #[derive(ToTLV, Debug)]
287    struct TestDerive {
288        a: u16,
289        b: u32,
290    }
291
292    #[test]
293    fn test_derive_totlv() {
294        test_to_tlv(
295            TestDerive {
296                a: 0x1010,
297                b: 0x20202020,
298            },
299            &[21, 37, 0, 0x10, 0x10, 38, 1, 0x20, 0x20, 0x20, 0x20, 24],
300        );
301    }
302
303    #[derive(FromTLV, Debug, PartialEq)]
304    #[cfg_attr(feature = "defmt", derive(defmt::Format))]
305    struct TestDeriveSimple {
306        a: u16,
307        b: u32,
308    }
309
310    #[test]
311    fn test_derive_fromtlv() {
312        test_from_tlv(
313            &[21, 37, 0, 10, 0, 38, 1, 20, 0, 0, 0, 24],
314            TestDeriveSimple { a: 10, b: 20 },
315        );
316    }
317
318    #[derive(FromTLV, Debug, PartialEq)]
319    #[cfg_attr(feature = "defmt", derive(defmt::Format))]
320    #[tlvargs(lifetime = "'a")]
321    struct TestDeriveStr<'a> {
322        a: u16,
323        b: OctetStr<'a>,
324    }
325
326    #[test]
327    fn test_derive_fromtlv_str() {
328        test_from_tlv(
329            &[21, 37, 0, 10, 0, 0x30, 0x01, 0x03, 10, 11, 12, 0],
330            TestDeriveStr {
331                a: 10,
332                b: Octets(&[10, 11, 12]),
333            },
334        );
335    }
336
337    #[derive(FromTLV, Debug, PartialEq)]
338    #[cfg_attr(feature = "defmt", derive(defmt::Format))]
339    struct TestDeriveOption {
340        a: u16,
341        b: Option<u16>,
342        c: Option<u16>,
343    }
344
345    #[test]
346    fn test_derive_fromtlv_option() {
347        test_from_tlv(
348            &[21, 37, 0, 10, 0, 37, 2, 11, 0],
349            TestDeriveOption {
350                a: 10,
351                b: None,
352                c: Some(11),
353            },
354        );
355    }
356
357    #[derive(FromTLV, ToTLV, Debug, PartialEq)]
358    #[cfg_attr(feature = "defmt", derive(defmt::Format))]
359    struct TestDeriveFabScoped {
360        a: u16,
361        #[tagval(0xFE)]
362        fab_idx: u16,
363    }
364
365    #[test]
366    fn test_derive_fromtlv_fab_scoped() {
367        test_from_tlv(
368            &[21, 37, 0, 10, 0, 37, 0xFE, 11, 0],
369            TestDeriveFabScoped { a: 10, fab_idx: 11 },
370        );
371    }
372
373    #[test]
374    fn test_derive_totlv_fab_scoped() {
375        test_to_tlv(
376            TestDeriveFabScoped { a: 20, fab_idx: 3 },
377            &[21, 36, 0, 20, 36, 0xFE, 3, 24],
378        );
379    }
380
381    #[derive(ToTLV, FromTLV, PartialEq, Debug)]
382    #[cfg_attr(feature = "defmt", derive(defmt::Format))]
383    enum TestDeriveEnum {
384        ValueA(u32),
385        ValueB(u32),
386    }
387
388    #[test]
389    fn test_derive_from_to_tlv_enum() {
390        // Test FromTLV
391        test_from_tlv(&[21, 36, 0, 100, 24, 0], TestDeriveEnum::ValueA(100));
392
393        // Test ToTLV
394        test_to_tlv(TestDeriveEnum::ValueB(10), &[21, 36, 1, 10, 24]);
395    }
396}