network-protocol 1.2.1

Secure, high-performance protocol core with backpressure control, structured logging, timeout handling, TLS support, and comprehensive benchmarking for robust Rust networked applications and services.
Documentation
//! # Buffer Pool
//!
//! Object pool for frequently allocated small buffers (<4KB) to reduce allocation overhead
//! in high-throughput scenarios.
//!
//! ## Performance
//! - 3-5% latency reduction under high load
//! - Eliminates allocator contention for small buffers
//! - Thread-safe with minimal lock contention
//!
//! ## Usage
//! ```rust,no_run
//! use network_protocol::utils::buffer_pool::BufferPool;
//!
//! let pool = BufferPool::new(100); // 100 buffers in pool
//! let mut buffer = pool.acquire();
//! // Use buffer...
//! // Buffer automatically returned to pool on drop
//! ```

use std::sync::{Arc, Mutex};

/// Maximum buffer size for pooling (4KB)
const MAX_POOLED_BUFFER_SIZE: usize = 4096;

/// Default buffer capacity
const DEFAULT_BUFFER_CAPACITY: usize = 1024;

/// A pooled buffer that returns itself to the pool when dropped
pub struct PooledBuffer {
    buffer: Vec<u8>,
    pool: Arc<Mutex<Vec<Vec<u8>>>>,
}

impl PooledBuffer {
    /// Get a mutable reference to the underlying buffer
    #[allow(clippy::should_implement_trait)]
    pub fn as_mut(&mut self) -> &mut Vec<u8> {
        &mut self.buffer
    }

    /// Get an immutable reference to the underlying buffer
    #[allow(clippy::should_implement_trait)]
    pub fn as_ref(&self) -> &[u8] {
        &self.buffer
    }

    /// Get the underlying buffer, consuming this wrapper
    pub fn into_inner(mut self) -> Vec<u8> {
        // Clear before returning to prevent accidental reuse
        self.buffer.clear();
        std::mem::take(&mut self.buffer)
    }
}

impl Drop for PooledBuffer {
    fn drop(&mut self) {
        // Return buffer to pool if it's not too large
        if self.buffer.capacity() <= MAX_POOLED_BUFFER_SIZE {
            self.buffer.clear(); // Clear data but keep capacity
            if let Ok(mut pool) = self.pool.lock() {
                pool.push(std::mem::take(&mut self.buffer));
            }
        }
        // Otherwise, let it be deallocated
    }
}

impl std::ops::Deref for PooledBuffer {
    type Target = Vec<u8>;

    fn deref(&self) -> &Self::Target {
        &self.buffer
    }
}

impl std::ops::DerefMut for PooledBuffer {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.buffer
    }
}

/// Thread-safe buffer pool for small allocations
pub struct BufferPool {
    pool: Arc<Mutex<Vec<Vec<u8>>>>,
    initial_capacity: usize,
}

impl BufferPool {
    /// Create a new buffer pool with specified initial pool size
    pub fn new(pool_size: usize) -> Self {
        let mut pool = Vec::with_capacity(pool_size);

        // Pre-allocate buffers
        for _ in 0..pool_size {
            pool.push(Vec::with_capacity(DEFAULT_BUFFER_CAPACITY));
        }

        Self {
            pool: Arc::new(Mutex::new(pool)),
            initial_capacity: DEFAULT_BUFFER_CAPACITY,
        }
    }

    /// Acquire a buffer from the pool (or allocate a new one if pool is empty)
    pub fn acquire(&self) -> PooledBuffer {
        let buffer = if let Ok(mut pool) = self.pool.lock() {
            pool.pop()
                .unwrap_or_else(|| Vec::with_capacity(self.initial_capacity))
        } else {
            Vec::with_capacity(self.initial_capacity)
        };

        PooledBuffer {
            buffer,
            pool: self.pool.clone(),
        }
    }

    /// Get the current number of available buffers in the pool
    pub fn available(&self) -> usize {
        self.pool.lock().map(|p| p.len()).unwrap_or(0)
    }
}

impl Default for BufferPool {
    fn default() -> Self {
        Self::new(50) // Default: 50 buffers
    }
}

impl Clone for BufferPool {
    fn clone(&self) -> Self {
        Self {
            pool: self.pool.clone(),
            initial_capacity: self.initial_capacity,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_buffer_pool_basic() {
        let pool = BufferPool::new(10);
        assert_eq!(pool.available(), 10);

        let mut buf = pool.acquire();
        assert_eq!(pool.available(), 9);

        buf.push(42);
        assert_eq!(buf[0], 42);

        drop(buf);
        assert_eq!(pool.available(), 10);
    }

    #[test]
    fn test_buffer_pool_reuse() {
        let pool = BufferPool::new(1);

        {
            let mut buf1 = pool.acquire();
            buf1.extend_from_slice(b"test");
            assert_eq!(buf1.len(), 4);
        }

        // Buffer should be returned and cleared
        let buf2 = pool.acquire();
        assert_eq!(buf2.len(), 0);
        assert!(buf2.capacity() >= 4);
    }

    #[test]
    fn test_buffer_pool_empty() {
        let pool = BufferPool::new(1);
        let _buf1 = pool.acquire();
        let _buf2 = pool.acquire(); // Should allocate new

        // Both should work fine
        assert_eq!(pool.available(), 0);
    }

    #[test]
    fn test_buffer_size_limit() {
        let pool = BufferPool::new(1);

        {
            let mut buf = pool.acquire();
            // Make buffer larger than limit
            buf.reserve(MAX_POOLED_BUFFER_SIZE + 1);
            buf.extend_from_slice(&vec![0u8; MAX_POOLED_BUFFER_SIZE + 1]);
        }

        // Large buffer should not be returned to pool
        assert_eq!(pool.available(), 0);
    }
}