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}