1#![no_std]
2use core::{
3 mem::size_of,
4 num::NonZeroUsize,
5 ops::{BitOrAssign, RangeFrom},
6};
7use nom::{
8 error::{make_error, ContextError, ErrorKind, ParseError},
9 IResult, InputIter, InputLength, Needed, Slice,
10};
11use num_traits::{PrimInt, Signed, WrappingNeg};
12
13const NEED_ONE: nom::Needed = Needed::Size(unsafe { NonZeroUsize::new_unchecked(1) });
15
16const fn leb128_size<T>() -> usize {
18 let bits = size_of::<T>() * 8;
19 (bits + 6) / 7 }
21
22macro_rules! impl_generic_leb128 {
23 ($fn_name:ident, $int_ty:ident, $post:tt, $int_name:expr) => {
24 #[doc="Recognizes an LEB128-encoded number that fits in a `"]
25 #[doc=$int_name]
26 #[doc="`."]
27 #[inline]
28 pub fn $fn_name<I, E>(input: I) -> IResult<I, $int_ty, E>
29 where
30 I: Clone + Slice<RangeFrom<usize>> + InputIter<Item = u8> + InputLength,
31 E: ParseError<I> + ContextError<I>,
32 {
33 let mut res = 0;
34 let mut shift = 0;
35
36 for (pos, byte) in input.iter_indices() {
37 if (byte & 0x80) == 0 {
38 res |= (byte as $int_ty) << shift;
39 $post(&mut res, shift, byte);
40 return Ok((input.slice(pos + 1..), res));
41 } else if pos == leb128_size::<$int_ty>() - 1 {
42 return Err(nom::Err::Error(E::add_context(
43 input.clone(),
44 concat!("LEB128 integer is too big to fit in ", $int_name),
45 make_error(input, ErrorKind::TooLarge),
46 )));
47 } else {
48 res |= ((byte & 0x7F) as $int_ty) << shift;
49 }
50 shift += 7;
51 }
52
53 Err(nom::Err::Incomplete(NEED_ONE))
54 }
55 };
56 ($fn_name:ident, $int_ty:ident, $post:tt) => {
57 impl_generic_leb128!($fn_name, $int_ty, $post, stringify!($int_ty));
58 };
59}
60
61macro_rules! impl_unsigned_leb128 {
62 ($fn_name:ident, $int_ty:ident) => {
63 impl_generic_leb128!($fn_name, $int_ty, (|_, _, _| {}));
64 };
65}
66
67impl_unsigned_leb128!(leb128_u8, u8);
68impl_unsigned_leb128!(leb128_u16, u16);
69impl_unsigned_leb128!(leb128_u32, u32);
70impl_unsigned_leb128!(leb128_u64, u64);
71impl_unsigned_leb128!(leb128_u128, u128);
72impl_unsigned_leb128!(leb128_usize, usize);
73
74#[inline]
75fn sign_extend<T: BitOrAssign + PrimInt + Signed + WrappingNeg>(
76 res: &mut T,
77 shift: usize,
78 byte: u8,
79) {
80 if (shift < size_of::<T>() * 8 - 7) && ((byte & 0x40) != 0) {
83 *res |= (T::one() << (shift + 7)).wrapping_neg()
85 }
86}
87
88macro_rules! impl_signed_leb128 {
89 ($fn_name:ident, $int_ty:ident) => {
90 impl_generic_leb128!($fn_name, $int_ty, sign_extend);
91 };
92}
93
94impl_signed_leb128!(leb128_i8, i8);
95impl_signed_leb128!(leb128_i16, i16);
96impl_signed_leb128!(leb128_i32, i32);
97impl_signed_leb128!(leb128_i64, i64);
98impl_signed_leb128!(leb128_i128, i128);
99impl_signed_leb128!(leb128_isize, isize);
100
101#[cfg(any(test, fuzzing))]
102mod tests;
103
104#[cfg(fuzzing)]
105pub use tests::{write_signed_leb128, write_unsigned_leb128};