[go: up one dir, main page]

slotmap/
lib.rs

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