summaryrefslogtreecommitdiffstats
path: root/third_party/rust/scroll/src/leb128.rs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--third_party/rust/scroll/src/leb128.rs249
1 files changed, 249 insertions, 0 deletions
diff --git a/third_party/rust/scroll/src/leb128.rs b/third_party/rust/scroll/src/leb128.rs
new file mode 100644
index 0000000000..43f50b95f1
--- /dev/null
+++ b/third_party/rust/scroll/src/leb128.rs
@@ -0,0 +1,249 @@
+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<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();
+ 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();
+ 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();
+ 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);
+ }
+}