1 #![deny(warnings, missing_docs, missing_debug_implementations)]
2 #![doc(html_root_url = "https://docs.rs/slotmap/0.4.0")]
3 #![crate_name = "slotmap"]
4 #![cfg_attr(feature = "unstable", feature(untagged_unions, try_reserve))]
5
6 //! # slotmap
7 //!
8 //! This library provides a container with persistent unique keys to access
9 //! stored values, [`SlotMap`]. Upon insertion a key is returned that can be
10 //! used to later access or remove the values. Insertion, removal and access all
11 //! take O(1) time with low overhead. Great for storing collections of objects
12 //! that need stable, safe references but have no clear ownership otherwise,
13 //! such as game entities or graph nodes.
14 //!
15 //! The difference between a [`BTreeMap`] or [`HashMap`] and a slot map is
16 //! that the slot map generates and returns the key when inserting a value. A
17 //! key is always unique and will only refer to the value that was inserted.
18 //! A slot map's main purpose is to simply own things in a safe and efficient
19 //! manner.
20 //!
21 //! You can also create (multiple) secondary maps that can map the keys returned
22 //! by [`SlotMap`] to other values, to associate arbitrary data with objects
23 //! stored in slot maps, without hashing required - it's direct indexing under
24 //! the hood.
25 //!
26 //! # Examples
27 //!
28 //! ```
29 //! # use slotmap::*;
30 //! let mut sm = SlotMap::new();
31 //! let foo = sm.insert("foo"); // Key generated on insert.
32 //! let bar = sm.insert("bar");
33 //! assert_eq!(sm[foo], "foo");
34 //! assert_eq!(sm[bar], "bar");
35 //!
36 //! sm.remove(bar);
37 //! let reuse = sm.insert("reuse"); // Space from bar reused.
38 //! assert_eq!(sm.contains_key(bar), false); // After deletion a key stays invalid.
39 //!
40 //! let mut sec = SecondaryMap::new();
41 //! sec.insert(foo, "noun"); // We provide the key for secondary maps.
42 //! sec.insert(reuse, "verb");
43 //!
44 //! for (key, val) in sm {
45 //! println!("{} is a {}", val, sec[key]);
46 //! }
47 //! ```
48 //!
49 //! # Serialization through [`serde`]
50 //!
51 //! Both keys and the slot maps have full (de)seralization support through
52 //! the [`serde`] library. A key remains valid for a slot map even after one or
53 //! both have been serialized and deserialized! This makes storing or
54 //! transferring complicated referential structures and graphs a breeze. Care has
55 //! been taken such that deserializing keys and slot maps from untrusted sources
56 //! is safe. If you wish to use these features you must enable the `serde`
57 //! feature flag for `slotmap` in your `Cargo.toml`.
58 //!
59 //! ```text
60 //! slotmap = { version = "...", features = ["serde"] }
61 //! ```
62 //!
63 //! # Why not [`slab`]?
64 //!
65 //! Unlike [`slab`], the keys returned by [`SlotMap`] are versioned. This means
66 //! that once a key is removed, it stays removed, even if the physical storage
67 //! inside the slotmap is reused for new elements. The key is a
68 //! permanently unique<sup>*</sup> reference to the inserted value. Despite
69 //! supporting versioning, a [`SlotMap`] is not slower than [`slab`], by
70 //! internally using carefully checked unsafe code. A [`HopSlotMap`]
71 //! also provides faster iteration than [`slab`] does, and [`DenseSlotMap`] even
72 //! faster still. Additionally, at the time of writing [`slab`] does not support
73 //! serialization.
74 //!
75 //! # Performance characteristics and implementation details
76 //!
77 //! Insertion, access and deletion is all O(1) with low overhead by storing the
78 //! elements inside a [`Vec`]. Unlike references or indices into a vector,
79 //! unless you remove a key it is never invalidated. Behind the scenes each
80 //! slot in the vector is a `(value, version)` tuple. After insertion the
81 //! returned key also contains a version. Only when the stored version and
82 //! version in a key match is a key valid. This allows us to reuse space in the
83 //! vector after deletion without letting removed keys point to spurious new
84 //! elements. <sup>*</sup>After 2<sup>31</sup> deletions and insertions to the
85 //! same underlying slot the version wraps around and such a spurious reference
86 //! could potentially occur. It is incredibly unlikely however, and in all
87 //! circumstances is the behavior safe. A slot map can hold up to
88 //! 2<sup>32</sup> - 2 elements at a time.
89 //!
90 //! The memory usage for each slot in [`SlotMap`] is `4 + max(sizeof(T), 4)`
91 //! rounded up to the alignment of `T`. Similarly it is `4 + max(sizeof(T), 12)`
92 //! for [`HopSlotMap`]. [`DenseSlotMap`] has an overhead of 8 bytes per element
93 //! and 8 bytes per slot.
94 //!
95 //! # Choosing `SlotMap`, `HopSlotMap` or `DenseSlotMap`
96 //!
97 //! A [`SlotMap`] can never shrink the size of its underlying storage, because
98 //! for each storage slot it must remember what the latest stored version was,
99 //! even if the slot is empty now. This means that iteration can be slow as it
100 //! must iterate over potentially a lot of empty slots.
101 //!
102 //! [`HopSlotMap`] solves this by maintaining more information on
103 //! insertion/removal, allowing it to iterate only over filled slots by 'hopping
104 //! over' contiguous blocks of vacant slots. This can give it significantly
105 //! better iteration speed. If you expect to iterate over all elements in a
106 //! [`SlotMap`] a lot, choose [`HopSlotMap`]. The downside is that insertion and
107 //! removal is roughly twice as slow. Random access is the same speed for both.
108 //!
109 //! [`DenseSlotMap`] goes even further and stores all elements on a contiguous
110 //! block of memory. It uses two indirects per random access; the slots contain
111 //! indices used to access the contiguous memory. This means random access is
112 //! slower than both [`SlotMap`] and [`HopSlotMap`], but iteration is
113 //! significantly faster. Finally, there is no trait requirement on the value
114 //! type of a [`DenseSlotMap`], see [`Slottable`] for more details.
115 //!
116 //! # Choosing `SecondaryMap` or `SparseSecondaryMap`
117 //!
118 //! You want to associate extra data with objects stored in a slot map, so you
119 //! use (multiple) secondary maps to map keys to that data.
120 //!
121 //! A [`SecondaryMap`] is simply a [`Vec`] of slots like slot map is, and
122 //! essentially provides all the same guarantees as [`SlotMap`] does for its
123 //! operations (with the exception that you provide the keys as produced by the
124 //! primary slot map). This does mean that even if you associate data to only
125 //! a single element from the primary slot map, you could need and have to
126 //! initialize as much memory as the original.
127 //!
128 //! A [`SparseSecondaryMap`] is like a [`HashMap`] from keys to objects, however
129 //! it automatically removes outdated keys for slots that had their space
130 //! reused. You should use this variant if you expect to store some associated
131 //! data for only a small portion of the primary slot map.
132 //!
133 //! # Custom key types
134 //!
135 //! If you have multiple slot maps it's an error to use the key of one slot map
136 //! on another slot map. The result is safe, but unspecified, and can not be
137 //! detected at runtime, so it can lead to a hard to find bug.
138 //!
139 //! To prevent this, slot maps allow you to specify what the type is of the key
140 //! they return, as long as that type implements the [`Key`] trait. To aid with
141 //! this, the [`new_key_type!`] macro is provided that builds such a type for
142 //! you. The resulting type is exactly like [`DefaultKey`]. So instead of simply
143 //! using `SlotMap<DefaultKey, Player>` you would use:
144 //!
145 //! ```
146 //! # use slotmap::*;
147 //! # #[derive(Copy, Clone)]
148 //! # struct Player;
149 //! new_key_type! { struct PlayerKey; }
150 //! let sm: SlotMap<PlayerKey, Player> = SlotMap::with_key();
151 //! ```
152 //!
153 //! [`Vec`]: https://doc.rust-lang.org/std/vec/struct.Vec.html
154 //! [`BTreeMap`]: https://doc.rust-lang.org/std/collections/struct.BTreeMap.html
155 //! [`HashMap`]: https://doc.rust-lang.org/std/collections/struct.HashMap.html
156 //! [`SlotMap`]: struct.SlotMap.html
157 //! [`HopSlotMap`]: hop/struct.HopSlotMap.html
158 //! [`DenseSlotMap`]: dense/struct.DenseSlotMap.html
159 //! [`SecondaryMap`]: secondary/struct.SecondaryMap.html
160 //! [`SparseSecondaryMap`]: sparse_secondary/struct.SparseSecondaryMap.html
161 //! [`Slottable`]: trait.Slottable.html
162 //! [`Key`]: trait.Key.html
163 //! [`new_key_type!`]: macro.new_key_type.html
164 //! [`serde`]: https://github.com/serde-rs/serde
165 //! [`slab`]: https://github.com/carllerche/slab
166 //! [`DefaultKey`]: struct.DefaultKey.html
167
168 #[cfg(feature = "serde")]
169 extern crate serde;
170
171 // So our macros can refer to these.
172 #[cfg(feature = "serde")]
173 #[doc(hidden)]
174 pub mod __impl {
175 pub use serde::{Deserialize, Deserializer, Serialize, Serializer};
176 }
177
178 #[cfg(test)]
179 #[macro_use]
180 extern crate quickcheck;
181
182 #[cfg(test)]
183 extern crate serde_json;
184
185 pub(crate) mod normal;
186 pub use crate::normal::*;
187
188 pub mod dense;
189 pub use crate::dense::DenseSlotMap;
190
191 pub mod hop;
192 pub use crate::hop::HopSlotMap;
193
194 pub mod secondary;
195 pub use crate::secondary::SecondaryMap;
196
197 pub mod sparse_secondary;
198 pub use crate::sparse_secondary::SparseSecondaryMap;
199
200 use std::fmt::{self, Debug, Formatter};
201 use std::num::NonZeroU32;
202
203 /// A trait for items that can go in a [`SlotMap`] or [`HopSlotMap`]. Due to
204 /// current stable Rust restrictions a type must be [`Copy`] to be placed in one
205 /// of those slot maps. This restriction does not apply to [`DenseSlotMap`],
206 /// [`SecondaryMap`] or [`SparseSecondaryMap`]. It also does not apply if you
207 /// use nightly Rust and enable the `unstable` feature for `slotmap` by editing
208 /// your `Cargo.toml`:
209 ///
210 /// ```text
211 /// slotmap = { version = "...", features = ["unstable"] }
212 /// ```
213 ///
214 /// This trait should already be automatically implemented for any type that is
215 /// slottable.
216 ///
217 /// [`Copy`]: https://doc.rust-lang.org/std/marker/trait.Copy.html
218 /// [`SecondaryMap`]: secondary/struct.SecondaryMap.html
219 /// [`SparseSecondaryMap`]: sparse_secondary/struct.SparseSecondaryMap.html
220 /// [`SlotMap`]: struct.SlotMap.html
221 /// [`HopSlotMap`]: hop/struct.HopSlotMap.html
222 /// [`DenseSlotMap`]: dense/struct.DenseSlotMap.html
223 #[cfg(not(feature = "unstable"))]
224 pub trait Slottable: Copy {}
225
226 /// A trait for items that can go in a [`SlotMap`] or [`HopSlotMap`]. Due to
227 /// current stable Rust restrictions a type must be [`Copy`] to be placed in one
228 /// of those slot maps. This restriction does not apply to [`DenseSlotMap`],
229 /// [`SecondaryMap`] or [`SparseSecondaryMap`]. It also does not apply if you
230 /// use nightly Rust and enable the `unstable` feature for `slotmap` by editing
231 /// your `Cargo.toml`:
232 ///
233 /// ```text
234 /// slotmap = { version = "...", features = ["unstable"] }
235 /// ```
236 ///
237 /// This trait should already be automatically implemented for any type that is
238 /// slottable.
239 ///
240 /// [`Copy`]: https://doc.rust-lang.org/std/marker/trait.Copy.html
241 /// [`SecondaryMap`]: secondary/struct.SecondaryMap.html
242 /// [`SparseSecondaryMap`]: sparse_secondary/struct.SparseSecondaryMap.html
243 /// [`SlotMap`]: struct.SlotMap.html
244 /// [`HopSlotMap`]: hop/struct.HopSlotMap.html
245 /// [`DenseSlotMap`]: dense/struct.DenseSlotMap.html
246 #[cfg(feature = "unstable")]
247 pub trait Slottable {}
248
249 #[cfg(not(feature = "unstable"))]
250 impl<T: Copy> Slottable for T {}
251
252 #[cfg(feature = "unstable")]
253 impl<T> Slottable for T {}
254
255 /// The actual data stored in a [`Key`].
256 ///
257 /// This implements `Ord` so keys can be stored in e.g. [`BTreeMap`], but the
258 /// order of keys is unspecified.
259 ///
260 /// [`Key`]: trait.Key.html
261 /// [`BTreeMap`]: https://doc.rust-lang.org/std/collections/struct.BTreeMap.html
262 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
263 pub struct KeyData {
264 idx: u32,
265 version: NonZeroU32,
266 }
267
268 impl KeyData {
new(idx: u32, version: u32) -> Self269 fn new(idx: u32, version: u32) -> Self {
270 Self {
271 idx,
272 version: NonZeroU32::new(version).expect("KeyData constructed with zero version"),
273 }
274 }
275
null() -> Self276 fn null() -> Self {
277 Self::new(std::u32::MAX, 1)
278 }
279
is_null(self) -> bool280 fn is_null(self) -> bool {
281 self.idx == std::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` will return a key
286 /// 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`]: index.html#serialization-through-serde
as_ffi(self) -> u64298 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.
from_ffi(value: u64) -> Self304 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
311 impl Debug for KeyData {
fmt(&self, f: &mut Formatter) -> fmt::Result312 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
313 write!(f, "{}v{}", self.idx, self.version.get())
314 }
315 }
316
317 impl Default for KeyData {
default() -> Self318 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. The easiest way to do this is through [`new_key_type!`], which
330 /// makes a new type identical to [`DefaultKey`], just with a different name.
331 ///
332 /// [`new_key_type!`]: macro.new_key_type.html
333 /// [`DefaultKey`]: struct.DefaultKey.html
334 pub trait Key: From<KeyData> + Into<KeyData> + Clone {
335 /// Creates a new key that is always invalid and distinct from any non-null
336 /// key. A null key can only be created through this method (or default
337 /// initialization of keys made with [`new_key_type!`], which calls this
338 /// method).
339 ///
340 /// A null key is always invalid, but an invalid key (that is, a key that
341 /// has been removed from the slot map) does not become a null key. A null
342 /// is safe to use with any safe method of any slot map instance.
343 ///
344 /// # Examples
345 ///
346 /// ```
347 /// # use slotmap::*;
348 /// let mut sm = SlotMap::new();
349 /// let k = sm.insert(42);
350 /// let nk = DefaultKey::null();
351 /// assert!(nk.is_null());
352 /// assert!(k != nk);
353 /// assert_eq!(sm.get(nk), None);
354 /// ```
355 ///
356 /// [`new_key_type!`]: macro.new_key_type.html
null() -> Self357 fn null() -> Self {
358 KeyData::null().into()
359 }
360
361 /// Checks if a key is null. There is only a single null key, that is
362 /// `a.is_null() && b.is_null()` implies `a == b`.
363 ///
364 /// # Examples
365 ///
366 /// ```
367 /// # use slotmap::*;
368 /// new_key_type! { struct MyKey; }
369 /// let a = MyKey::null();
370 /// let b = MyKey::default();
371 /// assert_eq!(a, b);
372 /// assert!(a.is_null());
373 /// ```
is_null(self) -> bool374 fn is_null(self) -> bool {
375 self.into().is_null()
376 }
377 }
378
379 /// A helper macro to conveniently create new key types. If you use a new key
380 /// type for each slot map you create you can entirely prevent using the wrong
381 /// key on the wrong slot map.
382 ///
383 /// The type constructed by this macro is identical to [`DefaultKey`], just with
384 /// a different name.
385 ///
386 /// [`DefaultKey`]: struct.DefaultKey.html
387 ///
388 /// # Examples
389 ///
390 /// ```
391 /// # extern crate slotmap;
392 /// # use slotmap::*;
393 /// new_key_type! {
394 /// struct EntityKey;
395 ///
396 /// /// Key for the Player slot map.
397 /// pub struct PlayerKey;
398 /// }
399 ///
400 /// fn main() {
401 /// let mut players = SlotMap::with_key();
402 /// let mut entities: SlotMap<EntityKey, (f64, f64)> = SlotMap::with_key();
403 /// let bob: PlayerKey = players.insert("bobby");
404 /// // Now this is a type error because entities.get expects an EntityKey:
405 /// // entities.get(bob);
406 /// }
407 /// ```
408 #[macro_export(local_inner_macros)]
409 macro_rules! new_key_type {
410 ( $(#[$outer:meta])* $vis:vis struct $name:ident; $($rest:tt)* ) => {
411 $(#[$outer])*
412 #[derive(Copy, Clone, Default,
413 Eq, PartialEq, Ord, PartialOrd,
414 Hash, Debug)]
415 #[repr(transparent)]
416 $vis struct $name($crate::KeyData);
417
418 impl From<$crate::KeyData> for $name {
419 fn from(k: $crate::KeyData) -> Self {
420 $name(k)
421 }
422 }
423
424 impl From<$name> for $crate::KeyData {
425 fn from(k: $name) -> Self {
426 k.0
427 }
428 }
429
430 impl $crate::Key for $name { }
431
432 $crate::__serialize_key!($name);
433
434 $crate::new_key_type!($($rest)*);
435 };
436
437 () => {}
438 }
439
440 #[cfg(feature = "serde")]
441 #[doc(hidden)]
442 #[macro_export]
443 macro_rules! __serialize_key {
444 ( $name:ty ) => {
445 impl $crate::__impl::Serialize for $name {
446 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
447 where
448 S: $crate::__impl::Serializer,
449 {
450 $crate::KeyData::from(*self).serialize(serializer)
451 }
452 }
453
454 impl<'de> $crate::__impl::Deserialize<'de> for $name {
455 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
456 where
457 D: $crate::__impl::Deserializer<'de>,
458 {
459 let key_data: $crate::KeyData =
460 $crate::__impl::Deserialize::deserialize(deserializer)?;
461 Ok(key_data.into())
462 }
463 }
464 };
465 }
466
467 #[cfg(not(feature = "serde"))]
468 #[doc(hidden)]
469 #[macro_export]
470 macro_rules! __serialize_key {
471 ( $name:ty ) => {};
472 }
473
474 new_key_type! {
475 /// The default slot map key type.
476 pub struct DefaultKey;
477 }
478
479 // Returns if a is an older version than b, taking into account wrapping of
480 // versions.
is_older_version(a: u32, b: u32) -> bool481 fn is_older_version(a: u32, b: u32) -> bool {
482 let diff = a.wrapping_sub(b);
483 diff >= (1 << 31)
484 }
485
486 // Serialization with serde.
487 #[cfg(feature = "serde")]
488 mod serialize {
489 use super::*;
490 use serde::{Deserialize, Deserializer, Serialize, Serializer};
491
492 #[derive(Serialize, Deserialize)]
493 pub struct SerKey {
494 idx: u32,
495 version: u32,
496 }
497
498 impl Serialize for KeyData {
serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer,499 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
500 where
501 S: Serializer,
502 {
503 let ser_key = SerKey {
504 idx: self.idx,
505 version: self.version.get(),
506 };
507 ser_key.serialize(serializer)
508 }
509 }
510
511 impl<'de> Deserialize<'de> for KeyData {
deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de>,512 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
513 where
514 D: Deserializer<'de>,
515 {
516 let mut ser_key: SerKey = Deserialize::deserialize(deserializer)?;
517
518 // Ensure a.is_null() && b.is_null() implies a == b.
519 if ser_key.idx == std::u32::MAX {
520 ser_key.version = 1;
521 }
522
523 ser_key.version |= 1; // Ensure version is odd.
524 Ok(Self::new(ser_key.idx, ser_key.version))
525 }
526 }
527 }
528
529 #[cfg(test)]
530 mod tests {
531 use super::*;
532
533 #[test]
check_is_older_version()534 fn check_is_older_version() {
535 let is_older = |a, b| is_older_version(a, b);
536 assert!(!is_older(42, 42));
537 assert!(is_older(0, 1));
538 assert!(is_older(0, 1 << 31));
539 assert!(!is_older(0, (1 << 31) + 1));
540 assert!(is_older((-1i32) as u32, 0));
541 }
542
543 #[cfg(feature = "serde")]
544 #[test]
key_serde()545 fn key_serde() {
546 // Check round-trip through serde.
547 let mut sm = SlotMap::new();
548 let k = sm.insert(42);
549 let ser = serde_json::to_string(&k).unwrap();
550 let de: DefaultKey = serde_json::from_str(&ser).unwrap();
551 assert_eq!(k, de);
552
553 // Even if a malicious entity sends up even (unoccupied) versions in the
554 // key, we make the version point to the occupied version.
555 let malicious: KeyData = serde_json::from_str(&r#"{"idx":0,"version":4}"#).unwrap();
556 assert_eq!(malicious.version.get(), 5);
557 }
558 }
559