1use parking_lot::Mutex;
30use std::collections::HashSet;
31use std::sync::Arc;
32use tracing::info;
33
34#[derive(Debug, Default)]
42pub struct StringPool {
43 strings: Mutex<HashSet<Arc<str>>>,
44}
45
46impl StringPool {
47 pub fn new() -> Self {
49 Self::default()
50 }
51
52 pub fn with_capacity(capacity: usize) -> Self {
54 info!(capacity, "StringPool initialized");
55 Self {
56 strings: Mutex::new(HashSet::with_capacity(capacity)),
57 }
58 }
59
60 pub fn intern(&self, s: &str) -> Arc<str> {
62 let mut strings = self.strings.lock();
63
64 if let Some(existing) = strings.get(s) {
66 return Arc::clone(existing);
67 }
68
69 let arc: Arc<str> = Arc::from(s);
71 strings.insert(Arc::clone(&arc));
72 arc
73 }
74
75 pub fn len(&self) -> usize {
77 self.strings.lock().len()
78 }
79
80 pub fn is_empty(&self) -> bool {
82 self.strings.lock().is_empty()
83 }
84
85 pub fn clear(&self) {
87 self.strings.lock().clear();
88 }
89
90 pub fn stats(&self) -> PoolStats {
92 let strings = self.strings.lock();
93 let count = strings.len();
94 let total_bytes: usize = strings.iter().map(|s| s.len()).sum();
95 PoolStats {
96 count,
97 total_bytes,
98 avg_bytes: if count > 0 { total_bytes / count } else { 0 },
99 }
100 }
101}
102
103#[derive(Debug, Clone, Default)]
105pub struct PoolStats {
106 pub count: usize,
108 pub total_bytes: usize,
110 pub avg_bytes: usize,
112}
113
114#[derive(Debug, Clone, PartialEq)]
124pub enum CompactFilter {
125 EqInt {
127 field: Arc<str>,
129 value: i64,
131 },
132 EqBool {
134 field: Arc<str>,
136 value: bool,
138 },
139 EqStr {
141 field: Arc<str>,
143 value: Arc<str>,
145 },
146 IsNull {
148 field: Arc<str>,
150 },
151 IsNotNull {
153 field: Arc<str>,
155 },
156 GtInt {
158 field: Arc<str>,
160 value: i64,
162 },
163 LtInt {
165 field: Arc<str>,
167 value: i64,
169 },
170 And(Box<CompactFilter>, Box<CompactFilter>),
172 Or(Box<CompactFilter>, Box<CompactFilter>),
174}
175
176impl CompactFilter {
177 #[inline]
179 pub fn eq_int(field: impl Into<Arc<str>>, value: i64) -> Self {
180 Self::EqInt {
181 field: field.into(),
182 value,
183 }
184 }
185
186 #[inline]
188 pub fn eq_bool(field: impl Into<Arc<str>>, value: bool) -> Self {
189 Self::EqBool {
190 field: field.into(),
191 value,
192 }
193 }
194
195 #[inline]
197 pub fn eq_str(field: impl Into<Arc<str>>, value: impl Into<Arc<str>>) -> Self {
198 Self::EqStr {
199 field: field.into(),
200 value: value.into(),
201 }
202 }
203
204 #[inline]
206 pub fn is_null(field: impl Into<Arc<str>>) -> Self {
207 Self::IsNull {
208 field: field.into(),
209 }
210 }
211
212 #[inline]
214 pub fn is_not_null(field: impl Into<Arc<str>>) -> Self {
215 Self::IsNotNull {
216 field: field.into(),
217 }
218 }
219
220 #[inline]
222 pub fn gt_int(field: impl Into<Arc<str>>, value: i64) -> Self {
223 Self::GtInt {
224 field: field.into(),
225 value,
226 }
227 }
228
229 #[inline]
231 pub fn lt_int(field: impl Into<Arc<str>>, value: i64) -> Self {
232 Self::LtInt {
233 field: field.into(),
234 value,
235 }
236 }
237
238 #[inline]
240 pub fn and(self, other: Self) -> Self {
241 Self::And(Box::new(self), Box::new(other))
242 }
243
244 #[inline]
246 pub fn or(self, other: Self) -> Self {
247 Self::Or(Box::new(self), Box::new(other))
248 }
249
250 pub fn to_sql_postgres(&self, param_offset: &mut usize) -> String {
252 match self {
253 Self::EqInt { field, .. } => {
254 *param_offset += 1;
255 format!("{} = ${}", field, *param_offset)
256 }
257 Self::EqBool { field, .. } => {
258 *param_offset += 1;
259 format!("{} = ${}", field, *param_offset)
260 }
261 Self::EqStr { field, .. } => {
262 *param_offset += 1;
263 format!("{} = ${}", field, *param_offset)
264 }
265 Self::IsNull { field } => format!("{} IS NULL", field),
266 Self::IsNotNull { field } => format!("{} IS NOT NULL", field),
267 Self::GtInt { field, .. } => {
268 *param_offset += 1;
269 format!("{} > ${}", field, *param_offset)
270 }
271 Self::LtInt { field, .. } => {
272 *param_offset += 1;
273 format!("{} < ${}", field, *param_offset)
274 }
275 Self::And(left, right) => {
276 let left_sql = left.to_sql_postgres(param_offset);
277 let right_sql = right.to_sql_postgres(param_offset);
278 format!("({} AND {})", left_sql, right_sql)
279 }
280 Self::Or(left, right) => {
281 let left_sql = left.to_sql_postgres(param_offset);
282 let right_sql = right.to_sql_postgres(param_offset);
283 format!("({} OR {})", left_sql, right_sql)
284 }
285 }
286 }
287
288 pub fn size_bytes(&self) -> usize {
290 match self {
291 Self::EqInt { .. } | Self::GtInt { .. } | Self::LtInt { .. } => 24, Self::EqBool { .. } => 17, Self::EqStr { field, value } => 16 + field.len() + value.len(),
294 Self::IsNull { .. } | Self::IsNotNull { .. } => 16, Self::And(l, r) | Self::Or(l, r) => 16 + l.size_bytes() + r.size_bytes(),
296 }
297 }
298}
299
300#[derive(Debug, Default)]
308pub struct BufferPool {
309 buffers: Mutex<Vec<String>>,
310 default_capacity: usize,
311}
312
313impl BufferPool {
314 pub fn new() -> Self {
316 Self {
317 buffers: Mutex::new(Vec::new()),
318 default_capacity: 256,
319 }
320 }
321
322 pub fn with_capacity(default_capacity: usize) -> Self {
324 info!(default_capacity, "BufferPool initialized");
325 Self {
326 buffers: Mutex::new(Vec::new()),
327 default_capacity,
328 }
329 }
330
331 pub fn get(&self) -> PooledBuffer<'_> {
333 let buffer = self
334 .buffers
335 .lock()
336 .pop()
337 .unwrap_or_else(|| String::with_capacity(self.default_capacity));
338 PooledBuffer { buffer, pool: self }
339 }
340
341 fn return_buffer(&self, mut buffer: String) {
343 buffer.clear();
344 if buffer.capacity() <= 4096 {
346 self.buffers.lock().push(buffer);
347 }
348 }
349
350 pub fn available(&self) -> usize {
352 self.buffers.lock().len()
353 }
354
355 pub fn clear(&self) {
357 self.buffers.lock().clear();
358 }
359}
360
361pub struct PooledBuffer<'a> {
365 buffer: String,
366 pool: &'a BufferPool,
367}
368
369impl<'a> PooledBuffer<'a> {
370 pub fn as_mut_str(&mut self) -> &mut String {
372 &mut self.buffer
373 }
374
375 pub fn take(mut self) -> String {
377 std::mem::take(&mut self.buffer)
378 }
379}
380
381impl<'a> std::ops::Deref for PooledBuffer<'a> {
382 type Target = String;
383
384 fn deref(&self) -> &Self::Target {
385 &self.buffer
386 }
387}
388
389impl<'a> std::ops::DerefMut for PooledBuffer<'a> {
390 fn deref_mut(&mut self) -> &mut Self::Target {
391 &mut self.buffer
392 }
393}
394
395impl<'a> Drop for PooledBuffer<'a> {
396 fn drop(&mut self) {
397 if !self.buffer.is_empty() || self.buffer.capacity() > 0 {
398 let buffer = std::mem::take(&mut self.buffer);
399 self.pool.return_buffer(buffer);
400 }
401 }
402}
403
404#[derive(Debug, Clone, Default)]
410pub struct MemoryStats {
411 pub allocations: u64,
413 pub deallocations: u64,
415 pub current_bytes: usize,
417 pub peak_bytes: usize,
419 pub total_bytes: usize,
421}
422
423impl MemoryStats {
424 pub fn new() -> Self {
426 Self::default()
427 }
428
429 pub fn record_alloc(&mut self, bytes: usize) {
431 self.allocations += 1;
432 self.current_bytes += bytes;
433 self.total_bytes += bytes;
434 if self.current_bytes > self.peak_bytes {
435 self.peak_bytes = self.current_bytes;
436 }
437 }
438
439 pub fn record_dealloc(&mut self, bytes: usize) {
441 self.deallocations += 1;
442 self.current_bytes = self.current_bytes.saturating_sub(bytes);
443 }
444
445 pub fn net_allocations(&self) -> i64 {
447 self.allocations as i64 - self.deallocations as i64
448 }
449}
450
451pub static GLOBAL_STRING_POOL: std::sync::LazyLock<StringPool> = std::sync::LazyLock::new(|| {
457 let pool = StringPool::with_capacity(128);
458 for name in COMMON_FIELD_NAMES {
460 pool.intern(name);
461 }
462 pool
463});
464
465pub static GLOBAL_BUFFER_POOL: std::sync::LazyLock<BufferPool> =
467 std::sync::LazyLock::new(|| BufferPool::with_capacity(256));
468
469const COMMON_FIELD_NAMES: &[&str] = &[
471 "id",
472 "uuid",
473 "name",
474 "email",
475 "username",
476 "password",
477 "title",
478 "description",
479 "content",
480 "body",
481 "status",
482 "type",
483 "role",
484 "active",
485 "enabled",
486 "deleted",
487 "verified",
488 "published",
489 "count",
490 "score",
491 "priority",
492 "order",
493 "position",
494 "age",
495 "amount",
496 "price",
497 "quantity",
498 "user_id",
499 "post_id",
500 "comment_id",
501 "category_id",
502 "parent_id",
503 "author_id",
504 "owner_id",
505 "created_at",
506 "updated_at",
507 "deleted_at",
508 "published_at",
509 "expires_at",
510 "starts_at",
511 "ends_at",
512 "last_login_at",
513 "verified_at",
514 "slug",
515 "url",
516 "path",
517 "key",
518 "value",
519 "token",
520 "code",
521 "version",
522];
523
524#[inline]
526pub fn intern(s: &str) -> Arc<str> {
527 GLOBAL_STRING_POOL.intern(s)
528}
529
530#[inline]
532pub fn get_buffer() -> PooledBuffer<'static> {
533 GLOBAL_BUFFER_POOL.get()
534}
535
536#[cfg(test)]
537mod tests {
538 use super::*;
539
540 #[test]
541 fn test_string_pool_interning() {
542 let pool = StringPool::new();
543
544 let s1 = pool.intern("hello");
545 let s2 = pool.intern("hello");
546
547 assert!(Arc::ptr_eq(&s1, &s2));
549 assert_eq!(pool.len(), 1);
550 }
551
552 #[test]
553 fn test_string_pool_different_strings() {
554 let pool = StringPool::new();
555
556 let s1 = pool.intern("hello");
557 let s2 = pool.intern("world");
558
559 assert!(!Arc::ptr_eq(&s1, &s2));
560 assert_eq!(pool.len(), 2);
561 }
562
563 #[test]
564 fn test_compact_filter_eq_int() {
565 let filter = CompactFilter::eq_int(Arc::from("id"), 42);
566 let mut offset = 0;
567 let sql = filter.to_sql_postgres(&mut offset);
568 assert_eq!(sql, "id = $1");
569 assert_eq!(offset, 1);
570 }
571
572 #[test]
573 fn test_compact_filter_and() {
574 let filter = CompactFilter::eq_int(Arc::from("id"), 42)
575 .and(CompactFilter::eq_bool(Arc::from("active"), true));
576 let mut offset = 0;
577 let sql = filter.to_sql_postgres(&mut offset);
578 assert_eq!(sql, "(id = $1 AND active = $2)");
579 assert_eq!(offset, 2);
580 }
581
582 #[test]
583 fn test_compact_filter_is_null() {
584 let filter = CompactFilter::is_null(Arc::from("deleted_at"));
585 let mut offset = 0;
586 let sql = filter.to_sql_postgres(&mut offset);
587 assert_eq!(sql, "deleted_at IS NULL");
588 assert_eq!(offset, 0); }
590
591 #[test]
592 fn test_buffer_pool() {
593 let pool = BufferPool::new();
594
595 {
596 let mut buffer = pool.get();
597 buffer.push_str("hello");
598 assert_eq!(&*buffer, "hello");
599 } assert_eq!(pool.available(), 1);
602
603 {
604 let buffer = pool.get();
605 assert!(buffer.is_empty()); }
607 }
608
609 #[test]
610 fn test_global_intern() {
611 let s1 = intern("id");
612 let s2 = intern("id");
613 assert!(Arc::ptr_eq(&s1, &s2));
614 }
615
616 #[test]
617 fn test_memory_stats() {
618 let mut stats = MemoryStats::new();
619
620 stats.record_alloc(100);
621 stats.record_alloc(200);
622 assert_eq!(stats.current_bytes, 300);
623 assert_eq!(stats.peak_bytes, 300);
624
625 stats.record_dealloc(100);
626 assert_eq!(stats.current_bytes, 200);
627 assert_eq!(stats.peak_bytes, 300); }
629}