Skip to main content

mago_allocator/
lib.rs

1#![allow(clippy::mut_from_ref)]
2#![allow(clippy::unnecessary_safety_comment)]
3
4//! Arena allocation for Mago.
5//!
6//! Three bump arenas, all implementing the [`Arena`] trait:
7//!
8//! - [`LocalArena`]: single-threaded, fastest, `Send` but `!Sync`.
9//! - [`SharedArena`]: `Send + Sync`, safe to share across threads (for example
10//!   behind a `&SharedArena` handed to every worker in a `rayon` pass).
11//! - [`ScopedArena`]: a thread-local view into a [`SharedArena`] (via
12//!   [`SharedArena::scoped`]) for contention-free, non-escaping scratch.
13//!
14//! Because all three implement [`Arena`] (and the underlying `Allocator` trait),
15//! allocation code is written generically over `A: Arena` and never needs to know
16//! which arena it was given:
17//!
18//! ```ignore
19//! fn lower<'arena, A: Arena + ?Sized>(arena: &'arena A, ast: &Ast) -> &'arena Ir<'arena> {
20//!     arena.alloc(Ir::from(ast))
21//! }
22//! ```
23//!
24//! Allocations live as long as the borrow of the arena they came from, and are
25//! reclaimed wholesale when the arena is dropped or reset. For growable
26//! collections, use the aliases in [`vec`](mod@vec), [`boxed`], and
27//! [`collections`], each built with its `*_in(arena)` constructor; to collect an
28//! iterator straight into the arena, use [`CollectIn`] (and `ParallelCollectIn`
29//! under the `rayon` feature); for an immutable string copied into the arena, use
30//! [`Arena::alloc_str`]; to deep-copy an arena-resident value into a different
31//! arena, implement [`CopyInto`].
32//!
33//! Each module also re-exports the items of its underlying crate (`allocator_api2`
34//! for [`alloc`](mod@alloc), [`boxed`], [`vec`](mod@vec); `hashbrown` for
35//! [`collections`]), so anything not surfaced by the aliases above is still
36//! reachable.
37
38pub mod alloc;
39pub mod arena;
40pub mod boxed;
41pub mod collections;
42pub mod copy;
43pub mod iter;
44pub mod prelude;
45pub mod vec;
46
47pub use arena::Arena;
48pub use arena::LocalArena;
49pub use arena::ScopedArena;
50pub use arena::SharedArena;
51pub use copy::CopyInto;
52pub use iter::CollectIn;
53pub use iter::FromIteratorIn;
54#[cfg(feature = "rayon")]
55pub use iter::ParallelCollectIn;
56
57/// Builds an arena-allocated [`Vec`](vec::Vec), analogous to [`std::vec!`].
58///
59/// The first argument is the arena (or any `&allocator`); the elements follow a
60/// `;`:
61///
62/// ```
63/// use mago_allocator::prelude::*;
64///
65/// let arena = LocalArena::new();
66/// let evens = vec_in![&arena; 0, 2, 4];
67/// assert_eq!(evens.as_slice(), &[0, 2, 4]);
68/// ```
69#[macro_export]
70macro_rules! vec_in {
71    ($arena:expr $(,)?) => {{
72        $crate::vec::Vec::new_in($arena)
73    }};
74    ($arena:expr; $element:expr; $count:expr) => {{
75        let mut vector = $crate::vec::Vec::new_in($arena);
76        vector.resize($count, $element);
77        vector
78    }};
79    ($arena:expr; $($element:expr),* $(,)?) => {{
80        let mut vector = $crate::vec::Vec::new_in($arena);
81        $( vector.push($element); )*
82        vector
83    }};
84}
85
86/// Formats its arguments into an arena, returning `&mut str`.
87///
88/// The macro form of [`Arena::alloc_fmt`], reading like [`format!`] but with no
89/// intermediate global allocation:
90///
91/// ```
92/// use mago_allocator::prelude::*;
93///
94/// let arena = LocalArena::new();
95/// let label = format_in!(arena, "{}::{}", "App", "VERSION");
96/// assert_eq!(label, "App::VERSION");
97/// ```
98#[macro_export]
99macro_rules! format_in {
100    ($arena:expr, $($arg:tt)*) => {{
101        use $crate::arena::Arena as _;
102
103        ($arena).alloc_fmt(::core::format_args!($($arg)*))
104    }};
105}