diff --git a/source/mijin/io/archive.hpp b/source/mijin/io/archive.hpp new file mode 100644 index 0000000..a522ae9 --- /dev/null +++ b/source/mijin/io/archive.hpp @@ -0,0 +1,160 @@ + +#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 requires (std::is_const_v) +Task<> c_serialize(ArchiveBase& archive, TValue& value) 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) 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) 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) 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) 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); + } + } +} +} // namespace mijin + +#endif // !defined(MIJIN_IO_ARCHIVE_HPP_INCLUDED) diff --git a/source/mijin/io/stream.hpp b/source/mijin/io/stream.hpp index f940716..5dfe7c8 100644 --- a/source/mijin/io/stream.hpp +++ b/source/mijin/io/stream.hpp @@ -66,13 +66,14 @@ enum class FileOpenMode enum class [[nodiscard]] StreamError { - SUCCESS = 0, - IO_ERROR = 1, - NOT_SUPPORTED = 2, - CONNECTION_CLOSED = 3, - PROTOCOL_ERROR = 4, - WOULD_BLOCK = 5, - UNKNOWN_ERROR = -1 + SUCCESS = 0, + IO_ERROR = 1, + NOT_SUPPORTED = 2, + CONNECTION_CLOSED = 3, + PROTOCOL_ERROR = 4, + WOULD_BLOCK = 5, + CONNECTION_REFUSED = 6, + UNKNOWN_ERROR = -1 }; class Stream @@ -450,6 +451,8 @@ inline const char* errorName(StreamError error) MIJIN_NOEXCEPT return "would block"; case StreamError::UNKNOWN_ERROR: return "unknown error"; + case StreamError::CONNECTION_REFUSED: + return "connection refused"; } return ""; }