tor_basic_utils/
intern.rs1use std::hash::Hash;
4use std::sync::{Arc, Mutex, MutexGuard, OnceLock, Weak};
5
6type WeakHashSet<T> = weak_table::WeakHashSet<T, std::hash::RandomState>;
10
11pub struct InternCache<T: ?Sized> {
22 cache: OnceLock<Mutex<WeakHashSet<Weak<T>>>>,
26}
27
28impl<T: ?Sized> InternCache<T> {
29 pub const fn new() -> Self {
31 InternCache {
32 cache: OnceLock::new(),
33 }
34 }
35}
36
37impl<T: ?Sized> Default for InternCache<T> {
38 fn default() -> Self {
39 Self::new()
40 }
41}
42
43impl<T: Eq + Hash + ?Sized> InternCache<T> {
44 fn cache(&self) -> MutexGuard<'_, WeakHashSet<Weak<T>>> {
46 let cache = self.cache.get_or_init(|| Mutex::new(WeakHashSet::new()));
47 cache.lock().expect("Poisoned lock lock for cache")
48 }
49}
50
51impl<T: Eq + Hash> InternCache<T> {
52 pub fn intern(&self, value: T) -> Arc<T> {
58 let mut cache = self.cache();
59 if let Some(pp) = cache.get(&value) {
60 pp
61 } else {
62 let arc = Arc::new(value);
63 cache.insert(Arc::clone(&arc));
64 arc
65 }
66 }
67}
68
69impl<T: Hash + Eq + ?Sized> InternCache<T> {
70 pub fn intern_ref<'a, V>(&self, value: &'a V) -> Arc<T>
75 where
76 V: Hash + Eq + ?Sized,
77 &'a V: Into<Arc<T>>,
78 T: std::borrow::Borrow<V>,
79 {
80 let mut cache = self.cache();
81 if let Some(arc) = cache.get(value) {
82 arc
83 } else {
84 let arc = value.into();
85 cache.insert(Arc::clone(&arc));
86 arc
87 }
88 }
89}
90
91#[cfg(test)]
92mod test {
93 #![allow(clippy::bool_assert_comparison)]
95 #![allow(clippy::clone_on_copy)]
96 #![allow(clippy::dbg_macro)]
97 #![allow(clippy::mixed_attributes_style)]
98 #![allow(clippy::print_stderr)]
99 #![allow(clippy::print_stdout)]
100 #![allow(clippy::single_char_pattern)]
101 #![allow(clippy::unwrap_used)]
102 #![allow(clippy::unchecked_time_subtraction)]
103 #![allow(clippy::useless_vec)]
104 #![allow(clippy::needless_pass_by_value)]
105 use super::*;
107
108 #[test]
109 fn interning_by_value() {
110 let c: InternCache<String> = InternCache::new();
112
113 let s1 = c.intern("abc".to_string());
114 let s2 = c.intern("def".to_string());
115 let s3 = c.intern("abc".to_string());
116 assert!(Arc::ptr_eq(&s1, &s3));
117 assert!(!Arc::ptr_eq(&s1, &s2));
118 assert_eq!(s2.as_ref(), "def");
119 assert_eq!(s3.as_ref(), "abc");
120 }
121
122 #[test]
123 fn interning_by_ref() {
124 let c: InternCache<str> = InternCache::new();
126
127 let s1 = c.intern_ref("abc");
128 let s2 = c.intern_ref("def");
129 let s3 = c.intern_ref("abc");
130 assert!(Arc::ptr_eq(&s1, &s3));
131 assert!(!Arc::ptr_eq(&s1, &s2));
132 assert_eq!(&*s2, "def");
133 assert_eq!(&*s3, "abc");
134 }
135}