From 93697287591464b9e1f42b27f2b3bb621ea987bb Mon Sep 17 00:00:00 2001 From: yyc12345 Date: Fri, 12 Dec 2025 23:23:02 +0800 Subject: [PATCH] feat: finish binstore storage --- src/yycc/carton/binstore/storage.cpp | 102 ++++++++++++++++++++++++++- src/yycc/carton/binstore/types.hpp | 7 +- 2 files changed, 103 insertions(+), 6 deletions(-) diff --git a/src/yycc/carton/binstore/storage.cpp b/src/yycc/carton/binstore/storage.cpp index 1726975..3941deb 100644 --- a/src/yycc/carton/binstore/storage.cpp +++ b/src/yycc/carton/binstore/storage.cpp @@ -13,6 +13,12 @@ namespace yycc::carton::binstore::storage { #pragma region Read and Write Helper Functions + static bool is_eof(std::istream& s) { + // Peek next value and check bit flag. + s.peek(); + return s.eof(); + } + static bool read_buffer(std::istream& s, void* buffer, size_t length) { // Cast length auto rv_length = SAFECAST::try_to(length); @@ -28,7 +34,7 @@ namespace yycc::carton::binstore::storage { } template - requires std::is_pod_v + requires std::is_pod_v static bool read_pod(std::istream& s, T& val) { return read_buffer(s, &val, sizeof(T)); } @@ -51,6 +57,22 @@ namespace yycc::carton::binstore::storage { return true; } + static bool read_u8string(std::istream& s, std::u8string& strl) { + size_t length = 0; + if (!read_pod(s, length)) return false; + + // Same reason for try-catch like ByteArray. + try { + strl.resize(length); + } catch (const std::exception&) { + return false; + } + // Read data into byte array. + read_buffer(s, strl.data(), length); + // Okey + return true; + } + static bool write_buffer(std::ostream& s, const void* buffer, size_t length) { // Cast length auto rv_length = SAFECAST::try_to(length); @@ -78,6 +100,14 @@ namespace yycc::carton::binstore::storage { return write_buffer(s, ba.get_data_ptr(), length); } + static bool write_u8string(std::ostream& s, const std::u8string_view& sv) { + // Write length header. + auto length = sv.length(); + if (!write_pod(s, length)) return false; + // Write body + return write_buffer(s, sv.data(), length); + } + #pragma endregion #pragma region Storage Class @@ -98,7 +128,56 @@ namespace yycc::carton::binstore::storage { } TYPES::BinstoreResult Storage::load(std::istream& s, LoadStrategy strategy) { - return TYPES::BinstoreResult(); + // Before loading, we need clear all stored raw data first. + this->raws.clear(); + + // Read identifier + TYPES::VersionIdentifier version; + if (!read_pod(s, version)) return std::unexpected(TYPES::BinstoreError::Io); + + // Check identifier with strategy + { + bool ok_for_read = false; + auto expected_version = this->cfg.get_version(); + switch (strategy) { + case LoadStrategy::OnlyCurrent: + ok_for_read = (version == expected_version); + break; + case LoadStrategy::MigrateOld: + ok_for_read = (version <= expected_version); + break; + case LoadStrategy::AcceptAll: + ok_for_read = true; + break; + } + if (!ok_for_read) return std::unexpected(TYPES::BinstoreError::BadVersion); + } + + // Read settings one by one. + const auto& settings = this->cfg.get_settings(); + while (!is_eof(s)) { + // Read setting name + std::u8string setting_name; + if (!read_u8string(s, setting_name)) return std::unexpected(TYPES::BinstoreError::Io); + + // Read setting body + TYPES::ByteArray ba; + if (!read_byte_array(s, ba)) return std::unexpected(TYPES::BinstoreError::Io); + + // Check whether there is such setting and its token. + auto token_finder = settings.find_name(setting_name); + // If no such name, skip this setting. + if (!token_finder.has_value()) continue; + auto token = token_finder.value(); + + // If there is duplicated entry, report error + if (this->raws.contains(token)) std::unexpected(TYPES::BinstoreError::DuplicatedAssign); + // Otherwise insert it + this->raws.emplace(token, std::move(ba)); + } + + // Okey + return {}; } TYPES::BinstoreResult Storage::save_into_file(const std::filesystem::path& fpath) { @@ -113,7 +192,24 @@ namespace yycc::carton::binstore::storage { } TYPES::BinstoreResult Storage::save(std::ostream& s) { - return TYPES::BinstoreResult(); + // Write version identifier + auto version = this->cfg.get_version(); + if (!write_pod(s, version)) return std::unexpected(TYPES::BinstoreError::Io); + + // Write settings one by one + const auto& settings = this->cfg.get_settings(); + for (const auto& [setting_token, setting_value] : this->raws) { + // Fetch setting name first + auto setting_name = settings.get_setting(setting_token).get_name(); + + // Write name + if (!write_u8string(s, setting_name)) return std::unexpected(TYPES::BinstoreError::Io); + // Write setting body + if (!write_byte_array(s, setting_value)) return std::unexpected(TYPES::BinstoreError::Io); + } + + // Okey + return {}; } bool Storage::has_setting(TYPES::Token token) const { diff --git a/src/yycc/carton/binstore/types.hpp b/src/yycc/carton/binstore/types.hpp index c308f2a..57d736b 100644 --- a/src/yycc/carton/binstore/types.hpp +++ b/src/yycc/carton/binstore/types.hpp @@ -8,9 +8,10 @@ namespace yycc::carton::binstore::types { /// @brief All possible error kind occurs in this module. enum class BinstoreError { - NoSuchSetting, ///< Given token is invalid for setting. - BadVersion, ///< The version provided in given file or IO is rejected by version strategy. - Io, ///< C++ IO error. + NoSuchSetting, ///< Given token is invalid for setting. + DuplicatedAssign, ///< Duplicated setting entry in data. + BadVersion, ///< The version provided in given file or IO is rejected by version strategy. + Io, ///< C++ IO error. }; /// @brief The result type used in this module.