cargo/core/
interning.rs

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