length_delimited/
lib.rs

1#![doc = include_str!("../README.md")]
2#![cfg_attr(not(any(feature = "std", test)), no_std)]
3#![cfg_attr(docsrs, feature(doc_cfg))]
4#![cfg_attr(docsrs, allow(unused_attributes))]
5#![deny(missing_docs)]
6
7pub use dbutils::{
8  error::{IncompleteBuffer, InsufficientBuffer},
9  leb128::*,
10};
11
12#[cfg(all(not(feature = "std"), feature = "alloc"))]
13extern crate alloc as std;
14
15#[cfg(feature = "std")]
16extern crate std;
17
18pub use sealed::{Bounds, ErrorBounds};
19
20mod sealed;
21
22/// A type can be encode to a buffer.
23pub trait LengthDelimitedEncoder {
24  /// The encode error type that can be returned when encoding the type.
25  type Error;
26
27  /// Returns the encoded length of the message without a length delimiter.
28  fn encoded_len(&self) -> usize;
29
30  /// Returns the encoded length of the message with a length delimiter.
31  fn encoded_length_delimited_len(&self) -> usize;
32
33  /// Encodes the message with a length-delimiter to a buffer.
34  ///
35  /// An error will be returned if the buffer does not have sufficient capacity.
36  fn encode_length_delimited(&self, buf: &mut [u8]) -> Result<usize, Self::Error>;
37
38  /// Encodes the message to a buffer.
39  ///
40  /// An error will be returned if the buffer does not have sufficient capacity.
41  fn encode(&self, buf: &mut [u8]) -> Result<usize, Self::Error>;
42}
43
44/// A type can be encode to a buffer.
45pub trait LengthDelimitedDecoder: core::fmt::Debug {
46  /// The decode error type that can be returned when decoding the type.
47  type Error;
48
49  /// Decodes an instance of the message from a buffer.
50  ///
51  /// The entire buffer will be consumed.
52  fn decode(src: &[u8]) -> Result<(usize, Self), Self::Error>
53  where
54    Self: Sized;
55
56  /// Decodes a length-delimited instance of the message from the buffer.
57  fn decode_length_delimited(src: &[u8]) -> Result<(usize, Self), Self::Error>
58  where
59    Self: Sized;
60}
61
62macro_rules! impl_length_delimited {
63  ($($src:ty as $dst:ty), +$(,)?) => {
64    impl_length_delimited!(@encoder $($src as $dst), +);
65    impl_length_delimited!(@decoder $($src as $dst), +);
66  };
67  (@encoder $($src:ty as $dst:ty), +$(,)?) => {
68    $(
69      impl $crate::LengthDelimitedEncoder for $src {
70        type Error = <$dst as LengthDelimitedEncoder>::Error;
71
72        fn encoded_len(&self) -> usize {
73          <$dst as LengthDelimitedEncoder>::encoded_len(&(*self as $dst))
74        }
75
76        fn encoded_length_delimited_len(&self) -> usize {
77          <$dst as LengthDelimitedEncoder>::encoded_length_delimited_len(&(*self as $dst))
78        }
79
80        fn encode_length_delimited(&self, buf: &mut [u8]) -> Result<usize, Self::Error> {
81          <$dst as LengthDelimitedEncoder>::encode_length_delimited(&(*self as $dst), buf)
82        }
83
84        fn encode(&self, buf: &mut [u8]) -> Result<usize, Self::Error> {
85          <$dst as LengthDelimitedEncoder>::encode(&(*self as $dst), buf)
86        }
87      }
88    )*
89  };
90  (@decoder $($src:ty as $dst:ty), +$(,)?) => {
91    $(
92      impl $crate::LengthDelimitedDecoder for $src {
93        type Error = <$dst as $crate::LengthDelimitedDecoder>::Error;
94
95        fn decode(src: &[u8]) -> Result<(usize, Self), Self::Error>
96        where
97          Self: Sized,
98        {
99          <$dst as LengthDelimitedDecoder>::decode(src).map(|(read, val)| (read, val as $src))
100        }
101
102        fn decode_length_delimited(src: &[u8]) -> Result<(usize, Self), Self::Error>
103        where
104          Self: Sized,
105        {
106          <$dst as LengthDelimitedDecoder>::decode_length_delimited(src).map(|(read, val)| (read, val as $src))
107        }
108      }
109    )*
110  };
111}
112
113mod bytes;
114mod net;
115mod primitives;
116mod string;
117
118pub use bytes::*;
119pub use primitives::*;
120pub use string::*;
121
122#[cfg(test)]
123mod fuzz_tests {
124  use core::net::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6};
125
126  use super::*;
127  use quickcheck_macros::quickcheck;
128
129  #[cfg(feature = "std")]
130  extern crate std;
131
132  #[cfg(all(not(feature = "std"), feature = "alloc"))]
133  extern crate alloc as std;
134
135  #[cfg(any(feature = "std", feature = "alloc"))]
136  use core::num::NonZeroUsize;
137  #[cfg(any(feature = "std", feature = "alloc"))]
138  use std::{string::String, vec, vec::Vec};
139
140  // Helper function to test roundtrip property
141  fn test_roundtrip<T>(value: T) -> bool
142  where
143    T: LengthDelimitedEncoder + LengthDelimitedDecoder + std::fmt::Debug + PartialEq,
144    T: Clone,
145    <T as LengthDelimitedDecoder>::Error: std::fmt::Debug,
146    <T as LengthDelimitedEncoder>::Error: std::fmt::Debug,
147  {
148    let mut buffer = vec![0u8; value.encoded_length_delimited_len()];
149
150    // Test length delimited encoding/decoding
151    if let Ok(written) = value.encode_length_delimited(&mut buffer) {
152      if written != value.encoded_length_delimited_len() {
153        return false;
154      }
155
156      if let Ok((read, decoded)) = T::decode_length_delimited(&buffer[..written]) {
157        return read == written && value == decoded;
158      }
159    }
160
161    false
162  }
163
164  macro_rules! roundtrip {
165    ($(
166      $(#[$meta:meta])*
167      $ty:ty
168    ), +$(,)?) => {
169      paste::paste! {
170        $(
171          #[quickcheck]
172          $(#[$meta])*
173          fn [<fuzz_ $ty:snake _roundtrip>](val: $ty) -> bool {
174            test_roundtrip(val)
175          }
176        )*
177      }
178    };
179  }
180
181  roundtrip!(
182    u8,
183    u16,
184    u32,
185    u64,
186    u128,
187    i8,
188    i16,
189    i32,
190    i64,
191    i128,
192    bool,
193    char,
194    Ipv4Addr,
195    Ipv6Addr,
196    SocketAddrV4,
197    #[cfg(any(feature = "std", feature = "alloc"))]
198    String,
199  );
200
201  #[quickcheck]
202  fn fuzz_f32_roundtrip(value: f32) -> bool {
203    let mut buffer = vec![0u8; value.encoded_length_delimited_len()];
204
205    // Test length delimited encoding/decoding
206    if let Ok(written) = value.encode_length_delimited(&mut buffer) {
207      if written != value.encoded_length_delimited_len() {
208        return false;
209      }
210
211      if let Ok((read, decoded)) = f32::decode_length_delimited(&buffer[..written]) {
212        if value.is_nan() {
213          return read == written && decoded.is_nan();
214        }
215        return read == written && value == decoded;
216      }
217    }
218
219    false
220  }
221
222  #[quickcheck]
223  fn fuzz_f64_roundtrip(value: f64) -> bool {
224    let mut buffer = vec![0u8; value.encoded_length_delimited_len()];
225
226    // Test length delimited encoding/decoding
227    if let Ok(written) = value.encode_length_delimited(&mut buffer) {
228      if written != value.encoded_length_delimited_len() {
229        return false;
230      }
231
232      if let Ok((read, decoded)) = f64::decode_length_delimited(&buffer[..written]) {
233        if value.is_nan() {
234          return read == written && decoded.is_nan();
235        }
236        return read == written && value == decoded;
237      }
238    }
239
240    false
241  }
242
243  #[quickcheck]
244  fn fuzz_socket_addr_v6_roundtrip(ip: Ipv6Addr, port: u16) -> bool {
245    test_roundtrip(SocketAddrV6::new(ip, port, 0, 0))
246  }
247
248  #[quickcheck]
249  fn fuzz_fixed_array_roundtrip(a: u8, b: u8, c: u8, d: u8) -> bool {
250    test_roundtrip([a, b, c, d])
251  }
252
253  #[quickcheck]
254  #[cfg(any(feature = "std", feature = "alloc"))]
255  fn fuzz_bytes_roundtrip(bytes: Vec<u8>) -> bool {
256    test_roundtrip(bytes)
257  }
258
259  // Test encoding with insufficient buffer
260  #[quickcheck]
261  #[cfg(any(feature = "std", feature = "alloc"))]
262  fn fuzz_insufficient_buffer(value: String, trim: NonZeroUsize) -> bool {
263    let required_len = value.encoded_length_delimited_len();
264    let trim: usize = trim.into();
265    if trim >= required_len {
266      return true; // Skip test if trim would make buffer larger than needed
267    }
268
269    let mut buffer = vec![0u8; required_len - trim];
270    value.encode_length_delimited(&mut buffer).is_err()
271  }
272
273  // Test decoding with incomplete data
274  #[quickcheck]
275  #[cfg(any(feature = "std", feature = "alloc"))]
276  fn fuzz_incomplete_decode(value: String, trim: NonZeroUsize) -> bool {
277    let mut buffer = vec![0u8; value.encoded_length_delimited_len()];
278    let trim: usize = trim.into();
279    if let Ok(written) = value.encode_length_delimited(&mut buffer) {
280      if trim >= written {
281        return true; // Skip test if trim would remove entire buffer
282      }
283
284      <String>::decode_length_delimited(&buffer[..written - trim]).is_err()
285    } else {
286      false
287    }
288  }
289
290  // Test with random buffer content
291  #[quickcheck]
292  #[cfg(any(feature = "std", feature = "alloc"))]
293  fn fuzz_random_buffer(buffer: Vec<u8>) -> bool {
294    // Attempt to decode various types from random buffer
295    // Should either succeed and roundtrip correctly, or fail gracefully
296    let string_result = <String>::decode_length_delimited(&buffer);
297    let u64_result = u64::decode_length_delimited(&buffer);
298    let ipv4_result = Ipv4Addr::decode_length_delimited(&buffer);
299
300    match (string_result, u64_result, ipv4_result) {
301      (Ok((_, s)), _, _) => {
302        // If we successfully decoded a string, verify roundtrip
303        let mut new_buffer = vec![0u8; s.encoded_length_delimited_len()];
304        if let Ok(written) = s.encode_length_delimited(&mut new_buffer) {
305          if let Ok((read, decoded)) = String::decode_length_delimited(&new_buffer[..written]) {
306            return read == written && s == decoded;
307          }
308        }
309        false
310      }
311      (Err(_), _, _) => true, // Error is acceptable for random data
312    }
313  }
314}