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.buffers.lock().pop().unwrap_or_else(|| {
334 String::with_capacity(self.default_capacity)
335 });
336 PooledBuffer {
337 buffer,
338 pool: self,
339 }
340 }
341
342 fn return_buffer(&self, mut buffer: String) {
344 buffer.clear();
345 if buffer.capacity() <= 4096 {
347 self.buffers.lock().push(buffer);
348 }
349 }
350
351 pub fn available(&self) -> usize {
353 self.buffers.lock().len()
354 }
355
356 pub fn clear(&self) {
358 self.buffers.lock().clear();
359 }
360}
361
362pub struct PooledBuffer<'a> {
366 buffer: String,
367 pool: &'a BufferPool,
368}
369
370impl<'a> PooledBuffer<'a> {
371 pub fn as_mut(&mut self) -> &mut String {
373 &mut self.buffer
374 }
375
376 pub fn take(mut self) -> String {
378 std::mem::take(&mut self.buffer)
379 }
380}
381
382impl<'a> std::ops::Deref for PooledBuffer<'a> {
383 type Target = String;
384
385 fn deref(&self) -> &Self::Target {
386 &self.buffer
387 }
388}
389
390impl<'a> std::ops::DerefMut for PooledBuffer<'a> {
391 fn deref_mut(&mut self) -> &mut Self::Target {
392 &mut self.buffer
393 }
394}
395
396impl<'a> Drop for PooledBuffer<'a> {
397 fn drop(&mut self) {
398 if !self.buffer.is_empty() || self.buffer.capacity() > 0 {
399 let buffer = std::mem::take(&mut self.buffer);
400 self.pool.return_buffer(buffer);
401 }
402 }
403}
404
405#[derive(Debug, Clone, Default)]
411pub struct MemoryStats {
412 pub allocations: u64,
414 pub deallocations: u64,
416 pub current_bytes: usize,
418 pub peak_bytes: usize,
420 pub total_bytes: usize,
422}
423
424impl MemoryStats {
425 pub fn new() -> Self {
427 Self::default()
428 }
429
430 pub fn record_alloc(&mut self, bytes: usize) {
432 self.allocations += 1;
433 self.current_bytes += bytes;
434 self.total_bytes += bytes;
435 if self.current_bytes > self.peak_bytes {
436 self.peak_bytes = self.current_bytes;
437 }
438 }
439
440 pub fn record_dealloc(&mut self, bytes: usize) {
442 self.deallocations += 1;
443 self.current_bytes = self.current_bytes.saturating_sub(bytes);
444 }
445
446 pub fn net_allocations(&self) -> i64 {
448 self.allocations as i64 - self.deallocations as i64
449 }
450}
451
452pub static GLOBAL_STRING_POOL: std::sync::LazyLock<StringPool> =
458 std::sync::LazyLock::new(|| {
459 let pool = StringPool::with_capacity(128);
460 for name in COMMON_FIELD_NAMES {
462 pool.intern(name);
463 }
464 pool
465 });
466
467pub static GLOBAL_BUFFER_POOL: std::sync::LazyLock<BufferPool> =
469 std::sync::LazyLock::new(|| BufferPool::with_capacity(256));
470
471const COMMON_FIELD_NAMES: &[&str] = &[
473 "id", "uuid", "name", "email", "username", "password",
474 "title", "description", "content", "body", "status", "type",
475 "role", "active", "enabled", "deleted", "verified", "published",
476 "count", "score", "priority", "order", "position", "age",
477 "amount", "price", "quantity", "user_id", "post_id", "comment_id",
478 "category_id", "parent_id", "author_id", "owner_id",
479 "created_at", "updated_at", "deleted_at", "published_at",
480 "expires_at", "starts_at", "ends_at", "last_login_at", "verified_at",
481 "slug", "url", "path", "key", "value", "token", "code", "version",
482];
483
484#[inline]
486pub fn intern(s: &str) -> Arc<str> {
487 GLOBAL_STRING_POOL.intern(s)
488}
489
490#[inline]
492pub fn get_buffer() -> PooledBuffer<'static> {
493 GLOBAL_BUFFER_POOL.get()
494}
495
496#[cfg(test)]
497mod tests {
498 use super::*;
499
500 #[test]
501 fn test_string_pool_interning() {
502 let pool = StringPool::new();
503
504 let s1 = pool.intern("hello");
505 let s2 = pool.intern("hello");
506
507 assert!(Arc::ptr_eq(&s1, &s2));
509 assert_eq!(pool.len(), 1);
510 }
511
512 #[test]
513 fn test_string_pool_different_strings() {
514 let pool = StringPool::new();
515
516 let s1 = pool.intern("hello");
517 let s2 = pool.intern("world");
518
519 assert!(!Arc::ptr_eq(&s1, &s2));
520 assert_eq!(pool.len(), 2);
521 }
522
523 #[test]
524 fn test_compact_filter_eq_int() {
525 let filter = CompactFilter::eq_int(Arc::from("id"), 42);
526 let mut offset = 0;
527 let sql = filter.to_sql_postgres(&mut offset);
528 assert_eq!(sql, "id = $1");
529 assert_eq!(offset, 1);
530 }
531
532 #[test]
533 fn test_compact_filter_and() {
534 let filter = CompactFilter::eq_int(Arc::from("id"), 42)
535 .and(CompactFilter::eq_bool(Arc::from("active"), true));
536 let mut offset = 0;
537 let sql = filter.to_sql_postgres(&mut offset);
538 assert_eq!(sql, "(id = $1 AND active = $2)");
539 assert_eq!(offset, 2);
540 }
541
542 #[test]
543 fn test_compact_filter_is_null() {
544 let filter = CompactFilter::is_null(Arc::from("deleted_at"));
545 let mut offset = 0;
546 let sql = filter.to_sql_postgres(&mut offset);
547 assert_eq!(sql, "deleted_at IS NULL");
548 assert_eq!(offset, 0); }
550
551 #[test]
552 fn test_buffer_pool() {
553 let pool = BufferPool::new();
554
555 {
556 let mut buffer = pool.get();
557 buffer.push_str("hello");
558 assert_eq!(&*buffer, "hello");
559 } assert_eq!(pool.available(), 1);
562
563 {
564 let buffer = pool.get();
565 assert!(buffer.is_empty()); }
567 }
568
569 #[test]
570 fn test_global_intern() {
571 let s1 = intern("id");
572 let s2 = intern("id");
573 assert!(Arc::ptr_eq(&s1, &s2));
574 }
575
576 #[test]
577 fn test_memory_stats() {
578 let mut stats = MemoryStats::new();
579
580 stats.record_alloc(100);
581 stats.record_alloc(200);
582 assert_eq!(stats.current_bytes, 300);
583 assert_eq!(stats.peak_bytes, 300);
584
585 stats.record_dealloc(100);
586 assert_eq!(stats.current_bytes, 200);
587 assert_eq!(stats.peak_bytes, 300); }
589}
590