include/ylt/struct_pack/alignment.hpp (180 lines of code) (raw):

/* * 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 (align::alignment_v < T >> 0 && 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