rsjsonnet_lang/interner/
mod.rs

1//! A reference-count based string interner.
2//!
3//! # Example
4//!
5//! ```
6//! let arena = rsjsonnet_lang::arena::Arena::new();
7//! let interner = rsjsonnet_lang::interner::StrInterner::new();
8//!
9//! let hello1 = interner.intern(&arena, "hello");
10//! let world1 = interner.intern(&arena, "world");
11//!
12//! // Interned strings preserve their value
13//! assert_eq!(hello1.value(), "hello");
14//! assert_eq!(world1.value(), "world");
15//!
16//! // Different strings are different
17//! assert_ne!(hello1, world1);
18//!
19//! // Interned strings are reference counted
20//! let hello2 = hello1.clone(); // cheap
21//! assert_eq!(hello1, hello2);
22//!
23//! // Interning a string again will return a reference to
24//! // the existing interned string
25//! let hello3 = interner.intern(&arena, "hello");
26//! assert_eq!(hello1, hello3);
27//! ```
28
29use crate::arena::Arena;
30
31mod inner;
32
33impl<'a> inner::Internable<'a> for str {
34    type Key = str;
35    type Container = &'a str;
36
37    #[inline]
38    fn get(this: &&'a str) -> &'a str {
39        this
40    }
41
42    #[inline]
43    fn key<'b>(this: &'b &'a str) -> &'b str {
44        this
45    }
46}
47
48impl<'a> inner::InternAs<'a, str> for &str {
49    #[inline]
50    fn key(&self) -> &str {
51        self
52    }
53
54    #[inline]
55    fn convert(self, arena: &'a Arena) -> &'a &'a str {
56        arena.alloc(arena.alloc_str(self))
57    }
58}
59
60/// The string interner. See the [module level documentation](self) for more.
61pub struct StrInterner<'a> {
62    inner: inner::Interner<'a, str>,
63}
64
65/// An interned string.
66///
67/// Implements [`Eq`], [`Ord`] and [`Hash`](std::hash::Hash). Note that
68/// comparison and hashing is done on the internal pointer value, not the actual
69/// string value. This means that:
70///
71/// * It does not make sense to compare [`InternedStr`] values from different
72///   [`StrInterner`] instances,
73/// * [`Ord`] should not be relied to be deterministic.
74///
75/// ```
76/// let arena = rsjsonnet_lang::arena::Arena::new();
77/// let interner = rsjsonnet_lang::interner::StrInterner::new();
78///
79/// let a1 = interner.intern(&arena, "a");
80/// let b1 = interner.intern(&arena, "b");
81///
82/// let a2 = interner.intern(&arena, "a");
83/// let b2 = interner.intern(&arena, "b");
84///
85/// // Of course, they are equal.
86/// assert_eq!(a1, a2);
87///
88/// // "a" and "b" are not equal, but there is no guarantee on the exact ordering.
89/// let cmp1 = a1.cmp(&b1);
90/// assert_ne!(cmp1, std::cmp::Ordering::Equal);
91///
92/// // But the ordering is consistent because they came from the same interner.
93/// let cmp2 = a2.cmp(&b2);
94/// assert_eq!(cmp1, cmp2);
95/// ```
96#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
97pub struct InternedStr<'a> {
98    inner: inner::Interned<'a, str>,
99}
100
101impl Default for StrInterner<'_> {
102    #[inline]
103    fn default() -> Self {
104        Self::new()
105    }
106}
107
108impl<'a> StrInterner<'a> {
109    /// Creates a new [`StrInterner`].
110    pub fn new() -> Self {
111        Self {
112            inner: inner::Interner::new(),
113        }
114    }
115
116    /// Interns a string or returns a reference an already interned string.
117    ///
118    /// # Example
119    ///
120    /// ```
121    /// let arena = rsjsonnet_lang::arena::Arena::new();
122    /// let interner = rsjsonnet_lang::interner::StrInterner::new();
123    ///
124    /// let hello = interner.intern(&arena, "hello");
125    /// assert_eq!(hello.value(), "hello");
126    /// ```
127    pub fn intern(&self, arena: &'a Arena, value: &str) -> InternedStr<'a> {
128        InternedStr {
129            inner: self.inner.intern(arena, value),
130        }
131    }
132
133    /// Returns a reference to an already interned string if it exists.
134    ///
135    /// # Example
136    ///
137    /// ```
138    /// let arena = rsjsonnet_lang::arena::Arena::new();
139    /// let interner = rsjsonnet_lang::interner::StrInterner::new();
140    ///
141    /// let hello1 = interner.intern(&arena, "hello");
142    /// assert_eq!(hello1.value(), "hello");
143    ///
144    /// let hello2 = interner.get_interned("hello").unwrap();
145    /// assert_eq!(hello2.value(), "hello");
146    ///
147    /// let world = interner.get_interned("world");
148    /// assert!(world.is_none()); // Not previously interned
149    /// ```
150    pub fn get_interned(&self, value: &str) -> Option<InternedStr<'a>> {
151        self.inner
152            .get_interned(value)
153            .map(|v| InternedStr { inner: v })
154    }
155}
156
157impl<'a> InternedStr<'a> {
158    /// Returns the underlying string value.
159    #[inline]
160    pub fn value(&self) -> &'a str {
161        self.inner.value()
162    }
163}
164
165impl std::fmt::Debug for InternedStr<'_> {
166    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
167        self.value().fmt(f)
168    }
169}
170
171/// Similar to [`InternedStr`], but [`Ord`] and [`PartialOrd`] will compare the
172/// actual string values.
173#[derive(Copy, Clone, PartialEq, Eq)]
174pub(crate) struct SortedInternedStr<'a>(pub(crate) InternedStr<'a>);
175
176impl PartialOrd for SortedInternedStr<'_> {
177    #[inline]
178    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
179        Some(self.cmp(other))
180    }
181
182    #[inline]
183    fn lt(&self, other: &Self) -> bool {
184        self.0.value() < other.0.value()
185    }
186
187    #[inline]
188    fn le(&self, other: &Self) -> bool {
189        self.0.value() <= other.0.value()
190    }
191
192    #[inline]
193    fn gt(&self, other: &Self) -> bool {
194        self.0.value() > other.0.value()
195    }
196
197    #[inline]
198    fn ge(&self, other: &Self) -> bool {
199        self.0.value() >= other.0.value()
200    }
201}
202
203impl Ord for SortedInternedStr<'_> {
204    #[inline]
205    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
206        self.0.value().cmp(other.0.value())
207    }
208}