/rust/registry/src/index.crates.io-6f17d22bba15001f/shared-buffer-0.1.4/src/mmap.rs
Line | Count | Source (jump to first uncovered line) |
1 | | use std::{ |
2 | | fmt::Display, |
3 | | fs::File, |
4 | | ops::{Bound, RangeBounds}, |
5 | | path::{Path, PathBuf}, |
6 | | sync::Arc, |
7 | | }; |
8 | | |
9 | | use bytes::Buf; |
10 | | use memmap2::Mmap; |
11 | | |
12 | | #[derive(Debug, Clone)] |
13 | | pub(crate) struct MmappedSlice { |
14 | | mmap: Arc<Mmap>, |
15 | | start: usize, |
16 | | end: usize, |
17 | | } |
18 | | |
19 | | impl MmappedSlice { |
20 | 0 | pub fn from_path(path: &Path) -> Result<Self, MmapError> { |
21 | 0 | let f = File::open(path).map_err(|error| MmapError::FileOpen { |
22 | 0 | error, |
23 | 0 | path: path.to_path_buf(), |
24 | 0 | })?; |
25 | 0 | MmappedSlice::from_file(&f) |
26 | 0 | } |
27 | | |
28 | 0 | pub fn from_file(file: &File) -> Result<Self, MmapError> { |
29 | | unsafe { |
30 | 0 | let mmap = Mmap::map(file).map_err(MmapError::Map)?; |
31 | 0 | let end = mmap.len(); |
32 | 0 | Ok(MmappedSlice { |
33 | 0 | mmap: Arc::new(mmap), |
34 | 0 | start: 0, |
35 | 0 | end, |
36 | 0 | }) |
37 | | } |
38 | 0 | } |
39 | | |
40 | | #[inline] |
41 | 0 | pub fn as_slice(&self) -> &[u8] { |
42 | 0 | &self.mmap[self.start..self.end] |
43 | 0 | } Unexecuted instantiation: <shared_buffer::mmap::MmappedSlice>::as_slice Unexecuted instantiation: <shared_buffer::mmap::MmappedSlice>::as_slice Unexecuted instantiation: <shared_buffer::mmap::MmappedSlice>::as_slice |
44 | | |
45 | | #[inline] |
46 | | #[track_caller] |
47 | 0 | pub fn slice(&self, range: impl RangeBounds<usize>) -> Self { |
48 | 0 | let (start, end) = bounds(self.start, self.end, range); |
49 | 0 | MmappedSlice { |
50 | 0 | mmap: Arc::clone(&self.mmap), |
51 | 0 | start, |
52 | 0 | end, |
53 | 0 | } |
54 | 0 | } |
55 | | } |
56 | | |
57 | | #[track_caller] |
58 | 0 | fn bounds( |
59 | 0 | original_start: usize, |
60 | 0 | original_end: usize, |
61 | 0 | range: impl RangeBounds<usize>, |
62 | 0 | ) -> (usize, usize) { |
63 | 0 | let start_offset = match range.start_bound() { |
64 | 0 | Bound::Included(&index) => index, |
65 | 0 | Bound::Excluded(index) => index.saturating_sub(1), |
66 | 0 | Bound::Unbounded => 0, |
67 | | }; |
68 | 0 | let start = original_start + start_offset; |
69 | | |
70 | 0 | let end = match range.end_bound() { |
71 | 0 | Bound::Included(index) => original_start + index.saturating_add(1), |
72 | 0 | Bound::Excluded(&index) => original_start + index, |
73 | 0 | Bound::Unbounded => original_end, |
74 | | }; |
75 | | |
76 | 0 | assert!(start <= end, "{start} <= {end}"); |
77 | 0 | assert!( |
78 | 0 | start >= original_start, |
79 | 0 | "Start offset out of bounds: {start} >= {original_start}" |
80 | | ); |
81 | 0 | assert!( |
82 | 0 | end <= original_end, |
83 | 0 | "End offset out of bounds: {end} <= {original_end}" |
84 | | ); |
85 | | |
86 | 0 | (start, end) |
87 | 0 | } |
88 | | |
89 | | impl Buf for MmappedSlice { |
90 | 0 | fn remaining(&self) -> usize { |
91 | 0 | self.as_slice().len() |
92 | 0 | } |
93 | | |
94 | 0 | fn chunk(&self) -> &[u8] { |
95 | 0 | self.as_slice() |
96 | 0 | } |
97 | | |
98 | 0 | fn advance(&mut self, cnt: usize) { |
99 | 0 | debug_assert!(cnt <= self.remaining()); |
100 | 0 | self.start += cnt; |
101 | 0 | } |
102 | | } |
103 | | |
104 | | /// Errors that may occur when using one of the mmap-based implementations of |
105 | | /// [`TryFrom`]. |
106 | | #[derive(Debug)] |
107 | | pub enum MmapError { |
108 | | /// Unable to open the file. |
109 | | FileOpen { |
110 | | error: std::io::Error, |
111 | | path: PathBuf, |
112 | | }, |
113 | | /// Mapping the file into memory failed. |
114 | | Map(std::io::Error), |
115 | | } |
116 | | |
117 | | impl Display for MmapError { |
118 | 0 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
119 | 0 | match self { |
120 | 0 | MmapError::FileOpen { path, .. } => write!(f, "Unable to open \"{}\"", path.display()), |
121 | 0 | MmapError::Map(_) => write!(f, "Unable to map the file into memory"), |
122 | | } |
123 | 0 | } |
124 | | } |
125 | | |
126 | | impl std::error::Error for MmapError { |
127 | 0 | fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { |
128 | 0 | match self { |
129 | 0 | MmapError::FileOpen { error, .. } | MmapError::Map(error) => Some(error), |
130 | 0 | } |
131 | 0 | } |
132 | | } |
133 | | |
134 | | #[cfg(test)] |
135 | | mod tests { |
136 | | use std::io::Write; |
137 | | |
138 | | use super::*; |
139 | | |
140 | | #[test] |
141 | | fn full_range() { |
142 | | let (start, end) = bounds(1, 10, ..); |
143 | | |
144 | | assert_eq!(start, 1); |
145 | | assert_eq!(end, 10); |
146 | | } |
147 | | |
148 | | #[test] |
149 | | fn range_to() { |
150 | | let (start, end) = bounds(1, 10, ..5); |
151 | | |
152 | | assert_eq!(start, 1); |
153 | | assert_eq!(end, 1 + 5); |
154 | | } |
155 | | |
156 | | #[test] |
157 | | fn range_to_inclusive() { |
158 | | let (start, end) = bounds(1, 10, ..=5); |
159 | | |
160 | | assert_eq!(start, 1); |
161 | | assert_eq!(end, 1 + 6); |
162 | | } |
163 | | |
164 | | #[test] |
165 | | fn range_from() { |
166 | | let (start, end) = bounds(1, 10, 5..); |
167 | | |
168 | | assert_eq!(start, 1 + 5); |
169 | | assert_eq!(end, 10); |
170 | | } |
171 | | |
172 | | #[test] |
173 | | fn range() { |
174 | | let (start, end) = bounds(1, 10, 5..8); |
175 | | |
176 | | assert_eq!(start, 1 + 5); |
177 | | assert_eq!(end, 1 + 8); |
178 | | } |
179 | | |
180 | | #[test] |
181 | | fn range_at_end() { |
182 | | let (start, end) = bounds(1, 10, 5..9); |
183 | | |
184 | | assert_eq!(start, 1 + 5); |
185 | | assert_eq!(end, 1 + 9); |
186 | | } |
187 | | |
188 | | #[test] |
189 | | fn range_at_start() { |
190 | | let (start, end) = bounds(1, 10, 1..5); |
191 | | |
192 | | assert_eq!(start, 1 + 1); |
193 | | assert_eq!(end, 1 + 5); |
194 | | } |
195 | | |
196 | | #[test] |
197 | | fn range_inclusive() { |
198 | | let (start, end) = bounds(1, 10, 1..=5); |
199 | | |
200 | | assert_eq!(start, 1 + 1); |
201 | | assert_eq!(end, 1 + 5 + 1); |
202 | | } |
203 | | |
204 | | #[test] |
205 | | fn range_inclusive_at_end() { |
206 | | let (start, end) = bounds(1, 10, 5..=8); |
207 | | |
208 | | assert_eq!(start, 1 + 5); |
209 | | assert_eq!(end, 1 + 8 + 1); |
210 | | } |
211 | | |
212 | | #[test] |
213 | | fn simple_mmap() { |
214 | | let mut temp = tempfile::tempfile().unwrap(); |
215 | | let content = b"Hello, World!"; |
216 | | temp.write_all(content).unwrap(); |
217 | | |
218 | | let mmap = MmappedSlice::from_file(&temp).unwrap(); |
219 | | |
220 | | assert_eq!(mmap.as_slice(), content); |
221 | | } |
222 | | |
223 | | #[test] |
224 | | fn slice_mmap() { |
225 | | let mut temp = tempfile::tempfile().unwrap(); |
226 | | let content = b"Hello, World!"; |
227 | | temp.write_all(content).unwrap(); |
228 | | let mmap = MmappedSlice::from_file(&temp).unwrap(); |
229 | | |
230 | | let slice = mmap.slice(..5); |
231 | | |
232 | | assert_eq!(slice.as_slice(), b"Hello"); |
233 | | } |
234 | | |
235 | | #[test] |
236 | | fn slicing_is_relative_to_the_slice_not_the_overall_file() { |
237 | | let mut temp = tempfile::tempfile().unwrap(); |
238 | | let content = "Hello, World!"; |
239 | | temp.write_all(content.as_ref()).unwrap(); |
240 | | let mmap = MmappedSlice::from_file(&temp).unwrap(); |
241 | | let slice = mmap.slice(3..); |
242 | | |
243 | | let sub_slice = slice.slice(4..7); |
244 | | |
245 | | assert_eq!( |
246 | | std::str::from_utf8(sub_slice.as_slice()).unwrap(), |
247 | | &content[3 + 4..3 + 7] |
248 | | ); |
249 | | } |
250 | | } |