]> git.proxmox.com Git - proxmox.git/blame - proxmox-tools/src/io/read.rs
replace std::mem::uninitialized
[proxmox.git] / proxmox-tools / src / io / read.rs
CommitLineData
9e7dfce8 1//! Helpers for `Read`.
2e6520a9
WB
2
3use std::io;
4
5use endian_trait::Endian;
6
3111d615 7use crate::vec::{self, ByteVecExt};
2e6520a9
WB
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 53pub 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 175impl<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> {
45b5839e 204 let mut value = std::mem::MaybeUninit::<T>::uninit();
2e6520a9 205 self.read_exact(std::slice::from_raw_parts_mut(
45b5839e 206 value.as_mut_ptr() as *mut u8,
2e6520a9
WB
207 std::mem::size_of::<T>(),
208 ))?;
45b5839e 209 Ok(value.assume_init())
2e6520a9
WB
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}