Skip to main content

driven/binary/
memory_map.rs

1//! Memory-Mapped Rule Files
2//!
3//! Zero-copy loading via mmap for instant rule access.
4
5use crate::{DrivenError, Result};
6use memmap2::Mmap;
7use std::fs::File;
8use std::path::Path;
9
10/// Memory-mapped rule file
11#[derive(Debug)]
12pub struct MappedRule {
13    /// Memory mapping
14    mmap: Mmap,
15    /// File path (for debugging)
16    path: std::path::PathBuf,
17}
18
19impl MappedRule {
20    /// Open a rule file with memory mapping
21    pub fn open(path: &Path) -> Result<Self> {
22        let file = File::open(path).map_err(|e| {
23            DrivenError::Io(std::io::Error::new(
24                e.kind(),
25                format!("Failed to open {}: {}", path.display(), e),
26            ))
27        })?;
28
29        // Safety: The file is opened read-only and we maintain the mapping
30        let mmap = unsafe { Mmap::map(&file) }.map_err(|e| {
31            DrivenError::Io(std::io::Error::other(format!(
32                "Failed to mmap {}: {}",
33                path.display(),
34                e
35            )))
36        })?;
37
38        Ok(Self {
39            mmap,
40            path: path.to_path_buf(),
41        })
42    }
43
44    /// Get the raw bytes (zero-copy)
45    pub fn as_bytes(&self) -> &[u8] {
46        &self.mmap
47    }
48
49    /// Get file size
50    pub fn len(&self) -> usize {
51        self.mmap.len()
52    }
53
54    /// Check if empty
55    pub fn is_empty(&self) -> bool {
56        self.mmap.is_empty()
57    }
58
59    /// Get the file path
60    pub fn path(&self) -> &Path {
61        &self.path
62    }
63
64    /// Advise the kernel about access patterns
65    pub fn advise_sequential(&self) -> Result<()> {
66        #[cfg(unix)]
67        {
68            self.mmap.advise(memmap2::Advice::Sequential).map_err(|e| {
69                DrivenError::Io(std::io::Error::new(
70                    std::io::ErrorKind::Other,
71                    e.to_string(),
72                ))
73            })?;
74        }
75        Ok(())
76    }
77
78    /// Advise the kernel to keep pages in memory
79    pub fn advise_willneed(&self) -> Result<()> {
80        #[cfg(unix)]
81        {
82            self.mmap.advise(memmap2::Advice::WillNeed).map_err(|e| {
83                DrivenError::Io(std::io::Error::new(
84                    std::io::ErrorKind::Other,
85                    e.to_string(),
86                ))
87            })?;
88        }
89        Ok(())
90    }
91}
92
93/// Rule mapping utilities
94#[derive(Debug, Default)]
95pub struct RuleMapping;
96
97impl RuleMapping {
98    /// Check if a path can be memory-mapped
99    pub fn can_map(path: &Path) -> bool {
100        path.is_file() && path.metadata().map(|m| m.len() > 0).unwrap_or(false)
101    }
102
103    /// Get recommended mapping for file size
104    pub fn strategy(size: u64) -> MappingStrategy {
105        if size < 4096 {
106            MappingStrategy::ReadAll // Small files: just read
107        } else if size < 1024 * 1024 {
108            MappingStrategy::MapPrivate // Medium: private mapping
109        } else {
110            MappingStrategy::MapShared // Large: shared mapping
111        }
112    }
113}
114
115/// Memory mapping strategy
116#[derive(Debug, Clone, Copy, PartialEq, Eq)]
117pub enum MappingStrategy {
118    /// Read entire file into memory
119    ReadAll,
120    /// Private memory mapping (copy-on-write)
121    MapPrivate,
122    /// Shared memory mapping
123    MapShared,
124}
125
126/// Pre-fetched rule data (for when mmap isn't suitable)
127#[derive(Debug)]
128pub struct PreloadedRule {
129    /// Rule data
130    data: Vec<u8>,
131    /// Original path
132    path: std::path::PathBuf,
133}
134
135impl PreloadedRule {
136    /// Load a rule file completely into memory
137    pub fn load(path: &Path) -> Result<Self> {
138        let data = std::fs::read(path)?;
139        Ok(Self {
140            data,
141            path: path.to_path_buf(),
142        })
143    }
144
145    /// Get the raw bytes
146    pub fn as_bytes(&self) -> &[u8] {
147        &self.data
148    }
149
150    /// Get file size
151    pub fn len(&self) -> usize {
152        self.data.len()
153    }
154
155    /// Check if empty
156    pub fn is_empty(&self) -> bool {
157        self.data.is_empty()
158    }
159
160    /// Get the file path
161    pub fn path(&self) -> &Path {
162        &self.path
163    }
164}
165
166#[cfg(test)]
167mod tests {
168    use super::*;
169
170    #[test]
171    fn test_mapping_strategy() {
172        assert_eq!(RuleMapping::strategy(100), MappingStrategy::ReadAll);
173        assert_eq!(RuleMapping::strategy(10_000), MappingStrategy::MapPrivate);
174        assert_eq!(
175            RuleMapping::strategy(10_000_000),
176            MappingStrategy::MapShared
177        );
178    }
179}