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
22impl From<core::convert::Infallible> for AllocError {
23 fn from(never: core::convert::Infallible) -> Self {
24 match never {}
25 }
26}
27
28pub trait Allocator: Clone {
38 fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError>;
42
43 unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout);
49
50 unsafe fn grow(
60 &self,
61 ptr: NonNull<u8>,
62 old_layout: Layout,
63 new_layout: Layout,
64 ) -> Result<NonNull<[u8]>, AllocError>;
65
66 unsafe fn shrink(
76 &self,
77 ptr: NonNull<u8>,
78 old_layout: Layout,
79 new_layout: Layout,
80 ) -> Result<NonNull<[u8]>, AllocError>;
81
82 fn allocate_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError>;
86}
87
88#[derive(Clone, Default)]
90pub struct DefaultAllocator;
91
92impl Allocator for DefaultAllocator {
93 fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
94 if layout.size() == 0 {
95 return Ok(NonNull::from_ref(&[]));
96 }
97 let ptr = unsafe { crate::alloc(layout).ok_or(AllocError)? };
98 Ok(NonNull::slice_from_raw_parts(ptr, layout.size()))
99 }
100
101 unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
102 if layout.size() == 0 {
103 return;
104 }
105 unsafe { crate::dealloc(ptr.as_ptr(), layout) }
106 }
107
108 unsafe fn grow(
109 &self,
110 ptr: NonNull<u8>,
111 old_layout: Layout,
112 new_layout: Layout,
113 ) -> Result<NonNull<[u8]>, AllocError> {
114 assert!(new_layout.size() >= old_layout.size());
115 if new_layout.size() == 0 {
116 return Ok(NonNull::from_ref(&[]));
117 }
118 if old_layout.size() == 0 {
119 return self.allocate(new_layout);
120 }
121 let ptr = unsafe {
122 crate::realloc(ptr.as_ptr(), old_layout, new_layout.size()).ok_or(AllocError)?
123 };
124 Ok(NonNull::slice_from_raw_parts(ptr, new_layout.size()))
125 }
126
127 unsafe fn shrink(
128 &self,
129 ptr: NonNull<u8>,
130 old_layout: Layout,
131 new_layout: Layout,
132 ) -> Result<NonNull<[u8]>, AllocError> {
133 assert!(new_layout.size() <= old_layout.size());
134 if new_layout.size() == 0 {
135 unsafe { self.deallocate(ptr, old_layout) };
136 return Ok(NonNull::from_ref(&[]));
137 }
138 if old_layout.size() == 0 {
139 return Ok(NonNull::from_ref(&[]));
140 }
141 let ptr = unsafe {
142 crate::realloc(ptr.as_ptr(), old_layout, new_layout.size()).ok_or(AllocError)?
143 };
144 Ok(NonNull::slice_from_raw_parts(ptr, new_layout.size()))
145 }
146
147 fn allocate_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
148 if layout.size() == 0 {
149 return Ok(NonNull::from_ref(&[]));
150 }
151 let ptr = unsafe { crate::alloc_zeroed(layout).ok_or(AllocError)? };
152 Ok(NonNull::slice_from_raw_parts(ptr, layout.size()))
153 }
154}
155
156#[derive(Clone, Default)]
162pub struct NoOpAllocator;
163
164impl Allocator for NoOpAllocator {
165 fn allocate(&self, _layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
166 Err(AllocError)
167 }
168
169 unsafe fn deallocate(&self, _ptr: NonNull<u8>, _layout: Layout) {
170 }
172
173 unsafe fn grow(
174 &self,
175 _ptr: NonNull<u8>,
176 _old_layout: Layout,
177 _new_layout: Layout,
178 ) -> Result<NonNull<[u8]>, AllocError> {
179 Err(AllocError)
180 }
181
182 unsafe fn shrink(
183 &self,
184 _ptr: NonNull<u8>,
185 _old_layout: Layout,
186 _new_layout: Layout,
187 ) -> Result<NonNull<[u8]>, AllocError> {
188 Err(AllocError)
189 }
190
191 fn allocate_zeroed(&self, _layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
192 Err(AllocError)
193 }
194}
195
196#[cfg(test)]
197mod tests {
198 use super::*;
199
200 #[test]
201 fn test_default_allocator() {
202 let layout = Layout::from_size_align(10, 1).unwrap();
203 let ptr = DefaultAllocator::default().allocate(layout).unwrap();
204 unsafe {
205 DefaultAllocator::default().deallocate(ptr.cast::<u8>(), layout);
206 }
207 }
208
209 #[test]
210 fn test_default_allocator_grow_shrink() {
211 let layout = Layout::from_size_align(10, 1).unwrap();
212 let ptr = DefaultAllocator::default().allocate(layout).unwrap();
213
214 let new_layout = Layout::from_size_align(20, 1).unwrap();
215 let grown_ptr = unsafe {
216 DefaultAllocator::default().grow(ptr.cast::<u8>(), layout, new_layout).unwrap()
217 };
218 assert!(grown_ptr.len() >= 20);
219
220 let shrunk_layout = Layout::from_size_align(5, 1).unwrap();
221 let shrunk_ptr = unsafe {
222 DefaultAllocator::default()
223 .shrink(grown_ptr.cast::<u8>(), new_layout, shrunk_layout)
224 .unwrap()
225 };
226 assert!(shrunk_ptr.len() >= 5);
227
228 unsafe {
229 DefaultAllocator::default().deallocate(shrunk_ptr.cast::<u8>(), shrunk_layout);
230 }
231 }
232
233 #[test]
234 fn test_default_allocator_zeroed() {
235 let layout = Layout::from_size_align(10, 1).unwrap();
236 let ptr = DefaultAllocator::default().allocate_zeroed(layout).unwrap();
237
238 let slice = unsafe { ptr.as_ref() };
240 for &b in slice {
241 assert_eq!(b, 0);
242 }
243
244 unsafe {
245 DefaultAllocator::default().deallocate(ptr.cast::<u8>(), layout);
246 }
247 }
248
249 #[test]
250 fn test_default_allocator_zero_sized() {
251 let allocator = DefaultAllocator::default();
252
253 let layout0 = Layout::from_size_align(0, 1).unwrap();
254 let ptr0 = allocator.allocate(layout0).unwrap();
255 assert_eq!(ptr0.len(), 0);
256
257 let ptr0_zeroed = allocator.allocate_zeroed(layout0).unwrap();
258 assert_eq!(ptr0_zeroed.len(), 0);
259
260 unsafe {
261 allocator.deallocate(ptr0.cast::<u8>(), layout0);
262 allocator.deallocate(ptr0_zeroed.cast::<u8>(), layout0);
263 }
264
265 let grown0 = unsafe { allocator.grow(ptr0.cast::<u8>(), layout0, layout0).unwrap() };
266 assert_eq!(grown0.len(), 0);
267
268 let layout10 = Layout::from_size_align(10, 1).unwrap();
269 let grown10 = unsafe { allocator.grow(ptr0.cast::<u8>(), layout0, layout10).unwrap() };
270 assert!(grown10.len() >= 10);
271
272 let shrunk0 = unsafe { allocator.shrink(grown10.cast::<u8>(), layout10, layout0).unwrap() };
273 assert_eq!(shrunk0.len(), 0);
274
275 let shrunk0_again =
276 unsafe { allocator.shrink(ptr0.cast::<u8>(), layout0, layout0).unwrap() };
277 assert_eq!(shrunk0_again.len(), 0);
278 }
279
280 #[test]
281 fn test_no_op_allocator() {
282 let allocator = NoOpAllocator;
283 let layout = Layout::from_size_align(10, 1).unwrap();
284
285 assert_eq!(allocator.allocate(layout), Err(AllocError));
287 assert_eq!(allocator.allocate_zeroed(layout), Err(AllocError));
288
289 let ptr = NonNull::dangling();
291 assert_eq!(unsafe { allocator.grow(ptr, layout, layout) }, Err(AllocError));
292 assert_eq!(unsafe { allocator.shrink(ptr, layout, layout) }, Err(AllocError));
293
294 unsafe {
296 allocator.deallocate(ptr, layout);
297 }
298 }
299}