From 15fca7dea86061178d18a47fc3f69525c7f4d07b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 16 Aug 2025 09:35:12 -0400 Subject: [PATCH] Avoid object_id string allocations for all entity info API messages --- esphome/components/api/api_connection.h | 12 +++++++++--- esphome/core/entity_base.cpp | 10 ++++++++++ esphome/core/entity_base.h | 11 +++++++++++ 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/esphome/components/api/api_connection.h b/esphome/components/api/api_connection.h index 076dccfad7..7524d43299 100644 --- a/esphome/components/api/api_connection.h +++ b/esphome/components/api/api_connection.h @@ -301,9 +301,15 @@ class APIConnection : public APIServerConnection { APIConnection *conn, uint32_t remaining_size, bool is_single) { // Set common fields that are shared by all entity types msg.key = entity->get_object_id_hash(); - // IMPORTANT: get_object_id() may return a temporary std::string - std::string object_id = entity->get_object_id(); - msg.set_object_id(StringRef(object_id)); + // Try to use static reference first to avoid allocation + StringRef static_ref = entity->get_object_id_ref_for_api_(); + if (!static_ref.empty()) { + msg.set_object_id(static_ref); + } else { + // Dynamic case - need to allocate + std::string object_id = entity->get_object_id(); + msg.set_object_id(StringRef(object_id)); + } if (entity->has_own_name()) { msg.set_name(entity->get_name()); diff --git a/esphome/core/entity_base.cpp b/esphome/core/entity_base.cpp index 2ea9c77a3e..97bf7147b5 100644 --- a/esphome/core/entity_base.cpp +++ b/esphome/core/entity_base.cpp @@ -1,6 +1,7 @@ #include "esphome/core/entity_base.h" #include "esphome/core/application.h" #include "esphome/core/helpers.h" +#include "esphome/core/string_ref.h" namespace esphome { @@ -58,6 +59,15 @@ std::string EntityBase::get_object_id() const { return this->object_id_c_str_; } } +StringRef EntityBase::get_object_id_ref_for_api_() const { + static constexpr auto EMPTY_STRING_REF = StringRef::from_lit(""); + // Return empty for dynamic case (MAC suffix) + if (!this->flags_.has_own_name && App.is_name_add_mac_suffix_enabled()) { + return EMPTY_STRING_REF; + } + // For static case, return the string or empty if null + return this->object_id_c_str_ == nullptr ? EMPTY_STRING_REF : StringRef(this->object_id_c_str_); +} void EntityBase::set_object_id(const char *object_id) { this->object_id_c_str_ = object_id; this->calc_object_id_(); diff --git a/esphome/core/entity_base.h b/esphome/core/entity_base.h index e60e0728bc..68163ce8c3 100644 --- a/esphome/core/entity_base.h +++ b/esphome/core/entity_base.h @@ -12,6 +12,11 @@ namespace esphome { +// Forward declaration for friend access +namespace api { +class APIConnection; +} // namespace api + enum EntityCategory : uint8_t { ENTITY_CATEGORY_NONE = 0, ENTITY_CATEGORY_CONFIG = 1, @@ -81,6 +86,12 @@ class EntityBase { void set_has_state(bool state) { this->flags_.has_state = state; } protected: + friend class api::APIConnection; + + // Get object_id as StringRef when it's static (for API usage) + // Returns empty StringRef if object_id is dynamic (needs allocation) + StringRef get_object_id_ref_for_api_() const; + /// The hash_base() function has been deprecated. It is kept in this /// class for now, to prevent external components from not compiling. virtual uint32_t hash_base() { return 0L; }