mod buffer;
mod config;
mod metrics;
mod pool;
mod size_class;
mod tls;
pub use buffer::PooledBuffer;
pub use metrics::{ClassInfo, PoolStats};
pub use pool::BufferPool;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_basic_pool_operations() {
let pool = BufferPool::new().min_buffer_size(0);
let buf = pool.get(1024);
assert_eq!(buf.len(), 1024);
drop(buf);
let buf2 = pool.get(1024);
assert_eq!(buf2.len(), 1024);
}
#[test]
fn test_buffer_sizing() {
let pool = BufferPool::new().min_buffer_size(0);
let buf = pool.get(2048);
assert_eq!(buf.len(), 2048);
drop(buf);
let buf2 = pool.get(1024);
assert_eq!(buf2.len(), 1024);
assert!(buf2.capacity() >= 2048);
}
#[test]
fn test_min_size_filtering() {
use std::thread;
let pool = BufferPool::new().min_buffer_size(1024 * 1024).max_buffers_per_class(16);
let tls_cache_size = pool.state.tls_cache_size;
let pool_clone = pool.clone();
thread::spawn(move || {
for _ in 0..tls_cache_size {
let buf = pool_clone.get(512);
drop(buf);
}
let small_buf = pool_clone.get(512);
drop(small_buf);
})
.join()
.unwrap();
assert_eq!(pool.len(), 0);
let pool_clone = pool.clone();
thread::spawn(move || {
let mut buffers = Vec::new();
for _ in 0..=tls_cache_size {
buffers.push(pool_clone.get(2 * 1024 * 1024));
}
for buf in buffers {
drop(buf);
}
})
.join()
.unwrap();
assert!(!pool.is_empty());
}
#[test]
fn test_max_pool_size() {
let pool = BufferPool::new().min_buffer_size(0).max_buffers_per_class(4).tls_cache_size(2);
let mut buffers = Vec::new();
for _ in 0..20 {
buffers.push(pool.get(4096));
}
for buf in buffers {
drop(buf);
}
assert!(pool.len() <= 4);
}
#[test]
fn test_thread_local_cache() {
let pool = BufferPool::new().min_buffer_size(0);
let cache_size = pool.state.tls_cache_size;
for _ in 0..cache_size {
let buf = pool.get(4096);
drop(buf);
}
assert_eq!(pool.len(), 0);
for _ in 0..cache_size {
let buf = pool.get(4096);
assert_eq!(buf.len(), 4096);
}
}
#[test]
fn test_config_api() {
let pool1 = BufferPool::new();
assert!(!pool1.is_empty() || pool1.is_empty());
let pool2 = BufferPool::new()
.min_buffer_size(4096)
.tls_cache_size(4)
.max_buffers_per_class(16)
.batch_size(2);
assert_eq!(pool2.state.min_buffer_size, 4096);
assert_eq!(pool2.state.tls_cache_size, 4);
assert_eq!(pool2.state.batch_size, 2);
let buf = pool2.get(8192);
assert_eq!(buf.len(), 8192);
}
#[test]
fn test_concurrent_access() {
use std::thread;
let pool = BufferPool::new().min_buffer_size(0);
let mut handles = vec![];
for _ in 0..8 {
let pool = pool.clone();
handles.push(thread::spawn(move || {
for _ in 0..100 {
let buf = pool.get(4096);
assert_eq!(buf.len(), 4096);
drop(buf);
}
}));
}
for handle in handles {
handle.join().unwrap();
}
assert!(pool.len() < 1000);
}
#[test]
fn test_clone_shares_state() {
let pool = BufferPool::new().min_buffer_size(0).tls_cache_size(2);
let buf1 = pool.get(4096);
let buf2 = pool.get(4096);
drop(buf1);
drop(buf2);
let pool_clone = pool.clone();
let buf3 = pool_clone.get(4096);
let buf4 = pool_clone.get(4096);
let buf5 = pool_clone.get(4096);
drop(buf3);
drop(buf4);
drop(buf5);
assert!(std::sync::Arc::ptr_eq(&pool.state, &pool_clone.state));
}
#[test]
fn test_preallocate() {
let pool = BufferPool::new().min_buffer_size(0);
let initial_len = pool.len();
pool.preallocate(10, 64 * 1024);
assert!(pool.len() > initial_len);
let buf = pool.get(64 * 1024);
assert!(buf.capacity() >= 64 * 1024);
}
#[test]
fn test_edge_cases() {
use std::thread;
let pool = BufferPool::new().tls_cache_size(2).min_buffer_size(0);
assert!(pool.is_empty());
let buf_zero = pool.get(0);
assert_eq!(buf_zero.len(), 0);
drop(buf_zero);
let pool_clone = pool.clone();
thread::spawn(move || {
let b1 = pool_clone.get(4096);
let b2 = pool_clone.get(4096);
drop(b1);
drop(b2);
let buf_large = pool_clone.get(100 * 1024 * 1024);
assert_eq!(buf_large.len(), 100 * 1024 * 1024);
drop(buf_large);
})
.join()
.unwrap();
}
#[test]
fn test_size_class_routing() {
use std::thread;
let pool = BufferPool::new().min_buffer_size(0).tls_cache_size(2);
let mut handles = vec![];
for _ in 0..4 {
let pool_clone = pool.clone();
handles.push(thread::spawn(move || {
let mut buffers = vec![];
for &size in &[4096, 16384, 65536, 262_144] {
for _ in 0..4 {
buffers.push(pool_clone.get(size));
}
}
for buf in buffers {
drop(buf);
}
}));
}
for handle in handles {
handle.join().unwrap();
}
assert!(!pool.is_empty());
}
#[test]
fn test_clear() {
let pool = BufferPool::new().min_buffer_size(0).tls_cache_size(2);
let mut buffers = vec![];
for _ in 0..10 {
buffers.push(pool.get(4096));
}
for buf in buffers {
drop(buf);
}
assert!(!pool.is_empty());
pool.clear();
assert_eq!(pool.len(), 0);
assert!(pool.is_empty());
let buf = pool.get(4096);
assert_eq!(buf.len(), 4096);
}
#[test]
fn test_pool_isolation() {
let pool1 = BufferPool::new().min_buffer_size(0);
let pool2 = BufferPool::new().min_buffer_size(0);
assert_ne!(pool1.state.id, pool2.state.id);
let buf1 = pool1.get(4096);
drop(buf1);
assert_eq!(pool2.len(), 0);
}
#[test]
fn test_batch_transfer() {
let pool = BufferPool::new().min_buffer_size(0).tls_cache_size(2).batch_size(2);
let mut buffers = Vec::new();
for _ in 0..10 {
buffers.push(pool.get(4096));
}
for buf in buffers {
drop(buf);
}
assert!(!pool.is_empty());
}
#[test]
fn test_stats_counters() {
let pool = BufferPool::new().min_buffer_size(0);
let s = pool.stats();
assert_eq!(s.gets, 0);
assert_eq!(s.puts, 0);
let buf = pool.get(4096);
let s = pool.stats();
assert_eq!(s.gets, 1);
assert_eq!(s.allocations, 1);
drop(buf);
let s = pool.stats();
assert_eq!(s.puts, 1);
}
#[test]
fn test_stats_hit_rates() {
let pool = BufferPool::new().min_buffer_size(0).tls_cache_size(4);
let buf = pool.get(4096);
drop(buf);
let buf = pool.get(4096);
drop(buf);
let s = pool.stats();
assert_eq!(s.gets, 2);
assert!(s.tls_hits >= 1);
assert!(s.hit_rate > 0.0);
}
#[test]
fn test_stats_oversize() {
let pool = BufferPool::new().min_buffer_size(0);
let buf = pool.get(128 * 1024 * 1024);
let s = pool.stats();
assert_eq!(s.oversize, 1);
drop(buf);
let s = pool.stats();
assert_eq!(s.discards, 1);
}
#[test]
fn test_stats_reset() {
let pool = BufferPool::new().min_buffer_size(0);
let buf = pool.get(4096);
drop(buf);
assert!(pool.stats().gets > 0);
pool.reset_stats();
let s = pool.stats();
assert_eq!(s.gets, 0);
assert_eq!(s.puts, 0);
}
#[test]
fn test_stats_display() {
let pool = BufferPool::new().min_buffer_size(0);
let buf = pool.get(4096);
drop(buf);
let output = format!("{}", pool.stats());
assert!(output.contains("gets: 1"));
assert!(output.contains("puts: 1"));
}
#[test]
fn test_stats_class_info() {
let pool = BufferPool::new().min_buffer_size(0);
pool.preallocate(4, 4096);
let s = pool.stats();
assert_eq!(s.classes.len(), 8);
assert!(s.classes.iter().any(|c| c.buffered >= 4));
}
}