#ifndef UTILS_BITFIELD_H
#define UTILS_BITFIELD_H
#include <cstdint>
#include <type_traits>
namespace bitfield {
namespace impl {
/// Constant check for supported underlying BITFILED types.
template <typename ValueType>
static constexpr bool is_bitfield_type_v =
std::is_integral<ValueType>::value && // NOLINT(misc-redundant-expression)
std::is_unsigned<ValueType>::value && // NOLINT(misc-redundant-expression)
!std::is_same<ValueType, bool>::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 <std::uint8_t LowBit, std::uint8_t HighBit, typename ValueType>
static constexpr inline ValueType compute_mask() {
using unsigned_t = std::make_unsigned_t<ValueType>;
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<unsigned_t>(0)
: ((static_cast<unsigned_t>(1) << (HighBit - LowBit + 1)) - 1)
<< LowBit;
}
} // namespace impl
// -- FIELD REF ----------------------------------------------------------------
/// A mutable reference to a single FIELD in a BITFIELD.
template <std::uint8_t LowBit, std::uint8_t HighBit, typename ValueType>
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<ValueType>(*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<LowBit, HighBit, ValueType>(),
};
};
// -- CONST FIELD REF ----------------------------------------------------------
/// A constant reference to a single FIELD in a BITFIELD.
template <std::uint8_t LowBit, std::uint8_t HighBit, typename ValueType>
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<ValueType>(*this);
}
private:
const ValueType& m_val;
enum : ValueType { // NOLINT(performance-enum-size)
kMask = impl::compute_mask<LowBit, HighBit, ValueType>(),
};
};
// -- BITFIELD -----------------------------------------------------------------
/// The BITFIELD base class.
template <typename ValueType = std::uint32_t>
struct bitfield {
static_assert(impl::is_bitfield_type_v<ValueType>,
"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<ValueType>(*this);
}
#define OPERATOR(OP) \
/* NOLINTNEXTLINE(bugprone-macro-parentheses) */ \
constexpr bitfield<ValueType>& 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<TYPE> { \
using ValueType = TYPE; \
using bitfield<ValueType>::operator=; \
\
constexpr explicit NAME(ValueType val) : bitfield<ValueType>{val} {}
#define BITFIELD_END() \
} \
;
#define FIELD(NAME, L, H) \
constexpr auto NAME() { \
return ::bitfield::field_ref<L, H, ValueType>(m_val); \
} \
\
constexpr auto NAME() const { \
return ::bitfield::const_field_ref<L, H, ValueType>(m_val); \
}
// -- TESTS --------------------------------------------------------------------
static_assert(impl::is_bitfield_type_v<std::uint32_t>, "");
static_assert(!impl::is_bitfield_type_v<bool>, "");
static_assert(!impl::is_bitfield_type_v<std::int32_t>, "");
static_assert(!impl::is_bitfield_type_v<std::uint32_t*>, "");
static_assert(!impl::is_bitfield_type_v<float>, "");
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