slotmap_map/lib.rs
1#![doc(html_root_url = "https://docs.rs/slotmap/1.0.7")]
2#![crate_name = "slotmap_map"]
3#![cfg_attr(all(nightly, feature = "unstable"), feature(try_reserve))]
4#![cfg_attr(all(not(test), not(feature = "std")), no_std)]
5#![cfg_attr(all(nightly, doc), feature(doc_cfg))]
6#![warn(
7 missing_debug_implementations,
8 trivial_casts,
9 trivial_numeric_casts,
10 unused_lifetimes,
11 unused_import_braces
12)]
13#![deny(missing_docs, unaligned_references)]
14#![cfg_attr(feature = "cargo-clippy", allow(renamed_and_removed_lints))]
15#![cfg_attr(feature = "cargo-clippy", deny(clippy, clippy_pedantic))]
16#![cfg_attr(
17 feature = "cargo-clippy",
18 allow(
19 // Style differences.
20 module_name_repetitions,
21 redundant_closure_for_method_calls,
22 unseparated_literal_suffix,
23
24 // I know what I'm doing and want these.
25 wildcard_imports,
26 inline_always,
27 cast_possible_truncation,
28 needless_pass_by_value,
29
30 // Very noisy.
31 missing_errors_doc,
32 must_use_candidate
33 ))]
34
35//! # slotmap
36//!
37//! This library provides a container with persistent unique keys to access
38//! stored values, [`SlotMap`]. Upon insertion a key is returned that can be
39//! used to later access or remove the values. Insertion, removal and access all
40//! take O(1) time with low overhead. Great for storing collections of objects
41//! that need stable, safe references but have no clear ownership otherwise,
42//! such as game entities or graph nodes.
43//!
44//! The difference between a [`BTreeMap`] or [`HashMap`] and a slot map is
45//! that the slot map generates and returns the key when inserting a value. A
46//! key is always unique and will only refer to the value that was inserted.
47//! A slot map's main purpose is to simply own things in a safe and efficient
48//! manner.
49//!
50//! You can also create (multiple) secondary maps that can map the keys returned
51//! by [`SlotMap`] to other values, to associate arbitrary data with objects
52//! stored in slot maps, without hashing required - it's direct indexing under
53//! the hood.
54//!
55//! The minimum required stable Rust version for this crate is 1.49.
56//!
57//! # Examples
58//!
59//! ```
60//! # use slotmap-map::*;
61//! let mut sm = SlotMap::new();
62//! let foo = sm.insert("foo"); // Key generated on insert.
63//! let bar = sm.insert("bar");
64//! assert_eq!(sm[foo], "foo");
65//! assert_eq!(sm[bar], "bar");
66//!
67//! sm.remove(bar);
68//! let reuse = sm.insert("reuse"); // Space from bar reused.
69//! assert_eq!(sm.contains_key(bar), false); // After deletion a key stays invalid.
70//!
71//! let mut sec = SecondaryMap::new();
72//! sec.insert(foo, "noun"); // We provide the key for secondary maps.
73//! sec.insert(reuse, "verb");
74//!
75//! for (key, val) in sm {
76//! println!("{} is a {}", val, sec[key]);
77//! }
78//! ```
79//!
80//! # Serialization through [`serde`], [`no_std`] support and unstable features
81//!
82//! Both keys and the slot maps have full (de)seralization support through
83//! the [`serde`] library. A key remains valid for a slot map even after one or
84//! both have been serialized and deserialized! This makes storing or
85//! transferring complicated referential structures and graphs a breeze. Care has
86//! been taken such that deserializing keys and slot maps from untrusted sources
87//! is safe. If you wish to use these features you must enable the `serde`
88//! feature flag for `slotmap` in your `Cargo.toml`.
89//!
90//! ```text
91//! slotmap = { version = "1.0", features = ["serde"] }
92//! ```
93//!
94//! This crate also supports [`no_std`] environments, but does require the
95//! [`alloc`] crate to be available. To enable this you have to disable the
96//! `std` feature that is enabled by default:
97//!
98//! ```text
99//! slotmap = { version = "1.0", default-features = false }
100//! ```
101//!
102//! Unfortunately [`SparseSecondaryMap`] is not available in [`no_std`], because
103//! it relies on [`HashMap`]. Finally the `unstable` feature can be defined to
104//! enable the parts of `slotmap` that only work on nightly Rust.
105//!
106//! # Why not index a [`Vec`], or use [`slab`], [`stable-vec`], etc?
107//!
108//! Those solutions either can not reclaim memory from deleted elements or
109//! suffer from the ABA problem. The keys returned by `slotmap` are versioned.
110//! This means that once a key is removed, it stays removed, even if the
111//! physical storage inside the slotmap is reused for new elements. The key is a
112//! permanently unique<sup>*</sup> reference to the inserted value. Despite
113//! supporting versioning, a [`SlotMap`] is often not (much) slower than the
114//! alternative, by internally using carefully checked unsafe code. Finally,
115//! `slotmap` simply has a lot of features that make your life easy.
116//!
117//! # Performance characteristics and implementation details
118//!
119//! Insertion, access and deletion is all O(1) with low overhead by storing the
120//! elements inside a [`Vec`]. Unlike references or indices into a vector,
121//! unless you remove a key it is never invalidated. Behind the scenes each
122//! slot in the vector is a `(value, version)` tuple. After insertion the
123//! returned key also contains a version. Only when the stored version and
124//! version in a key match is a key valid. This allows us to reuse space in the
125//! vector after deletion without letting removed keys point to spurious new
126//! elements. <sup>*</sup>After 2<sup>31</sup> deletions and insertions to the
127//! same underlying slot the version wraps around and such a spurious reference
128//! could potentially occur. It is incredibly unlikely however, and in all
129//! circumstances is the behavior safe. A slot map can hold up to
130//! 2<sup>32</sup> - 2 elements at a time.
131//!
132//! The memory usage for each slot in [`SlotMap`] is `4 + max(sizeof(T), 4)`
133//! rounded up to the alignment of `T`. Similarly it is `4 + max(sizeof(T), 12)`
134//! for [`HopSlotMap`]. [`DenseSlotMap`] has an overhead of 8 bytes per element
135//! and 8 bytes per slot.
136//!
137//! # Choosing [`SlotMap`], [`HopSlotMap`] or [`DenseSlotMap`]
138//!
139//! A [`SlotMap`] is the fastest for most operations, except iteration. It can
140//! never shrink the size of its underlying storage, because it must remember
141//! for each storage slot what the latest stored version was, even if the slot
142//! is empty now. This means that iteration can be slow as it must iterate over
143//! potentially a lot of empty slots.
144//!
145//! [`HopSlotMap`] solves this by maintaining more information on
146//! insertion/removal, allowing it to iterate only over filled slots by 'hopping
147//! over' contiguous blocks of vacant slots. This can give it significantly
148//! better iteration speed. If you expect to iterate over all elements in a
149//! [`SlotMap`] a lot, and potentially have a lot of deleted elements, choose
150//! [`HopSlotMap`]. The downside is that insertion and removal is roughly twice
151//! as slow. Random access is the same speed for both.
152//!
153//! [`DenseSlotMap`] goes even further and stores all elements on a contiguous
154//! block of memory. It uses two indirections per random access; the slots
155//! contain indices used to access the contiguous memory. This means random
156//! access is slower than both [`SlotMap`] and [`HopSlotMap`], but iteration is
157//! significantly faster, as fast as a normal [`Vec`].
158//!
159//! # Choosing [`SecondaryMap`] or [`SparseSecondaryMap`]
160//!
161//! You want to associate extra data with objects stored in a slot map, so you
162//! use (multiple) secondary maps to map keys to that data.
163//!
164//! A [`SecondaryMap`] is simply a [`Vec`] of slots like slot map is, and
165//! essentially provides all the same guarantees as [`SlotMap`] does for its
166//! operations (with the exception that you provide the keys as produced by the
167//! primary slot map). This does mean that even if you associate data to only
168//! a single element from the primary slot map, you could need and have to
169//! initialize as much memory as the original.
170//!
171//! A [`SparseSecondaryMap`] is like a [`HashMap`] from keys to objects, however
172//! it automatically removes outdated keys for slots that had their space
173//! reused. You should use this variant if you expect to store some associated
174//! data for only a small portion of the primary slot map.
175//!
176//! # Custom key types
177//!
178//! If you have multiple slot maps it's an error to use the key of one slot map
179//! on another slot map. The result is safe, but unspecified, and can not be
180//! detected at runtime, so it can lead to a hard to find bug.
181//!
182//! To prevent this, slot maps allow you to specify what the type is of the key
183//! they return. You can construct new key types using the [`new_key_type!`]
184//! macro. The resulting type behaves exactly like [`DefaultKey`], but is a
185//! distinct type. So instead of simply using `SlotMap<DefaultKey, Player>` you
186//! would use:
187//!
188//! ```
189//! # use slotmap-map::*;
190//! # #[derive(Copy, Clone)]
191//! # struct Player;
192//! new_key_type! { struct PlayerKey; }
193//! let sm: SlotMap<PlayerKey, Player> = SlotMap::with_key();
194//! ```
195//!
196//! You can write code generic over any key type using the [`Key`] trait.
197//!
198//! [`Vec`]: std::vec::Vec
199//! [`BTreeMap`]: std::collections::BTreeMap
200//! [`HashMap`]: std::collections::HashMap
201//! [`serde`]: https://github.com/serde-rs/serde
202//! [`slab`]: https://crates.io/crates/slab
203//! [`stable-vec`]: https://crates.io/crates/stable-vec
204//! [`no_std`]: https://doc.rust-lang.org/1.7.0/book/no-stdlib.html
205
206extern crate alloc;
207
208// So our macros can refer to these.
209#[doc(hidden)]
210pub mod __impl {
211 pub use core::convert::From;
212 pub use core::result::Result;
213 #[cfg(feature = "serde")]
214 pub use serde::{Deserialize, Deserializer, Serialize, Serializer};
215}
216
217pub mod basic;
218pub mod dense;
219pub mod hop;
220pub mod secondary;
221#[cfg(feature = "std")]
222pub mod sparse_secondary;
223pub(crate) mod util;
224
225use core::fmt::{self, Debug, Formatter};
226use core::hash::{Hash, Hasher};
227use core::num::NonZeroU32;
228
229#[doc(inline)]
230pub use crate::basic::SlotMap;
231#[doc(inline)]
232pub use crate::dense::DenseSlotMap;
233#[doc(inline)]
234pub use crate::hop::HopSlotMap;
235#[doc(inline)]
236pub use crate::secondary::SecondaryMap;
237#[cfg(feature = "std")]
238#[doc(inline)]
239pub use crate::sparse_secondary::SparseSecondaryMap;
240
241// Keep Slottable for backwards compatibility, but warn about deprecation
242// and hide from documentation.
243#[doc(hidden)]
244#[deprecated(
245 since = "1.0.0",
246 note = "Slottable is not necessary anymore, slotmap now supports all types on stable."
247)]
248pub trait Slottable {}
249
250#[doc(hidden)]
251#[allow(deprecated)]
252impl<T> Slottable for T {}
253
254/// The actual data stored in a [`Key`].
255///
256/// This implements [`Ord`](std::cmp::Ord) so keys can be stored in e.g.
257/// [`BTreeMap`](std::collections::BTreeMap), but the order of keys is
258/// unspecified.
259#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
260pub struct KeyData {
261 idx: u32,
262 version: NonZeroU32,
263}
264
265impl KeyData {
266 fn new(idx: u32, version: u32) -> Self {
267 debug_assert!(version > 0);
268
269 Self {
270 idx,
271 version: unsafe { NonZeroU32::new_unchecked(version | 1) },
272 }
273 }
274
275 fn null() -> Self {
276 Self::new(core::u32::MAX, 1)
277 }
278
279 fn is_null(self) -> bool {
280 self.idx == core::u32::MAX
281 }
282
283 /// Returns the key data as a 64-bit integer. No guarantees about its value
284 /// are made other than that passing it to [`from_ffi`](Self::from_ffi)
285 /// will return a key equal to the original.
286 ///
287 /// With this you can easily pass slot map keys as opaque handles to foreign
288 /// code. After you get them back you can confidently use them in your slot
289 /// map without worrying about unsafe behavior as you would with passing and
290 /// receiving back references or pointers.
291 ///
292 /// This is not a substitute for proper serialization, use [`serde`] for
293 /// that. If you are not doing FFI, you almost surely do not need this
294 /// function.
295 ///
296 /// [`serde`]: crate#serialization-through-serde-no_std-support-and-unstable-features
297 pub fn as_ffi(self) -> u64 {
298 (u64::from(self.version.get()) << 32) | u64::from(self.idx)
299 }
300
301 /// Iff `value` is a value received from `k.as_ffi()`, returns a key equal
302 /// to `k`. Otherwise the behavior is safe but unspecified.
303 pub fn from_ffi(value: u64) -> Self {
304 let idx = value & 0xffff_ffff;
305 let version = (value >> 32) | 1; // Ensure version is odd.
306 Self::new(idx as u32, version as u32)
307 }
308}
309
310impl Debug for KeyData {
311 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
312 write!(f, "{}v{}", self.idx, self.version.get())
313 }
314}
315
316impl Default for KeyData {
317 fn default() -> Self {
318 Self::null()
319 }
320}
321
322impl Hash for KeyData {
323 fn hash<H: Hasher>(&self, state: &mut H) {
324 // A derived Hash impl would call write_u32 twice. We call write_u64
325 // once, which is beneficial if the hasher implements write_u64
326 // explicitly.
327 state.write_u64(self.as_ffi())
328 }
329}
330
331/// Key used to access stored values in a slot map.
332///
333/// Do not use a key from one slot map in another. The behavior is safe but
334/// non-sensical (and might panic in case of out-of-bounds).
335///
336/// To prevent this, it is suggested to have a unique key type for each slot
337/// map. You can create new key types using [`new_key_type!`], which makes a
338/// new type identical to [`DefaultKey`], just with a different name.
339///
340/// This trait is intended to be a thin wrapper around [`KeyData`], and all
341/// methods must behave exactly as if we're operating on a [`KeyData`] directly.
342/// The internal unsafe code relies on this, therefore this trait is `unsafe` to
343/// implement. It is strongly suggested to simply use [`new_key_type!`] instead
344/// of implementing this trait yourself.
345pub unsafe trait Key:
346 From<KeyData>
347 + Copy
348 + Clone
349 + Default
350 + Eq
351 + PartialEq
352 + Ord
353 + PartialOrd
354 + core::hash::Hash
355 + core::fmt::Debug
356{
357 /// Creates a new key that is always invalid and distinct from any non-null
358 /// key. A null key can only be created through this method (or default
359 /// initialization of keys made with [`new_key_type!`], which calls this
360 /// method).
361 ///
362 /// A null key is always invalid, but an invalid key (that is, a key that
363 /// has been removed from the slot map) does not become a null key. A null
364 /// is safe to use with any safe method of any slot map instance.
365 ///
366 /// # Examples
367 ///
368 /// ```
369 /// # use slotmap-map::*;
370 /// let mut sm = SlotMap::new();
371 /// let k = sm.insert(42);
372 /// let nk = DefaultKey::null();
373 /// assert!(nk.is_null());
374 /// assert!(k != nk);
375 /// assert_eq!(sm.get(nk), None);
376 /// ```
377 fn null() -> Self {
378 KeyData::null().into()
379 }
380
381 /// Checks if a key is null. There is only a single null key, that is
382 /// `a.is_null() && b.is_null()` implies `a == b`.
383 ///
384 /// # Examples
385 ///
386 /// ```
387 /// # use slotmap-map::*;
388 /// new_key_type! { struct MyKey; }
389 /// let a = MyKey::null();
390 /// let b = MyKey::default();
391 /// assert_eq!(a, b);
392 /// assert!(a.is_null());
393 /// ```
394 fn is_null(&self) -> bool {
395 self.data().is_null()
396 }
397
398 /// Gets the [`KeyData`] stored in this key.
399 ///
400 /// # Examples
401 ///
402 /// ```
403 /// # use slotmap-map::*;
404 /// new_key_type! { struct MyKey; }
405 /// let dk = DefaultKey::null();
406 /// let mk = MyKey::null();
407 /// assert_eq!(dk.data(), mk.data());
408 /// ```
409 fn data(&self) -> KeyData;
410}
411
412/// A helper macro to create new key types. If you use a new key type for each
413/// slot map you create you can entirely prevent using the wrong key on the
414/// wrong slot map.
415///
416/// The type constructed by this macro is defined exactly as [`DefaultKey`],
417/// but is a distinct type for the type checker and does not implicitly convert.
418///
419/// # Examples
420///
421/// ```
422/// # extern crate slotmap;
423/// # use slotmap-map::*;
424/// new_key_type! {
425/// // A private key type.
426/// struct RocketKey;
427///
428/// // A public key type with a doc comment.
429/// /// Key for the user slot map.
430/// pub struct UserKey;
431/// }
432///
433/// fn main() {
434/// let mut users = SlotMap::with_key();
435/// let mut rockets = SlotMap::with_key();
436/// let bob: UserKey = users.insert("bobby");
437/// let apollo: RocketKey = rockets.insert("apollo");
438/// // Now this is a type error because rockets.get expects an RocketKey:
439/// // rockets.get(bob);
440///
441/// // If for some reason you do end up needing to convert (e.g. storing
442/// // keys of multiple slot maps in the same data structure without
443/// // boxing), you can use KeyData as an intermediate representation. This
444/// // does mean that once again you are responsible for not using the wrong
445/// // key on the wrong slot map.
446/// let keys = vec![bob.data(), apollo.data()];
447/// println!("{} likes rocket {}",
448/// users[keys[0].into()], rockets[keys[1].into()]);
449/// }
450/// ```
451#[macro_export(local_inner_macros)]
452macro_rules! new_key_type {
453 ( $(#[$outer:meta])* $vis:vis struct $name:ident; $($rest:tt)* ) => {
454 $(#[$outer])*
455 #[derive(Copy, Clone, Default,
456 Eq, PartialEq, Ord, PartialOrd,
457 Hash, Debug)]
458 #[repr(transparent)]
459 $vis struct $name($crate::KeyData);
460
461 impl $crate::__impl::From<$crate::KeyData> for $name {
462 fn from(k: $crate::KeyData) -> Self {
463 $name(k)
464 }
465 }
466
467 unsafe impl $crate::Key for $name {
468 fn data(&self) -> $crate::KeyData {
469 self.0
470 }
471 }
472
473 $crate::__serialize_key!($name);
474
475 $crate::new_key_type!($($rest)*);
476 };
477
478 () => {}
479}
480
481#[cfg(feature = "serde")]
482#[doc(hidden)]
483#[macro_export]
484macro_rules! __serialize_key {
485 ( $name:ty ) => {
486 impl $crate::__impl::Serialize for $name {
487 fn serialize<S>(&self, serializer: S) -> $crate::__impl::Result<S::Ok, S::Error>
488 where
489 S: $crate::__impl::Serializer,
490 {
491 $crate::Key::data(self).serialize(serializer)
492 }
493 }
494
495 impl<'de> $crate::__impl::Deserialize<'de> for $name {
496 fn deserialize<D>(deserializer: D) -> $crate::__impl::Result<Self, D::Error>
497 where
498 D: $crate::__impl::Deserializer<'de>,
499 {
500 let key_data: $crate::KeyData =
501 $crate::__impl::Deserialize::deserialize(deserializer)?;
502 Ok(key_data.into())
503 }
504 }
505 };
506}
507
508#[cfg(not(feature = "serde"))]
509#[doc(hidden)]
510#[macro_export]
511macro_rules! __serialize_key {
512 ( $name:ty ) => {};
513}
514
515new_key_type! {
516 /// The default slot map key type.
517 pub struct DefaultKey;
518}
519
520// Serialization with serde.
521#[cfg(feature = "serde")]
522mod serialize {
523 use serde::{Deserialize, Deserializer, Serialize, Serializer};
524
525 use super::*;
526
527 #[derive(Serialize, Deserialize)]
528 pub struct SerKey {
529 idx: u32,
530 version: u32,
531 }
532
533 impl Serialize for KeyData {
534 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
535 where
536 S: Serializer,
537 {
538 let ser_key = SerKey {
539 idx: self.idx,
540 version: self.version.get(),
541 };
542 ser_key.serialize(serializer)
543 }
544 }
545
546 impl<'de> Deserialize<'de> for KeyData {
547 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
548 where
549 D: Deserializer<'de>,
550 {
551 let mut ser_key: SerKey = Deserialize::deserialize(deserializer)?;
552
553 // Ensure a.is_null() && b.is_null() implies a == b.
554 if ser_key.idx == core::u32::MAX {
555 ser_key.version = 1;
556 }
557
558 ser_key.version |= 1; // Ensure version is odd.
559 Ok(Self::new(ser_key.idx, ser_key.version))
560 }
561 }
562}
563
564#[cfg(test)]
565mod tests {
566 // Intentionally no `use super::*;` because we want to test macro expansion
567 // in the *users* scope, which might not have that.
568 #[test]
569 fn macro_expansion() {
570 #![allow(dead_code)]
571 use super::new_key_type;
572
573 // Clobber namespace with clashing names - should still work.
574 trait Serialize {}
575 trait Deserialize {}
576 trait Serializer {}
577 trait Deserializer {}
578 trait Key {}
579 trait From {}
580 struct Result;
581 struct KeyData;
582
583 new_key_type! {
584 struct A;
585 pub(crate) struct B;
586 pub struct C;
587 }
588 }
589
590 #[test]
591 fn check_is_older_version() {
592 use super::util::is_older_version;
593
594 let is_older = |a, b| is_older_version(a, b);
595 assert!(!is_older(42, 42));
596 assert!(is_older(0, 1));
597 assert!(is_older(0, 1 << 31));
598 assert!(!is_older(0, (1 << 31) + 1));
599 assert!(is_older(u32::MAX, 0));
600 }
601
602 #[test]
603 fn iters_cloneable() {
604 use super::*;
605
606 struct NoClone;
607
608 let mut sm = SlotMap::new();
609 let mut hsm = HopSlotMap::new();
610 let mut dsm = DenseSlotMap::new();
611 let mut scm = SecondaryMap::new();
612 let mut sscm = SparseSecondaryMap::new();
613 scm.insert(sm.insert(NoClone), NoClone);
614 sscm.insert(hsm.insert(NoClone), NoClone);
615 dsm.insert(NoClone);
616
617 let _ = sm.keys().clone();
618 let _ = sm.values().clone();
619 let _ = sm.iter().clone();
620 let _ = hsm.keys().clone();
621 let _ = hsm.values().clone();
622 let _ = hsm.iter().clone();
623 let _ = dsm.keys().clone();
624 let _ = dsm.values().clone();
625 let _ = dsm.iter().clone();
626 let _ = scm.keys().clone();
627 let _ = scm.values().clone();
628 let _ = scm.iter().clone();
629 let _ = sscm.keys().clone();
630 let _ = sscm.values().clone();
631 let _ = sscm.iter().clone();
632 }
633
634 #[cfg(feature = "serde")]
635 #[test]
636 fn key_serde() {
637 use super::*;
638
639 // Check round-trip through serde.
640 let mut sm = SlotMap::new();
641 let k = sm.insert(42);
642 let ser = serde_json::to_string(&k).unwrap();
643 let de: DefaultKey = serde_json::from_str(&ser).unwrap();
644 assert_eq!(k, de);
645
646 // Even if a malicious entity sends up even (unoccupied) versions in the
647 // key, we make the version point to the occupied version.
648 let malicious: KeyData = serde_json::from_str(&r#"{"idx":0,"version":4}"#).unwrap();
649 assert_eq!(malicious.version.get(), 5);
650 }
651}