Skip to content

Commit 0767c46

Browse files
committed
ADDED: base constructor and allocation class and tests
1 parent fd2ee07 commit 0767c46

File tree

1 file changed

+279
-0
lines changed

1 file changed

+279
-0
lines changed

tests/src/resources/test_ResourceSystem.cpp

Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -561,3 +561,282 @@ UTEST_F(test_ResourceSystem, LoadAnimationData)
561561
}
562562
}
563563
}
564+
struct TestStruct
565+
{
566+
uint64_t inta {0};
567+
uint64_t intb {0};
568+
};
569+
570+
struct FreeBlock
571+
{
572+
size_t size {0};
573+
FreeBlock* prev {nullptr};
574+
FreeBlock* next {nullptr};
575+
};
576+
577+
struct SmallBlockHeader
578+
{
579+
uint16_t sizeAndFlags {0};
580+
};
581+
582+
struct SmallBlockFooter
583+
{
584+
uint16_t sizeAndFlags {0};
585+
};
586+
587+
struct BlockHeader
588+
{
589+
uint32_t sizeAndFlags {0};
590+
};
591+
592+
struct BlockFooter
593+
{
594+
uint32_t sizeAndFlags {0};
595+
};
596+
597+
struct LargeBlockHeader
598+
{
599+
uint64_t sizeAndFlags {0};
600+
};
601+
602+
struct LargeBlockFooter
603+
{
604+
uint64_t sizeAndFlags {0};
605+
};
606+
607+
#define FL_MIN 4
608+
#define FL_INDEX(capacity) 63 - __builtin_clzll(capacity)
609+
#define MAX_SL_INDEX 16
610+
611+
#define SL_BITS 4 // we get the log(2) the max sl index
612+
#define SL_MASK ((1 << SL_BITS) - 1)
613+
614+
#define INVALID_INDEX SIZE_T_MAX
615+
616+
class Allocator
617+
{
618+
public:
619+
Allocator() {};
620+
Allocator(const size_t capacity)
621+
: capacity {capacity}
622+
{
623+
data = reinterpret_cast<unsigned char*>(calloc(sizeof(FreeBlock), capacity));
624+
625+
// __builtin_clzll calculates the leading zeroes of a 64 bit integer. To find the furthest
626+
// bit set to 1 we need to minus 63 from the result (since a 64 bit integer has a bit range
627+
// of 0 - 63)
628+
uint64_t rawFl = FL_INDEX(capacity);
629+
uint64_t maxIndices = rawFl - FL_MIN + 1;
630+
631+
freeList = reinterpret_cast<FreeBlock**>(calloc(maxIndices * MAX_SL_INDEX, sizeof(FreeBlock*)));
632+
slBitmasks = reinterpret_cast<uint16_t*>(calloc(maxIndices, sizeof(uint16_t)));
633+
634+
FreeBlock* firstBlock = reinterpret_cast<FreeBlock*>(data);
635+
firstBlock->size = capacity;
636+
637+
size_t offsetFlIdx = rawFl - FL_MIN;
638+
639+
size_t sl = SlIndex(capacity, rawFl);
640+
size_t freeListIdx = (offsetFlIdx) * MAX_SL_INDEX + sl;
641+
642+
freeList[freeListIdx] = firstBlock;
643+
644+
flBitmask |= 1ULL << (offsetFlIdx);
645+
slBitmasks[offsetFlIdx] |= 1ULL << sl;
646+
}
647+
648+
~Allocator()
649+
{
650+
if (data) free(data);
651+
if (freeList) free(freeList);
652+
if (slBitmasks) free(slBitmasks);
653+
654+
capacity = 0;
655+
data = nullptr;
656+
freeList = nullptr;
657+
slBitmasks = nullptr;
658+
}
659+
660+
void* Allocate(size_t size)
661+
{
662+
if (size < 16) return nullptr;
663+
664+
size_t rawFl = FL_INDEX(size);
665+
size_t sl = SlIndex(size, rawFl);
666+
size_t fl = rawFl - FL_MIN;
667+
668+
size_t freeListIdx = 0;
669+
670+
freeListIdx = fl * MAX_SL_INDEX + sl;
671+
672+
size_t freeSlotFl = fl;
673+
size_t freeSlotSl = sl;
674+
675+
if (!IsFree(fl, sl)) freeListIdx = GetNextFreeSlotIndex(freeSlotFl, freeSlotSl);
676+
if (freeListIdx == INVALID_INDEX) return nullptr;
677+
678+
FreeBlock* block = freeList[freeListIdx];
679+
680+
size_t requiredSize = sizeof(BlockHeader) + size + sizeof(BlockFooter);
681+
682+
// Needs to be split
683+
if (block->size > requiredSize)
684+
{
685+
slBitmasks[freeSlotFl] &= ~(1 << freeSlotSl);
686+
if (!slBitmasks[freeSlotFl]) flBitmask &= ~(1 << freeSlotFl);
687+
688+
size_t newSize = block->size - requiredSize;
689+
size_t newRawFl = FL_INDEX(newSize);
690+
size_t newFl = newRawFl - FL_MIN;
691+
size_t newSl = SlIndex(newSize, newRawFl);
692+
size_t newFreeListIdx = newFl * MAX_SL_INDEX + newSl;
693+
694+
FreeBlock* newBlock = reinterpret_cast<FreeBlock*>(reinterpret_cast<unsigned char*>(block) + requiredSize);
695+
696+
newBlock->size = newSize;
697+
newBlock->prev = nullptr;
698+
newBlock->next = nullptr;
699+
700+
freeList[newFreeListIdx] = newBlock;
701+
702+
flBitmask |= 1ULL << (newFl);
703+
slBitmasks[newFl] |= 1ULL << newSl;
704+
}
705+
706+
BlockHeader* header = reinterpret_cast<BlockHeader*>(reinterpret_cast<unsigned char*>(block));
707+
size_t flags = 0;
708+
flags |= BitMask(0);
709+
710+
BlockFooter* prev = reinterpret_cast<BlockFooter*>(reinterpret_cast<unsigned char*>(header) - sizeof(BlockFooter));
711+
BlockHeader* prevHeader = reinterpret_cast<BlockHeader*>(reinterpret_cast<unsigned char*>(prev) - prev->sizeAndFlags - sizeof(BlockHeader));
712+
713+
if ((unsigned char*)header != data && !Mask(prevHeader->sizeAndFlags,0)) flags |= BitMask(1);
714+
715+
header->sizeAndFlags = (requiredSize << 3) | flags;
716+
717+
unsigned char* ptr = reinterpret_cast<unsigned char*>(reinterpret_cast<unsigned char*>(header)+sizeof(BlockHeader));
718+
719+
BlockFooter* footer = reinterpret_cast<BlockFooter*>(ptr+size);
720+
footer->sizeAndFlags = size;
721+
722+
return ptr;
723+
}
724+
725+
size_t GetNextFreeSlotIndex(OUT size_t& fl,OUT size_t& sl)
726+
{
727+
sl = FindLargerSlots(slBitmasks[fl], sl);
728+
if (sl) return fl * MAX_SL_INDEX + __builtin_ctz(sl);
729+
730+
fl = FindLargerSlots64(flBitmask, fl);
731+
732+
if (!fl) return INVALID_INDEX;
733+
734+
fl = __builtin_ctzll(fl);
735+
CC_ASSERT(slBitmasks[fl] > 0,
736+
"SlBitmasks is returning 0. This should not be happening and indicates an implementation error.")
737+
738+
sl = __builtin_ctz(slBitmasks[fl]);
739+
740+
return fl * MAX_SL_INDEX + sl;
741+
}
742+
743+
bool IsFree(size_t fl, size_t sl) { return Mask(slBitmasks[fl], BitMask(sl)); }
744+
const size_t FindLargerSlots(size_t mask, size_t idx) { return Mask(mask, FlipBits(BitMask(idx+1)-1)); }
745+
const size_t FindLargerSlots64(size_t mask, size_t idx) { return Mask(mask, FlipBits(BitMask64(idx+1)-1)); }
746+
const size_t BitMask(size_t shiftAmount) { return 1 << shiftAmount; }
747+
const size_t BitMask64(size_t shiftAmount) { return 1ULL << shiftAmount; }
748+
const size_t FlipBits(size_t mask) { return ~(mask); }
749+
const size_t Mask(size_t value, size_t mask) { return value & mask; }
750+
751+
752+
size_t& Capacity() { return capacity;}
753+
unsigned char* Data() { return data; };
754+
size_t SlIndex(size_t size, size_t fl) { return (size >> (fl - SL_BITS)) & SL_MASK; }
755+
const uint64_t& FlBitmask() { return flBitmask; }
756+
uint16_t*& SlBitmask() { return slBitmasks; }
757+
FreeBlock** FreeList() { return freeList; }
758+
759+
private:
760+
size_t capacity {0};
761+
unsigned char* data {nullptr};
762+
FreeBlock** freeList {nullptr};
763+
764+
// bitmasks
765+
uint64_t flBitmask {0};
766+
uint16_t* slBitmasks {nullptr};
767+
};
768+
769+
UTEST(test_ResourceSystem, TestEmptyAllocatorCreation)
770+
{
771+
Allocator a;
772+
ASSERT_EQ(a.Data(), nullptr);
773+
ASSERT_EQ(a.Capacity(), 0);
774+
}
775+
776+
UTEST(test_ResourceSystem, TestAllocatorCreationWithSize)
777+
{
778+
Allocator a(64);
779+
ASSERT_NE(a.Data(), nullptr);
780+
ASSERT_EQ(a.Capacity(), 64);
781+
782+
// 0001 0000
783+
ASSERT_EQ(a.FlBitmask(), 4);
784+
ASSERT_EQ(a.SlBitmask()[2], 1);
785+
786+
FreeBlock* block = a.FreeList()[2 * 16];
787+
ASSERT_EQ(block->size,64);
788+
ASSERT_EQ(block->next, nullptr);
789+
ASSERT_EQ(block->prev, nullptr);
790+
}
791+
792+
UTEST(test_ResourceSystem, TestAllocateFunction)
793+
{
794+
Allocator a(64);
795+
ASSERT_NE(a.Data(), nullptr);
796+
ASSERT_EQ(a.Capacity(), 64);
797+
798+
TestStruct* p = (TestStruct*) a.Allocate(sizeof(TestStruct));
799+
800+
p->inta = 10;
801+
p->intb = 20;
802+
803+
ASSERT_EQ(10, p->inta);
804+
ASSERT_EQ(20, p->intb);
805+
806+
ASSERT_EQ(a.FlBitmask(), 2);
807+
ASSERT_EQ(16, a.SlBitmask()[1]);
808+
809+
FreeBlock* block = a.FreeList()[(1 * 16) + 4];
810+
ASSERT_EQ(block->size,40);
811+
ASSERT_EQ(block->next, nullptr);
812+
ASSERT_EQ(block->prev, nullptr);
813+
814+
auto header = (BlockHeader*)a.Data();
815+
auto data = (TestStruct*) (((unsigned char*)header) + sizeof(BlockHeader));
816+
auto footer = (BlockFooter*) (((unsigned char*)data) + sizeof(TestStruct));
817+
818+
ASSERT_EQ(193, header->sizeAndFlags);
819+
ASSERT_EQ(data, p);
820+
ASSERT_EQ(sizeof(TestStruct), footer->sizeAndFlags);
821+
822+
Siege::String* str = (Siege::String*) a.Allocate(sizeof(Siege::String));
823+
824+
*str = "Hello There!";
825+
ASSERT_STREQ(str->Str(),"Hello There!");
826+
827+
ASSERT_EQ(a.FlBitmask(), 1);
828+
ASSERT_EQ(0, a.SlBitmask()[1]);
829+
830+
FreeBlock* NewBlock = a.FreeList()[0];
831+
ASSERT_EQ(NewBlock->size,16);
832+
ASSERT_EQ(NewBlock->next, nullptr);
833+
ASSERT_EQ(NewBlock->prev, nullptr);
834+
835+
auto newHeader = (BlockHeader*)((unsigned char*)footer + sizeof(BlockFooter));
836+
auto newData = (Siege::String*) (((unsigned char*)newHeader) + sizeof(BlockHeader));
837+
auto newFooter = (BlockFooter*) (((unsigned char*)newData) + sizeof(TestStruct));
838+
839+
ASSERT_EQ(195, newHeader->sizeAndFlags);
840+
ASSERT_EQ(newData, str);
841+
ASSERT_EQ(sizeof(Siege::String), newFooter->sizeAndFlags);
842+
}

0 commit comments

Comments
 (0)