From 1bdf6402a2f83029553d4df112a3a134534c61a4 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 27 Mar 2026 15:52:33 -1000 Subject: [PATCH] Move grow path to helpers.cpp as non-template function Since Callback is trivially copyable regardless of signature, the grow logic is type-independent. Move it to a concrete function in helpers.cpp using void*/memcpy, eliminating the template definition from the header entirely. --- esphome/core/helpers.cpp | 12 ++++++++++++ esphome/core/helpers.h | 21 ++++++--------------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index 1732fc72e8..8c97a03b12 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -22,6 +22,18 @@ namespace esphome { static const char *const TAG = "helpers"; +__attribute__((noinline, cold)) void *callback_manager_grow(void *data, uint16_t size, uint16_t &capacity, + size_t elem_size) { + uint16_t new_cap = size + 1; + auto *new_data = ::operator new(new_cap *elem_size); + if (data) { + __builtin_memcpy(new_data, data, size * elem_size); + ::operator delete(data); + } + capacity = new_cap; + return new_data; +} + static const uint16_t CRC16_A001_LE_LUT_L[] = {0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241, 0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440}; static const uint16_t CRC16_A001_LE_LUT_H[] = {0x0000, 0xcc01, 0xd801, 0x1400, 0xf001, 0x3c00, 0x2800, 0xe401, diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index 99eabb704f..a2b33dd7b1 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -1782,6 +1782,9 @@ template struct Callback { } }; +/// Grow a CallbackManager's backing array to exactly size+1. Defined in helpers.cpp. +void *callback_manager_grow(void *data, uint16_t size, uint16_t &capacity, size_t elem_size); + template class CallbackManager; /** Helper class to allow having multiple subscribers to a callback. @@ -1842,30 +1845,18 @@ template class CallbackManager { protected: template friend class LazyCallbackManager; /// Non-template core to avoid code duplication per lambda type. - /// Inline fast path; cold growth path is out-of-line in grow_(). + /// Inline fast path; cold growth path is in helpers.cpp via callback_manager_grow(). void add_(CbType cb) { if (this->size_ == this->capacity_) - this->grow_(); + this->data_ = + static_cast(callback_manager_grow(this->data_, this->size_, this->capacity_, sizeof(CbType))); this->data_[this->size_++] = cb; } - /// Out-of-line cold path: allocate exact size needed. - void grow_(); CbType *data_{nullptr}; uint16_t size_{0}; uint16_t capacity_{0}; }; -/// Out-of-line cold growth path for CallbackManager::add_(). -template __attribute__((noinline, cold)) void CallbackManager::grow_() { - auto *new_data = new CbType[this->size_ + 1]; - if (this->data_) { - __builtin_memcpy(new_data, this->data_, this->size_ * sizeof(CbType)); - delete[] this->data_; - } - this->data_ = new_data; - this->capacity_ = this->size_ + 1; -} - template class LazyCallbackManager; /** Lazy-allocating callback manager that only allocates memory when callbacks are registered.