#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 = std::make_unsigned_t; static constexpr bool can_be_packed = N < (sizeof(int) * 8 + (std::is_unsigned_v ? 1 : 0)) && sizeof(vtype) > sizeof(int); using compact_type = std::conditional_t, uint, int>, vtype>; // 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; using compact_type = typename bf_t::compact_type; // 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 static constexpr compact_type extract(const T& data) noexcept { if constexpr (std::is_signed_v) { // Load signed value (sign-extended) return static_cast(static_cast(static_cast(data) << (bf_t::bitmax - bitpos - N)) >> (bf_t::bitmax - N)); } else { // Load unsigned value return static_cast((static_cast(data) >> bitpos) & bf_t::vmask); } } // Bitfield insertion static constexpr vtype insert(compact_type value) { return static_cast((value & bf_t::vmask) << bitpos); } // Load bitfield value constexpr operator compact_type() const noexcept { 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 noexcept { return unshifted() != 0u; } // Store bitfield value bf_t& operator =(compact_type value) noexcept { this->m_data = static_cast((this->m_data & ~data_mask()) | insert(value)); return *this; } compact_type operator ++(int) { compact_type result = *this; *this = static_cast(result + 1u); return result; } bf_t& operator ++() { return *this = static_cast(*this + 1u); } compact_type operator --(int) { compact_type result = *this; *this = static_cast(result - 1u); return result; } bf_t& operator --() { return *this = static_cast(*this - 1u); } bf_t& operator +=(compact_type right) { return *this = static_cast(*this + right); } bf_t& operator -=(compact_type right) { return *this = static_cast(*this - right); } bf_t& operator *=(compact_type right) { return *this = static_cast(*this * right); } bf_t& operator &=(compact_type right) { this->m_data &= static_cast(((static_cast(right + 0u) & bf_t::vmask) << bitpos) | ~(bf_t::vmask << bitpos)); return *this; } bf_t& operator |=(compact_type right) { this->m_data |= static_cast((static_cast(right + 0u) & bf_t::vmask) << bitpos); return *this; } bf_t& operator ^=(compact_type right) { this->m_data ^= static_cast((static_cast(right + 0u) & bf_t::vmask) << bitpos); return *this; } }; template struct std::common_type, bf_t> : std::common_type {}; template struct std::common_type, T2> : std::common_type> {}; template struct std::common_type> : std::common_type, T2> {}; // 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; using compact_type = typename cf_t::compact_type; // 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 compact_type 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(compact_type value) { return static_cast(F::insert(value >> cf_t::bitsize) | cf_t::insert(value)); } // Load value constexpr operator compact_type() const noexcept { return extract(this->m_data); } // Store value cf_t& operator =(compact_type value) noexcept { 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 constexpr operator vtype() const noexcept { 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); } };