| // |
| // Copyright 2015 The ANGLE Project Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| // |
| // bitset_utils: |
| // Bitset-related helper classes, such as a fast iterator to scan for set bits. |
| // |
| |
| #ifndef COMMON_BITSETITERATOR_H_ |
| #define COMMON_BITSETITERATOR_H_ |
| |
| #include <stdint.h> |
| |
| #include <bitset> |
| |
| #include "common/angleutils.h" |
| #include "common/debug.h" |
| #include "common/mathutil.h" |
| #include "common/platform.h" |
| |
| namespace angle |
| { |
| template <typename BitsT, typename ParamT> |
| constexpr static BitsT Bit(ParamT x) |
| { |
| return (static_cast<BitsT>(1) << static_cast<size_t>(x)); |
| } |
| |
| template <size_t N, typename BitsT, typename ParamT = std::size_t> |
| class BitSetT final |
| { |
| public: |
| class Reference final |
| { |
| public: |
| ~Reference() {} |
| Reference &operator=(bool x) |
| { |
| mParent->set(mBit, x); |
| return *this; |
| } |
| explicit operator bool() const { return mParent->test(mBit); } |
| |
| private: |
| friend class BitSetT; |
| |
| Reference(BitSetT *parent, ParamT bit) : mParent(parent), mBit(bit) {} |
| |
| BitSetT *mParent; |
| ParamT mBit; |
| }; |
| |
| class Iterator final |
| { |
| public: |
| Iterator(const BitSetT &bits); |
| Iterator &operator++(); |
| |
| bool operator==(const Iterator &other) const; |
| bool operator!=(const Iterator &other) const; |
| ParamT operator*() const; |
| |
| // These helper functions allow mutating an iterator in-flight. |
| // They only operate on later bits to ensure we don't iterate the same bit twice. |
| void resetLaterBit(std::size_t index) |
| { |
| ASSERT(index > mCurrentBit); |
| mBitsCopy.reset(index); |
| } |
| |
| void setLaterBit(std::size_t index) |
| { |
| ASSERT(index > mCurrentBit); |
| mBitsCopy.set(index); |
| } |
| |
| private: |
| std::size_t getNextBit(); |
| |
| BitSetT mBitsCopy; |
| std::size_t mCurrentBit; |
| }; |
| |
| BitSetT(); |
| constexpr explicit BitSetT(BitsT value); |
| |
| BitSetT(const BitSetT &other); |
| BitSetT &operator=(const BitSetT &other); |
| |
| bool operator==(const BitSetT &other) const; |
| bool operator!=(const BitSetT &other) const; |
| |
| constexpr bool operator[](ParamT pos) const; |
| Reference operator[](ParamT pos) { return Reference(this, pos); } |
| |
| bool test(ParamT pos) const; |
| |
| bool all() const; |
| bool any() const; |
| bool none() const; |
| std::size_t count() const; |
| |
| constexpr std::size_t size() const { return N; } |
| |
| BitSetT &operator&=(const BitSetT &other); |
| BitSetT &operator|=(const BitSetT &other); |
| BitSetT &operator^=(const BitSetT &other); |
| BitSetT operator~() const; |
| |
| BitSetT &operator&=(BitsT value); |
| BitSetT &operator|=(BitsT value); |
| BitSetT &operator^=(BitsT value); |
| |
| BitSetT operator<<(std::size_t pos) const; |
| BitSetT &operator<<=(std::size_t pos); |
| BitSetT operator>>(std::size_t pos) const; |
| BitSetT &operator>>=(std::size_t pos); |
| |
| BitSetT &set(); |
| BitSetT &set(ParamT pos, bool value = true); |
| |
| BitSetT &reset(); |
| BitSetT &reset(ParamT pos); |
| |
| BitSetT &flip(); |
| BitSetT &flip(ParamT pos); |
| |
| unsigned long to_ulong() const { return static_cast<unsigned long>(mBits); } |
| BitsT bits() const { return mBits; } |
| |
| Iterator begin() const { return Iterator(*this); } |
| Iterator end() const { return Iterator(BitSetT()); } |
| |
| private: |
| // Produces a mask of ones up to the "x"th bit. |
| constexpr static BitsT Mask(std::size_t x) |
| { |
| return ((Bit<BitsT>(static_cast<ParamT>(x - 1)) - 1) << 1) + 1; |
| } |
| |
| BitsT mBits; |
| }; |
| |
| template <size_t N> |
| class IterableBitSet : public std::bitset<N> |
| { |
| public: |
| IterableBitSet() {} |
| IterableBitSet(const std::bitset<N> &implicitBitSet) : std::bitset<N>(implicitBitSet) {} |
| |
| class Iterator final |
| { |
| public: |
| Iterator(const std::bitset<N> &bits); |
| Iterator &operator++(); |
| |
| bool operator==(const Iterator &other) const; |
| bool operator!=(const Iterator &other) const; |
| unsigned long operator*() const { return mCurrentBit; } |
| |
| // These helper functions allow mutating an iterator in-flight. |
| // They only operate on later bits to ensure we don't iterate the same bit twice. |
| void resetLaterBit(std::size_t index) |
| { |
| ASSERT(index > mCurrentBit); |
| mBits.reset(index - mOffset); |
| } |
| |
| void setLaterBit(std::size_t index) |
| { |
| ASSERT(index > mCurrentBit); |
| mBits.set(index - mOffset); |
| } |
| |
| private: |
| unsigned long getNextBit(); |
| |
| static constexpr size_t BitsPerWord = sizeof(uint32_t) * 8; |
| std::bitset<N> mBits; |
| unsigned long mCurrentBit; |
| unsigned long mOffset; |
| }; |
| |
| Iterator begin() const { return Iterator(*this); } |
| Iterator end() const { return Iterator(std::bitset<N>(0)); } |
| }; |
| |
| template <size_t N> |
| IterableBitSet<N>::Iterator::Iterator(const std::bitset<N> &bitset) |
| : mBits(bitset), mCurrentBit(0), mOffset(0) |
| { |
| if (mBits.any()) |
| { |
| mCurrentBit = getNextBit(); |
| } |
| else |
| { |
| mOffset = static_cast<unsigned long>(rx::roundUp(N, BitsPerWord)); |
| } |
| } |
| |
| template <size_t N> |
| ANGLE_INLINE typename IterableBitSet<N>::Iterator &IterableBitSet<N>::Iterator::operator++() |
| { |
| ASSERT(mBits.any()); |
| mBits.set(mCurrentBit - mOffset, 0); |
| mCurrentBit = getNextBit(); |
| return *this; |
| } |
| |
| template <size_t N> |
| bool IterableBitSet<N>::Iterator::operator==(const Iterator &other) const |
| { |
| return mOffset == other.mOffset && mBits == other.mBits; |
| } |
| |
| template <size_t N> |
| bool IterableBitSet<N>::Iterator::operator!=(const Iterator &other) const |
| { |
| return !(*this == other); |
| } |
| |
| template <size_t N> |
| unsigned long IterableBitSet<N>::Iterator::getNextBit() |
| { |
| // TODO(jmadill): Use 64-bit scan when possible. |
| static constexpr std::bitset<N> wordMask(std::numeric_limits<uint32_t>::max()); |
| |
| while (mOffset < N) |
| { |
| uint32_t wordBits = static_cast<uint32_t>((mBits & wordMask).to_ulong()); |
| if (wordBits != 0) |
| { |
| return gl::ScanForward(wordBits) + mOffset; |
| } |
| |
| mBits >>= BitsPerWord; |
| mOffset += BitsPerWord; |
| } |
| return 0; |
| } |
| |
| template <size_t N, typename BitsT, typename ParamT> |
| BitSetT<N, BitsT, ParamT>::BitSetT() : mBits(0) |
| { |
| static_assert(N > 0, "Bitset type cannot support zero bits."); |
| static_assert(N <= sizeof(BitsT) * 8, "Bitset type cannot support a size this large."); |
| } |
| |
| template <size_t N, typename BitsT, typename ParamT> |
| constexpr BitSetT<N, BitsT, ParamT>::BitSetT(BitsT value) : mBits(value & Mask(N)) |
| {} |
| |
| template <size_t N, typename BitsT, typename ParamT> |
| BitSetT<N, BitsT, ParamT>::BitSetT(const BitSetT &other) : mBits(other.mBits) |
| {} |
| |
| template <size_t N, typename BitsT, typename ParamT> |
| BitSetT<N, BitsT, ParamT> &BitSetT<N, BitsT, ParamT>::operator=(const BitSetT &other) |
| { |
| mBits = other.mBits; |
| return *this; |
| } |
| |
| template <size_t N, typename BitsT, typename ParamT> |
| bool BitSetT<N, BitsT, ParamT>::operator==(const BitSetT &other) const |
| { |
| return mBits == other.mBits; |
| } |
| |
| template <size_t N, typename BitsT, typename ParamT> |
| bool BitSetT<N, BitsT, ParamT>::operator!=(const BitSetT &other) const |
| { |
| return mBits != other.mBits; |
| } |
| |
| template <size_t N, typename BitsT, typename ParamT> |
| constexpr bool BitSetT<N, BitsT, ParamT>::operator[](ParamT pos) const |
| { |
| return test(pos); |
| } |
| |
| template <size_t N, typename BitsT, typename ParamT> |
| bool BitSetT<N, BitsT, ParamT>::test(ParamT pos) const |
| { |
| return (mBits & Bit<BitsT>(pos)) != 0; |
| } |
| |
| template <size_t N, typename BitsT, typename ParamT> |
| bool BitSetT<N, BitsT, ParamT>::all() const |
| { |
| ASSERT(mBits == (mBits & Mask(N))); |
| return mBits == Mask(N); |
| } |
| |
| template <size_t N, typename BitsT, typename ParamT> |
| bool BitSetT<N, BitsT, ParamT>::any() const |
| { |
| ASSERT(mBits == (mBits & Mask(N))); |
| return (mBits != 0); |
| } |
| |
| template <size_t N, typename BitsT, typename ParamT> |
| bool BitSetT<N, BitsT, ParamT>::none() const |
| { |
| ASSERT(mBits == (mBits & Mask(N))); |
| return (mBits == 0); |
| } |
| |
| template <size_t N, typename BitsT, typename ParamT> |
| std::size_t BitSetT<N, BitsT, ParamT>::count() const |
| { |
| return gl::BitCount(mBits); |
| } |
| |
| template <size_t N, typename BitsT, typename ParamT> |
| BitSetT<N, BitsT, ParamT> &BitSetT<N, BitsT, ParamT>::operator&=(const BitSetT &other) |
| { |
| mBits &= other.mBits; |
| return *this; |
| } |
| |
| template <size_t N, typename BitsT, typename ParamT> |
| BitSetT<N, BitsT, ParamT> &BitSetT<N, BitsT, ParamT>::operator|=(const BitSetT &other) |
| { |
| mBits |= other.mBits; |
| return *this; |
| } |
| |
| template <size_t N, typename BitsT, typename ParamT> |
| BitSetT<N, BitsT, ParamT> &BitSetT<N, BitsT, ParamT>::operator^=(const BitSetT &other) |
| { |
| mBits = mBits ^ other.mBits; |
| return *this; |
| } |
| |
| template <size_t N, typename BitsT, typename ParamT> |
| BitSetT<N, BitsT, ParamT> BitSetT<N, BitsT, ParamT>::operator~() const |
| { |
| return BitSetT<N, BitsT, ParamT>(~mBits & Mask(N)); |
| } |
| |
| template <size_t N, typename BitsT, typename ParamT> |
| BitSetT<N, BitsT, ParamT> &BitSetT<N, BitsT, ParamT>::operator&=(BitsT value) |
| { |
| mBits &= value; |
| return *this; |
| } |
| |
| template <size_t N, typename BitsT, typename ParamT> |
| BitSetT<N, BitsT, ParamT> &BitSetT<N, BitsT, ParamT>::operator|=(BitsT value) |
| { |
| mBits |= value & Mask(N); |
| return *this; |
| } |
| |
| template <size_t N, typename BitsT, typename ParamT> |
| BitSetT<N, BitsT, ParamT> &BitSetT<N, BitsT, ParamT>::operator^=(BitsT value) |
| { |
| mBits ^= value & Mask(N); |
| return *this; |
| } |
| |
| template <size_t N, typename BitsT, typename ParamT> |
| BitSetT<N, BitsT, ParamT> BitSetT<N, BitsT, ParamT>::operator<<(std::size_t pos) const |
| { |
| return BitSetT<N, BitsT, ParamT>((mBits << pos) & Mask(N)); |
| } |
| |
| template <size_t N, typename BitsT, typename ParamT> |
| BitSetT<N, BitsT, ParamT> &BitSetT<N, BitsT, ParamT>::operator<<=(std::size_t pos) |
| { |
| mBits = (mBits << pos & Mask(N)); |
| return *this; |
| } |
| |
| template <size_t N, typename BitsT, typename ParamT> |
| BitSetT<N, BitsT, ParamT> BitSetT<N, BitsT, ParamT>::operator>>(std::size_t pos) const |
| { |
| return BitSetT<N, BitsT, ParamT>(mBits >> pos); |
| } |
| |
| template <size_t N, typename BitsT, typename ParamT> |
| BitSetT<N, BitsT, ParamT> &BitSetT<N, BitsT, ParamT>::operator>>=(std::size_t pos) |
| { |
| mBits = ((mBits >> pos) & Mask(N)); |
| return *this; |
| } |
| |
| template <size_t N, typename BitsT, typename ParamT> |
| BitSetT<N, BitsT, ParamT> &BitSetT<N, BitsT, ParamT>::set() |
| { |
| ASSERT(mBits == (mBits & Mask(N))); |
| mBits = Mask(N); |
| return *this; |
| } |
| |
| template <size_t N, typename BitsT, typename ParamT> |
| BitSetT<N, BitsT, ParamT> &BitSetT<N, BitsT, ParamT>::set(ParamT pos, bool value) |
| { |
| ASSERT(mBits == (mBits & Mask(N))); |
| if (value) |
| { |
| mBits |= Bit<BitsT>(pos) & Mask(N); |
| } |
| else |
| { |
| reset(pos); |
| } |
| return *this; |
| } |
| |
| template <size_t N, typename BitsT, typename ParamT> |
| BitSetT<N, BitsT, ParamT> &BitSetT<N, BitsT, ParamT>::reset() |
| { |
| ASSERT(mBits == (mBits & Mask(N))); |
| mBits = 0; |
| return *this; |
| } |
| |
| template <size_t N, typename BitsT, typename ParamT> |
| BitSetT<N, BitsT, ParamT> &BitSetT<N, BitsT, ParamT>::reset(ParamT pos) |
| { |
| ASSERT(mBits == (mBits & Mask(N))); |
| mBits &= ~Bit<BitsT>(pos); |
| return *this; |
| } |
| |
| template <size_t N, typename BitsT, typename ParamT> |
| BitSetT<N, BitsT, ParamT> &BitSetT<N, BitsT, ParamT>::flip() |
| { |
| ASSERT(mBits == (mBits & Mask(N))); |
| mBits ^= Mask(N); |
| return *this; |
| } |
| |
| template <size_t N, typename BitsT, typename ParamT> |
| BitSetT<N, BitsT, ParamT> &BitSetT<N, BitsT, ParamT>::flip(ParamT pos) |
| { |
| ASSERT(mBits == (mBits & Mask(N))); |
| mBits ^= Bit<BitsT>(pos) & Mask(N); |
| return *this; |
| } |
| |
| template <size_t N, typename BitsT, typename ParamT> |
| BitSetT<N, BitsT, ParamT>::Iterator::Iterator(const BitSetT &bits) : mBitsCopy(bits), mCurrentBit(0) |
| { |
| if (bits.any()) |
| { |
| mCurrentBit = getNextBit(); |
| } |
| } |
| |
| template <size_t N, typename BitsT, typename ParamT> |
| ANGLE_INLINE typename BitSetT<N, BitsT, ParamT>::Iterator &BitSetT<N, BitsT, ParamT>::Iterator:: |
| operator++() |
| { |
| ASSERT(mBitsCopy.any()); |
| mBitsCopy.reset(static_cast<ParamT>(mCurrentBit)); |
| mCurrentBit = getNextBit(); |
| return *this; |
| } |
| |
| template <size_t N, typename BitsT, typename ParamT> |
| bool BitSetT<N, BitsT, ParamT>::Iterator::operator==(const Iterator &other) const |
| { |
| return mBitsCopy == other.mBitsCopy; |
| } |
| |
| template <size_t N, typename BitsT, typename ParamT> |
| bool BitSetT<N, BitsT, ParamT>::Iterator::operator!=(const Iterator &other) const |
| { |
| return !(*this == other); |
| } |
| |
| template <size_t N, typename BitsT, typename ParamT> |
| ParamT BitSetT<N, BitsT, ParamT>::Iterator::operator*() const |
| { |
| return static_cast<ParamT>(mCurrentBit); |
| } |
| |
| template <size_t N, typename BitsT, typename ParamT> |
| std::size_t BitSetT<N, BitsT, ParamT>::Iterator::getNextBit() |
| { |
| if (mBitsCopy.none()) |
| { |
| return 0; |
| } |
| |
| return gl::ScanForward(mBitsCopy.mBits); |
| } |
| |
| template <size_t N> |
| using BitSet32 = BitSetT<N, uint32_t>; |
| |
| // ScanForward for 64-bits requires a 64-bit implementation. |
| #if defined(ANGLE_IS_64_BIT_CPU) |
| template <size_t N> |
| using BitSet64 = BitSetT<N, uint64_t>; |
| #endif // defined(ANGLE_IS_64_BIT_CPU) |
| |
| namespace priv |
| { |
| |
| template <size_t N, typename T> |
| using EnableIfBitsFit = typename std::enable_if<N <= sizeof(T) * 8>::type; |
| |
| template <size_t N, typename Enable = void> |
| struct GetBitSet |
| { |
| using Type = IterableBitSet<N>; |
| }; |
| |
| // Prefer 64-bit bitsets on 64-bit CPUs. They seem faster than 32-bit. |
| #if defined(ANGLE_IS_64_BIT_CPU) |
| template <size_t N> |
| struct GetBitSet<N, EnableIfBitsFit<N, uint64_t>> |
| { |
| using Type = BitSet64<N>; |
| }; |
| #else |
| template <size_t N> |
| struct GetBitSet<N, EnableIfBitsFit<N, uint32_t>> |
| { |
| using Type = BitSet32<N>; |
| }; |
| #endif // defined(ANGLE_IS_64_BIT_CPU) |
| |
| } // namespace priv |
| |
| template <size_t N> |
| using BitSet = typename priv::GetBitSet<N>::Type; |
| |
| } // namespace angle |
| |
| template <size_t N, typename BitsT, typename ParamT> |
| inline angle::BitSetT<N, BitsT, ParamT> operator&(const angle::BitSetT<N, BitsT, ParamT> &lhs, |
| const angle::BitSetT<N, BitsT, ParamT> &rhs) |
| { |
| angle::BitSetT<N, BitsT, ParamT> result(lhs); |
| result &= rhs.bits(); |
| return result; |
| } |
| |
| template <size_t N, typename BitsT, typename ParamT> |
| inline angle::BitSetT<N, BitsT, ParamT> operator|(const angle::BitSetT<N, BitsT, ParamT> &lhs, |
| const angle::BitSetT<N, BitsT, ParamT> &rhs) |
| { |
| angle::BitSetT<N, BitsT, ParamT> result(lhs); |
| result |= rhs.bits(); |
| return result; |
| } |
| |
| template <size_t N, typename BitsT, typename ParamT> |
| inline angle::BitSetT<N, BitsT, ParamT> operator^(const angle::BitSetT<N, BitsT, ParamT> &lhs, |
| const angle::BitSetT<N, BitsT, ParamT> &rhs) |
| { |
| angle::BitSetT<N, BitsT, ParamT> result(lhs); |
| result ^= rhs.bits(); |
| return result; |
| } |
| |
| #endif // COMMON_BITSETITERATOR_H_ |