Skip to main content

pjson_rs/parser/
buffer_pool.rs

1//! Buffer pool system for zero-copy parsing with memory management
2//!
3//! This module provides a memory pool system to minimize allocations during
4//! JSON parsing, with support for different buffer sizes and reuse strategies.
5
6use crate::{
7    config::SecurityConfig,
8    domain::{DomainError, DomainResult},
9    parser::aligned_alloc::aligned_allocator,
10    security::SecurityValidator,
11};
12use dashmap::DashMap;
13use std::{
14    alloc::Layout,
15    mem,
16    ptr::{self, NonNull},
17    slice,
18    sync::Arc,
19    time::{Duration, Instant},
20};
21
22/// Buffer pool that manages reusable byte buffers for parsing
23#[derive(Debug)]
24pub struct BufferPool {
25    pools: Arc<DashMap<BufferSize, BufferBucket>>,
26    config: PoolConfig,
27    stats: Arc<parking_lot::Mutex<PoolStats>>, // Keep stats under mutex as it's written less frequently
28}
29
30/// Configuration for buffer pool behavior
31#[derive(Debug, Clone)]
32pub struct PoolConfig {
33    /// Maximum number of buffers per size bucket
34    pub max_buffers_per_bucket: usize,
35    /// Maximum total memory usage in bytes
36    pub max_total_memory: usize,
37    /// How long to keep unused buffers before cleanup
38    pub buffer_ttl: Duration,
39    /// Enable/disable pool statistics tracking
40    pub track_stats: bool,
41    /// Alignment for SIMD operations (typically 32 or 64 bytes)
42    pub simd_alignment: usize,
43    /// Security validator for buffer validation
44    pub validator: SecurityValidator,
45}
46
47/// Standard buffer sizes for different parsing scenarios
48#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
49pub enum BufferSize {
50    /// Small buffers for short JSON strings (1KB)
51    Small = 1024,
52    /// Medium buffers for typical API responses (8KB)  
53    Medium = 8192,
54    /// Large buffers for complex documents (64KB)
55    Large = 65536,
56    /// Extra large buffers for bulk data (512KB)
57    XLarge = 524288,
58    /// Huge buffers for massive documents (4MB)
59    Huge = 4194304,
60}
61
62/// A bucket containing buffers of the same size
63#[derive(Debug)]
64struct BufferBucket {
65    buffers: Vec<AlignedBuffer>,
66    last_access: Instant,
67}
68
69/// SIMD-aligned buffer with metadata
70///
71/// This buffer guarantees proper alignment for SIMD operations using direct memory allocation.
72/// It supports SSE (16-byte), AVX2 (32-byte), and AVX-512 (64-byte) alignments.
73pub struct AlignedBuffer {
74    /// Raw pointer to aligned memory
75    ptr: NonNull<u8>,
76    /// Current length of valid data
77    len: usize,
78    /// Total capacity in bytes
79    capacity: usize,
80    /// Memory alignment requirement
81    alignment: usize,
82    /// Layout used for allocation (needed for deallocation)
83    layout: Layout,
84    /// Creation timestamp
85    created_at: Instant,
86    /// Last usage timestamp
87    last_used: Instant,
88}
89
90// Safety: AlignedBuffer can be safely sent between threads
91unsafe impl Send for AlignedBuffer {}
92
93// Safety: AlignedBuffer can be safely shared between threads (no interior mutability)
94unsafe impl Sync for AlignedBuffer {}
95
96impl std::fmt::Debug for AlignedBuffer {
97    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
98        f.debug_struct("AlignedBuffer")
99            .field("ptr", &format_args!("0x{:x}", self.ptr.as_ptr() as usize))
100            .field("len", &self.len)
101            .field("capacity", &self.capacity)
102            .field("alignment", &self.alignment)
103            .field("is_aligned", &self.is_aligned())
104            .field("created_at", &self.created_at)
105            .field("last_used", &self.last_used)
106            .finish()
107    }
108}
109
110/// Statistics about buffer pool usage
111#[derive(Debug, Clone)]
112pub struct PoolStats {
113    /// Total allocations requested
114    pub total_allocations: u64,
115    /// Cache hits (buffer reused)
116    pub cache_hits: u64,
117    /// Cache misses (new buffer allocated)
118    pub cache_misses: u64,
119    /// Current memory usage in bytes
120    pub current_memory_usage: usize,
121    /// Peak memory usage in bytes
122    pub peak_memory_usage: usize,
123    /// Number of cleanup operations performed
124    pub cleanup_count: u64,
125}
126
127impl BufferPool {
128    /// Create new buffer pool with default configuration
129    pub fn new() -> Self {
130        Self::with_config(PoolConfig::default())
131    }
132
133    /// Create buffer pool with custom configuration
134    pub fn with_config(config: PoolConfig) -> Self {
135        Self {
136            pools: Arc::new(DashMap::new()),
137            config,
138            stats: Arc::new(parking_lot::Mutex::new(PoolStats::new())),
139        }
140    }
141
142    /// Create buffer pool with security configuration
143    pub fn with_security_config(security_config: SecurityConfig) -> Self {
144        Self::with_config(PoolConfig::from(&security_config))
145    }
146
147    /// Acquire a buffer of specified size, reusing if available
148    pub fn acquire(&self, size: BufferSize) -> DomainResult<PooledBuffer> {
149        // Security validation: check buffer size
150        self.config
151            .validator
152            .validate_buffer_size(size as usize)
153            .map_err(|e| DomainError::SecurityViolation(e.to_string()))?;
154
155        // Check if we would exceed total memory limit
156        let current_usage = self.current_memory_usage().unwrap_or(0);
157        if current_usage + (size as usize) > self.config.max_total_memory {
158            return Err(DomainError::ResourceExhausted(format!(
159                "Adding buffer of size {} would exceed memory limit: current={}, limit={}",
160                size as usize, current_usage, self.config.max_total_memory
161            )));
162        }
163
164        if self.config.track_stats {
165            self.increment_allocations();
166        }
167
168        // Try to get a buffer from existing bucket
169        if let Some(mut bucket_ref) = self.pools.get_mut(&size)
170            && let Some(mut buffer) = bucket_ref.buffers.pop()
171        {
172            buffer.last_used = Instant::now();
173            bucket_ref.last_access = Instant::now();
174
175            if self.config.track_stats {
176                self.increment_cache_hits();
177            }
178
179            return Ok(PooledBuffer::new(
180                buffer,
181                Arc::clone(&self.pools),
182                size,
183                self.config.max_buffers_per_bucket,
184            ));
185        }
186
187        // No buffer available, create new one
188        if self.config.track_stats {
189            self.increment_cache_misses();
190        }
191
192        let buffer = AlignedBuffer::new(size as usize, self.config.simd_alignment)?;
193        Ok(PooledBuffer::new(
194            buffer,
195            Arc::clone(&self.pools),
196            size,
197            self.config.max_buffers_per_bucket,
198        ))
199    }
200
201    /// Acquire a buffer with at least the specified capacity
202    pub fn acquire_with_capacity(&self, min_capacity: usize) -> DomainResult<PooledBuffer> {
203        let size = BufferSize::for_capacity(min_capacity);
204        self.acquire(size)
205    }
206
207    /// Perform cleanup of old unused buffers
208    pub fn cleanup(&self) -> DomainResult<CleanupStats> {
209        let now = Instant::now();
210        let mut freed_buffers = 0;
211        let mut freed_memory = 0;
212
213        // DashMap doesn't have retain, so we collect keys to remove
214        let mut keys_to_remove = Vec::new();
215
216        for mut entry in self.pools.iter_mut() {
217            let bucket = entry.value_mut();
218            let old_count = bucket.buffers.len();
219
220            bucket.buffers.retain(|buffer| {
221                let age = now.duration_since(buffer.last_used);
222                if age > self.config.buffer_ttl {
223                    freed_memory += buffer.capacity;
224                    false
225                } else {
226                    true
227                }
228            });
229
230            freed_buffers += old_count - bucket.buffers.len();
231
232            // Mark bucket for removal if empty and not recently accessed
233            if bucket.buffers.is_empty()
234                && now.duration_since(bucket.last_access) >= self.config.buffer_ttl
235            {
236                keys_to_remove.push(*entry.key());
237            }
238        }
239
240        // Remove empty buckets
241        for key in keys_to_remove {
242            self.pools.remove(&key);
243        }
244
245        if self.config.track_stats {
246            self.increment_cleanup_count();
247            self.update_current_memory_usage(-(freed_memory as i64));
248        }
249
250        Ok(CleanupStats {
251            freed_buffers,
252            freed_memory,
253        })
254    }
255
256    /// Get current pool statistics
257    pub fn stats(&self) -> DomainResult<PoolStats> {
258        let stats = self.stats.lock();
259        Ok(stats.clone())
260    }
261
262    /// Get current memory usage across all pools
263    pub fn current_memory_usage(&self) -> DomainResult<usize> {
264        use rayon::prelude::*;
265
266        let usage = self
267            .pools
268            .iter()
269            .par_bridge()
270            .map(|entry| {
271                entry
272                    .value()
273                    .buffers
274                    .par_iter()
275                    .map(|b| b.capacity)
276                    .sum::<usize>()
277            })
278            .sum();
279
280        Ok(usage)
281    }
282
283    // Private statistics methods
284
285    fn increment_allocations(&self) {
286        let mut stats = self.stats.lock();
287        stats.total_allocations += 1;
288    }
289
290    fn increment_cache_hits(&self) {
291        let mut stats = self.stats.lock();
292        stats.cache_hits += 1;
293    }
294
295    fn increment_cache_misses(&self) {
296        let mut stats = self.stats.lock();
297        stats.cache_misses += 1;
298    }
299
300    fn increment_cleanup_count(&self) {
301        let mut stats = self.stats.lock();
302        stats.cleanup_count += 1;
303    }
304
305    fn update_current_memory_usage(&self, delta: i64) {
306        let mut stats = self.stats.lock();
307        stats.current_memory_usage = (stats.current_memory_usage as i64 + delta).max(0) as usize;
308        stats.peak_memory_usage = stats.peak_memory_usage.max(stats.current_memory_usage);
309    }
310}
311
312impl BufferSize {
313    /// Get appropriate buffer size for given capacity
314    pub fn for_capacity(capacity: usize) -> Self {
315        match capacity {
316            0..=1024 => BufferSize::Small,
317            1025..=8192 => BufferSize::Medium,
318            8193..=65536 => BufferSize::Large,
319            65537..=524288 => BufferSize::XLarge,
320            _ => BufferSize::Huge,
321        }
322    }
323
324    /// Get all available buffer sizes in order
325    pub fn all_sizes() -> &'static [BufferSize] {
326        &[
327            BufferSize::Small,
328            BufferSize::Medium,
329            BufferSize::Large,
330            BufferSize::XLarge,
331            BufferSize::Huge,
332        ]
333    }
334}
335
336impl AlignedBuffer {
337    /// Create new aligned buffer with guaranteed SIMD alignment
338    ///
339    /// # Arguments
340    /// * `capacity` - Minimum capacity in bytes
341    /// * `alignment` - Required alignment (must be power of 2)
342    ///
343    /// # Safety
344    /// This function uses unsafe code to allocate aligned memory.
345    /// The memory is properly tracked and will be deallocated on drop.
346    pub fn new(capacity: usize, alignment: usize) -> DomainResult<Self> {
347        // Validate alignment is power of 2 and reasonable
348        if !alignment.is_power_of_two() {
349            return Err(DomainError::InvalidInput(format!(
350                "Alignment {} is not a power of 2",
351                alignment
352            )));
353        }
354
355        // Validate alignment is not too large (max 4096 bytes for page alignment)
356        if alignment > 4096 {
357            return Err(DomainError::InvalidInput(format!(
358                "Alignment {} exceeds maximum of 4096",
359                alignment
360            )));
361        }
362
363        // Minimum alignment should be at least size of usize for proper alignment
364        let alignment = alignment.max(mem::align_of::<usize>());
365
366        // Align capacity to SIMD boundaries. `capacity + alignment - 1` can overflow `usize`
367        // when `capacity` is near `usize::MAX`; wrapping would silently mask `aligned_capacity`
368        // down to a small value instead of surfacing the out-of-range request as an error.
369        let rounded = capacity.checked_add(alignment - 1).ok_or_else(|| {
370            DomainError::InvalidInput(format!(
371                "Capacity {} overflows when rounding to alignment {}",
372                capacity, alignment
373            ))
374        })?;
375        let aligned_capacity = rounded & !(alignment - 1);
376
377        // Ensure minimum capacity for safety
378        let aligned_capacity = aligned_capacity.max(alignment);
379
380        // Create layout for allocation (kept for Drop implementation)
381        let layout = Layout::from_size_align(aligned_capacity, alignment).map_err(|e| {
382            DomainError::InvalidInput(format!(
383                "Invalid layout: capacity={}, alignment={}, error={}",
384                aligned_capacity, alignment, e
385            ))
386        })?;
387
388        // Use global SIMD allocator for better performance
389        let allocator = aligned_allocator();
390
391        // Allocate aligned memory using the appropriate allocator backend
392        // Safety: alignment has been validated above
393        let ptr = unsafe { allocator.alloc_aligned(aligned_capacity, alignment)? };
394
395        let now = Instant::now();
396        Ok(Self {
397            ptr,
398            len: 0,
399            capacity: aligned_capacity,
400            alignment,
401            layout,
402            created_at: now,
403            last_used: now,
404        })
405    }
406
407    /// Create an aligned buffer with specific SIMD level
408    pub fn new_sse(capacity: usize) -> DomainResult<Self> {
409        Self::new(capacity, 16) // SSE requires 16-byte alignment
410    }
411
412    /// Create an aligned buffer for AVX2 operations
413    pub fn new_avx2(capacity: usize) -> DomainResult<Self> {
414        Self::new(capacity, 32) // AVX2 requires 32-byte alignment
415    }
416
417    /// Create an aligned buffer for AVX-512 operations
418    pub fn new_avx512(capacity: usize) -> DomainResult<Self> {
419        Self::new(capacity, 64) // AVX-512 requires 64-byte alignment
420    }
421
422    /// Get mutable slice to buffer data
423    pub fn as_mut_slice(&mut self) -> &mut [u8] {
424        // SAFETY: `self.ptr` was allocated via `AlignedAllocator::alloc_aligned` and remains
425        // valid for at least `self.capacity` bytes. `self.len <= self.capacity` is a class
426        // invariant upheld by every method that modifies `len`. The `&mut self` receiver
427        // ensures exclusive access for the lifetime of the returned slice.
428        unsafe { slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len) }
429    }
430
431    /// Get immutable slice to buffer data
432    pub fn as_slice(&self) -> &[u8] {
433        // SAFETY: `self.ptr` was allocated via `AlignedAllocator::alloc_aligned` and remains
434        // valid for at least `self.capacity` bytes. `self.len <= self.capacity` is a class
435        // invariant upheld by every method that modifies `len`. The `&self` receiver ensures
436        // no mutable aliasing exists for the lifetime of the returned slice.
437        unsafe { slice::from_raw_parts(self.ptr.as_ptr(), self.len) }
438    }
439
440    /// Get a mutable slice with full capacity
441    pub fn as_mut_capacity_slice(&mut self) -> &mut [u8] {
442        // SAFETY: `self.ptr` was allocated via `AlignedAllocator::alloc_aligned` for exactly
443        // `self.capacity` bytes. The `&mut self` receiver ensures exclusive access for the
444        // lifetime of the returned slice. Callers are responsible for initializing bytes
445        // before reading them; `set_len` is `unsafe` and documents that requirement.
446        unsafe { slice::from_raw_parts_mut(self.ptr.as_ptr(), self.capacity) }
447    }
448
449    /// Set the length of valid data
450    ///
451    /// # Safety
452    /// Caller must ensure that `new_len` bytes are initialized
453    pub unsafe fn set_len(&mut self, new_len: usize) {
454        debug_assert!(
455            new_len <= self.capacity,
456            "new_len {} exceeds capacity {}",
457            new_len,
458            self.capacity
459        );
460        self.len = new_len;
461        self.last_used = Instant::now();
462    }
463
464    /// Reserve additional capacity
465    pub fn reserve(&mut self, additional: usize) -> DomainResult<()> {
466        let new_capacity = self
467            .len
468            .checked_add(additional)
469            .ok_or_else(|| DomainError::InvalidInput("Capacity overflow".to_string()))?;
470
471        if new_capacity <= self.capacity {
472            return Ok(());
473        }
474
475        // Align new capacity. `new_capacity + self.alignment - 1` can overflow `usize` when
476        // `new_capacity` is near `usize::MAX`; wrapping would silently mask `aligned_capacity`
477        // down to a small (possibly zero) value that would then slip past the `isize::MAX`
478        // check below despite the request being out of range.
479        let rounded = new_capacity
480            .checked_add(self.alignment - 1)
481            .ok_or_else(|| {
482                DomainError::InvalidInput(format!(
483                    "Capacity {} overflows when rounding to alignment {}",
484                    new_capacity, self.alignment
485                ))
486            })?;
487        let aligned_capacity = rounded & !(self.alignment - 1);
488
489        // `realloc` requires the rounded size to be greater than zero and to not overflow
490        // `isize::MAX`; reject here so this safe method can never pass an out-of-contract
491        // size to the unsafe allocator call.
492        if aligned_capacity == 0 || aligned_capacity > isize::MAX as usize {
493            return Err(DomainError::InvalidInput(format!(
494                "Aligned capacity {} is invalid (zero or exceeds isize::MAX)",
495                aligned_capacity
496            )));
497        }
498
499        // Build the new layout before reallocating: if this fails, we return early without
500        // having touched `self.ptr`, which `realloc_aligned` below would otherwise invalidate.
501        let new_layout = Layout::from_size_align(aligned_capacity, self.alignment)
502            .map_err(|e| DomainError::InvalidInput(format!("Invalid layout: {}", e)))?;
503
504        // Use global SIMD allocator for reallocation
505        let allocator = aligned_allocator();
506
507        // Reallocate using the allocator (which will handle data copying).
508        // SAFETY: `self.ptr` was allocated (or previously reallocated) via
509        // `AlignedAllocator::alloc_aligned` with `self.layout`. `aligned_capacity` is
510        // nonzero and does not overflow `isize::MAX` per the check above, satisfying `realloc`'s
511        // documented precondition. After this call `self.ptr` must not be used — it is
512        // replaced below.
513        let new_ptr =
514            unsafe { allocator.realloc_aligned(self.ptr, self.layout, aligned_capacity)? };
515
516        self.ptr = new_ptr;
517        self.capacity = aligned_capacity;
518        self.layout = new_layout;
519        self.last_used = Instant::now();
520
521        Ok(())
522    }
523
524    /// Push bytes to the buffer
525    pub fn extend_from_slice(&mut self, data: &[u8]) -> DomainResult<()> {
526        let required_capacity = self
527            .len
528            .checked_add(data.len())
529            .ok_or_else(|| DomainError::InvalidInput("Length overflow".to_string()))?;
530
531        if required_capacity > self.capacity {
532            self.reserve(data.len())?;
533        }
534
535        // SAFETY: `reserve` above ensures `self.capacity >= self.len + data.len()`, so
536        // `self.ptr.as_ptr().add(self.len)` is within the allocation. `data` is a valid
537        // `&[u8]` slice so its pointer is also valid for `data.len()` bytes. The destination
538        // range `[self.len, self.len + data.len())` does not overlap with `data` because
539        // `data` is an external caller-provided slice that cannot alias `self.ptr`.
540        unsafe {
541            ptr::copy_nonoverlapping(data.as_ptr(), self.ptr.as_ptr().add(self.len), data.len());
542            self.len += data.len();
543        }
544
545        self.last_used = Instant::now();
546        Ok(())
547    }
548
549    /// Clear buffer contents but keep allocated memory
550    pub fn clear(&mut self) {
551        self.len = 0;
552        self.last_used = Instant::now();
553    }
554
555    /// Get buffer capacity
556    pub fn capacity(&self) -> usize {
557        self.capacity
558    }
559
560    /// Get current length of valid data
561    pub fn len(&self) -> usize {
562        self.len
563    }
564
565    /// Check if buffer is empty
566    pub fn is_empty(&self) -> bool {
567        self.len == 0
568    }
569
570    /// Get the raw pointer to the buffer
571    pub fn as_ptr(&self) -> *const u8 {
572        self.ptr.as_ptr()
573    }
574
575    /// Get the mutable raw pointer to the buffer  
576    pub fn as_mut_ptr(&mut self) -> *mut u8 {
577        self.ptr.as_ptr()
578    }
579
580    /// Check if buffer is properly aligned
581    ///
582    /// This validates that the buffer pointer has the requested alignment,
583    /// which is critical for SIMD operations.
584    pub fn is_aligned(&self) -> bool {
585        let ptr_addr = self.ptr.as_ptr() as usize;
586        ptr_addr.is_multiple_of(self.alignment)
587    }
588
589    /// Get the actual alignment of the buffer
590    pub fn actual_alignment(&self) -> usize {
591        let ptr_addr = self.ptr.as_ptr() as usize;
592        // Find the highest power of 2 that divides the address
593        if ptr_addr == 0 {
594            return usize::MAX; // null pointer is infinitely aligned
595        }
596
597        // Use trailing zeros to find alignment
598        1 << ptr_addr.trailing_zeros()
599    }
600
601    /// Verify buffer is suitable for specific SIMD instruction set
602    pub fn is_simd_compatible(&self, simd_type: SimdType) -> bool {
603        let required_alignment = match simd_type {
604            SimdType::Sse => 16,
605            SimdType::Avx2 => 32,
606            SimdType::Avx512 => 64,
607            SimdType::Neon => 16,
608        };
609
610        self.actual_alignment() >= required_alignment
611    }
612}
613
614/// SIMD instruction set types
615#[derive(Debug, Clone, Copy, PartialEq, Eq)]
616pub enum SimdType {
617    /// SSE instructions (16-byte alignment)
618    Sse,
619    /// AVX2 instructions (32-byte alignment)  
620    Avx2,
621    /// AVX-512 instructions (64-byte alignment)
622    Avx512,
623    /// ARM NEON instructions (16-byte alignment)
624    Neon,
625}
626
627impl Drop for AlignedBuffer {
628    fn drop(&mut self) {
629        // Use the global SIMD allocator for deallocation
630        let allocator = aligned_allocator();
631
632        // Safety: We allocated this memory with the same layout
633        unsafe {
634            allocator.dealloc_aligned(self.ptr, self.layout);
635        }
636    }
637}
638
639impl Clone for AlignedBuffer {
640    fn clone(&self) -> Self {
641        // Create new buffer with same alignment and capacity
642        let mut new_buffer =
643            Self::new(self.capacity, self.alignment).expect("Failed to clone buffer");
644
645        // Copy data.
646        // SAFETY: `self.ptr` is valid for `self.len` bytes (allocation invariant).
647        // `new_buffer.ptr` is valid for `self.capacity` bytes because `Self::new` was called
648        // with `self.capacity` above. `self.len <= self.capacity` ensures the byte count fits
649        // in both ranges. The two allocations are independent heap regions, so they cannot
650        // overlap.
651        unsafe {
652            ptr::copy_nonoverlapping(self.ptr.as_ptr(), new_buffer.ptr.as_ptr(), self.len);
653            new_buffer.len = self.len;
654        }
655
656        new_buffer
657    }
658}
659
660/// RAII wrapper for pooled buffer that returns buffer to pool on drop
661pub struct PooledBuffer {
662    buffer: Option<AlignedBuffer>,
663    pool: Arc<DashMap<BufferSize, BufferBucket>>,
664    size: BufferSize,
665    max_buffers_per_bucket: usize,
666}
667
668impl PooledBuffer {
669    fn new(
670        buffer: AlignedBuffer,
671        pool: Arc<DashMap<BufferSize, BufferBucket>>,
672        size: BufferSize,
673        max_buffers_per_bucket: usize,
674    ) -> Self {
675        Self {
676            buffer: Some(buffer),
677            pool,
678            size,
679            max_buffers_per_bucket,
680        }
681    }
682
683    /// Get mutable reference to buffer
684    pub fn buffer_mut(&mut self) -> Option<&mut AlignedBuffer> {
685        self.buffer.as_mut()
686    }
687
688    /// Get immutable reference to buffer
689    pub fn buffer(&self) -> Option<&AlignedBuffer> {
690        self.buffer.as_ref()
691    }
692
693    /// Get buffer capacity
694    pub fn capacity(&self) -> usize {
695        self.buffer.as_ref().map(|b| b.capacity()).unwrap_or(0)
696    }
697
698    /// Clear buffer contents
699    pub fn clear(&mut self) {
700        if let Some(buffer) = &mut self.buffer {
701            buffer.clear();
702        }
703    }
704}
705
706impl Drop for PooledBuffer {
707    fn drop(&mut self) {
708        if let Some(mut buffer) = self.buffer.take() {
709            buffer.clear(); // Clear contents before returning to pool
710
711            // Get or create bucket for this buffer size
712            let mut bucket_ref = self.pool.entry(self.size).or_insert_with(|| BufferBucket {
713                buffers: Vec::new(),
714                last_access: Instant::now(),
715            });
716
717            // Only return to pool if we haven't exceeded the per-bucket limit
718            if bucket_ref.buffers.len() < self.max_buffers_per_bucket {
719                bucket_ref.buffers.push(buffer);
720                bucket_ref.last_access = Instant::now();
721            }
722        }
723    }
724}
725
726/// Result of cleanup operation
727#[derive(Debug, Clone)]
728pub struct CleanupStats {
729    /// Number of buffers reclaimed and returned to the system.
730    pub freed_buffers: usize,
731    /// Total memory freed by the cleanup, in bytes.
732    pub freed_memory: usize,
733}
734
735impl PoolConfig {
736    /// Create configuration from security config
737    pub fn from_security_config(security_config: &SecurityConfig) -> Self {
738        Self::from(security_config)
739    }
740
741    /// Create configuration optimized for SIMD operations
742    pub fn simd_optimized() -> Self {
743        let mut config = Self::from(&SecurityConfig::high_throughput());
744        config.simd_alignment = 64; // AVX-512 alignment
745        config
746    }
747
748    /// Create configuration for low-memory environments
749    pub fn low_memory() -> Self {
750        let mut config = Self::from(&SecurityConfig::low_memory());
751        config.track_stats = false; // Reduce overhead
752        config
753    }
754
755    /// Create configuration for development/testing
756    pub fn development() -> Self {
757        Self::from(&SecurityConfig::development())
758    }
759}
760
761impl Default for PoolConfig {
762    fn default() -> Self {
763        let security_config = SecurityConfig::default();
764        Self {
765            max_buffers_per_bucket: security_config.buffers.max_buffers_per_bucket,
766            max_total_memory: security_config.buffers.max_total_memory,
767            buffer_ttl: security_config.buffer_ttl(),
768            track_stats: true,
769            simd_alignment: 32, // AVX2 alignment
770            validator: SecurityValidator::new(security_config),
771        }
772    }
773}
774
775impl From<&SecurityConfig> for PoolConfig {
776    fn from(security_config: &SecurityConfig) -> Self {
777        Self {
778            max_buffers_per_bucket: security_config.buffers.max_buffers_per_bucket,
779            max_total_memory: security_config.buffers.max_total_memory,
780            buffer_ttl: security_config.buffer_ttl(),
781            track_stats: true,
782            simd_alignment: 32, // AVX2 alignment
783            validator: SecurityValidator::new(security_config.clone()),
784        }
785    }
786}
787
788impl PoolStats {
789    fn new() -> Self {
790        Self {
791            total_allocations: 0,
792            cache_hits: 0,
793            cache_misses: 0,
794            current_memory_usage: 0,
795            peak_memory_usage: 0,
796            cleanup_count: 0,
797        }
798    }
799
800    /// Get cache hit ratio
801    pub fn hit_ratio(&self) -> f64 {
802        if self.total_allocations == 0 {
803            0.0
804        } else {
805            self.cache_hits as f64 / self.total_allocations as f64
806        }
807    }
808
809    /// Get memory efficiency (current/peak ratio)
810    pub fn memory_efficiency(&self) -> f64 {
811        if self.peak_memory_usage == 0 {
812            1.0
813        } else {
814            self.current_memory_usage as f64 / self.peak_memory_usage as f64
815        }
816    }
817}
818
819impl Default for BufferPool {
820    fn default() -> Self {
821        Self::new()
822    }
823}
824
825/// Global buffer pool instance for convenient access
826static GLOBAL_BUFFER_POOL: std::sync::OnceLock<BufferPool> = std::sync::OnceLock::new();
827
828/// Get global buffer pool instance
829pub fn global_buffer_pool() -> &'static BufferPool {
830    GLOBAL_BUFFER_POOL.get_or_init(BufferPool::new)
831}
832
833/// Initialize global buffer pool with custom configuration
834pub fn initialize_global_buffer_pool(config: PoolConfig) -> DomainResult<()> {
835    GLOBAL_BUFFER_POOL
836        .set(BufferPool::with_config(config))
837        .map_err(|_| {
838            DomainError::InternalError("Global buffer pool already initialized".to_string())
839        })?;
840    Ok(())
841}
842
843#[cfg(test)]
844mod tests {
845    use super::*;
846
847    #[test]
848    fn test_buffer_pool_creation() {
849        let pool = BufferPool::new();
850        assert!(pool.stats().is_ok());
851    }
852
853    #[test]
854    fn test_buffer_allocation() {
855        let pool = BufferPool::new();
856        let buffer = pool.acquire(BufferSize::Medium);
857        assert!(buffer.is_ok());
858
859        let buffer = buffer.unwrap();
860        assert!(buffer.capacity() >= BufferSize::Medium as usize);
861    }
862
863    #[test]
864    fn test_buffer_reuse() {
865        let pool = BufferPool::new();
866
867        // Allocate and drop buffer
868        {
869            let _buffer = pool.acquire(BufferSize::Small).unwrap();
870        }
871
872        // Allocate another buffer of same size
873        let _buffer2 = pool.acquire(BufferSize::Small).unwrap();
874
875        // Should have cache hit
876        let stats = pool.stats().unwrap();
877        assert!(stats.cache_hits > 0);
878    }
879
880    #[test]
881    fn test_buffer_size_selection() {
882        assert_eq!(BufferSize::for_capacity(500), BufferSize::Small);
883        assert_eq!(BufferSize::for_capacity(2000), BufferSize::Medium);
884        assert_eq!(BufferSize::for_capacity(50000), BufferSize::Large);
885        assert_eq!(BufferSize::for_capacity(100000), BufferSize::XLarge);
886    }
887
888    #[test]
889    fn test_aligned_buffer_creation_guaranteed() {
890        // Test all common SIMD alignments
891        let test_cases = vec![
892            (1024, 16, "SSE alignment"),
893            (2048, 32, "AVX2 alignment"),
894            (4096, 64, "AVX-512 alignment"),
895        ];
896
897        for (capacity, alignment, description) in test_cases {
898            let buffer = AlignedBuffer::new(capacity, alignment).unwrap();
899
900            // Verify pointer alignment
901            let ptr_addr = buffer.as_ptr() as usize;
902            assert_eq!(
903                ptr_addr % alignment,
904                0,
905                "{}: pointer 0x{:x} is not {}-byte aligned",
906                description,
907                ptr_addr,
908                alignment
909            );
910
911            // Verify is_aligned method
912            assert!(
913                buffer.is_aligned(),
914                "{}: is_aligned() returned false for properly aligned buffer",
915                description
916            );
917
918            // Verify capacity
919            assert!(
920                buffer.capacity() >= capacity,
921                "{}: capacity {} is less than requested {}",
922                description,
923                buffer.capacity(),
924                capacity
925            );
926
927            // Verify actual alignment
928            assert!(
929                buffer.actual_alignment() >= alignment,
930                "{}: actual alignment {} is less than requested {}",
931                description,
932                buffer.actual_alignment(),
933                alignment
934            );
935        }
936    }
937
938    #[test]
939    fn test_buffer_operations() {
940        let mut buffer = AlignedBuffer::new(1024, 32).unwrap();
941
942        // Test initial state
943        assert_eq!(buffer.len(), 0);
944        assert!(buffer.is_empty());
945        assert_eq!(buffer.capacity(), 1024);
946
947        // Test extend_from_slice
948        let data = b"Hello, SIMD World!";
949        buffer.extend_from_slice(data).unwrap();
950        assert_eq!(buffer.len(), data.len());
951        assert_eq!(buffer.as_slice(), data);
952
953        // Test clear
954        buffer.clear();
955        assert_eq!(buffer.len(), 0);
956        assert!(buffer.is_empty());
957        assert_eq!(buffer.capacity(), 1024); // Capacity should remain
958
959        // Test unsafe set_len
960        unsafe {
961            // Write some data directly
962            let slice = buffer.as_mut_capacity_slice();
963            slice[0..5].copy_from_slice(b"SIMD!");
964            buffer.set_len(5);
965        }
966        assert_eq!(buffer.len(), 5);
967        assert_eq!(&buffer.as_slice()[0..5], b"SIMD!");
968    }
969
970    #[test]
971    fn test_buffer_reserve() {
972        let mut buffer = AlignedBuffer::new(64, 32).unwrap();
973        let _initial_alignment = buffer.actual_alignment();
974
975        // Set some length first
976        unsafe {
977            buffer.set_len(32);
978        }
979
980        // Reserve additional space - should need capacity for len + additional
981        buffer.reserve(256).unwrap();
982        assert!(
983            buffer.capacity() >= 32 + 256,
984            "Expected capacity >= {}, got {}",
985            32 + 256,
986            buffer.capacity()
987        );
988
989        // Alignment should be preserved after reallocation
990        assert!(
991            buffer.actual_alignment() >= 32,
992            "Alignment not preserved after reserve"
993        );
994        assert!(buffer.is_aligned());
995
996        // Test that data is preserved during reallocation
997        buffer.extend_from_slice(b"test data").unwrap();
998        let old_data = buffer.as_slice().to_vec();
999
1000        buffer.reserve(1024).unwrap();
1001        assert_eq!(buffer.as_slice(), &old_data[..]);
1002    }
1003
1004    #[test]
1005    fn test_reserve_rejects_capacity_exceeding_isize_max() {
1006        // `additional` is comfortably past isize::MAX while still passing the
1007        // `len.checked_add(additional)` overflow check.
1008        let mut buffer = AlignedBuffer::new(64, 32).unwrap();
1009
1010        let result = buffer.reserve(usize::MAX - 1000);
1011
1012        match result {
1013            Err(DomainError::InvalidInput(msg)) => {
1014                assert!(
1015                    msg.contains("isize::MAX"),
1016                    "unexpected error message: {}",
1017                    msg
1018                );
1019            }
1020            other => panic!("expected InvalidInput error, got {:?}", other),
1021        }
1022
1023        // Buffer must remain usable after the rejected reserve.
1024        assert_eq!(buffer.capacity(), 64);
1025        buffer.extend_from_slice(b"still usable").unwrap();
1026        assert_eq!(buffer.as_slice(), b"still usable");
1027    }
1028
1029    #[test]
1030    fn test_reserve_rejects_alignment_rounding_past_isize_max() {
1031        // `new_capacity == isize::MAX` passes the raw `new_capacity <= isize::MAX`
1032        // check, but rounding it up to the next 32-byte alignment boundary produces a
1033        // value that still overflows past isize::MAX (without wrapping `usize` itself,
1034        // since `isize::MAX + 31` fits comfortably in `usize`).
1035        let mut buffer = AlignedBuffer::new(64, 32).unwrap();
1036
1037        let result = buffer.reserve(isize::MAX as usize);
1038
1039        assert!(
1040            matches!(result, Err(DomainError::InvalidInput(_))),
1041            "expected InvalidInput error due to alignment rounding overflow, got {:?}",
1042            result
1043        );
1044    }
1045
1046    #[test]
1047    fn test_reserve_rejects_usize_wraparound_in_alignment_rounding() {
1048        // `new_capacity == usize::MAX` makes `new_capacity + (alignment - 1)` overflow
1049        // `usize` and wrap around to a small value; masking that wrapped value down to
1050        // the alignment boundary can produce 0, which would silently pass a naive
1051        // `aligned_capacity > isize::MAX` check and reach `realloc` with `new_size == 0` —
1052        // itself undefined behavior. The checked addition must reject this before rounding.
1053        let mut buffer = AlignedBuffer::new(64, 32).unwrap();
1054
1055        let result = buffer.reserve(usize::MAX);
1056
1057        assert!(
1058            matches!(result, Err(DomainError::InvalidInput(_))),
1059            "expected InvalidInput error due to usize wraparound in alignment rounding, got {:?}",
1060            result
1061        );
1062
1063        // Buffer must remain usable after the rejected reserve.
1064        assert_eq!(buffer.capacity(), 64);
1065        buffer.extend_from_slice(b"still usable").unwrap();
1066        assert_eq!(buffer.as_slice(), b"still usable");
1067    }
1068
1069    #[test]
1070    fn test_reserve_rejects_capacity_at_wraparound_boundary() {
1071        // With `alignment == 32`, the smallest `new_capacity` for which
1072        // `new_capacity + (alignment - 1)` overflows `usize` is `usize::MAX - 30`
1073        // (i.e. `usize::MAX - alignment + 2`). Verify the checked addition rejects
1074        // exactly at this boundary, not just for deep overflow like `usize::MAX`.
1075        let mut buffer = AlignedBuffer::new(64, 32).unwrap();
1076        let boundary_additional = usize::MAX - 30;
1077
1078        let result = buffer.reserve(boundary_additional);
1079
1080        match result {
1081            Err(DomainError::InvalidInput(msg)) => {
1082                assert!(
1083                    msg.contains("overflows"),
1084                    "expected the checked-add overflow guard's message, got: {}",
1085                    msg
1086                );
1087            }
1088            other => panic!("expected InvalidInput error, got {:?}", other),
1089        }
1090
1091        // One less than the boundary must not overflow `usize` during rounding; it is
1092        // still rejected, but via the isize::MAX bound rather than the overflow guard —
1093        // assert the distinct message to prove the two guards were exercised separately.
1094        let result = buffer.reserve(boundary_additional - 1);
1095        match result {
1096            Err(DomainError::InvalidInput(msg)) => {
1097                assert!(
1098                    msg.contains("isize::MAX"),
1099                    "expected the isize::MAX bound guard's message, got: {}",
1100                    msg
1101                );
1102            }
1103            other => panic!("expected InvalidInput error, got {:?}", other),
1104        }
1105    }
1106
1107    #[test]
1108    fn test_new_rejects_capacity_overflowing_alignment_rounding() {
1109        // `capacity == usize::MAX` makes `capacity + (alignment - 1)` overflow `usize`
1110        // in `AlignedBuffer::new`'s rounding step; this must be rejected rather than
1111        // silently wrapping to a small aligned capacity.
1112        let result = AlignedBuffer::new(usize::MAX, 64);
1113
1114        assert!(
1115            matches!(result, Err(DomainError::InvalidInput(_))),
1116            "expected InvalidInput error due to usize wraparound in alignment rounding, got {:?}",
1117            result
1118        );
1119    }
1120
1121    #[test]
1122    fn test_new_zero_capacity_rounds_up_to_alignment() {
1123        // `capacity == 0` is the ordinary "give me at least one alignment's worth" case,
1124        // not an overflow: `checked_add` succeeds trivially, and `.max(alignment)` clamps
1125        // the rounded-to-zero result up to `alignment` bytes rather than erroring.
1126        let buffer = AlignedBuffer::new(0, 64).unwrap();
1127        assert_eq!(buffer.capacity(), 64);
1128    }
1129
1130    #[test]
1131    fn test_buffer_clone() {
1132        let mut original = AlignedBuffer::new(512, 64).unwrap();
1133        original.extend_from_slice(b"Original data").unwrap();
1134
1135        let cloned = original.clone();
1136
1137        // Verify clone has same properties
1138        assert_eq!(cloned.len(), original.len());
1139        assert_eq!(cloned.capacity(), original.capacity());
1140        assert_eq!(cloned.alignment, original.alignment);
1141        assert_eq!(cloned.as_slice(), original.as_slice());
1142
1143        // Verify clone has different memory location
1144        assert_ne!(cloned.as_ptr(), original.as_ptr());
1145
1146        // Verify clone is also properly aligned
1147        assert!(cloned.is_aligned());
1148        assert!(cloned.actual_alignment() >= 64);
1149    }
1150
1151    #[test]
1152    fn test_alignment_validation() {
1153        // Test valid power-of-2 alignments
1154        let valid_alignments = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096];
1155
1156        for &alignment in &valid_alignments {
1157            let result = AlignedBuffer::new(1024, alignment);
1158            assert!(result.is_ok(), "Alignment {} should be valid", alignment);
1159
1160            let buffer = result.unwrap();
1161            assert!(
1162                buffer.is_aligned(),
1163                "Buffer with alignment {} should be aligned",
1164                alignment
1165            );
1166        }
1167
1168        // Test invalid non-power-of-2 alignments
1169        let invalid_alignments = [3, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15, 17, 31, 33, 63, 65];
1170
1171        for &alignment in &invalid_alignments {
1172            let result = AlignedBuffer::new(1024, alignment);
1173            assert!(result.is_err(), "Alignment {} should be invalid", alignment);
1174        }
1175
1176        // Test too large alignment
1177        assert!(AlignedBuffer::new(1024, 8192).is_err());
1178    }
1179
1180    #[test]
1181    fn test_actual_alignment_calculation() {
1182        // Create buffers with different alignments and verify actual_alignment()
1183        for &requested_align in &[16, 32, 64] {
1184            let buffer = AlignedBuffer::new(1024, requested_align).unwrap();
1185            let actual = buffer.actual_alignment();
1186
1187            assert!(
1188                actual >= requested_align,
1189                "Actual alignment {} is less than requested {}",
1190                actual,
1191                requested_align
1192            );
1193
1194            // actual_alignment should be a power of 2
1195            assert!(
1196                actual.is_power_of_two(),
1197                "Actual alignment {} is not a power of 2",
1198                actual
1199            );
1200        }
1201    }
1202
1203    #[test]
1204    fn test_simd_compatibility_check() {
1205        // SSE buffer should be compatible with SSE but might not be with AVX-512
1206        let sse_buffer = AlignedBuffer::new_sse(1024).unwrap();
1207        assert!(sse_buffer.is_simd_compatible(SimdType::Sse));
1208        assert!(sse_buffer.is_simd_compatible(SimdType::Neon)); // Same alignment as SSE
1209
1210        // AVX-512 buffer should be compatible with all instruction sets
1211        let avx512_buffer = AlignedBuffer::new_avx512(1024).unwrap();
1212        assert!(avx512_buffer.is_simd_compatible(SimdType::Sse));
1213        assert!(avx512_buffer.is_simd_compatible(SimdType::Avx2));
1214        assert!(avx512_buffer.is_simd_compatible(SimdType::Avx512));
1215        assert!(avx512_buffer.is_simd_compatible(SimdType::Neon));
1216    }
1217
1218    #[test]
1219    fn test_zero_copy_verification() {
1220        let mut buffer = AlignedBuffer::new(1024, 32).unwrap();
1221
1222        // Get raw pointer before modification
1223        let ptr_before = buffer.as_ptr();
1224
1225        // Perform various operations that should NOT move the buffer
1226        buffer.clear();
1227        buffer.extend_from_slice(b"test").unwrap();
1228        unsafe {
1229            buffer.set_len(2);
1230        }
1231
1232        // Pointer should remain the same (zero-copy)
1233        assert_eq!(
1234            ptr_before,
1235            buffer.as_ptr(),
1236            "Buffer was moved during operations (not zero-copy)"
1237        );
1238
1239        // Only reserve should potentially change the pointer
1240        buffer.reserve(2048).unwrap();
1241        // After reserve, pointer might change but should still be aligned
1242        assert!(buffer.is_aligned());
1243    }
1244
1245    #[test]
1246    fn test_pool_cleanup() {
1247        let config = PoolConfig {
1248            buffer_ttl: Duration::from_millis(1),
1249            ..Default::default()
1250        };
1251        let pool = BufferPool::with_config(config);
1252
1253        // Allocate and drop buffer
1254        {
1255            let _buffer = pool.acquire(BufferSize::Small).unwrap();
1256        }
1257
1258        // Wait for TTL
1259        std::thread::sleep(Duration::from_millis(10));
1260
1261        // Cleanup should free the buffer
1262        let cleanup_stats = pool.cleanup().unwrap();
1263        assert!(cleanup_stats.freed_buffers > 0);
1264    }
1265
1266    #[test]
1267    fn test_global_buffer_pool() {
1268        let pool = global_buffer_pool();
1269        let buffer = pool.acquire(BufferSize::Medium);
1270        assert!(buffer.is_ok());
1271    }
1272
1273    #[test]
1274    fn test_memory_limit_enforcement() {
1275        let config = PoolConfig {
1276            max_total_memory: 1024, // Very small limit
1277            max_buffers_per_bucket: 10,
1278            ..Default::default()
1279        };
1280        let pool = BufferPool::with_config(config);
1281
1282        // Create a buffer that exceeds the memory limit
1283        let result = pool.acquire(BufferSize::Medium); // 8KB > 1KB limit
1284
1285        assert!(result.is_err());
1286
1287        if let Err(e) = result {
1288            assert!(e.to_string().contains("memory limit"));
1289        }
1290    }
1291
1292    #[test]
1293    fn test_per_bucket_limit_enforcement() {
1294        let config = PoolConfig {
1295            max_buffers_per_bucket: 2,          // Very small limit
1296            max_total_memory: 10 * 1024 * 1024, // Generous memory limit
1297            ..Default::default()
1298        };
1299        let pool = BufferPool::with_config(config);
1300
1301        // Allocate and drop buffers to fill the bucket
1302        for _ in 0..3 {
1303            let _buffer = pool.acquire(BufferSize::Small).unwrap();
1304            // Buffer goes back to pool on drop
1305        }
1306
1307        // Only 2 buffers should be retained in the pool
1308        let stats = pool.stats().unwrap();
1309        assert!(stats.cache_hits <= 2, "Too many buffers retained in bucket");
1310    }
1311
1312    #[test]
1313    fn test_buffer_size_validation() {
1314        let pool = BufferPool::new();
1315
1316        // All standard buffer sizes should be valid
1317        for size in BufferSize::all_sizes() {
1318            let result = pool.acquire(*size);
1319            assert!(result.is_ok(), "Buffer size {:?} should be valid", size);
1320        }
1321    }
1322
1323    #[test]
1324    fn test_memory_safety() {
1325        // Test that dropping a buffer properly deallocates memory
1326        // This test would fail under valgrind/ASAN if there's a memory leak
1327        for _ in 0..100 {
1328            let buffer = AlignedBuffer::new(1024, 64).unwrap();
1329            drop(buffer);
1330        }
1331
1332        // Test clone and drop
1333        for _ in 0..100 {
1334            let buffer = AlignedBuffer::new(512, 32).unwrap();
1335            let cloned = buffer.clone();
1336            drop(buffer);
1337            drop(cloned);
1338        }
1339    }
1340
1341    #[test]
1342    fn test_simd_specific_constructors() {
1343        // Test SSE alignment (16 bytes)
1344        let sse_buffer = AlignedBuffer::new_sse(1024).unwrap();
1345        assert!(sse_buffer.is_aligned());
1346        assert!(sse_buffer.is_simd_compatible(SimdType::Sse));
1347        assert_eq!(sse_buffer.alignment, 16);
1348
1349        // Test AVX2 alignment (32 bytes)
1350        let avx2_buffer = AlignedBuffer::new_avx2(1024).unwrap();
1351        assert!(avx2_buffer.is_aligned());
1352        assert!(avx2_buffer.is_simd_compatible(SimdType::Avx2));
1353        assert_eq!(avx2_buffer.alignment, 32);
1354
1355        // Test AVX-512 alignment (64 bytes)
1356        let avx512_buffer = AlignedBuffer::new_avx512(1024).unwrap();
1357        assert!(avx512_buffer.is_aligned());
1358        assert!(avx512_buffer.is_simd_compatible(SimdType::Avx512));
1359        assert_eq!(avx512_buffer.alignment, 64);
1360    }
1361
1362    #[test]
1363    fn test_simd_alignment_compatibility() {
1364        let buffer_64 = AlignedBuffer::new(1024, 64).unwrap();
1365
1366        // 64-byte aligned buffer should be compatible with all SIMD types
1367        assert!(buffer_64.is_simd_compatible(SimdType::Sse)); // 16-byte requirement
1368        assert!(buffer_64.is_simd_compatible(SimdType::Avx2)); // 32-byte requirement
1369        assert!(buffer_64.is_simd_compatible(SimdType::Avx512)); // 64-byte requirement
1370        assert!(buffer_64.is_simd_compatible(SimdType::Neon)); // 16-byte requirement
1371
1372        // Note: We can't easily test incompatible alignments since the allocator
1373        // might provide better alignment than requested for performance reasons.
1374        // Instead, test the requested alignment vs required alignment directly.
1375        #[allow(clippy::assertions_on_constants)]
1376        {
1377            assert!(64 >= 16); // SSE compatible
1378            assert!(64 >= 32); // AVX2 compatible
1379            assert!(64 >= 64); // AVX512 compatible
1380            assert!(64 >= 16); // NEON compatible
1381        }
1382
1383        let buffer_16 = AlignedBuffer::new(1024, 16).unwrap();
1384
1385        // Test that buffer reports correct requested alignment
1386        assert_eq!(buffer_16.alignment, 16);
1387
1388        // 16-byte aligned buffer should be compatible with SSE and NEON
1389        assert!(buffer_16.is_simd_compatible(SimdType::Sse));
1390        assert!(buffer_16.is_simd_compatible(SimdType::Neon));
1391
1392        // Note: actual_alignment() might be higher than 16 due to allocator behavior
1393        // so we can't reliably test incompatibility. Instead verify logic:
1394        #[allow(clippy::assertions_on_constants)]
1395        {
1396            assert!(16 >= 16); // SSE requirement met
1397            assert!(16 < 32); // AVX2 requirement NOT met by requested alignment
1398            assert!(16 < 64); // AVX512 requirement NOT met by requested alignment
1399        }
1400    }
1401
1402    #[test]
1403    fn test_actual_alignment_detection() {
1404        let buffer = AlignedBuffer::new(1024, 64).unwrap();
1405
1406        let actual_alignment = buffer.actual_alignment();
1407        assert!(
1408            actual_alignment >= 64,
1409            "Buffer has actual alignment of {}, expected at least 64",
1410            actual_alignment
1411        );
1412
1413        // The actual alignment should be a power of 2 and >= requested alignment
1414        assert!(actual_alignment.is_power_of_two());
1415        assert!(actual_alignment >= buffer.alignment);
1416    }
1417
1418    #[test]
1419    fn test_simd_pool_configuration() {
1420        // Test pool with high SIMD alignment requirement
1421        let config = PoolConfig {
1422            simd_alignment: 64, // AVX-512 alignment
1423            ..Default::default()
1424        };
1425        let pool = BufferPool::with_config(config);
1426
1427        let buffer = pool.acquire(BufferSize::Medium).unwrap();
1428        assert!(buffer.buffer().unwrap().is_aligned());
1429        assert!(
1430            buffer
1431                .buffer()
1432                .unwrap()
1433                .is_simd_compatible(SimdType::Avx512)
1434        );
1435    }
1436
1437    #[test]
1438    fn test_alignment_edge_cases() {
1439        // Test minimum alignment
1440        let buffer_min = AlignedBuffer::new(64, 1).unwrap();
1441        assert!(buffer_min.is_aligned());
1442        assert!(buffer_min.alignment >= mem::align_of::<usize>());
1443
1444        // Test power-of-2 validation
1445        assert!(AlignedBuffer::new(1024, 3).is_err());
1446        assert!(AlignedBuffer::new(1024, 17).is_err());
1447        assert!(AlignedBuffer::new(1024, 33).is_err());
1448
1449        // Test maximum alignment limit
1450        assert!(AlignedBuffer::new(1024, 8192).is_err());
1451    }
1452
1453    #[test]
1454    fn test_simd_performance_oriented_allocation() {
1455        // Test that allocation pattern is suitable for high-performance SIMD
1456        let buffer = AlignedBuffer::new_avx512(4096).unwrap();
1457
1458        // Verify the buffer can be used for actual SIMD-like operations
1459        let slice = unsafe { std::slice::from_raw_parts_mut(buffer.ptr.as_ptr(), buffer.capacity) };
1460
1461        // Fill with test pattern
1462        for (i, byte) in slice.iter_mut().enumerate() {
1463            *byte = (i % 256) as u8;
1464        }
1465
1466        // Verify alignment is maintained through operations
1467        assert!(buffer.is_aligned());
1468        assert_eq!(slice[0], 0);
1469        assert_eq!(slice[255], 255);
1470        assert_eq!(slice[256], 0);
1471    }
1472}