Coverage Report

Created: 2026-03-31 06:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wasmi/crates/collections/src/string_interner.rs
Line
Count
Source
1
//! Data structure to efficiently store and deduplicate strings.
2
3
#[cfg(all(
4
    feature = "hash-collections",
5
    not(feature = "prefer-btree-collections")
6
))]
7
mod detail {
8
    use super::{GetOrInternWithHint, Sym};
9
    use crate::hash;
10
    use string_interner::{StringInterner, Symbol, backend::BufferBackend};
11
12
    pub type StringInternerImpl = StringInterner<BufferBackend<Sym>, hash::RandomState>;
13
14
    impl GetOrInternWithHint for StringInternerImpl {
15
        #[inline]
16
        fn get_or_intern_with_hint<T>(&mut self, string: T, _hint: super::InternHint) -> Sym
17
        where
18
            T: AsRef<str>,
19
        {
20
            self.get_or_intern(string)
21
        }
22
    }
23
24
    impl Symbol for Sym {
25
        #[inline]
26
        fn try_from_usize(index: usize) -> Option<Self> {
27
            let Ok(value) = u32::try_from(index) else {
28
                return None;
29
            };
30
            Some(Self::from_u32(value))
31
        }
32
33
        #[inline]
34
        fn to_usize(self) -> usize {
35
            self.into_u32() as usize
36
        }
37
    }
38
}
39
40
#[cfg(any(
41
    not(feature = "hash-collections"),
42
    feature = "prefer-btree-collections"
43
))]
44
mod detail;
45
46
/// Internment hint to speed-up certain use cases.
47
#[derive(Debug, Copy, Clone)]
48
pub enum InternHint {
49
    /// No hint is given to the [`StringInterner`].
50
    None,
51
    /// Hint that the string to be interned likely already exists.
52
    LikelyExists,
53
    /// Hint that the string to be interned likely does not yet exist.
54
    LikelyNew,
55
}
56
57
/// Symbols returned by the [`StringInterner`] to resolve interned strings.
58
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
59
pub struct Sym(u32);
60
61
impl Sym {
62
    /// Creates a new [`Sym`] from the `u32` value.
63
0
    pub fn from_u32(value: u32) -> Self {
64
0
        Self(value)
65
0
    }
66
67
    /// Creates a new [`Sym`] from the `usize` value.
68
    ///
69
    /// # Panics
70
    ///
71
    /// If the `usize` value is out of bounds for [`Sym`].
72
0
    pub fn from_usize(value: usize) -> Self {
73
0
        u32::try_from(value).map_or_else(|_| panic!("out of bounds symbol index: {value}"), Self)
74
0
    }
75
76
    /// Returns the `u32` value of the [`Sym`].
77
0
    pub fn into_u32(self) -> u32 {
78
0
        self.0
79
0
    }
80
81
    /// Returns the value of the [`Sym`] as `usize`.
82
0
    pub fn into_usize(self) -> usize {
83
0
        self.0 as usize
84
0
    }
85
}
86
87
/// Efficiently interns and deduplicates strings.
88
#[derive(Debug, Clone, PartialEq, Eq)]
89
pub struct StringInterner {
90
    inner: detail::StringInternerImpl,
91
}
92
93
impl Default for StringInterner {
94
    #[inline]
95
31.6k
    fn default() -> Self {
96
31.6k
        Self::new()
97
31.6k
    }
98
}
99
100
impl StringInterner {
101
    /// Creates a new empty [`StringInterner`].
102
    #[inline]
103
31.6k
    pub fn new() -> Self {
104
31.6k
        Self {
105
31.6k
            inner: detail::StringInternerImpl::new(),
106
31.6k
        }
107
31.6k
    }
108
109
    /// Returns the number of strings interned by the [`StringInterner`].
110
    #[inline]
111
0
    pub fn len(&self) -> usize {
112
0
        self.inner.len()
113
0
    }
114
115
    /// Returns `true` if the [`StringInterner`] has no interned strings.
116
    #[inline]
117
0
    pub fn is_empty(&self) -> bool {
118
0
        self.inner.is_empty()
119
0
    }
120
121
    /// Returns the symbol for the given string if any.
122
    ///
123
    /// Can be used to query if a string has already been interned without interning.
124
    #[inline]
125
5.56k
    pub fn get<T>(&self, string: T) -> Option<Sym>
126
5.56k
    where
127
5.56k
        T: AsRef<str>,
128
    {
129
5.56k
        self.inner.get(string)
130
5.56k
    }
<wasmi_collections::string_interner::StringInterner>::get::<&str>
Line
Count
Source
125
1.28k
    pub fn get<T>(&self, string: T) -> Option<Sym>
126
1.28k
    where
127
1.28k
        T: AsRef<str>,
128
    {
129
1.28k
        self.inner.get(string)
130
1.28k
    }
Unexecuted instantiation: <wasmi_collections::string_interner::StringInterner>::get::<_>
<wasmi_collections::string_interner::StringInterner>::get::<&str>
Line
Count
Source
125
4.28k
    pub fn get<T>(&self, string: T) -> Option<Sym>
126
4.28k
    where
127
4.28k
        T: AsRef<str>,
128
    {
129
4.28k
        self.inner.get(string)
130
4.28k
    }
131
132
    /// Interns the given string.
133
    ///
134
    /// Returns a symbol for resolution into the original string.
135
    ///
136
    /// # Panics
137
    ///
138
    /// If the interner already interns the maximum number of strings possible
139
    /// by the chosen symbol type.
140
    #[inline]
141
0
    pub fn get_or_intern<T>(&mut self, string: T) -> Sym
142
0
    where
143
0
        T: AsRef<str>,
144
    {
145
0
        self.inner.get_or_intern_with_hint(string, InternHint::None)
146
0
    }
147
148
    /// Interns the given string with usage hint.
149
    ///
150
    /// Returns a symbol for resolution into the original string.
151
    ///
152
    /// # Panics
153
    ///
154
    /// If the interner already interns the maximum number of strings possible
155
    /// by the chosen symbol type.
156
    #[inline]
157
0
    pub fn get_or_intern_with_hint<T>(&mut self, string: T, hint: InternHint) -> Sym
158
0
    where
159
0
        T: AsRef<str>,
160
    {
161
0
        self.inner.get_or_intern_with_hint(string, hint)
162
0
    }
163
164
    /// Returns the string for the given symbol if any.
165
    #[inline]
166
0
    pub fn resolve(&self, symbol: Sym) -> Option<&str> {
167
0
        self.inner.resolve(symbol)
168
0
    }
169
}
170
171
/// Extension trait for [`StringInterner`] backends.
172
trait GetOrInternWithHint {
173
    /// Interns the given string with usage hint.
174
    ///
175
    /// Returns a symbol for resolution into the original string.
176
    ///
177
    /// # Panics
178
    ///
179
    /// If the interner already interns the maximum number of strings possible
180
    /// by the chosen symbol type.
181
    fn get_or_intern_with_hint<T>(&mut self, string: T, hint: InternHint) -> Sym
182
    where
183
        T: AsRef<str>;
184
}