1 use crate::ctx::{FromCtx, IntoCtx, SizeWith};
2 use std::io::{Read, Result, Write};
4 /// An extension trait to `std::io::Read` streams; mainly targeted at reading primitive types with
7 /// Requires types to implement [`FromCtx`](ctx/trait.FromCtx.html) and [`SizeWith`](ctx/trait.SizeWith.html).
9 /// **NB** You should probably add `repr(C)` and be very careful how you implement
10 /// [`SizeWith`](ctx/trait.SizeWith.html), otherwise you will get IO errors failing to fill entire
11 /// buffer (the size you specified in `SizeWith`), or out of bound errors (depending on your impl)
14 /// Warning: Currently ioread/write uses a small 256-byte buffer and can not read/write larger types
18 /// use std::io::Cursor;
19 /// use scroll::{self, ctx, LE, Pread, IOread};
27 /// impl ctx::FromCtx<scroll::Endian> for Foo {
28 /// fn from_ctx(bytes: &[u8], ctx: scroll::Endian) -> Self {
29 /// Foo { foo: bytes.pread_with::<i64>(0, ctx).unwrap(), bar: bytes.pread_with::<u32>(8, ctx).unwrap() }
33 /// impl ctx::SizeWith<scroll::Endian> for Foo {
34 /// // our parsing context doesn't influence our size
35 /// fn size_with(_: &scroll::Endian) -> usize {
36 /// ::std::mem::size_of::<Foo>()
40 /// let bytes_ = [0x0b,0x0b,0x00,0x00,0x00,0x00,0x00,0x00, 0xef,0xbe,0x00,0x00,];
41 /// let mut bytes = Cursor::new(bytes_);
42 /// let foo = bytes.ioread_with::<i64>(LE).unwrap();
43 /// let bar = bytes.ioread_with::<u32>(LE).unwrap();
44 /// assert_eq!(foo, 0xb0b);
45 /// assert_eq!(bar, 0xbeef);
46 /// let error = bytes.ioread_with::<f64>(LE);
47 /// assert!(error.is_err());
48 /// let mut bytes = Cursor::new(bytes_);
49 /// let foo_ = bytes.ioread_with::<Foo>(LE).unwrap();
50 /// // Remember that you need to copy out fields from packed structs
51 /// // with a `{}` block instead of borrowing them directly
52 /// // ref: https://github.com/rust-lang/rust/issues/46043
53 /// assert_eq!({foo_.foo}, foo);
54 /// assert_eq!({foo_.bar}, bar);
57 pub trait IOread<Ctx: Copy>: Read {
58 /// Reads the type `N` from `Self`, with a default parsing context.
59 /// For the primitive numeric types, this will be at the host machine's endianness.
63 /// use scroll::IOread;
64 /// use std::io::Cursor;
65 /// let bytes = [0xef, 0xbe];
66 /// let mut bytes = Cursor::new(&bytes[..]);
67 /// let beef = bytes.ioread::<u16>().unwrap();
69 /// #[cfg(target_endian = "little")]
70 /// assert_eq!(0xbeef, beef);
71 /// #[cfg(target_endian = "big")]
72 /// assert_eq!(0xefbe, beef);
75 fn ioread<N: FromCtx<Ctx> + SizeWith<Ctx>>(&mut self) -> Result<N>
79 let ctx = Ctx::default();
83 /// Reads the type `N` from `Self`, with the parsing context `ctx`.
84 /// **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.
86 /// For the primitive numeric types, this will be at the host machine's endianness.
90 /// use scroll::{IOread, LE, BE};
91 /// use std::io::Cursor;
92 /// let bytes = [0xef, 0xbe, 0xb0, 0xb0, 0xfe, 0xed, 0xde, 0xad];
93 /// let mut bytes = Cursor::new(&bytes[..]);
94 /// let beef = bytes.ioread_with::<u16>(LE).unwrap();
95 /// assert_eq!(0xbeef, beef);
96 /// let b0 = bytes.ioread::<u8>().unwrap();
97 /// assert_eq!(0xb0, b0);
98 /// let b0 = bytes.ioread::<u8>().unwrap();
99 /// assert_eq!(0xb0, b0);
100 /// let feeddead = bytes.ioread_with::<u32>(BE).unwrap();
101 /// assert_eq!(0xfeeddead, feeddead);
104 fn ioread_with<N: FromCtx<Ctx> + SizeWith<Ctx>>(&mut self, ctx: Ctx) -> Result<N> {
105 let mut scratch = [0u8; 256];
106 let size = N::size_with(&ctx);
107 let mut buf = &mut scratch[0..size];
108 self.read_exact(&mut buf)?;
109 Ok(N::from_ctx(buf, ctx))
113 /// Types that implement `Read` get methods defined in `IOread`
115 impl<Ctx: Copy, R: Read + ?Sized> IOread<Ctx> for R {}
117 /// An extension trait to `std::io::Write` streams; this only serializes simple types, like `u8`, `i32`, `f32`, `usize`, etc.
119 /// To write custom types with a single `iowrite::<YourType>` call, implement [`IntoCtx`](ctx/trait.IntoCtx.html) and [`SizeWith`](ctx/trait.SizeWith.html) for `YourType`.
120 pub trait IOwrite<Ctx: Copy>: Write {
121 /// Writes the type `N` into `Self`, with the parsing context `ctx`.
122 /// **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.
124 /// For the primitive numeric types, this will be at the host machine's endianness.
128 /// use scroll::IOwrite;
129 /// use std::io::Cursor;
131 /// let mut bytes = [0x0u8; 4];
132 /// let mut bytes = Cursor::new(&mut bytes[..]);
133 /// bytes.iowrite(0xdeadbeef as u32).unwrap();
135 /// #[cfg(target_endian = "little")]
136 /// assert_eq!(bytes.into_inner(), [0xef, 0xbe, 0xad, 0xde,]);
137 /// #[cfg(target_endian = "big")]
138 /// assert_eq!(bytes.into_inner(), [0xde, 0xad, 0xbe, 0xef,]);
141 fn iowrite<N: SizeWith<Ctx> + IntoCtx<Ctx>>(&mut self, n: N) -> Result<()>
145 let ctx = Ctx::default();
146 self.iowrite_with(n, ctx)
149 /// Writes the type `N` into `Self`, with the parsing context `ctx`.
150 /// **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.
152 /// For the primitive numeric types, this will be at the host machine's endianness.
156 /// use scroll::{IOwrite, LE, BE};
157 /// use std::io::{Write, Cursor};
159 /// let mut bytes = [0x0u8; 10];
160 /// let mut cursor = Cursor::new(&mut bytes[..]);
161 /// cursor.write_all(b"hello").unwrap();
162 /// cursor.iowrite_with(0xdeadbeef as u32, BE).unwrap();
163 /// assert_eq!(cursor.into_inner(), [0x68, 0x65, 0x6c, 0x6c, 0x6f, 0xde, 0xad, 0xbe, 0xef, 0x0]);
166 fn iowrite_with<N: SizeWith<Ctx> + IntoCtx<Ctx>>(&mut self, n: N, ctx: Ctx) -> Result<()> {
167 let mut buf = [0u8; 256];
168 let size = N::size_with(&ctx);
169 let buf = &mut buf[0..size];
170 n.into_ctx(buf, ctx);
171 self.write_all(buf)?;
176 /// Types that implement `Write` get methods defined in `IOwrite`
178 impl<Ctx: Copy, W: Write + ?Sized> IOwrite<Ctx> for W {}