From 66754fa376b8495885c7718acacaf66b0872f7f0 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 29 Mar 2026 14:24:32 -1000 Subject: [PATCH] [text_sensor] Use std::array in SubstituteFilter (#15266) --- esphome/components/text_sensor/__init__.py | 4 +++- esphome/components/text_sensor/filter.cpp | 20 +++++++------------- esphome/components/text_sensor/filter.h | 16 +++++++++++----- 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/esphome/components/text_sensor/__init__.py b/esphome/components/text_sensor/__init__.py index 78a7a3a41b3..5b07dd29156 100644 --- a/esphome/components/text_sensor/__init__.py +++ b/esphome/components/text_sensor/__init__.py @@ -116,7 +116,9 @@ async def substitute_filter_to_code(config, filter_id): ) for conf in config ] - return cg.new_Pvariable(filter_id, substitutions) + return cg.new_Pvariable( + filter_id, cg.TemplateArguments(len(substitutions)), substitutions + ) @FILTER_REGISTRY.register("map", MapFilter, cv.ensure_list(validate_mapping)) diff --git a/esphome/components/text_sensor/filter.cpp b/esphome/components/text_sensor/filter.cpp index bc044f3a737..d4e6b5b9bbf 100644 --- a/esphome/components/text_sensor/filter.cpp +++ b/esphome/components/text_sensor/filter.cpp @@ -73,20 +73,14 @@ bool PrependFilter::new_value(std::string &value) { return true; } -// Substitute -SubstituteFilter::SubstituteFilter(const std::initializer_list &substitutions) - : substitutions_(substitutions) {} - -bool SubstituteFilter::new_value(std::string &value) { - for (const auto &sub : this->substitutions_) { - // Compute lengths once per substitution (strlen is fast, called infrequently) - const size_t from_len = strlen(sub.from); - const size_t to_len = strlen(sub.to); +// Substitute — non-template helper +bool substitute_filter_apply(const Substitution *substitutions, size_t count, std::string &value) { + for (size_t i = 0; i < count; i++) { + const size_t from_len = strlen(substitutions[i].from); + const size_t to_len = strlen(substitutions[i].to); std::size_t pos = 0; - while ((pos = value.find(sub.from, pos, from_len)) != std::string::npos) { - value.replace(pos, from_len, sub.to, to_len); - // Advance past the replacement to avoid infinite loop when - // the replacement contains the search pattern (e.g., f -> foo) + while ((pos = value.find(substitutions[i].from, pos, from_len)) != std::string::npos) { + value.replace(pos, from_len, substitutions[i].to, to_len); pos += to_len; } } diff --git a/esphome/components/text_sensor/filter.h b/esphome/components/text_sensor/filter.h index 07832af9e2b..6db76dcb640 100644 --- a/esphome/components/text_sensor/filter.h +++ b/esphome/components/text_sensor/filter.h @@ -123,14 +123,20 @@ struct Substitution { const char *to; }; -/// A simple filter that replaces a substring with another substring -class SubstituteFilter : public Filter { +/// Non-template helper (implementation in filter.cpp) +bool substitute_filter_apply(const Substitution *substitutions, size_t count, std::string &value); + +/// A simple filter that replaces a substring with another substring. +/// N is set by code generation to match the exact number of substitutions configured in YAML. +template class SubstituteFilter : public Filter { public: - explicit SubstituteFilter(const std::initializer_list &substitutions); - bool new_value(std::string &value) override; + explicit SubstituteFilter(const std::initializer_list &substitutions) { + init_array_from(this->substitutions_, substitutions); + } + bool new_value(std::string &value) override { return substitute_filter_apply(this->substitutions_.data(), N, value); } protected: - FixedVector substitutions_; + std::array substitutions_{}; }; /// Non-template helper (implementation in filter.cpp)