constexpr struct_pack::err_code inline deserialize_one()

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;
  }