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}