zigzag_alloc/alloc/allocator.rs
1//! Core allocator abstraction.
2//!
3//! This module defines the [`Allocator`] trait — the single interface that
4//! every allocator in the crate implements and that every collection depends on.
5//!
6//! The design is deliberately minimal: two methods (`alloc` / `dealloc`) that
7//! mirror the semantics of `posix_memalign` / `free` while being safe to
8//! compose behind higher-level wrappers.
9
10use core::alloc::Layout;
11use core::ptr::NonNull;
12
13/// Low-level memory allocator interface.
14///
15/// Inspired by the explicit allocation model of Zig, this trait decouples
16/// collections from any concrete or global allocator. Callers retain full
17/// control over *which* memory region is used and *when* it is released.
18///
19/// # Implementing `Allocator`
20///
21/// An implementation must uphold the following invariants:
22///
23/// * A successful call to [`alloc`](Allocator::alloc) returns a pointer that is
24/// valid for `layout.size()` bytes and aligned to at least `layout.align()`.
25/// * The returned memory may contain arbitrary (uninitialized) bytes.
26/// * The allocated block remains valid until an explicit call to
27/// [`dealloc`](Allocator::dealloc) with the *exact same pointer and layout*.
28/// * Calling `dealloc` with a pointer that was not obtained from the same
29/// allocator instance, or with a mismatched layout, is undefined behaviour.
30pub trait Allocator {
31 /// Allocates a block of memory described by `layout`.
32 ///
33 /// Returns `Some(ptr)` on success, where `ptr` is non-null and correctly
34 /// aligned. Returns `None` when the allocator cannot satisfy the request
35 /// (e.g. out of memory, capacity exhausted).
36 ///
37 /// # Safety
38 ///
39 /// * `layout.size()` **must** be greater than zero. Passing a zero-sized
40 /// layout is undefined behaviour; implementations may panic or return a
41 /// dangling pointer, but callers must never rely on that behaviour.
42 /// * The caller must eventually call [`dealloc`](Allocator::dealloc) with
43 /// the returned pointer and the *same* `layout`, unless the allocator
44 /// uses bulk reclamation (e.g. arena reset).
45 unsafe fn alloc(&self, layout: Layout) -> Option<NonNull<u8>>;
46
47 /// Releases the memory block previously obtained from this allocator.
48 ///
49 /// For allocators that use bulk reclamation (e.g. [`ArenaAllocator`],
50 /// [`BumpAllocator`]) this method is intentionally a no-op; memory is
51 /// reclaimed collectively via `reset()` or on drop.
52 ///
53 /// # Safety
54 ///
55 /// * `ptr` **must** have been returned by a prior call to
56 /// [`alloc`](Allocator::alloc) on the **same** allocator instance.
57 /// * `layout` **must** exactly match the layout passed to that `alloc` call.
58 /// * After `dealloc` returns, `ptr` is invalid and must never be
59 /// dereferenced (prevents use-after-free).
60 /// * Calling `dealloc` twice with the same pointer is undefined behaviour
61 /// (double-free).
62 ///
63 /// [`ArenaAllocator`]: crate::alloc::arena::ArenaAllocator
64 /// [`BumpAllocator`]: crate::alloc::bump::BumpAllocator
65 unsafe fn dealloc(&self, ptr: NonNull<u8>, layout: Layout);
66}
67
68/// Blanket implementation of [`Allocator`] for shared references.
69///
70/// This allows passing `&A` (or `&dyn Allocator`) into collections that are
71/// generic over `A: Allocator`, avoiding the need to move ownership of the
72/// allocator into every container.
73///
74/// # Example
75///
76/// ```rust,ignore
77/// let sys = SystemAllocator;
78/// let vec: ExVec<u32> = ExVec::new(&sys); // &SystemAllocator implements Allocator
79/// ```
80impl<A: Allocator + ?Sized> Allocator for &A {
81 /// Forwards the allocation request to the underlying allocator.
82 ///
83 /// # Safety
84 ///
85 /// Inherits all safety requirements from [`A::alloc`](Allocator::alloc).
86 #[inline]
87 unsafe fn alloc(&self, layout: Layout) -> Option<NonNull<u8>> {
88 // SAFETY: The caller is responsible for satisfying the layout preconditions
89 // (non-zero size, valid alignment). We simply forward the call unchanged.
90 unsafe { (**self).alloc(layout) }
91 }
92
93 /// Forwards the deallocation request to the underlying allocator.
94 ///
95 /// # Safety
96 ///
97 /// Inherits all safety requirements from [`A::dealloc`](Allocator::dealloc).
98 /// In particular, `ptr` must have been allocated by the same underlying
99 /// allocator instance and `layout` must match the original allocation.
100 #[inline]
101 unsafe fn dealloc(&self, ptr: NonNull<u8>, layout: Layout) {
102 // SAFETY: The caller guarantees `ptr` and `layout` are valid for the
103 // underlying allocator. We forward without modification.
104 unsafe { (**self).dealloc(ptr, layout) }
105 }
106}