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