forma/composition/
interner.rs
1use std::hash::Hash;
6use std::ops::Deref;
7use std::rc::Rc;
8
9use rustc_hash::FxHashSet;
10
11#[derive(Debug)]
12pub struct Interned<T: Eq + Hash> {
13 data: Rc<T>,
14}
15
16impl<T: Eq + Hash> Deref for Interned<T> {
17 type Target = T;
18
19 fn deref(&self) -> &Self::Target {
20 &self.data
21 }
22}
23
24unsafe impl<T: Eq + Hash + Sync> Sync for Interned<T> {}
26
27#[derive(Debug, Default)]
28pub struct Interner<T> {
29 pointers: FxHashSet<Rc<T>>,
32}
33
34impl<T: Eq + Hash> Interner<T> {
35 pub fn get(&mut self, val: T) -> Interned<T> {
36 if let Some(rc) = self.pointers.get(&val) {
37 return Interned { data: Rc::clone(rc) };
38 }
39
40 let data = Rc::new(val);
41
42 self.pointers.insert(Rc::clone(&data));
43
44 Interned { data }
45 }
46
47 pub fn compact(&mut self) {
48 self.pointers.retain(|rc| Rc::strong_count(rc) > 1);
49 }
50}
51
52#[cfg(test)]
53mod tests {
54 use super::*;
55
56 #[test]
57 fn interned_same_ptr() {
58 let mut interner = Interner::default();
59
60 let foo0 = interner.get(String::from("foo"));
61 let foo1 = interner.get(String::from("foo"));
62 let bar = interner.get(String::from("bar"));
63
64 assert!(Rc::ptr_eq(&foo0.data, &foo1.data));
65 assert!(!Rc::ptr_eq(&foo0.data, &bar.data));
66 }
67
68 #[test]
69 fn interned_drop_data() {
70 let mut interner = Interner::default();
71
72 let foo0 = interner.get(String::from("foo"));
73
74 {
75 let _foo1 = interner.get(String::from("foo"));
76 let _bar = interner.get(String::from("bar"));
77
78 assert_eq!(interner.pointers.len(), 2);
79 }
80
81 assert_eq!(interner.pointers.len(), 2);
82
83 interner.compact();
84
85 assert_eq!(interner.pointers.len(), 1);
86
87 let foo1 = interner.get(String::from("foo"));
88 assert!(Rc::ptr_eq(&foo0.data, &foo1.data));
89 }
90}