use std::collections::hash_map::Entry;
use std::ops::{Add, Range, RangeInclusive, Sub};
use core::slice::Iter;
use rustc_hash::FxHashMap;
use crate::compiler::PatternId;
#[derive(Debug, Clone)]
pub(crate) struct Match {
pub base: usize,
pub range: Range<usize>,
pub xor_key: Option<u8>,
}
impl Match {
pub fn new(range: Range<usize>) -> Self {
Self { base: 0, range, xor_key: None }
}
pub fn rebase(mut self, base: usize) -> Self {
self.range = self.range.start.sub(self.base).add(base)
..self.range.end.sub(self.base).add(base);
self.base = base;
self
}
pub fn xor_key(mut self, key: u8) -> Self {
self.xor_key = Some(key);
self
}
pub fn block_range(&self) -> Range<usize> {
self.range.start.sub(self.base)..self.range.end.sub(self.base)
}
}
#[derive(Debug, Default)]
pub(crate) struct MatchList {
matches: Vec<Match>,
}
impl MatchList {
pub fn with_capacity(capacity: usize) -> Self {
Self { matches: Vec::with_capacity(capacity) }
}
pub fn add(&mut self, m: Match, replace_if_longer: bool) {
let mut insertion_index = self.matches.len();
while insertion_index > 0 {
let existing_match = &mut self.matches[insertion_index - 1];
if m.range.start == existing_match.range.start {
if replace_if_longer && existing_match.range.end < m.range.end
{
existing_match.range.end = m.range.end;
}
return;
}
if m.range.start > existing_match.range.start {
break;
}
insertion_index -= 1;
}
if insertion_index == self.matches.len() {
self.matches.push(m);
} else {
self.matches.insert(insertion_index, m);
}
}
#[inline]
pub fn get(&self, i: usize) -> Option<&Match> {
self.matches.get(i)
}
pub fn matches_in_range(&self, range: RangeInclusive<isize>) -> i64 {
if range.end().is_negative() {
return 0;
}
let start: usize = (*range.start()).try_into().unwrap_or(0);
let end: usize = (*range.end()).try_into().unwrap();
match self.search(start) {
Ok(index) | Err(index) => {
let mut count = 0;
for m in &self.matches.as_slice()[index..] {
if (start..=end).contains(&m.range.start) {
count += 1;
} else {
break;
}
}
count
}
}
}
#[inline]
pub fn capacity(&self) -> usize {
self.matches.capacity()
}
#[inline]
pub fn clear(&mut self) {
self.matches.clear()
}
#[inline]
pub fn len(&self) -> usize {
self.matches.len()
}
#[inline]
pub fn iter(&self) -> Iter<'_, Match> {
self.matches.iter()
}
#[inline]
pub fn search(&self, offset: usize) -> Result<usize, usize> {
self.matches.binary_search_by(|m| m.range.start.cmp(&offset))
}
}
impl<'a> IntoIterator for &'a MatchList {
type Item = &'a Match;
type IntoIter = Iter<'a, Match>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
#[derive(Clone)]
pub struct UnconfirmedMatch {
pub range: Range<usize>,
pub chain_length: usize,
}
pub(crate) struct PatternMatches {
matches: FxHashMap<PatternId, MatchList>,
max_matches_per_pattern: usize,
capacity: usize,
}
impl PatternMatches {
const DEFAULT_MAX_MATCHES_PER_PATTERN: usize = 1_000_000;
pub fn new() -> Self {
Self {
matches: FxHashMap::default(),
max_matches_per_pattern: Self::DEFAULT_MAX_MATCHES_PER_PATTERN,
capacity: 0,
}
}
pub fn max_matches_per_pattern(&mut self, n: usize) -> &mut Self {
self.max_matches_per_pattern = n;
self
}
pub fn get(&self, pattern_id: PatternId) -> Option<&MatchList> {
self.matches.get(&pattern_id)
}
#[inline]
pub fn is_empty(&self) -> bool {
self.matches.is_empty()
}
pub fn clear(&mut self) {
if self.capacity > 10000 {
self.matches.clear();
self.capacity = 0;
} else {
for (_, matches) in self.matches.iter_mut() {
matches.clear();
}
}
}
pub fn add(
&mut self,
pattern_id: PatternId,
m: Match,
replace_if_longer: bool,
) -> bool {
match self.matches.entry(pattern_id) {
Entry::Occupied(mut entry) => {
let matches = entry.get_mut();
if matches.len() < self.max_matches_per_pattern {
self.capacity -= matches.capacity();
matches.add(m, replace_if_longer);
self.capacity += matches.capacity();
true
} else {
false
}
}
Entry::Vacant(entry) => {
let mut matches = MatchList::with_capacity(8);
self.capacity += matches.capacity();
matches.add(m, replace_if_longer);
entry.insert(matches);
true
}
}
}
pub fn matches_per_pattern(
&self,
) -> impl Iterator<Item = (&PatternId, &MatchList)> {
self.matches.iter()
}
}
#[cfg(test)]
mod test {
use crate::scanner::matches::{Match, MatchList};
use std::ops::Range;
#[test]
fn match_list() {
let mut ml = MatchList::with_capacity(5);
ml.add(Match::new(2..10), false);
ml.add(Match::new(1..10), false);
ml.add(Match::new(1..15), true);
ml.add(Match::new(4..10), false);
ml.add(Match::new(3..10), false);
ml.add(Match::new(5..10), false);
assert_eq!(
ml.iter().map(|m| m.range.clone()).collect::<Vec<Range<usize>>>(),
vec![(1..15), (2..10), (3..10), (4..10), (5..10)]
)
}
}