1#![allow(missing_docs)]
19#![cfg_attr(not(feature = "std"), no_std)]
22#[cfg(not(feature = "std"))]
23extern crate alloc;
24
25pub mod error;
26pub mod reader;
27pub mod writer;
28
29pub use error::TlvError;
30pub use reader::TlvReader;
31pub use writer::TlvWriter;
32
33pub fn read_varu64(buf: &[u8]) -> Result<(u64, usize), TlvError> {
40 let first = *buf.first().ok_or(TlvError::UnexpectedEof)?;
41 match first {
42 0..=252 => Ok((first as u64, 1)),
43 253 => {
44 if buf.len() < 3 {
45 return Err(TlvError::UnexpectedEof);
46 }
47 let v = u16::from_be_bytes([buf[1], buf[2]]);
48 if v < 253 {
49 return Err(TlvError::NonMinimalVarNumber);
50 }
51 Ok((v as u64, 3))
52 }
53 254 => {
54 if buf.len() < 5 {
55 return Err(TlvError::UnexpectedEof);
56 }
57 let v = u32::from_be_bytes([buf[1], buf[2], buf[3], buf[4]]);
58 if v < 0x1_0000 {
59 return Err(TlvError::NonMinimalVarNumber);
60 }
61 Ok((v as u64, 5))
62 }
63 255 => {
64 if buf.len() < 9 {
65 return Err(TlvError::UnexpectedEof);
66 }
67 let v = u64::from_be_bytes([
68 buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8],
69 ]);
70 if v < 0x1_0000_0000 {
71 return Err(TlvError::NonMinimalVarNumber);
72 }
73 Ok((v, 9))
74 }
75 }
76}
77
78pub fn write_varu64(buf: &mut [u8], value: u64) -> usize {
81 if value < 253 {
82 buf[0] = value as u8;
83 1
84 } else if value < 0x1_0000 {
85 buf[0] = 0xFD;
86 buf[1..3].copy_from_slice(&(value as u16).to_be_bytes());
87 3
88 } else if value < 0x1_0000_0000 {
89 buf[0] = 0xFE;
90 buf[1..5].copy_from_slice(&(value as u32).to_be_bytes());
91 5
92 } else {
93 buf[0] = 0xFF;
94 buf[1..9].copy_from_slice(&value.to_be_bytes());
95 9
96 }
97}
98
99pub fn varu64_size(value: u64) -> usize {
101 if value < 253 {
102 1
103 } else if value < 0x1_0000 {
104 3
105 } else if value < 0x1_0000_0000 {
106 5
107 } else {
108 9
109 }
110}
111
112#[cfg(test)]
113mod tests {
114 use super::*;
115
116 #[test]
119 fn varu64_size_boundaries() {
120 assert_eq!(varu64_size(0), 1);
121 assert_eq!(varu64_size(252), 1);
122 assert_eq!(varu64_size(253), 3);
123 assert_eq!(varu64_size(0xFFFF), 3);
124 assert_eq!(varu64_size(0x1_0000), 5);
125 assert_eq!(varu64_size(0xFFFF_FFFF), 5);
126 assert_eq!(varu64_size(0x1_0000_0000), 9);
127 assert_eq!(varu64_size(u64::MAX), 9);
128 }
129
130 fn roundtrip(value: u64) {
133 let mut buf = [0u8; 9];
134 let written = write_varu64(&mut buf, value);
135 assert_eq!(written, varu64_size(value));
136 let (decoded, read) = read_varu64(&buf[..written]).unwrap();
137 assert_eq!(decoded, value);
138 assert_eq!(read, written);
139 }
140
141 #[test]
142 fn varu64_roundtrip_1byte() {
143 roundtrip(0);
144 roundtrip(1);
145 roundtrip(252);
146 }
147
148 #[test]
149 fn varu64_roundtrip_3byte() {
150 roundtrip(253);
151 roundtrip(254);
152 roundtrip(0xFFFF);
153 }
154
155 #[test]
156 fn varu64_roundtrip_5byte() {
157 roundtrip(0x1_0000);
158 roundtrip(0xFFFF_FFFF);
159 }
160
161 #[test]
162 fn varu64_roundtrip_9byte() {
163 roundtrip(0x1_0000_0000);
164 roundtrip(u64::MAX);
165 }
166
167 #[test]
170 fn read_varu64_eof_empty() {
171 assert_eq!(read_varu64(&[]), Err(TlvError::UnexpectedEof));
172 }
173
174 #[test]
175 fn read_varu64_eof_truncated_3byte() {
176 assert_eq!(read_varu64(&[0xFD, 0x01]), Err(TlvError::UnexpectedEof));
177 }
178
179 #[test]
180 fn read_varu64_eof_truncated_5byte() {
181 assert_eq!(
182 read_varu64(&[0xFE, 0x00, 0x01, 0x00]),
183 Err(TlvError::UnexpectedEof)
184 );
185 }
186
187 #[test]
188 fn read_varu64_eof_truncated_9byte() {
189 assert_eq!(
190 read_varu64(&[0xFF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00]),
191 Err(TlvError::UnexpectedEof)
192 );
193 }
194
195 #[test]
196 fn read_varu64_rejects_non_minimal_3byte() {
197 assert_eq!(
199 read_varu64(&[0xFD, 0x00, 0x64]),
200 Err(TlvError::NonMinimalVarNumber)
201 );
202 }
203
204 #[test]
205 fn read_varu64_rejects_non_minimal_5byte() {
206 assert_eq!(
208 read_varu64(&[0xFE, 0x00, 0x00, 0x00, 0xFF]),
209 Err(TlvError::NonMinimalVarNumber)
210 );
211 }
212
213 #[test]
214 fn read_varu64_rejects_non_minimal_9byte() {
215 assert_eq!(
217 read_varu64(&[0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF]),
218 Err(TlvError::NonMinimalVarNumber)
219 );
220 }
221
222 #[test]
223 fn read_varu64_ignores_trailing_bytes() {
224 let (v, n) = read_varu64(&[0x2A, 0xFF, 0xFF]).unwrap();
226 assert_eq!(v, 0x2A);
227 assert_eq!(n, 1);
228 }
229}