]>
Commit | Line | Data |
---|---|---|
b7449926 XL |
1 | //! A cross-platform Rust API for memory mapped buffers. |
2 | ||
60c5eb7d | 3 | #![doc(html_root_url = "https://docs.rs/memmap/0.7.0")] |
b7449926 XL |
4 | |
5 | #[cfg(windows)] | |
6 | extern crate winapi; | |
7 | #[cfg(windows)] | |
8 | mod windows; | |
9 | #[cfg(windows)] | |
10 | use windows::MmapInner; | |
11 | ||
12 | #[cfg(unix)] | |
13 | mod unix; | |
14 | #[cfg(unix)] | |
15 | use unix::MmapInner; | |
16 | ||
17 | use std::fmt; | |
18 | use std::fs::File; | |
19 | use std::io::{Error, ErrorKind, Result}; | |
60c5eb7d | 20 | use std::ops::{Deref, DerefMut}; |
b7449926 XL |
21 | use std::slice; |
22 | use std::usize; | |
b7449926 XL |
23 | |
24 | /// A memory map builder, providing advanced options and flags for specifying memory map behavior. | |
25 | /// | |
26 | /// `MmapOptions` can be used to create an anonymous memory map using `MmapOptions::map_anon`, or a | |
27 | /// file-backed memory map using one of `MmapOptions::map`, `MmapOptions::map_mut`, | |
28 | /// `MmapOptions::map_exec`, or `MmapOptions::map_copy`. | |
29 | #[derive(Clone, Debug, Default)] | |
30 | pub struct MmapOptions { | |
60c5eb7d | 31 | offset: u64, |
b7449926 XL |
32 | len: Option<usize>, |
33 | stack: bool, | |
34 | } | |
35 | ||
36 | impl MmapOptions { | |
37 | /// Creates a new set of options for configuring and creating a memory map. | |
38 | /// | |
39 | /// # Example | |
40 | /// | |
41 | /// ``` | |
42 | /// use memmap::{MmapMut, MmapOptions}; | |
43 | /// # use std::io::Result; | |
44 | /// | |
60c5eb7d | 45 | /// # fn main() -> Result<()> { |
b7449926 XL |
46 | /// // Create a new memory map builder. |
47 | /// let mut mmap_options = MmapOptions::new(); | |
48 | /// | |
49 | /// // Configure the memory map builder using option setters, then create | |
50 | /// // a memory map using one of `mmap_options.map_anon`, `mmap_options.map`, | |
51 | /// // `mmap_options.map_mut`, `mmap_options.map_exec`, or `mmap_options.map_copy`: | |
52 | /// let mut mmap: MmapMut = mmap_options.len(36).map_anon()?; | |
53 | /// | |
54 | /// // Use the memory map: | |
55 | /// mmap.copy_from_slice(b"...data to copy to the memory map..."); | |
56 | /// # let _ = mmap_options; | |
57 | /// # Ok(()) | |
58 | /// # } | |
b7449926 XL |
59 | /// ``` |
60 | pub fn new() -> MmapOptions { | |
61 | MmapOptions::default() | |
62 | } | |
63 | ||
64 | /// Configures the memory map to start at byte `offset` from the beginning of the file. | |
65 | /// | |
66 | /// This option has no effect on anonymous memory maps. | |
67 | /// | |
68 | /// By default, the offset is 0. | |
69 | /// | |
70 | /// # Example | |
71 | /// | |
72 | /// ``` | |
73 | /// use memmap::MmapOptions; | |
74 | /// use std::fs::File; | |
75 | /// | |
60c5eb7d | 76 | /// # fn main() -> std::io::Result<()> { |
b7449926 XL |
77 | /// let mmap = unsafe { |
78 | /// MmapOptions::new() | |
79 | /// .offset(10) | |
80 | /// .map(&File::open("README.md")?)? | |
81 | /// }; | |
82 | /// assert_eq!(&b"A Rust library for cross-platform memory mapped IO."[..], | |
83 | /// &mmap[..51]); | |
84 | /// # Ok(()) | |
85 | /// # } | |
b7449926 | 86 | /// ``` |
60c5eb7d | 87 | pub fn offset(&mut self, offset: u64) -> &mut Self { |
b7449926 XL |
88 | self.offset = offset; |
89 | self | |
90 | } | |
91 | ||
92 | /// Configures the created memory mapped buffer to be `len` bytes long. | |
93 | /// | |
94 | /// This option is mandatory for anonymous memory maps. | |
95 | /// | |
96 | /// For file-backed memory maps, the length will default to the file length. | |
97 | /// | |
98 | /// # Example | |
99 | /// | |
100 | /// ``` | |
101 | /// use memmap::MmapOptions; | |
102 | /// use std::fs::File; | |
103 | /// | |
60c5eb7d | 104 | /// # fn main() -> std::io::Result<()> { |
b7449926 XL |
105 | /// let mmap = unsafe { |
106 | /// MmapOptions::new() | |
107 | /// .len(8) | |
108 | /// .map(&File::open("README.md")?)? | |
109 | /// }; | |
110 | /// assert_eq!(&b"# memmap"[..], &mmap[..]); | |
111 | /// # Ok(()) | |
112 | /// # } | |
b7449926 XL |
113 | /// ``` |
114 | pub fn len(&mut self, len: usize) -> &mut Self { | |
115 | self.len = Some(len); | |
116 | self | |
117 | } | |
118 | ||
119 | /// Returns the configured length, or the length of the provided file. | |
120 | fn get_len(&self, file: &File) -> Result<usize> { | |
121 | self.len.map(Ok).unwrap_or_else(|| { | |
60c5eb7d | 122 | let len = file.metadata()?.len() - self.offset; |
b7449926 XL |
123 | if len > (usize::MAX as u64) { |
124 | return Err(Error::new( | |
125 | ErrorKind::InvalidData, | |
60c5eb7d | 126 | "memory map length overflows usize", |
b7449926 XL |
127 | )); |
128 | } | |
60c5eb7d | 129 | Ok(len as usize) |
b7449926 XL |
130 | }) |
131 | } | |
132 | ||
133 | /// Configures the anonymous memory map to be suitable for a process or thread stack. | |
134 | /// | |
135 | /// This option corresponds to the `MAP_STACK` flag on Linux. | |
136 | /// | |
137 | /// This option has no effect on file-backed memory maps. | |
138 | /// | |
139 | /// # Example | |
140 | /// | |
141 | /// ``` | |
142 | /// use memmap::MmapOptions; | |
143 | /// | |
60c5eb7d | 144 | /// # fn main() -> std::io::Result<()> { |
b7449926 XL |
145 | /// let stack = MmapOptions::new().stack().len(4096).map_anon(); |
146 | /// # Ok(()) | |
147 | /// # } | |
b7449926 XL |
148 | /// ``` |
149 | pub fn stack(&mut self) -> &mut Self { | |
150 | self.stack = true; | |
151 | self | |
152 | } | |
153 | ||
154 | /// Creates a read-only memory map backed by a file. | |
155 | /// | |
156 | /// # Errors | |
157 | /// | |
158 | /// This method returns an error when the underlying system call fails, which can happen for a | |
159 | /// variety of reasons, such as when the file is not open with read permissions. | |
160 | /// | |
161 | /// # Example | |
162 | /// | |
163 | /// ``` | |
164 | /// use memmap::MmapOptions; | |
165 | /// use std::fs::File; | |
166 | /// use std::io::Read; | |
167 | /// | |
60c5eb7d | 168 | /// # fn main() -> std::io::Result<()> { |
b7449926 XL |
169 | /// let mut file = File::open("README.md")?; |
170 | /// | |
171 | /// let mut contents = Vec::new(); | |
172 | /// file.read_to_end(&mut contents)?; | |
173 | /// | |
174 | /// let mmap = unsafe { | |
175 | /// MmapOptions::new().map(&file)? | |
176 | /// }; | |
177 | /// | |
178 | /// assert_eq!(&contents[..], &mmap[..]); | |
179 | /// # Ok(()) | |
180 | /// # } | |
b7449926 XL |
181 | /// ``` |
182 | pub unsafe fn map(&self, file: &File) -> Result<Mmap> { | |
183 | MmapInner::map(self.get_len(file)?, file, self.offset).map(|inner| Mmap { inner: inner }) | |
184 | } | |
185 | ||
186 | /// Creates a readable and executable memory map backed by a file. | |
187 | /// | |
188 | /// # Errors | |
189 | /// | |
190 | /// This method returns an error when the underlying system call fails, which can happen for a | |
191 | /// variety of reasons, such as when the file is not open with read permissions. | |
192 | pub unsafe fn map_exec(&self, file: &File) -> Result<Mmap> { | |
193 | MmapInner::map_exec(self.get_len(file)?, file, self.offset) | |
194 | .map(|inner| Mmap { inner: inner }) | |
195 | } | |
196 | ||
197 | /// Creates a writeable memory map backed by a file. | |
198 | /// | |
199 | /// # Errors | |
200 | /// | |
201 | /// This method returns an error when the underlying system call fails, which can happen for a | |
202 | /// variety of reasons, such as when the file is not open with read and write permissions. | |
203 | /// | |
204 | /// # Example | |
205 | /// | |
206 | /// ``` | |
207 | /// # extern crate memmap; | |
208 | /// # extern crate tempdir; | |
209 | /// # | |
210 | /// use std::fs::OpenOptions; | |
211 | /// use std::path::PathBuf; | |
212 | /// | |
213 | /// use memmap::MmapOptions; | |
214 | /// # | |
60c5eb7d | 215 | /// # fn main() -> std::io::Result<()> { |
b7449926 XL |
216 | /// # let tempdir = tempdir::TempDir::new("mmap")?; |
217 | /// let path: PathBuf = /* path to file */ | |
218 | /// # tempdir.path().join("map_mut"); | |
219 | /// let file = OpenOptions::new().read(true).write(true).create(true).open(&path)?; | |
220 | /// file.set_len(13)?; | |
221 | /// | |
222 | /// let mut mmap = unsafe { | |
223 | /// MmapOptions::new().map_mut(&file)? | |
224 | /// }; | |
225 | /// | |
226 | /// mmap.copy_from_slice(b"Hello, world!"); | |
227 | /// # Ok(()) | |
228 | /// # } | |
b7449926 XL |
229 | /// ``` |
230 | pub unsafe fn map_mut(&self, file: &File) -> Result<MmapMut> { | |
231 | MmapInner::map_mut(self.get_len(file)?, file, self.offset) | |
232 | .map(|inner| MmapMut { inner: inner }) | |
233 | } | |
234 | ||
235 | /// Creates a copy-on-write memory map backed by a file. | |
236 | /// | |
237 | /// Data written to the memory map will not be visible by other processes, | |
238 | /// and will not be carried through to the underlying file. | |
239 | /// | |
240 | /// # Errors | |
241 | /// | |
242 | /// This method returns an error when the underlying system call fails, which can happen for a | |
243 | /// variety of reasons, such as when the file is not open with writable permissions. | |
244 | /// | |
245 | /// # Example | |
246 | /// | |
247 | /// ``` | |
248 | /// use memmap::MmapOptions; | |
249 | /// use std::fs::File; | |
250 | /// use std::io::Write; | |
251 | /// | |
60c5eb7d | 252 | /// # fn main() -> std::io::Result<()> { |
b7449926 XL |
253 | /// let file = File::open("README.md")?; |
254 | /// let mut mmap = unsafe { MmapOptions::new().map_copy(&file)? }; | |
255 | /// (&mut mmap[..]).write_all(b"Hello, world!")?; | |
256 | /// # Ok(()) | |
257 | /// # } | |
b7449926 XL |
258 | /// ``` |
259 | pub unsafe fn map_copy(&self, file: &File) -> Result<MmapMut> { | |
260 | MmapInner::map_copy(self.get_len(file)?, file, self.offset) | |
261 | .map(|inner| MmapMut { inner: inner }) | |
262 | } | |
263 | ||
264 | /// Creates an anonymous memory map. | |
265 | /// | |
266 | /// Note: the memory map length must be configured to be greater than 0 before creating an | |
267 | /// anonymous memory map using `MmapOptions::len()`. | |
268 | /// | |
269 | /// # Errors | |
270 | /// | |
271 | /// This method returns an error when the underlying system call fails. | |
272 | pub fn map_anon(&self) -> Result<MmapMut> { | |
273 | MmapInner::map_anon(self.len.unwrap_or(0), self.stack).map(|inner| MmapMut { inner: inner }) | |
274 | } | |
275 | } | |
276 | ||
277 | /// An immutable memory mapped buffer. | |
278 | /// | |
279 | /// A `Mmap` may be backed by a file, or it can be anonymous map, backed by volatile memory. | |
280 | /// | |
281 | /// Use `MmapOptions` to configure and create a file-backed memory map. To create an immutable | |
282 | /// anonymous memory map, first create a mutable anonymous memory map using `MmapOptions`, and then | |
283 | /// make it immutable with `MmapMut::make_read_only`. | |
284 | /// | |
285 | /// # Example | |
286 | /// | |
287 | /// ``` | |
288 | /// use memmap::MmapOptions; | |
289 | /// use std::io::Write; | |
290 | /// use std::fs::File; | |
291 | /// | |
60c5eb7d | 292 | /// # fn main() -> std::io::Result<()> { |
b7449926 XL |
293 | /// let file = File::open("README.md")?; |
294 | /// let mmap = unsafe { MmapOptions::new().map(&file)? }; | |
295 | /// assert_eq!(b"# memmap", &mmap[0..8]); | |
296 | /// # Ok(()) | |
297 | /// # } | |
b7449926 XL |
298 | /// ``` |
299 | /// | |
300 | /// See `MmapMut` for the mutable version. | |
301 | pub struct Mmap { | |
302 | inner: MmapInner, | |
303 | } | |
304 | ||
305 | impl Mmap { | |
306 | /// Creates a read-only memory map backed by a file. | |
307 | /// | |
308 | /// This is equivalent to calling `MmapOptions::new().map(file)`. | |
309 | /// | |
310 | /// # Errors | |
311 | /// | |
312 | /// This method returns an error when the underlying system call fails, which can happen for a | |
313 | /// variety of reasons, such as when the file is not open with read permissions. | |
314 | /// | |
315 | /// # Example | |
316 | /// | |
317 | /// ``` | |
318 | /// use std::fs::File; | |
319 | /// use std::io::Read; | |
320 | /// | |
321 | /// use memmap::Mmap; | |
322 | /// | |
60c5eb7d | 323 | /// # fn main() -> std::io::Result<()> { |
b7449926 XL |
324 | /// let mut file = File::open("README.md")?; |
325 | /// | |
326 | /// let mut contents = Vec::new(); | |
327 | /// file.read_to_end(&mut contents)?; | |
328 | /// | |
329 | /// let mmap = unsafe { Mmap::map(&file)? }; | |
330 | /// | |
331 | /// assert_eq!(&contents[..], &mmap[..]); | |
332 | /// # Ok(()) | |
333 | /// # } | |
b7449926 XL |
334 | /// ``` |
335 | pub unsafe fn map(file: &File) -> Result<Mmap> { | |
336 | MmapOptions::new().map(file) | |
337 | } | |
338 | ||
339 | /// Transition the memory map to be writable. | |
340 | /// | |
341 | /// If the memory map is file-backed, the file must have been opened with write permissions. | |
342 | /// | |
343 | /// # Errors | |
344 | /// | |
345 | /// This method returns an error when the underlying system call fails, which can happen for a | |
346 | /// variety of reasons, such as when the file is not open with writable permissions. | |
347 | /// | |
348 | /// # Example | |
349 | /// | |
350 | /// ``` | |
351 | /// # extern crate memmap; | |
352 | /// # extern crate tempdir; | |
353 | /// # | |
354 | /// use memmap::Mmap; | |
355 | /// use std::ops::DerefMut; | |
356 | /// use std::io::Write; | |
357 | /// # use std::fs::OpenOptions; | |
358 | /// | |
60c5eb7d | 359 | /// # fn main() -> std::io::Result<()> { |
b7449926 XL |
360 | /// # let tempdir = tempdir::TempDir::new("mmap")?; |
361 | /// let file = /* file opened with write permissions */ | |
362 | /// # OpenOptions::new() | |
363 | /// # .read(true) | |
364 | /// # .write(true) | |
365 | /// # .create(true) | |
366 | /// # .open(tempdir.path() | |
367 | /// # .join("make_mut"))?; | |
368 | /// # file.set_len(128)?; | |
369 | /// let mmap = unsafe { Mmap::map(&file)? }; | |
370 | /// // ... use the read-only memory map ... | |
371 | /// let mut mut_mmap = mmap.make_mut()?; | |
372 | /// mut_mmap.deref_mut().write_all(b"hello, world!")?; | |
373 | /// # Ok(()) | |
374 | /// # } | |
b7449926 XL |
375 | /// ``` |
376 | pub fn make_mut(mut self) -> Result<MmapMut> { | |
377 | self.inner.make_mut()?; | |
378 | Ok(MmapMut { inner: self.inner }) | |
379 | } | |
380 | } | |
381 | ||
382 | impl Deref for Mmap { | |
383 | type Target = [u8]; | |
384 | ||
385 | #[inline] | |
386 | fn deref(&self) -> &[u8] { | |
387 | unsafe { slice::from_raw_parts(self.inner.ptr(), self.inner.len()) } | |
388 | } | |
389 | } | |
390 | ||
391 | impl AsRef<[u8]> for Mmap { | |
392 | #[inline] | |
393 | fn as_ref(&self) -> &[u8] { | |
394 | self.deref() | |
395 | } | |
396 | } | |
397 | ||
398 | impl fmt::Debug for Mmap { | |
399 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { | |
400 | fmt.debug_struct("Mmap") | |
401 | .field("ptr", &self.as_ptr()) | |
402 | .field("len", &self.len()) | |
403 | .finish() | |
404 | } | |
405 | } | |
406 | ||
407 | /// A mutable memory mapped buffer. | |
408 | /// | |
409 | /// A file-backed `MmapMut` buffer may be used to read from or write to a file. An anonymous | |
410 | /// `MmapMut` buffer may be used any place that an in-memory byte buffer is needed. Use | |
411 | /// `MmapOptions` for creating memory maps. | |
412 | /// | |
413 | /// See `Mmap` for the immutable version. | |
414 | pub struct MmapMut { | |
415 | inner: MmapInner, | |
416 | } | |
417 | ||
418 | impl MmapMut { | |
419 | /// Creates a writeable memory map backed by a file. | |
420 | /// | |
421 | /// This is equivalent to calling `MmapOptions::new().map_mut(file)`. | |
422 | /// | |
423 | /// # Errors | |
424 | /// | |
425 | /// This method returns an error when the underlying system call fails, which can happen for a | |
426 | /// variety of reasons, such as when the file is not open with read and write permissions. | |
427 | /// | |
428 | /// # Example | |
429 | /// | |
430 | /// ``` | |
431 | /// # extern crate memmap; | |
432 | /// # extern crate tempdir; | |
433 | /// # | |
434 | /// use std::fs::OpenOptions; | |
435 | /// use std::path::PathBuf; | |
436 | /// | |
437 | /// use memmap::MmapMut; | |
438 | /// # | |
60c5eb7d | 439 | /// # fn main() -> std::io::Result<()> { |
b7449926 XL |
440 | /// # let tempdir = tempdir::TempDir::new("mmap")?; |
441 | /// let path: PathBuf = /* path to file */ | |
442 | /// # tempdir.path().join("map_mut"); | |
443 | /// let file = OpenOptions::new() | |
444 | /// .read(true) | |
445 | /// .write(true) | |
446 | /// .create(true) | |
447 | /// .open(&path)?; | |
448 | /// file.set_len(13)?; | |
449 | /// | |
450 | /// let mut mmap = unsafe { MmapMut::map_mut(&file)? }; | |
451 | /// | |
452 | /// mmap.copy_from_slice(b"Hello, world!"); | |
453 | /// # Ok(()) | |
454 | /// # } | |
b7449926 XL |
455 | /// ``` |
456 | pub unsafe fn map_mut(file: &File) -> Result<MmapMut> { | |
457 | MmapOptions::new().map_mut(file) | |
458 | } | |
459 | ||
460 | /// Creates an anonymous memory map. | |
461 | /// | |
462 | /// This is equivalent to calling `MmapOptions::new().len(length).map_anon()`. | |
463 | /// | |
464 | /// # Errors | |
465 | /// | |
466 | /// This method returns an error when the underlying system call fails. | |
467 | pub fn map_anon(length: usize) -> Result<MmapMut> { | |
468 | MmapOptions::new().len(length).map_anon() | |
469 | } | |
470 | ||
471 | /// Flushes outstanding memory map modifications to disk. | |
472 | /// | |
473 | /// When this method returns with a non-error result, all outstanding changes to a file-backed | |
474 | /// memory map are guaranteed to be durably stored. The file's metadata (including last | |
475 | /// modification timestamp) may not be updated. | |
476 | /// | |
477 | /// # Example | |
478 | /// | |
479 | /// ``` | |
480 | /// # extern crate memmap; | |
481 | /// # extern crate tempdir; | |
482 | /// # | |
483 | /// use std::fs::OpenOptions; | |
484 | /// use std::io::Write; | |
485 | /// use std::path::PathBuf; | |
486 | /// | |
487 | /// use memmap::MmapMut; | |
488 | /// | |
60c5eb7d | 489 | /// # fn main() -> std::io::Result<()> { |
b7449926 XL |
490 | /// # let tempdir = tempdir::TempDir::new("mmap")?; |
491 | /// let path: PathBuf = /* path to file */ | |
492 | /// # tempdir.path().join("flush"); | |
493 | /// let file = OpenOptions::new().read(true).write(true).create(true).open(&path)?; | |
494 | /// file.set_len(128)?; | |
495 | /// | |
496 | /// let mut mmap = unsafe { MmapMut::map_mut(&file)? }; | |
497 | /// | |
498 | /// (&mut mmap[..]).write_all(b"Hello, world!")?; | |
499 | /// mmap.flush()?; | |
500 | /// # Ok(()) | |
501 | /// # } | |
b7449926 XL |
502 | /// ``` |
503 | pub fn flush(&self) -> Result<()> { | |
504 | let len = self.len(); | |
505 | self.inner.flush(0, len) | |
506 | } | |
507 | ||
508 | /// Asynchronously flushes outstanding memory map modifications to disk. | |
509 | /// | |
510 | /// This method initiates flushing modified pages to durable storage, but it will not wait for | |
511 | /// the operation to complete before returning. The file's metadata (including last | |
512 | /// modification timestamp) may not be updated. | |
513 | pub fn flush_async(&self) -> Result<()> { | |
514 | let len = self.len(); | |
515 | self.inner.flush_async(0, len) | |
516 | } | |
517 | ||
518 | /// Flushes outstanding memory map modifications in the range to disk. | |
519 | /// | |
520 | /// The offset and length must be in the bounds of the memory map. | |
521 | /// | |
522 | /// When this method returns with a non-error result, all outstanding changes to a file-backed | |
523 | /// memory in the range are guaranteed to be durable stored. The file's metadata (including | |
524 | /// last modification timestamp) may not be updated. It is not guaranteed the only the changes | |
525 | /// in the specified range are flushed; other outstanding changes to the memory map may be | |
526 | /// flushed as well. | |
527 | pub fn flush_range(&self, offset: usize, len: usize) -> Result<()> { | |
528 | self.inner.flush(offset, len) | |
529 | } | |
530 | ||
531 | /// Asynchronously flushes outstanding memory map modifications in the range to disk. | |
532 | /// | |
533 | /// The offset and length must be in the bounds of the memory map. | |
534 | /// | |
535 | /// This method initiates flushing modified pages to durable storage, but it will not wait for | |
536 | /// the operation to complete before returning. The file's metadata (including last | |
537 | /// modification timestamp) may not be updated. It is not guaranteed that the only changes | |
538 | /// flushed are those in the specified range; other outstanding changes to the memory map may | |
539 | /// be flushed as well. | |
540 | pub fn flush_async_range(&self, offset: usize, len: usize) -> Result<()> { | |
541 | self.inner.flush_async(offset, len) | |
542 | } | |
543 | ||
544 | /// Returns an immutable version of this memory mapped buffer. | |
545 | /// | |
546 | /// If the memory map is file-backed, the file must have been opened with read permissions. | |
547 | /// | |
548 | /// # Errors | |
549 | /// | |
550 | /// This method returns an error when the underlying system call fails, which can happen for a | |
551 | /// variety of reasons, such as when the file has not been opened with read permissions. | |
552 | /// | |
553 | /// # Example | |
554 | /// | |
555 | /// ``` | |
556 | /// # extern crate memmap; | |
557 | /// # | |
558 | /// use std::io::Write; | |
559 | /// use std::path::PathBuf; | |
560 | /// | |
561 | /// use memmap::{Mmap, MmapMut}; | |
562 | /// | |
60c5eb7d | 563 | /// # fn main() -> std::io::Result<()> { |
b7449926 XL |
564 | /// let mut mmap = MmapMut::map_anon(128)?; |
565 | /// | |
566 | /// (&mut mmap[..]).write(b"Hello, world!")?; | |
567 | /// | |
568 | /// let mmap: Mmap = mmap.make_read_only()?; | |
569 | /// # Ok(()) | |
570 | /// # } | |
b7449926 XL |
571 | /// ``` |
572 | pub fn make_read_only(mut self) -> Result<Mmap> { | |
573 | self.inner.make_read_only()?; | |
574 | Ok(Mmap { inner: self.inner }) | |
575 | } | |
576 | ||
577 | /// Transition the memory map to be readable and executable. | |
578 | /// | |
579 | /// If the memory map is file-backed, the file must have been opened with execute permissions. | |
580 | /// | |
581 | /// # Errors | |
582 | /// | |
583 | /// This method returns an error when the underlying system call fails, which can happen for a | |
584 | /// variety of reasons, such as when the file has not been opened with execute permissions. | |
585 | pub fn make_exec(mut self) -> Result<Mmap> { | |
586 | self.inner.make_exec()?; | |
587 | Ok(Mmap { inner: self.inner }) | |
588 | } | |
589 | } | |
590 | ||
591 | impl Deref for MmapMut { | |
592 | type Target = [u8]; | |
593 | ||
594 | #[inline] | |
595 | fn deref(&self) -> &[u8] { | |
596 | unsafe { slice::from_raw_parts(self.inner.ptr(), self.inner.len()) } | |
597 | } | |
598 | } | |
599 | ||
600 | impl DerefMut for MmapMut { | |
601 | #[inline] | |
602 | fn deref_mut(&mut self) -> &mut [u8] { | |
603 | unsafe { slice::from_raw_parts_mut(self.inner.mut_ptr(), self.inner.len()) } | |
604 | } | |
605 | } | |
606 | ||
607 | impl AsRef<[u8]> for MmapMut { | |
608 | #[inline] | |
609 | fn as_ref(&self) -> &[u8] { | |
610 | self.deref() | |
611 | } | |
612 | } | |
613 | ||
614 | impl AsMut<[u8]> for MmapMut { | |
615 | #[inline] | |
616 | fn as_mut(&mut self) -> &mut [u8] { | |
617 | self.deref_mut() | |
618 | } | |
619 | } | |
620 | ||
621 | impl fmt::Debug for MmapMut { | |
622 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { | |
623 | fmt.debug_struct("MmapMut") | |
624 | .field("ptr", &self.as_ptr()) | |
625 | .field("len", &self.len()) | |
626 | .finish() | |
627 | } | |
628 | } | |
629 | ||
630 | #[cfg(test)] | |
631 | mod test { | |
632 | ||
633 | extern crate tempdir; | |
634 | #[cfg(windows)] | |
635 | extern crate winapi; | |
636 | ||
637 | use std::fs::OpenOptions; | |
60c5eb7d | 638 | use std::io::{Read, Write}; |
b7449926 XL |
639 | #[cfg(windows)] |
640 | use std::os::windows::fs::OpenOptionsExt; | |
b7449926 XL |
641 | use std::sync::Arc; |
642 | use std::thread; | |
643 | ||
644 | #[cfg(windows)] | |
645 | use winapi::um::winnt::GENERIC_ALL; | |
646 | ||
647 | use super::{Mmap, MmapMut, MmapOptions}; | |
648 | ||
649 | #[test] | |
650 | fn map_file() { | |
651 | let expected_len = 128; | |
652 | let tempdir = tempdir::TempDir::new("mmap").unwrap(); | |
653 | let path = tempdir.path().join("mmap"); | |
654 | ||
655 | let file = OpenOptions::new() | |
656 | .read(true) | |
657 | .write(true) | |
658 | .create(true) | |
659 | .open(&path) | |
660 | .unwrap(); | |
661 | ||
662 | file.set_len(expected_len as u64).unwrap(); | |
663 | ||
664 | let mut mmap = unsafe { MmapMut::map_mut(&file).unwrap() }; | |
665 | let len = mmap.len(); | |
666 | assert_eq!(expected_len, len); | |
667 | ||
668 | let zeros = vec![0; len]; | |
669 | let incr: Vec<u8> = (0..len as u8).collect(); | |
670 | ||
671 | // check that the mmap is empty | |
672 | assert_eq!(&zeros[..], &mmap[..]); | |
673 | ||
674 | // write values into the mmap | |
675 | (&mut mmap[..]).write_all(&incr[..]).unwrap(); | |
676 | ||
677 | // read values back | |
678 | assert_eq!(&incr[..], &mmap[..]); | |
679 | } | |
680 | ||
681 | /// Checks that a 0-length file will not be mapped. | |
682 | #[test] | |
683 | fn map_empty_file() { | |
684 | let tempdir = tempdir::TempDir::new("mmap").unwrap(); | |
685 | let path = tempdir.path().join("mmap"); | |
686 | ||
687 | let file = OpenOptions::new() | |
688 | .read(true) | |
689 | .write(true) | |
690 | .create(true) | |
691 | .open(&path) | |
692 | .unwrap(); | |
693 | let mmap = unsafe { Mmap::map(&file) }; | |
694 | assert!(mmap.is_err()); | |
695 | } | |
696 | ||
697 | #[test] | |
698 | fn map_anon() { | |
699 | let expected_len = 128; | |
700 | let mut mmap = MmapMut::map_anon(expected_len).unwrap(); | |
701 | let len = mmap.len(); | |
702 | assert_eq!(expected_len, len); | |
703 | ||
704 | let zeros = vec![0; len]; | |
705 | let incr: Vec<u8> = (0..len as u8).collect(); | |
706 | ||
707 | // check that the mmap is empty | |
708 | assert_eq!(&zeros[..], &mmap[..]); | |
709 | ||
710 | // write values into the mmap | |
711 | (&mut mmap[..]).write_all(&incr[..]).unwrap(); | |
712 | ||
713 | // read values back | |
714 | assert_eq!(&incr[..], &mmap[..]); | |
715 | } | |
716 | ||
717 | #[test] | |
718 | fn map_anon_zero_len() { | |
719 | assert!(MmapOptions::new().map_anon().is_err()) | |
720 | } | |
721 | ||
722 | #[test] | |
723 | fn file_write() { | |
724 | let tempdir = tempdir::TempDir::new("mmap").unwrap(); | |
725 | let path = tempdir.path().join("mmap"); | |
726 | ||
727 | let mut file = OpenOptions::new() | |
728 | .read(true) | |
729 | .write(true) | |
730 | .create(true) | |
731 | .open(&path) | |
732 | .unwrap(); | |
733 | file.set_len(128).unwrap(); | |
734 | ||
735 | let write = b"abc123"; | |
736 | let mut read = [0u8; 6]; | |
737 | ||
738 | let mut mmap = unsafe { MmapMut::map_mut(&file).unwrap() }; | |
739 | (&mut mmap[..]).write_all(write).unwrap(); | |
740 | mmap.flush().unwrap(); | |
741 | ||
742 | file.read(&mut read).unwrap(); | |
743 | assert_eq!(write, &read); | |
744 | } | |
745 | ||
746 | #[test] | |
747 | fn flush_range() { | |
748 | let tempdir = tempdir::TempDir::new("mmap").unwrap(); | |
749 | let path = tempdir.path().join("mmap"); | |
750 | ||
751 | let file = OpenOptions::new() | |
752 | .read(true) | |
753 | .write(true) | |
754 | .create(true) | |
755 | .open(&path) | |
756 | .unwrap(); | |
757 | file.set_len(128).unwrap(); | |
758 | let write = b"abc123"; | |
759 | ||
760 | let mut mmap = unsafe { | |
761 | MmapOptions::new() | |
762 | .offset(2) | |
763 | .len(write.len()) | |
764 | .map_mut(&file) | |
765 | .unwrap() | |
766 | }; | |
767 | (&mut mmap[..]).write_all(write).unwrap(); | |
768 | mmap.flush_range(0, write.len()).unwrap(); | |
769 | } | |
770 | ||
771 | #[test] | |
772 | fn map_copy() { | |
773 | let tempdir = tempdir::TempDir::new("mmap").unwrap(); | |
774 | let path = tempdir.path().join("mmap"); | |
775 | ||
776 | let mut file = OpenOptions::new() | |
777 | .read(true) | |
778 | .write(true) | |
779 | .create(true) | |
780 | .open(&path) | |
781 | .unwrap(); | |
782 | file.set_len(128).unwrap(); | |
783 | ||
784 | let nulls = b"\0\0\0\0\0\0"; | |
785 | let write = b"abc123"; | |
786 | let mut read = [0u8; 6]; | |
787 | ||
788 | let mut mmap = unsafe { MmapOptions::new().map_copy(&file).unwrap() }; | |
789 | ||
790 | (&mut mmap[..]).write(write).unwrap(); | |
791 | mmap.flush().unwrap(); | |
792 | ||
793 | // The mmap contains the write | |
794 | (&mmap[..]).read(&mut read).unwrap(); | |
795 | assert_eq!(write, &read); | |
796 | ||
797 | // The file does not contain the write | |
798 | file.read(&mut read).unwrap(); | |
799 | assert_eq!(nulls, &read); | |
800 | ||
801 | // another mmap does not contain the write | |
802 | let mmap2 = unsafe { MmapOptions::new().map(&file).unwrap() }; | |
803 | (&mmap2[..]).read(&mut read).unwrap(); | |
804 | assert_eq!(nulls, &read); | |
805 | } | |
806 | ||
807 | #[test] | |
808 | fn map_offset() { | |
809 | let tempdir = tempdir::TempDir::new("mmap").unwrap(); | |
810 | let path = tempdir.path().join("mmap"); | |
811 | ||
812 | let file = OpenOptions::new() | |
813 | .read(true) | |
814 | .write(true) | |
815 | .create(true) | |
816 | .open(&path) | |
817 | .unwrap(); | |
818 | ||
60c5eb7d XL |
819 | let offset = u32::max_value() as u64 + 2; |
820 | let len = 5432; | |
821 | file.set_len(offset + len as u64).unwrap(); | |
b7449926 | 822 | |
60c5eb7d XL |
823 | // Check inferred length mmap. |
824 | let mmap = unsafe { MmapOptions::new().offset(offset).map_mut(&file).unwrap() }; | |
825 | assert_eq!(len, mmap.len()); | |
b7449926 | 826 | |
60c5eb7d | 827 | // Check explicit length mmap. |
b7449926 XL |
828 | let mut mmap = unsafe { |
829 | MmapOptions::new() | |
830 | .offset(offset) | |
831 | .len(len) | |
832 | .map_mut(&file) | |
833 | .unwrap() | |
834 | }; | |
835 | assert_eq!(len, mmap.len()); | |
836 | ||
837 | let zeros = vec![0; len]; | |
838 | let incr: Vec<_> = (0..len).map(|i| i as u8).collect(); | |
839 | ||
840 | // check that the mmap is empty | |
841 | assert_eq!(&zeros[..], &mmap[..]); | |
842 | ||
843 | // write values into the mmap | |
844 | (&mut mmap[..]).write_all(&incr[..]).unwrap(); | |
845 | ||
846 | // read values back | |
847 | assert_eq!(&incr[..], &mmap[..]); | |
848 | } | |
849 | ||
850 | #[test] | |
851 | fn index() { | |
852 | let mut mmap = MmapMut::map_anon(128).unwrap(); | |
853 | mmap[0] = 42; | |
854 | assert_eq!(42, mmap[0]); | |
855 | } | |
856 | ||
857 | #[test] | |
858 | fn sync_send() { | |
859 | let mmap = Arc::new(MmapMut::map_anon(129).unwrap()); | |
860 | thread::spawn(move || { | |
861 | &mmap[..]; | |
862 | }); | |
863 | } | |
864 | ||
865 | #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] | |
866 | fn jit_x86(mut mmap: MmapMut) { | |
867 | use std::mem; | |
868 | mmap[0] = 0xB8; // mov eax, 0xAB | |
869 | mmap[1] = 0xAB; | |
870 | mmap[2] = 0x00; | |
871 | mmap[3] = 0x00; | |
872 | mmap[4] = 0x00; | |
873 | mmap[5] = 0xC3; // ret | |
874 | ||
875 | let mmap = mmap.make_exec().expect("make_exec"); | |
876 | ||
877 | let jitfn: extern "C" fn() -> u8 = unsafe { mem::transmute(mmap.as_ptr()) }; | |
878 | assert_eq!(jitfn(), 0xab); | |
879 | } | |
880 | ||
881 | #[test] | |
882 | #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] | |
883 | fn jit_x86_anon() { | |
884 | jit_x86(MmapMut::map_anon(4096).unwrap()); | |
885 | } | |
886 | ||
887 | #[test] | |
888 | #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] | |
889 | fn jit_x86_file() { | |
890 | let tempdir = tempdir::TempDir::new("mmap").unwrap(); | |
891 | let mut options = OpenOptions::new(); | |
892 | #[cfg(windows)] | |
893 | options.access_mode(GENERIC_ALL); | |
894 | ||
895 | let file = options | |
896 | .read(true) | |
897 | .write(true) | |
898 | .create(true) | |
899 | .open(&tempdir.path().join("jit_x86")) | |
900 | .expect("open"); | |
901 | ||
902 | file.set_len(4096).expect("set_len"); | |
903 | jit_x86(unsafe { MmapMut::map_mut(&file).expect("map_mut") }); | |
904 | } | |
905 | ||
906 | #[test] | |
907 | fn mprotect_file() { | |
908 | let tempdir = tempdir::TempDir::new("mmap").unwrap(); | |
909 | let path = tempdir.path().join("mmap"); | |
910 | ||
911 | let mut options = OpenOptions::new(); | |
912 | #[cfg(windows)] | |
913 | options.access_mode(GENERIC_ALL); | |
914 | ||
915 | let mut file = options | |
916 | .read(true) | |
917 | .write(true) | |
918 | .create(true) | |
919 | .open(&path) | |
920 | .expect("open"); | |
921 | file.set_len(256 as u64).expect("set_len"); | |
922 | ||
923 | let mmap = unsafe { MmapMut::map_mut(&file).expect("map_mut") }; | |
924 | ||
925 | let mmap = mmap.make_read_only().expect("make_read_only"); | |
926 | let mut mmap = mmap.make_mut().expect("make_mut"); | |
927 | ||
928 | let write = b"abc123"; | |
929 | let mut read = [0u8; 6]; | |
930 | ||
931 | (&mut mmap[..]).write(write).unwrap(); | |
932 | mmap.flush().unwrap(); | |
933 | ||
934 | // The mmap contains the write | |
935 | (&mmap[..]).read(&mut read).unwrap(); | |
936 | assert_eq!(write, &read); | |
937 | ||
938 | // The file should contain the write | |
939 | file.read(&mut read).unwrap(); | |
940 | assert_eq!(write, &read); | |
941 | ||
942 | // another mmap should contain the write | |
943 | let mmap2 = unsafe { MmapOptions::new().map(&file).unwrap() }; | |
944 | (&mmap2[..]).read(&mut read).unwrap(); | |
945 | assert_eq!(write, &read); | |
946 | ||
947 | let mmap = mmap.make_exec().expect("make_exec"); | |
948 | ||
949 | drop(mmap); | |
950 | } | |
951 | ||
952 | #[test] | |
953 | fn mprotect_copy() { | |
954 | let tempdir = tempdir::TempDir::new("mmap").unwrap(); | |
955 | let path = tempdir.path().join("mmap"); | |
956 | ||
957 | let mut options = OpenOptions::new(); | |
958 | #[cfg(windows)] | |
959 | options.access_mode(GENERIC_ALL); | |
960 | ||
961 | let mut file = options | |
962 | .read(true) | |
963 | .write(true) | |
964 | .create(true) | |
965 | .open(&path) | |
966 | .expect("open"); | |
967 | file.set_len(256 as u64).expect("set_len"); | |
968 | ||
969 | let mmap = unsafe { MmapOptions::new().map_copy(&file).expect("map_mut") }; | |
970 | ||
971 | let mmap = mmap.make_read_only().expect("make_read_only"); | |
972 | let mut mmap = mmap.make_mut().expect("make_mut"); | |
973 | ||
974 | let nulls = b"\0\0\0\0\0\0"; | |
975 | let write = b"abc123"; | |
976 | let mut read = [0u8; 6]; | |
977 | ||
978 | (&mut mmap[..]).write(write).unwrap(); | |
979 | mmap.flush().unwrap(); | |
980 | ||
981 | // The mmap contains the write | |
982 | (&mmap[..]).read(&mut read).unwrap(); | |
983 | assert_eq!(write, &read); | |
984 | ||
985 | // The file does not contain the write | |
986 | file.read(&mut read).unwrap(); | |
987 | assert_eq!(nulls, &read); | |
988 | ||
989 | // another mmap does not contain the write | |
990 | let mmap2 = unsafe { MmapOptions::new().map(&file).unwrap() }; | |
991 | (&mmap2[..]).read(&mut read).unwrap(); | |
992 | assert_eq!(nulls, &read); | |
993 | ||
994 | let mmap = mmap.make_exec().expect("make_exec"); | |
995 | ||
996 | drop(mmap); | |
997 | } | |
998 | ||
999 | #[test] | |
1000 | fn mprotect_anon() { | |
1001 | let mmap = MmapMut::map_anon(256).expect("map_mut"); | |
1002 | ||
1003 | let mmap = mmap.make_read_only().expect("make_read_only"); | |
1004 | let mmap = mmap.make_mut().expect("make_mut"); | |
1005 | let mmap = mmap.make_exec().expect("make_exec"); | |
1006 | drop(mmap); | |
1007 | } | |
1008 | } |