1use core::alloc::Layout;
8use core::ptr::NonNull;
9
10#[derive(Debug, Eq, PartialEq)]
12pub struct AllocError;
13
14impl core::fmt::Display for AllocError {
15 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
16 f.write_str("allocation failure")
17 }
18}
19
20impl core::error::Error for AllocError {}
21
22pub trait Allocator: Clone {
32 fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError>;
36
37 unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout);
43
44 unsafe fn grow(
54 &self,
55 ptr: NonNull<u8>,
56 old_layout: Layout,
57 new_layout: Layout,
58 ) -> Result<NonNull<[u8]>, AllocError>;
59
60 unsafe fn shrink(
70 &self,
71 ptr: NonNull<u8>,
72 old_layout: Layout,
73 new_layout: Layout,
74 ) -> Result<NonNull<[u8]>, AllocError>;
75
76 fn allocate_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError>;
80}
81
82#[derive(Clone, Default)]
84pub struct DefaultAllocator;
85
86impl Allocator for DefaultAllocator {
87 fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
88 if layout.size() == 0 {
89 return Ok(NonNull::from_ref(&[]));
90 }
91 let ptr = unsafe { crate::alloc(layout).ok_or(AllocError)? };
92 Ok(NonNull::slice_from_raw_parts(ptr, layout.size()))
93 }
94
95 unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
96 if layout.size() == 0 {
97 return;
98 }
99 unsafe { crate::dealloc(ptr.as_ptr(), layout) }
100 }
101
102 unsafe fn grow(
103 &self,
104 ptr: NonNull<u8>,
105 old_layout: Layout,
106 new_layout: Layout,
107 ) -> Result<NonNull<[u8]>, AllocError> {
108 assert!(new_layout.size() >= old_layout.size());
109 if new_layout.size() == 0 {
110 return Ok(NonNull::from_ref(&[]));
111 }
112 if old_layout.size() == 0 {
113 return self.allocate(new_layout);
114 }
115 let ptr = unsafe {
116 crate::realloc(ptr.as_ptr(), old_layout, new_layout.size()).ok_or(AllocError)?
117 };
118 Ok(NonNull::slice_from_raw_parts(ptr, new_layout.size()))
119 }
120
121 unsafe fn shrink(
122 &self,
123 ptr: NonNull<u8>,
124 old_layout: Layout,
125 new_layout: Layout,
126 ) -> Result<NonNull<[u8]>, AllocError> {
127 assert!(new_layout.size() <= old_layout.size());
128 if new_layout.size() == 0 {
129 unsafe { self.deallocate(ptr, old_layout) };
130 return Ok(NonNull::from_ref(&[]));
131 }
132 if old_layout.size() == 0 {
133 return Ok(NonNull::from_ref(&[]));
134 }
135 let ptr = unsafe {
136 crate::realloc(ptr.as_ptr(), old_layout, new_layout.size()).ok_or(AllocError)?
137 };
138 Ok(NonNull::slice_from_raw_parts(ptr, new_layout.size()))
139 }
140
141 fn allocate_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
142 if layout.size() == 0 {
143 return Ok(NonNull::from_ref(&[]));
144 }
145 let ptr = unsafe { crate::alloc_zeroed(layout).ok_or(AllocError)? };
146 Ok(NonNull::slice_from_raw_parts(ptr, layout.size()))
147 }
148}
149
150#[derive(Clone, Default)]
156pub struct NoOpAllocator;
157
158impl Allocator for NoOpAllocator {
159 fn allocate(&self, _layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
160 Err(AllocError)
161 }
162
163 unsafe fn deallocate(&self, _ptr: NonNull<u8>, _layout: Layout) {
164 }
166
167 unsafe fn grow(
168 &self,
169 _ptr: NonNull<u8>,
170 _old_layout: Layout,
171 _new_layout: Layout,
172 ) -> Result<NonNull<[u8]>, AllocError> {
173 Err(AllocError)
174 }
175
176 unsafe fn shrink(
177 &self,
178 _ptr: NonNull<u8>,
179 _old_layout: Layout,
180 _new_layout: Layout,
181 ) -> Result<NonNull<[u8]>, AllocError> {
182 Err(AllocError)
183 }
184
185 fn allocate_zeroed(&self, _layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
186 Err(AllocError)
187 }
188}
189
190#[cfg(test)]
191mod tests {
192 use super::*;
193
194 #[test]
195 fn test_default_allocator() {
196 let layout = Layout::from_size_align(10, 1).unwrap();
197 let ptr = DefaultAllocator::default().allocate(layout).unwrap();
198 unsafe {
199 DefaultAllocator::default().deallocate(ptr.cast::<u8>(), layout);
200 }
201 }
202
203 #[test]
204 fn test_default_allocator_grow_shrink() {
205 let layout = Layout::from_size_align(10, 1).unwrap();
206 let ptr = DefaultAllocator::default().allocate(layout).unwrap();
207
208 let new_layout = Layout::from_size_align(20, 1).unwrap();
209 let grown_ptr = unsafe {
210 DefaultAllocator::default().grow(ptr.cast::<u8>(), layout, new_layout).unwrap()
211 };
212 assert!(grown_ptr.len() >= 20);
213
214 let shrunk_layout = Layout::from_size_align(5, 1).unwrap();
215 let shrunk_ptr = unsafe {
216 DefaultAllocator::default()
217 .shrink(grown_ptr.cast::<u8>(), new_layout, shrunk_layout)
218 .unwrap()
219 };
220 assert!(shrunk_ptr.len() >= 5);
221
222 unsafe {
223 DefaultAllocator::default().deallocate(shrunk_ptr.cast::<u8>(), shrunk_layout);
224 }
225 }
226
227 #[test]
228 fn test_default_allocator_zeroed() {
229 let layout = Layout::from_size_align(10, 1).unwrap();
230 let ptr = DefaultAllocator::default().allocate_zeroed(layout).unwrap();
231
232 let slice = unsafe { ptr.as_ref() };
234 for &b in slice {
235 assert_eq!(b, 0);
236 }
237
238 unsafe {
239 DefaultAllocator::default().deallocate(ptr.cast::<u8>(), layout);
240 }
241 }
242
243 #[test]
244 fn test_default_allocator_zero_sized() {
245 let allocator = DefaultAllocator::default();
246
247 let layout0 = Layout::from_size_align(0, 1).unwrap();
248 let ptr0 = allocator.allocate(layout0).unwrap();
249 assert_eq!(ptr0.len(), 0);
250
251 let ptr0_zeroed = allocator.allocate_zeroed(layout0).unwrap();
252 assert_eq!(ptr0_zeroed.len(), 0);
253
254 unsafe {
255 allocator.deallocate(ptr0.cast::<u8>(), layout0);
256 allocator.deallocate(ptr0_zeroed.cast::<u8>(), layout0);
257 }
258
259 let grown0 = unsafe { allocator.grow(ptr0.cast::<u8>(), layout0, layout0).unwrap() };
260 assert_eq!(grown0.len(), 0);
261
262 let layout10 = Layout::from_size_align(10, 1).unwrap();
263 let grown10 = unsafe { allocator.grow(ptr0.cast::<u8>(), layout0, layout10).unwrap() };
264 assert!(grown10.len() >= 10);
265
266 let shrunk0 = unsafe { allocator.shrink(grown10.cast::<u8>(), layout10, layout0).unwrap() };
267 assert_eq!(shrunk0.len(), 0);
268
269 let shrunk0_again =
270 unsafe { allocator.shrink(ptr0.cast::<u8>(), layout0, layout0).unwrap() };
271 assert_eq!(shrunk0_again.len(), 0);
272 }
273
274 #[test]
275 fn test_no_op_allocator() {
276 let allocator = NoOpAllocator;
277 let layout = Layout::from_size_align(10, 1).unwrap();
278
279 assert_eq!(allocator.allocate(layout), Err(AllocError));
281 assert_eq!(allocator.allocate_zeroed(layout), Err(AllocError));
282
283 let ptr = NonNull::dangling();
285 assert_eq!(unsafe { allocator.grow(ptr, layout, layout) }, Err(AllocError));
286 assert_eq!(unsafe { allocator.shrink(ptr, layout, layout) }, Err(AllocError));
287
288 unsafe {
290 allocator.deallocate(ptr, layout);
291 }
292 }
293}