1#![warn(missing_docs)]
25
26use crate::{
27 parking_lot::Mutex,
28 uuid_provider,
29 visitor::{Visit, VisitResult, Visitor},
30 SafeLock,
31};
32use fxhash::{FxHashMap, FxHasher};
33pub use fyrox_core_derive::TypeUuidProvider;
34use serde::{Deserialize, Serialize};
35use std::{
36 fmt::{Debug, Display, Formatter},
37 hash::{Hash, Hasher},
38 ops::Deref,
39 sync::{Arc, LazyLock},
40};
41
42#[derive(Clone, Debug)]
43struct State {
44 string: String,
45 hash: u64,
46}
47
48#[derive(Clone)]
60pub struct ImmutableString(Arc<State>);
61
62uuid_provider!(ImmutableString = "452caac1-19f7-43d6-9e33-92c2c9163332");
63
64impl Display for ImmutableString {
65 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
66 f.write_str(self.0.string.as_ref())
67 }
68}
69
70impl Debug for ImmutableString {
71 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
72 Debug::fmt(&self.0.string, f)
73 }
74}
75
76impl From<ImmutableString> for String {
77 fn from(value: ImmutableString) -> Self {
78 value.0.string.clone()
79 }
80}
81
82impl Visit for ImmutableString {
83 fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
84 let mut string = self.0.string.clone();
86 string.visit(name, visitor)?;
87
88 if visitor.is_reading() {
90 *self = SSTORAGE.safe_lock().insert(string);
91 }
92
93 Ok(())
94 }
95}
96
97impl Serialize for ImmutableString {
98 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
99 where
100 S: serde::Serializer,
101 {
102 serializer.serialize_str(self.as_str())
103 }
104}
105
106impl<'de> Deserialize<'de> for ImmutableString {
107 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
108 where
109 D: serde::Deserializer<'de>,
110 {
111 Ok(ImmutableString::new(
112 deserializer.deserialize_string(ImmutableStringVisitor {})?,
113 ))
114 }
115}
116
117struct ImmutableStringVisitor {}
118
119impl serde::de::Visitor<'_> for ImmutableStringVisitor {
120 type Value = ImmutableString;
121
122 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
123 write!(formatter, "a string")
124 }
125
126 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
127 where
128 E: serde::de::Error,
129 {
130 Ok(ImmutableString::new(v))
131 }
132
133 fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
134 where
135 E: serde::de::Error,
136 {
137 Ok(v.into())
138 }
139}
140
141impl Default for ImmutableString {
142 fn default() -> Self {
143 Self::new("")
144 }
145}
146
147impl AsRef<str> for ImmutableString {
148 fn as_ref(&self) -> &str {
149 self.deref()
150 }
151}
152
153impl ImmutableString {
154 #[inline]
162 pub fn new<S: AsRef<str>>(string: S) -> ImmutableString {
163 SSTORAGE.safe_lock().insert(string)
164 }
165
166 #[inline]
169 pub fn cached_hash(&self) -> u64 {
170 self.0.hash
171 }
172
173 #[inline]
175 pub fn to_mutable(&self) -> String {
176 self.0.string.clone()
177 }
178
179 pub fn as_str(&self) -> &str {
181 self.deref()
182 }
183}
184
185impl From<&str> for ImmutableString {
186 fn from(value: &str) -> Self {
187 Self::new(value)
188 }
189}
190
191impl From<String> for ImmutableString {
192 fn from(value: String) -> Self {
193 SSTORAGE.safe_lock().insert_owned(value)
194 }
195}
196
197impl From<&String> for ImmutableString {
198 fn from(value: &String) -> Self {
199 SSTORAGE.safe_lock().insert(value)
200 }
201}
202
203impl Deref for ImmutableString {
204 type Target = str;
205
206 #[inline]
207 fn deref(&self) -> &Self::Target {
208 self.0.string.as_ref()
209 }
210}
211
212impl Hash for ImmutableString {
213 #[inline]
214 fn hash<H: Hasher>(&self, state: &mut H) {
215 state.write_u64(self.cached_hash())
216 }
217}
218
219impl PartialEq for ImmutableString {
220 #[inline]
221 fn eq(&self, other: &Self) -> bool {
222 self.cached_hash() == other.cached_hash()
223 }
224}
225
226impl Eq for ImmutableString {}
227
228#[derive(Default)]
231pub struct ImmutableStringStorage {
232 vec: FxHashMap<u64, Arc<State>>,
233}
234
235impl ImmutableStringStorage {
236 #[inline]
237 fn insert<S: AsRef<str>>(&mut self, string: S) -> ImmutableString {
238 let mut hasher = FxHasher::default();
239 string.as_ref().hash(&mut hasher);
240 let hash = hasher.finish();
241
242 if let Some(existing) = self.vec.get(&hash) {
243 ImmutableString(existing.clone())
244 } else {
245 let immutable = Arc::new(State {
246 string: string.as_ref().to_owned(),
247 hash,
248 });
249 self.vec.insert(hash, immutable.clone());
250 ImmutableString(immutable)
251 }
252 }
253 #[inline]
255 fn insert_owned(&mut self, string: String) -> ImmutableString {
256 let mut hasher = FxHasher::default();
257 string.hash(&mut hasher);
258 let hash = hasher.finish();
259
260 if let Some(existing) = self.vec.get(&hash) {
261 ImmutableString(existing.clone())
262 } else {
263 let immutable = Arc::new(State { string, hash });
264 self.vec.insert(hash, immutable.clone());
265 ImmutableString(immutable)
266 }
267 }
268}
269
270impl ImmutableStringStorage {
271 pub fn entry_count() -> usize {
273 SSTORAGE.safe_lock().vec.len()
274 }
275}
276
277static SSTORAGE: LazyLock<Arc<Mutex<ImmutableStringStorage>>> =
278 LazyLock::new(|| Arc::new(Mutex::new(ImmutableStringStorage::default())));
279
280#[cfg(test)]
281mod test {
282 use super::*;
283
284 #[test]
285 fn test_immutable_string_distinctness() {
286 let a = ImmutableString::new("Foobar");
287 let b = ImmutableString::new("rabooF");
288
289 assert_ne!(a.cached_hash(), b.cached_hash())
290 }
291
292 #[test]
293 fn test_immutable_string_uniqueness() {
294 let a = ImmutableString::new("Foobar");
295 let b = ImmutableString::new("Foobar");
296
297 assert_eq!(a.cached_hash(), b.cached_hash())
302 }
303
304 #[test]
305 fn test_immutable_string_uniqueness_from_owned() {
306 let a = ImmutableString::new("Foobar");
307 let b = ImmutableString::from("Foobar".to_owned());
308
309 assert_eq!(a.cached_hash(), b.cached_hash())
310 }
311
312 #[test]
313 fn visit_for_immutable_string() {
314 let mut a = ImmutableString::new("Foobar");
315 let mut visitor = Visitor::default();
316
317 assert!(a.visit("name", &mut visitor).is_ok());
318 }
319
320 #[test]
321 fn debug_for_immutable_string() {
322 let a = ImmutableString::new("Foobar");
323
324 assert_eq!(format!("{a:?}"), "\"Foobar\"");
325 }
326
327 #[test]
328 fn debug_for_immutable_string_from_owned() {
329 let a = ImmutableString::from("Foobar".to_owned());
330
331 assert_eq!(format!("{a:?}"), "\"Foobar\"");
332 }
333
334 #[test]
335 fn default_for_immutable_string() {
336 let a = ImmutableString::default();
337
338 assert_eq!(a.0.string, "");
339 }
340
341 #[test]
342 fn immutable_string_to_mutable() {
343 let a = ImmutableString::new("Foobar");
344
345 assert_eq!(a.to_mutable(), String::from("Foobar"));
346 }
347
348 #[test]
349 fn deref_for_immutable_string() {
350 let s = "Foobar";
351 let a = ImmutableString::new(s);
352
353 assert_eq!(a.deref(), s);
354 }
355
356 #[test]
357 fn eq_for_immutable_string() {
358 let a = ImmutableString::new("Foobar");
359 let b = ImmutableString::new("Foobar");
360
361 assert!(a == b);
362 }
363}