Skip to main content

timber_rs/
accelerated.rs

1use crate::analyzer::PatternMatcher;
2use memchr::memmem;
3use std::sync::OnceLock;
4
5// Feature detection flags
6struct CpuFeatures {
7    sse41_supported: bool,
8    avx2_supported: bool,
9}
10
11static CPU_FEATURES: OnceLock<CpuFeatures> = OnceLock::new();
12
13// Initialize CPU feature detection
14fn get_cpu_features() -> &'static CpuFeatures {
15    CPU_FEATURES.get_or_init(|| {
16        let mut features = CpuFeatures {
17            sse41_supported: false,
18            avx2_supported: false,
19        };
20
21        #[cfg(target_arch = "x86_64")]
22        {
23            if cfg!(feature = "simd_acceleration") {
24                features.sse41_supported = std::is_x86_feature_detected!("sse4.1");
25                features.avx2_supported = std::is_x86_feature_detected!("avx2");
26            }
27        }
28
29        features
30    })
31}
32
33/// SIMD-accelerated literal matcher using memchr crate
34pub struct SimdLiteralMatcher {
35    // Store both string and bytes to avoid repeated conversions
36    pattern_str: String,
37    pattern_bytes: Vec<u8>,
38}
39
40impl SimdLiteralMatcher {
41    pub fn new(pattern: &str) -> Self {
42        // Ensure CPU features are detected
43        get_cpu_features();
44
45        Self {
46            pattern_str: pattern.to_string(),
47            pattern_bytes: pattern.as_bytes().to_vec(),
48        }
49    }
50
51    // Determine if this instance can use SIMD acceleration
52    fn can_use_simd(&self) -> bool {
53        // Short patterns don't benefit much from SIMD, so use a threshold
54        let min_pattern_length = 3;
55
56        #[cfg(not(feature = "simd_acceleration"))]
57        return false;
58
59        #[cfg(feature = "simd_acceleration")]
60        {
61            // Patterns need to be long enough to benefit from SIMD
62            if self.pattern_bytes.len() < min_pattern_length {
63                return false;
64            }
65
66            // Return true if any SIMD features are available
67            let features = get_cpu_features();
68            features.sse41_supported || features.avx2_supported
69        }
70
71        #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
72        false
73    }
74}
75
76impl PatternMatcher for SimdLiteralMatcher {
77    fn is_match(&self, text: &str) -> bool {
78        if self.can_use_simd() {
79            // SIMD path - use memchr for high-performance search
80            memmem::find(text.as_bytes(), &self.pattern_bytes).is_some()
81        } else {
82            // Fallback path - use standard string search for short patterns
83            // or when SIMD isn't available
84            text.contains(&self.pattern_str)
85        }
86    }
87}
88
89/// Factory for creating the most appropriate pattern matcher based on the pattern
90/// and available hardware capabilities.
91pub struct PatternMatcherFactory;
92
93impl PatternMatcherFactory {
94    /// Creates the most optimized pattern matcher for the given pattern.
95    ///
96    /// This will automatically select between:
97    /// - SIMD-accelerated literal matcher for simple patterns (when hardware supports it)
98    /// - Standard literal matcher for simple patterns (fallback)
99    /// - Regex matcher for complex patterns
100    pub fn create(pattern: &str) -> Box<dyn PatternMatcher + Send + Sync> {
101        if Self::is_complex_pattern(pattern) {
102            // For complex patterns, use regex
103            use crate::analyzer::RegexMatcher;
104            Box::new(RegexMatcher::new(pattern))
105        } else {
106            // For simple patterns, try to use SIMD acceleration
107            #[cfg(feature = "simd_acceleration")]
108            {
109                Box::new(SimdLiteralMatcher::new(pattern))
110            }
111
112            // If SIMD acceleration feature is disabled, use regular matcher
113            #[cfg(not(feature = "simd_acceleration"))]
114            {
115                use crate::analyzer::LiteralMatcher;
116                Box::new(LiteralMatcher::new(pattern))
117            }
118        }
119    }
120
121    /// Determines if a pattern is complex and requires regex capabilities
122    fn is_complex_pattern(pattern: &str) -> bool {
123        // Look for regex metacharacters
124        pattern.contains(|c: char| {
125            c == '*'
126                || c == '?'
127                || c == '['
128                || c == '('
129                || c == '|'
130                || c == '+'
131                || c == '.'
132                || c == '^'
133                || c == '$'
134                || c == '\\'
135        })
136    }
137}
138
139// SIMD-accelerated line processing utilities
140pub mod line_processing {
141    use memchr::memchr_iter;
142
143    /// Split byte buffer into lines using SIMD-accelerated newline detection
144    pub fn find_line_endings(buffer: &[u8]) -> Vec<usize> {
145        memchr_iter(b'\n', buffer).collect()
146    }
147
148    /// Count lines in a buffer quickly
149    pub fn count_lines(buffer: &[u8]) -> usize {
150        memchr_iter(b'\n', buffer).count()
151            + if buffer.is_empty() || buffer[buffer.len() - 1] == b'\n' {
152                0
153            } else {
154                1
155            }
156    }
157}
158
159#[cfg(test)]
160mod tests {
161    use super::*;
162
163    #[test]
164    fn test_simd_literal_matcher_basic() {
165        let matcher = SimdLiteralMatcher::new("test");
166
167        assert!(matcher.is_match("This is a test string"));
168        assert!(!matcher.is_match("This does not match"));
169    }
170
171    #[test]
172    fn test_factory_creates_appropriate_matchers() {
173        // Simple pattern should use literal/SIMD matcher
174        let simple_matcher = PatternMatcherFactory::create("simple");
175        assert!(simple_matcher.is_match("simple pattern"));
176        assert!(!simple_matcher.is_match("not matching"));
177
178        // Complex pattern should use regex matcher
179        let complex_matcher = PatternMatcherFactory::create("comp.ex|pattern");
180        assert!(complex_matcher.is_match("complex"));
181        assert!(complex_matcher.is_match("pattern"));
182        assert!(!complex_matcher.is_match("not matching"));
183    }
184
185    #[test]
186    fn test_line_processing() {
187        use super::line_processing::*;
188
189        let text = b"Line 1\nLine 2\nLine 3";
190        let line_endings = find_line_endings(text);
191
192        assert_eq!(line_endings, vec![6, 13]);
193        assert_eq!(count_lines(text), 3);
194    }
195}