mmap_rs/
lib.rs

1#![doc = include_str!("../README.md")]
2#![deny(
3    missing_docs,
4    rustdoc::broken_intra_doc_links,
5    missing_debug_implementations
6)]
7
8mod areas;
9pub mod error;
10mod mmap;
11mod os_impl;
12
13pub use areas::*;
14pub use error::Error;
15pub use mmap::*;
16
17#[cfg(test)]
18mod tests {
19    #[test]
20    fn reserve_none() {
21        use crate::{MemoryAreas, MmapNone, MmapOptions, Protection, ShareMode};
22
23        let mapping = MmapOptions::new(MmapOptions::page_size())
24            .unwrap()
25            .reserve_none()
26            .unwrap();
27
28        assert!(mapping.as_ptr() != std::ptr::null());
29
30        let mapping: MmapNone = mapping.try_into().unwrap();
31
32        assert!(mapping.as_ptr() != std::ptr::null());
33
34        let region = MemoryAreas::query(mapping.as_ptr() as usize)
35            .unwrap()
36            .unwrap();
37
38        assert_eq!(region.share_mode, ShareMode::Private);
39        assert!(!region.protection.contains(Protection::READ));
40        assert!(!region.protection.contains(Protection::WRITE));
41        assert!(!region.protection.contains(Protection::EXECUTE));
42    }
43
44    #[test]
45    fn reserve() {
46        use crate::{MemoryAreas, Mmap, MmapOptions, Protection, ShareMode};
47
48        let mapping = MmapOptions::new(MmapOptions::page_size())
49            .unwrap()
50            .map()
51            .unwrap();
52
53        assert!(mapping.as_ptr() != std::ptr::null());
54
55        let mapping: Mmap = mapping.try_into().unwrap();
56
57        assert!(mapping.as_ptr() != std::ptr::null());
58
59        let region = MemoryAreas::query(mapping.as_ptr() as usize)
60            .unwrap()
61            .unwrap();
62
63        assert_eq!(region.share_mode, ShareMode::Private);
64        assert!(region.protection.contains(Protection::READ));
65        assert!(!region.protection.contains(Protection::WRITE));
66        assert!(!region.protection.contains(Protection::EXECUTE));
67    }
68
69    #[test]
70    fn reserve_mut() {
71        use crate::{MemoryAreas, MmapMut, MmapOptions, Protection, ShareMode};
72
73        let mapping = MmapOptions::new(MmapOptions::page_size())
74            .unwrap()
75            .reserve_mut()
76            .unwrap();
77
78        assert!(mapping.as_ptr() != std::ptr::null());
79
80        let mut mapping: MmapMut = mapping.try_into().unwrap();
81
82        mapping[0] = 0x42;
83
84        assert!(mapping.as_ptr() != std::ptr::null());
85        assert_eq!(mapping[0], 0x42);
86
87        let region = MemoryAreas::query(mapping.as_ptr() as usize)
88            .unwrap()
89            .unwrap();
90
91        assert_eq!(region.share_mode, ShareMode::Private);
92        assert!(region.protection.contains(Protection::READ));
93        assert!(region.protection.contains(Protection::WRITE));
94        assert!(!region.protection.contains(Protection::EXECUTE));
95    }
96
97    #[test]
98    fn map_none() {
99        use crate::{MemoryAreas, MmapOptions, Protection, ShareMode};
100
101        let mapping = MmapOptions::new(MmapOptions::page_size())
102            .unwrap()
103            .map_none()
104            .unwrap();
105
106        assert!(mapping.as_ptr() != std::ptr::null());
107
108        let region = MemoryAreas::query(mapping.as_ptr() as usize)
109            .unwrap()
110            .unwrap();
111
112        assert_eq!(region.share_mode, ShareMode::Private);
113        assert!(!region.protection.contains(Protection::READ));
114        assert!(!region.protection.contains(Protection::WRITE));
115        assert!(!region.protection.contains(Protection::EXECUTE));
116    }
117
118    #[test]
119    fn map() {
120        use crate::{MemoryAreas, MmapOptions, Protection, ShareMode};
121
122        let mapping = MmapOptions::new(MmapOptions::page_size())
123            .unwrap()
124            .map()
125            .unwrap();
126
127        assert!(mapping.as_ptr() != std::ptr::null());
128
129        let region = MemoryAreas::query(mapping.as_ptr() as usize)
130            .unwrap()
131            .unwrap();
132
133        assert_eq!(region.share_mode, ShareMode::Private);
134        assert!(region.protection.contains(Protection::READ));
135        assert!(!region.protection.contains(Protection::WRITE));
136        assert!(!region.protection.contains(Protection::EXECUTE));
137    }
138
139    #[test]
140    fn map_mut() {
141        use crate::{MemoryAreas, MmapOptions, Protection, ShareMode};
142
143        let mut mapping = MmapOptions::new(MmapOptions::page_size())
144            .unwrap()
145            .map_mut()
146            .unwrap();
147
148        mapping[0] = 0x42;
149
150        assert!(mapping.as_ptr() != std::ptr::null());
151        assert_eq!(mapping[0], 0x42);
152
153        let region = MemoryAreas::query(mapping.as_ptr() as usize)
154            .unwrap()
155            .unwrap();
156
157        assert_eq!(region.share_mode, ShareMode::Private);
158        assert!(region.protection.contains(Protection::READ));
159        assert!(region.protection.contains(Protection::WRITE));
160        assert!(!region.protection.contains(Protection::EXECUTE));
161    }
162
163    #[test]
164    fn map_file() {
165        use crate::{MemoryAreas, MmapFlags, MmapOptions, Protection, ShareMode};
166        use std::io::Write;
167        use tempfile::NamedTempFile;
168
169        let mut file = NamedTempFile::new().unwrap();
170
171        let mut bytes = vec![0u8; MmapOptions::page_size()];
172        bytes[0] = 0x42;
173        file.as_file_mut().write(&bytes).unwrap();
174
175        let mapping = unsafe {
176            MmapOptions::new(MmapOptions::page_size())
177                .unwrap()
178                .with_flags(MmapFlags::SHARED)
179                .with_file(file.as_file(), 0)
180                .map()
181                .unwrap()
182        };
183
184        let other_mapping = unsafe {
185            MmapOptions::new(MmapOptions::page_size())
186                .unwrap()
187                .with_flags(MmapFlags::SHARED)
188                .with_file(file.as_file(), 0)
189                .map()
190                .unwrap()
191        };
192
193        assert_ne!(mapping.as_ptr(), std::ptr::null());
194        assert_ne!(other_mapping.as_ptr(), std::ptr::null());
195        assert_eq!(mapping[0], 0x42);
196        assert_eq!(other_mapping[0], 0x42);
197
198        let region = MemoryAreas::query(mapping.as_ptr() as usize)
199            .unwrap()
200            .unwrap();
201
202        assert_eq!(region.share_mode, ShareMode::Shared);
203        assert!(region.protection.contains(Protection::READ));
204        assert!(!region.protection.contains(Protection::WRITE));
205        assert!(!region.protection.contains(Protection::EXECUTE));
206        #[cfg(not(target_os = "freebsd"))]
207        assert!(region.path().is_some());
208    }
209
210    #[test]
211    fn map_file_mut() {
212        use crate::{MemoryAreas, MmapFlags, MmapOptions, Protection, ShareMode};
213        use std::io::{Read, Seek, SeekFrom, Write};
214        use tempfile::NamedTempFile;
215
216        let mut file = NamedTempFile::new().unwrap();
217
218        let mut bytes = vec![0u8; MmapOptions::page_size()];
219        file.as_file_mut().write(&bytes).unwrap();
220
221        let mut mapping = unsafe {
222            MmapOptions::new(MmapOptions::page_size())
223                .unwrap()
224                .with_flags(MmapFlags::SHARED)
225                .with_file(file.as_file(), 0)
226                .map_mut()
227                .unwrap()
228        };
229
230        let other_mapping = unsafe {
231            MmapOptions::new(MmapOptions::page_size())
232                .unwrap()
233                .with_flags(MmapFlags::SHARED)
234                .with_file(file.as_file(), 0)
235                .map()
236                .unwrap()
237        };
238
239        assert_ne!(mapping.as_ptr(), std::ptr::null());
240        assert_ne!(other_mapping.as_ptr(), std::ptr::null());
241        mapping[0] = 0x42;
242        assert_eq!(other_mapping[0], 0x42);
243        mapping.flush(0..MmapOptions::page_size()).unwrap();
244        file.as_file_mut().sync_all().unwrap();
245
246        let region = MemoryAreas::query(mapping.as_ptr() as usize)
247            .unwrap()
248            .unwrap();
249
250        assert_eq!(region.share_mode, ShareMode::Shared);
251        assert!(region.protection.contains(Protection::READ));
252        assert!(region.protection.contains(Protection::WRITE));
253        assert!(!region.protection.contains(Protection::EXECUTE));
254        #[cfg(not(target_os = "freebsd"))]
255        assert!(region.path().is_some());
256
257        file.as_file_mut().seek(SeekFrom::Start(0)).unwrap();
258        file.as_file().read(&mut bytes).unwrap();
259        assert_eq!(bytes[0], 0x42);
260    }
261
262    #[test]
263    fn map_file_private() {
264        use crate::{MemoryAreas, MmapOptions, Protection, ShareMode};
265        use std::io::{Read, Seek, SeekFrom, Write};
266        use tempfile::NamedTempFile;
267
268        let mut file = NamedTempFile::new().unwrap();
269
270        let mut bytes = vec![0u8; MmapOptions::page_size()];
271        file.as_file_mut().write(&bytes).unwrap();
272
273        let mut mapping = unsafe {
274            MmapOptions::new(MmapOptions::page_size())
275                .unwrap()
276                .with_file(file.as_file(), 0)
277                .map_mut()
278                .unwrap()
279        };
280
281        let other_mapping = unsafe {
282            MmapOptions::new(MmapOptions::page_size())
283                .unwrap()
284                .with_file(file.as_file(), 0)
285                .map()
286                .unwrap()
287        };
288
289        assert_ne!(mapping.as_ptr(), std::ptr::null());
290        assert_ne!(other_mapping.as_ptr(), std::ptr::null());
291        mapping[0] = 0x42;
292        assert_ne!(other_mapping[0], 0x42);
293        mapping.flush(0..MmapOptions::page_size()).unwrap();
294        file.as_file_mut().sync_all().unwrap();
295
296        let region = MemoryAreas::query(mapping.as_ptr() as usize)
297            .unwrap()
298            .unwrap();
299
300        assert_eq!(region.share_mode, ShareMode::Private);
301        assert!(region.protection.contains(Protection::READ));
302        assert!(region.protection.contains(Protection::WRITE));
303        assert!(!region.protection.contains(Protection::EXECUTE));
304        #[cfg(not(target_os = "freebsd"))]
305        assert!(region.path().is_some());
306
307        file.as_file_mut().seek(SeekFrom::Start(0)).unwrap();
308        file.as_file().read(&mut bytes).unwrap();
309        assert_ne!(bytes[0], 0x42);
310    }
311
312    #[test]
313    fn reserve_and_split() {
314        use crate::{MmapMut, MmapOptions};
315
316        let mut mapping = MmapOptions::new(2 * MmapOptions::page_size())
317            .unwrap()
318            .reserve_mut()
319            .unwrap();
320
321        let rest = mapping.split_off(MmapOptions::page_size()).unwrap();
322
323        assert!(mapping.as_ptr() < rest.as_ptr());
324
325        let mut rest: MmapMut = rest.try_into().unwrap();
326
327        rest[0] = 0x42;
328        assert_eq!(rest[0], 0x42);
329
330        assert_eq!(mapping.len(), MmapOptions::page_size());
331        assert_eq!(rest.len(), MmapOptions::page_size());
332        assert!(mapping.as_ptr() < rest.as_ptr());
333    }
334
335    #[test]
336    fn split_off() {
337        use crate::MmapOptions;
338
339        let mut mapping = MmapOptions::new(2 * MmapOptions::page_size())
340            .unwrap()
341            .map_mut()
342            .unwrap();
343
344        assert!(mapping.split_off(1).is_err());
345
346        mapping[0] = 0x1;
347        mapping[MmapOptions::page_size()] = 0x2;
348
349        let rest = mapping.split_off(MmapOptions::page_size()).unwrap();
350
351        assert_eq!(mapping[0], 0x1);
352        assert_eq!(rest[0], 0x2);
353        assert_eq!(mapping.len(), MmapOptions::page_size());
354        assert_eq!(rest.len(), MmapOptions::page_size());
355        assert!(mapping.as_ptr() < rest.as_ptr());
356    }
357
358    #[test]
359    fn split_to() {
360        use crate::MmapOptions;
361
362        let mut mapping = MmapOptions::new(2 * MmapOptions::page_size())
363            .unwrap()
364            .map_mut()
365            .unwrap();
366
367        assert!(mapping.split_to(1).is_err());
368
369        mapping[0] = 0x1;
370        mapping[MmapOptions::page_size()] = 0x2;
371
372        let rest = mapping.split_to(MmapOptions::page_size()).unwrap();
373
374        assert_eq!(mapping[0], 0x2);
375        assert_eq!(rest[0], 0x1);
376        assert_eq!(mapping.len(), MmapOptions::page_size());
377        assert_eq!(rest.len(), MmapOptions::page_size());
378        assert!(mapping.as_ptr() > rest.as_ptr());
379    }
380
381    #[test]
382    fn split_file() {
383        use crate::MmapOptions;
384        use std::io::Write;
385        use tempfile::NamedTempFile;
386
387        let mut file = NamedTempFile::new().unwrap();
388
389        let mut bytes = vec![0u8; 2 * MmapOptions::page_size()];
390        bytes[0] = 0x1;
391        bytes[MmapOptions::page_size()] = 0x2;
392        file.as_file_mut().write(&bytes).unwrap();
393
394        let mut mapping = unsafe {
395            MmapOptions::new(2 * MmapOptions::page_size())
396                .unwrap()
397                .with_file(file.as_file(), 0)
398                .map()
399                .unwrap()
400        };
401
402        assert!(mapping.split_off(1).is_err());
403
404        let rest = mapping.split_off(MmapOptions::page_size()).unwrap();
405
406        assert_eq!(mapping[0], 0x1);
407        assert_eq!(rest[0], 0x2);
408        assert_eq!(mapping.len(), MmapOptions::page_size());
409        assert_eq!(rest.len(), MmapOptions::page_size());
410        assert!(mapping.as_ptr() < rest.as_ptr());
411    }
412
413    #[test]
414    fn split_and_merge() {
415        use crate::{MemoryAreas, MmapOptions, Protection, ShareMode};
416
417        // Allocate three pages.
418        let mut left = MmapOptions::new(3 * MmapOptions::page_size())
419            .unwrap()
420            .map_mut()
421            .unwrap();
422
423        // Split into left, middle and right.
424        let mut middle = left.split_off(MmapOptions::page_size()).unwrap();
425        let right = middle.split_off(MmapOptions::page_size()).unwrap();
426
427        assert!(left.as_ptr() < middle.as_ptr());
428        assert!(middle.as_ptr() < right.as_ptr());
429
430        // Merging left and right should fail in either order.
431        let Err((_, mut right)) = left.merge(right) else {
432            panic!("expected merge to fail")
433        };
434        let Err((_, left)) = right.merge(left) else {
435            panic!("expected merge to fail")
436        };
437
438        // Merging left and middle should succeed.
439        let Err((_, mut left)) = middle.merge(left) else {
440            panic!("expected merge to fail")
441        };
442        left.merge(middle).unwrap();
443
444        // Merging left and right should succeed.
445        let Err((_, mut left)) = right.merge(left) else {
446            panic!("expected merge to fail")
447        };
448        left.merge(right).unwrap();
449
450        // Ensure the size is correct.
451        assert_eq!(left.size(), 3 * MmapOptions::page_size());
452
453        let region = MemoryAreas::query(left.as_ptr() as usize).unwrap().unwrap();
454
455        assert_eq!(region.share_mode, ShareMode::Private);
456        assert!(region.protection.contains(Protection::READ));
457        assert!(region.protection.contains(Protection::WRITE));
458        assert!(!region.protection.contains(Protection::EXECUTE));
459
460        // Ensure that the region we got has the same start address or a lower one.
461        assert!(region.range.start <= left.start());
462
463        // Calculate the actual size from the start address.
464        let padding = left.start().saturating_sub(region.range.start);
465        let size = region.range.len().saturating_sub(padding);
466
467        // Ensure that the region is at least large enough to fully cover our memory mapping.
468        assert!(size >= 3 * MmapOptions::page_size());
469    }
470
471    #[test]
472    fn query_range() {
473        use crate::{MemoryAreas, MmapOptions};
474
475        // Allocate three pages.
476        let mut left = MmapOptions::new(3 * MmapOptions::page_size())
477            .unwrap()
478            .map_mut()
479            .unwrap();
480
481        // Split into left, middle and right.
482        let mut middle = left.split_off(MmapOptions::page_size()).unwrap();
483        let right = middle.split_off(MmapOptions::page_size()).unwrap();
484
485        assert!(left.as_ptr() < middle.as_ptr());
486        assert!(middle.as_ptr() < right.as_ptr());
487
488        // Drop the middle page.
489        drop(middle);
490
491        // Query the range, which should yield two memory regions.
492        let start = left.as_ptr() as usize;
493        let end = right.as_ptr() as usize + right.len();
494
495        let mut areas = MemoryAreas::query_range(start..end).unwrap();
496
497        let region = areas.next().unwrap().unwrap();
498        assert_eq!(
499            region.end(),
500            left.as_ptr() as usize + MmapOptions::page_size()
501        );
502        let mut region = areas.next().unwrap().unwrap();
503
504        if region.start() != right.as_ptr() as usize {
505            region = areas.next().unwrap().unwrap();
506        }
507
508        assert_eq!(region.start(), right.as_ptr() as usize);
509        assert!(areas.next().is_none());
510    }
511}