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
22pub trait LengthDelimitedEncoder {
24 type Error;
26
27 fn encoded_len(&self) -> usize;
29
30 fn encoded_length_delimited_len(&self) -> usize;
32
33 fn encode_length_delimited(&self, buf: &mut [u8]) -> Result<usize, Self::Error>;
37
38 fn encode(&self, buf: &mut [u8]) -> Result<usize, Self::Error>;
42}
43
44pub trait LengthDelimitedDecoder: core::fmt::Debug {
46 type Error;
48
49 fn decode(src: &[u8]) -> Result<(usize, Self), Self::Error>
53 where
54 Self: Sized;
55
56 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 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 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 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 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 #[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; }
268
269 let mut buffer = vec![0u8; required_len - trim];
270 value.encode_length_delimited(&mut buffer).is_err()
271 }
272
273 #[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; }
283
284 <String>::decode_length_delimited(&buffer[..written - trim]).is_err()
285 } else {
286 false
287 }
288 }
289
290 #[quickcheck]
292 #[cfg(any(feature = "std", feature = "alloc"))]
293 fn fuzz_random_buffer(buffer: Vec<u8>) -> bool {
294 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 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, }
313 }
314}