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}