use std::sync::Mutex;
pub const DEFAULT_MTU: usize = 1536;
pub struct BufferPool {
free: Mutex<Vec<Vec<u8>>>,
max_pooled: usize,
}
impl core::fmt::Debug for BufferPool {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let n = self.free.lock().map(|v| v.len()).unwrap_or(0);
f.debug_struct("BufferPool")
.field("free", &n)
.field("max_pooled", &self.max_pooled)
.finish()
}
}
impl Default for BufferPool {
fn default() -> Self {
Self::new()
}
}
impl BufferPool {
pub fn new() -> BufferPool {
BufferPool {
free: Mutex::new(Vec::new()),
max_pooled: usize::MAX,
}
}
pub fn with_cap(max_pooled: usize) -> BufferPool {
BufferPool {
free: Mutex::new(Vec::new()),
max_pooled,
}
}
pub fn alloc(&self, n: usize) -> Vec<u8> {
let mut free = self.free.lock().unwrap();
let mut buf = free.pop().unwrap_or_default();
drop(free);
if buf.capacity() < n {
buf.resize(n, 0);
} else {
buf.clear();
buf.resize(n, 0);
}
buf
}
pub fn free(&self, mut buf: Vec<u8>) {
let cap = buf.capacity();
unsafe {
buf.set_len(cap);
}
let mut free = self.free.lock().unwrap();
if free.len() < self.max_pooled {
free.push(buf);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn alloc_returns_requested_length() {
let p = BufferPool::new();
let b = p.alloc(100);
assert_eq!(b.len(), 100);
p.free(b);
}
#[test]
fn alloc_reuses_buffer() {
let p = BufferPool::new();
let mut b = p.alloc(100);
b[0] = 0x42;
let cap = b.capacity();
p.free(b);
let b2 = p.alloc(100);
assert!(b2.capacity() >= cap);
}
#[test]
fn cap_drops_overflow() {
let p = BufferPool::with_cap(2);
let b1 = p.alloc(10);
let b2 = p.alloc(10);
let b3 = p.alloc(10);
p.free(b1);
p.free(b2);
p.free(b3); assert_eq!(p.free.lock().unwrap().len(), 2);
}
}