yalantinglibs/include/ylt/standalone/iguana/pb_writer.hpp

220 lines
7.4 KiB
C++

#pragma once
#include "detail/string_resize.hpp"
#include "pb_util.hpp"
namespace iguana {
namespace detail {
template <uint32_t key, typename V, typename It>
IGUANA_INLINE void encode_varint_field(V val, It&& it) {
static_assert(std::is_integral_v<V>, "must be integral");
if constexpr (key != 0) {
serialize_varint_u32_constexpr<key>(it);
}
serialize_varint(val, it);
}
template <uint32_t key, typename V, typename It>
IGUANA_INLINE void encode_fixed_field(V val, It&& it) {
if constexpr (key != 0) {
serialize_varint_u32_constexpr<key>(it);
}
constexpr size_t size = sizeof(V);
// TODO: check Stream continuous
memcpy(it, &val, size);
it += size;
}
template <uint32_t key, bool omit_default_val = true, typename Type,
typename It>
IGUANA_INLINE void to_pb_impl(Type&& t, It&& it, uint32_t*& sz_ptr);
template <uint32_t key, typename V, typename It>
IGUANA_INLINE void encode_pair_value(V&& val, It&& it, size_t size,
uint32_t*& sz_ptr) {
if (size == 0)
IGUANA_UNLIKELY {
// map keys can't be omitted even if values are empty
// TODO: repeated ?
serialize_varint_u32_constexpr<key>(it);
serialize_varint(0, it);
}
else {
to_pb_impl<key, false>(val, it, sz_ptr);
}
}
template <uint32_t key, bool omit_default_val, typename T, typename It>
IGUANA_INLINE void encode_numeric_field(T t, It&& it) {
if constexpr (omit_default_val) {
if constexpr (is_fixed_v<T> || is_signed_varint_v<T>) {
if (t.val == 0) {
return;
}
}
else {
if (t == static_cast<T>(0))
IGUANA_UNLIKELY { return; }
}
}
if constexpr (std::is_integral_v<T>) {
detail::encode_varint_field<key>(t, it);
}
else if constexpr (detail::is_signed_varint_v<T>) {
detail::encode_varint_field<key>(encode_zigzag(t.val), it);
}
else if constexpr (detail::is_fixed_v<T>) {
detail::encode_fixed_field<key>(t.val, it);
}
else if constexpr (std::is_same_v<T, double> || std::is_same_v<T, float>) {
detail::encode_fixed_field<key>(t, it);
}
else if constexpr (std::is_enum_v<T>) {
using U = std::underlying_type_t<T>;
detail::encode_varint_field<key>(static_cast<U>(t), it);
}
else {
static_assert(!sizeof(T), "unsupported type");
}
}
template <uint32_t field_no, typename Type, typename It>
IGUANA_INLINE void to_pb_oneof(Type&& t, It&& it, uint32_t*& sz_ptr) {
using T = std::decay_t<Type>;
std::visit(
[&it, &sz_ptr](auto&& value) IGUANA__INLINE_LAMBDA {
using value_type =
std::remove_const_t<std::remove_reference_t<decltype(value)>>;
constexpr auto offset =
get_variant_index<T, value_type, std::variant_size_v<T> - 1>();
constexpr uint32_t key =
((field_no + offset) << 3) |
static_cast<uint32_t>(get_wire_type<value_type>());
to_pb_impl<key, false>(std::forward<value_type>(value), it, sz_ptr);
},
std::forward<Type>(t));
}
// omit_default_val = true indicates to omit the default value in searlization
template <uint32_t key, bool omit_default_val, typename Type, typename It>
IGUANA_INLINE void to_pb_impl(Type&& t, It&& it, uint32_t*& sz_ptr) {
using T = std::remove_const_t<std::remove_reference_t<Type>>;
if constexpr (is_reflection_v<T> || is_custom_reflection_v<T>) {
// can't be omitted even if values are empty
if constexpr (key != 0) {
auto len = pb_value_size(t, sz_ptr);
serialize_varint_u32_constexpr<key>(it);
serialize_varint(len, it);
if (len == 0)
IGUANA_UNLIKELY { return; }
}
static constexpr auto tuple = get_members_tuple<T>();
constexpr size_t SIZE = std::tuple_size_v<std::decay_t<decltype(tuple)>>;
for_each_n(
[&t, &it, &sz_ptr](auto i) IGUANA__INLINE_LAMBDA {
using field_type =
std::tuple_element_t<decltype(i)::value,
std::decay_t<decltype(tuple)>>;
constexpr auto value = std::get<decltype(i)::value>(tuple);
auto& val = value.value(t);
using U = typename field_type::value_type;
if constexpr (variant_v<U>) {
constexpr auto offset =
get_variant_index<U, typename field_type::sub_type,
std::variant_size_v<U> - 1>();
if constexpr (offset == 0) {
to_pb_oneof<value.field_no>(val, it, sz_ptr);
}
}
else {
constexpr uint32_t sub_key =
(value.field_no << 3) |
static_cast<uint32_t>(get_wire_type<U>());
to_pb_impl<sub_key>(val, it, sz_ptr);
}
},
std::make_index_sequence<SIZE>{});
}
else if constexpr (is_sequence_container<T>::value) {
// TODO support std::array
// repeated values can't be omitted even if values are empty
using item_type = typename T::value_type;
if constexpr (is_lenprefix_v<item_type>) {
// non-packed
for (auto& item : t) {
to_pb_impl<key, false>(item, it, sz_ptr);
}
}
else {
if (t.empty())
IGUANA_UNLIKELY { return; }
serialize_varint_u32_constexpr<key>(it);
serialize_varint(pb_value_size(t, sz_ptr), it);
for (auto& item : t) {
encode_numeric_field<false, 0>(item, it);
}
}
}
else if constexpr (is_map_container<T>::value) {
using first_type = typename T::key_type;
using second_type = typename T::mapped_type;
constexpr uint32_t key1 =
(1 << 3) | static_cast<uint32_t>(get_wire_type<first_type>());
constexpr auto key1_size = variant_uint32_size_constexpr(key1);
constexpr uint32_t key2 =
(2 << 3) | static_cast<uint32_t>(get_wire_type<second_type>());
constexpr auto key2_size = variant_uint32_size_constexpr(key2);
for (auto& [k, v] : t) {
serialize_varint_u32_constexpr<key>(it);
// k must be string or numeric
auto k_val_len = str_numeric_size<0, false>(k);
auto v_val_len = pb_value_size<false>(v, sz_ptr);
auto pair_len = key1_size + key2_size + k_val_len + v_val_len;
if constexpr (is_lenprefix_v<first_type>) {
pair_len += variant_uint32_size(k_val_len);
}
if constexpr (is_lenprefix_v<second_type>) {
pair_len += variant_uint32_size(v_val_len);
}
serialize_varint(pair_len, it);
// map k and v can't be omitted even if values are empty
encode_pair_value<key1>(k, it, k_val_len, sz_ptr);
encode_pair_value<key2>(v, it, v_val_len, sz_ptr);
}
}
else if constexpr (optional_v<T>) {
if (!t.has_value()) {
return;
}
to_pb_impl<key, omit_default_val>(*t, it, sz_ptr);
}
else if constexpr (std::is_same_v<T, std::string> ||
std::is_same_v<T, std::string_view>) {
if constexpr (omit_default_val) {
if (t.size() == 0)
IGUANA_UNLIKELY { return; }
}
serialize_varint_u32_constexpr<key>(it);
serialize_varint(t.size(), it);
memcpy(it, t.data(), t.size());
it += t.size();
}
else {
encode_numeric_field<key, omit_default_val>(t, it);
}
}
} // namespace detail
template <typename T, typename Stream>
IGUANA_INLINE void to_pb(T& t, Stream& out) {
std::vector<uint32_t> size_arr;
auto byte_len = detail::pb_key_value_size<0>(t, size_arr);
detail::resize(out, byte_len);
auto sz_ptr = size_arr.empty() ? nullptr : &size_arr[0];
detail::to_pb_impl<0>(t, &out[0], sz_ptr);
}
} // namespace iguana