Coverage Report

Created: 2025-11-16 06:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}