yasi/
lib.rs

1use std::borrow::{Borrow, Cow};
2use std::convert::Infallible;
3use std::ffi::OsStr;
4use std::fmt::{Debug, Display};
5use std::hash::{Hash, Hasher};
6use std::ops::Deref;
7use std::path::Path;
8use std::str::FromStr;
9use std::sync::{Arc, RwLock, Weak};
10
11use hashbrown::raw::RawTable;
12
13#[cfg(feature = "serde")]
14mod serde;
15
16#[inline]
17#[cold]
18fn cold() {}
19
20lazy_static::lazy_static! {
21    static ref TABLE: RwLock<RawTable<Weak<TableString>>> = RwLock::new(RawTable::new());
22}
23
24type TableHasher = ahash::AHasher;
25
26struct DisplayHasher<H: Hasher>(H);
27impl<H: Hasher> DisplayHasher<H> {
28    fn finish(&self) -> u64 {
29        self.0.finish()
30    }
31}
32impl<H: Hasher + Default> DisplayHasher<H> {
33    fn hash<T: Display>(t: &T) -> u64 {
34        use std::fmt::Write;
35        let mut h = Self(H::default());
36        let _ = write!(h, "{t}");
37        h.finish()
38    }
39}
40impl<H: Hasher> std::fmt::Write for DisplayHasher<H> {
41    fn write_str(&mut self, s: &str) -> std::fmt::Result {
42        self.0.write(s.as_bytes());
43        Ok(())
44    }
45}
46
47struct DisplayEq<'a> {
48    target: &'a [u8],
49}
50impl<'a> DisplayEq<'a> {
51    fn eq<T: Display>(src: &T, target: &'a str) -> bool {
52        use std::fmt::Write;
53        let mut eq = Self {
54            target: target.as_bytes(),
55        };
56        write!(eq, "{src}").is_ok() && eq.target.is_empty()
57    }
58}
59impl<'a> std::fmt::Write for DisplayEq<'a> {
60    fn write_str(&mut self, s: &str) -> std::fmt::Result {
61        let s = s.as_bytes();
62        if s.len() > self.target.len() || s != &self.target[..s.len()] {
63            return Err(std::fmt::Error);
64        }
65        self.target = &self.target[s.len()..];
66        Ok(())
67    }
68}
69
70struct TableString(String);
71impl Drop for TableString {
72    fn drop(&mut self) {
73        let hash = DisplayHasher::<TableHasher>::hash(&self.0);
74        let eq = |s: &Weak<TableString>| s.strong_count() == 0;
75        let mut guard = TABLE.write().unwrap();
76        if !guard.erase_entry(hash, eq) {
77            cold();
78            let hash = TableHasher::default().finish();
79            guard.erase_entry(hash, eq);
80        }
81    }
82}
83
84pub struct InternedString(Arc<TableString>);
85impl InternedString {
86    pub fn intern<T: Display + Into<String>>(t: T) -> Self {
87        let hash = DisplayHasher::<TableHasher>::hash(&t);
88        let eq = |s: &Weak<TableString>| {
89            if let Some(s) = Weak::upgrade(s) {
90                DisplayEq::eq(&t, s.0.as_str())
91            } else {
92                false
93            }
94        };
95        // READ section
96        {
97            if let Some(s) = TABLE.read().unwrap().get(hash, eq).and_then(Weak::upgrade) {
98                return Self(s);
99            }
100        }
101        // WRITE section
102        {
103            let mut guard = TABLE.write().unwrap();
104            // RACE CONDITION: check again if it exists
105            if let Some(s) = guard.get_mut(hash, eq) {
106                cold(); // unlikely
107                if let Some(s) = Weak::upgrade(s) {
108                    return Self(s);
109                } else {
110                    let res = Arc::new(TableString(t.into()));
111                    *s = Arc::downgrade(&res);
112                    return Self(res);
113                }
114            }
115            // we need to create it
116            let res = Arc::new(TableString(t.into()));
117            guard.insert(hash, Arc::downgrade(&res), |s| {
118                let mut hasher = TableHasher::default();
119                if let Some(s) = Weak::upgrade(s) {
120                    hasher.write(s.0.as_bytes())
121                }
122                hasher.finish()
123            });
124            return Self(res);
125        }
126    }
127
128    pub fn from_display<T: Display + ?Sized>(t: &T) -> Self {
129        struct IntoString<'a, T: ?Sized>(&'a T);
130        impl<'a, T: Display + ?Sized> From<IntoString<'a, T>> for String {
131            fn from(value: IntoString<'a, T>) -> Self {
132                value.0.to_string()
133            }
134        }
135        impl<'a, T: Display + ?Sized> std::fmt::Display for IntoString<'a, T> {
136            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
137                self.0.fmt(f)
138            }
139        }
140        Self::intern(IntoString(t))
141    }
142}
143
144impl Deref for InternedString {
145    type Target = str;
146    fn deref(&self) -> &Self::Target {
147        self.0.deref().0.deref()
148    }
149}
150
151impl AsRef<[u8]> for InternedString {
152    fn as_ref(&self) -> &[u8] {
153        self.0.deref().0.as_ref()
154    }
155}
156
157impl AsRef<OsStr> for InternedString {
158    fn as_ref(&self) -> &OsStr {
159        self.0.deref().0.as_ref()
160    }
161}
162
163impl AsRef<Path> for InternedString {
164    fn as_ref(&self) -> &Path {
165        self.0.deref().0.as_ref()
166    }
167}
168
169impl AsRef<str> for InternedString {
170    fn as_ref(&self) -> &str {
171        self.0.deref().0.as_ref()
172    }
173}
174
175impl Borrow<str> for InternedString {
176    fn borrow(&self) -> &str {
177        self.0.deref().0.borrow()
178    }
179}
180
181impl Clone for InternedString {
182    fn clone(&self) -> Self {
183        Self(self.0.clone())
184    }
185}
186
187impl Debug for InternedString {
188    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
189        Debug::fmt(&self.0.deref().0, f)
190    }
191}
192
193impl Default for InternedString {
194    fn default() -> Self {
195        Self::intern(String::default())
196    }
197}
198
199impl Display for InternedString {
200    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
201        Display::fmt(&self.0.deref().0, f)
202    }
203}
204
205impl PartialEq for InternedString {
206    fn eq(&self, other: &Self) -> bool {
207        Arc::ptr_eq(&self.0, &other.0)
208    }
209}
210
211impl Eq for InternedString {}
212
213impl PartialOrd for InternedString {
214    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
215        if Arc::ptr_eq(&self.0, &other.0) {
216            Some(std::cmp::Ordering::Equal)
217        } else {
218            self.0.deref().0.partial_cmp(&other.0.deref().0)
219        }
220    }
221}
222
223impl Ord for InternedString {
224    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
225        if Arc::ptr_eq(&self.0, &other.0) {
226            std::cmp::Ordering::Equal
227        } else {
228            self.0.deref().0.cmp(&other.0.deref().0)
229        }
230    }
231}
232
233impl<T: Display + Into<String>> From<T> for InternedString {
234    fn from(value: T) -> Self {
235        Self::intern(value)
236    }
237}
238
239impl FromStr for InternedString {
240    type Err = Infallible;
241    fn from_str(s: &str) -> Result<Self, Self::Err> {
242        Ok(Self::intern(s))
243    }
244}
245
246impl Hash for InternedString {
247    fn hash<H: Hasher>(&self, state: &mut H) {
248        self.0.deref().0.hash(state)
249    }
250}
251
252impl<'a> PartialEq<&'a str> for InternedString {
253    fn eq(&self, other: &&'a str) -> bool {
254        self.0.deref().0.eq(other)
255    }
256}
257
258impl<'a> PartialEq<Cow<'a, str>> for InternedString {
259    fn eq(&self, other: &Cow<'a, str>) -> bool {
260        self.0.deref().0.eq(other)
261    }
262}
263
264impl PartialEq<str> for InternedString {
265    fn eq(&self, other: &str) -> bool {
266        self.0.deref().0.eq(other)
267    }
268}