use std::collections::VecDeque;
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::sync::Mutex;
use crate::intercept::{PacketData, PacketRecord};
use crate::wire_tap::WireTap;
pub struct RingBufferCapture {
records: Mutex<VecDeque<PacketRecord>>,
capacity: usize,
unbounded: AtomicBool,
dropped: AtomicU64,
}
impl RingBufferCapture {
pub fn new(capacity: usize) -> Self {
let cap = capacity.max(1);
Self {
records: Mutex::new(VecDeque::with_capacity(cap)),
capacity: cap,
unbounded: AtomicBool::new(false),
dropped: AtomicU64::new(0),
}
}
pub fn unbounded() -> Self {
Self {
records: Mutex::new(VecDeque::with_capacity(1024)),
capacity: usize::MAX,
unbounded: AtomicBool::new(true),
dropped: AtomicU64::new(0),
}
}
pub fn len(&self) -> usize {
self.records.lock().unwrap_or_else(|e| e.into_inner()).len()
}
pub fn is_empty(&self) -> bool {
self.records
.lock()
.unwrap_or_else(|e| e.into_inner())
.is_empty()
}
pub fn dropped(&self) -> u64 {
self.dropped.load(Ordering::Relaxed)
}
pub fn drain(&self) -> Vec<PacketRecord> {
let mut r = self.records.lock().unwrap_or_else(|e| e.into_inner());
let capacity = r.capacity();
let old = std::mem::replace(&mut *r, VecDeque::with_capacity(capacity));
old.into_iter().collect()
}
pub fn snapshot(&self) -> Vec<PacketRecord> {
self.records
.lock()
.unwrap_or_else(|e| e.into_inner())
.iter()
.cloned()
.collect()
}
pub fn set_unbounded(&self) {
self.unbounded.store(true, Ordering::Relaxed);
}
pub fn set_bounded(&self) {
self.unbounded.store(false, Ordering::Relaxed);
}
fn record(&self, timestamp_us: u64, data: PacketData) {
let record = PacketRecord { timestamp_us, data };
let is_unbounded = self.unbounded.load(Ordering::Relaxed);
let mut r = self.records.lock().unwrap_or_else(|e| e.into_inner());
if is_unbounded || r.len() < self.capacity {
r.push_back(record);
} else {
r.pop_front();
r.push_back(record);
self.dropped.fetch_add(1, Ordering::Relaxed);
}
}
}
impl Default for RingBufferCapture {
fn default() -> Self {
Self::new(10_000)
}
}
impl WireTap for RingBufferCapture {
fn on_write(&self, bytes: &[u8], ts: u64) {
self.record(ts, PacketData::RawTx(bytes.to_vec()));
}
fn on_read(&self, bytes: &[u8], ts: u64) {
self.record(ts, PacketData::RawRx(bytes.to_vec()));
}
fn on_error(&self, bytes: &[u8], error: &str, ts: u64) {
self.record(ts, PacketData::RawError(bytes.to_vec(), error.to_string()));
}
}