use crate::ctx::TryFromCtx; use crate::error; use crate::Pread; use core::convert::{AsRef, From}; use core::result; use core::u8; #[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 { let tmp = bytes.pread::(*offset)?; *offset += tmp.size(); Ok(tmp.into()) } } impl AsRef for Uleb128 { fn as_ref(&self) -> &u64 { &self.value } } impl From 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 { let tmp = bytes.pread::(*offset)?; *offset += tmp.size(); Ok(tmp.into()) } } impl AsRef for Sleb128 { fn as_ref(&self) -> &i64 { &self.value } } impl From 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::(0).unwrap(); println!("num: {:?}", &num); assert_eq!(130u64, num.into()); assert_eq!(num.size(), 2); let buf = [0x00, 0x01]; let bytes = &buf[..]; let num = bytes.pread::(0).unwrap(); println!("num: {:?}", &num); assert_eq!(0u64, num.into()); assert_eq!(num.size(), 1); let buf = [0x21]; let bytes = &buf[..]; let num = bytes.pread::(0).unwrap(); 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::(0).expect("Should read Uleb128"); assert_eq!(130u64, num.into()); assert_eq!( 386, bytes.pread_with::(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::(0).is_err()); } #[test] fn sleb128() { use super::super::Pread; let bytes = [0x7fu8 | CONTINUATION_BIT, 0x7e]; let num: i64 = bytes .pread::(0) .expect("Should read Sleb128") .into(); assert_eq!(-129, num); } }