#include "protocol.hpp" #include <components/serialization/binaryreader.hpp> #include <components/serialization/binarywriter.hpp> #include <components/serialization/format.hpp> #include <components/serialization/sizeaccumulator.hpp> #include <iomanip> #include <sstream> #include <stdexcept> #include <string> namespace NavMeshTool { namespace { std::string formatMagic(const char (&value)[std::size(messageMagic)]) { std::ostringstream stream; for (const char v : value) { if (std::isprint(v) && !std::isspace(v)) stream << '\'' << v << '\''; else stream << "0x" << std::hex << std::uppercase << std::setfill('0') << std::setw(2) << static_cast<int>(v); stream << ' '; } return stream.str(); } template <Serialization::Mode mode> struct Format : Serialization::Format<mode, Format<mode>> { using Serialization::Format<mode, Format<mode>>::operator(); template <class Visitor, class T> auto operator()(Visitor&& visitor, T& value) const -> std::enable_if_t<std::is_same_v<std::decay_t<T>, Message>> { if constexpr (mode == Serialization::Mode::Write) visitor(*this, messageMagic); else { static_assert(mode == Serialization::Mode::Read); char magic[std::size(messageMagic)]; visitor(*this, magic); if (std::memcmp(magic, messageMagic, sizeof(magic)) != 0) throw std::runtime_error("Bad navmeshtool message magic: " + formatMagic(magic)); } visitor(*this, value.mType); visitor(*this, value.mSize); if constexpr (mode == Serialization::Mode::Write) visitor(*this, value.mData, value.mSize); else visitor(*this, value.mData); } template <class Visitor, class T> auto operator()(Visitor&& visitor, T& value) const -> std::enable_if_t<std::is_same_v<std::decay_t<T>, ExpectedCells>> { visitor(*this, value.mCount); } template <class Visitor, class T> auto operator()(Visitor&& visitor, T& value) const -> std::enable_if_t<std::is_same_v<std::decay_t<T>, ProcessedCells>> { visitor(*this, value.mCount); } template <class Visitor, class T> auto operator()(Visitor&& visitor, T& value) const -> std::enable_if_t<std::is_same_v<std::decay_t<T>, ExpectedTiles>> { visitor(*this, value.mCount); } template <class Visitor, class T> auto operator()(Visitor&& visitor, T& value) const -> std::enable_if_t<std::is_same_v<std::decay_t<T>, GeneratedTiles>> { visitor(*this, value.mCount); } }; template <class T> std::vector<std::byte> serializeToVector(const T& value) { constexpr Format<Serialization::Mode::Write> format; Serialization::SizeAccumulator sizeAccumulator; format(sizeAccumulator, value); std::vector<std::byte> buffer(sizeAccumulator.value()); format(Serialization::BinaryWriter(buffer.data(), buffer.data() + buffer.size()), value); return buffer; } template <class T> std::vector<std::byte> serializeImpl(const T& value) { const auto data = serializeToVector(value); const Message message{ static_cast<std::uint64_t>(T::sMessageType), static_cast<std::uint64_t>(data.size()), data.data() }; return serializeToVector(message); } } std::vector<std::byte> serialize(const ExpectedCells& value) { return serializeImpl(value); } std::vector<std::byte> serialize(const ProcessedCells& value) { return serializeImpl(value); } std::vector<std::byte> serialize(const ExpectedTiles& value) { return serializeImpl(value); } std::vector<std::byte> serialize(const GeneratedTiles& value) { return serializeImpl(value); } const std::byte* deserialize(const std::byte* begin, const std::byte* end, Message& message) { try { constexpr Format<Serialization::Mode::Read> format; Serialization::BinaryReader reader(begin, end); format(reader, message); return message.mData + message.mSize; } catch (const Serialization::NotEnoughData&) { return begin; } } TypedMessage decode(const Message& message) { constexpr Format<Serialization::Mode::Read> format; Serialization::BinaryReader reader(message.mData, message.mData + message.mSize); switch (static_cast<MessageType>(message.mType)) { case MessageType::ExpectedCells: { ExpectedCells value; format(reader, value); return value; } case MessageType::ProcessedCells: { ProcessedCells value; format(reader, value); return value; } case MessageType::ExpectedTiles: { ExpectedTiles value; format(reader, value); return value; } case MessageType::GeneratedTiles: { GeneratedTiles value; format(reader, value); return value; } } throw std::logic_error("Unsupported message type: " + std::to_string(message.mType)); } }