tor_netdoc/util/
intern.rs
1use std::hash::Hash;
4use std::sync::{Arc, Mutex, MutexGuard, OnceLock, Weak};
5use weak_table::WeakHashSet;
6
7pub(crate) struct InternCache<T: ?Sized> {
18 cache: OnceLock<Mutex<WeakHashSet<Weak<T>>>>,
20}
21
22impl<T: ?Sized> InternCache<T> {
23 pub(crate) const fn new() -> Self {
25 InternCache {
26 cache: OnceLock::new(),
27 }
28 }
29}
30
31impl<T: Eq + Hash + ?Sized> InternCache<T> {
32 fn cache(&self) -> MutexGuard<'_, WeakHashSet<Weak<T>>> {
34 let cache = self.cache.get_or_init(|| Mutex::new(WeakHashSet::new()));
35 cache.lock().expect("Poisoned lock lock for cache")
36 }
37}
38
39impl<T: Eq + Hash> InternCache<T> {
40 pub(crate) fn intern(&self, value: T) -> Arc<T> {
46 let mut cache = self.cache();
47 if let Some(pp) = cache.get(&value) {
48 pp
49 } else {
50 let arc = Arc::new(value);
51 cache.insert(Arc::clone(&arc));
52 arc
53 }
54 }
55}
56
57impl<T: Hash + Eq + ?Sized> InternCache<T> {
58 pub(crate) fn intern_ref<'a, V>(&self, value: &'a V) -> Arc<T>
63 where
64 V: Hash + Eq + ?Sized,
65 &'a V: Into<Arc<T>>,
66 T: std::borrow::Borrow<V>,
67 {
68 let mut cache = self.cache();
69 if let Some(arc) = cache.get(value) {
70 arc
71 } else {
72 let arc = value.into();
73 cache.insert(Arc::clone(&arc));
74 arc
75 }
76 }
77}
78
79#[cfg(test)]
80mod test {
81 #![allow(clippy::bool_assert_comparison)]
83 #![allow(clippy::clone_on_copy)]
84 #![allow(clippy::dbg_macro)]
85 #![allow(clippy::mixed_attributes_style)]
86 #![allow(clippy::print_stderr)]
87 #![allow(clippy::print_stdout)]
88 #![allow(clippy::single_char_pattern)]
89 #![allow(clippy::unwrap_used)]
90 #![allow(clippy::unchecked_duration_subtraction)]
91 #![allow(clippy::useless_vec)]
92 #![allow(clippy::needless_pass_by_value)]
93 use super::*;
95
96 #[test]
97 fn interning_by_value() {
98 let c: InternCache<String> = InternCache::new();
100
101 let s1 = c.intern("abc".to_string());
102 let s2 = c.intern("def".to_string());
103 let s3 = c.intern("abc".to_string());
104 assert!(Arc::ptr_eq(&s1, &s3));
105 assert!(!Arc::ptr_eq(&s1, &s2));
106 assert_eq!(s2.as_ref(), "def");
107 assert_eq!(s3.as_ref(), "abc");
108 }
109
110 #[test]
111 fn interning_by_ref() {
112 let c: InternCache<str> = InternCache::new();
114
115 let s1 = c.intern_ref("abc");
116 let s2 = c.intern_ref("def");
117 let s3 = c.intern_ref("abc");
118 assert!(Arc::ptr_eq(&s1, &s3));
119 assert!(!Arc::ptr_eq(&s1, &s2));
120 assert_eq!(&*s2, "def");
121 assert_eq!(&*s3, "abc");
122 }
123}