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