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