#pragma once #include #include #include using schar = signed char; using uchar = unsigned char; using ushort = unsigned short; using uint = unsigned int; using ulong = unsigned long; using ullong = unsigned long long; using llong = long long; using u8 = std::uint8_t; using u16 = std::uint16_t; using u32 = std::uint32_t; using u64 = std::uint64_t; using s8 = std::int8_t; using s16 = std::int16_t; using s32 = std::int32_t; using s64 = std::int64_t; namespace gsl { enum class byte : u8; } // Formatting helper, type-specific preprocessing for improving safety and functionality template struct fmt_unveil; struct fmt_type_info; namespace fmt { template const fmt_type_info* get_type_info(); } template struct se_storage; template class se_t; template struct atomic_storage; template struct atomic_add; template struct atomic_sub; template struct atomic_and; template struct atomic_or; template struct atomic_xor; template struct atomic_pre_inc; template struct atomic_post_inc; template struct atomic_pre_dec; template struct atomic_post_dec; template struct atomic_test_and_set; template struct atomic_test_and_reset; template struct atomic_test_and_complement; template class atomic_t; #ifdef _MSC_VER using std::void_t; #else namespace void_details { template struct make_void { using type = void; }; } template using void_t = typename void_details::make_void::type; #endif // Extract T::simple_type if available, remove cv qualifiers template struct simple_type_helper { using type = typename std::remove_cv::type; }; template struct simple_type_helper> { using type = typename T::simple_type; }; template using simple_t = typename simple_type_helper::type; // Bool type equivalent class b8 { u8 m_value; public: b8() = default; constexpr b8(bool value) : m_value(value) { } constexpr operator bool() const { return m_value != 0; } }; // Bool wrapper for restricting bool result conversions struct explicit_bool_t { const bool value; constexpr explicit_bool_t(bool value) : value(value) { } explicit constexpr operator bool() const { return value; } }; #ifndef _MSC_VER using u128 = __uint128_t; using s128 = __int128_t; #else #include "intrin.h" // Unsigned 128-bit integer implementation (TODO) struct alignas(16) u128 { u64 lo, hi; u128() = default; constexpr u128(u64 l) : lo(l) , hi(0) { } friend u128 operator +(const u128& l, const u128& r) { u128 value; _addcarry_u64(_addcarry_u64(0, r.lo, l.lo, &value.lo), r.hi, l.hi, &value.hi); return value; } friend u128 operator +(const u128& l, u64 r) { u128 value; _addcarry_u64(_addcarry_u64(0, r, l.lo, &value.lo), l.hi, 0, &value.hi); return value; } friend u128 operator +(u64 l, const u128& r) { u128 value; _addcarry_u64(_addcarry_u64(0, r.lo, l, &value.lo), 0, r.hi, &value.hi); return value; } friend u128 operator -(const u128& l, const u128& r) { u128 value; _subborrow_u64(_subborrow_u64(0, r.lo, l.lo, &value.lo), r.hi, l.hi, &value.hi); return value; } friend u128 operator -(const u128& l, u64 r) { u128 value; _subborrow_u64(_subborrow_u64(0, r, l.lo, &value.lo), 0, l.hi, &value.hi); return value; } friend u128 operator -(u64 l, const u128& r) { u128 value; _subborrow_u64(_subborrow_u64(0, r.lo, l, &value.lo), r.hi, 0, &value.hi); return value; } u128 operator +() const { return *this; } u128 operator -() const { u128 value; _subborrow_u64(_subborrow_u64(0, lo, 0, &value.lo), hi, 0, &value.hi); return value; } u128& operator ++() { _addcarry_u64(_addcarry_u64(0, 1, lo, &lo), 0, hi, &hi); return *this; } u128 operator ++(int) { u128 value = *this; _addcarry_u64(_addcarry_u64(0, 1, lo, &lo), 0, hi, &hi); return value; } u128& operator --() { _subborrow_u64(_subborrow_u64(0, 1, lo, &lo), 0, hi, &hi); return *this; } u128 operator --(int) { u128 value = *this; _subborrow_u64(_subborrow_u64(0, 1, lo, &lo), 0, hi, &hi); return value; } u128 operator ~() const { u128 value; value.lo = ~lo; value.hi = ~hi; return value; } friend u128 operator &(const u128& l, const u128& r) { u128 value; value.lo = l.lo & r.lo; value.hi = l.hi & r.hi; return value; } friend u128 operator |(const u128& l, const u128& r) { u128 value; value.lo = l.lo | r.lo; value.hi = l.hi | r.hi; return value; } friend u128 operator ^(const u128& l, const u128& r) { u128 value; value.lo = l.lo ^ r.lo; value.hi = l.hi ^ r.hi; return value; } u128& operator +=(const u128& r) { _addcarry_u64(_addcarry_u64(0, r.lo, lo, &lo), r.hi, hi, &hi); return *this; } u128& operator +=(uint64_t r) { _addcarry_u64(_addcarry_u64(0, r, lo, &lo), 0, hi, &hi); return *this; } u128& operator &=(const u128& r) { lo &= r.lo; hi &= r.hi; return *this; } u128& operator |=(const u128& r) { lo |= r.lo; hi |= r.hi; return *this; } u128& operator ^=(const u128& r) { lo ^= r.lo; hi ^= r.hi; return *this; } }; // Signed 128-bit integer implementation (TODO) struct alignas(16) s128 { u64 lo; s64 hi; s128() = default; constexpr s128(s64 l) : hi(l >> 63) , lo(l) { } constexpr s128(u64 l) : hi(0) , lo(l) { } }; #endif namespace std { /* Let's hack. */ template<> struct is_integral : true_type { }; template<> struct is_integral : true_type { }; template<> struct make_unsigned { using type = u128; }; template<> struct make_unsigned { using type = u128; }; template<> struct make_signed { using type = s128; }; template<> struct make_signed { using type = s128; }; } static_assert(std::is_arithmetic::value && std::is_integral::value && alignof(u128) == 16 && sizeof(u128) == 16, "Wrong u128 implementation"); static_assert(std::is_arithmetic::value && std::is_integral::value && alignof(s128) == 16 && sizeof(s128) == 16, "Wrong s128 implementation"); union alignas(2) f16 { u16 _u16; u8 _u8[2]; explicit f16(u16 raw) { _u16 = raw; } explicit operator float() const { // See http://stackoverflow.com/a/26779139 // The conversion doesn't handle NaN/Inf u32 raw = ((_u16 & 0x8000) << 16) | // Sign (just moved) (((_u16 & 0x7c00) + 0x1C000) << 13) | // Exponent ( exp - 15 + 127) ((_u16 & 0x03FF) << 13); // Mantissa return (float&)raw; } }; using f32 = float; using f64 = double; struct ignore { template ignore(T) { } }; template::value>> constexpr T align(const T& value, std::uint64_t align) { return static_cast((value + (align - 1)) & ~(align - 1)); } namespace fmt { [[noreturn]] void raw_error(const char* msg); [[noreturn]] void raw_narrow_error(const char* msg, const fmt_type_info* sup, u64 arg); } // Narrow cast (throws on failure) template(std::declval()))> inline To narrow(const From& value, const char* msg = nullptr) { // Allow "narrowing to void" and ensure it always fails in this case auto&& result = static_cast::value, From, To>>(value); if (std::is_void::value || static_cast(result) != value) { // Pack value as formatting argument fmt::raw_narrow_error(msg, fmt::get_type_info::type>(), fmt_unveil::get(value)); } return static_cast::value, void, decltype(result)>>(result); } // Returns u32 size() for container template(std::declval().size()))> inline u32 size32(const CT& container, const char* msg = nullptr) { return narrow(container.size(), msg); } // Returns u32 size for an array template constexpr u32 size32(const T(&)[Size], const char* msg = nullptr) { return static_cast(Size); } template::value>> constexpr bool test(const T1& value) { return value != 0; } template::value && std::is_integral::value>> constexpr bool test(const T1& lhs, const T2& rhs) { return (lhs & rhs) != 0; } template::value && std::is_integral::value>> inline bool test_and_set(T& lhs, const T2& rhs) { const bool result = (lhs & rhs) != 0; lhs |= rhs; return result; } template::value && std::is_integral::value>> inline bool test_and_reset(T& lhs, const T2& rhs) { const bool result = (lhs & rhs) != 0; lhs &= ~rhs; return result; } template::value && std::is_integral::value>> inline bool test_and_complement(T& lhs, const T2& rhs) { const bool result = (lhs & rhs) != 0; lhs ^= rhs; return result; } // Simplified hash algorithm for pointers. May be used in std::unordered_(map|set). template struct pointer_hash { std::size_t operator()(T* ptr) const { return reinterpret_cast(ptr) / Align; } }; template struct value_hash { std::size_t operator()(T value) const { return static_cast(value) >> Shift; } }; // 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. template class TT, std::size_t S, std::size_t A = S> struct alignas(A) any_pod { std::aligned_storage_t data; any_pod() = default; template, typename = std::enable_if_t::value && sizeof(T2) == S && alignof(T2) <= A>> any_pod(const T& value) { reinterpret_cast(data) = value; } template, typename = std::enable_if_t::value && sizeof(T2) == S && alignof(T2) <= A>> T2& as() { return reinterpret_cast(data); } template, typename = std::enable_if_t::value && sizeof(T2) == S && alignof(T2) <= A>> const T2& as() const { return reinterpret_cast(data); } }; using any16 = any_pod; using any32 = any_pod; using any64 = any_pod; // Allows to define integer convertible to multiple types template struct multicast : multicast { constexpr multicast() = default; // Implicit conversion to desired type constexpr operator T1() const { return static_cast(Value); } }; // Recursion terminator template struct multicast { constexpr multicast() = default; // Explicit conversion to base type explicit constexpr operator T() const { return Value; } }; // Tagged ID type template class id_value { // Initial value mutable ID m_value{ static_cast(-1) }; // Allow access for ID manager friend class idm; // Update ID void operator =(const ID& value) const { m_value = value; } public: constexpr id_value() {} // Get the value operator ID() const { return m_value; } }; template struct fmt_unveil> { using type = typename fmt_unveil::type; static inline auto get(const id_value& value) { return fmt_unveil::get(value); } };