stringtheory/interning/
mod.rs

1//! Interning utilities.
2use std::ops::Deref;
3
4pub(crate) mod fixed_size;
5pub use self::fixed_size::FixedSizeInterner;
6
7pub(crate) mod helpers;
8
9pub(crate) mod map;
10pub use self::map::GenericMapInterner;
11
12#[cfg(test)]
13pub(crate) mod test_support;
14
15/// A string interner.
16pub trait Interner {
17    /// Returns `true` if the interner contains no strings.
18    fn is_empty(&self) -> bool;
19
20    /// Returns the number of strings in the interner.
21    fn len(&self) -> usize;
22
23    /// Returns the total number of bytes in the interner.
24    fn len_bytes(&self) -> usize;
25
26    /// Returns the total number of bytes the interner can hold.
27    fn capacity_bytes(&self) -> usize;
28
29    /// Attempts to intern the given string.
30    ///
31    /// Returns `None` if the interner is full or the string can't fit.
32    fn try_intern(&self, s: &str) -> Option<InternedString>;
33}
34
35impl<T> Interner for &T
36where
37    T: Interner,
38{
39    fn is_empty(&self) -> bool {
40        (**self).is_empty()
41    }
42
43    fn len(&self) -> usize {
44        (**self).len()
45    }
46
47    fn len_bytes(&self) -> usize {
48        (**self).len_bytes()
49    }
50
51    fn capacity_bytes(&self) -> usize {
52        (**self).capacity_bytes()
53    }
54
55    fn try_intern(&self, s: &str) -> Option<InternedString> {
56        (**self).try_intern(s)
57    }
58}
59
60#[derive(Clone, Debug, PartialEq)]
61pub(crate) enum InternedStringState {
62    GenericMap(self::map::StringState),
63    FixedSize(self::fixed_size::StringState),
64}
65
66impl InternedStringState {
67    #[inline]
68    fn as_str(&self) -> &str {
69        match self {
70            Self::GenericMap(state) => state.as_str(),
71            Self::FixedSize(state) => state.as_str(),
72        }
73    }
74}
75
76impl From<self::fixed_size::StringState> for InternedStringState {
77    fn from(state: self::fixed_size::StringState) -> Self {
78        Self::FixedSize(state)
79    }
80}
81
82impl From<self::map::StringState> for InternedStringState {
83    fn from(state: self::map::StringState) -> Self {
84        Self::GenericMap(state)
85    }
86}
87
88/// An interned string.
89///
90/// This string type is read-only, and dereferences to `&str` for ergonomic usage. It's cheap to clone (16 bytes), but
91/// generally won't be interacted with directly. Instead, most usages should be wrapped in `MetaString`.
92#[derive(Clone, Debug, PartialEq)]
93pub struct InternedString {
94    state: InternedStringState,
95}
96
97impl InternedString {
98    pub(crate) fn into_state(self) -> InternedStringState {
99        self.state
100    }
101}
102
103impl<T> From<T> for InternedString
104where
105    T: Into<InternedStringState>,
106{
107    fn from(state: T) -> Self {
108        Self { state: state.into() }
109    }
110}
111
112impl Deref for InternedString {
113    type Target = str;
114
115    fn deref(&self) -> &str {
116        self.state.as_str()
117    }
118}
119
120#[cfg(test)]
121mod tests {
122    use super::*;
123
124    #[test]
125    fn size_of_interned_string() {
126        // We're asserting that `InternedString` itself is 24 bytes: an enum over possible interner implementations,
127        // each of which should be 16 bytes in size... making `InternedString` itself 24 bytes in size due to the additional
128        // discriminant field.
129        assert_eq!(std::mem::size_of::<InternedString>(), 24);
130    }
131}