cargo/util/
interning.rs
1use serde::{Serialize, Serializer};
2use serde_untagged::UntaggedEnumVisitor;
3use std::borrow::Borrow;
4use std::borrow::Cow;
5use std::cmp::Ordering;
6use std::collections::HashSet;
7use std::ffi::OsStr;
8use std::fmt;
9use std::hash::{Hash, Hasher};
10use std::ops::Deref;
11use std::path::Path;
12use std::ptr;
13use std::str;
14use std::sync::Mutex;
15use std::sync::OnceLock;
16
17pub static INTERNED_DEFAULT: InternedString = InternedString { inner: "default" };
18
19fn interned_storage() -> std::sync::MutexGuard<'static, HashSet<&'static str>> {
20 static STRING_CACHE: OnceLock<Mutex<HashSet<&'static str>>> = OnceLock::new();
21 STRING_CACHE
22 .get_or_init(|| {
23 let mut out: HashSet<&'static str> = Default::default();
24 out.insert(INTERNED_DEFAULT.as_str());
25 Mutex::new(out)
26 })
27 .lock()
28 .unwrap()
29}
30
31#[derive(Clone, Copy)]
32pub struct InternedString {
33 inner: &'static str,
34}
35
36impl<'a> From<&'a str> for InternedString {
37 fn from(item: &'a str) -> Self {
38 InternedString::new(item)
39 }
40}
41
42impl<'a> From<&'a String> for InternedString {
43 fn from(item: &'a String) -> Self {
44 InternedString::new(item)
45 }
46}
47
48impl From<String> for InternedString {
49 fn from(item: String) -> Self {
50 InternedString::from_cow(item.into())
51 }
52}
53
54impl PartialEq for InternedString {
55 fn eq(&self, other: &InternedString) -> bool {
56 ptr::eq(self.as_str(), other.as_str())
57 }
58}
59
60impl PartialEq<str> for InternedString {
61 fn eq(&self, other: &str) -> bool {
62 *self == other
63 }
64}
65
66impl<'a> PartialEq<&'a str> for InternedString {
67 fn eq(&self, other: &&str) -> bool {
68 **self == **other
69 }
70}
71
72impl Eq for InternedString {}
73
74impl InternedString {
75 pub fn new(s: &str) -> InternedString {
76 InternedString::from_cow(s.into())
77 }
78
79 fn from_cow<'a>(cs: Cow<'a, str>) -> InternedString {
80 let mut cache = interned_storage();
81 let s = cache.get(cs.as_ref()).copied().unwrap_or_else(|| {
82 let s = cs.into_owned().leak();
83 cache.insert(s);
84 s
85 });
86
87 InternedString { inner: s }
88 }
89
90 pub fn as_str(&self) -> &'static str {
91 self.inner
92 }
93}
94
95impl Deref for InternedString {
96 type Target = str;
97
98 fn deref(&self) -> &'static str {
99 self.as_str()
100 }
101}
102
103impl AsRef<str> for InternedString {
104 fn as_ref(&self) -> &str {
105 self.as_str()
106 }
107}
108
109impl AsRef<OsStr> for InternedString {
110 fn as_ref(&self) -> &OsStr {
111 self.as_str().as_ref()
112 }
113}
114
115impl AsRef<Path> for InternedString {
116 fn as_ref(&self) -> &Path {
117 self.as_str().as_ref()
118 }
119}
120
121impl Hash for InternedString {
122 fn hash<H: Hasher>(&self, state: &mut H) {
126 self.as_str().hash(state);
127 }
128}
129
130impl Borrow<str> for InternedString {
131 fn borrow(&self) -> &str {
134 self.as_str()
135 }
136}
137
138impl fmt::Debug for InternedString {
139 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
140 fmt::Debug::fmt(self.as_str(), f)
141 }
142}
143
144impl fmt::Display for InternedString {
145 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
146 fmt::Display::fmt(self.as_str(), f)
147 }
148}
149
150impl Ord for InternedString {
151 fn cmp(&self, other: &InternedString) -> Ordering {
152 self.as_str().cmp(other.as_str())
153 }
154}
155
156impl PartialOrd for InternedString {
157 fn partial_cmp(&self, other: &InternedString) -> Option<Ordering> {
158 Some(self.cmp(other))
159 }
160}
161
162impl Serialize for InternedString {
163 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
164 where
165 S: Serializer,
166 {
167 serializer.serialize_str(self.inner)
168 }
169}
170
171impl<'de> serde::Deserialize<'de> for InternedString {
172 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
173 where
174 D: serde::Deserializer<'de>,
175 {
176 UntaggedEnumVisitor::new()
177 .expecting("an String like thing")
178 .string(|value| Ok(InternedString::new(value)))
179 .deserialize(deserializer)
180 }
181}