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}