1use core::{fmt::Debug, hash::Hash, ops::Deref};
8use std::sync::{OnceLock, PoisonError, RwLock};
9
10use bevy_utils::HashSet;
11
12pub struct Interned<T: ?Sized + 'static>(pub &'static T);
40
41impl<T: ?Sized> Deref for Interned<T> {
42 type Target = T;
43
44 fn deref(&self) -> &Self::Target {
45 self.0
46 }
47}
48
49impl<T: ?Sized> Clone for Interned<T> {
50 fn clone(&self) -> Self {
51 *self
52 }
53}
54
55impl<T: ?Sized> Copy for Interned<T> {}
56
57impl<T: ?Sized + Internable> PartialEq for Interned<T> {
60 fn eq(&self, other: &Self) -> bool {
61 self.0.ref_eq(other.0)
62 }
63}
64
65impl<T: ?Sized + Internable> Eq for Interned<T> {}
66
67impl<T: ?Sized + Internable> Hash for Interned<T> {
69 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
70 self.0.ref_hash(state);
71 }
72}
73
74impl<T: ?Sized + Debug> Debug for Interned<T> {
75 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
76 self.0.fmt(f)
77 }
78}
79
80impl<T> From<&Interned<T>> for Interned<T> {
81 fn from(value: &Interned<T>) -> Self {
82 *value
83 }
84}
85
86pub trait Internable: Hash + Eq {
90 fn leak(&self) -> &'static Self;
92
93 fn ref_eq(&self, other: &Self) -> bool;
95
96 fn ref_hash<H: core::hash::Hasher>(&self, state: &mut H);
98}
99
100impl Internable for str {
101 fn leak(&self) -> &'static Self {
102 let str = self.to_owned().into_boxed_str();
103 Box::leak(str)
104 }
105
106 fn ref_eq(&self, other: &Self) -> bool {
107 self.as_ptr() == other.as_ptr() && self.len() == other.len()
108 }
109
110 fn ref_hash<H: core::hash::Hasher>(&self, state: &mut H) {
111 self.len().hash(state);
112 self.as_ptr().hash(state);
113 }
114}
115
116pub struct Interner<T: ?Sized + 'static>(OnceLock<RwLock<HashSet<&'static T>>>);
124
125impl<T: ?Sized> Interner<T> {
126 pub const fn new() -> Self {
128 Self(OnceLock::new())
129 }
130}
131
132impl<T: Internable + ?Sized> Interner<T> {
133 pub fn intern(&self, value: &T) -> Interned<T> {
139 let lock = self.0.get_or_init(Default::default);
140 {
141 let set = lock.read().unwrap_or_else(PoisonError::into_inner);
142 if let Some(value) = set.get(value) {
143 return Interned(*value);
144 }
145 }
146 {
147 let mut set = lock.write().unwrap_or_else(PoisonError::into_inner);
148 if let Some(value) = set.get(value) {
149 Interned(*value)
150 } else {
151 let leaked = value.leak();
152 set.insert(leaked);
153 Interned(leaked)
154 }
155 }
156 }
157}
158
159impl<T: ?Sized> Default for Interner<T> {
160 fn default() -> Self {
161 Self::new()
162 }
163}
164
165#[cfg(test)]
166mod tests {
167 use core::hash::{Hash, Hasher};
168 use std::collections::hash_map::DefaultHasher;
169
170 use crate::intern::{Internable, Interned, Interner};
171
172 #[test]
173 fn zero_sized_type() {
174 #[derive(PartialEq, Eq, Hash, Debug)]
175 pub struct A;
176
177 impl Internable for A {
178 fn leak(&self) -> &'static Self {
179 &A
180 }
181
182 fn ref_eq(&self, other: &Self) -> bool {
183 core::ptr::eq(self, other)
184 }
185
186 fn ref_hash<H: Hasher>(&self, state: &mut H) {
187 core::ptr::hash(self, state);
188 }
189 }
190
191 let interner = Interner::default();
192 let x = interner.intern(&A);
193 let y = interner.intern(&A);
194 assert_eq!(x, y);
195 }
196
197 #[test]
198 fn fieldless_enum() {
199 #[derive(PartialEq, Eq, Hash, Debug, Clone)]
200 pub enum A {
201 X,
202 Y,
203 }
204
205 impl Internable for A {
206 fn leak(&self) -> &'static Self {
207 match self {
208 A::X => &A::X,
209 A::Y => &A::Y,
210 }
211 }
212
213 fn ref_eq(&self, other: &Self) -> bool {
214 core::ptr::eq(self, other)
215 }
216
217 fn ref_hash<H: Hasher>(&self, state: &mut H) {
218 core::ptr::hash(self, state);
219 }
220 }
221
222 let interner = Interner::default();
223 let x = interner.intern(&A::X);
224 let y = interner.intern(&A::Y);
225 assert_ne!(x, y);
226 }
227
228 #[test]
229 fn static_sub_strings() {
230 let str = "ABC ABC";
231 let a = &str[0..3];
232 let b = &str[4..7];
233 assert_eq!(a, b);
235 let x = Interned(a);
236 let y = Interned(b);
237 assert_ne!(x, y);
239 let interner = Interner::default();
240 let x = interner.intern(a);
241 let y = interner.intern(b);
242 assert_eq!(x, y);
244 }
245
246 #[test]
247 fn same_interned_instance() {
248 let a = Interned("A");
249 let b = a;
250
251 assert_eq!(a, b);
252
253 let mut hasher = DefaultHasher::default();
254 a.hash(&mut hasher);
255 let hash_a = hasher.finish();
256
257 let mut hasher = DefaultHasher::default();
258 b.hash(&mut hasher);
259 let hash_b = hasher.finish();
260
261 assert_eq!(hash_a, hash_b);
262 }
263
264 #[test]
265 fn same_interned_content() {
266 let a = Interned::<str>(Box::leak(Box::new("A".to_string())));
267 let b = Interned::<str>(Box::leak(Box::new("A".to_string())));
268
269 assert_ne!(a, b);
270 }
271
272 #[test]
273 fn different_interned_content() {
274 let a = Interned::<str>("A");
275 let b = Interned::<str>("B");
276
277 assert_ne!(a, b);
278 }
279}