use bytes::{Bytes, BytesMut};
use std::sync::{Arc, Mutex};
#[derive(Clone)]
pub struct BufferPool {
inner: Arc<Inner>,
}
struct Inner {
idle: Mutex<(Vec<BytesMut>, usize)>,
max_idle_bytes: usize,
}
impl BufferPool {
pub fn new(max_idle_bytes: usize) -> Self {
Self {
inner: Arc::new(Inner {
idle: Mutex::new((Vec::new(), 0)),
max_idle_bytes,
}),
}
}
pub fn checkout(&self, capacity: usize) -> BytesMut {
let mut idle = self.inner.idle.lock().unwrap();
while let Some(buffer) = idle.0.pop() {
idle.1 -= buffer.capacity();
if buffer.capacity() >= capacity {
return buffer;
}
}
drop(idle);
let mut buffer = BytesMut::zeroed(capacity);
buffer.clear();
buffer
}
pub fn checkout_for_read(&self, len: usize) -> BytesMut {
let mut buffer = self.checkout(len);
unsafe { buffer.set_len(len) };
buffer
}
pub fn give_back(&self, mut buffer: BytesMut) {
buffer.clear();
let mut idle = self.inner.idle.lock().unwrap();
if idle.1 + buffer.capacity() <= self.inner.max_idle_bytes {
idle.1 += buffer.capacity();
idle.0.push(buffer);
}
}
pub fn freeze(&self, buffer: BytesMut) -> Bytes {
Bytes::from_owner(PooledChunk {
pool: self.clone(),
buffer,
})
}
}
struct PooledChunk {
pool: BufferPool,
buffer: BytesMut,
}
impl AsRef<[u8]> for PooledChunk {
fn as_ref(&self) -> &[u8] {
&self.buffer
}
}
impl Drop for PooledChunk {
fn drop(&mut self) {
self.pool.give_back(std::mem::take(&mut self.buffer));
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_buffer_pool_reuse() {
let pool = BufferPool::new(4096);
let buffer = pool.checkout(1024);
assert_eq!(buffer.capacity(), 1024);
assert!(buffer.is_empty());
let ptr = buffer.as_ptr();
pool.give_back(buffer);
let buffer = pool.checkout(512);
assert_eq!(buffer.as_ptr(), ptr);
assert_eq!(buffer.capacity(), 1024);
pool.give_back(buffer);
let buffer = pool.checkout(2048);
assert_eq!(buffer.capacity(), 2048);
assert!(pool.inner.idle.lock().unwrap().0.is_empty());
pool.give_back(buffer);
pool.give_back(BytesMut::zeroed(4096));
let idle = pool.inner.idle.lock().unwrap();
assert_eq!(idle.0.len(), 1);
assert_eq!(idle.1, 2048);
}
#[test]
fn test_buffer_pool_checkout_for_read() {
let pool = BufferPool::new(4096);
let mut buffer = pool.checkout_for_read(256);
assert_eq!(buffer.len(), 256);
buffer.fill(0xAB);
pool.give_back(buffer);
let buffer = pool.checkout_for_read(128);
assert_eq!(buffer.len(), 128);
assert!(buffer.iter().all(|&b| b == 0xAB));
}
#[test]
fn test_buffer_pool_freeze() {
let pool = BufferPool::new(4096);
let mut buffer = pool.checkout(1024);
buffer.extend_from_slice(b"hello, world!");
let ptr = buffer.as_ptr();
let bytes = pool.freeze(buffer);
assert_eq!(&bytes[..], b"hello, world!");
drop(bytes);
let buffer = pool.checkout(1024);
assert_eq!(buffer.as_ptr(), ptr);
}
}