Skip to main content

ealloc/
lib.rs

1#![doc = include_str!("../README.md")]
2#![cfg_attr(not(any(feature = "std", test)), no_std)]
3
4use core::{
5    alloc::Layout,
6    cell::UnsafeCell,
7    ptr::{NonNull, copy_nonoverlapping},
8};
9
10use ecore::int::PrimaryUInt;
11
12pub use self::{
13    block::{ElementCount, simple::SimpleFirstFit},
14    dv::DesignatedVictim,
15    first_fit::FirstFit,
16    half_tree::HalfTree,
17    ptr::{FixedBase, FixedBasePtr},
18    tlsf::{Tlsf, TlsfParms},
19};
20
21mod block;
22mod dv;
23mod first_fit;
24mod half_tree;
25mod ptr;
26mod tlsf;
27
28/// Error returned when memory allocation fails.
29///
30/// This is a zero-sized type indicating the allocator could not satisfy the request.
31pub struct AllocError;
32
33/// Custom memory allocator trait, similar to [`core::alloc::Allocator`].
34///
35/// # Safety
36///
37/// Implementors must ensure memory safety for all allocation and deallocation operations.
38pub unsafe trait Allocator {
39    fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError>;
40
41    /// # Safety
42    ///
43    /// `ptr` must have been allocated by this allocator with the given `layout` and not yet deallocated.
44    unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout);
45
46    /// Allocates zero-initialized memory with the given layout.
47    /// The default implementation calls [`allocate`](Self::allocate) then zeroes the memory.
48    fn allocate_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
49        let mut block = self.allocate(layout)?;
50        unsafe { block.as_mut().fill(0) };
51        Ok(block)
52    }
53
54    /// # Safety
55    ///
56    /// `ptr` must have been allocated by this allocator with `old_layout` and not yet deallocated.
57    /// `new_layout.size()` must be >= `old_layout.size()`.
58    unsafe fn grow(&self, ptr: NonNull<u8>, old_layout: Layout, new_layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
59        debug_assert!(new_layout.size() >= old_layout.size(), "`new_layout.size()` must be greater than or equal to `old_layout.size()`");
60        let new_ptr = self.allocate(new_layout)?;
61        unsafe { copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_ptr().cast(), old_layout.size()) }
62        unsafe { self.deallocate(ptr, old_layout) }
63        Ok(new_ptr)
64    }
65
66    /// # Safety
67    ///
68    /// `ptr` must have been allocated by this allocator with `old_layout` and not yet deallocated.
69    unsafe fn grow_zeroed(&self, ptr: NonNull<u8>, old_layout: Layout, new_layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
70        debug_assert!(new_layout.size() >= old_layout.size(), "`new_layout.size()` must be greater than or equal to `old_layout.size()`");
71        let new_ptr = self.allocate_zeroed(new_layout)?;
72        unsafe { copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_ptr().cast(), old_layout.size()) }
73        unsafe { self.deallocate(ptr, old_layout) }
74        Ok(new_ptr)
75    }
76
77    /// # Safety
78    ///
79    /// `ptr` must have been allocated by this allocator with `old_layout` and not yet deallocated.
80    /// `new_layout.size()` must be <= `old_layout.size()`.
81    unsafe fn shrink(&self, ptr: NonNull<u8>, old_layout: Layout, new_layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
82        debug_assert!(new_layout.size() <= old_layout.size(), "`new_layout.size()` must be smaller than or equal to `old_layout.size()`");
83        let new_ptr = self.allocate(new_layout)?;
84        unsafe { copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_ptr().cast(), new_layout.size()) }
85        unsafe { self.deallocate(ptr, old_layout) }
86        Ok(new_ptr)
87    }
88
89    /// Returns a reference to `self`, useful for passing the allocator by reference.
90    fn by_ref(&self) -> &Self
91    where
92        Self: Sized,
93    {
94        self
95    }
96}
97
98/// Free memory block manager for [LeanFlexAllocator], don't care about how block be splited or merged
99pub trait FreeBlockManager {
100    /// Block state element, block state exactually use 2 [Self::StateElement], must ensure `size_of::<Self::StateElement>() <= size_of::<usize>()`,
101    /// * when `size_of::<Self::StateElement>() < size_of::<usize>()` the extreme block size is `(Self::StateElement::BITS - 1) * size_of::<Self::Element>`
102    /// * when `size_of::<Self::StateElement>() == size_of::<usize>()` the extreme block size is `isize::MAX`
103    /// * [Self::MAX_ELEMENT_COUNT] limit the max block size which always <= extreme block size
104    type StateElement: PrimaryUInt;
105    /// Block element, block underlying is `[Self::Element]`, block align >= `align_of::<Self::Element>`, block size is multiple of `size_of::<Self::Element>()`,
106    /// must ensure `size_of::<Self::Element>() % align_of::<Self::Element>() == 0 && align_of::<Self::Element>() >= 2 && size_of::<Self::Element>() != 0`.
107    /// bigger size_of::<Self::Element> lead to bigger internal fragmentation, but can manage bigger memory region with same [Self::StateElement]
108    type Element;
109    /// Free block node, manager store [Self::Node] at free block head, then block size always >= `size_of::<Self::Node>()`
110    type Node;
111
112    /// max element count for single region, and is max allocable element count for single allocation,
113    /// memory region will be splited (when enxtend or init) if region element count bigger than [Self::MAX_ELEMENT_COUNT]
114    const MAX_ELEMENT_COUNT: ElementCount<Self::StateElement, Self::Element>;
115
116    /// acceptable address range, default full aligned range, maybe changed after init
117    fn address_range(&self) -> (usize, usize);
118
119    /// take out a free block (find and unregister)
120    fn take_out(&mut self, element_count: ElementCount<Self::StateElement, Self::Element>) -> Option<NonNull<Self::Node>>;
121
122    /// register a node to pool, caller must ensure node is free and in memory region managed by this manager
123    ///
124    /// # Safety
125    ///
126    /// `node` must point to a valid, free block within the memory region managed by this manager.
127    unsafe fn register(&mut self, node: NonNull<Self::Node>);
128
129    /// unregister a node from pool, caller must ensture node is in this pool
130    ///
131    /// # Safety
132    ///
133    /// `node` must be currently registered in this pool.
134    unsafe fn unregister(&mut self, node: NonNull<Self::Node>);
135
136    /// init manager, must call once and only once before call any other method, default call register.
137    /// caller must ensure block body in address range
138    ///
139    /// # Safety
140    ///
141    /// Must be called once and only once. `ptr` must point to a valid block within the address range.
142    unsafe fn init(&mut self, ptr: NonNull<Self::Element>) {
143        unsafe { self.register(ptr.cast()) };
144    }
145
146    /// extend with new anther memory region node, default call register,
147    /// caller must ensure block body in address range
148    ///
149    /// # Safety
150    ///
151    /// `ptr` must point to a valid block within the address range.
152    unsafe fn extend(&mut self, ptr: NonNull<Self::Element>) {
153        unsafe { self.register(ptr.cast()) };
154    }
155
156    /// called by allocator just before block return to user, usually used by monitor, default no-op
157    fn after_allocate(&mut self, ptr: NonNull<u8>, layout: Layout) {
158        let _ = (ptr, layout);
159    }
160
161    /// called by allocator just before deallocate block, usually used by monitor, default no-op
162    fn before_deallocate(&mut self, ptr: NonNull<u8>, layout: Layout) {
163        let _ = (ptr, layout);
164    }
165
166    /// validate memory region state, only used by test after block state changed
167    #[cfg(feature = "test-utils")]
168    fn validate(&mut self, addr: core::num::NonZero<usize>) {
169        let _ = addr;
170    }
171}
172
173/// A mutex-like lock for synchronizing access to the allocator's internal state.
174///
175/// Implementations include [`NoopMutex`] for single-threaded use and `std::sync::Mutex`.
176pub trait AllocatorMutex {
177    type Guard<'g>
178    where
179        Self: 'g;
180    #[must_use = "unlock when drop"]
181    fn lock(&self) -> Self::Guard<'_>;
182}
183
184/// Memory allocator which seperate memory allocation into two layer:
185/// * one for common memory block process, take out from [FreeBlockManager] and allocate (maybe split and register to [FreeBlockManager]) to user,
186///   and free from user and register (maybe combine block) to [FreeBlockManager],
187///   and other common memory allocate/deallocate process which does't implement free block management
188/// * one for [FreeBlockManager], which only care about free blocks
189pub struct LeanFlexAllocator<Mutex, Manager: ?Sized + FreeBlockManager> {
190    mutex: Mutex,
191    raw: UnsafeCell<block::RawAllocator<Manager>>,
192}
193
194unsafe impl<Mutex: Sync, Manager: ?Sized + FreeBlockManager> Sync for LeanFlexAllocator<Mutex, Manager> {}
195
196impl<Mutex, Manager: FreeBlockManager> LeanFlexAllocator<Mutex, Manager> {
197    pub const fn new(mutex: Mutex, manager: Manager) -> Self {
198        Self { mutex, raw: UnsafeCell::new(block::RawAllocator::new(manager)) }
199    }
200}
201
202impl<Manager: FreeBlockManager> LeanFlexAllocator<NoopMutex, Manager> {
203    pub const fn new_without_mutex(manager: Manager) -> Self {
204        Self { mutex: NoopMutex::new(), raw: UnsafeCell::new(block::RawAllocator::new(manager)) }
205    }
206}
207
208/// noop [AllocatorMutex], usually for no thread or inside single thread
209pub struct NoopMutex(UnsafeCell<()>);
210
211impl Default for NoopMutex {
212    fn default() -> Self {
213        Self::new()
214    }
215}
216
217impl NoopMutex {
218    pub const fn new() -> Self {
219        Self(UnsafeCell::new(()))
220    }
221}
222
223impl AllocatorMutex for NoopMutex {
224    type Guard<'g>
225        = ()
226    where
227        Self: 'g;
228
229    fn lock(&self) -> Self::Guard<'_> {}
230}
231
232#[cfg(any(feature = "std", test))]
233impl<T: ?Sized> AllocatorMutex for std::sync::Mutex<T> {
234    type Guard<'g>
235        = std::sync::MutexGuard<'g, T>
236    where
237        Self: 'g;
238
239    fn lock(&self) -> Self::Guard<'_> {
240        Self::lock(self).unwrap()
241    }
242}