1use core::ptr::NonNull;
2use core::fmt;
3
4use crate::num::NonZeroUsize;
5use crate::num::Pow2Usize;
6
7#[derive(PartialEq, Debug)]
8pub enum AllocError {
9 InvalidAlignment, AlignedSizeTooBig, UnsupportedAlignment, UnsupportedSize, NotEnoughMemory, OperationFailed, UnsupportedOperation, }
17
18impl AllocError {
19
20 pub fn to_str(&self) -> &'static str {
21 match self {
22 AllocError::InvalidAlignment => "invalid alignment",
23 AllocError::AlignedSizeTooBig => "aligned size too big",
24 AllocError::UnsupportedAlignment => "unsupported alignment",
25 AllocError::UnsupportedSize => "unsupported size",
26 AllocError::NotEnoughMemory => "not enough memory",
27 AllocError::OperationFailed => "operation failed",
28 AllocError::UnsupportedOperation => "unsupported operation",
29 }
30 }
31
32}
33
34impl fmt::Display for AllocError {
35 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36 self.to_str().fmt(f)
37 }
38}
39
40impl From<AllocError> for core::fmt::Error {
41 fn from(_e: AllocError) -> Self {
42 Self { }
43 }
44}
45
46impl<T> From<(AllocError, T)> for AllocError {
47 fn from(src: (AllocError, T)) -> Self {
48 src.0
49 }
50}
51
52pub unsafe trait Allocator {
53 unsafe fn alloc(
54 &self,
55 _size: NonZeroUsize,
56 _align: Pow2Usize
57 ) -> Result<NonNull<u8>, AllocError> {
58 panic!("alloc not implemented");
59 }
60 unsafe fn free(
61 &self,
62 _ptr: NonNull<u8>,
63 _current_size: NonZeroUsize,
64 _align: Pow2Usize
65 ) {
66 panic!("free not implemented!");
67 }
68 unsafe fn grow(
69 &self,
70 _ptr: NonNull<u8>,
71 _current_size: NonZeroUsize,
72 _new_larger_size: NonZeroUsize,
73 _align: Pow2Usize
74 ) -> Result<NonNull<u8>, AllocError> {
75 panic!("grow not implemented");
76 }
77 unsafe fn shrink(
78 &self,
79 _ptr: NonNull<u8>,
80 _current_size: NonZeroUsize,
81 _new_smaller_size: NonZeroUsize,
82 _align: Pow2Usize
83 ) -> Result<NonNull<u8>, AllocError> {
84 panic!("shrink not implemented");
85 }
86 fn supports_contains(&self) -> bool { false }
87 fn contains(
88 &self,
89 _ptr: NonNull<u8>
90 ) -> bool {
91 panic!("contains not implemented!");
92 }
93 fn name(&self) -> &'static str { "some-allocator" }
94 fn to_ref(&self) -> AllocatorRef
95 where Self: Sized {
96 AllocatorRef { allocator: self as &dyn Allocator }
97 }
98}
99
100#[derive(Copy, Clone)]
101pub struct AllocatorRef<'a> {
102 allocator: &'a (dyn Allocator + 'a)
103}
104
105impl<'a> core::fmt::Debug for AllocatorRef<'a> {
106 fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>)
107 -> core::result::Result<(), core::fmt::Error> {
108 write!(fmt, "{}@{:X}", self.name(), ((self.allocator as *const dyn Allocator) as *const u8) as usize)
109 }
110}
111
112unsafe impl<'a> Allocator for AllocatorRef<'a> {
113 unsafe fn alloc(
114 &self,
115 size: NonZeroUsize,
116 align: Pow2Usize
117 ) -> Result<NonNull<u8>, AllocError> {
118 self.allocator.alloc(size, align)
119 }
120 unsafe fn free(
121 &self,
122 ptr: NonNull<u8>,
123 size: NonZeroUsize,
124 align: Pow2Usize) {
125 self.allocator.free(ptr, size, align);
126 }
127 unsafe fn grow(
128 &self,
129 ptr: NonNull<u8>,
130 current_size: NonZeroUsize,
131 new_larger_size: NonZeroUsize,
132 align: Pow2Usize
133 ) -> Result<NonNull<u8>, AllocError> {
134 self.allocator.grow(ptr, current_size, new_larger_size, align)
135 }
136 unsafe fn shrink(
137 &self,
138 ptr: NonNull<u8>,
139 current_size: NonZeroUsize,
140 new_smaller_size: NonZeroUsize,
141 align: Pow2Usize
142 ) -> Result<NonNull<u8>, AllocError> {
143 self.allocator.shrink(ptr, current_size, new_smaller_size, align)
144 }
145 fn supports_contains(&self) -> bool {
146 self.allocator.supports_contains()
147 }
148 fn contains(
149 &self,
150 ptr: NonNull<u8>
151 ) -> bool {
152 self.allocator.contains(ptr)
153 }
154 fn name(&self) -> &'static str {
155 self.allocator.name()
156 }
157 fn to_ref(&self) -> AllocatorRef
158 where Self: Sized {
159 *self
160 }
161}
162
163pub mod no_sup_alloc;
164pub use no_sup_alloc::no_sup_allocator as no_sup_allocator;
165
166pub mod nop_alloc;
167pub use nop_alloc::NOP_ALLOCATOR as NOP_ALLOCATOR;
168
169pub mod single_alloc;
170pub use single_alloc::SingleAlloc as SingleAlloc;
171
172pub mod bump_alloc;
173pub use bump_alloc::BumpAllocator as BumpAllocator;
174
175#[cfg(feature = "use-libc")]
176pub mod libc_malloc;
177#[cfg(feature = "use-libc")]
178pub use libc_malloc::Malloc as Malloc;
179
180pub mod r#box;
181pub use r#box::Box as Box;
182
183pub mod vector;
184pub use vector::Vector as Vector;
185
186pub mod string;
187pub use string::String as String;
188
189pub mod rc;
190pub use rc::Rc as Rc;
191pub use rc::RcWeak as RcWeak;
192
193impl<'a> AllocatorRef<'a> {
194 pub fn alloc_item<T: Sized>(self, v: T) -> Result<Box<'a, T>, (AllocError, T)> {
195 Box::new(self, v)
196 }
197
198 pub fn vector<T: Sized>(&'a self) -> Vector<'a, T> {
199 Vector::new(*self)
200 }
201
202 pub unsafe fn alloc_or_grow(
203 &'a self,
204 ptr: NonNull<u8>,
205 current_size: usize,
206 new_larger_size: NonZeroUsize,
207 align: Pow2Usize
208 ) -> Result<NonNull<u8>, AllocError> {
209 if current_size == 0 {
210 self.alloc(new_larger_size, align)
211 } else {
212 self.grow(ptr, NonZeroUsize::new(current_size).unwrap(), new_larger_size, align)
213 }
214 }
215}
216
217#[cfg(test)]
218mod tests {
219 use super::*;
220
221 struct DefaultAllocator { }
222 unsafe impl Allocator for DefaultAllocator { }
223
224 #[test]
225 #[should_panic(expected = "alloc not implemented")]
226 fn default_alloc_panics() {
227 let a = DefaultAllocator { };
228 let _r = unsafe {
229 a.alloc(
230 NonZeroUsize::new(1).unwrap(),
231 Pow2Usize::new(1).unwrap()
232 )
233 };
234 }
235
236 #[test]
237 #[should_panic(expected = "free not implemented")]
238 fn default_free_panics() {
239 let a = DefaultAllocator { };
240 unsafe {
241 a.free(
242 NonNull::dangling(),
243 NonZeroUsize::new(1).unwrap(),
244 Pow2Usize::new(1).unwrap()
245 );
246 }
247 }
248
249 #[test]
250 #[should_panic(expected = "grow not implemented")]
251 fn default_grow_panics() {
252 let a = DefaultAllocator { };
253 match unsafe {
254 a.grow(
255 NonNull::dangling(),
256 NonZeroUsize::new(1).unwrap(),
257 NonZeroUsize::new(2).unwrap(),
258 Pow2Usize::new(1).unwrap()
259 )
260 } { _ => {} };
261 }
262
263 #[test]
264 #[should_panic(expected = "shrink not implemented")]
265 fn default_shrink_panics() {
266 let a = DefaultAllocator { };
267 match unsafe {
268 a.shrink(
269 NonNull::dangling(),
270 NonZeroUsize::new(1).unwrap(),
271 NonZeroUsize::new(2).unwrap(),
272 Pow2Usize::new(1).unwrap()
273 )
274 } { _ => {} };
275 }
276
277 #[test]
278 fn default_supports_contains_returns_false() {
279 let a = DefaultAllocator { };
280 assert!(!a.supports_contains());
281 }
282
283 #[test]
284 #[should_panic(expected = "contains not implemented")]
285 fn default_contains_panics() {
286 let a = DefaultAllocator { };
287 a.contains(NonNull::dangling());
288 }
289
290 #[test]
291 fn default_name_responds() {
292 let a = DefaultAllocator { };
293 assert!(a.name().contains("allocator"));
294 }
295
296 #[test]
297 fn default_to_ref_works() {
298 let a = DefaultAllocator { };
299 let ar = a.to_ref();
300 assert!(ar.name().contains("allocator"));
301 }
302
303 #[test]
304 fn fmt_error_from_alloc_error() {
305 let _fe: core::fmt::Error = AllocError::OperationFailed.into();
306 }
307
308 extern crate std;
309 use std::string::String as StdString;
310 use core::fmt::Write;
311
312 #[test]
313 fn fmt_on_default_allocator() {
314 let a = DefaultAllocator { };
315 let mut s = StdString::new();
316 write!(s, "{:?}", a.to_ref()).unwrap();
317 assert!(s.as_str().contains("allocator@"));
318 }
319
320 struct ShrinkTestAllocator { }
321 unsafe impl Allocator for ShrinkTestAllocator {
322 unsafe fn shrink(
323 &self,
324 _ptr: NonNull<u8>,
325 _current_size: NonZeroUsize,
326 _new_smaller_size: NonZeroUsize,
327 _align: Pow2Usize
328 ) -> Result<NonNull<u8>, AllocError> {
329 Ok(NonNull::new(0xA1B2C3D4_usize as *mut u8).unwrap())
330 }
331 }
332 #[test]
333 fn allocator_ref_shrink_calls_allocator_shrink() {
334 let a = ShrinkTestAllocator { };
335 let ar = a.to_ref();
336 let p = unsafe {
337 ar.shrink(
338 NonNull::dangling(),
339 NonZeroUsize::new(2).unwrap(),
340 NonZeroUsize::new(1).unwrap(),
341 Pow2Usize::new(1).unwrap()
342 )
343 }.unwrap();
344 assert_eq!(p.as_ptr(), 0xA1B2C3D4_usize as *mut u8);
345 }
346
347 struct ContainsSupTestAllocator { }
348 unsafe impl Allocator for ContainsSupTestAllocator {
349 fn supports_contains(&self) -> bool { true }
350 fn contains(&self, ptr: NonNull<u8>) -> bool {
351 (ptr.as_ptr() as usize) & 1 == 1
352 }
353 }
354 #[test]
355 fn contains_on_allocator_ref_forwards_to_allocator() {
356 let a = ContainsSupTestAllocator { };
357 let ar = a.to_ref();
358 assert!(ar.supports_contains());
359 assert!(ar.contains(NonNull::new(1 as *mut u8).unwrap()));
360 assert!(!ar.contains(NonNull::new(2 as *mut u8).unwrap()));
361 }
362
363 #[test]
364 fn allocator_ref_to_ref_copies_internal_ref() {
365 let a = DefaultAllocator { };
366 let ar = a.to_ref();
367 let arr = ar.to_ref();
368 let size = core::mem::size_of::<AllocatorRef<'_>>();
369 assert_eq!(
370 unsafe { core::slice::from_raw_parts(&ar as *const AllocatorRef as *const u8, size) },
371 unsafe { core::slice::from_raw_parts(&arr as *const AllocatorRef as *const u8, size) });
372 }
373
374 struct AllocOrGrowTestAllocator();
375 unsafe impl Allocator for AllocOrGrowTestAllocator {
376 unsafe fn alloc(
377 &self,
378 size: NonZeroUsize,
379 _align: Pow2Usize
380 ) -> Result<NonNull<u8>, AllocError> {
381 Ok(NonNull::new((size.get() * 1000 + size.get()) as *mut u8).unwrap())
382 }
383 unsafe fn grow(
384 &self,
385 ptr: NonNull<u8>,
386 current_size: NonZeroUsize,
387 new_larger_size: NonZeroUsize,
388 _align: Pow2Usize
389 ) -> Result<NonNull<u8>, AllocError> {
390 Ok(NonNull::new(((ptr.as_ptr() as usize) - current_size.get() + new_larger_size.get()) as *mut u8).unwrap())
391 }
392 }
393 #[test]
394 fn alloc_or_grow_first_allocates_then_grows() {
395 let a = AllocOrGrowTestAllocator();
396 let ar = a.to_ref();
397 let mut p = NonNull::<u8>::dangling();
398 p = unsafe { ar.alloc_or_grow(p, 0, NonZeroUsize::new(123).unwrap(), Pow2Usize::one()).unwrap() };
399 assert_eq!(p.as_ptr() as usize, 123123);
400 p = unsafe { ar.alloc_or_grow(p, 123, NonZeroUsize::new(456).unwrap(), Pow2Usize::one()).unwrap() };
401 assert_eq!(p.as_ptr() as usize, 123456);
402 p = unsafe { ar.alloc_or_grow(p, 456, NonZeroUsize::new(789).unwrap(), Pow2Usize::one()).unwrap() };
403 assert_eq!(p.as_ptr() as usize, 123789);
404 }
405}
406