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