258 lines
8.1 KiB
C++
258 lines
8.1 KiB
C++
#pragma once
|
|
#include "detail/string_resize.hpp"
|
|
#include "pb_util.hpp"
|
|
|
|
namespace iguana {
|
|
|
|
namespace detail {
|
|
|
|
template <typename T>
|
|
IGUANA_INLINE void from_pb_impl(T& val, std::string_view& pb_str,
|
|
uint32_t field_no = 0);
|
|
|
|
template <typename T>
|
|
IGUANA_INLINE void decode_pair_value(T& val, std::string_view& pb_str) {
|
|
size_t pos;
|
|
uint32_t key = detail::decode_varint(pb_str, pos);
|
|
pb_str = pb_str.substr(pos);
|
|
WireType wire_type = static_cast<WireType>(key & 0b0111);
|
|
if (wire_type != detail::get_wire_type<std::remove_reference_t<T>>()) {
|
|
return;
|
|
}
|
|
from_pb_impl(val, pb_str);
|
|
}
|
|
|
|
template <typename T>
|
|
IGUANA_INLINE void from_pb_impl(T& val, std::string_view& pb_str,
|
|
uint32_t field_no) {
|
|
size_t pos = 0;
|
|
if constexpr (is_reflection_v<T>) {
|
|
size_t pos;
|
|
uint32_t size = detail::decode_varint(pb_str, pos);
|
|
pb_str = pb_str.substr(pos);
|
|
if (pb_str.size() < size)
|
|
IGUANA_UNLIKELY {
|
|
throw std::invalid_argument("Invalid fixed int value: too few bytes.");
|
|
}
|
|
if (size == 0) {
|
|
return;
|
|
}
|
|
from_pb(val, pb_str.substr(0, size));
|
|
pb_str = pb_str.substr(size);
|
|
}
|
|
else if constexpr (is_sequence_container<T>::value) {
|
|
using item_type = typename T::value_type;
|
|
if constexpr (is_lenprefix_v<item_type>) {
|
|
// item_type non-packed
|
|
while (!pb_str.empty()) {
|
|
item_type item{};
|
|
from_pb_impl(item, pb_str);
|
|
val.push_back(std::move(item));
|
|
if (pb_str.empty()) {
|
|
break;
|
|
}
|
|
uint32_t key = detail::decode_varint(pb_str, pos);
|
|
uint32_t field_number = key >> 3;
|
|
if (field_number != field_no) {
|
|
break;
|
|
}
|
|
else {
|
|
pb_str = pb_str.substr(pos);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
// item_type packed
|
|
size_t pos;
|
|
uint32_t size = detail::decode_varint(pb_str, pos);
|
|
pb_str = pb_str.substr(pos);
|
|
if (pb_str.size() < size)
|
|
IGUANA_UNLIKELY {
|
|
throw std::invalid_argument(
|
|
"Invalid fixed int value: too few bytes.");
|
|
}
|
|
using item_type = typename T::value_type;
|
|
size_t start = pb_str.size();
|
|
|
|
while (!pb_str.empty()) {
|
|
item_type item;
|
|
from_pb_impl(item, pb_str);
|
|
val.push_back(std::move(item));
|
|
if (start - pb_str.size() == size) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if constexpr (is_map_container<T>::value) {
|
|
using item_type = std::pair<typename T::key_type, typename T::mapped_type>;
|
|
while (!pb_str.empty()) {
|
|
size_t pos;
|
|
uint32_t size = detail::decode_varint(pb_str, pos);
|
|
pb_str = pb_str.substr(pos);
|
|
if (pb_str.size() < size)
|
|
IGUANA_UNLIKELY {
|
|
throw std::invalid_argument(
|
|
"Invalid fixed int value: too few bytes.");
|
|
}
|
|
item_type item = {};
|
|
decode_pair_value(item.first, pb_str);
|
|
decode_pair_value(item.second, pb_str);
|
|
val.emplace(std::move(item));
|
|
|
|
if (pb_str.empty()) {
|
|
break;
|
|
}
|
|
uint32_t key = detail::decode_varint(pb_str, pos);
|
|
uint32_t field_number = key >> 3;
|
|
if (field_number != field_no) {
|
|
break;
|
|
}
|
|
pb_str = pb_str.substr(pos);
|
|
}
|
|
}
|
|
else if constexpr (std::is_integral_v<T>) {
|
|
val = static_cast<T>(detail::decode_varint(pb_str, pos));
|
|
pb_str = pb_str.substr(pos);
|
|
}
|
|
else if constexpr (detail::is_signed_varint_v<T>) {
|
|
constexpr size_t len = sizeof(typename T::value_type);
|
|
uint64_t temp = detail::decode_varint(pb_str, pos);
|
|
if constexpr (len == 8) {
|
|
val.val = detail::decode_zigzag(temp);
|
|
}
|
|
else {
|
|
val.val = detail::decode_zigzag(static_cast<uint32_t>(temp));
|
|
}
|
|
pb_str = pb_str.substr(pos);
|
|
}
|
|
else if constexpr (detail::is_fixed_v<T>) {
|
|
constexpr size_t size = sizeof(typename T::value_type);
|
|
if (pb_str.size() < size)
|
|
IGUANA_UNLIKELY {
|
|
throw std::invalid_argument("Invalid fixed int value: too few bytes.");
|
|
}
|
|
memcpy(&(val.val), pb_str.data(), size);
|
|
pb_str = pb_str.substr(size);
|
|
}
|
|
else if constexpr (std::is_same_v<T, double> || std::is_same_v<T, float>) {
|
|
constexpr size_t size = sizeof(T);
|
|
if (pb_str.size() < size)
|
|
IGUANA_UNLIKELY {
|
|
throw std::invalid_argument("Invalid fixed int value: too few bytes.");
|
|
}
|
|
memcpy(&(val), pb_str.data(), size);
|
|
pb_str = pb_str.substr(size);
|
|
}
|
|
else if constexpr (std::is_same_v<T, std::string> ||
|
|
std::is_same_v<T, std::string_view>) {
|
|
size_t size = detail::decode_varint(pb_str, pos);
|
|
if (pb_str.size() < pos + size)
|
|
IGUANA_UNLIKELY {
|
|
throw std::invalid_argument("Invalid string value: too few bytes.");
|
|
}
|
|
if constexpr (std::is_same_v<T, std::string_view>) {
|
|
val = std::string_view(pb_str.data() + pos, size);
|
|
}
|
|
else {
|
|
detail::resize(val, size);
|
|
memcpy(val.data(), pb_str.data() + pos, size);
|
|
}
|
|
pb_str = pb_str.substr(size + pos);
|
|
}
|
|
else if constexpr (std::is_enum_v<T>) {
|
|
using U = std::underlying_type_t<T>;
|
|
U value{};
|
|
from_pb_impl(value, pb_str);
|
|
val = static_cast<T>(value);
|
|
}
|
|
else if constexpr (optional_v<T>) {
|
|
from_pb_impl(val.emplace(), pb_str);
|
|
}
|
|
else {
|
|
static_assert(!sizeof(T), "err");
|
|
}
|
|
}
|
|
|
|
template <typename T, typename Field>
|
|
IGUANA_INLINE void parse_oneof(T& t, const Field& f, std::string_view& pb_str) {
|
|
using item_type = typename std::decay_t<Field>::sub_type;
|
|
from_pb_impl(t.template emplace<item_type>(), pb_str, f.field_no);
|
|
}
|
|
} // namespace detail
|
|
|
|
template <typename T>
|
|
IGUANA_INLINE void from_pb(T& t, std::string_view pb_str) {
|
|
if (pb_str.empty())
|
|
IGUANA_UNLIKELY { return; }
|
|
size_t pos = 0;
|
|
uint32_t key = detail::decode_varint(pb_str, pos);
|
|
WireType wire_type = static_cast<WireType>(key & 0b0111);
|
|
uint32_t field_number = key >> 3;
|
|
#ifdef SEQUENTIAL_PARSE
|
|
constexpr static auto tp = get_members_tuple<T>();
|
|
constexpr size_t SIZE = std::tuple_size_v<std::decay_t<decltype(tp)>>;
|
|
bool parse_done = false;
|
|
detail::for_each_n(
|
|
[&](auto i) IGUANA__INLINE_LAMBDA {
|
|
constexpr auto val = std::get<decltype(i)::value>(tp);
|
|
using sub_type = typename std::decay_t<decltype(val)>::sub_type;
|
|
using value_type = typename std::decay_t<decltype(val)>::value_type;
|
|
// sub_type is the element type when value_type is the variant type;
|
|
// otherwise, they are the same.
|
|
if (parse_done || field_number != val.field_no) {
|
|
return;
|
|
}
|
|
pb_str = pb_str.substr(pos);
|
|
if (wire_type != detail::get_wire_type<sub_type>())
|
|
IGUANA_UNLIKELY { throw std::runtime_error("unmatched wire_type"); }
|
|
if constexpr (variant_v<value_type>) {
|
|
detail::parse_oneof(val.value(t), val, pb_str);
|
|
}
|
|
else {
|
|
detail::from_pb_impl(val.value(t), pb_str, val.field_no);
|
|
}
|
|
if (pb_str.empty()) {
|
|
parse_done = true;
|
|
return;
|
|
}
|
|
key = detail::decode_varint(pb_str, pos);
|
|
wire_type = static_cast<WireType>(key & 0b0111);
|
|
field_number = key >> 3;
|
|
},
|
|
std::make_index_sequence<SIZE>{});
|
|
if (parse_done)
|
|
IGUANA_LIKELY { return; }
|
|
#endif
|
|
while (true) {
|
|
pb_str = pb_str.substr(pos);
|
|
constexpr static auto map = get_members<T>();
|
|
auto& member = map.at(field_number);
|
|
std::visit(
|
|
[&t, &pb_str, wire_type](auto& val) {
|
|
using sub_type = typename std::decay_t<decltype(val)>::sub_type;
|
|
using value_type = typename std::decay_t<decltype(val)>::value_type;
|
|
if (wire_type != detail::get_wire_type<sub_type>()) {
|
|
throw std::runtime_error("unmatched wire_type");
|
|
}
|
|
if constexpr (variant_v<value_type>) {
|
|
detail::parse_oneof(val.value(t), val, pb_str);
|
|
}
|
|
else {
|
|
detail::from_pb_impl(val.value(t), pb_str, val.field_no);
|
|
}
|
|
},
|
|
member);
|
|
if (!pb_str.empty())
|
|
IGUANA_LIKELY {
|
|
key = detail::decode_varint(pb_str, pos);
|
|
wire_type = static_cast<WireType>(key & 0b0111);
|
|
field_number = key >> 3;
|
|
}
|
|
else {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
} // namespace iguana
|