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