#![allow(
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
clippy::unreadable_literal,
clippy::panic,
clippy::manual_let_else
)]
use simdsieve::SimdSieve;
#[test]
fn boundary_empty_haystack() {
let sieve = SimdSieve::new(b"", &[b"a"]).unwrap();
assert_eq!(sieve.count(), 0);
}
#[test]
fn boundary_length_1() {
let haystack = b"a";
let sieve = SimdSieve::new(haystack, &[b"a"]).unwrap();
assert_eq!(sieve.collect::<Vec<_>>(), vec![0]);
}
#[test]
fn boundary_length_31() {
let haystack = &[b'a'; 31];
let sieve = SimdSieve::new(haystack, &[b"a"]).unwrap();
assert_eq!(sieve.count(), 31);
}
#[test]
fn boundary_length_32() {
let haystack = &[b'a'; 32];
let sieve = SimdSieve::new(haystack, &[b"a"]).unwrap();
assert_eq!(sieve.count(), 32);
}
#[test]
fn boundary_length_33() {
let haystack = &[b'a'; 33];
let sieve = SimdSieve::new(haystack, &[b"a"]).unwrap();
assert_eq!(sieve.count(), 33);
}
#[test]
fn boundary_length_63() {
let haystack = &[b'a'; 63];
let sieve = SimdSieve::new(haystack, &[b"a"]).unwrap();
assert_eq!(sieve.count(), 63);
}
#[test]
fn boundary_length_64() {
let haystack = &[b'a'; 64];
let sieve = SimdSieve::new(haystack, &[b"a"]).unwrap();
assert_eq!(sieve.count(), 64);
}
#[test]
fn boundary_length_65() {
let haystack = &[b'a'; 65];
let sieve = SimdSieve::new(haystack, &[b"a"]).unwrap();
assert_eq!(sieve.count(), 65);
}
#[test]
fn boundary_length_127() {
let haystack = &[b'a'; 127];
let sieve = SimdSieve::new(haystack, &[b"a"]).unwrap();
assert_eq!(sieve.count(), 127);
}
#[test]
fn boundary_length_128() {
let haystack = &[b'a'; 128];
let sieve = SimdSieve::new(haystack, &[b"a"]).unwrap();
assert_eq!(sieve.count(), 128);
}
#[test]
fn boundary_length_129() {
let haystack = &[b'a'; 129];
let sieve = SimdSieve::new(haystack, &[b"a"]).unwrap();
assert_eq!(sieve.count(), 129);
}
#[test]
fn position_match_at_byte_0() {
let haystack = b"abc def";
let sieve = SimdSieve::new(haystack, &[b"abc"]).unwrap();
assert_eq!(sieve.collect::<Vec<_>>(), vec![0]);
}
#[test]
fn position_match_at_last_byte() {
let haystack = b"xxx abc";
let sieve = SimdSieve::new(haystack, &[b"abc"]).unwrap();
assert_eq!(sieve.collect::<Vec<_>>(), vec![4]);
}
#[test]
fn position_spanning_block_boundary_32() {
let mut haystack = vec![b'x'; 34];
haystack[30..34].copy_from_slice(b"abca");
let sieve = SimdSieve::new(&haystack, &[b"abc"]).unwrap();
assert_eq!(sieve.collect::<Vec<_>>(), vec![30]);
}
#[test]
fn position_spanning_block_boundary_64() {
let mut haystack = vec![b'x'; 67];
haystack[62..66].copy_from_slice(b"abca");
let sieve = SimdSieve::new(&haystack, &[b"abc"]).unwrap();
assert_eq!(sieve.collect::<Vec<_>>(), vec![62]);
}
#[test]
fn position_spanning_block_boundary_128() {
let mut haystack = vec![b'x'; 131];
haystack[126..130].copy_from_slice(b"abca");
let sieve = SimdSieve::new(&haystack, &[b"abc"]).unwrap();
assert_eq!(sieve.collect::<Vec<_>>(), vec![126]);
}
#[test]
fn pattern_length_1() {
let haystack = b"aaaa";
let sieve = SimdSieve::new(haystack, &[b"a"]).unwrap();
assert_eq!(sieve.count(), 4);
}
#[test]
fn pattern_length_2() {
let haystack = b"ababab";
let sieve = SimdSieve::new(haystack, &[b"ab"]).unwrap();
assert_eq!(sieve.collect::<Vec<_>>(), vec![0, 2, 4]);
}
#[test]
fn pattern_length_3() {
let haystack = b"abcabcabc";
let sieve = SimdSieve::new(haystack, &[b"abc"]).unwrap();
assert_eq!(sieve.collect::<Vec<_>>(), vec![0, 3, 6]);
}
#[test]
fn pattern_length_4() {
let haystack = b"abcdabcd";
let sieve = SimdSieve::new(haystack, &[b"abcd"]).unwrap();
assert_eq!(sieve.collect::<Vec<_>>(), vec![0, 4]);
}
#[test]
fn pattern_length_5_plus() {
let haystack = b"abcdefghij";
let sieve = SimdSieve::new(haystack, &[b"abcde"]).unwrap();
assert_eq!(sieve.collect::<Vec<_>>(), vec![0]);
}
#[test]
fn pattern_longer_than_haystack() {
let haystack = b"short";
let sieve = SimdSieve::new(haystack, &[b"this is longer than haystack"]).unwrap();
assert_eq!(sieve.count(), 0);
}
#[test]
fn pattern_equals_haystack() {
let haystack = b"exact";
let sieve = SimdSieve::new(haystack, &[b"exact"]).unwrap();
assert_eq!(sieve.collect::<Vec<_>>(), vec![0]);
}
#[test]
fn multi_8_patterns_simultaneously() {
let patterns: Vec<Vec<u8>> = (0..8).map(|i| vec![b'a' + i]).collect();
let pattern_refs: Vec<&[u8]> = patterns.iter().map(std::vec::Vec::as_slice).collect();
let mut haystack = vec![b'x'; 100];
for i in 0..8 {
haystack[i * 10] = b'a' + i as u8;
}
let sieve = SimdSieve::new(&haystack, &pattern_refs).unwrap();
let matches: Vec<_> = sieve.collect();
assert_eq!(matches.len(), 8);
}
#[test]
fn multi_8_identical_patterns() {
let patterns: Vec<&[u8]> = vec![b"abc"; 8];
let haystack = b"xxxabcxxx";
let sieve = SimdSieve::new(haystack, &patterns).unwrap();
let matches: Vec<_> = sieve.collect();
assert_eq!(matches, vec![3]);
}
#[test]
fn multi_overlapping_patterns() {
let haystack = b"aaaa";
let pat1 = b"aa";
let pat2 = b"aaa";
let patterns: &[&[u8]] = &[pat1, pat2];
let sieve = SimdSieve::new(haystack, patterns).unwrap();
let matches: Vec<_> = sieve.collect();
assert_eq!(matches, vec![0, 1, 2]);
}
#[test]
fn density_all_positions_match() {
let haystack = b"aaaa";
let sieve = SimdSieve::new(haystack, &[b"a"]).unwrap();
assert_eq!(sieve.count(), 4);
}
#[test]
fn density_no_positions_match() {
let haystack = b"xxxxxxxxx";
let sieve = SimdSieve::new(haystack, &[b"a"]).unwrap();
assert_eq!(sieve.count(), 0);
}
#[test]
fn density_alternating() {
let haystack = b"abababab";
let sieve = SimdSieve::new(haystack, &[b"ab"]).unwrap();
assert_eq!(sieve.collect::<Vec<_>>(), vec![0, 2, 4, 6]);
}
#[test]
fn case_insensitive_ascii_lowercase() {
for low in b'a'..=b'z' {
let upper = low - 0x20;
let pattern = vec![upper];
let haystack = vec![low, low, low];
let sieve = SimdSieve::new_case_insensitive(&haystack, &[&pattern]).unwrap();
assert_eq!(sieve.count(), 3, "Failed for byte {low}");
}
}
#[test]
fn case_insensitive_non_ascii_unchanged() {
for byte in 0x80u8..=0xFF {
let haystack = vec![byte, byte];
let pattern = vec![byte];
let sieve = SimdSieve::new_case_insensitive(&haystack, &[&pattern]).unwrap();
assert_eq!(sieve.count(), 2, "Failed for byte {byte:#04x}");
}
}
#[test]
fn case_insensitive_uppercase_pattern() {
let haystack = b"Hello World";
let sieve = SimdSieve::new_case_insensitive(haystack, &[b"HELLO"]).unwrap();
assert_eq!(sieve.collect::<Vec<_>>(), vec![0]);
}
#[test]
fn case_insensitive_mixed_case() {
let haystack = b"HeLLo WoRLd";
let sieve = SimdSieve::new_case_insensitive(haystack, &[b"hello"]).unwrap();
assert_eq!(sieve.collect::<Vec<_>>(), vec![0]);
}
#[test]
fn adversarial_prefix_collision_same_4bytes() {
let haystack = b"abcdexxxx";
let pat1 = b"abcdex";
let pat2 = b"abcdeg";
let patterns: &[&[u8]] = &[pat1, pat2];
let sieve = SimdSieve::new(haystack, patterns).unwrap();
let matches: Vec<_> = sieve.collect();
assert_eq!(matches, vec![0]);
}
#[test]
fn adversarial_prefix_collision_different_5th() {
let haystack = b"xxxabcdeyyy";
let pat1 = b"abcdex";
let pat2 = b"abcdeg";
let patterns: &[&[u8]] = &[pat1, pat2];
let sieve = SimdSieve::new(haystack, patterns).unwrap();
let matches: Vec<_> = sieve.collect();
assert!(matches.is_empty());
}
#[test]
fn adversarial_repeated_pattern() {
let haystack = b"abababab";
let sieve = SimdSieve::new(haystack, &[b"aba"]).unwrap();
assert_eq!(sieve.collect::<Vec<_>>(), vec![0, 2, 4]);
}
#[test]
fn adversarial_prefix_of_another() {
let haystack = b"abcd abc";
let pat1 = b"abc";
let pat2 = b"abcd";
let patterns: &[&[u8]] = &[pat1, pat2];
let sieve = SimdSieve::new(haystack, patterns).unwrap();
let matches: Vec<_> = sieve.collect();
assert_eq!(matches, vec![0, 5]);
}