From f2af1c53051f1550e32ba4366e5d3d327dc1865b Mon Sep 17 00:00:00 2001 From: Cody P Schafer Date: Mon, 3 Apr 2023 16:48:02 -0400 Subject: [PATCH 1/3] nvs: support iteration over namespace by index Users of the nvs API are likely to have `nvs_handle_t` in all cases, but not all of them carry around the partition name and namespace name (as they aren't needed after creating an `nvs_handle_t`). Allow this common case to use nvs iteration without them tracking additional data by using the `nvs_handle_t` to locate the namespace index and the partition name by introducing an alterate to `nvs_entry_find` called `nvs_entry_find_in_handle`, which operates similarly except that it is given a `nvs_handle_t` instead of partition and namespace strings. This is somewhat more limited than the `nvs_entry_find` API as one cannot examine all keys in a given partition. --- .../host_test/nvs_host_test/main/test_nvs.cpp | 24 +++++++++ components/nvs_flash/include/nvs.h | 51 +++++++++++++++++-- components/nvs_flash/src/nvs_api.cpp | 40 +++++++++++++++ .../nvs_flash/src/nvs_handle_simple.cpp | 26 +++++----- .../nvs_flash/src/nvs_handle_simple.hpp | 4 ++ components/nvs_flash/src/nvs_storage.cpp | 9 ++++ components/nvs_flash/src/nvs_storage.hpp | 2 + tools/ci/check_copyright_ignore.txt | 1 - 8 files changed, 138 insertions(+), 19 deletions(-) diff --git a/components/nvs_flash/host_test/nvs_host_test/main/test_nvs.cpp b/components/nvs_flash/host_test/nvs_host_test/main/test_nvs.cpp index 444f18c14b..f7f9c6f7f5 100644 --- a/components/nvs_flash/host_test/nvs_host_test/main/test_nvs.cpp +++ b/components/nvs_flash/host_test/nvs_host_test/main/test_nvs.cpp @@ -761,6 +761,21 @@ TEST_CASE("nvs iterators tests", "[nvs]") return count; }; + auto entry_count_handle = [](nvs_handle_t handle, nvs_type_t type)-> int { + int count = 0; + nvs_iterator_t it = nullptr; + esp_err_t res = nvs_entry_find_in_handle(handle, type, &it); + for (count = 0; res == ESP_OK; count++) + { + res = nvs_entry_next(&it); + } + CHECK(res == ESP_ERR_NVS_NOT_FOUND); // after finishing the loop or if no entry was found to begin with, + // res has to be ESP_ERR_NVS_NOT_FOUND or some internal error + // or programming error occurred + nvs_release_iterator(it); // unneccessary call but emphasizes the programming pattern + return count; + }; + SECTION("No partition found return ESP_ERR_NVS_NOT_FOUND") { CHECK(nvs_entry_find("", NULL, NVS_TYPE_ANY, &it) == ESP_ERR_NVS_NOT_FOUND); } @@ -805,6 +820,15 @@ TEST_CASE("nvs iterators tests", "[nvs]") CHECK(entry_count(NVS_DEFAULT_PART_NAME, NULL, NVS_TYPE_U64) == 1); } + + SECTION("Number of entries found for specified handle and type is correct") { + CHECK(entry_count_handle(handle_1, NVS_TYPE_ANY) == 11); + CHECK(entry_count_handle(handle_1, NVS_TYPE_I32) == 3); + CHECK(entry_count_handle(handle_2, NVS_TYPE_ANY) == 4); + CHECK(entry_count_handle(handle_2, NVS_TYPE_I32) == 2); + CHECK(entry_count_handle(handle_2, NVS_TYPE_U64) == 1); + } + SECTION("New entry is not created when existing key-value pair is set") { CHECK(entry_count(NVS_DEFAULT_PART_NAME, name_2, NVS_TYPE_ANY) == 4); TEST_ESP_OK(nvs_set_i32(handle_2, "value1", -222)); diff --git a/components/nvs_flash/include/nvs.h b/components/nvs_flash/include/nvs.h index e45a641343..39b13b3441 100644 --- a/components/nvs_flash/include/nvs.h +++ b/components/nvs_flash/include/nvs.h @@ -675,14 +675,52 @@ esp_err_t nvs_entry_find(const char *part_name, nvs_type_t type, nvs_iterator_t *output_iterator); +/** + * @brief Create an iterator to enumerate NVS entries based on a handle and type + * + * \code{c} + * // Example of listing all the key-value pairs of any type under specified handle (which defines a partition and namespace) + * nvs_iterator_t it = NULL; + * esp_err_t res = nvs_entry_find_in_handle(, NVS_TYPE_ANY, &it); + * while(res == ESP_OK) { + * nvs_entry_info_t info; + * nvs_entry_info(it, &info); // Can omit error check if parameters are guaranteed to be non-NULL + * printf("key '%s', type '%d' \n", info.key, info.type); + * res = nvs_entry_next(&it); + * } + * nvs_release_iterator(it); + * \endcode + * + * @param[in] handle Handle obtained from nvs_open function. + * + * @param[in] type One of nvs_type_t values. + * + * @param[out] output_iterator + * Set to a valid iterator to enumerate all the entries found. + * Set to NULL if no entry for specified criteria was found. + * If any other error except ESP_ERR_INVALID_ARG occurs, \c output_iterator is NULL, too. + * If ESP_ERR_INVALID_ARG occurs, \c output_iterator is not changed. + * If a valid iterator is obtained through this function, it has to be released + * using \c nvs_release_iterator when not used any more, unless ESP_ERR_INVALID_ARG is returned. + * + * @return + * - ESP_OK if no internal error or programming error occurred. + * - ESP_ERR_NVS_NOT_FOUND if no element of specified criteria has been found. + * - ESP_ERR_NO_MEM if memory has been exhausted during allocation of internal structures. + * - ESP_ERR_INVALID_ARG if any of the parameters is NULL. + * Note: don't release \c output_iterator in case ESP_ERR_INVALID_ARG has been returned + */ +esp_err_t nvs_entry_find_in_handle(nvs_handle_t c_handle, nvs_type_t type, nvs_iterator_t *output_iterator); + /** * @brief Advances the iterator to next item matching the iterator criteria. * * Note that any copies of the iterator will be invalid after this call. * - * @param[inout] iterator Iterator obtained from nvs_entry_find function. Must be non-NULL. - * If any error except ESP_ERR_INVALID_ARG occurs, \c iterator is set to NULL. - * If ESP_ERR_INVALID_ARG occurs, \c iterator is not changed. + * @param[inout] iterator Iterator obtained from nvs_entry_find or nvs_entry_find_in_handle + * function. Must be non-NULL. If any error except ESP_ERR_INVALID_ARG + * occurs, \c iterator is set to NULL. If ESP_ERR_INVALID_ARG occurs, \c + * iterator is not changed. * * @return * - ESP_OK if no internal error or programming error occurred. @@ -695,7 +733,8 @@ esp_err_t nvs_entry_next(nvs_iterator_t *iterator); /** * @brief Fills nvs_entry_info_t structure with information about entry pointed to by the iterator. * - * @param[in] iterator Iterator obtained from nvs_entry_find function. Must be non-NULL. + * @param[in] iterator Iterator obtained from nvs_entry_find or nvs_entry_find_in_handle + * function. Must be non-NULL. * * @param[out] out_info Structure to which entry information is copied. * @@ -708,7 +747,9 @@ esp_err_t nvs_entry_info(const nvs_iterator_t iterator, nvs_entry_info_t *out_in /** * @brief Release iterator * - * @param[in] iterator Release iterator obtained from nvs_entry_find function. NULL argument is allowed. + * @param[in] iterator Release iterator obtained from nvs_entry_find or + * nvs_entry_find_in_handle or nvs_entry_next function. NULL argument is + * allowed. * */ void nvs_release_iterator(nvs_iterator_t iterator); diff --git a/components/nvs_flash/src/nvs_api.cpp b/components/nvs_flash/src/nvs_api.cpp index beb9e868ea..8e76c5fa8a 100644 --- a/components/nvs_flash/src/nvs_api.cpp +++ b/components/nvs_flash/src/nvs_api.cpp @@ -786,6 +786,46 @@ extern "C" esp_err_t nvs_entry_find(const char *part_name, const char *namespace return ESP_OK; } +extern "C" esp_err_t nvs_entry_find_in_handle(nvs_handle_t c_handle, nvs_type_t type, nvs_iterator_t *output_iterator) +{ + if (output_iterator == nullptr) { + return ESP_ERR_INVALID_ARG; + } + + esp_err_t lock_result = Lock::init(); + if (lock_result != ESP_OK) { + *output_iterator = nullptr; + return lock_result; + } + + Lock lock; + nvs::Storage *pStorage; + NVSHandleSimple *handle; + + auto err = nvs_find_ns_handle(c_handle, &handle); + if (err != ESP_OK) { + *output_iterator = nullptr; + return err; + } + + pStorage = handle->get_storage(); + nvs_iterator_t it = create_iterator(pStorage, type); + if (it == nullptr) { + *output_iterator = nullptr; + return ESP_ERR_NO_MEM; + } + + bool entryFound = handle->findEntryNs(it); + if (!entryFound) { + free(it); + *output_iterator = nullptr; + return ESP_ERR_NVS_NOT_FOUND; + } + + *output_iterator = it; + return ESP_OK; +} + extern "C" esp_err_t nvs_entry_next(nvs_iterator_t *iterator) { if (iterator == nullptr) { diff --git a/components/nvs_flash/src/nvs_handle_simple.cpp b/components/nvs_flash/src/nvs_handle_simple.cpp index 348e197b7c..658c811aad 100644 --- a/components/nvs_flash/src/nvs_handle_simple.cpp +++ b/components/nvs_flash/src/nvs_handle_simple.cpp @@ -1,16 +1,8 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +/* + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ #include #include "nvs_handle.hpp" #include "nvs_partition_manager.hpp" @@ -126,6 +118,10 @@ bool NVSHandleSimple::findEntry(nvs_opaque_iterator_t* it, const char* name) { return mStoragePtr->findEntry(it, name); } +bool NVSHandleSimple::findEntryNs(nvs_opaque_iterator_t* it) { + return mStoragePtr->findEntryNs(it, mNsIndex); +} + bool NVSHandleSimple::nextEntry(nvs_opaque_iterator_t* it) { return mStoragePtr->nextEntry(it); } @@ -134,4 +130,8 @@ const char *NVSHandleSimple::get_partition_name() const { return mStoragePtr->getPartName(); } +Storage *NVSHandleSimple::get_storage() const { + return mStoragePtr; +} + } diff --git a/components/nvs_flash/src/nvs_handle_simple.hpp b/components/nvs_flash/src/nvs_handle_simple.hpp index ff0e93a3d6..fe93f9f706 100644 --- a/components/nvs_flash/src/nvs_handle_simple.hpp +++ b/components/nvs_flash/src/nvs_handle_simple.hpp @@ -69,10 +69,14 @@ public: bool findEntry(nvs_opaque_iterator_t *it, const char *name); + bool findEntryNs(nvs_opaque_iterator_t *it); + bool nextEntry(nvs_opaque_iterator_t *it); const char *get_partition_name() const; + Storage *get_storage() const; + private: /** * The underlying storage's object. diff --git a/components/nvs_flash/src/nvs_storage.cpp b/components/nvs_flash/src/nvs_storage.cpp index ccba2263f7..48c8a20fc0 100644 --- a/components/nvs_flash/src/nvs_storage.cpp +++ b/components/nvs_flash/src/nvs_storage.cpp @@ -782,6 +782,15 @@ bool Storage::findEntry(nvs_opaque_iterator_t* it, const char* namespace_name) return nextEntry(it); } +bool Storage::findEntryNs(nvs_opaque_iterator_t* it, uint8_t nsIndex) +{ + it->entryIndex = 0; + it->nsIndex = nsIndex; + it->page = mPageManager.begin(); + + return nextEntry(it); +} + inline bool isIterableItem(Item& item) { return (item.nsIndex != 0 && diff --git a/components/nvs_flash/src/nvs_storage.hpp b/components/nvs_flash/src/nvs_storage.hpp index c2df7dc470..eefed6c23f 100644 --- a/components/nvs_flash/src/nvs_storage.hpp +++ b/components/nvs_flash/src/nvs_storage.hpp @@ -127,6 +127,8 @@ public: bool findEntry(nvs_opaque_iterator_t*, const char* name); + bool findEntryNs(nvs_opaque_iterator_t*, uint8_t nsIndex); + bool nextEntry(nvs_opaque_iterator_t* it); protected: diff --git a/tools/ci/check_copyright_ignore.txt b/tools/ci/check_copyright_ignore.txt index 0b8933a300..fbf6e8ea21 100644 --- a/tools/ci/check_copyright_ignore.txt +++ b/tools/ci/check_copyright_ignore.txt @@ -596,7 +596,6 @@ components/nvs_flash/src/nvs_cxx_api.cpp components/nvs_flash/src/nvs_encrypted_partition.hpp components/nvs_flash/src/nvs_handle_locked.cpp components/nvs_flash/src/nvs_handle_locked.hpp -components/nvs_flash/src/nvs_handle_simple.cpp components/nvs_flash/src/nvs_item_hash_list.cpp components/nvs_flash/src/nvs_pagemanager.hpp components/nvs_flash/src/nvs_partition.cpp From 92ff04ebeaaceab059adb050cad13d796c077d87 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Thu, 13 Apr 2023 16:06:05 +0200 Subject: [PATCH 2/3] nvs: use 'handle' argument name for consistency --- components/nvs_flash/include/nvs.h | 2 +- components/nvs_flash/src/nvs_api.cpp | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/components/nvs_flash/include/nvs.h b/components/nvs_flash/include/nvs.h index 39b13b3441..d14ec9295a 100644 --- a/components/nvs_flash/include/nvs.h +++ b/components/nvs_flash/include/nvs.h @@ -710,7 +710,7 @@ esp_err_t nvs_entry_find(const char *part_name, * - ESP_ERR_INVALID_ARG if any of the parameters is NULL. * Note: don't release \c output_iterator in case ESP_ERR_INVALID_ARG has been returned */ -esp_err_t nvs_entry_find_in_handle(nvs_handle_t c_handle, nvs_type_t type, nvs_iterator_t *output_iterator); +esp_err_t nvs_entry_find_in_handle(nvs_handle_t handle, nvs_type_t type, nvs_iterator_t *output_iterator); /** * @brief Advances the iterator to next item matching the iterator criteria. diff --git a/components/nvs_flash/src/nvs_api.cpp b/components/nvs_flash/src/nvs_api.cpp index 8e76c5fa8a..6472a80819 100644 --- a/components/nvs_flash/src/nvs_api.cpp +++ b/components/nvs_flash/src/nvs_api.cpp @@ -786,7 +786,7 @@ extern "C" esp_err_t nvs_entry_find(const char *part_name, const char *namespace return ESP_OK; } -extern "C" esp_err_t nvs_entry_find_in_handle(nvs_handle_t c_handle, nvs_type_t type, nvs_iterator_t *output_iterator) +extern "C" esp_err_t nvs_entry_find_in_handle(nvs_handle_t handle, nvs_type_t type, nvs_iterator_t *output_iterator) { if (output_iterator == nullptr) { return ESP_ERR_INVALID_ARG; @@ -800,22 +800,22 @@ extern "C" esp_err_t nvs_entry_find_in_handle(nvs_handle_t c_handle, nvs_type_t Lock lock; nvs::Storage *pStorage; - NVSHandleSimple *handle; + NVSHandleSimple *handle_obj; - auto err = nvs_find_ns_handle(c_handle, &handle); + auto err = nvs_find_ns_handle(handle, &handle_obj); if (err != ESP_OK) { *output_iterator = nullptr; return err; } - pStorage = handle->get_storage(); + pStorage = handle_obj->get_storage(); nvs_iterator_t it = create_iterator(pStorage, type); if (it == nullptr) { *output_iterator = nullptr; return ESP_ERR_NO_MEM; } - bool entryFound = handle->findEntryNs(it); + bool entryFound = handle_obj->findEntryNs(it); if (!entryFound) { free(it); *output_iterator = nullptr; From f693b78f932e14e88cec60f54f161f88f6fe448c Mon Sep 17 00:00:00 2001 From: "radek.tandler" Date: Tue, 29 Aug 2023 16:05:02 +0200 Subject: [PATCH 3/3] feature(nvs_flash) : added support iteration over namespace by index - review changes --- components/nvs_flash/include/nvs.h | 3 ++- components/nvs_flash/src/nvs_storage.hpp | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/components/nvs_flash/include/nvs.h b/components/nvs_flash/include/nvs.h index d14ec9295a..49fe9b7d60 100644 --- a/components/nvs_flash/include/nvs.h +++ b/components/nvs_flash/include/nvs.h @@ -707,7 +707,8 @@ esp_err_t nvs_entry_find(const char *part_name, * - ESP_OK if no internal error or programming error occurred. * - ESP_ERR_NVS_NOT_FOUND if no element of specified criteria has been found. * - ESP_ERR_NO_MEM if memory has been exhausted during allocation of internal structures. - * - ESP_ERR_INVALID_ARG if any of the parameters is NULL. + * - ESP_ERR_NVS_INVALID_HANDLE if unknown handle was specified. + * - ESP_ERR_INVALID_ARG if output_iterator parameter is NULL. * Note: don't release \c output_iterator in case ESP_ERR_INVALID_ARG has been returned */ esp_err_t nvs_entry_find_in_handle(nvs_handle_t handle, nvs_type_t type, nvs_iterator_t *output_iterator); diff --git a/components/nvs_flash/src/nvs_storage.hpp b/components/nvs_flash/src/nvs_storage.hpp index eefed6c23f..9e64ac9bb5 100644 --- a/components/nvs_flash/src/nvs_storage.hpp +++ b/components/nvs_flash/src/nvs_storage.hpp @@ -125,9 +125,9 @@ public: esp_err_t calcEntriesInNamespace(uint8_t nsIndex, size_t& usedEntries); - bool findEntry(nvs_opaque_iterator_t*, const char* name); + bool findEntry(nvs_opaque_iterator_t* it, const char* name); - bool findEntryNs(nvs_opaque_iterator_t*, uint8_t nsIndex); + bool findEntryNs(nvs_opaque_iterator_t* it, uint8_t nsIndex); bool nextEntry(nvs_opaque_iterator_t* it);