use std::io::{Read, Result, Write}; use crate::ctx::{FromCtx, IntoCtx, SizeWith}; /// 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 for Foo { /// fn from_ctx(bytes: &[u8], ctx: scroll::Endian) -> Self { /// Foo { foo: bytes.pread_with::(0, ctx).unwrap(), bar: bytes.pread_with::(8, ctx).unwrap() } /// } /// } /// /// impl ctx::SizeWith for Foo { /// // our parsing context doesn't influence our size /// fn size_with(_: &scroll::Endian) -> usize { /// ::std::mem::size_of::() /// } /// } /// /// 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::(LE).unwrap(); /// let bar = bytes.ioread_with::(LE).unwrap(); /// assert_eq!(foo, 0xb0b); /// assert_eq!(bar, 0xbeef); /// let error = bytes.ioread_with::(LE); /// assert!(error.is_err()); /// let mut bytes = Cursor::new(bytes_); /// let foo_ = bytes.ioread_with::(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: 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::().unwrap(); /// /// #[cfg(target_endian = "little")] /// assert_eq!(0xbeef, beef); /// #[cfg(target_endian = "big")] /// assert_eq!(0xefbe, beef); /// ``` #[inline] fn ioread + SizeWith>(&mut self) -> Result 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::(LE).unwrap(); /// assert_eq!(0xbeef, beef); /// let b0 = bytes.ioread::().unwrap(); /// assert_eq!(0xb0, b0); /// let b0 = bytes.ioread::().unwrap(); /// assert_eq!(0xb0, b0); /// let feeddead = bytes.ioread_with::(BE).unwrap(); /// assert_eq!(0xfeeddead, feeddead); /// ``` #[inline] fn ioread_with + SizeWith>(&mut self, ctx: Ctx) -> Result { let mut scratch = [0u8; 256]; let size = N::size_with(&ctx); let buf = &mut scratch[0..size]; self.read_exact(buf)?; Ok(N::from_ctx(buf, ctx)) } } /// Types that implement `Read` get methods defined in `IOread` /// for free. impl IOread 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::` call, implement [`IntoCtx`](ctx/trait.IntoCtx.html) and [`SizeWith`](ctx/trait.SizeWith.html) for `YourType`. pub trait IOwrite: 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 + IntoCtx>(&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 + IntoCtx>(&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 IOwrite for W {}