#ifndef UTILS_BITFIELD_H #define UTILS_BITFIELD_H #include #include namespace bitfield { namespace impl { /// Constant check for supported underlying BITFILED types. template static constexpr bool is_bitfield_type_v = std::is_integral::value && // NOLINT(misc-redundant-expression) std::is_unsigned::value && // NOLINT(misc-redundant-expression) !std::is_same::value; /// Compute a BITMASK based on the HIGHBIT and LOWBIT template parameters. /// The HIGHBIT and LOWBIT are inclusive. /// /// # Example /// compute_mask<4, 7, uint32_t>() -> 0xf0 template static constexpr inline ValueType compute_mask() { using unsigned_t = std::make_unsigned_t; constexpr unsigned_t kMaxBits = sizeof(unsigned_t) * 8; static_assert(HighBit < kMaxBits, "HighBit exceeds bits of ValueType"); static_assert(LowBit <= HighBit, "HighBit must not be larger than LowBit"); constexpr unsigned_t kLen = HighBit - LowBit; return kLen == (kMaxBits - 1) ? ~static_cast(0) : ((static_cast(1) << (HighBit - LowBit + 1)) - 1) << LowBit; } } // namespace impl // -- FIELD REF ---------------------------------------------------------------- /// A mutable reference to a single FIELD in a BITFIELD. template struct field_ref { constexpr explicit field_ref(ValueType& val) : m_val{val} {} constexpr operator ValueType() const { return (m_val & kMask) >> LowBit; } constexpr ValueType val() const { return static_cast(*this); } constexpr field_ref& operator=(ValueType val) { m_val &= ~kMask; operator|=(val); return *this; } constexpr field_ref& operator|=(ValueType val) { m_val |= (val << LowBit) & kMask; return *this; } constexpr field_ref& operator&=(ValueType val) { m_val &= (val << LowBit) & kMask; return *this; } private: ValueType& m_val; enum : ValueType { // NOLINT(performance-enum-size) kMask = impl::compute_mask(), }; }; // -- CONST FIELD REF ---------------------------------------------------------- /// A constant reference to a single FIELD in a BITFIELD. template struct const_field_ref { constexpr explicit const_field_ref(const ValueType& val) : m_val{val} {} constexpr operator ValueType() const { return (m_val & kMask) >> LowBit; } constexpr ValueType val() const { return static_cast(*this); } private: const ValueType& m_val; enum : ValueType { // NOLINT(performance-enum-size) kMask = impl::compute_mask(), }; }; // -- BITFIELD ----------------------------------------------------------------- /// The BITFIELD base class. template struct bitfield { static_assert(impl::is_bitfield_type_v, "bitfield instantiated with incorrect type"); constexpr explicit bitfield(ValueType val) : m_val{val} {} constexpr operator ValueType() const { return m_val; } constexpr ValueType val() const { return static_cast(*this); } #define OPERATOR(OP) \ /* NOLINTNEXTLINE(bugprone-macro-parentheses) */ \ constexpr bitfield& operator OP(ValueType val) { \ m_val OP val; \ return *this; \ } OPERATOR(=) OPERATOR(|=) OPERATOR(&=) #undef OPERATOR protected: ValueType m_val; }; // -- MACROS ------------------------------------------------------------------- /// Code generator macros to conveniently define BITFIELDs with multiple FIELDs. /// /// # Example /// /// BITFIELD_START(status_reg, std::uint32_t) /// FIELD(N, 0, 0) /// FIELD(V, 1, 1) /// FIELD(C, 2, 2) /// FIELD(Z, 3, 3) /// FIELD(MODE, 4, 7) /// BITFIELD_END() /// /// status_ref r(0); /// /// std::uint32_t n = r.V(); /// r.V() = 0xfff; /// assert(r.V() == 0x1); /// assert(r == 0x2); #define BITFIELD_START(NAME, TYPE) \ struct NAME : public bitfield::bitfield { \ using ValueType = TYPE; \ using bitfield::operator=; \ \ constexpr explicit NAME(ValueType val) : bitfield{val} {} #define BITFIELD_END() \ } \ ; #define FIELD(NAME, L, H) \ constexpr auto NAME() { \ return ::bitfield::field_ref(m_val); \ } \ \ constexpr auto NAME() const { \ return ::bitfield::const_field_ref(m_val); \ } // -- TESTS -------------------------------------------------------------------- static_assert(impl::is_bitfield_type_v, ""); static_assert(!impl::is_bitfield_type_v, ""); static_assert(!impl::is_bitfield_type_v, ""); static_assert(!impl::is_bitfield_type_v, ""); static_assert(!impl::is_bitfield_type_v, ""); static_assert(impl::compute_mask<0, 0, std::uint32_t>() == 0x1, ""); static_assert(impl::compute_mask<4, 7, std::uint32_t>() == 0xf0, ""); static_assert(impl::compute_mask<12, 12, std::uint32_t>() == 0x1000, ""); static_assert(impl::compute_mask<15, 16, std::uint32_t>() == 0x18000, ""); static_assert(impl::compute_mask<0, 31, std::uint32_t>() == 0xffffffff, ""); } // namespace bitfield #endif