mirror of
https://github.com/esphome/esphome.git
synced 2025-10-16 07:27:50 +02:00
[core] Add make_name_with_suffix helper to optimize string concatenation (#11176)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
parent
0f356fcc79
commit
8d8fcfeda2
@ -213,8 +213,11 @@ bool ESP32BLE::ble_setup_() {
|
||||
if (this->name_.has_value()) {
|
||||
name = this->name_.value();
|
||||
if (App.is_name_add_mac_suffix_enabled()) {
|
||||
name += "-";
|
||||
name += get_mac_address().substr(6);
|
||||
// MAC address suffix length (last 6 characters of 12-char MAC address string)
|
||||
constexpr size_t mac_address_suffix_len = 6;
|
||||
const std::string mac_addr = get_mac_address();
|
||||
const char *mac_suffix_ptr = mac_addr.c_str() + mac_address_suffix_len;
|
||||
name = make_name_with_suffix(name, '-', mac_suffix_ptr, mac_address_suffix_len);
|
||||
}
|
||||
} else {
|
||||
name = App.get_name();
|
||||
|
@ -691,7 +691,9 @@ void EthernetComponent::set_manual_ip(const ManualIP &manual_ip) { this->manual_
|
||||
|
||||
std::string EthernetComponent::get_use_address() const {
|
||||
if (this->use_address_.empty()) {
|
||||
return App.get_name() + ".local";
|
||||
// ".local" suffix length for mDNS hostnames
|
||||
constexpr size_t mdns_local_suffix_len = 5;
|
||||
return make_name_with_suffix(App.get_name(), '.', "local", mdns_local_suffix_len);
|
||||
}
|
||||
return this->use_address_;
|
||||
}
|
||||
|
@ -29,7 +29,8 @@ static const char *const TAG = "mqtt";
|
||||
|
||||
MQTTClientComponent::MQTTClientComponent() {
|
||||
global_mqtt_client = this;
|
||||
this->credentials_.client_id = App.get_name() + "-" + get_mac_address();
|
||||
const std::string mac_addr = get_mac_address();
|
||||
this->credentials_.client_id = make_name_with_suffix(App.get_name(), '-', mac_addr.c_str(), mac_addr.size());
|
||||
}
|
||||
|
||||
// Connection
|
||||
|
@ -267,7 +267,9 @@ network::IPAddress WiFiComponent::get_dns_address(int num) {
|
||||
}
|
||||
std::string WiFiComponent::get_use_address() const {
|
||||
if (this->use_address_.empty()) {
|
||||
return App.get_name() + ".local";
|
||||
// ".local" suffix length for mDNS hostnames
|
||||
constexpr size_t mdns_local_suffix_len = 5;
|
||||
return make_name_with_suffix(App.get_name(), '.', "local", mdns_local_suffix_len);
|
||||
}
|
||||
return this->use_address_;
|
||||
}
|
||||
|
@ -1195,6 +1195,13 @@ def validate_bytes(value):
|
||||
|
||||
|
||||
def hostname(value):
|
||||
"""Validate that the value is a valid hostname.
|
||||
|
||||
Maximum length is 63 characters per RFC 1035.
|
||||
|
||||
Note: If this limit is changed, update MAX_NAME_WITH_SUFFIX_SIZE in
|
||||
esphome/core/helpers.cpp to accommodate the new maximum length.
|
||||
"""
|
||||
value = string(value)
|
||||
if re.match(r"^[a-z0-9-]{1,63}$", value, re.IGNORECASE) is not None:
|
||||
return value
|
||||
|
@ -102,9 +102,15 @@ class Application {
|
||||
arch_init();
|
||||
this->name_add_mac_suffix_ = name_add_mac_suffix;
|
||||
if (name_add_mac_suffix) {
|
||||
const std::string mac_suffix = get_mac_address().substr(6);
|
||||
this->name_ = name + "-" + mac_suffix;
|
||||
this->friendly_name_ = friendly_name.empty() ? "" : friendly_name + " " + mac_suffix;
|
||||
// MAC address suffix length (last 6 characters of 12-char MAC address string)
|
||||
constexpr size_t mac_address_suffix_len = 6;
|
||||
const std::string mac_addr = get_mac_address();
|
||||
// Use pointer + offset to avoid substr() allocation
|
||||
const char *mac_suffix_ptr = mac_addr.c_str() + mac_address_suffix_len;
|
||||
this->name_ = make_name_with_suffix(name, '-', mac_suffix_ptr, mac_address_suffix_len);
|
||||
if (!friendly_name.empty()) {
|
||||
this->friendly_name_ = make_name_with_suffix(friendly_name, ' ', mac_suffix_ptr, mac_address_suffix_len);
|
||||
}
|
||||
} else {
|
||||
this->name_ = name;
|
||||
this->friendly_name_ = friendly_name;
|
||||
|
@ -200,7 +200,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_NAME): cv.valid_name,
|
||||
cv.Optional(CONF_FRIENDLY_NAME, ""): cv.string,
|
||||
cv.Optional(CONF_FRIENDLY_NAME, ""): cv.All(cv.string, cv.Length(max=120)),
|
||||
cv.Optional(CONF_AREA): validate_area_config,
|
||||
cv.Optional(CONF_COMMENT): cv.string,
|
||||
cv.Required(CONF_BUILD_PATH): cv.string,
|
||||
|
@ -235,6 +235,30 @@ std::string str_sprintf(const char *fmt, ...) {
|
||||
return str;
|
||||
}
|
||||
|
||||
// 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 std::string &name, char sep, const char *suffix_ptr, size_t suffix_len) {
|
||||
char buffer[MAX_NAME_WITH_SUFFIX_SIZE];
|
||||
size_t name_len = name.size();
|
||||
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,
|
||||
// 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
|
||||
total_len = name_len + 1 + suffix_len;
|
||||
}
|
||||
|
||||
memcpy(buffer, name.c_str(), name_len);
|
||||
buffer[name_len] = sep;
|
||||
memcpy(buffer + name_len + 1, suffix_ptr, suffix_len);
|
||||
buffer[total_len] = '\0';
|
||||
return std::string(buffer, total_len);
|
||||
}
|
||||
|
||||
// Parsing & formatting
|
||||
|
||||
size_t parse_hex(const char *str, size_t length, uint8_t *data, size_t count) {
|
||||
|
@ -354,6 +354,16 @@ std::string __attribute__((format(printf, 1, 3))) str_snprintf(const char *fmt,
|
||||
/// sprintf-like function returning std::string.
|
||||
std::string __attribute__((format(printf, 1, 2))) str_sprintf(const char *fmt, ...);
|
||||
|
||||
/// Concatenate a name with a separator and suffix using an efficient stack-based approach.
|
||||
/// This avoids multiple heap allocations during string construction.
|
||||
/// Maximum name length supported is 120 characters for friendly names.
|
||||
/// @param name The base name string
|
||||
/// @param sep The separator character (e.g., '-', ' ', or '.')
|
||||
/// @param suffix_ptr Pointer to the suffix characters
|
||||
/// @param suffix_len Length of the suffix
|
||||
/// @return The concatenated string: name + sep + suffix
|
||||
std::string make_name_with_suffix(const std::string &name, char sep, const char *suffix_ptr, size_t suffix_len);
|
||||
|
||||
///@}
|
||||
|
||||
/// @name Parsing & formatting
|
||||
|
Loading…
x
Reference in New Issue
Block a user