reifydb_profiler/
intern.rs1use std::{
5 fmt,
6 sync::atomic::{AtomicU32, Ordering},
7};
8
9use dashmap::DashMap;
10use reifydb_runtime::sync::rwlock::RwLock;
11
12use crate::record::{DIM_UNSET, DimIdx};
13
14pub struct DimInterner {
15 forward: DashMap<String, DimIdx>,
16 reverse: RwLock<Vec<String>>,
17 next: AtomicU32,
18}
19
20impl fmt::Debug for DimInterner {
21 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
22 f.debug_struct("DimInterner").field("len", &self.len()).finish()
23 }
24}
25
26impl Default for DimInterner {
27 fn default() -> Self {
28 Self::new()
29 }
30}
31
32impl DimInterner {
33 pub fn new() -> Self {
34 Self {
35 forward: DashMap::new(),
36 reverse: RwLock::new(vec![String::new()]),
37 next: AtomicU32::new(1),
38 }
39 }
40
41 pub fn get(&self, s: &str) -> Option<DimIdx> {
42 self.forward.get(s).map(|r| *r.value())
43 }
44
45 pub fn intern(&self, s: &str) -> DimIdx {
46 if let Some(idx) = self.get(s) {
47 return idx;
48 }
49 let idx = self.next.fetch_add(1, Ordering::Relaxed);
50 self.forward.insert(s.to_string(), idx);
51 let mut rev = self.reverse.write();
52 if rev.len() as u32 <= idx {
53 rev.resize((idx + 1) as usize, String::new());
54 }
55 rev[idx as usize] = s.to_string();
56 idx
57 }
58
59 pub fn resolve(&self, idx: DimIdx) -> Option<String> {
60 if idx == DIM_UNSET {
61 return None;
62 }
63 let rev = self.reverse.read();
64 rev.get(idx as usize).filter(|s| !s.is_empty()).cloned()
65 }
66
67 pub fn len(&self) -> usize {
68 (self.next.load(Ordering::Relaxed) as usize).saturating_sub(1)
69 }
70
71 pub fn is_empty(&self) -> bool {
72 self.len() == 0
73 }
74}
75
76#[cfg(test)]
77mod tests {
78 use super::*;
79
80 #[test]
81 fn get_on_uninterned_returns_none_without_inserting() {
82 let interner = DimInterner::new();
83 assert_eq!(interner.get("never_seen"), None);
84 assert!(interner.is_empty());
85 }
86
87 #[test]
88 fn intern_assigns_stable_indices() {
89 let interner = DimInterner::new();
90 let a = interner.intern("alpha");
91 let b = interner.intern("beta");
92 let a2 = interner.intern("alpha");
93 assert_eq!(a, a2);
94 assert_ne!(a, b);
95 assert_ne!(a, DIM_UNSET);
96 assert_ne!(b, DIM_UNSET);
97 }
98
99 #[test]
100 fn resolve_returns_original_string() {
101 let interner = DimInterner::new();
102 let idx = interner.intern("hello");
103 assert_eq!(interner.resolve(idx).as_deref(), Some("hello"));
104 assert_eq!(interner.resolve(DIM_UNSET), None);
105 assert_eq!(interner.resolve(99999), None);
106 }
107}