1
0
mirror of https://github.com/esphome/esphome.git synced 2025-10-28 21:53:48 +00:00

Merge branch 'integration' into memory_api

This commit is contained in:
J. Nick Koston
2025-10-13 14:20:29 -10:00
8 changed files with 102 additions and 22 deletions

View File

@@ -554,7 +554,7 @@ void WiFiComponent::start_scanning() {
// Using insertion sort instead of std::stable_sort saves flash memory // Using insertion sort instead of std::stable_sort saves flash memory
// by avoiding template instantiations (std::rotate, std::stable_sort, lambdas) // by avoiding template instantiations (std::rotate, std::stable_sort, lambdas)
// IMPORTANT: This sort is stable (preserves relative order of equal elements) // IMPORTANT: This sort is stable (preserves relative order of equal elements)
static void insertion_sort_scan_results(std::vector<WiFiScanResult> &results) { template<typename VectorType> static void insertion_sort_scan_results(VectorType &results) {
const size_t size = results.size(); const size_t size = results.size();
for (size_t i = 1; i < size; i++) { for (size_t i = 1; i < size; i++) {
// Make a copy to avoid issues with move semantics during comparison // Make a copy to avoid issues with move semantics during comparison

View File

@@ -121,6 +121,14 @@ struct EAPAuth {
using bssid_t = std::array<uint8_t, 6>; using bssid_t = std::array<uint8_t, 6>;
// Use std::vector for RP2040 since scan count is unknown (callback-based)
// Use FixedVector for other platforms where count is queried first
#ifdef USE_RP2040
template<typename T> using wifi_scan_vector_t = std::vector<T>;
#else
template<typename T> using wifi_scan_vector_t = FixedVector<T>;
#endif
class WiFiAP { class WiFiAP {
public: public:
void set_ssid(const std::string &ssid); void set_ssid(const std::string &ssid);
@@ -278,7 +286,7 @@ class WiFiComponent : public Component {
std::string get_use_address() const; std::string get_use_address() const;
void set_use_address(const std::string &use_address); void set_use_address(const std::string &use_address);
const std::vector<WiFiScanResult> &get_scan_result() const { return scan_result_; } const wifi_scan_vector_t<WiFiScanResult> &get_scan_result() const { return scan_result_; }
network::IPAddress wifi_soft_ap_ip(); network::IPAddress wifi_soft_ap_ip();
@@ -386,7 +394,7 @@ class WiFiComponent : public Component {
std::string use_address_; std::string use_address_;
std::vector<WiFiAP> sta_; std::vector<WiFiAP> sta_;
std::vector<WiFiSTAPriority> sta_priorities_; std::vector<WiFiSTAPriority> sta_priorities_;
std::vector<WiFiScanResult> scan_result_; wifi_scan_vector_t<WiFiScanResult> scan_result_;
WiFiAP selected_ap_; WiFiAP selected_ap_;
WiFiAP ap_; WiFiAP ap_;
optional<float> output_power_; optional<float> output_power_;

View File

@@ -696,7 +696,15 @@ void WiFiComponent::wifi_scan_done_callback_(void *arg, STATUS status) {
this->retry_connect(); this->retry_connect();
return; return;
} }
// Count the number of results first
auto *head = reinterpret_cast<bss_info *>(arg); auto *head = reinterpret_cast<bss_info *>(arg);
size_t count = 0;
for (bss_info *it = head; it != nullptr; it = STAILQ_NEXT(it, next)) {
count++;
}
this->scan_result_.init(count);
for (bss_info *it = head; it != nullptr; it = STAILQ_NEXT(it, next)) { for (bss_info *it = head; it != nullptr; it = STAILQ_NEXT(it, next)) {
WiFiScanResult res({it->bssid[0], it->bssid[1], it->bssid[2], it->bssid[3], it->bssid[4], it->bssid[5]}, WiFiScanResult res({it->bssid[0], it->bssid[1], it->bssid[2], it->bssid[3], it->bssid[4], it->bssid[5]},
std::string(reinterpret_cast<char *>(it->ssid), it->ssid_len), it->channel, it->rssi, std::string(reinterpret_cast<char *>(it->ssid), it->ssid_len), it->channel, it->rssi,

View File

@@ -784,7 +784,7 @@ void WiFiComponent::wifi_process_event_(IDFWiFiEvent *data) {
} }
records.resize(number); records.resize(number);
scan_result_.reserve(number); scan_result_.init(number);
for (int i = 0; i < number; i++) { for (int i = 0; i < number; i++) {
auto &record = records[i]; auto &record = records[i];
bssid_t bssid; bssid_t bssid;

View File

@@ -411,7 +411,7 @@ void WiFiComponent::wifi_scan_done_callback_() {
if (num < 0) if (num < 0)
return; return;
this->scan_result_.reserve(static_cast<unsigned int>(num)); this->scan_result_.init(static_cast<unsigned int>(num));
for (int i = 0; i < num; i++) { for (int i = 0; i < num; i++) {
String ssid = WiFi.SSID(i); String ssid = WiFi.SSID(i);
wifi_auth_mode_t authmode = WiFi.encryptionType(i); wifi_auth_mode_t authmode = WiFi.encryptionType(i);

View File

@@ -171,26 +171,67 @@ template<typename T> class FixedVector {
size_t size_{0}; size_t size_{0};
size_t capacity_{0}; size_t capacity_{0};
public: // Helper to destroy elements and free memory
FixedVector() = default; void cleanup_() {
~FixedVector() {
if (data_ != nullptr) { if (data_ != nullptr) {
delete[] data_; // Manually destroy all elements
for (size_t i = 0; i < size_; i++) {
data_[i].~T();
}
// Free raw memory
::operator delete(data_);
} }
} }
// Disable copy to avoid accidental copies // Helper to reset pointers after cleanup
FixedVector(const FixedVector &) = delete; void reset_() {
FixedVector &operator=(const FixedVector &) = delete; data_ = nullptr;
capacity_ = 0;
size_ = 0;
}
// Allocate capacity - can only be called once on empty vector public:
void init(size_t n) { FixedVector() = default;
if (data_ == nullptr && n > 0) {
data_ = new T[n]; ~FixedVector() { cleanup_(); }
capacity_ = n;
size_ = 0; // Enable move semantics for use in containers
FixedVector(FixedVector &&other) noexcept : data_(other.data_), size_(other.size_), capacity_(other.capacity_) {
other.reset_();
}
FixedVector &operator=(FixedVector &&other) noexcept {
if (this != &other) {
// Delete our current data
cleanup_();
// Take ownership of other's data
data_ = other.data_;
size_ = other.size_;
capacity_ = other.capacity_;
// Leave other in valid empty state
other.reset_();
} }
return *this;
}
// Allocate capacity - can be called multiple times to reinit
void init(size_t n) {
cleanup_();
reset_();
if (n > 0) {
// Allocate raw memory without calling constructors
data_ = static_cast<T *>(::operator new(n * sizeof(T)));
capacity_ = n;
}
}
// Clear the vector (reset size to 0, keep capacity)
void clear() { size_ = 0; }
// Shrink capacity to fit current size (frees all memory)
void shrink_to_fit() {
cleanup_();
reset_();
} }
/// Add element without bounds checking /// Add element without bounds checking
@@ -198,16 +239,39 @@ template<typename T> class FixedVector {
/// Silently ignores pushes beyond capacity (no exception or assertion) /// Silently ignores pushes beyond capacity (no exception or assertion)
void push_back(const T &value) { void push_back(const T &value) {
if (size_ < capacity_) { if (size_ < capacity_) {
data_[size_++] = value; // Use placement new to construct the object in pre-allocated memory
new (&data_[size_]) T(value);
size_++;
} }
} }
/// Construct element in place and return reference
/// Caller must ensure sufficient capacity was allocated via init()
T &emplace_back() {
if (size_ < capacity_) {
return data_[size_++];
}
// Should never happen with proper init() - return last element to avoid crash
return data_[capacity_ - 1];
}
/// Access last element
T &back() { return data_[size_ - 1]; }
const T &back() const { return data_[size_ - 1]; }
size_t size() const { return size_; } size_t size() const { return size_; }
bool empty() const { return size_ == 0; }
/// Access element without bounds checking (matches std::vector behavior) /// Access element without bounds checking (matches std::vector behavior)
/// Caller must ensure index is valid (i < size()) /// Caller must ensure index is valid (i < size())
T &operator[](size_t i) { return data_[i]; } T &operator[](size_t i) { return data_[i]; }
const T &operator[](size_t i) const { return data_[i]; } const T &operator[](size_t i) const { return data_[i]; }
// Iterator support for range-based for loops
T *begin() { return data_; }
T *end() { return data_ + size_; }
const T *begin() const { return data_; }
const T *end() const { return data_ + size_; }
}; };
///@} ///@}

View File

@@ -12,7 +12,7 @@ platformio==6.1.18 # When updating platformio, also update /docker/Dockerfile
esptool==5.1.0 esptool==5.1.0
click==8.1.7 click==8.1.7
esphome-dashboard==20251013.0 esphome-dashboard==20251013.0
aioesphomeapi==41.14.0 aioesphomeapi==41.16.0
zeroconf==0.148.0 zeroconf==0.148.0
puremagic==1.30 puremagic==1.30
ruamel.yaml==0.18.15 # dashboard_import ruamel.yaml==0.18.15 # dashboard_import

View File

@@ -1,4 +1,4 @@
pylint==3.3.9 pylint==4.0.0
flake8==7.3.0 # also change in .pre-commit-config.yaml when updating flake8==7.3.0 # also change in .pre-commit-config.yaml when updating
ruff==0.14.0 # also change in .pre-commit-config.yaml when updating ruff==0.14.0 # also change in .pre-commit-config.yaml when updating
pyupgrade==3.21.0 # also change in .pre-commit-config.yaml when updating pyupgrade==3.21.0 # also change in .pre-commit-config.yaml when updating