ndn_tlv/
lib.rs

1//! # ndn-tlv -- TLV encoding foundation for NDN
2//!
3//! Implements the NDN Type-Length-Value wire format used by all other crates
4//! in the ndn-rs workspace. Parsing is zero-copy over `bytes::Bytes` buffers.
5//!
6//! ## Key types
7//!
8//! - [`TlvReader`] -- zero-copy, streaming TLV parser over byte slices.
9//! - [`TlvWriter`] -- growable encoder that produces wire-format `BytesMut`.
10//! - [`read_varu64`] / [`write_varu64`] -- NDN variable-width integer codec.
11//! - [`TlvError`] -- error type for malformed or truncated input.
12//!
13//! ## Feature flags
14//!
15//! - **`std`** (default) -- enables `std` support in `bytes`.
16//!   Disable for `no_std` environments (an allocator is still required).
17
18#![allow(missing_docs)]
19// Enable no_std when the `std` feature is disabled.
20// The crate still requires an allocator (for `BytesMut` / `Bytes`).
21#![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
33/// Read a variable-width unsigned integer (NDN varint encoding).
34///
35/// - 1 byte  if value < 253
36/// - 3 bytes if value < 65536   (marker 0xFD + 2 bytes big-endian)
37/// - 5 bytes if value < 2^32    (marker 0xFE + 4 bytes big-endian)
38/// - 9 bytes otherwise          (marker 0xFF + 8 bytes big-endian)
39pub 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
78/// Write a variable-width unsigned integer into `buf`.
79/// Returns the number of bytes written.
80pub 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
99/// Returns the number of bytes needed to encode `value` as a varint.
100pub 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    // ── varu64_size ────────────────────────────────────────────────────────────
117
118    #[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    // ── write_varu64 / read_varu64 round-trips ─────────────────────────────────
131
132    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    // ── read_varu64 error cases ────────────────────────────────────────────────
168
169    #[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        // 0xFD marker but value 100 (< 253) — should use 1-byte form.
198        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        // 0xFE marker but value 0x00FF (< 0x10000) — should use 3-byte form.
207        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        // 0xFF marker but value 0x0000_FFFF (< 0x1_0000_0000) — should use 5-byte form.
216        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        // Extra bytes after the value are not consumed and not an error.
225        let (v, n) = read_varu64(&[0x2A, 0xFF, 0xFF]).unwrap();
226        assert_eq!(v, 0x2A);
227        assert_eq!(n, 1);
228    }
229}