StorageAPI: Add different "modes" for reading binary and string data into an buffer (C++ API only)

This commit is contained in:
Maschell 2024-02-18 15:08:47 +01:00
parent f534559706
commit 6f53d72e2d
2 changed files with 176 additions and 47 deletions

View File

@ -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<typename T>
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<int32_t>(wups_storage_item parent, std::string_view key, int32_t &outValue) noexcept {
inline WUPSStorageError GetEx<int32_t>(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<int64_t>(wups_storage_item parent, std::string_view key, int64_t &outValue) noexcept {
inline WUPSStorageError GetEx<int64_t>(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<uint32_t>(wups_storage_item parent, std::string_view key, uint32_t &outValue) noexcept {
inline WUPSStorageError GetEx<uint32_t>(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<uint64_t>(wups_storage_item parent, std::string_view key, uint64_t &outValue) noexcept {
inline WUPSStorageError GetEx<uint64_t>(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<bool>(wups_storage_item parent, std::string_view key, bool &outValue) noexcept {
inline WUPSStorageError GetEx<bool>(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<float>(wups_storage_item parent, std::string_view key, float &outValue) noexcept {
inline WUPSStorageError GetEx<float>(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<double>(wups_storage_item parent, std::string_view key, double &outValue) noexcept {
inline WUPSStorageError GetEx<double>(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<typename T>
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<T>::value, "T must be an enum of size sizeof(uint32_t)");
return WUPSStorageAPI_GetU32(parent, key.data(), (uint32_t *) &outValue);
}
@ -1015,26 +1033,31 @@ 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<std::vector<uint8_t>>(wups_storage_item parent, std::string_view key, std::vector<uint8_t> &outValue) noexcept {
inline WUPSStorageError GetEx<std::vector<uint8_t>>(wups_storage_item parent, std::string_view key, std::vector<uint8_t> &outValue, GetOptions options) noexcept {
uint32_t outSize = 0;
if (outValue.empty()) {
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) {
@ -1043,6 +1066,7 @@ namespace WUPSStorageAPI {
return r;
}
}
}
auto res = WUPSStorageAPI_GetBinary(parent, key.data(), outValue.data(), outValue.size(), &outSize);
if (res == WUPS_STORAGE_ERROR_SUCCESS) {
@ -1056,23 +1080,28 @@ 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<std::string>(wups_storage_item parent, std::string_view key, std::string &outValue) noexcept {
inline WUPSStorageError GetEx<std::string>(wups_storage_item parent, std::string_view key, std::string &outValue, GetOptions options) noexcept {
uint32_t outSize = 0;
if (outValue.empty()) {
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) {
@ -1081,6 +1110,7 @@ namespace WUPSStorageAPI {
return r;
}
}
}
auto res = WUPSStorageAPI_GetString(parent, key.data(), outValue.data(), outValue.size(), &outSize);
if (res == WUPS_STORAGE_ERROR_SUCCESS) {
@ -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<typename T>
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);
}
/**

View File

@ -1,8 +1,6 @@
#include "catch2/catch_test_macros.hpp"
#include "utils/logger.h"
#include "utils/utils.h"
#include <coreinit/ios.h>
#include <sys/stat.h>
#include <vector>
#include <wups/storage.h>
template<typename T>
@ -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<uint8_t> readBinaryData;
REQUIRE(!binaryData.empty());
readBinaryData.resize(binaryData.size() - 1); // Make the buffer just a bit to small
res = WUPSStorageAPI::Get<std::vector<uint8_t>>(itemName, readBinaryData);
res = WUPSStorageAPI::Get<std::vector<uint8_t>>(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<uint8_t> readBinaryData;
REQUIRE(!binaryData.empty());
readBinaryData.resize(binaryData.size());
res = WUPSStorageAPI::Get<std::vector<uint8_t>>(itemName, readBinaryData);
res = WUPSStorageAPI::Get<std::vector<uint8_t>>(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<uint8_t> binaryData = {8, 4, 5, 4, 12, 4, 2};
res = WUPSStorageAPI::Store<std::vector<uint8_t>>(itemName, binaryData);
REQUIRE(res == WUPS_STORAGE_ERROR_SUCCESS);
std::vector<uint8_t> readBinaryData;
REQUIRE(!binaryData.empty());
readBinaryData.resize(binaryData.size() * 2);
res = WUPSStorageAPI::Get<std::vector<uint8_t>>(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<uint8_t> &binaryData, uint32_t buffer_size) {
auto res = WUPSStorageAPI::WipeStorage();
REQUIRE(res == WUPS_STORAGE_ERROR_SUCCESS);
constexpr auto *itemName = "item";
res = WUPSStorageAPI::Store<std::vector<uint8_t>>(itemName, binaryData);
REQUIRE(res == WUPS_STORAGE_ERROR_SUCCESS);
std::vector<uint8_t> readBinaryData;
REQUIRE(!binaryData.empty());
readBinaryData.resize(buffer_size);
res = WUPSStorageAPI::Get<std::vector<uint8_t>>(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<uint8_t> 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<uint8_t> 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<uint8_t> 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<std::string>(itemName, readStr);
res = WUPSStorageAPI::Get<std::string>(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<std::string>(itemName, readStr);
res = WUPSStorageAPI::Get<std::string>(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<std::string>(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<std::string>(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<std::string>(itemName, strData);
REQUIRE(res == WUPS_STORAGE_ERROR_SUCCESS);
std::string readStr;
REQUIRE(!strData.empty());
readStr.resize(buffer_size);
res = WUPSStorageAPI::Get<std::string>(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);