use std::collections::HashMap;
use std::sync::Arc;
use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
use once_cell::sync::Lazy;
use parking_lot::{Mutex, RwLock};
struct Slot {
data: RwLock<Vec<u8>>,
generation: AtomicU64,
}
pub struct FramePool {
slots: Vec<Slot>,
slot_count: usize,
frame_size: usize,
write_cursor: AtomicUsize,
latest_slot: AtomicUsize,
generation: AtomicU64,
}
impl FramePool {
pub fn new(slot_count: usize, frame_size: usize) -> Self {
let slots: Vec<Slot> = (0..slot_count)
.map(|_| Slot {
data: RwLock::new(vec![0u8; frame_size]),
generation: AtomicU64::new(0),
})
.collect();
Self {
slots,
slot_count,
frame_size,
write_cursor: AtomicUsize::new(0),
latest_slot: AtomicUsize::new(0),
generation: AtomicU64::new(0),
}
}
pub fn write(&self, data: &[u8]) -> usize {
let slot_idx = self.write_cursor.fetch_add(1, Ordering::Relaxed) % self.slot_count;
let g = self.generation.fetch_add(1, Ordering::Relaxed) + 1;
let slot = &self.slots[slot_idx];
{
let mut buf = slot.data.write();
let len = data.len().min(buf.len());
buf[..len].copy_from_slice(&data[..len]);
if len < buf.len() {
buf[len..].fill(0);
}
}
slot.generation.store(g, Ordering::Release);
self.latest_slot.store(slot_idx, Ordering::Release);
slot_idx
}
pub fn write_dynamic(&self, data: &[u8]) -> usize {
let slot_idx = self.write_cursor.fetch_add(1, Ordering::Relaxed) % self.slot_count;
let g = self.generation.fetch_add(1, Ordering::Relaxed) + 1;
let slot = &self.slots[slot_idx];
{
let mut buf = slot.data.write();
if buf.len() != data.len() {
buf.resize(data.len(), 0);
}
buf.copy_from_slice(data);
}
slot.generation.store(g, Ordering::Release);
self.latest_slot.store(slot_idx, Ordering::Release);
slot_idx
}
pub fn read<R>(&self, slot_idx: usize, f: impl FnOnce(&[u8]) -> R) -> R {
let slot = &self.slots[slot_idx % self.slot_count];
let buf = slot.data.read();
f(&buf)
}
pub fn read_latest<R>(&self, f: impl FnOnce(&[u8], usize) -> R) -> R {
let slot_idx = self.latest_slot.load(Ordering::Acquire);
let slot = &self.slots[slot_idx % self.slot_count];
let buf = slot.data.read();
f(&buf, slot_idx)
}
pub fn clone_slot(&self, slot_idx: usize) -> Vec<u8> {
self.read(slot_idx, |data| data.to_vec())
}
pub fn latest(&self) -> usize {
self.latest_slot.load(Ordering::Acquire)
}
pub fn generation(&self) -> u64 {
self.generation.load(Ordering::Acquire)
}
pub fn capacity(&self) -> usize {
self.slot_count
}
pub fn frame_size(&self) -> usize {
self.frame_size
}
}
static POOLS: Lazy<Mutex<HashMap<String, Arc<FramePool>>>> =
Lazy::new(|| Mutex::new(HashMap::new()));
impl FramePool {
pub fn get_or_create(name: &str, slot_count: usize, frame_size: usize) -> Arc<FramePool> {
let mut pools = POOLS.lock();
pools
.entry(name.to_string())
.or_insert_with(|| Arc::new(FramePool::new(slot_count, frame_size)))
.clone()
}
pub fn get(name: &str) -> Option<Arc<FramePool>> {
POOLS.lock().get(name).cloned()
}
pub fn remove(name: &str) {
POOLS.lock().remove(name);
}
}