#![deny(missing_docs)]
use std::collections::{HashMap, VecDeque};
#[derive(Debug)]
pub struct LoopDetector {
window: usize,
threshold: u32,
buf: VecDeque<(String, String)>,
counts: HashMap<(String, String), u32>,
}
impl LoopDetector {
pub fn new(window: usize, threshold: u32) -> Self {
Self {
window,
threshold,
buf: VecDeque::with_capacity(window),
counts: HashMap::new(),
}
}
pub fn record(&mut self, tool: &str, args_fingerprint: &str) -> bool {
let key = (tool.to_string(), args_fingerprint.to_string());
if self.buf.len() == self.window {
if let Some(old) = self.buf.pop_front() {
if let Some(c) = self.counts.get_mut(&old) {
*c -= 1;
if *c == 0 {
self.counts.remove(&old);
}
}
}
}
self.buf.push_back(key.clone());
let entry = self.counts.entry(key).or_insert(0);
*entry += 1;
*entry >= self.threshold
}
pub fn is_looping(&self) -> bool {
self.counts.values().any(|&v| v >= self.threshold)
}
pub fn reset(&mut self) {
self.buf.clear();
self.counts.clear();
}
}