You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
246 lines
8.6 KiB
246 lines
8.6 KiB
1 week ago
|
#pragma once
|
||
|
|
||
|
#include <type_traits>
|
||
|
#include <limits>
|
||
|
#include <array>
|
||
|
#include <cstddef>
|
||
|
#include <new>
|
||
|
#include <utility>
|
||
|
#include <memory>
|
||
|
|
||
|
#include <utils/no_unique_address_emulation.hpp>
|
||
|
|
||
|
namespace detail
|
||
|
{
|
||
|
template <std::size_t _Np, typename... _Types>
|
||
|
struct __nth__ {
|
||
|
};
|
||
|
|
||
|
template <typename _Tp0, typename... _Rest>
|
||
|
struct __nth__<0, _Tp0, _Rest...> {
|
||
|
using type = _Tp0;
|
||
|
};
|
||
|
|
||
|
template <typename _Tp0, typename _Tp1, typename... _Rest>
|
||
|
struct __nth__<1, _Tp0, _Tp1, _Rest...> {
|
||
|
using type = _Tp1;
|
||
|
};
|
||
|
|
||
|
template <typename _Tp0, typename _Tp1, typename _Tp2, typename... _Rest>
|
||
|
struct __nth__<2, _Tp0, _Tp1, _Tp2, _Rest...> {
|
||
|
using type = _Tp2;
|
||
|
};
|
||
|
|
||
|
template <const std::size_t _Np, typename _Tp0, typename _Tp1, typename _Tp2, typename... _Rest>
|
||
|
struct __nth__<_Np, _Tp0, _Tp1, _Tp2, _Rest...> : __nth__<_Np - 3, _Rest...> {
|
||
|
};
|
||
|
|
||
|
template <const std::size_t _Idx, typename... _Types>
|
||
|
using _Select_nth_type = typename __nth__<_Idx, _Types...>::type;
|
||
|
|
||
|
template <unsigned long long _Val, typename... _Ints>
|
||
|
struct _Select_int_base;
|
||
|
|
||
|
template <unsigned long long _Val, typename _IntType, typename... _Ints>
|
||
|
struct _Select_int_base<_Val, _IntType, _Ints...> : std::conditional_t<(_Val <= std::numeric_limits<_IntType>::max()),
|
||
|
std::integral_constant<_IntType, (_IntType)_Val>,
|
||
|
_Select_int_base<_Val, _Ints...>> {
|
||
|
};
|
||
|
|
||
|
template <unsigned long long _Val>
|
||
|
struct _Select_int_base<_Val> {
|
||
|
};
|
||
|
|
||
|
template <unsigned long long _Val>
|
||
|
using _Select_int =
|
||
|
typename _Select_int_base<_Val, unsigned char, unsigned short, unsigned int, unsigned long, unsigned long long>::type::
|
||
|
value_type;
|
||
|
} // namespace detail
|
||
|
|
||
|
namespace impl::variadic
|
||
|
{
|
||
|
#pragma packed(push, 1)
|
||
|
|
||
|
template <typename _Index_type>
|
||
|
struct _Index_base {
|
||
|
_Index_type __this_;
|
||
|
_Index_type __next_;
|
||
|
|
||
|
constexpr _Index_base(const _Index_type __i, const _Index_type __j) noexcept : __this_{__i}, __next_{__j} {}
|
||
|
};
|
||
|
|
||
|
#pragma packed(pop)
|
||
|
|
||
|
template <typename _Ty, typename _Index_type>
|
||
|
struct _Element : public _Index_base<_Index_type>,
|
||
|
public no_unique_address_emulation<_Ty> {
|
||
|
template <typename... _Args>
|
||
|
explicit constexpr _Element(const std::size_t __i, _Args&&... __ctors) //
|
||
|
noexcept(std::is_nothrow_constructible_v<_Ty, _Args...>)
|
||
|
: _Index_base<_Index_type>{static_cast<_Index_type>(__i), std::numeric_limits<_Index_type>::max()},
|
||
|
no_unique_address_emulation<_Ty>(std::forward<_Args>(__ctors)...)
|
||
|
{
|
||
|
}
|
||
|
};
|
||
|
|
||
|
template <typename _Visitor>
|
||
|
struct __vtable__ {
|
||
|
template <std::size_t _Idx, typename _Ty = _Select_nth_type<_Idx, _Types...>>
|
||
|
static void _S_invoke(_Visitor&& __visitor, void* __ptr) noexcept(std::is_nothrow_invocable_v<_Visitor, _Ty&>)
|
||
|
{
|
||
|
auto* __box = std::launder(reinterpret_cast<_Box<_Ty>*>(__ptr));
|
||
|
std::forward<_Visitor>(__visitor)(__box->__val_);
|
||
|
}
|
||
|
|
||
|
template <std::size_t... _Indices>
|
||
|
static constexpr auto _S_make_vtab(std::index_sequence<_Indices...>) noexcept
|
||
|
{
|
||
|
return std::array<void (*)(_Visitor&&, void*), sizeof...(_Types)>{&_S_invoke<_Indices>...};
|
||
|
}
|
||
|
|
||
|
static constexpr auto __vtab_ = _S_make_vtab(std::index_sequence_for<_Types...>{});
|
||
|
};
|
||
|
} // namespace impl::variadic
|
||
|
|
||
|
template <const std::size_t __Sp_, typename... _Types>
|
||
|
struct variadic {
|
||
|
using _Index_type = detail::_Select_int<sizeof...(_Types)>;
|
||
|
|
||
|
struct _Idx_base {
|
||
|
_Index_type __this_;
|
||
|
_Index_type __next_;
|
||
|
} __attribute__((__packed__));
|
||
|
|
||
|
template <typename _Ty>
|
||
|
struct _Box : public _Idx_base {
|
||
|
[[__no_unique_address__]] _Ty __val_;
|
||
|
|
||
|
template <typename... _Args>
|
||
|
explicit constexpr _Box(const std::size_t __i,
|
||
|
_Args&&... __ctors) noexcept(std::is_nothrow_constructible_v<_Ty, _Args...>)
|
||
|
: _Idx_base{static_cast<_Index_type>(__i), std::numeric_limits<_Index_type>::max()},
|
||
|
__val_(std::forward<_Args>(__ctors)...)
|
||
|
{
|
||
|
}
|
||
|
};
|
||
|
|
||
|
static constexpr std::size_t __max_align_ = std::max({std::alignment_of_v<_Box<_Types>>...});
|
||
|
|
||
|
template <typename _Visitor>
|
||
|
struct __vtable__ {
|
||
|
template <std::size_t _Idx, typename _Ty = _Select_nth_type<_Idx, _Types...>>
|
||
|
static void _S_invoke(_Visitor&& __visitor, void* __ptr) noexcept(std::is_nothrow_invocable_v<_Visitor, _Ty&>)
|
||
|
{
|
||
|
auto* __box = std::launder(reinterpret_cast<_Box<_Ty>*>(__ptr));
|
||
|
std::forward<_Visitor>(__visitor)(__box->__val_);
|
||
|
}
|
||
|
|
||
|
template <std::size_t... _Indices>
|
||
|
static constexpr auto _S_make_vtab(std::index_sequence<_Indices...>) noexcept
|
||
|
{
|
||
|
return std::array<void (*)(_Visitor&&, void*), sizeof...(_Types)>{&_S_invoke<_Indices>...};
|
||
|
}
|
||
|
|
||
|
static constexpr auto __vtab_ = _S_make_vtab(std::index_sequence_for<_Types...>{});
|
||
|
};
|
||
|
|
||
|
alignas(__max_align_) std::byte __data_[__Sp_];
|
||
|
void* __p_ = &__data_[0];
|
||
|
std::size_t __sz_ = __Sp_;
|
||
|
std::size_t __count_ = 0;
|
||
|
_Index_type* __next_ = nullptr;
|
||
|
|
||
|
template <typename _Ty>
|
||
|
[[__nodiscard__]] //
|
||
|
_Ty* _M_implicit_aligned_alloc(const std::size_t __align = alignof(_Ty)) noexcept
|
||
|
{
|
||
|
if (std::align(__align, sizeof(_Ty), __p_, __sz_)) [[__likely__]] {
|
||
|
_Ty* __result = std::launder(reinterpret_cast<_Ty*>(__p_));
|
||
|
__p_ = static_cast<std::byte*>(__p_) + sizeof(_Ty);
|
||
|
__sz_ -= sizeof(_Ty);
|
||
|
return __result;
|
||
|
}
|
||
|
return nullptr;
|
||
|
}
|
||
|
|
||
|
template <std::size_t _Idx, typename... _Args, typename _Result = _Select_nth_type<_Idx, _Types...>>
|
||
|
_Result& emplace_back(_Args&&... __ctors)
|
||
|
{
|
||
|
if (__next_) { *__next_ = static_cast<_Index_type>(_Idx); }
|
||
|
if (auto* __ptr = _M_implicit_aligned_alloc<_Box<_Result>>()) [[__likely__]] {
|
||
|
::new (__ptr) _Box<_Result>{_Idx, std::forward<_Args>(__ctors)...};
|
||
|
__next_ = &__ptr->__next_;
|
||
|
++__count_;
|
||
|
return __ptr->__val_;
|
||
|
}
|
||
|
throw std::bad_alloc{};
|
||
|
}
|
||
|
|
||
|
template <typename _Visitor>
|
||
|
void loop(_Visitor&& __visitor)
|
||
|
{
|
||
|
if (__count_ == 0) [[__unlikely__]] { return; }
|
||
|
|
||
|
static constexpr auto& __table_ = __vtable__<_Visitor&&>::__vtab_;
|
||
|
|
||
|
void* __current = &__data_[0];
|
||
|
const void* const __end = __p_;
|
||
|
|
||
|
while (__current < __end) [[__likely__]] {
|
||
|
auto* __base = std::launder(reinterpret_cast<_Idx_base*>(__current));
|
||
|
const auto __i = __base->__this_;
|
||
|
const auto __j = __base->__next_;
|
||
|
if (__i < sizeof...(_Types)) [[__likely__]] { __table_[__i](std::forward<_Visitor>(__visitor), __current); }
|
||
|
__current = _S_advance_ptr(__current, __i, __j);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
template <std::size_t... _Indices>
|
||
|
static void* _S_advance_ptr_impl(const void* __ptr,
|
||
|
const std::size_t __i,
|
||
|
const std::size_t __j,
|
||
|
std::index_sequence<_Indices...>) noexcept
|
||
|
{
|
||
|
static constexpr std::array<std::size_t, sizeof...(_Types)> __alignments_ = {std::alignment_of_v<_Box<_Types>>...};
|
||
|
static constexpr std::array<std::size_t, sizeof...(_Types)> __sizes_ = {sizeof(_Box<_Types>)...};
|
||
|
const auto __intptr = reinterpret_cast<std::uintptr_t>(__ptr) + __sizes_[__i];
|
||
|
if (__j == std::numeric_limits<_Index_type>::max()) {
|
||
|
// NOLINTNEXTLINE
|
||
|
return reinterpret_cast<void*>(__intptr);
|
||
|
}
|
||
|
|
||
|
const auto __align = __alignments_[__j];
|
||
|
const auto __result = (__intptr - 1U + __align) & -__align;
|
||
|
// NOLINTNEXTLINE
|
||
|
return reinterpret_cast<void*>(__result);
|
||
|
}
|
||
|
|
||
|
static void* _S_advance_ptr(const void* __ptr, const std::size_t __i, const std::size_t __j)
|
||
|
{
|
||
|
return _S_advance_ptr_impl(__ptr, __i, __j, std::index_sequence_for<_Types...>{});
|
||
|
}
|
||
|
|
||
|
public:
|
||
|
[[__nodiscard__]] std::size_t size() const noexcept { return __count_; }
|
||
|
|
||
|
[[__nodiscard__]] bool empty() const noexcept { return __count_ == 0; }
|
||
|
|
||
|
[[__nodiscard__]] std::size_t capacity() const noexcept { return __Sp_; }
|
||
|
|
||
|
[[__nodiscard__]] std::size_t available() const noexcept { return __sz_; }
|
||
|
|
||
|
void clear() noexcept
|
||
|
{
|
||
|
loop([] [[__gnu__::__always_inline__]] (auto& __obj) noexcept {
|
||
|
using _Type = std::decay_t<decltype(__obj)>;
|
||
|
if constexpr (!std::is_trivially_destructible_v<_Type>) { __obj.~_Type(); }
|
||
|
});
|
||
|
|
||
|
__p_ = &__data_[0];
|
||
|
__sz_ = __Sp_;
|
||
|
__count_ = 0;
|
||
|
}
|
||
|
|
||
|
~variadic() { clear(); }
|
||
|
};
|