Skip to main content

atom_engine/
pool.rs

1//! Memory pool utilities for optimizing string allocations.
2//!
3//! This module provides memory pool structures for efficient string
4//! handling in high-throughput scenarios.
5
6use std::fmt;
7
8/// A memory pool for managing chunk allocations.
9pub struct MemoryPool {
10    chunk_size: usize,
11    max_chunks: usize,
12}
13
14impl MemoryPool {
15    /// Creates a new MemoryPool.
16    ///
17    /// # Arguments
18    ///
19    /// * `chunk_size` - Size of each chunk in bytes (minimum 64)
20    /// * `max_chunks` - Maximum number of chunks (minimum 1)
21    pub fn new(chunk_size: usize, max_chunks: usize) -> Self {
22        MemoryPool {
23            chunk_size: chunk_size.max(64),
24            max_chunks: max_chunks.max(1),
25        }
26    }
27
28    /// Returns the chunk size.
29    pub fn chunk_size(&self) -> usize {
30        self.chunk_size
31    }
32
33    /// Returns the maximum number of chunks.
34    pub fn max_chunks(&self) -> usize {
35        self.max_chunks
36    }
37}
38
39impl Default for MemoryPool {
40    fn default() -> Self {
41        Self::new(4096, 16)
42    }
43}
44
45impl fmt::Debug for MemoryPool {
46    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47        f.debug_struct("MemoryPool")
48            .field("chunk_size", &self.chunk_size)
49            .field("max_chunks", &self.max_chunks)
50            .finish()
51    }
52}
53
54/// A string backed by a pooled buffer.
55pub struct PooledString {
56    data: Vec<u8>,
57}
58
59impl PooledString {
60    /// Creates a new empty PooledString.
61    pub fn new() -> Self {
62        PooledString { data: Vec::new() }
63    }
64
65    /// Creates a PooledString with the given capacity.
66    pub fn with_capacity(capacity: usize) -> Self {
67        PooledString {
68            data: Vec::with_capacity(capacity),
69        }
70    }
71
72    /// Creates a PooledString from a String.
73    pub fn from_string(s: String) -> Self {
74        PooledString {
75            data: s.into_bytes(),
76        }
77    }
78
79    /// Returns the string as a &str.
80    pub fn as_str(&self) -> &str {
81        unsafe { std::str::from_utf8_unchecked(&self.data) }
82    }
83
84    /// Returns the string as bytes.
85    pub fn as_bytes(&self) -> &[u8] {
86        &self.data
87    }
88
89    /// Appends a string to the buffer.
90    pub fn push_str(&mut self, s: &str) {
91        self.data.extend(s.as_bytes());
92    }
93
94    /// Appends a character to the buffer.
95    pub fn push(&mut self, c: char) {
96        self.data.extend(c.encode_utf8(&mut [0u8; 4]).as_bytes());
97    }
98
99    /// Returns the length of the string in bytes.
100    pub fn len(&self) -> usize {
101        self.data.len()
102    }
103
104    /// Returns true if the string is empty.
105    pub fn is_empty(&self) -> bool {
106        self.data.is_empty()
107    }
108
109    /// Clears the string.
110    pub fn clear(&mut self) {
111        self.data.clear();
112    }
113
114    /// Returns the capacity of the underlying buffer.
115    pub fn capacity(&self) -> usize {
116        self.data.capacity()
117    }
118
119    /// Reserves additional capacity.
120    pub fn reserve(&mut self, additional: usize) {
121        self.data.reserve(additional);
122    }
123
124    /// Truncates the string to the given length.
125    pub fn truncate(&mut self, len: usize) {
126        self.data.truncate(len);
127    }
128}
129
130impl Default for PooledString {
131    fn default() -> Self {
132        Self::new()
133    }
134}
135
136impl fmt::Debug for PooledString {
137    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138        f.debug_struct("PooledString")
139            .field("len", &self.len())
140            .field("capacity", &self.capacity())
141            .finish()
142    }
143}
144
145impl Clone for PooledString {
146    fn clone(&self) -> Self {
147        PooledString {
148            data: self.data.clone(),
149        }
150    }
151}
152
153impl From<String> for PooledString {
154    fn from(s: String) -> Self {
155        PooledString::from_string(s)
156    }
157}
158
159impl From<&str> for PooledString {
160    fn from(s: &str) -> Self {
161        PooledString::from_string(s.to_string())
162    }
163}
164
165impl std::ops::Deref for PooledString {
166    type Target = str;
167
168    fn deref(&self) -> &Self::Target {
169        self.as_str()
170    }
171}
172
173/// A pool for managing multiple PooledStrings.
174pub struct StringPool {
175    strings: Vec<PooledString>,
176    #[allow(dead_code)]
177    max_size: usize,
178}
179
180impl StringPool {
181    /// Creates a new StringPool with default max size of 1024.
182    pub fn new() -> Self {
183        StringPool {
184            strings: Vec::new(),
185            max_size: 1024,
186        }
187    }
188
189    /// Creates a StringPool with the specified max size.
190    pub fn with_max_size(max_size: usize) -> Self {
191        StringPool {
192            strings: Vec::new(),
193            max_size,
194        }
195    }
196
197    /// Stores a string in the pool.
198    pub fn store(&mut self, s: &str) -> PooledString {
199        PooledString::from_string(s.to_string())
200    }
201
202    /// Gets a string from the pool or inserts it if not present.
203    pub fn get_or_insert(&mut self, s: &str) -> PooledString {
204        self.store(s)
205    }
206
207    /// Clears all strings from the pool.
208    pub fn clear(&mut self) {
209        self.strings.clear();
210    }
211
212    /// Returns the number of strings in the pool.
213    pub fn len(&self) -> usize {
214        self.strings.len()
215    }
216
217    /// Returns true if the pool is empty.
218    pub fn is_empty(&self) -> bool {
219        self.strings.is_empty()
220    }
221
222    /// Returns the total capacity of all strings in the pool.
223    pub fn total_capacity(&self) -> usize {
224        self.strings.iter().map(|s| s.capacity()).sum()
225    }
226}
227
228impl Default for StringPool {
229    fn default() -> Self {
230        Self::new()
231    }
232}
233
234impl fmt::Debug for StringPool {
235    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
236        f.debug_struct("StringPool")
237            .field("len", &self.len())
238            .field("total_capacity", &self.total_capacity())
239            .finish()
240    }
241}
242
243/// Allocation tracking utilities.
244#[allow(dead_code)]
245pub mod pool_alloc {
246    use std::sync::atomic::{AtomicUsize, Ordering};
247
248    static ALLOC_COUNT: AtomicUsize = AtomicUsize::new(0);
249    static ALLOC_COUNT_MAX: AtomicUsize = AtomicUsize::new(0);
250
251    /// Returns the current allocation count.
252    pub fn get_alloc_count() -> usize {
253        ALLOC_COUNT.load(Ordering::Relaxed)
254    }
255
256    /// Returns the maximum allocation count ever recorded.
257    pub fn get_max_alloc_count() -> usize {
258        ALLOC_COUNT_MAX.load(Ordering::Relaxed)
259    }
260
261    /// Resets the allocation counter to zero.
262    pub fn reset_count() {
263        ALLOC_COUNT.store(0, Ordering::Relaxed);
264    }
265
266    /// Records an allocation of the given size.
267    pub fn record_allocation(size: usize) {
268        let current = ALLOC_COUNT.fetch_add(size, Ordering::Relaxed);
269        let max = ALLOC_COUNT_MAX.load(Ordering::Relaxed);
270        if current > max {
271            ALLOC_COUNT_MAX.store(current, Ordering::Relaxed);
272        }
273    }
274
275    /// Records a deallocation of the given size.
276    pub fn record_deallocation(size: usize) {
277        ALLOC_COUNT.fetch_sub(size, Ordering::Relaxed);
278    }
279}