use log::info;
use serde::{Deserialize, Serialize};
use super::InstructionFilter;
use crate::instr::Instruction;
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct FilterList {
filters: Vec<InstructionFilter>,
}
impl FilterList {
pub fn new() -> Self {
FilterList {
filters: Vec::new(),
}
}
pub fn len(&self) -> usize {
self.filters.len()
}
pub fn is_empty(&self) -> bool {
self.filters.is_empty()
}
pub fn add(&mut self, mut filter: InstructionFilter) {
#[cfg(test)]
println!("New filter: {filter:?}");
if let Some(index) = self.filters.iter().position(|f| f.covers(&filter) || f == &filter) {
info!(
"Filter {:?} ignored, because it is redundant with {:?}",
filter, self.filters[index]
);
} else {
info!("Adding filter: {:?}", filter);
for f in self.filters.iter_mut() {
if let Some(new) = f.try_merge(&filter) {
if new.covers(f) {
*f = new;
}
}
}
for f in self.filters.iter() {
if let Some(new) = filter.try_merge(f) {
if new.covers(&filter) {
filter = new;
}
}
}
self.filters.retain(|f| !filter.covers(f));
self.filters.push(filter);
}
}
pub fn add_nomerge(&mut self, filter: InstructionFilter) {
if !self.filters.iter().any(|f| f.covers(&filter)) {
self.filters.push(filter);
}
}
pub fn should_extend(&self, instr: &Instruction) -> bool {
self.filters
.iter()
.any(|f| instr.byte_len() < f.len() && f.matches_smaller_instr_partially(instr))
}
pub fn matching_filter(&self, instr: &Instruction) -> Option<&InstructionFilter> {
self.filters
.iter()
.rev()
.find(|filter| filter.matches(instr) && filter.data.iter().skip(instr.byte_len()).all(|bf| bf.mask == 0))
}
pub fn next_matching_instruction(&self, instr: &Instruction) -> Option<Instruction> {
let mut next = None;
for filter in self.filters.iter() {
if let Some(nmi) = filter.next_matching_instruction(instr) {
if next.map(|next| nmi < next).unwrap_or(true) {
next = Some(nmi);
}
}
}
next
}
}
impl Default for FilterList {
fn default() -> Self {
Self::new()
}
}