FastLED 3.9.15
Loading...
Searching...
No Matches
allocator.h
Go to the documentation of this file.
1#pragma once
2
3#include "fl/stl/cstddef.h"
4#include "fl/stl/cstring.h"
7#include "fl/stl/bit_cast.h"
8#include "fl/stl/stdint.h"
9#include "fl/stl/bitset.h"
10#include "fl/stl/malloc.h"
11#include "fl/stl/align.h"
12#include "fl/stl/noexcept.h"
14
15#ifndef FASTLED_DEFAULT_SLAB_SIZE
16#define FASTLED_DEFAULT_SLAB_SIZE 8
17#endif
18
19namespace fl {
20
21// Forward declaration for allocate_result
22template <typename Pointer, typename SizeType>
24 Pointer ptr;
25 SizeType count; // Actual allocated count (may be > requested)
26};
27
28// Allocator traits for compile-time capability detection
29// Allows containers to use optimized code paths if allocators support them
30template <typename Allocator>
32 using allocator_type = Allocator;
33 using value_type = typename Allocator::value_type;
34 using pointer = typename Allocator::pointer;
35 using size_type = typename Allocator::size_type;
36
37 // Detect if allocator has reallocate() method
38 // Allows in-place memory resizing for POD types without copy
39 template <typename A = Allocator, typename = void>
41
42 template <typename A>
43 struct has_reallocate<A, decltype((void)fl::declval<A>().reallocate(
44 fl::declval<typename A::pointer>(),
45 fl::declval<typename A::size_type>(),
46 fl::declval<typename A::size_type>()
47 ))> : fl::true_type {};
48
50
51 // Detect if allocator has allocate_at_least() method
52 // Allows allocator to return more memory than requested to reduce reallocations
53 template <typename A = Allocator, typename = void>
55
56 template <typename A>
57 struct has_allocate_at_least<A, decltype((void)fl::declval<A>().allocate_at_least(
58 fl::declval<typename A::size_type>()
59 ))> : fl::true_type {};
60
62};
63
64// Test hooks for malloc/free operations
65#if defined(FASTLED_TESTING)
66// Interface class for malloc/free test hooks
67class MallocFreeHook {
68public:
69 virtual ~MallocFreeHook() FL_NOEXCEPT = default;
70 virtual void onMalloc(void* ptr, fl::size size) FL_NOEXCEPT = 0;
71 virtual void onFree(void* ptr) FL_NOEXCEPT = 0;
72};
73
74// Set test hooks for malloc and free operations
75void SetMallocFreeHook(MallocFreeHook* hook) FL_NOEXCEPT;
76
77// Clear test hooks (set to nullptr)
78void ClearMallocFreeHook() FL_NOEXCEPT;
79#endif
80
81void SetPSRamAllocator(void *(*alloc)(fl::size), void (*free)(void *)) FL_NOEXCEPT;
82void *PSRamAllocate(fl::size size, bool zero = true) FL_NOEXCEPT;
83void PSRamDeallocate(void *ptr) FL_NOEXCEPT;
84
85// Deleter for memory allocated via fl::PSRamAllocator (uses fl::PSRamDeallocate)
86template <typename T> struct PSRamDeleter {
88 void operator()(T *ptr) FL_NOEXCEPT {
89 if (ptr) {
90 PSRamDeallocate(ptr);
91 }
92 }
93};
94
95void* Malloc(fl::size size) FL_NOEXCEPT;
96void Free(void *ptr) FL_NOEXCEPT;
97
98// SlabAllocator registry for cross-DLL shared slab allocators.
99// On Windows DLLs, inline functions with static locals create per-DLL copies.
100// This registry ensures all DLLs in a process share the same SlabAllocator
101// for a given (block_size, slab_size) pair, preventing cross-DLL slab
102// deallocation errors (freeing a pointer inside another DLL's slab).
103namespace detail {
104 void* slab_allocator_registry_get(fl::size block_size, fl::size slab_size) FL_NOEXCEPT;
105 void slab_allocator_registry_set(fl::size block_size, fl::size slab_size, void* allocator) FL_NOEXCEPT;
106} // namespace detail
107
108#ifdef FL_IS_ESP32
109// ESP32-specific memory allocation functions for RMT buffer pooling
110void* InternalAlloc(fl::size size); // MALLOC_CAP_INTERNAL - fast DRAM
111void* InternalRealloc(void* ptr, fl::size size); // Realloc for DRAM
112void InternalFree(void* ptr);
113void* DMAAlloc(fl::size size); // MALLOC_CAP_DMA - DMA-capable memory
114void DMAFree(void* ptr);
115#endif
116
117template <typename T> class PSRamAllocator {
118 public:
119 static T *Alloc(fl::size n) FL_NOEXCEPT {
120 void *ptr = PSRamAllocate(sizeof(T) * n, true);
121 return fl::bit_cast_ptr<T>(ptr);
122 }
123
124 static void Free(T *p) FL_NOEXCEPT {
125 if (p == nullptr) {
126 return;
127 }
129 }
130};
131
132// std compatible allocator.
133template <typename T> class allocator {
134 public:
135 // Type definitions required by STL
136 using value_type = T;
137 using pointer = T*;
138 using const_pointer = const T*;
139 using reference = T&;
140 using const_reference = const T&;
141 using size_type = fl::size;
143
144 // Rebind allocator to type U
145 template <typename U>
146 struct rebind {
148 };
149
150 // Default constructor
152
153 // Copy constructor
154 template <typename U>
156
157 // Destructor
159
160 // Optional: allocate_at_least() for optimized resizing
161 // Allows allocator to return more memory than requested to reduce reallocations
162 // Returns allocation_result with actual allocated count (may be > requested)
164 if (n == 0) {
165 return {nullptr, 0};
166 }
167 // Default implementation: just allocate exactly what's requested
168 // Specialized allocators may override to return more for efficiency
169 return {allocate(n), n};
170 }
171
172 // Optional: reallocate() for in-place resizing
173 // Automatically uses fl::realloc() for trivially copyable types
174 // Returns nullptr on failure or if type is not trivially copyable
175 pointer reallocate(pointer ptr, fl::size old_count, fl::size new_count) FL_NOEXCEPT {
176 return reallocate_impl(ptr, old_count, new_count,
178 }
179
180private:
181 // SFINAE: Use fl::realloc() for trivially copyable types
182 pointer reallocate_impl(pointer ptr, fl::size old_count, fl::size new_count, fl::true_type) FL_NOEXCEPT {
183 if (new_count == 0) {
184 if (ptr) {
185 deallocate(ptr, old_count);
186 }
187 return nullptr;
188 }
189
190 // Safe to use realloc() - type is trivially copyable
191 void* result = fl::realloc(ptr, new_count * sizeof(T));
192 if (!result) {
193 return nullptr; // Realloc failed
194 }
195
196 T* new_ptr = static_cast<T*>(result);
197
198 // Zero-initialize any newly allocated memory
199 if (new_count > old_count) {
200 fl::memset(new_ptr + old_count, 0, (new_count - old_count) * sizeof(T));
201 }
202
203 return new_ptr;
204 }
205
206 // SFINAE: Don't use realloc() for non-trivially-copyable types
207 pointer reallocate_impl(pointer ptr, fl::size old_count, fl::size new_count, fl::false_type) FL_NOEXCEPT {
208 FASTLED_UNUSED(ptr);
209 FASTLED_UNUSED(old_count);
210 FASTLED_UNUSED(new_count);
211 return nullptr; // Signal: not supported, use standard allocate-copy-deallocate
212 }
213
214public:
215
216 // Use this to allocate large blocks of memory for T.
217 // This is useful for large arrays or objects that need to be allocated
218 // in a single block.
219 T* allocate(fl::size n) FL_NOEXCEPT {
220 if (n == 0) {
221 return nullptr; // Handle zero allocation
222 }
223 fl::size size = sizeof(T) * n;
224 void *ptr = Malloc(size);
225 if (ptr == nullptr) {
226 return nullptr; // Handle allocation failure
227 }
228 fl::memset(ptr, 0, sizeof(T) * n); // Zero-initialize the memory
229 return static_cast<T*>(ptr);
230 }
231
232 void deallocate(T* p, fl::size n) FL_NOEXCEPT {
234 if (p == nullptr) {
235 return; // Handle null pointer
236 }
237 Free(p); // Free the allocated memory
238 }
239
240 // Construct an object at the specified address
241 template <typename U, typename... Args>
242 void construct(U* p, Args&&... args) FL_NOEXCEPT {
243 if (p == nullptr) return;
244 new(static_cast<void*>(p)) U(fl::forward<Args>(args)...);
245 }
246
247 // Destroy an object at the specified address
248 template <typename U>
249 void destroy(U* p) FL_NOEXCEPT {
250 if (p == nullptr) return;
251 p->~U();
252 }
253};
254
255// DEPRECATED: allocator_realloc is now REDUNDANT
256//
257// ℹ️ The default fl::allocator<T> now automatically uses realloc() for trivially
258// copyable types, making this specialized allocator unnecessary.
259//
260// ✅ NEW RECOMMENDED USAGE:
261// fl::vector<int> vec; // Automatically uses realloc() optimization
262// fl::vector<CRGB> leds; // Automatically uses realloc() optimization
263// fl::vector<fl::string> strs; // Automatically uses safe allocate-copy-deallocate
264//
265// This class is kept for backwards compatibility but provides no additional benefit.
266// The compile-time safety check below ensures this allocator is only used with
267// trivially copyable types (same restriction as fl::allocator now has).
268//
269template <typename T>
271private:
272 // SAFETY CHECK: Compile-time verification that T is trivially copyable
273 // This prevents undefined behavior from using realloc() with non-trivially-copyable types
275 "allocator_realloc<T> requires T to be trivially copyable. "
276 "NOTE: This allocator is now redundant - fl::allocator<T> automatically "
277 "optimizes trivially copyable types. Just use fl::vector<T> instead.");
278
279public:
280 // Type definitions required by STL
281 using value_type = T;
282 using pointer = T*;
283 using const_pointer = const T*;
284 using reference = T&;
285 using const_reference = const T&;
286 using size_type = fl::size;
288
289 // Rebind allocator to type U
290 template <typename U>
291 struct rebind {
293 };
294
295 // Default constructor
297
298 // Copy constructor
299 template <typename U>
301
302 // Destructor
304
305 // Standard allocate() - allocates new memory
306 T* allocate(fl::size n) FL_NOEXCEPT {
307 if (n == 0) {
308 return nullptr;
309 }
310 fl::size size = sizeof(T) * n;
311 void *ptr = Malloc(size);
312 if (ptr == nullptr) {
313 return nullptr;
314 }
315 fl::memset(ptr, 0, sizeof(T) * n);
316 return static_cast<T*>(ptr);
317 }
318
319 // Standard deallocate()
320 void deallocate(T* p, fl::size n) FL_NOEXCEPT {
322 if (p == nullptr) {
323 return;
324 }
325 Free(p);
326 }
327
328 // Standard construct()
329 template <typename U, typename... Args>
330 void construct(U* p, Args&&... args) FL_NOEXCEPT {
331 if (p == nullptr) return;
332 new(static_cast<void*>(p)) U(fl::forward<Args>(args)...);
333 }
334
335 // Standard destroy()
336 template <typename U>
337 void destroy(U* p) FL_NOEXCEPT {
338 if (p == nullptr) return;
339 p->~U();
340 }
341
342 // OPTIMIZED: allocate_at_least() - can return more than requested
343 // This gives the vector a hint to allocate extra space and reduce future reallocations
345 if (n == 0) {
346 return {nullptr, 0};
347 }
348 // Ask for 1.5x to reduce future reallocations
349 fl::size requested = (3 * n) / 2;
350 T* ptr = allocate(requested);
351 if (ptr) {
352 return {ptr, requested};
353 }
354 // Fallback: try exact size if 1.5x failed
355 ptr = allocate(n);
356 return {ptr, ptr ? n : 0};
357 }
358
359 // OPTIMIZED: reallocate() - in-place resize using fl::realloc()
360 // Returns the new pointer if successful, nullptr if not supported/failed
361 // The caller (fl::vector) handles fallback to allocate-copy-deallocate
362 pointer reallocate(pointer ptr, fl::size old_count, fl::size new_count) FL_NOEXCEPT {
363 if (new_count == 0) {
364 if (ptr) {
365 deallocate(ptr, old_count);
366 }
367 return nullptr;
368 }
369
370 // Use fl::realloc() for in-place resize
371 void* result = fl::realloc(ptr, new_count * sizeof(T));
372 if (!result) {
373 return nullptr; // Realloc failed
374 }
375
376 T* new_ptr = static_cast<T*>(result);
377
378 // Zero-initialize any newly allocated memory
379 if (new_count > old_count) {
380 fl::memset(new_ptr + old_count, 0, (new_count - old_count) * sizeof(T));
381 }
382
383 return new_ptr;
384 }
385};
386
387template <typename T> class allocator_psram {
388 public:
389 // Type definitions required by STL
390 using value_type = T;
391 using pointer = T*;
392 using const_pointer = const T*;
393 using reference = T&;
394 using const_reference = const T&;
395 using size_type = fl::size;
397
398 // Rebind allocator to type U
399 template <typename U>
400 struct rebind {
402 };
403
404 // Default constructor
406
407 // Copy constructor
408 template <typename U>
410
411 // Destructor
413
414 // Allocate memory for n objects of type T
415 T* allocate(fl::size n) FL_NOEXCEPT {
416 return PSRamAllocator<T>::Alloc(n);
417 }
418
419 // Deallocate memory for n objects of type T
420 void deallocate(T* p, fl::size n) FL_NOEXCEPT {
423 }
424
425 // Construct an object at the specified address
426 template <typename U, typename... Args>
427 void construct(U* p, Args&&... args) FL_NOEXCEPT {
428 if (p == nullptr) return;
429 new(static_cast<void*>(p)) U(fl::forward<Args>(args)...);
430 }
431
432 // Destroy an object at the specified address
433 template <typename U>
434 void destroy(U* p) FL_NOEXCEPT {
435 if (p == nullptr) return;
436 p->~U();
437 }
438
439 // Optional: allocate_at_least() with default implementation
441 if (n == 0) {
442 return {nullptr, 0};
443 }
444 // Default: just allocate exactly what's requested
445 return {allocate(n), n};
446 }
447
448 // Optional: reallocate() - no-op for PSRAM
449 pointer reallocate(pointer ptr, fl::size old_count, fl::size new_count) FL_NOEXCEPT {
450 FASTLED_UNUSED(ptr);
451 FASTLED_UNUSED(old_count);
452 FASTLED_UNUSED(new_count);
453 return nullptr; // Signal: not supported
454 }
455};
456
457// Slab allocator for fixed-size objects
458// Optimized for frequent allocation/deallocation of objects of the same size
459// Uses pre-allocated memory slabs with free lists to reduce fragmentation
460template <typename T, fl::size SLAB_SIZE = FASTLED_DEFAULT_SLAB_SIZE>
462private:
463
464 static constexpr fl::size SLAB_BLOCK_SIZE = sizeof(T) > sizeof(void*) ? sizeof(T) : sizeof(void*);
465 static constexpr fl::size BLOCKS_PER_SLAB = SLAB_SIZE;
466 static constexpr fl::size SLAB_MEMORY_SIZE = SLAB_BLOCK_SIZE * BLOCKS_PER_SLAB;
467
468 struct Slab {
472 fl::bitset_fixed<BLOCKS_PER_SLAB> allocated_blocks; // Track which blocks are allocated
473
474 Slab() FL_NOEXCEPT : next(nullptr), memory(nullptr), allocated_count(0) {}
475
477 if (memory) {
478 Free(memory);
479 }
480 }
481 };
482
486
488 Slab* slab = static_cast<Slab*>(Malloc(sizeof(Slab)));
489 if (!slab) {
490 return nullptr;
491 }
492
493 // Use placement new to properly initialize the Slab
494 new(slab) Slab();
495
496 slab->memory = static_cast<u8*>(Malloc(SLAB_MEMORY_SIZE));
497 if (!slab->memory) {
498 slab->~Slab();
499 Free(slab);
500 return nullptr;
501 }
502
503 // Initialize all blocks in the slab as free
504 slab->allocated_blocks.reset(); // All blocks start as free
505
506 // Add slab to the slab list
507 slab->next = mSlabs;
508 mSlabs = slab;
509
510 return slab;
511 }
512
513 void* allocateFromSlab(fl::size n = 1) FL_NOEXCEPT {
514 // Try to find n contiguous free blocks in existing slabs
515 for (Slab* slab = mSlabs; slab; slab = slab->next) {
516 void* ptr = findContiguousBlocks(slab, n);
517 if (ptr) {
518 return ptr;
519 }
520 }
521
522 // No contiguous blocks found, create new slab if n fits
523 if (n <= BLOCKS_PER_SLAB) {
524 if (!createSlab()) {
525 return nullptr; // Out of memory
526 }
527
528 // Try again with the new slab
529 return findContiguousBlocks(mSlabs, n);
530 }
531
532 // Request too large for slab, fall back to malloc
533 return nullptr;
534 }
535
536 void* findContiguousBlocks(Slab* slab, fl::size n) FL_NOEXCEPT {
537 // Check if allocation is too large for this slab
538 if (n > BLOCKS_PER_SLAB) {
539 return nullptr;
540 }
541
542 // Use bitset's find_run to find n contiguous free blocks (false = free)
543 fl::i32 start = slab->allocated_blocks.find_run(false, static_cast<fl::u32>(n));
544 if (start >= 0) {
545 // Mark blocks as allocated
546 for (fl::size i = 0; i < n; ++i) {
547 slab->allocated_blocks.set(static_cast<fl::u32>(start + i), true);
548 }
549 slab->allocated_count += n;
550 mTotalAllocated += n;
551
552 // Return pointer to the first block
553 return slab->memory + static_cast<fl::size>(start) * SLAB_BLOCK_SIZE;
554 }
555
556 return nullptr;
557 }
558
559 void deallocateToSlab(void* ptr, fl::size n = 1) FL_NOEXCEPT {
560 if (!ptr) {
561 return;
562 }
563
564 // Find which slab this block belongs to
565 for (Slab* slab = mSlabs; slab; slab = slab->next) {
566 u8* slab_start = slab->memory;
567 u8* slab_end = slab_start + SLAB_MEMORY_SIZE;
568 u8* block_ptr = fl::bit_cast_ptr<u8>(ptr);
569
570 if (block_ptr >= slab_start && block_ptr < slab_end) {
571 fl::size block_index = (block_ptr - slab_start) / SLAB_BLOCK_SIZE;
572
573 // Mark blocks as free in the bitset
574 for (fl::size i = 0; i < n; ++i) {
575 if (block_index + i < BLOCKS_PER_SLAB) {
576 slab->allocated_blocks.set(block_index + i, false);
577 }
578 }
579
580 slab->allocated_count -= n;
582 break;
583 }
584 }
585 }
586
587public:
588 // Constructor
590
591 // Destructor
595
596 // Non-copyable
599
600 // Movable
602 : mSlabs(other.mSlabs), mTotalAllocated(other.mTotalAllocated), mTotalDeallocated(other.mTotalDeallocated) {
603 other.mSlabs = nullptr;
604 other.mTotalAllocated = 0;
605 other.mTotalDeallocated = 0;
606 }
607
609 if (this != &other) {
610 cleanup();
611 mSlabs = other.mSlabs;
612 mTotalAllocated = other.mTotalAllocated;
613 mTotalDeallocated = other.mTotalDeallocated;
614 other.mSlabs = nullptr;
615 other.mTotalAllocated = 0;
616 other.mTotalDeallocated = 0;
617 }
618 return *this;
619 }
620
621 T* allocate(fl::size n = 1) FL_NOEXCEPT {
622 if (n == 0) {
623 return nullptr;
624 }
625
626 // Try to allocate from slab first
627 void* ptr = allocateFromSlab(n);
628 if (ptr) {
629 fl::memset(ptr, 0, sizeof(T) * n);
630 return static_cast<T*>(ptr);
631 }
632
633 // Fall back to regular malloc for large allocations
634 ptr = Malloc(sizeof(T) * n);
635 if (ptr) {
636 fl::memset(ptr, 0, sizeof(T) * n);
637 }
638 return static_cast<T*>(ptr);
639 }
640
641 void deallocate(T* ptr, fl::size n = 1) FL_NOEXCEPT {
642 if (!ptr) {
643 return;
644 }
645
646 // Try to deallocate from slab first
647 bool found_in_slab = false;
648 for (Slab* slab = mSlabs; slab; slab = slab->next) {
649 u8* slab_start = slab->memory;
650 u8* slab_end = slab_start + SLAB_MEMORY_SIZE;
651 u8* block_ptr = fl::bit_cast_ptr<u8>(static_cast<void*>(ptr));
652
653 if (block_ptr >= slab_start && block_ptr < slab_end) {
654 deallocateToSlab(ptr, n);
655 found_in_slab = true;
656 break;
657 }
658 }
659
660 if (!found_in_slab) {
661 // This was allocated with regular malloc
662 Free(ptr);
663 }
664 }
665
666 // Get allocation statistics
667 fl::size getTotalAllocated() const FL_NOEXCEPT { return mTotalAllocated; }
670
671 // Get number of slabs
672 fl::size getSlabCount() const FL_NOEXCEPT {
673 fl::size count = 0;
674 for (Slab* slab = mSlabs; slab; slab = slab->next) {
675 ++count;
676 }
677 return count;
678 }
679
680 // Cleanup all slabs
682 while (mSlabs) {
683 Slab* next = mSlabs->next;
684 mSlabs->~Slab();
685 Free(mSlabs);
686 mSlabs = next;
687 }
688 mTotalAllocated = 0;
690 }
691};
692
693// STL-compatible slab allocator
694template <typename T, fl::size SLAB_SIZE = FASTLED_DEFAULT_SLAB_SIZE>
696public:
697 // Type definitions required by STL
698 using value_type = T;
699 using pointer = T*;
700 using const_pointer = const T*;
701 using reference = T&;
702 using const_reference = const T&;
703 using size_type = fl::size;
705
706 // Rebind allocator to type U
707 template <typename U>
715
716 // Default constructor
718
719 // Copy constructor
721 FASTLED_UNUSED(other);
722 }
723
724 // Copy assignment
726 FASTLED_UNUSED(other);
727 return *this;
728 }
729
730 // Template copy constructor
731 template <typename U>
735
736 // Destructor
738
739private:
740 // Get the shared process-wide allocator instance.
741 // Uses a DLL-exported registry to ensure all DLLs in the process share
742 // the same SlabAllocator for a given (block_size, slab_size) pair.
744 constexpr fl::size block_size = sizeof(T) > sizeof(void*) ? sizeof(T) : sizeof(void*);
745 void* ptr = detail::slab_allocator_registry_get(block_size, SLAB_SIZE);
746 if (ptr) {
747 return *static_cast<SlabAllocator<T, SLAB_SIZE>*>(ptr);
748 }
749 // First time for this (block_size, slab_size) pair - create and register
751 detail::slab_allocator_registry_set(block_size, SLAB_SIZE, &allocator);
752 return allocator;
753 }
754
755public:
756 // Allocate memory for n objects of type T
757 T* allocate(fl::size n) FL_NOEXCEPT {
758 // Use a static allocator instance per type/size combination
760 return allocator.allocate(n);
761 }
762
763 // Deallocate memory for n objects of type T
764 void deallocate(T* p, fl::size n) FL_NOEXCEPT {
765 // Use the same static allocator instance
767 allocator.deallocate(p, n);
768 }
769
770 // Construct an object at the specified address
771 template <typename U, typename... Args>
772 void construct(U* p, Args&&... args) FL_NOEXCEPT {
773 if (p == nullptr) return;
774 new(static_cast<void*>(p)) U(fl::forward<Args>(args)...);
775 }
776
777 // Destroy an object at the specified address
778 template <typename U>
779 void destroy(U* p) FL_NOEXCEPT {
780 if (p == nullptr) return;
781 p->~U();
782 }
783
784 // Cleanup method to clean up the static slab allocator
786 // Access the same static allocator instance and clean it up
788 allocator.cleanup();
789 }
790
791 // Equality comparison
792 bool operator==(const allocator_slab& other) const FL_NOEXCEPT {
793 FASTLED_UNUSED(other);
794 return true; // All instances are equivalent
795 }
796
797 bool operator!=(const allocator_slab& other) const FL_NOEXCEPT {
798 return !(*this == other);
799 }
800};
801
802// Inlined allocator that stores the first N elements inline
803// Falls back to the base allocator for additional elements
804template <typename T, fl::size N, typename BaseAllocator = fl::allocator<T>>
806private:
807
808 // Inlined storage block
810 FL_ALIGN_AS(T) u8 data[N * sizeof(T)];
811
813 fl::memset(data, 0, sizeof(data));
814 }
815 };
816
817 InlinedStorage mInlinedStorage;
818 BaseAllocator mBaseAllocator;
819 fl::size mInlinedUsed = 0;
820 fl::bitset_fixed<N> mFreeBits; // Track free slots for inlined memory only
821 fl::size mActiveAllocations = 0; // Track current active allocations
822
823public:
824 // Type definitions required by STL
825 using value_type = T;
826 using pointer = T*;
827 using const_pointer = const T*;
828 using reference = T&;
829 using const_reference = const T&;
830 using size_type = fl::size;
832
833 // Rebind allocator to type U
834 template <typename U>
838
839 // Default constructor
841
842 // Copy constructor
844 // Copy inlined data
845 mInlinedUsed = other.mInlinedUsed;
846 for (fl::size i = 0; i < mInlinedUsed; ++i) {
847 new (&get_inlined_ptr()[i]) T(other.get_inlined_ptr()[i]);
848 }
849
850 // Copy free bits
851 mFreeBits = other.mFreeBits;
852
853 // Note: Heap allocations are not copied, only inlined data
854
855 // Copy active allocations count
856 mActiveAllocations = other.mActiveAllocations;
857 }
858
859 // Copy assignment
861 if (this != &other) {
862 clear();
863
864 // Copy inlined data
865 mInlinedUsed = other.mInlinedUsed;
866 for (fl::size i = 0; i < mInlinedUsed; ++i) {
867 new (&get_inlined_ptr()[i]) T(other.get_inlined_ptr()[i]);
868 }
869
870 // Copy free bits
871 mFreeBits = other.mFreeBits;
872
873 // Note: Heap allocations are not copied, only inlined data
874
875 // Copy active allocations count
876 mActiveAllocations = other.mActiveAllocations;
877 }
878 return *this;
879 }
880
881 // Template copy constructor
882 template <typename U>
883 allocator_inlined(const allocator_inlined<U, N, typename BaseAllocator::template rebind<U>::other>& other) FL_NOEXCEPT {
884 FASTLED_UNUSED(other);
885 }
886
887 // Destructor
891
892 // Allocate memory for n objects of type T
893 T* allocate(fl::size n) FL_NOEXCEPT {
894 if (n == 0) {
895 return nullptr;
896 }
897
898 // For large allocations (n > 1), use base allocator directly
899 if (n > 1) {
900 T* ptr = mBaseAllocator.allocate(n);
901 if (ptr) {
903 }
904 return ptr;
905 }
906
907 // For single allocations, first try inlined memory
908 // Find first free inlined slot
909 fl::i32 free_slot = mFreeBits.find_first(false);
910 if (free_slot >= 0 && static_cast<fl::size>(free_slot) < N) {
911 // Mark the inlined slot as used
912 mFreeBits.set(static_cast<fl::u32>(free_slot), true);
913
914 // Update inlined usage tracking
915 if (static_cast<fl::size>(free_slot) + 1 > mInlinedUsed) {
916 mInlinedUsed = static_cast<fl::size>(free_slot) + 1;
917 }
919 return &get_inlined_ptr()[static_cast<fl::size>(free_slot)];
920 }
921
922 // No inlined slots available, use heap allocation
923 T* ptr = mBaseAllocator.allocate(1);
924 if (ptr) {
926 }
927 return ptr;
928 }
929
930 // Deallocate memory for n objects of type T
931 void deallocate(T* p, fl::size n) FL_NOEXCEPT {
932 if (!p || n == 0) {
933 return;
934 }
935
936 // Check if this is inlined memory
937 T* inlined_start = get_inlined_ptr();
938 T* inlined_end = inlined_start + N;
939
940 if (p >= inlined_start && p < inlined_end) {
941 // This is inlined memory, mark slots as free
942 fl::size slot_index = (p - inlined_start);
943 for (fl::size i = 0; i < n; ++i) {
944 if (slot_index + i < N) {
945 mFreeBits.set(slot_index + i, false); // Mark as free
946 }
947 }
949 return;
950 }
951
952 // Fallback to base allocator for heap allocations
953 mBaseAllocator.deallocate(p, n);
955 }
956
957 // Construct an object at the specified address
958 template <typename U, typename... Args>
959 void construct(U* p, Args&&... args) FL_NOEXCEPT {
960 if (p == nullptr) return;
961 new(static_cast<void*>(p)) U(fl::forward<Args>(args)...);
962 }
963
964 // Destroy an object at the specified address
965 template <typename U>
966 void destroy(U* p) FL_NOEXCEPT {
967 if (p == nullptr) return;
968 p->~U();
969 }
970
971 // Clear all allocated memory
973 // Destroy inlined objects
974 for (fl::size i = 0; i < mInlinedUsed; ++i) {
975 get_inlined_ptr()[i].~T();
976 }
977 mInlinedUsed = 0;
978 mFreeBits.reset();
980
981 // Clean up the base allocator (for SlabAllocator, this clears slabs and free lists)
983 }
984
985 // Get total allocated size
986 fl::size total_size() const FL_NOEXCEPT {
987 return mActiveAllocations;
988 }
989
990 // Get inlined capacity
991 fl::size inlined_capacity() const FL_NOEXCEPT {
992 return N;
993 }
994
995 // Check if using inlined storage
998 }
999
1000private:
1004
1005 const T* get_inlined_ptr() const FL_NOEXCEPT {
1007 }
1008
1009 // SFINAE helper to detect if base allocator has cleanup() method
1010 template<typename U>
1011 static auto has_cleanup_impl(int) FL_NOEXCEPT -> decltype(fl::declval<U>().cleanup(), fl::true_type{});
1012
1013 template<typename U>
1015
1016 using has_cleanup = decltype(has_cleanup_impl<BaseAllocator>(0));
1017
1018 // Call cleanup on base allocator if it has the method
1022
1026
1028 // Base allocator doesn't have cleanup method, do nothing
1029 }
1030
1031 // Equality comparison
1032 bool operator==(const allocator_inlined& other) const FL_NOEXCEPT {
1033 FASTLED_UNUSED(other);
1034 return true; // All instances are equivalent for now
1035 }
1036
1037 bool operator!=(const allocator_inlined& other) const FL_NOEXCEPT {
1038 return !(*this == other);
1039 }
1040};
1041
1042// Inlined allocator that uses PSRam for heap allocation
1043template <typename T, fl::size N>
1045
1046// Inlined allocator that uses slab allocator for heap allocation
1047template <typename T, fl::size N, fl::size SLAB_SIZE = 8>
1049
1050template <typename T, fl::size N>
1052
1053} // namespace fl
Alignment macros and utilities for FastLED.
static void Free(T *p) FL_NOEXCEPT
Definition allocator.h:124
static T * Alloc(fl::size n) FL_NOEXCEPT
Definition allocator.h:119
SlabAllocator(SlabAllocator &&other) FL_NOEXCEPT
Definition allocator.h:601
SlabAllocator(const SlabAllocator &) FL_NOEXCEPT=delete
Slab * createSlab() FL_NOEXCEPT
Definition allocator.h:487
static constexpr fl::size BLOCKS_PER_SLAB
Definition allocator.h:465
void * allocateFromSlab(fl::size n=1) FL_NOEXCEPT
Definition allocator.h:513
fl::size getSlabCount() const FL_NOEXCEPT
Definition allocator.h:672
T * allocate(fl::size n=1) FL_NOEXCEPT
Definition allocator.h:621
fl::size mTotalDeallocated
Definition allocator.h:485
fl::size getTotalAllocated() const FL_NOEXCEPT
Definition allocator.h:667
~SlabAllocator() FL_NOEXCEPT
Definition allocator.h:592
static constexpr fl::size SLAB_MEMORY_SIZE
Definition allocator.h:466
void cleanup() FL_NOEXCEPT
Definition allocator.h:681
fl::size getActiveAllocations() const FL_NOEXCEPT
Definition allocator.h:669
static constexpr fl::size SLAB_BLOCK_SIZE
Definition allocator.h:464
void deallocate(T *ptr, fl::size n=1) FL_NOEXCEPT
Definition allocator.h:641
void deallocateToSlab(void *ptr, fl::size n=1) FL_NOEXCEPT
Definition allocator.h:559
SlabAllocator() FL_NOEXCEPT
Definition allocator.h:589
fl::size mTotalAllocated
Definition allocator.h:484
void * findContiguousBlocks(Slab *slab, fl::size n) FL_NOEXCEPT
Definition allocator.h:536
fl::size getTotalDeallocated() const FL_NOEXCEPT
Definition allocator.h:668
SlabAllocator & operator=(const SlabAllocator &) FL_NOEXCEPT=delete
SlabAllocator & operator=(SlabAllocator &&other) FL_NOEXCEPT
Definition allocator.h:608
bool operator==(const allocator_inlined &other) const FL_NOEXCEPT
Definition allocator.h:1032
bool is_using_inlined() const FL_NOEXCEPT
Definition allocator.h:996
void destroy(U *p) FL_NOEXCEPT
Definition allocator.h:966
void deallocate(T *p, fl::size n) FL_NOEXCEPT
Definition allocator.h:931
const T * get_inlined_ptr() const FL_NOEXCEPT
Definition allocator.h:1005
~allocator_inlined() FL_NOEXCEPT
Definition allocator.h:888
allocator_inlined() FL_NOEXCEPT=default
void construct(U *p, Args &&... args) FL_NOEXCEPT
Definition allocator.h:959
fl::size inlined_capacity() const FL_NOEXCEPT
Definition allocator.h:991
const T & const_reference
Definition allocator.h:829
static fl::false_type has_cleanup_impl(...) FL_NOEXCEPT
void cleanup_base_allocator() FL_NOEXCEPT
Definition allocator.h:1019
T * allocate(fl::size n) FL_NOEXCEPT
Definition allocator.h:893
static auto has_cleanup_impl(int) FL_NOEXCEPT -> decltype(fl::declval< U >().cleanup(), fl::true_type{})
void cleanup_base_allocator_impl(fl::true_type) FL_NOEXCEPT
Definition allocator.h:1023
allocator_inlined & operator=(const allocator_inlined &other) FL_NOEXCEPT
Definition allocator.h:860
fl::ptrdiff_t difference_type
Definition allocator.h:831
allocator_inlined(const allocator_inlined< U, N, typename BaseAllocator::template rebind< U >::other > &other) FL_NOEXCEPT
Definition allocator.h:883
const T * const_pointer
Definition allocator.h:827
void clear() FL_NOEXCEPT
Definition allocator.h:972
T * get_inlined_ptr() FL_NOEXCEPT
Definition allocator.h:1001
void cleanup_base_allocator_impl(fl::false_type) FL_NOEXCEPT
Definition allocator.h:1027
bool operator!=(const allocator_inlined &other) const FL_NOEXCEPT
Definition allocator.h:1037
fl::size total_size() const FL_NOEXCEPT
Definition allocator.h:986
allocator_inlined< U, N, typename BaseAllocator::template rebind< U >::other > other
Definition allocator.h:836
~allocator_psram() FL_NOEXCEPT
Definition allocator.h:412
allocator_psram(const allocator_psram< U > &) FL_NOEXCEPT
Definition allocator.h:409
void destroy(U *p) FL_NOEXCEPT
Definition allocator.h:434
void construct(U *p, Args &&... args) FL_NOEXCEPT
Definition allocator.h:427
allocator_psram() FL_NOEXCEPT
Definition allocator.h:405
const T & const_reference
Definition allocator.h:394
const T * const_pointer
Definition allocator.h:392
T * allocate(fl::size n) FL_NOEXCEPT
Definition allocator.h:415
pointer reallocate(pointer ptr, fl::size old_count, fl::size new_count) FL_NOEXCEPT
Definition allocator.h:449
void deallocate(T *p, fl::size n) FL_NOEXCEPT
Definition allocator.h:420
allocation_result< pointer, size_type > allocate_at_least(fl::size n) FL_NOEXCEPT
Definition allocator.h:440
fl::ptrdiff_t difference_type
Definition allocator.h:396
allocator_psram< U > other
Definition allocator.h:401
pointer reallocate(pointer ptr, fl::size old_count, fl::size new_count) FL_NOEXCEPT
Definition allocator.h:362
const T & const_reference
Definition allocator.h:285
allocation_result< pointer, size_type > allocate_at_least(fl::size n) FL_NOEXCEPT
Definition allocator.h:344
void destroy(U *p) FL_NOEXCEPT
Definition allocator.h:337
allocator_realloc(const allocator_realloc< U > &) FL_NOEXCEPT
Definition allocator.h:300
FL_STATIC_ASSERT(fl::is_trivially_copyable< T >::value, "allocator_realloc<T> requires T to be trivially copyable. " "NOTE: This allocator is now redundant - fl::allocator<T> automatically " "optimizes trivially copyable types. Just use fl::vector<T> instead.")
const T * const_pointer
Definition allocator.h:283
allocator_realloc() FL_NOEXCEPT
Definition allocator.h:296
void deallocate(T *p, fl::size n) FL_NOEXCEPT
Definition allocator.h:320
T * allocate(fl::size n) FL_NOEXCEPT
Definition allocator.h:306
void construct(U *p, Args &&... args) FL_NOEXCEPT
Definition allocator.h:330
fl::ptrdiff_t difference_type
Definition allocator.h:287
~allocator_realloc() FL_NOEXCEPT
Definition allocator.h:303
allocator_realloc< U > other
Definition allocator.h:292
void destroy(U *p) FL_NOEXCEPT
Definition allocator.h:779
const T * const_pointer
Definition allocator.h:700
allocator_slab() FL_NOEXCEPT
Definition allocator.h:717
bool operator==(const allocator_slab &other) const FL_NOEXCEPT
Definition allocator.h:792
void deallocate(T *p, fl::size n) FL_NOEXCEPT
Definition allocator.h:764
allocator_slab & operator=(const allocator_slab &other) FL_NOEXCEPT
Definition allocator.h:725
T * allocate(fl::size n) FL_NOEXCEPT
Definition allocator.h:757
bool operator!=(const allocator_slab &other) const FL_NOEXCEPT
Definition allocator.h:797
fl::ptrdiff_t difference_type
Definition allocator.h:704
const T & const_reference
Definition allocator.h:702
allocator_slab(const allocator_slab &other) FL_NOEXCEPT
Definition allocator.h:720
static SlabAllocator< T, SLAB_SIZE > & get_allocator() FL_NOEXCEPT
Definition allocator.h:743
void construct(U *p, Args &&... args) FL_NOEXCEPT
Definition allocator.h:772
~allocator_slab() FL_NOEXCEPT
Definition allocator.h:737
allocator_slab(const allocator_slab< U, SLAB_SIZE > &other) FL_NOEXCEPT
Definition allocator.h:732
void cleanup() FL_NOEXCEPT
Definition allocator.h:785
typename fl::conditional< fl::is_same< U, void >::value, allocator_slab< char, SLAB_SIZE >, allocator_slab< U, SLAB_SIZE > >::type other
Definition allocator.h:709
pointer reallocate_impl(pointer ptr, fl::size old_count, fl::size new_count, fl::false_type) FL_NOEXCEPT
Definition allocator.h:207
allocation_result< pointer, size_type > allocate_at_least(fl::size n) FL_NOEXCEPT
Definition allocator.h:163
void deallocate(T *p, fl::size n) FL_NOEXCEPT
Definition allocator.h:232
T * allocate(fl::size n) FL_NOEXCEPT
Definition allocator.h:219
~allocator() FL_NOEXCEPT
Definition allocator.h:158
const T * const_pointer
Definition allocator.h:138
fl::ptrdiff_t difference_type
Definition allocator.h:142
void construct(U *p, Args &&... args) FL_NOEXCEPT
Definition allocator.h:242
void destroy(U *p) FL_NOEXCEPT
Definition allocator.h:249
fl::size size_type
Definition allocator.h:141
allocator() FL_NOEXCEPT
Definition allocator.h:151
const T & const_reference
Definition allocator.h:140
pointer reallocate_impl(pointer ptr, fl::size old_count, fl::size new_count, fl::true_type) FL_NOEXCEPT
Definition allocator.h:182
pointer reallocate(pointer ptr, fl::size old_count, fl::size new_count) FL_NOEXCEPT
Definition allocator.h:175
allocator(const allocator< U > &) FL_NOEXCEPT
Definition allocator.h:155
allocator< U > other
Definition allocator.h:147
void * slab_allocator_registry_get(fl::size block_size, fl::size slab_size)
void slab_allocator_registry_set(fl::size block_size, fl::size slab_size, void *allocator)
constexpr T && forward(typename remove_reference< T >::type &t) FL_NOEXCEPT
Definition s16x16x4.h:234
long ptrdiff_t
Definition s16x16x4.h:22
integral_constant< bool, true > true_type
Definition s16x16x4.h:27
add_rvalue_reference< T >::type declval() FL_NOEXCEPT
integral_constant< bool, false > false_type
Definition s16x16x4.h:28
void Free(void *ptr)
unsigned char u8
Definition stdint.h:131
add_rvalue_reference< T >::type declval() FL_NOEXCEPT
void * memset(void *s, int c, size_t n) FL_NOEXCEPT
void SetPSRamAllocator(void *(*alloc)(fl::size), void(*free)(void *))
void free(void *ptr)
expected< T, E > result
Alias for expected (Rust-style naming)
Definition result.h:31
void PSRamDeallocate(void *ptr)
void * Malloc(fl::size size)
void * realloc(void *ptr, size_t new_size)
allocator_inlined< T, N, fl::allocator_slab< T > > allocator_inlined_slab
Definition allocator.h:1051
To * bit_cast_ptr(void *storage) FL_NOEXCEPT
Definition bit_cast.h:60
void * PSRamAllocate(fl::size size, bool zero)
allocator_inlined< T, N, fl::allocator_slab< T, SLAB_SIZE > > allocator_inlined_slab_psram
Definition allocator.h:1048
allocator_inlined< T, N, fl::allocator_psram< T > > allocator_inlined_psram
Definition allocator.h:1044
Base definition for an LED controller.
Definition crgb.hpp:179
corkscrew_args args
Definition old.h:149
#define FASTLED_UNUSED(x)
#define FL_NOEXCEPT
Portable compile-time assertion wrapper.
PSRamDeleter() FL_NOEXCEPT=default
fl::bitset_fixed< BLOCKS_PER_SLAB > allocated_blocks
Definition allocator.h:472
~Slab() FL_NOEXCEPT
Definition allocator.h:476
FL_ALIGN_AS(T) u8 data[N *sizeof(T)]
typename Allocator::pointer pointer
Definition allocator.h:34
static constexpr bool has_allocate_at_least_v
Definition allocator.h:61
typename Allocator::value_type value_type
Definition allocator.h:33
static constexpr bool has_reallocate_v
Definition allocator.h:49
Allocator allocator_type
Definition allocator.h:32
typename Allocator::size_type size_type
Definition allocator.h:35