struct free_list { free_list* Next; s32 Index; }; struct bucket_index { s32 Bucket; s32 IndexInBucket; }; inline bucket_index GetBucketIndexForIndex (s32 Index, s32 BucketSize) { bucket_index Result = {}; Result.Bucket = Index / BucketSize; Result.IndexInBucket = Index % BucketSize; return Result; }; struct array_entry_handle { s32 Generation; s32 Index; }; // NOTE(Peter): This is a total bastardization of the preprocessor but it works and is // easier than writing a metaprogramming system at the moment. // TODO(Peter): Write a metaprogramming version of this that is actually easy to debug #define TYPEDEF_ARRAY(element_type) \ struct element_type##_array_entry { \ s32 Generation; \ union \ { \ element_type Entry; \ free_list Free; \ }; \ }; \ \ struct element_type##_array \ { \ element_type##_array_entry** Buckets; \ s32 BucketSize; \ s32 BucketCount; \ s32 Used; \ free_list FreeList; \ }; \ \ internal void \ GrowBuffer(element_type##_array* Buffer) \ { \ s32 NewBucketSize = sizeof(element_type##_array_entry) * Buffer->BucketSize; \ element_type##_array_entry* NewBucket = (element_type##_array_entry*)malloc(NewBucketSize); \ GSZeroMemory((u8*)NewBucket, NewBucketSize); \ \ s32 NewBucketIndex = Buffer->BucketCount++; \ if (!Buffer->Buckets) \ { \ Buffer->Buckets = (element_type##_array_entry**)malloc(sizeof(element_type##_array_entry*)); \ } \ else \ { \ Buffer->Buckets = (element_type##_array_entry**)realloc(Buffer->Buckets, sizeof(element_type##_array_entry*) * Buffer->BucketCount); \ } \ Buffer->Buckets[NewBucketIndex] = NewBucket; \ } \ \ internal element_type##_array_entry* \ GetEntryAtIndex (s32 Index, element_type##_array Buffer) \ { \ bucket_index BucketIndex = GetBucketIndexForIndex(Index, Buffer.BucketSize); \ element_type##_array_entry* Entry = Buffer.Buckets[BucketIndex.Bucket] + BucketIndex.IndexInBucket; \ return Entry; \ } \ \ internal array_entry_handle \ PushElement (element_type Data, element_type##_array* Buffer) \ { \ array_entry_handle Result = {}; \ \ if (Buffer->FreeList.Next != &Buffer->FreeList) \ { \ free_list* FreeList = Buffer->FreeList.Next; \ element_type##_array_entry* Entry = GetEntryAtIndex(FreeList->Index, *Buffer); \ Buffer->FreeList.Next = Entry->Free.Next; \ \ Result.Index = Entry->Free.Index; \ Result.Generation = Entry->Generation; \ Entry->Entry = Data; \ \ ++Buffer->Used; \ } \ else \ { \ if (Buffer->Used >= Buffer->BucketSize * Buffer->BucketCount) \ { \ GrowBuffer(Buffer); \ } \ \ s32 Index = Buffer->Used++; \ s32 BucketIndex = Index / Buffer->BucketSize; \ s32 IndexInBucket = Index % Buffer->BucketSize; \ \ Buffer->Buckets[BucketIndex][IndexInBucket].Entry = Data; \ Result.Index = Index; \ Result.Generation = Buffer->Buckets[BucketIndex][IndexInBucket].Generation; \ } \ \ return Result; \ } \ \ internal element_type* \ GetElementAtIndex (s32 Index, element_type##_array Buffer) \ { \ Assert(Index < Buffer.Used); \ element_type##_array_entry* Entry = GetEntryAtIndex(Index, Buffer); \ element_type* Result = &Entry->Entry; \ return Result; \ } \ \ internal element_type* \ GetElementWithHandle (array_entry_handle Handle, element_type##_array Buffer) \ { \ element_type* Result = 0; \ \ element_type##_array_entry* Entry = GetEntryAtIndex(Handle.Index, Buffer); \ \ if (Entry->Generation == Handle.Generation) \ { \ Result = &Entry->Entry; \ } \ \ return Result; \ } \ \ internal void \ RemoveElementAtIndex (s32 Index, element_type##_array* Buffer) \ { \ Assert(Index < Buffer->Used); \ \ element_type##_array_entry* Entry = GetEntryAtIndex(Index, *Buffer); \ ++Entry->Generation; \ Entry->Free.Index = Index; \ \ Entry->Free.Next = Buffer->FreeList.Next; \ Buffer->FreeList.Next = &Entry->Free; \ \ --Buffer->Used; \ } \ // END OF CRAZY MACRO #define TYPEDEF_CONTIGUOUS_ARRAY(element_type) \ struct element_type##_contiguous_array \ { \ element_type** Buckets; \ s32 BucketSize; \ s32 BucketCount; \ s32 Used; \ }; \ \ internal void \ GrowBuffer(element_type##_contiguous_array* Buffer) \ { \ s32 NewBucketSize = sizeof(element_type) * Buffer->BucketSize; \ element_type* NewBucket = (element_type*)malloc(NewBucketSize); \ GSZeroMemory((u8*)NewBucket, NewBucketSize); \ \ s32 NewBucketIndex = Buffer->BucketCount++; \ if (!Buffer->Buckets) \ { \ Buffer->Buckets = (element_type**)malloc(sizeof(element_type*)); \ } \ else \ { \ Buffer->Buckets = (element_type**)realloc(Buffer->Buckets, sizeof(element_type*) * Buffer->BucketCount); \ } \ Buffer->Buckets[NewBucketIndex] = NewBucket; \ } \ \ internal element_type* \ GetElementAtIndex (s32 Index, element_type##_contiguous_array Buffer) \ { \ bucket_index BucketIndex = GetBucketIndexForIndex(Index, Buffer.BucketSize); \ element_type* Entry = Buffer.Buckets[BucketIndex.Bucket] + BucketIndex.IndexInBucket; \ return Entry; \ } \ \ internal s32 \ PushElement (element_type Data, element_type##_contiguous_array* Buffer) \ { \ s32 Result = -1; \ \ if (Buffer->Used >= Buffer->BucketSize * Buffer->BucketCount) \ { \ GrowBuffer(Buffer); \ } \ \ s32 Index = Buffer->Used++; \ s32 BucketIndex = Index / Buffer->BucketSize; \ s32 IndexInBucket = Index % Buffer->BucketSize; \ \ Buffer->Buckets[BucketIndex][IndexInBucket] = Data; \ Result = Index; \ return Result; \ } \ \ internal void \ RemoveElementAtIndex (s32 Index, element_type##_contiguous_array* Buffer) \ { \ Assert(Index < Buffer->Used); \ \ bucket_index IndexToRemove = GetBucketIndexForIndex(Index, Buffer->BucketSize); \ bucket_index LastIndex = GetBucketIndexForIndex(Buffer->Used - 1, Buffer->BucketSize); \ element_type ValueAtLastIndex = Buffer->Buckets[LastIndex.Bucket][LastIndex.IndexInBucket]; \ Buffer->Buckets[IndexToRemove.Bucket][IndexToRemove.IndexInBucket] = ValueAtLastIndex; \ --Buffer->Used; \ } \ // END OF CRAZY MACRO TYPEDEF_ARRAY(array_entry_handle); TYPEDEF_CONTIGUOUS_ARRAY(array_entry_handle);