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