halfbit/mm/
mod.rs

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, // alignment not a power of 2
10    AlignedSizeTooBig, // aligned size overflows usize
11    UnsupportedAlignment, // allocator cannot guarantee requested alignment
12    UnsupportedSize, // allocator does not support requested size
13    NotEnoughMemory, // the proverbial hits the fan
14    OperationFailed, // failure performing the operation (OS mem mapping error)
15    UnsupportedOperation, // alloc, resize, free not supported
16}
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