#pragma once #include "types.h" #include "Platform.h" union alignas(16) v128 { char _bytes[16]; template struct masked_array_t // array type accessed as (index ^ M) { T m_data[N]; public: T& operator [](std::size_t index) { return m_data[index ^ M]; } const T& operator [](std::size_t index) const { return m_data[index ^ M]; } }; #if IS_LE_MACHINE == 1 template using normal_array_t = masked_array_t; template using reversed_array_t = masked_array_t; #endif normal_array_t _u64; normal_array_t _s64; reversed_array_t u64r; reversed_array_t s64r; normal_array_t _u32; normal_array_t _s32; reversed_array_t u32r; reversed_array_t s32r; normal_array_t _u16; normal_array_t _s16; reversed_array_t u16r; reversed_array_t s16r; normal_array_t _u8; normal_array_t _s8; reversed_array_t u8r; reversed_array_t s8r; normal_array_t _f; normal_array_t _d; reversed_array_t fr; reversed_array_t dr; __m128 vf; __m128i vi; __m128d vd; struct bit_array_128 { u64 m_data[2]; public: class bit_element { u64& data; const u64 mask; public: bit_element(u64& data, const u64 mask) : data(data) , mask(mask) { } operator bool() const { return (data & mask) != 0; } bit_element& operator =(const bool right) { if (right) { data |= mask; } else { data &= ~mask; } return *this; } bit_element& operator =(const bit_element& right) { if (right) { data |= mask; } else { data &= ~mask; } return *this; } }; // Index 0 returns the MSB and index 127 returns the LSB bit_element operator [](u32 index) { #if IS_LE_MACHINE == 1 return bit_element(m_data[1 - (index >> 6)], 0x8000000000000000ull >> (index & 0x3F)); #endif } // Index 0 returns the MSB and index 127 returns the LSB bool operator [](u32 index) const { #if IS_LE_MACHINE == 1 return (m_data[1 - (index >> 6)] & (0x8000000000000000ull >> (index & 0x3F))) != 0; #endif } } _bit; static v128 from64(u64 _0, u64 _1 = 0) { v128 ret; ret._u64[0] = _0; ret._u64[1] = _1; return ret; } static v128 from64r(u64 _1, u64 _0 = 0) { return from64(_0, _1); } static v128 from32(u32 _0, u32 _1 = 0, u32 _2 = 0, u32 _3 = 0) { v128 ret; ret._u32[0] = _0; ret._u32[1] = _1; ret._u32[2] = _2; ret._u32[3] = _3; return ret; } static v128 from32r(u32 _3, u32 _2 = 0, u32 _1 = 0, u32 _0 = 0) { return from32(_0, _1, _2, _3); } static v128 from32p(u32 value) { v128 ret; ret.vi = _mm_set1_epi32(static_cast(value)); return ret; } static v128 from16p(u16 value) { v128 ret; ret.vi = _mm_set1_epi16(static_cast(value)); return ret; } static v128 from8p(u8 value) { v128 ret; ret.vi = _mm_set1_epi8(static_cast(value)); return ret; } static v128 fromBit(u32 bit) { v128 ret = {}; ret._bit[bit] = true; return ret; } static v128 fromV(__m128i value) { v128 ret; ret.vi = value; return ret; } static v128 fromF(__m128 value) { v128 ret; ret.vf = value; return ret; } static v128 fromD(__m128d value) { v128 ret; ret.vd = value; return ret; } static inline v128 add8(const v128& left, const v128& right) { return fromV(_mm_add_epi8(left.vi, right.vi)); } static inline v128 add16(const v128& left, const v128& right) { return fromV(_mm_add_epi16(left.vi, right.vi)); } static inline v128 add32(const v128& left, const v128& right) { return fromV(_mm_add_epi32(left.vi, right.vi)); } static inline v128 addfs(const v128& left, const v128& right) { return fromF(_mm_add_ps(left.vf, right.vf)); } static inline v128 addfd(const v128& left, const v128& right) { return fromD(_mm_add_pd(left.vd, right.vd)); } static inline v128 sub8(const v128& left, const v128& right) { return fromV(_mm_sub_epi8(left.vi, right.vi)); } static inline v128 sub16(const v128& left, const v128& right) { return fromV(_mm_sub_epi16(left.vi, right.vi)); } static inline v128 sub32(const v128& left, const v128& right) { return fromV(_mm_sub_epi32(left.vi, right.vi)); } static inline v128 subfs(const v128& left, const v128& right) { return fromF(_mm_sub_ps(left.vf, right.vf)); } static inline v128 subfd(const v128& left, const v128& right) { return fromD(_mm_sub_pd(left.vd, right.vd)); } static inline v128 maxu8(const v128& left, const v128& right) { return fromV(_mm_max_epu8(left.vi, right.vi)); } static inline v128 minu8(const v128& left, const v128& right) { return fromV(_mm_min_epu8(left.vi, right.vi)); } static inline v128 eq8(const v128& left, const v128& right) { return fromV(_mm_cmpeq_epi8(left.vi, right.vi)); } static inline v128 eq16(const v128& left, const v128& right) { return fromV(_mm_cmpeq_epi16(left.vi, right.vi)); } static inline v128 eq32(const v128& left, const v128& right) { return fromV(_mm_cmpeq_epi32(left.vi, right.vi)); } bool operator ==(const v128& right) const { return _u64[0] == right._u64[0] && _u64[1] == right._u64[1]; } bool operator !=(const v128& right) const { return _u64[0] != right._u64[0] || _u64[1] != right._u64[1]; } // result = (~left) & (right) static inline v128 andnot(const v128& left, const v128& right) { return fromV(_mm_andnot_si128(left.vi, right.vi)); } void clear() { _u64[0] = 0; _u64[1] = 0; } std::string to_hex() const; std::string to_xyzw() const; }; inline v128 operator |(const v128& left, const v128& right) { return v128::fromV(_mm_or_si128(left.vi, right.vi)); } inline v128 operator &(const v128& left, const v128& right) { return v128::fromV(_mm_and_si128(left.vi, right.vi)); } inline v128 operator ^(const v128& left, const v128& right) { return v128::fromV(_mm_xor_si128(left.vi, right.vi)); } inline v128 operator ~(const v128& other) { return v128::from64(~other._u64[0], ~other._u64[1]); } #define IS_INTEGER(t) (std::is_integral::value || std::is_enum::value) #define IS_BINARY_COMPARABLE(t1, t2) (IS_INTEGER(t1) && IS_INTEGER(t2) && sizeof(t1) == sizeof(t2)) template struct se_storage { static_assert(!Size, "Bad se_storage<> type"); }; template struct se_storage { using type = u16; static constexpr u16 swap(u16 src) { #if defined(__GNUG__) return __builtin_bswap16(src); #else return _byteswap_ushort(src); #endif } static inline u16 to(const T& src) { return swap(reinterpret_cast(src)); } static inline T from(u16 src) { const u16 result = swap(src); return reinterpret_cast(result); } }; template struct se_storage { using type = u32; static constexpr u32 swap(u32 src) { #if defined(__GNUG__) return __builtin_bswap32(src); #else return _byteswap_ulong(src); #endif } static inline u32 to(const T& src) { return swap(reinterpret_cast(src)); } static inline T from(u32 src) { const u32 result = swap(src); return reinterpret_cast(result); } }; template struct se_storage { using type = u64; static constexpr u64 swap(u64 src) { #if defined(__GNUG__) return __builtin_bswap64(src); #else return _byteswap_uint64(src); #endif } static inline u64 to(const T& src) { return swap(reinterpret_cast(src)); } static inline T from(u64 src) { const u64 result = swap(src); return reinterpret_cast(result); } }; template struct se_storage { using type = v128; static inline v128 swap(const v128& src) { return v128::fromV(_mm_shuffle_epi8(src.vi, _mm_set_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15))); } static inline v128 to(const T& src) { return swap(reinterpret_cast(src)); } static inline T from(const v128& src) { const v128 result = swap(src); return reinterpret_cast(result); } }; template using se_storage_t = typename se_storage::type; template struct se_convert { using type_from = std::remove_cv_t; using type_to = std::remove_cv_t; using stype_from = se_storage_t>; using stype_to = se_storage_t>; using storage_from = se_storage>; using storage_to = se_storage>; static inline std::enable_if_t::value, stype_to> convert(const stype_from& data) { return data; } static inline stype_to convert(const stype_from& data, ...) { return storage_to::to(storage_from::from(data)); } }; static struct se_raw_tag_t {} constexpr se_raw{}; template class se_t; // Switched endianness template class se_t { using type = typename std::remove_cv::type; using stype = se_storage_t; using storage = se_storage; stype m_data; static_assert(!std::is_pointer::value, "se_t<> error: invalid type (pointer)"); static_assert(!std::is_reference::value, "se_t<> error: invalid type (reference)"); static_assert(!std::is_array::value, "se_t<> error: invalid type (array)"); static_assert(sizeof(type) == alignof(type), "se_t<> error: unexpected alignment"); template struct bool_converter { static inline bool to_bool(const se_t& value) { return static_cast(value.value()); } }; template struct bool_converter::value>> { static inline bool to_bool(const se_t& value) { return value.m_data != 0; } }; public: se_t() = default; se_t(const se_t& right) = default; se_t(type value) : m_data(storage::to(value)) { } // Construct directly from raw data (don't use) constexpr se_t(const stype& raw_value, const se_raw_tag_t&) : m_data(raw_value) { } type value() const { return storage::from(m_data); } // Access underlying raw data (don't use) constexpr const stype& raw_data() const noexcept { return m_data; } se_t& operator =(const se_t&) = default; se_t& operator =(type value) { return m_data = storage::to(value), *this; } using simple_type = simple_t; operator type() const { return storage::from(m_data); } // Optimization explicit operator bool() const { return bool_converter::to_bool(*this); } // Optimization template std::enable_if_t operator &=(const se_t& right) { return m_data &= right.raw_data(), *this; } // Optimization template std::enable_if_t::value && std::is_convertible::value, se_t&> operator &=(CT right) { return m_data &= storage::to(right), *this; } // Optimization template std::enable_if_t operator |=(const se_t& right) { return m_data |= right.raw_data(), *this; } // Optimization template std::enable_if_t::value && std::is_convertible::value, se_t&> operator |=(CT right) { return m_data |= storage::to(right), *this; } // Optimization template std::enable_if_t operator ^=(const se_t& right) { return m_data ^= right.raw_data(), *this; } // Optimization template std::enable_if_t::value && std::is_convertible::value, se_t&> operator ^=(CT right) { return m_data ^= storage::to(right), *this; } }; // Native endianness template class se_t { using type = typename std::remove_cv::type; static_assert(!std::is_pointer::value, "se_t<> error: invalid type (pointer)"); static_assert(!std::is_reference::value, "se_t<> error: invalid type (reference)"); static_assert(!std::is_array::value, "se_t<> error: invalid type (array)"); static_assert(sizeof(type) == alignof(type), "se_t<> error: unexpected alignment"); type m_data; public: se_t() = default; constexpr se_t(type value) : m_data(value) { } // Construct directly from raw data (don't use) constexpr se_t(const type& raw_value, const se_raw_tag_t&) : m_data(raw_value) { } constexpr type value() const { return m_data; } // Access underlying raw data (don't use) constexpr const type& raw_data() const noexcept { return m_data; } se_t& operator =(const se_t& value) = default; se_t& operator =(type value) { return m_data = value, *this; } using simple_type = simple_t; constexpr operator type() const { return m_data; } template std::enable_if_t::value && std::is_convertible::value, se_t&> operator &=(const CT& right) { return m_data &= right, *this; } template std::enable_if_t::value && std::is_convertible::value, se_t&> operator |=(const CT& right) { return m_data |= right, *this; } template std::enable_if_t::value && std::is_convertible::value, se_t&> operator ^=(const CT& right) { return m_data ^= right, *this; } }; // se_t with native endianness (alias) template using nse_t = se_t; template inline se_t& operator +=(se_t& left, const T1& right) { auto value = left.value(); return left = (value += right); } template inline se_t& operator -=(se_t& left, const T1& right) { auto value = left.value(); return left = (value -= right); } template inline se_t& operator *=(se_t& left, const T1& right) { auto value = left.value(); return left = (value *= right); } template inline se_t& operator /=(se_t& left, const T1& right) { auto value = left.value(); return left = (value /= right); } template inline se_t& operator %=(se_t& left, const T1& right) { auto value = left.value(); return left = (value %= right); } template inline se_t& operator <<=(se_t& left, const T1& right) { auto value = left.value(); return left = (value <<= right); } template inline se_t& operator >>=(se_t& left, const T1& right) { auto value = left.value(); return left = (value >>= right); } template inline se_t operator ++(se_t& left, int) { auto value = left.value(); auto result = value++; left = value; return result; } template inline se_t operator --(se_t& left, int) { auto value = left.value(); auto result = value--; left = value; return result; } template inline se_t& operator ++(se_t& right) { auto value = right.value(); return right = ++value; } template inline se_t& operator --(se_t& right) { auto value = right.value(); return right = --value; } // Optimization template inline std::enable_if_t operator ==(const se_t& left, const se_t& right) { return left.raw_data() == right.raw_data(); } // Optimization template inline std::enable_if_t::value && IS_INTEGER(T2) && sizeof(T1) >= sizeof(T2), bool> operator ==(const se_t& left, T2 right) { return left.raw_data() == se_storage::to(right); } // Optimization template inline std::enable_if_t::value && sizeof(T1) <= sizeof(T2), bool> operator ==(T1 left, const se_t& right) { return se_storage::to(left) == right.raw_data(); } // Optimization template inline std::enable_if_t operator !=(const se_t& left, const se_t& right) { return left.raw_data() != right.raw_data(); } // Optimization template inline std::enable_if_t::value && IS_INTEGER(T2) && sizeof(T1) >= sizeof(T2), bool> operator !=(const se_t& left, T2 right) { return left.raw_data() != se_storage::to(right); } // Optimization template inline std::enable_if_t::value && sizeof(T1) <= sizeof(T2), bool> operator !=(T1 left, const se_t& right) { return se_storage::to(left) != right.raw_data(); } // Optimization template inline std::enable_if_t= 4, se_t> operator &(const se_t& left, const se_t& right) { return{ left.raw_data() & right.raw_data(), se_raw }; } // Optimization template inline std::enable_if_t::value && IS_INTEGER(T2) && sizeof(T1) >= sizeof(T2) && sizeof(T1) >= 4, se_t> operator &(const se_t& left, T2 right) { return{ left.raw_data() & se_storage::to(right), se_raw }; } // Optimization template inline std::enable_if_t::value && sizeof(T1) <= sizeof(T2) && sizeof(T2) >= 4, se_t> operator &(T1 left, const se_t& right) { return{ se_storage::to(left) & right.raw_data(), se_raw }; } // Optimization template inline std::enable_if_t= 4, se_t> operator |(const se_t& left, const se_t& right) { return{ left.raw_data() | right.raw_data(), se_raw }; } // Optimization template inline std::enable_if_t::value && IS_INTEGER(T2) && sizeof(T1) >= sizeof(T2) && sizeof(T1) >= 4, se_t> operator |(const se_t& left, T2 right) { return{ left.raw_data() | se_storage::to(right), se_raw }; } // Optimization template inline std::enable_if_t::value && sizeof(T1) <= sizeof(T2) && sizeof(T2) >= 4, se_t> operator |(T1 left, const se_t& right) { return{ se_storage::to(left) | right.raw_data(), se_raw }; } // Optimization template inline std::enable_if_t= 4, se_t> operator ^(const se_t& left, const se_t& right) { return{ left.raw_data() ^ right.raw_data(), se_raw }; } // Optimization template inline std::enable_if_t::value && IS_INTEGER(T2) && sizeof(T1) >= sizeof(T2) && sizeof(T1) >= 4, se_t> operator ^(const se_t& left, T2 right) { return{ left.raw_data() ^ se_storage::to(right), se_raw }; } // Optimization template inline std::enable_if_t::value && sizeof(T1) <= sizeof(T2) && sizeof(T2) >= 4, se_t> operator ^(T1 left, const se_t& right) { return{ se_storage::to(left) ^ right.raw_data(), se_raw }; } // Optimization template inline std::enable_if_t::value && sizeof(T) >= 4, se_t> operator ~(const se_t& right) { return{ ~right.raw_data(), se_raw }; } #if IS_LE_MACHINE == 1 template using be_t = se_t; template using le_t = se_t; #endif // Type converter: converts native endianness arithmetic/enum types to appropriate se_t<> type template struct to_se { // Convert arithmetic and enum types using type = typename std::conditional::value || std::is_enum::value, se_t, T>::type; }; template struct to_se { using type = se_t; }; template struct to_se { using type = bool; }; template struct to_se { using type = char; }; template struct to_se { using type = u8; }; template struct to_se { using type = s8; }; template struct to_se::value>> { // Move const qualifier using type = const typename to_se::type; }; template struct to_se::value && !std::is_const::value>> { // Move volatile qualifier using type = volatile typename to_se::type; }; template struct to_se { // Move array qualifier using type = typename to_se::type[]; }; template struct to_se { // Move array qualifier using type = typename to_se::type[N]; }; // BE/LE aliases for to_se<> #if IS_LE_MACHINE == 1 template using to_be_t = typename to_se::type; template using to_le_t = typename to_se::type; #endif // BE/LE aliases for atomic_t #if IS_LE_MACHINE == 1 template using atomic_be_t = atomic_t>; template using atomic_le_t = atomic_t>; #endif namespace fmt { // Formatting for BE/LE data template struct unveil, void> { using result_type = typename unveil::result_type; static inline result_type get_value(const se_t& arg) { return unveil::get_value(arg); } }; } #undef IS_BINARY_COMPARABLE #undef IS_INTEGER