From 3239782138947261928218e918c1ca72735da402 Mon Sep 17 00:00:00 2001 From: Andrew Gallant Date: Sun, 4 Sep 2016 11:52:18 -0400 Subject: [PATCH] Replace the slow loop in Teddy with Aho-Corasick. This can have a significant impact on performance when the match rate is low (common) and the number of patterns is high (e.g., for a case insensitive search). --- src/simd_accel/teddy128.rs | 47 ++++++++++---------------------------- 1 file changed, 12 insertions(+), 35 deletions(-) diff --git a/src/simd_accel/teddy128.rs b/src/simd_accel/teddy128.rs index a15e4401a6..6b5a1e1819 100644 --- a/src/simd_accel/teddy128.rs +++ b/src/simd_accel/teddy128.rs @@ -328,6 +328,7 @@ References use std::cmp; use std::ptr; +use aho_corasick::{Automaton, AcAutomaton, FullAcAutomaton}; use simd::u8x16; use simd::x86::sse2::Sse2Bool8ix16; use simd::x86::ssse3::Ssse3U8x16; @@ -354,6 +355,9 @@ pub struct Match { pub struct Teddy { /// A list of substrings to match. pats: Vec>, + /// An Aho-Corasick automaton of the patterns. We use this when we need to + /// search pieces smaller than the Teddy block size. + ac: FullAcAutomaton>, /// A set of 8 buckets. Each bucket corresponds to a single member of a /// bitset. A bucket contains zero or more substrings. This is useful /// when the number of substrings exceeds 8, since our bitsets cannot have @@ -403,6 +407,7 @@ impl Teddy { } Some(Teddy { pats: pats.to_vec(), + ac: AcAutomaton::new(pats.to_vec()).into_full(), buckets: buckets, masks: masks, }) @@ -570,7 +575,7 @@ impl Teddy { prev0 = res0; prev1 = res1; - + let bitfield = res.ne(zero).move_mask(); if bitfield != 0 { let pos = pos.checked_sub(2).unwrap(); @@ -659,27 +664,13 @@ impl Teddy { /// This is used when we don't have enough bytes in the haystack for our /// block based approach. fn slow(&self, haystack: &[u8], pos: usize) -> Option { - // TODO: Use Aho-Corasick, or otherwise adapt the block based approach - // to be capable of using smaller blocks. - let mut m = None; - for (pi, p) in self.pats.iter().enumerate() { - if let Some(i) = find_slow(p, &haystack[pos..]) { - let candidate = Match { - pat: pi, - start: pos + i, - end: pos + i + p.len(), - }; - match m { - None => m = Some(candidate), - Some(ref mut m) => { - if candidate.start < m.start { - *m = candidate; - } - } - } + self.ac.find(&haystack[pos..]).next().map(|m| { + Match { + pat: m.pati, + start: pos + m.start, + end: pos + m.end, } - } - m + }) } } @@ -802,17 +793,3 @@ impl UnsafeLoad for u8x16 { x } } - -/// Slow single-substring search use for naive brute force matching. -#[cold] -pub fn find_slow(pattern: &[u8], haystack: &[u8]) -> Option { - if pattern.len() > haystack.len() { - return None; - } - for i in 0..(haystack.len() - pattern.len() + 1) { - if pattern == &haystack[i..i + pattern.len()] { - return Some(i); - } - } - None -}