nom_leb128/
lib.rs

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
13// TODO: when stable, use const NonZeroUsize::new(1).unwrap()
14const NEED_ONE: nom::Needed = Needed::Size(unsafe { NonZeroUsize::new_unchecked(1) });
15
16/// Maximum LEB128-encoded size of an integer type
17const fn leb128_size<T>() -> usize {
18    let bits = size_of::<T>() * 8;
19    (bits + 6) / 7 // equivalent to ceil(bits/7) w/o floats
20}
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    // leb128_generic skips the last shift update for efficiency on unsigned ints
81
82    if (shift < size_of::<T>() * 8 - 7) && ((byte & 0x40) != 0) {
83        // sign extend
84        *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};