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}