use std::cell::RefCell;
use std::mem;
use std::sync::Arc;
use bytes::Bytes;
use concurrent_queue::ConcurrentQueue;
const RETURN_LANE_DEPTH: usize = 256;
const MAX_RECYCLE: usize = 8 * 1024 * 1024;
const INITIAL_CAPACITY: usize = 512;
pub struct BufferPool {
pool: Vec<Vec<u8>>,
return_lane: Arc<ConcurrentQueue<Vec<u8>>>,
initial_capacity: usize,
max_recycle_size: usize,
}
impl BufferPool {
#[must_use]
pub fn new() -> Self {
Self {
pool: Vec::new(),
return_lane: Arc::new(ConcurrentQueue::bounded(RETURN_LANE_DEPTH)),
initial_capacity: INITIAL_CAPACITY,
max_recycle_size: MAX_RECYCLE,
}
}
#[must_use]
pub fn get(&mut self) -> Vec<u8> {
if let Some(mut buf) = self.pool.pop() {
buf.clear();
return buf;
}
while let Ok(buf) = self.return_lane.pop() {
self.pool.push(buf);
}
match self.pool.pop() {
Some(mut buf) => {
buf.clear();
buf
}
None => Vec::with_capacity(self.initial_capacity),
}
}
pub fn put(&mut self, mut buf: Vec<u8>) {
if buf.capacity() <= self.max_recycle_size {
buf.clear();
self.pool.push(buf);
}
}
#[must_use]
pub fn freeze(&self, buf: Vec<u8>) -> Bytes {
Bytes::from_owner(PooledFrame {
buf,
home: Arc::clone(&self.return_lane),
})
}
#[must_use]
pub fn pending_returns(&self) -> usize {
self.return_lane.len()
}
#[must_use]
pub fn free_list_len(&self) -> usize {
self.pool.len()
}
}
impl Default for BufferPool {
fn default() -> Self {
Self::new()
}
}
struct PooledFrame {
buf: Vec<u8>,
home: Arc<ConcurrentQueue<Vec<u8>>>,
}
impl AsRef<[u8]> for PooledFrame {
fn as_ref(&self) -> &[u8] {
&self.buf
}
}
impl Drop for PooledFrame {
fn drop(&mut self) {
let mut buf = mem::take(&mut self.buf);
if buf.capacity() != 0 && buf.capacity() <= MAX_RECYCLE {
buf.clear();
let _ = self.home.push(buf);
}
}
}
thread_local! {
static WORKER_POOL: RefCell<BufferPool> = RefCell::new(BufferPool::new());
}
pub fn with_worker_buffer<R>(f: impl FnOnce(&mut Vec<u8>) -> R) -> R {
WORKER_POOL.with(|cell| {
let mut pool = cell.borrow_mut();
let mut buf = pool.get();
let out = f(&mut buf);
pool.put(buf);
out
})
}
pub fn worker_freeze(fill: impl FnOnce(&mut Vec<u8>)) -> Bytes {
WORKER_POOL.with(|cell| {
let mut pool = cell.borrow_mut();
let mut buf = pool.get();
fill(&mut buf);
pool.freeze(buf)
})
}