198 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			198 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| 
 | |
| #pragma once
 | |
| 
 | |
| #if !defined(MIJIN_IO_ARCHIVE_HPP_INCLUDED)
 | |
| #define MIJIN_IO_ARCHIVE_HPP_INCLUDED 1
 | |
| 
 | |
| #include <cstdint>
 | |
| #include <vector>
 | |
| #include "mijin/container/optional.hpp"
 | |
| #include "mijin/io/stream.hpp"
 | |
| 
 | |
| namespace mijin
 | |
| {
 | |
| enum class ArchiveMode : std::uint8_t
 | |
| {
 | |
|     READ,
 | |
|     WRITE
 | |
| };
 | |
| 
 | |
| template<typename TPointer>
 | |
| 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<Stream*>;
 | |
| using OwningArchive = ArchiveBase<std::unique_ptr<Stream>>;
 | |
| 
 | |
| 
 | |
| template<typename TContent>
 | |
| struct PODWrapper
 | |
| {
 | |
|     TContent* content;
 | |
| };
 | |
| 
 | |
| template<typename TContent>
 | |
| PODWrapper<TContent> pod(TContent& content) MIJIN_NOEXCEPT
 | |
| {
 | |
|     return PODWrapper<TContent>(&content);
 | |
| }
 | |
| 
 | |
| template<typename TPointer, typename TValue> requires (std::is_const_v<TValue>)
 | |
| Task<> c_serialize(ArchiveBase<TPointer>& 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<std::remove_const_t<TValue>&>(value));
 | |
| }
 | |
| 
 | |
| template<typename TPointer>
 | |
| Task<> c_serialize(ArchiveBase<TPointer>& 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<typename TPointer, typename TValue> requires (!std::is_const_v<TValue> && (std::is_arithmetic_v<TValue> || std::is_enum_v<TValue>))
 | |
| Task<> c_serialize(ArchiveBase<TPointer>& 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<typename TPointer, typename TElement, typename TAllocator>
 | |
| Task<> c_serialize(ArchiveBase<TPointer>& archive, std::vector<TElement, TAllocator>& 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<typename TPointer, typename TContent>
 | |
| Task<> c_serialize(ArchiveBase<TPointer> archive, Optional<TContent>& 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<typename TPointer, typename TContent>
 | |
| Task<> c_serialize(ArchiveBase<TPointer>& archive, PODWrapper<TContent> value) MIJIN_NOEXCEPT
 | |
| {
 | |
|     if (archive.getError() != StreamError::SUCCESS)
 | |
|     {
 | |
|         co_return;
 | |
|     }
 | |
|     if (archive.getMode() == ArchiveMode::READ)
 | |
|     {
 | |
|         if constexpr (std::is_const_v<TContent>)
 | |
|         {
 | |
|             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)
 |