2019-06-29 00:27:49 +02:00
# pragma once
2015-12-02 10:23:25 +01:00
2016-08-14 02:22:19 +02:00
# ifdef _MSC_VER
# include <intrin.h>
# else
# include <x86intrin.h>
# endif
# include <immintrin.h>
# include <emmintrin.h>
2015-10-04 00:45:26 +02:00
# include <cstdint>
2018-08-24 18:07:38 +02:00
# include <cstddef>
2019-05-10 19:24:14 +02:00
# include <cstring>
2016-04-27 00:27:24 +02:00
# include <type_traits>
2016-08-14 02:22:19 +02:00
# include <utility>
2017-01-29 00:00:49 +01:00
# include <chrono>
2018-09-05 21:28:37 +02:00
# include <limits>
2017-04-19 13:31:56 +02:00
# include <array>
2016-08-14 02:22:19 +02:00
2020-08-14 18:11:35 +02:00
# ifdef _MSC_VER
2020-08-17 16:19:49 +02:00
# if !defined(__cpp_lib_bitops) && _MSC_VER < 1928
2020-08-14 18:11:35 +02:00
# define __cpp_lib_bitops
# endif
# endif
2020-04-13 14:31:41 +02:00
# include <bit>
2016-08-14 02:22:19 +02:00
2019-02-27 21:10:40 +01:00
# ifndef __has_builtin
# define __has_builtin(x) 0
# endif
2016-08-14 02:22:19 +02:00
# ifdef _MSC_VER
2018-09-06 23:04:26 +02:00
2020-06-06 18:23:52 +02:00
# define ASSUME(...) ((__VA_ARGS__) ? void() : __assume(0)) // MSVC __assume ignores side-effects
2016-08-14 02:22:19 +02:00
# define SAFE_BUFFERS __declspec(safebuffers)
# define NEVER_INLINE __declspec(noinline)
# define FORCE_INLINE __forceinline
2019-07-08 17:16:05 +02:00
# define RESTRICT __restrict
2018-09-06 23:04:26 +02:00
# else // not _MSC_VER
# ifdef __clang__
# if defined(__has_builtin) && __has_builtin(__builtin_assume)
2020-06-06 18:23:52 +02:00
# define ASSUME(...) ((__VA_ARGS__) ? void() : __builtin_assume(0)) // __builtin_assume (supported by modern clang) ignores side-effects
2018-09-06 23:04:26 +02:00
# endif
# endif
# ifndef ASSUME // gcc and old clang
2020-06-06 18:23:52 +02:00
# define ASSUME(...) ((__VA_ARGS__) ? void() : __builtin_unreachable()) // note: the compiler will generate code to evaluate "cond" if the expression is opaque
2018-09-06 23:04:26 +02:00
# endif
2020-03-24 19:12:54 +01:00
# define SAFE_BUFFERS __attribute__((no_stack_protector))
2020-11-03 02:12:29 +01:00
# define NEVER_INLINE __attribute__((noinline)) inline
2016-08-14 02:22:19 +02:00
# define FORCE_INLINE __attribute__((always_inline)) inline
2019-07-08 17:16:05 +02:00
# define RESTRICT __restrict__
2018-09-06 23:04:26 +02:00
# endif // _MSC_VER
2016-08-14 02:22:19 +02:00
# define CHECK_SIZE(type, size) static_assert(sizeof(type) == size, "Invalid " #type " type size")
# define CHECK_ALIGN(type, align) static_assert(alignof(type) == align, "Invalid " #type " type alignment")
# define CHECK_MAX_SIZE(type, size) static_assert(sizeof(type) <= size, #type " type size is too big")
# define CHECK_SIZE_ALIGN(type, size, align) CHECK_SIZE(type, size); CHECK_ALIGN(type, align)
2018-08-23 23:31:48 +02:00
// Variant pattern matching helper
# define MATCH(arg, ...) constexpr(std::is_same_v<std::decay_t<decltype(arg)>, __VA_ARGS__>)
2016-08-14 02:22:19 +02:00
# define CONCATENATE_DETAIL(x, y) x ## y
# define CONCATENATE(x, y) CONCATENATE_DETAIL(x, y)
2017-10-16 06:52:11 +02:00
# define STRINGIZE_DETAIL(x) #x ""
2016-08-14 02:22:19 +02:00
# define STRINGIZE(x) STRINGIZE_DETAIL(x)
# define HERE "\n(in file " __FILE__ ":" STRINGIZE(__LINE__) ")"
# define DECLARE(...) decltype(__VA_ARGS__) __VA_ARGS__
# define STR_CASE(...) case __VA_ARGS__: return #__VA_ARGS__
2016-02-01 22:55:43 +01:00
2018-09-06 23:04:26 +02:00
2020-06-06 18:23:52 +02:00
# define ASSERT(...) ((__VA_ARGS__) ? void() : fmt::raw_error("Assertion failed: " STRINGIZE(__VA_ARGS__) HERE))
2018-09-06 23:04:26 +02:00
# if defined(_DEBUG) || defined(_AUDIT)
# define AUDIT(...) ASSERT(__VA_ARGS__)
# else
# define AUDIT(...) ((void)0)
# endif
2020-02-17 18:55:20 +01:00
# if __cpp_lib_bit_cast >= 201806L
2019-06-01 23:12:17 +02:00
# include <bit>
# else
namespace std
{
template < class To , class From , typename = std : : enable_if_t < sizeof ( To ) = = sizeof ( From ) > >
constexpr To bit_cast ( const From & from ) noexcept
{
static_assert ( sizeof ( To ) = = sizeof ( From ) , " std::bit_cast<>: incompatible type size " ) ;
2020-07-25 10:46:47 +02:00
if constexpr ( ( std : : is_same_v < std : : remove_const_t < To > , std : : remove_const_t < From > > & & std : : is_constructible_v < To , From > ) | | ( std : : is_integral_v < From > & & std : : is_integral_v < To > ) )
2020-06-26 05:45:05 +02:00
{
return static_cast < To > ( from ) ;
}
2019-09-08 18:27:29 +02:00
To result { } ;
2019-06-01 23:12:17 +02:00
std : : memcpy ( & result , & from , sizeof ( From ) ) ;
return result ;
}
}
# endif
2018-09-06 23:04:26 +02:00
2016-08-13 15:36:04 +02:00
using schar = signed char ;
using uchar = unsigned char ;
2015-10-04 00:45:26 +02:00
using ushort = unsigned short ;
2016-08-13 15:36:04 +02:00
using uint = unsigned int ;
using ulong = unsigned long ;
2015-10-04 00:45:26 +02:00
using ullong = unsigned long long ;
2016-08-13 15:36:04 +02:00
using llong = long long ;
2015-10-04 00:45:26 +02:00
2018-08-18 03:13:25 +02:00
# if __APPLE__
using uptr = std : : uint64_t ;
# else
2017-06-22 23:39:31 +02:00
using uptr = std : : uintptr_t ;
2018-08-18 03:13:25 +02:00
# endif
2017-06-22 23:39:31 +02:00
2016-08-13 15:36:04 +02:00
using u8 = std : : uint8_t ;
2015-12-02 10:23:25 +01:00
using u16 = std : : uint16_t ;
using u32 = std : : uint32_t ;
using u64 = std : : uint64_t ;
2015-10-04 00:45:26 +02:00
2016-08-13 15:36:04 +02:00
using s8 = std : : int8_t ;
2015-12-02 10:23:25 +01:00
using s16 = std : : int16_t ;
using s32 = std : : int32_t ;
using s64 = std : : int64_t ;
2015-10-04 00:45:26 +02:00
2020-05-17 20:33:36 +02:00
# if __APPLE__
namespace std
{
template < typename T , typename = std : : enable_if_t < std : : is_unsigned_v < T > > >
constexpr int countr_zero ( T x ) noexcept
{
if ( x = = 0 )
return sizeof ( T ) * 8 ;
if constexpr ( sizeof ( T ) < = sizeof ( uint ) )
return __builtin_ctz ( x ) ;
else if constexpr ( sizeof ( T ) < = sizeof ( ulong ) )
return __builtin_ctzl ( x ) ;
else if constexpr ( sizeof ( T ) < = sizeof ( ullong ) )
return __builtin_ctzll ( x ) ;
else
static_assert ( sizeof ( T ) < = sizeof ( ullong ) ) ;
}
template < typename T , typename = std : : enable_if_t < std : : is_unsigned_v < T > > >
constexpr int countr_one ( T x ) noexcept
{
return countr_zero < T > ( ~ x ) ;
}
template < typename T , typename = std : : enable_if_t < std : : is_unsigned_v < T > > >
constexpr int countl_zero ( T x ) noexcept
{
if ( x = = 0 )
return sizeof ( T ) * 8 ;
if constexpr ( sizeof ( T ) < = sizeof ( uint ) )
return __builtin_clz ( x ) - ( sizeof ( uint ) - sizeof ( T ) ) * 8 ;
else if constexpr ( sizeof ( T ) < = sizeof ( ulong ) )
return __builtin_clzl ( x ) - ( sizeof ( ulong ) - sizeof ( T ) ) * 8 ;
else if constexpr ( sizeof ( T ) < = sizeof ( ullong ) )
return __builtin_clzll ( x ) - ( sizeof ( ullong ) - sizeof ( T ) ) * 8 ;
else
static_assert ( sizeof ( T ) < = sizeof ( ullong ) ) ;
}
template < typename T , typename = std : : enable_if_t < std : : is_unsigned_v < T > > >
constexpr int countl_one ( T x ) noexcept
{
return countl_zero < T > ( ~ x ) ;
}
}
# endif
2017-01-29 00:00:49 +01:00
using steady_clock = std : : conditional <
std : : chrono : : high_resolution_clock : : is_steady ,
std : : chrono : : high_resolution_clock , std : : chrono : : steady_clock > : : type ;
2019-07-26 18:00:56 +02:00
// Get integral type from type size
template < std : : size_t N >
2019-07-07 10:53:07 +02:00
struct get_int_impl
{
} ;
2019-07-26 18:00:56 +02:00
template < >
2019-07-07 10:53:07 +02:00
struct get_int_impl < sizeof ( u8 ) >
{
2019-07-26 18:00:56 +02:00
using utype = u8 ;
using stype = s8 ;
2019-07-07 10:53:07 +02:00
} ;
2019-07-26 18:00:56 +02:00
template < >
2019-07-07 10:53:07 +02:00
struct get_int_impl < sizeof ( u16 ) >
{
2019-07-26 18:00:56 +02:00
using utype = u16 ;
using stype = s16 ;
2019-07-07 10:53:07 +02:00
} ;
2019-07-26 18:00:56 +02:00
template < >
2019-07-07 10:53:07 +02:00
struct get_int_impl < sizeof ( u32 ) >
{
2019-07-26 18:00:56 +02:00
using utype = u32 ;
using stype = s32 ;
2019-07-07 10:53:07 +02:00
} ;
2019-07-26 18:00:56 +02:00
template < >
2019-07-07 10:53:07 +02:00
struct get_int_impl < sizeof ( u64 ) >
{
2019-07-26 18:00:56 +02:00
using utype = u64 ;
using stype = s64 ;
2019-07-07 10:53:07 +02:00
} ;
2019-07-26 18:00:56 +02:00
template < std : : size_t N >
using get_uint_t = typename get_int_impl < N > : : utype ;
template < std : : size_t N >
using get_sint_t = typename get_int_impl < N > : : stype ;
2019-07-07 10:53:07 +02:00
2020-04-11 20:34:31 +02:00
template < typename T >
2020-04-12 07:11:58 +02:00
std : : remove_cvref_t < T > as_rvalue ( T & & obj )
2020-04-11 20:34:31 +02:00
{
2020-04-11 21:52:42 +02:00
return std : : forward < T > ( obj ) ;
2020-04-11 20:34:31 +02:00
}
2016-08-03 22:51:05 +02:00
// Formatting helper, type-specific preprocessing for improving safety and functionality
2016-08-13 15:36:04 +02:00
template < typename T , typename = void >
2016-08-03 22:51:05 +02:00
struct fmt_unveil ;
2016-04-27 00:27:24 +02:00
2016-08-15 12:18:47 +02:00
template < typename Arg >
using fmt_unveil_t = typename fmt_unveil < Arg > : : type ;
2016-08-08 18:01:06 +02:00
struct fmt_type_info ;
namespace fmt
{
2016-08-13 15:36:04 +02:00
template < typename . . . Args >
2016-08-08 18:01:06 +02:00
const fmt_type_info * get_type_info ( ) ;
}
2020-10-25 22:28:58 +01:00
template < typename T , std : : size_t Align >
2016-02-01 22:55:43 +01:00
class atomic_t ;
// Extract T::simple_type if available, remove cv qualifiers
2016-08-13 15:36:04 +02:00
template < typename T , typename = void >
2016-02-01 22:55:43 +01:00
struct simple_type_helper
{
using type = typename std : : remove_cv < T > : : type ;
} ;
2016-08-13 15:36:04 +02:00
template < typename T >
2018-08-23 23:31:27 +02:00
struct simple_type_helper < T , std : : void_t < typename T : : simple_type > >
2016-02-01 22:55:43 +01:00
{
using type = typename T : : simple_type ;
} ;
2016-08-13 15:36:04 +02:00
template < typename T >
using simple_t = typename simple_type_helper < T > : : type ;
2016-02-01 22:55:43 +01:00
// Bool type equivalent
class b8
{
2016-04-27 00:27:24 +02:00
u8 m_value ;
2016-02-01 22:55:43 +01:00
public :
b8 ( ) = default ;
2020-10-05 14:37:59 +02:00
constexpr b8 ( bool value ) noexcept
2016-02-01 22:55:43 +01:00
: m_value ( value )
{
}
2020-10-05 14:37:59 +02:00
constexpr operator bool ( ) const noexcept
2016-02-01 22:55:43 +01:00
{
return m_value ! = 0 ;
}
2020-10-05 14:37:59 +02:00
constexpr bool set ( bool value ) noexcept
{
m_value = value ;
return value ;
}
2016-02-01 22:55:43 +01:00
} ;
# ifndef _MSC_VER
using u128 = __uint128_t ;
using s128 = __int128_t ;
# else
// Unsigned 128-bit integer implementation (TODO)
struct alignas ( 16 ) u128
{
2016-04-27 00:27:24 +02:00
u64 lo , hi ;
2016-02-01 22:55:43 +01:00
u128 ( ) = default ;
2016-04-27 00:27:24 +02:00
constexpr u128 ( u64 l )
2016-02-01 22:55:43 +01:00
: lo ( l )
, hi ( 0 )
{
}
2020-10-27 19:34:08 +01:00
constexpr explicit operator bool ( ) const noexcept
{
return ! ! ( lo | hi ) ;
}
2016-08-13 15:36:04 +02:00
friend u128 operator + ( const u128 & l , const u128 & r )
2016-02-01 22:55:43 +01:00
{
u128 value ;
_addcarry_u64 ( _addcarry_u64 ( 0 , r . lo , l . lo , & value . lo ) , r . hi , l . hi , & value . hi ) ;
return value ;
}
2016-08-13 15:36:04 +02:00
friend u128 operator + ( const u128 & l , u64 r )
2016-02-01 22:55:43 +01:00
{
u128 value ;
_addcarry_u64 ( _addcarry_u64 ( 0 , r , l . lo , & value . lo ) , l . hi , 0 , & value . hi ) ;
return value ;
}
2016-08-13 15:36:04 +02:00
friend u128 operator + ( u64 l , const u128 & r )
2016-02-01 22:55:43 +01:00
{
u128 value ;
_addcarry_u64 ( _addcarry_u64 ( 0 , r . lo , l , & value . lo ) , 0 , r . hi , & value . hi ) ;
return value ;
}
2016-08-13 15:36:04 +02:00
friend u128 operator - ( const u128 & l , const u128 & r )
2016-02-01 22:55:43 +01:00
{
u128 value ;
_subborrow_u64 ( _subborrow_u64 ( 0 , r . lo , l . lo , & value . lo ) , r . hi , l . hi , & value . hi ) ;
return value ;
}
2016-08-13 15:36:04 +02:00
friend u128 operator - ( const u128 & l , u64 r )
2016-02-01 22:55:43 +01:00
{
u128 value ;
_subborrow_u64 ( _subborrow_u64 ( 0 , r , l . lo , & value . lo ) , 0 , l . hi , & value . hi ) ;
return value ;
}
2016-08-13 15:36:04 +02:00
friend u128 operator - ( u64 l , const u128 & r )
2016-02-01 22:55:43 +01:00
{
u128 value ;
_subborrow_u64 ( _subborrow_u64 ( 0 , r . lo , l , & value . lo ) , r . hi , 0 , & value . hi ) ;
return value ;
}
2016-08-13 15:36:04 +02:00
u128 operator + ( ) const
2016-02-01 22:55:43 +01:00
{
return * this ;
}
2016-08-13 15:36:04 +02:00
u128 operator - ( ) const
2016-02-01 22:55:43 +01:00
{
u128 value ;
_subborrow_u64 ( _subborrow_u64 ( 0 , lo , 0 , & value . lo ) , hi , 0 , & value . hi ) ;
return value ;
}
2016-08-13 15:36:04 +02:00
u128 & operator + + ( )
2016-02-01 22:55:43 +01:00
{
_addcarry_u64 ( _addcarry_u64 ( 0 , 1 , lo , & lo ) , 0 , hi , & hi ) ;
return * this ;
}
2016-08-13 15:36:04 +02:00
u128 operator + + ( int )
2016-02-01 22:55:43 +01:00
{
u128 value = * this ;
_addcarry_u64 ( _addcarry_u64 ( 0 , 1 , lo , & lo ) , 0 , hi , & hi ) ;
return value ;
}
2016-08-13 15:36:04 +02:00
u128 & operator - - ( )
2016-02-01 22:55:43 +01:00
{
_subborrow_u64 ( _subborrow_u64 ( 0 , 1 , lo , & lo ) , 0 , hi , & hi ) ;
return * this ;
}
2016-08-13 15:36:04 +02:00
u128 operator - - ( int )
2016-02-01 22:55:43 +01:00
{
u128 value = * this ;
_subborrow_u64 ( _subborrow_u64 ( 0 , 1 , lo , & lo ) , 0 , hi , & hi ) ;
return value ;
}
2020-10-27 19:34:08 +01:00
u128 operator < < ( u128 shift_value )
{
const u64 v0 = lo < < ( shift_value . lo & 63 ) ;
const u64 v1 = __shiftleft128 ( lo , hi , shift_value . lo ) ;
u128 value ;
value . lo = ( shift_value . lo & 64 ) ? 0 : v0 ;
value . hi = ( shift_value . lo & 64 ) ? v0 : v1 ;
return value ;
}
u128 operator > > ( u128 shift_value )
{
const u64 v0 = hi > > ( shift_value . lo & 63 ) ;
const u64 v1 = __shiftright128 ( lo , hi , shift_value . lo ) ;
u128 value ;
value . lo = ( shift_value . lo & 64 ) ? v0 : v1 ;
value . hi = ( shift_value . lo & 64 ) ? 0 : v0 ;
return value ;
}
2016-08-13 15:36:04 +02:00
u128 operator ~ ( ) const
2016-02-01 22:55:43 +01:00
{
u128 value ;
value . lo = ~ lo ;
value . hi = ~ hi ;
return value ;
}
2016-08-13 15:36:04 +02:00
friend u128 operator & ( const u128 & l , const u128 & r )
2016-02-01 22:55:43 +01:00
{
u128 value ;
value . lo = l . lo & r . lo ;
value . hi = l . hi & r . hi ;
return value ;
}
2016-08-13 15:36:04 +02:00
friend u128 operator | ( const u128 & l , const u128 & r )
2016-02-01 22:55:43 +01:00
{
u128 value ;
value . lo = l . lo | r . lo ;
value . hi = l . hi | r . hi ;
return value ;
}
2016-08-13 15:36:04 +02:00
friend u128 operator ^ ( const u128 & l , const u128 & r )
2016-02-01 22:55:43 +01:00
{
u128 value ;
value . lo = l . lo ^ r . lo ;
value . hi = l . hi ^ r . hi ;
return value ;
}
2016-08-13 15:36:04 +02:00
u128 & operator + = ( const u128 & r )
2016-02-01 22:55:43 +01:00
{
_addcarry_u64 ( _addcarry_u64 ( 0 , r . lo , lo , & lo ) , r . hi , hi , & hi ) ;
return * this ;
}
2016-08-13 15:36:04 +02:00
u128 & operator + = ( uint64_t r )
2016-02-01 22:55:43 +01:00
{
_addcarry_u64 ( _addcarry_u64 ( 0 , r , lo , & lo ) , 0 , hi , & hi ) ;
return * this ;
}
2016-08-13 15:36:04 +02:00
u128 & operator & = ( const u128 & r )
2016-02-01 22:55:43 +01:00
{
lo & = r . lo ;
hi & = r . hi ;
return * this ;
}
2016-08-13 15:36:04 +02:00
u128 & operator | = ( const u128 & r )
2016-02-01 22:55:43 +01:00
{
lo | = r . lo ;
hi | = r . hi ;
return * this ;
}
2016-08-13 15:36:04 +02:00
u128 & operator ^ = ( const u128 & r )
2016-02-01 22:55:43 +01:00
{
lo ^ = r . lo ;
hi ^ = r . hi ;
return * this ;
}
} ;
// Signed 128-bit integer implementation (TODO)
struct alignas ( 16 ) s128
{
2016-04-27 00:27:24 +02:00
u64 lo ;
s64 hi ;
2016-02-01 22:55:43 +01:00
s128 ( ) = default ;
2016-04-27 00:27:24 +02:00
constexpr s128 ( s64 l )
2016-02-01 22:55:43 +01:00
: hi ( l > > 63 )
, lo ( l )
{
}
2016-04-27 00:27:24 +02:00
constexpr s128 ( u64 l )
2016-02-01 22:55:43 +01:00
: hi ( 0 )
, lo ( l )
{
}
} ;
# endif
2016-08-14 02:22:19 +02:00
CHECK_SIZE_ALIGN ( u128 , 16 , 16 ) ;
CHECK_SIZE_ALIGN ( s128 , 16 , 16 ) ;
2016-02-01 22:55:43 +01:00
2020-02-19 17:34:04 +01:00
// Return magic value for any unsigned type
constexpr inline struct umax_helper
{
constexpr umax_helper ( ) noexcept = default ;
2020-02-20 13:00:22 +01:00
template < typename T , typename S = simple_t < T > , typename = std : : enable_if_t < std : : is_unsigned_v < S > > >
2020-02-19 17:34:04 +01:00
explicit constexpr operator T ( ) const
{
return std : : numeric_limits < S > : : max ( ) ;
}
2020-02-20 13:00:22 +01:00
template < typename T , typename S = simple_t < T > , typename = std : : enable_if_t < std : : is_unsigned_v < S > > >
2020-02-19 17:34:04 +01:00
constexpr bool operator = = ( const T & rhs ) const
{
2020-02-20 15:13:41 +01:00
return rhs = = std : : numeric_limits < S > : : max ( ) ;
2020-02-19 17:34:04 +01:00
}
# if __cpp_impl_three_way_comparison >= 201711 && !__INTELLISENSE__
# else
template < typename T >
2020-02-20 13:00:22 +01:00
friend constexpr std : : enable_if_t < std : : is_unsigned_v < simple_t < T > > , bool > operator = = ( const T & lhs , const umax_helper & rhs )
2020-02-19 17:34:04 +01:00
{
2020-02-20 15:13:41 +01:00
return lhs = = std : : numeric_limits < simple_t < T > > : : max ( ) ;
2020-02-19 17:34:04 +01:00
}
# endif
# if __cpp_impl_three_way_comparison >= 201711
# else
2020-02-20 13:00:22 +01:00
template < typename T , typename S = simple_t < T > , typename = std : : enable_if_t < std : : is_unsigned_v < S > > >
2020-02-19 17:34:04 +01:00
constexpr bool operator ! = ( const T & rhs ) const
{
2020-02-20 15:13:41 +01:00
return rhs ! = std : : numeric_limits < S > : : max ( ) ;
2020-02-19 17:34:04 +01:00
}
template < typename T >
2020-02-20 13:00:22 +01:00
friend constexpr std : : enable_if_t < std : : is_unsigned_v < simple_t < T > > , bool > operator ! = ( const T & lhs , const umax_helper & rhs )
2020-02-19 17:34:04 +01:00
{
2020-02-20 15:13:41 +01:00
return lhs ! = std : : numeric_limits < simple_t < T > > : : max ( ) ;
2020-02-19 17:34:04 +01:00
}
# endif
} umax ;
2019-05-10 19:24:14 +02:00
using f32 = float ;
using f64 = double ;
2015-10-04 00:45:26 +02:00
union alignas ( 2 ) f16
{
u16 _u16 ;
u8 _u8 [ 2 ] ;
2015-10-25 23:40:02 +01:00
explicit f16 ( u16 raw )
{
_u16 = raw ;
}
2019-05-10 19:24:14 +02:00
explicit operator f32 ( ) const
2015-10-25 23:40:02 +01:00
{
// See http://stackoverflow.com/a/26779139
// The conversion doesn't handle NaN/Inf
2016-08-13 15:36:04 +02:00
u32 raw = ( ( _u16 & 0x8000 ) < < 16 ) | // Sign (just moved)
( ( ( _u16 & 0x7c00 ) + 0x1C000 ) < < 13 ) | // Exponent ( exp - 15 + 127)
( ( _u16 & 0x03FF ) < < 13 ) ; // Mantissa
2019-05-10 19:24:14 +02:00
2019-06-01 23:12:17 +02:00
return std : : bit_cast < f32 > ( raw ) ;
2015-10-25 23:40:02 +01:00
}
2015-10-04 00:45:26 +02:00
} ;
2016-08-14 02:22:19 +02:00
CHECK_SIZE_ALIGN ( f16 , 2 , 2 ) ;
2019-12-16 20:56:14 +01:00
template < typename T , typename = std : : enable_if_t < std : : is_integral < T > : : value & & std : : is_unsigned < T > : : value > >
constexpr T align ( T value , ullong align )
{
return static_cast < T > ( ( value + ( align - 1 ) ) & ( 0 - align ) ) ;
}
// General purpose aligned division, the result is rounded up not truncated
template < typename T , typename = std : : enable_if_t < std : : is_integral < T > : : value & & std : : is_unsigned < T > : : value > >
constexpr T aligned_div ( T value , ullong align )
{
return static_cast < T > ( ( value + align - 1 ) / align ) ;
}
// General purpose aligned division, the result is rounded to nearest
2016-08-13 15:36:04 +02:00
template < typename T , typename = std : : enable_if_t < std : : is_integral < T > : : value > >
2019-12-16 20:56:14 +01:00
constexpr T rounded_div ( T value , std : : conditional_t < std : : is_signed < T > : : value , llong , ullong > align )
2016-08-08 18:01:06 +02:00
{
2019-12-16 20:56:14 +01:00
if constexpr ( std : : is_unsigned < T > : : value )
{
return static_cast < T > ( ( value + ( align / 2 ) ) / align ) ;
}
return static_cast < T > ( ( value + ( value < 0 ? 0 - align : align ) / 2 ) / align ) ;
2016-08-08 18:01:06 +02:00
}
2017-04-19 13:31:56 +02:00
template < typename T , typename T2 >
inline u32 offset32 ( T T2 : : * const mptr )
{
# ifdef _MSC_VER
2019-06-01 23:12:17 +02:00
return std : : bit_cast < u32 > ( mptr ) ;
2017-04-19 13:31:56 +02:00
# elif __GNUG__
2019-06-01 23:12:17 +02:00
return std : : bit_cast < std : : size_t > ( mptr ) ;
2017-04-19 13:31:56 +02:00
# else
2019-06-01 23:12:17 +02:00
static_assert ( sizeof ( mptr ) = = 0 , " Unsupported pointer-to-member size " ) ;
2017-04-19 13:31:56 +02:00
# endif
}
template < typename T >
struct offset32_array
{
static_assert ( std : : is_array < T > : : value , " Invalid pointer-to-member type (array expected) " ) ;
template < typename Arg >
static inline u32 index32 ( const Arg & arg )
{
2018-09-03 17:46:14 +02:00
return u32 { sizeof ( std : : remove_extent_t < T > ) } * static_cast < u32 > ( arg ) ;
2017-04-19 13:31:56 +02:00
}
} ;
template < typename T , std : : size_t N >
struct offset32_array < std : : array < T , N > >
{
template < typename Arg >
static inline u32 index32 ( const Arg & arg )
{
2018-09-03 17:46:14 +02:00
return u32 { sizeof ( T ) } * static_cast < u32 > ( arg ) ;
2017-04-19 13:31:56 +02:00
}
} ;
template < typename Arg >
struct offset32_detail ;
template < typename T , typename T2 , typename Arg , typename . . . Args >
inline u32 offset32 ( T T2 : : * const mptr , const Arg & arg , const Args & . . . args )
{
return offset32_detail < Arg > : : offset32 ( mptr , arg , args . . . ) ;
}
template < typename Arg >
struct offset32_detail
{
template < typename T , typename T2 , typename . . . Args >
static inline u32 offset32 ( T T2 : : * const mptr , const Arg & arg , const Args & . . . args )
{
return : : offset32 ( mptr , args . . . ) + offset32_array < T > : : index32 ( arg ) ;
}
} ;
template < typename T3 , typename T4 >
struct offset32_detail < T3 T4 : : * >
{
template < typename T , typename T2 , typename . . . Args >
static inline u32 offset32 ( T T2 : : * const mptr , T3 T4 : : * const mptr2 , const Args & . . . args )
{
return : : offset32 ( mptr ) + : : offset32 ( mptr2 , args . . . ) ;
}
} ;
2016-08-14 02:22:19 +02:00
// Helper function, used by ""_u16, ""_u32, ""_u64
2019-12-23 20:51:40 +01:00
constexpr u32 to_u8 ( char c )
2016-08-14 02:22:19 +02:00
{
2016-08-15 12:18:47 +02:00
return static_cast < u8 > ( c ) ;
2016-08-14 02:22:19 +02:00
}
2020-02-17 18:55:20 +01:00
// Convert 1-2-byte string to u16 value like reinterpret_cast does
2019-12-26 21:01:48 +01:00
constexpr u16 operator " " _u16 ( const char * s , std : : size_t /*length*/ )
2016-08-14 02:22:19 +02:00
{
2020-02-17 18:55:20 +01:00
if constexpr ( std : : endian : : little = = std : : endian : : native )
{
return static_cast < u16 > ( to_u8 ( s [ 1 ] ) < < 8 | to_u8 ( s [ 0 ] ) ) ;
}
else
{
return static_cast < u16 > ( to_u8 ( s [ 0 ] ) < < 8 | to_u8 ( s [ 1 ] ) ) ;
}
2016-08-14 02:22:19 +02:00
}
2020-02-17 18:55:20 +01:00
// Convert 3-4-byte string to u32 value like reinterpret_cast does
2019-12-26 21:01:48 +01:00
constexpr u32 operator " " _u32 ( const char * s , std : : size_t /*length*/ )
2016-08-14 02:22:19 +02:00
{
2020-02-17 18:55:20 +01:00
if constexpr ( std : : endian : : little = = std : : endian : : native )
{
return to_u8 ( s [ 3 ] ) < < 24 | to_u8 ( s [ 2 ] ) < < 16 | to_u8 ( s [ 1 ] ) < < 8 | to_u8 ( s [ 0 ] ) ;
}
else
{
return to_u8 ( s [ 0 ] ) < < 24 | to_u8 ( s [ 1 ] ) < < 16 | to_u8 ( s [ 2 ] ) < < 8 | to_u8 ( s [ 3 ] ) ;
}
2016-08-14 02:22:19 +02:00
}
2020-02-17 18:55:20 +01:00
// Convert 5-6-byte string to u64 value like reinterpret_cast does
constexpr u64 operator " " _u48 ( const char * s , std : : size_t /*length*/ )
{
if constexpr ( std : : endian : : little = = std : : endian : : native )
{
return static_cast < u64 > ( to_u8 ( s [ 5 ] ) < < 8 | to_u8 ( s [ 4 ] ) ) < < 32 | to_u8 ( s [ 3 ] ) < < 24 | to_u8 ( s [ 2 ] ) < < 16 | to_u8 ( s [ 1 ] ) < < 8 | to_u8 ( s [ 0 ] ) ;
}
else
{
return static_cast < u64 > ( to_u8 ( s [ 0 ] ) < < 8 | to_u8 ( s [ 1 ] ) ) < < 32 | to_u8 ( s [ 2 ] ) < < 24 | to_u8 ( s [ 3 ] ) < < 16 | to_u8 ( s [ 4 ] ) < < 8 | to_u8 ( s [ 5 ] ) ;
}
}
// Convert 7-8-byte string to u64 value like reinterpret_cast does
2019-12-26 21:01:48 +01:00
constexpr u64 operator " " _u64 ( const char * s , std : : size_t /*length*/ )
2016-08-14 02:22:19 +02:00
{
2020-02-17 18:55:20 +01:00
if constexpr ( std : : endian : : little = = std : : endian : : native )
{
return static_cast < u64 > ( to_u8 ( s [ 7 ] ) < < 24 | to_u8 ( s [ 6 ] ) < < 16 | to_u8 ( s [ 5 ] ) < < 8 | to_u8 ( s [ 4 ] ) ) < < 32 | to_u8 ( s [ 3 ] ) < < 24 | to_u8 ( s [ 2 ] ) < < 16 | to_u8 ( s [ 1 ] ) < < 8 | to_u8 ( s [ 0 ] ) ;
}
else
{
return static_cast < u64 > ( to_u8 ( s [ 0 ] ) < < 24 | to_u8 ( s [ 1 ] ) < < 16 | to_u8 ( s [ 2 ] ) < < 8 | to_u8 ( s [ 3 ] ) ) < < 32 | to_u8 ( s [ 4 ] ) < < 24 | to_u8 ( s [ 5 ] ) < < 16 | to_u8 ( s [ 6 ] ) < < 8 | to_u8 ( s [ 7 ] ) ;
}
2016-08-14 02:22:19 +02:00
}
2016-08-08 18:01:06 +02:00
namespace fmt
{
[ [ noreturn ] ] void raw_error ( const char * msg ) ;
2016-08-15 12:18:47 +02:00
[ [ noreturn ] ] void raw_verify_error ( const char * msg , const fmt_type_info * sup , u64 arg ) ;
2016-08-08 18:01:06 +02:00
[ [ noreturn ] ] void raw_narrow_error ( const char * msg , const fmt_type_info * sup , u64 arg ) ;
}
2016-08-13 15:36:04 +02:00
struct verify_func
{
template < typename T >
bool operator ( ) ( T & & value ) const
{
if ( std : : forward < T > ( value ) )
{
return true ;
}
return false ;
}
} ;
template < uint N >
struct verify_impl
{
const char * cause ;
template < typename T >
auto operator , ( T & & value ) const
{
// Verification (can be safely disabled)
if ( ! verify_func ( ) ( std : : forward < T > ( value ) ) )
{
2017-01-24 21:19:52 +01:00
fmt : : raw_verify_error ( cause , nullptr , N ) ;
2016-08-13 15:36:04 +02:00
}
return verify_impl < N + 1 > { cause } ;
}
} ;
// Verification helper, checks several conditions delimited with comma operator
inline auto verify ( const char * cause )
{
return verify_impl < 0 > { cause } ;
}
// Verification helper (returns value or lvalue reference, may require to use verify_move instead)
template < typename F = verify_func , typename T >
2016-08-15 12:18:47 +02:00
inline T verify ( const char * cause , T & & value , F & & pred = F ( ) )
2016-08-13 15:36:04 +02:00
{
2016-08-15 12:18:47 +02:00
if ( ! pred ( std : : forward < T > ( value ) ) )
2016-08-13 15:36:04 +02:00
{
2016-08-15 12:18:47 +02:00
using unref = std : : remove_const_t < std : : remove_reference_t < T > > ;
fmt : : raw_verify_error ( cause , fmt : : get_type_info < fmt_unveil_t < unref > > ( ) , fmt_unveil < unref > : : get ( value ) ) ;
2016-08-13 15:36:04 +02:00
}
return std : : forward < T > ( value ) ;
}
// Verification helper (must be used in return expression or in place of std::move)
template < typename F = verify_func , typename T >
2016-08-15 12:18:47 +02:00
inline std : : remove_reference_t < T > & & verify_move ( const char * cause , T & & value , F & & pred = F ( ) )
2016-08-13 15:36:04 +02:00
{
2016-08-15 12:18:47 +02:00
if ( ! pred ( std : : forward < T > ( value ) ) )
2016-08-13 15:36:04 +02:00
{
2016-08-15 12:18:47 +02:00
using unref = std : : remove_const_t < std : : remove_reference_t < T > > ;
fmt : : raw_verify_error ( cause , fmt : : get_type_info < fmt_unveil_t < unref > > ( ) , fmt_unveil < unref > : : get ( value ) ) ;
2016-08-13 15:36:04 +02:00
}
return std : : move ( value ) ;
}
2016-08-14 19:22:07 +02:00
// narrow() function details
template < typename From , typename To = void , typename = void >
struct narrow_impl
{
// Temporarily (diagnostic)
static_assert ( std : : is_void < To > : : value , " narrow_impl<> specialization not found " ) ;
// Returns true if value cannot be represented in type To
static constexpr bool test ( const From & value )
{
// Unspecialized cases (including cast to void) always considered narrowing
return true ;
}
} ;
// Unsigned to unsigned narrowing
template < typename From , typename To >
struct narrow_impl < From , To , std : : enable_if_t < std : : is_unsigned < From > : : value & & std : : is_unsigned < To > : : value > >
{
static constexpr bool test ( const From & value )
{
return sizeof ( To ) < sizeof ( From ) & & static_cast < To > ( value ) ! = value ;
}
} ;
// Signed to signed narrowing
template < typename From , typename To >
struct narrow_impl < From , To , std : : enable_if_t < std : : is_signed < From > : : value & & std : : is_signed < To > : : value > >
{
static constexpr bool test ( const From & value )
{
return sizeof ( To ) < sizeof ( From ) & & static_cast < To > ( value ) ! = value ;
}
} ;
// Unsigned to signed narrowing
template < typename From , typename To >
struct narrow_impl < From , To , std : : enable_if_t < std : : is_unsigned < From > : : value & & std : : is_signed < To > : : value > >
{
static constexpr bool test ( const From & value )
{
return sizeof ( To ) < = sizeof ( From ) & & value > ( static_cast < std : : make_unsigned_t < To > > ( - 1 ) > > 1 ) ;
}
} ;
// Signed to unsigned narrowing (I)
template < typename From , typename To >
struct narrow_impl < From , To , std : : enable_if_t < std : : is_signed < From > : : value & & std : : is_unsigned < To > : : value & & sizeof ( To ) > = sizeof ( From ) > >
{
static constexpr bool test ( const From & value )
{
return value < static_cast < From > ( 0 ) ;
}
} ;
// Signed to unsigned narrowing (II)
template < typename From , typename To >
struct narrow_impl < From , To , std : : enable_if_t < std : : is_signed < From > : : value & & std : : is_unsigned < To > : : value & & sizeof ( To ) < sizeof ( From ) > >
{
static constexpr bool test ( const From & value )
{
return static_cast < std : : make_unsigned_t < From > > ( value ) > static_cast < To > ( - 1 ) ;
}
} ;
2016-08-15 12:18:47 +02:00
// Simple type enabled (TODO: allow for To as well)
2016-08-14 19:22:07 +02:00
template < typename From , typename To >
2018-08-23 23:31:27 +02:00
struct narrow_impl < From , To , std : : void_t < typename From : : simple_type > >
2016-08-14 19:22:07 +02:00
: narrow_impl < simple_t < From > , To >
{
} ;
2016-08-13 15:36:04 +02:00
template < typename To = void , typename From , typename = decltype ( static_cast < To > ( std : : declval < From > ( ) ) ) >
2016-08-08 18:01:06 +02:00
inline To narrow ( const From & value , const char * msg = nullptr )
{
2016-08-14 19:22:07 +02:00
// Narrow check
if ( narrow_impl < From , To > : : test ( value ) )
2016-08-08 18:01:06 +02:00
{
// Pack value as formatting argument
2016-08-15 12:18:47 +02:00
fmt : : raw_narrow_error ( msg , fmt : : get_type_info < fmt_unveil_t < From > > ( ) , fmt_unveil < From > : : get ( value ) ) ;
2016-08-08 18:01:06 +02:00
}
2016-08-14 19:22:07 +02:00
return static_cast < To > ( value ) ;
2016-08-08 18:01:06 +02:00
}
// Returns u32 size() for container
2016-08-13 15:36:04 +02:00
template < typename CT , typename = decltype ( static_cast < u32 > ( std : : declval < CT > ( ) . size ( ) ) ) >
2016-08-08 18:01:06 +02:00
inline u32 size32 ( const CT & container , const char * msg = nullptr )
{
return narrow < u32 > ( container . size ( ) , msg ) ;
}
// Returns u32 size for an array
2016-08-13 15:36:04 +02:00
template < typename T , std : : size_t Size >
constexpr u32 size32 ( const T ( & ) [ Size ] , const char * msg = nullptr )
2016-08-08 18:01:06 +02:00
{
return static_cast < u32 > ( Size ) ;
}
2016-05-13 16:01:48 +02:00
// Simplified hash algorithm for pointers. May be used in std::unordered_(map|set).
2016-08-13 15:36:04 +02:00
template < typename T , std : : size_t Align = alignof ( T ) >
2016-05-13 16:01:48 +02:00
struct pointer_hash
{
std : : size_t operator ( ) ( T * ptr ) const
{
return reinterpret_cast < std : : uintptr_t > ( ptr ) / Align ;
}
} ;
2016-08-13 15:36:04 +02:00
template < typename T , std : : size_t Shift = 0 >
2016-06-02 17:16:01 +02:00
struct value_hash
{
std : : size_t operator ( ) ( T value ) const
{
return static_cast < std : : size_t > ( value ) > > Shift ;
}
} ;
2016-04-25 12:49:12 +02:00
// Contains value of any POD type with fixed size and alignment. TT<> is the type converter applied.
// For example, `simple_t` may be used to remove endianness.
2016-08-13 15:36:04 +02:00
template < template < typename > class TT , std : : size_t S , std : : size_t A = S >
2016-04-25 12:49:12 +02:00
struct alignas ( A ) any_pod
{
2018-08-25 14:47:03 +02:00
alignas ( A ) std : : byte data [ S ] ;
2016-04-25 12:49:12 +02:00
any_pod ( ) = default ;
2020-03-18 16:27:43 +01:00
template < typename T , typename T2 = TT < T > , typename = std : : enable_if_t < std : : is_trivially_copyable_v < T > & & sizeof ( T2 ) = = S & & alignof ( T2 ) < = A > >
2016-04-25 12:49:12 +02:00
any_pod ( const T & value )
{
2020-03-18 16:27:43 +01:00
* this = std : : bit_cast < any_pod > ( value ) ;
2016-04-25 12:49:12 +02:00
}
2020-03-18 16:27:43 +01:00
template < typename T , typename T2 = TT < T > , typename = std : : enable_if_t < std : : is_trivially_copyable_v < T > & & sizeof ( T2 ) = = S & & alignof ( T2 ) < = A > >
2016-04-25 12:49:12 +02:00
T2 & as ( )
{
return reinterpret_cast < T2 & > ( data ) ;
}
2020-03-18 16:27:43 +01:00
template < typename T , typename T2 = TT < T > , typename = std : : enable_if_t < std : : is_trivially_copyable_v < T > & & sizeof ( T2 ) = = S & & alignof ( T2 ) < = A > >
2016-04-25 12:49:12 +02:00
const T2 & as ( ) const
{
return reinterpret_cast < const T2 & > ( data ) ;
}
} ;
using any16 = any_pod < simple_t , sizeof ( u16 ) > ;
using any32 = any_pod < simple_t , sizeof ( u32 ) > ;
using any64 = any_pod < simple_t , sizeof ( u64 ) > ;
2016-08-09 16:14:41 +02:00
struct cmd64 : any64
{
struct pair_t
{
any32 arg1 ;
any32 arg2 ;
} ;
cmd64 ( ) = default ;
2016-08-13 15:36:04 +02:00
template < typename T >
2016-08-09 16:14:41 +02:00
cmd64 ( const T & value )
: any64 ( value )
{
}
2016-08-13 15:36:04 +02:00
template < typename T1 , typename T2 >
2016-08-09 16:14:41 +02:00
cmd64 ( const T1 & arg1 , const T2 & arg2 )
: any64 ( pair_t { arg1 , arg2 } )
{
}
explicit operator bool ( ) const
{
return as < u64 > ( ) ! = 0 ;
}
// TODO: compatibility with std::pair/std::tuple?
2016-08-13 15:36:04 +02:00
template < typename T >
2016-08-09 16:14:41 +02:00
decltype ( auto ) arg1 ( )
{
return as < pair_t > ( ) . arg1 . as < T > ( ) ;
}
2016-08-13 15:36:04 +02:00
template < typename T >
2016-08-09 16:14:41 +02:00
decltype ( auto ) arg1 ( ) const
{
return as < const pair_t > ( ) . arg1 . as < const T > ( ) ;
}
2016-08-13 15:36:04 +02:00
template < typename T >
2016-08-09 16:14:41 +02:00
decltype ( auto ) arg2 ( )
{
return as < pair_t > ( ) . arg2 . as < T > ( ) ;
}
2016-08-13 15:36:04 +02:00
template < typename T >
2016-08-09 16:14:41 +02:00
decltype ( auto ) arg2 ( ) const
{
return as < const pair_t > ( ) . arg2 . as < const T > ( ) ;
}
} ;
2020-03-18 16:27:43 +01:00
static_assert ( sizeof ( cmd64 ) = = 8 & & std : : is_trivially_copyable_v < cmd64 > , " Incorrect cmd64 type " ) ;
2016-08-09 16:14:41 +02:00
2016-08-17 18:50:20 +02:00
// Error code type (return type), implements error reporting. Could be a template.
2020-06-25 17:21:37 +02:00
class error_code
2016-08-15 17:30:33 +02:00
{
2016-08-17 18:50:20 +02:00
// Use fixed s32 type for now
2016-08-16 17:46:24 +02:00
s32 value ;
2020-06-25 17:21:37 +02:00
public :
2016-08-16 17:46:24 +02:00
error_code ( ) = default ;
2016-08-17 18:50:20 +02:00
// Implementation must be provided specially
2017-08-08 21:13:48 +02:00
static s32 error_report ( const fmt_type_info * sup , u64 arg , const fmt_type_info * sup2 , u64 arg2 ) ;
2016-08-16 17:46:24 +02:00
2016-08-17 18:50:20 +02:00
// Helper type
enum class not_an_error : s32
2016-08-16 17:46:24 +02:00
{
__not_an_error // SFINAE marker
} ;
// __not_an_error tester
template < typename ET , typename = void >
struct is_error : std : : integral_constant < bool , std : : is_enum < ET > : : value | | std : : is_integral < ET > : : value >
{
} ;
template < typename ET >
2018-08-24 13:01:53 +02:00
struct is_error < ET , std : : enable_if_t < sizeof ( ET : : __not_an_error ) ! = 0 > > : std : : false_type
2016-08-16 17:46:24 +02:00
{
} ;
2018-08-24 13:01:53 +02:00
// Common constructor
template < typename ET >
2016-08-16 17:46:24 +02:00
error_code ( const ET & value )
2018-08-24 13:01:53 +02:00
: value ( static_cast < s32 > ( value ) )
2017-08-08 21:13:48 +02:00
{
2018-08-24 13:01:53 +02:00
if constexpr ( is_error < ET > : : value )
{
this - > value = error_report ( fmt : : get_type_info < fmt_unveil_t < ET > > ( ) , fmt_unveil < ET > : : get ( value ) , nullptr , 0 ) ;
}
2017-08-08 21:13:48 +02:00
}
// Error constructor (2 args)
2018-08-24 13:01:53 +02:00
template < typename ET , typename T2 >
2017-08-08 21:13:48 +02:00
error_code ( const ET & value , const T2 & value2 )
: value ( error_report ( fmt : : get_type_info < fmt_unveil_t < ET > > ( ) , fmt_unveil < ET > : : get ( value ) , fmt : : get_type_info < fmt_unveil_t < T2 > > ( ) , fmt_unveil < T2 > : : get ( value2 ) ) )
2016-08-16 17:46:24 +02:00
{
}
operator s32 ( ) const
{
return value ;
}
2016-08-15 17:30:33 +02:00
} ;
2016-08-16 17:46:24 +02:00
// Helper function for error_code
2016-08-15 17:30:33 +02:00
template < typename T >
2016-08-16 17:46:24 +02:00
constexpr FORCE_INLINE error_code : : not_an_error not_an_error ( const T & value )
2016-08-15 17:30:33 +02:00
{
2016-08-16 17:46:24 +02:00
return static_cast < error_code : : not_an_error > ( static_cast < s32 > ( value ) ) ;
2016-08-15 17:30:33 +02:00
}
2017-02-15 16:07:42 +01:00
// Synchronization helper (cache-friendly busy waiting)
2018-04-03 21:42:47 +02:00
inline void busy_wait ( std : : size_t cycles = 3000 )
2017-02-15 16:07:42 +01:00
{
2018-04-03 21:42:47 +02:00
const u64 s = __rdtsc ( ) ;
do _mm_pause ( ) ; while ( __rdtsc ( ) - s < cycles ) ;
2017-02-15 16:07:42 +01:00
}
2019-07-07 10:53:07 +02:00
// TODO: Remove when moving to c++20
template < typename T >
inline constexpr uintmax_t floor2 ( T value )
{
value > > = 1 ;
for ( uintmax_t i = 0 ; ; i + + , value > > = 1 )
{
if ( value = = 0 )
{
return i ;
}
}
}
template < typename T >
inline constexpr uintmax_t ceil2 ( T value )
{
const uintmax_t ispow2 = value & ( value - 1 ) ; // if power of 2 the result is 0
value > > = 1 ;
for ( uintmax_t i = 0 ; ; i + + , value > > = 1 )
{
if ( value = = 0 )
{
return i + std : : min < uintmax_t > ( ispow2 , 1 ) ;
}
}
2020-07-15 15:02:12 +02:00
}