yalantinglibs/include/ylt/struct_pack/unpacker.hpp

1474 lines
50 KiB
C++

/*
* Copyright (c) 2023, Alibaba Group Holding Limited;
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <climits>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <memory>
#include <tuple>
#include <type_traits>
#include <utility>
#include <variant>
#include "alignment.hpp"
#include "calculate_size.hpp"
#include "derived_helper.hpp"
#include "endian_wrapper.hpp"
#include "error_code.hpp"
#include "reflection.hpp"
#include "type_calculate.hpp"
#include "type_id.hpp"
#include "type_trait.hpp"
#include "varint.hpp"
#define STRUCT_PACK_MAX_UNCONFIRM_PREREAD_SIZE 1 * 1024 * 1024 // 1MB
namespace struct_pack {
namespace detail {
struct memory_reader {
const char *now;
const char *end;
constexpr memory_reader(const char *beg, const char *end) noexcept
: now(beg), end(end) {}
bool read(char *target, size_t len) {
if SP_UNLIKELY (end - now < len) {
return false;
}
memcpy(target, now, len);
now += len;
return true;
}
bool check(size_t len) { return end - now >= len; }
const char *read_view(size_t len) {
if SP_UNLIKELY (end - now < len) {
return nullptr;
}
auto ret = now;
now += len;
return ret;
}
bool ignore(size_t len) {
if SP_UNLIKELY (end - now < len) {
return false;
}
now += len;
return true;
}
std::size_t tellg() { return (std::size_t)now; }
};
#if __cpp_concepts >= 201907L
template <reader_t Reader, uint64_t conf = sp_config::DEFAULT,
bool force_optimize = false>
#else
template <typename Reader, uint64_t conf = sp_config::DEFAULT,
bool force_optimize = false>
#endif
class unpacker {
public:
unpacker() = delete;
unpacker(const unpacker &) = delete;
unpacker &operator=(const unpacker &) = delete;
STRUCT_PACK_INLINE unpacker(Reader &reader) : reader_(reader) {
#if __cpp_concepts < 201907L
static_assert(reader_t<Reader>,
"The writer type must satisfy requirements!");
#endif
}
template <std::size_t size_width, typename R, typename T>
friend STRUCT_PACK_INLINE struct_pack::err_code read(Reader &reader, T &t);
template <typename T, typename... Args>
STRUCT_PACK_MAY_INLINE struct_pack::err_code deserialize(T &t,
Args &...args) {
using Type = get_args_type<T, Args...>;
constexpr bool has_compatible =
check_if_compatible_element_exist<decltype(get_types<Type>())>();
if constexpr (has_compatible) {
data_len_ = reader_.tellg();
}
auto &&[err_code, buffer_len] = deserialize_metainfo<Type>();
if SP_UNLIKELY (err_code) {
return err_code;
}
if constexpr (has_compatible) {
data_len_ += buffer_len;
}
switch (size_type_) {
case 0:
err_code = deserialize_many<1, UINT64_MAX, true>(t, args...);
break;
#ifdef STRUCT_PACK_OPTIMIZE
case 1:
err_code = deserialize_many<2, UINT64_MAX, true>(t, args...);
break;
case 2:
err_code = deserialize_many<4, UINT64_MAX, true>(t, args...);
break;
case 3:
if constexpr (sizeof(std::size_t) >= 8) {
err_code = deserialize_many<8, UINT64_MAX, true>(t, args...);
}
else {
return struct_pack::errc::invalid_width_of_container_length;
}
break;
#else
case 3:
if constexpr (sizeof(std::size_t) < 8) {
return struct_pack::errc::invalid_width_of_container_length;
}
case 2:
case 1:
err_code = deserialize_many<2, UINT64_MAX, true>(t, args...);
break;
#endif
default:
unreachable();
}
if constexpr (has_compatible) {
if SP_UNLIKELY (err_code) {
return err_code;
}
constexpr std::size_t sz = compatible_version_number<Type>.size();
err_code = deserialize_compatibles<T, Args...>(
t, std::make_index_sequence<sz>{}, args...);
}
return err_code;
}
template <typename T, typename... Args>
STRUCT_PACK_MAY_INLINE struct_pack::err_code deserialize_with_len(
std::size_t &len, T &t, Args &...args) {
using Type = get_args_type<T, Args...>;
constexpr bool has_compatible =
check_if_compatible_element_exist<decltype(get_types<Type>())>();
if constexpr (has_compatible) {
data_len_ = reader_.tellg();
}
auto &&[err_code, buffer_len] = deserialize_metainfo<Type>();
len = buffer_len;
if SP_UNLIKELY (err_code) {
return err_code;
}
if constexpr (has_compatible) {
data_len_ += buffer_len;
}
switch (size_type_) {
case 0:
err_code = deserialize_many<1, UINT64_MAX, true>(t, args...);
break;
#ifdef STRUCT_PACK_OPTIMIZE
case 1:
err_code = deserialize_many<2, UINT64_MAX, true>(t, args...);
break;
case 2:
err_code = deserialize_many<4, UINT64_MAX, true>(t, args...);
break;
case 3:
if constexpr (sizeof(std::size_t) >= 8) {
err_code = deserialize_many<8, UINT64_MAX, true>(t, args...);
}
else {
return struct_pack::errc::invalid_width_of_container_length;
}
break;
#else
case 3:
if constexpr (sizeof(std::size_t) < 8) {
return struct_pack::errc::invalid_width_of_container_length;
}
case 2:
case 1:
err_code = deserialize_many<2, UINT64_MAX, true>(t, args...);
break;
#endif
default:
unreachable();
}
if constexpr (has_compatible) {
if SP_UNLIKELY (err_code) {
return err_code;
}
constexpr std::size_t sz = compatible_version_number<Type>.size();
err_code = deserialize_compatibles<T, Args...>(
t, std::make_index_sequence<sz>{}, args...);
}
return err_code;
}
template <typename U, size_t I>
STRUCT_PACK_MAY_INLINE struct_pack::err_code get_field(
std::tuple_element_t<I, decltype(get_types<U>())> &field) {
using T = remove_cvref_t<U>;
using Type = get_args_type<T>;
constexpr bool has_compatible =
check_if_compatible_element_exist<decltype(get_types<Type>())>();
if constexpr (has_compatible) {
data_len_ = reader_.tellg();
}
auto &&[err_code, buffer_len] = deserialize_metainfo<T>();
if SP_UNLIKELY (err_code) {
return err_code;
}
if constexpr (has_compatible) {
data_len_ += buffer_len;
}
switch (size_type_) {
case 0:
err_code = get_field_impl<1, UINT64_MAX, U, I>(field);
break;
#ifdef STRUCT_PACK_OPTIMIZE
case 1:
err_code = get_field_impl<2, UINT64_MAX, U, I>(field);
break;
case 2:
err_code = get_field_impl<4, UINT64_MAX, U, I>(field);
break;
case 3:
if constexpr (sizeof(std::size_t) >= 8) {
err_code = get_field_impl<8, UINT64_MAX, U, I>(field);
}
else {
return struct_pack::errc::invalid_width_of_container_length;
}
break;
#else
case 3:
if constexpr (sizeof(std::size_t) < 8) {
return struct_pack::errc::invalid_width_of_container_length;
}
case 2:
case 1:
err_code = get_field_impl<2, UINT64_MAX, U, I>(field);
break;
#endif
default:
unreachable();
}
if constexpr (has_compatible) {
if SP_UNLIKELY (err_code) {
return err_code;
}
constexpr std::size_t sz = compatible_version_number<Type>.size();
err_code = deserialize_compatible_fields<U, I>(
field, std::make_index_sequence<sz>{});
}
return err_code;
}
template <typename T, typename... Args, size_t... I>
STRUCT_PACK_INLINE struct_pack::err_code deserialize_compatibles(
T &t, std::index_sequence<I...>, Args &...args) {
using Type = get_args_type<T, Args...>;
struct_pack::err_code err_code;
switch (size_type_) {
case 0:
([&] {
err_code =
deserialize_many<1, compatible_version_number<Type>[I], true>(
t, args...);
return err_code == errc::ok;
}() &&
...);
break;
#ifdef STRUCT_PACK_OPTIMIZE
case 1:
([&] {
err_code =
deserialize_many<2, compatible_version_number<Type>[I], true>(
t, args...);
return err_code == errc::ok;
}() &&
...);
break;
case 2:
([&] {
err_code =
deserialize_many<4, compatible_version_number<Type>[I], true>(
t, args...);
return err_code == errc::ok;
}() &&
...);
break;
case 3:
if constexpr (sizeof(std::size_t) >= 8) {
([&] {
err_code =
deserialize_many<8, compatible_version_number<Type>[I], true>(
t, args...);
return err_code == errc::ok;
}() &&
...);
}
else {
return struct_pack::errc::invalid_width_of_container_length;
}
break;
#else
case 3:
if constexpr (sizeof(std::size_t) < 8) {
return struct_pack::errc::invalid_width_of_container_length;
}
case 2:
case 1:
([&] {
err_code =
deserialize_many<2, compatible_version_number<Type>[I], true>(
t, args...);
return err_code == errc::ok;
}() &&
...);
break;
#endif
default:
unreachable();
}
if (size_type_ ==
UCHAR_MAX) { // reuse size_type_ as a tag that the buffer miss some
// compatible field, whic is legal.
err_code = {};
}
return err_code;
}
template <typename U, size_t I, size_t... Is>
STRUCT_PACK_INLINE struct_pack::err_code deserialize_compatible_fields(
std::tuple_element_t<I, decltype(get_types<U>())> &field,
std::index_sequence<Is...>) {
using T = remove_cvref_t<U>;
using Type = get_args_type<T>;
struct_pack::err_code err_code;
switch (size_type_) {
case 0:
([&] {
err_code =
get_field_impl<1, compatible_version_number<Type>[Is], U, I>(
field);
return err_code == errc::ok;
}() &&
...);
break;
#ifdef STRUCT_PACK_OPTIMIZE
case 1:
([&] {
err_code =
get_field_impl<2, compatible_version_number<Type>[Is], U, I>(
field);
return err_code == errc::ok;
}() &&
...);
break;
case 2:
([&] {
err_code =
get_field_impl<4, compatible_version_number<Type>[Is], U, I>(
field);
return err_code == errc::ok;
}() &&
...);
break;
case 3:
if constexpr (sizeof(std::size_t) >= 8) {
([&] {
err_code =
get_field_impl<8, compatible_version_number<Type>[Is], U, I>(
field);
return err_code == errc::ok;
}() &&
...);
}
else {
return errc::invalid_width_of_container_length;
}
break;
#else
case 3:
if constexpr (sizeof(std::size_t) < 8) {
return errc::invalid_width_of_container_length;
}
case 2:
case 1:
([&] {
err_code =
get_field_impl<2, compatible_version_number<Type>[Is], U, I>(
field);
return err_code == errc::ok;
}() &&
...);
break;
#endif
default:
unreachable();
}
if (size_type_ ==
UCHAR_MAX) { // reuse size_type_ as a tag that the buffer miss some
// compatible field, which is legal.
err_code = {};
}
return err_code;
}
template <std::size_t size_type, uint64_t version, typename U, size_t I>
STRUCT_PACK_INLINE struct_pack::err_code get_field_impl(
std::tuple_element_t<I, decltype(get_types<U>())> &field) {
using T = remove_cvref_t<U>;
T t;
struct_pack::err_code err_code;
if constexpr (tuple<T>) {
err_code = std::apply(
[&](auto &&...items) CONSTEXPR_INLINE_LAMBDA {
static_assert(I < sizeof...(items), "out of range");
return for_each<size_type, version, I>(field, items...);
},
t);
}
else if constexpr (std::is_class_v<T>) {
if constexpr (!user_defined_refl<T>)
static_assert(std::is_aggregate_v<remove_cvref_t<T>>,
"struct_pack only support aggregated type, or you should "
"add macro STRUCT_PACK_REFL(Type,field1,field2...)");
err_code = visit_members(t, [&](auto &&...items) CONSTEXPR_INLINE_LAMBDA {
static_assert(I < sizeof...(items), "out of range");
return for_each<size_type, version, I>(field, items...);
});
}
else {
static_assert(!sizeof(T), "illegal type");
}
return err_code;
}
template <typename size_type, typename version, typename NotSkip>
struct variant_construct_helper {
template <size_t index, typename unpack, typename variant_t>
static STRUCT_PACK_INLINE constexpr void run(unpack &unpacker,
variant_t &v) {
if constexpr (index >= std::variant_size_v<variant_t>) {
unreachable();
return;
}
else {
v = variant_t{std::in_place_index_t<index>{}};
unpacker.template deserialize_one<size_type::value, version::value,
NotSkip::value>(std::get<index>(v));
}
}
};
STRUCT_PACK_INLINE std::pair<struct_pack::err_code, std::uint64_t>
deserialize_compatible(unsigned compatible_sz_len) {
constexpr std::size_t sz[] = {0, 2, 4, 8};
auto len_sz = sz[compatible_sz_len];
std::size_t data_len = 0;
bool result;
switch (compatible_sz_len) {
case 1:
if SP_LIKELY (low_bytes_read_wrapper<2>(reader_, data_len)) {
return {errc{}, data_len};
}
break;
case 2:
if SP_LIKELY (low_bytes_read_wrapper<4>(reader_, data_len)) {
return {errc{}, data_len};
}
break;
case 3:
if constexpr (sizeof(std::size_t) >= 8) {
if SP_LIKELY (low_bytes_read_wrapper<8>(reader_, data_len)) {
return {errc{}, data_len};
}
}
else {
return {errc::invalid_width_of_container_length, 0};
}
break;
default:
unreachable();
}
return {errc::no_buffer_space, 0};
}
template <typename T>
STRUCT_PACK_INLINE struct_pack::err_code deserialize_type_literal() {
constexpr auto literal = struct_pack::get_type_literal<T>();
if constexpr (view_reader_t<Reader>) {
const char *buffer = reader_.read_view(literal.size() + 1);
if SP_UNLIKELY (!buffer) {
return errc::no_buffer_space;
}
if SP_UNLIKELY (memcmp(buffer, literal.data(), literal.size() + 1)) {
return errc::hash_conflict;
}
}
else {
char buffer[literal.size() + 1];
if SP_UNLIKELY (!read_bytes_array(reader_, buffer, literal.size() + 1)) {
return errc::no_buffer_space;
}
if SP_UNLIKELY (memcmp(buffer, literal.data(), literal.size() + 1)) {
return errc::hash_conflict;
}
}
return errc{};
}
template <class T>
STRUCT_PACK_INLINE std::pair<struct_pack::err_code, std::uint64_t>
deserialize_metainfo() {
uint32_t current_types_code;
if constexpr (check_if_disable_hash_head<conf, T>()) {
if constexpr (is_MD5_reader_wrapper<Reader>) {
static_assert(!sizeof(T),
"it's illegal if you want to deserialize a derived class "
"without md5");
}
else if constexpr (check_if_has_container<T>()) {
unsigned char metainfo;
if SP_UNLIKELY (!read_wrapper<sizeof(unsigned char)>(
reader_, (char *)&metainfo)) {
return {struct_pack::errc::no_buffer_space, 0};
}
size_type_ = (metainfo & 0b11000) >> 3;
return {};
}
else {
size_type_ = 0;
return {};
}
}
else {
if constexpr (is_MD5_reader_wrapper<Reader>) {
reader_.read_head((char *)&current_types_code);
if SP_LIKELY (current_types_code % 2 == 0) // unexist metainfo
{
size_type_ = 0;
return {};
}
}
else {
if SP_UNLIKELY (!read_wrapper<sizeof(uint32_t)>(
reader_, (char *)&current_types_code)) {
return {struct_pack::errc::no_buffer_space, 0};
}
constexpr uint32_t types_code = get_types_code<T>();
if SP_UNLIKELY ((current_types_code / 2) != (types_code / 2)) {
return {struct_pack::errc::invalid_buffer, 0};
}
if SP_LIKELY (current_types_code % 2 == 0) // unexist metainfo
{
size_type_ = 0;
return {};
}
}
unsigned char metainfo;
if SP_UNLIKELY (!read_wrapper<sizeof(unsigned char)>(reader_,
(char *)&metainfo)) {
return {struct_pack::errc::no_buffer_space, 0};
}
std::pair<err_code, std::uint64_t> ret;
auto compatible_sz_len = metainfo & 0b11;
if (compatible_sz_len) {
ret = deserialize_compatible(compatible_sz_len);
if SP_UNLIKELY (ret.first) {
return ret;
}
}
auto has_type_literal = metainfo & 0b100;
if (has_type_literal) {
auto ec = deserialize_type_literal<T>();
if SP_UNLIKELY (ec) {
return {ec, 0};
}
}
size_type_ = (metainfo & 0b11000) >> 3;
return ret;
}
}
template <size_t size_type, uint64_t version, bool NotSkip>
constexpr struct_pack::err_code STRUCT_PACK_INLINE deserialize_many() {
return {};
}
template <size_t size_type, uint64_t version, bool NotSkip,
uint64_t parent_tag = 0, typename First, typename... Args>
constexpr struct_pack::err_code STRUCT_PACK_INLINE
deserialize_many(First &&first_item, Args &&...items) {
auto code =
deserialize_one<size_type, version, NotSkip, parent_tag>(first_item);
if SP_UNLIKELY (code) {
return code;
}
if constexpr (sizeof...(items) > 0) {
return deserialize_many<size_type, version, NotSkip, parent_tag>(
items...);
}
else {
return code;
}
}
constexpr struct_pack::err_code STRUCT_PACK_INLINE
ignore_padding(std::size_t sz) {
if (sz > 0) {
return reader_.ignore(sz) ? errc{} : errc::no_buffer_space;
}
else {
return errc{};
}
}
template <uint64_t parent_tag, bool no_skip, std::size_t width,
std::size_t bitset_width, typename Arg>
constexpr struct_pack::err_code STRUCT_PACK_INLINE
deserialize_one_fast_varint(char (&vec)[bitset_width], unsigned int &i,
Arg &item) {
if constexpr (varint_t<Arg, parent_tag>) {
constexpr auto real_width = (std::min)(width, sizeof(Arg));
auto index = i++;
if (!(vec[index / 8] & (0b1 << (index % 8))))
return {};
if constexpr (!no_skip) {
return reader_.ignore(real_width) ? errc{} : errc::no_buffer_space;
}
else {
bool ec{};
if constexpr (std::is_unsigned_v<std::remove_reference_t<
decltype(get_varint_value(item))>>) {
get_varint_value(item) = 0;
bool ec = low_bytes_read_wrapper<real_width>(reader_,
get_varint_value(item));
if SP_UNLIKELY (!ec) {
return errc::no_buffer_space;
}
}
else {
typename int_t<real_width>::type target;
ec = read_wrapper<real_width>(reader_, (char *)&target);
if SP_UNLIKELY (!ec) {
return errc::no_buffer_space;
}
item = target;
}
return {};
}
}
else {
return {};
}
}
template <uint64_t parent_tag, bool no_skip, std::size_t width,
std::size_t bitset_width, typename Arg, typename... Args>
constexpr struct_pack::err_code STRUCT_PACK_INLINE
deserialize_fast_varint_helper(char (&vec)[bitset_width], unsigned int &i,
Arg &item, Args &...items) {
auto ec =
deserialize_one_fast_varint<parent_tag, no_skip, width>(vec, i, item);
if constexpr (sizeof...(items) > 0) {
if SP_UNLIKELY (ec) {
return ec;
}
else {
return deserialize_fast_varint_helper<parent_tag, no_skip, width>(
vec, i, items...);
}
}
else {
return ec;
}
}
template <uint64_t parent_tag, bool no_skip, typename... Args>
constexpr struct_pack::err_code STRUCT_PACK_INLINE
deserialize_fast_varint(Args &...items) {
constexpr auto cnt = calculate_fast_varint_count<parent_tag, Args...>();
constexpr auto bitset_size = ((cnt + 2) + 7) / 8;
if constexpr (cnt == 0) {
static_assert(cnt != 0, "ilegal branch");
return {};
}
else {
char vec[bitset_size];
if (auto ec = read_bytes_array(reader_, vec, bitset_size); !ec) {
return errc::no_buffer_space;
}
int width = !!(vec[cnt / 8] & (0b1 << (cnt % 8))) +
!!(vec[(cnt + 1) / 8] & (0b1 << ((cnt + 1) % 8))) * 2;
unsigned int i = 0;
struct_pack::err_code ec{};
switch (width) {
case 0:
ec = deserialize_fast_varint_helper<parent_tag, no_skip, 1>(vec, i,
items...);
break;
case 1:
ec = deserialize_fast_varint_helper<parent_tag, no_skip, 2>(vec, i,
items...);
break;
case 2:
ec = deserialize_fast_varint_helper<parent_tag, no_skip, 4>(vec, i,
items...);
break;
case 3:
if constexpr (has_64bits_varint<parent_tag, Args...>()) {
ec = deserialize_fast_varint_helper<parent_tag, no_skip, 8>(
vec, i, items...);
}
else {
return struct_pack::errc::invalid_buffer;
}
break;
default:
unreachable();
}
return ec;
}
}
template <size_t size_type, uint64_t version, bool NotSkip,
uint64_t parent_tag = 0, typename T>
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 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;
bool result{};
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;
}
}
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)>) {
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 (optional<type> || 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 (expected<type>) {
item = typename type::unexpected_type{typename type::error_type{}};
deserialize_one<size_type, version, NotSkip>(item.error());
}
else {
return {};
}
}
else {
if constexpr (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 {
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 STRUCT_PACK_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 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 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 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 (optional<type> || expected<type>) {
bool has_value = item.has_value();
if (!has_value) {
if constexpr (expected<type>) {
deserialize_one<size_type, version, NotSkip>(item.error());
}
}
else {
if constexpr (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;
}
// partial deserialize_to
template <size_t size_type, uint64_t version, size_t I, size_t FieldIndex,
typename FieldType, typename T>
STRUCT_PACK_INLINE constexpr bool set_value(struct_pack::err_code &err_code,
FieldType &field, T &&t) {
if constexpr (FieldIndex == I) {
static_assert(
std::is_same_v<remove_cvref_t<FieldType>, remove_cvref_t<T>>);
err_code = deserialize_one<size_type, version, true>(field);
return /*don't skip=*/true;
}
else {
err_code = deserialize_one<size_type, version, false>(t);
return /*skip=*/false;
}
}
template <int I, class... Ts>
decltype(auto) get_nth(Ts &&...ts) {
return std::get<I>(std::forward_as_tuple(ts...));
}
template <size_t size_type, uint64_t version, size_t FieldIndex,
typename FieldType, typename... Args, std::size_t... I>
STRUCT_PACK_INLINE constexpr void for_each_helper(struct_pack::err_code &code,
FieldType &field,
std::index_sequence<I...>,
Args &&...items) {
[[maybe_unused]] auto result =
(!set_value<size_type, version, I, FieldIndex>(code, field,
get_nth<I>(items...)) &&
...);
}
template <size_t size_type, uint64_t version, size_t FieldIndex,
typename FieldType, typename... Args>
STRUCT_PACK_INLINE constexpr struct_pack::err_code for_each(FieldType &field,
Args &&...items) {
struct_pack::err_code code{};
for_each_helper<size_type, version, FieldIndex, FieldType>(
code, field, std::make_index_sequence<sizeof...(Args)>(), items...);
return code;
}
public:
std::size_t data_len_;
private:
Reader &reader_;
unsigned char size_type_;
};
template <typename Reader>
struct MD5_reader_wrapper : public Reader {
MD5_reader_wrapper(Reader &&reader) : Reader(std::move(reader)) {
is_failed =
!read_wrapper<sizeof(head_chars)>(*(Reader *)this, (char *)&head_chars);
}
bool read_head(char *target) {
if SP_UNLIKELY (is_failed) {
return false;
}
memcpy(target, &head_chars, sizeof(head_chars));
return true;
}
Reader &&release_reader() { return std::move(*(Reader *)this); }
bool is_failed;
uint32_t get_md5() { return head_chars & 0xFFFFFFFE; }
private:
std::uint32_t head_chars;
std::size_t read_pos;
};
template <typename BaseClass, typename... DerivedClasses, typename Reader>
[[nodiscard]] STRUCT_PACK_INLINE struct_pack::err_code
deserialize_derived_class(std::unique_ptr<BaseClass> &base, Reader &reader) {
MD5_reader_wrapper wrapper{std::move(reader)};
if (wrapper.is_failed) {
return struct_pack::errc::no_buffer_space;
}
unpacker<MD5_reader_wrapper<Reader>> unpack{wrapper};
constexpr auto &MD5s = MD5_set<std::tuple<DerivedClasses...>>::value;
MD5_pair md5_pair{wrapper.get_md5(), 0};
auto result = std::lower_bound(MD5s.begin(), MD5s.end(), md5_pair);
if (result == MD5s.end() || result->md5 != md5_pair.md5) {
return struct_pack::errc::invalid_buffer;
}
auto ret = template_switch<
deserialize_derived_class_helper<std::tuple<DerivedClasses...>>>(
result->index, base, unpack);
reader = std::move(wrapper.release_reader());
return ret;
}
} // namespace detail
} // namespace struct_pack
#undef STRUCT_PACK_MAX_UNCONFIRM_PREREAD_SIZE