From e17cdffc78cb879bb7eac15e0e2b11ac66d8118b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 13 Oct 2025 15:04:40 -1000 Subject: [PATCH 1/4] merge --- esphome/core/helpers.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index e838c82f3e..4dcd44a574 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -217,6 +217,8 @@ template class FixedVector { reset_(); if (n > 0) { // Allocate raw memory without calling constructors + // sizeof(T) is correct here - when T is a pointer type, we want the pointer size + // NOLINTNEXTLINE(bugprone-sizeof-expression) data_ = static_cast(::operator new(n * sizeof(T))); capacity_ = n; } From d5ba16f13a57add5db95a600e8bb6d09a1493236 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 13 Oct 2025 15:22:52 -1000 Subject: [PATCH 2/4] merge --- esphome/core/helpers.h | 39 +++++++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index 4dcd44a574..6d7ae564e8 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -224,8 +224,14 @@ template class FixedVector { } } - // Clear the vector (reset size to 0, keep capacity) - void clear() { size_ = 0; } + // Clear the vector (destroy all elements, reset size to 0, keep capacity) + void clear() { + // Manually destroy all elements + for (size_t i = 0; i < size_; i++) { + data_[i].~T(); + } + size_ = 0; + } // Shrink capacity to fit current size (frees all memory) void shrink_to_fit() { @@ -244,17 +250,34 @@ template class FixedVector { } } - /// Construct element in place and return reference + /// Add element by move without bounds checking /// Caller must ensure sufficient capacity was allocated via init() - T &emplace_back() { + /// Silently ignores pushes beyond capacity (no exception or assertion) + void push_back(T &&value) { if (size_ < capacity_) { - return data_[size_++]; + // Use placement new to move-construct the object in pre-allocated memory + new (&data_[size_]) T(std::move(value)); + size_++; } - // Should never happen with proper init() - return last element to avoid crash - return data_[capacity_ - 1]; } - /// Access last element + /// Emplace element without bounds checking - constructs in-place + /// Caller must ensure sufficient capacity was allocated via init() + /// Returns reference to the newly constructed element + /// Silently ignores emplaces beyond capacity (returns reference to last element) + T &emplace_back() { + if (size_ < capacity_) { + // Use placement new to default-construct the object in pre-allocated memory + new (&data_[size_]) T(); + size_++; + return data_[size_ - 1]; + } + // Beyond capacity - return reference to last element to avoid crash + return data_[size_ - 1]; + } + + /// Access last element (no bounds checking - matches std::vector behavior) + /// Caller must ensure vector is not empty (size() > 0) T &back() { return data_[size_ - 1]; } const T &back() const { return data_[size_ - 1]; } From 9775274007f13edc65f6a3230daa464abbf7fc64 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 13 Oct 2025 15:25:47 -1000 Subject: [PATCH 3/4] preen --- esphome/core/helpers.h | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index 6d7ae564e8..349ed663ad 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -168,13 +168,17 @@ template class FixedVector { size_t size_{0}; size_t capacity_{0}; + // Helper to destroy all elements without freeing memory + void destroy_elements_() { + for (size_t i = 0; i < size_; i++) { + data_[i].~T(); + } + } + // Helper to destroy elements and free memory void cleanup_() { if (data_ != nullptr) { - // Manually destroy all elements - for (size_t i = 0; i < size_; i++) { - data_[i].~T(); - } + destroy_elements_(); // Free raw memory ::operator delete(data_); } @@ -226,10 +230,7 @@ template class FixedVector { // Clear the vector (destroy all elements, reset size to 0, keep capacity) void clear() { - // Manually destroy all elements - for (size_t i = 0; i < size_; i++) { - data_[i].~T(); - } + destroy_elements_(); size_ = 0; } From 2626a851fbf1e7feb85c1661373941359bd72f08 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 13 Oct 2025 15:30:18 -1000 Subject: [PATCH 4/4] cleanup --- esphome/core/helpers.h | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index 349ed663ad..3ca62a68cb 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -170,8 +170,11 @@ template class FixedVector { // Helper to destroy all elements without freeing memory void destroy_elements_() { - for (size_t i = 0; i < size_; i++) { - data_[i].~T(); + // Only call destructors for non-trivially destructible types + if constexpr (!std::is_trivially_destructible::value) { + for (size_t i = 0; i < size_; i++) { + data_[i].~T(); + } } } @@ -221,7 +224,7 @@ template class FixedVector { reset_(); if (n > 0) { // Allocate raw memory without calling constructors - // sizeof(T) is correct here - when T is a pointer type, we want the pointer size + // sizeof(T) is correct here for any type T (value types, pointers, etc.) // NOLINTNEXTLINE(bugprone-sizeof-expression) data_ = static_cast(::operator new(n * sizeof(T))); capacity_ = n; @@ -265,15 +268,11 @@ template class FixedVector { /// Emplace element without bounds checking - constructs in-place /// Caller must ensure sufficient capacity was allocated via init() /// Returns reference to the newly constructed element - /// Silently ignores emplaces beyond capacity (returns reference to last element) + /// NOTE: Caller MUST ensure size_ < capacity_ before calling T &emplace_back() { - if (size_ < capacity_) { - // Use placement new to default-construct the object in pre-allocated memory - new (&data_[size_]) T(); - size_++; - return data_[size_ - 1]; - } - // Beyond capacity - return reference to last element to avoid crash + // Use placement new to default-construct the object in pre-allocated memory + new (&data_[size_]) T(); + size_++; return data_[size_ - 1]; }