| use core::convert::{AsRef, From}; |
| use core::{result, u8}; |
| |
| use crate::ctx::TryFromCtx; |
| use crate::{error, Pread}; |
| |
| #[derive(Debug, PartialEq, Copy, Clone)] |
| /// An unsigned leb128 integer |
| pub struct Uleb128 { |
| value: u64, |
| count: usize, |
| } |
| |
| impl Uleb128 { |
| #[inline] |
| /// Return how many bytes this Uleb128 takes up in memory |
| pub fn size(&self) -> usize { |
| self.count |
| } |
| #[inline] |
| /// Read a variable length u64 from `bytes` at `offset` |
| pub fn read(bytes: &[u8], offset: &mut usize) -> error::Result<u64> { |
| let tmp = bytes.pread::<Uleb128>(*offset)?; |
| *offset += tmp.size(); |
| Ok(tmp.into()) |
| } |
| } |
| |
| impl AsRef<u64> for Uleb128 { |
| fn as_ref(&self) -> &u64 { |
| &self.value |
| } |
| } |
| |
| impl From<Uleb128> for u64 { |
| #[inline] |
| fn from(uleb128: Uleb128) -> u64 { |
| uleb128.value |
| } |
| } |
| |
| #[derive(Debug, PartialEq, Copy, Clone)] |
| /// An signed leb128 integer |
| pub struct Sleb128 { |
| value: i64, |
| count: usize, |
| } |
| |
| impl Sleb128 { |
| #[inline] |
| /// Return how many bytes this Sleb128 takes up in memory |
| pub fn size(&self) -> usize { |
| self.count |
| } |
| #[inline] |
| /// Read a variable length i64 from `bytes` at `offset` |
| pub fn read(bytes: &[u8], offset: &mut usize) -> error::Result<i64> { |
| let tmp = bytes.pread::<Sleb128>(*offset)?; |
| *offset += tmp.size(); |
| Ok(tmp.into()) |
| } |
| } |
| |
| impl AsRef<i64> for Sleb128 { |
| fn as_ref(&self) -> &i64 { |
| &self.value |
| } |
| } |
| |
| impl From<Sleb128> for i64 { |
| #[inline] |
| fn from(sleb128: Sleb128) -> i64 { |
| sleb128.value |
| } |
| } |
| |
| // Below implementation heavily adapted from: https://github.com/fitzgen/leb128 |
| const CONTINUATION_BIT: u8 = 1 << 7; |
| const SIGN_BIT: u8 = 1 << 6; |
| |
| #[inline] |
| fn mask_continuation(byte: u8) -> u8 { |
| byte & !CONTINUATION_BIT |
| } |
| |
| // #[inline] |
| // fn mask_continuation_u64(val: u64) -> u8 { |
| // let byte = val & (u8::MAX as u64); |
| // mask_continuation(byte as u8) |
| // } |
| |
| impl<'a> TryFromCtx<'a> for Uleb128 { |
| type Error = error::Error; |
| #[inline] |
| fn try_from_ctx(src: &'a [u8], _ctx: ()) -> result::Result<(Self, usize), Self::Error> { |
| let mut result = 0; |
| let mut shift = 0; |
| let mut count = 0; |
| loop { |
| let byte: u8 = src.pread(count)?; |
| |
| if shift == 63 && byte != 0x00 && byte != 0x01 { |
| return Err(error::Error::BadInput { |
| size: src.len(), |
| msg: "failed to parse", |
| }); |
| } |
| |
| let low_bits = u64::from(mask_continuation(byte)); |
| result |= low_bits << shift; |
| |
| count += 1; |
| shift += 7; |
| |
| if byte & CONTINUATION_BIT == 0 { |
| return Ok(( |
| Uleb128 { |
| value: result, |
| count, |
| }, |
| count, |
| )); |
| } |
| } |
| } |
| } |
| |
| impl<'a> TryFromCtx<'a> for Sleb128 { |
| type Error = error::Error; |
| #[inline] |
| fn try_from_ctx(src: &'a [u8], _ctx: ()) -> result::Result<(Self, usize), Self::Error> { |
| let o = 0; |
| let offset = &mut 0; |
| let mut result = 0; |
| let mut shift = 0; |
| let size = 64; |
| let mut byte: u8; |
| loop { |
| byte = src.gread(offset)?; |
| |
| if shift == 63 && byte != 0x00 && byte != 0x7f { |
| return Err(error::Error::BadInput { |
| size: src.len(), |
| msg: "failed to parse", |
| }); |
| } |
| |
| let low_bits = i64::from(mask_continuation(byte)); |
| result |= low_bits << shift; |
| shift += 7; |
| |
| if byte & CONTINUATION_BIT == 0 { |
| break; |
| } |
| } |
| |
| if shift < size && (SIGN_BIT & byte) == SIGN_BIT { |
| // Sign extend the result. |
| result |= !0 << shift; |
| } |
| let count = *offset - o; |
| Ok(( |
| Sleb128 { |
| value: result, |
| count, |
| }, |
| count, |
| )) |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::super::LE; |
| use super::{Sleb128, Uleb128}; |
| |
| const CONTINUATION_BIT: u8 = 1 << 7; |
| //const SIGN_BIT: u8 = 1 << 6; |
| |
| #[test] |
| fn uleb_size() { |
| use super::super::Pread; |
| let buf = [2u8 | CONTINUATION_BIT, 1]; |
| let bytes = &buf[..]; |
| let num = bytes.pread::<Uleb128>(0).unwrap(); |
| #[cfg(feature = "std")] |
| println!("num: {num:?}"); |
| assert_eq!(130u64, num.into()); |
| assert_eq!(num.size(), 2); |
| |
| let buf = [0x00, 0x01]; |
| let bytes = &buf[..]; |
| let num = bytes.pread::<Uleb128>(0).unwrap(); |
| #[cfg(feature = "std")] |
| println!("num: {num:?}"); |
| assert_eq!(0u64, num.into()); |
| assert_eq!(num.size(), 1); |
| |
| let buf = [0x21]; |
| let bytes = &buf[..]; |
| let num = bytes.pread::<Uleb128>(0).unwrap(); |
| #[cfg(feature = "std")] |
| println!("num: {num:?}"); |
| assert_eq!(0x21u64, num.into()); |
| assert_eq!(num.size(), 1); |
| } |
| |
| #[test] |
| fn uleb128() { |
| use super::super::Pread; |
| let buf = [2u8 | CONTINUATION_BIT, 1]; |
| let bytes = &buf[..]; |
| let num = bytes.pread::<Uleb128>(0).expect("Should read Uleb128"); |
| assert_eq!(130u64, num.into()); |
| assert_eq!( |
| 386, |
| bytes.pread_with::<u16>(0, LE).expect("Should read number") |
| ); |
| } |
| |
| #[test] |
| fn uleb128_overflow() { |
| use super::super::Pread; |
| let buf = [ |
| 2u8 | CONTINUATION_BIT, |
| 2 | CONTINUATION_BIT, |
| 2 | CONTINUATION_BIT, |
| 2 | CONTINUATION_BIT, |
| 2 | CONTINUATION_BIT, |
| 2 | CONTINUATION_BIT, |
| 2 | CONTINUATION_BIT, |
| 2 | CONTINUATION_BIT, |
| 2 | CONTINUATION_BIT, |
| 2 | CONTINUATION_BIT, |
| 1, |
| ]; |
| let bytes = &buf[..]; |
| assert!(bytes.pread::<Uleb128>(0).is_err()); |
| } |
| |
| #[test] |
| fn sleb128() { |
| use super::super::Pread; |
| let bytes = [0x7fu8 | CONTINUATION_BIT, 0x7e]; |
| let num: i64 = bytes |
| .pread::<Sleb128>(0) |
| .expect("Should read Sleb128") |
| .into(); |
| assert_eq!(-129, num); |
| } |
| } |