/rust/registry/src/index.crates.io-1949cf8c6b5b557f/scroll-0.13.0/src/lesser.rs
Line | Count | Source |
1 | | use std::io::{Read, Result, Write}; |
2 | | |
3 | | use 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 | | /// |
58 | | pub 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 | 0 | fn ioread<N: FromCtx<Ctx> + SizeWith<Ctx>>(&mut self) -> Result<N> |
77 | 0 | where |
78 | 0 | Ctx: Default, |
79 | | { |
80 | 0 | let ctx = Ctx::default(); |
81 | 0 | self.ioread_with(ctx) |
82 | 0 | } |
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 | 0 | fn ioread_with<N: FromCtx<Ctx> + SizeWith<Ctx>>(&mut self, ctx: Ctx) -> Result<N> { |
106 | 0 | let mut scratch = [0u8; 256]; |
107 | 0 | let size = N::size_with(&ctx); |
108 | 0 | let buf = &mut scratch[0..size]; |
109 | 0 | self.read_exact(buf)?; |
110 | 0 | Ok(N::from_ctx(buf, ctx)) |
111 | 0 | } |
112 | | } |
113 | | |
114 | | /// Types that implement `Read` get methods defined in `IOread` |
115 | | /// for free. |
116 | | impl<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`. |
121 | | pub 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 | 0 | fn iowrite<N: SizeWith<Ctx> + IntoCtx<Ctx>>(&mut self, n: N) -> Result<()> |
143 | 0 | where |
144 | 0 | Ctx: Default, |
145 | | { |
146 | 0 | let ctx = Ctx::default(); |
147 | 0 | self.iowrite_with(n, ctx) |
148 | 0 | } |
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 | 0 | fn iowrite_with<N: SizeWith<Ctx> + IntoCtx<Ctx>>(&mut self, n: N, ctx: Ctx) -> Result<()> { |
168 | 0 | let mut buf = [0u8; 256]; |
169 | 0 | let size = N::size_with(&ctx); |
170 | 0 | let buf = &mut buf[0..size]; |
171 | 0 | n.into_ctx(buf, ctx); |
172 | 0 | self.write_all(buf)?; |
173 | 0 | Ok(()) |
174 | 0 | } |
175 | | } |
176 | | |
177 | | /// Types that implement `Write` get methods defined in `IOwrite` |
178 | | /// for free. |
179 | | impl<Ctx: Copy, W: Write + ?Sized> IOwrite<Ctx> for W {} |