use std::collections::{HashMap, VecDeque};
const DEFAULT_CAPACITY: usize = 1024;
const DEFAULT_MAX_RINGS: usize = 1024;
pub struct RecordBuffer {
rings: HashMap<String, VecDeque<Vec<u8>>>,
capacity: usize,
max_rings: usize,
name_drops: u64,
}
impl Default for RecordBuffer {
fn default() -> Self {
Self {
rings: HashMap::new(),
capacity: DEFAULT_CAPACITY,
max_rings: DEFAULT_MAX_RINGS,
name_drops: 0,
}
}
}
impl RecordBuffer {
pub fn new() -> Self {
Self::default()
}
pub fn with_caps(per_ring: usize, max_rings: usize) -> Self {
Self {
rings: HashMap::new(),
capacity: per_ring,
max_rings,
name_drops: 0,
}
}
pub fn record(&mut self, name: &str, bytes: Vec<u8>) {
if !self.rings.contains_key(name) && self.rings.len() >= self.max_rings {
self.name_drops += 1;
return;
}
let cap = self.capacity;
let ring = self.rings.entry(name.to_string()).or_default();
if ring.len() >= cap {
ring.pop_front();
}
ring.push_back(bytes);
}
pub fn snapshot(&self, name: &str) -> Vec<Vec<u8>> {
self.rings
.get(name)
.map(|r| r.iter().cloned().collect())
.unwrap_or_default()
}
pub fn name_drops(&self) -> u64 {
self.name_drops
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn record_snapshot_round_trip() {
let mut b = RecordBuffer::new();
b.record("m", b"a".to_vec());
b.record("m", b"b".to_vec());
let snap = b.snapshot("m");
assert_eq!(snap, vec![b"a".to_vec(), b"b".to_vec()]);
}
#[test]
fn record_overflow_drops_new_name_and_ticks_counter() {
let mut b = RecordBuffer::with_caps(8, 2);
b.record("a", b"1".to_vec());
b.record("b", b"2".to_vec());
assert_eq!(b.name_drops(), 0);
b.record("c", b"3".to_vec());
assert_eq!(b.name_drops(), 1);
assert_eq!(b.snapshot("c"), Vec::<Vec<u8>>::new());
b.record("a", b"more".to_vec());
assert_eq!(b.snapshot("a"), vec![b"1".to_vec(), b"more".to_vec()]);
}
}