#include "protocol.hpp" #include #include #include #include #include #include #include #include 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(v); stream << ' '; } return stream.str(); } template struct Format : Serialization::Format> { using Serialization::Format>::operator(); template auto operator()(Visitor&& visitor, T& value) const -> std::enable_if_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 auto operator()(Visitor&& visitor, T& value) const -> std::enable_if_t, ExpectedCells>> { visitor(*this, value.mCount); } template auto operator()(Visitor&& visitor, T& value) const -> std::enable_if_t, ProcessedCells>> { visitor(*this, value.mCount); } template auto operator()(Visitor&& visitor, T& value) const -> std::enable_if_t, ExpectedTiles>> { visitor(*this, value.mCount); } template auto operator()(Visitor&& visitor, T& value) const -> std::enable_if_t, GeneratedTiles>> { visitor(*this, value.mCount); } }; template std::vector serializeToVector(const T& value) { constexpr Format format; Serialization::SizeAccumulator sizeAccumulator; format(sizeAccumulator, value); std::vector buffer(sizeAccumulator.value()); format(Serialization::BinaryWriter(buffer.data(), buffer.data() + buffer.size()), value); return buffer; } template std::vector serializeImpl(const T& value) { const auto data = serializeToVector(value); const Message message{ static_cast(T::sMessageType), static_cast(data.size()), data.data() }; return serializeToVector(message); } } std::vector serialize(const ExpectedCells& value) { return serializeImpl(value); } std::vector serialize(const ProcessedCells& value) { return serializeImpl(value); } std::vector serialize(const ExpectedTiles& value) { return serializeImpl(value); } std::vector serialize(const GeneratedTiles& value) { return serializeImpl(value); } const std::byte* deserialize(const std::byte* begin, const std::byte* end, Message& message) { try { constexpr Format 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 format; Serialization::BinaryReader reader(message.mData, message.mData + message.mSize); switch (static_cast(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)); } }