supercov_engine/
interned.rs1use std::{
14 borrow::Borrow,
15 collections::{HashMap, HashSet},
16 fmt,
17 hash::{BuildHasherDefault, Hash, Hasher},
18 ops::Deref,
19 sync::Arc,
20};
21
22#[derive(Default, Clone, Copy)]
27pub struct FxHasher {
28 hash: u64,
29}
30
31const FX_SEED: u64 = 0x51_7c_c1_b7_27_22_0a_95;
32
33impl FxHasher {
34 fn add(&mut self, word: u64) {
35 self.hash = (self.hash.rotate_left(5) ^ word).wrapping_mul(FX_SEED);
36 }
37}
38
39impl Hasher for FxHasher {
40 fn write(&mut self, bytes: &[u8]) {
41 let mut chunks = bytes.chunks_exact(8);
42 for chunk in &mut chunks {
43 self.add(u64::from_le_bytes(chunk.try_into().expect("eight bytes")));
44 }
45 let rest = chunks.remainder();
46 if !rest.is_empty() {
47 let mut word = [0_u8; 8];
48 word[..rest.len()].copy_from_slice(rest);
49 self.add(u64::from_le_bytes(word) ^ ((rest.len() as u64) << 56));
50 }
51 }
52
53 fn write_u8(&mut self, value: u8) {
54 self.add(u64::from(value));
55 }
56
57 fn write_u32(&mut self, value: u32) {
58 self.add(u64::from(value));
59 }
60
61 fn write_u64(&mut self, value: u64) {
62 self.add(value);
63 }
64
65 fn write_usize(&mut self, value: usize) {
66 self.add(value as u64);
67 }
68
69 fn finish(&self) -> u64 {
70 self.hash
71 }
72}
73
74pub type FastHash = BuildHasherDefault<FxHasher>;
75pub type FastMap<K, V> = HashMap<K, V, FastHash>;
76pub type FastSet<T> = HashSet<T, FastHash>;
77
78use serde::{Serialize, Serializer};
79
80#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
81pub struct Id(Arc<str>);
82
83impl Id {
84 pub fn as_str(&self) -> &str {
85 &self.0
86 }
87}
88
89impl Deref for Id {
90 type Target = str;
91 fn deref(&self) -> &str {
92 &self.0
93 }
94}
95
96impl Borrow<str> for Id {
97 fn borrow(&self) -> &str {
98 &self.0
99 }
100}
101
102impl AsRef<str> for Id {
103 fn as_ref(&self) -> &str {
104 &self.0
105 }
106}
107
108impl Hash for Id {
110 fn hash<H: Hasher>(&self, state: &mut H) {
111 (*self.0).hash(state);
112 }
113}
114
115impl fmt::Debug for Id {
116 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
117 fmt::Debug::fmt(&*self.0, formatter)
118 }
119}
120
121impl fmt::Display for Id {
122 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
123 fmt::Display::fmt(&*self.0, formatter)
124 }
125}
126
127impl Serialize for Id {
128 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
129 serializer.serialize_str(&self.0)
130 }
131}
132
133impl From<&str> for Id {
134 fn from(value: &str) -> Self {
135 Self(Arc::from(value))
136 }
137}
138
139impl From<String> for Id {
140 fn from(value: String) -> Self {
141 Self(Arc::from(value))
142 }
143}
144
145impl From<&String> for Id {
146 fn from(value: &String) -> Self {
147 Self(Arc::from(value.as_str()))
148 }
149}
150
151impl From<Id> for String {
152 fn from(value: Id) -> Self {
153 value.0.to_string()
154 }
155}
156
157impl PartialEq<str> for Id {
158 fn eq(&self, other: &str) -> bool {
159 &*self.0 == other
160 }
161}
162
163impl PartialEq<&str> for Id {
164 fn eq(&self, other: &&str) -> bool {
165 &*self.0 == *other
166 }
167}
168
169impl PartialEq<String> for Id {
170 fn eq(&self, other: &String) -> bool {
171 &*self.0 == other.as_str()
172 }
173}
174
175impl PartialEq<Id> for String {
176 fn eq(&self, other: &Id) -> bool {
177 self.as_str() == &*other.0
178 }
179}
180
181impl PartialEq<Id> for str {
182 fn eq(&self, other: &Id) -> bool {
183 self == &*other.0
184 }
185}
186
187impl PartialEq<Id> for &str {
188 fn eq(&self, other: &Id) -> bool {
189 *self == &*other.0
190 }
191}
192
193#[cfg(test)]
194impl Id {
195 pub(crate) fn same_allocation(&self, other: &Id) -> bool {
197 Arc::ptr_eq(&self.0, &other.0)
198 }
199}
200
201#[derive(Default)]
203pub struct Interner {
204 ids: FastSet<Id>,
205}
206
207impl Interner {
208 pub fn id(&mut self, text: &str) -> Id {
209 if let Some(id) = self.ids.get(text) {
210 return id.clone();
211 }
212 let id = Id::from(text);
213 self.ids.insert(id.clone());
214 id
215 }
216}
217
218#[cfg(test)]
219mod tests {
220 use super::*;
221
222 const TEXTS: [&str; 14] = [
225 "",
226 "a",
227 "A",
228 "ab",
229 "a/b",
230 "h11/tests/test_x.py::test_b[2-50]",
231 "h11/tests/test_x.py::test_b[10-50]",
232 "h11/tests/test_x.py::test_b",
233 "python-phase:9",
234 "python-phase:10",
235 "quote \" and backslash \\",
236 "tab\tnewline\ncontrol\u{1}",
237 "unicodé ✓ 名前",
238 "z",
239 ];
240
241 #[test]
242 fn ids_order_hash_and_serialize_exactly_as_their_strings() {
243 use std::collections::{BTreeSet, HashSet};
244 let mut interner = Interner::default();
245 let ids = TEXTS
246 .iter()
247 .map(|text| interner.id(text))
248 .collect::<Vec<_>>();
249 for (left, left_text) in ids.iter().zip(TEXTS) {
252 for (right, right_text) in ids.iter().zip(TEXTS) {
253 assert_eq!(
254 left.cmp(right),
255 left_text.cmp(right_text),
256 "{left_text:?} vs {right_text:?}"
257 );
258 }
259 }
260 let by_id = ids.iter().cloned().collect::<BTreeSet<_>>();
261 let by_text = TEXTS
262 .iter()
263 .map(|text| text.to_string())
264 .collect::<BTreeSet<_>>();
265 assert_eq!(
266 by_id.iter().map(|id| id.as_str()).collect::<Vec<_>>(),
267 by_text.iter().map(String::as_str).collect::<Vec<_>>()
268 );
269 let hashed = ids.iter().cloned().collect::<HashSet<_>>();
271 for text in TEXTS {
272 assert!(hashed.contains(text), "{text:?}");
273 assert!(by_id.contains(text), "{text:?}");
274 }
275 assert!(!hashed.contains("absent"));
276 for (id, text) in ids.iter().zip(TEXTS) {
278 assert_eq!(
279 serde_json::to_string(id).unwrap(),
280 serde_json::to_string(text).unwrap()
281 );
282 assert_eq!(id.to_string(), text);
283 assert_eq!(format!("{id:?}"), format!("{text:?}"));
284 let owned = String::from(text);
285 assert_eq!(*id, text);
286 assert_eq!(*id, owned);
287 assert_eq!(owned, *id);
288 assert_eq!(text, *id);
289 }
290 assert_eq!(
291 serde_json::to_string(&ids).unwrap(),
292 serde_json::to_string(&TEXTS).unwrap()
293 );
294 }
295
296 #[test]
297 fn an_id_is_the_text_it_holds_everywhere_it_is_seen() {
298 let mut interner = Interner::default();
299 let first = interner.id("h11/tests/test_x.py::test_a");
300 let again = interner.id("h11/tests/test_x.py::test_a");
301 assert!(
302 Arc::ptr_eq(&first.0, &again.0),
303 "one copy per distinct text"
304 );
305 assert_eq!(first, "h11/tests/test_x.py::test_a");
306 assert_eq!(
307 serde_json::to_string(&vec![first.clone()]).unwrap(),
308 r#"["h11/tests/test_x.py::test_a"]"#
309 );
310 let mut set = std::collections::BTreeSet::new();
311 set.insert(interner.id("b"));
312 set.insert(interner.id("a"));
313 assert!(set.contains("a"), "found by text");
314 assert_eq!(
315 set.into_iter().map(String::from).collect::<Vec<_>>(),
316 ["a", "b"]
317 );
318 }
319}