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}