use core::convert::{AsRef, From};
use core::{result, u8};
use crate::ctx::TryFromCtx;
use crate::{error, Pread};
#[derive(Debug, PartialEq, Copy, Clone)]
pub struct Uleb128 {
value: u64,
count: usize,
}
impl Uleb128 {
#[inline]
pub fn size(&self) -> usize {
self.count
}
#[inline]
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)]
pub struct Sleb128 {
value: i64,
count: usize,
}
impl Sleb128 {
#[inline]
pub fn size(&self) -> usize {
self.count
}
#[inline]
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
}
}
const CONTINUATION_BIT: u8 = 1 << 7;
const SIGN_BIT: u8 = 1 << 6;
#[inline]
fn mask_continuation(byte: u8) -> u8 {
byte & !CONTINUATION_BIT
}
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 {
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;
#[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);
}
}