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::Owned(item))
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<'a> From<Cow<'a, str>> for InternedString {
73    fn from(cs: Cow<'a, str>) -> Self {
74        let mut cache = interned_storage();
75        let s = cache.get(cs.as_ref()).copied().unwrap_or_else(|| {
76            let s = cs.into_owned().leak();
77            cache.insert(s);
78            s
79        });
80
81        InternedString { inner: s }
82    }
83}
84
85impl Eq for InternedString {}
86
87impl InternedString {
88    pub fn new(s: &str) -> InternedString {
89        InternedString::from(Cow::Borrowed(s))
90    }
91    pub fn as_str(&self) -> &'static str {
92        self.inner
93    }
94}
95
96impl Deref for InternedString {
97    type Target = str;
98
99    fn deref(&self) -> &'static str {
100        self.as_str()
101    }
102}
103
104impl AsRef<str> for InternedString {
105    fn as_ref(&self) -> &str {
106        self.as_str()
107    }
108}
109
110impl AsRef<OsStr> for InternedString {
111    fn as_ref(&self) -> &OsStr {
112        self.as_str().as_ref()
113    }
114}
115
116impl AsRef<Path> for InternedString {
117    fn as_ref(&self) -> &Path {
118        self.as_str().as_ref()
119    }
120}
121
122impl Hash for InternedString {
123    // N.B., we can't implement this as `identity(self).hash(state)`,
124    // because we use this for on-disk fingerprints and so need
125    // stability across Cargo invocations.
126    fn hash<H: Hasher>(&self, state: &mut H) {
127        self.as_str().hash(state);
128    }
129}
130
131impl Borrow<str> for InternedString {
132    // If we implement Hash as `identity(self).hash(state)`,
133    // then this will need to be removed.
134    fn borrow(&self) -> &str {
135        self.as_str()
136    }
137}
138
139impl fmt::Debug for InternedString {
140    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
141        fmt::Debug::fmt(self.as_str(), f)
142    }
143}
144
145impl fmt::Display for InternedString {
146    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
147        fmt::Display::fmt(self.as_str(), f)
148    }
149}
150
151impl Ord for InternedString {
152    fn cmp(&self, other: &InternedString) -> Ordering {
153        self.as_str().cmp(other.as_str())
154    }
155}
156
157impl PartialOrd for InternedString {
158    fn partial_cmp(&self, other: &InternedString) -> Option<Ordering> {
159        Some(self.cmp(other))
160    }
161}
162
163impl Serialize for InternedString {
164    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
165    where
166        S: Serializer,
167    {
168        serializer.serialize_str(self.inner)
169    }
170}
171
172impl<'de> serde::Deserialize<'de> for InternedString {
173    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
174    where
175        D: serde::Deserializer<'de>,
176    {
177        UntaggedEnumVisitor::new()
178            .expecting("an String like thing")
179            .string(|value| Ok(value.into()))
180            .deserialize(deserializer)
181    }
182}