| 101 | static constexpr fl::size kInlineLambdaSize = FASTLED_INLINE_LAMBDA_SIZE; |
| 102 | |
| 103 | struct InlinedLambda { |
| 104 | // Storage for the lambda/functor object |
| 105 | // Use aligned storage to ensure proper alignment for any type |
| 106 | FL_ALIGN_MAX char bytes[kInlineLambdaSize]; |
| 107 | |
| 108 | // Type-erased invoker and destructor function pointers |
| 109 | R (*invoker)(const InlinedLambda& storage, Args... args); |
| 110 | void (*destructor)(InlinedLambda& storage); |
| 111 | |
| 112 | template <typename Function> |
| 113 | InlinedLambda(Function f) FL_NOEXCEPT { |
| 114 | FL_STATIC_ASSERT(sizeof(Function) <= kInlineLambdaSize, |
| 115 | "Lambda/functor too large for inline storage"); |
| 116 | FL_STATIC_ASSERT(alignof(Function) <= alignof(max_align_t), |
| 117 | "Lambda/functor requires stricter alignment than storage provides"); |
| 118 | |
| 119 | // Initialize the entire storage to zero to avoid copying uninitialized memory |
| 120 | fl::memset(bytes, 0, kInlineLambdaSize); |
| 121 | |
| 122 | // Construct the lambda/functor in-place |
| 123 | new (bytes) Function(fl::move(f)); |
| 124 | |
| 125 | // Set up type-erased function pointers |
| 126 | invoker = &invoke_lambda<Function>; |
| 127 | destructor = &destroy_lambda<Function>; |
| 128 | } |
| 129 | |
| 130 | // Copy constructor |
| 131 | InlinedLambda(const InlinedLambda& other) FL_NOEXCEPT |
| 132 | : invoker(other.invoker), destructor(other.destructor) { |
| 133 | // This is tricky - we need to copy the stored object |
| 134 | // For now, we'll use memcopy (works for trivially copyable types) |
| 135 | fl::memcpy(bytes, other.bytes, kInlineLambdaSize); |
| 136 | } |
| 137 | |
| 138 | // Move constructor |
| 139 | InlinedLambda(InlinedLambda&& other) FL_NOEXCEPT |
| 140 | : invoker(other.invoker), destructor(other.destructor) { |
| 141 | fl::memcpy(bytes, other.bytes, kInlineLambdaSize); |
| 142 | // Reset the other object to prevent double destruction |
| 143 | other.destructor = nullptr; |
| 144 | } |
| 145 | |
| 146 | ~InlinedLambda() FL_NOEXCEPT { |
| 147 | if (destructor) { |
| 148 | destructor(*this); |
| 149 | } |
| 150 | } |
| 151 | |
| 152 | template <typename FUNCTOR> |
| 153 | static R invoke_lambda(const InlinedLambda& storage, Args... args) FL_NOEXCEPT { |
| 154 | // Use placement new to safely access the stored lambda |
| 155 | FL_ALIGN_AS(FUNCTOR) char temp_storage[sizeof(FUNCTOR)]; |
| 156 | // Copy the lambda from storage |
| 157 | fl::memcpy(temp_storage, storage.bytes, sizeof(FUNCTOR)); |
| 158 | // Get a properly typed pointer to the copied lambda (non-const for mutable lambdas) |
| 159 | FUNCTOR* f = static_cast<FUNCTOR*>(static_cast<void*>(temp_storage)); |
| 160 | // Invoke the lambda |