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 memmap::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 
deref(&self) -> &Self::Target30     fn deref(&self) -> &Self::Target {
31         match *self {
32             ByteViewBacking::Buf(ref buf) => buf,
33             ByteViewBacking::Mmap(ref mmap) => mmap,
34         }
35     }
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> {
with_backing(backing: ByteViewBacking<'a>) -> Self71     fn with_backing(backing: ByteViewBacking<'a>) -> Self {
72         ByteView {
73             backing: Arc::new(backing),
74         }
75     }
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     /// ```
from_cow(cow: Cow<'a, [u8]>) -> Self88     pub fn from_cow(cow: Cow<'a, [u8]>) -> Self {
89         ByteView::with_backing(ByteViewBacking::Buf(cow))
90     }
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     /// ```
from_slice(buffer: &'a [u8]) -> Self101     pub fn from_slice(buffer: &'a [u8]) -> Self {
102         ByteView::from_cow(Cow::Borrowed(buffer))
103     }
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     /// ```
from_vec(buffer: Vec<u8>) -> Self115     pub fn from_vec(buffer: Vec<u8>) -> Self {
116         ByteView::from_cow(Cow::Owned(buffer))
117     }
118 
119     /// Constructs a `ByteView` from an open file handle by memory mapping the file.
120     ///
121     /// # Example
122     ///
123     /// ```
124     /// use std::io::Write;
125     /// use symbolic_common::ByteView;
126     ///
127     /// fn main() -> Result<(), std::io::Error> {
128     ///     let mut file = tempfile::tempfile()?;
129     ///     let view = ByteView::map_file(file)?;
130     ///     Ok(())
131     /// }
132     /// ```
map_file(file: File) -> Result<Self, io::Error>133     pub fn map_file(file: File) -> Result<Self, io::Error> {
134         let backing = match unsafe { Mmap::map(&file) } {
135             Ok(mmap) => ByteViewBacking::Mmap(mmap),
136             Err(err) => {
137                 // this is raised on empty mmaps which we want to ignore. The 1006 Windows error
138                 // looks like "The volume for a file has been externally altered so that the opened
139                 // file is no longer valid."
140                 if err.kind() == io::ErrorKind::InvalidInput
141                     || (cfg!(windows) && err.raw_os_error() == Some(1006))
142                 {
143                     ByteViewBacking::Buf(Cow::Borrowed(b""))
144                 } else {
145                     return Err(err);
146                 }
147             }
148         };
149 
150         Ok(ByteView::with_backing(backing))
151     }
152 
153     /// Constructs a `ByteView` from any `std::io::Reader`.
154     ///
155     /// **Note**: This currently consumes the entire reader and stores its data in an internal
156     /// buffer. Prefer [`open`] when reading from the file system or [`from_slice`] / [`from_vec`]
157     /// for in-memory operations. This behavior might change in the future.
158     ///
159     /// # Example
160     ///
161     /// ```
162     /// use std::io::Cursor;
163     /// use symbolic_common::ByteView;
164     ///
165     /// fn main() -> Result<(), std::io::Error> {
166     ///     let reader = Cursor::new(b"1234");
167     ///     let view = ByteView::read(reader)?;
168     ///     Ok(())
169     /// }
170     /// ```
171     ///
172     /// [`open`]: struct.ByteView.html#method.open
173     /// [`from_slice`]: struct.ByteView.html#method.from_slice
174     /// [`from_vec`]: struct.ByteView.html#method.from_vec
read<R: io::Read>(mut reader: R) -> Result<Self, io::Error>175     pub fn read<R: io::Read>(mut reader: R) -> Result<Self, io::Error> {
176         let mut buffer = vec![];
177         reader.read_to_end(&mut buffer)?;
178         Ok(ByteView::from_vec(buffer))
179     }
180 
181     /// Constructs a `ByteView` from a file path by memory mapping the file.
182     ///
183     /// # Example
184     ///
185     /// ```no_run
186     /// use symbolic_common::ByteView;
187     ///
188     /// fn main() -> Result<(), std::io::Error> {
189     ///     let view = ByteView::open("test.txt")?;
190     ///     Ok(())
191     /// }
192     /// ```
open<P: AsRef<Path>>(path: P) -> Result<Self, io::Error>193     pub fn open<P: AsRef<Path>>(path: P) -> Result<Self, io::Error> {
194         let file = File::open(path)?;
195         Self::map_file(file)
196     }
197 
198     /// Returns a slice of the underlying data.
199     ///
200     ///
201     /// # Example
202     ///
203     /// ```
204     /// use symbolic_common::ByteView;
205     ///
206     /// let view = ByteView::from_slice(b"1234");
207     /// let data = view.as_slice();
208     /// ```
209     #[inline(always)]
as_slice(&self) -> &[u8]210     pub fn as_slice(&self) -> &[u8] {
211         self.backing.deref()
212     }
213 }
214 
215 impl AsRef<[u8]> for ByteView<'_> {
216     #[inline(always)]
as_ref(&self) -> &[u8]217     fn as_ref(&self) -> &[u8] {
218         self.as_slice()
219     }
220 }
221 
222 impl Deref for ByteView<'_> {
223     type Target = [u8];
224 
225     #[inline(always)]
deref(&self) -> &Self::Target226     fn deref(&self) -> &Self::Target {
227         self.as_slice()
228     }
229 }
230 
231 unsafe impl StableDeref for ByteView<'_> {}
232 
233 #[cfg(test)]
234 mod tests {
235     use super::*;
236 
237     use std::io::Write;
238 
239     use similar_asserts::assert_eq;
240     use tempfile::NamedTempFile;
241 
242     #[test]
test_open_empty_file() -> Result<(), std::io::Error>243     fn test_open_empty_file() -> Result<(), std::io::Error> {
244         let tmp = NamedTempFile::new()?;
245 
246         let view = ByteView::open(&tmp.path())?;
247         assert_eq!(&*view, b"");
248 
249         Ok(())
250     }
251 
252     #[test]
test_open_file() -> Result<(), std::io::Error>253     fn test_open_file() -> Result<(), std::io::Error> {
254         let mut tmp = NamedTempFile::new()?;
255 
256         tmp.write_all(b"1234")?;
257 
258         let view = ByteView::open(&tmp.path())?;
259         assert_eq!(&*view, b"1234");
260 
261         Ok(())
262     }
263 }
264