Files
UnrealEngine/Engine/Source/Runtime/AutoRTFM/Private/BitStack.h
2025-05-18 13:04:45 +08:00

194 lines
4.8 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#if (defined(__AUTORTFM) && __AUTORTFM)
#include "ContainerValidation.h"
#include "Stack.h"
#include "Utils.h"
#include <cstddef>
#include <cstdint>
namespace AutoRTFM
{
// A dynamically sized stack of bits.
//
// Notes:
// * Heap allocated memory is not automatically freed when popping elements.
// Only calling Reset() or destructing the stack will free heap allocated
// memory.
// * This class is not relocatable and so is not safe to use in UE's containers
// which require elements to be relocatable.
//
// Template parameters:
// InlineCapacity - the number of elements that can be held before spilling to
// the heap.
// Validation - if Disabled, do not perform validity assertions.
template<size_t InlineCapacity, EContainerValidation Validation = EContainerValidation::Enabled>
class TBitStack
{
using WordType = uint64_t;
static constexpr size_t NumWordBits = 64;
static_assert(NumWordBits == sizeof(WordType) * 8);
public:
// A reference to a single bit in the stack.
// Becomes invalid if the stack holding the bit is modified.
class FBitRef
{
public:
// Constructor
FBitRef(WordType& Word, WordType Mask) : Word(Word), Mask(Mask) {}
// Returns true if the bit is set.
operator bool() const
{
return (Word & Mask) != 0;
}
// Copy assignment operator.
// Changes the value of the referenced bit in the stack.
FBitRef& operator = (const FBitRef& Other)
{
return *this = static_cast<bool>(Other);
}
// Assignment operator.
// Changes the value of the referenced bit in the stack.
FBitRef& operator = (bool Value)
{
Word = Value ? (Word | Mask) : (Word & ~Mask);
return *this;
}
private:
WordType& Word;
const WordType Mask;
};
// Constructor.
TBitStack() = default;
// Copy constructor.
TBitStack(const TBitStack& Other) = default;
// Move constructor.
TBitStack(TBitStack&& Other) : Words{std::move(Other.Words)}, Count{Other.Count}
{
if (&Other != this)
{
Other.Count = 0;
}
}
// Destructor.
~TBitStack() = default;
// Copy assignment operator.
TBitStack& operator=(const TBitStack& Other) = default;
// Move assignment operator.
TBitStack& operator=(TBitStack&& Other)
{
if (&Other != this)
{
Words = std::move(Other.Words);
Count = Other.Count;
Other.Count = 0;
}
return *this;
}
// Clears all the items from the stack, preserving the capacity.
void Clear()
{
Words.Clear();
Count = 0;
}
// Clears all the items from the stack, freeing all heap allocations and
// resetting the capacity to InlineCapacity
void Reset()
{
Words.Reset();
Count = 0;
}
// Pushes a new bit on to the stack.
inline void Push(bool Bit)
{
const WordType WordBitIndex = Count & (NumWordBits - 1);
if (WordBitIndex == 0)
{
Words.Push(Bit ? 1 : 0);
}
else
{
WordType& Word = Words.Back();
WordType Mask = static_cast<WordType>(1) << WordBitIndex;
Word = Bit ? (Word | Mask) : (Word & ~Mask);
}
++Count;
AUTORTFM_ASSERT(Validation == EContainerValidation::Disabled || NumWordsFor(Count) == Words.Num());
}
// Removes the last item on the stack.
inline bool Pop()
{
AUTORTFM_ASSERT(Validation == EContainerValidation::Disabled || Count > 0);
--Count;
const WordType WordBitIndex = Count & (NumWordBits - 1);
const WordType Word = Words.Back();
const bool Value = ((Word >> WordBitIndex) & 1) != 0;
if (WordBitIndex == 0)
{
Words.Pop();
}
AUTORTFM_ASSERT(Validation == EContainerValidation::Disabled || NumWordsFor(Count) == Words.Num());
return Value;
}
// Reserves memory for NewCapacity bits.
inline void Reserve(size_t NewCapacity)
{
Words.Reserve(NumWordsFor(NewCapacity));
}
// Returns the number of bits held by the stack.
inline size_t Num() const { return Count; }
// Returns true if the stack holds no bits.
inline bool IsEmpty() const { return Count == 0; }
// Index operator.
// Returns a reference to the bit with the given index in the stack.
FBitRef operator[](size_t Index)
{
AUTORTFM_ASSERT(Validation == EContainerValidation::Disabled || Index < Count);
WordType& Word = Words[Index / NumWordBits];
WordType Mask = static_cast<WordType>(1) << (Index & (NumWordBits-1));
return FBitRef{Word, Mask};
}
// Constant index operator.
// Returns true if the bit with the given index is set.
bool operator[](size_t Index) const
{
AUTORTFM_ASSERT(Validation == EContainerValidation::Disabled || Index < Count);
WordType Word = Words[Index / NumWordBits];
WordType Mask = static_cast<WordType>(1) << (Index & (NumWordBits-1));
return (Word & Mask) != 0;
}
private:
static constexpr size_t NumWordsFor(size_t NumBits)
{
return (NumBits + (NumWordBits-1)) / NumWordBits;
}
TStack<WordType, NumWordsFor(InlineCapacity), Validation> Words;
size_t Count = 0; // In bits
};
}
#endif // (defined(__AUTORTFM) && __AUTORTFM)