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#[cfg(test)]
151mod tests {
152 use super::*;
153
154 #[test]
155 fn test_default_allocator() {
156 let layout = Layout::from_size_align(10, 1).unwrap();
157 let ptr = DefaultAllocator::default().allocate(layout).unwrap();
158 unsafe {
159 DefaultAllocator::default().deallocate(ptr.cast::<u8>(), layout);
160 }
161 }
162
163 #[test]
164 fn test_default_allocator_grow_shrink() {
165 let layout = Layout::from_size_align(10, 1).unwrap();
166 let ptr = DefaultAllocator::default().allocate(layout).unwrap();
167
168 let new_layout = Layout::from_size_align(20, 1).unwrap();
169 let grown_ptr = unsafe {
170 DefaultAllocator::default().grow(ptr.cast::<u8>(), layout, new_layout).unwrap()
171 };
172 assert!(grown_ptr.len() >= 20);
173
174 let shrunk_layout = Layout::from_size_align(5, 1).unwrap();
175 let shrunk_ptr = unsafe {
176 DefaultAllocator::default()
177 .shrink(grown_ptr.cast::<u8>(), new_layout, shrunk_layout)
178 .unwrap()
179 };
180 assert!(shrunk_ptr.len() >= 5);
181
182 unsafe {
183 DefaultAllocator::default().deallocate(shrunk_ptr.cast::<u8>(), shrunk_layout);
184 }
185 }
186
187 #[test]
188 fn test_default_allocator_zeroed() {
189 let layout = Layout::from_size_align(10, 1).unwrap();
190 let ptr = DefaultAllocator::default().allocate_zeroed(layout).unwrap();
191
192 let slice = unsafe { ptr.as_ref() };
194 for &b in slice {
195 assert_eq!(b, 0);
196 }
197
198 unsafe {
199 DefaultAllocator::default().deallocate(ptr.cast::<u8>(), layout);
200 }
201 }
202
203 #[test]
204 fn test_default_allocator_zero_sized() {
205 let allocator = DefaultAllocator::default();
206
207 let layout0 = Layout::from_size_align(0, 1).unwrap();
208 let ptr0 = allocator.allocate(layout0).unwrap();
209 assert_eq!(ptr0.len(), 0);
210
211 let ptr0_zeroed = allocator.allocate_zeroed(layout0).unwrap();
212 assert_eq!(ptr0_zeroed.len(), 0);
213
214 unsafe {
215 allocator.deallocate(ptr0.cast::<u8>(), layout0);
216 allocator.deallocate(ptr0_zeroed.cast::<u8>(), layout0);
217 }
218
219 let grown0 = unsafe { allocator.grow(ptr0.cast::<u8>(), layout0, layout0).unwrap() };
220 assert_eq!(grown0.len(), 0);
221
222 let layout10 = Layout::from_size_align(10, 1).unwrap();
223 let grown10 = unsafe { allocator.grow(ptr0.cast::<u8>(), layout0, layout10).unwrap() };
224 assert!(grown10.len() >= 10);
225
226 let shrunk0 = unsafe { allocator.shrink(grown10.cast::<u8>(), layout10, layout0).unwrap() };
227 assert_eq!(shrunk0.len(), 0);
228
229 let shrunk0_again =
230 unsafe { allocator.shrink(ptr0.cast::<u8>(), layout0, layout0).unwrap() };
231 assert_eq!(shrunk0_again.len(), 0);
232 }
233}