#pragma once #ifdef _MSC_VER #include #else #include #endif #define IS_LE_MACHINE // only draft union v128 { template class 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]; } T& at(std::size_t index) { return (index ^ M) < N ? m_data[index ^ M] : throw std::out_of_range(__FUNCTION__); } const T& at(std::size_t index) const { return (index ^ M) < N ? m_data[index ^ M] : throw std::out_of_range(__FUNCTION__); } }; #ifdef IS_LE_MACHINE template using normal_array_t = masked_array_t; template using reversed_array_t = masked_array_t; #else 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; class 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) { #ifdef IS_LE_MACHINE return bit_element(m_data[1 - (index >> 6)], 0x8000000000000000ull >> (index & 0x3F)); #else return bit_element(m_data[index >> 6], 0x8000000000000000ull >> (index & 0x3F)); #endif } // Index 0 returns the MSB and index 127 returns the LSB bool operator [](u32 index) const { #ifdef IS_LE_MACHINE return (m_data[1 - (index >> 6)] & (0x8000000000000000ull >> (index & 0x3F))) != 0; #else return (m_data[index >> 6] & (0x8000000000000000ull >> (index & 0x3F))) != 0; #endif } bit_element at(u32 index) { if (index >= 128) throw std::out_of_range(__FUNCTION__); return operator[](index); } bool at(u32 index) const { if (index >= 128) throw std::out_of_range(__FUNCTION__); return operator[](index); } } _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]; } bool is_any_1() const // check if any bit is 1 { return _u64[0] || _u64[1]; } bool is_any_0() const // check if any bit is 0 { return ~_u64[0] || ~_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; static inline v128 byteswap(const v128 val) { return fromV(_mm_shuffle_epi8(val.vi, _mm_set_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15))); } }; CHECK_SIZE_ALIGN(v128, 16, 16); 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]); } template struct se_storage { static_assert(!Size, "Bad se_storage<> type"); }; template struct se_storage { using type = u16; [[deprecated]] static constexpr u16 _swap(u16 src) // for reference { return (src >> 8) | (src << 8); } static inline 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; [[deprecated]] static constexpr u32 _swap(u32 src) // for reference { return (src >> 24) | (src << 24) | ((src >> 8) & 0x0000ff00) | ((src << 8) & 0x00ff0000); } static inline 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; [[deprecated]] static constexpr u64 _swap(u64 src) // for reference { return (src >> 56) | (src << 56) | ((src >> 40) & 0x000000000000ff00) | ((src >> 24) & 0x0000000000ff0000) | ((src >> 8) & 0x00000000ff000000) | ((src << 8) & 0x000000ff00000000) | ((src << 24) & 0x0000ff0000000000) | ((src << 40) & 0x00ff000000000000); } static inline 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 to(const T& src) { return v128::byteswap(reinterpret_cast(src)); } static inline T from(const v128& src) { const v128 result = v128::byteswap(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; // se_t with 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_union::value && !std::is_class::value || std::is_same::value || std::is_same::value, "se_t<> error: invalid type (struct or union)"); 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(!std::is_enum::value, "se_t<> error: invalid type (enumeration), use integral type instead"); static_assert(alignof(type) == alignof(stype), "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; } 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, 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, 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, se_t&> operator ^=(CT right) { return m_data ^= storage::to(right), *this; } }; // se_t with native endianness template class se_t { using type = typename std::remove_cv::type; type m_data; static_assert(!std::is_union::value && !std::is_class::value || std::is_same::value || std::is_same::value, "se_t<> error: invalid type (struct or union)"); 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(!std::is_enum::value, "se_t<> error: invalid type (enumeration), use integral type instead"); public: se_t() = default; se_t(const se_t&) = default; constexpr se_t(type value) : m_data(value) { } type value() const { return m_data; } se_t& operator =(const se_t& value) = default; se_t& operator =(type value) { return m_data = value, *this; } operator type() const { return m_data; } template std::enable_if_t::value, se_t&> operator &=(const CT& right) { return m_data &= right, *this; } template std::enable_if_t::value, se_t&> operator |=(const CT& right) { return m_data |= right, *this; } template std::enable_if_t::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= 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 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= 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 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= 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= 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= 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= 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= 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= 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& right) { return{ ~right.raw_data(), se_raw }; } #ifdef IS_LE_MACHINE template using be_t = se_t; template using le_t = se_t; #else template using be_t = se_t; template using le_t = se_t; #endif template struct to_se { using type = typename std::conditional::value || std::is_enum::value, se_t, T>::type; }; 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 { using type = typename to_se::type[]; }; template struct to_se { using type = typename to_se::type[N]; }; template struct to_se { using type = se_t; }; 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; }; #ifdef IS_LE_MACHINE template using to_be_t = typename to_se::type; template using to_le_t = typename to_se::type; #else template using to_be_t = typename to_se::type; template using to_le_t = typename to_se::type; #endif template struct to_ne { using type = T; }; template struct to_ne> { using type = typename std::remove_cv::type; }; template struct to_ne::value>> // move const qualifier { using type = const typename to_ne::type; }; template struct to_ne::value && !std::is_const::value>> // move volatile qualifier { using type = volatile typename to_ne::type; }; template struct to_ne { using type = typename to_ne::type[]; }; template struct to_ne { using type = typename to_ne::type[N]; }; // restore native endianness for T: returns T for be_t or le_t, T otherwise template using to_ne_t = typename to_ne::type;