mod common;
use std::{collections::HashMap, panic::catch_unwind};
use common::*;
use patterns::*;
#[track_caller]
fn all_alignments(pattern: &str, data: &[u8], matches: &[usize]) -> bool {
let location = std::panic::Location::caller();
let parsed = pattern.parse::<Pattern>().unwrap();
let run = |data: &[u8]| -> Vec<Result<Vec<usize>, String>> {
(0..=63)
.map(|i| {
with_misaligned(data, i, |data| {
let hook = std::panic::take_hook();
std::panic::set_hook(Box::new(|_| {}));
let ret =
catch_unwind(|| parsed.matches(data).collect::<Vec<_>>()).map_err(|msg| {
msg.downcast::<String>()
.map(|s| *s)
.or_else(|msg| msg.downcast::<&str>().map(|s| s.to_string()))
.unwrap_or_else(|_| "other panic".to_owned())
});
std::panic::set_hook(hook);
ret
})
})
.collect()
};
let results = run(data);
if results
.iter()
.all(|result| result.as_ref().is_ok_and(|r| r == matches))
{
return true;
}
eprintln!();
eprintln!("[{location}] TEST FAILED");
eprintln!("[{location}] pattern = {pattern:?}");
if data.len() < 16 {
eprintln!(
"[{location}] data = [{}]",
data.iter()
.map(|i| format!("{:02x}", i))
.collect::<Vec<_>>()
.join(" ")
);
}
eprintln!("[{location}] matches = {matches:x?}");
let mut hm: HashMap<Result<&[usize], &String>, Vec<usize>> = HashMap::new();
for (i, r) in results.iter().enumerate() {
match r {
Ok(r) => hm.entry(Ok(r)).or_default().push(i),
Err(msg) => hm.entry(Err(msg)).or_default().push(i),
};
}
if hm.len() == 1 {
match &results[0] {
Ok(r) => eprintln!("[{location}] result = {:x?}", r),
Err(msg) => eprintln!("[{location}] result = panic {:?}", msg),
}
return false;
}
hm.remove(&Ok(matches));
if (1..=10).contains(&hm.len()) {
let mut aligns = ['.'; 64];
let mut tmp = hm.iter().collect::<Vec<_>>();
tmp.sort_by_key(|(_, v)| *v);
for (i, (_, v)) in tmp.iter().enumerate() {
for &a in *v {
aligns[a] = char::from_digit(i as u32, 10).unwrap();
}
}
eprintln!(
"[{location}] offset ({})",
results
.iter()
.enumerate()
.map(|(i, _)| char::from_digit(i as u32 / 10, 10).unwrap())
.collect::<String>()
);
eprintln!(
"[{location}] ({})",
results
.iter()
.enumerate()
.map(|(i, _)| char::from_digit(i as u32 % 10, 10).unwrap())
.collect::<String>()
);
eprintln!(
"[{location}] result[] ({})",
aligns.iter().collect::<String>()
);
for (i, (k, _)) in tmp.iter().enumerate() {
match k {
Ok(r) => eprintln!("[{location}] result[{i}] = {r:x?}"),
Err(msg) => eprintln!("[{location}] result[{i}] = panic: {msg:?}"),
}
}
} else {
let mut tmp = hm.iter().collect::<Vec<_>>();
tmp.sort_by_key(|(_, v)| *v);
for (result, alignments) in tmp {
eprintln!(
"[{location}] aligns {}",
(0..63)
.map(|i| if alignments.contains(&i) { "#" } else { "." })
.collect::<Vec<_>>()
.join("")
);
eprintln!("[{location}] result = {result:x?}");
}
}
eprintln!();
false
}
#[test]
fn basic() {
let mut ok = true;
ok &= all_alignments("42", &[0x42], &[0]);
ok &= all_alignments("24", &[0x42], &[]);
ok &= all_alignments("42", &[0x42, 0x42], &[0, 1]);
assert!(ok);
}
#[test]
fn leading_wildcard() {
let mut ok = true;
ok &= all_alignments("? 42", &[0x42], &[]);
ok &= all_alignments("? 42", &[0x42, 0x22], &[]);
ok &= all_alignments("? 42", &[0x22, 0x42], &[0]);
assert!(ok);
}
#[test]
fn trailing_wildcard() {
let mut ok = true;
ok &= all_alignments("42 ?", &[0x42], &[]);
ok &= all_alignments("42 ?", &[0x42, 0x22], &[0]);
assert!(ok);
}
#[test]
fn trailing_zero() {
let mut ok = true;
ok &= all_alignments("00", &[0x42], &[]);
ok &= all_alignments("42 00", &[0x42], &[]);
ok &= all_alignments("00", &[0x00], &[0]);
ok &= all_alignments("42 00", &[0x42, 0x00], &[0]);
assert!(ok);
}
#[test]
fn xxh3_data_test() {
assert_eq!(
xxh3_data(16).as_slice(),
&[199, 123, 58, 187, 111, 135, 172, 217, 243, 107, 74, 26, 68, 247, 139, 243]
);
}
#[test]
fn overlap() {
let mut ok = true;
let data = &[0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd];
ok &= all_alignments("ab ?? ?? cd", data, &[0, 2]);
ok &= all_alignments("ab ?? ??", data, &[0, 2]);
ok &= all_alignments("?? ?? cd", data, &[1, 3]);
assert!(ok);
}
#[test]
fn repeat_across_buffer() {
let mut ok = true;
let mut data = [0_u8; 64];
data[0] = 1;
data[1] = 1;
ok &= all_alignments("01", &data, &[0, 1]);
assert!(ok);
}
#[test]
fn small() {
let mut ok = true;
let data = xxh3_data(64);
ok &= all_alignments("c7 7b", &data, &[0]);
ok &= all_alignments("c7 7b ?", &data, &[0]);
ok &= all_alignments("? c7 7b", &data, &[]);
ok &= all_alignments("f3", &data, &[0x08, 0x0F]);
ok &= all_alignments("f3 ? 4a", &data, &[0x08]);
ok &= all_alignments("f3 ? 69", &data, &[0x0F]);
ok &= all_alignments("c2", &data, &[0x28, 0x37]);
ok &= all_alignments("c2 ? ? 5e", &data, &[0x28]);
ok &= all_alignments("c2 ? ? 12", &data, &[0x37]);
ok &= all_alignments("14 53 22 e9 63", &data, &[0x31]);
ok &= all_alignments("c2", &data[..=0x37], &[0x28, 0x37]);
ok &= all_alignments("14 53 22 e9 63", &data[..=0x37], &[0x31]);
let data2 = data.repeat(2);
ok &= all_alignments("c7 7b", &data2, &[0, 64]);
ok &= all_alignments("c7 7b ?", &data2, &[0, 64]);
ok &= all_alignments("? c7 7b", &data2, &[63]);
ok &= all_alignments("f3", &data2, &[0x08, 0x0F, 0x48, 0x4F]);
ok &= all_alignments("f3 ? 4a", &data2, &[0x08, 0x48]);
ok &= all_alignments("f3 ? 69", &data2, &[0x0F, 0x4F]);
ok &= all_alignments("c2", &data2, &[0x28, 0x37, 0x68, 0x77]);
ok &= all_alignments("c2 ? ? 5e", &data2, &[0x28, 0x68]);
ok &= all_alignments("c2 ? ? 12", &data2, &[0x37, 0x77]);
ok &= all_alignments("14 53 22 e9 63", &data2, &[0x31, 0x71]);
ok &= all_alignments("fa fc c7", &data2, &[0x3E]);
ok &= all_alignments("fc c7 7b", &data2, &[0x3F]);
ok &= all_alignments("fc ?? 7b", &data2, &[0x3F]);
ok &= all_alignments("c2", &data2[..=0x77], &[0x28, 0x37, 0x68, 0x77]);
ok &= all_alignments("14 53 22 e9 63", &data2[..=0x77], &[0x31, 0x71]);
ok &= all_alignments("6b ?? ?? ?? ?? ??", &data2, &[0x09, 0x3B, 0x49]);
assert!(ok);
}
#[test]
fn medium() {
let mut ok = true;
let data = xxh3_data(256);
ok &= all_alignments("34", &data, &[0x83]);
ok &= all_alignments("34 a5", &data, &[0x83]);
ok &= all_alignments("34 a5 38", &data, &[0x83]);
assert!(ok);
}