Skip to main content

hedl_stream/
buffer_pool.rs

1// Dweve HEDL - Hierarchical Entity Data Language
2//
3// Copyright (c) 2025 Dweve IP B.V. and individual contributors.
4//
5// SPDX-License-Identifier: Apache-2.0
6//
7// Licensed under the Apache License, Version 2.0 (the "License");
8// you may not use this file except in compliance with the License.
9// You may obtain a copy of the LICENSE file at the
10// root of this repository or at: http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! Buffer pooling for memory-efficient parsing.
19//!
20//! This module provides object pooling for frequently allocated objects
21//! during parsing (strings and value vectors). Pooling reduces allocator
22//! pressure in high-throughput scenarios.
23//!
24//! # Design
25//!
26//! - **Type-specific pools**: Separate pools for String and `Vec<Value>`
27//! - **Capacity limits**: Configurable maximum pool sizes to prevent unbounded growth
28//! - **Clear-on-release**: Buffers are cleared but capacity is retained
29//! - **Lazy growth**: Pools grow on demand up to configured limits
30//!
31//! # Performance Characteristics
32//!
33//! - **Allocation elimination**: Reuses buffers instead of allocating new ones
34//! - **Reduced GC pressure**: Fewer allocations means less garbage collection
35//! - **Memory trade-off**: Holds buffers between operations (configurable)
36//!
37//! # Use Cases
38//!
39//! - **High-throughput parsing**: Many files processed in sequence
40//! - **Server workloads**: Long-running processes with continuous parsing
41//! - **Memory-constrained with high volume**: Amortize allocation cost
42
43use hedl_core::Value;
44
45/// Memory limits for buffer management.
46///
47/// Controls maximum buffer sizes, line lengths, and pool configuration
48/// to prevent unbounded memory growth and handle memory-constrained environments.
49///
50/// # Examples
51///
52/// ## Default Configuration
53///
54/// ```rust
55/// use hedl_stream::MemoryLimits;
56///
57/// let limits = MemoryLimits::default();
58/// assert_eq!(limits.max_buffer_size, 1024 * 1024);
59/// assert_eq!(limits.max_line_length, 1_000_000);
60/// assert_eq!(limits.enable_buffer_pooling, true);
61/// assert_eq!(limits.max_pool_size, 10);
62/// ```
63///
64/// ## Memory-Constrained Configuration
65///
66/// ```rust
67/// use hedl_stream::MemoryLimits;
68///
69/// let limits = MemoryLimits {
70///     max_buffer_size: 64 * 1024,       // 64KB max I/O buffer
71///     max_line_length: 100_000,         // 100KB max line
72///     enable_buffer_pooling: false,     // Disable pooling
73///     max_pool_size: 0,
74/// };
75/// ```
76///
77/// ## High-Throughput Configuration
78///
79/// ```rust
80/// use hedl_stream::MemoryLimits;
81///
82/// let limits = MemoryLimits {
83///     max_buffer_size: 2 * 1024 * 1024, // 2MB max I/O buffer
84///     max_line_length: 10_000_000,      // 10MB max line
85///     enable_buffer_pooling: true,
86///     max_pool_size: 50,                // Large pool
87/// };
88/// ```
89#[derive(Debug, Clone, Copy, PartialEq, Eq)]
90pub struct MemoryLimits {
91    /// Maximum I/O buffer size in bytes.
92    ///
93    /// Controls the size of the read buffer used by `BufReader`.
94    /// Larger buffers reduce syscall overhead but use more memory.
95    ///
96    /// Default: 1MB
97    pub max_buffer_size: usize,
98
99    /// Maximum line length in bytes.
100    ///
101    /// Lines exceeding this length cause a parsing error.
102    /// This protects against malformed input with extremely long lines.
103    ///
104    /// Default: 1,000,000 bytes (1MB)
105    pub max_line_length: usize,
106
107    /// Enable buffer pooling.
108    ///
109    /// When true, reuses string and value buffers across parsing operations.
110    /// Reduces allocation overhead in high-throughput scenarios.
111    ///
112    /// Default: true
113    pub enable_buffer_pooling: bool,
114
115    /// Maximum number of buffers to pool.
116    ///
117    /// Limits pool growth to prevent unbounded memory usage.
118    /// Only effective when `enable_buffer_pooling` is true.
119    ///
120    /// Default: 10 buffers
121    pub max_pool_size: usize,
122}
123
124impl Default for MemoryLimits {
125    fn default() -> Self {
126        Self {
127            max_buffer_size: 1024 * 1024, // 1MB max I/O buffer
128            max_line_length: 1_000_000,   // 1MB max line
129            enable_buffer_pooling: true,
130            max_pool_size: 10, // Pool up to 10 buffers
131        }
132    }
133}
134
135impl MemoryLimits {
136    /// Configuration for embedded systems or memory-constrained environments.
137    ///
138    /// Uses minimal buffer sizes and disables pooling to minimize memory footprint.
139    ///
140    /// # Examples
141    ///
142    /// ```rust
143    /// use hedl_stream::MemoryLimits;
144    ///
145    /// let limits = MemoryLimits::embedded();
146    /// assert_eq!(limits.max_buffer_size, 8 * 1024);
147    /// assert_eq!(limits.enable_buffer_pooling, false);
148    /// ```
149    #[must_use]
150    pub fn embedded() -> Self {
151        Self {
152            max_buffer_size: 8 * 1024,    // 8KB buffer
153            max_line_length: 10_000,      // 10KB max line
154            enable_buffer_pooling: false, // No pooling on embedded
155            max_pool_size: 0,
156        }
157    }
158
159    /// Configuration for large file processing with high throughput.
160    ///
161    /// Uses large buffers and extensive pooling for maximum performance.
162    ///
163    /// # Examples
164    ///
165    /// ```rust
166    /// use hedl_stream::MemoryLimits;
167    ///
168    /// let limits = MemoryLimits::high_throughput();
169    /// assert_eq!(limits.max_buffer_size, 2 * 1024 * 1024);
170    /// assert_eq!(limits.max_pool_size, 50);
171    /// ```
172    #[must_use]
173    pub fn high_throughput() -> Self {
174        Self {
175            max_buffer_size: 2 * 1024 * 1024, // 2MB buffer
176            max_line_length: 10_000_000,      // 10MB max line
177            enable_buffer_pooling: true,
178            max_pool_size: 50, // Large pool
179        }
180    }
181
182    /// Configuration for untrusted input.
183    ///
184    /// Uses conservative limits to protect against malicious input.
185    ///
186    /// # Examples
187    ///
188    /// ```rust
189    /// use hedl_stream::MemoryLimits;
190    ///
191    /// let limits = MemoryLimits::untrusted();
192    /// assert_eq!(limits.max_line_length, 100_000);
193    /// ```
194    #[must_use]
195    pub fn untrusted() -> Self {
196        Self {
197            max_buffer_size: 64 * 1024, // 64KB buffer
198            max_line_length: 100_000,   // 100KB max line
199            enable_buffer_pooling: true,
200            max_pool_size: 5, // Small pool
201        }
202    }
203}
204
205/// Buffer pool for String and `Vec<Value>` reuse.
206///
207/// Maintains pools of pre-allocated buffers to reduce allocation overhead
208/// during high-throughput parsing operations.
209///
210/// # Memory Management
211///
212/// - Buffers are cleared (content removed) when released but capacity is retained
213/// - Pool size is limited to prevent unbounded memory growth
214/// - Acquire operations fall back to fresh allocation if pool is empty
215/// - Release operations drop buffers if pool is full
216///
217/// # Thread Safety
218///
219/// This is NOT thread-safe. Each parser instance should have its own pool.
220/// For multi-threaded scenarios, use one pool per thread.
221///
222/// # Examples
223///
224/// ## Basic Usage
225///
226/// ```rust
227/// use hedl_stream::BufferPool;
228///
229/// let mut pool = BufferPool::new(10);
230///
231/// // Acquire a string buffer
232/// let mut s = pool.acquire_string();
233/// s.push_str("hello");
234///
235/// // Release it back to pool
236/// pool.release_string(s);
237///
238/// // Next acquire reuses the buffer
239/// let s2 = pool.acquire_string();
240/// assert_eq!(s2.len(), 0);  // Cleared but capacity retained
241/// ```
242///
243/// ## With Capacity Hints
244///
245/// ```rust
246/// use hedl_stream::BufferPool;
247///
248/// let mut pool = BufferPool::with_capacity_hints(10, 256, 16);
249///
250/// let s = pool.acquire_string();
251/// assert!(s.capacity() >= 256);
252///
253/// let v = pool.acquire_value_vec();
254/// assert!(v.capacity() >= 16);
255/// ```
256#[derive(Debug)]
257pub struct BufferPool {
258    string_pool: Vec<String>,
259    value_pool: Vec<Vec<Value>>,
260    max_pool_size: usize,
261    string_capacity_hint: usize,
262    value_capacity_hint: usize,
263}
264
265impl BufferPool {
266    /// Create a new buffer pool with default capacity hints.
267    ///
268    /// # Parameters
269    ///
270    /// - `max_pool_size`: Maximum number of buffers to pool per type
271    ///
272    /// # Examples
273    ///
274    /// ```rust
275    /// use hedl_stream::BufferPool;
276    ///
277    /// let pool = BufferPool::new(10);
278    /// ```
279    #[must_use]
280    pub fn new(max_pool_size: usize) -> Self {
281        Self {
282            string_pool: Vec::with_capacity(max_pool_size.min(4)),
283            value_pool: Vec::with_capacity(max_pool_size.min(4)),
284            max_pool_size,
285            string_capacity_hint: 256,
286            value_capacity_hint: 16,
287        }
288    }
289
290    /// Create a buffer pool with custom capacity hints.
291    ///
292    /// Capacity hints determine the initial capacity of newly allocated buffers
293    /// when the pool is empty.
294    ///
295    /// # Parameters
296    ///
297    /// - `max_pool_size`: Maximum buffers to pool per type
298    /// - `string_capacity_hint`: Initial capacity for String buffers
299    /// - `value_capacity_hint`: Initial capacity for `Vec<Value>` buffers
300    ///
301    /// # Examples
302    ///
303    /// ```rust
304    /// use hedl_stream::BufferPool;
305    ///
306    /// // Pool for parsing with long lines and wide rows
307    /// let pool = BufferPool::with_capacity_hints(20, 1024, 50);
308    /// ```
309    #[must_use]
310    pub fn with_capacity_hints(
311        max_pool_size: usize,
312        string_capacity_hint: usize,
313        value_capacity_hint: usize,
314    ) -> Self {
315        Self {
316            string_pool: Vec::with_capacity(max_pool_size.min(4)),
317            value_pool: Vec::with_capacity(max_pool_size.min(4)),
318            max_pool_size,
319            string_capacity_hint,
320            value_capacity_hint,
321        }
322    }
323
324    /// Acquire a String buffer from the pool.
325    ///
326    /// Returns a pooled buffer if available, otherwise allocates a new one
327    /// with the configured capacity hint.
328    ///
329    /// # Examples
330    ///
331    /// ```rust
332    /// use hedl_stream::BufferPool;
333    ///
334    /// let mut pool = BufferPool::new(10);
335    /// let mut s = pool.acquire_string();
336    /// s.push_str("data");
337    /// ```
338    #[inline]
339    pub fn acquire_string(&mut self) -> String {
340        self.string_pool
341            .pop()
342            .unwrap_or_else(|| String::with_capacity(self.string_capacity_hint))
343    }
344
345    /// Release a String buffer back to the pool.
346    ///
347    /// The buffer is cleared but retains its capacity. If the pool is full,
348    /// the buffer is dropped.
349    ///
350    /// # Examples
351    ///
352    /// ```rust
353    /// use hedl_stream::BufferPool;
354    ///
355    /// let mut pool = BufferPool::new(10);
356    /// let mut s = pool.acquire_string();
357    /// s.push_str("data");
358    /// pool.release_string(s);
359    ///
360    /// // Buffer is reused with content cleared
361    /// let s2 = pool.acquire_string();
362    /// assert_eq!(s2.len(), 0);
363    /// ```
364    #[inline]
365    pub fn release_string(&mut self, mut s: String) {
366        if self.string_pool.len() < self.max_pool_size {
367            s.clear();
368            self.string_pool.push(s);
369        }
370        // Otherwise drop the buffer
371    }
372
373    /// Acquire a `Vec<Value>` buffer from the pool.
374    ///
375    /// Returns a pooled buffer if available, otherwise allocates a new one
376    /// with the configured capacity hint.
377    ///
378    /// # Examples
379    ///
380    /// ```rust
381    /// use hedl_stream::BufferPool;
382    /// use hedl_core::Value;
383    ///
384    /// let mut pool = BufferPool::new(10);
385    /// let mut v = pool.acquire_value_vec();
386    /// v.push(Value::Int(42));
387    /// ```
388    #[inline]
389    pub fn acquire_value_vec(&mut self) -> Vec<Value> {
390        self.value_pool
391            .pop()
392            .unwrap_or_else(|| Vec::with_capacity(self.value_capacity_hint))
393    }
394
395    /// Release a `Vec<Value>` buffer back to the pool.
396    ///
397    /// The buffer is cleared but retains its capacity. If the pool is full,
398    /// the buffer is dropped.
399    ///
400    /// # Examples
401    ///
402    /// ```rust
403    /// use hedl_stream::BufferPool;
404    /// use hedl_core::Value;
405    ///
406    /// let mut pool = BufferPool::new(10);
407    /// let mut v = pool.acquire_value_vec();
408    /// v.push(Value::Int(42));
409    /// pool.release_value_vec(v);
410    ///
411    /// // Buffer is reused with content cleared
412    /// let v2 = pool.acquire_value_vec();
413    /// assert_eq!(v2.len(), 0);
414    /// ```
415    #[inline]
416    pub fn release_value_vec(&mut self, mut v: Vec<Value>) {
417        if self.value_pool.len() < self.max_pool_size {
418            v.clear();
419            self.value_pool.push(v);
420        }
421        // Otherwise drop the buffer
422    }
423
424    /// Get the current number of String buffers in the pool.
425    ///
426    /// # Examples
427    ///
428    /// ```rust
429    /// use hedl_stream::BufferPool;
430    ///
431    /// let mut pool = BufferPool::new(10);
432    /// assert_eq!(pool.string_pool_size(), 0);
433    ///
434    /// let s = pool.acquire_string();
435    /// pool.release_string(s);
436    /// assert_eq!(pool.string_pool_size(), 1);
437    /// ```
438    #[inline]
439    #[must_use]
440    pub fn string_pool_size(&self) -> usize {
441        self.string_pool.len()
442    }
443
444    /// Get the current number of `Vec<Value>` buffers in the pool.
445    ///
446    /// # Examples
447    ///
448    /// ```rust
449    /// use hedl_stream::BufferPool;
450    ///
451    /// let mut pool = BufferPool::new(10);
452    /// assert_eq!(pool.value_pool_size(), 0);
453    ///
454    /// let v = pool.acquire_value_vec();
455    /// pool.release_value_vec(v);
456    /// assert_eq!(pool.value_pool_size(), 1);
457    /// ```
458    #[inline]
459    #[must_use]
460    pub fn value_pool_size(&self) -> usize {
461        self.value_pool.len()
462    }
463
464    /// Clear all buffers from the pool.
465    ///
466    /// This releases all pooled buffers, freeing memory back to the allocator.
467    /// Useful for manual memory management or cleanup.
468    ///
469    /// # Examples
470    ///
471    /// ```rust
472    /// use hedl_stream::BufferPool;
473    ///
474    /// let mut pool = BufferPool::new(10);
475    /// let s = pool.acquire_string();
476    /// pool.release_string(s);
477    /// assert_eq!(pool.string_pool_size(), 1);
478    ///
479    /// pool.clear();
480    /// assert_eq!(pool.string_pool_size(), 0);
481    /// ```
482    pub fn clear(&mut self) {
483        self.string_pool.clear();
484        self.value_pool.clear();
485    }
486
487    /// Get the maximum pool size.
488    ///
489    /// # Examples
490    ///
491    /// ```rust
492    /// use hedl_stream::BufferPool;
493    ///
494    /// let pool = BufferPool::new(15);
495    /// assert_eq!(pool.max_pool_size(), 15);
496    /// ```
497    #[inline]
498    #[must_use]
499    pub fn max_pool_size(&self) -> usize {
500        self.max_pool_size
501    }
502}
503
504#[cfg(test)]
505mod tests {
506    use super::*;
507
508    // ==================== MemoryLimits tests ====================
509
510    #[test]
511    fn test_memory_limits_default() {
512        let limits = MemoryLimits::default();
513        assert_eq!(limits.max_buffer_size, 1024 * 1024);
514        assert_eq!(limits.max_line_length, 1_000_000);
515        assert!(limits.enable_buffer_pooling);
516        assert_eq!(limits.max_pool_size, 10);
517    }
518
519    #[test]
520    fn test_memory_limits_embedded() {
521        let limits = MemoryLimits::embedded();
522        assert_eq!(limits.max_buffer_size, 8 * 1024);
523        assert_eq!(limits.max_line_length, 10_000);
524        assert!(!limits.enable_buffer_pooling);
525        assert_eq!(limits.max_pool_size, 0);
526    }
527
528    #[test]
529    fn test_memory_limits_high_throughput() {
530        let limits = MemoryLimits::high_throughput();
531        assert_eq!(limits.max_buffer_size, 2 * 1024 * 1024);
532        assert_eq!(limits.max_line_length, 10_000_000);
533        assert!(limits.enable_buffer_pooling);
534        assert_eq!(limits.max_pool_size, 50);
535    }
536
537    #[test]
538    fn test_memory_limits_untrusted() {
539        let limits = MemoryLimits::untrusted();
540        assert_eq!(limits.max_buffer_size, 64 * 1024);
541        assert_eq!(limits.max_line_length, 100_000);
542        assert!(limits.enable_buffer_pooling);
543        assert_eq!(limits.max_pool_size, 5);
544    }
545
546    #[test]
547    fn test_memory_limits_custom() {
548        let limits = MemoryLimits {
549            max_buffer_size: 128 * 1024,
550            max_line_length: 500_000,
551            enable_buffer_pooling: false,
552            max_pool_size: 3,
553        };
554        assert_eq!(limits.max_buffer_size, 128 * 1024);
555        assert_eq!(limits.max_line_length, 500_000);
556        assert!(!limits.enable_buffer_pooling);
557        assert_eq!(limits.max_pool_size, 3);
558    }
559
560    #[test]
561    fn test_memory_limits_clone() {
562        let limits1 = MemoryLimits::default();
563        let limits2 = limits1;
564        assert_eq!(limits1, limits2);
565    }
566
567    #[test]
568    fn test_memory_limits_debug() {
569        let limits = MemoryLimits::default();
570        let debug = format!("{limits:?}");
571        assert!(debug.contains("MemoryLimits"));
572    }
573
574    // ==================== BufferPool basic tests ====================
575
576    #[test]
577    fn test_buffer_pool_new() {
578        let pool = BufferPool::new(10);
579        assert_eq!(pool.max_pool_size(), 10);
580        assert_eq!(pool.string_pool_size(), 0);
581        assert_eq!(pool.value_pool_size(), 0);
582    }
583
584    #[test]
585    fn test_buffer_pool_with_capacity_hints() {
586        let pool = BufferPool::with_capacity_hints(10, 512, 32);
587        assert_eq!(pool.max_pool_size(), 10);
588        assert_eq!(pool.string_capacity_hint, 512);
589        assert_eq!(pool.value_capacity_hint, 32);
590    }
591
592    #[test]
593    fn test_acquire_string_from_empty_pool() {
594        let mut pool = BufferPool::new(10);
595        let s = pool.acquire_string();
596        assert_eq!(s.len(), 0);
597        assert!(s.capacity() >= 256); // Default hint
598    }
599
600    #[test]
601    fn test_release_and_acquire_string() {
602        let mut pool = BufferPool::new(10);
603        let mut s = pool.acquire_string();
604        s.push_str("hello");
605        assert_eq!(s.len(), 5);
606
607        pool.release_string(s);
608        assert_eq!(pool.string_pool_size(), 1);
609
610        let s2 = pool.acquire_string();
611        assert_eq!(s2.len(), 0); // Cleared
612        assert!(s2.capacity() > 0); // Capacity retained
613    }
614
615    #[test]
616    fn test_acquire_value_vec_from_empty_pool() {
617        let mut pool = BufferPool::new(10);
618        let v = pool.acquire_value_vec();
619        assert_eq!(v.len(), 0);
620        assert!(v.capacity() >= 16); // Default hint
621    }
622
623    #[test]
624    fn test_release_and_acquire_value_vec() {
625        let mut pool = BufferPool::new(10);
626        let mut v = pool.acquire_value_vec();
627        v.push(Value::Int(42));
628        v.push(Value::Bool(true));
629        assert_eq!(v.len(), 2);
630
631        pool.release_value_vec(v);
632        assert_eq!(pool.value_pool_size(), 1);
633
634        let v2 = pool.acquire_value_vec();
635        assert_eq!(v2.len(), 0); // Cleared
636        assert!(v2.capacity() > 0); // Capacity retained
637    }
638
639    #[test]
640    fn test_pool_size_limit_string() {
641        let mut pool = BufferPool::new(2);
642
643        // Acquire multiple buffers first
644        let s1 = pool.acquire_string();
645        let s2 = pool.acquire_string();
646        let s3 = pool.acquire_string();
647
648        // Release them to fill the pool
649        pool.release_string(s1);
650        pool.release_string(s2);
651        assert_eq!(pool.string_pool_size(), 2);
652
653        // Releasing another should not increase pool size (it gets dropped)
654        pool.release_string(s3);
655        assert_eq!(pool.string_pool_size(), 2);
656    }
657
658    #[test]
659    fn test_pool_size_limit_value_vec() {
660        let mut pool = BufferPool::new(2);
661
662        // Acquire multiple buffers first
663        let v1 = pool.acquire_value_vec();
664        let v2 = pool.acquire_value_vec();
665        let v3 = pool.acquire_value_vec();
666
667        // Release them to fill the pool
668        pool.release_value_vec(v1);
669        pool.release_value_vec(v2);
670        assert_eq!(pool.value_pool_size(), 2);
671
672        // Releasing another should not increase pool size (it gets dropped)
673        pool.release_value_vec(v3);
674        assert_eq!(pool.value_pool_size(), 2);
675    }
676
677    #[test]
678    fn test_clear_pool() {
679        let mut pool = BufferPool::new(10);
680
681        let s = pool.acquire_string();
682        pool.release_string(s);
683        let v = pool.acquire_value_vec();
684        pool.release_value_vec(v);
685        assert_eq!(pool.string_pool_size(), 1);
686        assert_eq!(pool.value_pool_size(), 1);
687
688        pool.clear();
689        assert_eq!(pool.string_pool_size(), 0);
690        assert_eq!(pool.value_pool_size(), 0);
691    }
692
693    #[test]
694    fn test_multiple_acquire_release_cycles() {
695        let mut pool = BufferPool::new(5);
696
697        for i in 0..10 {
698            let mut s = pool.acquire_string();
699            s.push_str(&format!("iteration {i}"));
700            pool.release_string(s);
701
702            let mut v = pool.acquire_value_vec();
703            v.push(Value::Int(i64::from(i)));
704            pool.release_value_vec(v);
705        }
706
707        // Pool should be at max size
708        assert!(pool.string_pool_size() <= 5);
709        assert!(pool.value_pool_size() <= 5);
710    }
711
712    #[test]
713    fn test_capacity_preserved_across_cycles() {
714        let mut pool = BufferPool::new(10);
715
716        let mut s = pool.acquire_string();
717        s.push_str(&"x".repeat(1000)); // Force capacity growth
718        let capacity_after_growth = s.capacity();
719        pool.release_string(s);
720
721        let s2 = pool.acquire_string();
722        assert_eq!(s2.capacity(), capacity_after_growth);
723    }
724
725    #[test]
726    fn test_custom_capacity_hints() {
727        let mut pool = BufferPool::with_capacity_hints(10, 1024, 64);
728
729        let s = pool.acquire_string();
730        assert!(s.capacity() >= 1024);
731
732        let v = pool.acquire_value_vec();
733        assert!(v.capacity() >= 64);
734    }
735
736    #[test]
737    fn test_zero_max_pool_size() {
738        let mut pool = BufferPool::new(0);
739
740        let s = pool.acquire_string();
741        pool.release_string(s);
742        let v = pool.acquire_value_vec();
743        pool.release_value_vec(v);
744
745        // Nothing should be pooled
746        assert_eq!(pool.string_pool_size(), 0);
747        assert_eq!(pool.value_pool_size(), 0);
748    }
749
750    #[test]
751    fn test_large_pool_size() {
752        let mut pool = BufferPool::new(100);
753
754        // Acquire 50 strings
755        let strings: Vec<_> = (0..50).map(|_| pool.acquire_string()).collect();
756
757        // Release them all
758        for s in strings {
759            pool.release_string(s);
760        }
761
762        assert_eq!(pool.string_pool_size(), 50);
763
764        // Acquire 50 value vecs
765        let vecs: Vec<_> = (0..50).map(|_| pool.acquire_value_vec()).collect();
766
767        // Release them all
768        for v in vecs {
769            pool.release_value_vec(v);
770        }
771
772        assert_eq!(pool.value_pool_size(), 50);
773    }
774
775    #[test]
776    fn test_pool_independence() {
777        let mut pool = BufferPool::new(10);
778
779        // Acquire and release string buffers
780        let strings: Vec<_> = (0..5).map(|_| pool.acquire_string()).collect();
781        for s in strings {
782            pool.release_string(s);
783        }
784
785        // Acquire and release value buffers
786        let vecs: Vec<_> = (0..3).map(|_| pool.acquire_value_vec()).collect();
787        for v in vecs {
788            pool.release_value_vec(v);
789        }
790
791        assert_eq!(pool.string_pool_size(), 5);
792        assert_eq!(pool.value_pool_size(), 3);
793
794        // Acquiring from one pool doesn't affect the other
795        let _ = pool.acquire_string();
796        assert_eq!(pool.string_pool_size(), 4);
797        assert_eq!(pool.value_pool_size(), 3);
798    }
799
800    #[test]
801    fn test_string_content_cleared() {
802        let mut pool = BufferPool::new(10);
803
804        let mut s = pool.acquire_string();
805        s.push_str("test data");
806        pool.release_string(s);
807
808        let s2 = pool.acquire_string();
809        assert_eq!(s2.len(), 0);
810        assert!(!s2.contains("test"));
811    }
812
813    #[test]
814    fn test_value_vec_content_cleared() {
815        let mut pool = BufferPool::new(10);
816
817        let mut v = pool.acquire_value_vec();
818        v.push(Value::Int(1));
819        v.push(Value::Bool(true));
820        v.push(Value::String("test".to_string().into()));
821        pool.release_value_vec(v);
822
823        let v2 = pool.acquire_value_vec();
824        assert_eq!(v2.len(), 0);
825    }
826
827    #[test]
828    fn test_pool_debug() {
829        let pool = BufferPool::new(10);
830        let debug = format!("{pool:?}");
831        assert!(debug.contains("BufferPool"));
832    }
833
834    // ==================== Edge case tests ====================
835
836    #[test]
837    fn test_acquire_without_release() {
838        let mut pool = BufferPool::new(10);
839
840        // Acquire many without releasing
841        for _ in 0..20 {
842            let _s = pool.acquire_string();
843            let _v = pool.acquire_value_vec();
844        }
845
846        // Pool should still be empty
847        assert_eq!(pool.string_pool_size(), 0);
848        assert_eq!(pool.value_pool_size(), 0);
849    }
850
851    #[test]
852    fn test_release_without_acquire() {
853        let mut pool = BufferPool::new(10);
854
855        // Release externally created buffers
856        pool.release_string(String::from("external"));
857        pool.release_value_vec(vec![Value::Null]);
858
859        assert_eq!(pool.string_pool_size(), 1);
860        assert_eq!(pool.value_pool_size(), 1);
861    }
862
863    #[test]
864    fn test_interleaved_operations() {
865        let mut pool = BufferPool::new(5);
866
867        let s1 = pool.acquire_string();
868        let v1 = pool.acquire_value_vec();
869        let s2 = pool.acquire_string();
870
871        pool.release_string(s1);
872        let v2 = pool.acquire_value_vec();
873        pool.release_value_vec(v1);
874
875        pool.release_string(s2);
876        pool.release_value_vec(v2);
877
878        assert_eq!(pool.string_pool_size(), 2);
879        assert_eq!(pool.value_pool_size(), 2);
880    }
881}