226 lines
6.3 KiB
C
226 lines
6.3 KiB
C
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); |