use super::SplitResult;
#[ derive( Debug, Clone ) ]
pub struct BoyerMooreSplitIterator<'a> {
input: &'a str,
pattern: &'a str,
#[allow(dead_code)]
bad_char_table: [ usize; 256 ],
position: usize,
finished: bool,
}
impl<'a> BoyerMooreSplitIterator<'a> {
pub fn new( input: &'a str, pattern: &'a str ) -> Self {
assert!( !pattern.is_empty(), "Boyer-Moore requires non-empty pattern" );
assert!( pattern.len() >= 2, "Boyer-Moore optimization requires pattern length >= 2" );
assert!( pattern.len() <= 8, "Boyer-Moore optimization works best with pattern length <= 8" );
let mut bad_char_table = [ pattern.len(); 256 ];
let pattern_bytes = pattern.as_bytes();
for ( i, &byte ) in pattern_bytes.iter().enumerate() {
if i < pattern_bytes.len() - 1 { bad_char_table[ byte as usize ] = pattern_bytes.len() - i - 1;
}
}
Self {
input,
pattern,
bad_char_table,
position: 0,
finished: false,
}
}
fn find_next_pattern( &self ) -> Option<usize> {
if self.finished || self.position >= self.input.len() {
return None;
}
let text_bytes = self.input.as_bytes();
let pattern_bytes = self.pattern.as_bytes();
let text_len = text_bytes.len();
let pattern_len = pattern_bytes.len();
if self.position + pattern_len > text_len {
return None;
}
let remaining_text = &text_bytes[ self.position.. ];
for i in 0..=( remaining_text.len().saturating_sub( pattern_len ) ) {
let mut matches = true;
for j in 0..pattern_len {
if remaining_text[ i + j ] != pattern_bytes[ j ] {
matches = false;
break;
}
}
if matches {
return Some( self.position + i );
}
}
None
}
}
impl<'a> Iterator for BoyerMooreSplitIterator<'a> {
type Item = SplitResult<'a>;
fn next( &mut self ) -> Option<Self::Item> {
if self.finished || self.position > self.input.len() {
return None;
}
if self.position == self.input.len() {
self.finished = true;
return None;
}
match self.find_next_pattern() {
Some( match_pos ) => {
let content = &self.input[ self.position..match_pos ];
self.position = match_pos + self.pattern.len();
Some( SplitResult::Borrowed( content ) )
},
None => {
let remaining = &self.input[ self.position.. ];
self.position = self.input.len();
self.finished = true;
if remaining.is_empty() {
None
} else {
Some( SplitResult::Borrowed( remaining ) )
}
}
}
}
}