From a76461cf5f142af4065335a4614a7ade6496f056 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 19 Dec 2025 15:50:25 -1000 Subject: [PATCH] [esp32_ble] Avoid string allocation when setting BLE device name --- esphome/components/esp32_ble/ble.cpp | 27 ++++++++++++++++++--------- esphome/core/helpers.cpp | 20 +++++++++++++------- esphome/core/helpers.h | 12 ++++++++++++ 3 files changed, 43 insertions(+), 16 deletions(-) diff --git a/esphome/components/esp32_ble/ble.cpp b/esphome/components/esp32_ble/ble.cpp index a279f7d2a4..2959657113 100644 --- a/esphome/components/esp32_ble/ble.cpp +++ b/esphome/components/esp32_ble/ble.cpp @@ -256,8 +256,11 @@ bool ESP32BLE::ble_setup_() { } #endif + // BLE device names are limited to 20 characters + // Buffer: 20 chars + null terminator + constexpr size_t BLE_NAME_MAX_LEN = 21; + char name_buffer[BLE_NAME_MAX_LEN]; const char *device_name; - std::string name_with_suffix; if (this->name_ != nullptr) { if (App.is_name_add_mac_suffix_enabled()) { @@ -268,23 +271,29 @@ bool ESP32BLE::ble_setup_() { char mac_addr[mac_address_len]; get_mac_address_into_buffer(mac_addr); const char *mac_suffix_ptr = mac_addr + mac_address_suffix_len; - name_with_suffix = - make_name_with_suffix(this->name_, strlen(this->name_), '-', mac_suffix_ptr, mac_address_suffix_len); - device_name = name_with_suffix.c_str(); + make_name_with_suffix_to(name_buffer, sizeof(name_buffer), this->name_, strlen(this->name_), '-', mac_suffix_ptr, + mac_address_suffix_len); + device_name = name_buffer; } else { device_name = this->name_; } } else { - name_with_suffix = App.get_name(); - if (name_with_suffix.length() > 20) { + const std::string &app_name = App.get_name(); + size_t name_len = app_name.length(); + if (name_len > 20) { if (App.is_name_add_mac_suffix_enabled()) { // Keep first 13 chars and last 7 chars (MAC suffix), remove middle - name_with_suffix.erase(13, name_with_suffix.length() - 20); + memcpy(name_buffer, app_name.c_str(), 13); + memcpy(name_buffer + 13, app_name.c_str() + name_len - 7, 7); + name_buffer[20] = '\0'; } else { - name_with_suffix.resize(20); + memcpy(name_buffer, app_name.c_str(), 20); + name_buffer[20] = '\0'; } + } else { + memcpy(name_buffer, app_name.c_str(), name_len + 1); // Include null terminator } - device_name = name_with_suffix.c_str(); + device_name = name_buffer; } err = esp_ble_gap_set_device_name(device_name); diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index bbe59e53f1..e3135a5496 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -244,17 +244,16 @@ std::string str_sprintf(const char *fmt, ...) { // Maximum size for name with suffix: 120 (max friendly name) + 1 (separator) + 6 (MAC suffix) + 1 (null term) static constexpr size_t MAX_NAME_WITH_SUFFIX_SIZE = 128; -std::string make_name_with_suffix(const char *name, size_t name_len, char sep, const char *suffix_ptr, - size_t suffix_len) { - char buffer[MAX_NAME_WITH_SUFFIX_SIZE]; +size_t make_name_with_suffix_to(char *buffer, size_t buffer_size, const char *name, size_t name_len, char sep, + const char *suffix_ptr, size_t suffix_len) { size_t total_len = name_len + 1 + suffix_len; // Silently truncate if needed: prioritize keeping the full suffix - if (total_len >= MAX_NAME_WITH_SUFFIX_SIZE) { - // NOTE: This calculation could underflow if suffix_len >= MAX_NAME_WITH_SUFFIX_SIZE - 2, + if (total_len >= buffer_size) { + // NOTE: This calculation could underflow if suffix_len >= buffer_size - 2, // but this is safe because this helper is only called with small suffixes: // MAC suffixes (6-12 bytes), ".local" (5 bytes), etc. - name_len = MAX_NAME_WITH_SUFFIX_SIZE - suffix_len - 2; // -2 for separator and null terminator + name_len = buffer_size - suffix_len - 2; // -2 for separator and null terminator total_len = name_len + 1 + suffix_len; } @@ -262,7 +261,14 @@ std::string make_name_with_suffix(const char *name, size_t name_len, char sep, c buffer[name_len] = sep; memcpy(buffer + name_len + 1, suffix_ptr, suffix_len); buffer[total_len] = '\0'; - return std::string(buffer, total_len); + return total_len; +} + +std::string make_name_with_suffix(const char *name, size_t name_len, char sep, const char *suffix_ptr, + size_t suffix_len) { + char buffer[MAX_NAME_WITH_SUFFIX_SIZE]; + size_t len = make_name_with_suffix_to(buffer, sizeof(buffer), name, name_len, sep, suffix_ptr, suffix_len); + return std::string(buffer, len); } std::string make_name_with_suffix(const std::string &name, char sep, const char *suffix_ptr, size_t suffix_len) { diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index f9dcfccb45..def98106e7 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -549,6 +549,18 @@ std::string make_name_with_suffix(const std::string &name, char sep, const char std::string make_name_with_suffix(const char *name, size_t name_len, char sep, const char *suffix_ptr, size_t suffix_len); +/// Zero-allocation version: format name + separator + suffix directly into buffer. +/// @param buffer Output buffer (must have space for result + null terminator) +/// @param buffer_size Size of the output buffer +/// @param name The base name string +/// @param name_len Length of the name +/// @param sep Single character separator +/// @param suffix_ptr Pointer to the suffix characters +/// @param suffix_len Length of the suffix +/// @return Length written (excluding null terminator) +size_t make_name_with_suffix_to(char *buffer, size_t buffer_size, const char *name, size_t name_len, char sep, + const char *suffix_ptr, size_t suffix_len); + ///@} /// @name Parsing & formatting