at boundry

This commit is contained in:
J. Nick Koston
2025-12-15 09:06:33 -06:00
parent ee5a3088b9
commit 0bc81633bf
+35 -32
View File
@@ -14,18 +14,14 @@ 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;
// Buffer size for converting uint32_t to string: max "4294967295" (10 chars) + null terminator + 1 padding
static constexpr size_t KEY_BUFFER_SIZE = 12;
struct NVSData {
char key[PREF_KEY_SIZE];
uint32_t key;
std::unique_ptr<uint8_t[]> 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) {
this->data = std::make_unique<uint8_t[]>(size);
memcpy(this->data.get(), src, size);
@@ -37,27 +33,27 @@ static std::vector<NVSData> s_pending_save; // NOLINT(cppcoreguidelines-avoid-n
class ESP32PreferenceBackend : public ESPPreferenceBackend {
public:
char key[PREF_KEY_SIZE];
uint32_t key;
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 (strcmp(obj.key, this->key) == 0) {
if (obj.key == this->key) {
obj.set_data(data, len);
return true;
}
}
NVSData save{};
save.set_key(this->key);
save.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", this->key, len);
ESP_LOGVV(TAG, "s_pending_save: key: %" PRIu32 ", 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 (strcmp(obj.key, this->key) == 0) {
if (obj.key == this->key) {
if (obj.len != len) {
// size mismatch
return false;
@@ -67,22 +63,24 @@ class ESP32PreferenceBackend : public ESPPreferenceBackend {
}
}
char key_str[KEY_BUFFER_SIZE];
snprintf(key_str, sizeof(key_str), "%" PRIu32, this->key);
size_t actual_len;
esp_err_t err = nvs_get_blob(this->nvs_handle, this->key, nullptr, &actual_len);
esp_err_t err = nvs_get_blob(this->nvs_handle, key_str, nullptr, &actual_len);
if (err != 0) {
ESP_LOGV(TAG, "nvs_get_blob('%s'): %s - the key might not be set yet", this->key, esp_err_to_name(err));
ESP_LOGV(TAG, "nvs_get_blob('%s'): %s - the key might not be set yet", key_str, 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(this->nvs_handle, this->key, data, &len);
err = nvs_get_blob(this->nvs_handle, key_str, data, &len);
if (err != 0) {
ESP_LOGV(TAG, "nvs_get_blob('%s') failed: %s", this->key, esp_err_to_name(err));
ESP_LOGV(TAG, "nvs_get_blob('%s') failed: %s", key_str, esp_err_to_name(err));
return false;
} else {
ESP_LOGVV(TAG, "nvs_get_blob: key: %s, len: %zu", this->key, len);
ESP_LOGVV(TAG, "nvs_get_blob: key: %s, len: %zu", key_str, len);
}
return true;
}
@@ -109,13 +107,12 @@ class ESP32Preferences : public ESPPreferences {
}
}
ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash) override {
return make_preference(length, type);
return this->make_preference(length, type);
}
ESPPreferenceObject make_preference(size_t length, uint32_t type) override {
auto *pref = new ESP32PreferenceBackend(); // NOLINT(cppcoreguidelines-owning-memory)
pref->nvs_handle = this->nvs_handle;
snprintf(pref->key, sizeof(pref->key), "%" PRIu32, type);
pref->key = type;
return ESPPreferenceObject(pref);
}
@@ -128,25 +125,27 @@ 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;
char last_key[PREF_KEY_SIZE] = {};
uint32_t last_key = 0;
// 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);
ESP_LOGVV(TAG, "Checking if NVS data %" PRIu32 " 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);
char key_str[KEY_BUFFER_SIZE];
snprintf(key_str, sizeof(key_str), "%" PRIu32, save.key);
esp_err_t err = nvs_set_blob(this->nvs_handle, key_str, save.data.get(), save.len);
ESP_LOGV(TAG, "sync: key: %s, len: %zu", key_str, save.len);
if (err != 0) {
ESP_LOGV(TAG, "nvs_set_blob('%s', len=%zu) failed: %s", save.key, save.len, esp_err_to_name(err));
ESP_LOGV(TAG, "nvs_set_blob('%s', len=%zu) failed: %s", key_str, save.len, esp_err_to_name(err));
failed++;
last_err = err;
strncpy(last_key, save.key, sizeof(last_key) - 1);
last_key = save.key;
continue;
}
written++;
} else {
ESP_LOGV(TAG, "NVS data not changed skipping %s len=%zu", save.key, save.len);
ESP_LOGV(TAG, "NVS data not changed skipping %" PRIu32 " len=%zu", save.key, save.len);
cached++;
}
s_pending_save.erase(s_pending_save.begin() + i);
@@ -154,7 +153,8 @@ 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);
ESP_LOGE(TAG, "Writing %d items failed. Last error=%s for key=%" PRIu32, failed, esp_err_to_name(last_err),
last_key);
}
// note: commit on esp-idf currently is a no-op, nvs_set_blob always writes
@@ -167,10 +167,13 @@ class ESP32Preferences : public ESPPreferences {
return failed == 0;
}
bool is_changed(const uint32_t nvs_handle, const NVSData &to_save) {
char key_str[KEY_BUFFER_SIZE];
snprintf(key_str, sizeof(key_str), "%" PRIu32, to_save.key);
size_t actual_len;
esp_err_t err = nvs_get_blob(nvs_handle, to_save.key, nullptr, &actual_len);
esp_err_t err = nvs_get_blob(nvs_handle, key_str, nullptr, &actual_len);
if (err != 0) {
ESP_LOGV(TAG, "nvs_get_blob('%s'): %s - the key might not be set yet", to_save.key, esp_err_to_name(err));
ESP_LOGV(TAG, "nvs_get_blob('%s'): %s - the key might not be set yet", key_str, esp_err_to_name(err));
return true;
}
// Check size first before allocating memory
@@ -178,9 +181,9 @@ class ESP32Preferences : public ESPPreferences {
return true;
}
auto stored_data = std::make_unique<uint8_t[]>(actual_len);
err = nvs_get_blob(nvs_handle, to_save.key, stored_data.get(), &actual_len);
err = nvs_get_blob(nvs_handle, key_str, stored_data.get(), &actual_len);
if (err != 0) {
ESP_LOGV(TAG, "nvs_get_blob('%s') failed: %s", to_save.key, esp_err_to_name(err));
ESP_LOGV(TAG, "nvs_get_blob('%s') failed: %s", key_str, esp_err_to_name(err));
return true;
}
return memcmp(to_save.data.get(), stored_data.get(), to_save.len) != 0;