rpcs3/Utilities/BitField.h

290 lines
7.0 KiB
C
Raw Permalink Normal View History

2020-12-25 02:18:36 +00:00
#pragma once
2020-12-12 12:01:29 +00:00
#include "util/types.hpp"
#include "Utilities/StrFmt.h"
2021-03-30 15:31:46 +00:00
#ifndef _MSC_VER
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#endif
template <typename T, uint N>
2016-02-01 21:55:43 +00:00
struct bf_base
{
using type = T;
using vtype = std::common_type_t<type>;
using utype = std::make_unsigned_t<vtype>;
static constexpr bool can_be_packed = N < (sizeof(int) * 8 + (std::is_unsigned_v<vtype> ? 1 : 0)) && sizeof(vtype) > sizeof(int);
using compact_type = std::conditional_t<can_be_packed, std::conditional_t<std::is_unsigned_v<vtype>, uint, int>, vtype>;
2016-02-01 21:55:43 +00:00
// Datatype bitsize
2016-08-14 00:22:19 +00:00
static constexpr uint bitmax = sizeof(T) * 8; static_assert(N - 1 < bitmax, "bf_base<> error: N out of bounds");
2016-02-01 21:55:43 +00:00
// Field bitsize
static constexpr uint bitsize = N;
2017-06-30 22:53:16 +00:00
// All ones mask
2019-11-10 05:48:42 +00:00
static constexpr utype mask1 = static_cast<utype>(~static_cast<utype>(0));
// Value mask
static constexpr utype vmask = mask1 >> (bitmax - bitsize);
2017-06-30 22:53:16 +00:00
2016-02-01 21:55:43 +00:00
protected:
type m_data;
2016-02-01 21:55:43 +00:00
};
// Bitfield accessor (N bits from I position, 0 is LSB)
template <typename T, uint I, uint N>
2016-02-01 21:55:43 +00:00
struct bf_t : bf_base<T, N>
{
using type = typename bf_t::type;
using vtype = typename bf_t::vtype;
2017-03-04 13:54:43 +00:00
using utype = typename bf_t::utype;
using compact_type = typename bf_t::compact_type;
2016-02-01 21:55:43 +00:00
// 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
2017-03-04 13:54:43 +00:00
static constexpr utype data_mask()
2016-02-01 21:55:43 +00:00
{
2017-06-30 22:53:16 +00:00
return static_cast<utype>(static_cast<utype>(bf_t::mask1 >> (bf_t::bitmax - bf_t::bitsize)) << bitpos);
2016-02-01 21:55:43 +00:00
}
// Bitfield extraction
static constexpr compact_type extract(const T& data) noexcept
{
if constexpr (std::is_signed_v<T>)
{
// Load signed value (sign-extended)
return static_cast<compact_type>(static_cast<vtype>(static_cast<utype>(data) << (bf_t::bitmax - bitpos - N)) >> (bf_t::bitmax - N));
}
else
{
// Load unsigned value
return static_cast<compact_type>((static_cast<utype>(data) >> bitpos) & bf_t::vmask);
}
}
2016-02-01 21:55:43 +00:00
// Bitfield insertion
static constexpr vtype insert(compact_type value)
{
2017-03-04 13:54:43 +00:00
return static_cast<vtype>((value & bf_t::vmask) << bitpos);
}
2016-02-01 21:55:43 +00:00
// Load bitfield value
constexpr operator compact_type() const noexcept
{
2016-02-01 21:55:43 +00:00
return extract(this->m_data);
}
2016-02-01 21:55:43 +00:00
// Load raw data with mask applied
constexpr T unshifted() const
{
2017-03-04 13:54:43 +00:00
return static_cast<T>(this->m_data & data_mask());
2016-02-01 21:55:43 +00:00
}
// Optimized bool conversion (must be removed if inappropriate)
explicit constexpr operator bool() const noexcept
{
return unshifted() != 0u;
}
2016-02-01 21:55:43 +00:00
// Store bitfield value
bf_t& operator =(compact_type value) noexcept
2016-02-01 21:55:43 +00:00
{
2017-03-04 13:54:43 +00:00
this->m_data = static_cast<vtype>((this->m_data & ~data_mask()) | insert(value));
2016-02-01 21:55:43 +00:00
return *this;
}
compact_type operator ++(int)
{
compact_type result = *this;
*this = static_cast<compact_type>(result + 1u);
return result;
}
bf_t& operator ++()
{
return *this = static_cast<compact_type>(*this + 1u);
}
compact_type operator --(int)
{
compact_type result = *this;
*this = static_cast<compact_type>(result - 1u);
return result;
}
bf_t& operator --()
{
return *this = static_cast<compact_type>(*this - 1u);
}
bf_t& operator +=(compact_type right)
{
return *this = static_cast<compact_type>(*this + right);
}
bf_t& operator -=(compact_type right)
{
return *this = static_cast<compact_type>(*this - right);
}
bf_t& operator *=(compact_type right)
{
return *this = static_cast<compact_type>(*this * right);
}
bf_t& operator &=(compact_type right)
{
this->m_data &= static_cast<vtype>(((static_cast<utype>(right + 0u) & bf_t::vmask) << bitpos) | ~(bf_t::vmask << bitpos));
return *this;
}
bf_t& operator |=(compact_type right)
{
this->m_data |= static_cast<vtype>((static_cast<utype>(right + 0u) & bf_t::vmask) << bitpos);
return *this;
}
bf_t& operator ^=(compact_type right)
{
this->m_data ^= static_cast<vtype>((static_cast<utype>(right + 0u) & bf_t::vmask) << bitpos);
return *this;
}
};
template <typename T, uint I, uint N>
struct std::common_type<bf_t<T, I, N>, bf_t<T, I, N>> : std::common_type<T> {};
template <typename T, uint I, uint N, typename T2>
struct std::common_type<bf_t<T, I, N>, T2> : std::common_type<T2, std::common_type_t<T>> {};
template <typename T, uint I, uint N, typename T2>
struct std::common_type<T2, bf_t<T, I, N>> : std::common_type<std::common_type_t<T>, T2> {};
2016-02-01 21:55:43 +00:00
// Field pack (concatenated from left to right)
template <typename F = void, typename... Fields>
2016-02-01 21:55:43 +00:00
struct cf_t : bf_base<typename F::type, F::bitsize + cf_t<Fields...>::bitsize>
{
using type = typename cf_t::type;
using vtype = typename cf_t::vtype;
2017-03-04 13:54:43 +00:00
using utype = typename cf_t::utype;
using compact_type = typename cf_t::compact_type;
2016-02-01 21:55:43 +00:00
// Get disjunction of all "data" masks of concatenated values
static constexpr vtype data_mask()
{
2017-03-04 13:54:43 +00:00
return static_cast<vtype>(F::data_mask() | cf_t<Fields...>::data_mask());
2016-02-01 21:55:43 +00:00
}
// Extract all bitfields and concatenate
static constexpr compact_type extract(const type& data)
2016-02-01 21:55:43 +00:00
{
return static_cast<compact_type>(static_cast<utype>(F::extract(data)) << cf_t<Fields...>::bitsize | cf_t<Fields...>::extract(data));
2016-02-01 21:55:43 +00:00
}
// Split bitfields and insert them
static constexpr vtype insert(compact_type value)
2016-02-01 21:55:43 +00:00
{
2017-03-04 13:54:43 +00:00
return static_cast<vtype>(F::insert(value >> cf_t<Fields...>::bitsize) | cf_t<Fields...>::insert(value));
2016-02-01 21:55:43 +00:00
}
// Load value
constexpr operator compact_type() const noexcept
2016-02-01 21:55:43 +00:00
{
return extract(this->m_data);
}
// Store value
cf_t& operator =(compact_type value) noexcept
2016-02-01 21:55:43 +00:00
{
this->m_data = (this->m_data & ~data_mask()) | insert(value);
return *this;
}
};
// Empty field pack (recursion terminator)
template <>
2016-02-01 21:55:43 +00:00
struct cf_t<void>
{
static constexpr uint bitsize = 0;
static constexpr uint data_mask()
{
return 0;
}
template <typename T>
2021-03-05 19:05:37 +00:00
static constexpr auto extract(const T&) -> decltype(+T())
2016-02-01 21:55:43 +00:00
{
return 0;
}
template <typename T>
2019-12-26 20:01:48 +00:00
static constexpr T insert(T /*value*/)
2016-02-01 21:55:43 +00:00
{
return 0;
}
};
// Fixed field (provides constant values in field pack)
template <typename T, T V, uint N>
2016-02-01 21:55:43 +00:00
struct ff_t : bf_base<T, N>
{
using type = typename ff_t::type;
using vtype = typename ff_t::vtype;
// Return constant value
2021-03-05 19:05:37 +00:00
static constexpr vtype extract(const type&)
2016-02-01 21:55:43 +00:00
{
static_assert((V & ff_t::vmask) == V, "ff_t<> error: V out of bounds");
return V;
}
// Get value
constexpr operator vtype() const noexcept
2016-02-01 21:55:43 +00:00
{
return V;
}
};
2021-03-30 15:31:46 +00:00
#ifndef _MSC_VER
#pragma GCC diagnostic pop
#endif
template <typename T, uint I, uint N>
struct fmt_unveil<bf_t<T, I, N>, void>
{
using type = typename fmt_unveil<std::common_type_t<T>>::type;
2016-08-07 13:59:46 +00:00
static inline auto get(const bf_t<T, I, N>& bf)
{
return fmt_unveil<type>::get(bf);
}
};
template <typename F, typename... Fields>
struct fmt_unveil<cf_t<F, Fields...>, void>
{
using type = typename fmt_unveil<std::common_type_t<typename F::type>>::type;
2016-08-07 13:59:46 +00:00
static inline auto get(const cf_t<F, Fields...>& cf)
{
return fmt_unveil<type>::get(cf);
}
};
template <typename T, T V, uint N>
struct fmt_unveil<ff_t<T, V, N>, void>
{
using type = typename fmt_unveil<std::common_type_t<T>>::type;
2016-08-07 13:59:46 +00:00
static inline auto get(const ff_t<T, V, N>& ff)
{
return fmt_unveil<type>::get(ff);
}
};