286 lines
8.0 KiB
C++
286 lines
8.0 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 <array>
|
|
#include <cstddef>
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <type_traits>
|
|
#include <vector>
|
|
|
|
#include "marco.h"
|
|
|
|
namespace struct_pack::detail {
|
|
|
|
template <typename T>
|
|
constexpr std::string_view get_raw_name() {
|
|
#ifdef _MSC_VER
|
|
return __FUNCSIG__;
|
|
#else
|
|
return __PRETTY_FUNCTION__;
|
|
#endif
|
|
}
|
|
|
|
template <auto T>
|
|
constexpr std::string_view get_raw_name() {
|
|
#ifdef _MSC_VER
|
|
return __FUNCSIG__;
|
|
#else
|
|
return __PRETTY_FUNCTION__;
|
|
#endif
|
|
}
|
|
|
|
template <typename T>
|
|
inline constexpr std::string_view type_string() {
|
|
constexpr std::string_view sample = get_raw_name<int>();
|
|
constexpr size_t pos = sample.find("int");
|
|
constexpr std::string_view str = get_raw_name<T>();
|
|
constexpr auto next1 = str.rfind(sample[pos + 3]);
|
|
#if defined(_MSC_VER)
|
|
constexpr std::size_t npos = str.find_first_of(" ", pos);
|
|
if (npos != std::string_view::npos)
|
|
return str.substr(npos + 1, next1 - npos - 1);
|
|
else
|
|
return str.substr(pos, next1 - pos);
|
|
#else
|
|
return str.substr(pos, next1 - pos);
|
|
#endif
|
|
}
|
|
|
|
#if __cpp_concepts >= 201907L
|
|
constexpr bool is_string_reserve_shrink = requires { std::string{}.reserve(); };
|
|
#else
|
|
constexpr bool is_string_reserve_shrink = true;
|
|
#endif
|
|
|
|
template <typename T>
|
|
using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>;
|
|
|
|
[[noreturn]] inline void unreachable() {
|
|
// Uses compiler specific extensions if possible.
|
|
// Even if no extension is used, undefined behavior is still raised by
|
|
// an empty function body and the noreturn attribute.
|
|
#ifdef __GNUC__ // GCC, Clang, ICC
|
|
__builtin_unreachable();
|
|
#elif defined(_MSC_VER) // msvc
|
|
__assume(false);
|
|
#endif
|
|
}
|
|
|
|
template <typename T>
|
|
[[noreturn]] constexpr T declval() {
|
|
unreachable();
|
|
}
|
|
|
|
template <typename T, std::size_t sz>
|
|
constexpr void STRUCT_PACK_INLINE compile_time_sort(std::array<T, sz> &array) {
|
|
// FIXME: use faster compile-time sort
|
|
for (std::size_t i = 0; i < array.size(); ++i) {
|
|
for (std::size_t j = i + 1; j < array.size(); ++j) {
|
|
if (array[i] > array[j]) {
|
|
auto tmp = array[i];
|
|
array[i] = array[j];
|
|
array[j] = tmp;
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
template <typename T, std::size_t sz>
|
|
constexpr std::size_t STRUCT_PACK_INLINE
|
|
calculate_uniqued_size(const std::array<T, sz> &input) {
|
|
std::size_t unique_cnt = sz;
|
|
for (std::size_t i = 1; i < input.size(); ++i) {
|
|
if (input[i] == input[i - 1]) {
|
|
--unique_cnt;
|
|
}
|
|
}
|
|
return unique_cnt;
|
|
}
|
|
|
|
template <typename T, std::size_t sz1, std::size_t sz2>
|
|
constexpr void STRUCT_PACK_INLINE compile_time_unique(
|
|
const std::array<T, sz1> &input, std::array<T, sz2> &output) {
|
|
std::size_t j = 0;
|
|
static_assert(sz1 != 0, "not allow empty input!");
|
|
output[0] = input[0];
|
|
for (std::size_t i = 1; i < input.size(); ++i) {
|
|
if (input[i] != input[i - 1]) {
|
|
output[++j] = input[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifndef __clang__
|
|
#define struct_pack_has_feature(X) false
|
|
#else
|
|
#define struct_pack_has_feature __has_feature
|
|
#endif
|
|
|
|
#if __cpp_lib_string_resize_and_overwrite >= 202110L
|
|
template <typename ch>
|
|
inline void resize(std::basic_string<ch> &str, std::size_t sz) {
|
|
str.resize_and_overwrite(sz, [sz](ch *, std::size_t) {
|
|
return sz;
|
|
});
|
|
}
|
|
#elif (defined(_MSC_VER) && _MSC_VER <= 1920)
|
|
// old msvc don't support visit private, discard it.
|
|
|
|
#else
|
|
|
|
template <typename Function, Function func_ptr>
|
|
class string_thief {
|
|
public:
|
|
friend void string_set_length_hacker(std::string &self, std::size_t sz) {
|
|
#if defined(_MSVC_STL_VERSION)
|
|
(self.*func_ptr)._Myval2._Mysize = sz;
|
|
#else
|
|
#if defined(_LIBCPP_VERSION)
|
|
(self.*func_ptr)(sz);
|
|
#else
|
|
#if (_GLIBCXX_USE_CXX11_ABI == 0) && defined(__GLIBCXX__)
|
|
(self.*func_ptr)()->_M_set_length_and_sharable(sz);
|
|
#else
|
|
#if defined(__GLIBCXX__)
|
|
(self.*func_ptr)(sz);
|
|
#endif
|
|
#endif
|
|
#endif
|
|
#endif
|
|
}
|
|
};
|
|
|
|
#if defined(__GLIBCXX__) // libstdc++
|
|
#if (_GLIBCXX_USE_CXX11_ABI == 0)
|
|
template class string_thief<decltype(&std::string::_M_rep),
|
|
&std::string::_M_rep>;
|
|
#else
|
|
template class string_thief<decltype(&std::string::_M_set_length),
|
|
&std::string::_M_set_length>;
|
|
#endif
|
|
#elif defined(_LIBCPP_VERSION)
|
|
template class string_thief<decltype(&std::string::__set_size),
|
|
&std::string::__set_size>;
|
|
#elif defined(_MSVC_STL_VERSION)
|
|
template class string_thief<decltype(&std::string::_Mypair),
|
|
&std::string::_Mypair>;
|
|
#endif
|
|
|
|
void string_set_length_hacker(std::string &, std::size_t);
|
|
|
|
template <typename ch>
|
|
inline void resize(std::basic_string<ch> &raw_str, std::size_t sz) {
|
|
std::string &str = *reinterpret_cast<std::string *>(&raw_str);
|
|
#if defined(_GLIBCXX_USE_CXX11_ABI)
|
|
constexpr bool is_use_cxx11_abi = _GLIBCXX_USE_CXX11_ABI;
|
|
#else
|
|
constexpr bool is_use_cxx11_abi = true;
|
|
#endif
|
|
if constexpr (std::is_same_v<ch, char> == false &&
|
|
is_use_cxx11_abi == false) {
|
|
raw_str.resize(sz);
|
|
}
|
|
else {
|
|
#if defined(__SANITIZE_ADDRESS__) || \
|
|
struct_pack_has_feature(address_sanitizer) || \
|
|
(!defined(NDEBUG) && defined(_MSVC_STL_VERSION))
|
|
raw_str.resize(sz);
|
|
#elif defined(__GLIBCXX__) || defined(_LIBCPP_VERSION) || \
|
|
defined(_MSVC_STL_VERSION)
|
|
if constexpr (is_string_reserve_shrink) {
|
|
if (sz > raw_str.capacity()) {
|
|
str.reserve(sz * sizeof(ch));
|
|
}
|
|
}
|
|
else {
|
|
str.reserve(sz * sizeof(ch));
|
|
}
|
|
string_set_length_hacker(str, sz);
|
|
for (auto i = sz; i < sz + sizeof(ch); ++i) str[i] = '\0';
|
|
#else
|
|
raw_str.resize(sz);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
#if (defined(_MSC_VER) && _MSC_VER <= 1920)
|
|
#else
|
|
void vector_set_length_hacker(std::vector<char> &self, std::size_t sz);
|
|
|
|
template <typename Function, Function func_ptr>
|
|
class vector_thief {
|
|
public:
|
|
friend void vector_set_length_hacker(std::vector<char> &self,
|
|
std::size_t sz) {
|
|
#if defined(_MSVC_STL_VERSION)
|
|
(self.*func_ptr)._Myval2._Mylast = self.data() + sz;
|
|
#else
|
|
#if defined(_LIBCPP_VERSION)
|
|
#if _LIBCPP_VERSION < 14000
|
|
((*(std::__vector_base<char, std::allocator<char>> *)(&self)).*func_ptr) =
|
|
self.data() + sz;
|
|
#else
|
|
(self.*func_ptr) = self.data() + sz;
|
|
#endif
|
|
#else
|
|
#if defined(__GLIBCXX__)
|
|
((*(std::_Vector_base<char, std::allocator<char>> *)(&self)).*func_ptr)
|
|
._M_finish = self.data() + sz;
|
|
#endif
|
|
#endif
|
|
#endif
|
|
}
|
|
};
|
|
|
|
#if defined(__GLIBCXX__) // libstdc++
|
|
template class vector_thief<decltype(&std::vector<char>::_M_impl),
|
|
&std::vector<char>::_M_impl>;
|
|
#elif defined(_LIBCPP_VERSION)
|
|
template class vector_thief<decltype(&std::vector<char>::__end_),
|
|
&std::vector<char>::__end_>;
|
|
#elif defined(_MSVC_STL_VERSION)
|
|
template class vector_thief<decltype(&std::vector<char>::_Mypair),
|
|
&std::vector<char>::_Mypair>;
|
|
#endif
|
|
|
|
template <typename ch>
|
|
inline void resize(std::vector<ch> &raw_vec, std::size_t sz) {
|
|
#if defined(__SANITIZE_ADDRESS__) || struct_pack_has_feature(address_sanitizer)
|
|
raw_vec.resize(sz);
|
|
#elif defined(__GLIBCXX__) || \
|
|
(defined(_LIBCPP_VERSION) && defined(_LIBCPP_HAS_NO_ASAN)) || \
|
|
defined(_MSVC_STL_VERSION)
|
|
std::vector<char> &vec = *reinterpret_cast<std::vector<char> *>(&raw_vec);
|
|
vec.reserve(sz * sizeof(ch));
|
|
vector_set_length_hacker(vec, sz * sizeof(ch));
|
|
#else
|
|
raw_vec.resize(sz);
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
#undef struct_pack_has_feature
|
|
|
|
template <typename T>
|
|
inline void resize(T &str, std::size_t sz) {
|
|
str.resize(sz);
|
|
}
|
|
|
|
} // namespace struct_pack::detail
|