|
| 1 | +//! Bump allocator trait for arena-style memory management. |
| 2 | +//! |
| 3 | +//! This module provides the [`BumpAllocator`] trait, an extension to the standard |
| 4 | +//! [`Allocator`] trait that adds support for bulk deallocation via [`reset`](BumpAllocator::reset). |
| 5 | +//! |
| 6 | +//! # Overview |
| 7 | +//! |
| 8 | +//! Bump allocators (also known as arena allocators) allocate memory by incrementing |
| 9 | +//! a pointer ("bumping") within a contiguous memory region. Individual deallocations |
| 10 | +//! are not supported; instead, all allocations are freed at once by resetting the |
| 11 | +//! bump pointer. |
| 12 | +//! |
| 13 | +//! This trade-off makes bump allocators ideal for: |
| 14 | +//! |
| 15 | +//! - **Compiler passes**: Temporary data structures that live for a single pass |
| 16 | +//! - **AST construction**: Nodes allocated during parsing, freed after compilation |
| 17 | +//! - **Batch processing**: Work items processed together, then discarded |
| 18 | +//! |
| 19 | +//! # Usage |
| 20 | +//! |
| 21 | +//! The trait is implemented by [`Heap`] and [`Scratch`] allocators: |
| 22 | +//! |
| 23 | +//! ``` |
| 24 | +//! # #![feature(allocator_api)] |
| 25 | +//! use hashql_core::heap::{BumpAllocator, CollectIn, Scratch}; |
| 26 | +//! |
| 27 | +//! let mut scratch = Scratch::new(); |
| 28 | +//! |
| 29 | +//! // Allocate some data |
| 30 | +//! let vec: Vec<u32, &Scratch> = (1..=3).collect_in(&scratch); |
| 31 | +//! drop(vec); |
| 32 | +//! |
| 33 | +//! // Reset frees all allocations at once |
| 34 | +//! scratch.reset(); |
| 35 | +//! ``` |
| 36 | +//! |
| 37 | +//! # Pass Pattern |
| 38 | +//! |
| 39 | +//! Compiler passes commonly reset the allocator at the start of `run()` to reuse |
| 40 | +//! memory from previous invocations: |
| 41 | +//! |
| 42 | +//! ```ignore |
| 43 | +//! impl TransformPass for MyPass<A: BumpAllocator> { |
| 44 | +//! fn run(&mut self, context: &mut Context, body: &mut Body) { |
| 45 | +//! self.alloc.reset(); // Reuse memory from previous run |
| 46 | +//! // ... pass implementation using self.alloc ... |
| 47 | +//! } |
| 48 | +//! } |
| 49 | +//! ``` |
| 50 | +//! |
| 51 | +//! [`Heap`]: super::Heap |
| 52 | +//! [`Scratch`]: super::Scratch |
| 53 | +#![expect(clippy::mut_from_ref, reason = "allocator")] |
| 54 | +use core::alloc::{AllocError, Allocator}; |
| 55 | + |
| 56 | +/// A bump allocator that supports bulk deallocation. |
| 57 | +/// |
| 58 | +/// This trait extends [`Allocator`] with arena-style memory management: |
| 59 | +/// allocations are made by bumping a pointer, and all memory is freed at once |
| 60 | +/// via [`reset`](Self::reset). |
| 61 | +/// |
| 62 | +/// # Implementors |
| 63 | +/// |
| 64 | +/// - [`Heap`](super::Heap): Full-featured arena with string interning |
| 65 | +/// - [`Scratch`](super::Scratch): Lightweight arena for temporary allocations |
| 66 | +pub trait BumpAllocator: Allocator { |
| 67 | + /// Copies a slice into the arena, returning a mutable reference to the copy. |
| 68 | + /// |
| 69 | + /// This is useful for transferring borrowed data into arena-owned memory. |
| 70 | + /// The source slice is copied element-by-element into freshly allocated arena memory. |
| 71 | + /// |
| 72 | + /// # Type Requirements |
| 73 | + /// |
| 74 | + /// The element type must be [`Copy`] to ensure safe bitwise copying without |
| 75 | + /// running destructors on the source data. |
| 76 | + /// |
| 77 | + /// # Errors |
| 78 | + /// |
| 79 | + /// Returns [`AllocError`] if memory allocation fails. |
| 80 | + fn allocate_slice_copy<T: Copy>(&self, slice: &[T]) -> Result<&mut [T], AllocError>; |
| 81 | + |
| 82 | + /// Resets the allocator, freeing all allocations at once. |
| 83 | + /// |
| 84 | + /// After calling `reset`, the allocator's memory is available for reuse. |
| 85 | + /// All previously allocated references become invalid; using them is |
| 86 | + /// undefined behavior (prevented by Rust's borrow checker in safe code). |
| 87 | + /// |
| 88 | + /// The allocator retains its current capacity, avoiding reallocation |
| 89 | + /// on subsequent use. |
| 90 | + fn reset(&mut self); |
| 91 | +} |
| 92 | + |
| 93 | +/// Blanket implementation allowing `&mut A` to be used where `A: BumpAllocator`. |
| 94 | +/// |
| 95 | +/// This enables passes to store `&mut Scratch` or `&mut Heap` while still |
| 96 | +/// calling [`reset`](BumpAllocator::reset) through the mutable reference. |
| 97 | +impl<A> BumpAllocator for &mut A |
| 98 | +where |
| 99 | + A: BumpAllocator, |
| 100 | +{ |
| 101 | + #[inline] |
| 102 | + fn allocate_slice_copy<T: Copy>(&self, slice: &[T]) -> Result<&mut [T], AllocError> { |
| 103 | + A::allocate_slice_copy(self, slice) |
| 104 | + } |
| 105 | + |
| 106 | + #[inline] |
| 107 | + fn reset(&mut self) { |
| 108 | + A::reset(self); |
| 109 | + } |
| 110 | +} |
0 commit comments