feat(containers): enable TBitset to support custom block type

This commit is contained in:
_Redstone_c_ 2023-03-13 19:13:31 +08:00
parent 432b6e0a6b
commit dd8b698bb3
2 changed files with 90 additions and 19 deletions

View File

@ -284,11 +284,11 @@ void TestBitset()
}
{
FBitset BitsetA(64, 0x0339'0339'0339'0339ull);
uint64 IntA = 0x0339'0339'0339'0339ull;
FBitset BitsetA(64, 0x0139'0239'0339'0439ull);
uint64 IntA = 0x0139'0239'0339'0439ull;
FBitset BitsetB(32, 0x017F'017Full);
uint32 IntB = 0x017F'017Full;
FBitset BitsetB(32, 0x017F'027Full);
uint32 IntB = 0x017F'027Full;
FBitset BitsetANDA = BitsetA; BitsetANDA &= BitsetB;
FBitset BitsetANDB = BitsetB; BitsetANDB &= BitsetA;
@ -317,11 +317,11 @@ void TestBitset()
}
{
FBitset BitsetA(64, 0x0339'0339'0339'0339ull);
uint64 IntA = 0x0339'0339'0339'0339ull;
FBitset BitsetA(64, 0x0139'0239'0339'0439ull);
uint64 IntA = 0x0139'0239'0339'0439ull;
FBitset BitsetB(32, 0x017F'017Full);
uint32 IntB = 0x017F'017Full;
FBitset BitsetB(32, 0x017F'027Full);
uint32 IntB = 0x017F'027Full;
always_check(((BitsetA & BitsetB).ToIntegral() == (IntA & IntB)));
always_check(((BitsetA | BitsetB).ToIntegral() == (IntA | IntB)));
@ -329,8 +329,8 @@ void TestBitset()
}
{
FBitset Bitset(64, 0x0339'0339'0339'0339ull);
uint64 Int = 0x0339'0339'0339'0339ull;
FBitset Bitset(64, 0x0139'0239'0339'0439ull);
uint64 Int = 0x0139'0239'0339'0439ull;
always_check(((Bitset << 40).ToIntegral() == (Int << 40)));
always_check(((Bitset >> 40).ToIntegral() == (Int >> 40)));

View File

@ -17,7 +17,14 @@ NAMESPACE_REDCRAFT_BEGIN
NAMESPACE_MODULE_BEGIN(Redcraft)
NAMESPACE_MODULE_BEGIN(Utility)
template <CInstantiableAllocator Allocator>
NAMESPACE_PRIVATE_BEGIN
template <CUnsignedIntegral InBlockType> requires (!CSameAs<InBlockType, bool>)
using TDefaultBitsetAllocator = TInlineAllocator<(40 - 3 * sizeof(size_t)) / sizeof(InBlockType)>;
NAMESPACE_PRIVATE_END
template <CUnsignedIntegral InBlockType, CInstantiableAllocator Allocator = NAMESPACE_PRIVATE::TDefaultBitsetAllocator<InBlockType>> requires (!CSameAs<InBlockType, bool>)
class TBitset final
{
private:
@ -27,7 +34,7 @@ private:
public:
using BlockType = uint64;
using BlockType = InBlockType;
using ElementType = bool;
using AllocatorType = Allocator;
@ -59,9 +66,38 @@ public:
/** Constructs a bitset from an integer. */
TBitset(size_t InCount, uint64 InValue) : TBitset(InCount > 64 ? InCount : 64)
{
size_t BlockInteger = sizeof(uint64) / sizeof(BlockType);
static_assert(sizeof(BlockType) <= sizeof(uint64), "The block width of TBitset is unexpected");
*reinterpret_cast<uint64*>(Impl.Pointer) = InValue;
if constexpr (sizeof(BlockType) == sizeof(uint8))
{
Impl.Pointer[0] = static_cast<BlockType>(InValue >> 0);
Impl.Pointer[1] = static_cast<BlockType>(InValue >> 8);
Impl.Pointer[2] = static_cast<BlockType>(InValue >> 16);
Impl.Pointer[3] = static_cast<BlockType>(InValue >> 24);
Impl.Pointer[4] = static_cast<BlockType>(InValue >> 32);
Impl.Pointer[5] = static_cast<BlockType>(InValue >> 40);
Impl.Pointer[6] = static_cast<BlockType>(InValue >> 48);
Impl.Pointer[7] = static_cast<BlockType>(InValue >> 56);
}
else if constexpr (sizeof(BlockType) == sizeof(uint16))
{
Impl.Pointer[0] = static_cast<BlockType>(InValue >> 0);
Impl.Pointer[1] = static_cast<BlockType>(InValue >> 16);
Impl.Pointer[2] = static_cast<BlockType>(InValue >> 32);
Impl.Pointer[3] = static_cast<BlockType>(InValue >> 48);
}
else if constexpr (sizeof(BlockType) == sizeof(uint32))
{
Impl.Pointer[0] = static_cast<BlockType>(InValue >> 0);
Impl.Pointer[1] = static_cast<BlockType>(InValue >> 32);
}
else if constexpr (sizeof(BlockType) == sizeof(uint64))
{
Impl.Pointer[0] = static_cast<BlockType>(InValue >> 0);
}
else check_no_entry();
size_t BlockInteger = sizeof(uint64) / sizeof(BlockType);
Memory::Memset(Impl.Pointer + BlockInteger, 0, (NumBlocks() - BlockInteger) * sizeof(BlockType));
@ -151,7 +187,7 @@ public:
if (NumToAllocate != MaxBlocks())
{
Impl->Deallocate(Impl.Pointer);
Impl.BitsetNum = InValue.Num();
Impl.BlocksMax = NumToAllocate;
Impl.Pointer = Impl->Allocate(MaxBlocks());
@ -199,7 +235,7 @@ public:
TBitset& operator=(initializer_list<bool> IL)
{
auto First = Iteration::Begin(IL);
const size_t BlocksCount = (GetNum(IL) + BlockWidth - 1) / BlockWidth;
size_t NumToAllocate = BlocksCount;
@ -210,7 +246,7 @@ public:
if (NumToAllocate != MaxBlocks())
{
Impl->Deallocate(Impl.Pointer);
Impl.BitsetNum = GetNum(IL);
Impl.BlocksMax = NumToAllocate;
Impl.Pointer = Impl->Allocate(MaxBlocks());
@ -497,6 +533,8 @@ public:
static constexpr auto BlockCount = [](BlockType Block)
{
static_assert(sizeof(BlockType) <= sizeof(uint64), "The block width of TBitset is unexpected");
if constexpr (sizeof(BlockType) == sizeof(uint8))
{
Block = (Block & 0x55ull) + ((Block >> 1) & 0x55ull);
@ -576,9 +614,42 @@ public:
{
checkf(Num() <= 64, TEXT("The bitset can not be represented in uint64. Please check Num()."));
uint64 Result = 0;
static_assert(sizeof(BlockType) <= sizeof(uint64), "The block width of TBitset is unexpected");
if constexpr (sizeof(BlockType) == sizeof(uint8))
{
Result |= static_cast<uint64>(Impl.Pointer[0]) << 0;
Result |= static_cast<uint64>(Impl.Pointer[1]) << 8;
Result |= static_cast<uint64>(Impl.Pointer[2]) << 16;
Result |= static_cast<uint64>(Impl.Pointer[3]) << 24;
Result |= static_cast<uint64>(Impl.Pointer[4]) << 32;
Result |= static_cast<uint64>(Impl.Pointer[5]) << 40;
Result |= static_cast<uint64>(Impl.Pointer[6]) << 48;
Result |= static_cast<uint64>(Impl.Pointer[7]) << 56;
}
else if constexpr (sizeof(BlockType) == sizeof(uint16))
{
Result |= static_cast<uint64>(Impl.Pointer[0]) << 0;
Result |= static_cast<uint64>(Impl.Pointer[1]) << 16;
Result |= static_cast<uint64>(Impl.Pointer[2]) << 32;
Result |= static_cast<uint64>(Impl.Pointer[3]) << 48;
}
else if constexpr (sizeof(BlockType) == sizeof(uint32))
{
Result |= static_cast<uint64>(Impl.Pointer[0]) << 0;
Result |= static_cast<uint64>(Impl.Pointer[1]) << 32;
}
else if constexpr (sizeof(BlockType) == sizeof(uint64))
{
Result |= static_cast<uint64>(Impl.Pointer[0]) << 0;
}
else check_no_entry();
const uint64 Mask = Num() < 64 ? (1ull << Num()) - 1 : -1;
return *reinterpret_cast<uint64*>(Impl.Pointer) & Mask;
return Result & Mask;
}
/** Appends the given bit value to the end of the bitset. */
@ -960,7 +1031,7 @@ private:
};
using FBitset = TBitset<TInlineAllocator<((40 - 3 * sizeof(size_t)) / sizeof(TBitset<FHeapAllocator>::BlockType))>>;
using FBitset = TBitset<uint64>;
static_assert(sizeof(FBitset) == 40, "The byte size of FBitset is unexpected");