slotmap_careful/
lib.rs

1#![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg))]
2#![doc = include_str!("../README.md")]
3// @@ begin lint list maintained by maint/add_warning @@
4#![allow(renamed_and_removed_lints)] // @@REMOVE_WHEN(ci_arti_stable)
5#![allow(unknown_lints)] // @@REMOVE_WHEN(ci_arti_nightly)
6#![warn(missing_docs)]
7#![warn(noop_method_call)]
8#![warn(unreachable_pub)]
9#![warn(clippy::all)]
10#![deny(clippy::await_holding_lock)]
11#![deny(clippy::cargo_common_metadata)]
12#![deny(clippy::cast_lossless)]
13#![deny(clippy::checked_conversions)]
14#![warn(clippy::cognitive_complexity)]
15#![deny(clippy::debug_assert_with_mut_call)]
16#![deny(clippy::exhaustive_enums)]
17#![deny(clippy::exhaustive_structs)]
18#![deny(clippy::expl_impl_clone_on_copy)]
19#![deny(clippy::fallible_impl_from)]
20#![deny(clippy::implicit_clone)]
21#![deny(clippy::large_stack_arrays)]
22#![warn(clippy::manual_ok_or)]
23#![deny(clippy::missing_docs_in_private_items)]
24#![warn(clippy::needless_borrow)]
25#![warn(clippy::needless_pass_by_value)]
26#![warn(clippy::option_option)]
27#![deny(clippy::print_stderr)]
28#![deny(clippy::print_stdout)]
29#![warn(clippy::rc_buffer)]
30#![deny(clippy::ref_option_ref)]
31#![warn(clippy::semicolon_if_nothing_returned)]
32#![warn(clippy::trait_duplication_in_bounds)]
33#![deny(clippy::unchecked_duration_subtraction)]
34#![deny(clippy::unnecessary_wraps)]
35#![warn(clippy::unseparated_literal_suffix)]
36#![deny(clippy::unwrap_used)]
37#![deny(clippy::mod_module_files)]
38#![allow(clippy::let_unit_value)] // This can reasonably be done for explicitness
39#![allow(clippy::uninlined_format_args)]
40#![allow(clippy::significant_drop_in_scrutinee)] // arti/-/merge_requests/588/#note_2812945
41#![allow(clippy::result_large_err)] // temporary workaround for arti#587
42#![allow(clippy::needless_raw_string_hashes)] // complained-about code is fine, often best
43#![allow(clippy::needless_lifetimes)] // See arti#1765
44#![allow(mismatched_lifetime_syntaxes)] // temporary workaround for arti#2060
45//! <!-- @@ end lint list maintained by maint/add_warning @@ -->
46
47mod key_data;
48
49pub use slotmap::{
50    new_key_type, secondary, DefaultKey, Key, KeyData, SecondaryMap, SparseSecondaryMap,
51};
52
53use key_data::key_version_serde as key_version;
54
55//use key_version::key_version_serde;
56
57/// A single entry in one of our careful slotmaps.
58///
59/// An entry can either be `Present` (in which case we treat it normally),
60/// or `Unusable`, in which case we
61#[cfg_attr(test, derive(serde::Serialize, serde::Deserialize))]
62#[derive(Debug, Clone)]
63enum Entry<V> {
64    /// The entry is available.
65    Present(V),
66    /// The entry can no longer be used, removed, or set to anything else.
67    ///
68    /// It must not be removed from the slot map, since doing so would
69    /// increase its slot's version number too high.
70    Unusable,
71}
72
73impl<V> Entry<V> {
74    /// Remove the value of `self` (if any), and make it unusable.
75    fn take_and_mark_unusable(&mut self) -> Option<V> {
76        match std::mem::replace(self, Entry::Unusable) {
77            Entry::Present(v) => Some(v),
78            Entry::Unusable => None,
79        }
80    }
81    /// Return a reference to the value of `self`, if there is one.
82    fn value(&self) -> Option<&V> {
83        match self {
84            Entry::Present(val) => Some(val),
85            Entry::Unusable => None,
86        }
87    }
88    /// Return a mutable reference to the value of `self``, if there is one.
89    fn value_mut(&mut self) -> Option<&mut V> {
90        match self {
91            Entry::Present(val) => Some(val),
92            Entry::Unusable => None,
93        }
94    }
95    /// Consume this entry (which must be `Present`), and return its value.
96    ///
97    /// # Panics
98    ///
99    /// Panics if this entry is `Unusable`.
100    fn unwrap(self) -> V {
101        match self {
102            Entry::Present(val) => val,
103            Entry::Unusable => panic!("Tried to unwrap an unusable slot."),
104        }
105    }
106}
107
108/// Helper: Define a wrapper for a single SlotMap type.
109///
110/// This works for SlotMap, DenseSlotMap, and HopSlotMap.
111///
112/// (The alternative to using a macro here would be to define a new trait
113/// implemented by all of the SlotMaps, and then to define our own SlotMap as a wrapper around an
114/// instance of that trait.)
115macro_rules! define_implementation {
116        { $mapname:ident } => {paste::paste!{
117
118        /// A variation of
119        #[doc = concat!("[`slotmap::", stringify!($mapname), "`]")]
120        /// that can never give the same key for multiple objects.
121        ///
122        /// Unlike a regular version of
123        #[doc = concat!("`", stringify!($mapname), "`,")]
124        /// this version will not allow a slot's version counter to roll over to
125        /// 0 if it reaches 2^31.  Instead, it will mark the slot as unusable for future values.
126        ///
127        /// # Limitations
128        ///
129        /// The possibility of marking a slot as unusable
130        /// makes it possible, given enough removals and re-insertions,
131        /// for a slotmap to use an unbounded amount of memory, even if it is not storing much actual data.
132        /// (From a DOS point of view: Given the ability to re-insert an entry ~2^31 times, an attacker can
133        /// cause a slot-map to render approximately `4+sizeof(V)` bytes unusable.)
134        ///
135        /// This type does not include implementations for:
136        ///   * `get_unchecked_mut()`
137        ///   * `get_disjoint_unchecked_mut()`
138        ///   * `IntoIterator`.
139        ///   * `serde::{Serialize, Deserialize}`.
140        ///
141        /// # Risky business!
142        ///
143        /// This code relies upon stability of some undocumented properties of `slotmap` keys.
144        /// In particular, it assumes:
145        ///  * that the slotmap KeyData `serde` format is stable,
146        ///  * that slot versions are represented as `u32`.
147        ///  * that the least significant bit of a slot version is 1 if the slot is full,
148        ///    and 0 if the slot is empty.
149        ///  * that slot versions start at 0, and increase monotonically as the slot is
150        ///    emptied and reused.
151        ///
152        /// Note that these assumptions are _probably_ okay: if `slotmap` were to change them,
153        /// it would thereby create a breaking change in its serde version.
154        //
155        // Invariants:
156        //
157        // For every `(key,value)` that is present in `base`:
158        //   - `key_okay(key)` is true.
159        //   - if `value` is `Entry::Unusable`, then `key_version(key) == SATURATE_AT_VERSION`.
160        //
161        // `n_unusable` is the number of entries in `base` whose value is `Entry::Unusable`.
162        //
163        // To maintain these invariants:
164        //   - Never remove a key with `key_version(key) == SATURATE_AT_VERSION`
165        //   - Whenever setting a value to `Unusable`, increment `n_unusable`.
166        #[derive(Clone, Debug)]
167        pub struct $mapname<K: Key, V> {
168            /// An underlying SlotMap, obeying the invariants above.
169            base: slotmap::$mapname<K, Entry<V>>,
170            /// The number of entries in this SlotMap that are filled with [`Entry::Unusable`] values.
171            n_unusable: usize,
172            /// A ZST, used to guarantee that we have spot-checked the behavior of the underlying
173            /// SlotMap implementation.
174            _valid: [<$mapname ValidationToken>],
175        }
176
177        impl<V> $mapname<DefaultKey, V> {
178            /// Construct a new empty map, using a default key type.
179            ///
180            /// See
181            #[doc = concat!("[`slotmap::", stringify!($mapname), "::new()`].")]
182            pub fn new() -> Self {
183                Self::with_key()
184            }
185
186            /// Construct a new empty map with a specified capacity, using a default key type.
187            ///
188            /// See
189            #[doc = concat!("[`slotmap::", stringify!($mapname), "::with_capacity()`].")]
190            /// ::with_capacity()`].
191            pub fn with_capacity(capacity: usize) -> Self {
192                Self::with_capacity_and_key(capacity)
193            }
194        }
195
196        impl<K: Key, V> Default for $mapname<K, V> {
197            fn default() -> Self {
198                Self::with_key()
199            }
200        }
201
202        impl<K: Key, V> $mapname<K, V> {
203            /// Construct a new empty map, using a specialized key type.
204            ///
205            /// See
206            #[doc= concat!("[`slotmap::", stringify!($mapname), "::with_key()`].")]
207            pub fn with_key() -> Self {
208                Self::with_capacity_and_key(0)
209            }
210
211            /// Construct a new empty map with a specified capacity, using a specialized key type.
212            ///
213            /// See
214            #[doc= concat!("[`slotmap::", stringify!($mapname), "::with_capacity_and_key()`].")]
215            pub fn with_capacity_and_key(capacity: usize) -> Self {
216                Self {
217                    base: slotmap::$mapname::with_capacity_and_key(capacity),
218                    n_unusable: 0,
219                    _valid: [<validate_ $mapname:snake _behavior>](),
220                }
221            }
222
223            /// Return the number of items in this map.
224            ///
225            /// See
226            #[doc= concat!("[`slotmap::", stringify!($mapname), "::len()`].")]
227            pub fn len(&self) -> usize {
228                self.base
229                    .len()
230                    .checked_sub(self.n_unusable)
231                    .expect("logic error")
232            }
233
234            /// Return true if this map has no items.
235            ///
236            /// See
237            #[doc= concat!("[`slotmap::", stringify!($mapname), "::is_empty()`].")]
238            pub fn is_empty(&self) -> bool {
239                self.len() == 0
240            }
241
242            /// Return the total number of slots available for entries in this map.
243            ///
244            /// This number includes used slots, as well as empty slots that may become used.
245            ///
246            /// See
247            #[doc= concat!("[`slotmap::", stringify!($mapname), "::capacity()`],")]
248            /// but note that a `slotmap-careful` implementation may _lose_ capacity over time,
249            /// as slots are marked unusable.
250            pub fn capacity(&self) -> usize {
251                self.base
252                    .capacity()
253                    .checked_sub(self.n_unusable)
254                    .expect("logic error")
255            }
256
257            /// Reserve space as needed.
258            ///
259            /// Allocates if needed, so that this map can hold `additional` new entries
260            /// without having to resize.
261            ///
262            /// See
263            #[doc= concat!("[`slotmap::", stringify!($mapname), "::reserve()`].")]
264            pub fn reserve(&mut self, additional: usize) {
265                // Note that we don't need to check n_unusable here: the underlying
266                // map type thinks that unusable entries are full, and so will allocate
267                // correctly.
268                self.base.reserve(additional);
269            }
270
271            /// Return true if the map contains an entry with a given key.
272            ///
273            /// See
274            #[doc= concat!("[`slotmap::", stringify!($mapname), "::contains_key()`].")]
275            pub fn contains_key(&self, key: K) -> bool {
276                // Calling self.get, not self.base.get, so it will be None if the
277                // slot is unusable.
278                self.get(key).is_some()
279            }
280
281            /// Insert a new value into the map, and return the key used for it.
282            ///
283            /// See
284            #[doc= concat!("[`slotmap::", stringify!($mapname), "::insert()`].")]
285            pub fn insert(&mut self, value: V) -> K {
286                let key = self.base.insert(Entry::Present(value));
287                debug_assert!(key_okay(key));
288                key
289            }
290
291            /// Insert a new value into the map, constructing it using its own new key.
292            ///
293            /// This method is useful for the case where a value needs to refer to the
294            /// key that will be assigned to it.
295            ///
296            /// See
297            #[doc= concat!("[`slotmap::", stringify!($mapname), "::insert_with_key()`].")]
298            pub fn insert_with_key<F>(&mut self, f: F) -> K
299            where
300                F: FnOnce(K) -> V,
301            {
302                let key = self.base.insert_with_key(|k| Entry::Present(f(k)));
303                debug_assert!(key_okay(key));
304                key
305            }
306
307            /// As [`Self::insert_with_key`], but may return an `Err`.
308            ///
309            /// See
310            #[doc= concat!("[`slotmap::", stringify!($mapname), "::try_insert_with_key()`].")]
311            pub fn try_insert_with_key<F, E>(&mut self, f: F) -> Result<K, E>
312            where
313                F: FnOnce(K) -> Result<V, E>,
314            {
315                let key = self
316                    .base
317                    .try_insert_with_key(|k| Ok(Entry::Present(f(k)?)))?;
318                debug_assert!(key_okay(key));
319                Ok(key)
320            }
321
322            /// Remove and return the element of this map with a given key.
323            ///
324            /// Return None if the key is not present in the map.
325            ///
326            /// See
327            #[doc= concat!("[`slotmap::", stringify!($mapname), "::remove()`].")]
328            pub fn remove(&mut self, key: K) -> Option<V> {
329                if key_version_is_maximal(key) {
330                    // The key is as large as it is allowed to get,
331                    // so we should not actually remove this Entry.
332                    match self.base.get_mut(key) {
333                        Some(slot) => {
334                            // The entry is Present: extract its value and mark it unusable.
335                            let rv = slot.take_and_mark_unusable();
336                            if rv.is_some() {
337                                self.n_unusable += 1;
338                            }
339                            rv
340                        }
341                        // The entry is Unusable; treat it as if it weren't there.
342                        None => None,
343                    }
344                } else {
345                    // The Entry::unwrap function will panic if its argument is
346                    // Entry::Unusable.  But that is impossible in this case,
347                    // since we already checked key_version_is_maximal() for this key,
348                    // and our invariant guarantees that, if the value is Entry::Unusable,
349                    // then key_version(key) == SATURATE_AT_VERSION,
350                    // so key_version_is_maximal is true.
351                    self.base.remove(key).map(Entry::unwrap)
352                }
353            }
354
355            /// Remove every element of this map that does not satisfy a given predicate.
356            ///
357            /// See
358            #[doc= concat!("[`slotmap::", stringify!($mapname), "::retain()`].")]
359            pub fn retain<F>(&mut self, mut f: F)
360            where
361                F: FnMut(K, &mut V) -> bool,
362            {
363                self.base.retain(|k, v| {
364                    let Entry::Present(v_inner) = v else {
365                        return true;
366                    };
367
368                    if f(k, v_inner) {
369                        true
370                    } else if key_version_is_maximal(k) {
371                        self.n_unusable += 1;
372                        *v = Entry::Unusable;
373                        true
374                    } else {
375                        false
376                    }
377                });
378            }
379
380            /// Remove every element of this map.
381            ///
382            /// See
383            #[doc= concat!("[`slotmap::", stringify!($mapname), "::clear()`].")]
384            pub fn clear(&mut self) {
385                self.retain(|_, _| false);
386            }
387
388            /// Return a reference to the element of this map with a given key.
389            ///
390            /// Return None if there is no such element.
391            ///
392            /// See
393            #[doc= concat!("[`slotmap::", stringify!($mapname), "::get()`].")]
394            pub fn get(&self, key: K) -> Option<&V> {
395                self.base.get(key).and_then(Entry::value)
396            }
397            /// Return a mutable reference to the element of this map with a given key.
398            ///
399            /// Return None if there is no such element.
400            ///
401            /// See
402            #[doc= concat!("[`slotmap::", stringify!($mapname), "::get_mut()`].")]
403            pub fn get_mut(&mut self, key: K) -> Option<&mut V> {
404                self.base.get_mut(key).and_then(|ent| ent.value_mut())
405            }
406
407            /// Return an array of mutable references to the elements of this map with a given list
408            /// of keys.
409            ///
410            /// Return None if any key is not present, or if the same key is given twice.
411            ///
412            /// See
413            #[doc= concat!("[`slotmap::", stringify!($mapname), "::get_disjoint_mut()`].")]
414            pub fn get_disjoint_mut<const N: usize>(&mut self, keys: [K; N]) -> Option<[&mut V; N]> {
415                let vals = self.base.get_disjoint_mut(keys)?;
416                // TODO array::try_map would be preferable, but it isn't stable.
417                if vals.iter().all(|e| matches!(e, Entry::Present(_))) {
418                    // Cannot panic, since we checked that every entry is present.
419                    Some(vals.map(|v| match v {
420                        Entry::Present(v) => v,
421                        Entry::Unusable => panic!("Logic error"),
422                    }))
423                } else {
424                    None
425                }
426            }
427
428            /// Return an iterator over the elements of this map.
429            ///
430            /// See
431            #[doc= concat!("[`slotmap::", stringify!($mapname), "::iter()`].")]
432            ///
433            /// # Current limitations
434            ///
435            /// Does not return a named type.
436            pub fn iter(&self) -> impl Iterator<Item = (K, &V)> + '_ {
437                self.base.iter().filter_map(|(k, v)| match v {
438                    Entry::Present(v) => Some((k, v)),
439                    Entry::Unusable => None,
440                })
441            }
442
443            /// Remove every element of this map.
444            ///
445            /// See
446            #[doc= concat!("[`slotmap::", stringify!($mapname), "::drain()`].")]
447            pub fn drain(&mut self) -> impl Iterator<Item = (K, V)> + '_ {
448                self.base.drain().filter_map(|(k, v)| match v {
449                    Entry::Present(v) => Some((k, v)),
450                    Entry::Unusable => None,
451                })
452            }
453
454            /// Return a mutable iterator over the elements of this map.
455            ///
456            /// See
457            #[doc= concat!("[`slotmap::", stringify!($mapname), "::iter_mut()`].")]
458            ///
459            /// # Current limitations
460            ///
461            /// Does not return a named type.
462            pub fn iter_mut(&mut self) -> impl Iterator<Item = (K, &mut V)> + '_ {
463                self.base.iter_mut().filter_map(|(k, v)| match v {
464                    Entry::Present(v) => Some((k, v)),
465                    Entry::Unusable => None,
466                })
467            }
468
469            /// Return an iterator over all the keys in this map.
470            ///
471            /// See
472            #[doc= concat!("[`slotmap::", stringify!($mapname), "::keys()`].")]
473            ///
474            /// # Current limitations
475            ///
476            /// Does not return a named type.
477            pub fn keys(&self) -> impl Iterator<Item = K> + '_ {
478                self.iter().map(|(k, _)| k)
479            }
480
481            /// Return an iterator over the values in this map.
482            ///
483            /// See
484            #[doc= concat!("[`slotmap::", stringify!($mapname), "::values()`].")]
485            ///
486            /// # Current limitations
487            ///
488            /// Does not return a named type.
489            pub fn values(&self) -> impl Iterator<Item = &V> + '_ {
490                self.base.values().filter_map(Entry::value)
491            }
492
493            /// Return a mutable iterator over the values in this map.
494            ///
495            /// See
496            #[doc= concat!("[`slotmap::", stringify!($mapname), "::values_mut()`].")]
497            ///
498            /// # Current limitations
499            ///
500            /// Does not return a named type.
501            pub fn values_mut(&mut self) -> impl Iterator<Item = &mut V> + '_ {
502                self.base.values_mut().filter_map(Entry::value_mut)
503            }
504
505            /// Testing helper: Assert that every invariant holds for this map.
506            ///
507            /// # Panics
508            ///
509            /// Panics if any invariant does not hold.
510            #[cfg(test)]
511            fn assert_rep_ok(&self) {
512                let mut n_unusable_found = 0;
513                for (k, v) in self.base.iter() {
514                    assert!(key_okay(k), "Key {:?} was invalid", k.data());
515                    if matches!(v, Entry::Unusable) {
516                        n_unusable_found += 1;
517                        assert_eq!(key_version(k), SATURATE_AT_VERSION);
518                    }
519                }
520                assert_eq!(n_unusable_found, self.n_unusable);
521            }
522        }
523
524        /// Helper: a token constructed if the slotmap behavior matches our expectations.
525        ///
526        /// See `validate_*_behavior()`
527        #[derive(Clone, Debug)]
528        struct [<$mapname ValidationToken>];
529
530        /// Spot-check whether `SlotMap` has changed its key encoding behavior; panic if so.
531        ///
532        /// (Our implementation relies on our ability to check whether a version number is about to
533        /// overflow. But the only efficient way to access a version number is via `KeyData::as_ffi`,
534        /// which does not guarantee anything about the actual encoding of the versions.)
535        ///
536        /// This function returns a ZST ValidationToken; nothing else must return one.
537        /// Being able to construct a ValidationToken implies
538        /// that `slotmap` has probably not changed its behavior in a way that will break us.
539        ///
540        /// # Panics
541        ///
542        /// May panic if slotmap does not encode its keys in the expected manner.
543        fn [<validate_ $mapname:snake _behavior>]() -> [<$mapname ValidationToken>] {
544            use std::sync::atomic::{AtomicBool, Ordering::Relaxed};
545            /// Helper:
546            static VALIDATED: AtomicBool = AtomicBool::new(false);
547            if VALIDATED.load(Relaxed) {
548                // We have already validated it at least once.
549                return [<$mapname ValidationToken>];
550            }
551            /// Helper: assert that key has bit 32 set.
552            fn ver_lsb_check<K: Key>(key: K) {
553                let (ver, _) = key_data::key_data_parts(key.data()).expect("slotmap has changed its serde representation");
554                assert_eq!(ver & 1, 1,
555                    "Key version LSB not set as expected"
556                );
557            }
558
559            let mut map = slotmap::$mapname::new();
560            let k1 = map.insert("a");
561            assert_eq!(key_version(k1), 0, "Keys do not begin with version 0.");
562            assert_eq!(key_slot(k1), 1, "Keys do not begin with index 1.");
563            ver_lsb_check(k1);
564
565            // This is a basic correctness check.
566            map.remove(k1).expect("insert+remove failed");
567            let k2 = map.insert("b");
568            assert_eq!(key_slot(k1), key_slot(k2), "Slot not re-used as expected.");
569            assert_eq!(
570                key_version(k1) + 1,
571                key_version(k2),
572                "Key version did not increment by 1 after slot reuse"
573            );
574            ver_lsb_check(k2);
575
576            let k3 = map.insert("c");
577            assert_eq!(
578                key_version(k3),
579                0,
580                "A different slot did not begin with version 0.",
581            );
582            assert_eq!(
583                key_slot(k3),
584                key_slot(k1) + 1,
585                "Slots not allocated in expected order."
586            );
587            ver_lsb_check(k3);
588
589            // Remember that we've validated SlotMap.
590            VALIDATED.store(true, Relaxed);
591            [<$mapname ValidationToken>]
592        }
593    }
594
595    impl<K:Key, V> std::ops::Index<K> for $mapname<K,V> {
596        type Output = V;
597        fn index(&self, key: K) -> &V {
598            self.get(key).expect("key invalid")
599        }
600    }
601    impl<K:Key, V> std::ops::IndexMut<K> for $mapname<K,V> {
602        fn index_mut(&mut self, key: K) -> &mut V {
603            self.get_mut(key).expect("key invalid")
604        }
605    }
606}} // END OF MACRO.
607
608define_implementation! { SlotMap }
609
610define_implementation! { DenseSlotMap }
611
612define_implementation! { HopSlotMap }
613
614/// Return true if this key is apparently valid.
615///
616/// We should use debug_assert! to test this on every new key, every time an entry is inserted.
617///
618/// If inserting an entry results in a _not_ valid key,
619/// we have messed up, and allowed a version counter to grow too high.
620fn key_okay<K: Key>(key: K) -> bool {
621    key_version(key) <= SATURATE_AT_VERSION
622}
623
624/// Return true if the version number for this key should not be allowed to grow any larger.
625///
626/// We should call this whenever we are about to remove an entry with a given key.
627/// If it returns true, we should instead replace the entry with [`Entry::Unusable`]
628fn key_version_is_maximal<K: Key>(key: K) -> bool {
629    key_version(key) == SATURATE_AT_VERSION
630}
631/// The maximal version that we allow a key to reach.
632///
633/// When it reaches this version, we do not remove the entry with the key any longer;
634/// instead, when we would remove the entry, we instead set its value to [`Entry::Unusable`]
635///
636/// This value is deliberately chosen to be less than the largest possible value (`0x7fff_ffff`),
637/// so that we can detect any bugs that would risk overflowing the version.
638const SATURATE_AT_VERSION: u32 = 0x7fff_fffe;
639
640/// Helper: return the slot of a key, assuming that the representation is as we expect.
641///
642/// Used for testing and verify functions.
643fn key_slot<K: Key>(key: K) -> u32 {
644    let (_, idx) =
645        key_data::key_data_parts(key.data()).expect("slotmap has changed its serde representation");
646    idx
647}
648
649#[cfg(test)]
650mod test {
651    // @@ begin test lint list maintained by maint/add_warning @@
652    #![allow(clippy::bool_assert_comparison)]
653    #![allow(clippy::clone_on_copy)]
654    #![allow(clippy::dbg_macro)]
655    #![allow(clippy::mixed_attributes_style)]
656    #![allow(clippy::print_stderr)]
657    #![allow(clippy::print_stdout)]
658    #![allow(clippy::single_char_pattern)]
659    #![allow(clippy::unwrap_used)]
660    #![allow(clippy::unchecked_duration_subtraction)]
661    #![allow(clippy::useless_vec)]
662    #![allow(clippy::needless_pass_by_value)]
663    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
664
665    /// Create a new key, using `ver` as its version field (includes trailing 1)
666    /// and `idx` as its index field.
667    fn construct_key(ver: u32, idx: u32) -> slotmap::DefaultKey {
668        let j = serde_json::json! {
669            {
670                "version": ver,
671                "idx": idx,
672            }
673        };
674        serde_json::from_value(j).expect("invalid representation")
675    }
676
677    /// Define a set of tests for one of the map variants, in a module named after that variant.
678    macro_rules! tests_for {
679            { $mapname:ident } => {paste::paste!{
680
681            mod [<$mapname:snake>] {
682
683                use slotmap::DefaultKey;
684                use crate::*;
685
686            #[test]
687            fn validate() {
688                let _tok = [<validate_ $mapname:snake _behavior>]();
689            }
690
691            #[test]
692            fn empty() {
693                let mut m: $mapname<DefaultKey, ()> = $mapname::default();
694
695                for _ in 1..=3 {
696                    assert_eq!(m.len(), 0);
697                    assert!(m.is_empty());
698                    m.assert_rep_ok();
699
700                    let k1 = m.insert(());
701                    let k2 = m.insert(());
702                    let k3 = m.insert(());
703                    m.remove(k1);
704                    m.remove(k2);
705                    m.remove(k3);
706                }
707            }
708
709            fn construct_near_saturated_slotmap() -> ($mapname<DefaultKey, String>, DefaultKey, DefaultKey) {
710                fn encode_ver(v: u32) -> u32 {
711                    (v << 1) | 1
712                }
713
714                let json = serde_json::json! {
715                    [
716                        // sentinel entry.
717                        { "value": null, "version": 0},
718                        { "value": {"Present": "hello"}, "version": encode_ver(SATURATE_AT_VERSION) },
719                        { "value": {"Present": "world"}, "version": encode_ver(SATURATE_AT_VERSION - 2) }
720                    ]
721                };
722
723                let m = $mapname {
724                    base: serde_json::from_value(json).expect("invalid json"),
725                    n_unusable: 0,
726                    _valid: [<validate_ $mapname:snake _behavior>](),
727                };
728                let mut k1 = None;
729                let mut k2 = None;
730
731                for (k, v) in m.iter() {
732                    if v == "hello" {
733                        k1 = Some(k);
734                    }
735                    if v == "world" {
736                        k2 = Some(k);
737                    }
738                }
739                let (k1, k2) = (k1.unwrap(), k2.unwrap());
740                (m, k1, k2)
741            }
742
743            #[test]
744            #[allow(clippy::cognitive_complexity)]
745            fn saturating() {
746                let (mut m, k1, k2) = construct_near_saturated_slotmap();
747
748                assert_eq!(key_version(k1), SATURATE_AT_VERSION);
749                assert_eq!(key_version(k2), SATURATE_AT_VERSION - 2);
750
751                // Replace k1, and make sure that the index is _not_ reused.
752                let v = m.remove(k1);
753                assert_eq!(v.unwrap(), "hello");
754                assert!(matches!(m.base.get(k1), Some(Entry::Unusable)));
755                let k1_new = m.insert("HELLO".into());
756                assert_ne!(key_slot(k1), key_slot(k1_new));
757                assert_eq!(key_version(k1_new), 0);
758                assert!(matches!(m.base.get(k1), Some(Entry::Unusable)));
759                assert_eq!(m.get(k1_new).unwrap(), "HELLO");
760                assert!(m.get(k1).is_none());
761                m.assert_rep_ok();
762
763                // Replace k2 and make sure that that the index gets reused twice.
764                let v = m.remove(k2);
765                assert_eq!(v.unwrap(), "world");
766                let k2_2 = m.insert("WoRlD".into());
767                assert_eq!(key_version(k2_2), SATURATE_AT_VERSION - 1);
768                m.remove(k2_2);
769                m.assert_rep_ok();
770                assert!(m.base.get(k2_2).is_none());
771                let k2_3 = m.insert("WORLD".into());
772                assert_eq!(key_slot(k2), key_slot(k2_2));
773                assert_eq!(key_slot(k2), key_slot(k2_3));
774                assert_eq!(key_version(k2_3), SATURATE_AT_VERSION);
775                m.remove(k2_3);
776                assert!(m.base.get(k2_2).is_none());
777                m.assert_rep_ok();
778
779                let k2_4 = m.insert("World!".into());
780                assert!(matches!(m.base.get(k2_3), Some(Entry::Unusable)));
781                assert_eq!(m.get(k2_4).unwrap(), "World!");
782                assert_ne!(key_slot(k2_4), key_slot(k2));
783                assert!(m.contains_key(k2_4));
784                assert!(!m.contains_key(k2_3));
785                m.assert_rep_ok();
786            }
787
788            #[test]
789            fn insert_variations() {
790                let mut m = $mapname::new();
791                let k1 = m.insert("hello".to_string());
792                let k2 = m.insert_with_key(|k| format!("{:?}", k));
793                let k3 = m
794                    .try_insert_with_key(|k| Result::<_, ()>::Ok(format!("{:?}", k)))
795                    .unwrap();
796                let () = m.try_insert_with_key(|_k| Err(())).unwrap_err();
797
798                assert!(m.contains_key(k1));
799                assert!(m.contains_key(k2));
800                assert!(m.contains_key(k3));
801                assert_eq!(m.len(), 3);
802            }
803
804            #[test]
805            fn remove_large_but_bogus() {
806                let mut m: $mapname<DefaultKey, String> = $mapname::with_capacity(0);
807                let _k1 = m.insert("hello".to_string());
808                // Construct a key with maximal version (so we would expect to freeze it),
809                // but which won't actually be present.
810                let k_fake = super::construct_key((SATURATE_AT_VERSION << 1) | 1, 1);
811
812                let v = m.remove(k_fake);
813                assert!(v.is_none());
814                m.assert_rep_ok();
815            }
816
817            #[test]
818            fn remove_many_times() {
819                let (mut m, k1, _k2) = construct_near_saturated_slotmap();
820
821                let mut n_removed = 0;
822                for _ in 0..10 {
823                    if m.remove(k1).is_some() {
824                        n_removed += 1;
825                    }
826                    m.assert_rep_ok();
827                    assert_eq!(m.n_unusable, 1);
828                    assert_eq!(m.len(), 1);
829                }
830                assert_eq!(n_removed, 1);
831            }
832
833            #[test]
834            fn clear() {
835                let (mut m, k1, k2) = construct_near_saturated_slotmap();
836                assert_eq!(m.len(), 2);
837                assert_eq!(m.is_empty(), false);
838                assert_eq!(m.n_unusable, 0);
839
840                for _ in 0..=2 {
841                    m.clear();
842                    m.assert_rep_ok();
843
844                    assert_eq!(m.len(), 0);
845                    assert_eq!(m.is_empty(), true);
846                    assert!(m.get(k1).is_none());
847                    assert!(m.get(k2).is_none());
848                    assert!(matches!(m.base.get(k1), Some(Entry::Unusable)));
849                    assert_eq!(m.n_unusable, 1);
850                }
851
852                let k_next = m.insert("probe".into());
853                assert_eq!(key_slot(k_next), key_slot(k2));
854                assert_eq!(key_version(k_next), SATURATE_AT_VERSION - 1);
855            }
856
857            #[test]
858            fn retain() {
859                let (mut m, k1, k2) = construct_near_saturated_slotmap();
860
861                // drop all but the nearly-saturated (but not saturated) "world" item.
862                m.retain(|_k, v| v == "world");
863                m.assert_rep_ok();
864                assert_eq!(m.len(), 1);
865                assert!(!m.is_empty());
866                assert_eq!(m.n_unusable, 1);
867                assert_eq!(m.contains_key(k1), false);
868                assert_eq!(m.contains_key(k2), true);
869                assert_eq!(m.base.contains_key(k1), true); // key still internally present as Unusable.
870
871                let (mut m, k1, k2) = construct_near_saturated_slotmap();
872
873                // drop all but the saturated (but not saturated) "hello" item.
874                m.retain(|_k, v| v == "hello");
875                m.assert_rep_ok();
876                assert_eq!(m.len(), 1);
877                assert!(!m.is_empty());
878                assert_eq!(m.n_unusable, 0);
879                assert_eq!(m.contains_key(k1), true);
880                assert_eq!(m.contains_key(k2), false);
881                assert_eq!(m.base.contains_key(k2), false); // key not present.
882            }
883
884            #[test]
885            fn retain_and_panic() {
886                use std::panic::AssertUnwindSafe;
887                let (mut m, k1, _k2) = construct_near_saturated_slotmap();
888
889                let _ = std::panic::catch_unwind(AssertUnwindSafe(|| {
890                    m.retain(|k,_| if k == k1 { false } else { panic!() })
891                })).unwrap_err();
892                m.assert_rep_ok();
893            }
894
895            #[test]
896            fn modify() {
897                let (mut m, k1, k2) = construct_near_saturated_slotmap();
898
899                *m.get_mut(k1).unwrap() = "HELLO".to_string();
900                *m.get_mut(k2).unwrap() = "WORLD".to_string();
901
902                let v: Vec<_> = m.values().collect();
903                assert_eq!(v, vec![&"HELLO".to_string(), &"WORLD".to_string()]);
904            }
905
906            #[test]
907            fn iterators() {
908                let (mut m, k1, k2) = construct_near_saturated_slotmap();
909
910                m.remove(k1);
911                assert_eq!(m.n_unusable, 1);
912
913                for v in m.values_mut() {
914                    *v = "WORLD".to_string();
915                }
916
917                let v: Vec<_> = m.values().collect();
918                assert_eq!(v, vec![&"WORLD".to_string()]);
919
920                let v: Vec<_> = m.iter().collect();
921                assert_eq!(v, vec![(k2, &"WORLD".to_string())]);
922
923                for (k, v) in m.iter_mut() {
924                    assert_eq!(k, k2);
925                    *v = "World".to_string();
926                }
927
928                let v: Vec<_> = m.iter().collect();
929                assert_eq!(v, vec![(k2, &"World".to_string())]);
930
931                let v: Vec<_> = m.keys().collect();
932                assert_eq!(v, vec![k2]);
933
934                m.assert_rep_ok();
935            }
936
937            #[test]
938            fn get_mut_multiple() {
939                let (mut m, k1, k2) = construct_near_saturated_slotmap();
940
941                assert!(m.get_disjoint_mut([k1,k1]).is_none());
942
943                if let Some([v1, v2]) = m.get_disjoint_mut([k1, k2]) {
944                    assert_eq!(v1, "hello");
945                    assert_eq!(v2, "world");
946                    *v1 = "HELLO".into();
947                    *v2 = "WORLD".into();
948                } else {
949                    panic!("get_disjoint_mut failed.");
950                };
951
952                m.remove(k1);
953                assert_eq!(m.contains_key(k1), false);
954                assert_eq!(m.base.contains_key(k1), true);
955                m.assert_rep_ok();
956
957                if let Some([_v1, _v2]) = m.get_disjoint_mut([k1, k2]) {
958                    panic!("get_disjoint_mut succeeded unexpectedly.")
959                }
960            }
961
962            #[test]
963            fn get_capacity() {
964                let (mut m, k1, _) = construct_near_saturated_slotmap();
965
966                let cap_orig = dbg!(m.capacity());
967                m.remove(k1);
968                m.assert_rep_ok();
969
970                assert_eq!(m.n_unusable, 1);
971                assert_eq!(m.capacity(), cap_orig - 1); // capacity decreased, since there is an unusable slot.
972
973                m.reserve(5);
974                assert!(m.capacity() >= 5);
975            }
976
977            #[test]
978            fn index() {
979                let (mut m, k1, k2) = construct_near_saturated_slotmap();
980
981                assert_eq!(m[k1], "hello");
982                assert_eq!(*(&mut m[k2]), "world");
983            }
984        } // end module.
985        }}} // End macro rules
986
987    tests_for! {SlotMap}
988    tests_for! {DenseSlotMap}
989    tests_for! {HopSlotMap}
990}