use std::collections::HashMap;
pub struct BufferPool {
pub pools: HashMap<usize, Vec<Vec<f64>>>,
pub cap_per_bucket: usize,
}
impl BufferPool {
pub fn new() -> Self {
Self {
pools: HashMap::new(),
cap_per_bucket: 64,
}
}
pub fn alloc(&mut self, len: usize) -> Vec<f64> {
let bucket = len.next_power_of_two();
self.pools
.entry(bucket)
.or_default()
.pop()
.unwrap_or_else(|| vec![0.0; bucket])
}
pub fn free(&mut self, len: usize, buf: Vec<f64>) {
let bucket = len.next_power_of_two();
let pool = self.pools.entry(bucket).or_default();
if pool.len() < self.cap_per_bucket {
pool.push(buf);
}
}
pub fn total_free(&self) -> usize {
self.pools.values().map(|v| v.len()).sum()
}
pub fn clear(&mut self) {
self.pools.clear();
}
}
impl Default for BufferPool {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pool_alloc_returns_correctly_sized_vec() {
let mut pool = BufferPool::new();
let buf = pool.alloc(10);
assert!(buf.len() >= 10);
assert_eq!(buf.capacity(), 16);
}
#[test]
fn test_pool_alloc_free_reuses_buffer() {
let mut pool = BufferPool::new();
let buf = pool.alloc(8);
let ptr = buf.as_ptr();
pool.free(8, buf);
let buf2 = pool.alloc(8);
assert_eq!(buf2.as_ptr(), ptr);
}
#[test]
fn test_pool_bucket_cap_does_not_grow_unbounded() {
let mut pool = BufferPool::new();
pool.cap_per_bucket = 4;
for _ in 0..8 {
let buf = vec![0.0f64; 8];
pool.free(8, buf);
}
assert_eq!(pool.total_free(), 4);
}
#[test]
fn test_pool_clear_releases_all() {
let mut pool = BufferPool::new();
pool.free(4, vec![0.0; 4]);
pool.free(8, vec![0.0; 8]);
assert_eq!(pool.total_free(), 2);
pool.clear();
assert_eq!(pool.total_free(), 0);
}
#[test]
fn test_pool_alloc_zero_len_returns_power_of_two() {
let mut pool = BufferPool::new();
let buf = pool.alloc(0);
assert!(!buf.is_empty());
}
}