From 6c166c904c06ce08e4ff2f396a2e07671dffbf6b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 14 Dec 2025 22:06:42 -0600 Subject: [PATCH] [esp32] Replace std::string with char[12] for NVS preference keys --- esphome/components/esp32/preferences.cpp | 75 +++++++++++++----------- 1 file changed, 40 insertions(+), 35 deletions(-) diff --git a/esphome/components/esp32/preferences.cpp b/esphome/components/esp32/preferences.cpp index 7bdbb265ca..b0d2534832 100644 --- a/esphome/components/esp32/preferences.cpp +++ b/esphome/components/esp32/preferences.cpp @@ -4,26 +4,32 @@ #include "esphome/core/log.h" #include "esphome/core/preferences.h" #include +#include #include -#include -#include -#include #include +#include namespace esphome { namespace esp32 { static const char *const TAG = "esp32.preferences"; +// Max uint32_t is "4294967295" (10 chars) + null terminator + 1 padding +static constexpr size_t PREF_KEY_SIZE = 12; + struct NVSData { - std::string key; + char key[PREF_KEY_SIZE]; std::unique_ptr data; size_t len; + void set_key(const char *k) { + strncpy(this->key, k, sizeof(this->key) - 1); + this->key[sizeof(this->key) - 1] = '\0'; + } void set_data(const uint8_t *src, size_t size) { - data = std::make_unique(size); - memcpy(data.get(), src, size); - len = size; + this->data = std::make_unique(size); + memcpy(this->data.get(), src, size); + this->len = size; } }; @@ -31,27 +37,27 @@ static std::vector s_pending_save; // NOLINT(cppcoreguidelines-avoid-n class ESP32PreferenceBackend : public ESPPreferenceBackend { public: - std::string key; + char key[PREF_KEY_SIZE]; uint32_t nvs_handle; bool save(const uint8_t *data, size_t len) override { // try find in pending saves and update that for (auto &obj : s_pending_save) { - if (obj.key == key) { + if (strcmp(obj.key, this->key) == 0) { obj.set_data(data, len); return true; } } NVSData save{}; - save.key = key; + save.set_key(this->key); save.set_data(data, len); s_pending_save.emplace_back(std::move(save)); - ESP_LOGVV(TAG, "s_pending_save: key: %s, len: %zu", key.c_str(), len); + ESP_LOGVV(TAG, "s_pending_save: key: %s, len: %zu", this->key, len); return true; } bool load(uint8_t *data, size_t len) override { // try find in pending saves and load from that for (auto &obj : s_pending_save) { - if (obj.key == key) { + if (strcmp(obj.key, this->key) == 0) { if (obj.len != len) { // size mismatch return false; @@ -62,21 +68,21 @@ class ESP32PreferenceBackend : public ESPPreferenceBackend { } size_t actual_len; - esp_err_t err = nvs_get_blob(nvs_handle, key.c_str(), nullptr, &actual_len); + esp_err_t err = nvs_get_blob(this->nvs_handle, this->key, nullptr, &actual_len); if (err != 0) { - ESP_LOGV(TAG, "nvs_get_blob('%s'): %s - the key might not be set yet", key.c_str(), esp_err_to_name(err)); + ESP_LOGV(TAG, "nvs_get_blob('%s'): %s - the key might not be set yet", this->key, esp_err_to_name(err)); return false; } if (actual_len != len) { ESP_LOGVV(TAG, "NVS length does not match (%zu!=%zu)", actual_len, len); return false; } - err = nvs_get_blob(nvs_handle, key.c_str(), data, &len); + err = nvs_get_blob(this->nvs_handle, this->key, data, &len); if (err != 0) { - ESP_LOGV(TAG, "nvs_get_blob('%s') failed: %s", key.c_str(), esp_err_to_name(err)); + ESP_LOGV(TAG, "nvs_get_blob('%s') failed: %s", this->key, esp_err_to_name(err)); return false; } else { - ESP_LOGVV(TAG, "nvs_get_blob: key: %s, len: %zu", key.c_str(), len); + ESP_LOGVV(TAG, "nvs_get_blob: key: %s, len: %zu", this->key, len); } return true; } @@ -107,10 +113,10 @@ class ESP32Preferences : public ESPPreferences { } ESPPreferenceObject make_preference(size_t length, uint32_t type) override { auto *pref = new ESP32PreferenceBackend(); // NOLINT(cppcoreguidelines-owning-memory) - pref->nvs_handle = nvs_handle; + pref->nvs_handle = this->nvs_handle; - uint32_t keyval = type; - pref->key = str_sprintf("%" PRIu32, keyval); + auto [ptr, ec] = std::to_chars(pref->key, pref->key + sizeof(pref->key), type); + *ptr = '\0'; return ESPPreferenceObject(pref); } @@ -123,25 +129,25 @@ class ESP32Preferences : public ESPPreferences { // goal try write all pending saves even if one fails int cached = 0, written = 0, failed = 0; esp_err_t last_err = ESP_OK; - std::string last_key{}; + char last_key[PREF_KEY_SIZE] = {}; // go through vector from back to front (makes erase easier/more efficient) for (ssize_t i = s_pending_save.size() - 1; i >= 0; i--) { const auto &save = s_pending_save[i]; - ESP_LOGVV(TAG, "Checking if NVS data %s has changed", save.key.c_str()); - if (is_changed(nvs_handle, save)) { - esp_err_t err = nvs_set_blob(nvs_handle, save.key.c_str(), save.data.get(), save.len); - ESP_LOGV(TAG, "sync: key: %s, len: %zu", save.key.c_str(), save.len); + ESP_LOGVV(TAG, "Checking if NVS data %s has changed", save.key); + if (this->is_changed(this->nvs_handle, save)) { + esp_err_t err = nvs_set_blob(this->nvs_handle, save.key, save.data.get(), save.len); + ESP_LOGV(TAG, "sync: key: %s, len: %zu", save.key, save.len); if (err != 0) { - ESP_LOGV(TAG, "nvs_set_blob('%s', len=%zu) failed: %s", save.key.c_str(), save.len, esp_err_to_name(err)); + ESP_LOGV(TAG, "nvs_set_blob('%s', len=%zu) failed: %s", save.key, save.len, esp_err_to_name(err)); failed++; last_err = err; - last_key = save.key; + strncpy(last_key, save.key, sizeof(last_key) - 1); continue; } written++; } else { - ESP_LOGV(TAG, "NVS data not changed skipping %s len=%zu", save.key.c_str(), save.len); + ESP_LOGV(TAG, "NVS data not changed skipping %s len=%zu", save.key, save.len); cached++; } s_pending_save.erase(s_pending_save.begin() + i); @@ -149,12 +155,11 @@ class ESP32Preferences : public ESPPreferences { ESP_LOGD(TAG, "Writing %d items: %d cached, %d written, %d failed", cached + written + failed, cached, written, failed); if (failed > 0) { - ESP_LOGE(TAG, "Writing %d items failed. Last error=%s for key=%s", failed, esp_err_to_name(last_err), - last_key.c_str()); + ESP_LOGE(TAG, "Writing %d items failed. Last error=%s for key=%s", failed, esp_err_to_name(last_err), last_key); } // note: commit on esp-idf currently is a no-op, nvs_set_blob always writes - esp_err_t err = nvs_commit(nvs_handle); + esp_err_t err = nvs_commit(this->nvs_handle); if (err != 0) { ESP_LOGV(TAG, "nvs_commit() failed: %s", esp_err_to_name(err)); return false; @@ -164,9 +169,9 @@ class ESP32Preferences : public ESPPreferences { } bool is_changed(const uint32_t nvs_handle, const NVSData &to_save) { size_t actual_len; - esp_err_t err = nvs_get_blob(nvs_handle, to_save.key.c_str(), nullptr, &actual_len); + esp_err_t err = nvs_get_blob(nvs_handle, to_save.key, nullptr, &actual_len); if (err != 0) { - ESP_LOGV(TAG, "nvs_get_blob('%s'): %s - the key might not be set yet", to_save.key.c_str(), esp_err_to_name(err)); + ESP_LOGV(TAG, "nvs_get_blob('%s'): %s - the key might not be set yet", to_save.key, esp_err_to_name(err)); return true; } // Check size first before allocating memory @@ -174,9 +179,9 @@ class ESP32Preferences : public ESPPreferences { return true; } auto stored_data = std::make_unique(actual_len); - err = nvs_get_blob(nvs_handle, to_save.key.c_str(), stored_data.get(), &actual_len); + err = nvs_get_blob(nvs_handle, to_save.key, stored_data.get(), &actual_len); if (err != 0) { - ESP_LOGV(TAG, "nvs_get_blob('%s') failed: %s", to_save.key.c_str(), esp_err_to_name(err)); + ESP_LOGV(TAG, "nvs_get_blob('%s') failed: %s", to_save.key, esp_err_to_name(err)); return true; } return memcmp(to_save.data.get(), stored_data.get(), to_save.len) != 0;