diff --git a/iguana/pb_util.hpp b/iguana/pb_util.hpp new file mode 100644 index 00000000..37f34a77 --- /dev/null +++ b/iguana/pb_util.hpp @@ -0,0 +1,92 @@ +#pragma once +#include +#include +#include +#include +#include + +namespace iguana { +namespace detail { +[[nodiscard]] inline uint32_t encode_zigzag(int32_t v) { + return (static_cast(v) << 1U) ^ + static_cast( + -static_cast(static_cast(v) >> 31U)); +} +[[nodiscard]] inline uint64_t encode_zigzag(int64_t v) { + return (static_cast(v) << 1U) ^ + static_cast( + -static_cast(static_cast(v) >> 63U)); +} + +[[nodiscard]] inline int64_t decode_zigzag(uint64_t u) { + return static_cast((u >> 1U)) ^ + static_cast(-static_cast(u & 1U)); +} +[[nodiscard]] inline int64_t decode_zigzag(uint32_t u) { + return static_cast((u >> 1U)) ^ + static_cast(-static_cast(u & 1U)); +} + +[[nodiscard]] inline bool decode_varint(const char* data, std::size_t& pos_, + std::size_t size_, uint64_t& v) { + // fix test failed on arm due to different char definition + const signed char* data_ = reinterpret_cast(data); + // from https://github.com/facebook/folly/blob/main/folly/Varint.h + if (pos_ < size_ && (static_cast(data_[pos_]) & 0x80U) == 0) { + v = static_cast(data_[pos_]); + pos_++; + return true; + } + constexpr const int8_t max_varint_length = sizeof(uint64_t) * 8 / 7 + 1; + uint64_t val = 0; + if (size_ - pos_ >= max_varint_length) [[likely]] { + do { + // clang-format off + int64_t b = data_[pos_++]; + val = ((uint64_t(b) & 0x7fU) ); if (b >= 0) { break; } + b = data_[pos_++]; val |= ((uint64_t(b) & 0x7fU) << 7U); if (b >= 0) { break; } + b = data_[pos_++]; val |= ((uint64_t(b) & 0x7fU) << 14U); if (b >= 0) { break; } + b = data_[pos_++]; val |= ((uint64_t(b) & 0x7fU) << 21U); if (b >= 0) { break; } + b = data_[pos_++]; val |= ((uint64_t(b) & 0x7fU) << 28U); if (b >= 0) { break; } + b = data_[pos_++]; val |= ((uint64_t(b) & 0x7fU) << 35U); if (b >= 0) { break; } + b = data_[pos_++]; val |= ((uint64_t(b) & 0x7fU) << 42U); if (b >= 0) { break; } + b = data_[pos_++]; val |= ((uint64_t(b) & 0x7fU) << 49U); if (b >= 0) { break; } + b = data_[pos_++]; val |= ((uint64_t(b) & 0x7fU) << 56U); if (b >= 0) { break; } + b = data_[pos_++]; val |= ((uint64_t(b) & 0x01U) << 63U); if (b >= 0) { break; } + // clang-format on + return false; + } while (false); + } + else { + unsigned int shift = 0; + while (pos_ != size_ && int64_t(data_[pos_]) < 0) { + val |= (uint64_t(data_[pos_++]) & 0x7fU) << shift; + shift += 7; + } + if (pos_ == size_) { + return false; + } + val |= uint64_t(data_[pos_++]) << shift; + } + v = val; + return true; +} + +template +inline void serialize_varint(uint64_t v, Stream& out) { + while (v >= 0x80) { + out.push_back(static_cast(v | 0x80)); + v >>= 7; + } + out.push_back(static_cast(v)); +} +[[nodiscard]] inline bool deserialize_varint(const char* data, std::size_t& pos, + std::size_t size, uint64_t& v) { + return decode_varint(data, pos, size, v); +} +[[nodiscard]] inline bool read_tag(const char* data, std::size_t& pos, + std::size_t size, uint64_t& tag) { + return deserialize_varint(data, pos, size, tag); +} +} // namespace detail +} // namespace iguana \ No newline at end of file diff --git a/iguana/struct_pb.hpp b/iguana/struct_pb.hpp index f10edc0b..9ad5c4dd 100644 --- a/iguana/struct_pb.hpp +++ b/iguana/struct_pb.hpp @@ -7,7 +7,9 @@ #include #include +#include "pb_util.hpp" #include "reflection.hpp" + namespace iguana { namespace detail { enum class WireType : uint32_t { @@ -19,53 +21,21 @@ enum class WireType : uint32_t { Fixed32 = 5, }; -template -inline To bit_cast(From from) { - static_assert(sizeof(To) == sizeof(From), ""); - static_assert(std::is_trivially_copyable_v, ""); - static_assert(std::is_trivially_copyable_v, ""); - - To to; - std::memcpy(&to, &from, sizeof(from)); - return to; -} - -inline void write_varint(uint32_t value, auto& out) { - uint8_t b[5]{}; - for (size_t i = 0; i < 5; ++i) { - b[i] = value & 0b0111'1111; - value >>= 7; - if (value) { - b[i] |= 0b1000'0000; - } - else { - out.append((const char*)b, i + 1); - // out.write(b, i + 1); - break; - } - } -} - -inline void write_varint(int32_t value, auto& out) { - write_varint(bit_cast(value), out); -} - inline uint32_t make_tag_wire_type(uint32_t tag, WireType wire_type) { return (tag << 3) | static_cast(wire_type); } -inline void write_tag_wire_type(uint32_t tag, WireType wire_type, auto& out) { - write_varint(make_tag_wire_type(tag, wire_type), out); -} - struct varint_serializer { - static void serialize(uint32_t tag, int32_t value, auto& out) { + template + static void serialize(uint32_t field_number, int32_t value, T& out) { if (value == 0) { return; } - write_tag_wire_type(tag, WireType::Varint, out); - write_varint(value, out); + uint32_t key = + (field_number << 3) | static_cast(WireType::Varint); + serialize_varint(key, out); + serialize_varint(value, out); } }; } // namespace detail