1 //! Simple string interning.
2 
3 use crate::{
4     collections::{HashMap, String, Vec},
5     error::OutOfMemory,
6     prelude::*,
7 };
8 use core::{fmt, mem, num::NonZeroU32};
9 use wasmtime_core::alloc::TryClone;
10 
11 /// An interned string associated with a particular string in a `StringPool`.
12 ///
13 /// Allows for $O(1)$ equality tests, $O(1)$ hashing, and $O(1)$
14 /// arbitrary-but-stable ordering.
15 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
16 pub struct Atom {
17     index: NonZeroU32,
18 }
19 
20 /// A pool of interned strings.
21 ///
22 /// Insert new strings with [`StringPool::insert`] to get an `Atom` that is
23 /// unique per string within the context of the associated pool.
24 ///
25 /// Once you have interned a string into the pool and have its `Atom`, you can
26 /// get the interned string slice via `&pool[atom]` or `pool.get(atom)`.
27 ///
28 /// In general, there are no correctness protections against indexing into a
29 /// different `StringPool` from the one that the `Atom` was not allocated
30 /// inside. Doing so is memory safe but may panic or otherwise return incorrect
31 /// results.
32 #[derive(Default)]
33 pub struct StringPool {
34     /// A map from each string in this pool (as an unsafe borrow from
35     /// `self.strings`) to its `Atom`.
36     map: mem::ManuallyDrop<HashMap<&'static str, Atom>>,
37 
38     /// Strings in this pool. These must never be mutated or reallocated once
39     /// inserted.
40     strings: mem::ManuallyDrop<Vec<Box<str>>>,
41 }
42 
43 impl Drop for StringPool {
44     fn drop(&mut self) {
45         // Ensure that `self.map` is dropped before `self.strings`, since
46         // `self.map` borrows from `self.strings`.
47         //
48         // Safety: Neither field will be used again.
49         unsafe {
50             mem::ManuallyDrop::drop(&mut self.map);
51             mem::ManuallyDrop::drop(&mut self.strings);
52         }
53     }
54 }
55 
56 impl fmt::Debug for StringPool {
57     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
58         struct Strings<'a>(&'a StringPool);
59         impl fmt::Debug for Strings<'_> {
60             fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61                 f.debug_map()
62                     .entries(
63                         self.0
64                             .strings
65                             .iter()
66                             .enumerate()
67                             .map(|(i, s)| (Atom::new(i), s)),
68                     )
69                     .finish()
70             }
71         }
72 
73         f.debug_struct("StringPool")
74             .field("strings", &Strings(self))
75             .finish()
76     }
77 }
78 
79 impl TryClone for StringPool {
80     fn try_clone(&self) -> Result<Self, OutOfMemory> {
81         Ok(StringPool {
82             map: self.map.try_clone()?,
83             strings: self.strings.try_clone()?,
84         })
85     }
86 }
87 
88 impl TryClone for Atom {
89     fn try_clone(&self) -> Result<Self, OutOfMemory> {
90         Ok(*self)
91     }
92 }
93 
94 impl core::ops::Index<Atom> for StringPool {
95     type Output = str;
96 
97     #[inline]
98     #[track_caller]
99     fn index(&self, atom: Atom) -> &Self::Output {
100         self.get(atom).unwrap()
101     }
102 }
103 
104 impl serde::ser::Serialize for StringPool {
105     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
106     where
107         S: serde::Serializer,
108     {
109         serde::ser::Serialize::serialize(&*self.strings, serializer)
110     }
111 }
112 
113 impl<'de> serde::de::Deserialize<'de> for StringPool {
114     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
115     where
116         D: serde::Deserializer<'de>,
117     {
118         struct Visitor;
119         impl<'de> serde::de::Visitor<'de> for Visitor {
120             type Value = StringPool;
121 
122             fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
123                 f.write_str("a `StringPool` sequence of strings")
124             }
125 
126             fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
127             where
128                 A: serde::de::SeqAccess<'de>,
129             {
130                 use serde::de::Error as _;
131 
132                 let mut pool = StringPool::new();
133 
134                 if let Some(len) = seq.size_hint() {
135                     pool.map.reserve(len).map_err(|oom| A::Error::custom(oom))?;
136                     pool.strings
137                         .reserve(len)
138                         .map_err(|oom| A::Error::custom(oom))?;
139                 }
140 
141                 while let Some(s) = seq.next_element::<String>()? {
142                     debug_assert_eq!(s.len(), s.capacity());
143                     let s = s.into_boxed_str().map_err(|oom| A::Error::custom(oom))?;
144                     if !pool.map.contains_key(&*s) {
145                         pool.insert_new_boxed_str(s)
146                             .map_err(|oom| A::Error::custom(oom))?;
147                     }
148                 }
149 
150                 Ok(pool)
151             }
152         }
153         deserializer.deserialize_seq(Visitor)
154     }
155 }
156 
157 impl StringPool {
158     /// Create a new, empty pool.
159     pub fn new() -> Self {
160         Self::default()
161     }
162 
163     /// Insert a new string into this pool.
164     pub fn insert(&mut self, s: &str) -> Result<Atom, OutOfMemory> {
165         if let Some(atom) = self.map.get(s) {
166             return Ok(*atom);
167         }
168 
169         self.map.reserve(1)?;
170         self.strings.reserve(1)?;
171 
172         let mut owned = String::new();
173         owned.reserve_exact(s.len())?;
174         owned.push_str(s).expect("reserved capacity");
175         let owned = owned
176             .into_boxed_str()
177             .expect("reserved exact capacity, so shouldn't need to realloc");
178 
179         self.insert_new_boxed_str(owned)
180     }
181 
182     fn insert_new_boxed_str(&mut self, owned: Box<str>) -> Result<Atom, OutOfMemory> {
183         debug_assert!(!self.map.contains_key(&*owned));
184 
185         let index = self.strings.len();
186         let atom = Atom::new(index);
187         self.strings.push(owned)?;
188 
189         // SAFETY: We never expose this borrow and never mutate or reallocate
190         // strings once inserted into the pool.
191         let s = unsafe { mem::transmute::<&str, &'static str>(&self.strings[index]) };
192 
193         let old = self.map.insert(s, atom)?;
194         debug_assert!(old.is_none());
195 
196         Ok(atom)
197     }
198 
199     /// Get the `Atom` for the given string, if it has already been inserted
200     /// into this pool.
201     pub fn get_atom(&self, s: &str) -> Option<Atom> {
202         self.map.get(s).copied()
203     }
204 
205     /// Does this pool contain the given `atom`?
206     #[inline]
207     pub fn contains(&self, atom: Atom) -> bool {
208         atom.index() < self.strings.len()
209     }
210 
211     /// Get the string associated with the given `atom`, if the pool contains
212     /// the atom.
213     #[inline]
214     pub fn get(&self, atom: Atom) -> Option<&str> {
215         if self.contains(atom) {
216             Some(&self.strings[atom.index()])
217         } else {
218             None
219         }
220     }
221 }
222 
223 impl fmt::Debug for Atom {
224     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
225         f.debug_struct("Atom")
226             .field("index", &self.index())
227             .finish()
228     }
229 }
230 
231 impl serde::ser::Serialize for Atom {
232     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
233     where
234         S: serde::Serializer,
235     {
236         serde::ser::Serialize::serialize(&self.index, serializer)
237     }
238 }
239 
240 impl<'de> serde::de::Deserialize<'de> for Atom {
241     fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
242     where
243         D: serde::Deserializer<'de>,
244     {
245         let index = serde::de::Deserialize::deserialize(deserializer)?;
246         Ok(Self { index })
247     }
248 }
249 
250 impl Atom {
251     fn new(index: usize) -> Self {
252         assert!(index < usize::try_from(u32::MAX).unwrap());
253         let index = u32::try_from(index).unwrap();
254         let index = NonZeroU32::new(index + 1).unwrap();
255         Self { index }
256     }
257 
258     /// Get this atom's index in its pool.
259     pub fn index(&self) -> usize {
260         let index = self.index.get() - 1;
261         usize::try_from(index).unwrap()
262     }
263 }
264 
265 #[cfg(test)]
266 mod tests {
267     use super::*;
268 
269     #[test]
270     fn basic() -> Result<()> {
271         let mut pool = StringPool::new();
272 
273         let a = pool.insert("a")?;
274         assert_eq!(&pool[a], "a");
275         assert_eq!(pool.get_atom("a"), Some(a));
276 
277         let a2 = pool.insert("a")?;
278         assert_eq!(a, a2);
279         assert_eq!(&pool[a2], "a");
280 
281         let b = pool.insert("b")?;
282         assert_eq!(&pool[b], "b");
283         assert_ne!(a, b);
284         assert_eq!(pool.get_atom("b"), Some(b));
285 
286         assert!(pool.get_atom("zzz").is_none());
287 
288         let mut pool2 = StringPool::new();
289         let c = pool2.insert("c")?;
290         assert_eq!(&pool2[c], "c");
291         assert_eq!(a, c);
292         assert_eq!(&pool2[a], "c");
293         assert!(!pool2.contains(b));
294         assert!(pool2.get(b).is_none());
295 
296         Ok(())
297     }
298 
299     #[test]
300     fn stress() -> Result<()> {
301         let mut pool = StringPool::new();
302 
303         let n = if cfg!(miri) { 100 } else { 10_000 };
304 
305         for _ in 0..2 {
306             let atoms: Vec<_> = (0..n).map(|i| pool.insert(&i.to_string())).try_collect()?;
307 
308             for atom in atoms {
309                 assert!(pool.contains(atom));
310                 assert_eq!(&pool[atom], atom.index().to_string());
311             }
312         }
313 
314         Ok(())
315     }
316 
317     #[test]
318     fn roundtrip_serialize_deserialize() -> Result<()> {
319         let mut pool = StringPool::new();
320         let a = pool.insert("a")?;
321         let b = pool.insert("b")?;
322         let c = pool.insert("c")?;
323 
324         let bytes = postcard::to_allocvec(&(pool, a, b, c))?;
325         let (pool, a2, b2, c2) = postcard::from_bytes::<(StringPool, Atom, Atom, Atom)>(&bytes)?;
326 
327         assert_eq!(&pool[a], "a");
328         assert_eq!(&pool[b], "b");
329         assert_eq!(&pool[c], "c");
330 
331         assert_eq!(&pool[a2], "a");
332         assert_eq!(&pool[b2], "b");
333         assert_eq!(&pool[c2], "c");
334 
335         Ok(())
336     }
337 }
338