oxirs_ttl/toolkit/
buffer_manager.rs1use std::sync::{Arc, Mutex};
7
8#[derive(Debug)]
35pub struct BufferManager {
36 string_buffers: Vec<String>,
38 max_pooled_buffers: usize,
40 stats: BufferStats,
42}
43
44#[derive(Debug, Clone, Default)]
46pub struct BufferStats {
47 pub total_acquisitions: usize,
49 pub pool_hits: usize,
51 pub pool_misses: usize,
53 pub total_releases: usize,
55 pub current_pool_size: usize,
57}
58
59impl BufferManager {
60 pub fn new() -> Self {
62 Self::with_capacity(32)
63 }
64
65 pub fn with_capacity(max_pooled_buffers: usize) -> Self {
67 Self {
68 string_buffers: Vec::with_capacity(max_pooled_buffers),
69 max_pooled_buffers,
70 stats: BufferStats::default(),
71 }
72 }
73
74 pub fn acquire_string_buffer(&mut self) -> String {
79 self.stats.total_acquisitions += 1;
80
81 if let Some(mut buffer) = self.string_buffers.pop() {
82 self.stats.pool_hits += 1;
83 self.stats.current_pool_size = self.string_buffers.len();
84 buffer.clear();
85 buffer
86 } else {
87 self.stats.pool_misses += 1;
88 String::with_capacity(256) }
90 }
91
92 pub fn acquire_string_buffer_with_capacity(&mut self, capacity: usize) -> String {
94 self.stats.total_acquisitions += 1;
95
96 for (i, buffer) in self.string_buffers.iter().enumerate() {
98 if buffer.capacity() >= capacity {
99 let mut buffer = self.string_buffers.swap_remove(i);
100 self.stats.pool_hits += 1;
101 self.stats.current_pool_size = self.string_buffers.len();
102 buffer.clear();
103 return buffer;
104 }
105 }
106
107 self.stats.pool_misses += 1;
109 String::with_capacity(capacity)
110 }
111
112 pub fn release_string_buffer(&mut self, buffer: String) {
117 self.stats.total_releases += 1;
118
119 if self.string_buffers.len() < self.max_pooled_buffers {
120 if buffer.capacity() <= 4096 {
122 self.string_buffers.push(buffer);
123 self.stats.current_pool_size = self.string_buffers.len();
124 }
125 }
126 }
128
129 pub fn generate_blank_node_id(&mut self, counter: usize) -> String {
131 let mut buffer = self.acquire_string_buffer();
132 buffer.push_str("_:b");
133 buffer.push_str(&counter.to_string());
134 buffer
136 }
137
138 pub fn expand_prefixed_name(&mut self, _prefix: &str, local: &str, namespace: &str) -> String {
140 let total_len = namespace.len() + local.len();
141 let mut buffer = self.acquire_string_buffer_with_capacity(total_len);
142 buffer.push_str(namespace);
143 buffer.push_str(local);
144 buffer
146 }
147
148 pub fn stats(&self) -> &BufferStats {
150 &self.stats
151 }
152
153 pub fn hit_rate(&self) -> f64 {
155 if self.stats.total_acquisitions == 0 {
156 return 0.0;
157 }
158 self.stats.pool_hits as f64 / self.stats.total_acquisitions as f64
159 }
160
161 pub fn clear(&mut self) {
163 self.string_buffers.clear();
164 self.stats = BufferStats::default();
165 }
166
167 pub fn shrink_to_fit(&mut self) {
169 self.string_buffers.shrink_to_fit();
170 }
171
172 pub fn pool_size(&self) -> usize {
174 self.string_buffers.len()
175 }
176}
177
178impl Default for BufferManager {
179 fn default() -> Self {
180 Self::new()
181 }
182}
183
184impl BufferStats {
185 pub fn report(&self) -> String {
187 let hit_rate = if self.total_acquisitions > 0 {
188 (self.pool_hits as f64 / self.total_acquisitions as f64) * 100.0
189 } else {
190 0.0
191 };
192
193 format!(
194 "Buffer Pool Statistics:\n\
195 - Total acquisitions: {}\n\
196 - Pool hits: {} ({:.1}%)\n\
197 - Pool misses: {}\n\
198 - Total releases: {}\n\
199 - Current pool size: {}",
200 self.total_acquisitions,
201 self.pool_hits,
202 hit_rate,
203 self.pool_misses,
204 self.total_releases,
205 self.current_pool_size
206 )
207 }
208}
209
210pub struct GlobalBufferManager {
212 inner: Arc<Mutex<BufferManager>>,
213}
214
215impl GlobalBufferManager {
216 pub fn new() -> Self {
218 Self {
219 inner: Arc::new(Mutex::new(BufferManager::new())),
220 }
221 }
222
223 pub fn with_capacity(max_pooled_buffers: usize) -> Self {
225 Self {
226 inner: Arc::new(Mutex::new(BufferManager::with_capacity(max_pooled_buffers))),
227 }
228 }
229
230 pub fn acquire_string_buffer(&self) -> String {
232 self.inner
233 .lock()
234 .expect("lock should not be poisoned")
235 .acquire_string_buffer()
236 }
237
238 pub fn release_string_buffer(&self, buffer: String) {
240 self.inner
241 .lock()
242 .expect("lock should not be poisoned")
243 .release_string_buffer(buffer);
244 }
245
246 pub fn generate_blank_node_id(&self, counter: usize) -> String {
248 self.inner
249 .lock()
250 .expect("lock should not be poisoned")
251 .generate_blank_node_id(counter)
252 }
253
254 pub fn stats(&self) -> BufferStats {
256 self.inner
257 .lock()
258 .expect("lock should not be poisoned")
259 .stats()
260 .clone()
261 }
262
263 pub fn hit_rate(&self) -> f64 {
265 self.inner
266 .lock()
267 .expect("lock should not be poisoned")
268 .hit_rate()
269 }
270
271 pub fn clear(&self) {
273 self.inner
274 .lock()
275 .expect("lock should not be poisoned")
276 .clear();
277 }
278}
279
280impl Default for GlobalBufferManager {
281 fn default() -> Self {
282 Self::new()
283 }
284}
285
286impl Clone for GlobalBufferManager {
287 fn clone(&self) -> Self {
288 Self {
289 inner: Arc::clone(&self.inner),
290 }
291 }
292}
293
294#[cfg(test)]
295mod tests {
296 use super::*;
297
298 #[test]
299 fn test_buffer_acquisition_and_release() {
300 let mut manager = BufferManager::new();
301
302 let buffer1 = manager.acquire_string_buffer();
304 assert_eq!(manager.stats().total_acquisitions, 1);
305 assert_eq!(manager.stats().pool_misses, 1); manager.release_string_buffer(buffer1);
309 assert_eq!(manager.stats().total_releases, 1);
310 assert_eq!(manager.pool_size(), 1);
311
312 let buffer2 = manager.acquire_string_buffer();
314 assert_eq!(manager.stats().total_acquisitions, 2);
315 assert_eq!(manager.stats().pool_hits, 1); assert_eq!(manager.pool_size(), 0);
317
318 manager.release_string_buffer(buffer2);
319 }
320
321 #[test]
322 fn test_buffer_pool_limit() {
323 let mut manager = BufferManager::with_capacity(2);
324
325 let buf1 = manager.acquire_string_buffer();
327 let buf2 = manager.acquire_string_buffer();
328 let buf3 = manager.acquire_string_buffer();
329
330 manager.release_string_buffer(buf1);
331 manager.release_string_buffer(buf2);
332 manager.release_string_buffer(buf3);
333
334 assert_eq!(manager.pool_size(), 2);
336 }
337
338 #[test]
339 fn test_buffer_cleared_on_reuse() {
340 let mut manager = BufferManager::new();
341
342 let mut buffer = manager.acquire_string_buffer();
343 buffer.push_str("old content");
344 manager.release_string_buffer(buffer);
345
346 let reused_buffer = manager.acquire_string_buffer();
347 assert_eq!(reused_buffer.len(), 0); assert_eq!(reused_buffer, "");
349
350 manager.release_string_buffer(reused_buffer);
351 }
352
353 #[test]
354 fn test_hit_rate() {
355 let mut manager = BufferManager::new();
356
357 let buf1 = manager.acquire_string_buffer();
358 manager.release_string_buffer(buf1);
359
360 let buf2 = manager.acquire_string_buffer(); manager.release_string_buffer(buf2);
362
363 let buf3 = manager.acquire_string_buffer(); manager.release_string_buffer(buf3);
365
366 let hit_rate = manager.hit_rate();
368 assert!((hit_rate - 0.666).abs() < 0.01);
369 }
370
371 #[test]
372 fn test_blank_node_id_generation() {
373 let mut manager = BufferManager::new();
374
375 let id1 = manager.generate_blank_node_id(0);
376 assert_eq!(id1, "_:b0");
377
378 let id2 = manager.generate_blank_node_id(42);
379 assert_eq!(id2, "_:b42");
380
381 let id3 = manager.generate_blank_node_id(999);
382 assert_eq!(id3, "_:b999");
383 }
384
385 #[test]
386 fn test_prefixed_name_expansion() {
387 let mut manager = BufferManager::new();
388
389 let iri = manager.expand_prefixed_name("ex", "Person", "http://example.org/");
390 assert_eq!(iri, "http://example.org/Person");
391
392 let iri2 = manager.expand_prefixed_name(
393 "rdf",
394 "type",
395 "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
396 );
397 assert_eq!(iri2, "http://www.w3.org/1999/02/22-rdf-syntax-ns#type");
398 }
399
400 #[test]
401 fn test_buffer_capacity_hint() {
402 let mut manager = BufferManager::new();
403
404 let buffer = manager.acquire_string_buffer_with_capacity(1024);
405 assert!(buffer.capacity() >= 1024);
406
407 manager.release_string_buffer(buffer);
408
409 let buffer2 = manager.acquire_string_buffer_with_capacity(512);
411 assert!(buffer2.capacity() >= 512); manager.release_string_buffer(buffer2);
414 }
415
416 #[test]
417 fn test_stats_report() {
418 let mut manager = BufferManager::new();
419
420 let buf = manager.acquire_string_buffer();
421 manager.release_string_buffer(buf);
422
423 let report = manager.stats().report();
424 assert!(report.contains("Total acquisitions: 1"));
425 assert!(report.contains("Pool hits: 0"));
426 assert!(report.contains("Pool misses: 1"));
427 assert!(report.contains("Total releases: 1"));
428 }
429
430 #[test]
431 fn test_clear() {
432 let mut manager = BufferManager::new();
433
434 let buf = manager.acquire_string_buffer();
435 manager.release_string_buffer(buf);
436
437 assert_eq!(manager.pool_size(), 1);
438 assert_eq!(manager.stats().total_acquisitions, 1);
439
440 manager.clear();
441
442 assert_eq!(manager.pool_size(), 0);
443 assert_eq!(manager.stats().total_acquisitions, 0);
444 }
445
446 #[test]
447 fn test_global_buffer_manager() {
448 let manager = GlobalBufferManager::new();
449
450 let buf1 = manager.acquire_string_buffer();
451 manager.release_string_buffer(buf1);
452
453 let buf2 = manager.acquire_string_buffer();
454 manager.release_string_buffer(buf2);
455
456 assert!(manager.hit_rate() > 0.0);
458 }
459
460 #[test]
461 fn test_global_buffer_manager_clone() {
462 let manager1 = GlobalBufferManager::new();
463 let manager2 = manager1.clone();
464
465 let buf = manager1.acquire_string_buffer();
466 manager2.release_string_buffer(buf);
467
468 assert_eq!(manager1.stats().total_releases, 1);
470 assert_eq!(manager2.stats().total_releases, 1);
471 }
472}