From a760e2f76e6efaea28852728fb30f6cf8ae9a0ce Mon Sep 17 00:00:00 2001 From: Maschell Date: Sun, 18 Feb 2024 15:08:47 +0100 Subject: [PATCH] StorageAPI: Add different "modes" for reading binary and string data into an buffer (C++ API only) --- include/wups/storage.h | 101 ++++++++++++------- plugins/storage_test_plugin/src/test.cpp | 122 ++++++++++++++++++++--- 2 files changed, 176 insertions(+), 47 deletions(-) diff --git a/include/wups/storage.h b/include/wups/storage.h index f74741d..830f4bd 100644 --- a/include/wups/storage.h +++ b/include/wups/storage.h @@ -676,6 +676,15 @@ namespace WUPSStorageAPI { static constexpr WUPSStorageItemTypes value = WUPS_STORAGE_ITEM_BINARY; }; + /** + * @enum GetOptions + * Enumerates options for retrieving items from storage. + */ + enum GetOptions { + RESIZE_EXISTING_BUFFER, /**< Resizes the given buffer to the item size before loading an item from storage */ + USE_EXISTING_BUFFER, /**< Does not resize the buffer before loading an item from storage */ + }; + /** * @brief Gets a string representation of the specified storage status. * @param err The storage error status. @@ -864,11 +873,12 @@ namespace WUPSStorageAPI { * @param parent The parent storage item. Can be NULL to refer to the root of the storage. * @param key The key under which the value is stored. * @param[out] outValue A reference to the variable where the retrieved value will be stored. + * @param options additional options, see @GetOptions for more information * @return WUPSStorageError WUPS_STORAGE_ERROR_SUCCESS on success, otherwise an appropriate error code. * \see WUPSStorageAPI_GetItem */ template - inline WUPSStorageError GetEx(wups_storage_item parent, std::string_view key, T &outValue) noexcept; + inline WUPSStorageError GetEx(wups_storage_item parent, std::string_view key, T &outValue, GetOptions options = RESIZE_EXISTING_BUFFER) noexcept; /** * @brief Retrieves a signed 32-bit integer value from a storage item. @@ -878,12 +888,13 @@ namespace WUPSStorageAPI { * @param parent The parent storage item. Can be NULL to refer to the root of the storage. * @param key The key under which the value is stored. * @param[out] outValue A reference to the variable where the retrieved value will be stored. + * @param options unused parameter * @return WUPSStorageError Returns `WUPS_STORAGE_ERROR_SUCCESS` on success, otherwise an appropriate error code. * \see WUPSStorageAPI_GetS32 * \see WUPSStorageAPI_GetItem */ template<> - inline WUPSStorageError GetEx(wups_storage_item parent, std::string_view key, int32_t &outValue) noexcept { + inline WUPSStorageError GetEx(wups_storage_item parent, std::string_view key, int32_t &outValue, GetOptions options) noexcept { return WUPSStorageAPI_GetS32(parent, key.data(), &outValue); } @@ -895,12 +906,13 @@ namespace WUPSStorageAPI { * @param parent The parent storage item. Can be NULL to refer to the root of the storage. * @param key The key under which the value is stored. * @param[out] outValue A reference to the variable where the retrieved value will be stored. + * @param options unused parameter * @return WUPSStorageError Returns `WUPS_STORAGE_ERROR_SUCCESS` on success, otherwise an appropriate error code. * \see WUPSStorageAPI_GetS64 * \see WUPSStorageAPI_GetItem */ template<> - inline WUPSStorageError GetEx(wups_storage_item parent, std::string_view key, int64_t &outValue) noexcept { + inline WUPSStorageError GetEx(wups_storage_item parent, std::string_view key, int64_t &outValue, GetOptions options) noexcept { return WUPSStorageAPI_GetS64(parent, key.data(), &outValue); } @@ -912,12 +924,13 @@ namespace WUPSStorageAPI { * @param parent The parent storage item. Can be NULL to refer to the root of the storage. * @param key The key under which the value is stored. * @param[out] outValue A reference to the variable where the retrieved value will be stored. + * @param options unused parameter * @return WUPSStorageError Returns `WUPS_STORAGE_ERROR_SUCCESS` on success, otherwise an appropriate error code. * \see WUPSStorageAPI_GetU32 * \see WUPSStorageAPI_GetItem */ template<> - inline WUPSStorageError GetEx(wups_storage_item parent, std::string_view key, uint32_t &outValue) noexcept { + inline WUPSStorageError GetEx(wups_storage_item parent, std::string_view key, uint32_t &outValue, GetOptions options) noexcept { return WUPSStorageAPI_GetU32(parent, key.data(), &outValue); } @@ -929,12 +942,13 @@ namespace WUPSStorageAPI { * @param parent The parent storage item. Can be NULL to refer to the root of the storage. * @param key The key under which the value is stored. * @param[out] outValue A reference to the variable where the retrieved value will be stored. + * @param options unused parameter * @return WUPSStorageError Returns `WUPS_STORAGE_ERROR_SUCCESS` on success, otherwise an appropriate error code. * \see WUPSStorageAPI_GetU64 * \see WUPSStorageAPI_GetItem */ template<> - inline WUPSStorageError GetEx(wups_storage_item parent, std::string_view key, uint64_t &outValue) noexcept { + inline WUPSStorageError GetEx(wups_storage_item parent, std::string_view key, uint64_t &outValue, GetOptions options) noexcept { return WUPSStorageAPI_GetU64(parent, key.data(), &outValue); } @@ -946,12 +960,13 @@ namespace WUPSStorageAPI { * @param parent The parent storage item. Can be NULL to refer to the root of the storage. * @param key The key under which the value is stored. * @param[out] outValue A reference to the variable where the retrieved value will be stored. + * @param options unused parameter * @return WUPSStorageError Returns `WUPS_STORAGE_ERROR_SUCCESS` on success, otherwise an appropriate error code. * \see WUPSStorageAPI_GetBool * \see WUPSStorageAPI_GetItem */ template<> - inline WUPSStorageError GetEx(wups_storage_item parent, std::string_view key, bool &outValue) noexcept { + inline WUPSStorageError GetEx(wups_storage_item parent, std::string_view key, bool &outValue, GetOptions options) noexcept { return WUPSStorageAPI_GetBool(parent, key.data(), &outValue); } @@ -963,12 +978,13 @@ namespace WUPSStorageAPI { * @param parent The parent storage item. Can be NULL to refer to the root of the storage. * @param key The key under which the value is stored. * @param[out] outValue A reference to the variable where the retrieved value will be stored. + * @param options unused parameter * @return WUPSStorageError Returns `WUPS_STORAGE_ERROR_SUCCESS` on success, otherwise an appropriate error code. * \see WUPSStorageAPI_GetFloat * \see WUPSStorageAPI_GetItem */ template<> - inline WUPSStorageError GetEx(wups_storage_item parent, std::string_view key, float &outValue) noexcept { + inline WUPSStorageError GetEx(wups_storage_item parent, std::string_view key, float &outValue, GetOptions options) noexcept { return WUPSStorageAPI_GetFloat(parent, key.data(), &outValue); } @@ -980,12 +996,13 @@ namespace WUPSStorageAPI { * @param parent The parent storage item. Can be NULL to refer to the root of the storage. * @param key The key under which the value is stored. * @param[out] outValue A reference to the variable where the retrieved value will be stored. + * @param options unused parameter * @return WUPSStorageError Returns `WUPS_STORAGE_ERROR_SUCCESS` on success, otherwise an appropriate error code. * \see WUPSStorageAPI_GetFloat * \see WUPSStorageAPI_GetItem */ template<> - inline WUPSStorageError GetEx(wups_storage_item parent, std::string_view key, double &outValue) noexcept { + inline WUPSStorageError GetEx(wups_storage_item parent, std::string_view key, double &outValue, GetOptions options) noexcept { return WUPSStorageAPI_GetDouble(parent, key.data(), &outValue); } @@ -999,6 +1016,7 @@ namespace WUPSStorageAPI { * @param parent The parent storage item. Can be NULL to refer to the root of the storage. * @param key The key under which the value is stored. * @param[out] outValue A reference to the variable where the retrieved enum value will be stored. + * @param options unused parameter * @return WUPSStorageError Returns WUPS_STORAGE_ERROR_SUCCESS on success, otherwise an appropriate error code. * * @note This function assumes that the enum type is of size sizeof(uint32_t). @@ -1007,7 +1025,7 @@ namespace WUPSStorageAPI { * \see WUPSStorageAPI_GetItem */ template - inline WUPSStorageError GetEx(wups_storage_item parent, std::string_view key, T &outValue) noexcept { + inline WUPSStorageError GetEx(wups_storage_item parent, std::string_view key, T &outValue, GetOptions options) noexcept { static_assert(sizeof(T) == sizeof(uint32_t) && std::is_enum::value, "T must be an enum of size sizeof(uint32_t)"); return WUPSStorageAPI_GetU32(parent, key.data(), (uint32_t *) &outValue); } @@ -1015,32 +1033,38 @@ namespace WUPSStorageAPI { /** * @brief Retrieves a vector of uint8_t from storage. * - * This template specialization of the GetEx function retrieves a vector of uint8_t from the storage. - * It first checks if the outValue vector is empty (size == 0). - * If it is, it retrieves the size of the data and resizes the outValue vector to that size. Otherwise the existing vector (size) will the be used. - * Then, it retrieves the binary data from the storage using the WUPSStorageAPI_GetBinary function. - * If the retrieval is successful, the outValue vector is resized to the size of the retrieved data. - * If the retrieval fails, the outValue vector is resized to 0. + * This template specialization of the GetEx function retrieves a vector of uint8_t from the storage. \n + * Before getting the actual data, the buffer might be prepared depending on the given option parameter: \n + * - USE_EXISTING_BUFFER: the buffer will not be resized to the item size before getting the item. The caller has to make sure the buffer is big enough. \n + * - RESIZE_EXISTING_BUFFER: the buffer will be resized to the actual item size before getting the item. \n + * Then, it retrieves the binary data from the storage using the WUPSStorageAPI_GetBinary function. \n + * If the retrieval is successful, the outValue vector is resized to the size of the retrieved data. \n + * If the retrieval fails, the outValue vector is resized to 0. \n * * @param parent The parent storage item. Can be NULL to refer to the root of the storage. * @param key The key under which the data is stored. * @param[out] outValue A reference to the vector where the retrieved data will be stored. Will be resized to fit the data only if it's empty. + * @param options Defines how the given outValue buffer is used. * @return WUPSStorageError WUPS_STORAGE_ERROR_SUCCESS on success, otherwise an appropriate error code. * * @see WUPSStorageAPI_GetBinary * @see WUPSStorageAPI_GetItemSize */ template<> - inline WUPSStorageError GetEx>(wups_storage_item parent, std::string_view key, std::vector &outValue) noexcept { + inline WUPSStorageError GetEx>(wups_storage_item parent, std::string_view key, std::vector &outValue, GetOptions options) noexcept { uint32_t outSize = 0; - if (outValue.empty()) { - uint32_t resizeToSize = 0; - auto r = WUPSStorageAPI_GetItemSize(parent, key.data(), WUPS_STORAGE_ITEM_BINARY, &resizeToSize); - if (r == WUPS_STORAGE_ERROR_SUCCESS) { - outValue.resize(resizeToSize); - } else { - return r; + switch (options) { + case USE_EXISTING_BUFFER: + break; + case RESIZE_EXISTING_BUFFER: { + uint32_t resizeToSize = 0; + auto r = WUPSStorageAPI_GetItemSize(parent, key.data(), WUPS_STORAGE_ITEM_BINARY, &resizeToSize); + if (r == WUPS_STORAGE_ERROR_SUCCESS) { + outValue.resize(resizeToSize); + } else { + return r; + } } } @@ -1056,29 +1080,35 @@ namespace WUPSStorageAPI { /** * @brief Retrieves a string value from a storage item with the given key. * - * If the input `outValue` is empty, the function first retrieves the size of the stored data using `WUPSStorageAPI_GetItemSize`. + * Before getting the actual data, the string might be resized to fit the data depending on the given option parameter: \n + * - USE_EXISTING_BUFFER: the string will not be resized to the item size before getting the item. The caller has to make sure the string is already big enough. \n + * - RESIZE_EXISTING_BUFFER: the string will be resized to the actual string size before getting the item. \n * If the retrieval is successful, the function resizes `outValue` to the retrieved size. * Then, it calls `WUPSStorageAPI_GetString` to retrieve the string from the storage item. - * If the retrieval is successful, the function resizes `outValue` to remove the null terminator. * * @param parent The parent storage item. Can be `NULL` to refer to the root of the storage. * @param key The key under which the string is stored. * @param[out] outValue A reference to a `std::string` object where the retrieved string will be stored. Will be resized to fit the data only if it's empty. + * @param options Defines how the given outValue buffer is used. * @return WUPSStorageError WUPS_STORAGE_ERROR_SUCCESS on success, otherwise an appropriate error code. * * @see WUPSStorageAPI_GetBinary * @see WUPSStorageAPI_GetString */ template<> - inline WUPSStorageError GetEx(wups_storage_item parent, std::string_view key, std::string &outValue) noexcept { + inline WUPSStorageError GetEx(wups_storage_item parent, std::string_view key, std::string &outValue, GetOptions options) noexcept { uint32_t outSize = 0; - if (outValue.empty()) { - uint32_t resizeToSize = 0; - auto r = WUPSStorageAPI_GetItemSize(parent, key.data(), WUPS_STORAGE_ITEM_STRING, &resizeToSize); - if (r == WUPS_STORAGE_ERROR_SUCCESS) { - outValue.resize(resizeToSize); - } else { - return r; + switch (options) { + case USE_EXISTING_BUFFER: + break; + case RESIZE_EXISTING_BUFFER: { + uint32_t resizeToSize = 0; + auto r = WUPSStorageAPI_GetItemSize(parent, key.data(), WUPS_STORAGE_ITEM_STRING, &resizeToSize); + if (r == WUPS_STORAGE_ERROR_SUCCESS) { + outValue.resize(resizeToSize); + } else { + return r; + } } } @@ -1102,14 +1132,15 @@ namespace WUPSStorageAPI { * * @param key The key under which the value is stored. * @param[out] outValue A reference to the variable where the retrieved value will be stored. + * @param[optional] options Defines how a given buffer might be used, only relevant for certain item types (e.g. strings or buffer). Default value is RESIZE_EXISTING_BUFFER. * @return WUPSStorageError WUPS_STORAGE_ERROR_SUCCESS on success, otherwise an appropriate error code. * * @see WUPSStorageAPI_GetItem * @see GetEx */ template - inline WUPSStorageError Get(std::string_view key, T &outValue) noexcept { - return GetEx(nullptr, key.data(), outValue); + inline WUPSStorageError Get(std::string_view key, T &outValue, GetOptions options = RESIZE_EXISTING_BUFFER) noexcept { + return GetEx(nullptr, key.data(), outValue, options); } /** diff --git a/plugins/storage_test_plugin/src/test.cpp b/plugins/storage_test_plugin/src/test.cpp index 7733134..a194b59 100644 --- a/plugins/storage_test_plugin/src/test.cpp +++ b/plugins/storage_test_plugin/src/test.cpp @@ -1,8 +1,6 @@ #include "catch2/catch_test_macros.hpp" #include "utils/logger.h" #include "utils/utils.h" -#include -#include #include #include template @@ -199,7 +197,7 @@ TEST_CASE("Test getSize fails with other type") { REQUIRE(itemSize == 0); itemSize = 0; - res = WUPSStorageAPI::GetItemSize>(key, itemSize); + res = WUPSStorageAPI::GetItemSize>(key, itemSize); REQUIRE(res == WUPS_STORAGE_ERROR_UNEXPECTED_DATA_TYPE); REQUIRE(itemSize == 0); } @@ -218,7 +216,6 @@ TEST_CASE("Saving Base64 string as string works") { res = WUPSStorageAPI::ForceReloadStorage(); REQUIRE(res == WUPS_STORAGE_ERROR_SUCCESS); - uint32_t itemSize = 0; std::string retValue; res = WUPSStorageAPI::Get(key, retValue); REQUIRE(res == WUPS_STORAGE_ERROR_SUCCESS); @@ -627,7 +624,7 @@ TEST_CASE("Store string and load it as binary fails") { REQUIRE(res == WUPS_STORAGE_ERROR_UNEXPECTED_DATA_TYPE); } -TEST_CASE("Get binary fails if buffer is too small") { +TEST_CASE("USE_EXISTING_BUFFER: Get binary fails if buffer is too small") { auto res = WUPSStorageAPI::WipeStorage(); REQUIRE(res == WUPS_STORAGE_ERROR_SUCCESS); @@ -640,11 +637,11 @@ TEST_CASE("Get binary fails if buffer is too small") { std::vector readBinaryData; REQUIRE(!binaryData.empty()); readBinaryData.resize(binaryData.size() - 1); // Make the buffer just a bit to small - res = WUPSStorageAPI::Get>(itemName, readBinaryData); + res = WUPSStorageAPI::Get>(itemName, readBinaryData, WUPSStorageAPI::GetOptions::USE_EXISTING_BUFFER); REQUIRE(res == WUPS_STORAGE_ERROR_BUFFER_TOO_SMALL); } -TEST_CASE("Get binary works with exact buffer size") { +TEST_CASE("USE_EXISTING_BUFFER: Get binary works with exact buffer size") { auto res = WUPSStorageAPI::WipeStorage(); REQUIRE(res == WUPS_STORAGE_ERROR_SUCCESS); @@ -657,12 +654,62 @@ TEST_CASE("Get binary works with exact buffer size") { std::vector readBinaryData; REQUIRE(!binaryData.empty()); readBinaryData.resize(binaryData.size()); - res = WUPSStorageAPI::Get>(itemName, readBinaryData); + res = WUPSStorageAPI::Get>(itemName, readBinaryData, WUPSStorageAPI::GetOptions::USE_EXISTING_BUFFER); REQUIRE(res == WUPS_STORAGE_ERROR_SUCCESS); REQUIRE(isEqual(binaryData, readBinaryData)); } -TEST_CASE("Get string fails if buffer is too small") { +TEST_CASE("USE_EXISTING_BUFFER: Get binary works if buffer is too big") { + auto res = WUPSStorageAPI::WipeStorage(); + REQUIRE(res == WUPS_STORAGE_ERROR_SUCCESS); + + constexpr auto *itemName = "item"; + + std::vector binaryData = {8, 4, 5, 4, 12, 4, 2}; + res = WUPSStorageAPI::Store>(itemName, binaryData); + REQUIRE(res == WUPS_STORAGE_ERROR_SUCCESS); + + std::vector readBinaryData; + REQUIRE(!binaryData.empty()); + readBinaryData.resize(binaryData.size() * 2); + res = WUPSStorageAPI::Get>(itemName, readBinaryData, WUPSStorageAPI::GetOptions::USE_EXISTING_BUFFER); + REQUIRE(res == WUPS_STORAGE_ERROR_SUCCESS); + REQUIRE(isEqual(binaryData, readBinaryData)); +} + +void test_binary_get_RESIZE_EXISTING_BUFFER_generic(std::vector &binaryData, uint32_t buffer_size) { + auto res = WUPSStorageAPI::WipeStorage(); + REQUIRE(res == WUPS_STORAGE_ERROR_SUCCESS); + + constexpr auto *itemName = "item"; + + res = WUPSStorageAPI::Store>(itemName, binaryData); + REQUIRE(res == WUPS_STORAGE_ERROR_SUCCESS); + + std::vector readBinaryData; + REQUIRE(!binaryData.empty()); + readBinaryData.resize(buffer_size); + res = WUPSStorageAPI::Get>(itemName, readBinaryData, WUPSStorageAPI::GetOptions::RESIZE_EXISTING_BUFFER); + REQUIRE(res == WUPS_STORAGE_ERROR_SUCCESS); + REQUIRE(isEqual(binaryData, readBinaryData)); +} + +TEST_CASE("RESIZE_EXISTING_BUFFER: Get binary fails if buffer is empty") { + std::vector binaryData = {8, 4, 5, 4, 12, 4, 2}; + test_binary_get_RESIZE_EXISTING_BUFFER_generic(binaryData, 0); +} + +TEST_CASE("RESIZE_EXISTING_BUFFER: Get binary works with exact buffer size") { + std::vector binaryData = {8, 4, 5, 4, 12, 4, 2}; + test_binary_get_RESIZE_EXISTING_BUFFER_generic(binaryData, binaryData.size()); +} + +TEST_CASE("RESIZE_EXISTING_BUFFER: Get binary works if buffer is too big") { + std::vector binaryData = {8, 4, 5, 4, 12, 4, 2}; + test_binary_get_RESIZE_EXISTING_BUFFER_generic(binaryData, binaryData.size() * 2); +} + +TEST_CASE("USE_EXISTING_BUFFER: Get string fails if buffer is too small") { auto res = WUPSStorageAPI::WipeStorage(); REQUIRE(res == WUPS_STORAGE_ERROR_SUCCESS); @@ -676,11 +723,11 @@ TEST_CASE("Get string fails if buffer is too small") { REQUIRE(!strData.empty()); // Make the buffer just a bit to small readStr.resize(strData.length() - 1); - res = WUPSStorageAPI::Get(itemName, readStr); + res = WUPSStorageAPI::Get(itemName, readStr, WUPSStorageAPI::GetOptions::USE_EXISTING_BUFFER); REQUIRE(res == WUPS_STORAGE_ERROR_BUFFER_TOO_SMALL); } -TEST_CASE("Get string works with exact buffer size") { +TEST_CASE("USE_EXISTING_BUFFER: Get string works with exact buffer size") { auto res = WUPSStorageAPI::WipeStorage(); REQUIRE(res == WUPS_STORAGE_ERROR_SUCCESS); @@ -694,11 +741,62 @@ TEST_CASE("Get string works with exact buffer size") { REQUIRE(!strData.empty()); // We need to add one byte because of the null terminator readStr.resize(strData.length() + 1); - res = WUPSStorageAPI::Get(itemName, readStr); + res = WUPSStorageAPI::Get(itemName, readStr, WUPSStorageAPI::GetOptions::USE_EXISTING_BUFFER); REQUIRE(res == WUPS_STORAGE_ERROR_SUCCESS); REQUIRE(isEqual(strData, readStr)); } +TEST_CASE("USE_EXISTING_BUFFER: Get string works if buffer it too big.") { + auto res = WUPSStorageAPI::WipeStorage(); + REQUIRE(res == WUPS_STORAGE_ERROR_SUCCESS); + + constexpr auto *itemName = "item"; + + std::string strData = "Random string I just need for this test."; + res = WUPSStorageAPI::Store(itemName, strData); + REQUIRE(res == WUPS_STORAGE_ERROR_SUCCESS); + + std::string readStr; + REQUIRE(!strData.empty()); + // We need to add one byte because of the null terminator + readStr.resize(strData.length() * 2); + res = WUPSStorageAPI::Get(itemName, readStr, WUPSStorageAPI::GetOptions::USE_EXISTING_BUFFER); + REQUIRE(res == WUPS_STORAGE_ERROR_SUCCESS); + REQUIRE(isEqual(strData, readStr)); +} + +void test_string_get_RESIZE_EXISTING_BUFFER_generic(std::string &strData, uint32_t buffer_size) { + auto res = WUPSStorageAPI::WipeStorage(); + REQUIRE(res == WUPS_STORAGE_ERROR_SUCCESS); + + constexpr auto *itemName = "item"; + + res = WUPSStorageAPI::Store(itemName, strData); + REQUIRE(res == WUPS_STORAGE_ERROR_SUCCESS); + + std::string readStr; + REQUIRE(!strData.empty()); + + readStr.resize(buffer_size); + res = WUPSStorageAPI::Get(itemName, readStr, WUPSStorageAPI::GetOptions::RESIZE_EXISTING_BUFFER); + REQUIRE(res == WUPS_STORAGE_ERROR_SUCCESS); + REQUIRE(isEqual(strData, readStr)); +} + +TEST_CASE("RESIZE_EXISTING_BUFFER: Get string works if buffer is too small") { + std::string strData = "Random string I just need for this test."; + test_string_get_RESIZE_EXISTING_BUFFER_generic(strData, 0); +} + +TEST_CASE("RESIZE_EXISTING_BUFFER: Get string works if buffer is perfect") { + std::string strData = "Random string I just need for this test."; + test_string_get_RESIZE_EXISTING_BUFFER_generic(strData, strData.length() + 1); //add null terminator +} +TEST_CASE("RESIZE_EXISTING_BUFFER: Get string works if buffer is too big") { + std::string strData = "Random string I just need for this test."; + test_string_get_RESIZE_EXISTING_BUFFER_generic(strData, strData.length() * 2); +} + TEST_CASE("Create two sub-items, storing in first still works") { auto res = WUPSStorageAPI::WipeStorage(); REQUIRE(res == WUPS_STORAGE_ERROR_SUCCESS);