steel_parser/
interner.rs

1use compact_str::CompactString;
2use fxhash::FxBuildHasher;
3use lasso::Key;
4use lasso::Spur;
5use once_cell::sync::OnceCell;
6use serde::{Deserialize, Serialize};
7use std::{fmt, sync::Arc};
8
9// TODO: Serialize and Deserialize should resolve() -> Otherwise we're in for deep trouble
10// trying to serialize and deserialize this
11#[derive(Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
12#[repr(transparent)]
13pub struct InternedString(Spur);
14
15impl Serialize for InternedString {
16    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
17    where
18        S: serde::Serializer,
19    {
20        self.resolve().serialize(serializer)
21    }
22}
23
24impl<'de> Deserialize<'de> for InternedString {
25    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
26    where
27        D: serde::Deserializer<'de>,
28    {
29        let key = <&str>::deserialize(deserializer)?;
30
31        Ok(InternedString::from(key))
32    }
33}
34
35impl InternedString {
36    pub fn from_static(ident: &'static str) -> Self {
37        Self(
38            INTERNER
39                .get_or_init(|| Arc::new(ThreadedRodeo::with_hasher(FxBuildHasher::default())))
40                .get_or_intern_static(ident),
41        )
42    }
43
44    pub fn from_string(ident: String) -> Self {
45        Self(
46            INTERNER
47                .get_or_init(|| Arc::new(ThreadedRodeo::with_hasher(FxBuildHasher::default())))
48                .get_or_intern(ident),
49        )
50    }
51
52    pub fn from_str(ident: &str) -> Self {
53        Self(
54            INTERNER
55                .get_or_init(|| Arc::new(ThreadedRodeo::with_hasher(FxBuildHasher::default())))
56                .get_or_intern(ident),
57        )
58    }
59
60    pub fn new(key: usize) -> Self {
61        Self(Spur::try_from_usize(key).unwrap())
62    }
63
64    pub fn get(self) -> Spur {
65        self.0
66    }
67
68    pub fn try_get(ident: &str) -> Option<InternedString> {
69        INTERNER.get().unwrap().get(ident).map(InternedString)
70    }
71
72    #[doc(hidden)]
73    pub fn as_u32(self) -> u32 {
74        self.get().into_usize() as u32
75    }
76
77    pub fn resolve(&self) -> &str {
78        resolve(&self.0)
79    }
80}
81
82impl From<CompactString> for InternedString {
83    fn from(value: CompactString) -> Self {
84        Self::from_str(&value)
85    }
86}
87
88impl From<&str> for InternedString {
89    fn from(ident: &str) -> Self {
90        Self::from_str(ident)
91    }
92}
93
94impl From<String> for InternedString {
95    fn from(ident: String) -> Self {
96        Self::from_string(ident)
97    }
98}
99
100impl From<Spur> for InternedString {
101    fn from(spur: Spur) -> Self {
102        Self(spur)
103    }
104}
105
106impl fmt::Debug for InternedString {
107    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
108        write!(f, "{}", self.get().into_usize())
109    }
110}
111
112impl fmt::Display for InternedString {
113    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
114        write!(f, "{}", self.resolve())
115    }
116}
117
118// impl Serialize for InternedString {
119//     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
120//     where
121//         S: Serializer,
122//     {
123//         serializer.serialize_str(self.resolve())
124//     }
125// }
126
127use lasso::ThreadedRodeo;
128
129static INTERNER: OnceCell<Arc<ThreadedRodeo<Spur, fxhash::FxBuildHasher>>> = OnceCell::new();
130
131pub fn interned_current_memory_usage() -> usize {
132    INTERNER.get().unwrap().current_memory_usage()
133}
134
135pub fn take_interner() -> Arc<ThreadedRodeo<Spur, fxhash::FxBuildHasher>> {
136    Arc::clone(INTERNER.get().unwrap())
137}
138
139pub fn initialize_with(
140    interner: Arc<ThreadedRodeo<Spur, fxhash::FxBuildHasher>>,
141) -> Result<(), Arc<ThreadedRodeo<Spur, fxhash::FxBuildHasher>>> {
142    INTERNER.set(interner)
143}
144
145pub fn get_interner() -> Option<&'static Arc<ThreadedRodeo<Spur, fxhash::FxBuildHasher>>> {
146    INTERNER.get()
147}
148
149pub fn add_interner(interner: Arc<ThreadedRodeo>) {
150    let guard = INTERNER.get().unwrap();
151
152    for key in interner.strings() {
153        guard.get_or_intern(key);
154    }
155}
156
157#[test]
158fn test_initialization() {
159    INTERNER.get_or_init(|| Arc::new(ThreadedRodeo::with_hasher(FxBuildHasher::default())));
160    let key = INTERNER.get().unwrap().get_or_intern_static("hello world");
161
162    let resolved_string = INTERNER.get().unwrap().resolve(&key);
163
164    println!("resolved string: {resolved_string:?}");
165}
166
167fn resolve(key: &Spur) -> &str {
168    INTERNER.get().unwrap().resolve(key)
169}