#pragma once #include #include #include #include #include #include // Schema-less protobuf wire-format reading: enough to walk the game's cached // catalogue records by field number without generated message types. namespace dingosdk::protobuf { enum class Wire : std::uint8_t { varint = 0, fixed64 = 1, bytes = 2, fixed32 = 5 }; struct Field { std::uint32_t number{}; Wire wire{}; std::uint64_t integer{}; // varint and fixed values std::span bytes; // length-delimited payloads }; inline std::optional read_varint(std::span data, std::size_t& at) noexcept { std::uint64_t result{}; for (unsigned shift = 0; shift < 64 && at < data.size(); shift += 7) { const auto byte = data[at++]; result |= std::uint64_t{byte & 0x7fu} << shift; if (byte < 0x80) return result; } return std::nullopt; } // Every field of one message, or nothing when the bytes are not a well-formed message. inline std::optional> parse(std::span data) { std::vector fields; for (std::size_t at = 0; at < data.size();) { const auto tag = read_varint(data, at); if (!tag || !(*tag >> 3) || (*tag >> 3) > 0x1fffffff) return std::nullopt; Field field{static_cast(*tag >> 3), static_cast(*tag & 7)}; switch (field.wire) { case Wire::varint: { const auto value = read_varint(data, at); if (!value) return std::nullopt; field.integer = *value; break; } case Wire::fixed64: case Wire::fixed32: { const std::size_t size = field.wire == Wire::fixed64 ? 8 : 4; if (data.size() - at < size) return std::nullopt; for (std::size_t i = 0; i < size; ++i) field.integer |= std::uint64_t{data[at + i]} << (8 * i); at += size; break; } case Wire::bytes: { const auto size = read_varint(data, at); if (!size || *size > data.size() - at) return std::nullopt; field.bytes = data.subspan(at, static_cast(*size)); at += static_cast(*size); break; } default: return std::nullopt; } fields.push_back(field); } return fields; } // A stream of varint-length-prefixed messages, or nothing when the framing breaks. inline std::optional>> frames(std::span data) { std::vector> result; for (std::size_t at = 0; at < data.size();) { const auto size = read_varint(data, at); if (!size || !*size || *size > data.size() - at) return std::nullopt; result.push_back(data.subspan(at, static_cast(*size))); at += static_cast(*size); } return result; } inline std::string_view text(std::span bytes) noexcept { return {reinterpret_cast(bytes.data()), bytes.size()}; } inline const Field* first(const std::vector& fields, std::uint32_t number, Wire wire) noexcept { for (const auto& field : fields) if (field.number == number && field.wire == wire) return &field; return nullptr; } }