diff options
Diffstat (limited to '')
-rw-r--r-- | third_party/rust/scroll/src/lesser.rs | 178 |
1 files changed, 178 insertions, 0 deletions
diff --git a/third_party/rust/scroll/src/lesser.rs b/third_party/rust/scroll/src/lesser.rs new file mode 100644 index 0000000000..46ef4c5b11 --- /dev/null +++ b/third_party/rust/scroll/src/lesser.rs @@ -0,0 +1,178 @@ +use crate::ctx::{FromCtx, IntoCtx, SizeWith}; +use std::io::{Read, Result, Write}; + +/// An extension trait to `std::io::Read` streams; mainly targeted at reading primitive types with +/// a known size. +/// +/// Requires types to implement [`FromCtx`](ctx/trait.FromCtx.html) and [`SizeWith`](ctx/trait.SizeWith.html). +/// +/// **NB** You should probably add `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`. +/// +/// Warning: Currently ioread/write uses a small 256-byte buffer and can not read/write larger types +/// +/// # 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`](ctx/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 {} |