grep_searcher/searcher/mmap.rs
1use std::{fs::File, path::Path};
2
3use memmap::Mmap;
4
5/// Controls the strategy used for determining when to use memory maps.
6///
7/// If a searcher is called in circumstances where it is possible to use memory
8/// maps, and memory maps are enabled, then it will attempt to do so if it
9/// believes it will make the search faster.
10///
11/// By default, memory maps are disabled.
12#[derive(Clone, Debug)]
13pub struct MmapChoice(MmapChoiceImpl);
14
15#[derive(Clone, Debug)]
16enum MmapChoiceImpl {
17 Auto,
18 Never,
19}
20
21impl Default for MmapChoice {
22 fn default() -> MmapChoice {
23 MmapChoice(MmapChoiceImpl::Never)
24 }
25}
26
27impl MmapChoice {
28 /// Use memory maps when they are believed to be advantageous.
29 ///
30 /// The heuristics used to determine whether to use a memory map or not
31 /// may depend on many things, including but not limited to, file size
32 /// and platform.
33 ///
34 /// If memory maps are unavailable or cannot be used for a specific input,
35 /// then normal OS read calls are used instead.
36 ///
37 /// # Safety
38 ///
39 /// This constructor is not safe because there is no obvious way to
40 /// encapsulate the safety of file backed memory maps on all platforms
41 /// without simultaneously negating some or all of their benefits.
42 ///
43 /// The specific contract the caller is required to uphold isn't precise,
44 /// but it basically amounts to something like, "the caller guarantees that
45 /// the underlying file won't be mutated." This, of course, isn't feasible
46 /// in many environments. However, command line tools may still decide to
47 /// take the risk of, say, a `SIGBUS` occurring while attempting to read a
48 /// memory map.
49 pub unsafe fn auto() -> MmapChoice {
50 MmapChoice(MmapChoiceImpl::Auto)
51 }
52
53 /// Never use memory maps, no matter what. This is the default.
54 pub fn never() -> MmapChoice {
55 MmapChoice(MmapChoiceImpl::Never)
56 }
57
58 /// Return a memory map if memory maps are enabled and if creating a
59 /// memory from the given file succeeded and if memory maps are believed
60 /// to be advantageous for performance.
61 ///
62 /// If this does attempt to open a memory map and it fails, then `None`
63 /// is returned and the corresponding error (along with the file path, if
64 /// present) is logged at the debug level.
65 pub(crate) fn open(
66 &self,
67 file: &File,
68 path: Option<&Path>,
69 ) -> Option<Mmap> {
70 if !self.is_enabled() {
71 return None;
72 }
73 if cfg!(target_os = "macos") {
74 // I guess memory maps on macOS aren't great. Should re-evaluate.
75 return None;
76 }
77 // SAFETY: This is acceptable because the only way `MmapChoiceImpl` can
78 // be `Auto` is if the caller invoked the `auto` constructor, which
79 // is itself not safe. Thus, this is a propagation of the caller's
80 // assertion that using memory maps is safe.
81 match unsafe { Mmap::map(file) } {
82 Ok(mmap) => Some(mmap),
83 Err(err) => {
84 if let Some(path) = path {
85 log::debug!(
86 "{}: failed to open memory map: {}",
87 path.display(),
88 err
89 );
90 } else {
91 log::debug!("failed to open memory map: {}", err);
92 }
93 None
94 }
95 }
96 }
97
98 /// Whether this strategy may employ memory maps or not.
99 pub(crate) fn is_enabled(&self) -> bool {
100 match self.0 {
101 MmapChoiceImpl::Auto => true,
102 MmapChoiceImpl::Never => false,
103 }
104 }
105}