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