Skip to main content

pui_arena/
lib.rs

1#![no_std]
2#![forbid(missing_docs)]
3#![deny(clippy::missing_safety_doc)]
4#![cfg_attr(docsrs, feature(doc_cfg))]
5#![allow(clippy::transmute_ptr_to_ptr)]
6// FIXME - the docs in this crate a *very* minimal, and need to be expanded upon
7
8//! A set of very efficient, and very customizable arenas that
9//! can elide bounds checks wherever possible.
10//!
11//! This crate is heavily inspired by crates like [`slotmap`](https://crates.io/crate/slotmap)
12//! and [`slab`](https://crates.io/crate/slab).
13//!
14//! `pui-arena` provides a set of collections that allow you to insert and delete
15//! items in at least amortized `O(1)`, access elements in `O(1)`. It also provides
16//! the tools required to avoid the [`ABA` problem](https://en.wikipedia.org/wiki/ABA_problem).
17//!
18//! You can think of the collections in `pui-arena` as a `HashMap`/`BTreeMap` where
19//! the arena manages the keys, and provides a very efficient way to access elements.
20//!
21//! # Why use `pui-arena` over alternatives
22//!
23//! `pui-arena` allows you to minimize overhead wherever possible, and fully customize
24//! the arenas. This allows you to use an api like `slab` or `slotmap` based on how
25//! you use the api. (There are also newtypes featured-gated by the features `slab`
26//! and `slotmap` that implement a similar interface to those two crates).
27//!
28//! If you use the `pui`/`scoped` feature, then you can also eliminate bounds checks
29//! entirely, which can be a huge performance save in performance sensitive regions.
30//!
31//! `pui-arena` also provides a more features than competitors, such as a vacant entry
32//! api for versioned arenas, and `drain_filter` for all arenas.
33//!
34//! # Choosing [`sparse`](base::sparse), [`hop`](base::hop), or [`dense`](base::dense)
35//!
36//! * If you want fast insertion/deletion/acccess and don't care about iteration speed,
37//! use [`sparse`](base::sparse).
38//!
39//! * If you want fast iteration speed above all else, use [`dense`](base::dense)
40//!
41//! * If you want reasonable iteration speed and also fast access/delete, or if [`dense`](base::dense)
42//! is to memory heavy, use [`hop`](base::hop)
43//!
44//! You can read about the details of how each works in the corrosponding module docs
45//!
46//! # Performance characteristics
47//!
48//! ## Speed
49//!
50//! all of the collections in `pui-arena` allow you to
51//! * insert elements in amortized `O(1)`
52//! * delete/access elements in `O(1)`
53//! * guarantee that keys *never* get invalidated unless `remove` is called
54//!
55//! ## Memory
56//!
57//! For each `Arena<T, _, V>` where `V: Version`, the memory overhead is as follows:
58//! * [`sparse`](base::sparse) [`Arena`](base::sparse::Arena) -
59//!     `size_of(V) + max(size_of(T), size_of(usize))` per slot
60//! * [`hop`](base::hop) [`Arena`](base::hop::Arena) -
61//!     `size_of(V) + max(size_of(T), 3 * size_of(usize))` per slot
62//! * [`dense`](base::dense) [`Arena`](base::dense::Arena) -
63//!     `size_of(V) + size_of(usize)` per slot,
64//!     and `size_of(usize) + size_of(T)` per value
65//!
66//! ## Implementation Details
67//!
68//! The core of this crate is the the [`Version`](version::Version) trait,
69//! the [`ArenaKey`](ArenaKey) trait, and the [`BuildArenaKey`](BuildArenaKey) trait.
70//!
71//! `Version` specifies the behavior of the arenas.
72//! `pui-arena` provides three implementations,
73//! see [`Version`](version::Version) for more details:
74//!
75//! * [`DefaultVersion`](version::DefaultVersion)
76//!     * Ensures that all keys produced by `insert` are unique
77//!     * backed by a `u32`, so it may waste space for small values
78//!         * technically if items are inserted/removed many times,
79//!             slots will be "leaked", and iteraton performance may degrade
80//!             but, this is unlikely, unless the same slot is reused over
81//!             2 billion times
82//! * [`TinyVersion`](version::TinyVersion) -
83//!     * Ensures that all keys produced by `insert` are unique
84//!     * backed by a `u8`, if items are inserted/removed many times,
85//!         slots will be "leaked", and iteraton performance may degrade
86//! * [`Unversioned`](version::Unversioned) -
87//!     * Keys produced by `insert` are not guartneed to be unique
88//!     * slots will never be "leaked"
89//!
90//! [`ArenaKey`] specifies the behavior of keys into arenas.
91//! `pui-arena` provides a number of implementations. See [`ArenaKey`]
92//! for details.
93//!
94//! * [`usize`] - allows accessing a given slot directly, with no regard for it's version
95//!     * Note: when I say "with no regard for it's version", it still checks the version
96//!         to see if the slot is occupied, but it has no means of checking if a slot
97//!         a value was re-inserted into the same slot
98//! * [`Key<K, _>`](Key) - allows accessing a slot specified by `K`, and checks the generation
99//!     of the slot before providing a value.
100//!     * `K` can be one of the other keys listed here (except for `ScopedKey`)
101//! * [`TrustedIndex`] - allows accessing a given slot directly, with no regard for it's version
102//!     * elides bounds checks, but is unsafe to construct
103//!     * This one should be used with care, if at all. It is better to use the `pui` feature
104//!         and use `pui_vec::Id` instead. It is safe, and also guartnees bound check elision
105//! * [`ScopedKey<'_, _>`](scoped::ScopedKey) - only allows access into scoped arenas
106//!     (otherwise identical to `Key`)
107//!
108//! enabled with the `pui` feature
109//!
110//! * [`pui_vec::Id`] - allows accessing a given slot directly, with no regard for it's version
111//!     * elides bounds checks
112//!
113//! [`BuildArenaKey`] specifies how arenas should create keys, all implementors of [`ArenaKey`]
114//! provided by this crate also implement [`BuildArenaKey`] except for [`TrustedIndex`].
115//!
116//! # Custom arenas
117//!
118//! You can newtype arenas with the [`newtype`] macro, or the features: `slab`, `slotmap`, or `scoped`.
119//!
120//! * [`slab`] - provides a similar api to the [`slab` crate](https://crates.io/crate/slab)
121//!     * uses `usize` keys, and [`Unversioned`](version::Unversioned) slots
122//! * [`slotmap`] - provides a similar api to the [`slab` crate](https://crates.io/crate/slotmap)
123//!     * uses [`Key<usize>`](Key) keys, and [`DefaultVersion`](version::DefaultVersion) slots
124//! * [`scoped`] - provides newtyped arenas that use `pui_core::scoped` to elide bounds checks
125//!     * uses [`scoped::ScopedKey<'_, _>`](scoped::ScopedKey) keys,
126//!     and is generic over the version
127//! * [`newtype`] - creates a set of newtyped arenas with the module structure of `base`
128//!     * These arenas elide bounds checks, in favor of id checks, which are cheaper,
129//!     and depending on your backing id, can be no check at all!
130//!     (see [`pui_core::scalar_allocator`] details)
131//!
132//! ```
133//! // Because the backing id type is `()`, there are no bounds checks when using
134//! // this arena!
135//! pui_arena::newtype! {
136//!     struct MyCustomArena;
137//! }
138//!
139//! let my_sparse_arena = sparse::Arena::new();
140//! let my_dense_arena = dense::Arena::new();
141//! let my_hop_arena = hop::Arena::new();
142//! ```
143//!
144//! Becomes something like
145//!
146//! ```ignore
147//! pui_core::scalar_allocator! {
148//!     struct MyCustomArena;
149//! }
150//!
151//! mod sparse {
152//!     pub(super) Arena(pub(super) pui_arena::base::sparse::Arena<...>);
153//!
154//!     /// more type aliases here
155//! }
156//!
157//! mod dense {
158//!     pub(super) Arena(pub(super) pui_arena::base::dense::Arena<...>);
159//!
160//!     /// more type aliases here
161//! }
162//!
163//! mod hop {
164//!     pub(super) Arena(pub(super) pui_arena::base::hop::Arena<...>);
165//!
166//!     /// more type aliases here
167//! }
168//!
169//! let my_sparse_arena = sparse::Arena::new();
170//! let my_dense_arena = dense::Arena::new();
171//! let my_hop_arena = hop::Arena::new();
172//! ```
173//!
174//! Where each `Arena` newtype has a simplified api, and better error messages.
175
176#[doc(hidden)]
177pub extern crate alloc as std;
178
179pub mod version;
180
181mod arena_access;
182pub use arena_access::{ArenaKey, BuildArenaKey, CompleteValidator, Key, Validator};
183
184/// the core implementations of different types of arenas
185pub mod base {
186    pub mod dense;
187    pub mod hop;
188    pub mod sparse;
189}
190
191#[cfg(feature = "scoped")]
192#[cfg_attr(docsrs, doc(cfg(feature = "scoped")))]
193pub mod scoped;
194/// a reimplementation of [`slab`](https://docs.rs/slab/) in terms
195/// of the generic arenas in [`base`]
196#[cfg(feature = "slab")]
197#[cfg_attr(docsrs, doc(cfg(feature = "slab")))]
198pub mod slab;
199#[cfg(feature = "slotmap")]
200#[cfg_attr(docsrs, doc(cfg(feature = "slotmap")))]
201pub mod slotmap;
202
203#[doc(hidden)]
204#[cfg(feature = "pui")]
205pub use {core, pui_core, pui_vec};
206
207/// An index that's guaranteed to be in bounds of the arena it's used on
208#[derive(Clone, Copy)]
209pub struct TrustedIndex(usize);
210
211impl TrustedIndex {
212    /// Create a new `TrustedIndex`
213    ///
214    /// # Safety
215    ///
216    /// This `index` must be in bounds on all arenas this `Self` is used on
217    #[inline]
218    pub unsafe fn new(index: usize) -> Self { Self(index) }
219}
220
221struct SetOnDrop<'a>(&'a mut bool);
222
223impl Drop for SetOnDrop<'_> {
224    fn drop(&mut self) { *self.0 = true; }
225}
226
227impl SetOnDrop<'_> {
228    fn defuse(self) { core::mem::forget(self) }
229}
230
231/// Create newtype of all the arenas in [`base`]
232///
233/// The module structure here is identical to [`crate::base`], and
234/// you can look there for detailed documentation about the types.
235/// Each implementation of `SlotMap` will have all the methods from the
236/// corrosponding `Arena`, and those that take or produce generic keys
237/// will instead take/produce a `Key`.
238///
239/// In each module, you'll find an `Arena` newtype (with one public field),
240/// a `VacantEntry` newtype (again with one public field). These are thin
241/// wrappers around their generic counterparts. Their only serve the purpose
242/// of making error messages easier to parse, and use a default `Key`.
243/// You will also find a vareity of type aliases for various iterators, and
244/// for the default `Key` type for ease of use.
245///
246/// If you want to access the raw backing `Arena`/`VacantEntry`, you still can,
247/// it is the only public field of each scoped arena/vacant entry.
248#[macro_export]
249#[cfg(feature = "pui")]
250#[cfg_attr(docsrs, doc(cfg(feature = "pui")))]
251macro_rules! newtype {
252    (
253        $(#[$meta:meta])*
254        $( pub $(( $($vis:tt)* ))?  )? struct $name:ident;
255        $(type Version = $version:ty;)?
256    ) => {
257        $crate::pui_core::scalar_allocator! {
258            $(#[$meta])*
259            $( pub $(( $($vis)* ))?  )? struct $name;
260        }
261
262        $crate::__newtype! { @resolve_vis $( pub $(( $($vis)* ))?  )? $name, $($version,)? $crate::version::DefaultVersion }
263    };
264    (
265        $(#[$meta:meta])*
266        $( pub $(( $($vis:tt)* ))?  )? struct $name:ident($inner:ty);
267        $(type Version = $version:ty;)?
268    ) => {
269        $crate::pui_core::scalar_allocator! {
270            $(#[$meta])*
271            $( pub $(( $($vis)* ))?  )? struct $name($inner);
272        }
273
274        $crate::__newtype! { @resolve_vis $( pub $(( $($vis)* ))?  )? $name, $($version,)? $crate::version::DefaultVersion }
275    };
276}
277
278#[doc(hidden)]
279#[macro_export]
280#[cfg(feature = "pui")]
281macro_rules! __newtype {
282    (@resolve_vis $name:ident, $default_version:ty $(, $extra:ty)?) => {
283        $crate::__newtype! {  @build_module (pub(self)) (pub(super)) $name, $default_version }
284    };
285    (@resolve_vis pub $name:ident, $default_version:ty $(, $extra:ty)?) => {
286        $crate::__newtype! {  @build_module (pub) (pub) $name, $default_version }
287    };
288    (@resolve_vis pub(self) $name:ident, $default_version:ty $(, $extra:ty)?) => {
289        $crate::__newtype! {  @build_module (pub(self)) (pub(super)) $name, $default_version }
290    };
291    (@resolve_vis pub(crate) $name:ident, $default_version:ty $(, $extra:ty)?) => {
292        $crate::__newtype! {  @build_module (pub(crate)) (pub(crate)) $name, $default_version }
293    };
294    (@resolve_vis pub(in $($path:tt)*) $name:ident, $default_version:ty $(, $extra:ty)?) => {
295        $crate::__newtype! {  @build_module (pub(in $($path)*)) (pub(in super::$($path)*)) $name, $default_version }
296    };
297    (
298        @forward
299        ($item_vis:vis) $name:ident
300        slots: $slots:ident
301        $($keys:ident)?
302    ) => {
303        /// The backing identifier for [`Arena`]
304        $item_vis type Identifier = $crate::pui_core::dynamic::Dynamic<super::$name>;
305        /// The key for [`Arena`]
306        $item_vis type Key = key::Key<$crate::pui_vec::Id<$crate::pui_core::dynamic::DynamicToken<super::$name>>, <Version as $crate::version::Version>::Save>;
307
308        /// The backing arena for [`Arena`]
309        $item_vis type BaseArena<T> = imp::Arena<T, Identifier, Version>;
310        /// The backing vacant entry for [`VacantEntry`]
311        $item_vis type BaseVacantEntry<'a, T> = imp::VacantEntry<'a, T, Identifier, Version>;
312
313        /// A newtyped arena
314        $item_vis struct Arena<T>($item_vis imp::Arena<T, Identifier, Version>);
315        /// A newtyped vacant entry
316        $item_vis struct VacantEntry<'a, T>($item_vis imp::VacantEntry<'a, T, Identifier, Version>);
317
318        /// Returned from [`Arena::entries`]
319        $item_vis type Entries<'a, T> = imp::Entries<'a, T, Identifier, Version, Key>;
320        /// Returned from [`Arena::entries_mut`]
321        $item_vis type EntriesMut<'a, T> = imp::EntriesMut<'a, T, Identifier, Version, Key>;
322        /// Returned from [`Arena::into_entries`]
323        $item_vis type IntoEntries<T> = imp::IntoEntries<T, Identifier, Version, Key>;
324
325        impl<T> VacantEntry<'_, T> {
326            /// see [`VacantEntry::key`](imp::VacantEntry::key)
327            pub fn key(&self) -> Key { self.0.key() }
328
329            /// see [`VacantEntry::insert`](imp::VacantEntry::insert)
330            pub fn insert(self, value: T) -> Key { self.0.insert(value) }
331        }
332
333        impl<T> $crate::core::default::Default for Arena<T> {
334            fn default() -> Self { Self::new() }
335        }
336
337        impl<T> Arena<T> {
338            /// Create a new slab
339            pub fn new() -> Self {
340                Self(BaseArena::with_ident(super::$name::oneshot()))
341            }
342            /// see [`Arena::is_empty`](imp::Arena::is_empty)
343            pub fn is_empty(&self) -> bool { self.0.is_empty() }
344            /// see [`Arena::len`](imp::Arena::is_empty)
345            pub fn len(&self) -> usize { self.0.len() }
346            /// see [`Arena::capacity`](imp::Arena::capacity)
347            pub fn capacity(&self) -> usize { self.0.capacity() }
348            /// see [`Arena::reserve`](imp::Arena::reserve)
349            pub fn reserve(&mut self, additional: usize) { self.0.reserve(additional) }
350            /// see [`Arena::vacant_entry`](imp::Arena::vacant_entry)
351            pub fn vacant_entry(&mut self) -> VacantEntry<'_, T> { VacantEntry(self.0.vacant_entry()) }
352            /// see [`Arena::insert`](imp::Arena::insert)
353            pub fn insert(&mut self, value: T) -> Key { self.0.insert(value) }
354            /// see [`Arena::contains`](imp::Arena::contains)
355            pub fn contains(&self, key: Key) -> bool { self.0.contains(key) }
356            /// see [`Arena::remove`](imp::Arena::remove)
357            pub fn remove(&mut self, key: Key) -> T { self.0.remove(key) }
358            /// see [`Arena::try_remove`](imp::Arena::try_remove)
359            pub fn try_remove(&mut self, key: Key) -> Option<T> { self.0.try_remove(key) }
360            /// see [`Arena::delete`](imp::Arena::delete)
361            pub fn delete(&mut self, key: Key) -> bool { self.0.delete(key) }
362            /// see [`Arena::get`](imp::Arena::get)
363            pub fn get(&self, key: Key) -> Option<&T> { self.0.get(key) }
364            /// see [`Arena::get_mut`](imp::Arena::get_mut)
365            pub fn get_mut(&mut self, key: Key) -> Option<&mut T> { self.0.get_mut(key) }
366            /// see [`Arena::get_unchecked`](imp::Arena::get_unchecked)
367            #[allow(clippy::missing_safety_doc)]
368            pub unsafe fn get_unchecked(&self, index: usize) -> &T { self.0.get_unchecked(index) }
369            /// see [`Arena::get_unchecked_mut`](imp::Arena::get_unchecked_mut)
370            #[allow(clippy::missing_safety_doc)]
371            pub unsafe fn get_unchecked_mut(&mut self, index: usize) -> &mut T { self.0.get_unchecked_mut(index) }
372            /// see [`Arena::delete_all`](imp::Arena::delete_all)
373            pub fn delete_all(&mut self) { self.0.delete_all() }
374            /// see [`Arena::retain`](imp::Arena::retain)
375            pub fn retain<F: FnMut(&mut T) -> bool>(&mut self, f: F) { self.0.retain(f) }
376            /// see [`Arena::keys`](imp::Arena::keys)
377            pub fn keys(&self) -> Keys<'_ $(, $keys)?> { self.0.keys() }
378            /// see [`Arena::iter`](imp::Arena::iter)
379            pub fn iter(&self) -> Iter<'_, T> { self.0.iter() }
380            /// see [`Arena::iter_mut`](imp::Arena::iter_mut)
381            pub fn iter_mut(&mut self) -> IterMut<'_, T> { self.0.iter_mut() }
382            /// see [`Arena::drain`](imp::Arena::drain)
383            pub fn drain(&mut self) -> Drain<'_, T> { self.0.drain() }
384            /// see [`Arena::drain_filter`](imp::Arena::drain_filter)
385            pub fn drain_filter<F: FnMut(&mut T) -> bool>(&mut self, filter: F) -> DrainFilter<'_, T, F> { self.0.drain_filter(filter) }
386            /// see [`Arena::entries`](imp::Arena::entries)
387            pub fn entries(&self) -> Entries<'_, T> { self.0.entries() }
388            /// see [`Arena::entries_mut`](imp::Arena::entries_mut)
389            pub fn entries_mut(&mut self) -> EntriesMut<'_, T> { self.0.entries_mut() }
390            /// see [`Arena::into_entries`](imp::Arena::into_entries)
391            pub fn into_entries(self) -> IntoEntries<T> { self.0.into_entries() }
392        }
393
394        impl<T> $crate::core::iter::IntoIterator for Arena<T> {
395            type IntoIter = IntoIter<T>;
396            type Item = T;
397
398            fn into_iter(self) -> Self::IntoIter { self.0.into_iter() }
399        }
400
401        impl<T> Index<Key> for Arena<T> {
402            type Output = T;
403
404            fn index(&self, key: Key) -> &Self::Output { &self.0[key] }
405        }
406
407        impl<T> IndexMut<Key> for Arena<T> {
408            fn index_mut(&mut self, key: Key) -> &mut Self::Output { &mut self.0[key] }
409        }
410    };
411    (@build_module ($mod_vis:vis) ($item_vis:vis) $name:ident, $version:ty) => {
412        /// a sparse arena
413        ///
414        /// see [`pui_arena::base::sparse`](sparse::imp) for details
415        $mod_vis mod sparse {
416            use $crate::core::ops::*;
417            #[doc(hidden)]
418            pub(super) use $crate::base::sparse as imp;
419            use $crate::base::sparse as key;
420
421            /// The version for [`Arena`]
422            $item_vis type Version = $version;
423            /// Returned from [`Arena::iter`]
424            $item_vis type Iter<'a, T> = imp::Iter<'a, T, Version>;
425            /// Returned from [`Arena::iter_mut`]
426            $item_vis type IterMut<'a, T> = imp::IterMut<'a, T, Version>;
427            /// Returned from [`Arena::into_iter`]
428            $item_vis type IntoIter<T> = imp::IntoIter<T, Version>;
429
430            /// Returned from [`Arena::drain`]
431            $item_vis type Drain<'a, T> = imp::Drain<'a, T, Version>;
432            /// Returned from [`Arena::drain_filter`]
433            $item_vis type DrainFilter<'a, T, F> = imp::DrainFilter<'a, T, Version, F>;
434
435            /// Returned from [`Arena::keys`]
436            $item_vis type Keys<'a, T> = imp::Keys<'a, T, Identifier, Version, Key>;
437
438            $crate::__newtype! {
439                @forward
440                ($item_vis) $name
441                slots: slots
442                T
443            }
444        }
445
446        /// a hop arena
447        ///
448        /// see [`pui_arena::base::hop`](hop::imp) for details
449        $mod_vis mod hop {
450            use $crate::core::ops::*;
451            #[doc(hidden)]
452            pub(super) use $crate::base::hop as imp;
453            use $crate::base::hop as key;
454
455            /// The version for [`Arena`]
456            $item_vis type Version = $version;
457            /// Returned from [`Arena::iter`]
458            $item_vis type Iter<'a, T> = imp::Iter<'a, T, Version>;
459            /// Returned from [`Arena::iter_mut`]
460            $item_vis type IterMut<'a, T> = imp::IterMut<'a, T, Version>;
461            /// Returned from [`Arena::into_iter`]
462            $item_vis type IntoIter<T> = imp::IntoIter<T, Version>;
463
464            /// Returned from [`Arena::drain`]
465            $item_vis type Drain<'a, T> = imp::Drain<'a, T, Version>;
466            /// Returned from [`Arena::drain_filter`]
467            $item_vis type DrainFilter<'a, T, F> = imp::DrainFilter<'a, T, Version, F>;
468
469            /// Returned from [`Arena::keys`]
470            $item_vis type Keys<'a, T> = imp::Keys<'a, T, Identifier, Version, Key>;
471
472            $crate::__newtype! {
473                @forward
474                ($item_vis) $name
475                slots: len
476                T
477            }
478        }
479
480        /// a dense arena
481        ///
482        /// see [`pui_arena::base::dense`](dense::imp) for details
483        $mod_vis mod dense {
484            use $crate::core::ops::*;
485            #[doc(hidden)]
486            pub(super) use $crate::base::dense as imp;
487            use $crate::base::sparse as key;
488
489            /// The version for [`Arena`]
490            $item_vis type Version = $version;
491            /// Returned from [`Arena::iter`]
492            $item_vis type Iter<'a, T> = $crate::core::slice::Iter<'a, T>;
493            /// Returned from [`Arena::iter_mut`]
494            $item_vis type IterMut<'a, T> = $crate::core::slice::IterMut<'a, T>;
495            /// Returned from [`Arena::into_iter`]
496            $item_vis type IntoIter<T> = $crate::std::vec::IntoIter<T>;
497
498            /// Returned from [`Arena::drain`]
499            $item_vis type Drain<'a, T> = imp::Drain<'a, T, Identifier, Version>;
500            /// Returned from [`Arena::drain_filter`]
501            $item_vis type DrainFilter<'a, T, F> = imp::DrainFilter<'a, T, Identifier, Version, F>;
502
503            /// Returned from [`Arena::keys`]
504            $item_vis type Keys<'a> = imp::Keys<'a, Identifier, Version, Key>;
505
506            $crate::__newtype! {
507                @forward
508                ($item_vis) $name
509                slots: len
510            }
511        }
512    };
513}