1use std::fs::File;
2use std::ops::Deref;
3use std::ops::Range;
4use std::path::Path;
5use std::rc::Rc;
6
7use memmap2::Mmap as Mapping;
8use memmap2::MmapOptions;
9
10use crate::Error;
11use crate::ErrorExt as _;
12use crate::Result;
13
14#[cfg(not(windows))]
15pub(crate) type Advice = memmap2::Advice;
16
17
18#[doc(hidden)]
19#[derive(Debug)]
20pub struct Builder {
21 exec: bool,
22}
23
24impl Builder {
25 fn new() -> Self {
26 Self { exec: false }
27 }
28
29 #[doc(hidden)]
31 pub fn exec(mut self) -> Self {
32 self.exec = true;
33 self
34 }
35
36 #[doc(hidden)]
38 pub fn open<P>(self, path: P) -> Result<Mmap>
39 where
40 P: AsRef<Path>,
41 {
42 let file = File::open(path)?;
43 self.map(&file)
44 }
45
46 pub(crate) fn map(self, file: &File) -> Result<Mmap> {
48 let len = libc::size_t::try_from(file.metadata()?.len())
49 .map_err(Error::with_invalid_data)
50 .context("file is too large to mmap")?;
51
52 let mmap = if len == 0 {
55 Mmap {
56 mapping: None,
57 view: 0..1,
58 }
59 } else {
60 let opts = MmapOptions::new();
61
62 let mapping = if self.exec {
67 unsafe { opts.map_exec(file) }
68 } else {
69 unsafe { opts.map(file) }
70 }?;
71
72 Mmap {
73 mapping: Some(Rc::new(mapping)),
74 view: 0..len as u64,
75 }
76 };
77 Ok(mmap)
78 }
79}
80
81
82#[derive(Clone, Debug)]
84pub struct Mmap {
85 mapping: Option<Rc<Mapping>>,
87 view: Range<u64>,
89}
90
91#[allow(rustdoc::private_intra_doc_links)]
92impl Mmap {
93 #[doc(hidden)]
95 pub fn builder() -> Builder {
96 Builder::new()
97 }
98
99 pub(crate) fn map(file: &File) -> Result<Self> {
101 Self::builder().map(file)
102 }
103
104 #[cfg(not(windows))]
106 pub(crate) fn advise(&self, advise: Advice) -> Result<()> {
107 self.mapping
108 .as_ref()
109 .map(|mmap| mmap.advise(advise).map_err(Error::from))
110 .unwrap_or(Ok(()))
111 }
112
113 #[doc(hidden)]
117 pub fn constrain(&self, range: Range<u64>) -> Option<Self> {
118 if self.view.start + range.end > self.view.end {
119 return None
120 }
121
122 let mut mmap = self.clone();
123 mmap.view.end = mmap.view.start + range.end;
124 mmap.view.start += range.start;
125 Some(mmap)
126 }
127}
128
129impl Deref for Mmap {
130 type Target = [u8];
131
132 fn deref(&self) -> &Self::Target {
133 if let Some(mapping) = &self.mapping {
134 mapping
135 .deref()
136 .get(self.view.start as usize..self.view.end as usize)
137 .unwrap_or(&[])
138 } else {
139 &[]
140 }
141 }
142}
143
144
145#[cfg(test)]
146mod tests {
147 use super::*;
148
149 use std::ffi::CStr;
150 use std::io::Write;
151
152 use tempfile::tempfile;
153 use test_log::test;
154
155 use crate::util::ReadRaw;
156
157
158 #[test]
160 fn debug_repr() {
161 let builder = Builder::new();
162 assert_ne!(format!("{builder:?}"), "");
163 }
164
165 #[test]
167 fn mmap_empty_file() {
168 let file = tempfile().unwrap();
169 let mmap = Mmap::map(&file).unwrap();
170 assert_eq!(mmap.deref(), &[] as &[u8]);
171 }
172
173 #[test]
175 fn mmap() {
176 let mut file = tempfile().unwrap();
177 let cstr = b"Daniel was here. Briefly.\0";
178 let () = file.write_all(cstr).unwrap();
179 let () = file.sync_all().unwrap();
180
181 let mmap = Mmap::map(&file).unwrap();
182 let mut data = mmap.deref();
183 let s = data.read_cstr().unwrap();
184 assert_eq!(
185 s.to_str().unwrap(),
186 CStr::from_bytes_with_nul(cstr).unwrap().to_str().unwrap()
187 );
188 }
189
190 #[test]
192 fn view_constraining() {
193 let mut file = tempfile().unwrap();
194 let s = b"abcdefghijklmnopqrstuvwxyz";
195 let () = file.write_all(s).unwrap();
196 let () = file.sync_all().unwrap();
197
198 let mmap = Mmap::map(&file).unwrap();
199 assert_eq!(mmap.deref(), b"abcdefghijklmnopqrstuvwxyz");
200
201 let mmap = mmap.constrain(1..15).unwrap();
202 assert_eq!(mmap.deref(), b"bcdefghijklmno");
203
204 let mmap = mmap.constrain(5..6).unwrap();
205 assert_eq!(mmap.deref(), b"g");
206
207 assert!(mmap.constrain(1..2).is_none());
208 }
209}