From a9e13b6e8cbc5e04d54913f8347689fd88937569 Mon Sep 17 00:00:00 2001 From: Tomer27cz Date: Sat, 7 Mar 2026 20:58:19 +0100 Subject: [PATCH] Optimize DlmsParser allocations and types Use stack buffers and C-strings to avoid heap allocations, and modernize token handling and ownership. - Replace heap-allocated std::string temporaries in dlms_parser.cpp with stack buffers and pass const char* to callbacks/logs to reduce allocations and copying. - Change DlmsDataCallback signature to accept const char* for obis and string values; update DlmsPushComponent to match. - Switch DlmsParser ownership to std::unique_ptr in DlmsPushComponent and add include. - Replace std::list with std::vector for DSL token parsing, update iteration/insertion logic accordingly. - Remove unused include and adjust related function signatures/whitespace. These changes improve performance and memory usage; note the callback/API signature change may require updating any external callers. --- components/dlms_push/dlms_parser.cpp | 22 ++++++++++------------ components/dlms_push/dlms_parser.h | 3 +-- components/dlms_push/dlms_push.cpp | 14 +++++++------- components/dlms_push/dlms_push.h | 5 +++-- 4 files changed, 21 insertions(+), 23 deletions(-) diff --git a/components/dlms_push/dlms_parser.cpp b/components/dlms_push/dlms_parser.cpp index 442f9ec..18f0ef4 100644 --- a/components/dlms_push/dlms_parser.cpp +++ b/components/dlms_push/dlms_parser.cpp @@ -399,10 +399,9 @@ bool DlmsParser::match_pattern_(uint8_t elem_idx, const AxdrDescriptorPattern &p void DlmsParser::emit_object_(const AxdrDescriptorPattern &pat, const AxdrCaptures &c) { if (!c.obis || !this->callback_) return; - // Use stack-allocated buffer for OBIS to avoid string allocation + // Use stack-allocated buffer for OBIS to avoid heap allocation char obis_str_buf[32]; this->obis_to_string_(c.obis, obis_str_buf, sizeof(obis_str_buf)); - std::string obis_str(obis_str_buf); float raw_val_f = this->data_as_float_(c.value_type, c.value_ptr, c.value_len); float val_f = raw_val_f; @@ -410,7 +409,6 @@ void DlmsParser::emit_object_(const AxdrDescriptorPattern &pat, const AxdrCaptur // Use stack-allocated buffer for formatting data char val_s_buf[128]; this->data_to_string_(c.value_type, c.value_ptr, c.value_len, val_s_buf, sizeof(val_s_buf)); - std::string val_s(val_s_buf); bool is_numeric = (c.value_type != DLMS_DATA_TYPE_OCTET_STRING && c.value_type != DLMS_DATA_TYPE_STRING && @@ -424,7 +422,7 @@ void DlmsParser::emit_object_(const AxdrDescriptorPattern &pat, const AxdrCaptur ESP_LOGD(TAG, "Pattern match '%s' at idx %u ===============", pat.name.c_str(), c.elem_idx); uint16_t cid = c.class_id ? c.class_id : pat.default_class_id; - ESP_LOGI(TAG, "Found attribute descriptor: class_id=%d, obis=%s", cid, obis_str.c_str()); + ESP_LOGI(TAG, "Found attribute descriptor: class_id=%d, obis=%s", cid, obis_str_buf); if (c.has_scaler_unit) { ESP_LOGI(TAG, "Value type: %s, len %d, scaler %d, unit %d", @@ -438,7 +436,7 @@ void DlmsParser::emit_object_(const AxdrDescriptorPattern &pat, const AxdrCaptur esphome::format_hex_pretty_to(hex_buf, sizeof(hex_buf), c.value_ptr, c.value_len); ESP_LOGI(TAG, " as hex dump : %s", hex_buf); } - ESP_LOGI(TAG, " as string :'%s'", val_s.c_str()); + ESP_LOGI(TAG, " as string :'%s'", val_s_buf); ESP_LOGI(TAG, " as number : %f", raw_val_f); if (c.has_scaler_unit && is_numeric) { @@ -446,7 +444,7 @@ void DlmsParser::emit_object_(const AxdrDescriptorPattern &pat, const AxdrCaptur } } - this->callback_(obis_str, val_f, val_s, is_numeric); + this->callback_(obis_str_buf, val_f, val_s_buf, is_numeric); this->objects_found_++; } @@ -620,7 +618,7 @@ void DlmsParser::register_pattern_dsl_(const std::string &name, const std::strin return s.substr(b, e - b + 1); }; - std::list tokens; + std::vector tokens; std::string current; int paren = 0; for (char c : dsl) { @@ -639,8 +637,8 @@ void DlmsParser::register_pattern_dsl_(const std::string &name, const std::strin } if (!current.empty()) tokens.push_back(trim(current)); - for (auto it = tokens.begin(); it != tokens.end(); ++it) { - std::string tok = *it; + for (size_t i = 0; i < tokens.size(); i++) { + std::string tok = tokens[i]; if (tok.empty()) continue; if (tok == "F") pat.steps.push_back({AxdrTokenType::EXPECT_TO_BE_FIRST}); @@ -671,7 +669,7 @@ void DlmsParser::register_pattern_dsl_(const std::string &name, const std::strin size_t r = tok.rfind(')'); if (l != std::string::npos && r != std::string::npos && r > l + 1) { std::string inner = tok.substr(l + 1, r - l - 1); - std::list inner_tokens; + std::vector inner_tokens; std::string cur; for (char c2 : inner) { if (c2 == ',') { @@ -685,9 +683,9 @@ void DlmsParser::register_pattern_dsl_(const std::string &name, const std::strin if (!inner_tokens.empty()) { pat.steps.push_back({AxdrTokenType::EXPECT_STRUCTURE_N, static_cast(inner_tokens.size())}); - inner_tokens.push_front("DN"); + inner_tokens.insert(inner_tokens.begin(), "DN"); inner_tokens.push_back("UP"); - tokens.insert(std::next(it), inner_tokens.begin(), inner_tokens.end()); + tokens.insert(tokens.begin() + i + 1, inner_tokens.begin(), inner_tokens.end()); } } } diff --git a/components/dlms_push/dlms_parser.h b/components/dlms_push/dlms_parser.h index 1acf5a4..6e3cdee 100644 --- a/components/dlms_push/dlms_parser.h +++ b/components/dlms_push/dlms_parser.h @@ -4,7 +4,6 @@ #include #include #include -#include namespace esphome { namespace dlms_push { @@ -37,7 +36,7 @@ enum DlmsDataType : uint8_t { }; // Callback for the hub: OBIS code (e.g. "1.0.1.8.0.255"), numeric value, string value, is_numeric flag -using DlmsDataCallback = std::function; +using DlmsDataCallback = std::function; // --- Pattern Matching Enums & Structs --- enum class AxdrTokenType : uint8_t { diff --git a/components/dlms_push/dlms_push.cpp b/components/dlms_push/dlms_push.cpp index 6118c08..9105113 100644 --- a/components/dlms_push/dlms_push.cpp +++ b/components/dlms_push/dlms_push.cpp @@ -11,7 +11,7 @@ namespace dlms_push { static const char *const TAG = "dlms_push"; DlmsPushComponent::DlmsPushComponent() { - this->parser_ = new DlmsParser(); + this->parser_ = std::make_unique(); } void DlmsPushComponent::setup() { @@ -96,7 +96,7 @@ void DlmsPushComponent::process_frame_() { ESP_LOGD(TAG, "PUSH frame size: %zu bytes", this->rx_buffer_len_); } - auto callback = [this](const std::string &obis_code, float float_val, const std::string &str_val, bool is_numeric) { + auto callback = [this](const char *obis_code, float float_val, const char *str_val, bool is_numeric) { this->on_data_parsed_(obis_code, float_val, str_val, is_numeric); }; @@ -109,7 +109,7 @@ void DlmsPushComponent::process_frame_() { this->rx_buffer_len_ = 0; } -void DlmsPushComponent::on_data_parsed_(const std::string &obis_code, float float_val, const std::string &str_val, bool is_numeric) { +void DlmsPushComponent::on_data_parsed_(const char *obis_code, float float_val, const char *str_val, bool is_numeric) { int updated_count = 0; #ifdef USE_SENSOR @@ -117,7 +117,7 @@ void DlmsPushComponent::on_data_parsed_(const std::string &obis_code, float floa for (const auto &entry : this->sensors_) { if (entry.obis == obis_code) { if (this->show_log_) { - ESP_LOGD(TAG, "Found sensor for OBIS code %s: '%s'", obis_code.c_str(), entry.sensor->get_name().c_str()); + ESP_LOGD(TAG, "Found sensor for OBIS code %s: '%s'", obis_code, entry.sensor->get_name().c_str()); ESP_LOGD(TAG, "Publishing data"); } entry.sensor->publish_state(float_val); @@ -131,7 +131,7 @@ void DlmsPushComponent::on_data_parsed_(const std::string &obis_code, float floa for (const auto &entry : this->text_sensors_) { if (entry.obis == obis_code) { if (this->show_log_) { - ESP_LOGD(TAG, "Found sensor for OBIS code %s: '%s'", obis_code.c_str(), entry.sensor->get_name().c_str()); + ESP_LOGD(TAG, "Found sensor for OBIS code %s: '%s'", obis_code, entry.sensor->get_name().c_str()); ESP_LOGD(TAG, "Publishing data"); } entry.sensor->publish_state(str_val); @@ -146,7 +146,7 @@ void DlmsPushComponent::on_data_parsed_(const std::string &obis_code, float floa for (const auto &entry : this->binary_sensors_) { if (entry.obis == obis_code) { if (this->show_log_) { - ESP_LOGD(TAG, "Found sensor for OBIS code %s: '%s'", obis_code.c_str(), entry.sensor->get_name().c_str()); + ESP_LOGD(TAG, "Found sensor for OBIS code %s: '%s'", obis_code, entry.sensor->get_name().c_str()); ESP_LOGD(TAG, "Publishing data"); } entry.sensor->publish_state(state); @@ -157,7 +157,7 @@ void DlmsPushComponent::on_data_parsed_(const std::string &obis_code, float floa #endif if (this->show_log_ && updated_count == 0) { - ESP_LOGV(TAG, "Received OBIS %s, but no sensors are registered for it.", obis_code.c_str()); + ESP_LOGV(TAG, "Received OBIS %s, but no sensors are registered for it.", obis_code); } } diff --git a/components/dlms_push/dlms_push.h b/components/dlms_push/dlms_push.h index 60caf7a..aade383 100644 --- a/components/dlms_push/dlms_push.h +++ b/components/dlms_push/dlms_push.h @@ -16,6 +16,7 @@ #include #include #include +#include namespace esphome { namespace dlms_push { @@ -49,7 +50,7 @@ class DlmsPushComponent : public Component, public uart::UARTDevice { void read_rx_buffer_(); void process_frame_(); - void on_data_parsed_(const std::string &obis_code, float float_val, const std::string &str_val, bool is_numeric); + void on_data_parsed_(const char *obis_code, float float_val, const char *str_val, bool is_numeric); uint32_t receive_timeout_ms_{50}; bool show_log_{false}; @@ -61,7 +62,7 @@ class DlmsPushComponent : public Component, public uart::UARTDevice { uint32_t last_rx_char_time_{0}; bool receiving_{false}; - DlmsParser *parser_{nullptr}; + std::unique_ptr parser_; #ifdef USE_SENSOR struct NumericSensorEntry {