in include/ylt/struct_pack/unpacker.hpp [752:1374]
constexpr struct_pack::err_code inline deserialize_one(T &item) {
struct_pack::err_code code{};
using type = remove_cvref_t<decltype(item)>;
static_assert(!std::is_pointer_v<type>);
constexpr auto id = get_type_id<type, parent_tag>();
if constexpr (is_trivial_view_v<type>) {
static_assert(view_reader_t<Reader>,
"The Reader isn't a view_reader, can't deserialize "
"a trivial_view<T>");
static_assert(
is_little_endian_copyable<sizeof(typename type::value_type)>,
"get a trivial view with byte width > 1 in big-endian system is "
"not allowed.");
const char *view = reader_.read_view(sizeof(typename T::value_type));
if SP_LIKELY (view != nullptr) {
item = *reinterpret_cast<const typename T::value_type *>(view);
code = errc::ok;
}
else {
code = errc::no_buffer_space;
}
}
else if constexpr (version == UINT64_MAX) {
if constexpr (id == type_id::compatible_t) {
// do nothing
}
else if constexpr (std::is_same_v<type, std::monostate>) {
// do nothing
}
else if constexpr (id == type_id::user_defined_type) {
if constexpr (NotSkip) {
code = sp_deserialize_to(reader_, item);
}
else {
code = sp_deserialize_to_with_skip(reader_, item);
}
}
else if constexpr (detail::varint_t<type, parent_tag>) {
if constexpr (is_enable_fast_varint_coding(parent_tag)) {
// do nothing, we have deserialized it in parent.
}
else {
code = detail::deserialize_varint<NotSkip>(reader_, item);
}
}
else if constexpr (std::is_fundamental_v<type> || std::is_enum_v<type> ||
id == type_id::int128_t || id == type_id::uint128_t) {
if constexpr (NotSkip) {
if SP_UNLIKELY (!read_wrapper<sizeof(type)>(reader_, (char *)&item)) {
return struct_pack::errc::no_buffer_space;
}
}
else {
return reader_.ignore(sizeof(type)) ? errc{} : errc::no_buffer_space;
}
}
else if constexpr (id == type_id::bitset_t) {
if constexpr (NotSkip) {
if SP_UNLIKELY (!read_bytes_array(reader_, (char *)&item,
sizeof(type))) {
return struct_pack::errc::no_buffer_space;
}
}
else {
return reader_.ignore(sizeof(type)) ? errc{} : errc::no_buffer_space;
}
}
else if constexpr (unique_ptr<type>) {
bool has_value{};
if SP_UNLIKELY (!read_wrapper<sizeof(bool)>(reader_,
(char *)&has_value)) {
return struct_pack::errc::no_buffer_space;
}
if (!has_value) {
return {};
}
if constexpr (is_base_class<typename type::element_type>) {
uint32_t id{};
read_wrapper<sizeof(id)>(reader_, (char *)&id);
bool ok{};
auto index = search_type_by_md5<typename type::element_type>(id, ok);
if SP_UNLIKELY (!ok) {
return errc::invalid_buffer;
}
else {
return ylt::reflection::template_switch<
deserialize_one_derived_class_helper<
derived_class_set_t<typename type::element_type>,
std::integral_constant<std::size_t, size_type>,
std::integral_constant<std::uint64_t, version>,
std::integral_constant<std::uint64_t, NotSkip>>>(
index, this, item);
}
}
else {
item = std::make_unique<typename type::element_type>();
deserialize_one<size_type, version, NotSkip>(*item);
}
}
else if constexpr (id == type_id::array_t) {
if constexpr (is_trivial_serializable<type>::value &&
is_little_endian_copyable<sizeof(item[0])>) {
if constexpr (NotSkip) {
if SP_UNLIKELY (!read_bytes_array(reader_, (char *)&item,
sizeof(item))) {
return struct_pack::errc::no_buffer_space;
}
}
else {
return reader_.ignore(sizeof(type)) ? errc{}
: errc::no_buffer_space;
}
}
else {
for (auto &i : item) {
code = deserialize_one<size_type, version, NotSkip>(i);
if SP_UNLIKELY (code) {
return code;
}
}
}
}
else if constexpr (container<type>) {
std::size_t size = 0;
if constexpr (size_type == 1) {
if SP_UNLIKELY (!low_bytes_read_wrapper<size_type>(reader_, size)) {
return struct_pack::errc::no_buffer_space;
}
}
else {
#ifdef STRUCT_PACK_OPTIMIZE
constexpr bool struct_pack_optimize = true;
#else
constexpr bool struct_pack_optimize = false;
#endif
if constexpr (force_optimize || struct_pack_optimize) {
if constexpr (size_type == 2) {
if SP_UNLIKELY (!low_bytes_read_wrapper<size_type>(reader_,
size)) {
return struct_pack::errc::no_buffer_space;
}
}
else if constexpr (size_type == 4) {
if SP_UNLIKELY (!low_bytes_read_wrapper<size_type>(reader_,
size)) {
return struct_pack::errc::no_buffer_space;
}
}
else if constexpr (size_type == 8) {
if constexpr (sizeof(std::size_t) >= 8) {
if SP_UNLIKELY (!low_bytes_read_wrapper<size_type>(reader_,
size)) {
return struct_pack::errc::no_buffer_space;
}
}
else {
std::uint64_t sz{};
if SP_UNLIKELY (!low_bytes_read_wrapper<size_type>(reader_,
sz)) {
return struct_pack::errc::no_buffer_space;
}
if SP_UNLIKELY (sz > UINT32_MAX) {
return struct_pack::errc::invalid_width_of_container_length;
}
size = sz;
}
}
else {
static_assert(!sizeof(T), "illegal size_type");
}
}
else {
switch (size_type_) {
case 1:
if SP_UNLIKELY (!low_bytes_read_wrapper<2>(reader_, size)) {
return struct_pack::errc::no_buffer_space;
}
break;
case 2:
if SP_UNLIKELY (!low_bytes_read_wrapper<4>(reader_, size)) {
return struct_pack::errc::no_buffer_space;
}
break;
case 3:
if constexpr (sizeof(std::size_t) >= 8) {
if SP_UNLIKELY (!low_bytes_read_wrapper<8>(reader_, size)) {
return struct_pack::errc::no_buffer_space;
}
}
else {
unreachable();
}
break;
default:
unreachable();
}
}
}
if (size == 0) {
return {};
}
if constexpr (map_container<type>) {
std::pair<typename type::key_type, typename type::mapped_type>
value{};
if constexpr (is_trivial_serializable<decltype(value)>::value &&
!NotSkip) {
if constexpr (sizeof(value) > 1) {
if SP_UNLIKELY (size > SIZE_MAX / sizeof(value)) {
return errc::no_buffer_space;
}
}
return reader_.ignore(size * sizeof(value)) ? errc{}
: errc::no_buffer_space;
}
else {
item.clear();
for (uint64_t i = 0; i < size; ++i) {
code = deserialize_one<size_type, version, NotSkip>(value);
if SP_UNLIKELY (code) {
return code;
}
if constexpr (NotSkip) {
item.emplace(std::move(value));
// TODO: mapped_type can deserialize without be moved
}
}
}
}
else if constexpr (set_container<type>) {
typename type::value_type value{};
if constexpr (is_trivial_serializable<decltype(value)>::value &&
!NotSkip) {
if constexpr (sizeof(value) > 1) {
if SP_UNLIKELY (size > SIZE_MAX / sizeof(value)) {
return errc::no_buffer_space;
}
}
return reader_.ignore(size * sizeof(value)) ? errc{}
: errc::no_buffer_space;
}
else {
item.clear();
for (uint64_t i = 0; i < size; ++i) {
code = deserialize_one<size_type, version, NotSkip>(value);
if SP_UNLIKELY (code) {
return code;
}
if constexpr (NotSkip) {
item.emplace(std::move(value));
// TODO: mapped_type can deserialize without be moved
}
}
}
}
else {
using value_type = typename type::value_type;
constexpr std::size_t block_lim_cnt =
STRUCT_PACK_MAX_UNCONFIRM_PREREAD_SIZE / sizeof(value_type);
if constexpr (trivially_copyable_container<type>) {
if constexpr (sizeof(value_type) > 1) {
if SP_UNLIKELY (size > SIZE_MAX / sizeof(value_type)) {
return errc::no_buffer_space;
}
}
[[maybe_unused]] std::size_t mem_sz = size * sizeof(value_type);
if constexpr (NotSkip) {
if constexpr (string_view<type> || dynamic_span<type>) {
static_assert(
view_reader_t<Reader>,
"The Reader isn't a view_reader, can't deserialize "
"a string_view/span");
const char *view = reader_.read_view(mem_sz);
if SP_UNLIKELY (view == nullptr) {
return struct_pack::errc::no_buffer_space;
}
item = {(value_type *)(view), (std::size_t)size};
}
else {
if constexpr (checkable_reader_t<Reader>) {
if SP_UNLIKELY (!reader_.check(mem_sz)) {
return struct_pack::errc::no_buffer_space;
}
resize(item, size);
if constexpr (is_little_endian_copyable<sizeof(value_type)>) {
[[maybe_unused]] auto ec =
read_bytes_array(reader_, (char *)item.data(), mem_sz);
assert(ec == true);
}
else {
for (auto &i : item) {
code = deserialize_one<size_type, version, NotSkip>(i);
if SP_UNLIKELY (code) {
return code;
}
}
}
}
else {
for (size_t i = 0, len = block_lim_cnt; i < size;
i += block_lim_cnt) {
if (i + block_lim_cnt >= size) {
len = size - i;
}
resize(item, i + len);
if constexpr (is_little_endian_copyable<sizeof(
value_type)>) {
if SP_UNLIKELY (!read_bytes_array(
reader_, (char *)(item.data() + i),
len * sizeof(value_type))) {
item.resize(i);
if constexpr (can_shrink_to_fit<type>) {
item.shrink_to_fit();
}
return struct_pack::errc::no_buffer_space;
}
}
else {
for (size_t j = i; j < i + len; ++j) {
code = deserialize_one<size_type, version, NotSkip>(
item[j]);
if SP_UNLIKELY (code) {
return code;
}
}
}
}
}
}
}
else {
return reader_.ignore(mem_sz) ? errc{} : errc::no_buffer_space;
}
}
else {
if constexpr (dynamic_span<type>) {
static_assert(!dynamic_span<type>,
"It's illegal to deserialize a span<T> which T "
"is a non-trival-serializable type.");
}
else if constexpr (NotSkip) {
item.clear();
if constexpr (can_reserve<type>) {
item.reserve((std::min)(size, block_lim_cnt));
}
for (size_t i = 0; i < size; ++i) {
item.emplace_back();
code =
deserialize_one<size_type, version, NotSkip>(item.back());
if SP_UNLIKELY (code) {
if constexpr (can_reserve<type>) {
if constexpr (can_shrink_to_fit<type>) {
item.shrink_to_fit(); // release reserve memory
}
}
return code;
}
}
}
else {
value_type useless;
for (size_t i = 0; i < size; ++i) {
code = deserialize_one<size_type, version, NotSkip>(useless);
if SP_UNLIKELY (code) {
return code;
}
}
}
}
}
}
else if constexpr (container_adapter<type>) {
static_assert(!sizeof(type),
"the container adapter type is not supported");
}
else if constexpr (!pair<type> && tuple<type> &&
!is_trivial_tuple<type>) {
std::apply(
[&](auto &&...items) CONSTEXPR_INLINE_LAMBDA {
code = deserialize_many<size_type, version, NotSkip>(items...);
},
item);
}
else if constexpr (ylt::reflection::optional<type> ||
ylt::reflection::expected<type>) {
bool has_value{};
if SP_UNLIKELY (!read_wrapper<sizeof(bool)>(reader_,
(char *)&has_value)) {
return struct_pack::errc::no_buffer_space;
}
if SP_UNLIKELY (!has_value) {
if constexpr (ylt::reflection::expected<type>) {
item = typename type::unexpected_type{typename type::error_type{}};
deserialize_one<size_type, version, NotSkip>(item.error());
}
else {
return {};
}
}
else {
if constexpr (ylt::reflection::expected<type>) {
if constexpr (!std::is_same_v<typename type::value_type, void>)
deserialize_one<size_type, version, NotSkip>(item.value());
}
else {
item = type{std::in_place_t{}};
deserialize_one<size_type, version, NotSkip>(*item);
}
}
}
else if constexpr (is_variant_v<type>) {
uint8_t index{};
if SP_UNLIKELY (!read_wrapper<sizeof(index)>(reader_, (char *)&index)) {
return struct_pack::errc::no_buffer_space;
}
if SP_UNLIKELY (index >= std::variant_size_v<type>) {
return struct_pack::errc::invalid_buffer;
}
else {
ylt::reflection::template_switch<variant_construct_helper<
std::integral_constant<std::size_t, size_type>,
std::integral_constant<std::uint64_t, version>,
std::integral_constant<bool, NotSkip>>>(index, *this, item);
}
}
else if constexpr (std::is_class_v<type>) {
if constexpr (!pair<type> && !is_trivial_tuple<type>)
if constexpr (!user_defined_refl<type>)
static_assert(
std::is_aggregate_v<remove_cvref_t<type>>,
"struct_pack only support aggregated type, or you should "
"add macro YLT_REFL(Type,field1,field2...)");
if constexpr (is_trivial_serializable<type>::value &&
is_little_endian_copyable<sizeof(type)>) {
if constexpr (NotSkip) {
if SP_UNLIKELY (!read_wrapper<sizeof(type)>(reader_,
(char *)&item)) {
return struct_pack::errc::no_buffer_space;
}
}
else {
return reader_.ignore(sizeof(type)) ? errc{}
: errc::no_buffer_space;
}
}
else if constexpr ((is_trivial_serializable<type>::value &&
!is_little_endian_copyable<sizeof(type)>) ||
is_trivial_serializable<type, true>::value) {
visit_members(item, [&](auto &&...items) CONSTEXPR_INLINE_LAMBDA {
int i = 1;
auto f = [&](auto &&item) -> bool {
code = deserialize_one<size_type, version, NotSkip>(item);
if SP_LIKELY (!code) {
code = ignore_padding(align::padding_size<type>[i++]);
}
return !code;
};
[[maybe_unused]] bool op = (f(items) && ...);
});
}
else {
constexpr uint64_t tag = get_parent_tag<type>();
if constexpr (is_enable_fast_varint_coding(tag)) {
code = visit_members(
item, [this](auto &&...items) CONSTEXPR_INLINE_LAMBDA {
constexpr uint64_t tag =
get_parent_tag<type>(); // to pass msvc with c++17
return this->deserialize_fast_varint<tag, NotSkip>(items...);
});
if SP_UNLIKELY (code) {
return code;
}
}
code = visit_members(
item, [this](auto &&...items) CONSTEXPR_INLINE_LAMBDA {
constexpr uint64_t tag =
get_parent_tag<type>(); // to pass msvc with c++17
return this->deserialize_many<size_type, version, NotSkip, tag>(
items...);
});
}
}
else {
static_assert(!sizeof(type), "the type is not supported yet");
}
}
else if constexpr (exist_compatible_member<type, version>) {
if constexpr (id == type_id::compatible_t) {
if constexpr (version == type::version_number) {
auto pos = reader_.tellg();
if ((std::size_t)pos >= data_len_) {
if (std::is_unsigned_v<decltype(pos)> || pos >= 0) {
size_type_ = UCHAR_MAX; // Just notice that this is not a real
// error, this is a flag for exit.
}
return struct_pack::errc::no_buffer_space;
}
bool has_value{};
if SP_UNLIKELY (!read_wrapper<sizeof(bool)>(reader_,
(char *)&has_value)) {
return struct_pack::errc::no_buffer_space;
}
if (!has_value) {
return code;
}
else {
item = type{std::in_place_t{}};
deserialize_one<size_type, UINT64_MAX, NotSkip>(*item);
}
}
}
else if constexpr (unique_ptr<type>) {
if (item == nullptr) {
return {};
}
if constexpr (is_base_class<typename type::element_type>) {
uint32_t id = item->get_struct_pack_id();
bool ok{};
auto index = search_type_by_md5<typename type::element_type>(id, ok);
assert(ok);
return ylt::reflection::template_switch<
deserialize_one_derived_class_helper<
derived_class_set_t<typename type::element_type>,
std::integral_constant<std::size_t, size_type>,
std::integral_constant<std::uint64_t, version>,
std::integral_constant<std::uint64_t, NotSkip>>>(index, this,
item);
}
else {
deserialize_one<size_type, version, NotSkip>(*item);
}
}
else if constexpr (id == type_id::array_t) {
for (auto &i : item) {
code = deserialize_one<size_type, version, NotSkip>(i);
if SP_UNLIKELY (code) {
return code;
}
}
}
else if constexpr (container<type>) {
if constexpr (id == type_id::set_container_t) {
// TODO: support it.
static_assert(!sizeof(type),
"we don't support compatible field in set now.");
}
else if constexpr (id == type_id::map_container_t) {
static_assert(
!exist_compatible_member<typename type::key_type>,
"we don't support compatible field in map's key_type now.");
if constexpr (NotSkip) {
for (auto &e : item) {
code = deserialize_one<size_type, version, NotSkip>(e.second);
if SP_UNLIKELY (code) {
return code;
}
}
}
else {
// TODO: support it.
static_assert(
!sizeof(type),
"we don't support skip compatible field in container now.");
}
// how to deserialize it quickly?
}
else {
if constexpr (NotSkip) {
for (auto &i : item) {
code = deserialize_one<size_type, version, NotSkip>(i);
if SP_UNLIKELY (code) {
return code;
}
}
}
else {
// TODO: support it.
static_assert(
!sizeof(type),
"we don't support skip compatible field in container now.");
}
}
}
else if constexpr (!pair<type> && tuple<type> &&
!is_trivial_tuple<type>) {
std::apply(
[&](auto &&...items) CONSTEXPR_INLINE_LAMBDA {
code = deserialize_many<size_type, version, NotSkip>(items...);
},
item);
}
else if constexpr (ylt::reflection::optional<type> ||
ylt::reflection::expected<type>) {
bool has_value = item.has_value();
if (!has_value) {
if constexpr (ylt::reflection::expected<type>) {
deserialize_one<size_type, version, NotSkip>(item.error());
}
}
else {
if constexpr (ylt::reflection::expected<type>) {
if constexpr (!std::is_same_v<typename type::value_type, void>)
deserialize_one<size_type, version, NotSkip>(item.value());
}
else {
deserialize_one<size_type, version, NotSkip>(item.value());
}
}
}
else if constexpr (is_variant_v<type>) {
std::visit(
[this](auto &item) {
deserialize_one<size_type, version, NotSkip>(item);
},
item);
}
else if constexpr (std::is_class_v<type>) {
visit_members(item, [&](auto &&...items) CONSTEXPR_INLINE_LAMBDA {
code = deserialize_many<size_type, version, NotSkip>(items...);
});
}
}
return code;
}