blob: 7276735b6b4e40a6e4ea25d5e9e9379039b3767d [file] [log] [blame]
//
// 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 <size_t N, typename BitsT>
class BitSetT final
{
public:
class Reference final
{
public:
~Reference() {}
Reference &operator=(bool x)
{
mParent->set(mBit, x);
return *this;
}
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<unsigned long>(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<BitsT>(1) << x); }
constexpr static BitsT Mask(std::size_t x) { return ((Bit(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; }
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>
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>
BitSetT<N, BitsT>::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>
BitSetT<N, BitsT>::BitSetT(BitsT value) : mBits(value & Mask(N))
{
}
template <size_t N, typename BitsT>
BitSetT<N, BitsT>::~BitSetT()
{
}
template <size_t N, typename BitsT>
BitSetT<N, BitsT>::BitSetT(const BitSetT &other) : mBits(other.mBits)
{
}
template <size_t N, typename BitsT>
BitSetT<N, BitsT> &BitSetT<N, BitsT>::operator=(const BitSetT &other)
{
mBits = other.mBits;
return *this;
}
template <size_t N, typename BitsT>
bool BitSetT<N, BitsT>::operator==(const BitSetT &other) const
{
return mBits == other.mBits;
}
template <size_t N, typename BitsT>
bool BitSetT<N, BitsT>::operator!=(const BitSetT &other) const
{
return mBits != other.mBits;
}
template <size_t N, typename BitsT>
constexpr bool BitSetT<N, BitsT>::operator[](std::size_t pos) const
{
return test(pos);
}
template <size_t N, typename BitsT>
bool BitSetT<N, BitsT>::test(std::size_t pos) const
{
return (mBits & Bit(pos)) != 0;
}
template <size_t N, typename BitsT>
bool BitSetT<N, BitsT>::all() const
{
ASSERT(mBits == (mBits & Mask(N)));
return mBits == Mask(N);
}
template <size_t N, typename BitsT>
bool BitSetT<N, BitsT>::any() const
{
ASSERT(mBits == (mBits & Mask(N)));
return (mBits != 0);
}
template <size_t N, typename BitsT>
bool BitSetT<N, BitsT>::none() const
{
ASSERT(mBits == (mBits & Mask(N)));
return (mBits == 0);
}
template <size_t N, typename BitsT>
std::size_t BitSetT<N, BitsT>::count() const
{
return gl::BitCount(mBits);
}
template <size_t N, typename BitsT>
BitSetT<N, BitsT> &BitSetT<N, BitsT>::operator&=(const BitSetT &other)
{
mBits &= other.mBits;
return *this;
}
template <size_t N, typename BitsT>
BitSetT<N, BitsT> &BitSetT<N, BitsT>::operator|=(const BitSetT &other)
{
mBits |= other.mBits;
return *this;
}
template <size_t N, typename BitsT>
BitSetT<N, BitsT> &BitSetT<N, BitsT>::operator^=(const BitSetT &other)
{
mBits = (mBits ^ other.mBits) & Mask(N);
return *this;
}
template <size_t N, typename BitsT>
BitSetT<N, BitsT> BitSetT<N, BitsT>::operator~() const
{
return BitSetT<N, BitsT>(~mBits & Mask(N));
}
template <size_t N, typename BitsT>
BitSetT<N, BitsT> BitSetT<N, BitsT>::operator<<(std::size_t pos) const
{
return BitSetT<N, BitsT>((mBits << pos) & Mask(N));
}
template <size_t N, typename BitsT>
BitSetT<N, BitsT> &BitSetT<N, BitsT>::operator<<=(std::size_t pos)
{
mBits = (mBits << pos & Mask(N));
return *this;
}
template <size_t N, typename BitsT>
BitSetT<N, BitsT> BitSetT<N, BitsT>::operator>>(std::size_t pos) const
{
return BitSetT<N, BitsT>(mBits >> pos);
}
template <size_t N, typename BitsT>
BitSetT<N, BitsT> &BitSetT<N, BitsT>::operator>>=(std::size_t pos)
{
mBits = ((mBits >> pos) & Mask(N));
return *this;
}
template <size_t N, typename BitsT>
BitSetT<N, BitsT> &BitSetT<N, BitsT>::set()
{
mBits = Mask(N);
return *this;
}
template <size_t N, typename BitsT>
BitSetT<N, BitsT> &BitSetT<N, BitsT>::set(std::size_t pos, bool value)
{
if (value)
{
mBits |= Bit(pos);
}
else
{
reset(pos);
}
return *this;
}
template <size_t N, typename BitsT>
BitSetT<N, BitsT> &BitSetT<N, BitsT>::reset()
{
mBits = 0;
return *this;
}
template <size_t N, typename BitsT>
BitSetT<N, BitsT> &BitSetT<N, BitsT>::reset(std::size_t pos)
{
mBits &= ~Bit(pos);
return *this;
}
template <size_t N, typename BitsT>
BitSetT<N, BitsT> &BitSetT<N, BitsT>::flip()
{
mBits ^= Mask(N);
return *this;
}
template <size_t N, typename BitsT>
BitSetT<N, BitsT> &BitSetT<N, BitsT>::flip(std::size_t pos)
{
mBits ^= Bit(pos);
return *this;
}
template <size_t N, typename BitsT>
BitSetT<N, BitsT>::Iterator::Iterator(const BitSetT &bits) : mBitsCopy(bits), mCurrentBit(0)
{
if (bits.any())
{
mCurrentBit = getNextBit();
}
}
template <size_t N, typename BitsT>
typename BitSetT<N, BitsT>::Iterator &BitSetT<N, BitsT>::Iterator::operator++()
{
ASSERT(mBitsCopy.any());
mBitsCopy.reset(mCurrentBit);
mCurrentBit = getNextBit();
return *this;
}
template <size_t N, typename BitsT>
bool BitSetT<N, BitsT>::Iterator::operator==(const Iterator &other) const
{
return mBitsCopy == other.mBitsCopy;
}
template <size_t N, typename BitsT>
bool BitSetT<N, BitsT>::Iterator::operator!=(const Iterator &other) const
{
return !(*this == other);
}
template <size_t N, typename BitsT>
std::size_t BitSetT<N, BitsT>::Iterator::operator*() const
{
return mCurrentBit;
}
template <size_t N, typename BitsT>
std::size_t BitSetT<N, BitsT>::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_X64_CPU)
template <size_t N>
using BitSet64 = BitSetT<N, uint64_t>;
#endif // defined(ANGLE_X64_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_X64_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_X64_CPU)
} // namespace priv
template <size_t N>
using BitSet = typename priv::GetBitSet<N>::Type;
} // angle
template <size_t N, typename BitsT>
inline angle::BitSetT<N, BitsT> operator&(const angle::BitSetT<N, BitsT> &lhs,
const angle::BitSetT<N, BitsT> &rhs)
{
return angle::BitSetT<N, BitsT>(lhs.bits() & rhs.bits());
}
template <size_t N, typename BitsT>
inline angle::BitSetT<N, BitsT> operator|(const angle::BitSetT<N, BitsT> &lhs,
const angle::BitSetT<N, BitsT> &rhs)
{
return angle::BitSetT<N, BitsT>(lhs.bits() | rhs.bits());
}
template <size_t N, typename BitsT>
inline angle::BitSetT<N, BitsT> operator^(const angle::BitSetT<N, BitsT> &lhs,
const angle::BitSetT<N, BitsT> &rhs)
{
return angle::BitSetT<N, BitsT>(lhs.bits() ^ rhs.bits());
}
#endif // COMMON_BITSETITERATOR_H_