diff options
Diffstat (limited to 'third_party/rust/scroll/src')
-rw-r--r-- | third_party/rust/scroll/src/ctx.rs | 638 | ||||
-rw-r--r-- | third_party/rust/scroll/src/endian.rs | 47 | ||||
-rw-r--r-- | third_party/rust/scroll/src/error.rs | 68 | ||||
-rw-r--r-- | third_party/rust/scroll/src/greater.rs | 151 | ||||
-rw-r--r-- | third_party/rust/scroll/src/leb128.rs | 223 | ||||
-rw-r--r-- | third_party/rust/scroll/src/lesser.rs | 169 | ||||
-rw-r--r-- | third_party/rust/scroll/src/lib.rs | 526 | ||||
-rw-r--r-- | third_party/rust/scroll/src/pread.rs | 203 | ||||
-rw-r--r-- | third_party/rust/scroll/src/pwrite.rs | 79 |
9 files changed, 2104 insertions, 0 deletions
diff --git a/third_party/rust/scroll/src/ctx.rs b/third_party/rust/scroll/src/ctx.rs new file mode 100644 index 0000000000..46bd835051 --- /dev/null +++ b/third_party/rust/scroll/src/ctx.rs @@ -0,0 +1,638 @@ +//! Generic context-aware conversion traits, for automatic _downstream_ extension of `Pread`, et. al +//! +//! # Discussion +//! +//! Implementors of `TryFromCtx` automatically grant any client user of `pread, pwrite, gread, gwrite` the ability to parse their structure out of the source it has been implemented for, typically `&[u8]`. +//! +//! The implementor only needs to specify the error type, and the type of their size, and then implement the parsing/marshalling logic given a byte sequence, starting at the offset `pread`, et. al was called at, with the context you have implemented it for. +//! +//! Returning the size allows dynamic content (e.g., `&str`s) to be parsed alongside fixed size content (e.g., `u64`). The parsing context is any information you the implementor need to correctly parse out your datatype - this could be the endianness of the type, more offsets, or other complex data. The only requirement is that your `Ctx` be `Copy`, and hence encourages lightweight contexts (but this isn't required of course). +//! +//! +//! # Example +//! +//! Suppose we have a datatype and we want to specify how to parse or serialize this datatype out of some arbitrary +//! byte buffer. In order to do this, we need to provide a `TryFromCtx` impl for our datatype. In particular, if we +//! do this for the `[u8]` target, with a "parsing contex", `YourCtx`, you will automatically get access to +//! calling `pread_with::<YourDatatype>(offset, your_ctx)` on arrays of bytes. +//! +//! In the example below, we implement `TryFromCtx` using the `Endian` parsing context provided by `scroll`, which is used to specifying the endianness at which numbers should be parsed, but you could provide anything, as long as it implements `Copy`. +//! +//! ```rust +//! use scroll::{self, ctx, Endian, Pread, BE}; +//! +//! struct Data<'a> { +//! name: &'a str, +//! id: u32, +//! } +//! +//! impl<'a> ctx::TryFromCtx<'a, Endian> for Data<'a> { +//! type Error = scroll::Error; +//! fn try_from_ctx (src: &'a [u8], ctx: Endian) +//! -> Result<(Self, usize), Self::Error> { +//! let name = src.pread::<&str>(0)?; +//! let id = src.pread_with(name.len() + 1, ctx)?; +//! Ok((Data { name: name, id: id }, name.len() + 1 + 4)) +//! } +//! } +//! +//! let bytes = b"UserName\x00\x01\x02\x03\x04"; +//! let data = bytes.pread_with::<Data>(0, BE).unwrap(); +//! assert_eq!(data.id, 0x01020304); +//! assert_eq!(data.name.to_string(), "UserName".to_string()); +//! +//! ``` + +use core::ptr::copy_nonoverlapping; +use core::mem::transmute; +use core::mem::size_of; +use core::str; +use core::result; + +#[cfg(feature = "std")] +use std::ffi::{CStr, CString}; + +use crate::error; +use crate::endian::Endian; + +/// A trait for measuring how large something is; for a byte sequence, it will be its length. +pub trait MeasureWith<Ctx> { + /// How large is `Self`, given the `ctx`? + fn measure_with(&self, ctx: &Ctx) -> usize; +} + +impl<Ctx> MeasureWith<Ctx> for [u8] { + #[inline] + fn measure_with(&self, _ctx: &Ctx) -> usize { + self.len() + } +} + +impl<Ctx, T: AsRef<[u8]>> MeasureWith<Ctx> for T { + #[inline] + fn measure_with(&self, _ctx: &Ctx) -> usize { + self.as_ref().len() + } +} + +/// The parsing context for converting a byte sequence to a `&str` +/// +/// `StrCtx` specifies what byte delimiter to use, and defaults to C-style null terminators. Be careful. +#[derive(Debug, Copy, Clone)] +pub enum StrCtx { + Delimiter(u8), + DelimiterUntil(u8, usize), + Length(usize), +} + +/// A C-style, null terminator based delimiter +pub const NULL: u8 = 0; +/// A space-based delimiter +pub const SPACE: u8 = 0x20; +/// A newline-based delimiter +pub const RET: u8 = 0x0a; +/// A tab-based delimiter +pub const TAB: u8 = 0x09; + +impl Default for StrCtx { + #[inline] + fn default() -> Self { + StrCtx::Delimiter(NULL) + } +} + +impl StrCtx { + pub fn len(&self) -> usize { + match *self { + StrCtx::Delimiter(_) | + StrCtx::DelimiterUntil(_, _) => 1, + StrCtx::Length(_) => 0, + } + } + + pub fn is_empty(&self) -> bool { + if let StrCtx::Length(_) = *self { true } else { false } + } +} + +/// Reads `Self` from `This` using the context `Ctx`; must _not_ fail +pub trait FromCtx<Ctx: Copy = (), This: ?Sized = [u8]> { + fn from_ctx(this: &This, ctx: Ctx) -> Self; +} + +/// Tries to read `Self` from `This` using the context `Ctx` +pub trait TryFromCtx<'a, Ctx: Copy = (), This: ?Sized = [u8]> where Self: 'a + Sized { + type Error; + fn try_from_ctx(from: &'a This, ctx: Ctx) -> Result<(Self, usize), Self::Error>; +} + +/// Writes `Self` into `This` using the context `Ctx` +pub trait IntoCtx<Ctx: Copy = (), This: ?Sized = [u8]>: Sized { + fn into_ctx(self, _: &mut This, ctx: Ctx); +} + +/// Tries to write `Self` into `This` using the context `Ctx` +pub trait TryIntoCtx<Ctx: Copy = (), This: ?Sized = [u8]>: Sized { + type Error; + fn try_into_ctx(self, _: &mut This, ctx: Ctx) -> Result<usize, Self::Error>; +} + +/// Gets the size of `Self` with a `Ctx`, and in `Self::Units`. Implementors can then call `Gread` related functions +/// +/// The rationale behind this trait is to: +/// +/// 1. Prevent `gread` from being used, and the offset being modified based on simply the sizeof the value, which can be a misnomer, e.g., for Leb128, etc. +/// 2. Allow a context based size, which is useful for 32/64 bit variants for various containers, etc. +pub trait SizeWith<Ctx = ()> { + fn size_with(ctx: &Ctx) -> usize; +} + +macro_rules! signed_to_unsigned { + (i8) => {u8 }; + (u8) => {u8 }; + (i16) => {u16}; + (u16) => {u16}; + (i32) => {u32}; + (u32) => {u32}; + (i64) => {u64}; + (u64) => {u64}; + (i128) => {u128}; + (u128) => {u128}; + (f32) => {u32}; + (f64) => {u64}; +} + +macro_rules! write_into { + ($typ:ty, $size:expr, $n:expr, $dst:expr, $endian:expr) => ({ + unsafe { + assert!($dst.len() >= $size); + let bytes = transmute::<$typ, [u8; $size]>(if $endian.is_little() { $n.to_le() } else { $n.to_be() }); + copy_nonoverlapping((&bytes).as_ptr(), $dst.as_mut_ptr(), $size); + } + }); +} + +macro_rules! into_ctx_impl { + ($typ:tt, $size:expr) => { + impl IntoCtx<Endian> for $typ { + #[inline] + fn into_ctx(self, dst: &mut [u8], le: Endian) { + assert!(dst.len() >= $size); + write_into!($typ, $size, self, dst, le); + } + } + impl<'a> IntoCtx<Endian> for &'a $typ { + #[inline] + fn into_ctx(self, dst: &mut [u8], le: Endian) { + (*self).into_ctx(dst, le) + } + } + impl TryIntoCtx<Endian> for $typ where $typ: IntoCtx<Endian> { + type Error = error::Error; + #[inline] + fn try_into_ctx(self, dst: &mut [u8], le: Endian) -> error::Result<usize> { + if $size > dst.len () { + Err(error::Error::TooBig{size: $size, len: dst.len()}) + } else { + <$typ as IntoCtx<Endian>>::into_ctx(self, dst, le); + Ok($size) + } + } + } + impl<'a> TryIntoCtx<Endian> for &'a $typ { + type Error = error::Error; + #[inline] + fn try_into_ctx(self, dst: &mut [u8], le: Endian) -> error::Result<usize> { + (*self).try_into_ctx(dst, le) + } + } + } +} + +macro_rules! from_ctx_impl { + ($typ:tt, $size:expr) => { + impl<'a> FromCtx<Endian> for $typ { + #[inline] + fn from_ctx(src: &[u8], le: Endian) -> Self { + assert!(src.len() >= $size); + let mut data: signed_to_unsigned!($typ) = 0; + unsafe { + copy_nonoverlapping( + src.as_ptr(), + &mut data as *mut signed_to_unsigned!($typ) as *mut u8, + $size); + } + (if le.is_little() { data.to_le() } else { data.to_be() }) as $typ + } + } + + impl<'a> TryFromCtx<'a, Endian> for $typ where $typ: FromCtx<Endian> { + type Error = error::Error; + #[inline] + fn try_from_ctx(src: &'a [u8], le: Endian) -> result::Result<(Self, usize), Self::Error> { + if $size > src.len () { + Err(error::Error::TooBig{size: $size, len: src.len()}) + } else { + Ok((FromCtx::from_ctx(&src, le), $size)) + } + } + } + // as ref + impl<'a, T> FromCtx<Endian, T> for $typ where T: AsRef<[u8]> { + #[inline] + fn from_ctx(src: &T, le: Endian) -> Self { + let src = src.as_ref(); + assert!(src.len() >= $size); + let mut data: signed_to_unsigned!($typ) = 0; + unsafe { + copy_nonoverlapping( + src.as_ptr(), + &mut data as *mut signed_to_unsigned!($typ) as *mut u8, + $size); + } + (if le.is_little() { data.to_le() } else { data.to_be() }) as $typ + } + } + + impl<'a, T> TryFromCtx<'a, Endian, T> for $typ where $typ: FromCtx<Endian, T>, T: AsRef<[u8]> { + type Error = error::Error; + #[inline] + fn try_from_ctx(src: &'a T, le: Endian) -> result::Result<(Self, usize), Self::Error> { + let src = src.as_ref(); + Self::try_from_ctx(src, le) + } + } + }; +} + +macro_rules! ctx_impl { + ($typ:tt, $size:expr) => { + from_ctx_impl!($typ, $size); + }; +} + +ctx_impl!(u8, 1); +ctx_impl!(i8, 1); +ctx_impl!(u16, 2); +ctx_impl!(i16, 2); +ctx_impl!(u32, 4); +ctx_impl!(i32, 4); +ctx_impl!(u64, 8); +ctx_impl!(i64, 8); +ctx_impl!(u128, 16); +ctx_impl!(i128, 16); + +macro_rules! from_ctx_float_impl { + ($typ:tt, $size:expr) => { + impl<'a> FromCtx<Endian> for $typ { + #[inline] + fn from_ctx(src: &[u8], le: Endian) -> Self { + assert!(src.len() >= ::core::mem::size_of::<Self>()); + let mut data: signed_to_unsigned!($typ) = 0; + unsafe { + copy_nonoverlapping( + src.as_ptr(), + &mut data as *mut signed_to_unsigned!($typ) as *mut u8, + $size); + transmute(if le.is_little() { data.to_le() } else { data.to_be() }) + } + } + } + impl<'a> TryFromCtx<'a, Endian> for $typ where $typ: FromCtx<Endian> { + type Error = error::Error; + #[inline] + fn try_from_ctx(src: &'a [u8], le: Endian) -> result::Result<(Self, usize), Self::Error> { + if $size > src.len () { + Err(error::Error::TooBig{size: $size, len: src.len()}) + } else { + Ok((FromCtx::from_ctx(src, le), $size)) + } + } + } + } +} + +from_ctx_float_impl!(f32, 4); +from_ctx_float_impl!(f64, 8); + +into_ctx_impl!(u8, 1); +into_ctx_impl!(i8, 1); +into_ctx_impl!(u16, 2); +into_ctx_impl!(i16, 2); +into_ctx_impl!(u32, 4); +into_ctx_impl!(i32, 4); +into_ctx_impl!(u64, 8); +into_ctx_impl!(i64, 8); +into_ctx_impl!(u128, 16); +into_ctx_impl!(i128, 16); + +macro_rules! into_ctx_float_impl { + ($typ:tt, $size:expr) => { + impl IntoCtx<Endian> for $typ { + #[inline] + fn into_ctx(self, dst: &mut [u8], le: Endian) { + assert!(dst.len() >= $size); + write_into!(signed_to_unsigned!($typ), $size, transmute::<$typ, signed_to_unsigned!($typ)>(self), dst, le); + } + } + impl<'a> IntoCtx<Endian> for &'a $typ { + #[inline] + fn into_ctx(self, dst: &mut [u8], le: Endian) { + (*self).into_ctx(dst, le) + } + } + impl TryIntoCtx<Endian> for $typ where $typ: IntoCtx<Endian> { + type Error = error::Error; + #[inline] + fn try_into_ctx(self, dst: &mut [u8], le: Endian) -> error::Result<usize> { + if $size > dst.len () { + Err(error::Error::TooBig{size: $size, len: dst.len()}) + } else { + <$typ as IntoCtx<Endian>>::into_ctx(self, dst, le); + Ok($size) + } + } + } + impl<'a> TryIntoCtx<Endian> for &'a $typ { + type Error = error::Error; + #[inline] + fn try_into_ctx(self, dst: &mut [u8], le: Endian) -> error::Result<usize> { + (*self).try_into_ctx(dst, le) + } + } + } +} + +into_ctx_float_impl!(f32, 4); +into_ctx_float_impl!(f64, 8); + +impl<'a> TryFromCtx<'a, StrCtx> for &'a str { + type Error = error::Error; + #[inline] + /// Read a `&str` from `src` using `delimiter` + fn try_from_ctx(src: &'a [u8], ctx: StrCtx) -> Result<(Self, usize), Self::Error> { + let len = match ctx { + StrCtx::Length(len) => len, + StrCtx::Delimiter(delimiter) => src.iter().take_while(|c| **c != delimiter).count(), + StrCtx::DelimiterUntil(delimiter, len) => { + if len > src.len() { + return Err(error::Error::TooBig{size: len, len: src.len()}); + }; + src + .iter() + .take_while(|c| **c != delimiter) + .take(len) + .count() + } + }; + + if len > src.len() { + return Err(error::Error::TooBig{size: len, len: src.len()}); + }; + + match str::from_utf8(&src[..len]) { + Ok(res) => Ok((res, len + ctx.len())), + Err(_) => Err(error::Error::BadInput{size: src.len(), msg: "invalid utf8"}) + } + } +} + +impl<'a, T> TryFromCtx<'a, StrCtx, T> for &'a str where T: AsRef<[u8]> { + type Error = error::Error; + #[inline] + fn try_from_ctx(src: &'a T, ctx: StrCtx) -> result::Result<(Self, usize), Self::Error> { + let src = src.as_ref(); + TryFromCtx::try_from_ctx(src, ctx) + } +} + +impl<'a> TryIntoCtx for &'a [u8] { + type Error = error::Error; + #[inline] + fn try_into_ctx(self, dst: &mut [u8], _ctx: ()) -> error::Result<usize> { + let src_len = self.len() as isize; + let dst_len = dst.len() as isize; + // if src_len < 0 || dst_len < 0 || offset < 0 { + // return Err(error::Error::BadOffset(format!("requested operation has negative casts: src len: {} dst len: {} offset: {}", src_len, dst_len, offset)).into()) + // } + if src_len > dst_len { + Err(error::Error::TooBig{ size: self.len(), len: dst.len()}) + } else { + unsafe { copy_nonoverlapping(self.as_ptr(), dst.as_mut_ptr(), src_len as usize) }; + Ok(self.len()) + } + } +} + +// TODO: make TryIntoCtx use StrCtx for awesomeness +impl<'a> TryIntoCtx for &'a str { + type Error = error::Error; + #[inline] + fn try_into_ctx(self, dst: &mut [u8], _ctx: ()) -> error::Result<usize> { + let bytes = self.as_bytes(); + TryIntoCtx::try_into_ctx(bytes, dst, ()) + } +} + +// TODO: we can make this compile time without size_of call, but compiler probably does that anyway +macro_rules! sizeof_impl { + ($ty:ty) => { + impl SizeWith<Endian> for $ty { + #[inline] + fn size_with(_ctx: &Endian) -> usize { + size_of::<$ty>() + } + } + } +} + +sizeof_impl!(u8); +sizeof_impl!(i8); +sizeof_impl!(u16); +sizeof_impl!(i16); +sizeof_impl!(u32); +sizeof_impl!(i32); +sizeof_impl!(u64); +sizeof_impl!(i64); +sizeof_impl!(u128); +sizeof_impl!(i128); +sizeof_impl!(f32); +sizeof_impl!(f64); +sizeof_impl!(usize); +sizeof_impl!(isize); + +impl FromCtx<Endian> for usize { + #[inline] + fn from_ctx(src: &[u8], le: Endian) -> Self { + let size = ::core::mem::size_of::<Self>(); + assert!(src.len() >= size); + let mut data: usize = 0; + unsafe { + copy_nonoverlapping( + src.as_ptr(), + &mut data as *mut usize as *mut u8, + size); + if le.is_little() { data.to_le() } else { data.to_be() } + } + } +} + +impl<'a> TryFromCtx<'a, Endian> for usize where usize: FromCtx<Endian> { + type Error = error::Error; + #[inline] + fn try_from_ctx(src: &'a [u8], le: Endian) -> result::Result<(Self, usize), Self::Error> { + let size = ::core::mem::size_of::<usize>(); + if size > src.len () { + Err(error::Error::TooBig{size, len: src.len()}) + } else { + Ok((FromCtx::from_ctx(src, le), size)) + } + } +} + +impl<'a> TryFromCtx<'a, usize> for &'a[u8] { + type Error = error::Error; + #[inline] + fn try_from_ctx(src: &'a [u8], size: usize) -> result::Result<(Self, usize), Self::Error> { + if size > src.len () { + Err(error::Error::TooBig{size, len: src.len()}) + } else { + Ok((&src[..size], size)) + } + } +} + +impl IntoCtx<Endian> for usize { + #[inline] + fn into_ctx(self, dst: &mut [u8], le: Endian) { + let size = ::core::mem::size_of::<Self>(); + assert!(dst.len() >= size); + let mut data = if le.is_little() { self.to_le() } else { self.to_be() }; + let data = &mut data as *mut usize as *mut u8; + unsafe { + copy_nonoverlapping(data, dst.as_mut_ptr(), size); + } + } +} + +impl TryIntoCtx<Endian> for usize where usize: IntoCtx<Endian> { + type Error = error::Error; + #[inline] + fn try_into_ctx(self, dst: &mut [u8], le: Endian) -> error::Result<usize> { + let size = ::core::mem::size_of::<usize>(); + if size > dst.len() { + Err(error::Error::TooBig{size, len: dst.len()}) + } else { + <usize as IntoCtx<Endian>>::into_ctx(self, dst, le); + Ok(size) + } + } +} + +#[cfg(feature = "std")] +impl<'a> TryFromCtx<'a> for &'a CStr { + type Error = error::Error; + #[inline] + fn try_from_ctx(src: &'a [u8], _ctx: ()) -> result::Result<(Self, usize), Self::Error> { + let null_byte = match src.iter().position(|b| *b == 0) { + Some(ix) => ix, + None => return Err(error::Error::BadInput { + size: 0, + msg: "The input doesn't contain a null byte", + }) + }; + + let cstr = unsafe { CStr::from_bytes_with_nul_unchecked(&src[..=null_byte]) }; + Ok((cstr, null_byte+1)) + } +} + +#[cfg(feature = "std")] +impl<'a> TryFromCtx<'a> for CString { + type Error = error::Error; + #[inline] + fn try_from_ctx(src: &'a [u8], _ctx: ()) -> result::Result<(Self, usize), Self::Error> { + let (raw, bytes_read) = <&CStr as TryFromCtx>::try_from_ctx(src, _ctx)?; + Ok((raw.to_owned(), bytes_read)) + } +} + +#[cfg(feature = "std")] +impl<'a> TryIntoCtx for &'a CStr { + type Error = error::Error; + #[inline] + fn try_into_ctx(self, dst: &mut [u8], _ctx: ()) -> error::Result<usize> { + let data = self.to_bytes_with_nul(); + + if dst.len() < data.len() { + Err(error::Error::TooBig { + size: dst.len(), + len: data.len(), + }) + } else { + unsafe { + copy_nonoverlapping(data.as_ptr(), dst.as_mut_ptr(), data.len()); + } + + Ok(data.len()) + } + } +} + +#[cfg(feature = "std")] +impl TryIntoCtx for CString { + type Error = error::Error; + #[inline] + fn try_into_ctx(self, dst: &mut [u8], _ctx: ()) -> error::Result<usize> { + self.as_c_str().try_into_ctx(dst, ()) + } +} + + +// example of marshalling to bytes, let's wait until const is an option +// impl FromCtx for [u8; 10] { +// fn from_ctx(bytes: &[u8], _ctx: Endian) -> Self { +// let mut dst: Self = [0; 10]; +// assert!(bytes.len() >= dst.len()); +// unsafe { +// copy_nonoverlapping(bytes.as_ptr(), dst.as_mut_ptr(), dst.len()); +// } +// dst +// } +// } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + #[cfg(feature = "std")] + fn parse_a_cstr() { + let src = CString::new("Hello World").unwrap(); + let as_bytes = src.as_bytes_with_nul(); + + let (got, bytes_read) = <&CStr as TryFromCtx>::try_from_ctx(as_bytes, ()).unwrap(); + + assert_eq!(bytes_read, as_bytes.len()); + assert_eq!(got, src.as_c_str()); + } + + #[test] + #[cfg(feature = "std")] + fn round_trip_a_c_str() { + let src = CString::new("Hello World").unwrap(); + let src = src.as_c_str(); + let as_bytes = src.to_bytes_with_nul(); + + let mut buffer = vec![0; as_bytes.len()]; + let bytes_written = src.try_into_ctx(&mut buffer, ()).unwrap(); + assert_eq!(bytes_written, as_bytes.len()); + + let (got, bytes_read) = <&CStr as TryFromCtx>::try_from_ctx(&buffer, ()).unwrap(); + + assert_eq!(bytes_read, as_bytes.len()); + assert_eq!(got, src); + } +} + + diff --git a/third_party/rust/scroll/src/endian.rs b/third_party/rust/scroll/src/endian.rs new file mode 100644 index 0000000000..7652227998 --- /dev/null +++ b/third_party/rust/scroll/src/endian.rs @@ -0,0 +1,47 @@ +#[derive(PartialEq, Eq, Copy, Debug, Clone)] +/// The endianness (byte order) of a stream of bytes +pub enum Endian { + Little, + Big, +} + +/// Little Endian byte order context +pub const LE: Endian = Endian::Little; +/// Big Endian byte order context +pub const BE: Endian = Endian::Big; +/// Network byte order context +pub const NETWORK: Endian = Endian::Big; +#[cfg(target_endian = "little")] +/// The machine's native byte order +pub const NATIVE: Endian = LE; +#[cfg(target_endian = "big")] +/// The machine's native byte order +pub const NATIVE: Endian = BE; + +impl Default for Endian { + #[inline] + fn default() -> Self { + NATIVE + } +} + +impl From<bool> for Endian { + #[inline] + fn from(little_endian: bool) -> Self { + if little_endian { LE } else { BE } + } +} + +impl Endian { + #[inline] + pub fn network() -> Endian { + NETWORK + } + #[inline] + pub fn is_little(&self) -> bool { + match *self { + LE => true, + _ => false, + } + } +} diff --git a/third_party/rust/scroll/src/error.rs b/third_party/rust/scroll/src/error.rs new file mode 100644 index 0000000000..0257544139 --- /dev/null +++ b/third_party/rust/scroll/src/error.rs @@ -0,0 +1,68 @@ +use core::fmt::{self, Display}; +use core::result; + +#[cfg(feature = "std")] +use std::io; +#[cfg(feature = "std")] +use std::error; + +#[derive(Debug)] +/// A custom Scroll error +pub enum Error { + /// The type you tried to read was too big + TooBig { size: usize, len: usize }, + /// The requested offset to read/write at is invalid + BadOffset(usize), + BadInput{ size: usize, msg: &'static str }, + #[cfg(feature = "std")] + /// A custom Scroll error for reporting messages to clients + Custom(String), + #[cfg(feature = "std")] + /// Returned when IO based errors are encountered + IO(io::Error), +} + +#[cfg(feature = "std")] +impl error::Error for Error { + fn description(&self) -> &str { + match *self { + Error::TooBig{ .. } => { "TooBig" } + Error::BadOffset(_) => { "BadOffset" } + Error::BadInput{ .. } => { "BadInput" } + Error::Custom(_) => { "Custom" } + Error::IO(_) => { "IO" } + } + } + fn cause(&self) -> Option<&dyn error::Error> { + match *self { + Error::TooBig{ .. } => { None } + Error::BadOffset(_) => { None } + Error::BadInput{ .. } => { None } + Error::Custom(_) => { None } + Error::IO(ref io) => { io.source() } + } + } +} + +#[cfg(feature = "std")] +impl From<io::Error> for Error { + fn from(err: io::Error) -> Error { + Error::IO(err) + } +} + +impl Display for Error { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + match *self { + Error::TooBig{ ref size, ref len } => { write! (fmt, "type is too big ({}) for {}", size, len) }, + Error::BadOffset(ref offset) => { write! (fmt, "bad offset {}", offset) }, + Error::BadInput{ ref msg, ref size } => { write! (fmt, "bad input {} ({})", msg, size) }, + #[cfg(feature = "std")] + Error::Custom(ref msg) => { write! (fmt, "{}", msg) }, + #[cfg(feature = "std")] + Error::IO(ref err) => { write!(fmt, "{}", err) }, + } + } +} + +pub type Result<T> = result::Result<T, Error>; diff --git a/third_party/rust/scroll/src/greater.rs b/third_party/rust/scroll/src/greater.rs new file mode 100644 index 0000000000..7a33051128 --- /dev/null +++ b/third_party/rust/scroll/src/greater.rs @@ -0,0 +1,151 @@ +use core::ops::{Index, IndexMut, RangeFrom}; + +use crate::ctx::{FromCtx, IntoCtx}; + +/// Core-read - core, no_std friendly trait for reading basic traits from byte buffers. Cannot fail unless the buffer is too small, in which case an assert fires and the program panics. +/// +/// If your type implements [FromCtx](trait.FromCtx.html) then you can `cread::<YourType>(offset)`. +/// +/// # Example +/// +/// ```rust +/// use scroll::{ctx, Cread, LE}; +/// +/// #[repr(packed)] +/// struct Bar { +/// foo: i32, +/// bar: u32, +/// } +/// +/// impl ctx::FromCtx<scroll::Endian> for Bar { +/// fn from_ctx(bytes: &[u8], ctx: scroll::Endian) -> Self { +/// use scroll::Cread; +/// Bar { foo: bytes.cread_with(0, ctx), bar: bytes.cread_with(4, ctx) } +/// } +/// } +/// +/// let bytes = [0xff, 0xff, 0xff, 0xff, 0xef,0xbe,0xad,0xde,]; +/// let bar = bytes.cread_with::<Bar>(0, LE); +/// // Remember that you need to copy out fields from packed structs +/// // with a `{}` block instead of borrowing them directly +/// // ref: https://github.com/rust-lang/rust/issues/46043 +/// assert_eq!({bar.foo}, -1); +/// assert_eq!({bar.bar}, 0xdeadbeef); +/// ``` +pub trait Cread<Ctx, I = usize> : Index<I> + Index<RangeFrom<I>> + where + Ctx: Copy, +{ + /// Reads a value from `Self` at `offset` with `ctx`. Cannot fail. + /// If the buffer is too small for the value requested, this will panic. + /// + /// # Example + /// + /// ```rust + /// use scroll::{Cread, BE, LE}; + /// use std::i64::MAX; + /// + /// let bytes = [0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0xef,0xbe,0xad,0xde,]; + /// let foo = bytes.cread_with::<i64>(0, BE); + /// let bar = bytes.cread_with::<u32>(8, LE); + /// assert_eq!(foo, MAX); + /// assert_eq!(bar, 0xdeadbeef); + /// ``` + #[inline] + fn cread_with<N: FromCtx<Ctx, <Self as Index<RangeFrom<I>>>::Output>>(&self, offset: I, ctx: Ctx) -> N { + N::from_ctx(&self[offset..], ctx) + } + /// Reads a value implementing `FromCtx` from `Self` at `offset`, + /// with the **target machine**'s endianness. + /// For the primitive types, this will be the **target machine**'s endianness. + /// + /// # Example + /// + /// ```rust + /// use scroll::Cread; + /// + /// let bytes = [0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xef,0xbe,0x00,0x00,]; + /// let foo = bytes.cread::<i64>(0); + /// let bar = bytes.cread::<u32>(8); + /// #[cfg(target_endian = "little")] + /// assert_eq!(foo, 1); + /// #[cfg(target_endian = "big")] + /// assert_eq!(foo, 0x100_0000_0000_0000); + /// + /// #[cfg(target_endian = "little")] + /// assert_eq!(bar, 0xbeef); + /// #[cfg(target_endian = "big")] + /// assert_eq!(bar, 0xefbe0000); + /// ``` + #[inline] + fn cread<N: FromCtx<Ctx, <Self as Index<RangeFrom<I>>>::Output>>(&self, offset: I) -> N where Ctx: Default { + let ctx = Ctx::default(); + N::from_ctx(&self[offset..], ctx) + } +} + +impl<Ctx: Copy, I, R: ?Sized + Index<I> + Index<RangeFrom<I>>> Cread<Ctx, I> for R {} + +/// Core-write - core, no_std friendly trait for writing basic types into byte buffers. Cannot fail unless the buffer is too small, in which case an assert fires and the program panics. +/// Similar to [Cread](trait.Cread.html), if your type implements [IntoCtx](trait.IntoCtx.html) then you can `cwrite(your_type, offset)`. +/// +/// # Example +/// +/// ```rust +/// use scroll::{ctx, Cwrite}; +/// +/// #[repr(packed)] +/// struct Bar { +/// foo: i32, +/// bar: u32, +/// } +/// +/// impl ctx::IntoCtx<scroll::Endian> for Bar { +/// fn into_ctx(self, bytes: &mut [u8], ctx: scroll::Endian) { +/// use scroll::Cwrite; +/// bytes.cwrite_with(self.foo, 0, ctx); +/// bytes.cwrite_with(self.bar, 4, ctx); +/// } +/// } +/// +/// let bar = Bar { foo: -1, bar: 0xdeadbeef }; +/// let mut bytes = [0x0; 16]; +/// bytes.cwrite::<Bar>(bar, 0); +/// ``` +pub trait Cwrite<Ctx: Copy, I = usize>: Index<I> + IndexMut<RangeFrom<I>> { + /// Writes `n` into `Self` at `offset`; uses default context. + /// For the primitive types, this will be the **target machine**'s endianness. + /// + /// # Example + /// + /// ``` + /// use scroll::{Cwrite, Cread}; + /// let mut bytes = [0x0; 16]; + /// bytes.cwrite::<i64>(42, 0); + /// bytes.cwrite::<u32>(0xdeadbeef, 8); + /// + /// assert_eq!(bytes.cread::<i64>(0), 42); + /// assert_eq!(bytes.cread::<u32>(8), 0xdeadbeef); + #[inline] + fn cwrite<N: IntoCtx<Ctx, <Self as Index<RangeFrom<I>>>::Output>>(&mut self, n: N, offset: I) where Ctx: Default { + let ctx = Ctx::default(); + n.into_ctx(self.index_mut(offset..), ctx) + } + /// Writes `n` into `Self` at `offset` with `ctx` + /// + /// # Example + /// + /// ``` + /// use scroll::{Cwrite, Cread, LE, BE}; + /// let mut bytes = [0x0; 0x10]; + /// bytes.cwrite_with::<i64>(42, 0, LE); + /// bytes.cwrite_with::<u32>(0xdeadbeef, 8, BE); + /// assert_eq!(bytes.cread_with::<i64>(0, LE), 42); + /// assert_eq!(bytes.cread_with::<u32>(8, LE), 0xefbeadde); + #[inline] + fn cwrite_with<N: IntoCtx<Ctx, <Self as Index<RangeFrom<I>>>::Output>>(&mut self, n: N, offset: I, ctx: Ctx) { + n.into_ctx(self.index_mut(offset..), ctx) + } +} + +impl<Ctx: Copy, I, W: ?Sized + Index<I> + IndexMut<RangeFrom<I>>> Cwrite<Ctx, I> for W {} diff --git a/third_party/rust/scroll/src/leb128.rs b/third_party/rust/scroll/src/leb128.rs new file mode 100644 index 0000000000..657b4400a7 --- /dev/null +++ b/third_party/rust/scroll/src/leb128.rs @@ -0,0 +1,223 @@ +use core::u8; +use core::convert::{From, AsRef}; +use core::result; +use crate::Pread; +use crate::ctx::TryFromCtx; +use crate::error; + +#[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::{Uleb128, Sleb128}; + use super::super::LE; + + 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); + } +} diff --git a/third_party/rust/scroll/src/lesser.rs b/third_party/rust/scroll/src/lesser.rs new file mode 100644 index 0000000000..84cb000135 --- /dev/null +++ b/third_party/rust/scroll/src/lesser.rs @@ -0,0 +1,169 @@ +use std::io::{Result, Read, Write}; +use crate::ctx::{FromCtx, IntoCtx, SizeWith}; + +/// An extension trait to `std::io::Read` streams; this only deserializes simple types, like `u8`, `i32`, `f32`, `usize`, etc. +/// +/// If you implement [`FromCtx`](trait.FromCtx.html) and [`SizeWith`](ctx/trait.SizeWith.html) for your type, you can then `ioread::<YourType>()` on a `Read`. Note: [`FromCtx`](trait.FromCtx.html) is only meant for very simple types, and should _never_ fail. +/// +/// **NB** You should probably add `repr(packed)` or `repr(C)` and be very careful how you implement [`SizeWith`](ctx/trait.SizeWith.html), otherwise you +/// will get IO errors failing to fill entire buffer (the size you specified in `SizeWith`), or out of bound errors (depending on your impl) in `from_ctx` +/// +/// # Example +/// ```rust +/// use std::io::Cursor; +/// use scroll::{self, ctx, LE, Pread, IOread}; +/// +/// #[repr(packed)] +/// struct Foo { +/// foo: i64, +/// bar: u32, +/// } +/// +/// impl ctx::FromCtx<scroll::Endian> for Foo { +/// fn from_ctx(bytes: &[u8], ctx: scroll::Endian) -> Self { +/// Foo { foo: bytes.pread_with::<i64>(0, ctx).unwrap(), bar: bytes.pread_with::<u32>(8, ctx).unwrap() } +/// } +/// } +/// +/// impl ctx::SizeWith<scroll::Endian> for Foo { +/// // our parsing context doesn't influence our size +/// fn size_with(_: &scroll::Endian) -> usize { +/// ::std::mem::size_of::<Foo>() +/// } +/// } +/// +/// let bytes_ = [0x0b,0x0b,0x00,0x00,0x00,0x00,0x00,0x00, 0xef,0xbe,0x00,0x00,]; +/// let mut bytes = Cursor::new(bytes_); +/// let foo = bytes.ioread_with::<i64>(LE).unwrap(); +/// let bar = bytes.ioread_with::<u32>(LE).unwrap(); +/// assert_eq!(foo, 0xb0b); +/// assert_eq!(bar, 0xbeef); +/// let error = bytes.ioread_with::<f64>(LE); +/// assert!(error.is_err()); +/// let mut bytes = Cursor::new(bytes_); +/// let foo_ = bytes.ioread_with::<Foo>(LE).unwrap(); +/// // Remember that you need to copy out fields from packed structs +/// // with a `{}` block instead of borrowing them directly +/// // ref: https://github.com/rust-lang/rust/issues/46043 +/// assert_eq!({foo_.foo}, foo); +/// assert_eq!({foo_.bar}, bar); +/// ``` +/// +pub trait IOread<Ctx: Copy> : Read +{ + /// Reads the type `N` from `Self`, with a default parsing context. + /// For the primitive numeric types, this will be at the host machine's endianness. + /// + /// # Example + /// ```rust + /// use scroll::IOread; + /// use std::io::Cursor; + /// let bytes = [0xef, 0xbe]; + /// let mut bytes = Cursor::new(&bytes[..]); + /// let beef = bytes.ioread::<u16>().unwrap(); + /// + /// #[cfg(target_endian = "little")] + /// assert_eq!(0xbeef, beef); + /// #[cfg(target_endian = "big")] + /// assert_eq!(0xefbe, beef); + /// ``` + #[inline] + fn ioread<N: FromCtx<Ctx> + SizeWith<Ctx>>(&mut self) -> Result<N> where Ctx: Default { + let ctx = Ctx::default(); + self.ioread_with(ctx) + } + + /// Reads the type `N` from `Self`, with the parsing context `ctx`. + /// **NB**: this will panic if the type you're reading has a size greater than 256. Plans are to have this allocate in larger cases. + /// + /// For the primitive numeric types, this will be at the host machine's endianness. + /// + /// # Example + /// ```rust + /// use scroll::{IOread, LE, BE}; + /// use std::io::Cursor; + /// let bytes = [0xef, 0xbe, 0xb0, 0xb0, 0xfe, 0xed, 0xde, 0xad]; + /// let mut bytes = Cursor::new(&bytes[..]); + /// let beef = bytes.ioread_with::<u16>(LE).unwrap(); + /// assert_eq!(0xbeef, beef); + /// let b0 = bytes.ioread::<u8>().unwrap(); + /// assert_eq!(0xb0, b0); + /// let b0 = bytes.ioread::<u8>().unwrap(); + /// assert_eq!(0xb0, b0); + /// let feeddead = bytes.ioread_with::<u32>(BE).unwrap(); + /// assert_eq!(0xfeeddead, feeddead); + /// ``` + #[inline] + fn ioread_with<N: FromCtx<Ctx> + SizeWith<Ctx>>(&mut self, ctx: Ctx) -> Result<N> { + let mut scratch = [0u8; 256]; + let size = N::size_with(&ctx); + let mut buf = &mut scratch[0..size]; + self.read_exact(&mut buf)?; + Ok(N::from_ctx(buf, ctx)) + } +} + +/// Types that implement `Read` get methods defined in `IOread` +/// for free. +impl<Ctx: Copy, R: Read + ?Sized> IOread<Ctx> for R {} + +/// An extension trait to `std::io::Write` streams; this only serializes simple types, like `u8`, `i32`, `f32`, `usize`, etc. +/// +/// To write custom types with a single `iowrite::<YourType>` call, implement [`IntoCtx`](trait.IntoCtx.html) and [`SizeWith`](ctx/trait.SizeWith.html) for `YourType`. +pub trait IOwrite<Ctx: Copy>: Write +{ + /// Writes the type `N` into `Self`, with the parsing context `ctx`. + /// **NB**: this will panic if the type you're writing has a size greater than 256. Plans are to have this allocate in larger cases. + /// + /// For the primitive numeric types, this will be at the host machine's endianness. + /// + /// # Example + /// ```rust + /// use scroll::IOwrite; + /// use std::io::Cursor; + /// + /// let mut bytes = [0x0u8; 4]; + /// let mut bytes = Cursor::new(&mut bytes[..]); + /// bytes.iowrite(0xdeadbeef as u32).unwrap(); + /// + /// #[cfg(target_endian = "little")] + /// assert_eq!(bytes.into_inner(), [0xef, 0xbe, 0xad, 0xde,]); + /// #[cfg(target_endian = "big")] + /// assert_eq!(bytes.into_inner(), [0xde, 0xad, 0xbe, 0xef,]); + /// ``` + #[inline] + fn iowrite<N: SizeWith<Ctx> + IntoCtx<Ctx>>(&mut self, n: N) -> Result<()> where Ctx: Default { + let ctx = Ctx::default(); + self.iowrite_with(n, ctx) + } + + /// Writes the type `N` into `Self`, with the parsing context `ctx`. + /// **NB**: this will panic if the type you're writing has a size greater than 256. Plans are to have this allocate in larger cases. + /// + /// For the primitive numeric types, this will be at the host machine's endianness. + /// + /// # Example + /// ```rust + /// use scroll::{IOwrite, LE, BE}; + /// use std::io::{Write, Cursor}; + /// + /// let mut bytes = [0x0u8; 10]; + /// let mut cursor = Cursor::new(&mut bytes[..]); + /// cursor.write_all(b"hello").unwrap(); + /// cursor.iowrite_with(0xdeadbeef as u32, BE).unwrap(); + /// assert_eq!(cursor.into_inner(), [0x68, 0x65, 0x6c, 0x6c, 0x6f, 0xde, 0xad, 0xbe, 0xef, 0x0]); + /// ``` + #[inline] + fn iowrite_with<N: SizeWith<Ctx> + IntoCtx<Ctx>>(&mut self, n: N, ctx: Ctx) -> Result<()> { + let mut buf = [0u8; 256]; + let size = N::size_with(&ctx); + let buf = &mut buf[0..size]; + n.into_ctx(buf, ctx); + self.write_all(buf)?; + Ok(()) + } +} + +/// Types that implement `Write` get methods defined in `IOwrite` +/// for free. +impl<Ctx: Copy, W: Write + ?Sized> IOwrite<Ctx> for W {} diff --git a/third_party/rust/scroll/src/lib.rs b/third_party/rust/scroll/src/lib.rs new file mode 100644 index 0000000000..f16f153810 --- /dev/null +++ b/third_party/rust/scroll/src/lib.rs @@ -0,0 +1,526 @@ +//! # Scroll +//! +//! ```text, no_run +//! _______________ +//! ()==( (@==() +//! '______________'| +//! | | +//! | ἀρετή | +//! __)_____________| +//! ()==( (@==() +//! '--------------' +//! +//! ``` +//! +//! Scroll is a library for efficiently and easily reading/writing types from byte arrays. All the builtin types are supported, e.g., `u32`, `i8`, etc., where the type is specified as a type parameter, or type inferred when possible. In addition, it supports zero-copy reading of string slices, or any other kind of slice. The library can be used in a no_std context as well; the [Error](enum.Error.html) type only has the `IO` and `String` variants if the default features are used, and is `no_std` safe when compiled without default features. +//! +//! There are 3 traits for reading that you can import: +//! +//! 1. [Pread](trait.Pread.html), for reading (immutable) data at an offset; +//! 2. [Gread](trait.Gread.html), for reading data at an offset which automatically gets incremented by the size; +//! 3. [IOread](trait.IOread.html), for reading _simple_ data out of a `std::io::Read` based interface, e.g., a stream. (**Note**: only available when compiled with `std`) +//! +//! Each of these interfaces also have their corresponding writer versions as well, e.g., [Pwrite](trait.Pwrite.html), [Gwrite](trait.Gwrite.html), and [IOwrite](trait.IOwrite.html), respectively. +//! +//! Most familiar will likely be the `Pread` trait (inspired from the C function), which in our case takes an immutable reference to self, an immutable offset to read at, (and _optionally_ a parsing context, more on that later), and then returns the deserialized value. +//! +//! Because self is immutable, _**all** reads can be performed in parallel_ and hence are trivially parallelizable. +//! +//! For most usecases, you can use [scroll_derive](https://docs.rs/scroll_derive) to annotate your types with `derive(Pread, Pwrite, IOread, IOwrite, SizeWith)` to automatically add sensible derive defaults, and you should be ready to roll. For more complex usescases, you can implement the conversion traits yourself, see the [context module](ctx/index.html) for more information. +//! +//! # Example +//! +//! A simple example demonstrates its flexibility: +//! +//! ```rust +//! use scroll::{ctx, Pread, LE}; +//! let bytes: [u8; 4] = [0xde, 0xad, 0xbe, 0xef]; +//! +//! // reads a u32 out of `b` with the endianness of the host machine, at offset 0, turbofish-style +//! let number: u32 = bytes.pread::<u32>(0).unwrap(); +//! // ...or a byte, with type ascription on the binding. +//! let byte: u8 = bytes.pread(0).unwrap(); +//! +//! //If the type is known another way by the compiler, say reading into a struct field, we can omit the turbofish, and type ascription altogether! +//! +//! // If we want, we can explicitly add a endianness to read with by calling `pread_with`. +//! // The following reads a u32 out of `b` with Big Endian byte order, at offset 0 +//! let be_number: u32 = bytes.pread_with(0, scroll::BE).unwrap(); +//! // or a u16 - specify the type either on the variable or with the beloved turbofish +//! let be_number2 = bytes.pread_with::<u16>(2, scroll::BE).unwrap(); +//! +//! // Scroll has core friendly errors (no allocation). This will have the type `scroll::Error::BadOffset` because it tried to read beyond the bound +//! let byte: scroll::Result<i64> = bytes.pread(0); +//! +//! // Scroll is extensible: as long as the type implements `TryWithCtx`, then you can read your type out of the byte array! +//! +//! // We can parse out custom datatypes, or types with lifetimes +//! // if they implement the conversion trait `TryFromCtx`; here we parse a C-style \0 delimited &str (safely) +//! let hello: &[u8] = b"hello_world\0more words"; +//! let hello_world: &str = hello.pread(0).unwrap(); +//! assert_eq!("hello_world", hello_world); +//! +//! // ... and this parses the string if its space separated! +//! use scroll::ctx::*; +//! let spaces: &[u8] = b"hello world some junk"; +//! let world: &str = spaces.pread_with(6, StrCtx::Delimiter(SPACE)).unwrap(); +//! assert_eq!("world", world); +//! ``` +//! +//! # `std::io` API +//! +//! Scroll can also read/write simple types from a `std::io::Read` or `std::io::Write` implementor. The built-in numeric types are taken care of for you. If you want to read a custom type, you need to implement the [FromCtx](trait.FromCtx.html) (_how_ to parse) and [SizeWith](ctx/trait.SizeWith.html) (_how_ big the parsed thing will be) traits. You must compile with default features. For example: +//! +//! ```rust +//! use std::io::Cursor; +//! use scroll::IOread; +//! let bytes_ = [0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xef,0xbe,0x00,0x00,]; +//! let mut bytes = Cursor::new(bytes_); +//! +//! // this will bump the cursor's Seek +//! let foo = bytes.ioread::<u64>().unwrap(); +//! // ..ditto +//! let bar = bytes.ioread::<u32>().unwrap(); +//! ``` +//! +//! Similarly, we can write to anything that implements `std::io::Write` quite naturally: +//! +//! ```rust +//! use scroll::{IOwrite, LE, BE}; +//! use std::io::{Write, Cursor}; +//! +//! let mut bytes = [0x0u8; 10]; +//! let mut cursor = Cursor::new(&mut bytes[..]); +//! cursor.write_all(b"hello").unwrap(); +//! cursor.iowrite_with(0xdeadbeef as u32, BE).unwrap(); +//! assert_eq!(cursor.into_inner(), [0x68, 0x65, 0x6c, 0x6c, 0x6f, 0xde, 0xad, 0xbe, 0xef, 0x0]); +//! ``` +//! +//! # Advanced Uses +//! +//! Scroll is designed to be highly configurable - it allows you to implement various context (`Ctx`) sensitive traits, which then grants the implementor _automatic_ uses of the `Pread` and/or `Pwrite` traits. +//! +//! For example, suppose we have a datatype and we want to specify how to parse or serialize this datatype out of some arbitrary +//! byte buffer. In order to do this, we need to provide a [TryFromCtx](trait.TryFromCtx.html) impl for our datatype. +//! +//! In particular, if we do this for the `[u8]` target, using the convention `(usize, YourCtx)`, you will automatically get access to +//! calling `pread_with::<YourDatatype>` on arrays of bytes. +//! +//! ```rust +//! use scroll::{self, ctx, Pread, BE, Endian}; +//! +//! struct Data<'a> { +//! name: &'a str, +//! id: u32, +//! } +//! +//! // note the lifetime specified here +//! impl<'a> ctx::TryFromCtx<'a, Endian> for Data<'a> { +//! type Error = scroll::Error; +//! // and the lifetime annotation on `&'a [u8]` here +//! fn try_from_ctx (src: &'a [u8], endian: Endian) +//! -> Result<(Self, usize), Self::Error> { +//! let offset = &mut 0; +//! let name = src.gread::<&str>(offset)?; +//! let id = src.gread_with(offset, endian)?; +//! Ok((Data { name: name, id: id }, *offset)) +//! } +//! } +//! +//! let bytes = b"UserName\x00\x01\x02\x03\x04"; +//! let data = bytes.pread_with::<Data>(0, BE).unwrap(); +//! assert_eq!(data.id, 0x01020304); +//! assert_eq!(data.name.to_string(), "UserName".to_string()); +//! ``` +//! +//! Please see the [Pread documentation examples](trait.Pread.html#implementing-your-own-reader) + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(feature = "derive")] +#[allow(unused_imports)] +pub use scroll_derive::{Pread, Pwrite, SizeWith, IOread, IOwrite}; + +#[cfg(feature = "std")] +extern crate core; + +pub mod ctx; +mod pread; +mod pwrite; +mod greater; +mod error; +mod endian; +mod leb128; +#[cfg(feature = "std")] +mod lesser; + +pub use crate::endian::*; +pub use crate::pread::*; +pub use crate::pwrite::*; +pub use crate::greater::*; +pub use crate::error::*; +pub use crate::leb128::*; +#[cfg(feature = "std")] +pub use crate::lesser::*; + +#[doc(hidden)] +pub mod export { + pub use ::core::result; + pub use ::core::mem; +} + +#[cfg(test)] +mod tests { + #[allow(overflowing_literals)] + use super::{LE}; + + #[test] + fn test_measure_with_bytes() { + use super::ctx::MeasureWith; + let bytes: [u8; 4] = [0xef, 0xbe, 0xad, 0xde]; + assert_eq!(bytes.measure_with(&()), 4); + } + + #[test] + fn test_measurable() { + use super::ctx::SizeWith; + assert_eq!(8, u64::size_with(&LE)); + } + + ////////////////////////////////////////////////////////////// + // begin pread_with + ////////////////////////////////////////////////////////////// + + macro_rules! pwrite_test { + ($write:ident, $read:ident, $deadbeef:expr) => { + #[test] + fn $write() { + use super::{Pwrite, Pread, BE}; + let mut bytes: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 0]; + let b = &mut bytes[..]; + b.pwrite_with::<$read>($deadbeef, 0, LE).unwrap(); + assert_eq!(b.pread_with::<$read>(0, LE).unwrap(), $deadbeef); + b.pwrite_with::<$read>($deadbeef, 0, BE).unwrap(); + assert_eq!(b.pread_with::<$read>(0, BE).unwrap(), $deadbeef); + } + } + } + + pwrite_test!(pwrite_and_pread_roundtrip_u16, u16, 0xbeef); + pwrite_test!(pwrite_and_pread_roundtrip_i16, i16, 0x7eef); + pwrite_test!(pwrite_and_pread_roundtrip_u32, u32, 0xbeefbeef); + pwrite_test!(pwrite_and_pread_roundtrip_i32, i32, 0x7eefbeef); + pwrite_test!(pwrite_and_pread_roundtrip_u64, u64, 0xbeefbeef7eef7eef); + pwrite_test!(pwrite_and_pread_roundtrip_i64, i64, 0x7eefbeef7eef7eef); + + #[test] + fn pread_with_be() { + use super::{Pread}; + let bytes: [u8; 2] = [0x7e, 0xef]; + let b = &bytes[..]; + let byte: u16 = b.pread_with(0, super::BE).unwrap(); + assert_eq!(0x7eef, byte); + let bytes: [u8; 2] = [0xde, 0xad]; + let dead: u16 = bytes.pread_with(0, super::BE).unwrap(); + assert_eq!(0xdead, dead); + } + + #[test] + fn pread() { + use super::{Pread}; + let bytes: [u8; 2] = [0x7e, 0xef]; + let b = &bytes[..]; + let byte: u16 = b.pread(0).unwrap(); + #[cfg(target_endian = "little")] + assert_eq!(0xef7e, byte); + #[cfg(target_endian = "big")] + assert_eq!(0x7eef, byte); + } + + #[test] + fn pread_slice() { + use super::{Pread}; + use super::ctx::StrCtx; + let bytes: [u8; 2] = [0x7e, 0xef]; + let b = &bytes[..]; + let iserr: Result<&str, _> = b.pread_with(0, StrCtx::Length(3)); + assert!(iserr.is_err()); + // let bytes2: &[u8] = b.pread_with(0, 2).unwrap(); + // assert_eq!(bytes2.len(), bytes[..].len()); + // for i in 0..bytes2.len() { + // assert_eq!(bytes2[i], bytes[i]) + // } + } + + #[test] + fn pread_str() { + use super::Pread; + use super::ctx::*; + let bytes: [u8; 2] = [0x2e, 0x0]; + let b = &bytes[..]; + let s: &str = b.pread(0).unwrap(); + println!("str: {}", s); + assert_eq!(s.len(), bytes[..].len() - 1); + let bytes: &[u8] = b"hello, world!\0some_other_things"; + let hello_world: &str = bytes.pread_with(0, StrCtx::Delimiter(NULL)).unwrap(); + println!("{:?}", &hello_world); + assert_eq!(hello_world.len(), 13); + let hello: &str = bytes.pread_with(0, StrCtx::Delimiter(SPACE)).unwrap(); + println!("{:?}", &hello); + assert_eq!(hello.len(), 6); + // this could result in underflow so we just try it + let _error = bytes.pread_with::<&str>(6, StrCtx::Delimiter(SPACE)); + let error = bytes.pread_with::<&str>(7, StrCtx::Delimiter(SPACE)); + println!("{:?}", &error); + assert!(error.is_ok()); + } + + #[test] + fn pread_str_weird() { + use super::Pread; + use super::ctx::*; + let bytes: &[u8] = b""; + let hello_world = bytes.pread_with::<&str>(0, StrCtx::Delimiter(NULL)); + println!("1 {:?}", &hello_world); + assert_eq!(hello_world.is_err(), true); + let error = bytes.pread_with::<&str>(7, StrCtx::Delimiter(SPACE)); + println!("2 {:?}", &error); + assert!(error.is_err()); + let bytes: &[u8] = b"\0"; + let null = bytes.pread::<&str>(0).unwrap(); + println!("3 {:?}", &null); + assert_eq!(null.len(), 0); + } + + #[test] + fn pwrite_str_and_bytes() { + use super::{Pread, Pwrite}; + use super::ctx::*; + let astring: &str = "lol hello_world lal\0ala imabytes"; + let mut buffer = [0u8; 33]; + buffer.pwrite(astring, 0).unwrap(); + { + let hello_world = buffer.pread_with::<&str>(4, StrCtx::Delimiter(SPACE)).unwrap(); + assert_eq!(hello_world, "hello_world"); + } + let bytes: &[u8] = b"more\0bytes"; + buffer.pwrite(bytes, 0).unwrap(); + let more = bytes.pread_with::<&str>(0, StrCtx::Delimiter(NULL)).unwrap(); + assert_eq!(more, "more"); + let bytes = bytes.pread_with::<&str>(more.len() + 1, StrCtx::Delimiter(NULL)).unwrap(); + assert_eq!(bytes, "bytes"); + } + + use std::error; + use std::fmt::{self, Display}; + + #[derive(Debug)] + pub struct ExternalError {} + + impl Display for ExternalError { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "ExternalError") + } + } + + impl error::Error for ExternalError { + fn description(&self) -> &str { + "ExternalError" + } + fn cause(&self) -> Option<&dyn error::Error> { None} + } + + impl From<super::Error> for ExternalError { + fn from(err: super::Error) -> Self { + //use super::Error::*; + match err { + _ => ExternalError{}, + } + } + } + + #[derive(Debug, PartialEq, Eq)] + pub struct Foo(u16); + + impl super::ctx::TryIntoCtx<super::Endian> for Foo { + type Error = ExternalError; + fn try_into_ctx(self, this: &mut [u8], le: super::Endian) -> Result<usize, Self::Error> { + use super::Pwrite; + if this.len() < 2 { return Err((ExternalError {}).into()) } + this.pwrite_with(self.0, 0, le)?; + Ok(2) + } + } + + impl<'a> super::ctx::TryFromCtx<'a, super::Endian> for Foo { + type Error = ExternalError; + fn try_from_ctx(this: &'a [u8], le: super::Endian) -> Result<(Self, usize), Self::Error> { + use super::Pread; + if this.len() > 2 { return Err((ExternalError {}).into()) } + let n = this.pread_with(0, le)?; + Ok((Foo(n), 2)) + } + } + + #[test] + fn pread_with_iter_bytes() { + use super::{Pread}; + let mut bytes_to: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 0]; + let bytes_from: [u8; 8] = [1, 2, 3, 4, 5, 6, 7, 8]; + let bytes_to = &mut bytes_to[..]; + let bytes_from = &bytes_from[..]; + for i in 0..bytes_from.len() { + bytes_to[i] = bytes_from.pread(i).unwrap(); + } + assert_eq!(bytes_to, bytes_from); + } + + ////////////////////////////////////////////////////////////// + // end pread_with + ////////////////////////////////////////////////////////////// + + ////////////////////////////////////////////////////////////// + // begin gread_with + ////////////////////////////////////////////////////////////// + macro_rules! g_test { + ($read:ident, $deadbeef:expr, $typ:ty) => { + #[test] + fn $read() { + use super::Pread; + let bytes: [u8; 8] = [0xf, 0xe, 0xe, 0xb, 0xd, 0xa, 0xe, 0xd]; + let mut offset = 0; + let deadbeef: $typ = bytes.gread_with(&mut offset, LE).unwrap(); + assert_eq!(deadbeef, $deadbeef as $typ); + assert_eq!(offset, ::std::mem::size_of::<$typ>()); + } + } + } + + g_test!(simple_gread_u16, 0xe0f, u16); + g_test!(simple_gread_u32, 0xb0e0e0f, u32); + g_test!(simple_gread_u64, 0xd0e0a0d0b0e0e0f, u64); + g_test!(simple_gread_i64, 940700423303335439, i64); + + macro_rules! simple_float_test { + ($read:ident, $deadbeef:expr, $typ:ty) => { + #[test] + fn $read() { + use super::Pread; + let bytes: [u8; 8] = [0u8, 0, 0, 0, 0, 0, 224, 63]; + let mut offset = 0; + let deadbeef: $typ = bytes.gread_with(&mut offset, LE).unwrap(); + assert_eq!(deadbeef, $deadbeef as $typ); + assert_eq!(offset, ::std::mem::size_of::<$typ>()); + } + }; + } + + simple_float_test!(gread_f32, 0.0, f32); + simple_float_test!(gread_f64, 0.5, f64); + + macro_rules! g_read_write_test { + ($read:ident, $val:expr, $typ:ty) => { + #[test] + fn $read() { + use super::{LE, BE, Pread, Pwrite}; + let mut buffer = [0u8; 16]; + let offset = &mut 0; + buffer.gwrite_with($val.clone(), offset, LE).unwrap(); + let o2 = &mut 0; + let val: $typ = buffer.gread_with(o2, LE).unwrap(); + assert_eq!(val, $val); + assert_eq!(*offset, ::std::mem::size_of::<$typ>()); + assert_eq!(*o2, ::std::mem::size_of::<$typ>()); + assert_eq!(*o2, *offset); + buffer.gwrite_with($val.clone(), offset, BE).unwrap(); + let val: $typ = buffer.gread_with(o2, BE).unwrap(); + assert_eq!(val, $val); + } + }; + } + + g_read_write_test!(gread_gwrite_f64_1, 0.25f64, f64); + g_read_write_test!(gread_gwrite_f64_2, 0.5f64, f64); + g_read_write_test!(gread_gwrite_f64_3, 0.064, f64); + + g_read_write_test!(gread_gwrite_f32_1, 0.25f32, f32); + g_read_write_test!(gread_gwrite_f32_2, 0.5f32, f32); + g_read_write_test!(gread_gwrite_f32_3, 0.0f32, f32); + + g_read_write_test!(gread_gwrite_i64_1, 0i64, i64); + g_read_write_test!(gread_gwrite_i64_2, -1213213211111i64, i64); + g_read_write_test!(gread_gwrite_i64_3, -3000i64, i64); + + g_read_write_test!(gread_gwrite_i32_1, 0i32, i32); + g_read_write_test!(gread_gwrite_i32_2, -1213213232, i32); + g_read_write_test!(gread_gwrite_i32_3, -3000i32, i32); + + // useful for ferreting out problems with impls + #[test] + fn gread_with_iter_bytes() { + use super::{Pread}; + let mut bytes_to: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 0]; + let bytes_from: [u8; 8] = [1, 2, 3, 4, 5, 6, 7, 8]; + let bytes_to = &mut bytes_to[..]; + let bytes_from = &bytes_from[..]; + let mut offset = &mut 0; + for i in 0..bytes_from.len() { + bytes_to[i] = bytes_from.gread(&mut offset).unwrap(); + } + assert_eq!(bytes_to, bytes_from); + assert_eq!(*offset, bytes_to.len()); + } + + #[test] + fn gread_inout() { + use super::{Pread}; + let mut bytes_to: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 0]; + let bytes_from: [u8; 8] = [1, 2, 3, 4, 5, 6, 7, 8]; + let bytes = &bytes_from[..]; + let offset = &mut 0; + bytes.gread_inout(offset, &mut bytes_to[..]).unwrap(); + assert_eq!(bytes_to, bytes_from); + assert_eq!(*offset, bytes_to.len()); + } + + #[test] + fn gread_with_byte() { + use super::{Pread}; + let bytes: [u8; 1] = [0x7f]; + let b = &bytes[..]; + let offset = &mut 0; + let byte: u8 = b.gread(offset).unwrap(); + assert_eq!(0x7f, byte); + assert_eq!(*offset, 1); + } + + #[test] + fn gread_slice() { + use super::{Pread}; + use super::ctx::{StrCtx}; + let bytes: [u8; 2] = [0x7e, 0xef]; + let b = &bytes[..]; + let offset = &mut 0; + let res = b.gread_with::<&str>(offset, StrCtx::Length(3)); + assert!(res.is_err()); + *offset = 0; + let astring: [u8; 3] = [0x45, 042, 0x44]; + let string = astring.gread_with::<&str>(offset, StrCtx::Length(2)); + match &string { + &Ok(_) => {}, + &Err(ref err) => {println!("{}", &err); panic!();} + } + assert_eq!(string.unwrap(), "E*"); + *offset = 0; + let bytes2: &[u8] = b.gread_with(offset, 2).unwrap(); + assert_eq!(*offset, 2); + assert_eq!(bytes2.len(), bytes[..].len()); + for i in 0..bytes2.len() { + assert_eq!(bytes2[i], bytes[i]) + } + } + + ///////////////////////////////////////////////////////////////// + // end gread_with + ///////////////////////////////////////////////////////////////// +} diff --git a/third_party/rust/scroll/src/pread.rs b/third_party/rust/scroll/src/pread.rs new file mode 100644 index 0000000000..64f85d4476 --- /dev/null +++ b/third_party/rust/scroll/src/pread.rs @@ -0,0 +1,203 @@ +use core::result; +use core::ops::{Index, RangeFrom}; + +use crate::ctx::{TryFromCtx, MeasureWith}; +use crate::error; + +/// A very generic, contextual pread interface in Rust. Allows completely parallelized reads, as `Self` is immutable +/// +/// Don't be scared! The `Pread` definition _is_ terrifying, but it is definitely tractable. Essentially, `E` is the error, `Ctx` the parsing context, `I` is the indexing type, `TryCtx` is the "offset + ctx" Context given to the `TryFromCtx` trait bounds, and `SliceCtx` is the "offset + size + ctx" context given to the `TryRefFromCtx` trait bound. +/// +/// # Implementing Your Own Reader +/// If you want to implement your own reader for a type `Foo` from some kind of buffer (say `[u8]`), then you need to implement [TryFromCtx](trait.TryFromCtx.html) +/// +/// ```rust +/// use scroll::{self, ctx, Pread}; +/// #[derive(Debug, PartialEq, Eq)] +/// pub struct Foo(u16); +/// +/// impl<'a> ctx::TryFromCtx<'a, scroll::Endian> for Foo { +/// type Error = scroll::Error; +/// fn try_from_ctx(this: &'a [u8], le: scroll::Endian) -> Result<(Self, usize), Self::Error> { +/// if this.len() < 2 { return Err((scroll::Error::Custom("whatever".to_string())).into()) } +/// let n = this.pread_with(0, le)?; +/// Ok((Foo(n), 2)) +/// } +/// } +/// +/// let bytes: [u8; 4] = [0xde, 0xad, 0, 0]; +/// let foo = bytes.pread_with::<Foo>(0, scroll::LE).unwrap(); +/// assert_eq!(Foo(0xadde), foo); +/// +/// let foo2 = bytes.pread_with::<Foo>(0, scroll::BE).unwrap(); +/// assert_eq!(Foo(0xdeadu16), foo2); +/// ``` +/// +/// # Advanced: Using Your Own Error in `TryFromCtx` +/// ```rust +/// use scroll::{self, ctx, Pread}; +/// use std::error; +/// use std::fmt::{self, Display}; +/// // make some kind of normal error which also can transform a scroll error ideally (quick_error, error_chain allow this automatically nowadays) +/// #[derive(Debug)] +/// pub struct ExternalError {} +/// +/// impl Display for ExternalError { +/// fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { +/// write!(fmt, "ExternalError") +/// } +/// } +/// +/// impl error::Error for ExternalError { +/// fn description(&self) -> &str { +/// "ExternalError" +/// } +/// fn cause(&self) -> Option<&error::Error> { None} +/// } +/// +/// impl From<scroll::Error> for ExternalError { +/// fn from(err: scroll::Error) -> Self { +/// match err { +/// _ => ExternalError{}, +/// } +/// } +/// } +/// #[derive(Debug, PartialEq, Eq)] +/// pub struct Foo(u16); +/// +/// impl<'a> ctx::TryFromCtx<'a, scroll::Endian> for Foo { +/// type Error = ExternalError; +/// fn try_from_ctx(this: &'a [u8], le: scroll::Endian) -> Result<(Self, usize), Self::Error> { +/// if this.len() <= 2 { return Err((ExternalError {}).into()) } +/// let offset = &mut 0; +/// let n = this.gread_with(offset, le)?; +/// Ok((Foo(n), *offset)) +/// } +/// } +/// +/// let bytes: [u8; 4] = [0xde, 0xad, 0, 0]; +/// let foo: Result<Foo, ExternalError> = bytes.pread(0); +/// ``` +pub trait Pread<Ctx, E> : Index<usize> + Index<RangeFrom<usize>> + MeasureWith<Ctx> + where + Ctx: Copy, + E: From<error::Error>, +{ + #[inline] + /// Reads a value from `self` at `offset` with a default `Ctx`. For the primitive numeric values, this will read at the machine's endianness. + /// # Example + /// ```rust + /// use scroll::Pread; + /// let bytes = [0x7fu8; 0x01]; + /// let byte = bytes.pread::<u8>(0).unwrap(); + fn pread<'a, N: TryFromCtx<'a, Ctx, <Self as Index<RangeFrom<usize>>>::Output, Error = E>>(&'a self, offset: usize) -> result::Result<N, E> where <Self as Index<RangeFrom<usize>>>::Output: 'a, Ctx: Default { + self.pread_with(offset, Ctx::default()) + } + #[inline] + /// Reads a value from `self` at `offset` with the given `ctx` + /// # Example + /// ```rust + /// use scroll::Pread; + /// let bytes: [u8; 2] = [0xde, 0xad]; + /// let dead: u16 = bytes.pread_with(0, scroll::BE).unwrap(); + /// assert_eq!(dead, 0xdeadu16); + fn pread_with<'a, N: TryFromCtx<'a, Ctx, <Self as Index<RangeFrom<usize>>>::Output, Error = E>>(&'a self, offset: usize, ctx: Ctx) -> result::Result<N, E> where <Self as Index<RangeFrom<usize>>>::Output: 'a { + let len = self.measure_with(&ctx); + if offset >= len { + return Err(error::Error::BadOffset(offset).into()) + } + N::try_from_ctx(&self[offset..], ctx).and_then(|(n, _)| Ok(n)) + } + #[inline] + /// Reads a value from `self` at `offset` with a default `Ctx`. For the primitive numeric values, this will read at the machine's endianness. Updates the offset + /// # Example + /// ```rust + /// use scroll::Pread; + /// let offset = &mut 0; + /// let bytes = [0x7fu8; 0x01]; + /// let byte = bytes.gread::<u8>(offset).unwrap(); + /// assert_eq!(*offset, 1); + fn gread<'a, N: TryFromCtx<'a, Ctx, <Self as Index<RangeFrom<usize>>>::Output, Error = E>>(&'a self, offset: &mut usize) -> result::Result<N, E> where Ctx: Default, <Self as Index<RangeFrom<usize>>>::Output: 'a { + let ctx = Ctx::default(); + self.gread_with(offset, ctx) + } + /// Reads a value from `self` at `offset` with the given `ctx`, and updates the offset. + /// # Example + /// ```rust + /// use scroll::Pread; + /// let offset = &mut 0; + /// let bytes: [u8; 2] = [0xde, 0xad]; + /// let dead: u16 = bytes.gread_with(offset, scroll::BE).unwrap(); + /// assert_eq!(dead, 0xdeadu16); + /// assert_eq!(*offset, 2); + #[inline] + fn gread_with<'a, N: TryFromCtx<'a, Ctx, <Self as Index<RangeFrom<usize>>>::Output, Error = E>> + (&'a self, offset: &mut usize, ctx: Ctx) -> + result::Result<N, E> + where <Self as Index<RangeFrom<usize>>>::Output: 'a + { + let o = *offset; + // self.pread_with(o, ctx).and_then(|(n, size)| { + // *offset += size; + // Ok(n) + // }) + let len = self.measure_with(&ctx); + if o >= len { + return Err(error::Error::BadOffset(o).into()) + } + N::try_from_ctx(&self[o..], ctx).and_then(|(n, size)| { + *offset += size; + Ok(n) + }) + } + + /// Trys to write `inout.len()` `N`s into `inout` from `Self` starting at `offset`, using the default context for `N`, and updates the offset. + /// # Example + /// ```rust + /// use scroll::Pread; + /// let mut bytes: Vec<u8> = vec![0, 0]; + /// let offset = &mut 0; + /// let bytes_from: [u8; 2] = [0x48, 0x49]; + /// bytes_from.gread_inout(offset, &mut bytes).unwrap(); + /// assert_eq!(&bytes, &bytes_from); + /// assert_eq!(*offset, 2); + #[inline] + fn gread_inout<'a, N>(&'a self, offset: &mut usize, inout: &mut [N]) -> result::Result<(), E> + where + N: TryFromCtx<'a, Ctx, <Self as Index<RangeFrom<usize>>>::Output, Error = E>, + Ctx: Default, + <Self as Index<RangeFrom<usize>>>::Output: 'a + { + for i in inout.iter_mut() { + *i = self.gread(offset)?; + } + Ok(()) + } + + /// Trys to write `inout.len()` `N`s into `inout` from `Self` starting at `offset`, using the context `ctx` + /// # Example + /// ```rust + /// use scroll::{ctx, LE, Pread}; + /// let mut bytes: Vec<u8> = vec![0, 0]; + /// let offset = &mut 0; + /// let bytes_from: [u8; 2] = [0x48, 0x49]; + /// bytes_from.gread_inout_with(offset, &mut bytes, LE).unwrap(); + /// assert_eq!(&bytes, &bytes_from); + /// assert_eq!(*offset, 2); + #[inline] + fn gread_inout_with<'a, N>(&'a self, offset: &mut usize, inout: &mut [N], ctx: Ctx) -> result::Result<(), E> + where + N: TryFromCtx<'a, Ctx, <Self as Index<RangeFrom<usize>>>::Output, Error = E>, + <Self as Index<RangeFrom<usize>>>::Output: 'a + { + for i in inout.iter_mut() { + *i = self.gread_with(offset, ctx)?; + } + Ok(()) + } +} + +impl<Ctx: Copy, + E: From<error::Error>, + R: ?Sized + Index<usize> + Index<RangeFrom<usize>> + MeasureWith<Ctx>> + Pread<Ctx, E> for R {} diff --git a/third_party/rust/scroll/src/pwrite.rs b/third_party/rust/scroll/src/pwrite.rs new file mode 100644 index 0000000000..08aa82913c --- /dev/null +++ b/third_party/rust/scroll/src/pwrite.rs @@ -0,0 +1,79 @@ +use core::result; +use core::ops::{Index, IndexMut, RangeFrom}; + +use crate::ctx::{TryIntoCtx, MeasureWith}; +use crate::error; + +/// Writes into `Self` at an offset of type `I` using a `Ctx` +/// +/// To implement writing into an arbitrary byte buffer, implement `TryIntoCtx` +/// # Example +/// ```rust +/// use scroll::{self, ctx, LE, Endian, Pwrite}; +/// #[derive(Debug, PartialEq, Eq)] +/// pub struct Foo(u16); +/// +/// // this will use the default `DefaultCtx = scroll::Endian` and `I = usize`... +/// impl ctx::TryIntoCtx<Endian> for Foo { +/// // you can use your own error here too, but you will then need to specify it in fn generic parameters +/// type Error = scroll::Error; +/// // you can write using your own context too... see `leb128.rs` +/// fn try_into_ctx(self, this: &mut [u8], le: Endian) -> Result<usize, Self::Error> { +/// if this.len() < 2 { return Err((scroll::Error::Custom("whatever".to_string())).into()) } +/// this.pwrite_with(self.0, 0, le)?; +/// Ok(2) +/// } +/// } +/// // now we can write a `Foo` into some buffer (in this case, a byte buffer, because that's what we implemented it for above) +/// +/// let mut bytes: [u8; 4] = [0, 0, 0, 0]; +/// bytes.pwrite_with(Foo(0x7f), 1, LE).unwrap(); +/// +pub trait Pwrite<Ctx, E> : Index<usize> + IndexMut<RangeFrom<usize>> + MeasureWith<Ctx> + where + Ctx: Copy, + E: From<error::Error>, +{ + fn pwrite<N: TryIntoCtx<Ctx, <Self as Index<RangeFrom<usize>>>::Output, Error = E>>(&mut self, n: N, offset: usize) -> result::Result<usize, E> where Ctx: Default { + self.pwrite_with(n, offset, Ctx::default()) + } + /// Write `N` at offset `I` with context `Ctx` + /// # Example + /// ``` + /// use scroll::{Pwrite, Pread, LE}; + /// let mut bytes: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 0]; + /// bytes.pwrite_with::<u32>(0xbeefbeef, 0, LE).unwrap(); + /// assert_eq!(bytes.pread_with::<u32>(0, LE).unwrap(), 0xbeefbeef); + fn pwrite_with<N: TryIntoCtx<Ctx, <Self as Index<RangeFrom<usize>>>::Output, Error = E>>(&mut self, n: N, offset: usize, ctx: Ctx) -> result::Result<usize, E> { + let len = self.measure_with(&ctx); + if offset >= len { + return Err(error::Error::BadOffset(offset).into()) + } + let dst = &mut self[offset..]; + n.try_into_ctx(dst, ctx) + } + /// Write `n` into `self` at `offset`, with a default `Ctx`. Updates the offset. + #[inline] + fn gwrite<N: TryIntoCtx<Ctx, <Self as Index<RangeFrom<usize>>>::Output, Error = E>>(&mut self, n: N, offset: &mut usize) -> result::Result<usize, E> where + Ctx: Default { + let ctx = Ctx::default(); + self.gwrite_with(n, offset, ctx) + } + /// Write `n` into `self` at `offset`, with the `ctx`. Updates the offset. + #[inline] + fn gwrite_with<N: TryIntoCtx<Ctx, <Self as Index<RangeFrom<usize>>>::Output, Error = E>>(&mut self, n: N, offset: &mut usize, ctx: Ctx) -> result::Result<usize, E> { + let o = *offset; + match self.pwrite_with(n, o, ctx) { + Ok(size) => { + *offset += size; + Ok(size) + }, + err => err + } + } +} + +impl<Ctx: Copy, + E: From<error::Error>, + R: ?Sized + Index<usize> + IndexMut<RangeFrom<usize>> + MeasureWith<Ctx>> + Pwrite<Ctx, E> for R {} |