yalantinglibs/include/ylt/struct_pack/derived_helper.hpp

230 lines
7.4 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 <cassert>
#include <cstdint>
#include <memory>
#include <tuple>
#include "error_code.hpp"
#include "marco.h"
#include "size_info.hpp"
#include "util.h"
namespace struct_pack {
template <typename... Args>
STRUCT_PACK_INLINE constexpr std::uint32_t get_type_code();
namespace detail {
struct MD5_pair {
uint32_t md5;
uint32_t index;
constexpr friend bool operator<(const MD5_pair &l, const MD5_pair &r) {
return l.md5 < r.md5;
}
constexpr friend bool operator>(const MD5_pair &l, const MD5_pair &r) {
return l.md5 > r.md5;
}
constexpr friend bool operator==(const MD5_pair &l, const MD5_pair &r) {
return l.md5 == r.md5;
}
};
template <typename T>
constexpr uint32_t get_types_code();
template <typename DerivedClasses>
struct MD5_set {
static constexpr int size = std::tuple_size_v<DerivedClasses>;
static_assert(size <= 256);
private:
template <std::size_t... Index>
static constexpr std::array<MD5_pair, size> calculate_md5(
std::index_sequence<Index...>) {
std::array<MD5_pair, size> md5{};
((md5[Index] =
MD5_pair{
get_types_code<std::tuple_element_t<Index, DerivedClasses>>(),
Index}),
...);
compile_time_sort(md5);
return md5;
}
static constexpr std::size_t has_hash_collision_impl() {
for (std::size_t i = 1; i < size; ++i) {
if (value[i - 1] == value[i]) {
return value[i].index;
}
}
return 0;
}
public:
static constexpr std::array<MD5_pair, size> value =
calculate_md5(std::make_index_sequence<size>());
static constexpr std::size_t has_hash_collision = has_hash_collision_impl();
};
template <typename BaseClass, typename DerivedClasses>
struct public_base_class_checker {
static_assert(std::tuple_size_v<DerivedClasses> <= 256);
private:
template <std::size_t... Index>
static constexpr bool calculate_md5(std::index_sequence<Index...>) {
return (std::is_base_of_v<BaseClass,
std::tuple_element_t<Index, DerivedClasses>> &&
...);
}
public:
static constexpr bool value = public_base_class_checker::calculate_md5(
std::make_index_sequence<std::tuple_size_v<DerivedClasses>>());
};
template <typename DerivedClasses>
struct deserialize_derived_class_helper {
template <size_t index, typename BaseClass, typename unpack>
static STRUCT_PACK_INLINE constexpr struct_pack::err_code run(
std::unique_ptr<BaseClass> &base, unpack &unpacker) {
if constexpr (index >= std::tuple_size_v<DerivedClasses>) {
unreachable();
}
else {
using derived_class = std::tuple_element_t<index, DerivedClasses>;
base = std::make_unique<derived_class>();
return unpacker.deserialize(*(derived_class *)base.get());
}
}
};
template <typename T, uint64_t parent_tag = 0>
constexpr size_info inline calculate_one_size(const T &item);
template <typename DerivedClasses>
struct calculate_one_size_derived_class_helper {
template <size_t index, typename BaseClass>
static STRUCT_PACK_INLINE constexpr size_info run(BaseClass *base) {
if constexpr (index >= std::tuple_size_v<DerivedClasses>) {
unreachable();
}
else {
using derived_class = std::tuple_element_t<index, DerivedClasses>;
#ifdef STRUCT_PACK_RTTI_ENABLED
assert(dynamic_cast<derived_class *>(base));
#endif
return calculate_one_size(*(derived_class *)base);
}
}
};
template <typename DerivedClasses, typename size_type, typename version>
struct serialize_one_derived_class_helper {
template <size_t index, typename packer, typename BaseClass>
static STRUCT_PACK_INLINE constexpr void run(packer *self, BaseClass *base) {
if constexpr (index >= std::tuple_size_v<DerivedClasses>) {
unreachable();
}
else {
using derived_class = std::tuple_element_t<index, DerivedClasses>;
#ifdef STRUCT_PACK_RTTI_ENABLED
assert(dynamic_cast<derived_class *>(base));
#endif
self->template serialize_one<size_type::value, version::value>(
*(derived_class *)base);
}
}
};
template <typename DerivedClasses, typename size_type, typename version,
typename NotSkip>
struct deserialize_one_derived_class_helper {
template <size_t index, typename unpacker, typename Pointer>
static STRUCT_PACK_INLINE constexpr struct_pack::err_code run(unpacker *self,
Pointer &base) {
if constexpr (index >= std::tuple_size_v<DerivedClasses>) {
unreachable();
}
else {
using derived_class = std::tuple_element_t<index, DerivedClasses>;
base = std::make_unique<derived_class>();
return self->template deserialize_one<size_type::value, version::value,
NotSkip::value>(
*(derived_class *)base.get());
}
}
};
template <typename Base>
using derived_class_set_t = decltype(struct_pack_derived_decl((Base *)nullptr));
template <typename Base>
constexpr std::size_t search_type_by_md5(uint32_t id, bool &ok) {
constexpr auto &MD5s = MD5_set<derived_class_set_t<Base>>::value;
auto result = std::lower_bound(MD5s.begin(), MD5s.end(), MD5_pair{id, 0});
ok = (result != MD5s.end() && result->md5 == id);
if (!ok)
return MD5s.size();
else
return result->index;
}
template <typename T>
std::uint32_t get_struct_pack_id_impl() {
if constexpr (std::is_abstract_v<T>) {
struct_pack::detail::unreachable();
}
else {
return struct_pack::get_type_code<T>();
}
}
template <typename Base, typename... Derives>
constexpr auto derived_decl_impl() {
if constexpr (std::is_abstract_v<Base>) {
return struct_pack::detail::declval<std::tuple<Derives...>>();
}
else {
return struct_pack::detail::declval<std::tuple<Base, Derives...>>();
}
}
template <typename Base, typename... Derives>
constexpr bool check_ID_collision() {
if constexpr (std::is_abstract_v<Base>) {
constexpr auto has_collision = struct_pack::detail::MD5_set<
std::tuple<Derives...>>::has_hash_collision;
if constexpr (has_collision != 0) {
static_assert(
!sizeof(std::tuple_element_t<has_collision, std::tuple<Derives...>>),
"ID collision happened, consider add member `static "
"constexpr uint64_t struct_pack_id` for collision type. ");
}
}
else {
constexpr auto has_collision = struct_pack::detail::MD5_set<
std::tuple<Base, Derives...>>::has_hash_collision;
if constexpr (has_collision != 0) {
static_assert(!sizeof(std::tuple_element_t<has_collision,
std::tuple<Base, Derives...>>),
"ID collision happened, consider add member `static "
"constexpr uint64_t struct_pack_id` for collision type. ");
}
}
return true;
}
} // namespace detail
} // namespace struct_pack