Skip to main content

slotmap_careful/
lib.rs

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