From d635892ecf672c9077aa872f6acf03bf61b9aa26 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 8 Dec 2025 16:36:13 +0100 Subject: [PATCH] [core] Use StringRef for get_comment and get_compilation_time to avoid allocations (#12219) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- esphome/components/mqtt/mqtt_component.cpp | 2 +- esphome/components/sen5x/sen5x.cpp | 2 +- esphome/components/sgp30/sgp30.cpp | 2 +- esphome/components/sgp4x/sgp4x.cpp | 2 +- esphome/components/version/version_text_sensor.cpp | 2 +- esphome/components/web_server/web_server.cpp | 2 +- esphome/components/wifi/wifi_component.cpp | 2 +- esphome/core/application.h | 2 ++ esphome/core/string_ref.h | 11 +++++++++++ 9 files changed, 20 insertions(+), 7 deletions(-) diff --git a/esphome/components/mqtt/mqtt_component.cpp b/esphome/components/mqtt/mqtt_component.cpp index 1cd818964e..5d2bedae79 100644 --- a/esphome/components/mqtt/mqtt_component.cpp +++ b/esphome/components/mqtt/mqtt_component.cpp @@ -154,7 +154,7 @@ bool MQTTComponent::send_discovery_() { device_info[MQTT_DEVICE_MANUFACTURER] = model == nullptr ? ESPHOME_PROJECT_NAME : std::string(ESPHOME_PROJECT_NAME, model - ESPHOME_PROJECT_NAME); #else - device_info[MQTT_DEVICE_SW_VERSION] = ESPHOME_VERSION " (" + App.get_compilation_time() + ")"; + device_info[MQTT_DEVICE_SW_VERSION] = ESPHOME_VERSION " (" + App.get_compilation_time_ref() + ")"; device_info[MQTT_DEVICE_MODEL] = ESPHOME_BOARD; #if defined(USE_ESP8266) || defined(USE_ESP32) device_info[MQTT_DEVICE_MANUFACTURER] = "Espressif"; diff --git a/esphome/components/sen5x/sen5x.cpp b/esphome/components/sen5x/sen5x.cpp index 3298a5b8db..ffb9e2bc02 100644 --- a/esphome/components/sen5x/sen5x.cpp +++ b/esphome/components/sen5x/sen5x.cpp @@ -157,7 +157,7 @@ void SEN5XComponent::setup() { // Hash with compilation time and serial number // This ensures the baseline storage is cleared after OTA // Serial numbers are unique to each sensor, so mulitple sensors can be used without conflict - uint32_t hash = fnv1_hash(App.get_compilation_time() + std::to_string(combined_serial)); + uint32_t hash = fnv1_hash(App.get_compilation_time_ref() + std::to_string(combined_serial)); this->pref_ = global_preferences->make_preference(hash, true); if (this->pref_.load(&this->voc_baselines_storage_)) { diff --git a/esphome/components/sgp30/sgp30.cpp b/esphome/components/sgp30/sgp30.cpp index 9e8d6b332c..fa548ce94e 100644 --- a/esphome/components/sgp30/sgp30.cpp +++ b/esphome/components/sgp30/sgp30.cpp @@ -75,7 +75,7 @@ void SGP30Component::setup() { // Hash with compilation time and serial number // This ensures the baseline storage is cleared after OTA // Serial numbers are unique to each sensor, so mulitple sensors can be used without conflict - uint32_t hash = fnv1_hash(App.get_compilation_time() + std::to_string(this->serial_number_)); + uint32_t hash = fnv1_hash(App.get_compilation_time_ref() + std::to_string(this->serial_number_)); this->pref_ = global_preferences->make_preference(hash, true); if (this->store_baseline_ && this->pref_.load(&this->baselines_storage_)) { diff --git a/esphome/components/sgp4x/sgp4x.cpp b/esphome/components/sgp4x/sgp4x.cpp index 99d88006f7..a0c957d608 100644 --- a/esphome/components/sgp4x/sgp4x.cpp +++ b/esphome/components/sgp4x/sgp4x.cpp @@ -59,7 +59,7 @@ void SGP4xComponent::setup() { // Hash with compilation time and serial number // This ensures the baseline storage is cleared after OTA // Serial numbers are unique to each sensor, so mulitple sensors can be used without conflict - uint32_t hash = fnv1_hash(App.get_compilation_time() + std::to_string(this->serial_number_)); + uint32_t hash = fnv1_hash(App.get_compilation_time_ref() + std::to_string(this->serial_number_)); this->pref_ = global_preferences->make_preference(hash, true); if (this->pref_.load(&this->voc_baselines_storage_)) { diff --git a/esphome/components/version/version_text_sensor.cpp b/esphome/components/version/version_text_sensor.cpp index 65dbfd27cf..78d0fb501b 100644 --- a/esphome/components/version/version_text_sensor.cpp +++ b/esphome/components/version/version_text_sensor.cpp @@ -13,7 +13,7 @@ void VersionTextSensor::setup() { if (this->hide_timestamp_) { this->publish_state(ESPHOME_VERSION); } else { - this->publish_state(str_sprintf(ESPHOME_VERSION " %s", App.get_compilation_time().c_str())); + this->publish_state(str_sprintf(ESPHOME_VERSION " %s", App.get_compilation_time_ref().c_str())); } } float VersionTextSensor::get_setup_priority() const { return setup_priority::DATA; } diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index ca3aa21a95..0c22c2f08d 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -287,7 +287,7 @@ std::string WebServer::get_config_json() { JsonObject root = builder.root(); root[ESPHOME_F("title")] = App.get_friendly_name().empty() ? App.get_name() : App.get_friendly_name(); - root[ESPHOME_F("comment")] = App.get_comment(); + root[ESPHOME_F("comment")] = App.get_comment_ref(); #if defined(USE_WEBSERVER_OTA_DISABLED) || !defined(USE_WEBSERVER_OTA) root[ESPHOME_F("ota")] = false; // Note: USE_WEBSERVER_OTA_DISABLED only affects web_server, not captive_portal #else diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index ff33a81fcf..d46916bfd9 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -360,7 +360,7 @@ void WiFiComponent::start() { get_mac_address_pretty_into_buffer(mac_s)); this->last_connected_ = millis(); - uint32_t hash = this->has_sta() ? fnv1_hash(App.get_compilation_time()) : 88491487UL; + uint32_t hash = this->has_sta() ? fnv1_hash(App.get_compilation_time_ref().c_str()) : 88491487UL; this->pref_ = global_preferences->make_preference(hash, true); #ifdef USE_WIFI_FAST_CONNECT diff --git a/esphome/core/application.h b/esphome/core/application.h index 14e800342e..8e2035b7c5 100644 --- a/esphome/core/application.h +++ b/esphome/core/application.h @@ -256,6 +256,8 @@ class Application { /// Get the comment of this Application set by pre_setup(). std::string get_comment() const { return this->comment_; } + /// Get the comment as StringRef (avoids allocation) + StringRef get_comment_ref() const { return StringRef(this->comment_); } bool is_name_add_mac_suffix_enabled() const { return this->name_add_mac_suffix_; } diff --git a/esphome/core/string_ref.h b/esphome/core/string_ref.h index efaa17181d..505fdd906a 100644 --- a/esphome/core/string_ref.h +++ b/esphome/core/string_ref.h @@ -128,6 +128,17 @@ inline std::string operator+(const StringRef &lhs, const char *rhs) { return str; } +inline std::string operator+(const StringRef &lhs, const std::string &rhs) { + auto str = lhs.str(); + str.append(rhs); + return str; +} + +inline std::string operator+(const std::string &lhs, const StringRef &rhs) { + std::string str(lhs); + str.append(rhs.c_str(), rhs.size()); + return str; +} #ifdef USE_JSON // NOLINTNEXTLINE(readability-identifier-naming) inline void convertToJson(const StringRef &src, JsonVariant dst) { dst.set(src.c_str()); }