#pragma once #if !defined(MIJIN_IO_ARCHIVE_HPP_INCLUDED) #define MIJIN_IO_ARCHIVE_HPP_INCLUDED 1 #include #include #include "mijin/container/optional.hpp" #include "mijin/io/stream.hpp" namespace mijin { enum class ArchiveMode : std::uint8_t { READ, WRITE }; template class ArchiveBase { private: ArchiveMode mode_ = ArchiveMode::READ; StreamError error_ = StreamError::SUCCESS; TPointer stream_ = nullptr; public: ArchiveBase() MIJIN_NOEXCEPT = default; ArchiveBase(const ArchiveBase&) MIJIN_NOEXCEPT = default; ArchiveBase(ArchiveBase&&) noexcept = default; ArchiveBase(ArchiveMode mode, TPointer stream) MIJIN_NOEXCEPT : mode_(mode), stream_(std::move(stream)) {} ArchiveBase& operator=(const ArchiveBase&) MIJIN_NOEXCEPT = default; ArchiveBase& operator=(ArchiveBase&&) noexcept = default; [[nodiscard]] ArchiveMode getMode() const MIJIN_NOEXCEPT { return mode_; } [[nodiscard]] StreamError getError() const MIJIN_NOEXCEPT { return error_; } void setError(StreamError value) MIJIN_NOEXCEPT { error_ = value; } [[nodiscard]] Stream& getStream() MIJIN_NOEXCEPT { return *stream_; } }; using Archive = ArchiveBase; using OwningArchive = ArchiveBase>; template struct PODWrapper { TContent* content; }; template PODWrapper pod(TContent& content) MIJIN_NOEXCEPT { return PODWrapper(&content); } template requires (std::is_const_v) Task<> c_serialize(ArchiveBase& archive, TValue& value) MIJIN_NOEXCEPT { if (archive.getMode() != ArchiveMode::WRITE) { MIJIN_ERROR("Attempt to write to a const object."); archive.setError(StreamError::IO_ERROR); co_return; } co_await c_serialize(archive, const_cast&>(value)); } template Task<> c_serialize(ArchiveBase& archive, std::string& value) MIJIN_NOEXCEPT { if (archive.getError() != StreamError::SUCCESS) { co_return; } if (archive.getMode() == ArchiveMode::READ) { archive.setError(co_await archive.getStream().c_readBinaryString(value)); } else { archive.setError(co_await archive.getStream().c_writeBinaryString(value)); } co_return; } template requires (!std::is_const_v && (std::is_arithmetic_v || std::is_enum_v)) Task<> c_serialize(ArchiveBase& archive, TValue& value) MIJIN_NOEXCEPT { if (archive.getError() != StreamError::SUCCESS) { co_return; } if (archive.getMode() == ArchiveMode::READ) { archive.setError(co_await archive.getStream().c_read(value)); } else { archive.setError(co_await archive.getStream().c_write(value)); } co_return; } template Task<> c_serialize(ArchiveBase& archive, std::vector& value) MIJIN_NOEXCEPT { if (archive.getError() != StreamError::SUCCESS) { co_return; } std::uint64_t size = value.size(); co_await c_serialize(archive, size); if (archive.getError() != StreamError::SUCCESS) { co_return; } if (archive.getMode() == ArchiveMode::READ) { value.resize(size); } for (TElement& element : value) { co_await c_serialize(archive, element); if (archive.getError() != StreamError::SUCCESS) { co_return; } } } template Task<> c_serialize(ArchiveBase archive, Optional& value) MIJIN_NOEXCEPT { if (archive.getError() != StreamError::SUCCESS) { co_return; } std::uint8_t exists = !value.empty(); co_await c_serialize(archive, exists); if (exists) { if (archive.getMode() == ArchiveMode::READ) { TContent content; co_await c_serialize(archive, content); if (archive.getError() != StreamError::SUCCESS) { co_return; } value.emplace(std::move(content)); } else { co_await c_serialize(archive, *value); } } } template Task<> c_serialize(ArchiveBase& archive, PODWrapper value) MIJIN_NOEXCEPT { if (archive.getError() != StreamError::SUCCESS) { co_return; } if (archive.getMode() == ArchiveMode::READ) { if constexpr (std::is_const_v) { MIJIN_ERROR("Attempt to write to a const object."); archive.setError(StreamError::IO_ERROR); } else { archive.setError(co_await archive.getStream().c_read(*value.content)); } } else { archive.setError(co_await archive.getStream().c_write(*value.content)); } co_return; } } // namespace mijin #endif // !defined(MIJIN_IO_ARCHIVE_HPP_INCLUDED)