1#![warn(missing_docs)]
5
6use crate::{
7 parking_lot::Mutex,
8 visitor::{Visit, VisitResult, Visitor},
9};
10use fxhash::{FxHashMap, FxHasher};
11use std::{
12 fmt::{Display, Formatter},
13 hash::{Hash, Hasher},
14 ops::Deref,
15 sync::Arc,
16};
17
18#[derive(Clone, Debug)]
30pub struct ImmutableString(Arc<String>);
31
32impl Display for ImmutableString {
33 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
34 f.write_str(self.0.as_ref())
35 }
36}
37
38impl Visit for ImmutableString {
39 fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
40 let mut string = self.0.deref().clone();
42 string.visit(name, visitor)?;
43
44 if visitor.is_reading() {
46 *self = SSTORAGE.lock().insert(string);
47 }
48
49 Ok(())
50 }
51}
52
53impl Default for ImmutableString {
54 fn default() -> Self {
55 Self::new("")
56 }
57}
58
59impl ImmutableString {
60 #[inline]
68 pub fn new<S: AsRef<str>>(string: S) -> ImmutableString {
69 SSTORAGE.lock().insert(string)
70 }
71
72 #[inline]
75 pub fn id(&self) -> u64 {
76 &*self.0 as *const _ as u64
77 }
78
79 #[inline]
81 pub fn to_mutable(&self) -> String {
82 (*self.0).clone()
83 }
84}
85
86impl Deref for ImmutableString {
87 type Target = str;
88
89 #[inline]
90 fn deref(&self) -> &Self::Target {
91 self.0.as_ref()
92 }
93}
94
95impl Hash for ImmutableString {
96 #[inline]
97 fn hash<H: Hasher>(&self, state: &mut H) {
98 state.write_u64(self.id())
99 }
100}
101
102impl PartialEq for ImmutableString {
103 #[inline]
104 fn eq(&self, other: &Self) -> bool {
105 self.id() == other.id()
106 }
107}
108
109impl Eq for ImmutableString {}
110
111#[derive(Default)]
114pub struct ImmutableStringStorage {
115 vec: FxHashMap<u64, Arc<String>>,
116}
117
118impl ImmutableStringStorage {
119 #[inline]
120 fn insert<S: AsRef<str>>(&mut self, string: S) -> ImmutableString {
121 let mut hasher = FxHasher::default();
122 string.as_ref().hash(&mut hasher);
123 let hash = hasher.finish();
124
125 if let Some(existing) = self.vec.get(&hash) {
126 ImmutableString(existing.clone())
127 } else {
128 let immutable = Arc::new(string.as_ref().to_owned());
129 self.vec.insert(hash, immutable.clone());
130 ImmutableString(immutable)
131 }
132 }
133}
134
135impl ImmutableStringStorage {
136 pub fn entry_count() -> usize {
138 SSTORAGE.lock().vec.len()
139 }
140}
141
142lazy_static! {
143 static ref SSTORAGE: Arc<Mutex<ImmutableStringStorage>> =
144 Arc::new(Mutex::new(ImmutableStringStorage::default()));
145}
146
147#[cfg(test)]
148mod test {
149 use crate::sstorage::{ImmutableString, ImmutableStringStorage};
150
151 #[test]
152 fn test_immutable_string_uniqueness() {
153 let a = ImmutableString::new("Foobar");
154 let b = ImmutableString::new("Foobar");
155
156 assert_eq!(ImmutableStringStorage::entry_count(), 1);
157 assert_eq!(a.id(), b.id())
158 }
159}