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}