use core::cmp;
use cow::CowBytes;
use ext_slice::ByteSlice;
use search::prefilter::{Freqy, PrefilterState};
#[derive(Clone, Debug)]
pub struct TwoWay<'b> {
needle: CowBytes<'b>,
freqy: Freqy,
critical_pos: usize,
shift: Shift,
}
impl<'b> TwoWay<'b> {
pub fn forward(needle: &'b [u8]) -> TwoWay<'b> {
let freqy = Freqy::forward(needle);
if needle.is_empty() {
return TwoWay {
needle: CowBytes::new(needle),
freqy,
critical_pos: 0,
shift: Shift::Large { shift: 0 },
};
}
let min_suffix = Suffix::forward(needle, SuffixKind::Minimal);
let max_suffix = Suffix::forward(needle, SuffixKind::Maximal);
let (period_lower_bound, critical_pos) =
if min_suffix.pos > max_suffix.pos {
(min_suffix.period, min_suffix.pos)
} else {
(max_suffix.period, max_suffix.pos)
};
let shift = Shift::forward(needle, period_lower_bound, critical_pos);
let needle = CowBytes::new(needle);
TwoWay { needle, freqy, critical_pos, shift }
}
pub fn reverse(needle: &'b [u8]) -> TwoWay<'b> {
let freqy = Freqy::reverse(needle);
if needle.is_empty() {
return TwoWay {
needle: CowBytes::new(needle),
freqy,
critical_pos: 0,
shift: Shift::Large { shift: 0 },
};
}
let min_suffix = Suffix::reverse(needle, SuffixKind::Minimal);
let max_suffix = Suffix::reverse(needle, SuffixKind::Maximal);
let (period_lower_bound, critical_pos) =
if min_suffix.pos < max_suffix.pos {
(min_suffix.period, min_suffix.pos)
} else {
(max_suffix.period, max_suffix.pos)
};
let shift = Shift::reverse(needle, period_lower_bound, critical_pos);
let needle = CowBytes::new(needle);
TwoWay { needle, freqy, critical_pos, shift }
}
pub fn prefilter_state(&self) -> PrefilterState {
self.freqy.prefilter_state()
}
pub fn needle(&self) -> &[u8] {
self.needle.as_slice()
}
#[cfg(feature = "std")]
pub fn into_owned(self) -> TwoWay<'static> {
TwoWay {
needle: self.needle.into_owned(),
freqy: self.freqy,
critical_pos: self.critical_pos,
shift: self.shift,
}
}
pub fn find(&self, haystack: &[u8]) -> Option<usize> {
self.find_with(&mut self.prefilter_state(), haystack)
}
pub fn rfind(&self, haystack: &[u8]) -> Option<usize> {
self.rfind_with(&mut self.prefilter_state(), haystack)
}
pub fn find_with(
&self,
prestate: &mut PrefilterState,
haystack: &[u8],
) -> Option<usize> {
if self.needle.is_empty() {
return Some(0);
} else if haystack.len() < self.needle.len() {
return None;
} else if self.needle.len() == 1 {
return haystack.find_byte(self.needle[0]);
}
match self.shift {
Shift::Small { period } => {
self.find_small(prestate, haystack, period)
}
Shift::Large { shift } => {
self.find_large(prestate, haystack, shift)
}
}
}
pub fn rfind_with(
&self,
prestate: &mut PrefilterState,
haystack: &[u8],
) -> Option<usize> {
if self.needle.is_empty() {
return Some(haystack.len());
} else if haystack.len() < self.needle.len() {
return None;
} else if self.needle.len() == 1 {
return haystack.rfind_byte(self.needle[0]);
}
match self.shift {
Shift::Small { period } => {
self.rfind_small(prestate, haystack, period)
}
Shift::Large { shift } => {
self.rfind_large(prestate, haystack, shift)
}
}
}
#[inline(never)]
fn find_small(
&self,
prestate: &mut PrefilterState,
haystack: &[u8],
period: usize,
) -> Option<usize> {
if prestate.is_effective() {
self.find_small_imp(prestate, true, haystack, period)
} else {
self.find_small_imp(prestate, false, haystack, period)
}
}
#[inline(always)]
fn find_small_imp(
&self,
prestate: &mut PrefilterState,
prefilter: bool,
haystack: &[u8],
period: usize,
) -> Option<usize> {
let needle = self.needle.as_slice();
let mut pos = 0;
let mut shift = 0;
while pos + needle.len() <= haystack.len() {
let mut i = cmp::max(self.critical_pos, shift);
if prefilter && prestate.is_effective() {
match self.freqy.find_candidate(prestate, &haystack[pos..]) {
None => return None,
Some(found) => {
shift = 0;
i = self.critical_pos;
pos += found;
if pos + needle.len() > haystack.len() {
return None;
}
}
}
}
while i < needle.len() && needle[i] == haystack[pos + i] {
i += 1;
}
if i < needle.len() {
pos += i - self.critical_pos + 1;
shift = 0;
} else {
let mut j = self.critical_pos;
while j > shift && needle[j] == haystack[pos + j] {
j -= 1;
}
if j <= shift && needle[shift] == haystack[pos + shift] {
return Some(pos);
}
pos += period;
shift = needle.len() - period;
}
}
None
}
#[inline(never)]
fn find_large(
&self,
prestate: &mut PrefilterState,
haystack: &[u8],
shift: usize,
) -> Option<usize> {
if prestate.is_effective() {
self.find_large_imp(prestate, true, haystack, shift)
} else {
self.find_large_imp(prestate, false, haystack, shift)
}
}
#[inline(always)]
fn find_large_imp(
&self,
prestate: &mut PrefilterState,
prefilter: bool,
haystack: &[u8],
shift: usize,
) -> Option<usize> {
let needle = self.needle.as_slice();
let mut pos = 0;
while pos + needle.len() <= haystack.len() {
let mut i = self.critical_pos;
if prefilter && prestate.is_effective() {
match self.freqy.find_candidate(prestate, &haystack[pos..]) {
None => return None,
Some(found) => {
pos += found;
if pos + needle.len() > haystack.len() {
return None;
}
}
}
}
while i < needle.len() && needle[i] == haystack[pos + i] {
i += 1;
}
if i < needle.len() {
pos += i - self.critical_pos + 1;
} else {
let mut j = self.critical_pos;
while j > 0 && needle[j] == haystack[pos + j] {
j -= 1;
}
if j == 0 && needle[0] == haystack[pos] {
return Some(pos);
}
pos += shift;
}
}
None
}
#[inline(never)]
fn rfind_small(
&self,
prestate: &mut PrefilterState,
haystack: &[u8],
period: usize,
) -> Option<usize> {
if prestate.is_effective() {
self.rfind_small_imp(prestate, true, haystack, period)
} else {
self.rfind_small_imp(prestate, false, haystack, period)
}
}
#[inline(always)]
fn rfind_small_imp(
&self,
prestate: &mut PrefilterState,
prefilter: bool,
haystack: &[u8],
period: usize,
) -> Option<usize> {
let needle = &*self.needle;
let nlen = needle.len();
let mut pos = haystack.len();
let mut shift = nlen;
while pos >= nlen {
let mut i = cmp::min(self.critical_pos, shift);
if prefilter && prestate.is_effective() {
match self.freqy.rfind_candidate(prestate, &haystack[..pos]) {
None => return None,
Some(found) => {
shift = nlen;
i = self.critical_pos;
pos = found;
if pos < nlen {
return None;
}
}
}
}
while i > 0 && needle[i - 1] == haystack[pos - nlen + i - 1] {
i -= 1;
}
if i > 0 || needle[0] != haystack[pos - nlen] {
pos -= self.critical_pos - i + 1;
shift = nlen;
} else {
let mut j = self.critical_pos;
while j < shift && needle[j] == haystack[pos - nlen + j] {
j += 1;
}
if j == shift {
return Some(pos - nlen);
}
pos -= period;
shift = period;
}
}
None
}
#[inline(never)]
fn rfind_large(
&self,
prestate: &mut PrefilterState,
haystack: &[u8],
shift: usize,
) -> Option<usize> {
if prestate.is_effective() {
self.rfind_large_imp(prestate, true, haystack, shift)
} else {
self.rfind_large_imp(prestate, false, haystack, shift)
}
}
#[inline(always)]
fn rfind_large_imp(
&self,
prestate: &mut PrefilterState,
prefilter: bool,
haystack: &[u8],
shift: usize,
) -> Option<usize> {
let needle = &*self.needle;
let nlen = needle.len();
let mut pos = haystack.len();
while pos >= nlen {
if prefilter && prestate.is_effective() {
match self.freqy.rfind_candidate(prestate, &haystack[..pos]) {
None => return None,
Some(found) => {
pos = found;
if pos < nlen {
return None;
}
}
}
}
let mut i = self.critical_pos;
while i > 0 && needle[i - 1] == haystack[pos - nlen + i - 1] {
i -= 1;
}
if i > 0 || needle[0] != haystack[pos - nlen] {
pos -= self.critical_pos - i + 1;
} else {
let mut j = self.critical_pos;
while j < nlen && needle[j] == haystack[pos - nlen + j] {
j += 1;
}
if j == nlen {
return Some(pos - nlen);
}
pos -= shift;
}
}
None
}
}
#[derive(Clone, Debug)]
enum Shift {
Small { period: usize },
Large { shift: usize },
}
impl Shift {
fn forward(
needle: &[u8],
period_lower_bound: usize,
critical_pos: usize,
) -> Shift {
let large = cmp::max(critical_pos, needle.len() - critical_pos);
if critical_pos * 2 >= needle.len() {
return Shift::Large { shift: large };
}
let (u, v) = needle.split_at(critical_pos);
if !v[..period_lower_bound].ends_with(u) {
return Shift::Large { shift: large };
}
Shift::Small { period: period_lower_bound }
}
fn reverse(
needle: &[u8],
period_lower_bound: usize,
critical_pos: usize,
) -> Shift {
let large = cmp::max(critical_pos, needle.len() - critical_pos);
if (needle.len() - critical_pos) * 2 >= needle.len() {
return Shift::Large { shift: large };
}
let (v, u) = needle.split_at(critical_pos);
if !v[v.len() - period_lower_bound..].starts_with(u) {
return Shift::Large { shift: large };
}
Shift::Small { period: period_lower_bound }
}
}
#[derive(Debug)]
struct Suffix {
pos: usize,
period: usize,
}
impl Suffix {
fn forward(needle: &[u8], kind: SuffixKind) -> Suffix {
debug_assert!(!needle.is_empty());
let mut suffix = Suffix { pos: 0, period: 1 };
let mut candidate_start = 1;
let mut offset = 0;
while candidate_start + offset < needle.len() {
let current = needle[suffix.pos + offset];
let candidate = needle[candidate_start + offset];
match kind.cmp(current, candidate) {
SuffixOrdering::Accept => {
suffix = Suffix { pos: candidate_start, period: 1 };
candidate_start += 1;
offset = 0;
}
SuffixOrdering::Skip => {
candidate_start += offset + 1;
offset = 0;
suffix.period = candidate_start - suffix.pos;
}
SuffixOrdering::Push => {
if offset + 1 == suffix.period {
candidate_start += suffix.period;
offset = 0;
} else {
offset += 1;
}
}
}
}
suffix
}
fn reverse(needle: &[u8], kind: SuffixKind) -> Suffix {
debug_assert!(!needle.is_empty());
let mut suffix = Suffix { pos: needle.len(), period: 1 };
if needle.len() == 1 {
return suffix;
}
let mut candidate_start = needle.len() - 1;
let mut offset = 0;
while offset < candidate_start {
let current = needle[suffix.pos - offset - 1];
let candidate = needle[candidate_start - offset - 1];
match kind.cmp(current, candidate) {
SuffixOrdering::Accept => {
suffix = Suffix { pos: candidate_start, period: 1 };
candidate_start -= 1;
offset = 0;
}
SuffixOrdering::Skip => {
candidate_start -= offset + 1;
offset = 0;
suffix.period = suffix.pos - candidate_start;
}
SuffixOrdering::Push => {
if offset + 1 == suffix.period {
candidate_start -= suffix.period;
offset = 0;
} else {
offset += 1;
}
}
}
}
suffix
}
}
#[derive(Clone, Copy, Debug)]
enum SuffixKind {
Minimal,
Maximal,
}
#[derive(Clone, Copy, Debug)]
enum SuffixOrdering {
Accept,
Skip,
Push,
}
impl SuffixKind {
fn cmp(self, current: u8, candidate: u8) -> SuffixOrdering {
use self::SuffixOrdering::*;
match self {
SuffixKind::Minimal if candidate < current => Accept,
SuffixKind::Minimal if candidate > current => Skip,
SuffixKind::Minimal => Push,
SuffixKind::Maximal if candidate > current => Accept,
SuffixKind::Maximal if candidate < current => Skip,
SuffixKind::Maximal => Push,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use ext_slice::B;
fn get_suffix_forward(needle: &[u8], kind: SuffixKind) -> (&[u8], usize) {
let s = Suffix::forward(needle, kind);
(&needle[s.pos..], s.period)
}
fn get_suffix_reverse(needle: &[u8], kind: SuffixKind) -> (&[u8], usize) {
let s = Suffix::reverse(needle, kind);
(&needle[..s.pos], s.period)
}
fn suffixes(bytes: &[u8]) -> Vec<&[u8]> {
(0..bytes.len()).map(|i| &bytes[i..]).collect()
}
fn naive_maximal_suffix_forward(needle: &[u8]) -> &[u8] {
let mut sufs = suffixes(needle);
sufs.sort();
sufs.pop().unwrap()
}
fn naive_maximal_suffix_reverse(needle: &[u8]) -> Vec<u8> {
let mut reversed = needle.to_vec();
reversed.reverse();
let mut got = naive_maximal_suffix_forward(&reversed).to_vec();
got.reverse();
got
}
#[test]
fn suffix_forward() {
macro_rules! assert_suffix_min {
($given:expr, $expected:expr, $period:expr) => {
let (got_suffix, got_period) =
get_suffix_forward($given.as_bytes(), SuffixKind::Minimal);
assert_eq!((B($expected), $period), (got_suffix, got_period));
};
}
macro_rules! assert_suffix_max {
($given:expr, $expected:expr, $period:expr) => {
let (got_suffix, got_period) =
get_suffix_forward($given.as_bytes(), SuffixKind::Maximal);
assert_eq!((B($expected), $period), (got_suffix, got_period));
};
}
assert_suffix_min!("a", "a", 1);
assert_suffix_max!("a", "a", 1);
assert_suffix_min!("ab", "ab", 2);
assert_suffix_max!("ab", "b", 1);
assert_suffix_min!("ba", "a", 1);
assert_suffix_max!("ba", "ba", 2);
assert_suffix_min!("abc", "abc", 3);
assert_suffix_max!("abc", "c", 1);
assert_suffix_min!("acb", "acb", 3);
assert_suffix_max!("acb", "cb", 2);
assert_suffix_min!("cba", "a", 1);
assert_suffix_max!("cba", "cba", 3);
assert_suffix_min!("abcabc", "abcabc", 3);
assert_suffix_max!("abcabc", "cabc", 3);
assert_suffix_min!("abcabcabc", "abcabcabc", 3);
assert_suffix_max!("abcabcabc", "cabcabc", 3);
assert_suffix_min!("abczz", "abczz", 5);
assert_suffix_max!("abczz", "zz", 1);
assert_suffix_min!("zzabc", "abc", 3);
assert_suffix_max!("zzabc", "zzabc", 5);
assert_suffix_min!("aaa", "aaa", 1);
assert_suffix_max!("aaa", "aaa", 1);
assert_suffix_min!("foobar", "ar", 2);
assert_suffix_max!("foobar", "r", 1);
}
#[test]
fn suffix_reverse() {
macro_rules! assert_suffix_min {
($given:expr, $expected:expr, $period:expr) => {
let (got_suffix, got_period) =
get_suffix_reverse($given.as_bytes(), SuffixKind::Minimal);
assert_eq!((B($expected), $period), (got_suffix, got_period));
};
}
macro_rules! assert_suffix_max {
($given:expr, $expected:expr, $period:expr) => {
let (got_suffix, got_period) =
get_suffix_reverse($given.as_bytes(), SuffixKind::Maximal);
assert_eq!((B($expected), $period), (got_suffix, got_period));
};
}
assert_suffix_min!("a", "a", 1);
assert_suffix_max!("a", "a", 1);
assert_suffix_min!("ab", "a", 1);
assert_suffix_max!("ab", "ab", 2);
assert_suffix_min!("ba", "ba", 2);
assert_suffix_max!("ba", "b", 1);
assert_suffix_min!("abc", "a", 1);
assert_suffix_max!("abc", "abc", 3);
assert_suffix_min!("acb", "a", 1);
assert_suffix_max!("acb", "ac", 2);
assert_suffix_min!("cba", "cba", 3);
assert_suffix_max!("cba", "c", 1);
assert_suffix_min!("abcabc", "abca", 3);
assert_suffix_max!("abcabc", "abcabc", 3);
assert_suffix_min!("abcabcabc", "abcabca", 3);
assert_suffix_max!("abcabcabc", "abcabcabc", 3);
assert_suffix_min!("abczz", "a", 1);
assert_suffix_max!("abczz", "abczz", 5);
assert_suffix_min!("zzabc", "zza", 3);
assert_suffix_max!("zzabc", "zz", 1);
assert_suffix_min!("aaa", "aaa", 1);
assert_suffix_max!("aaa", "aaa", 1);
}
quickcheck! {
fn qc_suffix_forward_maximal(bytes: Vec<u8>) -> bool {
if bytes.is_empty() {
return true;
}
let (got, _) = get_suffix_forward(&bytes, SuffixKind::Maximal);
let expected = naive_maximal_suffix_forward(&bytes);
got == expected
}
fn qc_suffix_reverse_maximal(bytes: Vec<u8>) -> bool {
if bytes.is_empty() {
return true;
}
let (got, _) = get_suffix_reverse(&bytes, SuffixKind::Maximal);
let expected = naive_maximal_suffix_reverse(&bytes);
expected == got
}
}
}