251 lines
6.2 KiB
Rust
251 lines
6.2 KiB
Rust
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);
|
|
}
|
|
}
|