aboutsummaryrefslogtreecommitdiffstats
path: root/util/generic/bitmap.h
diff options
context:
space:
mode:
authorDevtools Arcadia <arcadia-devtools@yandex-team.ru>2022-02-07 18:08:42 +0300
committerDevtools Arcadia <arcadia-devtools@mous.vla.yp-c.yandex.net>2022-02-07 18:08:42 +0300
commit1110808a9d39d4b808aef724c861a2e1a38d2a69 (patch)
treee26c9fed0de5d9873cce7e00bc214573dc2195b7 /util/generic/bitmap.h
downloadydb-1110808a9d39d4b808aef724c861a2e1a38d2a69.tar.gz
intermediate changes
ref:cde9a383711a11544ce7e107a78147fb96cc4029
Diffstat (limited to 'util/generic/bitmap.h')
-rw-r--r--util/generic/bitmap.h1114
1 files changed, 1114 insertions, 0 deletions
diff --git a/util/generic/bitmap.h b/util/generic/bitmap.h
new file mode 100644
index 0000000000..f77d182460
--- /dev/null
+++ b/util/generic/bitmap.h
@@ -0,0 +1,1114 @@
+#pragma once
+
+#include "fwd.h"
+#include "ptr.h"
+#include "bitops.h"
+#include "typetraits.h"
+#include "algorithm.h"
+#include "utility.h"
+
+#include <util/system/yassert.h>
+#include <util/system/defaults.h>
+#include <util/str_stl.h>
+#include <util/ysaveload.h>
+
+namespace NBitMapPrivate {
+ // Returns number of bits set; result is in most significatnt byte
+ inline ui64 ByteSums(ui64 x) {
+ ui64 byteSums = x - ((x & 0xAAAAAAAAAAAAAAAAULL) >> 1);
+
+ byteSums = (byteSums & 0x3333333333333333ULL) + ((byteSums >> 2) & 0x3333333333333333ULL);
+ byteSums = (byteSums + (byteSums >> 4)) & 0x0F0F0F0F0F0F0F0FULL;
+
+ return byteSums * 0x0101010101010101ULL;
+ }
+
+ // better than intrinsics without -mpopcnt
+ template <typename T>
+ static unsigned CountBitsPrivate(T v) noexcept {
+ return static_cast<unsigned>(ByteSums(v) >> 56);
+ }
+
+ template <typename TChunkType, size_t ExtraBits>
+ struct TSanitizeMask {
+ static constexpr TChunkType Value = ~((~TChunkType(0)) << ExtraBits);
+ };
+
+ template <typename TChunkType>
+ struct TSanitizeMask<TChunkType, 0> {
+ static constexpr TChunkType Value = (TChunkType)~TChunkType(0u);
+ };
+
+ template <typename TTargetChunk, typename TSourceChunk>
+ struct TBigToSmallDataCopier {
+ static_assert(sizeof(TTargetChunk) < sizeof(TSourceChunk), "expect sizeof(TTargetChunk) < sizeof(TSourceChunk)");
+ static_assert(0 == sizeof(TSourceChunk) % sizeof(TTargetChunk), "expect 0 == sizeof(TSourceChunk) % sizeof(TTargetChunk)");
+
+ static constexpr size_t BLOCK_SIZE = sizeof(TSourceChunk) / sizeof(TTargetChunk);
+
+ union TCnv {
+ TSourceChunk BigData;
+ TTargetChunk SmallData[BLOCK_SIZE];
+ };
+
+ static inline void CopyChunk(TTargetChunk* target, TSourceChunk source) {
+ TCnv c;
+ c.BigData = source;
+#if defined(_big_endian_)
+ ::ReverseCopy(c.SmallData, c.SmallData + Y_ARRAY_SIZE(c.SmallData), target);
+#else
+ ::Copy(c.SmallData, c.SmallData + Y_ARRAY_SIZE(c.SmallData), target);
+#endif
+ }
+
+ static inline void Copy(TTargetChunk* target, size_t targetSize, const TSourceChunk* source, size_t sourceSize) {
+ Y_ASSERT(targetSize >= sourceSize * BLOCK_SIZE);
+ if (targetSize > sourceSize * BLOCK_SIZE) {
+ ::Fill(target + sourceSize * BLOCK_SIZE, target + targetSize, 0);
+ }
+ for (size_t i = 0; i < sourceSize; ++i) {
+ CopyChunk(target + i * BLOCK_SIZE, source[i]);
+ }
+ }
+ };
+
+ template <typename TTargetChunk, typename TSourceChunk>
+ struct TSmallToBigDataCopier {
+ static_assert(sizeof(TTargetChunk) > sizeof(TSourceChunk), "expect sizeof(TTargetChunk) > sizeof(TSourceChunk)");
+ static_assert(0 == sizeof(TTargetChunk) % sizeof(TSourceChunk), "expect 0 == sizeof(TTargetChunk) % sizeof(TSourceChunk)");
+
+ static constexpr size_t BLOCK_SIZE = sizeof(TTargetChunk) / sizeof(TSourceChunk);
+
+ union TCnv {
+ TSourceChunk SmallData[BLOCK_SIZE];
+ TTargetChunk BigData;
+ };
+
+ static inline TTargetChunk CopyFullChunk(const TSourceChunk* source) {
+ TCnv c;
+#if defined(_big_endian_)
+ ::ReverseCopy(source, source + BLOCK_SIZE, c.SmallData);
+#else
+ ::Copy(source, source + BLOCK_SIZE, c.SmallData);
+#endif
+ return c.BigData;
+ }
+
+ static inline TTargetChunk CopyPartChunk(const TSourceChunk* source, size_t count) {
+ Y_ASSERT(count <= BLOCK_SIZE);
+ TCnv c;
+ c.BigData = 0;
+#if defined(_big_endian_)
+ ::ReverseCopy(source, source + count, c.SmallData);
+#else
+ ::Copy(source, source + count, c.SmallData);
+#endif
+ return c.BigData;
+ }
+
+ static inline void Copy(TTargetChunk* target, size_t targetSize, const TSourceChunk* source, size_t sourceSize) {
+ Y_ASSERT(targetSize * BLOCK_SIZE >= sourceSize);
+ if (targetSize * BLOCK_SIZE > sourceSize) {
+ ::Fill(target + sourceSize / BLOCK_SIZE, target + targetSize, 0);
+ }
+ size_t i = 0;
+ for (; i < sourceSize / BLOCK_SIZE; ++i) {
+ target[i] = CopyFullChunk(source + i * BLOCK_SIZE);
+ }
+ if (0 != sourceSize % BLOCK_SIZE) {
+ target[i] = CopyPartChunk(source + i * BLOCK_SIZE, sourceSize % BLOCK_SIZE);
+ }
+ }
+ };
+
+ template <typename TChunk>
+ struct TUniformDataCopier {
+ static inline void Copy(TChunk* target, size_t targetSize, const TChunk* source, size_t sourceSize) {
+ Y_ASSERT(targetSize >= sourceSize);
+ for (size_t i = 0; i < sourceSize; ++i) {
+ target[i] = source[i];
+ }
+ for (size_t i = sourceSize; i < targetSize; ++i) {
+ target[i] = 0;
+ }
+ }
+ };
+
+ template <typename TFirst, typename TSecond>
+ struct TIsSmaller {
+ enum {
+ Result = sizeof(TFirst) < sizeof(TSecond)
+ };
+ };
+
+ template <typename TTargetChunk, typename TSourceChunk>
+ struct TDataCopier: public std::conditional_t<std::is_same<TTargetChunk, TSourceChunk>::value, TUniformDataCopier<TTargetChunk>, std::conditional_t<TIsSmaller<TTargetChunk, TSourceChunk>::Result, TBigToSmallDataCopier<TTargetChunk, TSourceChunk>, TSmallToBigDataCopier<TTargetChunk, TSourceChunk>>> {
+ };
+
+ template <typename TTargetChunk, typename TSourceChunk>
+ inline void CopyData(TTargetChunk* target, size_t targetSize, const TSourceChunk* source, size_t sourceSize) {
+ TDataCopier<TTargetChunk, TSourceChunk>::Copy(target, targetSize, source, sourceSize);
+ }
+
+ template <size_t BitCount, typename TChunkType>
+ struct TFixedStorage {
+ using TChunk = TChunkType;
+
+ static constexpr size_t Size = (BitCount + 8 * sizeof(TChunk) - 1) / (8 * sizeof(TChunk));
+
+ TChunk Data[Size];
+
+ TFixedStorage() {
+ Zero(Data);
+ }
+
+ TFixedStorage(const TFixedStorage<BitCount, TChunkType>& st) {
+ for (size_t i = 0; i < Size; ++i) {
+ Data[i] = st.Data[i];
+ }
+ }
+
+ template <typename TOtherChunk>
+ TFixedStorage(const TOtherChunk* data, size_t size) {
+ Y_VERIFY(Size * sizeof(TChunk) >= size * sizeof(TOtherChunk), "Exceeding bitmap storage capacity");
+ CopyData(Data, Size, data, size);
+ }
+
+ Y_FORCE_INLINE void Swap(TFixedStorage<BitCount, TChunkType>& st) {
+ for (size_t i = 0; i < Size; ++i) {
+ DoSwap(Data[i], st.Data[i]);
+ }
+ }
+
+ Y_FORCE_INLINE static constexpr size_t GetBitCapacity() noexcept {
+ return BitCount;
+ }
+
+ Y_FORCE_INLINE static constexpr size_t GetChunkCapacity() noexcept {
+ return Size;
+ }
+
+ // Returns true if the resulting storage capacity is enough to fit the requested size
+ Y_FORCE_INLINE static constexpr bool ExpandBitSize(const size_t bitSize) noexcept {
+ return bitSize <= BitCount;
+ }
+
+ Y_FORCE_INLINE void Sanitize() {
+ Data[Size - 1] &= TSanitizeMask<TChunk, BitCount % (8 * sizeof(TChunk))>::Value;
+ }
+ };
+
+ // Dynamically expanded storage.
+ // It uses "on stack" realization with no allocation for one chunk spaces
+ template <typename TChunkType>
+ struct TDynamicStorage {
+ using TChunk = TChunkType;
+
+ size_t Size;
+ TChunk StackData;
+ TArrayHolder<TChunk> ArrayData;
+ TChunk* Data;
+
+ TDynamicStorage()
+ : Size(1)
+ , StackData(0)
+ , Data(&StackData)
+ {
+ }
+
+ TDynamicStorage(const TDynamicStorage<TChunk>& st)
+ : Size(1)
+ , StackData(0)
+ , Data(&StackData)
+ {
+ ExpandSize(st.Size, false);
+ for (size_t i = 0; i < st.Size; ++i) {
+ Data[i] = st.Data[i];
+ }
+ for (size_t i = st.Size; i < Size; ++i) {
+ Data[i] = 0;
+ }
+ }
+
+ template <typename TOtherChunk>
+ TDynamicStorage(const TOtherChunk* data, size_t size)
+ : Size(1)
+ , StackData(0)
+ , Data(&StackData)
+ {
+ ExpandBitSize(size * sizeof(TOtherChunk) * 8, false);
+ CopyData(Data, Size, data, size);
+ }
+
+ Y_FORCE_INLINE void Swap(TDynamicStorage<TChunkType>& st) {
+ DoSwap(Size, st.Size);
+ DoSwap(StackData, st.StackData);
+ DoSwap(ArrayData, st.ArrayData);
+ Data = 1 == Size ? &StackData : ArrayData.Get();
+ st.Data = 1 == st.Size ? &st.StackData : st.ArrayData.Get();
+ }
+
+ Y_FORCE_INLINE size_t GetBitCapacity() const {
+ return Size * 8 * sizeof(TChunk);
+ }
+
+ Y_FORCE_INLINE size_t GetChunkCapacity() const {
+ return Size;
+ }
+
+ // Returns true if the resulting storage capacity is enough to fit the requested size
+ Y_FORCE_INLINE bool ExpandSize(size_t size, bool keepData = true) {
+ if (size > Size) {
+ size = Max(size, Size * 2);
+ TArrayHolder<TChunk> newData(new TChunk[size]);
+ if (keepData) {
+ for (size_t i = 0; i < Size; ++i) {
+ newData[i] = Data[i];
+ }
+ for (size_t i = Size; i < size; ++i) {
+ newData[i] = 0;
+ }
+ }
+ DoSwap(ArrayData, newData);
+ Data = ArrayData.Get();
+ Size = size;
+ }
+ return true;
+ }
+
+ Y_FORCE_INLINE bool ExpandBitSize(size_t bitSize, bool keepData = true) {
+ return ExpandSize((bitSize + 8 * sizeof(TChunk) - 1) / (8 * sizeof(TChunk)), keepData);
+ }
+
+ Y_FORCE_INLINE void Sanitize() {
+ }
+ };
+
+ template <size_t num>
+ struct TDivCount {
+ static constexpr size_t Value = 1 + TDivCount<(num >> 1)>::Value;
+ };
+
+ template <>
+ struct TDivCount<0> {
+ static constexpr size_t Value = 0;
+ };
+
+}
+
+template <size_t BitCount, typename TChunkType>
+struct TFixedBitMapTraits {
+ using TChunk = TChunkType;
+ using TStorage = NBitMapPrivate::TFixedStorage<BitCount, TChunkType>;
+};
+
+template <typename TChunkType>
+struct TDynamicBitMapTraits {
+ using TChunk = TChunkType;
+ using TStorage = NBitMapPrivate::TDynamicStorage<TChunkType>;
+};
+
+template <class TTraits>
+class TBitMapOps {
+public:
+ using TChunk = typename TTraits::TChunk;
+ using TThis = TBitMapOps<TTraits>;
+
+private:
+ static_assert(std::is_unsigned<TChunk>::value, "expect std::is_unsigned<TChunk>::value");
+
+ static constexpr size_t BitsPerChunk = 8 * sizeof(TChunk);
+ static constexpr TChunk ModMask = static_cast<TChunk>(BitsPerChunk - 1);
+ static constexpr size_t DivCount = NBitMapPrivate::TDivCount<BitsPerChunk>::Value - 1;
+ static constexpr TChunk FullChunk = (TChunk)~TChunk(0);
+
+ template <class>
+ friend class TBitMapOps;
+
+ using TStorage = typename TTraits::TStorage;
+
+ // The smallest unsigned type, which can be used in bit ops
+ using TIntType = std::conditional_t<sizeof(TChunk) < sizeof(unsigned int), unsigned int, TChunk>;
+
+ TStorage Mask;
+
+public:
+ class TReference {
+ private:
+ friend class TBitMapOps<TTraits>;
+
+ TChunk* Chunk;
+ size_t Offset;
+
+ TReference(TChunk* c, size_t offset)
+ : Chunk(c)
+ , Offset(offset)
+ {
+ }
+
+ public:
+ ~TReference() = default;
+
+ Y_FORCE_INLINE TReference& operator=(bool val) {
+ if (val)
+ *Chunk |= static_cast<TChunk>(1) << Offset;
+ else
+ *Chunk &= ~(static_cast<TChunk>(1) << Offset);
+
+ return *this;
+ }
+
+ Y_FORCE_INLINE TReference& operator=(const TReference& ref) {
+ if (ref)
+ *Chunk |= static_cast<TChunk>(1) << Offset;
+ else
+ *Chunk &= ~(static_cast<TChunk>(1) << Offset);
+
+ return *this;
+ }
+
+ Y_FORCE_INLINE bool operator~() const {
+ return 0 == (*Chunk & (static_cast<TChunk>(1) << Offset));
+ }
+
+ Y_FORCE_INLINE operator bool() const {
+ return 0 != (*Chunk & (static_cast<TChunk>(1) << Offset));
+ }
+
+ Y_FORCE_INLINE TReference& Flip() {
+ *Chunk ^= static_cast<TChunk>(1) << Offset;
+ return *this;
+ }
+ };
+
+private:
+ struct TSetOp {
+ static constexpr TChunk Op(const TChunk src, const TChunk mask) noexcept {
+ return src | mask;
+ }
+ };
+
+ struct TResetOp {
+ static constexpr TChunk Op(const TChunk src, const TChunk mask) noexcept {
+ return src & ~mask;
+ }
+ };
+
+ template <class TUpdateOp>
+ void UpdateRange(size_t start, size_t end) {
+ const size_t startChunk = start >> DivCount;
+ const size_t startBitOffset = start & ModMask;
+
+ const size_t endChunk = end >> DivCount;
+ const size_t endBitOffset = end & ModMask;
+
+ size_t bitOffset = startBitOffset;
+ for (size_t chunk = startChunk; chunk <= endChunk; ++chunk) {
+ TChunk updateMask = FullChunk << bitOffset;
+ if (chunk == endChunk) {
+ updateMask ^= FullChunk << endBitOffset;
+ if (!updateMask)
+ break;
+ }
+ Mask.Data[chunk] = TUpdateOp::Op(Mask.Data[chunk], updateMask);
+ bitOffset = 0;
+ }
+ }
+
+public:
+ TBitMapOps() = default;
+
+ TBitMapOps(TChunk val) {
+ Mask.Data[0] = val;
+ Mask.Sanitize();
+ }
+
+ TBitMapOps(const TThis&) = default;
+
+ template <class T>
+ TBitMapOps(const TBitMapOps<T>& bitmap)
+ : Mask(bitmap.Mask.Data, bitmap.Mask.GetChunkCapacity())
+ {
+ Mask.Sanitize();
+ }
+
+ template <class T>
+ Y_FORCE_INLINE bool operator==(const TBitMapOps<T>& bitmap) const {
+ return Equal(bitmap);
+ }
+
+ Y_FORCE_INLINE TThis& operator=(const TThis& bitmap) {
+ if (this != &bitmap) {
+ TThis bm(bitmap);
+ Swap(bm);
+ }
+ return *this;
+ }
+
+ template <class T>
+ Y_FORCE_INLINE TThis& operator=(const TBitMapOps<T>& bitmap) {
+ TThis bm(bitmap);
+ Swap(bm);
+ return *this;
+ }
+
+ template <class T>
+ Y_FORCE_INLINE TThis& operator&=(const TBitMapOps<T>& bitmap) {
+ return And(bitmap);
+ }
+
+ Y_FORCE_INLINE TThis& operator&=(const TChunk& val) {
+ return And(val);
+ }
+
+ template <class T>
+ Y_FORCE_INLINE TThis& operator|=(const TBitMapOps<T>& bitmap) {
+ return Or(bitmap);
+ }
+
+ Y_FORCE_INLINE TThis& operator|=(const TChunk& val) {
+ return Or(val);
+ }
+
+ template <class T>
+ Y_FORCE_INLINE TThis& operator^=(const TBitMapOps<T>& bitmap) {
+ return Xor(bitmap);
+ }
+
+ Y_FORCE_INLINE TThis& operator^=(const TChunk& val) {
+ return Xor(val);
+ }
+
+ template <class T>
+ Y_FORCE_INLINE TThis& operator-=(const TBitMapOps<T>& bitmap) {
+ return SetDifference(bitmap);
+ }
+
+ Y_FORCE_INLINE TThis& operator-=(const TChunk& val) {
+ return SetDifference(val);
+ }
+
+ Y_FORCE_INLINE TThis& operator<<=(size_t pos) {
+ return LShift(pos);
+ }
+
+ Y_FORCE_INLINE TThis& operator>>=(size_t pos) {
+ return RShift(pos);
+ }
+
+ Y_FORCE_INLINE TThis operator<<(size_t pos) const {
+ return TThis(*this).LShift(pos);
+ }
+
+ Y_FORCE_INLINE TThis operator>>(size_t pos) const {
+ return TThis(*this).RShift(pos);
+ }
+
+ Y_FORCE_INLINE bool operator[](size_t pos) const {
+ return Get(pos);
+ }
+
+ Y_FORCE_INLINE TReference operator[](size_t pos) {
+ const bool fitStorage = Mask.ExpandBitSize(pos + 1);
+ Y_ASSERT(fitStorage);
+ return TReference(&Mask.Data[pos >> DivCount], ModMask & pos);
+ }
+
+ Y_FORCE_INLINE void Swap(TThis& bitmap) {
+ DoSwap(Mask, bitmap.Mask);
+ }
+
+ Y_FORCE_INLINE TThis& Set(size_t pos) {
+ const bool fitStorage = Mask.ExpandBitSize(pos + 1);
+ Y_ASSERT(fitStorage);
+ Mask.Data[pos >> DivCount] |= static_cast<TChunk>(1) << (pos & ModMask);
+ return *this;
+ }
+
+ // Fills the specified [start, end) bit range by the 1. Other bits are kept unchanged
+ TThis& Set(size_t start, size_t end) {
+ Y_ASSERT(start <= end);
+ if (start < end) {
+ Reserve(end);
+ UpdateRange<TSetOp>(start, end);
+ }
+ return *this;
+ }
+
+ Y_FORCE_INLINE TThis& Reset(size_t pos) {
+ if ((pos >> DivCount) < Mask.GetChunkCapacity()) {
+ Mask.Data[pos >> DivCount] &= ~(static_cast<TChunk>(1) << (pos & ModMask));
+ }
+ return *this;
+ }
+
+ // Clears the specified [start, end) bit range. Other bits are kept unchanged
+ TThis& Reset(size_t start, size_t end) {
+ Y_ASSERT(start <= end);
+ if (start < end && (start >> DivCount) < Mask.GetChunkCapacity()) {
+ UpdateRange<TResetOp>(start, Min(end, Mask.GetBitCapacity()));
+ }
+ return *this;
+ }
+
+ Y_FORCE_INLINE TThis& Flip(size_t pos) {
+ const bool fitStorage = Mask.ExpandBitSize(pos + 1);
+ Y_ASSERT(fitStorage);
+ Mask.Data[pos >> DivCount] ^= static_cast<TChunk>(1) << (pos & ModMask);
+ return *this;
+ }
+
+ Y_FORCE_INLINE bool Get(size_t pos) const {
+ if ((pos >> DivCount) < Mask.GetChunkCapacity()) {
+ return Mask.Data[pos >> DivCount] & (static_cast<TChunk>(1) << (pos & ModMask));
+ }
+ return false;
+ }
+
+ template <class TTo>
+ void Export(size_t pos, TTo& to) const {
+ static_assert(std::is_unsigned<TTo>::value, "expect std::is_unsigned<TTo>::value");
+ to = 0;
+ size_t chunkpos = pos >> DivCount;
+ if (chunkpos >= Mask.GetChunkCapacity())
+ return;
+ if ((pos & ModMask) == 0) {
+ if (sizeof(TChunk) >= sizeof(TTo))
+ to = (TTo)Mask.Data[chunkpos];
+ else //if (sizeof(TChunk) < sizeof(TTo))
+ NBitMapPrivate::CopyData(&to, 1, Mask.Data + chunkpos, Min(((sizeof(TTo) * 8) >> DivCount), Mask.GetChunkCapacity() - chunkpos));
+ } else if ((pos & (sizeof(TTo) * 8 - 1)) == 0 && sizeof(TChunk) >= 2 * sizeof(TTo))
+ to = (TTo)(Mask.Data[chunkpos] >> (pos & ModMask));
+ else {
+ static constexpr size_t copyToSize = (sizeof(TChunk) >= sizeof(TTo)) ? (sizeof(TChunk) / sizeof(TTo)) + 2 : 3;
+ TTo temp[copyToSize] = {0, 0};
+ //or use non defined by now TBitmap<copyToSize, TTo>::CopyData,RShift(pos & ModMask),Export(0,to)
+ NBitMapPrivate::CopyData(temp, copyToSize, Mask.Data + chunkpos, Min((sizeof(TTo) / sizeof(TChunk)) + 1, Mask.GetChunkCapacity() - chunkpos));
+ to = (temp[0] >> (pos & ModMask)) | (temp[1] << (8 * sizeof(TTo) - (pos & ModMask)));
+ }
+ }
+
+ Y_FORCE_INLINE bool Test(size_t n) const {
+ return Get(n);
+ }
+
+ Y_FORCE_INLINE TThis& Push(bool val) {
+ LShift(1);
+ return val ? Set(0) : *this;
+ }
+
+ Y_FORCE_INLINE bool Pop() {
+ bool val = Get(0);
+ return RShift(1), val;
+ }
+
+ // Clear entire bitmap. Current capacity is kept unchanged
+ Y_FORCE_INLINE TThis& Clear() {
+ for (size_t i = 0; i < Mask.GetChunkCapacity(); ++i) {
+ Mask.Data[i] = 0;
+ }
+ return *this;
+ }
+
+ // Returns bits capacity
+ Y_FORCE_INLINE constexpr size_t Size() const noexcept {
+ return Mask.GetBitCapacity();
+ }
+
+ Y_FORCE_INLINE void Reserve(size_t bitCount) {
+ Y_VERIFY(Mask.ExpandBitSize(bitCount), "Exceeding bitmap storage capacity");
+ }
+
+ Y_FORCE_INLINE size_t ValueBitCount() const {
+ size_t nonZeroChunk = Mask.GetChunkCapacity() - 1;
+ while (nonZeroChunk != 0 && !Mask.Data[nonZeroChunk])
+ --nonZeroChunk;
+ return nonZeroChunk || Mask.Data[nonZeroChunk]
+ ? nonZeroChunk * BitsPerChunk + GetValueBitCount(TIntType(Mask.Data[nonZeroChunk]))
+ : 0;
+ }
+
+ Y_PURE_FUNCTION Y_FORCE_INLINE bool Empty() const {
+ for (size_t i = 0; i < Mask.GetChunkCapacity(); ++i)
+ if (Mask.Data[i])
+ return false;
+ return true;
+ }
+
+ bool HasAny(const TThis& bitmap) const {
+ for (size_t i = 0; i < Min(Mask.GetChunkCapacity(), bitmap.Mask.GetChunkCapacity()); ++i) {
+ if (0 != (Mask.Data[i] & bitmap.Mask.Data[i])) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ template <class T>
+ Y_FORCE_INLINE bool HasAny(const TBitMapOps<T>& bitmap) const {
+ return HasAny(TThis(bitmap));
+ }
+
+ Y_FORCE_INLINE bool HasAny(const TChunk& val) const {
+ return 0 != (Mask.Data[0] & val);
+ }
+
+ bool HasAll(const TThis& bitmap) const {
+ for (size_t i = 0; i < Min(Mask.GetChunkCapacity(), bitmap.Mask.GetChunkCapacity()); ++i) {
+ if (bitmap.Mask.Data[i] != (Mask.Data[i] & bitmap.Mask.Data[i])) {
+ return false;
+ }
+ }
+ for (size_t i = Mask.GetChunkCapacity(); i < bitmap.Mask.GetChunkCapacity(); ++i) {
+ if (bitmap.Mask.Data[i] != 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ template <class T>
+ Y_FORCE_INLINE bool HasAll(const TBitMapOps<T>& bitmap) const {
+ return HasAll(TThis(bitmap));
+ }
+
+ Y_FORCE_INLINE bool HasAll(const TChunk& val) const {
+ return (Mask.Data[0] & val) == val;
+ }
+
+ TThis& And(const TThis& bitmap) {
+ // Don't expand capacity here, because resulting bits in positions,
+ // which are greater then size of one of these bitmaps, will be zero
+ for (size_t i = 0; i < Min(bitmap.Mask.GetChunkCapacity(), Mask.GetChunkCapacity()); ++i)
+ Mask.Data[i] &= bitmap.Mask.Data[i];
+ // Clear bits if current bitmap size is greater than AND-ed one
+ for (size_t i = bitmap.Mask.GetChunkCapacity(); i < Mask.GetChunkCapacity(); ++i)
+ Mask.Data[i] = 0;
+ return *this;
+ }
+
+ template <class T>
+ Y_FORCE_INLINE TThis& And(const TBitMapOps<T>& bitmap) {
+ return And(TThis(bitmap));
+ }
+
+ Y_FORCE_INLINE TThis& And(const TChunk& val) {
+ Mask.Data[0] &= val;
+ for (size_t i = 1; i < Mask.GetChunkCapacity(); ++i)
+ Mask.Data[i] = 0;
+ return *this;
+ }
+
+ TThis& Or(const TThis& bitmap) {
+ const size_t valueBitCount = bitmap.ValueBitCount();
+ if (valueBitCount) {
+ // Memory optimization: expand size only for non-zero bits
+ Reserve(valueBitCount);
+ for (size_t i = 0; i < Min(bitmap.Mask.GetChunkCapacity(), Mask.GetChunkCapacity()); ++i)
+ Mask.Data[i] |= bitmap.Mask.Data[i];
+ }
+ return *this;
+ }
+
+ template <class T>
+ Y_FORCE_INLINE TThis& Or(const TBitMapOps<T>& bitmap) {
+ return Or(TThis(bitmap));
+ }
+
+ Y_FORCE_INLINE TThis& Or(const TChunk& val) {
+ Mask.Data[0] |= val;
+ Mask.Sanitize();
+ return *this;
+ }
+
+ TThis& Xor(const TThis& bitmap) {
+ Reserve(bitmap.Size());
+ for (size_t i = 0; i < bitmap.Mask.GetChunkCapacity(); ++i)
+ Mask.Data[i] ^= bitmap.Mask.Data[i];
+ return *this;
+ }
+
+ template <class T>
+ Y_FORCE_INLINE TThis& Xor(const TBitMapOps<T>& bitmap) {
+ return Xor(TThis(bitmap));
+ }
+
+ Y_FORCE_INLINE TThis& Xor(const TChunk& val) {
+ Mask.Data[0] ^= val;
+ Mask.Sanitize();
+ return *this;
+ }
+
+ TThis& SetDifference(const TThis& bitmap) {
+ for (size_t i = 0; i < Min(bitmap.Mask.GetChunkCapacity(), Mask.GetChunkCapacity()); ++i)
+ Mask.Data[i] &= ~bitmap.Mask.Data[i];
+ return *this;
+ }
+
+ template <class T>
+ Y_FORCE_INLINE TThis& SetDifference(const TBitMapOps<T>& bitmap) {
+ return SetDifference(TThis(bitmap));
+ }
+
+ Y_FORCE_INLINE TThis& SetDifference(const TChunk& val) {
+ Mask.Data[0] &= ~val;
+ return *this;
+ }
+
+ Y_FORCE_INLINE TThis& Flip() {
+ for (size_t i = 0; i < Mask.GetChunkCapacity(); ++i)
+ Mask.Data[i] = ~Mask.Data[i];
+ Mask.Sanitize();
+ return *this;
+ }
+
+ TThis& LShift(size_t shift) {
+ if (shift != 0) {
+ const size_t valueBitCount = ValueBitCount();
+ // Do nothing for empty bitmap
+ if (valueBitCount != 0) {
+ const size_t eshift = shift / BitsPerChunk;
+ const size_t offset = shift % BitsPerChunk;
+ const size_t subOffset = BitsPerChunk - offset;
+
+ // Don't verify expand result, so l-shift of fixed bitmap will work in the same way as for unsigned integer.
+ Mask.ExpandBitSize(valueBitCount + shift);
+
+ if (offset == 0) {
+ for (size_t i = Mask.GetChunkCapacity() - 1; i >= eshift; --i) {
+ Mask.Data[i] = Mask.Data[i - eshift];
+ }
+ } else {
+ for (size_t i = Mask.GetChunkCapacity() - 1; i > eshift; --i)
+ Mask.Data[i] = (Mask.Data[i - eshift] << offset) | (Mask.Data[i - eshift - 1] >> subOffset);
+ if (eshift < Mask.GetChunkCapacity())
+ Mask.Data[eshift] = Mask.Data[0] << offset;
+ }
+ for (size_t i = 0; i < Min(eshift, Mask.GetChunkCapacity()); ++i)
+ Mask.Data[i] = 0;
+
+ // Cleanup extra high bits in the storage
+ Mask.Sanitize();
+ }
+ }
+ return *this;
+ }
+
+ TThis& RShift(size_t shift) {
+ if (shift != 0) {
+ const size_t eshift = shift / BitsPerChunk;
+ const size_t offset = shift % BitsPerChunk;
+ if (eshift >= Mask.GetChunkCapacity()) {
+ Clear();
+
+ } else {
+ const size_t limit = Mask.GetChunkCapacity() - eshift - 1;
+
+ if (offset == 0) {
+ for (size_t i = 0; i <= limit; ++i) {
+ Mask.Data[i] = Mask.Data[i + eshift];
+ }
+ } else {
+ const size_t subOffset = BitsPerChunk - offset;
+ for (size_t i = 0; i < limit; ++i)
+ Mask.Data[i] = (Mask.Data[i + eshift] >> offset) | (Mask.Data[i + eshift + 1] << subOffset);
+ Mask.Data[limit] = Mask.Data[Mask.GetChunkCapacity() - 1] >> offset;
+ }
+
+ for (size_t i = limit + 1; i < Mask.GetChunkCapacity(); ++i)
+ Mask.Data[i] = 0;
+ }
+ }
+ return *this;
+ }
+
+ // Applies bitmap at the specified offset using OR operator.
+ // This method is optimized combination of Or() and LShift(), which allows reducing memory allocation
+ // when combining long dynamic bitmaps.
+ TThis& Or(const TThis& bitmap, size_t offset) {
+ if (0 == offset)
+ return Or(bitmap);
+
+ const size_t otherValueBitCount = bitmap.ValueBitCount();
+ // Continue only if OR-ed bitmap have non-zero bits
+ if (otherValueBitCount) {
+ const size_t chunkShift = offset / BitsPerChunk;
+ const size_t subShift = offset % BitsPerChunk;
+ const size_t subOffset = BitsPerChunk - subShift;
+
+ Reserve(otherValueBitCount + offset);
+
+ if (subShift == 0) {
+ for (size_t i = chunkShift; i < Min(bitmap.Mask.GetChunkCapacity() + chunkShift, Mask.GetChunkCapacity()); ++i) {
+ Mask.Data[i] |= bitmap.Mask.Data[i - chunkShift];
+ }
+ } else {
+ Mask.Data[chunkShift] |= bitmap.Mask.Data[0] << subShift;
+ size_t i = chunkShift + 1;
+ for (; i < Min(bitmap.Mask.GetChunkCapacity() + chunkShift, Mask.GetChunkCapacity()); ++i) {
+ Mask.Data[i] |= (bitmap.Mask.Data[i - chunkShift] << subShift) | (bitmap.Mask.Data[i - chunkShift - 1] >> subOffset);
+ }
+ if (i < Mask.GetChunkCapacity())
+ Mask.Data[i] |= bitmap.Mask.Data[i - chunkShift - 1] >> subOffset;
+ }
+ }
+
+ return *this;
+ }
+
+ bool Equal(const TThis& bitmap) const {
+ if (Mask.GetChunkCapacity() > bitmap.Mask.GetChunkCapacity()) {
+ for (size_t i = bitmap.Mask.GetChunkCapacity(); i < Mask.GetChunkCapacity(); ++i) {
+ if (0 != Mask.Data[i])
+ return false;
+ }
+ } else if (Mask.GetChunkCapacity() < bitmap.Mask.GetChunkCapacity()) {
+ for (size_t i = Mask.GetChunkCapacity(); i < bitmap.Mask.GetChunkCapacity(); ++i) {
+ if (0 != bitmap.Mask.Data[i])
+ return false;
+ }
+ }
+ size_t size = Min(Mask.GetChunkCapacity(), bitmap.Mask.GetChunkCapacity());
+ for (size_t i = 0; i < size; ++i) {
+ if (Mask.Data[i] != bitmap.Mask.Data[i])
+ return false;
+ }
+ return true;
+ }
+
+ template <class T>
+ Y_FORCE_INLINE bool Equal(const TBitMapOps<T>& bitmap) const {
+ return Equal(TThis(bitmap));
+ }
+
+ int Compare(const TThis& bitmap) const {
+ size_t size = Min(Mask.GetChunkCapacity(), bitmap.Mask.GetChunkCapacity());
+ int res = ::memcmp(Mask.Data, bitmap.Mask.Data, size * sizeof(TChunk));
+ if (0 != res || Mask.GetChunkCapacity() == bitmap.Mask.GetChunkCapacity())
+ return res;
+
+ if (Mask.GetChunkCapacity() > bitmap.Mask.GetChunkCapacity()) {
+ for (size_t i = bitmap.Mask.GetChunkCapacity(); i < Mask.GetChunkCapacity(); ++i) {
+ if (0 != Mask.Data[i])
+ return 1;
+ }
+ } else {
+ for (size_t i = Mask.GetChunkCapacity(); i < bitmap.Mask.GetChunkCapacity(); ++i) {
+ if (0 != bitmap.Mask.Data[i])
+ return -1;
+ }
+ }
+ return 0;
+ }
+
+ template <class T>
+ Y_FORCE_INLINE int Compare(const TBitMapOps<T>& bitmap) const {
+ return Compare(TThis(bitmap));
+ }
+
+ // For backward compatibility
+ Y_FORCE_INLINE static int Compare(const TThis& l, const TThis& r) {
+ return l.Compare(r);
+ }
+
+ size_t FirstNonZeroBit() const {
+ for (size_t i = 0; i < Mask.GetChunkCapacity(); ++i) {
+ if (Mask.Data[i]) {
+ // CountTrailingZeroBits() expects unsigned types not smaller than unsigned int. So, convert before calling
+ return BitsPerChunk * i + CountTrailingZeroBits(TIntType(Mask.Data[i]));
+ }
+ }
+ return Size();
+ }
+
+ // Returns position of the next non-zero bit, which offset is greater than specified pos
+ // Typical loop for iterating bits:
+ // for (size_t pos = bits.FirstNonZeroBit(); pos != bits.Size(); pos = bits.NextNonZeroBit(pos)) {
+ // ...
+ // }
+ // See Y_FOR_EACH_BIT macro definition at the bottom
+ size_t NextNonZeroBit(size_t pos) const {
+ size_t i = (pos + 1) >> DivCount;
+ if (i < Mask.GetChunkCapacity()) {
+ const size_t offset = (pos + 1) & ModMask;
+ // Process the current chunk
+ if (offset) {
+ // Zero already iterated trailing bits using mask
+ const TChunk val = Mask.Data[i] & ((~TChunk(0)) << offset);
+ if (val) {
+ return BitsPerChunk * i + CountTrailingZeroBits(TIntType(val));
+ }
+ // Continue with other chunks
+ ++i;
+ }
+
+ for (; i < Mask.GetChunkCapacity(); ++i) {
+ if (Mask.Data[i]) {
+ return BitsPerChunk * i + CountTrailingZeroBits(TIntType(Mask.Data[i]));
+ }
+ }
+ }
+ return Size();
+ }
+
+ Y_FORCE_INLINE size_t Count() const {
+ size_t count = 0;
+ for (size_t i = 0; i < Mask.GetChunkCapacity(); ++i)
+ count += ::NBitMapPrivate::CountBitsPrivate(Mask.Data[i]);
+ return count;
+ }
+
+ void Save(IOutputStream* out) const {
+ ::Save(out, ui8(sizeof(TChunk)));
+ ::Save(out, ui64(Size()));
+ ::SavePodArray(out, Mask.Data, Mask.GetChunkCapacity());
+ }
+
+ void Load(IInputStream* inp) {
+ ui8 chunkSize = 0;
+ ::Load(inp, chunkSize);
+ Y_VERIFY(size_t(chunkSize) == sizeof(TChunk), "Chunk size is not the same");
+
+ ui64 bitCount64 = 0;
+ ::Load(inp, bitCount64);
+ size_t bitCount = size_t(bitCount64);
+ Reserve(bitCount);
+
+ size_t chunkCount = 0;
+ if (bitCount > 0) {
+ chunkCount = ((bitCount - 1) >> DivCount) + 1;
+ ::LoadPodArray(inp, Mask.Data, chunkCount);
+ }
+
+ if (chunkCount < Mask.GetChunkCapacity()) {
+ ::memset(Mask.Data + chunkCount, 0, (Mask.GetChunkCapacity() - chunkCount) * sizeof(TChunk));
+ }
+ Mask.Sanitize();
+ }
+
+ inline size_t Hash() const {
+ THash<TChunk> chunkHasher;
+
+ size_t hash = chunkHasher(0);
+ bool tailSkipped = false;
+ for (size_t i = Mask.GetChunkCapacity(); i > 0; --i) {
+ if (tailSkipped || Mask.Data[i - 1]) {
+ hash = ::CombineHashes(hash, chunkHasher(Mask.Data[i - 1]));
+ tailSkipped = true;
+ }
+ }
+
+ return hash;
+ }
+
+ inline const TChunk* GetChunks() const {
+ return Mask.Data;
+ }
+
+ constexpr size_t GetChunkCount() const noexcept {
+ return Mask.GetChunkCapacity();
+ }
+};
+
+template <class X, class Y>
+inline TBitMapOps<X> operator&(const TBitMapOps<X>& x, const TBitMapOps<Y>& y) {
+ return TBitMapOps<X>(x).And(y);
+}
+
+template <class X>
+inline TBitMapOps<X> operator&(const TBitMapOps<X>& x, const typename TBitMapOps<X>::TChunk& y) {
+ return TBitMapOps<X>(x).And(y);
+}
+
+template <class X>
+inline TBitMapOps<X> operator&(const typename TBitMapOps<X>::TChunk& x, const TBitMapOps<X>& y) {
+ return TBitMapOps<X>(x).And(y);
+}
+
+template <class X, class Y>
+inline TBitMapOps<X> operator|(const TBitMapOps<X>& x, const TBitMapOps<Y>& y) {
+ return TBitMapOps<X>(x).Or(y);
+}
+
+template <class X>
+inline TBitMapOps<X> operator|(const TBitMapOps<X>& x, const typename TBitMapOps<X>::TChunk& y) {
+ return TBitMapOps<X>(x).Or(y);
+}
+
+template <class X>
+inline TBitMapOps<X> operator|(const typename TBitMapOps<X>::TChunk& x, const TBitMapOps<X>& y) {
+ return TBitMapOps<X>(x).Or(y);
+}
+
+template <class X, class Y>
+inline TBitMapOps<X> operator^(const TBitMapOps<X>& x, const TBitMapOps<Y>& y) {
+ return TBitMapOps<X>(x).Xor(y);
+}
+
+template <class X>
+inline TBitMapOps<X> operator^(const TBitMapOps<X>& x, const typename TBitMapOps<X>::TChunk& y) {
+ return TBitMapOps<X>(x).Xor(y);
+}
+
+template <class X>
+inline TBitMapOps<X> operator^(const typename TBitMapOps<X>::TChunk& x, const TBitMapOps<X>& y) {
+ return TBitMapOps<X>(x).Xor(y);
+}
+
+template <class X, class Y>
+inline TBitMapOps<X> operator-(const TBitMapOps<X>& x, const TBitMapOps<Y>& y) {
+ return TBitMapOps<X>(x).SetDifference(y);
+}
+
+template <class X>
+inline TBitMapOps<X> operator-(const TBitMapOps<X>& x, const typename TBitMapOps<X>::TChunk& y) {
+ return TBitMapOps<X>(x).SetDifference(y);
+}
+
+template <class X>
+inline TBitMapOps<X> operator-(const typename TBitMapOps<X>::TChunk& x, const TBitMapOps<X>& y) {
+ return TBitMapOps<X>(x).SetDifference(y);
+}
+
+template <class X>
+inline TBitMapOps<X> operator~(const TBitMapOps<X>& x) {
+ return TBitMapOps<X>(x).Flip();
+}
+
+/////////////////// Specialization ///////////////////////////
+
+template <size_t BitCount, typename TChunkType /*= ui64*/>
+class TBitMap: public TBitMapOps<TFixedBitMapTraits<BitCount, TChunkType>> {
+private:
+ using TBase = TBitMapOps<TFixedBitMapTraits<BitCount, TChunkType>>;
+
+public:
+ TBitMap()
+ : TBase()
+ {
+ }
+
+ TBitMap(typename TBase::TChunk val)
+ : TBase(val)
+ {
+ }
+
+ TBitMap(const TBitMap<BitCount, TChunkType>&) = default;
+
+ template <class T>
+ TBitMap(const TBitMapOps<T>& bitmap)
+ : TBase(bitmap)
+ {
+ }
+};
+
+using TDynBitMap = TBitMapOps<TDynamicBitMapTraits<ui64>>;
+
+#define Y_FOR_EACH_BIT(var, bitmap) for (size_t var = (bitmap).FirstNonZeroBit(); var != (bitmap).Size(); var = (bitmap).NextNonZeroBit(var))
+
+template <typename TTraits>
+struct THash<TBitMapOps<TTraits>> {
+ size_t operator()(const TBitMapOps<TTraits>& elem) const {
+ return elem.Hash();
+ }
+};