diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 6fb0542081..e0372b6cd4 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -1898,13 +1898,13 @@ void APIConnection::process_batch_() { uint16_t APIConnection::MessageCreator::operator()(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single, uint16_t message_type) const { - if (is_string()) { + if (has_tagged_string_ptr_()) { // Handle string-based messages switch (message_type) { #ifdef USE_EVENT case EventResponse::MESSAGE_TYPE: { auto *e = static_cast(entity); - return APIConnection::try_send_event_response(e, *get_string_ptr(), conn, remaining_size, is_single); + return APIConnection::try_send_event_response(e, *get_string_ptr_(), conn, remaining_size, is_single); } #endif default: diff --git a/esphome/components/api/api_connection.h b/esphome/components/api/api_connection.h index a25aa8012e..f983a0483b 100644 --- a/esphome/components/api/api_connection.h +++ b/esphome/components/api/api_connection.h @@ -486,6 +486,9 @@ class APIConnection : public APIServerConnection { // Optimized MessageCreator class using tagged pointer class MessageCreator { + // Ensure pointer alignment allows LSB tagging + static_assert(alignof(std::string *) > 1, "String pointer alignment must be > 1 for LSB tagging"); + public: // Constructor for function pointer MessageCreator(MessageCreatorPtr ptr) { @@ -503,15 +506,15 @@ class APIConnection : public APIServerConnection { // Destructor ~MessageCreator() { - if (is_string()) { - delete get_string_ptr(); + if (has_tagged_string_ptr_()) { + delete get_string_ptr_(); } } // Copy constructor MessageCreator(const MessageCreator &other) { - if (other.is_string()) { - auto *str = new std::string(*other.get_string_ptr()); + if (other.has_tagged_string_ptr_()) { + auto *str = new std::string(*other.get_string_ptr_()); data_.tagged = reinterpret_cast(str) | 1; } else { data_ = other.data_; @@ -525,12 +528,12 @@ class APIConnection : public APIServerConnection { MessageCreator &operator=(const MessageCreator &other) { if (this != &other) { // Clean up current string data if needed - if (is_string()) { - delete get_string_ptr(); + if (has_tagged_string_ptr_()) { + delete get_string_ptr_(); } // Copy new data - if (other.is_string()) { - auto *str = new std::string(*other.get_string_ptr()); + if (other.has_tagged_string_ptr_()) { + auto *str = new std::string(*other.get_string_ptr_()); data_.tagged = reinterpret_cast(str) | 1; } else { data_ = other.data_; @@ -542,8 +545,8 @@ class APIConnection : public APIServerConnection { MessageCreator &operator=(MessageCreator &&other) noexcept { if (this != &other) { // Clean up current string data if needed - if (is_string()) { - delete get_string_ptr(); + if (has_tagged_string_ptr_()) { + delete get_string_ptr_(); } // Move data data_ = other.data_; @@ -559,10 +562,13 @@ class APIConnection : public APIServerConnection { private: // Check if this contains a string pointer - bool is_string() const { return (data_.tagged & 1) != 0; } + bool has_tagged_string_ptr_() const { return (data_.tagged & 1) != 0; } // Get the actual string pointer (clears the tag bit) - std::string *get_string_ptr() const { return reinterpret_cast(data_.tagged & ~uintptr_t(1)); } + std::string *get_string_ptr_() const { + // NOLINTNEXTLINE(performance-no-int-to-ptr) + return reinterpret_cast(data_.tagged & ~uintptr_t(1)); + } union { MessageCreatorPtr ptr; diff --git a/esphome/core/automation.h b/esphome/core/automation.h index 02c9d44f16..e156818312 100644 --- a/esphome/core/automation.h +++ b/esphome/core/automation.h @@ -27,20 +27,67 @@ template class TemplatableValue { public: TemplatableValue() : type_(NONE) {} - template::value, int> = 0> - TemplatableValue(F value) : type_(VALUE), value_(std::move(value)) {} + template::value, int> = 0> TemplatableValue(F value) : type_(VALUE) { + new (&this->value_) T(std::move(value)); + } - template::value, int> = 0> - TemplatableValue(F f) : type_(LAMBDA), f_(f) {} + template::value, int> = 0> TemplatableValue(F f) : type_(LAMBDA) { + this->f_ = new std::function(std::move(f)); + } + + // Copy constructor + TemplatableValue(const TemplatableValue &other) : type_(other.type_) { + if (type_ == VALUE) { + new (&this->value_) T(other.value_); + } else if (type_ == LAMBDA) { + this->f_ = new std::function(*other.f_); + } + } + + // Move constructor + TemplatableValue(TemplatableValue &&other) noexcept : type_(other.type_) { + if (type_ == VALUE) { + new (&this->value_) T(std::move(other.value_)); + } else if (type_ == LAMBDA) { + this->f_ = other.f_; + other.f_ = nullptr; + } + other.type_ = NONE; + } + + // Assignment operators + TemplatableValue &operator=(const TemplatableValue &other) { + if (this != &other) { + this->~TemplatableValue(); + new (this) TemplatableValue(other); + } + return *this; + } + + TemplatableValue &operator=(TemplatableValue &&other) noexcept { + if (this != &other) { + this->~TemplatableValue(); + new (this) TemplatableValue(std::move(other)); + } + return *this; + } + + ~TemplatableValue() { + if (type_ == VALUE) { + this->value_.~T(); + } else if (type_ == LAMBDA) { + delete this->f_; + } + } bool has_value() { return this->type_ != NONE; } T value(X... x) { if (this->type_ == LAMBDA) { - return this->f_(x...); + return (*this->f_)(x...); } // return value also when none - return this->value_; + return this->type_ == VALUE ? this->value_ : T{}; } optional optional_value(X... x) { @@ -58,14 +105,16 @@ template class TemplatableValue { } protected: - enum { + enum : uint8_t { NONE, VALUE, LAMBDA, } type_; - T value_{}; - std::function f_{}; + union { + T value_; + std::function *f_; + }; }; /** Base class for all automation conditions. diff --git a/esphome/core/component_iterator.cpp b/esphome/core/component_iterator.cpp index da593340c1..03c8fb44f9 100644 --- a/esphome/core/component_iterator.cpp +++ b/esphome/core/component_iterator.cpp @@ -375,7 +375,7 @@ void ComponentIterator::advance() { } if (advance_platform) { - this->state_ = static_cast(static_cast(this->state_) + 1); + this->state_ = static_cast(static_cast(this->state_) + 1); this->at_ = 0; } else if (success) { this->at_++; diff --git a/esphome/core/component_iterator.h b/esphome/core/component_iterator.h index 9e187f6c57..4b41872db7 100644 --- a/esphome/core/component_iterator.h +++ b/esphome/core/component_iterator.h @@ -93,7 +93,9 @@ class ComponentIterator { virtual bool on_end(); protected: - enum class IteratorState { + // Iterates over all ESPHome entities (sensors, switches, lights, etc.) + // Supports up to 256 entity types and up to 65,535 entities of each type + enum class IteratorState : uint8_t { NONE = 0, BEGIN, #ifdef USE_BINARY_SENSOR @@ -167,7 +169,7 @@ class ComponentIterator { #endif MAX, } state_{IteratorState::NONE}; - size_t at_{0}; + uint16_t at_{0}; // Supports up to 65,535 entities per type bool include_internal_{false}; };