yalantinglibs/include/ylt/struct_pack/alignment.hpp

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