use parking_lot::Mutex;
use std::sync::Arc;
const DEFAULT_BUFFER_SIZE: usize = 64 * 1024;
const DEFAULT_POOL_SIZE: usize = 16;
pub struct PooledBuffer {
buffer: Vec<u8>,
pool: Arc<BufferPoolInner>,
}
impl PooledBuffer {
#[inline]
pub fn as_mut(&mut self) -> &mut Vec<u8> {
&mut self.buffer
}
#[inline]
pub fn len(&self) -> usize {
self.buffer.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.buffer.is_empty()
}
#[inline]
pub fn clear(&mut self) {
self.buffer.clear();
}
#[inline]
pub fn extend_from_slice(&mut self, slice: &[u8]) {
self.buffer.extend_from_slice(slice);
}
#[inline]
pub fn as_slice(&self) -> &[u8] {
&self.buffer
}
#[inline]
pub fn reserve(&mut self, additional: usize) {
self.buffer.reserve(additional);
}
}
impl Drop for PooledBuffer {
fn drop(&mut self) {
if self.buffer.capacity() <= DEFAULT_BUFFER_SIZE * 4 {
let mut taken = std::mem::take(&mut self.buffer);
taken.clear();
let mut pool = self.pool.buffers.lock();
if pool.len() < self.pool.max_size {
pool.push(taken);
}
}
}
}
impl std::ops::Deref for PooledBuffer {
type Target = [u8];
fn deref(&self) -> &Self::Target {
&self.buffer
}
}
struct BufferPoolInner {
buffers: Mutex<Vec<Vec<u8>>>,
buffer_size: usize,
max_size: usize,
}
#[derive(Clone)]
pub struct BufferPool {
inner: Arc<BufferPoolInner>,
}
impl BufferPool {
pub fn new() -> Self {
Self::with_config(DEFAULT_BUFFER_SIZE, DEFAULT_POOL_SIZE)
}
pub fn with_config(buffer_size: usize, pool_size: usize) -> Self {
let mut buffers = Vec::with_capacity(pool_size);
for _ in 0..pool_size {
buffers.push(Vec::with_capacity(buffer_size));
}
Self {
inner: Arc::new(BufferPoolInner {
buffers: Mutex::new(buffers),
buffer_size,
max_size: pool_size,
}),
}
}
#[inline]
pub fn get(&self) -> PooledBuffer {
let buffer = {
let mut pool = self.inner.buffers.lock();
pool.pop()
.unwrap_or_else(|| Vec::with_capacity(self.inner.buffer_size))
};
PooledBuffer {
buffer,
pool: Arc::clone(&self.inner),
}
}
#[inline]
pub fn get_with_capacity(&self, min_capacity: usize) -> PooledBuffer {
let mut buffer = {
let mut pool = self.inner.buffers.lock();
pool.pop()
.unwrap_or_else(|| Vec::with_capacity(min_capacity))
};
if buffer.capacity() < min_capacity {
buffer.reserve(min_capacity - buffer.capacity());
}
PooledBuffer {
buffer,
pool: Arc::clone(&self.inner),
}
}
pub fn available(&self) -> usize {
self.inner.buffers.lock().len()
}
}
impl Default for BufferPool {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_buffer_pool_basic() {
let pool = BufferPool::new();
assert_eq!(pool.available(), DEFAULT_POOL_SIZE);
let mut buf = pool.get();
assert_eq!(pool.available(), DEFAULT_POOL_SIZE - 1);
buf.extend_from_slice(b"hello world");
assert_eq!(buf.len(), 11);
drop(buf);
assert_eq!(pool.available(), DEFAULT_POOL_SIZE);
}
#[test]
fn test_buffer_reuse() {
let pool = BufferPool::new();
{
let mut buf = pool.get();
buf.extend_from_slice(&[0u8; 1000]);
}
let buf = pool.get();
assert!(buf.buffer.capacity() >= DEFAULT_BUFFER_SIZE);
}
#[test]
fn test_buffer_pool_exhaustion() {
let pool = BufferPool::with_config(1024, 2);
let _buf1 = pool.get();
let _buf2 = pool.get();
assert_eq!(pool.available(), 0);
let _buf3 = pool.get();
}
}