mirror of
https://github.com/revng/revng
synced 2026-06-21 14:07:57 +00:00
724 lines
19 KiB
C++
724 lines
19 KiB
C++
#pragma once
|
|
|
|
//
|
|
// This file is distributed under the MIT License. See LICENSE.md for details.
|
|
//
|
|
|
|
#include <climits>
|
|
#include <cstdint>
|
|
#include <cstring>
|
|
#include <limits>
|
|
|
|
#include "boost/iterator/iterator_facade.hpp"
|
|
|
|
#include "revng/Support/Assert.h"
|
|
|
|
// TODO: implement shrinking
|
|
|
|
// TODO: implement using __builtin_clz if available
|
|
/// \brief Returns the minimum amount of bits required to represent \p Value
|
|
template<typename T>
|
|
inline unsigned requiredBits(T Value) {
|
|
unsigned Result = 0;
|
|
|
|
while (Value != 0) {
|
|
Result++;
|
|
Value = Value >> 1;
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
template<typename T, typename A, typename B>
|
|
concept is_either = std::is_same_v<T, A> or std::is_same_v<T, B>;
|
|
|
|
template<typename IntT>
|
|
requires is_either<IntT, unsigned, int>
|
|
inline unsigned findFirstBit(IntT Value) {
|
|
return ffs(Value);
|
|
}
|
|
|
|
template<typename LongT>
|
|
requires is_either<LongT, unsigned long, long>
|
|
inline unsigned findFirstBit(LongT Value) {
|
|
return ffsl(Value);
|
|
}
|
|
|
|
template<typename LongLongT>
|
|
requires is_either<LongLongT, unsigned long long, long long>
|
|
inline unsigned findFirstBit(LongLongT Value) {
|
|
return ffsll(Value);
|
|
}
|
|
|
|
template<typename T>
|
|
inline unsigned findFirstBit(T Value) {
|
|
return findFirstBit<T>(Value);
|
|
}
|
|
|
|
template<typename T>
|
|
inline T excessDivide(T A, unsigned B) {
|
|
return (A + (B - 1)) / B;
|
|
}
|
|
|
|
class LazySmallBitVector;
|
|
|
|
template<typename LSBV>
|
|
class LazySmallBitVectorIterator
|
|
: public boost::iterator_facade<LazySmallBitVectorIterator<LSBV>,
|
|
unsigned,
|
|
boost::forward_traversal_tag,
|
|
unsigned> {
|
|
public:
|
|
LazySmallBitVectorIterator() : BitVector(nullptr), NextBitIndex(0) {}
|
|
LazySmallBitVectorIterator(LSBV *BitVector);
|
|
LazySmallBitVectorIterator(LSBV *BitVector, unsigned Index);
|
|
|
|
private:
|
|
void increment();
|
|
|
|
bool equal(LazySmallBitVectorIterator const &Other) const {
|
|
return BitVector == Other.BitVector && NextBitIndex == Other.NextBitIndex;
|
|
}
|
|
|
|
unsigned dereference() const {
|
|
revng_assert(BitVector != nullptr && NextBitIndex != 0);
|
|
return NextBitIndex - 1;
|
|
}
|
|
|
|
private:
|
|
friend class boost::iterator_core_access;
|
|
|
|
LSBV *BitVector;
|
|
unsigned NextBitIndex;
|
|
};
|
|
|
|
/// \brief Infinite zero-initialized BitVector, automatically enlarging and
|
|
/// in-place up to sizeof(uintptr_t) * CHAR_BIT - 1 bits
|
|
class LazySmallBitVector {
|
|
public:
|
|
using const_iterator = LazySmallBitVectorIterator<const LazySmallBitVector>;
|
|
using iterator = LazySmallBitVectorIterator<LazySmallBitVector>;
|
|
typedef bool value_type;
|
|
|
|
private:
|
|
static const unsigned BitsPerPointer = sizeof(uintptr_t) * CHAR_BIT;
|
|
static const unsigned MaxSmallSize = BitsPerPointer - 1;
|
|
static const uintptr_t One = 1;
|
|
static const unsigned IntMax = std::numeric_limits<int32_t>::max();
|
|
|
|
struct LargeStorage {
|
|
unsigned wordCount() const { return Capacity / BitsPerPointer; }
|
|
unsigned capacity() const { return Capacity; }
|
|
|
|
unsigned requiredBits() const {
|
|
for (signed I = wordCount() - 1; I >= 0; I--)
|
|
if (at(I) != 0)
|
|
return BitsPerPointer * I + ::requiredBits(at(I));
|
|
|
|
return 0;
|
|
}
|
|
|
|
uintptr_t &at(size_t Index) {
|
|
revng_assert(Index < wordCount());
|
|
return Storage[Index];
|
|
}
|
|
|
|
const uintptr_t &at(size_t Index) const {
|
|
revng_assert(Index < wordCount());
|
|
return Storage[Index];
|
|
}
|
|
|
|
void zero(size_t From, size_t Count) {
|
|
revng_assert(From + Count <= wordCount());
|
|
memset(&at(From), 0, Count * sizeof(uintptr_t));
|
|
}
|
|
|
|
void zero(size_t From) { zero(From, wordCount()); }
|
|
|
|
void zero() { zero(0); }
|
|
|
|
void setCapacity(size_t Count) { Capacity = Count; }
|
|
|
|
LargeStorage &operator=(const LargeStorage &Other) {
|
|
revng_assert(Capacity >= Other.Capacity);
|
|
memcpy(&at(0), &Other.at(0), Other.Capacity / sizeof(uintptr_t));
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
size_t Capacity;
|
|
uintptr_t Storage[1];
|
|
};
|
|
|
|
public:
|
|
LazySmallBitVector() : Storage(1) {}
|
|
|
|
LazySmallBitVector(const LazySmallBitVector &Other) : Storage(1) {
|
|
*this = Other;
|
|
}
|
|
|
|
LazySmallBitVector(LazySmallBitVector &&Other) : Storage(Other.Storage) {
|
|
Other.Storage = 1;
|
|
}
|
|
|
|
bool isSmall() const { return Storage & 1; }
|
|
|
|
void reserve(unsigned Size) {
|
|
if (Size + 1 >= capacity())
|
|
alloc(Size);
|
|
}
|
|
|
|
void set(unsigned Index) {
|
|
revng_assert(Index < IntMax);
|
|
|
|
if (Index >= capacity())
|
|
alloc(Index + 1);
|
|
|
|
if (isSmall()) {
|
|
revng_assert(Index < MaxSmallSize);
|
|
Storage = Storage | (One << (Index + 1));
|
|
} else {
|
|
uintptr_t &Target = getLarge().at(Index / BitsPerPointer);
|
|
Target = Target | (One << (Index % BitsPerPointer));
|
|
}
|
|
}
|
|
|
|
void unset(unsigned Index) {
|
|
revng_assert(Index < IntMax);
|
|
|
|
if (Index >= capacity())
|
|
return;
|
|
|
|
if (isSmall()) {
|
|
revng_assert(Index < MaxSmallSize);
|
|
Storage = Storage & ~(One << (Index + 1));
|
|
} else {
|
|
uintptr_t &Target = getLarge().at(Index / BitsPerPointer);
|
|
Target = Target & ~(One << (Index % BitsPerPointer));
|
|
}
|
|
}
|
|
|
|
void zero(size_t From, size_t Count) {
|
|
if (From >= capacity() || From + Count > capacity())
|
|
return;
|
|
|
|
if (isSmall()) {
|
|
setSmall(getSmall() & ~(((One << Count) - 1) << From));
|
|
} else {
|
|
LargeStorage &Large = getLarge();
|
|
|
|
// Blank leading bits
|
|
unsigned Head = BitsPerPointer - (From % BitsPerPointer);
|
|
unsigned HeadComplement = From % BitsPerPointer;
|
|
if (HeadComplement != 0) {
|
|
uintptr_t Mask = ~(((One << Head) - 1) << HeadComplement);
|
|
Large.at(From / BitsPerPointer) &= Mask;
|
|
From += Head;
|
|
Count -= Head;
|
|
revng_assert(From % BitsPerPointer == 0);
|
|
}
|
|
|
|
// Blank trailing bits
|
|
unsigned Tail = Count % BitsPerPointer;
|
|
if (Tail != 0) {
|
|
Large.at((From + Count) / BitsPerPointer) &= ~((One << Tail) - 1);
|
|
Count -= Tail;
|
|
revng_assert(Count % BitsPerPointer == 0);
|
|
}
|
|
|
|
if (Count != 0)
|
|
Large.zero(From / BitsPerPointer, Count / BitsPerPointer);
|
|
}
|
|
}
|
|
|
|
void zero(size_t From) { zero(From, capacity() - From); }
|
|
|
|
void zero() { zero(0); }
|
|
|
|
bool operator[](unsigned Index) const {
|
|
if (Index >= capacity())
|
|
return false;
|
|
|
|
if (isSmall()) {
|
|
return (Storage >> (Index + 1)) & 1;
|
|
} else {
|
|
uintptr_t Value = getLarge().at(Index / BitsPerPointer);
|
|
return (Value >> (Index % BitsPerPointer)) & 1;
|
|
}
|
|
}
|
|
|
|
/// \brief The bits required to represent this bit vector
|
|
///
|
|
/// This is the index of the most significant set bit, plus 1. 0 means the bit
|
|
/// vector is composed exclusively by zeros.
|
|
unsigned requiredBits() const {
|
|
if (isSmall()) {
|
|
uintptr_t Value = getSmall();
|
|
return ::requiredBits(Value);
|
|
} else {
|
|
return getLarge().requiredBits();
|
|
}
|
|
}
|
|
|
|
bool isZero() const { return requiredBits() == 0; }
|
|
|
|
LazySmallBitVector &operator=(const LazySmallBitVector &Other) {
|
|
if (!(Other.isSmall() || Other.capacity() > 63))
|
|
revng_abort();
|
|
|
|
if (Other.isSmall()) {
|
|
Storage = Other.Storage;
|
|
} else {
|
|
if (Other.capacity() > capacity())
|
|
alloc(Other.capacity());
|
|
|
|
getLarge() = Other.getLarge();
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
LazySmallBitVector &operator=(LazySmallBitVector &&Other) {
|
|
if (!isSmall())
|
|
free(&getLarge());
|
|
|
|
Storage = Other.Storage;
|
|
|
|
Other.Storage = 1;
|
|
|
|
return *this;
|
|
}
|
|
|
|
bool operator==(const LazySmallBitVector &Other) const {
|
|
if (Storage == Other.Storage)
|
|
return true;
|
|
|
|
if (isSmall() && Other.isSmall())
|
|
return false;
|
|
|
|
if (!isSmall() && !Other.isSmall()) {
|
|
const LargeStorage &OtherLarge = Other.getLarge();
|
|
const LargeStorage &ThisLarge = getLarge();
|
|
|
|
unsigned Max = std::min(ThisLarge.capacity(), OtherLarge.capacity());
|
|
Max /= BitsPerPointer;
|
|
|
|
for (unsigned I = 0; I < Max; I++)
|
|
if (ThisLarge.at(I) != OtherLarge.at(I))
|
|
return false;
|
|
|
|
if (ThisLarge.capacity() > OtherLarge.capacity()) {
|
|
for (unsigned I = Max; I < ThisLarge.wordCount(); I++)
|
|
if (ThisLarge.at(I) != 0)
|
|
return false;
|
|
} else {
|
|
for (unsigned I = Max; I < OtherLarge.wordCount(); I++)
|
|
if (OtherLarge.at(I) != 0)
|
|
return false;
|
|
}
|
|
|
|
} else if (!isSmall() && Other.isSmall()) {
|
|
const LargeStorage &ThisLarge = getLarge();
|
|
if (ThisLarge.at(0) != Other.getSmall())
|
|
return false;
|
|
|
|
for (unsigned I = 1; I < ThisLarge.wordCount(); I++)
|
|
if (ThisLarge.at(I) != 0)
|
|
return false;
|
|
} else if (isSmall() && !Other.isSmall()) {
|
|
const LargeStorage &OtherLarge = Other.getLarge();
|
|
if (OtherLarge.at(0) != getSmall())
|
|
return false;
|
|
|
|
for (unsigned I = 1; I < OtherLarge.wordCount(); I++)
|
|
if (OtherLarge.at(I) != 0)
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool operator!=(const LazySmallBitVector &Other) const {
|
|
return !(*this == Other);
|
|
}
|
|
|
|
bool operator<(const LazySmallBitVector &Other) const {
|
|
if (isSmall() && Other.isSmall()) {
|
|
return Storage < Other.Storage;
|
|
} else if (!isSmall() && !Other.isSmall()) {
|
|
const LargeStorage &OtherLarge = Other.getLarge();
|
|
const LargeStorage &ThisLarge = getLarge();
|
|
|
|
signed Max = std::min(ThisLarge.capacity(), OtherLarge.capacity());
|
|
Max /= BitsPerPointer;
|
|
|
|
if (ThisLarge.capacity() > OtherLarge.capacity()) {
|
|
for (signed I = ThisLarge.wordCount() - 1; I >= Max; I--)
|
|
if (ThisLarge.at(I) != 0)
|
|
return false;
|
|
} else {
|
|
for (signed I = OtherLarge.wordCount() - 1; I >= Max; I--)
|
|
if (OtherLarge.at(I) != 0)
|
|
return true;
|
|
}
|
|
|
|
for (signed I = Max - 1; I >= 0; I--)
|
|
if (ThisLarge.at(I) != OtherLarge.at(I))
|
|
return ThisLarge.at(I) < OtherLarge.at(I);
|
|
|
|
return false;
|
|
|
|
} else if (!isSmall() && Other.isSmall()) {
|
|
const LargeStorage &ThisLarge = getLarge();
|
|
for (signed I = ThisLarge.wordCount() - 1; I >= 1; I--)
|
|
if (ThisLarge.at(I) != 0)
|
|
return false;
|
|
|
|
return ThisLarge.at(0) < Other.getSmall();
|
|
|
|
} else if (isSmall() && !Other.isSmall()) {
|
|
const LargeStorage &OtherLarge = Other.getLarge();
|
|
for (signed I = OtherLarge.wordCount() - 1; I >= 1; I--)
|
|
if (OtherLarge.at(I) != 0)
|
|
return true;
|
|
|
|
return getSmall() < OtherLarge.at(0);
|
|
}
|
|
|
|
revng_abort();
|
|
}
|
|
|
|
LazySmallBitVector &operator^=(const LazySmallBitVector &Other) {
|
|
// Ensure we have at least the same capacity as Other
|
|
if (Other.capacity() > this->capacity())
|
|
alloc(Other.capacity());
|
|
|
|
// This situation should never happen, since we just ensured we have at
|
|
// least the same capacity
|
|
revng_assert(!(isSmall() && !Other.isSmall()));
|
|
|
|
if (isSmall() && Other.isSmall()) {
|
|
Storage = (Storage ^ Other.Storage) | 1;
|
|
} else if (!isSmall() && !Other.isSmall()) {
|
|
const LargeStorage &OtherLarge = Other.getLarge();
|
|
LargeStorage &ThisLarge = getLarge();
|
|
|
|
unsigned Max = std::min(ThisLarge.capacity(), OtherLarge.capacity());
|
|
Max /= BitsPerPointer;
|
|
for (unsigned I = 0; I < Max; I++)
|
|
ThisLarge.at(I) = ThisLarge.at(I) ^ OtherLarge.at(I);
|
|
|
|
} else if (!isSmall() && Other.isSmall()) {
|
|
LargeStorage &ThisLarge = getLarge();
|
|
ThisLarge.at(0) = ThisLarge.at(0) ^ Other.getSmall();
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
LazySmallBitVector &operator|=(const LazySmallBitVector &Other) {
|
|
// Ensure we have at least the same capacity as Other
|
|
if (Other.capacity() > this->capacity())
|
|
alloc(Other.capacity());
|
|
|
|
// This situation should never happen, since we just ensured we have at
|
|
// least the same capacity
|
|
revng_assert(!(isSmall() && !Other.isSmall()));
|
|
|
|
if (isSmall() && Other.isSmall()) {
|
|
Storage = Storage | Other.Storage;
|
|
} else if (!isSmall() && !Other.isSmall()) {
|
|
const LargeStorage &OtherLarge = Other.getLarge();
|
|
LargeStorage &ThisLarge = getLarge();
|
|
|
|
unsigned Max = std::min(ThisLarge.capacity(), OtherLarge.capacity());
|
|
Max /= BitsPerPointer;
|
|
|
|
for (unsigned I = 0; I < Max; I++)
|
|
ThisLarge.at(I) = ThisLarge.at(I) | OtherLarge.at(I);
|
|
|
|
} else if (!isSmall() && Other.isSmall()) {
|
|
LargeStorage &ThisLarge = getLarge();
|
|
ThisLarge.at(0) = ThisLarge.at(0) | Other.getSmall();
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
LazySmallBitVector &operator&=(const LazySmallBitVector &Other) {
|
|
if (isSmall()) {
|
|
uintptr_t OtherValue;
|
|
if (Other.isSmall())
|
|
OtherValue = Other.getSmall();
|
|
else
|
|
OtherValue = Other.getLarge().at(0);
|
|
|
|
setSmall(getSmall() & OtherValue);
|
|
} else {
|
|
LargeStorage &Large = getLarge();
|
|
size_t ThisPointersCount = Large.wordCount();
|
|
|
|
if (Other.isSmall()) {
|
|
// We have to discard everything except the first uintptr_t
|
|
if (ThisPointersCount > 1) {
|
|
// Zero out all the uintptr_t after the first one
|
|
Large.zero(1, ThisPointersCount - 1);
|
|
}
|
|
|
|
Large.at(0) = Large.at(0) & Other.getSmall();
|
|
} else {
|
|
const LargeStorage &OtherLarge = Other.getLarge();
|
|
size_t OtherPointersCount = OtherLarge.wordCount();
|
|
|
|
if (ThisPointersCount > OtherPointersCount) {
|
|
// Zero out all the uintptr_t after OtherPointersCount
|
|
Large.zero(OtherPointersCount,
|
|
ThisPointersCount - OtherPointersCount);
|
|
}
|
|
|
|
unsigned Max = std::min(OtherPointersCount, ThisPointersCount);
|
|
for (unsigned I = 0; I < Max; I++)
|
|
Large.at(I) = Large.at(I) & OtherLarge.at(I);
|
|
}
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
LazySmallBitVector &operator>>=(unsigned Amount) {
|
|
revng_assert(Amount <= capacity());
|
|
|
|
if (isSmall()) {
|
|
Storage >>= Amount;
|
|
Storage |= 1;
|
|
return *this;
|
|
}
|
|
|
|
LargeStorage &Large = getLarge();
|
|
|
|
if (Amount == Large.capacity()) {
|
|
Large.zero();
|
|
return *this;
|
|
}
|
|
|
|
unsigned SourceIndex = Amount / BitsPerPointer;
|
|
unsigned Count = Large.wordCount() - SourceIndex;
|
|
|
|
auto Destination = [&Large](unsigned I) -> uintptr_t & {
|
|
return Large.at(I);
|
|
};
|
|
|
|
auto Source = [SourceIndex, &Large](unsigned I) -> uintptr_t & {
|
|
return Large.at(SourceIndex + I);
|
|
};
|
|
|
|
unsigned Bits = Amount % BitsPerPointer;
|
|
unsigned OtherBits = BitsPerPointer - Bits;
|
|
|
|
// Shift all the word except the last one
|
|
unsigned I = 0;
|
|
for (; I < Count - 1; I++)
|
|
Destination(I) = Source(I) >> Bits | (Source(I + 1) << OtherBits);
|
|
|
|
Destination(I) = Source(I) >> Bits;
|
|
|
|
if (SourceIndex > 0)
|
|
Large.zero(I + 1);
|
|
|
|
return *this;
|
|
}
|
|
|
|
LazySmallBitVector &operator<<=(unsigned Amount) {
|
|
// Get the current amount of bits
|
|
unsigned RequiredBits = requiredBits();
|
|
|
|
if (isSmall()) {
|
|
unsigned NewSize = RequiredBits + Amount;
|
|
|
|
if (NewSize <= BitsPerPointer) {
|
|
// We fit where we are
|
|
Storage = Storage & ~1;
|
|
Storage <<= Amount;
|
|
Storage |= 1;
|
|
return *this;
|
|
} else {
|
|
// We have to enlarge
|
|
alloc(NewSize);
|
|
}
|
|
}
|
|
|
|
LargeStorage &Large = getLarge();
|
|
|
|
// Is it all zeros? Do nothing.
|
|
if (RequiredBits == 0)
|
|
return *this;
|
|
|
|
// Enlarge, if required
|
|
RequiredBits += Amount;
|
|
if (RequiredBits > Large.capacity())
|
|
alloc(RequiredBits + 1);
|
|
|
|
LargeStorage &NewLarge = getLarge();
|
|
|
|
unsigned ToSkip = Amount / BitsPerPointer;
|
|
auto Destination = [ToSkip, &NewLarge](unsigned I) -> uintptr_t & {
|
|
return NewLarge.at(ToSkip + I);
|
|
};
|
|
|
|
auto Source = [&NewLarge](unsigned I) -> uintptr_t & {
|
|
return NewLarge.at(I);
|
|
};
|
|
|
|
unsigned Bits = Amount % BitsPerPointer;
|
|
unsigned OtherBits = BitsPerPointer - Bits;
|
|
for (signed I = NewLarge.wordCount() - 1 - ToSkip; I >= 0 + 1; I--)
|
|
Destination(I) = (Source(I) << Bits) | (Source(I - 1) >> OtherBits);
|
|
|
|
Destination(0) = Source(0) << Bits;
|
|
|
|
if (ToSkip > 0)
|
|
NewLarge.zero(0, ToSkip);
|
|
|
|
return *this;
|
|
}
|
|
|
|
/// \brief Returns the 1-based index of the next set bit after \p StartIndex
|
|
///
|
|
/// \return 0 if no bits are set after \p StartIndex, the 1-based index of the
|
|
/// next bit set otherwise
|
|
unsigned findNext(unsigned StartIndex) const {
|
|
if (StartIndex >= requiredBits())
|
|
return 0;
|
|
|
|
if (isSmall()) {
|
|
return StartIndex + findFirstBit(getSmall() >> StartIndex);
|
|
} else {
|
|
const LargeStorage &Large = getLarge();
|
|
unsigned Index = StartIndex / BitsPerPointer;
|
|
unsigned ShiftAmount = StartIndex % BitsPerPointer;
|
|
|
|
uintptr_t FirstValue = Large.at(Index) >> ShiftAmount;
|
|
if (FirstValue != 0)
|
|
return StartIndex + findFirstBit(FirstValue);
|
|
|
|
do {
|
|
Index++;
|
|
if (Index * BitsPerPointer >= capacity())
|
|
return 0;
|
|
} while (Large.at(Index) == 0);
|
|
|
|
return Index * BitsPerPointer + findFirstBit(Large.at(Index));
|
|
}
|
|
}
|
|
|
|
const_iterator begin() const { return const_iterator(this); }
|
|
const_iterator end() const { return const_iterator(this, 0); }
|
|
|
|
iterator begin() { return iterator(this); }
|
|
iterator end() { return iterator(this, 0); }
|
|
|
|
~LazySmallBitVector() {
|
|
if (!isSmall())
|
|
free(&getLarge());
|
|
Storage = 0;
|
|
}
|
|
|
|
private:
|
|
friend iterator;
|
|
friend const_iterator;
|
|
|
|
uintptr_t getSmall() const {
|
|
revng_assert(isSmall());
|
|
return Storage >> 1;
|
|
}
|
|
|
|
void setSmall(uintptr_t Value) {
|
|
revng_assert(isSmall());
|
|
Storage = (Value << 1) | 1;
|
|
revng_assert(isSmall());
|
|
}
|
|
|
|
size_t capacity() const {
|
|
if (isSmall())
|
|
return BitsPerPointer - 1;
|
|
else
|
|
return getLarge().capacity();
|
|
}
|
|
|
|
void alloc(size_t NewSize) {
|
|
revng_assert(NewSize > capacity());
|
|
|
|
// Allocate the maximum between the requested index and twice the current
|
|
// capacity (rounding to the size of a uintptr_t)
|
|
size_t RequestedBits = std::max(NewSize, 2 * capacity());
|
|
size_t PointersCount = excessDivide(RequestedBits, BitsPerPointer);
|
|
|
|
// The `- 1` is due to the fact that LargeStorage already includes an
|
|
// uintptr_t entry
|
|
size_t ExtraSize = (PointersCount - 1) * sizeof(uintptr_t);
|
|
void *Ptr = malloc(sizeof(LargeStorage) + ExtraSize);
|
|
revng_assert(Ptr != nullptr);
|
|
LargeStorage &Result = *reinterpret_cast<LargeStorage *>(Ptr);
|
|
|
|
// Initialize the Capacity field
|
|
Result.setCapacity(PointersCount * BitsPerPointer);
|
|
|
|
// Zero out the storage
|
|
Result.zero();
|
|
|
|
// Copy the old values into the new storage
|
|
if (isSmall()) {
|
|
Result.at(0) = getSmall();
|
|
} else {
|
|
LargeStorage &Old = getLarge();
|
|
Result = Old;
|
|
|
|
// Also, deallocate the old storage
|
|
free(&Old);
|
|
}
|
|
|
|
Storage = reinterpret_cast<uintptr_t>(&Result);
|
|
revng_assert(!isSmall());
|
|
}
|
|
|
|
LargeStorage &getLarge() {
|
|
revng_assert(!isSmall());
|
|
return *reinterpret_cast<LargeStorage *>(Storage);
|
|
}
|
|
|
|
const LargeStorage &getLarge() const {
|
|
revng_assert(!isSmall());
|
|
return *reinterpret_cast<const LargeStorage *>(Storage);
|
|
}
|
|
|
|
private:
|
|
uintptr_t Storage;
|
|
};
|
|
|
|
template<typename LSBV>
|
|
inline void LazySmallBitVectorIterator<LSBV>::increment() {
|
|
revng_assert(BitVector != nullptr);
|
|
revng_assert(NextBitIndex == 0 || (*BitVector)[NextBitIndex - 1] == true);
|
|
|
|
NextBitIndex = BitVector->findNext(NextBitIndex);
|
|
}
|
|
|
|
#define LSBVI LazySmallBitVectorIterator
|
|
|
|
template<typename LSBV>
|
|
inline LSBVI<LSBV>::LSBVI(LSBV *BitVector) :
|
|
BitVector(BitVector), NextBitIndex(0) {
|
|
|
|
revng_assert(BitVector != nullptr);
|
|
if (!BitVector->isZero())
|
|
increment();
|
|
}
|
|
|
|
template<typename LSBV>
|
|
inline LSBVI<LSBV>::LSBVI(LSBV *BitVector, unsigned Index) :
|
|
BitVector(BitVector), NextBitIndex(Index) {
|
|
revng_assert(BitVector != nullptr);
|
|
}
|
|
|
|
#undef LSBVI
|