1use bytes::{Bytes, BytesMut};
11use std::collections::HashMap;
12use std::sync::{Arc, Mutex};
13
14#[derive(Clone)]
19pub struct BytesPool {
20 pool: Arc<Mutex<HashMap<usize, Vec<BytesMut>>>>,
23 stats: Arc<Mutex<PoolStats>>,
25}
26
27impl Default for BytesPool {
28 fn default() -> Self {
29 Self::new()
30 }
31}
32
33impl BytesPool {
34 pub fn new() -> Self {
36 Self {
37 pool: Arc::new(Mutex::new(HashMap::new())),
38 stats: Arc::new(Mutex::new(PoolStats::default())),
39 }
40 }
41
42 pub fn get(&self, capacity: usize) -> BytesMut {
47 let bucket = Self::capacity_bucket(capacity);
48
49 let mut pool = self.pool.lock().unwrap_or_else(|e| e.into_inner());
50 let mut stats = self.stats.lock().unwrap_or_else(|e| e.into_inner());
51
52 if let Some(buffers) = pool.get_mut(&bucket) {
53 if let Some(mut buf) = buffers.pop() {
54 buf.clear();
55 stats.hits += 1;
56 return buf;
57 }
58 }
59
60 stats.misses += 1;
61 stats.allocations += 1;
62 BytesMut::with_capacity(bucket)
63 }
64
65 pub fn put(&self, mut buf: BytesMut) {
69 if buf.capacity() > 1024 * 1024 * 4 {
71 return;
73 }
74
75 buf.clear();
76 let bucket = Self::capacity_bucket(buf.capacity());
77
78 let mut pool = self.pool.lock().unwrap_or_else(|e| e.into_inner());
79 let buffers = pool.entry(bucket).or_default();
80
81 if buffers.len() < 100 {
83 buffers.push(buf);
84 }
85 }
86
87 pub fn stats(&self) -> PoolStats {
89 *self.stats.lock().unwrap_or_else(|e| e.into_inner())
90 }
91
92 pub fn clear(&self) {
94 self.pool.lock().unwrap_or_else(|e| e.into_inner()).clear();
95 }
96
97 fn capacity_bucket(capacity: usize) -> usize {
99 if capacity == 0 {
100 return 1024; }
102 capacity.next_power_of_two().max(1024)
103 }
104}
105
106#[derive(Clone)]
111pub struct CidStringPool {
112 pool: Arc<Mutex<HashMap<String, Arc<str>>>>,
114 stats: Arc<Mutex<PoolStats>>,
116}
117
118impl Default for CidStringPool {
119 fn default() -> Self {
120 Self::new()
121 }
122}
123
124impl CidStringPool {
125 pub fn new() -> Self {
127 Self {
128 pool: Arc::new(Mutex::new(HashMap::new())),
129 stats: Arc::new(Mutex::new(PoolStats::default())),
130 }
131 }
132
133 pub fn intern(&self, s: &str) -> Arc<str> {
138 let mut pool = self.pool.lock().unwrap_or_else(|e| e.into_inner());
139 let mut stats = self.stats.lock().unwrap_or_else(|e| e.into_inner());
140
141 if let Some(existing) = pool.get(s) {
142 stats.hits += 1;
143 return Arc::clone(existing);
144 }
145
146 stats.misses += 1;
147 let arc: Arc<str> = Arc::from(s);
148 pool.insert(s.to_string(), Arc::clone(&arc));
149 arc
150 }
151
152 pub fn stats(&self) -> PoolStats {
154 *self.stats.lock().unwrap_or_else(|e| e.into_inner())
155 }
156
157 pub fn clear(&self) {
159 self.pool.lock().unwrap_or_else(|e| e.into_inner()).clear();
160 }
161
162 pub fn len(&self) -> usize {
164 self.pool.lock().unwrap_or_else(|e| e.into_inner()).len()
165 }
166
167 pub fn is_empty(&self) -> bool {
169 self.pool
170 .lock()
171 .unwrap_or_else(|e| e.into_inner())
172 .is_empty()
173 }
174}
175
176#[derive(Debug, Clone, Copy, Default)]
178pub struct PoolStats {
179 pub hits: u64,
181 pub misses: u64,
183 pub allocations: u64,
185}
186
187impl PoolStats {
188 pub fn hit_rate(&self) -> f64 {
190 let total = self.hits + self.misses;
191 if total == 0 {
192 return 0.0;
193 }
194 self.hits as f64 / total as f64
195 }
196
197 pub fn miss_rate(&self) -> f64 {
199 1.0 - self.hit_rate()
200 }
201}
202
203static GLOBAL_BYTES_POOL: once_cell::sync::Lazy<BytesPool> =
205 once_cell::sync::Lazy::new(BytesPool::new);
206
207static GLOBAL_CID_STRING_POOL: once_cell::sync::Lazy<CidStringPool> =
209 once_cell::sync::Lazy::new(CidStringPool::new);
210
211pub fn global_bytes_pool() -> &'static BytesPool {
213 &GLOBAL_BYTES_POOL
214}
215
216pub fn global_cid_string_pool() -> &'static CidStringPool {
218 &GLOBAL_CID_STRING_POOL
219}
220
221pub fn freeze_bytes(buf: BytesMut) -> Bytes {
223 buf.freeze()
224}
225
226#[cfg(test)]
227mod tests {
228 use super::*;
229
230 #[test]
231 fn test_bytes_pool_basic() {
232 let pool = BytesPool::new();
233
234 let buf1 = pool.get(1024);
236 assert!(buf1.capacity() >= 1024);
237
238 pool.put(buf1);
240
241 let buf2 = pool.get(1024);
243 assert!(buf2.capacity() >= 1024);
244
245 let stats = pool.stats();
246 assert_eq!(stats.hits, 1);
247 assert_eq!(stats.misses, 1);
248 }
249
250 #[test]
251 fn test_bytes_pool_capacity_bucketing() {
252 let pool = BytesPool::new();
253
254 let buf1 = pool.get(100);
256 let buf2 = pool.get(1000);
257 let buf3 = pool.get(2000);
258
259 assert!(buf1.capacity() >= 100);
261 assert!(buf2.capacity() >= 1000);
262 assert!(buf3.capacity() >= 2000);
263
264 pool.put(buf1);
265 pool.put(buf2);
266 pool.put(buf3);
267
268 let buf4 = pool.get(150); let buf5 = pool.get(1100); assert!(buf4.capacity() >= 150);
273 assert!(buf5.capacity() >= 1100);
274 }
275
276 #[test]
277 fn test_cid_string_pool_basic() {
278 let pool = CidStringPool::new();
279
280 let s1 = pool.intern("QmTest123");
282 let s2 = pool.intern("QmTest123");
283
284 assert_eq!(s1.as_ref(), s2.as_ref());
286 assert!(Arc::ptr_eq(&s1, &s2));
287
288 let stats = pool.stats();
289 assert_eq!(stats.hits, 1);
290 assert_eq!(stats.misses, 1);
291 }
292
293 #[test]
294 fn test_cid_string_pool_different_strings() {
295 let pool = CidStringPool::new();
296
297 let s1 = pool.intern("QmTest1");
298 let s2 = pool.intern("QmTest2");
299
300 assert_ne!(s1.as_ref(), s2.as_ref());
302 assert!(!Arc::ptr_eq(&s1, &s2));
303
304 let stats = pool.stats();
305 assert_eq!(stats.hits, 0);
306 assert_eq!(stats.misses, 2);
307 }
308
309 #[test]
310 fn test_pool_stats_hit_rate() {
311 let stats = PoolStats {
312 hits: 80,
313 misses: 20,
314 allocations: 20,
315 };
316
317 assert!((stats.hit_rate() - 0.8).abs() < 0.001);
318 assert!((stats.miss_rate() - 0.2).abs() < 0.001);
319 }
320
321 #[test]
322 fn test_pool_stats_empty() {
323 let stats = PoolStats::default();
324 assert_eq!(stats.hit_rate(), 0.0);
325 assert_eq!(stats.miss_rate(), 1.0);
326 }
327
328 #[test]
329 fn test_bytes_pool_clear() {
330 let pool = BytesPool::new();
331 let buf = pool.get(1024);
332 pool.put(buf);
333
334 pool.clear();
335
336 let _buf2 = pool.get(1024);
338 let stats = pool.stats();
339 assert_eq!(stats.misses, 2); }
341
342 #[test]
343 fn test_cid_string_pool_len() {
344 let pool = CidStringPool::new();
345 assert_eq!(pool.len(), 0);
346 assert!(pool.is_empty());
347
348 pool.intern("QmTest1");
349 assert_eq!(pool.len(), 1);
350 assert!(!pool.is_empty());
351
352 pool.intern("QmTest2");
353 assert_eq!(pool.len(), 2);
354
355 pool.intern("QmTest1"); assert_eq!(pool.len(), 2); }
358
359 #[test]
360 fn test_bytes_pool_size_limit() {
361 let pool = BytesPool::new();
362
363 let large_buf = BytesMut::with_capacity(10 * 1024 * 1024);
365 pool.put(large_buf);
366
367 let _buf = pool.get(10 * 1024 * 1024);
369 let stats = pool.stats();
370
371 assert!(stats.misses >= 1);
373 }
374
375 #[test]
376 fn test_global_pools() {
377 let bytes_pool = global_bytes_pool();
378 let cid_pool = global_cid_string_pool();
379
380 let _buf = bytes_pool.get(1024);
382 let _s = cid_pool.intern("QmTest");
383 }
384
385 #[test]
386 fn test_freeze_bytes() {
387 let mut buf = BytesMut::with_capacity(1024);
388 buf.extend_from_slice(b"Hello, world!");
389 let bytes = freeze_bytes(buf);
390 assert_eq!(&bytes[..], b"Hello, world!");
391 }
392}