]>
Commit | Line | Data |
---|---|---|
9e7dfce8 | 1 | //! Helpers for `Read`. |
2e6520a9 WB |
2 | |
3 | use std::io; | |
4 | ||
5 | use endian_trait::Endian; | |
6 | ||
7 | use crate::vec::{self, ops::*}; | |
8 | ||
9 | /// Adds some additional related functionality for types implementing [`Read`](std::io::Read). | |
10 | /// | |
11 | /// Particularly for reading into a newly allocated buffer, appending to a `Vec<u8>` or reading | |
12 | /// values of a specific endianess (types implementing [`Endian`]). | |
13 | /// | |
14 | /// Examples: | |
15 | /// ```no_run | |
9e7dfce8 | 16 | /// use proxmox::tools::io::ReadExt; |
2e6520a9 WB |
17 | /// |
18 | /// # fn code() -> std::io::Result<()> { | |
19 | /// let mut file = std::fs::File::open("some.data")?; | |
20 | /// | |
21 | /// // read some bytes into a newly allocated Vec<u8>: | |
22 | /// let mut data = file.read_exact_allocated(64)?; | |
23 | /// | |
24 | /// // appending data to a vector: | |
25 | /// let actually_appended = file.append_to_vec(&mut data, 64)?; // .read() version | |
26 | /// file.append_exact_to_vec(&mut data, 64)?; // .read_exact() version | |
27 | /// # Ok(()) | |
28 | /// # } | |
29 | /// ``` | |
30 | /// | |
31 | /// Or for reading values of a defined representation and endianess: | |
32 | /// | |
33 | /// ```no_run | |
34 | /// # use endian_trait::Endian; | |
9e7dfce8 | 35 | /// # use proxmox::tools::io::ReadExt; |
2e6520a9 WB |
36 | /// |
37 | /// #[derive(Endian)] | |
38 | /// #[repr(C)] | |
39 | /// struct Header { | |
40 | /// version: u16, | |
41 | /// data_size: u16, | |
42 | /// } | |
43 | /// | |
44 | /// # fn code(mut file: std::fs::File) -> std::io::Result<()> { | |
45 | /// // We have given `Header` a proper binary representation via `#[repr]`, so this is safe: | |
46 | /// let header: Header = unsafe { file.read_le_value()? }; | |
47 | /// let mut blob = file.read_exact_allocated(header.data_size as usize)?; | |
48 | /// # Ok(()) | |
49 | /// # } | |
50 | /// ``` | |
51 | /// | |
52 | /// [`Endian`]: https://docs.rs/endian_trait/0.6/endian_trait/trait.Endian.html | |
9e7dfce8 | 53 | pub trait ReadExt { |
2e6520a9 WB |
54 | /// Read data into a newly allocated vector. This is a shortcut for: |
55 | /// ```ignore | |
56 | /// let mut data = Vec::with_capacity(len); | |
57 | /// unsafe { | |
58 | /// data.set_len(len); | |
59 | /// } | |
60 | /// reader.read_exact(&mut data)?; | |
61 | /// ``` | |
62 | /// | |
63 | /// With this trait, we just use: | |
64 | /// ```no_run | |
9e7dfce8 | 65 | /// use proxmox::tools::io::ReadExt; |
2e6520a9 WB |
66 | /// # fn code(mut reader: std::fs::File, len: usize) -> std::io::Result<()> { |
67 | /// let data = reader.read_exact_allocated(len)?; | |
68 | /// # Ok(()) | |
69 | /// # } | |
70 | /// ``` | |
71 | fn read_exact_allocated(&mut self, size: usize) -> io::Result<Vec<u8>>; | |
72 | ||
73 | /// Append data to a vector, growing it as necessary. Returns the amount of data appended. | |
74 | fn append_to_vec(&mut self, out: &mut Vec<u8>, size: usize) -> io::Result<usize>; | |
75 | ||
76 | /// Append an exact amount of data to a vector, growing it as necessary. | |
77 | fn append_exact_to_vec(&mut self, out: &mut Vec<u8>, size: usize) -> io::Result<()>; | |
78 | ||
79 | /// Read a value with host endianess. | |
80 | /// | |
81 | /// This is limited to types implementing the [`Endian`] trait under the assumption that | |
82 | /// this is only done for types which are supposed to be read/writable directly. | |
83 | /// | |
84 | /// There's no way to directly depend on a type having a specific `#[repr(...)]`, therefore | |
85 | /// this is considered unsafe. | |
86 | /// | |
87 | /// ```no_run | |
88 | /// # use endian_trait::Endian; | |
9e7dfce8 | 89 | /// use proxmox::tools::io::ReadExt; |
2e6520a9 WB |
90 | /// |
91 | /// #[derive(Endian)] | |
92 | /// #[repr(C, packed)] | |
93 | /// struct Data { | |
94 | /// value: u16, | |
95 | /// count: u32, | |
96 | /// } | |
97 | /// | |
98 | /// # fn code() -> std::io::Result<()> { | |
99 | /// let mut file = std::fs::File::open("my-raw.dat")?; | |
100 | /// // We know `Data` has a safe binary representation (#[repr(C, packed)]), so we can | |
101 | /// // safely use our helper: | |
102 | /// let data: Data = unsafe { file.read_host_value()? }; | |
103 | /// # Ok(()) | |
104 | /// # } | |
105 | /// ``` | |
106 | /// | |
107 | /// [`Endian`]: https://docs.rs/endian_trait/0.6/endian_trait/trait.Endian.html | |
108 | unsafe fn read_host_value<T: Endian>(&mut self) -> io::Result<T>; | |
109 | ||
110 | /// Read a little endian value. | |
111 | /// | |
112 | /// The return type is required to implement the [`Endian`] trait, and we make the | |
113 | /// assumption that this is only done for types which are supposed to be read/writable | |
114 | /// directly. | |
115 | /// | |
116 | /// There's no way to directly depend on a type having a specific `#[repr(...)]`, therefore | |
117 | /// this is considered unsafe. | |
118 | /// | |
119 | /// ```no_run | |
120 | /// # use endian_trait::Endian; | |
9e7dfce8 | 121 | /// use proxmox::tools::io::ReadExt; |
2e6520a9 WB |
122 | /// |
123 | /// #[derive(Endian)] | |
124 | /// #[repr(C, packed)] | |
125 | /// struct Data { | |
126 | /// value: u16, | |
127 | /// count: u32, | |
128 | /// } | |
129 | /// | |
130 | /// # fn code() -> std::io::Result<()> { | |
131 | /// let mut file = std::fs::File::open("my-little-endian.dat")?; | |
132 | /// // We know `Data` has a safe binary representation (#[repr(C, packed)]), so we can | |
133 | /// // safely use our helper: | |
134 | /// let data: Data = unsafe { file.read_le_value()? }; | |
135 | /// # Ok(()) | |
136 | /// # } | |
137 | /// ``` | |
138 | /// | |
139 | /// [`Endian`]: https://docs.rs/endian_trait/0.6/endian_trait/trait.Endian.html | |
140 | unsafe fn read_le_value<T: Endian>(&mut self) -> io::Result<T>; | |
141 | ||
142 | /// Read a big endian value. | |
143 | /// | |
144 | /// The return type is required to implement the [`Endian`] trait, and we make the | |
145 | /// assumption that this is only done for types which are supposed to be read/writable | |
146 | /// directly. | |
147 | /// | |
148 | /// There's no way to directly depend on a type having a specific `#[repr(...)]`, therefore | |
149 | /// this is considered unsafe. | |
150 | /// | |
151 | /// ```no_run | |
152 | /// # use endian_trait::Endian; | |
9e7dfce8 | 153 | /// use proxmox::tools::io::ReadExt; |
2e6520a9 WB |
154 | /// |
155 | /// #[derive(Endian)] | |
156 | /// #[repr(C, packed)] | |
157 | /// struct Data { | |
158 | /// value: u16, | |
159 | /// count: u32, | |
160 | /// } | |
161 | /// | |
162 | /// # fn code() -> std::io::Result<()> { | |
163 | /// let mut file = std::fs::File::open("my-big-endian.dat")?; | |
164 | /// // We know `Data` has a safe binary representation (#[repr(C, packed)]), so we can | |
165 | /// // safely use our helper: | |
166 | /// let data: Data = unsafe { file.read_be_value()? }; | |
167 | /// # Ok(()) | |
168 | /// # } | |
169 | /// ``` | |
170 | /// | |
171 | /// [`Endian`]: https://docs.rs/endian_trait/0.6/endian_trait/trait.Endian.html | |
172 | unsafe fn read_be_value<T: Endian>(&mut self) -> io::Result<T>; | |
173 | } | |
174 | ||
9e7dfce8 | 175 | impl<R: io::Read> ReadExt for R { |
2e6520a9 WB |
176 | fn read_exact_allocated(&mut self, size: usize) -> io::Result<Vec<u8>> { |
177 | let mut out = unsafe { vec::uninitialized(size) }; | |
178 | self.read_exact(&mut out)?; | |
179 | Ok(out) | |
180 | } | |
181 | ||
182 | fn append_to_vec(&mut self, out: &mut Vec<u8>, size: usize) -> io::Result<usize> { | |
183 | let pos = out.len(); | |
184 | unsafe { | |
185 | out.grow_uninitialized(size); | |
186 | } | |
187 | let got = self.read(&mut out[pos..])?; | |
188 | unsafe { | |
189 | out.set_len(pos + got); | |
190 | } | |
191 | Ok(got) | |
192 | } | |
193 | ||
194 | fn append_exact_to_vec(&mut self, out: &mut Vec<u8>, size: usize) -> io::Result<()> { | |
195 | let pos = out.len(); | |
196 | unsafe { | |
197 | out.grow_uninitialized(size); | |
198 | } | |
199 | self.read_exact(&mut out[pos..])?; | |
200 | Ok(()) | |
201 | } | |
202 | ||
203 | unsafe fn read_host_value<T: Endian>(&mut self) -> io::Result<T> { | |
204 | let mut value: T = std::mem::uninitialized(); | |
205 | self.read_exact(std::slice::from_raw_parts_mut( | |
206 | &mut value as *mut T as *mut u8, | |
207 | std::mem::size_of::<T>(), | |
208 | ))?; | |
209 | Ok(value) | |
210 | } | |
211 | ||
212 | unsafe fn read_le_value<T: Endian>(&mut self) -> io::Result<T> { | |
3dd6cd3f | 213 | Ok(self.read_host_value::<T>()?.from_le()) |
2e6520a9 WB |
214 | } |
215 | ||
216 | unsafe fn read_be_value<T: Endian>(&mut self) -> io::Result<T> { | |
3dd6cd3f | 217 | Ok(self.read_host_value::<T>()?.from_be()) |
2e6520a9 WB |
218 | } |
219 | } |