]> git.proxmox.com Git - rustc.git/blob - compiler/rustc_data_structures/src/sso/map.rs
New upstream version 1.50.0+dfsg1
[rustc.git] / compiler / rustc_data_structures / src / sso / map.rs
1 use super::either_iter::EitherIter;
2 use crate::fx::FxHashMap;
3 use arrayvec::ArrayVec;
4 use std::fmt;
5 use std::hash::Hash;
6 use std::iter::FromIterator;
7 use std::ops::Index;
8
9 // For pointer-sized arguments arrays
10 // are faster than set/map for up to 64
11 // arguments.
12 //
13 // On the other hand such a big array
14 // hurts cache performance, makes passing
15 // sso structures around very expensive.
16 //
17 // Biggest performance benefit is gained
18 // for reasonably small arrays that stay
19 // small in vast majority of cases.
20 //
21 // '8' is choosen as a sane default, to be
22 // reevaluated later.
23 //
24 // Note: As of now ArrayVec design prevents
25 // us from making it user-customizable.
26 const SSO_ARRAY_SIZE: usize = 8;
27
28 /// Small-storage-optimized implementation of a map.
29 ///
30 /// Stores elements in a small array up to a certain length
31 /// and switches to `HashMap` when that length is exceeded.
32 //
33 // FIXME: Implements subset of HashMap API.
34 //
35 // Missing HashMap API:
36 // all hasher-related
37 // try_reserve (unstable)
38 // shrink_to (unstable)
39 // drain_filter (unstable)
40 // into_keys/into_values (unstable)
41 // all raw_entry-related
42 // PartialEq/Eq (requires sorting the array)
43 // Entry::or_insert_with_key
44 // Vacant/Occupied entries and related
45 //
46 // FIXME: In HashMap most methods accepting key reference
47 // accept reference to generic `Q` where `K: Borrow<Q>`.
48 //
49 // However, using this approach in `HashMap::get` apparently
50 // breaks inlining and noticeably reduces performance.
51 //
52 // Performance *should* be the same given that borrow is
53 // a NOP in most cases, but in practice that's not the case.
54 //
55 // Further investigation is required.
56 //
57 // Affected methods:
58 // SsoHashMap::get
59 // SsoHashMap::get_mut
60 // SsoHashMap::get_entry
61 // SsoHashMap::get_key_value
62 // SsoHashMap::contains_key
63 // SsoHashMap::remove
64 // SsoHashMap::remove_entry
65 // Index::index
66 // SsoHashSet::take
67 // SsoHashSet::get
68 // SsoHashSet::remove
69 // SsoHashSet::contains
70
71 #[derive(Clone)]
72 pub enum SsoHashMap<K, V> {
73 Array(ArrayVec<[(K, V); SSO_ARRAY_SIZE]>),
74 Map(FxHashMap<K, V>),
75 }
76
77 impl<K, V> SsoHashMap<K, V> {
78 /// Creates an empty `SsoHashMap`.
79 #[inline]
80 pub fn new() -> Self {
81 SsoHashMap::Array(ArrayVec::new())
82 }
83
84 /// Creates an empty `SsoHashMap` with the specified capacity.
85 pub fn with_capacity(cap: usize) -> Self {
86 if cap <= SSO_ARRAY_SIZE {
87 Self::new()
88 } else {
89 SsoHashMap::Map(FxHashMap::with_capacity_and_hasher(cap, Default::default()))
90 }
91 }
92
93 /// Clears the map, removing all key-value pairs. Keeps the allocated memory
94 /// for reuse.
95 pub fn clear(&mut self) {
96 match self {
97 SsoHashMap::Array(array) => array.clear(),
98 SsoHashMap::Map(map) => map.clear(),
99 }
100 }
101
102 /// Returns the number of elements the map can hold without reallocating.
103 pub fn capacity(&self) -> usize {
104 match self {
105 SsoHashMap::Array(_) => SSO_ARRAY_SIZE,
106 SsoHashMap::Map(map) => map.capacity(),
107 }
108 }
109
110 /// Returns the number of elements in the map.
111 pub fn len(&self) -> usize {
112 match self {
113 SsoHashMap::Array(array) => array.len(),
114 SsoHashMap::Map(map) => map.len(),
115 }
116 }
117
118 /// Returns `true` if the map contains no elements.
119 pub fn is_empty(&self) -> bool {
120 match self {
121 SsoHashMap::Array(array) => array.is_empty(),
122 SsoHashMap::Map(map) => map.is_empty(),
123 }
124 }
125
126 /// An iterator visiting all key-value pairs in arbitrary order.
127 /// The iterator element type is `(&'a K, &'a V)`.
128 #[inline]
129 pub fn iter(&self) -> <&Self as IntoIterator>::IntoIter {
130 self.into_iter()
131 }
132
133 /// An iterator visiting all key-value pairs in arbitrary order,
134 /// with mutable references to the values.
135 /// The iterator element type is `(&'a K, &'a mut V)`.
136 #[inline]
137 pub fn iter_mut(&mut self) -> impl Iterator<Item = (&'_ K, &'_ mut V)> {
138 self.into_iter()
139 }
140
141 /// An iterator visiting all keys in arbitrary order.
142 /// The iterator element type is `&'a K`.
143 pub fn keys(&self) -> impl Iterator<Item = &'_ K> {
144 match self {
145 SsoHashMap::Array(array) => EitherIter::Left(array.iter().map(|(k, _v)| k)),
146 SsoHashMap::Map(map) => EitherIter::Right(map.keys()),
147 }
148 }
149
150 /// An iterator visiting all values in arbitrary order.
151 /// The iterator element type is `&'a V`.
152 pub fn values(&self) -> impl Iterator<Item = &'_ V> {
153 match self {
154 SsoHashMap::Array(array) => EitherIter::Left(array.iter().map(|(_k, v)| v)),
155 SsoHashMap::Map(map) => EitherIter::Right(map.values()),
156 }
157 }
158
159 /// An iterator visiting all values mutably in arbitrary order.
160 /// The iterator element type is `&'a mut V`.
161 pub fn values_mut(&mut self) -> impl Iterator<Item = &'_ mut V> {
162 match self {
163 SsoHashMap::Array(array) => EitherIter::Left(array.iter_mut().map(|(_k, v)| v)),
164 SsoHashMap::Map(map) => EitherIter::Right(map.values_mut()),
165 }
166 }
167
168 /// Clears the map, returning all key-value pairs as an iterator. Keeps the
169 /// allocated memory for reuse.
170 pub fn drain(&mut self) -> impl Iterator<Item = (K, V)> + '_ {
171 match self {
172 SsoHashMap::Array(array) => EitherIter::Left(array.drain(..)),
173 SsoHashMap::Map(map) => EitherIter::Right(map.drain()),
174 }
175 }
176 }
177
178 impl<K: Eq + Hash, V> SsoHashMap<K, V> {
179 /// Changes underlying storage from array to hashmap
180 /// if array is full.
181 fn migrate_if_full(&mut self) {
182 if let SsoHashMap::Array(array) = self {
183 if array.is_full() {
184 *self = SsoHashMap::Map(array.drain(..).collect());
185 }
186 }
187 }
188
189 /// Reserves capacity for at least `additional` more elements to be inserted
190 /// in the `SsoHashMap`. The collection may reserve more space to avoid
191 /// frequent reallocations.
192 pub fn reserve(&mut self, additional: usize) {
193 match self {
194 SsoHashMap::Array(array) => {
195 if SSO_ARRAY_SIZE < (array.len() + additional) {
196 let mut map: FxHashMap<K, V> = array.drain(..).collect();
197 map.reserve(additional);
198 *self = SsoHashMap::Map(map);
199 }
200 }
201 SsoHashMap::Map(map) => map.reserve(additional),
202 }
203 }
204
205 /// Shrinks the capacity of the map as much as possible. It will drop
206 /// down as much as possible while maintaining the internal rules
207 /// and possibly leaving some space in accordance with the resize policy.
208 pub fn shrink_to_fit(&mut self) {
209 if let SsoHashMap::Map(map) = self {
210 if map.len() <= SSO_ARRAY_SIZE {
211 *self = SsoHashMap::Array(map.drain().collect());
212 } else {
213 map.shrink_to_fit();
214 }
215 }
216 }
217
218 /// Retains only the elements specified by the predicate.
219 pub fn retain<F>(&mut self, mut f: F)
220 where
221 F: FnMut(&K, &mut V) -> bool,
222 {
223 match self {
224 SsoHashMap::Array(array) => array.retain(|(k, v)| f(k, v)),
225 SsoHashMap::Map(map) => map.retain(f),
226 }
227 }
228
229 /// Inserts a key-value pair into the map.
230 ///
231 /// If the map did not have this key present, [`None`] is returned.
232 ///
233 /// If the map did have this key present, the value is updated, and the old
234 /// value is returned. The key is not updated, though; this matters for
235 /// types that can be `==` without being identical. See the [module-level
236 /// documentation] for more.
237 pub fn insert(&mut self, key: K, value: V) -> Option<V> {
238 match self {
239 SsoHashMap::Array(array) => {
240 for (k, v) in array.iter_mut() {
241 if *k == key {
242 let old_value = std::mem::replace(v, value);
243 return Some(old_value);
244 }
245 }
246 if let Err(error) = array.try_push((key, value)) {
247 let mut map: FxHashMap<K, V> = array.drain(..).collect();
248 let (key, value) = error.element();
249 map.insert(key, value);
250 *self = SsoHashMap::Map(map);
251 }
252 None
253 }
254 SsoHashMap::Map(map) => map.insert(key, value),
255 }
256 }
257
258 /// Removes a key from the map, returning the value at the key if the key
259 /// was previously in the map.
260 pub fn remove(&mut self, key: &K) -> Option<V> {
261 match self {
262 SsoHashMap::Array(array) => {
263 if let Some(index) = array.iter().position(|(k, _v)| k == key) {
264 Some(array.swap_remove(index).1)
265 } else {
266 None
267 }
268 }
269 SsoHashMap::Map(map) => map.remove(key),
270 }
271 }
272
273 /// Removes a key from the map, returning the stored key and value if the
274 /// key was previously in the map.
275 pub fn remove_entry(&mut self, key: &K) -> Option<(K, V)> {
276 match self {
277 SsoHashMap::Array(array) => {
278 if let Some(index) = array.iter().position(|(k, _v)| k == key) {
279 Some(array.swap_remove(index))
280 } else {
281 None
282 }
283 }
284 SsoHashMap::Map(map) => map.remove_entry(key),
285 }
286 }
287
288 /// Returns a reference to the value corresponding to the key.
289 pub fn get(&self, key: &K) -> Option<&V> {
290 match self {
291 SsoHashMap::Array(array) => {
292 for (k, v) in array {
293 if k == key {
294 return Some(v);
295 }
296 }
297 None
298 }
299 SsoHashMap::Map(map) => map.get(key),
300 }
301 }
302
303 /// Returns a mutable reference to the value corresponding to the key.
304 pub fn get_mut(&mut self, key: &K) -> Option<&mut V> {
305 match self {
306 SsoHashMap::Array(array) => {
307 for (k, v) in array {
308 if k == key {
309 return Some(v);
310 }
311 }
312 None
313 }
314 SsoHashMap::Map(map) => map.get_mut(key),
315 }
316 }
317
318 /// Returns the key-value pair corresponding to the supplied key.
319 pub fn get_key_value(&self, key: &K) -> Option<(&K, &V)> {
320 match self {
321 SsoHashMap::Array(array) => {
322 for (k, v) in array {
323 if k == key {
324 return Some((k, v));
325 }
326 }
327 None
328 }
329 SsoHashMap::Map(map) => map.get_key_value(key),
330 }
331 }
332
333 /// Returns `true` if the map contains a value for the specified key.
334 pub fn contains_key(&self, key: &K) -> bool {
335 match self {
336 SsoHashMap::Array(array) => array.iter().any(|(k, _v)| k == key),
337 SsoHashMap::Map(map) => map.contains_key(key),
338 }
339 }
340
341 /// Gets the given key's corresponding entry in the map for in-place manipulation.
342 #[inline]
343 pub fn entry(&mut self, key: K) -> Entry<'_, K, V> {
344 Entry { ssomap: self, key }
345 }
346 }
347
348 impl<K, V> Default for SsoHashMap<K, V> {
349 #[inline]
350 fn default() -> Self {
351 Self::new()
352 }
353 }
354
355 impl<K: Eq + Hash, V> FromIterator<(K, V)> for SsoHashMap<K, V> {
356 fn from_iter<I: IntoIterator<Item = (K, V)>>(iter: I) -> SsoHashMap<K, V> {
357 let mut map: SsoHashMap<K, V> = Default::default();
358 map.extend(iter);
359 map
360 }
361 }
362
363 impl<K: Eq + Hash, V> Extend<(K, V)> for SsoHashMap<K, V> {
364 fn extend<I>(&mut self, iter: I)
365 where
366 I: IntoIterator<Item = (K, V)>,
367 {
368 for (key, value) in iter.into_iter() {
369 self.insert(key, value);
370 }
371 }
372
373 #[inline]
374 fn extend_one(&mut self, (k, v): (K, V)) {
375 self.insert(k, v);
376 }
377
378 fn extend_reserve(&mut self, additional: usize) {
379 match self {
380 SsoHashMap::Array(array) => {
381 if SSO_ARRAY_SIZE < (array.len() + additional) {
382 let mut map: FxHashMap<K, V> = array.drain(..).collect();
383 map.extend_reserve(additional);
384 *self = SsoHashMap::Map(map);
385 }
386 }
387 SsoHashMap::Map(map) => map.extend_reserve(additional),
388 }
389 }
390 }
391
392 impl<'a, K, V> Extend<(&'a K, &'a V)> for SsoHashMap<K, V>
393 where
394 K: Eq + Hash + Copy,
395 V: Copy,
396 {
397 fn extend<T: IntoIterator<Item = (&'a K, &'a V)>>(&mut self, iter: T) {
398 self.extend(iter.into_iter().map(|(k, v)| (*k, *v)))
399 }
400
401 #[inline]
402 fn extend_one(&mut self, (&k, &v): (&'a K, &'a V)) {
403 self.insert(k, v);
404 }
405
406 #[inline]
407 fn extend_reserve(&mut self, additional: usize) {
408 Extend::<(K, V)>::extend_reserve(self, additional)
409 }
410 }
411
412 impl<K, V> IntoIterator for SsoHashMap<K, V> {
413 type IntoIter = EitherIter<
414 <ArrayVec<[(K, V); 8]> as IntoIterator>::IntoIter,
415 <FxHashMap<K, V> as IntoIterator>::IntoIter,
416 >;
417 type Item = <Self::IntoIter as Iterator>::Item;
418
419 fn into_iter(self) -> Self::IntoIter {
420 match self {
421 SsoHashMap::Array(array) => EitherIter::Left(array.into_iter()),
422 SsoHashMap::Map(map) => EitherIter::Right(map.into_iter()),
423 }
424 }
425 }
426
427 /// adapts Item of array reference iterator to Item of hashmap reference iterator.
428 #[inline(always)]
429 fn adapt_array_ref_it<K, V>(pair: &'a (K, V)) -> (&'a K, &'a V) {
430 let (a, b) = pair;
431 (a, b)
432 }
433
434 /// adapts Item of array mut reference iterator to Item of hashmap mut reference iterator.
435 #[inline(always)]
436 fn adapt_array_mut_it<K, V>(pair: &'a mut (K, V)) -> (&'a K, &'a mut V) {
437 let (a, b) = pair;
438 (a, b)
439 }
440
441 impl<'a, K, V> IntoIterator for &'a SsoHashMap<K, V> {
442 type IntoIter = EitherIter<
443 std::iter::Map<
444 <&'a ArrayVec<[(K, V); 8]> as IntoIterator>::IntoIter,
445 fn(&'a (K, V)) -> (&'a K, &'a V),
446 >,
447 <&'a FxHashMap<K, V> as IntoIterator>::IntoIter,
448 >;
449 type Item = <Self::IntoIter as Iterator>::Item;
450
451 fn into_iter(self) -> Self::IntoIter {
452 match self {
453 SsoHashMap::Array(array) => EitherIter::Left(array.into_iter().map(adapt_array_ref_it)),
454 SsoHashMap::Map(map) => EitherIter::Right(map.iter()),
455 }
456 }
457 }
458
459 impl<'a, K, V> IntoIterator for &'a mut SsoHashMap<K, V> {
460 type IntoIter = EitherIter<
461 std::iter::Map<
462 <&'a mut ArrayVec<[(K, V); 8]> as IntoIterator>::IntoIter,
463 fn(&'a mut (K, V)) -> (&'a K, &'a mut V),
464 >,
465 <&'a mut FxHashMap<K, V> as IntoIterator>::IntoIter,
466 >;
467 type Item = <Self::IntoIter as Iterator>::Item;
468
469 fn into_iter(self) -> Self::IntoIter {
470 match self {
471 SsoHashMap::Array(array) => EitherIter::Left(array.into_iter().map(adapt_array_mut_it)),
472 SsoHashMap::Map(map) => EitherIter::Right(map.iter_mut()),
473 }
474 }
475 }
476
477 impl<K, V> fmt::Debug for SsoHashMap<K, V>
478 where
479 K: fmt::Debug,
480 V: fmt::Debug,
481 {
482 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
483 f.debug_map().entries(self.iter()).finish()
484 }
485 }
486
487 impl<'a, K, V> Index<&'a K> for SsoHashMap<K, V>
488 where
489 K: Eq + Hash,
490 {
491 type Output = V;
492
493 #[inline]
494 fn index(&self, key: &K) -> &V {
495 self.get(key).expect("no entry found for key")
496 }
497 }
498
499 /// A view into a single entry in a map.
500 pub struct Entry<'a, K, V> {
501 ssomap: &'a mut SsoHashMap<K, V>,
502 key: K,
503 }
504
505 impl<'a, K: Eq + Hash, V> Entry<'a, K, V> {
506 /// Provides in-place mutable access to an occupied entry before any
507 /// potential inserts into the map.
508 pub fn and_modify<F>(self, f: F) -> Self
509 where
510 F: FnOnce(&mut V),
511 {
512 if let Some(value) = self.ssomap.get_mut(&self.key) {
513 f(value);
514 }
515 self
516 }
517
518 /// Ensures a value is in the entry by inserting the default if empty, and returns
519 /// a mutable reference to the value in the entry.
520 #[inline]
521 pub fn or_insert(self, value: V) -> &'a mut V {
522 self.or_insert_with(|| value)
523 }
524
525 /// Ensures a value is in the entry by inserting the result of the default function if empty,
526 /// and returns a mutable reference to the value in the entry.
527 pub fn or_insert_with<F: FnOnce() -> V>(self, default: F) -> &'a mut V {
528 self.ssomap.migrate_if_full();
529 match self.ssomap {
530 SsoHashMap::Array(array) => {
531 let key_ref = &self.key;
532 let found_index = array.iter().position(|(k, _v)| k == key_ref);
533 let index = if let Some(index) = found_index {
534 index
535 } else {
536 let index = array.len();
537 array.try_push((self.key, default())).unwrap();
538 index
539 };
540 &mut array[index].1
541 }
542 SsoHashMap::Map(map) => map.entry(self.key).or_insert_with(default),
543 }
544 }
545
546 /// Returns a reference to this entry's key.
547 #[inline]
548 pub fn key(&self) -> &K {
549 &self.key
550 }
551 }
552
553 impl<'a, K: Eq + Hash, V: Default> Entry<'a, K, V> {
554 /// Ensures a value is in the entry by inserting the default value if empty,
555 /// and returns a mutable reference to the value in the entry.
556 #[inline]
557 pub fn or_default(self) -> &'a mut V {
558 self.or_insert_with(Default::default)
559 }
560 }