| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713 |
- //-------------------------------------------------------------------------------------------------------
- // Copyright (C) Microsoft. All rights reserved.
- // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
- //-------------------------------------------------------------------------------------------------------
- #pragma once
- #define FOREACH_BITSET_IN_FIXEDBV(index, bv) \
- { \
- BVIndex index; \
- for(JsUtil::FBVEnumerator _bvenum = bv->BeginSetBits(); \
- !_bvenum.End(); \
- _bvenum++) \
- { \
- index = _bvenum.GetCurrent(); \
- #define NEXT_BITSET_IN_FIXEDBV }}
- class BVFixed
- {
- // Data
- protected:
- BVIndex len;
- BVUnit data[];
- private:
- BVFixed(BVFixed * initBv);
- BVFixed(BVIndex length, bool initialSet = false);
- void ClearEnd();
- // Creation Factory
- public:
- template <typename TAllocator>
- static BVFixed * New(TAllocator* alloc, BVFixed * initBv);
- template <typename TAllocator>
- static BVFixed * New(DECLSPEC_GUARD_OVERFLOW BVIndex length, TAllocator* alloc, bool initialSet = false);
- template <typename TAllocator>
- static BVFixed * NewNoThrow(DECLSPEC_GUARD_OVERFLOW BVIndex length, TAllocator* alloc, bool initialSet = false);
- template <typename TAllocator>
- void Delete(TAllocator * alloc);
- // For preallocated memory
- static size_t GetAllocSize(BVIndex length);
- void Init(BVIndex length);
- // Implementation
- protected:
- void AssertRange(BVIndex i) const;
- void AssertBV(const BVFixed * bv) const;
- static BVIndex WordCount(BVIndex length);
- const BVUnit * BitsFromIndex(BVIndex i) const;
- BVUnit * BitsFromIndex(BVIndex i);
- const BVUnit * BeginUnit() const;
- BVUnit * BeginUnit();
- const BVUnit * EndUnit() const;
- BVUnit * EndUnit();
- template<class Fn>
- inline void for_each(const BVFixed *bv2, const Fn callback)
- {
- AssertMsg(this->len == bv2->len, "Fatal: The 2 bitvectors should have had the same length.");
- BVUnit * i;
- const BVUnit * j;
- for(i = this->BeginUnit(), j = bv2->BeginUnit();
- i != this->EndUnit() ;
- i++, j++)
- {
- (i->*callback)(*j);
- }
- }
- // Methods
- public:
- void Set(BVIndex i)
- {
- AssertRange(i);
- this->BitsFromIndex(i)->Set(BVUnit::Offset(i));
- }
- void Clear(BVIndex i)
- {
- AssertRange(i);
- this->BitsFromIndex(i)->Clear(BVUnit::Offset(i));
- }
- void Compliment(BVIndex i)
- {
- AssertRange(i);
- this->BitsFromIndex(i)->Complement(BVUnit::Offset(i));
- }
- BOOLEAN Test(BVIndex i) const
- {
- AssertRange(i);
- return this->BitsFromIndex(i)->Test(BVUnit::Offset(i));
- }
- BOOLEAN operator[](BVIndex i) const;
- BVIndex GetNextBit(BVIndex i) const;
- BOOLEAN TestAndSet(BVIndex i);
- BOOLEAN TestAndClear(BVIndex i);
- void OrComplimented(const BVFixed * bv);
- void Or(const BVFixed *bv);
- uint DiffCount(const BVFixed* bv) const;
- void And(const BVFixed *bv);
- void Minus(const BVFixed *bv);
- void Copy(const BVFixed *bv);
- void CopyBits(const BVFixed * bv, BVIndex i);
- void ComplimentAll();
- void SetAll();
- void ClearAll();
- BVIndex Count() const;
- BVIndex Length() const;
- JsUtil::FBVEnumerator BeginSetBits();
- BVIndex WordCount() const;
- bool IsAllClear() const;
- template<typename Container>
- Container GetRange(BVIndex start, BVIndex len) const;
- template<typename Container>
- void SetRange(Container* value, BVIndex start, BVIndex len);
- BVUnit* GetData() const
- {
- return (BVUnit*)data;
- }
- #if DBG_DUMP
- void Dump() const;
- #endif
- };
- template <typename TAllocator>
- BVFixed * BVFixed::New(TAllocator * alloc, BVFixed * initBv)
- {
- BVIndex length = initBv->Length();
- BVFixed *result = AllocatorNewPlusLeaf(TAllocator, alloc, sizeof(BVUnit) * BVFixed::WordCount(length), BVFixed, initBv);
- return result;
- }
- template <typename TAllocator>
- BVFixed * BVFixed::New(DECLSPEC_GUARD_OVERFLOW BVIndex length, TAllocator * alloc, bool initialSet)
- {
- BVFixed *result = AllocatorNewPlusLeaf(TAllocator, alloc, sizeof(BVUnit) * BVFixed::WordCount(length), BVFixed, length, initialSet);
- return result;
- }
- template <typename TAllocator>
- BVFixed * BVFixed::NewNoThrow(DECLSPEC_GUARD_OVERFLOW BVIndex length, TAllocator * alloc, bool initialSet)
- {
- BVFixed *result = AllocatorNewNoThrowNoRecoveryPlus(TAllocator, alloc, sizeof(BVUnit) * BVFixed::WordCount(length), BVFixed, length, initialSet);
- return result;
- }
- template <typename TAllocator>
- void BVFixed::Delete(TAllocator * alloc)
- {
- AllocatorDeletePlus(TAllocator, alloc, sizeof(BVUnit) * this->WordCount(), this);
- }
- template<typename Container>
- Container BVFixed::GetRange(BVIndex start, BVIndex len) const
- {
- AssertRange(start);
- if (len == 0)
- {
- return Container(0);
- }
- Assert(len <= sizeof(Container) * MachBits);
- AssertMsg(len <= 64, "Currently doesn't support range bigger than 64 bits");
- BVIndex end = start + len - 1;
- AssertRange(end);
- BVIndex iStart = BVUnit::Position(start);
- BVIndex iEnd = BVUnit::Position(end);
- BVIndex oStart = BVUnit::Offset(start);
- BVIndex oEnd = BVUnit::Offset(end);
- // Simply using uint64 because it is much easier than to juggle with BVUnit::BVUnitTContainer's size
- // Special case, if oEnd == 63, 1 << 64 == 1. Therefore the result is incorrect
- uint64 mask = oEnd < 63 ? (((uint64)1 << (oEnd + 1)) - 1) : 0xFFFFFFFFFFFFFFFF;
- uint64 range;
- // Trivial case
- if (iStart == iEnd)
- {
- // remove the bits after oEnd with mask, then remove the bits before start with shift
- range = (mask & this->data[iStart].GetWord()) >> oStart;
- }
- // Still simple enough
- else if (iStart + 1 == iEnd)
- {
- auto startWord = this->data[iStart].GetWord();
- auto endWord = this->data[iEnd].GetWord();
- // remove the bits before start with shift
- range = startWord >> oStart;
- // remove the bits after oEnd with mask then position it after start bits
- range |= (mask & endWord) << (BVUnit::BitsPerWord - oStart);
- }
- // Spans over multiple value, need to loop
- else
- {
- // Get the first bits and move them to the beginning
- range = this->data[iStart].GetWord() >> oStart;
- // track how many bits have been read so far
- int nBitsUsed = BVUnit::BitsPerWord - oStart;
- for (uint i = iStart + 1; i < iEnd; ++i)
- {
- // put all bits from the data in the mid-range. Use the tracked read bits to position them
- range |= ((uint64)(this->data[i].GetWord())) << nBitsUsed;
- nBitsUsed += BVUnit::BitsPerWord;
- }
- // Read the last bits and remove those after oEnd with mask
- range |= (mask & this->data[iEnd].GetWord()) << nBitsUsed;
- }
- return Container(range);
- }
- template<typename Container>
- #ifdef NO_SANITIZE_ADDRESS_FIXVC
- NO_SANITIZE_ADDRESS
- #endif
- void BVFixed::SetRange(Container* value, BVIndex start, BVIndex len)
- {
- AssertRange(start);
- if (len == 0)
- {
- return;
- }
- Assert(len <= sizeof(Container) * MachBits);
- BVIndex end = start + len - 1;
- AssertRange(end);
- BVIndex iStart = BVUnit::Position(start);
- BVIndex iEnd = BVUnit::Position(end);
- BVIndex oStart = BVUnit::Offset(start);
- BVIndex oEnd = BVUnit::Offset(end);
- BVUnit::BVUnitTContainer temp;
- BVUnit::BVUnitTContainer* bits;
- static_assert(sizeof(Container) == 1 || sizeof(Container) == sizeof(BVUnit::BVUnitTContainer),
- "Container is not suitable to represent the calculated value");
- if (sizeof(BVUnit::BVUnitTContainer) == 1)
- {
- temp = *((BVUnit::BVUnitTContainer*)value);
- bits = &temp;
- }
- else
- {
- bits = (BVUnit::BVUnitTContainer*)value;
- }
- const int oStartComplement = BVUnit::BitsPerWord - oStart;
- static_assert((BVUnit::BVUnitTContainer)BVUnit::AllOnesMask > 0, "Container type of BVFixed must be unsigned");
- //When making the mask, check the special case when we need all bits
- #define MAKE_MASK(start, end) ( ((end) == BVUnit::BitsPerWord ? BVUnit::AllOnesMask : (((BVUnit::BVUnitTContainer)1 << ((end) - (start))) - 1)) << (start))
- // Or the value to set the bits to 1. And the value to set the bits to 0
- // The mask is used to make sure we don't modify the bits outside the range
- #define SET_RANGE(i, value, mask) \
- this->data[i].Or((value) & mask);\
- this->data[i].And((value) | ~mask);
- BVUnit::BVUnitTContainer bitsToSet;
- // Fast Path
- if (iEnd == iStart)
- {
- const BVUnit::BVUnitTContainer mask = MAKE_MASK(oStart, oEnd + 1);
- // Shift to position the bits
- bitsToSet = (*bits << oStart);
- SET_RANGE(iStart, bitsToSet, mask);
- }
- // TODO: case iEnd == iStart + 1 to avoid a loop
- else if (oStart == 0)
- {
- // Simpler case where we don't have to shift the bits around
- for (uint i = iStart; i < iEnd; ++i)
- {
- SET_RANGE(i, *bits, BVUnit::AllOnesMask);
- ++bits;
- }
- // We still need to use a mask to remove the unused bits
- const BVUnit::BVUnitTContainer mask = MAKE_MASK(0, oEnd + 1);
- SET_RANGE(iEnd, *bits, mask);
- }
- else
- {
- // Default case. We need to process everything 1 at a time
- {
- // First set the first bits
- const BVUnit::BVUnitTContainer mask = MAKE_MASK(oStart, BVUnit::BitsPerWord);
- SET_RANGE(iStart, *bits << oStart, mask);
- }
- // Set the bits in the middle
- for (uint i = iStart + 1; i < iEnd; ++i)
- {
- bitsToSet = *bits >> oStartComplement;
- ++bits;
- bitsToSet |= *bits << oStart;
- SET_RANGE(i, bitsToSet, BVUnit::AllOnesMask);
- }
- // Set the last bits
- bitsToSet = *bits >> oStartComplement;
- ++bits;
- bitsToSet |= *bits << oStart;
- {
- const BVUnit::BVUnitTContainer mask = MAKE_MASK(0, oEnd + 1);
- SET_RANGE(iEnd, bitsToSet, mask);
- }
- }
- if (sizeof(Container) == 1)
- {
- // Calculation above might overflow the original container.
- // normalize the overflow value. LE only
- temp = (*((char*)bits)) + (*(((char*)bits) + 1));
- memcpy(value, bits, 1);
- }
- #undef MAKE_MASK
- #undef SET_RANGE
- }
- template <size_t bitCount>
- class BVStatic
- {
- public:
- // Made public to allow for compile-time use
- static const size_t wordCount = ((bitCount - 1) >> BVUnit::ShiftValue) + 1;
- // Data
- private:
- Field(BVUnit) data[wordCount];
- public:
- // Break on member changes. We rely on the layout of this class being static so we can
- // use initializer lists to generate collections of BVStatic.
- BVStatic()
- {
- Assert(sizeof(BVStatic<bitCount>) == sizeof(data));
- Assert((void*)this == (void*)&this->data);
- }
- // Implementation
- private:
- void AssertRange(BVIndex i) const { Assert(i < bitCount); }
- const BVUnit * BitsFromIndex(BVIndex i) const { AssertRange(i); return &this->data[BVUnit::Position(i)]; }
- BVUnit * BitsFromIndex(BVIndex i) { AssertRange(i); return &this->data[BVUnit::Position(i)]; }
- const BVUnit * BeginUnit() const { return &this->data[0]; }
- BVUnit * BeginUnit() { return &this->data[0]; }
- const BVUnit * EndUnit() const { return &this->data[wordCount]; }
- BVUnit * EndUnit() { return &this->data[wordCount]; }
- template<class Fn>
- inline void for_each(const BVStatic *bv2, const Fn callback)
- {
- BVUnit * i;
- const BVUnit * j;
- for(i = this->BeginUnit(), j = bv2->BeginUnit();
- i != this->EndUnit() ;
- i++, j++)
- {
- (i->*callback)(*j);
- }
- }
- template<class Fn>
- static bool MapUntil(const BVStatic *bv1, const BVStatic *bv2, const Fn callback)
- {
- const BVUnit * i;
- const BVUnit * j;
- for(i = bv1->BeginUnit(), j = bv2->BeginUnit();
- i != bv1->EndUnit() ;
- i++, j++)
- {
- if (!callback(*i, *j))
- {
- return false;
- }
- }
- return true;
- }
- void ClearEnd()
- {
- uint offset = BVUnit::Offset(bitCount);
- if (offset != 0)
- {
- this->data[wordCount - 1].And((1 << offset) - 1);
- }
- }
- // Methods
- public:
- void Set(BVIndex i)
- {
- AssertRange(i);
- this->BitsFromIndex(i)->Set(BVUnit::Offset(i));
- }
- void Clear(BVIndex i)
- {
- AssertRange(i);
- this->BitsFromIndex(i)->Clear(BVUnit::Offset(i));
- }
- void Compliment(BVIndex i)
- {
- AssertRange(i);
- this->BitsFromIndex(i)->Complement(BVUnit::Offset(i));
- }
- BOOLEAN Equal(BVStatic<bitCount> const * o)
- {
- return MapUntil(this, o, [](BVUnit const& i, BVUnit const &j) { return i.Equal(j); });
- }
- BOOLEAN Test(BVIndex i) const
- {
- AssertRange(i);
- return this->BitsFromIndex(i)->Test(BVUnit::Offset(i));
- }
- BOOLEAN TestAndSet(BVIndex i)
- {
- AssertRange(i);
- return PlatformAgnostic::_BitTestAndSet((LONG *)this->data, (LONG) i);
- }
- BOOLEAN TestIntrinsic(BVIndex i) const
- {
- AssertRange(i);
- return PlatformAgnostic::_BitTest((LONG *)this->data, (LONG) i);
- }
- BOOLEAN TestAndSetInterlocked(BVIndex i)
- {
- AssertRange(i);
- return PlatformAgnostic::_InterlockedBitTestAndSet((LONG *)this->data, (LONG) i);
- }
- BOOLEAN TestAndClear(BVIndex i)
- {
- AssertRange(i);
- BVUnit * bvUnit = this->BitsFromIndex(i);
- BVIndex offset = BVUnit::Offset(i);
- BOOLEAN bit = bvUnit->Test(offset);
- bvUnit->Clear(offset);
- return bit;
- }
- BOOLEAN TestAndClearInterlocked(BVIndex i)
- {
- AssertRange(i);
- return PlatformAgnostic::_InterlockedBitTestAndReset((LONG *)this->data, (LONG)i);
- }
- void OrComplimented(const BVStatic * bv) { this->for_each(bv, &BVUnit::OrComplimented); ClearEnd(); }
- void Or(const BVStatic *bv) { this->for_each(bv, &BVUnit::Or); }
- void And(const BVStatic *bv) { this->for_each(bv, &BVUnit::And); }
- void Minus(const BVStatic *bv) { this->for_each(bv, &BVUnit::Minus); }
- void Copy(const BVStatic *bv) { js_memcpy_s(&this->data[0], wordCount * sizeof(BVUnit), &bv->data[0], wordCount * sizeof(BVUnit)); }
- void SetAll() { memset(&this->data[0], -1, wordCount * sizeof(BVUnit)); ClearEnd(); }
- void ClearAll() { memset(&this->data[0], 0, wordCount * sizeof(BVUnit)); }
- void ComplimentAll()
- {
- for (BVIndex i = 0; i < wordCount; i++)
- {
- this->data[i].ComplimentAll();
- }
- ClearEnd();
- }
- BVIndex Count() const
- {
- BVIndex sum = 0;
- for (BVIndex i = 0; i < wordCount; i++)
- {
- sum += this->data[i].Count();
- }
- Assert(sum <= bitCount);
- return sum;
- }
- BVIndex Length() const
- {
- return bitCount;
- }
- JsUtil::FBVEnumerator BeginSetBits() { return JsUtil::FBVEnumerator(this->BeginUnit(), this->EndUnit()); }
- BVIndex GetNextBit(BVIndex i) const
- {
- AssertRange(i);
- const BVUnit * chunk = BitsFromIndex(i);
- BVIndex base = BVUnit::Floor(i);
- BVIndex offset = chunk->GetNextBit(BVUnit::Offset(i));
- if (-1 != offset)
- {
- return base + offset;
- }
- while (++chunk != this->EndUnit())
- {
- base += BVUnit::BitsPerWord;
- offset = chunk->GetNextBit();
- if (-1 != offset)
- {
- return base + offset;
- }
- }
- return BVInvalidIndex;
- }
- const BVUnit * GetRawData() const { return data; }
- template <size_t rangeSize>
- BVStatic<rangeSize> * GetRange(BVIndex startOffset) const
- {
- AssertRange(startOffset);
- AssertRange(startOffset + rangeSize - 1);
- // Start offset and size must be word-aligned
- Assert(BVUnit::Offset(startOffset) == 0);
- Assert(BVUnit::Offset(rangeSize) == 0);
- return (BVStatic<rangeSize> *)BitsFromIndex(startOffset);
- }
- BOOLEAN TestRange(const BVIndex index, uint length) const
- {
- AssertRange(index);
- AssertRange(index + length - 1);
- const BVUnit * bvUnit = BitsFromIndex(index);
- uint offset = BVUnit::Offset(index);
- if (offset + length <= BVUnit::BitsPerWord)
- {
- // Bit range is in a single word
- return bvUnit->TestRange(offset, length);
- }
- // Bit range spans words.
- // Test the first word, from start offset to end of word
- if (!bvUnit->TestRange(offset, (BVUnit::BitsPerWord - offset)))
- {
- return FALSE;
- }
- bvUnit++;
- length -= (BVUnit::BitsPerWord - offset);
- // Test entire words until we are at the last word
- while (length >= BVUnit::BitsPerWord)
- {
- if (!bvUnit->IsFull())
- {
- return FALSE;
- }
- bvUnit++;
- length -= BVUnit::BitsPerWord;
- }
- // Test last word (unless we already ended on a word boundary)
- if (length > 0)
- {
- if (!bvUnit->TestRange(0, length))
- {
- return FALSE;
- }
- }
- return TRUE;
- }
- void SetRange(const BVIndex index, uint length)
- {
- AssertRange(index);
- AssertRange(index + length - 1);
- BVUnit * bvUnit = BitsFromIndex(index);
- uint offset = BVUnit::Offset(index);
- if (offset + length <= BVUnit::BitsPerWord)
- {
- // Bit range is in a single word
- return bvUnit->SetRange(offset, length);
- }
- // Bit range spans words.
- // Set the first word, from start offset to end of word
- bvUnit->SetRange(offset, (BVUnit::BitsPerWord - offset));
- bvUnit++;
- length -= (BVUnit::BitsPerWord - offset);
- // Set entire words until we are at the last word
- while (length >= BVUnit::BitsPerWord)
- {
- bvUnit->SetAll();
- bvUnit++;
- length -= BVUnit::BitsPerWord;
- }
- // Set last word (unless we already ended on a word boundary)
- if (length > 0)
- {
- bvUnit->SetRange(0, length);
- }
- }
- void ClearRange(const BVIndex index, uint length)
- {
- AssertRange(index);
- AssertRange(index + length - 1);
- BVUnit * bvUnit = BitsFromIndex(index);
- uint offset = BVUnit::Offset(index);
- if (offset + length <= BVUnit::BitsPerWord)
- {
- // Bit range is in a single word
- return bvUnit->ClearRange(offset, length);
- }
- // Bit range spans words.
- // Clear the first word, from start offset to end of word
- bvUnit->ClearRange(offset, (BVUnit::BitsPerWord - offset));
- bvUnit++;
- length -= (BVUnit::BitsPerWord - offset);
- // Set entire words until we are at the last word
- while (length >= BVUnit::BitsPerWord)
- {
- bvUnit->ClearAll();
- bvUnit++;
- length -= BVUnit::BitsPerWord;
- }
- // Set last word (unless we already ended on a word boundary)
- if (length > 0)
- {
- bvUnit->ClearRange(0, length);
- }
- }
- bool IsAllClear()
- {
- for (BVIndex i = 0; i < wordCount; i++)
- {
- if (!this->data[i].IsEmpty())
- {
- return false;
- }
- }
- return true;
- }
- bool IsAllSet() const
- {
- for (BVIndex i = 0; i < this->wordCount; i++)
- {
- if (i == this->wordCount - 1 && Length() % BVUnit::BitsPerWord != 0)
- {
- return this->data[i].Count() == Length() % BVUnit::BitsPerWord;
- }
- else if (!this->data[i].IsFull())
- {
- return false;
- }
- }
- return true;
- }
- #if DBG_DUMP
- void Dump() const
- {
- bool hasBits = false;
- Output::Print(_u("[ "));
- for (BVIndex i = 0; i < wordCount; i++)
- {
- hasBits = this->data[i].Dump(i * BVUnit::BitsPerWord, hasBits);
- }
- Output::Print(_u("]\n"));
- }
- #endif
- };
|