use alloc::{boxed::Box, vec, vec::Vec};
const SEEDS: &[Seed] = &[
Seed { first: b'x', index1: b'y', index2: b'z' },
Seed { first: b'x', index1: b'x', index2: b'z' },
Seed { first: b'x', index1: b'y', index2: b'x' },
Seed { first: b'x', index1: b'x', index2: b'x' },
Seed { first: b'x', index1: b'y', index2: b'y' },
];
pub(crate) struct Runner {
fwd: Option<
Box<
dyn FnMut(&[u8], &[u8], u8, u8) -> Option<Option<usize>> + 'static,
>,
>,
}
impl Runner {
pub(crate) fn new() -> Runner {
Runner { fwd: None }
}
pub(crate) fn run(self) {
if let Some(mut fwd) = self.fwd {
for seed in SEEDS.iter() {
for t in seed.generate() {
match fwd(&t.haystack, &t.needle, t.index1, t.index2) {
None => continue,
Some(result) => {
assert_eq!(
t.fwd, result,
"FORWARD, needle: {:?}, haystack: {:?}, \
index1: {:?}, index2: {:?}",
t.needle, t.haystack, t.index1, t.index2,
)
}
}
}
}
}
}
pub(crate) fn fwd(
mut self,
search: impl FnMut(&[u8], &[u8], u8, u8) -> Option<Option<usize>> + 'static,
) -> Runner {
self.fwd = Some(Box::new(search));
self
}
}
struct Test {
haystack: Vec<u8>,
needle: Vec<u8>,
index1: u8,
index2: u8,
fwd: Option<usize>,
}
impl Test {
fn new(
seed: Seed,
index1: usize,
index2: usize,
haystack_len: usize,
needle_len: usize,
fwd: Option<usize>,
) -> Option<Test> {
let mut index1: u8 = index1.try_into().unwrap();
let mut index2: u8 = index2.try_into().unwrap();
let mut haystack = vec![b'@'; haystack_len];
let mut needle = vec![b'#'; needle_len];
needle[0] = seed.first;
needle[index1 as usize] = seed.index1;
needle[index2 as usize] = seed.index2;
if let Some(i) = fwd {
haystack[i..i + needle.len()].copy_from_slice(&needle);
}
if let Some(i) = crate::memchr(seed.index1, &needle) {
index1 = u8::try_from(i).unwrap();
}
if let Some(i) = crate::memchr(seed.index2, &needle) {
index2 = u8::try_from(i).unwrap();
}
Some(Test { haystack, needle, index1, index2, fwd })
}
}
#[derive(Clone, Copy)]
struct Seed {
first: u8,
index1: u8,
index2: u8,
}
impl Seed {
const NEEDLE_LENGTH_LIMIT: usize = {
#[cfg(not(miri))]
{
33
}
#[cfg(miri)]
{
5
}
};
const HAYSTACK_LENGTH_LIMIT: usize = {
#[cfg(not(miri))]
{
65
}
#[cfg(miri)]
{
8
}
};
fn generate(self) -> impl Iterator<Item = Test> {
let len_start = 2;
(len_start..=Seed::NEEDLE_LENGTH_LIMIT).flat_map(move |needle_len| {
let index_start = len_start - 1;
(index_start..needle_len).flat_map(move |index1| {
(index1..needle_len).flat_map(move |index2| {
(needle_len..=Seed::HAYSTACK_LENGTH_LIMIT).flat_map(
move |haystack_len| {
Test::new(
self,
index1,
index2,
haystack_len,
needle_len,
None,
)
.into_iter()
.chain(
(0..=(haystack_len - needle_len)).flat_map(
move |output| {
Test::new(
self,
index1,
index2,
haystack_len,
needle_len,
Some(output),
)
},
),
)
},
)
})
})
})
}
}