aboutsummaryrefslogblamecommitdiff
path: root/bitfield.h
blob: 850a326ba2033cdbb13f0b2a4d24fffb625fab4f (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12











                                                          
                                          

                                                                               











































                                                                                

                    

















































                                                                                
                                                    












                                                               
                                                               























































                                                                                




                                                             








                                                                            
#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
 * 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;
    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 {
    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_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 ::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_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