use std::cell::RefCell;
use std::sync::Arc;
use thread_local::CachedThreadLocal;
use grep_matcher::{NoCaptures, Match, Matcher};
use hyperscan::{
CompileFlags, pattern, Patterns, ScratchAllocator,
DatabaseBuilder, BlockDatabase
};
use crate::hyperscan_bridge::{
ThreadScratch, MatchData, ScanMutCtxtExt,
hyperscan_callback
};
#[derive(Clone, Debug)]
pub struct RegexMatcherBuilder {
}
impl Default for RegexMatcherBuilder {
fn default() -> Self {
RegexMatcherBuilder {
}
}
}
impl RegexMatcherBuilder {
pub fn new() -> RegexMatcherBuilder {
Default::default()
}
pub fn build(
&self,
patterns: &[String]
) -> Result<RegexMatcher, hyperscan::Error> {
let compile_flags = CompileFlags(hyperscan::HS_FLAG_SOM_LEFTMOST);
let parsed: Patterns = patterns.iter()
.enumerate()
.map(|(i,p)| pattern!(p, flags => compile_flags, id => i))
.collect();
Ok(RegexMatcher {
db : Arc::new(parsed.build()?),
match_data: CachedThreadLocal::new(),
scratchpad: CachedThreadLocal::new(),
})
}
}
#[derive(Debug)]
pub struct RegexMatcher {
db : Arc<BlockDatabase>,
match_data: CachedThreadLocal<RefCell<MatchData>>,
scratchpad: CachedThreadLocal<ThreadScratch>,
}
impl Clone for RegexMatcher {
fn clone(&self) -> Self {
RegexMatcher {
db : self.db.clone(),
match_data: CachedThreadLocal::new(),
scratchpad: CachedThreadLocal::new(),
}
}
}
impl RegexMatcher {
pub fn new(patterns: &[String]) -> Result<RegexMatcher, hyperscan::Error> {
RegexMatcherBuilder::new().build(patterns)
}
}
impl Matcher for RegexMatcher {
type Captures = NoCaptures;
type Error = hyperscan::Error;
fn find_at(
&self,
haystack: &[u8],
at: usize,
) -> Result<Option<Match>, Self::Error> {
self.find_at_with_match_data(self.match_data(), haystack, at)
}
fn new_captures(&self) -> Result<NoCaptures, Self::Error> {
Ok(NoCaptures::new())
}
}
impl RegexMatcher {
fn match_data(&self) -> &RefCell<MatchData> {
let create = || RefCell::new(MatchData::new_match_data());
self.match_data.get_or(create)
}
fn scratchpad(&self) -> Result<&ThreadScratch, hyperscan::Error> {
let create = || Ok(ThreadScratch { s : self.db.alloc()? });
self.scratchpad.get_or_try(create)
}
#[inline(always)]
fn find_at_with_match_data<'s>(
&self,
match_data: &RefCell<MatchData>,
subject: &'s [u8],
start: usize,
) -> Result<Option<Match>, hyperscan::Error> {
let mut match_data = match_data.borrow_mut();
if match_data.should_restart(subject.len()) {
match_data.restart(subject.len());
let scratchpad = self.scratchpad()?;
self.db.scan_mut_ctxt(subject, 0,
&scratchpad.s,
Some(hyperscan_callback),
Some(&mut match_data))?;
}
while let Some(m) = match_data.results.pop_front() {
if m.start() >= start {
return Ok(Some(m));
}
}
match_data.stop();
Ok(None)
}
}