use std::cell::RefCell;
use bytes::BytesMut;
pub const DEFAULT_BUFFER_SIZE: usize = 4096;
pub const MAX_POOL_SIZE: usize = 128;
thread_local! {
static BUFFER_POOL: RefCell<Vec<BytesMut>> = const { RefCell::new(Vec::new()) };
}
pub fn acquire_buffer(min_capacity: usize) -> BytesMut {
let min_capacity = min_capacity.max(DEFAULT_BUFFER_SIZE);
BUFFER_POOL.with(|pool| {
let mut pool = pool.borrow_mut();
while let Some(buf) = pool.pop() {
if buf.capacity() >= min_capacity {
let mut buf = buf;
buf.clear();
return buf;
}
}
BytesMut::with_capacity(min_capacity)
})
}
pub fn release_buffer(buf: BytesMut) {
if buf.capacity() > DEFAULT_BUFFER_SIZE * 2 {
return;
}
BUFFER_POOL.with(|pool| {
let mut pool = pool.borrow_mut();
if pool.len() < MAX_POOL_SIZE {
pool.push(buf);
}
});
}
#[allow(dead_code)]
pub struct PooledBuffer {
buf: Option<BytesMut>,
}
impl PooledBuffer {
#[allow(dead_code)]
pub fn new() -> Self {
Self {
buf: Some(acquire_buffer(DEFAULT_BUFFER_SIZE)),
}
}
#[allow(dead_code)]
pub fn with_capacity(min_capacity: usize) -> Self {
Self {
buf: Some(acquire_buffer(min_capacity)),
}
}
#[allow(dead_code)]
pub fn get(&self) -> &BytesMut {
self.buf.as_ref().expect("buffer not stolen")
}
#[allow(dead_code)]
pub fn get_mut(&mut self) -> &mut BytesMut {
self.buf.as_mut().expect("buffer not stolen")
}
#[allow(dead_code)]
pub fn take(&mut self) -> BytesMut {
self.buf.take().expect("buffer already taken")
}
}
impl Default for PooledBuffer {
fn default() -> Self {
Self::new()
}
}
impl Drop for PooledBuffer {
fn drop(&mut self) {
if let Some(buf) = self.buf.take() {
release_buffer(buf);
}
}
}
#[allow(dead_code)]
pub fn clear_pool() {
BUFFER_POOL.with(|pool| {
pool.borrow_mut().clear();
});
}
#[allow(dead_code)]
pub fn pool_stats() -> (usize, usize) {
BUFFER_POOL.with(|pool| {
let pool = pool.borrow();
let size = pool.len();
let capacity: usize = pool.iter().map(|b| b.capacity()).sum();
(size, capacity)
})
}
#[cfg(test)]
mod tests {
use super::*;
fn reset_pool() {
clear_pool();
}
#[test]
fn test_acquire_and_release() {
reset_pool();
let buf = acquire_buffer(DEFAULT_BUFFER_SIZE);
assert_eq!(buf.capacity(), DEFAULT_BUFFER_SIZE);
release_buffer(buf);
let (size, _) = pool_stats();
assert_eq!(size, 1);
let buf2 = acquire_buffer(DEFAULT_BUFFER_SIZE);
assert_eq!(buf2.capacity(), DEFAULT_BUFFER_SIZE);
let (size, _) = pool_stats();
assert_eq!(size, 0);
}
#[test]
fn test_pooled_buffer_guard() {
reset_pool();
{
let guard = PooledBuffer::new();
assert_eq!(guard.get().capacity(), DEFAULT_BUFFER_SIZE);
}
let (size, _) = pool_stats();
assert_eq!(size, 1);
}
#[test]
fn test_pooled_buffer_take() {
reset_pool();
let mut guard = PooledBuffer::new();
let buf = guard.take();
drop(guard);
let (size, _) = pool_stats();
assert_eq!(size, 0);
drop(buf);
}
#[test]
fn test_oversized_buffer_not_pooled() {
reset_pool();
let buf = BytesMut::with_capacity(DEFAULT_BUFFER_SIZE * 10);
release_buffer(buf);
let (size, _) = pool_stats();
assert_eq!(size, 0);
}
#[test]
fn test_pool_size_limit() {
reset_pool();
let mut buffers = Vec::new();
for _ in 0..MAX_POOL_SIZE + 10 {
buffers.push(acquire_buffer(DEFAULT_BUFFER_SIZE));
}
for buf in buffers {
release_buffer(buf);
}
let (size, _) = pool_stats();
assert_eq!(size, MAX_POOL_SIZE);
}
#[test]
fn test_buffer_cleared_on_acquire() {
reset_pool();
let mut buf = acquire_buffer(DEFAULT_BUFFER_SIZE);
buf.extend_from_slice(b"hello world");
release_buffer(buf);
let buf = acquire_buffer(DEFAULT_BUFFER_SIZE);
assert!(buf.is_empty());
}
#[test]
fn test_minimum_capacity() {
reset_pool();
let buf = acquire_buffer(100);
assert!(buf.capacity() >= DEFAULT_BUFFER_SIZE);
}
}