1093 lines
30 KiB
C++
1093 lines
30 KiB
C++
//
|
|
// 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 <array>
|
|
|
|
#include "common/angleutils.h"
|
|
#include "common/debug.h"
|
|
#include "common/mathutil.h"
|
|
#include "common/platform.h"
|
|
|
|
namespace angle
|
|
{
|
|
// Given x, create 1 << x.
|
|
template <typename BitsT, typename ParamT>
|
|
constexpr BitsT Bit(ParamT x)
|
|
{
|
|
// It's undefined behavior if the shift size is equal to or larger than the width of the type.
|
|
ASSERT(static_cast<size_t>(x) < sizeof(BitsT) * 8);
|
|
|
|
return (static_cast<BitsT>(1) << static_cast<size_t>(x));
|
|
}
|
|
|
|
// Given x, create (1 << x) - 1, i.e. a mask with x bits set.
|
|
template <typename BitsT, typename ParamT>
|
|
constexpr BitsT BitMask(ParamT x)
|
|
{
|
|
if (static_cast<size_t>(x) == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
return ((Bit<BitsT>(static_cast<ParamT>(static_cast<size_t>(x) - 1)) - 1) << 1) | 1;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
void setLaterBits(const BitSetT &bits)
|
|
{
|
|
ASSERT((BitSetT(bits) &= Mask(mCurrentBit + 1)).none());
|
|
mBitsCopy |= bits;
|
|
}
|
|
|
|
private:
|
|
std::size_t getNextBit();
|
|
|
|
BitSetT mBitsCopy;
|
|
std::size_t mCurrentBit;
|
|
};
|
|
|
|
using value_type = BitsT;
|
|
using param_type = ParamT;
|
|
|
|
constexpr BitSetT();
|
|
constexpr explicit BitSetT(BitsT value);
|
|
constexpr explicit BitSetT(std::initializer_list<ParamT> init);
|
|
|
|
constexpr BitSetT(const BitSetT &other);
|
|
constexpr BitSetT &operator=(const BitSetT &other);
|
|
|
|
constexpr bool operator==(const BitSetT &other) const;
|
|
constexpr bool operator!=(const BitSetT &other) const;
|
|
|
|
constexpr bool operator[](ParamT pos) const;
|
|
Reference operator[](ParamT pos) { return Reference(this, pos); }
|
|
|
|
constexpr bool test(ParamT pos) const;
|
|
|
|
constexpr bool all() const;
|
|
constexpr bool any() const;
|
|
constexpr bool none() const;
|
|
constexpr std::size_t count() const;
|
|
|
|
constexpr std::size_t size() const { return N; }
|
|
|
|
constexpr BitSetT &operator&=(const BitSetT &other);
|
|
constexpr BitSetT &operator|=(const BitSetT &other);
|
|
constexpr BitSetT &operator^=(const BitSetT &other);
|
|
constexpr BitSetT operator~() const;
|
|
|
|
constexpr BitSetT &operator&=(BitsT value);
|
|
constexpr BitSetT &operator|=(BitsT value);
|
|
constexpr BitSetT &operator^=(BitsT value);
|
|
|
|
constexpr BitSetT operator<<(std::size_t pos) const;
|
|
constexpr BitSetT &operator<<=(std::size_t pos);
|
|
constexpr BitSetT operator>>(std::size_t pos) const;
|
|
constexpr BitSetT &operator>>=(std::size_t pos);
|
|
|
|
constexpr BitSetT &set();
|
|
constexpr BitSetT &set(ParamT pos, bool value = true);
|
|
|
|
constexpr BitSetT &reset();
|
|
constexpr BitSetT &reset(ParamT pos);
|
|
|
|
constexpr BitSetT &flip();
|
|
constexpr BitSetT &flip(ParamT pos);
|
|
|
|
constexpr unsigned long to_ulong() const { return static_cast<unsigned long>(mBits); }
|
|
constexpr BitsT bits() const { return mBits; }
|
|
|
|
Iterator begin() const { return Iterator(*this); }
|
|
Iterator end() const { return Iterator(BitSetT()); }
|
|
|
|
constexpr static BitSetT Zero() { return BitSetT(); }
|
|
|
|
constexpr ParamT first() const;
|
|
constexpr ParamT last() const;
|
|
|
|
// Produces a mask of ones up to the "x"th bit.
|
|
constexpr static BitsT Mask(std::size_t x) { return BitMask<BitsT>(static_cast<ParamT>(x)); }
|
|
|
|
private:
|
|
BitsT mBits;
|
|
};
|
|
|
|
template <size_t N, typename BitsT, typename ParamT>
|
|
constexpr 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>
|
|
constexpr BitSetT<N, BitsT, ParamT>::BitSetT(std::initializer_list<ParamT> init) : mBits(0)
|
|
{
|
|
for (ParamT element : init)
|
|
{
|
|
mBits |= Bit<BitsT>(element);
|
|
}
|
|
ASSERT(mBits == (mBits & Mask(N)));
|
|
}
|
|
|
|
template <size_t N, typename BitsT, typename ParamT>
|
|
constexpr BitSetT<N, BitsT, ParamT>::BitSetT(const BitSetT &other) : mBits(other.mBits)
|
|
{}
|
|
|
|
template <size_t N, typename BitsT, typename ParamT>
|
|
constexpr 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>
|
|
constexpr 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!=(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>
|
|
constexpr bool BitSetT<N, BitsT, ParamT>::test(ParamT pos) const
|
|
{
|
|
return (mBits & Bit<BitsT>(pos)) != 0;
|
|
}
|
|
|
|
template <size_t N, typename BitsT, typename ParamT>
|
|
constexpr bool BitSetT<N, BitsT, ParamT>::all() const
|
|
{
|
|
ASSERT(mBits == (mBits & Mask(N)));
|
|
return mBits == Mask(N);
|
|
}
|
|
|
|
template <size_t N, typename BitsT, typename ParamT>
|
|
constexpr bool BitSetT<N, BitsT, ParamT>::any() const
|
|
{
|
|
ASSERT(mBits == (mBits & Mask(N)));
|
|
return (mBits != 0);
|
|
}
|
|
|
|
template <size_t N, typename BitsT, typename ParamT>
|
|
constexpr bool BitSetT<N, BitsT, ParamT>::none() const
|
|
{
|
|
ASSERT(mBits == (mBits & Mask(N)));
|
|
return (mBits == 0);
|
|
}
|
|
|
|
template <size_t N, typename BitsT, typename ParamT>
|
|
constexpr std::size_t BitSetT<N, BitsT, ParamT>::count() const
|
|
{
|
|
return gl::BitCount(mBits);
|
|
}
|
|
|
|
template <size_t N, typename BitsT, typename ParamT>
|
|
constexpr 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>
|
|
constexpr 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>
|
|
constexpr 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>
|
|
constexpr 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>
|
|
constexpr BitSetT<N, BitsT, ParamT> &BitSetT<N, BitsT, ParamT>::operator&=(BitsT value)
|
|
{
|
|
mBits &= value;
|
|
return *this;
|
|
}
|
|
|
|
template <size_t N, typename BitsT, typename ParamT>
|
|
constexpr BitSetT<N, BitsT, ParamT> &BitSetT<N, BitsT, ParamT>::operator|=(BitsT value)
|
|
{
|
|
mBits |= value;
|
|
ASSERT(mBits == (mBits & Mask(N)));
|
|
return *this;
|
|
}
|
|
|
|
template <size_t N, typename BitsT, typename ParamT>
|
|
constexpr BitSetT<N, BitsT, ParamT> &BitSetT<N, BitsT, ParamT>::operator^=(BitsT value)
|
|
{
|
|
mBits ^= value;
|
|
ASSERT(mBits == (mBits & Mask(N)));
|
|
return *this;
|
|
}
|
|
|
|
template <size_t N, typename BitsT, typename ParamT>
|
|
constexpr 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>
|
|
constexpr 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>
|
|
constexpr 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>
|
|
constexpr 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>
|
|
constexpr 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>
|
|
constexpr BitSetT<N, BitsT, ParamT> &BitSetT<N, BitsT, ParamT>::set(ParamT pos, bool value)
|
|
{
|
|
ASSERT(static_cast<size_t>(pos) < N);
|
|
if (value)
|
|
{
|
|
mBits |= Bit<BitsT>(pos);
|
|
}
|
|
else
|
|
{
|
|
reset(pos);
|
|
}
|
|
ASSERT(mBits == (mBits & Mask(N)));
|
|
return *this;
|
|
}
|
|
|
|
template <size_t N, typename BitsT, typename ParamT>
|
|
constexpr 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>
|
|
constexpr BitSetT<N, BitsT, ParamT> &BitSetT<N, BitsT, ParamT>::reset(ParamT pos)
|
|
{
|
|
ASSERT(static_cast<size_t>(pos) < N);
|
|
ASSERT(mBits == (mBits & Mask(N)));
|
|
mBits &= ~Bit<BitsT>(pos);
|
|
return *this;
|
|
}
|
|
|
|
template <size_t N, typename BitsT, typename ParamT>
|
|
constexpr 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>
|
|
constexpr BitSetT<N, BitsT, ParamT> &BitSetT<N, BitsT, ParamT>::flip(ParamT pos)
|
|
{
|
|
ASSERT(static_cast<size_t>(pos) < N);
|
|
mBits ^= Bit<BitsT>(pos);
|
|
ASSERT(mBits == (mBits & Mask(N)));
|
|
return *this;
|
|
}
|
|
|
|
template <size_t N, typename BitsT, typename ParamT>
|
|
constexpr ParamT BitSetT<N, BitsT, ParamT>::first() const
|
|
{
|
|
ASSERT(!none());
|
|
return static_cast<ParamT>(gl::ScanForward(mBits));
|
|
}
|
|
|
|
template <size_t N, typename BitsT, typename ParamT>
|
|
constexpr ParamT BitSetT<N, BitsT, ParamT>::last() const
|
|
{
|
|
ASSERT(!none());
|
|
return static_cast<ParamT>(gl::ScanReverse(mBits));
|
|
}
|
|
|
|
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 BitSet8 = BitSetT<N, uint8_t>;
|
|
|
|
template <size_t N>
|
|
using BitSet16 = BitSetT<N, uint16_t>;
|
|
|
|
template <size_t N>
|
|
using BitSet32 = BitSetT<N, uint32_t>;
|
|
|
|
template <size_t N>
|
|
using BitSet64 = BitSetT<N, uint64_t>;
|
|
|
|
template <std::size_t N>
|
|
class BitSetArray;
|
|
|
|
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 = BitSetArray<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>;
|
|
};
|
|
constexpr std::size_t kDefaultBitSetSize = 64;
|
|
using BaseBitSetType = BitSet64<kDefaultBitSetSize>;
|
|
#else
|
|
template <size_t N>
|
|
struct GetBitSet<N, EnableIfBitsFit<N, uint32_t>>
|
|
{
|
|
using Type = BitSet32<N>;
|
|
};
|
|
constexpr std::size_t kDefaultBitSetSize = 32;
|
|
using BaseBitSetType = BitSet32<kDefaultBitSetSize>;
|
|
#endif // defined(ANGLE_IS_64_BIT_CPU)
|
|
|
|
} // namespace priv
|
|
|
|
template <size_t N>
|
|
using BitSet = typename priv::GetBitSet<N>::Type;
|
|
|
|
template <std::size_t N>
|
|
class BitSetArray final
|
|
{
|
|
public:
|
|
using BaseBitSet = priv::BaseBitSetType;
|
|
|
|
BitSetArray();
|
|
BitSetArray(const BitSetArray<N> &other);
|
|
|
|
using value_type = BaseBitSet::value_type;
|
|
using param_type = BaseBitSet::param_type;
|
|
|
|
class Reference final
|
|
{
|
|
public:
|
|
~Reference() {}
|
|
Reference &operator=(bool x)
|
|
{
|
|
mParent.set(mPosition, x);
|
|
return *this;
|
|
}
|
|
explicit operator bool() const { return mParent.test(mPosition); }
|
|
|
|
private:
|
|
friend class BitSetArray;
|
|
|
|
Reference(BitSetArray &parent, std::size_t pos) : mParent(parent), mPosition(pos) {}
|
|
|
|
BitSetArray &mParent;
|
|
std::size_t mPosition;
|
|
};
|
|
class Iterator final
|
|
{
|
|
public:
|
|
Iterator(const BitSetArray<N> &bitSetArray, std::size_t index);
|
|
Iterator &operator++();
|
|
bool operator==(const Iterator &other) const;
|
|
bool operator!=(const Iterator &other) const;
|
|
size_t 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 pos)
|
|
{
|
|
ASSERT(pos > (mIndex * priv::kDefaultBitSetSize) + *mCurrentIterator);
|
|
prepareCopy();
|
|
mParentCopy.reset(pos);
|
|
updateIteratorBit(pos, false);
|
|
}
|
|
|
|
void setLaterBit(std::size_t pos)
|
|
{
|
|
ASSERT(pos > (mIndex * priv::kDefaultBitSetSize) + *mCurrentIterator);
|
|
prepareCopy();
|
|
mParentCopy.set(pos);
|
|
updateIteratorBit(pos, true);
|
|
}
|
|
|
|
void setLaterBits(const BitSetArray &bits)
|
|
{
|
|
prepareCopy();
|
|
mParentCopy |= bits;
|
|
updateIteratorBits(bits);
|
|
}
|
|
|
|
private:
|
|
ANGLE_INLINE void prepareCopy()
|
|
{
|
|
ASSERT(mParent.mBaseBitSetArray[mIndex].end() ==
|
|
mParentCopy.mBaseBitSetArray[mIndex].end());
|
|
if (mParentCopy.none())
|
|
{
|
|
mParentCopy = mParent;
|
|
mCurrentParent = &mParentCopy;
|
|
}
|
|
}
|
|
|
|
ANGLE_INLINE void updateIteratorBit(std::size_t pos, bool setBit)
|
|
{
|
|
// Get the index and offset, update current interator if within range
|
|
size_t index = pos >> kShiftForDivision;
|
|
size_t offset = pos & kDefaultBitSetSizeMinusOne;
|
|
if (index == mIndex)
|
|
{
|
|
if (setBit)
|
|
{
|
|
mCurrentIterator.setLaterBit(offset);
|
|
}
|
|
else
|
|
{
|
|
mCurrentIterator.resetLaterBit(offset);
|
|
}
|
|
}
|
|
}
|
|
|
|
ANGLE_INLINE void updateIteratorBits(const BitSetArray &bits)
|
|
{
|
|
mCurrentIterator.setLaterBits(bits.mBaseBitSetArray[mIndex]);
|
|
}
|
|
|
|
// Problem -
|
|
// We want to provide the fastest path possible for usecases that iterate though the bitset.
|
|
//
|
|
// Options -
|
|
// 1) For non-mutating iterations the const ref <mParent> is set as mCurrentParent and only
|
|
// for usecases that need to mutate the bitset while iterating we perform a copy of
|
|
// <mParent> into <mParentCopy> and modify its bits accordingly.
|
|
// 2) The alternate approach was to perform a copy all the time in the constructor
|
|
// irrespective of whether it was a mutating usecase or not.
|
|
//
|
|
// Experiment -
|
|
// BitSetIteratorPerfTest was run on a Windows machine with Intel CPU and these were the
|
|
// results -
|
|
// 1) Copy only when necessary -
|
|
// RESULT BitSetIteratorPerf.wall_time: run = 116.1067374961 ns
|
|
// RESULT BitSetIteratorPerf.trial_steps : run = 8416124 count
|
|
// RESULT BitSetIteratorPerf.total_steps : run = 16832251 count
|
|
// 2) Copy always -
|
|
// RESULT BitSetIteratorPerf.wall_time: run = 242.7446459439 ns
|
|
// RESULT BitSetIteratorPerf.trial_steps : run = 4171416 count
|
|
// RESULT BitSetIteratorPerf.total_steps : run = 8342834 count
|
|
//
|
|
// Resolution -
|
|
// We settled on the copy only when necessary path.
|
|
size_t mIndex;
|
|
const BitSetArray &mParent;
|
|
BitSetArray mParentCopy;
|
|
const BitSetArray *mCurrentParent;
|
|
typename BaseBitSet::Iterator mCurrentIterator;
|
|
};
|
|
|
|
constexpr std::size_t size() const { return N; }
|
|
Iterator begin() const { return Iterator(*this, 0); }
|
|
Iterator end() const { return Iterator(*this, kArraySize); }
|
|
unsigned long to_ulong() const
|
|
{
|
|
// TODO(anglebug.com/5628): Handle serializing more than kDefaultBitSetSize
|
|
for (std::size_t index = 1; index < kArraySize; index++)
|
|
{
|
|
ASSERT(mBaseBitSetArray[index].none());
|
|
}
|
|
return static_cast<unsigned long>(mBaseBitSetArray[0].to_ulong());
|
|
}
|
|
|
|
// Assignment operators
|
|
BitSetArray &operator=(const BitSetArray &other);
|
|
BitSetArray &operator&=(const BitSetArray &other);
|
|
BitSetArray &operator|=(const BitSetArray &other);
|
|
BitSetArray &operator^=(const BitSetArray &other);
|
|
|
|
// Bitwise operators
|
|
BitSetArray<N> operator&(const angle::BitSetArray<N> &other) const;
|
|
BitSetArray<N> operator|(const angle::BitSetArray<N> &other) const;
|
|
BitSetArray<N> operator^(const angle::BitSetArray<N> &other) const;
|
|
|
|
// Relational Operators
|
|
bool operator==(const angle::BitSetArray<N> &other) const;
|
|
bool operator!=(const angle::BitSetArray<N> &other) const;
|
|
|
|
// Unary operators
|
|
BitSetArray operator~() const;
|
|
bool operator[](std::size_t pos) const;
|
|
Reference operator[](std::size_t pos)
|
|
{
|
|
ASSERT(pos < size());
|
|
return Reference(*this, pos);
|
|
}
|
|
|
|
// Setter, getters and other helper methods
|
|
BitSetArray &set();
|
|
BitSetArray &set(std::size_t pos, bool value = true);
|
|
BitSetArray &reset();
|
|
BitSetArray &reset(std::size_t pos);
|
|
bool test(std::size_t pos) const;
|
|
bool all() const;
|
|
bool any() const;
|
|
bool none() const;
|
|
std::size_t count() const;
|
|
bool intersects(const BitSetArray &other) const;
|
|
BitSetArray<N> &flip();
|
|
param_type first() const;
|
|
param_type last() const;
|
|
|
|
value_type bits(size_t index) const;
|
|
|
|
private:
|
|
static constexpr std::size_t kDefaultBitSetSizeMinusOne = priv::kDefaultBitSetSize - 1;
|
|
static constexpr std::size_t kShiftForDivision =
|
|
static_cast<std::size_t>(rx::Log2(static_cast<unsigned int>(priv::kDefaultBitSetSize)));
|
|
static constexpr std::size_t kArraySize =
|
|
((N + kDefaultBitSetSizeMinusOne) >> kShiftForDivision);
|
|
constexpr static std::size_t kLastElementCount = (N & kDefaultBitSetSizeMinusOne);
|
|
constexpr static std::size_t kLastElementMask = priv::BaseBitSetType::Mask(
|
|
kLastElementCount == 0 ? priv::kDefaultBitSetSize : kLastElementCount);
|
|
|
|
std::array<BaseBitSet, kArraySize> mBaseBitSetArray;
|
|
};
|
|
|
|
template <std::size_t N>
|
|
BitSetArray<N>::BitSetArray()
|
|
{
|
|
static_assert(N > priv::kDefaultBitSetSize, "BitSetArray type can't support requested size.");
|
|
reset();
|
|
}
|
|
|
|
template <size_t N>
|
|
BitSetArray<N>::BitSetArray(const BitSetArray<N> &other)
|
|
{
|
|
for (std::size_t index = 0; index < kArraySize; index++)
|
|
{
|
|
mBaseBitSetArray[index] = other.mBaseBitSetArray[index];
|
|
}
|
|
}
|
|
|
|
template <size_t N>
|
|
BitSetArray<N>::Iterator::Iterator(const BitSetArray<N> &bitSetArray, std::size_t index)
|
|
: mIndex(index),
|
|
mParent(bitSetArray),
|
|
mCurrentParent(&mParent),
|
|
mCurrentIterator(mParent.mBaseBitSetArray[0].begin())
|
|
{
|
|
while (mIndex < mCurrentParent->kArraySize)
|
|
{
|
|
if (mCurrentParent->mBaseBitSetArray[mIndex].any())
|
|
{
|
|
break;
|
|
}
|
|
mIndex++;
|
|
}
|
|
|
|
if (mIndex < mCurrentParent->kArraySize)
|
|
{
|
|
mCurrentIterator = mCurrentParent->mBaseBitSetArray[mIndex].begin();
|
|
}
|
|
else
|
|
{
|
|
mCurrentIterator = mCurrentParent->mBaseBitSetArray[mCurrentParent->kArraySize - 1].end();
|
|
}
|
|
}
|
|
|
|
template <std::size_t N>
|
|
typename BitSetArray<N>::Iterator &BitSetArray<N>::Iterator::operator++()
|
|
{
|
|
++mCurrentIterator;
|
|
while (mCurrentIterator == mCurrentParent->mBaseBitSetArray[mIndex].end())
|
|
{
|
|
mIndex++;
|
|
if (mIndex >= mCurrentParent->kArraySize)
|
|
{
|
|
break;
|
|
}
|
|
mCurrentIterator = mCurrentParent->mBaseBitSetArray[mIndex].begin();
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
template <std::size_t N>
|
|
bool BitSetArray<N>::Iterator::operator==(const BitSetArray<N>::Iterator &other) const
|
|
{
|
|
return mCurrentIterator == other.mCurrentIterator;
|
|
}
|
|
|
|
template <std::size_t N>
|
|
bool BitSetArray<N>::Iterator::operator!=(const BitSetArray<N>::Iterator &other) const
|
|
{
|
|
return mCurrentIterator != other.mCurrentIterator;
|
|
}
|
|
|
|
template <std::size_t N>
|
|
std::size_t BitSetArray<N>::Iterator::operator*() const
|
|
{
|
|
return (mIndex * priv::kDefaultBitSetSize) + *mCurrentIterator;
|
|
}
|
|
|
|
template <std::size_t N>
|
|
BitSetArray<N> &BitSetArray<N>::operator=(const BitSetArray<N> &other)
|
|
{
|
|
for (std::size_t index = 0; index < kArraySize; index++)
|
|
{
|
|
mBaseBitSetArray[index] = other.mBaseBitSetArray[index];
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
template <std::size_t N>
|
|
BitSetArray<N> &BitSetArray<N>::operator&=(const BitSetArray<N> &other)
|
|
{
|
|
for (std::size_t index = 0; index < kArraySize; index++)
|
|
{
|
|
mBaseBitSetArray[index] &= other.mBaseBitSetArray[index];
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
template <std::size_t N>
|
|
BitSetArray<N> &BitSetArray<N>::operator|=(const BitSetArray<N> &other)
|
|
{
|
|
for (std::size_t index = 0; index < kArraySize; index++)
|
|
{
|
|
mBaseBitSetArray[index] |= other.mBaseBitSetArray[index];
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
template <std::size_t N>
|
|
BitSetArray<N> &BitSetArray<N>::operator^=(const BitSetArray<N> &other)
|
|
{
|
|
for (std::size_t index = 0; index < kArraySize; index++)
|
|
{
|
|
mBaseBitSetArray[index] ^= other.mBaseBitSetArray[index];
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
template <std::size_t N>
|
|
BitSetArray<N> BitSetArray<N>::operator&(const angle::BitSetArray<N> &other) const
|
|
{
|
|
angle::BitSetArray<N> result(other);
|
|
result &= *this;
|
|
return result;
|
|
}
|
|
|
|
template <std::size_t N>
|
|
BitSetArray<N> BitSetArray<N>::operator|(const angle::BitSetArray<N> &other) const
|
|
{
|
|
angle::BitSetArray<N> result(other);
|
|
result |= *this;
|
|
return result;
|
|
}
|
|
|
|
template <std::size_t N>
|
|
BitSetArray<N> BitSetArray<N>::operator^(const angle::BitSetArray<N> &other) const
|
|
{
|
|
angle::BitSetArray<N> result(other);
|
|
result ^= *this;
|
|
return result;
|
|
}
|
|
|
|
template <std::size_t N>
|
|
bool BitSetArray<N>::operator==(const angle::BitSetArray<N> &other) const
|
|
{
|
|
for (std::size_t index = 0; index < kArraySize; index++)
|
|
{
|
|
if (mBaseBitSetArray[index] != other.mBaseBitSetArray[index])
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
template <std::size_t N>
|
|
bool BitSetArray<N>::operator!=(const angle::BitSetArray<N> &other) const
|
|
{
|
|
return !(*this == other);
|
|
}
|
|
|
|
template <std::size_t N>
|
|
BitSetArray<N> BitSetArray<N>::operator~() const
|
|
{
|
|
angle::BitSetArray<N> result;
|
|
for (std::size_t index = 0; index < kArraySize; index++)
|
|
{
|
|
result.mBaseBitSetArray[index] |= ~mBaseBitSetArray[index];
|
|
}
|
|
// The last element in result may need special handling
|
|
result.mBaseBitSetArray[kArraySize - 1] &= kLastElementMask;
|
|
|
|
return result;
|
|
}
|
|
|
|
template <std::size_t N>
|
|
bool BitSetArray<N>::operator[](std::size_t pos) const
|
|
{
|
|
ASSERT(pos < size());
|
|
return test(pos);
|
|
}
|
|
|
|
template <std::size_t N>
|
|
BitSetArray<N> &BitSetArray<N>::set()
|
|
{
|
|
for (BaseBitSet &baseBitSet : mBaseBitSetArray)
|
|
{
|
|
baseBitSet.set();
|
|
}
|
|
// The last element in mBaseBitSetArray may need special handling
|
|
mBaseBitSetArray[kArraySize - 1] &= kLastElementMask;
|
|
|
|
return *this;
|
|
}
|
|
|
|
template <std::size_t N>
|
|
BitSetArray<N> &BitSetArray<N>::set(std::size_t pos, bool value)
|
|
{
|
|
ASSERT(pos < size());
|
|
// Get the index and offset, then set the bit
|
|
size_t index = pos >> kShiftForDivision;
|
|
size_t offset = pos & kDefaultBitSetSizeMinusOne;
|
|
mBaseBitSetArray[index].set(offset, value);
|
|
return *this;
|
|
}
|
|
|
|
template <std::size_t N>
|
|
BitSetArray<N> &BitSetArray<N>::reset()
|
|
{
|
|
for (BaseBitSet &baseBitSet : mBaseBitSetArray)
|
|
{
|
|
baseBitSet.reset();
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
template <std::size_t N>
|
|
BitSetArray<N> &BitSetArray<N>::reset(std::size_t pos)
|
|
{
|
|
ASSERT(pos < size());
|
|
return set(pos, false);
|
|
}
|
|
|
|
template <std::size_t N>
|
|
bool BitSetArray<N>::test(std::size_t pos) const
|
|
{
|
|
ASSERT(pos < size());
|
|
// Get the index and offset, then test the bit
|
|
size_t index = pos >> kShiftForDivision;
|
|
size_t offset = pos & kDefaultBitSetSizeMinusOne;
|
|
return mBaseBitSetArray[index].test(offset);
|
|
}
|
|
|
|
template <std::size_t N>
|
|
bool BitSetArray<N>::all() const
|
|
{
|
|
static priv::BaseBitSetType kLastElementBitSet = priv::BaseBitSetType(kLastElementMask);
|
|
|
|
for (std::size_t index = 0; index < kArraySize - 1; index++)
|
|
{
|
|
if (!mBaseBitSetArray[index].all())
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// The last element in mBaseBitSetArray may need special handling
|
|
return mBaseBitSetArray[kArraySize - 1] == kLastElementBitSet;
|
|
}
|
|
|
|
template <std::size_t N>
|
|
bool BitSetArray<N>::any() const
|
|
{
|
|
for (const BaseBitSet &baseBitSet : mBaseBitSetArray)
|
|
{
|
|
if (baseBitSet.any())
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
template <std::size_t N>
|
|
bool BitSetArray<N>::none() const
|
|
{
|
|
for (const BaseBitSet &baseBitSet : mBaseBitSetArray)
|
|
{
|
|
if (!baseBitSet.none())
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
template <std::size_t N>
|
|
std::size_t BitSetArray<N>::count() const
|
|
{
|
|
size_t count = 0;
|
|
for (const BaseBitSet &baseBitSet : mBaseBitSetArray)
|
|
{
|
|
count += baseBitSet.count();
|
|
}
|
|
return count;
|
|
}
|
|
|
|
template <std::size_t N>
|
|
bool BitSetArray<N>::intersects(const BitSetArray<N> &other) const
|
|
{
|
|
for (std::size_t index = 0; index < kArraySize; index++)
|
|
{
|
|
if ((mBaseBitSetArray[index].bits() & other.mBaseBitSetArray[index].bits()) != 0)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
template <std::size_t N>
|
|
BitSetArray<N> &BitSetArray<N>::flip()
|
|
{
|
|
for (BaseBitSet &baseBitSet : mBaseBitSetArray)
|
|
{
|
|
baseBitSet.flip();
|
|
}
|
|
|
|
// The last element in mBaseBitSetArray may need special handling
|
|
mBaseBitSetArray[kArraySize - 1] &= kLastElementMask;
|
|
return *this;
|
|
}
|
|
|
|
template <std::size_t N>
|
|
typename BitSetArray<N>::param_type BitSetArray<N>::first() const
|
|
{
|
|
ASSERT(any());
|
|
for (size_t arrayIndex = 0; arrayIndex < kArraySize; ++arrayIndex)
|
|
{
|
|
const BaseBitSet &baseBitSet = mBaseBitSetArray[arrayIndex];
|
|
if (baseBitSet.any())
|
|
{
|
|
return baseBitSet.first() + arrayIndex * priv::kDefaultBitSetSize;
|
|
}
|
|
}
|
|
UNREACHABLE();
|
|
return 0;
|
|
}
|
|
|
|
template <std::size_t N>
|
|
typename BitSetArray<N>::param_type BitSetArray<N>::last() const
|
|
{
|
|
ASSERT(any());
|
|
for (size_t arrayIndex = kArraySize; arrayIndex > 0; --arrayIndex)
|
|
{
|
|
const BaseBitSet &baseBitSet = mBaseBitSetArray[arrayIndex - 1];
|
|
if (baseBitSet.any())
|
|
{
|
|
return baseBitSet.last() + (arrayIndex - 1) * priv::kDefaultBitSetSize;
|
|
}
|
|
}
|
|
UNREACHABLE();
|
|
return 0;
|
|
}
|
|
|
|
template <std::size_t N>
|
|
typename BitSetArray<N>::value_type BitSetArray<N>::bits(size_t index) const
|
|
{
|
|
return mBaseBitSetArray[index].bits();
|
|
}
|
|
} // namespace angle
|
|
|
|
template <size_t N, typename BitsT, typename ParamT>
|
|
inline constexpr 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 constexpr 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 constexpr 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 bool operator==(angle::BitSetT<N, BitsT, ParamT> &lhs, angle::BitSetT<N, BitsT, ParamT> &rhs)
|
|
{
|
|
return lhs.bits() == rhs.bits();
|
|
}
|
|
|
|
template <size_t N, typename BitsT, typename ParamT>
|
|
inline bool operator!=(angle::BitSetT<N, BitsT, ParamT> &lhs, angle::BitSetT<N, BitsT, ParamT> &rhs)
|
|
{
|
|
return !(lhs == rhs);
|
|
}
|
|
|
|
#endif // COMMON_BITSETITERATOR_H_
|