scroll/
leb128.rs

1use core::convert::{AsRef, From};
2use core::result;
3
4use crate::ctx::TryFromCtx;
5use crate::{Pread, error};
6
7#[derive(Debug, PartialEq, Copy, Clone)]
8/// An unsigned leb128 integer
9pub struct Uleb128 {
10    value: u64,
11    count: usize,
12}
13
14impl Uleb128 {
15    #[inline]
16    /// Return how many bytes this Uleb128 takes up in memory
17    pub fn size(&self) -> usize {
18        self.count
19    }
20    #[inline]
21    /// Read a variable length u64 from `bytes` at `offset`
22    pub fn read(bytes: &[u8], offset: &mut usize) -> error::Result<u64> {
23        let tmp = bytes.pread::<Uleb128>(*offset)?;
24        *offset += tmp.size();
25        Ok(tmp.into())
26    }
27}
28
29impl AsRef<u64> for Uleb128 {
30    fn as_ref(&self) -> &u64 {
31        &self.value
32    }
33}
34
35impl From<Uleb128> for u64 {
36    #[inline]
37    fn from(uleb128: Uleb128) -> u64 {
38        uleb128.value
39    }
40}
41
42#[derive(Debug, PartialEq, Copy, Clone)]
43/// An signed leb128 integer
44pub struct Sleb128 {
45    value: i64,
46    count: usize,
47}
48
49impl Sleb128 {
50    #[inline]
51    /// Return how many bytes this Sleb128 takes up in memory
52    pub fn size(&self) -> usize {
53        self.count
54    }
55    #[inline]
56    /// Read a variable length i64 from `bytes` at `offset`
57    pub fn read(bytes: &[u8], offset: &mut usize) -> error::Result<i64> {
58        let tmp = bytes.pread::<Sleb128>(*offset)?;
59        *offset += tmp.size();
60        Ok(tmp.into())
61    }
62}
63
64impl AsRef<i64> for Sleb128 {
65    fn as_ref(&self) -> &i64 {
66        &self.value
67    }
68}
69
70impl From<Sleb128> for i64 {
71    #[inline]
72    fn from(sleb128: Sleb128) -> i64 {
73        sleb128.value
74    }
75}
76
77// Below implementation heavily adapted from: https://github.com/fitzgen/leb128
78const CONTINUATION_BIT: u8 = 1 << 7;
79const SIGN_BIT: u8 = 1 << 6;
80
81#[inline]
82fn mask_continuation(byte: u8) -> u8 {
83    byte & !CONTINUATION_BIT
84}
85
86// #[inline]
87// fn mask_continuation_u64(val: u64) -> u8 {
88//     let byte = val & (u8::MAX as u64);
89//     mask_continuation(byte as u8)
90// }
91
92impl<'a> TryFromCtx<'a> for Uleb128 {
93    type Error = error::Error;
94    #[inline]
95    fn try_from_ctx(src: &'a [u8], _ctx: ()) -> result::Result<(Self, usize), Self::Error> {
96        let mut result = 0;
97        let mut shift = 0;
98        let mut count = 0;
99        loop {
100            let byte: u8 = src.pread(count)?;
101
102            if shift == 63 && byte != 0x00 && byte != 0x01 {
103                return Err(error::Error::BadInput {
104                    size: src.len(),
105                    msg: "failed to parse",
106                });
107            }
108
109            let low_bits = u64::from(mask_continuation(byte));
110            result |= low_bits << shift;
111
112            count += 1;
113            shift += 7;
114
115            if byte & CONTINUATION_BIT == 0 {
116                return Ok((
117                    Uleb128 {
118                        value: result,
119                        count,
120                    },
121                    count,
122                ));
123            }
124        }
125    }
126}
127
128impl<'a> TryFromCtx<'a> for Sleb128 {
129    type Error = error::Error;
130    #[inline]
131    fn try_from_ctx(src: &'a [u8], _ctx: ()) -> result::Result<(Self, usize), Self::Error> {
132        let o = 0;
133        let offset = &mut 0;
134        let mut result = 0;
135        let mut shift = 0;
136        let size = 64;
137        let mut byte: u8;
138        loop {
139            byte = src.gread(offset)?;
140
141            if shift == 63 && byte != 0x00 && byte != 0x7f {
142                return Err(error::Error::BadInput {
143                    size: src.len(),
144                    msg: "failed to parse",
145                });
146            }
147
148            let low_bits = i64::from(mask_continuation(byte));
149            result |= low_bits << shift;
150            shift += 7;
151
152            if byte & CONTINUATION_BIT == 0 {
153                break;
154            }
155        }
156
157        if shift < size && (SIGN_BIT & byte) == SIGN_BIT {
158            // Sign extend the result.
159            result |= !0 << shift;
160        }
161        let count = *offset - o;
162        Ok((
163            Sleb128 {
164                value: result,
165                count,
166            },
167            count,
168        ))
169    }
170}
171
172#[cfg(test)]
173mod tests {
174    use super::super::LE;
175    use super::{Sleb128, Uleb128};
176
177    const CONTINUATION_BIT: u8 = 1 << 7;
178    //const SIGN_BIT: u8 = 1 << 6;
179
180    #[test]
181    fn uleb_size() {
182        use super::super::Pread;
183        let buf = [2u8 | CONTINUATION_BIT, 1];
184        let bytes = &buf[..];
185        let num = bytes.pread::<Uleb128>(0).unwrap();
186        #[cfg(feature = "std")]
187        println!("num: {num:?}");
188        assert_eq!(130u64, num.into());
189        assert_eq!(num.size(), 2);
190
191        let buf = [0x00, 0x01];
192        let bytes = &buf[..];
193        let num = bytes.pread::<Uleb128>(0).unwrap();
194        #[cfg(feature = "std")]
195        println!("num: {num:?}");
196        assert_eq!(0u64, num.into());
197        assert_eq!(num.size(), 1);
198
199        let buf = [0x21];
200        let bytes = &buf[..];
201        let num = bytes.pread::<Uleb128>(0).unwrap();
202        #[cfg(feature = "std")]
203        println!("num: {num:?}");
204        assert_eq!(0x21u64, num.into());
205        assert_eq!(num.size(), 1);
206    }
207
208    #[test]
209    fn uleb128() {
210        use super::super::Pread;
211        let buf = [2u8 | CONTINUATION_BIT, 1];
212        let bytes = &buf[..];
213        let num = bytes.pread::<Uleb128>(0).expect("Should read Uleb128");
214        assert_eq!(130u64, num.into());
215        assert_eq!(
216            386,
217            bytes.pread_with::<u16>(0, LE).expect("Should read number")
218        );
219    }
220
221    #[test]
222    fn uleb128_overflow() {
223        use super::super::Pread;
224        let buf = [
225            2u8 | CONTINUATION_BIT,
226            2 | CONTINUATION_BIT,
227            2 | CONTINUATION_BIT,
228            2 | CONTINUATION_BIT,
229            2 | CONTINUATION_BIT,
230            2 | CONTINUATION_BIT,
231            2 | CONTINUATION_BIT,
232            2 | CONTINUATION_BIT,
233            2 | CONTINUATION_BIT,
234            2 | CONTINUATION_BIT,
235            1,
236        ];
237        let bytes = &buf[..];
238        assert!(bytes.pread::<Uleb128>(0).is_err());
239    }
240
241    #[test]
242    fn sleb128() {
243        use super::super::Pread;
244        let bytes = [0x7fu8 | CONTINUATION_BIT, 0x7e];
245        let num: i64 = bytes
246            .pread::<Sleb128>(0)
247            .expect("Should read Sleb128")
248            .into();
249        assert_eq!(-129, num);
250    }
251}