diff options
Diffstat (limited to 'bitfield.h')
-rw-r--r-- | bitfield.h | 193 |
1 files changed, 193 insertions, 0 deletions
diff --git a/bitfield.h b/bitfield.h new file mode 100644 index 0000000..833a722 --- /dev/null +++ b/bitfield.h @@ -0,0 +1,193 @@ +#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 = + std::is_integral<ValueType>::value && std::is_unsigned<ValueType>::value && + !std::is_same<ValueType, bool>::value; + +/** + * Compute a BITMASK based on the HIGHBIT and LOWBIT template parameters. + * The HIGHBIT and LOWBIT are inclusive. + * + * # Example + * ComputeMask<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 = typename std::make_unsigned<ValueType>::type; + + 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; + return operator|=(val); + } + + 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 { + 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 { + 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<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) \ + 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 ::bitfield::field_ref<L, H, ValueType> NAME() { \ + return ::bitfield::field_ref<L, H, ValueType>(m_val); \ + } \ + \ + constexpr ::bitfield::const_field_ref<L, H, ValueType> NAME() const { \ + return ::bitfield::const_field_ref<L, H, ValueType>(m_val); \ + } + +// -- TESTS -------------------------------------------------------------------- + +static_assert(impl::is_bitfield_type<std::uint32_t>, ""); +static_assert(!impl::is_bitfield_type<bool>, ""); +static_assert(!impl::is_bitfield_type<std::int32_t>, ""); +static_assert(!impl::is_bitfield_type<std::uint32_t*>, ""); +static_assert(!impl::is_bitfield_type<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 |