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)
 |