#pragma once #include "util/types.hpp" #include "Utilities/StrFmt.h" #ifndef _MSC_VER #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Weffc++" #endif template struct bf_base { using type = T; using vtype = std::common_type_t; using utype = typename std::make_unsigned::type; // Datatype bitsize static constexpr uint bitmax = sizeof(T) * 8; static_assert(N - 1 < bitmax, "bf_base<> error: N out of bounds"); // Field bitsize static constexpr uint bitsize = N; // All ones mask static constexpr utype mask1 = static_cast(~static_cast(0)); // Value mask static constexpr utype vmask = mask1 >> (bitmax - bitsize); protected: type m_data; }; // Bitfield accessor (N bits from I position, 0 is LSB) template struct bf_t : bf_base { using type = typename bf_t::type; using vtype = typename bf_t::vtype; using utype = typename bf_t::utype; // Field offset static constexpr uint bitpos = I; static_assert(bitpos + N <= bf_t::bitmax, "bf_t<> error: I out of bounds"); // Get bitmask of size N, at I pos static constexpr utype data_mask() { return static_cast(static_cast(bf_t::mask1 >> (bf_t::bitmax - bf_t::bitsize)) << bitpos); } // Bitfield extraction helper template struct extract_impl { static_assert(!sizeof(T2), "bf_t<> error: Invalid type"); }; template struct extract_impl::value>> { // Load unsigned value static constexpr T2 extract(const T& data) { return static_cast((static_cast(data) >> bitpos) & bf_t::vmask); } }; template struct extract_impl::value>> { // Load signed value (sign-extended) static constexpr T2 extract(const T& data) { return static_cast(static_cast(static_cast(data) << (bf_t::bitmax - bitpos - N)) >> (bf_t::bitmax - N)); } }; // Bitfield extraction static constexpr vtype extract(const T& data) { return extract_impl::extract(data); } // Bitfield insertion static constexpr vtype insert(vtype value) { return static_cast((value & bf_t::vmask) << bitpos); } // Load bitfield value constexpr operator vtype() const { return extract(this->m_data); } // Load raw data with mask applied constexpr T unshifted() const { return static_cast(this->m_data & data_mask()); } // Optimized bool conversion (must be removed if inappropriate) explicit constexpr operator bool() const { return unshifted() != 0u; } // Store bitfield value bf_t& operator =(vtype value) { this->m_data = static_cast((this->m_data & ~data_mask()) | insert(value)); return *this; } vtype operator ++(int) { utype result = *this; *this = static_cast(result + 1); return result; } bf_t& operator ++() { return *this = *this + 1; } vtype operator --(int) { utype result = *this; *this = static_cast(result - 1); return result; } bf_t& operator --() { return *this = *this - 1; } bf_t& operator +=(vtype right) { return *this = *this + right; } bf_t& operator -=(vtype right) { return *this = *this - right; } bf_t& operator *=(vtype right) { return *this = *this * right; } bf_t& operator &=(vtype right) { this->m_data &= static_cast(((static_cast(right) & bf_t::vmask) << bitpos) | ~(bf_t::vmask << bitpos)); return *this; } bf_t& operator |=(vtype right) { this->m_data |= static_cast((static_cast(right) & bf_t::vmask) << bitpos); return *this; } bf_t& operator ^=(vtype right) { this->m_data ^= static_cast((static_cast(right) & bf_t::vmask) << bitpos); return *this; } }; // Field pack (concatenated from left to right) template struct cf_t : bf_base::bitsize> { using type = typename cf_t::type; using vtype = typename cf_t::vtype; using utype = typename cf_t::utype; // Get disjunction of all "data" masks of concatenated values static constexpr vtype data_mask() { return static_cast(F::data_mask() | cf_t::data_mask()); } // Extract all bitfields and concatenate static constexpr vtype extract(const type& data) { return static_cast(static_cast(F::extract(data)) << cf_t::bitsize | cf_t::extract(data)); } // Split bitfields and insert them static constexpr vtype insert(vtype value) { return static_cast(F::insert(value >> cf_t::bitsize) | cf_t::insert(value)); } // Load value constexpr operator vtype() const { return extract(this->m_data); } // Store value cf_t& operator =(vtype value) { this->m_data = (this->m_data & ~data_mask()) | insert(value); return *this; } }; // Empty field pack (recursion terminator) template<> struct cf_t { static constexpr uint bitsize = 0; static constexpr uint data_mask() { return 0; } template static constexpr auto extract(const T&) -> decltype(+T()) { return 0; } template static constexpr T insert(T /*value*/) { return 0; } }; // Fixed field (provides constant values in field pack) template struct ff_t : bf_base { using type = typename ff_t::type; using vtype = typename ff_t::vtype; // Return constant value static constexpr vtype extract(const type&) { static_assert((V & ff_t::vmask) == V, "ff_t<> error: V out of bounds"); return V; } // Get value operator vtype() const { return V; } }; #ifndef _MSC_VER #pragma GCC diagnostic pop #endif template struct fmt_unveil, void> { using type = typename fmt_unveil>::type; static inline auto get(const bf_t& bf) { return fmt_unveil::get(bf); } }; template struct fmt_unveil, void> { using type = typename fmt_unveil>::type; static inline auto get(const cf_t& cf) { return fmt_unveil::get(cf); } }; template struct fmt_unveil, void> { using type = typename fmt_unveil>::type; static inline auto get(const ff_t& ff) { return fmt_unveil::get(ff); } };