namespace yycc::carton::binstore { /** \page binstore Binary Settings Storage (Binstore) The binstore module provides a binary settings storage system that allows applications to persistently store and retrieve configuration settings in a binary format. It includes functionality for type-safe serialization and deserialization, setting management with unique tokens for access control, version control with migration strategies, and comprehensive error handling. \section binstore__overview Overview The binstore module consists of several key components: \li types: Basic types and error handling for the module \li serdes: Serialization/deserialization functionality for different data types \li setting: Management of settings with name-based lookup and token-based access \li configuration: Version and settings collection management \li storage: Main storage class for loading/saving settings to/from files or streams \section binstore__example Example Usage Here is a complete example showing how to use the binstore module: \code{.cpp} #include #include #include #include #include using namespace yycc::carton::binstore; using namespace yycc::patch::stream; enum class LogLevel : uint8_t { Debug, Info, Warning, Error }; int main() { // Create settings collection auto settings = setting::SettingCollection(); auto int_setting_token = settings.add_setting(setting::Setting(u8"max_connections")); auto float_setting_token = settings.add_setting(setting::Setting(u8"timeout")); auto string_setting_token = settings.add_setting(setting::Setting(u8"server_address")); auto bool_setting_token = settings.add_setting(setting::Setting(u8"enable_logging")); auto enum_setting_token = settings.add_setting(setting::Setting(u8"log_level")); // Create configuration with version 1 auto config = configuration::Configuration(1, std::move(settings)); // Create storage with the configuration auto storage = storage::Storage(std::move(config)); // Using appropriate SerDes types for different data types using IntSerDes = serdes::IntegralSerDes; using FloatSerDes = serdes::FloatingPointSerDes; using StringSerDes = serdes::StringSerDes; using BoolSerDes = serdes::BoolSerDes; // true as default value using EnumSerDes = serdes::EnumSerDes; // Set values storage.set_value(int_setting_token, 100); storage.set_value(float_setting_token, 2.5f); storage.set_value(string_setting_token, u8"localhost"); storage.set_value(bool_setting_token, true); storage.set_value(enum_setting_token, LogLevel::Debug); // Save to file if (auto result = storage.save_into_file("config.bin"); result.has_value()) { std::cout << "Configuration saved successfully" << std::endl; } else { std::cout << "Failed to save configuration" << std::endl; } // Load from file auto new_config = configuration::Configuration(1, setting::SettingCollection()); auto new_storage = storage::Storage(std::move(new_config)); if (auto result = new_storage.load_from_file("config.bin", storage::LoadStrategy::MigrateOld); result.has_value()) { std::cout << "Configuration loaded successfully" << std::endl; // Get values int32_t max_conn = new_storage.get_value(int_setting_token); float timeout = new_storage.get_value(float_setting_token); std::u8string addr = new_storage.get_value(string_setting_token); bool logging = new_storage.get_value(bool_setting_token); LogLevel level = new_storage.get_value(enum_setting_token); std::cout << "Max connections: " << max_conn << std::endl; std::cout << "Timeout: " << timeout << std::endl; std::cout << "Server address: " << addr << std::endl; std::cout << "Logging enabled: " << (logging ? "yes" : "no") << std::endl; std::cout << "Log level: " << static_cast(level) << std::endl; } else { std::cout << "Failed to load configuration" << std::endl; } return 0; } \endcode \section binstore__components Components \subsection binstore__settings Settings Management Settings are identified by unique names and accessed via tokens. The [SettingCollection](\ref setting::SettingCollection) manages a collection of settings and ensures no duplicates. \subsection binstore__configuration Configuration The [Configuration](\ref configuration::Configuration) class holds the version identifier and the collection of settings. Version control is crucial for handling configuration migration between application versions. \subsection binstore__storage Storage The [Storage](\ref storage::Storage) class is the main interface for setting/getting values and loading/saving configurations. It provides methods for both file-based and stream-based operations. \subsection binstore__serdes Serialization/Deserialization SerDes (Serializer/Deserializer) classes handle type-safe conversion between values and their binary representation. Built-in SerDes types include: \li Integral types ([IntegralSerDes](\ref serdes::IntegralSerDes)) \li Floating-point types ([FloatingPointSerDes](\ref serdes::FloatingPointSerDes)) \li String types ([StringSerDes](\ref serdes::StringSerDes)) \li Boolean types ([BoolSerDes](\ref serdes::BoolSerDes)) \li Enum types ([EnumSerDes](\ref serdes::EnumSerDes)) For some of them, you can specify value range and default value via template parameters. \section binstore__load_strategies Load Strategies The binstore module provides different strategies for handling version mismatches: \li [OnlyCurrent](\ref storage::LoadStrategy::OnlyCurrent): Only accept configurations with matching version \li [MigrateOld](\ref storage::LoadStrategy::MigrateOld): Accept matching and older versions, reject newer versions \li [AcceptAll](\ref storage::LoadStrategy::AcceptAll): Accept all versions (not recommended for production) \section binstore__custom_serdes Custom SerDes Custom SerDes (Serializer/Deserializer) can be created by implementing the \c SerDes concept. A valid SerDes must satisfy the following requirements: \li Have a type alias called \c ValueType indicating the corresponding setting type \li Have a member function called \c serialize that accepts a const reference of the setting data and returns \c ByteArray or \c std::nullopt if serialization fails. \li Have a member function called \c deserialize that converts \c ByteArray to the desired type or returns \c std::nullopt if deserialization fails. \li Have a member function called \c reset that returns a default \c ByteArray value. Here is an example of a custom SerDes for storing IPv4 addresses: \code{.cpp} #include #include struct IPv4Address { std::uint8_t octets[4]; IPv4Address() : octets{0, 0, 0, 0} {} IPv4Address(std::uint8_t a, std::uint8_t b, std::uint8_t c, std::uint8_t d) { octets[0] = a; octets[1] = b; octets[2] = c; octets[3] = d; } }; struct IPv4SerDes { using ValueType = IPv4Address; static constexpr size_t VALUE_SIZE = sizeof(IPv4Address); // 4 octets std::optional serialize(const ValueType& value) const { types::ByteArray ba; ba.resize_data(VALUE_SIZE); std::memcpy(ba.get_data_ptr(), value.octets, VALUE_SIZE); return ba; } std::optional deserialize(const types::ByteArray& ba) const { if (ba.get_data_size() != VALUE_SIZE) return std::nullopt; ValueType value; std::memcpy(value.octets, ba.get_data_ptr(), VALUE_SIZE); return value; } types::ByteArray reset() const { // Reset to local address ValueType default_value(127, 0, 0, 1); return this->serialize(default_value).value(); } }; \endcode To use the custom SerDes: \code{.cpp} // Add setting to collection auto ip_setting_token = settings.add_setting(setting::Setting(u8"server_ip")); // Use custom SerDes IPv4SerDes ip_serdes; storage.set_value(ip_setting_token, IPv4Address(192, 168, 1, 1)); // Retrieve value IPv4Address ip_addr = storage.get_value(ip_setting_token); \endcode */ }