mijin2/source/mijin/io/archive.hpp

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)