1pub use core::alloc::Layout;
53pub use iceoryx2_bb_elementary_traits::allocator::*;
54
55use iceoryx2_bb_concurrency::atomic::Ordering;
56
57use iceoryx2_bb_concurrency::atomic::AtomicBool;
58use iceoryx2_bb_concurrency::cell::UnsafeCell;
59use iceoryx2_bb_elementary::bump_allocator::BumpAllocator;
60use iceoryx2_bb_elementary::math::align;
61use iceoryx2_bb_elementary_traits::relocatable_container::*;
62use iceoryx2_bb_lock_free::mpmc::unique_index_set::*;
63use iceoryx2_log::fail;
64use iceoryx2_log::fatal_panic;
65
66#[derive(Debug)]
67pub struct PoolAllocator {
68 buckets: UniqueIndexSet,
69 bucket_size: usize,
70 bucket_alignment: usize,
71 start: usize,
72 size: usize,
73 is_memory_initialized: AtomicBool,
74}
75
76impl PoolAllocator {
77 fn verify_init(&self, source: &str) {
78 debug_assert!(
79 self.is_memory_initialized.load(Ordering::Relaxed),
80 "From: {self:?}, Undefined behavior when calling \"{source}\" and the object is not initialized."
81 );
82 }
83
84 pub fn number_of_buckets(&self) -> u32 {
85 self.buckets.capacity()
86 }
87
88 pub fn bucket_size(&self) -> usize {
89 self.bucket_size
90 }
91
92 pub fn size(&self) -> usize {
93 self.size
94 }
95
96 pub fn start_address(&self) -> usize {
97 self.start
98 }
99
100 pub fn max_alignment(&self) -> usize {
101 self.bucket_alignment
102 }
103
104 pub unsafe fn deallocate_bucket(&self, ptr: NonNull<u8>) {
112 self.verify_init("deallocate");
113
114 self.buckets
115 .release_raw_index(self.get_index(ptr), ReleaseMode::Default);
116 }
117
118 pub unsafe fn new_uninit(bucket_layout: Layout, ptr: NonNull<u8>, size: usize) -> Self {
124 let adjusted_start = align(ptr.as_ptr() as usize, bucket_layout.align());
125
126 PoolAllocator {
127 buckets: unsafe {
128 UniqueIndexSet::new_uninit(Self::calc_number_of_buckets(bucket_layout, ptr, size))
129 },
130 bucket_size: bucket_layout.size(),
131 bucket_alignment: bucket_layout.align(),
132 start: adjusted_start,
133 size,
134 is_memory_initialized: AtomicBool::new(false),
135 }
136 }
137
138 pub unsafe fn init<Allocator: BaseAllocator>(
143 &mut self,
144 allocator: &Allocator,
145 ) -> Result<(), AllocationError> {
146 if self.is_memory_initialized.load(Ordering::Relaxed) {
147 fatal_panic!(
148 from self,
149 "Memory already initialized. Initializing it twice may lead to undefined behavior."
150 );
151 }
152
153 fail!(from self, when self.buckets.init(allocator),
154 "Unable to initialize pool allocator");
155
156 self.is_memory_initialized.store(true, Ordering::Relaxed);
157 Ok(())
158 }
159
160 pub fn memory_size(bucket_layout: Layout, size: usize) -> usize {
161 let min_required_buckets = size / bucket_layout.size();
162
163 UniqueIndexSet::memory_size(min_required_buckets)
164 }
165
166 fn calc_number_of_buckets(bucket_layout: Layout, ptr: NonNull<u8>, size: usize) -> usize {
167 let adjusted_start = align(ptr.as_ptr() as usize, bucket_layout.align());
168 let bucket_size = align(bucket_layout.size(), bucket_layout.align());
169
170 (ptr.as_ptr() as usize + size - adjusted_start) / bucket_size
171 }
172
173 fn verify_ptr_is_managaed_by_allocator(&self, ptr: NonNull<u8>) {
174 let position = ptr.as_ptr() as usize;
175 debug_assert!(
176 !(position < self.start
177 || position > self.start + self.size
178 || (position - self.start) % self.bucket_size != 0),
179 "The pointer {ptr:?} is not managed by this allocator."
180 );
181 }
182
183 fn get_index(&self, ptr: NonNull<u8>) -> u32 {
184 self.verify_ptr_is_managaed_by_allocator(ptr);
185 let position = ptr.as_ptr() as usize;
186
187 ((position - self.start) / self.bucket_size) as u32
188 }
189}
190
191impl BaseAllocator for PoolAllocator {
192 fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocationError> {
193 self.verify_init("allocate");
194
195 if layout.size() > self.bucket_size {
196 fail!(from self, with AllocationError::SizeTooLarge,
197 "The requested allocation size {} is greater than the maximum supported size of {}.", layout.size(), self.bucket_size);
198 }
199
200 if layout.align() > self.bucket_alignment {
201 fail!(from self, with AllocationError::AlignmentFailure,
202 "The requested allocation alignment {} is greater than the maximum supported alignment of {}.", layout.align(), self.bucket_alignment);
203 }
204
205 match unsafe { self.buckets.acquire_raw_index() } {
206 Ok(v) => Ok(unsafe {
207 NonNull::new_unchecked(core::ptr::slice_from_raw_parts_mut(
208 (self.start + v as usize * self.bucket_size) as *mut u8,
209 layout.size(),
210 ))
211 }),
212 Err(_) => {
213 fail!(from self, with AllocationError::OutOfMemory,
214 "No more buckets available to allocate {} bytes with an alignment of {}.",
215 layout.size(), layout.align());
216 }
217 }
218 }
219
220 unsafe fn deallocate(&self, ptr: NonNull<u8>, _layout: Layout) {
221 self.deallocate_bucket(ptr);
222 }
223}
224
225impl Allocator for PoolAllocator {
226 unsafe fn grow(
228 &self,
229 ptr: NonNull<u8>,
230 old_layout: Layout,
231 new_layout: Layout,
232 ) -> Result<NonNull<[u8]>, AllocationGrowError> {
233 self.verify_init("grow");
234
235 let msg = "Unable to grow memory chunk";
236 self.verify_ptr_is_managaed_by_allocator(ptr);
237
238 if old_layout.size() >= new_layout.size() {
239 fail!(from self, with AllocationGrowError::GrowWouldShrink,
240 "{} since the new size of {} would be smaller than the old size of {}. Use Allocator::shrink instead.", msg, new_layout.size(), old_layout.size());
241 }
242
243 if self.bucket_alignment < new_layout.align() {
244 fail!(from self, with AllocationGrowError::AlignmentFailure,
245 "{} since the new alignment {} exceeds the maximum supported alignment.", msg, new_layout.align() );
246 }
247
248 if self.bucket_size < new_layout.size() {
249 fail!(from self, with AllocationGrowError::OutOfMemory,
250 "{} since the new size {} exceeds the maximum supported size.", msg, new_layout.size());
251 }
252
253 Ok(NonNull::new(core::ptr::slice_from_raw_parts_mut(
254 ptr.as_ptr(),
255 new_layout.size(),
256 ))
257 .unwrap())
258 }
259
260 unsafe fn shrink(
261 &self,
262 ptr: NonNull<u8>,
263 old_layout: Layout,
264 new_layout: Layout,
265 ) -> Result<NonNull<[u8]>, AllocationShrinkError> {
266 self.verify_init("shrink");
267
268 let msg = "Unable to shrink memory chunk";
269 self.verify_ptr_is_managaed_by_allocator(ptr);
270
271 if old_layout.size() <= new_layout.size() {
272 fail!(from self, with AllocationShrinkError::ShrinkWouldGrow,
273 "{} since the new size of {} would be greater than the old size of {}. Use Allocator::grow instead.", msg, new_layout.size(), old_layout.size());
274 }
275
276 if self.bucket_alignment < new_layout.align() {
277 fail!(from self, with AllocationShrinkError::AlignmentFailure,
278 "{} since the new alignment {} exceeds the maximum supported alignment.", msg, new_layout.align() );
279 }
280
281 Ok(NonNull::new(core::ptr::slice_from_raw_parts_mut(
282 ptr.as_ptr(),
283 new_layout.size(),
284 ))
285 .unwrap())
286 }
287}
288
289#[derive(Debug)]
290#[repr(C)]
291pub struct FixedSizePoolAllocator<const MAX_NUMBER_OF_BUCKETS: usize> {
292 state: PoolAllocator,
293 next_free_index: [UnsafeCell<u32>; MAX_NUMBER_OF_BUCKETS],
294 next_free_index_plus_one: UnsafeCell<u32>,
295}
296
297unsafe impl<const MAX_NUMBER_OF_BUCKETS: usize> Send
298 for FixedSizePoolAllocator<MAX_NUMBER_OF_BUCKETS>
299{
300}
301
302unsafe impl<const MAX_NUMBER_OF_BUCKETS: usize> Sync
303 for FixedSizePoolAllocator<MAX_NUMBER_OF_BUCKETS>
304{
305}
306
307impl<const MAX_NUMBER_OF_BUCKETS: usize> FixedSizePoolAllocator<MAX_NUMBER_OF_BUCKETS> {
308 pub fn number_of_buckets(&self) -> u32 {
309 self.state.number_of_buckets()
310 }
311
312 pub fn bucket_size(&self) -> usize {
313 self.state.bucket_size()
314 }
315
316 pub fn size(&self) -> usize {
317 self.state.size()
318 }
319
320 pub fn max_alignment(&self) -> usize {
321 self.state.max_alignment()
322 }
323
324 pub fn new(bucket_layout: Layout, ptr: NonNull<u8>, size: usize) -> Self {
325 let adjusted_start = align(ptr.as_ptr() as usize, bucket_layout.align());
326 let bucket_size = align(bucket_layout.size(), bucket_layout.align());
327 let number_of_buckets = (ptr.as_ptr() as usize + size - adjusted_start) / bucket_size;
328
329 let mut new_self = FixedSizePoolAllocator {
330 state: PoolAllocator {
331 buckets: unsafe {
332 UniqueIndexSet::new_uninit(core::cmp::min(
333 number_of_buckets,
334 MAX_NUMBER_OF_BUCKETS,
335 ))
336 },
337 bucket_size: bucket_layout.size(),
338 bucket_alignment: bucket_layout.align(),
339 start: adjusted_start,
340 size,
341 is_memory_initialized: AtomicBool::new(true),
342 },
343 next_free_index: core::array::from_fn(|i| UnsafeCell::new(i as u32 + 1)),
344 next_free_index_plus_one: UnsafeCell::new(MAX_NUMBER_OF_BUCKETS as u32 + 1),
345 };
346
347 let allocator = BumpAllocator::new(new_self.next_free_index.as_mut_ptr().cast());
348 unsafe {
349 new_self
350 .state
351 .buckets
352 .init(&allocator)
353 .expect("All required memory is preallocated.")
354 };
355 new_self
356 }
357}
358
359impl<const MAX_NUMBER_OF_BUCKETS: usize> BaseAllocator
360 for FixedSizePoolAllocator<MAX_NUMBER_OF_BUCKETS>
361{
362 fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocationError> {
363 self.state.allocate(layout)
364 }
365
366 unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
367 self.state.deallocate(ptr, layout);
368 }
369}
370
371impl<const MAX_NUMBER_OF_BUCKETS: usize> Allocator
372 for FixedSizePoolAllocator<MAX_NUMBER_OF_BUCKETS>
373{
374 unsafe fn grow(
376 &self,
377 ptr: NonNull<u8>,
378 old_layout: Layout,
379 new_layout: Layout,
380 ) -> Result<NonNull<[u8]>, AllocationGrowError> {
381 self.state.grow(ptr, old_layout, new_layout)
382 }
383
384 unsafe fn shrink(
385 &self,
386 ptr: NonNull<u8>,
387 old_layout: Layout,
388 new_layout: Layout,
389 ) -> Result<NonNull<[u8]>, AllocationShrinkError> {
390 self.state.shrink(ptr, old_layout, new_layout)
391 }
392}