use bytes::Bytes;
use crossbeam_queue::ArrayQueue;
use std::mem::ManuallyDrop;
use std::sync::Arc;
#[derive(Debug)]
pub struct RecvBufPool {
pool: ArrayQueue<Vec<u8>>,
buf_size: usize,
}
impl RecvBufPool {
pub fn new(max_in_flight: usize, buf_size: usize) -> Arc<Self> {
Arc::new(Self {
pool: ArrayQueue::new(max_in_flight.max(1)),
buf_size,
})
}
#[inline]
pub fn get(self: &Arc<Self>) -> PooledRecvBuf {
let buf = match self.pool.pop() {
Some(mut buf) => {
buf.resize(self.buf_size, 0);
buf
}
None => vec![0u8; self.buf_size],
};
PooledRecvBuf {
buf: Some(buf),
pool: self.clone(),
}
}
pub fn idle_len(&self) -> usize {
self.pool.len()
}
fn return_buf(&self, mut buf: Vec<u8>) {
buf.resize(self.buf_size, 0);
let _ = self.pool.push(buf);
}
}
pub struct PooledRecvBuf {
buf: Option<Vec<u8>>,
pool: Arc<RecvBufPool>,
}
impl PooledRecvBuf {
#[inline]
pub fn as_mut_slice(&mut self) -> &mut [u8] {
self.buf
.as_mut()
.expect("PooledRecvBuf consumed")
.as_mut_slice()
}
#[inline]
pub fn as_slice(&self) -> &[u8] {
self.buf
.as_ref()
.expect("PooledRecvBuf consumed")
.as_slice()
}
pub fn into_bytes(mut self, size: usize) -> Bytes {
let mut buf = self.buf.take().expect("PooledRecvBuf already consumed");
debug_assert!(size <= buf.len(), "size {} > buf.len() {}", size, buf.len());
buf.truncate(size);
Bytes::from_owner(OwnedRecvBuf {
buf: ManuallyDrop::new(buf),
pool: self.pool.clone(),
})
}
}
impl Drop for PooledRecvBuf {
fn drop(&mut self) {
if let Some(buf) = self.buf.take() {
self.pool.return_buf(buf);
}
}
}
struct OwnedRecvBuf {
buf: ManuallyDrop<Vec<u8>>,
pool: Arc<RecvBufPool>,
}
impl AsRef<[u8]> for OwnedRecvBuf {
#[inline]
fn as_ref(&self) -> &[u8] {
&self.buf
}
}
impl Drop for OwnedRecvBuf {
fn drop(&mut self) {
let buf = unsafe { ManuallyDrop::take(&mut self.buf) };
self.pool.return_buf(buf);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn drop_handle_returns_buf_to_pool() {
let pool = RecvBufPool::new(4, 1500);
assert_eq!(pool.idle_len(), 0);
{
let _buf = pool.get();
}
assert_eq!(pool.idle_len(), 1);
}
#[test]
fn into_bytes_returns_buf_after_bytes_drop() {
let pool = RecvBufPool::new(4, 1500);
let buf = pool.get();
let bytes = buf.into_bytes(200);
assert_eq!(bytes.len(), 200);
assert_eq!(pool.idle_len(), 0);
drop(bytes);
assert_eq!(pool.idle_len(), 1);
}
#[test]
fn buf_is_reusable() {
let pool = RecvBufPool::new(4, 1500);
drop(pool.get());
assert_eq!(pool.idle_len(), 1);
let buf = pool.get();
assert_eq!(buf.as_slice().len(), 1500);
assert_eq!(pool.idle_len(), 0);
}
#[test]
fn pool_caps_returned_buffers_at_capacity() {
let pool = RecvBufPool::new(2, 1500);
let a = pool.get();
let b = pool.get();
let c = pool.get();
drop(a);
drop(b);
drop(c);
assert_eq!(pool.idle_len(), 2);
}
}