// // 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 #include #include "common/angleutils.h" #include "common/debug.h" #include "common/mathutil.h" #include "common/platform.h" namespace angle { template 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, std::size_t bit) : mParent(parent), mBit(bit) {} BitSetT *mParent; std::size_t mBit; }; class Iterator final { public: Iterator(const BitSetT &bits); Iterator &operator++(); bool operator==(const Iterator &other) const; bool operator!=(const Iterator &other) const; std::size_t operator*() const; private: std::size_t getNextBit(); BitSetT mBitsCopy; std::size_t mCurrentBit; }; BitSetT(); BitSetT(BitsT value); ~BitSetT(); BitSetT(const BitSetT &other); BitSetT &operator=(const BitSetT &other); bool operator==(const BitSetT &other) const; bool operator!=(const BitSetT &other) const; constexpr bool operator[](std::size_t pos) const; Reference operator[](std::size_t pos) { return Reference(this, pos); } bool test(std::size_t 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<<(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(std::size_t pos, bool value = true); BitSetT &reset(); BitSetT &reset(std::size_t pos); BitSetT &flip(); BitSetT &flip(std::size_t pos); unsigned long to_ulong() const { return static_cast(mBits); } BitsT bits() const { return mBits; } Iterator begin() const { return Iterator(*this); } Iterator end() const { return Iterator(BitSetT()); } private: constexpr static BitsT Bit(std::size_t x) { return (static_cast(1) << x); } constexpr static BitsT Mask(std::size_t x) { return ((Bit(x - 1) - 1) << 1) + 1; } BitsT mBits; }; template class IterableBitSet : public std::bitset { public: IterableBitSet() {} IterableBitSet(const std::bitset &implicitBitSet) : std::bitset(implicitBitSet) {} class Iterator final { public: Iterator(const std::bitset &bits); Iterator &operator++(); bool operator==(const Iterator &other) const; bool operator!=(const Iterator &other) const; unsigned long operator*() const { return mCurrentBit; } private: unsigned long getNextBit(); static constexpr size_t BitsPerWord = sizeof(uint32_t) * 8; std::bitset mBits; unsigned long mCurrentBit; unsigned long mOffset; }; Iterator begin() const { return Iterator(*this); } Iterator end() const { return Iterator(std::bitset(0)); } }; template IterableBitSet::Iterator::Iterator(const std::bitset &bitset) : mBits(bitset), mCurrentBit(0), mOffset(0) { if (mBits.any()) { mCurrentBit = getNextBit(); } else { mOffset = static_cast(rx::roundUp(N, BitsPerWord)); } } template typename IterableBitSet::Iterator &IterableBitSet::Iterator::operator++() { ASSERT(mBits.any()); mBits.set(mCurrentBit - mOffset, 0); mCurrentBit = getNextBit(); return *this; } template bool IterableBitSet::Iterator::operator==(const Iterator &other) const { return mOffset == other.mOffset && mBits == other.mBits; } template bool IterableBitSet::Iterator::operator!=(const Iterator &other) const { return !(*this == other); } template unsigned long IterableBitSet::Iterator::getNextBit() { // TODO(jmadill): Use 64-bit scan when possible. static constexpr std::bitset wordMask(std::numeric_limits::max()); while (mOffset < N) { uint32_t wordBits = static_cast((mBits & wordMask).to_ulong()); if (wordBits != 0) { return gl::ScanForward(wordBits) + mOffset; } mBits >>= BitsPerWord; mOffset += BitsPerWord; } return 0; } template BitSetT::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 BitSetT::BitSetT(BitsT value) : mBits(value & Mask(N)) { } template BitSetT::~BitSetT() { } template BitSetT::BitSetT(const BitSetT &other) : mBits(other.mBits) { } template BitSetT &BitSetT::operator=(const BitSetT &other) { mBits = other.mBits; return *this; } template bool BitSetT::operator==(const BitSetT &other) const { return mBits == other.mBits; } template bool BitSetT::operator!=(const BitSetT &other) const { return mBits != other.mBits; } template constexpr bool BitSetT::operator[](std::size_t pos) const { return test(pos); } template bool BitSetT::test(std::size_t pos) const { return (mBits & Bit(pos)) != 0; } template bool BitSetT::all() const { ASSERT(mBits == (mBits & Mask(N))); return mBits == Mask(N); } template bool BitSetT::any() const { ASSERT(mBits == (mBits & Mask(N))); return (mBits != 0); } template bool BitSetT::none() const { ASSERT(mBits == (mBits & Mask(N))); return (mBits == 0); } template std::size_t BitSetT::count() const { return gl::BitCount(mBits); } template BitSetT &BitSetT::operator&=(const BitSetT &other) { mBits &= other.mBits; return *this; } template BitSetT &BitSetT::operator|=(const BitSetT &other) { mBits |= other.mBits; return *this; } template BitSetT &BitSetT::operator^=(const BitSetT &other) { mBits = (mBits ^ other.mBits) & Mask(N); return *this; } template BitSetT BitSetT::operator~() const { return BitSetT(~mBits & Mask(N)); } template BitSetT BitSetT::operator<<(std::size_t pos) const { return BitSetT((mBits << pos) & Mask(N)); } template BitSetT &BitSetT::operator<<=(std::size_t pos) { mBits = (mBits << pos & Mask(N)); return *this; } template BitSetT BitSetT::operator>>(std::size_t pos) const { return BitSetT(mBits >> pos); } template BitSetT &BitSetT::operator>>=(std::size_t pos) { mBits = ((mBits >> pos) & Mask(N)); return *this; } template BitSetT &BitSetT::set() { mBits = Mask(N); return *this; } template BitSetT &BitSetT::set(std::size_t pos, bool value) { if (value) { mBits |= Bit(pos); } else { reset(pos); } return *this; } template BitSetT &BitSetT::reset() { mBits = 0; return *this; } template BitSetT &BitSetT::reset(std::size_t pos) { mBits &= ~Bit(pos); return *this; } template BitSetT &BitSetT::flip() { mBits ^= Mask(N); return *this; } template BitSetT &BitSetT::flip(std::size_t pos) { mBits ^= Bit(pos); return *this; } template BitSetT::Iterator::Iterator(const BitSetT &bits) : mBitsCopy(bits), mCurrentBit(0) { if (bits.any()) { mCurrentBit = getNextBit(); } } template typename BitSetT::Iterator &BitSetT::Iterator::operator++() { ASSERT(mBitsCopy.any()); mBitsCopy.reset(mCurrentBit); mCurrentBit = getNextBit(); return *this; } template bool BitSetT::Iterator::operator==(const Iterator &other) const { return mBitsCopy == other.mBitsCopy; } template bool BitSetT::Iterator::operator!=(const Iterator &other) const { return !(*this == other); } template std::size_t BitSetT::Iterator::operator*() const { return mCurrentBit; } template std::size_t BitSetT::Iterator::getNextBit() { if (mBitsCopy.none()) { return 0; } return gl::ScanForward(mBitsCopy.mBits); } template using BitSet32 = BitSetT; // ScanForward for 64-bits requires a 64-bit implementation. #if defined(ANGLE_IS_64_BIT_CPU) template using BitSet64 = BitSetT; #endif // defined(ANGLE_IS_64_BIT_CPU) namespace priv { template using EnableIfBitsFit = typename std::enable_if::type; template struct GetBitSet { using Type = IterableBitSet; }; // Prefer 64-bit bitsets on 64-bit CPUs. They seem faster than 32-bit. #if defined(ANGLE_IS_64_BIT_CPU) template struct GetBitSet> { using Type = BitSet64; }; #else template struct GetBitSet> { using Type = BitSet32; }; #endif // defined(ANGLE_IS_64_BIT_CPU) } // namespace priv template using BitSet = typename priv::GetBitSet::Type; } // angle template inline angle::BitSetT operator&(const angle::BitSetT &lhs, const angle::BitSetT &rhs) { return angle::BitSetT(lhs.bits() & rhs.bits()); } template inline angle::BitSetT operator|(const angle::BitSetT &lhs, const angle::BitSetT &rhs) { return angle::BitSetT(lhs.bits() | rhs.bits()); } template inline angle::BitSetT operator^(const angle::BitSetT &lhs, const angle::BitSetT &rhs) { return angle::BitSetT(lhs.bits() ^ rhs.bits()); } #endif // COMMON_BITSETITERATOR_H_