Skip to main content

twine_tlv/
traits.rs

1// Copyright (c) 2025 Jake Swensen
2// SPDX-License-Identifier: MPL-2.0
3//
4// This Source Code Form is subject to the terms of the Mozilla Public
5// License, v. 2.0. If a copy of the MPL was not distributed with this
6// file, You can obtain one at http://mozilla.org/MPL/2.0/.
7
8use bytes::{Buf, BufMut};
9
10use crate::error::TwineTlvError;
11
12macro_rules! encoded_len {
13    ($len:expr) => {{
14        let len = $len;
15        if len < 0xFF {
16            len + 2 // Type + Length
17        } else {
18            len + 4 // Type + Extended Length
19        }
20    }};
21}
22
23pub trait PutTlvLength: BufMut {
24    fn put_tlv_length(&mut self, length: usize);
25}
26
27impl PutTlvLength for &mut [u8] {
28    fn put_tlv_length(&mut self, length: usize) {
29        if length < 0xFF {
30            self.put_u8(length as u8);
31        } else {
32            self.put_u8(0xFF);
33            self.put_u16(length as u16);
34        }
35    }
36}
37
38pub trait GetTlvLength: Buf {
39    /// Extract the length of a TLV from the buffer
40    ///
41    /// Expects the next byte(s) to represent the length of the TLV.
42    fn get_tlv_length(&mut self) -> usize;
43}
44
45impl GetTlvLength for &[u8] {
46    fn get_tlv_length(&mut self) -> usize {
47        let start_len = self.get_u8();
48
49        let extended_len = if start_len == 0xFF {
50            Some(self.get_u16())
51        } else {
52            None
53        };
54
55        let len = if let Some(len) = extended_len {
56            len
57        } else {
58            start_len as u16
59        };
60
61        len as usize
62    }
63}
64
65pub trait DecodeTlvUnchecked {
66    /// Decode some data type using the TLV format, but skip validation checks.
67    ///
68    /// **Note**
69    ///
70    /// * The first byte should align with the TLV "type" byte.
71    /// * Does not perform any validation checks on the data.
72    fn decode_tlv_unchecked(buffer: impl AsRef<[u8]>) -> Self
73    where
74        Self: Sized;
75}
76
77pub trait DecodeTlvValueUnchecked {
78    /// Decode the value portion of a TLV data type without validation.
79    ///
80    /// **Note**
81    ///
82    /// * The first byte should align with the TLV value bytes.
83    /// * Does not perform any validation checks on the data.
84    fn decode_tlv_value_unchecked(buffer: impl AsRef<[u8]>) -> Self
85    where
86        Self: Sized;
87}
88
89impl DecodeTlvValueUnchecked for u8 {
90    fn decode_tlv_value_unchecked(buffer: impl AsRef<[u8]>) -> Self {
91        let mut buffer = buffer.as_ref();
92        buffer.get_u8()
93    }
94}
95
96impl DecodeTlvValueUnchecked for u16 {
97    fn decode_tlv_value_unchecked(buffer: impl AsRef<[u8]>) -> Self {
98        let mut buffer = buffer.as_ref();
99        buffer.get_u16()
100    }
101}
102
103impl DecodeTlvValueUnchecked for u32 {
104    fn decode_tlv_value_unchecked(buffer: impl AsRef<[u8]>) -> Self {
105        let mut buffer = buffer.as_ref();
106        buffer.get_u32()
107    }
108}
109
110impl DecodeTlvValueUnchecked for u64 {
111    fn decode_tlv_value_unchecked(buffer: impl AsRef<[u8]>) -> Self {
112        let mut buffer = buffer.as_ref();
113        buffer.get_u64()
114    }
115}
116
117impl<const N: usize> DecodeTlvValueUnchecked for [u8; N] {
118    fn decode_tlv_value_unchecked(buffer: impl AsRef<[u8]>) -> Self {
119        let mut buffer = buffer.as_ref();
120        let mut array = [0_u8; N];
121
122        let count = core::cmp::min(N, buffer.len());
123        for slot in array.iter_mut().take(count) {
124            *slot = buffer.get_u8();
125        }
126        array
127    }
128}
129
130pub trait TryEncodeTlv: TryEncodeTlvValue {
131    /// Encode some data type into the TLV format.
132    ///
133    /// Returns the number of bytes written to the buffer (including the type and length bytes).
134    fn try_encode_tlv(&self, buffer: &mut [u8]) -> Result<usize, TwineTlvError>;
135}
136
137pub trait TryEncodeTlvValue {
138    /// Encode the value portion of a TLV data type.
139    ///
140    /// Returns the number of bytes written to the buffer.
141    fn try_encode_tlv_value(&self, buffer: &mut [u8]) -> Result<usize, TwineTlvError>;
142}
143
144impl TryEncodeTlvValue for u8 {
145    fn try_encode_tlv_value(&self, buffer: &mut [u8]) -> Result<usize, TwineTlvError> {
146        let mut buffer = buffer;
147        buffer.put_u8(*self);
148        Ok(1)
149    }
150}
151
152impl TryEncodeTlvValue for u16 {
153    fn try_encode_tlv_value(&self, buffer: &mut [u8]) -> Result<usize, TwineTlvError> {
154        let mut buffer = buffer;
155        buffer.put_u16(*self);
156        Ok(2)
157    }
158}
159
160impl TryEncodeTlvValue for u32 {
161    fn try_encode_tlv_value(&self, buffer: &mut [u8]) -> Result<usize, TwineTlvError> {
162        let mut buffer = buffer;
163        buffer.put_u32(*self);
164        Ok(4)
165    }
166}
167
168impl TryEncodeTlvValue for u64 {
169    fn try_encode_tlv_value(&self, buffer: &mut [u8]) -> Result<usize, TwineTlvError> {
170        let mut buffer = buffer;
171        buffer.put_u64(*self);
172        Ok(8)
173    }
174}
175
176impl<const N: usize> TryEncodeTlvValue for [u8; N] {
177    fn try_encode_tlv_value(&self, buffer: &mut [u8]) -> Result<usize, TwineTlvError> {
178        let mut buffer = buffer;
179
180        if buffer.len() < N {
181            return Err(TwineTlvError::BufferEncodeTooShort);
182        }
183
184        for &byte in self.iter() {
185            buffer.put_u8(byte);
186        }
187
188        Ok(N)
189    }
190}
191
192pub trait TlvType {
193    /// The TLV type of the data.
194    const TLV_TYPE: u8;
195}
196
197pub trait TlvLength {
198    /// The length of the TLV value payload.
199    fn tlv_len(&self) -> usize;
200
201    /// Determine if the type has a constant length.
202    fn tlv_len_is_constant() -> bool {
203        false
204    }
205
206    /// The total length of the TLV, including type and length bytes.
207    fn tlv_total_len(&self) -> usize {
208        encoded_len!(self.tlv_len())
209    }
210}
211
212/// Generic metadata for a type that can be represented as a TLV.
213pub trait TlvMetadata: TlvType + TlvLength {}
214
215/// Metadata for a TLV that will always have a constant, fixed length.
216pub trait TlvConstantMetadata: TlvMetadata {
217    /// Constant expected length for the TLV
218    const TLV_LEN: usize;
219
220    /// Constant expected length of the entire TLV (including type and length bytes)
221    fn tlv_total_constant_len() -> usize {
222        encoded_len!(Self::TLV_LEN)
223    }
224}