stack_arena/
traits.rs

1use crate::AllocError;
2use std::{alloc::Layout, ptr::NonNull};
3
4/// A trait for memory allocators that manage memory in a stack-like fashion.
5///
6/// This trait defines the core interface for memory allocation and deallocation
7/// operations used by the arena allocator system. Implementors of this trait
8/// can be used interchangeably in contexts that require memory allocation.
9///
10/// # Implementation
11///
12/// The trait provides a complete interface for memory management:
13///
14/// - `allocate` - Allocates memory with the specified layout
15/// - `allocate_zeroed` - Allocates zeroed memory with the specified layout
16/// - `deallocate` - Deallocates previously allocated memory
17/// - `grow` - Grows a previously allocated memory block to a larger size
18/// - `grow_zeroed` - Grows a memory block and zeroes the new memory
19/// - `shrink` - Shrinks a previously allocated memory block to a smaller size
20///
21/// # Safety
22///
23/// This trait uses unsafe methods because it deals directly with memory allocation
24/// and raw pointers. Implementors must ensure that:
25///
26/// - Allocated memory is properly aligned and sized according to the layout
27/// - Deallocated pointers were previously allocated by the same allocator
28/// - Memory is not used after deallocation
29/// - Grow and shrink operations maintain the validity of the memory
30/// - All memory safety invariants are preserved
31///
32/// # Examples
33///
34/// This trait is implemented by [`StackArena`](crate::StackArena) and [`BufferArena`](crate::BufferArena):
35///
36/// ```
37/// use stack_arena::{StackArena, Allocator};
38/// use std::alloc::Layout;
39///
40/// let mut arena = StackArena::new();
41/// let layout = Layout::from_size_align(16, 8).unwrap();
42///
43/// // Allocate memory
44/// let ptr = unsafe { arena.allocate(layout).unwrap() };
45///
46/// // Use the memory...
47///
48/// // Deallocate when done
49/// unsafe { arena.deallocate(ptr.cast(), layout) };
50/// ```
51pub trait Allocator {
52    /// Allocates memory with the specified layout.
53    ///
54    /// # Safety
55    ///
56    /// This method is unsafe because it returns a raw pointer that must be used
57    /// carefully to avoid memory safety issues.
58    ///
59    /// # Parameters
60    ///
61    /// * `layout` - The layout of the memory to allocate
62    ///
63    /// # Returns
64    ///
65    /// A non-null pointer to the allocated memory on success, or an error if
66    /// allocation fails.
67    unsafe fn allocate(&mut self, layout: Layout) -> Result<NonNull<[u8]>, AllocError>;
68
69    /// Allocates zeroed memory with the specified layout.
70    ///
71    /// This method allocates memory and initializes it with zeros.
72    ///
73    /// # Safety
74    ///
75    /// This method is unsafe for the same reasons as `allocate`.
76    ///
77    /// # Parameters
78    ///
79    /// * `layout` - The layout of the memory to allocate
80    ///
81    /// # Returns
82    ///
83    /// A non-null pointer to the allocated zeroed memory on success, or an error if
84    /// allocation fails.
85    #[inline]
86    unsafe fn allocate_zeroed(&mut self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
87        let ptr = unsafe { self.allocate(layout) }?;
88        unsafe { std::ptr::write_bytes(ptr.as_ptr() as *mut u8, 0, layout.size()) };
89        Ok(ptr)
90    }
91
92    /// Deallocates memory previously allocated by this allocator.
93    ///
94    /// # Safety
95    ///
96    /// This method is unsafe because it must be called with a pointer returned
97    /// by `allocate` and the same layout that was used to allocate it.
98    ///
99    /// # Parameters
100    ///
101    /// * `ptr` - A pointer to the memory to deallocate
102    /// * `layout` - The layout used to allocate the memory
103    unsafe fn deallocate(&mut self, ptr: NonNull<u8>, layout: Layout);
104
105    /// Grows a previously allocated memory block to a larger size.
106    ///
107    /// # Safety
108    ///
109    /// This method is unsafe because it must be called with a pointer returned
110    /// by `allocate` and the same layout that was used to allocate it.
111    ///
112    /// # Parameters
113    ///
114    /// * `ptr` - A pointer to the memory to grow
115    /// * `old_layout` - The layout used to allocate the memory
116    /// * `new_layout` - The new layout for the memory
117    ///
118    /// # Returns
119    ///
120    /// A non-null pointer to the grown memory on success, or an error if
121    /// the operation fails.
122    unsafe fn grow(
123        &mut self,
124        ptr: NonNull<u8>,
125        old_layout: Layout,
126        new_layout: Layout,
127    ) -> Result<NonNull<[u8]>, AllocError>;
128
129    /// Grows a previously allocated memory block to a larger size and zeroes the new memory.
130    ///
131    /// # Safety
132    ///
133    /// This method is unsafe for the same reasons as `grow`.
134    ///
135    /// # Parameters
136    ///
137    /// * `ptr` - A pointer to the memory to grow
138    /// * `old_layout` - The layout used to allocate the memory
139    /// * `new_layout` - The new layout for the memory
140    ///
141    /// # Returns
142    ///
143    /// A non-null pointer to the grown memory on success, or an error if
144    /// the operation fails.
145    #[inline]
146    unsafe fn grow_zeroed(
147        &mut self,
148        ptr: NonNull<u8>,
149        old_layout: Layout,
150        new_layout: Layout,
151    ) -> Result<NonNull<[u8]>, AllocError> {
152        let ptr = unsafe { self.grow(ptr, old_layout, new_layout) }?;
153        unsafe {
154            ptr.cast::<u8>()
155                .write_bytes(0, new_layout.size() - old_layout.size())
156        };
157        Ok(ptr)
158    }
159
160    /// Shrinks a previously allocated memory block to a smaller size.
161    ///
162    /// # Safety
163    ///
164    /// This method is unsafe because it must be called with a pointer returned
165    /// by `allocate` and the same layout that was used to allocate it.
166    ///
167    /// # Parameters
168    ///
169    /// * `ptr` - A pointer to the memory to shrink
170    /// * `old_layout` - The layout used to allocate the memory
171    /// * `new_layout` - The new layout for the memory
172    ///
173    /// # Returns
174    ///
175    /// A non-null pointer to the shrunk memory on success, or an error if
176    /// the operation fails.
177    unsafe fn shrink(
178        &mut self,
179        ptr: NonNull<u8>,
180        old_layout: Layout,
181        new_layout: Layout,
182    ) -> Result<NonNull<[u8]>, AllocError>;
183
184    /// Returns a reference to this allocator.
185    ///
186    /// This method is used to get a reference to the allocator for use in
187    /// contexts that require a reference.
188    ///
189    /// # Returns
190    ///
191    /// A reference to this allocator.
192    #[inline]
193    fn by_ref(&self) -> &Self
194    where
195        Self: Sized,
196    {
197        self
198    }
199}