use crate::analyzer::PatternMatcher;
use memchr::memmem;
use std::sync::OnceLock;
struct CpuFeatures {
sse41_supported: bool,
avx2_supported: bool,
}
static CPU_FEATURES: OnceLock<CpuFeatures> = OnceLock::new();
fn get_cpu_features() -> &'static CpuFeatures {
CPU_FEATURES.get_or_init(|| {
let mut features = CpuFeatures {
sse41_supported: false,
avx2_supported: false,
};
#[cfg(target_arch = "x86_64")]
{
if cfg!(feature = "simd_acceleration") {
features.sse41_supported = std::is_x86_feature_detected!("sse4.1");
features.avx2_supported = std::is_x86_feature_detected!("avx2");
}
}
features
})
}
pub struct SimdLiteralMatcher {
pattern_str: String,
pattern_bytes: Vec<u8>,
}
impl SimdLiteralMatcher {
pub fn new(pattern: &str) -> Self {
get_cpu_features();
Self {
pattern_str: pattern.to_string(),
pattern_bytes: pattern.as_bytes().to_vec(),
}
}
fn can_use_simd(&self) -> bool {
let min_pattern_length = 3;
#[cfg(not(feature = "simd_acceleration"))]
return false;
#[cfg(feature = "simd_acceleration")]
{
if self.pattern_bytes.len() < min_pattern_length {
return false;
}
let features = get_cpu_features();
features.sse41_supported || features.avx2_supported
}
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
false
}
}
impl PatternMatcher for SimdLiteralMatcher {
fn is_match(&self, text: &str) -> bool {
if self.can_use_simd() {
memmem::find(text.as_bytes(), &self.pattern_bytes).is_some()
} else {
text.contains(&self.pattern_str)
}
}
}
pub struct PatternMatcherFactory;
impl PatternMatcherFactory {
pub fn create(pattern: &str) -> Box<dyn PatternMatcher + Send + Sync> {
if Self::is_complex_pattern(pattern) {
use crate::analyzer::RegexMatcher;
Box::new(RegexMatcher::new(pattern))
} else {
#[cfg(feature = "simd_acceleration")]
{
Box::new(SimdLiteralMatcher::new(pattern))
}
#[cfg(not(feature = "simd_acceleration"))]
{
use crate::analyzer::LiteralMatcher;
Box::new(LiteralMatcher::new(pattern))
}
}
}
fn is_complex_pattern(pattern: &str) -> bool {
pattern.contains(|c: char| {
c == '*'
|| c == '?'
|| c == '['
|| c == '('
|| c == '|'
|| c == '+'
|| c == '.'
|| c == '^'
|| c == '$'
|| c == '\\'
})
}
}
pub mod line_processing {
use memchr::memchr_iter;
pub fn find_line_endings(buffer: &[u8]) -> Vec<usize> {
memchr_iter(b'\n', buffer).collect()
}
pub fn count_lines(buffer: &[u8]) -> usize {
memchr_iter(b'\n', buffer).count()
+ if buffer.is_empty() || buffer[buffer.len() - 1] == b'\n' {
0
} else {
1
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_simd_literal_matcher_basic() {
let matcher = SimdLiteralMatcher::new("test");
assert!(matcher.is_match("This is a test string"));
assert!(!matcher.is_match("This does not match"));
}
#[test]
fn test_factory_creates_appropriate_matchers() {
let simple_matcher = PatternMatcherFactory::create("simple");
assert!(simple_matcher.is_match("simple pattern"));
assert!(!simple_matcher.is_match("not matching"));
let complex_matcher = PatternMatcherFactory::create("comp.ex|pattern");
assert!(complex_matcher.is_match("complex"));
assert!(complex_matcher.is_match("pattern"));
assert!(!complex_matcher.is_match("not matching"));
}
#[test]
fn test_line_processing() {
use super::line_processing::*;
let text = b"Line 1\nLine 2\nLine 3";
let line_endings = find_line_endings(text);
assert_eq!(line_endings, vec![6, 13]);
assert_eq!(count_lines(text), 3);
}
}