/rust/registry/src/index.crates.io-1949cf8c6b5b557f/symbolic-common-12.13.4/src/byteview.rs
Line | Count | Source |
1 | | //! A wrapper type providing direct memory access to binary data. |
2 | | //! |
3 | | //! See the [`ByteView`] struct for more documentation. |
4 | | //! |
5 | | //! [`ByteView`]: struct.ByteView.html |
6 | | |
7 | | use std::borrow::Cow; |
8 | | use std::fs::File; |
9 | | use std::io; |
10 | | use std::ops::Deref; |
11 | | use std::path::Path; |
12 | | use std::sync::Arc; |
13 | | |
14 | | use memmap2::Mmap; |
15 | | |
16 | | use crate::cell::StableDeref; |
17 | | |
18 | | /// The owner of data behind a ByteView. |
19 | | /// |
20 | | /// This can either be an mmapped file, an owned buffer or a borrowed binary slice. |
21 | | #[derive(Debug)] |
22 | | enum ByteViewBacking<'a> { |
23 | | Buf(Cow<'a, [u8]>), |
24 | | Mmap(Mmap), |
25 | | } |
26 | | |
27 | | impl Deref for ByteViewBacking<'_> { |
28 | | type Target = [u8]; |
29 | | |
30 | 0 | fn deref(&self) -> &Self::Target { |
31 | 0 | match *self { |
32 | 0 | ByteViewBacking::Buf(ref buf) => buf, |
33 | 0 | ByteViewBacking::Mmap(ref mmap) => mmap, |
34 | | } |
35 | 0 | } |
36 | | } |
37 | | |
38 | | /// A smart pointer for byte data. |
39 | | /// |
40 | | /// This type can be used to uniformly access bytes that were created either from mmapping in a |
41 | | /// path, a vector or a borrowed slice. A `ByteView` dereferences into a `&[u8]` and guarantees |
42 | | /// random access to the underlying buffer or file. |
43 | | /// |
44 | | /// A `ByteView` can be constructed from borrowed slices, vectors or memory mapped from the file |
45 | | /// system directly. |
46 | | /// |
47 | | /// # Example |
48 | | /// |
49 | | /// The most common way to use `ByteView` is to construct it from a file handle. This will own the |
50 | | /// underlying file handle until the `ByteView` is dropped: |
51 | | /// |
52 | | /// ``` |
53 | | /// use std::io::Write; |
54 | | /// use symbolic_common::ByteView; |
55 | | /// |
56 | | /// fn main() -> Result<(), std::io::Error> { |
57 | | /// let mut file = tempfile::tempfile()?; |
58 | | /// file.write_all(b"1234"); |
59 | | /// |
60 | | /// let view = ByteView::map_file(file)?; |
61 | | /// assert_eq!(view.as_slice(), b"1234"); |
62 | | /// Ok(()) |
63 | | /// } |
64 | | /// ``` |
65 | | #[derive(Clone, Debug)] |
66 | | pub struct ByteView<'a> { |
67 | | backing: Arc<ByteViewBacking<'a>>, |
68 | | } |
69 | | |
70 | | impl<'a> ByteView<'a> { |
71 | 0 | fn with_backing(backing: ByteViewBacking<'a>) -> Self { |
72 | 0 | ByteView { |
73 | 0 | backing: Arc::new(backing), |
74 | 0 | } |
75 | 0 | } |
76 | | |
77 | | /// Constructs a `ByteView` from a `Cow`. |
78 | | /// |
79 | | /// # Example |
80 | | /// |
81 | | /// ``` |
82 | | /// use std::borrow::Cow; |
83 | | /// use symbolic_common::ByteView; |
84 | | /// |
85 | | /// let cow = Cow::Borrowed(&b"1234"[..]); |
86 | | /// let view = ByteView::from_cow(cow); |
87 | | /// ``` |
88 | 0 | pub fn from_cow(cow: Cow<'a, [u8]>) -> Self { |
89 | 0 | ByteView::with_backing(ByteViewBacking::Buf(cow)) |
90 | 0 | } |
91 | | |
92 | | /// Constructs a `ByteView` from a byte slice. |
93 | | /// |
94 | | /// # Example |
95 | | /// |
96 | | /// ``` |
97 | | /// use symbolic_common::ByteView; |
98 | | /// |
99 | | /// let view = ByteView::from_slice(b"1234"); |
100 | | /// ``` |
101 | 0 | pub fn from_slice(buffer: &'a [u8]) -> Self { |
102 | 0 | ByteView::from_cow(Cow::Borrowed(buffer)) |
103 | 0 | } |
104 | | |
105 | | /// Constructs a `ByteView` from a vector of bytes. |
106 | | /// |
107 | | /// # Example |
108 | | /// |
109 | | /// ``` |
110 | | /// use symbolic_common::ByteView; |
111 | | /// |
112 | | /// let vec = b"1234".to_vec(); |
113 | | /// let view = ByteView::from_vec(vec); |
114 | | /// ``` |
115 | 0 | pub fn from_vec(buffer: Vec<u8>) -> Self { |
116 | 0 | ByteView::from_cow(Cow::Owned(buffer)) |
117 | 0 | } |
118 | | |
119 | | /// Constructs a `ByteView` from an open file handle by memory mapping the file. |
120 | | /// |
121 | | /// See [`ByteView::map_file_ref`] for a non-consuming version of this constructor. |
122 | | /// |
123 | | /// # Example |
124 | | /// |
125 | | /// ``` |
126 | | /// use std::io::Write; |
127 | | /// use symbolic_common::ByteView; |
128 | | /// |
129 | | /// fn main() -> Result<(), std::io::Error> { |
130 | | /// let mut file = tempfile::tempfile()?; |
131 | | /// let view = ByteView::map_file(file)?; |
132 | | /// Ok(()) |
133 | | /// } |
134 | | /// ``` |
135 | 0 | pub fn map_file(file: File) -> Result<Self, io::Error> { |
136 | 0 | Self::map_file_ref(&file) |
137 | 0 | } |
138 | | |
139 | | /// Constructs a `ByteView` from an open file handle by memory mapping the file. |
140 | | /// |
141 | | /// The main difference with [`ByteView::map_file`] is that this takes the [`File`] by |
142 | | /// reference rather than consuming it. |
143 | | /// |
144 | | /// # Example |
145 | | /// |
146 | | /// ``` |
147 | | /// use std::io::Write; |
148 | | /// use symbolic_common::ByteView; |
149 | | /// |
150 | | /// fn main() -> Result<(), std::io::Error> { |
151 | | /// let mut file = tempfile::tempfile()?; |
152 | | /// let view = ByteView::map_file_ref(&file)?; |
153 | | /// Ok(()) |
154 | | /// } |
155 | | /// ``` |
156 | 0 | pub fn map_file_ref(file: &File) -> Result<Self, io::Error> { |
157 | 0 | let backing = match unsafe { Mmap::map(file) } { |
158 | 0 | Ok(mmap) => ByteViewBacking::Mmap(mmap), |
159 | 0 | Err(err) => { |
160 | | // this is raised on empty mmaps which we want to ignore. The 1006 Windows error |
161 | | // looks like "The volume for a file has been externally altered so that the opened |
162 | | // file is no longer valid." |
163 | 0 | if err.kind() == io::ErrorKind::InvalidInput |
164 | 0 | || (cfg!(windows) && err.raw_os_error() == Some(1006)) |
165 | | { |
166 | 0 | ByteViewBacking::Buf(Cow::Borrowed(b"")) |
167 | | } else { |
168 | 0 | return Err(err); |
169 | | } |
170 | | } |
171 | | }; |
172 | | |
173 | 0 | Ok(ByteView::with_backing(backing)) |
174 | 0 | } |
175 | | |
176 | | /// Constructs a `ByteView` from any `std::io::Reader`. |
177 | | /// |
178 | | /// **Note**: This currently consumes the entire reader and stores its data in an internal |
179 | | /// buffer. Prefer [`open`] when reading from the file system or [`from_slice`] / [`from_vec`] |
180 | | /// for in-memory operations. This behavior might change in the future. |
181 | | /// |
182 | | /// # Example |
183 | | /// |
184 | | /// ``` |
185 | | /// use std::io::Cursor; |
186 | | /// use symbolic_common::ByteView; |
187 | | /// |
188 | | /// fn main() -> Result<(), std::io::Error> { |
189 | | /// let reader = Cursor::new(b"1234"); |
190 | | /// let view = ByteView::read(reader)?; |
191 | | /// Ok(()) |
192 | | /// } |
193 | | /// ``` |
194 | | /// |
195 | | /// [`open`]: struct.ByteView.html#method.open |
196 | | /// [`from_slice`]: struct.ByteView.html#method.from_slice |
197 | | /// [`from_vec`]: struct.ByteView.html#method.from_vec |
198 | 0 | pub fn read<R: io::Read>(mut reader: R) -> Result<Self, io::Error> { |
199 | 0 | let mut buffer = vec![]; |
200 | 0 | reader.read_to_end(&mut buffer)?; |
201 | 0 | Ok(ByteView::from_vec(buffer)) |
202 | 0 | } |
203 | | |
204 | | /// Constructs a `ByteView` from a file path by memory mapping the file. |
205 | | /// |
206 | | /// # Example |
207 | | /// |
208 | | /// ```no_run |
209 | | /// use symbolic_common::ByteView; |
210 | | /// |
211 | | /// fn main() -> Result<(), std::io::Error> { |
212 | | /// let view = ByteView::open("test.txt")?; |
213 | | /// Ok(()) |
214 | | /// } |
215 | | /// ``` |
216 | 0 | pub fn open<P: AsRef<Path>>(path: P) -> Result<Self, io::Error> { |
217 | 0 | let file = File::open(path)?; |
218 | 0 | Self::map_file(file) |
219 | 0 | } |
220 | | |
221 | | /// Returns a slice of the underlying data. |
222 | | /// |
223 | | /// |
224 | | /// # Example |
225 | | /// |
226 | | /// ``` |
227 | | /// use symbolic_common::ByteView; |
228 | | /// |
229 | | /// let view = ByteView::from_slice(b"1234"); |
230 | | /// let data = view.as_slice(); |
231 | | /// ``` |
232 | | #[inline(always)] |
233 | 0 | pub fn as_slice(&self) -> &[u8] { |
234 | 0 | self.backing.deref() |
235 | 0 | } |
236 | | } |
237 | | |
238 | | impl AsRef<[u8]> for ByteView<'_> { |
239 | | #[inline(always)] |
240 | 0 | fn as_ref(&self) -> &[u8] { |
241 | 0 | self.as_slice() |
242 | 0 | } |
243 | | } |
244 | | |
245 | | impl Deref for ByteView<'_> { |
246 | | type Target = [u8]; |
247 | | |
248 | | #[inline(always)] |
249 | 0 | fn deref(&self) -> &Self::Target { |
250 | 0 | self.as_slice() |
251 | 0 | } |
252 | | } |
253 | | |
254 | | unsafe impl StableDeref for ByteView<'_> {} |
255 | | |
256 | | #[cfg(test)] |
257 | | mod tests { |
258 | | use super::*; |
259 | | |
260 | | use std::io::{Read, Seek, Write}; |
261 | | |
262 | | use similar_asserts::assert_eq; |
263 | | use tempfile::NamedTempFile; |
264 | | |
265 | | #[test] |
266 | | fn test_open_empty_file() -> Result<(), std::io::Error> { |
267 | | let tmp = NamedTempFile::new()?; |
268 | | |
269 | | let view = ByteView::open(tmp.path())?; |
270 | | assert_eq!(&*view, b""); |
271 | | |
272 | | Ok(()) |
273 | | } |
274 | | |
275 | | #[test] |
276 | | fn test_open_file() -> Result<(), std::io::Error> { |
277 | | let mut tmp = NamedTempFile::new()?; |
278 | | |
279 | | tmp.write_all(b"1234")?; |
280 | | |
281 | | let view = ByteView::open(tmp.path())?; |
282 | | assert_eq!(&*view, b"1234"); |
283 | | |
284 | | Ok(()) |
285 | | } |
286 | | |
287 | | #[test] |
288 | | fn test_mmap_fd_reuse() -> Result<(), std::io::Error> { |
289 | | let mut tmp = NamedTempFile::new()?; |
290 | | tmp.write_all(b"1234")?; |
291 | | |
292 | | let view = ByteView::map_file_ref(tmp.as_file())?; |
293 | | |
294 | | // This deletes the file on disk. |
295 | | let _path = tmp.path().to_path_buf(); |
296 | | let mut file = tmp.into_file(); |
297 | | #[cfg(not(windows))] |
298 | | { |
299 | | assert!(!_path.exists()); |
300 | | } |
301 | | |
302 | | // Ensure we can still read from the the file after mmapping and deleting it on disk. |
303 | | let mut buf = Vec::new(); |
304 | | file.rewind()?; |
305 | | file.read_to_end(&mut buf)?; |
306 | | assert_eq!(buf, b"1234"); |
307 | | drop(file); |
308 | | |
309 | | // Ensure the byteview can still read the file as well. |
310 | | assert_eq!(&*view, b"1234"); |
311 | | |
312 | | Ok(()) |
313 | | } |
314 | | } |