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}