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//! <!-- @@ end lint list maintained by maint/add_warning @@ -->
48
49mod key_data;
50
51pub use slotmap::{
52 DefaultKey, Key, KeyData, SecondaryMap, SparseSecondaryMap, new_key_type, secondary,
53};
54
55use key_data::key_version_serde as key_version;
56
57//use key_version::key_version_serde;
58
59/// A single entry in one of our careful slotmaps.
60///
61/// An entry can either be `Present` (in which case we treat it normally),
62/// or `Unusable`, in which case we
63#[cfg_attr(test, derive(serde::Serialize, serde::Deserialize))]
64#[derive(Debug, Clone)]
65enum Entry<V> {
66 /// The entry is available.
67 Present(V),
68 /// The entry can no longer be used, removed, or set to anything else.
69 ///
70 /// It must not be removed from the slot map, since doing so would
71 /// increase its slot's version number too high.
72 Unusable,
73}
74
75impl<V> Entry<V> {
76 /// Remove the value of `self` (if any), and make it unusable.
77 fn take_and_mark_unusable(&mut self) -> Option<V> {
78 match std::mem::replace(self, Entry::Unusable) {
79 Entry::Present(v) => Some(v),
80 Entry::Unusable => None,
81 }
82 }
83 /// Return a reference to the value of `self`, if there is one.
84 fn value(&self) -> Option<&V> {
85 match self {
86 Entry::Present(val) => Some(val),
87 Entry::Unusable => None,
88 }
89 }
90 /// Return a mutable reference to the value of `self``, if there is one.
91 fn value_mut(&mut self) -> Option<&mut V> {
92 match self {
93 Entry::Present(val) => Some(val),
94 Entry::Unusable => None,
95 }
96 }
97 /// Consume this entry (which must be `Present`), and return its value.
98 ///
99 /// # Panics
100 ///
101 /// Panics if this entry is `Unusable`.
102 fn unwrap(self) -> V {
103 match self {
104 Entry::Present(val) => val,
105 Entry::Unusable => panic!("Tried to unwrap an unusable slot."),
106 }
107 }
108}
109
110/// Helper: Define a wrapper for a single SlotMap type.
111///
112/// This works for SlotMap, and DenseSlotMap.
113///
114/// (The alternative to using a macro here would be to define a new trait
115/// implemented by all of the SlotMaps, and then to define our own SlotMap as a wrapper around an
116/// instance of that trait.)
117macro_rules! define_implementation {
118 { $mapname:ident } => {paste::paste!{
119
120 /// A variation of
121 #[doc = concat!("[`slotmap::", stringify!($mapname), "`]")]
122 /// that can never give the same key for multiple objects.
123 ///
124 /// Unlike a regular version of
125 #[doc = concat!("`", stringify!($mapname), "`,")]
126 /// this version will not allow a slot's version counter to roll over to
127 /// 0 if it reaches 2^31. Instead, it will mark the slot as unusable for future values.
128 ///
129 /// # Limitations
130 ///
131 /// The possibility of marking a slot as unusable
132 /// makes it possible, given enough removals and re-insertions,
133 /// for a slotmap to use an unbounded amount of memory, even if it is not storing much actual data.
134 /// (From a DOS point of view: Given the ability to re-insert an entry ~2^31 times, an attacker can
135 /// cause a slot-map to render approximately `4+sizeof(V)` bytes unusable.)
136 ///
137 /// This type does not include implementations for:
138 /// * `get_unchecked_mut()`
139 /// * `get_disjoint_unchecked_mut()`
140 /// * `IntoIterator`.
141 /// * `serde::{Serialize, Deserialize}`.
142 ///
143 /// # Risky business!
144 ///
145 /// This code relies upon stability of some undocumented properties of `slotmap` keys.
146 /// In particular, it assumes:
147 /// * that the slotmap KeyData `serde` format is stable,
148 /// * that slot versions are represented as `u32`.
149 /// * that the least significant bit of a slot version is 1 if the slot is full,
150 /// and 0 if the slot is empty.
151 /// * that slot versions start at 0, and increase monotonically as the slot is
152 /// emptied and reused.
153 ///
154 /// Note that these assumptions are _probably_ okay: if `slotmap` were to change them,
155 /// it would thereby create a breaking change in its serde version.
156 //
157 // Invariants:
158 //
159 // For every `(key,value)` that is present in `base`:
160 // - `key_okay(key)` is true.
161 // - if `value` is `Entry::Unusable`, then `key_version(key) == SATURATE_AT_VERSION`.
162 //
163 // `n_unusable` is the number of entries in `base` whose value is `Entry::Unusable`.
164 //
165 // To maintain these invariants:
166 // - Never remove a key with `key_version(key) == SATURATE_AT_VERSION`
167 // - Whenever setting a value to `Unusable`, increment `n_unusable`.
168 #[derive(Clone, Debug)]
169 pub struct $mapname<K: Key, V> {
170 /// An underlying SlotMap, obeying the invariants above.
171 base: slotmap::$mapname<K, Entry<V>>,
172 /// The number of entries in this SlotMap that are filled with [`Entry::Unusable`] values.
173 n_unusable: usize,
174 /// A ZST, used to guarantee that we have spot-checked the behavior of the underlying
175 /// SlotMap implementation.
176 _valid: [<$mapname ValidationToken>],
177 }
178
179 impl<V> $mapname<DefaultKey, V> {
180 /// Construct a new empty map, using a default key type.
181 ///
182 /// See
183 #[doc = concat!("[`slotmap::", stringify!($mapname), "::new()`].")]
184 pub fn new() -> Self {
185 Self::with_key()
186 }
187
188 /// Construct a new empty map with a specified capacity, using a default key type.
189 ///
190 /// See
191 #[doc = concat!("[`slotmap::", stringify!($mapname), "::with_capacity()`].")]
192 /// ::with_capacity()`].
193 pub fn with_capacity(capacity: usize) -> Self {
194 Self::with_capacity_and_key(capacity)
195 }
196 }
197
198 impl<K: Key, V> Default for $mapname<K, V> {
199 fn default() -> Self {
200 Self::with_key()
201 }
202 }
203
204 impl<K: Key, V> $mapname<K, V> {
205 /// Construct a new empty map, using a specialized key type.
206 ///
207 /// See
208 #[doc= concat!("[`slotmap::", stringify!($mapname), "::with_key()`].")]
209 pub fn with_key() -> Self {
210 Self::with_capacity_and_key(0)
211 }
212
213 /// Construct a new empty map with a specified capacity, using a specialized key type.
214 ///
215 /// See
216 #[doc= concat!("[`slotmap::", stringify!($mapname), "::with_capacity_and_key()`].")]
217 pub fn with_capacity_and_key(capacity: usize) -> Self {
218 Self {
219 base: slotmap::$mapname::with_capacity_and_key(capacity),
220 n_unusable: 0,
221 _valid: [<validate_ $mapname:snake _behavior>](),
222 }
223 }
224
225 /// Return the number of items in this map.
226 ///
227 /// See
228 #[doc= concat!("[`slotmap::", stringify!($mapname), "::len()`].")]
229 pub fn len(&self) -> usize {
230 self.base
231 .len()
232 .checked_sub(self.n_unusable)
233 .expect("logic error")
234 }
235
236 /// Return true if this map has no items.
237 ///
238 /// See
239 #[doc= concat!("[`slotmap::", stringify!($mapname), "::is_empty()`].")]
240 pub fn is_empty(&self) -> bool {
241 self.len() == 0
242 }
243
244 /// Return the total number of slots available for entries in this map.
245 ///
246 /// This number includes used slots, as well as empty slots that may become used.
247 ///
248 /// See
249 #[doc= concat!("[`slotmap::", stringify!($mapname), "::capacity()`],")]
250 /// but note that a `slotmap-careful` implementation may _lose_ capacity over time,
251 /// as slots are marked unusable.
252 pub fn capacity(&self) -> usize {
253 self.base
254 .capacity()
255 .checked_sub(self.n_unusable)
256 .expect("logic error")
257 }
258
259 /// Reserve space as needed.
260 ///
261 /// Allocates if needed, so that this map can hold `additional` new entries
262 /// without having to resize.
263 ///
264 /// See
265 #[doc= concat!("[`slotmap::", stringify!($mapname), "::reserve()`].")]
266 pub fn reserve(&mut self, additional: usize) {
267 // Note that we don't need to check n_unusable here: the underlying
268 // map type thinks that unusable entries are full, and so will allocate
269 // correctly.
270 self.base.reserve(additional);
271 }
272
273 /// Return true if the map contains an entry with a given key.
274 ///
275 /// See
276 #[doc= concat!("[`slotmap::", stringify!($mapname), "::contains_key()`].")]
277 pub fn contains_key(&self, key: K) -> bool {
278 // Calling self.get, not self.base.get, so it will be None if the
279 // slot is unusable.
280 self.get(key).is_some()
281 }
282
283 /// Insert a new value into the map, and return the key used for it.
284 ///
285 /// See
286 #[doc= concat!("[`slotmap::", stringify!($mapname), "::insert()`].")]
287 pub fn insert(&mut self, value: V) -> K {
288 let key = self.base.insert(Entry::Present(value));
289 debug_assert!(key_okay(key));
290 key
291 }
292
293 /// Insert a new value into the map, constructing it using its own new key.
294 ///
295 /// This method is useful for the case where a value needs to refer to the
296 /// key that will be assigned to it.
297 ///
298 /// See
299 #[doc= concat!("[`slotmap::", stringify!($mapname), "::insert_with_key()`].")]
300 pub fn insert_with_key<F>(&mut self, f: F) -> K
301 where
302 F: FnOnce(K) -> V,
303 {
304 let key = self.base.insert_with_key(|k| Entry::Present(f(k)));
305 debug_assert!(key_okay(key));
306 key
307 }
308
309 /// As [`Self::insert_with_key`], but may return an `Err`.
310 ///
311 /// See
312 #[doc= concat!("[`slotmap::", stringify!($mapname), "::try_insert_with_key()`].")]
313 pub fn try_insert_with_key<F, E>(&mut self, f: F) -> Result<K, E>
314 where
315 F: FnOnce(K) -> Result<V, E>,
316 {
317 let key = self
318 .base
319 .try_insert_with_key(|k| Ok(Entry::Present(f(k)?)))?;
320 debug_assert!(key_okay(key));
321 Ok(key)
322 }
323
324 /// Remove and return the element of this map with a given key.
325 ///
326 /// Return None if the key is not present in the map.
327 ///
328 /// See
329 #[doc= concat!("[`slotmap::", stringify!($mapname), "::remove()`].")]
330 pub fn remove(&mut self, key: K) -> Option<V> {
331 if key_version_is_maximal(key) {
332 // The key is as large as it is allowed to get,
333 // so we should not actually remove this Entry.
334 match self.base.get_mut(key) {
335 Some(slot) => {
336 // The entry is Present: extract its value and mark it unusable.
337 let rv = slot.take_and_mark_unusable();
338 if rv.is_some() {
339 self.n_unusable += 1;
340 }
341 rv
342 }
343 // The entry is Unusable; treat it as if it weren't there.
344 None => None,
345 }
346 } else {
347 // The Entry::unwrap function will panic if its argument is
348 // Entry::Unusable. But that is impossible in this case,
349 // since we already checked key_version_is_maximal() for this key,
350 // and our invariant guarantees that, if the value is Entry::Unusable,
351 // then key_version(key) == SATURATE_AT_VERSION,
352 // so key_version_is_maximal is true.
353 self.base.remove(key).map(Entry::unwrap)
354 }
355 }
356
357 /// Remove every element of this map that does not satisfy a given predicate.
358 ///
359 /// See
360 #[doc= concat!("[`slotmap::", stringify!($mapname), "::retain()`].")]
361 pub fn retain<F>(&mut self, mut f: F)
362 where
363 F: FnMut(K, &mut V) -> bool,
364 {
365 self.base.retain(|k, v| {
366 let Entry::Present(v_inner) = v else {
367 return true;
368 };
369
370 if f(k, v_inner) {
371 true
372 } else if key_version_is_maximal(k) {
373 self.n_unusable += 1;
374 *v = Entry::Unusable;
375 true
376 } else {
377 false
378 }
379 });
380 }
381
382 /// Remove every element of this map.
383 ///
384 /// See
385 #[doc= concat!("[`slotmap::", stringify!($mapname), "::clear()`].")]
386 pub fn clear(&mut self) {
387 self.retain(|_, _| false);
388 }
389
390 /// Return a reference to the element of this map with a given key.
391 ///
392 /// Return None if there is no such element.
393 ///
394 /// See
395 #[doc= concat!("[`slotmap::", stringify!($mapname), "::get()`].")]
396 pub fn get(&self, key: K) -> Option<&V> {
397 self.base.get(key).and_then(Entry::value)
398 }
399 /// Return a mutable reference to the element of this map with a given key.
400 ///
401 /// Return None if there is no such element.
402 ///
403 /// See
404 #[doc= concat!("[`slotmap::", stringify!($mapname), "::get_mut()`].")]
405 pub fn get_mut(&mut self, key: K) -> Option<&mut V> {
406 self.base.get_mut(key).and_then(|ent| ent.value_mut())
407 }
408
409 /// Return an array of mutable references to the elements of this map with a given list
410 /// of keys.
411 ///
412 /// Return None if any key is not present, or if the same key is given twice.
413 ///
414 /// See
415 #[doc= concat!("[`slotmap::", stringify!($mapname), "::get_disjoint_mut()`].")]
416 pub fn get_disjoint_mut<const N: usize>(&mut self, keys: [K; N]) -> Option<[&mut V; N]> {
417 let vals = self.base.get_disjoint_mut(keys)?;
418 // TODO array::try_map would be preferable, but it isn't stable.
419 if vals.iter().all(|e| matches!(e, Entry::Present(_))) {
420 // Cannot panic, since we checked that every entry is present.
421 Some(vals.map(|v| match v {
422 Entry::Present(v) => v,
423 Entry::Unusable => panic!("Logic error"),
424 }))
425 } else {
426 None
427 }
428 }
429
430 /// Return an iterator over the elements of this map.
431 ///
432 /// See
433 #[doc= concat!("[`slotmap::", stringify!($mapname), "::iter()`].")]
434 ///
435 /// # Current limitations
436 ///
437 /// Does not return a named type.
438 pub fn iter(&self) -> impl Iterator<Item = (K, &V)> + '_ {
439 self.base.iter().filter_map(|(k, v)| match v {
440 Entry::Present(v) => Some((k, v)),
441 Entry::Unusable => None,
442 })
443 }
444
445 /// Remove every element of this map.
446 ///
447 /// See
448 #[doc= concat!("[`slotmap::", stringify!($mapname), "::drain()`].")]
449 pub fn drain(&mut self) -> impl Iterator<Item = (K, V)> + '_ {
450 self.base.drain().filter_map(|(k, v)| match v {
451 Entry::Present(v) => Some((k, v)),
452 Entry::Unusable => None,
453 })
454 }
455
456 /// Return a mutable iterator over the elements of this map.
457 ///
458 /// See
459 #[doc= concat!("[`slotmap::", stringify!($mapname), "::iter_mut()`].")]
460 ///
461 /// # Current limitations
462 ///
463 /// Does not return a named type.
464 pub fn iter_mut(&mut self) -> impl Iterator<Item = (K, &mut V)> + '_ {
465 self.base.iter_mut().filter_map(|(k, v)| match v {
466 Entry::Present(v) => Some((k, v)),
467 Entry::Unusable => None,
468 })
469 }
470
471 /// Return an iterator over all the keys in this map.
472 ///
473 /// See
474 #[doc= concat!("[`slotmap::", stringify!($mapname), "::keys()`].")]
475 ///
476 /// # Current limitations
477 ///
478 /// Does not return a named type.
479 pub fn keys(&self) -> impl Iterator<Item = K> + '_ {
480 self.iter().map(|(k, _)| k)
481 }
482
483 /// Return an iterator over the values in this map.
484 ///
485 /// See
486 #[doc= concat!("[`slotmap::", stringify!($mapname), "::values()`].")]
487 ///
488 /// # Current limitations
489 ///
490 /// Does not return a named type.
491 pub fn values(&self) -> impl Iterator<Item = &V> + '_ {
492 self.base.values().filter_map(Entry::value)
493 }
494
495 /// Return a mutable iterator over the values in this map.
496 ///
497 /// See
498 #[doc= concat!("[`slotmap::", stringify!($mapname), "::values_mut()`].")]
499 ///
500 /// # Current limitations
501 ///
502 /// Does not return a named type.
503 pub fn values_mut(&mut self) -> impl Iterator<Item = &mut V> + '_ {
504 self.base.values_mut().filter_map(Entry::value_mut)
505 }
506
507 /// Testing helper: Assert that every invariant holds for this map.
508 ///
509 /// # Panics
510 ///
511 /// Panics if any invariant does not hold.
512 #[cfg(test)]
513 fn assert_rep_ok(&self) {
514 let mut n_unusable_found = 0;
515 for (k, v) in self.base.iter() {
516 assert!(key_okay(k), "Key {:?} was invalid", k.data());
517 if matches!(v, Entry::Unusable) {
518 n_unusable_found += 1;
519 assert_eq!(key_version(k), SATURATE_AT_VERSION);
520 }
521 }
522 assert_eq!(n_unusable_found, self.n_unusable);
523 }
524 }
525
526 /// Helper: a token constructed if the slotmap behavior matches our expectations.
527 ///
528 /// See `validate_*_behavior()`
529 #[derive(Clone, Debug)]
530 struct [<$mapname ValidationToken>];
531
532 /// Spot-check whether `SlotMap` has changed its key encoding behavior; panic if so.
533 ///
534 /// (Our implementation relies on our ability to check whether a version number is about to
535 /// overflow. But the only efficient way to access a version number is via `KeyData::as_ffi`,
536 /// which does not guarantee anything about the actual encoding of the versions.)
537 ///
538 /// This function returns a ZST ValidationToken; nothing else must return one.
539 /// Being able to construct a ValidationToken implies
540 /// that `slotmap` has probably not changed its behavior in a way that will break us.
541 ///
542 /// # Panics
543 ///
544 /// May panic if slotmap does not encode its keys in the expected manner.
545 fn [<validate_ $mapname:snake _behavior>]() -> [<$mapname ValidationToken>] {
546 use std::sync::atomic::{AtomicBool, Ordering::Relaxed};
547 /// Helper:
548 static VALIDATED: AtomicBool = AtomicBool::new(false);
549 if VALIDATED.load(Relaxed) {
550 // We have already validated it at least once.
551 return [<$mapname ValidationToken>];
552 }
553 /// Helper: assert that key has bit 32 set.
554 fn ver_lsb_check<K: Key>(key: K) {
555 let (ver, _) = key_data::key_data_parts(key.data()).expect("slotmap has changed its serde representation");
556 assert_eq!(ver & 1, 1,
557 "Key version LSB not set as expected"
558 );
559 }
560
561 let mut map = slotmap::$mapname::new();
562 let k1 = map.insert("a");
563 assert_eq!(key_version(k1), 0, "Keys do not begin with version 0.");
564 assert_eq!(key_slot(k1), 1, "Keys do not begin with index 1.");
565 ver_lsb_check(k1);
566
567 // This is a basic correctness check.
568 map.remove(k1).expect("insert+remove failed");
569 let k2 = map.insert("b");
570 assert_eq!(key_slot(k1), key_slot(k2), "Slot not re-used as expected.");
571 assert_eq!(
572 key_version(k1) + 1,
573 key_version(k2),
574 "Key version did not increment by 1 after slot reuse"
575 );
576 ver_lsb_check(k2);
577
578 let k3 = map.insert("c");
579 assert_eq!(
580 key_version(k3),
581 0,
582 "A different slot did not begin with version 0.",
583 );
584 assert_eq!(
585 key_slot(k3),
586 key_slot(k1) + 1,
587 "Slots not allocated in expected order."
588 );
589 ver_lsb_check(k3);
590
591 // Remember that we've validated SlotMap.
592 VALIDATED.store(true, Relaxed);
593 [<$mapname ValidationToken>]
594 }
595 }
596
597 impl<K:Key, V> std::ops::Index<K> for $mapname<K,V> {
598 type Output = V;
599 fn index(&self, key: K) -> &V {
600 self.get(key).expect("key invalid")
601 }
602 }
603 impl<K:Key, V> std::ops::IndexMut<K> for $mapname<K,V> {
604 fn index_mut(&mut self, key: K) -> &mut V {
605 self.get_mut(key).expect("key invalid")
606 }
607 }
608}} // END OF MACRO.
609
610define_implementation! { SlotMap }
611
612define_implementation! { DenseSlotMap }
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_time_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}