Lumenarium/gs_libs/gs_list.h

248 lines
6.5 KiB
C++

//
// gs_list.h - cpp template implementation of a growable, freeable list
// Author: Peter Slattery
// Date: December 30, 2019
//
// The key difference between gs_list.h and gs_bucket.h is that gs_bucket.h keeps everything
// sequential (new elements are appended to the end, free elements are filled in from the end),
// whereas gs_list.h maintiains a free list and deals with holes in the list
struct gs_list_handle
{
// NOTE(Peter): A generation of 0 in a handle denotes an invalid handle
u32 Generation;
u32 Index;
};
#define ListHandleIsInvalid(handle) ((handle).Generation == 0)
#define ListHandleIsValid(handle) ((handle).Generation != 0)
internal b32
GSListHandlesAreEqual(gs_list_handle A, gs_list_handle B)
{
b32 Result = (A.Index == B.Index) && (A.Generation == B.Generation);
return Result;
}
struct gs_free_list
{
u8* NextFreeEntry;
};
template <typename T>
struct gs_list_entry
{
gs_list_handle Handle;
gs_free_list Free;
T Value;
};
#define EntryIsFree(entry) ((entry)->Free.NextFreeEntry != 0)
template <typename T>
struct gs_list_bucket
{
gs_list_entry<T>* Contents;
};
#define GS_LIST_DEFAULT_BUCKET_SIZE 256
template <typename T>
class gs_list {
public:
u32 BucketCapacity;
u32 BucketCount;
gs_list_bucket<T>* Buckets;
gs_list_entry<T> FreeList;
u32 Used;
// NOTE(Peter): this is just here so we can get the next entry which is
// not a part of the free list but is still unused.
// It shouldn't be used outside of the actual list implementation.
u32 OnePastLastUsed;
gs_list();
gs_list(u32 BucketSize);
void GrowList();
gs_list_entry<T>* GetEntryAtIndex(u32 Index);
T* GetElementAtIndex(u32 Index);
T* GetElementWithHandle(gs_list_handle Handle);
gs_list_entry<T>* TakeFreeEntry();
T* TakeElement();
gs_list_handle PushElementOnList(T Ele);
void FreeElementAtIndex(u32 Index);
void FreeElementWithHandle(gs_list_handle Handle);
};
template <typename T>
gs_list<T>::gs_list()
{
this->BucketCapacity = GS_LIST_DEFAULT_BUCKET_SIZE;
this->BucketCount = 0;
this->Buckets = 0;
this->FreeList.Free.NextFreeEntry = (u8*)&this->FreeList;
this->Used = 0;
this->OnePastLastUsed = 0;
}
template <typename T>
gs_list<T>::gs_list(u32 BucketCapacity)
{
this->BucketCapacity = BucketCapacity;
this->BucketCount = 0;
this->Buckets = 0;
this->FreeList.Free.NextFreeEntry = (u8*)&this->FreeList;
this->Used = 0;
this->OnePastLastUsed = 0;
}
template <typename T>
void gs_list<T>::GrowList()
{
if (this->BucketCapacity == 0)
{
this->BucketCapacity = GS_LIST_DEFAULT_BUCKET_SIZE;
}
this->BucketCount += 1;
this->Buckets = (gs_list_bucket<T>*)realloc(this->Buckets, sizeof(gs_list_bucket<T>) * this->BucketCount);
gs_list_bucket<T>* NewBucket = this->Buckets + (this->BucketCount - 1);
NewBucket->Contents = (gs_list_entry<T>*)malloc(sizeof(gs_list_entry<T>) * this->BucketCapacity);
u32 IndexOffset = (this->BucketCount - 1) * this->BucketCapacity;
for (u32 i = 0; i < this->BucketCapacity; i++)
{
NewBucket->Contents[i].Handle.Index = IndexOffset + i;
NewBucket->Contents[i].Handle.Generation = 0;
}
}
template <typename T>
gs_list_entry<T>* gs_list<T>::GetEntryAtIndex(u32 Index)
{
gs_list_entry<T>* Result = 0;
u32 BucketIndex = Index / this->BucketCapacity;
u32 IndexInBucket = Index % this->BucketCapacity;
Assert(BucketIndex < this->BucketCount);
Assert(IndexInBucket < this->BucketCapacity); // this should always be true no matter what
Result = this->Buckets[BucketIndex].Contents + IndexInBucket;
return Result;
}
template <typename T>
T* gs_list<T>::GetElementAtIndex(u32 Index)
{
T* Result = 0;
gs_list_entry<T>* Entry = this->GetEntryAtIndex(Index);
Result = &Entry->Value;
return Result;
}
template <typename T>
T* gs_list<T>::GetElementWithHandle(gs_list_handle Handle)
{
T* Result = 0;
gs_list_entry<T>* Entry = this->GetEntryAtIndex(Handle.Index);
if (Entry->Handle.Generation == Handle.Generation)
{
Result = &Entry->Value;
}
return Result;
}
template <typename T>
gs_list_entry<T>* gs_list<T>::TakeFreeEntry()
{
gs_list_entry<T>* FreeEntry = (gs_list_entry<T>*)this->FreeList.Free.NextFreeEntry;
if (FreeEntry == 0 || this->BucketCapacity == 0 || this->BucketCount == 0)
{
this->FreeList.Free.NextFreeEntry = (u8*)&this->FreeList;
FreeEntry = (gs_list_entry<T>*)this->FreeList.Free.NextFreeEntry;
}
this->FreeList.Free.NextFreeEntry = FreeEntry->Free.NextFreeEntry;
if (FreeEntry == &this->FreeList)
{
if (this->Used >= this->BucketCapacity * this->BucketCount)
{
this->GrowList();
}
FreeEntry = this->GetEntryAtIndex(this->OnePastLastUsed++);
}
Assert(FreeEntry != 0);
FreeEntry->Handle.Generation++;
FreeEntry->Free.NextFreeEntry = 0;
this->Used++;
return FreeEntry;
}
template <typename T>
T* gs_list<T>::TakeElement()
{
T* Result = 0;
gs_list_entry<T>* FreeEntry = this->TakeFreeEntry();
Result = &FreeEntry->Value;
return Result;
}
template <typename T>
gs_list_handle gs_list<T>::PushElementOnList(T Ele)
{
gs_list_handle Result = {0};
gs_list_entry<T>* FreeEntry = this->TakeFreeEntry();
FreeEntry->Value = Ele;
Result = FreeEntry->Handle;
return Result;
}
template <typename T>
void gs_list<T>::FreeElementAtIndex(u32 Index)
{
Assert(Index < this->BucketCapacity * this->BucketCount);
gs_list_entry<T>* EntryToFree = this->GetEntryAtIndex(Index);
// If the entry has a value in NextFreeEntry, then it is already free
Assert(EntryToFree->Free.NextFreeEntry == 0);
EntryToFree->Free.NextFreeEntry = this->FreeList.Free.NextFreeEntry;
this->FreeList.Free.NextFreeEntry = (u8*)EntryToFree;
this->Used--;
}
template <typename T>
void gs_list<T>::FreeElementWithHandle(gs_list_handle Handle)
{
Assert(Handle.Index < this->BucketCapacity * this->BucketCount);
gs_list_entry<T>* EntryToFree = this->GetEntryAtIndex(Handle.Index);
// If the entry has a value in NextFreeEntry, then it is already free
Assert(EntryToFree->Free.NextFreeEntry == 0);
if (EntryToFree->Handle.Generation == Handle.Generation)
{
EntryToFree->Free.NextFreeEntry = this->FreeList.Free.NextFreeEntry;
this->FreeList.Free.NextFreeEntry = (u8*)EntryToFree;
this->Used--;
}
}