refcount_interner/arc_interner.rs
1use std::sync::Arc;
2use std::hash::Hash;
3use std::collections::HashSet;
4
5/// An interner returning atomically reference-counted pointers to the interned
6/// data
7///
8/// Interned objects will be deallocated when there are no references to them
9/// any more and `shrink_to_fit()` is called on the interner
10///
11/// # Example
12/// ```rust
13/// # use std::sync::Arc;
14/// use refcount_interner::ArcInterner;
15///
16/// let mut interner = ArcInterner::new();
17///
18/// let x = interner.intern(42);
19/// let y = interner.intern(1337);
20///
21/// assert_eq!(*x, 42);
22/// assert_ne!(x, y);
23/// assert!(Arc::ptr_eq(&x, &interner.intern(42)));
24/// ```
25#[derive(Debug)]
26pub struct ArcInterner<T: ?Sized>(HashSet<Arc<T>>);
27
28impl<T: ?Sized> Default for ArcInterner<T> {
29 fn default() -> ArcInterner<T> {
30 ArcInterner(HashSet::new())
31 }
32}
33
34impl<T: ?Sized + Hash + Eq> ArcInterner<T> {
35 /// Create a new, empty interner.
36 ///
37 /// # Example
38 /// ```rust
39 /// # use refcount_interner::ArcInterner;
40 /// let mut interner = ArcInterner::new();
41 /// # let x = interner.intern(42);
42 /// ```
43 pub fn new() -> ArcInterner<T> {
44 Default::default()
45 }
46
47 /// Attempt to get a reference to an already interned object.
48 ///
49 /// If the object has already been interned, an option containing a
50 /// reference to the already interned object will be returned.
51 ///
52 /// If the object has not yet been interned, `None` will be returned.
53 ///
54 /// # Example
55 /// ```rust
56 /// # use std::sync::Arc;
57 /// # use refcount_interner::ArcInterner;
58 /// let mut interner = ArcInterner::new();
59 ///
60 /// let x = interner.intern(42);
61 /// assert_eq!(interner.try_intern(&42), Some(Arc::new(42)));
62 /// assert_eq!(interner.try_intern(&1337), None);
63 /// ```
64 pub fn try_intern(&self, t: &T) -> Option<Arc<T>> {
65 self.0.get(t).cloned()
66 }
67
68 /// Intern a boxed object
69 ///
70 /// This method must be used to intern unsized types, since unsized types
71 /// cannot be passed to `intern()`. The two most common unsized types,
72 /// `&[T]` and `&str` can be interned with `intern_slice()` and
73 /// `intern_str()` as well.
74 ///
75 /// If the object has already been interned, the passed object will be
76 /// dropped and deallocated, and a reference to the already interned object
77 /// will be returned.
78 ///
79 /// If the object has not yet been interned, the passed object will be moved
80 /// into an `Arc<T>`, remembered for future calls to `intern()`, and
81 /// returned.
82 ///
83 /// # Example
84 /// ```rust
85 /// # use refcount_interner::ArcInterner;
86 /// let mut interner = ArcInterner::new();
87 ///
88 /// let x = Box::new(42);
89 /// let y = interner.intern_boxed(x);
90 ///
91 /// assert_eq!(*y, 42);
92 /// ```
93 pub fn intern_boxed(&mut self, t: Box<T>) -> Arc<T> {
94 if let Some(value) = self.0.get(t.as_ref()) {
95 value.clone()
96 } else {
97 let value: Arc<T> = Arc::from(t);
98 self.0.insert(value.clone());
99 value
100 }
101 }
102
103 /// Deallocate all interned objects that are no longer referenced and shrink
104 /// the internal storage to fit.
105 ///
106 /// # Example
107 /// ```rust
108 /// # use std::sync::Arc;
109 /// # use refcount_interner::ArcInterner;
110 /// let mut interner = ArcInterner::new();
111 ///
112 /// let x = interner.intern(42);
113 /// let y = interner.intern(1337);
114 /// let z = y.clone();
115 ///
116 /// drop(x);
117 /// drop(y);
118 ///
119 /// interner.shrink_to_fit();
120 /// assert_eq!(interner.try_intern(&42), None);
121 /// assert_eq!(interner.try_intern(&1337), Some(Arc::new(1337)));
122 /// ```
123 pub fn shrink_to_fit(&mut self) {
124 self.0.retain(|value| Arc::strong_count(value) > 1);
125 self.0.shrink_to_fit();
126 }
127}
128
129impl<T: Sized + Hash + Eq> ArcInterner<T> {
130 /// Intern an owned object
131 ///
132 /// If the object has already been interned, the passed object will be
133 /// dropped, and a reference to the already interned object will be
134 /// returned.
135 ///
136 /// If the object has not yet been interned, the passed object will be moved
137 /// into an `Arc<T>`, remembered for future calls to `intern()`, and
138 /// returned.
139 ///
140 /// # Example
141 /// ```rust
142 /// # use std::sync::Arc;
143 /// # use refcount_interner::ArcInterner;
144 /// let mut interner = ArcInterner::new();
145 ///
146 /// let x = interner.intern(42);
147 /// let y = interner.intern(1337);
148 ///
149 /// assert_eq!(*x, 42);
150 /// assert_ne!(x, y);
151 /// assert!(Arc::ptr_eq(&x, &interner.intern(42)));
152 /// ```
153 pub fn intern(&mut self, t: T) -> Arc<T> {
154 if let Some(value) = self.0.get(&t) {
155 value.clone()
156 } else {
157 let value = Arc::new(t);
158 self.0.insert(value.clone());
159 value
160 }
161 }
162}
163
164impl<T: Sized + Hash + Eq + Clone> ArcInterner<T> {
165 /// Intern a borrowed object, cloning if it has not yet been interned
166 ///
167 /// If the object has already been interned, a reference to the already
168 /// interned object will be returned.
169 ///
170 /// If the object has not yet been interned, the passed object will be moved
171 /// into an `Arc<T>`, remembered for future calls to `intern()`, and
172 /// returned.
173 ///
174 /// # Example
175 /// ```rust
176 /// # use refcount_interner::ArcInterner;
177 /// let mut interner = ArcInterner::new();
178 ///
179 /// let x = 42;
180 /// let y = interner.intern_cloned(&x);
181 ///
182 /// assert_eq!(x, *y);
183 /// ```
184 pub fn intern_cloned(&mut self, t: &T) -> Arc<T> {
185 if let Some(value) = self.0.get(t) {
186 value.clone()
187 } else {
188 let value = Arc::new(t.clone());
189 self.0.insert(value.clone());
190 value
191 }
192 }
193}
194
195impl<T: Sized + Hash + Eq + Clone> ArcInterner<[T]> {
196 /// Intern a slice object
197 ///
198 /// This method can be used to intern slices without boxing them.
199 ///
200 /// If the slice has already been interned, a reference to the already
201 /// interned slice will be returned.
202 ///
203 /// If the slice has not yet been interned, the passed object will be
204 /// cloned into an `Arc<[T]>`, remembered for future calls to `intern()`,
205 /// and returned.
206 ///
207 /// # Example
208 /// ```rust
209 /// # use refcount_interner::ArcInterner;
210 /// let mut interner = ArcInterner::new();
211 ///
212 /// let x = interner.intern_slice(&[1, 2, 3]);
213 ///
214 /// assert_eq!(x.as_ref(), &[1, 2, 3]);
215 /// ```
216 pub fn intern_slice(&mut self, t: &[T]) -> Arc<[T]> {
217 if let Some(value) = self.0.get(t) {
218 value.clone()
219 } else {
220 let value: Arc<[T]> = Arc::from(t);
221 self.0.insert(value.clone());
222 value
223 }
224 }
225
226 /// Intern an owned vector
227 ///
228 /// If the slice behind the vector has already been interned, a reference
229 /// to the already / interned slice will be returned.
230 ///
231 /// If the slice has not yet been interned, the passed vector will be moved
232 /// into an `Arc<[T]>`, remembered for future calls to `intern()`, and
233 /// returned.
234 ///
235 /// # Example
236 /// ```rust
237 ///
238 /// # use refcount_interner::ArcInterner;
239 /// let mut interner = ArcInterner::new();
240 ///
241 /// let v = vec![1, 2, 3];
242 /// let x = interner.intern_vec(v);
243 ///
244 /// assert_eq!(x.as_ref(), &[1, 2, 3]);
245 /// ```
246 pub fn intern_vec(&mut self, t: Vec<T>) -> Arc<[T]> {
247 self.intern_boxed(t.into_boxed_slice())
248 }
249}
250
251impl ArcInterner<str> {
252 /// Intern a string slice
253 ///
254 /// This method can be used to intern string slices without boxing them.
255 ///
256 /// If the string slice has already been interned, a reference to the
257 /// already interned string slice will be returned.
258 ///
259 /// If the string slice has not yet been interned, the passed object will be
260 /// cloned into an `Arc<str>`, remembered for future calls to `intern()`,
261 /// and returned.
262 ///
263 /// # Example
264 /// ```rust
265 /// # use refcount_interner::ArcInterner;
266 /// let mut interner = ArcInterner::new();
267 ///
268 /// let x = interner.intern_str("hello");
269 ///
270 /// assert_eq!(x.as_ref(), "hello");
271 /// ```
272 pub fn intern_str(&mut self, t: &str) -> Arc<str> {
273 if let Some(value) = self.0.get(t) {
274 value.clone()
275 } else {
276 let value: Arc<str> = Arc::from(t);
277 self.0.insert(value.clone());
278 value
279 }
280 }
281
282 /// Intern an owned string
283 ///
284 /// If the string has already been interned, a reference to the already
285 /// interned string slice will be returned.
286 ///
287 /// If the string has not yet been interned, the passed string will be moved
288 /// into an `Arc<str>`, remembered for future calls to `intern()`, and
289 /// returned.
290 ///
291 /// # Example
292 /// ```rust
293 ///
294 /// # use refcount_interner::ArcInterner;
295 /// let mut interner = ArcInterner::new();
296 ///
297 /// let s = String::from("hello");
298 /// let x = interner.intern_string(s);
299 ///
300 /// assert_eq!(x.as_ref(), "hello");
301 /// ```
302 pub fn intern_string(&mut self, t: String) -> Arc<str> {
303 self.intern_boxed(t.into_boxed_str())
304 }
305}