mirror of
https://github.com/Milxnor/Project-Reboot-3.0.git
synced 2026-01-13 02:42:22 +01:00
334 lines
7.6 KiB
C++
334 lines
7.6 KiB
C++
#pragma once
|
|
|
|
#include "ContainerAllocationPolicies.h"
|
|
#include "UnrealMathUtility.h"
|
|
|
|
#define NumBitsPerDWORD ((int32)32)
|
|
#define NumBitsPerDWORDLogTwo ((int32)5)
|
|
|
|
template<typename Allocator = FDefaultBitArrayAllocator>
|
|
class TBitArray;
|
|
|
|
template<typename Allocator = FDefaultBitArrayAllocator>
|
|
class TConstSetBitIterator;
|
|
|
|
class FBitReference
|
|
{
|
|
public:
|
|
|
|
FORCEINLINE FBitReference(uint32& InData, uint32 InMask)
|
|
: Data(InData)
|
|
, Mask(InMask)
|
|
{}
|
|
|
|
FORCEINLINE operator bool() const
|
|
{
|
|
return (Data & Mask) != 0;
|
|
}
|
|
FORCEINLINE void operator=(const bool NewValue)
|
|
{
|
|
if (NewValue)
|
|
{
|
|
Data |= Mask;
|
|
}
|
|
else
|
|
{
|
|
Data &= ~Mask;
|
|
}
|
|
}
|
|
/*
|
|
FORCEINLINE void AtomicSet(const bool NewValue)
|
|
{
|
|
if (NewValue)
|
|
{
|
|
if (!(Data & Mask))
|
|
{
|
|
while (1)
|
|
{
|
|
uint32 Current = Data;
|
|
uint32 Desired = Current | Mask;
|
|
if (Current == Desired || FPlatformAtomics::InterlockedCompareExchange((volatile int32*)&Data, (int32)Desired, (int32)Current) == (int32)Current)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (Data & Mask)
|
|
{
|
|
while (1)
|
|
{
|
|
uint32 Current = Data;
|
|
uint32 Desired = Current & ~Mask;
|
|
if (Current == Desired || FPlatformAtomics::InterlockedCompareExchange((volatile int32*)&Data, (int32)Desired, (int32)Current) == (int32)Current)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
FORCEINLINE FBitReference& operator=(const FBitReference& Copy)
|
|
{
|
|
// As this is emulating a reference, assignment should not rebind,
|
|
// it should write to the referenced bit.
|
|
*this = (bool)Copy;
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
uint32& Data;
|
|
uint32 Mask;
|
|
};
|
|
|
|
class FConstBitReference
|
|
{
|
|
public:
|
|
|
|
FORCEINLINE FConstBitReference(const uint32& InData, uint32 InMask)
|
|
: Data(InData)
|
|
, Mask(InMask)
|
|
{}
|
|
|
|
FORCEINLINE operator bool() const
|
|
{
|
|
return (Data & Mask) != 0;
|
|
}
|
|
|
|
private:
|
|
const uint32& Data;
|
|
uint32 Mask;
|
|
};
|
|
|
|
template<typename Allocator /*= FDefaultBitArrayAllocator*/>
|
|
class TBitArray
|
|
{
|
|
public:
|
|
/**
|
|
* Move constructor.
|
|
*/
|
|
FORCEINLINE TBitArray(TBitArray&& Other)
|
|
{
|
|
MoveOrCopy(*this, Other);
|
|
}
|
|
|
|
FORCEINLINE int32 Num() const { return NumBits; }
|
|
|
|
/**
|
|
* Copy constructor.
|
|
*/
|
|
FORCEINLINE TBitArray(const TBitArray& Copy)
|
|
: NumBits(0)
|
|
, MaxBits(0)
|
|
{
|
|
*this = Copy;
|
|
}
|
|
|
|
FORCEINLINE FBitReference operator[](int32 Index)
|
|
{
|
|
// check(Index >= 0 && Index < NumBits);
|
|
return FBitReference(
|
|
GetData()[Index / NumBitsPerDWORD],
|
|
1 << (Index & (NumBitsPerDWORD - 1))
|
|
);
|
|
}
|
|
FORCEINLINE const FConstBitReference operator[](int32 Index) const
|
|
{
|
|
// check(Index >= 0 && Index < NumBits);
|
|
return FConstBitReference(
|
|
GetData()[Index / NumBitsPerDWORD],
|
|
1 << (Index & (NumBitsPerDWORD - 1))
|
|
);
|
|
}
|
|
|
|
FORCEINLINE const uint32* GetData() const
|
|
{
|
|
return (uint32*)AllocatorInstance.GetAllocation();
|
|
}
|
|
|
|
FORCEINLINE uint32* GetData()
|
|
{
|
|
return (uint32*)AllocatorInstance.GetAllocation();
|
|
}
|
|
|
|
/**
|
|
* Move assignment.
|
|
*/
|
|
FORCEINLINE TBitArray& operator=(TBitArray&& Other)
|
|
{
|
|
if (this != &Other)
|
|
{
|
|
MoveOrCopy(*this, Other);
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
typedef typename Allocator::template ForElementType<uint32> AllocatorType;
|
|
|
|
AllocatorType AllocatorInstance;
|
|
int32 NumBits;
|
|
int32 MaxBits;
|
|
|
|
template <typename BitArrayType>
|
|
static FORCEINLINE typename TEnableIf<TContainerTraits<BitArrayType>::MoveWillEmptyContainer>::Type MoveOrCopy(BitArrayType& ToArray, BitArrayType& FromArray)
|
|
{
|
|
ToArray.AllocatorInstance.MoveToEmpty(FromArray.AllocatorInstance);
|
|
|
|
ToArray.NumBits = FromArray.NumBits;
|
|
ToArray.MaxBits = FromArray.MaxBits;
|
|
FromArray.NumBits = 0;
|
|
FromArray.MaxBits = 0;
|
|
}
|
|
|
|
template <typename BitArrayType>
|
|
static FORCEINLINE typename TEnableIf<!TContainerTraits<BitArrayType>::MoveWillEmptyContainer>::Type MoveOrCopy(BitArrayType& ToArray, BitArrayType& FromArray)
|
|
{
|
|
ToArray = FromArray;
|
|
}
|
|
|
|
FORCENOINLINE void Realloc(int32 PreviousNumBits)
|
|
{
|
|
const int32 PreviousNumDWORDs = FMath::DivideAndRoundUp(PreviousNumBits, NumBitsPerDWORD);
|
|
const int32 MaxDWORDs = FMath::DivideAndRoundUp(MaxBits, NumBitsPerDWORD);
|
|
|
|
AllocatorInstance.ResizeAllocation(PreviousNumDWORDs, MaxDWORDs, sizeof(uint32));
|
|
|
|
if (MaxDWORDs)
|
|
{
|
|
// Reset the newly allocated slack DWORDs.
|
|
FMemory::Memzero((uint32*)AllocatorInstance.GetAllocation() + PreviousNumDWORDs, (MaxDWORDs - PreviousNumDWORDs) * sizeof(uint32));
|
|
}
|
|
}
|
|
public:
|
|
};
|
|
|
|
class FRelativeBitReference
|
|
{
|
|
public:
|
|
FORCEINLINE explicit FRelativeBitReference(int32 BitIndex)
|
|
: DWORDIndex(BitIndex >> NumBitsPerDWORDLogTwo)
|
|
, Mask(1 << (BitIndex & (NumBitsPerDWORD - 1)))
|
|
{
|
|
}
|
|
|
|
int32 DWORDIndex;
|
|
uint32 Mask;
|
|
};
|
|
|
|
template<typename Allocator>
|
|
class TConstSetBitIterator : public FRelativeBitReference
|
|
{
|
|
public:
|
|
|
|
/** Constructor. */
|
|
TConstSetBitIterator(const TBitArray<Allocator>& InArray, int32 StartIndex = 0)
|
|
: FRelativeBitReference(StartIndex)
|
|
, Array(InArray)
|
|
, UnvisitedBitMask((~0U) << (StartIndex & (NumBitsPerDWORD - 1)))
|
|
, CurrentBitIndex(StartIndex)
|
|
, BaseBitIndex(StartIndex & ~(NumBitsPerDWORD - 1))
|
|
{
|
|
// check(StartIndex >= 0 && StartIndex <= Array.Num());
|
|
if (StartIndex != Array.Num())
|
|
{
|
|
FindFirstSetBit();
|
|
}
|
|
}
|
|
|
|
/** Forwards iteration operator. */
|
|
FORCEINLINE TConstSetBitIterator& operator++()
|
|
{
|
|
// Mark the current bit as visited.
|
|
UnvisitedBitMask &= ~this->Mask;
|
|
|
|
// Find the first set bit that hasn't been visited yet.
|
|
FindFirstSetBit();
|
|
|
|
return *this;
|
|
}
|
|
|
|
FORCEINLINE friend bool operator==(const TConstSetBitIterator& Lhs, const TConstSetBitIterator& Rhs)
|
|
{
|
|
// We only need to compare the bit index and the array... all the rest of the state is unobservable.
|
|
return Lhs.CurrentBitIndex == Rhs.CurrentBitIndex && &Lhs.Array == &Rhs.Array;
|
|
}
|
|
|
|
FORCEINLINE friend bool operator!=(const TConstSetBitIterator& Lhs, const TConstSetBitIterator& Rhs)
|
|
{
|
|
return !(Lhs == Rhs);
|
|
}
|
|
|
|
/** conversion to "bool" returning true if the iterator is valid. */
|
|
FORCEINLINE explicit operator bool() const
|
|
{
|
|
return CurrentBitIndex < Array.Num();
|
|
}
|
|
/** inverse of the "bool" operator */
|
|
FORCEINLINE bool operator !() const
|
|
{
|
|
return !(bool)*this;
|
|
}
|
|
|
|
/** Index accessor. */
|
|
FORCEINLINE int32 GetIndex() const
|
|
{
|
|
return CurrentBitIndex;
|
|
}
|
|
|
|
private:
|
|
|
|
const TBitArray<Allocator>& Array;
|
|
|
|
uint32 UnvisitedBitMask;
|
|
int32 CurrentBitIndex;
|
|
int32 BaseBitIndex;
|
|
|
|
|
|
/** Find the first set bit starting with the current bit, inclusive. */
|
|
void FindFirstSetBit()
|
|
{
|
|
const uint32* ArrayData = Array.GetData();
|
|
const int32 ArrayNum = Array.Num();
|
|
const int32 LastDWORDIndex = (ArrayNum - 1) / NumBitsPerDWORD;
|
|
|
|
// Advance to the next non-zero uint32.
|
|
uint32 RemainingBitMask = ArrayData[this->DWORDIndex] & UnvisitedBitMask;
|
|
while (!RemainingBitMask)
|
|
{
|
|
++this->DWORDIndex;
|
|
BaseBitIndex += NumBitsPerDWORD;
|
|
if (this->DWORDIndex > LastDWORDIndex)
|
|
{
|
|
// We've advanced past the end of the array.
|
|
CurrentBitIndex = ArrayNum;
|
|
return;
|
|
}
|
|
|
|
RemainingBitMask = ArrayData[this->DWORDIndex];
|
|
UnvisitedBitMask = ~0;
|
|
}
|
|
|
|
// This operation has the effect of unsetting the lowest set bit of BitMask
|
|
const uint32 NewRemainingBitMask = RemainingBitMask & (RemainingBitMask - 1);
|
|
|
|
// This operation XORs the above mask with the original mask, which has the effect
|
|
// of returning only the bits which differ; specifically, the lowest bit
|
|
this->Mask = NewRemainingBitMask ^ RemainingBitMask;
|
|
|
|
// If the Nth bit was the lowest set bit of BitMask, then this gives us N
|
|
CurrentBitIndex = BaseBitIndex + NumBitsPerDWORD - 1 - FMath::CountLeadingZeros(this->Mask);
|
|
|
|
// If we've accidentally iterated off the end of an array but still within the same DWORD
|
|
// then set the index to the last index of the array
|
|
if (CurrentBitIndex > ArrayNum)
|
|
{
|
|
CurrentBitIndex = ArrayNum;
|
|
}
|
|
}
|
|
};
|