Skip to main content

reifydb_profiler/
intern.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4use 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}