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#[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
118use 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}