Skip to main content

rs_matter/tlv/
write.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 num_traits::ToBytes;
19
20use crate::error::{Error, ErrorCode};
21use crate::utils::storage::WriteBuf;
22
23use super::{TLVControl, TLVTag, TLVTagType, TLVValue, TLVValueType};
24
25/// A trait representing a storage where data can be serialized as a TLV stream.
26/// by synchronously emitting bytes to the storage.
27///
28/// The one method that needs to be implemented by user code is `write`.
29///
30/// The trait operates in an append-only manner without requiring access to the serialized
31/// TLV data, so it can be implemented with an in-memory storage, or a file storage, or anything
32/// that can output a byte to somewhere (like the `Write` Rust traits).
33///
34/// With that said, the trait has two additional methods that (optionally) allow for "rewinding"
35/// the storage. Implementing these is optional, and they currently exist only for backwards
36/// compatibility with code implemented prior to the introduction of this trait.
37///
38/// For iterator-style TLV serialization look at the `ToTLVIter` trait.
39pub trait TLVWrite {
40    type Position: PartialEq + Copy;
41
42    /// Write a TLV tag and value to the TLV stream.
43    fn tlv(&mut self, tag: &TLVTag, value: &TLVValue) -> Result<(), Error> {
44        self.raw_value(tag, value.value_type(), &[])?;
45
46        match value {
47            TLVValue::Str8l(a) => self.write_raw_data((a.len() as u8).to_le_bytes()),
48            TLVValue::Str16l(a) => self.write_raw_data((a.len() as u16).to_le_bytes()),
49            TLVValue::Str32l(a) => self.write_raw_data((a.len() as u32).to_le_bytes()),
50            TLVValue::Str64l(a) => self.write_raw_data((a.len() as u64).to_le_bytes()),
51            TLVValue::Utf8l(a) => self.write_raw_data((a.len() as u8).to_le_bytes()),
52            TLVValue::Utf16l(a) => self.write_raw_data((a.len() as u16).to_le_bytes()),
53            TLVValue::Utf32l(a) => self.write_raw_data((a.len() as u32).to_le_bytes()),
54            TLVValue::Utf64l(a) => self.write_raw_data((a.len() as u64).to_le_bytes()),
55            _ => Ok(()),
56        }?;
57
58        match value {
59            TLVValue::S8(a) => self.write_raw_data(a.to_le_bytes()),
60            TLVValue::S16(a) => self.write_raw_data(a.to_le_bytes()),
61            TLVValue::S32(a) => self.write_raw_data(a.to_le_bytes()),
62            TLVValue::S64(a) => self.write_raw_data(a.to_le_bytes()),
63            TLVValue::U8(a) => self.write_raw_data(a.to_le_bytes()),
64            TLVValue::U16(a) => self.write_raw_data(a.to_le_bytes()),
65            TLVValue::U32(a) => self.write_raw_data(a.to_le_bytes()),
66            TLVValue::U64(a) => self.write_raw_data(a.to_le_bytes()),
67            TLVValue::False => Ok(()),
68            TLVValue::True => Ok(()),
69            TLVValue::F32(a) => self.write_raw_data(a.to_le_bytes()),
70            TLVValue::F64(a) => self.write_raw_data(a.to_le_bytes()),
71            TLVValue::Utf8l(a)
72            | TLVValue::Utf16l(a)
73            | TLVValue::Utf32l(a)
74            | TLVValue::Utf64l(a) => self.write_raw_data(a.as_bytes().iter().copied()),
75            TLVValue::Str8l(a)
76            | TLVValue::Str16l(a)
77            | TLVValue::Str32l(a)
78            | TLVValue::Str64l(a) => self.write_raw_data(a.iter().copied()),
79            TLVValue::Null
80            | TLVValue::Struct
81            | TLVValue::Array
82            | TLVValue::List
83            | TLVValue::EndCnt => Ok(()),
84        }
85    }
86
87    /// Write a tag and a TLV S8 value to the TLV stream.
88    fn i8(&mut self, tag: &TLVTag, data: i8) -> Result<(), Error> {
89        self.raw_value(tag, TLVValueType::S8, &data.to_le_bytes())
90    }
91
92    /// Write a tag and a TLV U8 value to the TLV stream.
93    fn u8(&mut self, tag: &TLVTag, data: u8) -> Result<(), Error> {
94        self.raw_value(tag, TLVValueType::U8, &data.to_le_bytes())
95    }
96
97    /// Write a tag and a TLV S16 or (if the data is small enough) S8 value to the TLV stream.
98    fn i16(&mut self, tag: &TLVTag, data: i16) -> Result<(), Error> {
99        if data >= i8::MIN as i16 && data <= i8::MAX as i16 {
100            self.i8(tag, data as i8)
101        } else {
102            self.raw_value(tag, TLVValueType::S16, &data.to_le_bytes())
103        }
104    }
105
106    /// Write a tag and a TLV U16 or (if the data is small enough) U8 value to the TLV stream.
107    fn u16(&mut self, tag: &TLVTag, data: u16) -> Result<(), Error> {
108        if data <= u8::MAX as u16 {
109            self.u8(tag, data as u8)
110        } else {
111            self.raw_value(tag, TLVValueType::U16, &data.to_le_bytes())
112        }
113    }
114
115    /// Write a tag and a TLV S32 or (if the data is small enough) S16 or S8 value to the TLV stream.
116    fn i32(&mut self, tag: &TLVTag, data: i32) -> Result<(), Error> {
117        if data >= i16::MIN as i32 && data <= i16::MAX as i32 {
118            self.i16(tag, data as i16)
119        } else {
120            self.raw_value(tag, TLVValueType::S32, &data.to_le_bytes())
121        }
122    }
123
124    /// Write a tag and a TLV U32 or (if the data is small enough) U16 or U8 value to the TLV stream.
125    fn u32(&mut self, tag: &TLVTag, data: u32) -> Result<(), Error> {
126        if data <= u16::MAX as u32 {
127            self.u16(tag, data as u16)
128        } else {
129            self.raw_value(tag, TLVValueType::U32, &data.to_le_bytes())
130        }
131    }
132
133    /// Write a tag and a TLV S64 or (if the data is small enough) S32, S16, or S8 value to the TLV stream.
134    fn i64(&mut self, tag: &TLVTag, data: i64) -> Result<(), Error> {
135        if data >= i32::MIN as i64 && data <= i32::MAX as i64 {
136            self.i32(tag, data as i32)
137        } else {
138            self.raw_value(tag, TLVValueType::S64, &data.to_le_bytes())
139        }
140    }
141
142    /// Write a tag and a TLV U64 or (if the data is small enough) U32, U16, or U8 value to the TLV stream.
143    fn u64(&mut self, tag: &TLVTag, data: u64) -> Result<(), Error> {
144        if data <= u32::MAX as u64 {
145            self.u32(tag, data as u32)
146        } else {
147            self.raw_value(tag, TLVValueType::U64, &data.to_le_bytes())
148        }
149    }
150
151    /// Write a tag and a TLV F32 to the TLV stream.
152    fn f32(&mut self, tag: &TLVTag, data: f32) -> Result<(), Error> {
153        self.raw_value(tag, TLVValueType::F32, &data.to_le_bytes())
154    }
155
156    /// Write a tag and a TLV F64 to the TLV stream.
157    fn f64(&mut self, tag: &TLVTag, data: f64) -> Result<(), Error> {
158        self.raw_value(tag, TLVValueType::F64, &data.to_le_bytes())
159    }
160
161    /// Write a tag and a TLV Octet String to the TLV stream, where the Octet String is a slice of u8 bytes.
162    ///
163    /// The exact octet string type (Str8l, Str16l, Str32l, or Str64l) is chosen based on the length of the data,
164    /// whereas the smallest type filling the provided data length is chosen.
165    fn str(&mut self, tag: &TLVTag, data: &[u8]) -> Result<(), Error> {
166        self.stri(tag, data.len(), data.iter().copied())
167    }
168
169    /// Write a tag and a TLV Octet String to the TLV stream, where the Octet String is a slice of u8 bytes.
170    ///
171    /// The writing is done via a user-supplied callback `cb`, that is expected to fill the provided buffer with the data
172    /// and to return the length of the written data.
173    ///
174    /// This method is useful when the data to be written needs to be computed first, and the computation needs a buffer where
175    /// to operate.
176    ///
177    /// Note that this method always uses a Str16l value type to write the data, which restricts the data length to no more than
178    /// 65535 bytes.
179    ///
180    /// Note also that this method might not be supported by all `TLVWrite` implementations.
181    fn str_cb(
182        &mut self,
183        _tag: &TLVTag,
184        _cb: impl FnOnce(&mut [u8]) -> Result<usize, Error>,
185    ) -> Result<(), Error> {
186        unimplemented!("str_cb not implemented for this TLVWrite instance");
187    }
188
189    /// Write a tag and a TLV Octet String to the TLV stream, where the Octet String is
190    /// anything that can be turned into an iterator of u8 bytes.
191    ///
192    /// The exact octet string type (Str8l, Str16l, Str32l, or Str64l) is chosen based on the length of the data,
193    /// whereas the smallest type filling the provided data length is chosen.
194    ///
195    /// NOTE: The length of the Octet String must be provided by the user and it must match the
196    /// number of bytes returned by the provided iterator, or else the generated TLV stream will be invalid.
197    fn stri<I>(&mut self, tag: &TLVTag, len: usize, data: I) -> Result<(), Error>
198    where
199        I: IntoIterator<Item = u8>,
200    {
201        if len <= u8::MAX as usize {
202            self.raw_value(tag, TLVValueType::Str8l, &(len as u8).to_le_bytes())?;
203        } else if len <= u16::MAX as usize {
204            self.raw_value(tag, TLVValueType::Str16l, &(len as u16).to_le_bytes())?;
205        } else if len <= u32::MAX as usize {
206            self.raw_value(tag, TLVValueType::Str32l, &(len as u32).to_le_bytes())?;
207        } else {
208            self.raw_value(tag, TLVValueType::Str64l, &(len as u64).to_le_bytes())?;
209        }
210
211        self.write_raw_data(data)
212    }
213
214    /// Write a tag and a TLV UTF-8 String to the TLV stream, where the UTF-8 String is a str.
215    ///
216    /// The exact UTF-8 string type (Utf8l, Utf16l, Utf32l, or Utf64l) is chosen based on the length of the data,
217    /// whereas the smallest type filling the provided data length is chosen.
218    fn utf8(&mut self, tag: &TLVTag, data: &str) -> Result<(), Error> {
219        self.utf8i(tag, data.len(), data.as_bytes().iter().copied())
220    }
221
222    /// Write a tag and a TLV UTF-8 String to the TLV stream, where the UTF-8 String is
223    /// anything that can be turned into an iterator of u8 bytes.
224    ///
225    /// The exact UTF-8 string type (Utf8l, Utf16l, Utf32l, or Utf64l) is chosen based on the length of the data,
226    /// whereas the smallest type filling the provided data length is chosen.
227    ///
228    /// NOTE 1: The length of the UTF-8 String must be provided by the user and it must match the
229    /// number of bytes returned by the provided iterator, or else the generated TLV stream will be invalid.
230    ///
231    /// NOTE 2: The provided iterator must return valid UTF-8 bytes, or else the generated TLV stream will be invalid.
232    fn utf8i<I>(&mut self, tag: &TLVTag, len: usize, data: I) -> Result<(), Error>
233    where
234        I: IntoIterator<Item = u8>,
235    {
236        if len <= u8::MAX as usize {
237            self.raw_value(tag, TLVValueType::Utf8l, &(len as u8).to_le_bytes())?;
238        } else if len <= u16::MAX as usize {
239            self.raw_value(tag, TLVValueType::Utf16l, &(len as u16).to_le_bytes())?;
240        } else if len <= u32::MAX as usize {
241            self.raw_value(tag, TLVValueType::Utf32l, &(len as u32).to_le_bytes())?;
242        } else {
243            self.raw_value(tag, TLVValueType::Utf64l, &(len as u64).to_le_bytes())?;
244        }
245
246        self.write_raw_data(data)
247    }
248
249    /// Write a tag and a TLV UTF-8 String to the TLV stream, where the UTF-8 String is a str.
250    ///
251    /// The writing is done via a user-supplied callback `cb`, that is expected to fill the provided buffer with the data
252    /// and to return the length of the written data.
253    ///
254    /// This method is useful when the data to be written needs to be computed first, and the computation needs a buffer where
255    /// to operate.
256    ///
257    /// Note that this method always uses a Utf16l value type to write the data, which restricts the data length to no more than
258    /// 65535 bytes.
259    ///
260    /// Note also that this method might not be supported by all `TLVWrite` implementations.
261    fn utf8_cb(
262        &mut self,
263        _tag: &TLVTag,
264        _cb: impl FnOnce(&mut [u8]) -> Result<usize, Error>,
265    ) -> Result<(), Error> {
266        unimplemented!("utf8_cb not implemented for this TLVWrite instance");
267    }
268
269    /// Write a tag and a value indicating the start of a Struct TLV container.
270    ///
271    /// NOTE: The user must call `end_container` after writing all the Struct fields
272    /// to close the Struct container or else the generated TLV stream will be invalid.
273    fn start_struct(&mut self, tag: &TLVTag) -> Result<(), Error> {
274        self.raw_value(tag, TLVValueType::Struct, &[])
275    }
276
277    /// Write a tag and a value indicating the start of an Array TLV container.
278    ///
279    /// NOTE: The user must call `end_container` after writing all the Array elements
280    /// to close the Array container or else the generated TLV stream will be invalid.
281    fn start_array(&mut self, tag: &TLVTag) -> Result<(), Error> {
282        self.raw_value(tag, TLVValueType::Array, &[])
283    }
284
285    /// Write a tag and a value indicating the start of a List TLV container.
286    ///
287    /// NOTE: The user must call `end_container` after writing all the List elements
288    /// to close the List container or else the generated TLV stream will be invalid.
289    fn start_list(&mut self, tag: &TLVTag) -> Result<(), Error> {
290        self.raw_value(tag, TLVValueType::List, &[])
291    }
292
293    /// Write a tag and a value indicating the start of a Struct TLV container.
294    ///
295    /// NOTE: The user must call `end_container` after writing all the Struct fields
296    /// to close the Struct container or else the generated TLV stream will be invalid.
297    fn start_container(&mut self, tag: &TLVTag, container_type: TLVValueType) -> Result<(), Error> {
298        if !container_type.is_container() {
299            Err(ErrorCode::TLVTypeMismatch)?;
300        }
301
302        self.raw_value(tag, container_type, &[])
303    }
304
305    /// Write a value indicating the end of a Struct, Array, or List TLV container.
306    ///
307    /// NOTE: This method must be called only when the corresponding container has been opened
308    /// using `start_struct`, `start_array`, or `start_list`, or else the generated TLV stream will be invalid.
309    fn end_container(&mut self) -> Result<(), Error> {
310        self.write(TLVControl::new(TLVTagType::Anonymous, TLVValueType::EndCnt).as_raw())
311    }
312
313    /// Write a tag and a TLV Null value to the TLV stream.
314    fn null(&mut self, tag: &TLVTag) -> Result<(), Error> {
315        self.raw_value(tag, TLVValueType::Null, &[])
316    }
317
318    /// Write a tag and a TLV True or False value to the TLV stream.
319    fn bool(&mut self, tag: &TLVTag, val: bool) -> Result<(), Error> {
320        self.raw_value(
321            tag,
322            if val {
323                TLVValueType::True
324            } else {
325                TLVValueType::False
326            },
327            &[],
328        )
329    }
330
331    /// Write a tag and a raw, already-encoded TLV value represented as a byte slice.
332    fn raw_value(
333        &mut self,
334        tag: &TLVTag,
335        value_type: TLVValueType,
336        value_payload: &[u8],
337    ) -> Result<(), Error> {
338        self.write(TLVControl::new(tag.tag_type(), value_type).as_raw())?;
339
340        match tag {
341            TLVTag::Anonymous => Ok(()),
342            TLVTag::Context(v) => self.write_raw_data(v.to_le_bytes()),
343            TLVTag::CommonPrf16(v) | TLVTag::ImplPrf16(v) => self.write_raw_data(v.to_le_bytes()),
344            TLVTag::CommonPrf32(v) | TLVTag::ImplPrf32(v) => self.write_raw_data(v.to_le_bytes()),
345            TLVTag::FullQual48 {
346                vendor_id,
347                profile,
348                tag,
349            } => {
350                self.write_raw_data(vendor_id.to_le_bytes())?;
351                self.write_raw_data(profile.to_le_bytes())?;
352                self.write_raw_data(tag.to_le_bytes())
353            }
354            TLVTag::FullQual64 {
355                vendor_id,
356                profile,
357                tag,
358            } => {
359                self.write_raw_data(vendor_id.to_le_bytes())?;
360                self.write_raw_data(profile.to_le_bytes())?;
361                self.write_raw_data(tag.to_le_bytes())
362            }
363        }?;
364
365        self.write_raw_data(value_payload.iter().copied())
366    }
367
368    /// Append multiple raw bytes to the TLV stream.
369    fn write_raw_data<I>(&mut self, bytes: I) -> Result<(), Error>
370    where
371        I: IntoIterator<Item = u8>,
372    {
373        for byte in bytes {
374            self.write(byte)?;
375        }
376
377        Ok(())
378    }
379
380    /// Append a single byte to the TLV stream.
381    fn write(&mut self, byte: u8) -> Result<(), Error>;
382
383    /// Get the current position in the TLV stream.
384    ///
385    /// NOTE: This method might not be supported by all implementations and therefore it might panic.
386    fn get_tail(&self) -> Self::Position {
387        unimplemented!("get_tail not implemented for this TLVWrite instance");
388    }
389
390    /// Rewind the TLV stream to a previous position.
391    ///
392    /// NOTE: This method might not be supported by all implementations and therefore it might panic.
393    fn rewind_to(&mut self, _pos: Self::Position) {
394        unimplemented!("rewind_to not implemented for this TLVWrite instance");
395    }
396
397    /// Get a mutable slice of the available space in the TLV stream.
398    ///
399    /// NOTE: This method assumes that the TLV write implementation is writing to a buffer,
400    /// which might not be the case for all implementations. Therefore, this method might panic
401    /// if the implementation does not support it.
402    fn available_space(&mut self) -> &mut [u8] {
403        unimplemented!("available_space not implemented for this TLVWrite instance");
404    }
405}
406
407impl<T> TLVWrite for &mut T
408where
409    T: TLVWrite,
410{
411    type Position = T::Position;
412
413    fn write(&mut self, byte: u8) -> Result<(), Error> {
414        (**self).write(byte)
415    }
416
417    fn get_tail(&self) -> Self::Position {
418        (**self).get_tail()
419    }
420
421    fn rewind_to(&mut self, pos: Self::Position) {
422        (**self).rewind_to(pos)
423    }
424
425    fn available_space(&mut self) -> &mut [u8] {
426        (**self).available_space()
427    }
428
429    fn str_cb(
430        &mut self,
431        tag: &TLVTag,
432        cb: impl FnOnce(&mut [u8]) -> Result<usize, Error>,
433    ) -> Result<(), Error> {
434        (**self).str_cb(tag, cb)
435    }
436
437    fn utf8_cb(
438        &mut self,
439        tag: &TLVTag,
440        cb: impl FnOnce(&mut [u8]) -> Result<usize, Error>,
441    ) -> Result<(), Error> {
442        (**self).utf8_cb(tag, cb)
443    }
444}
445
446impl TLVWrite for WriteBuf<'_> {
447    type Position = usize;
448
449    fn write(&mut self, byte: u8) -> Result<(), Error> {
450        WriteBuf::append(self, &[byte])
451    }
452
453    fn get_tail(&self) -> Self::Position {
454        WriteBuf::get_tail(self)
455    }
456
457    fn rewind_to(&mut self, pos: Self::Position) {
458        WriteBuf::rewind_tail_to(self, pos)
459    }
460
461    fn available_space(&mut self) -> &mut [u8] {
462        WriteBuf::empty_as_mut_slice(self)
463    }
464
465    fn str_cb(
466        &mut self,
467        tag: &TLVTag,
468        cb: impl FnOnce(&mut [u8]) -> Result<usize, Error>,
469    ) -> Result<(), Error> {
470        WriteBuf::str_cb(self, tag, cb)
471    }
472
473    fn utf8_cb(
474        &mut self,
475        tag: &TLVTag,
476        cb: impl FnOnce(&mut [u8]) -> Result<usize, Error>,
477    ) -> Result<(), Error> {
478        WriteBuf::utf8_cb(self, tag, cb)
479    }
480}
481
482impl WriteBuf<'_> {
483    /// Write a tag and a TLV Octet String to the TLV stream, where the Octet String is a slice of u8 bytes.
484    ///
485    /// The writing is done via a user-supplied callback `cb`, that is expected to fill the provided buffer with the data
486    /// and to return the length of the written data.
487    ///
488    /// This method is useful when the data to be written needs to be computed first, and the computation needs a buffer where
489    /// to operate.
490    ///
491    /// Note that this method reserves a Str16l header before invoking the callback, restricting
492    /// the data length to no more than 65535 bytes. If the actual length fits in a single byte,
493    /// the header is downgraded in-place to a canonical Str8l encoding (shortest-form length),
494    /// which some strict Matter controllers (e.g. SmartThings) require.
495    pub fn str_cb(
496        &mut self,
497        tag: &TLVTag,
498        cb: impl FnOnce(&mut [u8]) -> Result<usize, Error>,
499    ) -> Result<(), Error> {
500        let control_offset = self.get_tail();
501        self.raw_value(tag, TLVValueType::Str16l, &0_u16.to_le_bytes())?;
502
503        let value_offset = self.get_tail();
504
505        let len = self.append_with_buf(cb)?;
506
507        Self::finalize_len_header(
508            self,
509            control_offset,
510            value_offset,
511            len,
512            tag.tag_type(),
513            TLVValueType::Str8l,
514        );
515
516        Ok(())
517    }
518
519    /// Write a tag and a TLV UTF-8 String to the TLV stream, where the UTF-8 String is a str.
520    ///
521    /// The writing is done via a user-supplied callback `cb`, that is expected to fill the provided buffer with the data
522    /// and to return the length of the written data.
523    ///
524    /// This method is useful when the data to be written needs to be computed first, and the computation needs a buffer where
525    /// to operate.
526    ///
527    /// Note that this method reserves a Utf16l header before invoking the callback, restricting
528    /// the data length to no more than 65535 bytes. If the actual length fits in a single byte,
529    /// the header is downgraded in-place to a canonical Utf8l encoding (shortest-form length).
530    pub fn utf8_cb(
531        &mut self,
532        tag: &TLVTag,
533        cb: impl FnOnce(&mut [u8]) -> Result<usize, Error>,
534    ) -> Result<(), Error> {
535        let control_offset = self.get_tail();
536        self.raw_value(tag, TLVValueType::Utf16l, &0_u16.to_le_bytes())?;
537
538        let value_offset = self.get_tail();
539
540        let len = self.append_with_buf(cb)?;
541
542        Self::finalize_len_header(
543            self,
544            control_offset,
545            value_offset,
546            len,
547            tag.tag_type(),
548            TLVValueType::Utf8l,
549        );
550
551        Ok(())
552    }
553
554    /// Rewrite the length header reserved by `str_cb` / `utf8_cb` so that the shortest
555    /// possible length encoding is used (canonical Matter TLV).
556    ///
557    /// Before the call, the buffer layout is:
558    ///   [... prefix ...][control=<16l>][tag bytes][0x00][0x00][data ...]
559    ///                   ^control_offset           ^value_offset-2      ^value_offset+len
560    ///
561    /// If `len <= u8::MAX`, the header is rewritten to:
562    ///   [... prefix ...][control=<8l>][tag bytes][len][data ...]
563    /// and `end` is decremented by 1.
564    ///
565    /// Otherwise, the 16-bit length is simply patched in place.
566    fn finalize_len_header(
567        this: &mut WriteBuf<'_>,
568        control_offset: usize,
569        value_offset: usize,
570        len: usize,
571        tag_type: TLVTagType,
572        short_value_type: TLVValueType,
573    ) {
574        if len <= u8::MAX as usize {
575            // Canonical: use 1-byte length encoding.
576            this.buf[control_offset] = TLVControl::new(tag_type, short_value_type).as_raw();
577            this.buf[value_offset - 2] = len as u8;
578            this.buf
579                .copy_within(value_offset..value_offset + len, value_offset - 1);
580            this.rewind_tail_to(this.get_tail() - 1);
581        } else if len <= u16::MAX as usize {
582            // Keep 2-byte length encoding and patch the placeholder.
583            this.buf[value_offset - 2..value_offset].copy_from_slice(&(len as u16).to_le_bytes());
584        } else {
585            // This should not happen, as the callback is expected to respect the reserved header size.
586            panic!("Callback wrote more data than the reserved header can encode");
587        }
588    }
589}
590
591#[cfg(test)]
592mod tests {
593    use core::f32;
594
595    use super::{TLVTag, TLVWrite};
596    use crate::{tlv::TLVValue, utils::storage::WriteBuf};
597
598    #[test]
599    fn test_write_success() {
600        let mut buf = [0; 20];
601        let mut tw = WriteBuf::new(&mut buf);
602
603        tw.start_struct(&TLVTag::Anonymous).unwrap();
604        tw.u8(&TLVTag::Anonymous, 12).unwrap();
605        tw.u8(&TLVTag::Context(1), 13).unwrap();
606        tw.u16(&TLVTag::Anonymous, 0x1212).unwrap();
607        tw.u16(&TLVTag::Context(2), 0x1313).unwrap();
608        tw.start_array(&TLVTag::Context(3)).unwrap();
609        tw.bool(&TLVTag::Anonymous, true).unwrap();
610        tw.end_container().unwrap();
611        tw.end_container().unwrap();
612        assert_eq!(
613            buf,
614            [21, 4, 12, 36, 1, 13, 5, 0x12, 0x012, 37, 2, 0x13, 0x13, 54, 3, 9, 24, 24, 0, 0]
615        );
616    }
617
618    #[test]
619    fn test_write_overflow() {
620        let mut buf = [0; 6];
621        let mut tw = WriteBuf::new(&mut buf);
622
623        tw.u8(&TLVTag::Anonymous, 12).unwrap();
624        tw.u8(&TLVTag::Context(1), 13).unwrap();
625        if tw.u16(&TLVTag::Anonymous, 12).is_ok() {
626            panic!("This should have returned error")
627        }
628        if tw.u16(&TLVTag::Context(2), 13).is_ok() {
629            panic!("This should have returned error")
630        }
631        assert_eq!(buf, [4, 12, 36, 1, 13, 4]);
632    }
633
634    #[test]
635    fn test_put_str8() {
636        let mut buf = [0; 20];
637        let mut tw = WriteBuf::new(&mut buf);
638
639        tw.u8(&TLVTag::Context(1), 13).unwrap();
640        tw.str(&TLVTag::Anonymous, &[10, 11, 12, 13, 14]).unwrap();
641        tw.u16(&TLVTag::Context(2), 0x1313).unwrap();
642        tw.str(&TLVTag::Context(3), &[20, 21, 22]).unwrap();
643        assert_eq!(
644            buf,
645            [36, 1, 13, 16, 5, 10, 11, 12, 13, 14, 37, 2, 0x13, 0x13, 48, 3, 3, 20, 21, 22]
646        );
647    }
648
649    #[test]
650    fn test_matter_spec_examples() {
651        let mut buf = [0; 200];
652        let mut tw = WriteBuf::new(&mut buf);
653
654        // Boolean false
655
656        tw.bool(&TLVTag::Anonymous, false).unwrap();
657        assert_eq!(&[0x08], tw.as_slice());
658
659        // Boolean true
660
661        tw.reset();
662        tw.bool(&TLVTag::Anonymous, true).unwrap();
663        assert_eq!(&[0x09], tw.as_slice());
664
665        // Signed Integer, 1-octet, value 42
666
667        tw.reset();
668        tw.i8(&TLVTag::Anonymous, 42).unwrap();
669        assert_eq!(&[0x00, 0x2a], tw.as_slice());
670
671        // Signed Integer, 1-octet, value -17
672
673        tw.reset();
674        tw.i32(&TLVTag::Anonymous, -17).unwrap();
675        assert_eq!(&[0x00, 0xef], tw.as_slice());
676
677        // Unsigned Integer, 1-octet, value 42U
678
679        tw.reset();
680        tw.u8(&TLVTag::Anonymous, 42).unwrap();
681        assert_eq!(&[0x04, 0x2a], tw.as_slice());
682
683        // Signed Integer, 2-octet, value 422
684
685        tw.reset();
686        tw.i16(&TLVTag::Anonymous, 422).unwrap();
687        assert_eq!(&[0x01, 0xa6, 0x01], tw.as_slice());
688
689        // Signed Integer, 4-octet, value -170000
690
691        tw.reset();
692        tw.i32(&TLVTag::Anonymous, -170000).unwrap();
693        assert_eq!(&[0x02, 0xf0, 0x67, 0xfd, 0xff], tw.as_slice());
694
695        // Signed Integer, 8-octet, value 40000000000
696
697        tw.reset();
698        tw.i64(&TLVTag::Anonymous, 40000000000).unwrap();
699        assert_eq!(
700            &[0x03, 0x00, 0x90, 0x2f, 0x50, 0x09, 0x00, 0x00, 0x00],
701            tw.as_slice()
702        );
703
704        // UTF-8 String, 1-octet length, "Hello!"
705
706        tw.reset();
707        tw.utf8(&TLVTag::Anonymous, "Hello!").unwrap();
708        assert_eq!(
709            &[0x0c, 0x06, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x21],
710            tw.as_slice()
711        );
712
713        // UTF-8 String, 1-octet length, "Tschüs"
714
715        tw.reset();
716        tw.utf8i(
717            &TLVTag::Anonymous,
718            "Tschüs".len(),
719            "Tschüs".as_bytes().iter().copied(),
720        )
721        .unwrap();
722        assert_eq!(
723            &[0x0c, 0x07, 0x54, 0x73, 0x63, 0x68, 0xc3, 0xbc, 0x73],
724            tw.as_slice()
725        );
726
727        // Octet String, 1-octet length, octets 00 01 02 03 04
728
729        tw.reset();
730        tw.str(&TLVTag::Anonymous, &[0x00, 0x01, 0x02, 0x03, 0x04])
731            .unwrap();
732        assert_eq!(&[0x10, 0x05, 0x00, 0x01, 0x02, 0x03, 0x04], tw.as_slice());
733
734        // Null
735
736        tw.reset();
737        tw.tlv(&TLVTag::Anonymous, &TLVValue::Null).unwrap();
738        assert_eq!(&[0x14], tw.as_slice());
739
740        // Single precision floating point 0.0
741
742        tw.reset();
743        tw.tlv(&TLVTag::Anonymous, &TLVValue::F32(0.0)).unwrap();
744        assert_eq!(&[0x0a, 0x00, 0x00, 0x00, 0x00], tw.as_slice());
745
746        // Single precision floating point (1.0 / 3.0)
747
748        tw.reset();
749        tw.f32(&TLVTag::Anonymous, 1.0 / 3.0).unwrap();
750        assert_eq!(&[0x0a, 0xab, 0xaa, 0xaa, 0x3e], tw.as_slice());
751
752        // Single precision floating point 17.9
753
754        tw.reset();
755        tw.f32(&TLVTag::Anonymous, 17.9).unwrap();
756        assert_eq!(&[0x0a, 0x33, 0x33, 0x8f, 0x41], tw.as_slice());
757
758        // Single precision floating point infinity
759
760        tw.reset();
761        tw.f32(&TLVTag::Anonymous, f32::INFINITY).unwrap();
762        assert_eq!(&[0x0a, 0x00, 0x00, 0x80, 0x7f], tw.as_slice());
763
764        // Single precision floating point negative infinity
765
766        tw.reset();
767        tw.f32(&TLVTag::Anonymous, f32::NEG_INFINITY).unwrap();
768        assert_eq!(&[0x0a, 0x00, 0x00, 0x80, 0xff], tw.as_slice());
769
770        // Double precision floating point 0.0
771
772        tw.reset();
773        tw.f64(&TLVTag::Anonymous, 0.0).unwrap();
774        assert_eq!(
775            &[0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
776            tw.as_slice()
777        );
778
779        // Double precision floating point (1.0 / 3.0)
780
781        tw.reset();
782        tw.f64(&TLVTag::Anonymous, 1.0 / 3.0).unwrap();
783        assert_eq!(
784            &[0x0b, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0xd5, 0x3f],
785            tw.as_slice()
786        );
787
788        // Double precision floating point 17.9
789
790        tw.reset();
791        tw.f64(&TLVTag::Anonymous, 17.9).unwrap();
792        assert_eq!(
793            &[0x0b, 0x66, 0x66, 0x66, 0x66, 0x66, 0xe6, 0x31, 0x40],
794            tw.as_slice()
795        );
796
797        // Double precision floating point infinity (∞)
798
799        tw.reset();
800        tw.f64(&TLVTag::Anonymous, f64::INFINITY).unwrap();
801        assert_eq!(
802            &[0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x7f],
803            tw.as_slice()
804        );
805
806        // Double precision floating point negative infinity
807
808        tw.reset();
809        tw.f64(&TLVTag::Anonymous, f64::NEG_INFINITY).unwrap();
810        assert_eq!(
811            &[0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xff],
812            tw.as_slice()
813        );
814
815        // Empty Structure, {}
816
817        tw.reset();
818        tw.start_struct(&TLVTag::Anonymous).unwrap();
819        tw.end_container().unwrap();
820        assert_eq!(&[0x15, 0x18], tw.as_slice());
821
822        // Empty Array, []
823
824        tw.reset();
825        tw.start_array(&TLVTag::Anonymous).unwrap();
826        tw.end_container().unwrap();
827        assert_eq!(&[0x16, 0x18], tw.as_slice());
828
829        // Empty List, []
830
831        tw.reset();
832        tw.start_list(&TLVTag::Anonymous).unwrap();
833        tw.end_container().unwrap();
834        assert_eq!(&[0x17, 0x18], tw.as_slice());
835
836        // Structure, two context specific tags, Signed Integer, 1 octet values, {0 = 42, 1 = -17}
837
838        tw.reset();
839        tw.start_struct(&TLVTag::Anonymous).unwrap();
840        tw.i8(&TLVTag::Context(0), 42).unwrap();
841        tw.i32(&TLVTag::Context(1), -17).unwrap();
842        tw.end_container().unwrap();
843        assert_eq!(
844            &[0x15, 0x20, 0x00, 0x2a, 0x20, 0x01, 0xef, 0x18],
845            tw.as_slice()
846        );
847
848        // Array, Signed Integer, 1-octet values, [0, 1, 2, 3, 4]
849
850        tw.reset();
851        tw.start_array(&TLVTag::Anonymous).unwrap();
852        for i in 0..5 {
853            tw.i8(&TLVTag::Anonymous, i as i8).unwrap();
854        }
855        tw.end_container().unwrap();
856        assert_eq!(
857            &[0x16, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x18],
858            tw.as_slice()
859        );
860
861        // List, mix of anonymous and context tags, Signed Integer, 1 octet values, [[1, 0 = 42, 2, 3, 0 = -17]]
862
863        tw.reset();
864        tw.start_list(&TLVTag::Anonymous).unwrap();
865        tw.i64(&TLVTag::Anonymous, 1).unwrap();
866        tw.i16(&TLVTag::Context(0), 42).unwrap();
867        tw.i8(&TLVTag::Anonymous, 2).unwrap();
868        tw.i8(&TLVTag::Anonymous, 3).unwrap();
869        tw.i32(&TLVTag::Context(0), -17).unwrap();
870        tw.end_container().unwrap();
871        assert_eq!(
872            &[0x17, 0x00, 0x01, 0x20, 0x00, 0x2a, 0x00, 0x02, 0x00, 0x03, 0x20, 0x00, 0xef, 0x18],
873            tw.as_slice()
874        );
875
876        // Array, mix of element types, [42, -170000, {}, 17.9, "Hello!"]
877
878        tw.reset();
879        tw.start_array(&TLVTag::Anonymous).unwrap();
880        tw.i64(&TLVTag::Anonymous, 42).unwrap();
881        tw.i64(&TLVTag::Anonymous, -170000).unwrap();
882        tw.start_struct(&TLVTag::Anonymous).unwrap();
883        tw.end_container().unwrap();
884        tw.f32(&TLVTag::Anonymous, 17.9).unwrap();
885        tw.utf8(&TLVTag::Anonymous, "Hello!").unwrap();
886        tw.end_container().unwrap();
887        assert_eq!(
888            &[
889                0x16, 0x00, 0x2a, 0x02, 0xf0, 0x67, 0xfd, 0xff, 0x15, 0x18, 0x0a, 0x33, 0x33, 0x8f,
890                0x41, 0x0c, 0x06, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x21, 0x18,
891            ],
892            tw.as_slice()
893        );
894
895        // Anonymous tag, Unsigned Integer, 1-octet value, 42U
896
897        tw.reset();
898        tw.u64(&TLVTag::Anonymous, 42).unwrap();
899        assert_eq!(&[0x04, 0x2a], tw.as_slice());
900
901        // Context tag 1, Unsigned Integer, 1-octet value, 1 = 42U
902
903        tw.reset();
904        tw.u64(&TLVTag::Context(1), 42).unwrap();
905        assert_eq!(&[0x24, 0x01, 0x2a], tw.as_slice());
906
907        // Common profile tag 1, Unsigned Integer, 1-octet value, Matter::1 = 42U
908
909        tw.reset();
910        tw.u64(&TLVTag::CommonPrf16(1), 42).unwrap();
911        assert_eq!(&[0x44, 0x01, 0x00, 0x2a], tw.as_slice());
912
913        // Common profile tag 100000, Unsigned Integer, 1-octet value, Matter::100000 = 42U
914
915        tw.reset();
916        tw.u64(&TLVTag::CommonPrf32(100000), 42).unwrap();
917        assert_eq!(&[0x64, 0xa0, 0x86, 0x01, 0x00, 0x2a], tw.as_slice());
918
919        // Fully qualified tag, Vendor ID 0xFFF1/65521, pro­file number 0xDEED/57069,
920        // 2-octet tag 1, Unsigned Integer, 1-octet value 42, 65521::57069:1 = 42U
921
922        tw.reset();
923        tw.u64(
924            &TLVTag::FullQual48 {
925                vendor_id: 65521,
926                profile: 57069,
927                tag: 1,
928            },
929            42,
930        )
931        .unwrap();
932        assert_eq!(
933            &[0xc4, 0xf1, 0xff, 0xed, 0xde, 0x01, 0x00, 0x2a],
934            tw.as_slice()
935        );
936
937        // Fully qualified tag, Vendor ID 0xFFF1/65521, pro­file number 0xDEED/57069,
938        // 4-octet tag 0xAA55FEED/2857762541, Unsigned Integer, 1-octet value 42, 65521::57069:2857762541 = 42U
939
940        tw.reset();
941        tw.u64(
942            &TLVTag::FullQual64 {
943                vendor_id: 65521,
944                profile: 57069,
945                tag: 2857762541,
946            },
947            42,
948        )
949        .unwrap();
950        assert_eq!(
951            &[0xe4, 0xf1, 0xff, 0xed, 0xde, 0xed, 0xfe, 0x55, 0xaa, 0x2a],
952            tw.as_slice()
953        );
954
955        // Structure with the fully qualified tag, Vendor ID 0xFFF1/65521, profile number 0xDEED/57069,
956        // 2-octet tag 1. The structure contains a single ele­ment labeled using a fully qualified tag under
957        // the same profile, with 2-octet tag 0xAA55/43605. 65521::57069:1 = {65521::57069:43605 = 42U}
958
959        tw.reset();
960        tw.start_struct(&TLVTag::FullQual48 {
961            vendor_id: 65521,
962            profile: 57069,
963            tag: 1,
964        })
965        .unwrap();
966        tw.u64(
967            &TLVTag::FullQual48 {
968                vendor_id: 65521,
969                profile: 57069,
970                tag: 43605,
971            },
972            42,
973        )
974        .unwrap();
975        tw.end_container().unwrap();
976        assert_eq!(
977            &[
978                0xd5, 0xf1, 0xff, 0xed, 0xde, 0x01, 0x00, 0xc4, 0xf1, 0xff, 0xed, 0xde, 0x55, 0xaa,
979                0x2a, 0x18,
980            ],
981            tw.as_slice()
982        );
983    }
984}