From cef546c8d806467ade66b83fe312e28493a3b6e2 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 27 Mar 2026 15:26:03 -1000 Subject: [PATCH 1/4] [binary_sensor] Move deleted copy/move to public (clang-tidy modernize-use-equals-delete) --- esphome/components/binary_sensor/filter.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/binary_sensor/filter.h b/esphome/components/binary_sensor/filter.h index f8434a57832..0c660477696 100644 --- a/esphome/components/binary_sensor/filter.h +++ b/esphome/components/binary_sensor/filter.h @@ -94,11 +94,11 @@ class AutorepeatFilterBase : public Filter, public Component { public: optional new_value(bool value) override; float get_setup_priority() const override; + AutorepeatFilterBase(const AutorepeatFilterBase &) = delete; + AutorepeatFilterBase &operator=(const AutorepeatFilterBase &) = delete; protected: AutorepeatFilterBase() = default; - AutorepeatFilterBase(const AutorepeatFilterBase &) = delete; - AutorepeatFilterBase &operator=(const AutorepeatFilterBase &) = delete; void next_timing_(); void next_value_(bool val); From f8138eef7778ecfb5b7cc0e000e1484735ea2f3b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 27 Mar 2026 15:48:07 -1000 Subject: [PATCH 2/4] [binary_sensor] Replace truncation guard with ESPHOME_DEBUG_ASSERT --- esphome/components/binary_sensor/filter.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/esphome/components/binary_sensor/filter.h b/esphome/components/binary_sensor/filter.h index 0c660477696..8d52e5b8962 100644 --- a/esphome/components/binary_sensor/filter.h +++ b/esphome/components/binary_sensor/filter.h @@ -114,10 +114,9 @@ template class AutorepeatFilter : public AutorepeatFilterBase { explicit AutorepeatFilter(std::initializer_list timings) { size_t i = 0; for (const auto &t : timings) { - if (i >= N) - break; this->timings_storage_[i++] = t; } + ESPHOME_DEBUG_ASSERT(i == N); this->timings_ = this->timings_storage_.data(); this->timings_count_ = N; } From f9c2d60b7dade0fd7237c09dcd7bc56e7b20b56a Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 27 Mar 2026 15:54:12 -1000 Subject: [PATCH 3/4] [binary_sensor] Assert size before writing, not after --- esphome/components/binary_sensor/filter.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/binary_sensor/filter.h b/esphome/components/binary_sensor/filter.h index 8d52e5b8962..d308c375b03 100644 --- a/esphome/components/binary_sensor/filter.h +++ b/esphome/components/binary_sensor/filter.h @@ -112,11 +112,11 @@ class AutorepeatFilterBase : public Filter, public Component { template class AutorepeatFilter : public AutorepeatFilterBase { public: explicit AutorepeatFilter(std::initializer_list timings) { + ESPHOME_DEBUG_ASSERT(timings.size() == N); size_t i = 0; for (const auto &t : timings) { this->timings_storage_[i++] = t; } - ESPHOME_DEBUG_ASSERT(i == N); this->timings_ = this->timings_storage_.data(); this->timings_count_ = N; } From 8b42629e3ebbe7df8471d54a6052a0adddbd0b5a Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 27 Mar 2026 16:14:53 -1000 Subject: [PATCH 4/4] Use init_array_from helper for optimal codegen Uses memcpy for trivially copyable types, element-wise copy otherwise. ESPHOME_DEBUG_ASSERT catches size mismatches in integration tests. --- esphome/components/binary_sensor/filter.h | 6 +----- esphome/core/helpers.h | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/esphome/components/binary_sensor/filter.h b/esphome/components/binary_sensor/filter.h index d308c375b03..37c6bf0092c 100644 --- a/esphome/components/binary_sensor/filter.h +++ b/esphome/components/binary_sensor/filter.h @@ -112,11 +112,7 @@ class AutorepeatFilterBase : public Filter, public Component { template class AutorepeatFilter : public AutorepeatFilterBase { public: explicit AutorepeatFilter(std::initializer_list timings) { - ESPHOME_DEBUG_ASSERT(timings.size() == N); - size_t i = 0; - for (const auto &t : timings) { - this->timings_storage_[i++] = t; - } + init_array_from(this->timings_storage_, timings); this->timings_ = this->timings_storage_.data(); this->timings_count_ = N; } diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index 82c6b3833ce..51feaa57c50 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -497,6 +497,21 @@ template::max()> index_type capacity_{0}; }; +/// Initialize a std::array from an initializer_list. Uses memcpy for trivially copyable types (optimal codegen), +/// falls back to element-wise copy for non-trivially copyable types (e.g. TemplatableValue). +/// N is set by code generation; ESPHOME_DEBUG_ASSERT catches mismatches in debug/integration tests. +template inline void init_array_from(std::array &dest, std::initializer_list src) { + ESPHOME_DEBUG_ASSERT(src.size() == N); + if constexpr (std::is_trivially_copyable_v) { + __builtin_memcpy(dest.data(), src.begin(), N * sizeof(T)); + } else { + size_t i = 0; + for (const auto &v : src) { + dest[i++] = v; + } + } +} + /// Fixed-capacity vector - allocates once at runtime, never reallocates /// This avoids std::vector template overhead (_M_realloc_insert, _M_default_append) /// when size is known at initialization but not at compile time