1#![no_std]
2#![forbid(unsafe_code)]
3
4use core::marker::PhantomData;
5
6pub mod raw_tlv;
7
8#[cfg(feature = "alloc")]
9extern crate alloc;
10
11use raw_tlv::RawTLV;
12use scroll::{
13 ctx::{MeasureWith, SizeWith, TryFromCtx, TryIntoCtx},
14 Endian, Pread, Pwrite,
15};
16
17#[derive(Debug, Clone, PartialEq, Eq, Default)]
18pub struct TLV<Type, Length, EncodedType, Payload> {
26 pub tlv_type: EncodedType,
27 pub payload: Payload,
28 pub _phantom: PhantomData<(Type, Length)>,
29}
30impl<
31 'a,
32 Type: TryFromCtx<'a, Endian, Error = scroll::Error>
33 + TryIntoCtx<Endian, Error = scroll::Error>
34 + From<EncodedType>,
35 Length: 'a
36 + TryFromCtx<'a, Endian, Error = scroll::Error>
37 + TryIntoCtx<Endian, Error = scroll::Error>
38 + TryInto<usize>
39 + TryFrom<usize>,
40 EncodedType: 'a + From<Type>,
41 Payload: TryFromCtx<'a, Error = scroll::Error> + TryIntoCtx<Error = scroll::Error> + MeasureWith<()>,
42 > TLV<Type, Length, EncodedType, Payload>
43{
44 pub fn from_bytes(bytes: &'a [u8], big_endian: bool) -> Result<Self, scroll::Error> {
46 bytes.pread_with(
47 0,
48 if big_endian {
49 Endian::Big
50 } else {
51 Endian::Little
52 },
53 )
54 }
55 pub fn into_bytes(self, buf: &mut [u8], big_endian: bool) -> Result<usize, scroll::Error> {
57 buf.pwrite_with(
58 self,
59 0,
60 if big_endian {
61 Endian::Big
62 } else {
63 Endian::Little
64 },
65 )
66 }
67 pub fn into_bytes_capped<const N: usize>(
69 self,
70 big_endian: bool,
71 ) -> Result<heapless::Vec<u8, N>, scroll::Error> {
72 let mut buf = [0x00; N];
73 self.into_bytes(&mut buf, big_endian)?;
74 Ok(heapless::Vec::<u8, N>::from_slice(&buf).unwrap())
75 }
76
77 #[cfg(feature = "alloc")]
78 pub fn to_bytes_dynamic(
83 &'a self,
84 big_endian: bool,
85 ) -> Result<alloc::vec::Vec<u8>, scroll::Error> {
86 let mut buf = alloc::vec::Vec::new();
87 buf.reserve_exact(self.measure_with(&()));
88
89 self.clone().into_bytes(buf.as_mut_slice(), big_endian)?;
90 Ok(buf)
91 }
92}
93impl<Type: SizeWith, Length: SizeWith, EncodedType: From<Type>, Payload: MeasureWith<()>>
94 MeasureWith<()> for TLV<Type, Length, EncodedType, Payload>
95{
96 fn measure_with(&self, ctx: &()) -> usize {
97 Type::size_with(ctx) + Length::size_with(ctx) + self.payload.measure_with(ctx)
98 }
99}
100impl<
101 'a,
102 Type: TryFromCtx<'a, Endian, Error = scroll::Error>,
103 Length: TryFromCtx<'a, Endian, Error = scroll::Error> + TryInto<usize>,
104 EncodedType: 'a + From<Type>,
105 Payload: TryFromCtx<'a, Error = scroll::Error>,
106 > TryFromCtx<'a, Endian> for TLV<Type, Length, EncodedType, Payload>
107{
108 type Error = scroll::Error;
109 fn try_from_ctx(from: &'a [u8], ctx: Endian) -> Result<(Self, usize), Self::Error> {
110 let (raw_tlv, len) =
111 <RawTLV<'a, Type, Length> as TryFromCtx<'a, Endian>>::try_from_ctx(from, ctx)?;
112 Ok((
113 Self {
114 tlv_type: raw_tlv.tlv_type.into(),
115 payload: raw_tlv.slice.pread(0)?,
116 _phantom: PhantomData,
117 },
118 len,
119 ))
120 }
121}
122impl<
123 Type: TryIntoCtx<Endian, Error = scroll::Error>,
124 Length: TryIntoCtx<Endian, Error = scroll::Error> + TryFrom<usize>,
125 EncodedType: Into<Type>,
126 Payload: TryIntoCtx<Error = scroll::Error> + MeasureWith<()>,
127 > TryIntoCtx<Endian> for TLV<Type, Length, EncodedType, Payload>
128{
129 type Error = scroll::Error;
130 fn try_into_ctx(self, buf: &mut [u8], ctx: Endian) -> Result<usize, Self::Error> {
131 let mut offset = 0;
132
133 buf.gwrite_with(self.tlv_type.into(), &mut offset, ctx)?;
134
135 let len = match Length::try_from(self.payload.measure_with(&())) {
136 Ok(len) => len,
137 Err(_) => {
138 return Err(scroll::Error::BadInput {
139 size: offset,
140 msg: "Couldn't convert usize to Length",
141 })
142 }
143 };
144 buf.gwrite_with(len, &mut offset, ctx)?;
145
146 buf.gwrite(self.payload, &mut offset)?;
147
148 Ok(offset)
149 }
150}