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