212 lines
6.4 KiB
C++
212 lines
6.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 <algorithm>
|
|
|
|
#include "reflection.hpp"
|
|
#include "util.h"
|
|
|
|
namespace struct_pack::detail {
|
|
namespace align {
|
|
|
|
template <typename T>
|
|
constexpr std::size_t alignment_impl();
|
|
|
|
template <typename T>
|
|
constexpr std::size_t alignment_v = alignment_impl<T>();
|
|
|
|
template <typename type, std::size_t... I>
|
|
constexpr std::size_t default_alignment_helper(std::index_sequence<I...>) {
|
|
return (std::max)(
|
|
{(is_compatible_v<remove_cvref_t<std::tuple_element_t<I, type>>>
|
|
? std::size_t{0}
|
|
: align::alignment_v<
|
|
remove_cvref_t<std::tuple_element_t<I, type>>>)...});
|
|
}
|
|
|
|
template <typename T>
|
|
constexpr std::size_t default_alignment() {
|
|
if constexpr (!is_trivial_serializable<T>::value && !is_trivial_view_v<T>) {
|
|
using type = decltype(get_types<T>());
|
|
return default_alignment_helper<type>(
|
|
std::make_index_sequence<std::tuple_size_v<type>>());
|
|
}
|
|
else if constexpr (is_trivial_view_v<T>) {
|
|
return std::alignment_of_v<typename T::value_type>;
|
|
}
|
|
else {
|
|
return std::alignment_of_v<T>;
|
|
}
|
|
}
|
|
template <typename T>
|
|
constexpr std::size_t default_alignment_v = default_alignment<T>();
|
|
|
|
template <typename T>
|
|
constexpr std::size_t alignment_impl();
|
|
|
|
template <typename type, std::size_t... I>
|
|
constexpr std::size_t pack_alignment_impl_helper(std::index_sequence<I...>) {
|
|
return (std::max)(
|
|
{(is_compatible_v<remove_cvref_t<std::tuple_element_t<I, type>>>
|
|
? std::size_t{0}
|
|
: align::alignment_v<
|
|
remove_cvref_t<std::tuple_element_t<I, type>>>)...});
|
|
}
|
|
|
|
template <typename T>
|
|
constexpr std::size_t pack_alignment_impl() {
|
|
static_assert(std::is_class_v<T>);
|
|
static_assert(!is_trivial_view_v<T>);
|
|
constexpr auto ret = struct_pack::pack_alignment_v<T>;
|
|
static_assert(ret == 0 || ret == 1 || ret == 2 || ret == 4 || ret == 8 ||
|
|
ret == 16);
|
|
if constexpr (ret == 0) {
|
|
using type = decltype(get_types<T>());
|
|
return pack_alignment_impl_helper<type>(
|
|
std::make_index_sequence<std::tuple_size_v<type>>());
|
|
}
|
|
else {
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
constexpr std::size_t pack_alignment_v = pack_alignment_impl<T>();
|
|
|
|
template <typename T>
|
|
constexpr std::size_t alignment_impl() {
|
|
if constexpr (struct_pack::alignment_v<T> == 0 &&
|
|
struct_pack::pack_alignment_v<T> == 0) {
|
|
return default_alignment_v<T>;
|
|
}
|
|
else if constexpr (struct_pack::alignment_v<T> != 0) {
|
|
if constexpr (is_trivial_serializable<T>::value) {
|
|
static_assert(default_alignment_v<T> == alignment_v<T>);
|
|
}
|
|
constexpr auto ret = struct_pack::alignment_v<T>;
|
|
static_assert(
|
|
[](std::size_t align) constexpr {
|
|
while (align % 2 == 0) {
|
|
align /= 2;
|
|
}
|
|
return align == 1;
|
|
}(ret),
|
|
"alignment should be power of 2");
|
|
return ret;
|
|
}
|
|
else {
|
|
if constexpr (is_trivial_serializable<T>::value) {
|
|
return default_alignment_v<T>;
|
|
}
|
|
else {
|
|
return pack_alignment_v<T>;
|
|
}
|
|
}
|
|
}
|
|
template <typename P, typename T, std::size_t I>
|
|
struct calculate_trival_obj_size {
|
|
constexpr void operator()(std::size_t &total);
|
|
};
|
|
|
|
template <typename P, typename T, std::size_t I>
|
|
struct calculate_padding_size_impl {
|
|
constexpr void operator()(
|
|
std::size_t &offset,
|
|
std::array<std::size_t, struct_pack::members_count<P> + 1>
|
|
&padding_size) {
|
|
if constexpr (is_compatible_v<T>) {
|
|
padding_size[I] = 0;
|
|
}
|
|
else if constexpr (is_trivial_view_v<T>) {
|
|
calculate_padding_size_impl<P, typename T::value_type, I>{}(offset,
|
|
padding_size);
|
|
}
|
|
else {
|
|
if (offset % align::alignment_v<T>) {
|
|
padding_size[I] =
|
|
(std::min)(align::pack_alignment_v<P> - 1,
|
|
align::alignment_v<T> - offset % align::alignment_v<T>);
|
|
}
|
|
else {
|
|
padding_size[I] = 0;
|
|
}
|
|
offset += padding_size[I];
|
|
if constexpr (is_trivial_serializable<T>::value)
|
|
offset += sizeof(T);
|
|
else {
|
|
for_each<T, calculate_trival_obj_size>(offset);
|
|
static_assert(is_trivial_serializable<T, true>::value);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
template <typename T>
|
|
constexpr auto calculate_padding_size() {
|
|
std::array<std::size_t, struct_pack::members_count<T> + 1> padding_size{};
|
|
std::size_t offset = 0;
|
|
for_each<T, calculate_padding_size_impl>(offset, padding_size);
|
|
if (offset % align::alignment_v<T>) {
|
|
padding_size[struct_pack::members_count<T>] =
|
|
align::alignment_v<T> - offset % align::alignment_v<T>;
|
|
}
|
|
else {
|
|
padding_size[struct_pack::members_count<T>] = 0;
|
|
}
|
|
return padding_size;
|
|
}
|
|
template <typename T>
|
|
constexpr std::array<std::size_t, struct_pack::members_count<T> + 1>
|
|
padding_size = calculate_padding_size<T>();
|
|
template <typename T>
|
|
constexpr std::size_t get_total_padding_size() {
|
|
std::size_t sum = 0;
|
|
for (auto &e : padding_size<T>) {
|
|
sum += e;
|
|
}
|
|
return sum;
|
|
};
|
|
template <typename T>
|
|
constexpr std::size_t total_padding_size = get_total_padding_size<T>();
|
|
|
|
template <typename P, typename T, std::size_t I>
|
|
using calculate_trival_obj_size_wrapper = calculate_trival_obj_size<P, T, I>;
|
|
|
|
template <typename P, typename T, std::size_t I>
|
|
constexpr void calculate_trival_obj_size<P, T, I>::operator()(
|
|
std::size_t &total) {
|
|
if constexpr (I == 0) {
|
|
total += total_padding_size<P>;
|
|
}
|
|
if constexpr (!is_compatible_v<T>) {
|
|
if constexpr (is_trivial_serializable<T>::value) {
|
|
total += sizeof(T);
|
|
}
|
|
else if constexpr (is_trivial_view_v<T>) {
|
|
total += sizeof(typename T::value_type);
|
|
}
|
|
else {
|
|
static_assert(is_trivial_serializable<T, true>::value);
|
|
std::size_t offset = 0;
|
|
for_each<T, calculate_trival_obj_size_wrapper>(offset);
|
|
total += offset;
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace align
|
|
} // namespace struct_pack::detail
|