File Cleanup, removed unused files, and pulled gs_libs into the codebase so that its a standalone codebase
This commit is contained in:
parent
af11a85e94
commit
d23a2a68d4
|
@ -0,0 +1,130 @@
|
||||||
|
//
|
||||||
|
// gs_bucket.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
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct gs_bucket_bucket
|
||||||
|
{
|
||||||
|
T* Contents;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define GS_LIST_DEFAULT_BUCKET_SIZE 256
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class gs_bucket {
|
||||||
|
public:
|
||||||
|
u32 BucketSize;
|
||||||
|
u32 BucketCount;
|
||||||
|
gs_bucket_bucket<T>* Buckets;
|
||||||
|
|
||||||
|
u32 Used;
|
||||||
|
|
||||||
|
gs_bucket();
|
||||||
|
gs_bucket(u32 BucketSize);
|
||||||
|
|
||||||
|
void GrowBucket();
|
||||||
|
|
||||||
|
T* GetElementAtIndex(u32 Index);
|
||||||
|
T* TakeElement();
|
||||||
|
u32 PushElementOnBucket(T Ele);
|
||||||
|
|
||||||
|
void FreeElementAtIndex(u32 Index);
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
gs_bucket<T>::gs_bucket()
|
||||||
|
{
|
||||||
|
this->BucketSize = 0;
|
||||||
|
this->BucketCount = 0;
|
||||||
|
this->Buckets = 0;
|
||||||
|
this->Used = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
gs_bucket<T>::gs_bucket(u32 BucketSize)
|
||||||
|
{
|
||||||
|
this->BucketSize = BucketSize;
|
||||||
|
this->BucketCount = 0;
|
||||||
|
this->Buckets = 0;
|
||||||
|
this->Used = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void gs_bucket<T>::GrowBucket()
|
||||||
|
{
|
||||||
|
if (this->BucketCount == 0)
|
||||||
|
{
|
||||||
|
// First Grow Attempt
|
||||||
|
this->Buckets = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->BucketSize == 0)
|
||||||
|
{
|
||||||
|
this->BucketSize = GS_LIST_DEFAULT_BUCKET_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->BucketCount += 1;
|
||||||
|
this->Buckets = (gs_bucket_bucket<T>*)realloc(this->Buckets, sizeof(gs_bucket_bucket<T>) * this->BucketCount);
|
||||||
|
gs_bucket_bucket<T>* NewBucket = this->Buckets + (this->BucketCount - 1);
|
||||||
|
NewBucket->Contents = (T*)malloc(sizeof(T) * this->BucketSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T* gs_bucket<T>::GetElementAtIndex(u32 Index)
|
||||||
|
{
|
||||||
|
Assert(Index < this->BucketSize * this->BucketCount);
|
||||||
|
Assert(Index < this->Used);
|
||||||
|
|
||||||
|
u32 BucketIndex = Index / this->BucketSize;
|
||||||
|
u32 IndexInBucket = Index % this->BucketSize;
|
||||||
|
|
||||||
|
T* Result = this->Buckets[BucketIndex].Contents + IndexInBucket;
|
||||||
|
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T* gs_bucket<T>::TakeElement()
|
||||||
|
{
|
||||||
|
if (this->Used >= this->BucketSize * this->BucketCount)
|
||||||
|
{
|
||||||
|
this->GrowBucket();
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 Index = this->Used++;
|
||||||
|
T* Result = this->GetElementAtIndex(Index);
|
||||||
|
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
u32 gs_bucket<T>::PushElementOnBucket(T Ele)
|
||||||
|
{
|
||||||
|
u32 ResultIndex = 0;
|
||||||
|
|
||||||
|
if (this->Used >= this->BucketSize * this->BucketCount)
|
||||||
|
{
|
||||||
|
this->GrowBucket();
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultIndex = this->Used++;
|
||||||
|
T* FreeElement = this->GetElementAtIndex(ResultIndex);
|
||||||
|
*FreeElement = Ele;
|
||||||
|
|
||||||
|
return ResultIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void gs_bucket<T>::FreeElementAtIndex(u32 Index)
|
||||||
|
{
|
||||||
|
Assert(Index < this->BucketSize * this->BucketCount);
|
||||||
|
|
||||||
|
T* ToFillIn = this->GetElementAtIndex(Index);
|
||||||
|
T* ToFree = this->GetElementAtIndex(--this->Used);
|
||||||
|
*ToFillIn = *ToFree;
|
||||||
|
}
|
|
@ -0,0 +1,468 @@
|
||||||
|
#ifndef GS_LANGUAGE_H
|
||||||
|
|
||||||
|
#if defined(_WIN32) || defined(_WIN64) || defined(__WIN32__)
|
||||||
|
#include <intrin.h>
|
||||||
|
|
||||||
|
// TODO(Peter): Get rid of math.h
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#elif defined(__APPLE__) && defined(__MAC__)
|
||||||
|
// TODO(Peter):
|
||||||
|
|
||||||
|
#else // Std lib
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define internal static
|
||||||
|
#define local_persist static
|
||||||
|
#define global_variable static
|
||||||
|
|
||||||
|
|
||||||
|
#if !defined(GS_TYPES)
|
||||||
|
|
||||||
|
#define GSINT64(s) (s) ## L
|
||||||
|
#define GSUINT64(s) (s) ## UL
|
||||||
|
|
||||||
|
typedef signed char b8;
|
||||||
|
typedef short int b16;
|
||||||
|
typedef int b32;
|
||||||
|
typedef long long int b64;
|
||||||
|
|
||||||
|
typedef unsigned char u8;
|
||||||
|
typedef unsigned short int u16;
|
||||||
|
typedef unsigned int u32;
|
||||||
|
typedef unsigned long long int u64;
|
||||||
|
|
||||||
|
typedef signed char s8;
|
||||||
|
typedef short int s16;
|
||||||
|
typedef int s32;
|
||||||
|
typedef long long int s64;
|
||||||
|
|
||||||
|
typedef float r32;
|
||||||
|
typedef double r64;
|
||||||
|
|
||||||
|
#ifndef _STDINT
|
||||||
|
|
||||||
|
#define INT8_MIN (-128)
|
||||||
|
#define INT16_MIN (-32767-1)
|
||||||
|
#define INT32_MIN (-2147483647-1)
|
||||||
|
#define INT64_MIN (-GSINT64(9223372036854775807)-1)
|
||||||
|
|
||||||
|
#define INT8_MAX (127)
|
||||||
|
#define INT16_MAX (32767)
|
||||||
|
#define INT32_MAX (2147483647)
|
||||||
|
#define INT64_MAX (GSINT64(9223372036854775807))
|
||||||
|
|
||||||
|
#define UINT8_MAX (255)
|
||||||
|
#define UINT16_MAX (65535)
|
||||||
|
#define UINT32_MAX (4294967295U)
|
||||||
|
#define UINT64_MAX (GSUINT64(18446744073709551615))
|
||||||
|
|
||||||
|
#endif // _STDINT
|
||||||
|
|
||||||
|
#define FLOAT_MIN (1.175494351e-38F)
|
||||||
|
#define FLOAT_MAX (3.402823466e+38F)
|
||||||
|
#define DOUBLE_MIN (2.2250738585072014e-308)
|
||||||
|
#define DOUBLE_MAX (1.7976931348623158e+308)
|
||||||
|
|
||||||
|
#define Kilobytes(Value) ((Value) * 1024)
|
||||||
|
#define Megabytes(Value) (Kilobytes(Value) * 1024)
|
||||||
|
#define Gigabytes(Value) (Megabytes(Value) * 1024)
|
||||||
|
#define Terabytes(Value) (Gigabytes(Value) * 1024)
|
||||||
|
|
||||||
|
#ifndef PI
|
||||||
|
#define PI 3.14159265359
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define TAU 6.2831853071
|
||||||
|
#define PI_OVER_180 0.01745329251f
|
||||||
|
|
||||||
|
#define GS_TYPES
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
|
||||||
|
static void DebugPrint(char* Format, ...);
|
||||||
|
|
||||||
|
#if !defined(Assert)
|
||||||
|
// NOTE(peter): this writes to address 0 which is always illegal and will cause a crash
|
||||||
|
#define Assert(expression) if(!(expression)){ *((int *)0) = 5; }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define DEBUG_IF(condition) if (condition)
|
||||||
|
|
||||||
|
#define InvalidCodePath Assert(0)
|
||||||
|
#define InvalidDefaultCase default: { Assert(0); }
|
||||||
|
#define DebugBreak __debugbreak()
|
||||||
|
|
||||||
|
#define STBI_ASSERT(x) Assert(x)
|
||||||
|
|
||||||
|
#ifdef GS_TEST_SUTE
|
||||||
|
#define TestClean(v, c) SuccessCount += Test(v, c, &TestCount)
|
||||||
|
internal s32
|
||||||
|
Test(b32 Result, char* Description, s32* Count)
|
||||||
|
{
|
||||||
|
char* Passed = (Result ? "Success" : "Failed");
|
||||||
|
if (!Result)
|
||||||
|
DebugPrint("%s:\n................................................%s\n\n", Description, Passed);
|
||||||
|
|
||||||
|
*Count = *Count + 1;
|
||||||
|
return (Result ? 1 : 0);
|
||||||
|
}
|
||||||
|
#endif // GS_TEST_SUTE
|
||||||
|
|
||||||
|
#ifndef GS_LANGUAGE_NO_PROFILER_DEFINES
|
||||||
|
#ifndef DEBUG_TRACK_SCOPE
|
||||||
|
#define DEBUG_TRACK_SCOPE(a)
|
||||||
|
#endif // DEBUG_TRACK_SCOPE
|
||||||
|
#ifndef DEBUG_TRACK_FUNCTION
|
||||||
|
#define DEBUG_TRACK_FUNCTION
|
||||||
|
#endif // DEBUG_TRACK_FUNCTION
|
||||||
|
#endif // GS_LANGUAGE_NO_PROFILER_DEFINES
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define Assert(expression)
|
||||||
|
#define InvalidCodePath
|
||||||
|
#define DEBUG_IF(condition)
|
||||||
|
|
||||||
|
#ifndef GS_LANGUAGE_NO_PROFILER_DEFINES
|
||||||
|
#ifndef DEBUG_TRACK_SCOPE
|
||||||
|
#define DEBUG_TRACK_SCOPE(a)
|
||||||
|
#endif // DEBUG_TRACK_SCOPE
|
||||||
|
#ifndef DEBUG_TRACK_FUNCTION
|
||||||
|
#define DEBUG_TRACK_FUNCTION
|
||||||
|
#endif // DEBUG_TRACK_FUNCTION
|
||||||
|
#endif // GS_LANGUAGE_NO_PROFILER_DEFINES
|
||||||
|
|
||||||
|
#endif // DEBUG
|
||||||
|
|
||||||
|
#ifndef GS_LANGUAGE_MATH
|
||||||
|
|
||||||
|
#define GSArrayLength(arr) (sizeof(arr) / sizeof(arr[0]))
|
||||||
|
|
||||||
|
#define GSZeroStruct(data) GSZeroMemory_((u8*)(&(data)), sizeof(data))
|
||||||
|
#define GSZeroMemory(mem, size) GSZeroMemory_((u8*)(mem), (size))
|
||||||
|
#define GSZeroArray(arr, type, count) GSZeroMemory_((u8*)(arr), (sizeof(type) * count))
|
||||||
|
static void
|
||||||
|
GSZeroMemory_ (u8* Memory, s32 Size)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < Size; i++) { Memory[i] = 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
#define GSMemCopy(from, to, size) GSMemCopy_((u8*)from, (u8*)to, size)
|
||||||
|
static void
|
||||||
|
GSMemCopy_ (u8* From, u8* To, s32 Size)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < Size; i++) { To[i] = From[i]; }
|
||||||
|
}
|
||||||
|
|
||||||
|
#define GSMemSet(buffer, value, size) GSMemSet_((u8*)buffer, value, size)
|
||||||
|
internal void
|
||||||
|
GSMemSet_ (u8* Buffer, u8 Value, s32 Length)
|
||||||
|
{
|
||||||
|
u8* Cursor = Buffer;
|
||||||
|
for (s32 i = 0; i < Length; i++)
|
||||||
|
{
|
||||||
|
*Cursor++ = Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define GSMinDef(type) static type GSMin(type A, type B) { return (A < B ? A : B); }
|
||||||
|
GSMinDef(s8)
|
||||||
|
GSMinDef(s16)
|
||||||
|
GSMinDef(s32)
|
||||||
|
GSMinDef(s64)
|
||||||
|
GSMinDef(u8)
|
||||||
|
GSMinDef(u16)
|
||||||
|
GSMinDef(u32)
|
||||||
|
GSMinDef(u64)
|
||||||
|
GSMinDef(r32)
|
||||||
|
GSMinDef(r64)
|
||||||
|
#undef GSMinDef
|
||||||
|
|
||||||
|
#define GSMaxDef(type) static type GSMax(type A, type B) { return (A > B ? A : B); }
|
||||||
|
GSMaxDef(s8)
|
||||||
|
GSMaxDef(s16)
|
||||||
|
GSMaxDef(s32)
|
||||||
|
GSMaxDef(s64)
|
||||||
|
GSMaxDef(u8)
|
||||||
|
GSMaxDef(u16)
|
||||||
|
GSMaxDef(u32)
|
||||||
|
GSMaxDef(u64)
|
||||||
|
GSMaxDef(r32)
|
||||||
|
GSMaxDef(r64)
|
||||||
|
#undef GSMaxDef
|
||||||
|
|
||||||
|
inline b32 XOR(b32 A, b32 B)
|
||||||
|
{
|
||||||
|
b32 Result = (A == !B);
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
#define GSClampDef(type) static type GSClamp(type Min, type V, type Max) { \
|
||||||
|
type Result = V; \
|
||||||
|
if (V < Min) { Result = Min; } \
|
||||||
|
if (V > Max) { Result = Max; } \
|
||||||
|
return Result; \
|
||||||
|
}
|
||||||
|
GSClampDef(s8)
|
||||||
|
GSClampDef(s16)
|
||||||
|
GSClampDef(s32)
|
||||||
|
GSClampDef(s64)
|
||||||
|
GSClampDef(u8)
|
||||||
|
GSClampDef(u16)
|
||||||
|
GSClampDef(u32)
|
||||||
|
GSClampDef(u64)
|
||||||
|
GSClampDef(r32)
|
||||||
|
GSClampDef(r64)
|
||||||
|
#undef GSClampDef
|
||||||
|
|
||||||
|
#define GSClamp01Def(type) static type GSClamp01(type V) { \
|
||||||
|
type Min = 0; type Max = 1; \
|
||||||
|
type Result = V; \
|
||||||
|
if (V < Min) { Result = Min; } \
|
||||||
|
if (V > Max) { Result = Max; } \
|
||||||
|
return Result; \
|
||||||
|
}
|
||||||
|
GSClamp01Def(r32)
|
||||||
|
GSClamp01Def(r64)
|
||||||
|
#undef GSClamp01Def
|
||||||
|
|
||||||
|
#define GSAbsDef(type) static type GSAbs(type A) { return (A < 0 ? -A : A); }
|
||||||
|
GSAbsDef(s8)
|
||||||
|
GSAbsDef(s16)
|
||||||
|
GSAbsDef(s32)
|
||||||
|
GSAbsDef(s64)
|
||||||
|
GSAbsDef(r32)
|
||||||
|
GSAbsDef(r64)
|
||||||
|
#undef GSAbsDef
|
||||||
|
|
||||||
|
#define GSPowDef(type) static type GSPow(type N, s32 Power) { \
|
||||||
|
type Result = N; \
|
||||||
|
for(s32 i = 1; i < Power; i++) { Result *= N; } \
|
||||||
|
return Result; \
|
||||||
|
}
|
||||||
|
GSPowDef(s8)
|
||||||
|
GSPowDef(s16)
|
||||||
|
GSPowDef(s32)
|
||||||
|
GSPowDef(s64)
|
||||||
|
GSPowDef(u8)
|
||||||
|
GSPowDef(u16)
|
||||||
|
GSPowDef(u32)
|
||||||
|
GSPowDef(u64)
|
||||||
|
GSPowDef(r32)
|
||||||
|
GSPowDef(r64)
|
||||||
|
#undef GSPowDef
|
||||||
|
|
||||||
|
|
||||||
|
#define GSLerpDef(type) type GSLerp(type A, type B, type Percent) { return (A * (1.0f - Percent))+(B * Percent);}
|
||||||
|
GSLerpDef(r32)
|
||||||
|
GSLerpDef(r64)
|
||||||
|
#undef GSLerpDef
|
||||||
|
|
||||||
|
static r32 GSSqrt(r32 V)
|
||||||
|
{
|
||||||
|
r32 Result = _mm_cvtss_f32(_mm_sqrt_ss(_mm_set_ss(V)));
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
#if 0
|
||||||
|
// TODO(Peter): Need a way to split the input into two f32's to supply to _mm_sqrt_sd
|
||||||
|
static r64 GSSqrt(r64 V)
|
||||||
|
{
|
||||||
|
r64 Result = _mm_cvtsd_f64(_mm_sqrt_sd(_mm_set_sd(V)));
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static r32 DegreesToRadians (r32 Degrees) { return Degrees * PI_OVER_180; }
|
||||||
|
static r64 DegreesToRadians (r64 Degrees) { return Degrees * PI_OVER_180; }
|
||||||
|
|
||||||
|
#define GSIsPowerOfTwoDef(type) static type IsPowerOfTwo(type V) { return (V & (V - 1)) == 0; }
|
||||||
|
GSIsPowerOfTwoDef(u8);
|
||||||
|
GSIsPowerOfTwoDef(u16);
|
||||||
|
GSIsPowerOfTwoDef(u32);
|
||||||
|
GSIsPowerOfTwoDef(u64);
|
||||||
|
#undef GSIsPowerOfTwoDef
|
||||||
|
|
||||||
|
#define GSIsOddDef(type) inline type IsOdd(type V) { return (V & 1); }
|
||||||
|
GSIsOddDef(u8);
|
||||||
|
GSIsOddDef(u16);
|
||||||
|
GSIsOddDef(u32);
|
||||||
|
GSIsOddDef(u64);
|
||||||
|
GSIsOddDef(s8);
|
||||||
|
GSIsOddDef(s16);
|
||||||
|
GSIsOddDef(s32);
|
||||||
|
GSIsOddDef(s64);
|
||||||
|
#undef GSIsOddDef
|
||||||
|
|
||||||
|
#define GSIntDivideRoundUpDef(type) static type IntegerDivideRoundUp (type A, type B) { r32 Result = (r32)A / (r32)B; Result += .99999f; return (type)Result; }
|
||||||
|
GSIntDivideRoundUpDef(u8);
|
||||||
|
GSIntDivideRoundUpDef(u16);
|
||||||
|
GSIntDivideRoundUpDef(u32);
|
||||||
|
GSIntDivideRoundUpDef(u64);
|
||||||
|
GSIntDivideRoundUpDef(s8);
|
||||||
|
GSIntDivideRoundUpDef(s16);
|
||||||
|
GSIntDivideRoundUpDef(s32);
|
||||||
|
GSIntDivideRoundUpDef(s64);
|
||||||
|
#undef GSIntDivideRoundUpDef
|
||||||
|
|
||||||
|
#define GSRemapDef(type) \
|
||||||
|
static type GSRemap(type Value, type OldMin, type OldMax, type NewMin, type NewMax) { \
|
||||||
|
type Result = (Value - OldMin) / (OldMax - OldMin); \
|
||||||
|
Result = (Result * (NewMax - NewMin)) + NewMin; \
|
||||||
|
return Result; \
|
||||||
|
}
|
||||||
|
GSRemapDef(u8);
|
||||||
|
GSRemapDef(u16);
|
||||||
|
GSRemapDef(u32);
|
||||||
|
GSRemapDef(u64);
|
||||||
|
GSRemapDef(s8);
|
||||||
|
GSRemapDef(s16);
|
||||||
|
GSRemapDef(s32);
|
||||||
|
GSRemapDef(s64);
|
||||||
|
GSRemapDef(r32);
|
||||||
|
GSRemapDef(r64);
|
||||||
|
#undef GSRemapDef
|
||||||
|
|
||||||
|
static r32
|
||||||
|
GSFloor(r32 Value)
|
||||||
|
{
|
||||||
|
return floor(Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static r32
|
||||||
|
GSFract(r32 Value)
|
||||||
|
{
|
||||||
|
return Value - GSFloor(Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static r32
|
||||||
|
GSModF(r32 Value, r32 Int)
|
||||||
|
{
|
||||||
|
r32 Div = Value / Int;
|
||||||
|
r32 Fract = GSAbs(GSFract(Div));
|
||||||
|
return Int * Fract;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define GSTrigFunctionDef(name, type, func) static type name(type V) { return func(V); }
|
||||||
|
GSTrigFunctionDef(GSSin, r32, sinf);
|
||||||
|
GSTrigFunctionDef(GSSin, r64, sin);
|
||||||
|
GSTrigFunctionDef(GSCos, r32, cosf);
|
||||||
|
GSTrigFunctionDef(GSCos, r64, cos);
|
||||||
|
GSTrigFunctionDef(GSTan, r32, tanf);
|
||||||
|
GSTrigFunctionDef(GSTan, r64, tan);
|
||||||
|
#undef GSTrigFunctionDef
|
||||||
|
|
||||||
|
static u8
|
||||||
|
RoundToNearestPowerOfTwo (u8 V)
|
||||||
|
{
|
||||||
|
u8 Result = 0;
|
||||||
|
|
||||||
|
if (IsPowerOfTwo(V))
|
||||||
|
{
|
||||||
|
Result = V;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Result = V - 1;
|
||||||
|
Result |= Result >> 1;
|
||||||
|
Result |= Result >> 2;
|
||||||
|
Result |= Result >> 4;
|
||||||
|
Result += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u16
|
||||||
|
RoundToNearestPowerOfTwo (u16 V)
|
||||||
|
{
|
||||||
|
u16 Result = 0;
|
||||||
|
|
||||||
|
if (IsPowerOfTwo(V))
|
||||||
|
{
|
||||||
|
Result = V;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Result = V - 1;
|
||||||
|
Result |= Result >> 1;
|
||||||
|
Result |= Result >> 2;
|
||||||
|
Result |= Result >> 4;
|
||||||
|
Result |= Result >> 8;
|
||||||
|
Result += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u32
|
||||||
|
RoundToNearestPowerOfTwo (u32 V)
|
||||||
|
{
|
||||||
|
u32 Result = 0;
|
||||||
|
|
||||||
|
if (IsPowerOfTwo(V))
|
||||||
|
{
|
||||||
|
Result = V;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Result = V - 1;
|
||||||
|
Result |= Result >> 1;
|
||||||
|
Result |= Result >> 2;
|
||||||
|
Result |= Result >> 4;
|
||||||
|
Result |= Result >> 8;
|
||||||
|
Result |= Result >> 16;
|
||||||
|
Result += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u64
|
||||||
|
RoundToNearestPowerOfTwo (u64 V)
|
||||||
|
{
|
||||||
|
u64 Result = 0;
|
||||||
|
|
||||||
|
if (IsPowerOfTwo(V))
|
||||||
|
{
|
||||||
|
Result = V;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Result = V - 1;
|
||||||
|
Result |= Result >> 1;
|
||||||
|
Result |= Result >> 2;
|
||||||
|
Result |= Result >> 4;
|
||||||
|
Result |= Result >> 8;
|
||||||
|
Result |= Result >> 16;
|
||||||
|
Result |= Result >> 32;
|
||||||
|
Result += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define GS_LANGUAGE_MATH
|
||||||
|
#endif // GS_LANGUAGE_MATH
|
||||||
|
|
||||||
|
static u32
|
||||||
|
HostToNetU32(u32 In)
|
||||||
|
{
|
||||||
|
unsigned char *s = (unsigned char *)&In;
|
||||||
|
u32 Result = (u32)(s[0] << 24 | s[1] << 16 | s[2] << 8 | s[3]);
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u16
|
||||||
|
HostToNetU16(u16 In)
|
||||||
|
{
|
||||||
|
unsigned char *s = (unsigned char *)&In;
|
||||||
|
u16 Result = (u16)(s[0] << 8 | s[1]);
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define GS_LANGUAGE_H
|
||||||
|
#endif
|
|
@ -0,0 +1,247 @@
|
||||||
|
//
|
||||||
|
// 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--;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,614 @@
|
||||||
|
// File: gs_memory_arena.h
|
||||||
|
// Description: Single header file library that defines a push-only memory arena
|
||||||
|
// Author: Peter Slattery
|
||||||
|
// Date Created: 2019-12-22
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// -----------------
|
||||||
|
// Set Up
|
||||||
|
// -----------------
|
||||||
|
//
|
||||||
|
// Include 'gs_memory_arena.h' in a file and start using it! (Simple! Nice!)
|
||||||
|
//
|
||||||
|
// -----------------
|
||||||
|
// Usage
|
||||||
|
// -----------------
|
||||||
|
// Simply create a memory_arena and use PushSize, PushStruct, or PushArray
|
||||||
|
// to allocate out of it.
|
||||||
|
// See Example Program below.
|
||||||
|
//
|
||||||
|
// While there are options you can set (see Options below), the library adheres
|
||||||
|
// to a 'zero-is-initialization' policy, that is, a memory_arena initialized to
|
||||||
|
// zero, under all default options, will 'just work'.
|
||||||
|
//
|
||||||
|
// Alignment:
|
||||||
|
// By default, the Push functions use 4 byte alignment
|
||||||
|
// If you need to control the alignment of an allocation, there are variants of the
|
||||||
|
// Push functions that allow for this: PushSizeAligned, PushStructAligned, and PushArrayAligned
|
||||||
|
// These functions simply take a final parameter which specifies the alignment.
|
||||||
|
// Note: Alignment must be a power of two
|
||||||
|
//
|
||||||
|
// -----------------
|
||||||
|
// Options
|
||||||
|
// -----------------
|
||||||
|
//
|
||||||
|
// DEBUG:
|
||||||
|
// Define DEBUG for debug functionality.
|
||||||
|
//
|
||||||
|
// To override the default assert function define GSMem_Assert(expression)
|
||||||
|
// before inluding this file.
|
||||||
|
//
|
||||||
|
// GS_MEMORY_NO_STD_LIBS:
|
||||||
|
// if you don't want stdlib.h to be included, define GS_MEMORY_NO_STD_LIBS
|
||||||
|
// before including this file.
|
||||||
|
// Note that if you do this, zero-is-initialization will no longer work for
|
||||||
|
// memory_arenas. You must either:
|
||||||
|
// 1. Set each memory_arena's Alloc and Realloc so they can grow fields
|
||||||
|
// 2. Set each memory_arena's ExpansionRule to ExpansionRule_Disallowed
|
||||||
|
// If DEBUG is defined, the program will assert if one of the 2 rules above
|
||||||
|
// aren't followed.
|
||||||
|
//
|
||||||
|
// memory_arena.Alloc and memory_arena.Realloc
|
||||||
|
// By default, memory_arena's will use malloc and realloc to grow.
|
||||||
|
// You can override this by setting the Alloc and Realloc function pointers
|
||||||
|
// of a memory_arena. See the example program below for an implementation of this.
|
||||||
|
//
|
||||||
|
// GS_MEMORY_BUFFER_SIZE:
|
||||||
|
// This defines the minimum buffer size for memory_arena's. If an arena doesn't have
|
||||||
|
// room to fit an allocation, it will allocate a new buffer no smaller than GS_MEMORY_BUFFER_SIZE
|
||||||
|
// and place the allocation in the new buffer.
|
||||||
|
// By default this is 4096 bytes. To override, define GS_MEMORY_BUFFER_SIZE before including
|
||||||
|
// this file
|
||||||
|
//
|
||||||
|
// GS_MEMORY_TRACK_ALLOCATIONS:
|
||||||
|
// If you want to keep records of each allocation performed in every arena, define
|
||||||
|
// GS_MEMORY_TRACK_ALLOCATIONS before including this file.
|
||||||
|
// When defined, memory arenas gain fields that allow them to keep a list of every
|
||||||
|
// allocation they contain. It also adds a footer on the end of each allocation that
|
||||||
|
// can be checked to ensure there are no writes to allocations that overflow their bounds
|
||||||
|
// Note that calling ClearArena also clears this list
|
||||||
|
// You can then call AssertAllocationsNoOverflow occasionally throughout your program
|
||||||
|
// to check that no allocations have been written beyond their boundaries
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Example Program
|
||||||
|
// (this compiles - copy it into its own file though)
|
||||||
|
#if 0
|
||||||
|
// #include "gs_memory_arena.h"
|
||||||
|
|
||||||
|
// Places the characters 'gs' at the end of each allocation. This would allow for an external
|
||||||
|
// function to check that we haven't written past the end of an allocation
|
||||||
|
void* MallocWrapper(gs_mem_u32 Size)
|
||||||
|
{
|
||||||
|
int SizeWithFooter = Size + (sizeof(char) * 2);
|
||||||
|
void* Result = malloc(SizeWithFooter);
|
||||||
|
char* Footer = (char*)(Result + Size);
|
||||||
|
Footer[0] = 'g';
|
||||||
|
Footer[1] = 's';
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* ReallocWrapper(void* Address, gs_mem_u32 Size)
|
||||||
|
{
|
||||||
|
return realloc(Address, Size);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int ArgCount, char** Args)
|
||||||
|
{
|
||||||
|
memory_arena Arena = {};
|
||||||
|
// Uncomment these lines for an example of how you can implement custom allocation functions
|
||||||
|
// Arena.Alloc = MallocWrapper;
|
||||||
|
// Arena.Realloc = ReallocWrapper;
|
||||||
|
|
||||||
|
int ArrayLength = 10;
|
||||||
|
|
||||||
|
int* A = PushArray(&Arena, int, ArrayLength);
|
||||||
|
int* B = PushArray(&Arena, int, ArrayLength);
|
||||||
|
int* C = PushArray(&Arena, int, ArrayLength);
|
||||||
|
int* D = PushArrayAligned(&Arena, int, ArrayLength, 8);
|
||||||
|
int* E = PushArrayAligned(&Arena, int, ArrayLength, 16);
|
||||||
|
|
||||||
|
// Just ensure that we can actually write to each address of each array
|
||||||
|
for (s32 i = 0; i < ArrayLength; i++)
|
||||||
|
{
|
||||||
|
A[i] = i;
|
||||||
|
B[i] = i;
|
||||||
|
C[i] = i;
|
||||||
|
D[i] = i;
|
||||||
|
E[i] = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClearArena(&Arena);
|
||||||
|
|
||||||
|
A = PushArray(&Arena, int, ArrayLength);
|
||||||
|
for (s32 i = 0; i < ArrayLength; i++)
|
||||||
|
{
|
||||||
|
A[i] = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// -------------------
|
||||||
|
// Begin Library
|
||||||
|
// -------------------
|
||||||
|
#ifndef GS_MEMORY_ARENA_H
|
||||||
|
|
||||||
|
#ifndef GS_MEMORY_NO_STD_LIBS
|
||||||
|
|
||||||
|
// NOTE(Peter): We use this so that we can fall back on malloc and realloc
|
||||||
|
// in the event that a memory_arena needs to grow but doesn't have a
|
||||||
|
// alloc or realloc function pointer assigned to it.
|
||||||
|
//
|
||||||
|
// See GrowArena to see where this is used
|
||||||
|
//
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef unsigned char gs_mem_u8;
|
||||||
|
typedef unsigned int gs_mem_u32;
|
||||||
|
|
||||||
|
typedef unsigned long long int gs_mem_u64;
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
#if !defined(GSMem_Assert)
|
||||||
|
#define GSMem_Assert(expression) \
|
||||||
|
if(!(expression)) { \
|
||||||
|
*((int *)0) = 5; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#define GSMem_Assert(expression)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum gs_memory_expansion_rule
|
||||||
|
{
|
||||||
|
MemoryExpansion_Allowed, // Zero is initialization lets the memory grow on its own
|
||||||
|
MemoryExpansion_OnlyIfFunctionsProvided,
|
||||||
|
MemoryExpansion_Disallowed,
|
||||||
|
MemoryExpansion_Count,
|
||||||
|
};
|
||||||
|
|
||||||
|
// NOTE(Peter):
|
||||||
|
// This rule is only here to allow for taking arena snapshots. The problem this solves
|
||||||
|
// is if you take a snapshot while there are 'holes' in memory_buffers behind the
|
||||||
|
// most recently added memory_buffer, take a snapshot of that arena, then push something
|
||||||
|
// on that fits in one of those holes, we will fill the hole and be unable to track/free
|
||||||
|
// that addition via the snapshot construct.
|
||||||
|
//
|
||||||
|
// By requiring that allocations in a buffer only come from the most recent memory_buffer
|
||||||
|
// we can very easily rewind the buffer to the correct location.
|
||||||
|
// Hence FindAddress_InLastBufferOnly
|
||||||
|
enum gs_memory_find_address_rule
|
||||||
|
{
|
||||||
|
FindAddress_InAnyBuffer,
|
||||||
|
FindAddress_InLastBufferOnly,
|
||||||
|
FindAddress_Count,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef void* gs_memory_alloc(gs_mem_u32 Size);
|
||||||
|
typedef void* gs_memory_realloc(void* Address, gs_mem_u32 OldSize, gs_mem_u32 NewSize);
|
||||||
|
typedef void gs_memory_free(void* Address, gs_mem_u32 Size);
|
||||||
|
|
||||||
|
#ifndef GS_MEMORY_BUFFER_SIZE
|
||||||
|
#define GS_MEMORY_BUFFER_SIZE 1024
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define GS_MEMORY_FOOTER_SIZE 4
|
||||||
|
#define GS_MEMORY_FOOTER_0 'g'
|
||||||
|
#define GS_MEMORY_FOOTER_1 's'
|
||||||
|
#define GS_MEMORY_FOOTER_2 'p'
|
||||||
|
#define GS_MEMORY_FOOTER_3 's'
|
||||||
|
|
||||||
|
struct tracked_allocation
|
||||||
|
{
|
||||||
|
gs_mem_u8* Head;
|
||||||
|
gs_mem_u8* Footer;
|
||||||
|
char* File;
|
||||||
|
gs_mem_u32 LineNumber;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define GS_MEM_TRACKED_ALLOCATION_BUFFER_SIZE 512
|
||||||
|
struct tracked_allocation_buffer
|
||||||
|
{
|
||||||
|
tracked_allocation Buffer[GS_MEM_TRACKED_ALLOCATION_BUFFER_SIZE];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct memory_buffer
|
||||||
|
{
|
||||||
|
gs_mem_u8* Buffer;
|
||||||
|
gs_mem_u32 Size;
|
||||||
|
gs_mem_u32 Used;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct memory_arena
|
||||||
|
{
|
||||||
|
memory_buffer* Buffers;
|
||||||
|
gs_mem_u32 BuffersCount;
|
||||||
|
|
||||||
|
gs_mem_u32 TotalUsed;
|
||||||
|
gs_mem_u32 TotalSize;
|
||||||
|
|
||||||
|
gs_memory_find_address_rule FindAddressRule;
|
||||||
|
gs_memory_expansion_rule ExpansionRule;
|
||||||
|
gs_memory_alloc* Alloc;
|
||||||
|
gs_memory_realloc* Realloc;
|
||||||
|
|
||||||
|
#ifdef GS_MEMORY_TRACK_ALLOCATIONS
|
||||||
|
tracked_allocation_buffer** AllocationBuffers;
|
||||||
|
gs_mem_u32 AllocationBuffersCount;
|
||||||
|
gs_mem_u32 AllocationsUsed;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
struct address_and_buffer
|
||||||
|
{
|
||||||
|
memory_buffer* Buffer;
|
||||||
|
gs_mem_u64 Address;
|
||||||
|
gs_mem_u32 SizeWithAlignment;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct arena_snapshot
|
||||||
|
{
|
||||||
|
gs_mem_u32 ArenaUsedAtSnapshot;
|
||||||
|
gs_mem_u32 HeadBufferUsedAtSnapshot;
|
||||||
|
gs_mem_u32 HeadBufferAtSnapshot;
|
||||||
|
memory_arena* Arena;
|
||||||
|
|
||||||
|
#ifdef GS_MEMORY_TRACK_ALLOCATIONS
|
||||||
|
gs_mem_u32 AllocationsUsedAtSnapshot;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
FreeMemoryArena(memory_arena* Arena, gs_memory_free* Free = 0)
|
||||||
|
{
|
||||||
|
if (Free)
|
||||||
|
{
|
||||||
|
for (gs_mem_u32 i = 0; i < Arena->BuffersCount; i++)
|
||||||
|
{
|
||||||
|
memory_buffer* Buffer = Arena->Buffers + i;
|
||||||
|
Free(Buffer->Buffer, Buffer->Size);
|
||||||
|
}
|
||||||
|
Free(Arena->Buffers, sizeof(memory_buffer) * Arena->BuffersCount);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#ifdef GS_MEMORY_NO_STD_LIBS
|
||||||
|
GSMem_Assert(0);
|
||||||
|
#else
|
||||||
|
for (gs_mem_u32 i = 0; i < Arena->BuffersCount; i++)
|
||||||
|
{
|
||||||
|
memory_buffer* Buffer = Arena->Buffers + i;
|
||||||
|
free(Buffer->Buffer);
|
||||||
|
}
|
||||||
|
free(Arena->Buffers);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define IsPowerOfTwo(v) ((v != 0) && ((v & (v - 1)) == 0))
|
||||||
|
|
||||||
|
inline gs_mem_u32
|
||||||
|
GetAlignmentOffset (gs_mem_u64 Address, gs_mem_u32 Alignment, gs_mem_u32 AlignmentMask)
|
||||||
|
{
|
||||||
|
gs_mem_u32 AlignmentOffset = 0;
|
||||||
|
if (Address & AlignmentMask)
|
||||||
|
{
|
||||||
|
AlignmentOffset = Alignment - (Address & AlignmentMask);
|
||||||
|
}
|
||||||
|
return AlignmentOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
static address_and_buffer
|
||||||
|
GetAlignedAddressInBuffer(memory_buffer* Buffer, gs_mem_u32 Size, gs_mem_u32 Alignment, gs_mem_u32 AlignmentMask)
|
||||||
|
{
|
||||||
|
address_and_buffer Result = {};
|
||||||
|
|
||||||
|
gs_mem_u64 HeadAddress = (gs_mem_u64)Buffer->Buffer + Buffer->Used;
|
||||||
|
gs_mem_u32 AlignmentOffset = GetAlignmentOffset(HeadAddress, Alignment, AlignmentMask);
|
||||||
|
gs_mem_u64 AlignedAddress = HeadAddress + AlignmentOffset;
|
||||||
|
|
||||||
|
if (Buffer->Used + AlignmentOffset + Size <= Buffer->Size)
|
||||||
|
{
|
||||||
|
Result.Buffer = Buffer;
|
||||||
|
Result.Address = AlignedAddress;
|
||||||
|
Result.SizeWithAlignment = Size + AlignmentOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static address_and_buffer
|
||||||
|
FindAlignedAddressInBufferWithRoom(memory_arena* Arena, gs_mem_u32 Size, gs_mem_u32 Alignment, gs_mem_u32 AlignmentMask)
|
||||||
|
{
|
||||||
|
address_and_buffer Result = {};
|
||||||
|
for (gs_mem_u32 i = 0; i < Arena->BuffersCount; i++)
|
||||||
|
{
|
||||||
|
memory_buffer* At = Arena->Buffers + i;
|
||||||
|
GSMem_Assert(At);
|
||||||
|
|
||||||
|
address_and_buffer AddressInCurrentBuffer = GetAlignedAddressInBuffer(At, Size, Alignment, AlignmentMask);
|
||||||
|
if (AddressInCurrentBuffer.Address != 0)
|
||||||
|
{
|
||||||
|
Result = AddressInCurrentBuffer;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gs_mem_u8*
|
||||||
|
ArenaAlloc(memory_arena* Arena, gs_mem_u32 Size)
|
||||||
|
{
|
||||||
|
gs_mem_u8* Result = 0;
|
||||||
|
|
||||||
|
if (Arena->Alloc)
|
||||||
|
{
|
||||||
|
Result = (gs_mem_u8*)Arena->Alloc(sizeof(gs_mem_u8) * Size);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#ifdef GS_MEMORY_NO_STD_LIBS
|
||||||
|
// NOTE(Peter): If you specify no std libs AND don't supply a allocation function
|
||||||
|
// we should assert as this is an invalid codepath
|
||||||
|
GSMem_Assert(0);
|
||||||
|
#else
|
||||||
|
Result = (gs_mem_u8*)malloc(sizeof(gs_mem_u8) * Size);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gs_mem_u8*
|
||||||
|
ArenaRealloc(memory_arena* Arena, gs_mem_u8* Head, gs_mem_u32 OldSize, gs_mem_u32 NewSize)
|
||||||
|
{
|
||||||
|
gs_mem_u8* Result = 0;
|
||||||
|
|
||||||
|
if (Arena->Realloc != 0)
|
||||||
|
{
|
||||||
|
Result = (gs_mem_u8*)Arena->Realloc(Head, OldSize, NewSize);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#ifdef GS_MEMORY_NO_STD_LIBS
|
||||||
|
// NOTE(Peter): If you specify no std libs AND don't supply a reallocation function
|
||||||
|
// we should assert as this is an invalid codepath
|
||||||
|
GSMem_Assert(0);
|
||||||
|
#else
|
||||||
|
Result = (gs_mem_u8*)realloc(Head, NewSize);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static memory_buffer*
|
||||||
|
GrowArena(memory_arena* Arena, gs_mem_u32 SizeNeeded)
|
||||||
|
{
|
||||||
|
GSMem_Assert(Arena->ExpansionRule != MemoryExpansion_Disallowed);
|
||||||
|
if (Arena->ExpansionRule == MemoryExpansion_OnlyIfFunctionsProvided)
|
||||||
|
{
|
||||||
|
GSMem_Assert((Arena->Alloc != 0) && (Arena->Realloc != 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
gs_mem_u32 NewBuffersCount = (Arena->BuffersCount + 1);
|
||||||
|
gs_mem_u32 OldBuffersSize = sizeof(memory_buffer) * Arena->BuffersCount;
|
||||||
|
gs_mem_u32 NewBuffersSize = sizeof(memory_buffer) * NewBuffersCount;
|
||||||
|
Arena->Buffers = (memory_buffer*)ArenaRealloc(Arena, (gs_mem_u8*)Arena->Buffers, OldBuffersSize, NewBuffersSize);
|
||||||
|
Arena->BuffersCount = NewBuffersCount;
|
||||||
|
|
||||||
|
memory_buffer* NewBuffer = Arena->Buffers + (Arena->BuffersCount - 1);
|
||||||
|
NewBuffer->Size = GS_MEMORY_BUFFER_SIZE;
|
||||||
|
if (SizeNeeded > NewBuffer->Size)
|
||||||
|
{
|
||||||
|
NewBuffer->Size = SizeNeeded;
|
||||||
|
}
|
||||||
|
|
||||||
|
NewBuffer->Buffer = ArenaAlloc(Arena, sizeof(gs_mem_u8) * NewBuffer->Size);
|
||||||
|
NewBuffer->Used = 0;
|
||||||
|
|
||||||
|
Arena->TotalSize += NewBuffer->Size;
|
||||||
|
return NewBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef GS_MEMORY_TRACK_ALLOCATIONS
|
||||||
|
|
||||||
|
#define DetermineAllocationSize(size) (size) + GS_MEMORY_FOOTER_SIZE
|
||||||
|
#define ClearAllocationsUsed(arena) (arena)->AllocationsUsed = 0
|
||||||
|
#define ClearAllocationsUsedToSnapshot(arena, snapshot) \
|
||||||
|
(arena)->AllocationsUsed = (snapshot).AllocationsUsedAtSnapshot;
|
||||||
|
|
||||||
|
static void
|
||||||
|
TrackAllocation(memory_arena* Arena, gs_mem_u8* Head, gs_mem_u32 Size, char* Filename, gs_mem_u32 LineNumber)
|
||||||
|
{
|
||||||
|
gs_mem_u32 AllocationsMax = Arena->AllocationBuffersCount * GS_MEM_TRACKED_ALLOCATION_BUFFER_SIZE;
|
||||||
|
if (Arena->AllocationsUsed >= AllocationsMax)
|
||||||
|
{
|
||||||
|
gs_mem_u32 NewAllocationBuffersCount = Arena->AllocationBuffersCount + 1;
|
||||||
|
Arena->AllocationBuffers = (tracked_allocation_buffer**)ArenaRealloc(Arena,
|
||||||
|
(gs_mem_u8*)Arena->AllocationBuffers,
|
||||||
|
Arena->AllocationBuffersCount * sizeof(void*),
|
||||||
|
NewAllocationBuffersCount * sizeof(void*));
|
||||||
|
Arena->AllocationBuffersCount = NewAllocationBuffersCount;
|
||||||
|
|
||||||
|
gs_mem_u32 NewBufferIndex = Arena->AllocationBuffersCount - 1;
|
||||||
|
Arena->AllocationBuffers[NewBufferIndex] = (tracked_allocation_buffer*)ArenaAlloc(Arena, sizeof(tracked_allocation_buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
gs_mem_u32 AllocationIndex = Arena->AllocationsUsed++;
|
||||||
|
gs_mem_u32 BufferIndex = AllocationIndex / GS_MEM_TRACKED_ALLOCATION_BUFFER_SIZE;
|
||||||
|
gs_mem_u32 IndexInBuffer = AllocationIndex % GS_MEM_TRACKED_ALLOCATION_BUFFER_SIZE;
|
||||||
|
tracked_allocation_buffer* Buffer = Arena->AllocationBuffers[BufferIndex];
|
||||||
|
tracked_allocation* NewAllocationTracker = Buffer->Buffer + IndexInBuffer;
|
||||||
|
|
||||||
|
NewAllocationTracker->Head = Head;
|
||||||
|
|
||||||
|
NewAllocationTracker->Footer = Head + Size - GS_MEMORY_FOOTER_SIZE;
|
||||||
|
NewAllocationTracker->Footer[0] = GS_MEMORY_FOOTER_0;
|
||||||
|
NewAllocationTracker->Footer[1] = GS_MEMORY_FOOTER_1;
|
||||||
|
NewAllocationTracker->Footer[2] = GS_MEMORY_FOOTER_2;
|
||||||
|
NewAllocationTracker->Footer[3] = GS_MEMORY_FOOTER_3;
|
||||||
|
|
||||||
|
NewAllocationTracker->File = Filename;
|
||||||
|
NewAllocationTracker->LineNumber = LineNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool
|
||||||
|
VerifyAllocationNoOverflow (tracked_allocation Allocation)
|
||||||
|
{
|
||||||
|
bool Result = ((Allocation.Footer[0] == GS_MEMORY_FOOTER_0) &&
|
||||||
|
(Allocation.Footer[1] == GS_MEMORY_FOOTER_1) &&
|
||||||
|
(Allocation.Footer[2] == GS_MEMORY_FOOTER_2) &&
|
||||||
|
(Allocation.Footer[3] == GS_MEMORY_FOOTER_3));
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
AssertAllocationsNoOverflow (memory_arena Arena)
|
||||||
|
{
|
||||||
|
for (gs_mem_u32 AllocationIndex = 0;
|
||||||
|
AllocationIndex< Arena.AllocationsUsed;
|
||||||
|
AllocationIndex++)
|
||||||
|
{
|
||||||
|
gs_mem_u32 BufferIndex = AllocationIndex / GS_MEM_TRACKED_ALLOCATION_BUFFER_SIZE;
|
||||||
|
gs_mem_u32 IndexInBuffer = AllocationIndex % GS_MEM_TRACKED_ALLOCATION_BUFFER_SIZE;
|
||||||
|
|
||||||
|
tracked_allocation_buffer* Buffer = Arena.AllocationBuffers[BufferIndex];
|
||||||
|
tracked_allocation Allocation = Buffer->Buffer[IndexInBuffer];
|
||||||
|
|
||||||
|
GSMem_Assert(VerifyAllocationNoOverflow(Allocation));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define PushSize(arena, size) PushSize_((arena), (size), 4, __FILE__, __LINE__)
|
||||||
|
#define PushArray(arena, type, length) (type*)PushSize_((arena), sizeof(type) * length, 4, __FILE__, __LINE__)
|
||||||
|
#define PushStruct(arena, type) (type*)PushSize_((arena), sizeof(type), 4, __FILE__, __LINE__)
|
||||||
|
#define PushSizeAligned(arena, size, alignment) PushSize_((arena), (size), (alignment), __FILE__, __LINE__)
|
||||||
|
#define PushArrayAligned(arena, type, length, alignment) (type*)PushSize_((arena), sizeof(type) * length, (alignment), __FILE__, __LINE__)
|
||||||
|
#define PushStructAligned(arena, type, alignment) (type*)PushSize_((arena), sizeof(type), (alignment), __FILE__, __LINE__)
|
||||||
|
|
||||||
|
#else // GS_MEMORY_TRACK_ALLOCATIONS
|
||||||
|
|
||||||
|
#define AssertAllocationsNoOverflow(arena)
|
||||||
|
#define DetermineAllocationSize(size) size
|
||||||
|
#define ClearAllocationsUsed(arena)
|
||||||
|
#define ClearAllocationsUsedToSnapshot(arena, snapshot)
|
||||||
|
|
||||||
|
#define TrackAllocation(arena, head, size, filename, linenumber)
|
||||||
|
|
||||||
|
#define PushSize(arena, size) PushSize_((arena), (size))
|
||||||
|
#define PushArray(arena, type, length) (type*)PushSize_((arena), sizeof(type) * length)
|
||||||
|
#define PushStruct(arena, type) (type*)PushSize_((arena), sizeof(type))
|
||||||
|
#define PushSizeAligned(arena, size, alignment) PushSize_((arena), (size), (alignment))
|
||||||
|
#define PushArrayAligned(arena, type, length, alignment) (type*)PushSize_((arena), sizeof(type) * length, (alignment))
|
||||||
|
#define PushStructAligned(arena, type, alignment) (type*)PushSize_((arena), sizeof(type), (alignment))
|
||||||
|
|
||||||
|
#endif // GS_MEMORY_TRACK_ALLOCATIONS
|
||||||
|
|
||||||
|
static gs_mem_u8*
|
||||||
|
PushSize_(memory_arena* Arena, gs_mem_u32 Size, gs_mem_u32 Alignment = 4, char* Filename = 0, gs_mem_u32 LineNumber = 0)
|
||||||
|
{
|
||||||
|
// ie. Alignment = 4 = 100 (binary)
|
||||||
|
// 4 - 1 = 3
|
||||||
|
// 100 - 1 = 011 which is a mask of the bits we don't want set in the start address
|
||||||
|
GSMem_Assert(IsPowerOfTwo(Alignment));
|
||||||
|
gs_mem_u32 AlignmentMask = Alignment - 1;
|
||||||
|
|
||||||
|
gs_mem_u32 AllocationSize = DetermineAllocationSize(Size);
|
||||||
|
|
||||||
|
address_and_buffer ResultAddress = {};
|
||||||
|
if (Arena->FindAddressRule == FindAddress_InAnyBuffer)
|
||||||
|
{
|
||||||
|
ResultAddress = FindAlignedAddressInBufferWithRoom(Arena, AllocationSize, Alignment, AlignmentMask);
|
||||||
|
}
|
||||||
|
else if (Arena->FindAddressRule == FindAddress_InLastBufferOnly
|
||||||
|
&& Arena->BuffersCount > 0)
|
||||||
|
{
|
||||||
|
memory_buffer* LastBuffer = Arena->Buffers + Arena->BuffersCount - 1;
|
||||||
|
ResultAddress = GetAlignedAddressInBuffer(LastBuffer, Size, Alignment, AlignmentMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ResultAddress.Address == 0)
|
||||||
|
{
|
||||||
|
memory_buffer* Buffer = GrowArena(Arena, AllocationSize);
|
||||||
|
ResultAddress = GetAlignedAddressInBuffer(Buffer, AllocationSize, Alignment, AlignmentMask);
|
||||||
|
}
|
||||||
|
GSMem_Assert(ResultAddress.Address != 0);
|
||||||
|
GSMem_Assert((ResultAddress.Address & AlignmentMask) == 0);
|
||||||
|
|
||||||
|
gs_mem_u8* Result = (gs_mem_u8*)ResultAddress.Address;
|
||||||
|
ResultAddress.Buffer->Used += ResultAddress.SizeWithAlignment;
|
||||||
|
Arena->TotalUsed += ResultAddress.SizeWithAlignment;
|
||||||
|
|
||||||
|
TrackAllocation(Arena, Result, AllocationSize, Filename, LineNumber);
|
||||||
|
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ClearArena(memory_arena* Arena)
|
||||||
|
{
|
||||||
|
for (gs_mem_u32 i = 0; i < Arena->BuffersCount; i++)
|
||||||
|
{
|
||||||
|
memory_buffer* At = Arena->Buffers + i;
|
||||||
|
At->Used = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Arena->TotalUsed = 0;
|
||||||
|
ClearAllocationsUsed(Arena);
|
||||||
|
}
|
||||||
|
|
||||||
|
static arena_snapshot
|
||||||
|
TakeSnapshotOfArena(memory_arena* Arena)
|
||||||
|
{
|
||||||
|
Assert(Arena->FindAddressRule == FindAddress_InLastBufferOnly);
|
||||||
|
|
||||||
|
arena_snapshot Result = {};
|
||||||
|
Result.Arena = Arena;
|
||||||
|
Result.ArenaUsedAtSnapshot = Arena->TotalUsed;
|
||||||
|
if (Arena->BuffersCount > 0)
|
||||||
|
{
|
||||||
|
Result.HeadBufferAtSnapshot = Arena->BuffersCount - 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Result.HeadBufferAtSnapshot = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
memory_buffer* HeadBuffer = Arena->Buffers + Result.HeadBufferAtSnapshot;
|
||||||
|
if (HeadBuffer)
|
||||||
|
{
|
||||||
|
Result.HeadBufferUsedAtSnapshot = HeadBuffer->Used;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ClearArenaToSnapshot(memory_arena* Arena, arena_snapshot Snapshot)
|
||||||
|
{
|
||||||
|
Assert(Arena == Snapshot.Arena);
|
||||||
|
|
||||||
|
memory_buffer* HeadBufferAtSnapshot = Arena->Buffers + Snapshot.HeadBufferAtSnapshot;
|
||||||
|
if (HeadBufferAtSnapshot)
|
||||||
|
{
|
||||||
|
HeadBufferAtSnapshot->Used = Snapshot.HeadBufferUsedAtSnapshot;
|
||||||
|
|
||||||
|
for (gs_mem_u32 i = Snapshot.HeadBufferAtSnapshot + 1; i < Arena->BuffersCount; i++)
|
||||||
|
{
|
||||||
|
memory_buffer* Buffer = Arena->Buffers + i;
|
||||||
|
Buffer->Used = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Arena->TotalUsed = Snapshot.ArenaUsedAtSnapshot;
|
||||||
|
ClearAllocationsUsedToSnapshot(Arena, Snapshot);
|
||||||
|
}
|
||||||
|
#define GS_MEMORY_ARENA_H
|
||||||
|
#endif // GS_MEMORY_ARENA_H
|
|
@ -0,0 +1,81 @@
|
||||||
|
/*
|
||||||
|
gs_radix_sort.h - An implementation of radix sort for fixed size unsigned 32bit integer buffers
|
||||||
|
|
||||||
|
TODO
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef GS_RADIX_SORT_H
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
#if !defined(GSRad_Assert)
|
||||||
|
#define GSRad_Assert(expression) \
|
||||||
|
if(!(expression)) { \
|
||||||
|
*((int *)0) = 5; \
|
||||||
|
}
|
||||||
|
#endif // !defined(GSRad_Assert)
|
||||||
|
#endif // DEBUG
|
||||||
|
|
||||||
|
typedef unsigned int gs_rad_u32;
|
||||||
|
typedef unsigned int gs_rad_b32;
|
||||||
|
|
||||||
|
struct gs_radix_entry
|
||||||
|
{
|
||||||
|
gs_rad_u32 Radix;
|
||||||
|
gs_rad_u32 ID;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
RadixSortInPlace_ (gs_radix_entry* Data, gs_rad_u32 Start, gs_rad_u32 End, gs_rad_u32 Iteration)
|
||||||
|
{
|
||||||
|
gs_rad_u32 Shift = Iteration;
|
||||||
|
gs_rad_u32 ZerosBoundary = Start;
|
||||||
|
gs_rad_u32 OnesBoundary = End - 1;
|
||||||
|
|
||||||
|
for (gs_rad_u32 d = Start; d < End; d++)
|
||||||
|
{
|
||||||
|
gs_radix_entry Entry = Data[ZerosBoundary];
|
||||||
|
gs_rad_u32 Place = (Entry.Radix >> Shift) & 0x1;
|
||||||
|
if (Place)
|
||||||
|
{
|
||||||
|
gs_radix_entry Evicted = Data[OnesBoundary];
|
||||||
|
Data[OnesBoundary] = Entry;
|
||||||
|
Data[ZerosBoundary] = Evicted;
|
||||||
|
OnesBoundary -= 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ZerosBoundary += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Iteration > 0)
|
||||||
|
{
|
||||||
|
RadixSortInPlace_(Data, Start, ZerosBoundary, Iteration - 1);
|
||||||
|
RadixSortInPlace_(Data, ZerosBoundary, End, Iteration - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
RadixSortInPlace (gs_radix_entry* Data, gs_rad_u32 Count)
|
||||||
|
{
|
||||||
|
gs_rad_u32 Highest = 0;
|
||||||
|
for (gs_rad_u32 i = 0; i < Count; i++)
|
||||||
|
{
|
||||||
|
if (Data[i].Radix > Highest)
|
||||||
|
{
|
||||||
|
Highest = Data[i].Radix;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gs_rad_u32 Iterations = 0;
|
||||||
|
while (Highest > 1)
|
||||||
|
{
|
||||||
|
++Iterations;
|
||||||
|
Highest = Highest >> 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
RadixSortInPlace_(Data, 0, Count, Iterations);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define GS_RADIX_SORT_H
|
||||||
|
#endif // GS_RADIX_SORT_H
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,104 @@
|
||||||
|
//
|
||||||
|
// File: gs_string_builder.h
|
||||||
|
// Author: Peter Slattery
|
||||||
|
// Creation Date: 2020-01-01
|
||||||
|
//
|
||||||
|
#ifndef GS_STRING_BUILDER_H
|
||||||
|
|
||||||
|
#ifndef GS_STRING_BUILDER_NO_STDIO
|
||||||
|
#include <stdio.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define STRING_BUILDER_BUFFER_CAPACITY 4096
|
||||||
|
struct string_builder_buffer
|
||||||
|
{
|
||||||
|
u8* BufferMemory;
|
||||||
|
string String;
|
||||||
|
string_builder_buffer* Next;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct string_builder
|
||||||
|
{
|
||||||
|
string_builder_buffer* Buffers;
|
||||||
|
string_builder_buffer* Head;
|
||||||
|
};
|
||||||
|
|
||||||
|
internal void
|
||||||
|
GrowStringBuilder(string_builder* StringBuilder)
|
||||||
|
{
|
||||||
|
u8* BufferAndHeader = (u8*)malloc(sizeof(string_builder_buffer) + STRING_BUILDER_BUFFER_CAPACITY);
|
||||||
|
string_builder_buffer* NewBuffer = (string_builder_buffer*)BufferAndHeader;
|
||||||
|
*NewBuffer = {0};
|
||||||
|
|
||||||
|
NewBuffer->BufferMemory = (u8*)(NewBuffer + 1);
|
||||||
|
NewBuffer->String = MakeString((char*)NewBuffer->BufferMemory, 0, STRING_BUILDER_BUFFER_CAPACITY);
|
||||||
|
|
||||||
|
if (!StringBuilder->Buffers)
|
||||||
|
{
|
||||||
|
StringBuilder->Buffers = NewBuffer;
|
||||||
|
StringBuilder->Head = NewBuffer;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
StringBuilder->Head->Next = NewBuffer;
|
||||||
|
StringBuilder->Head = NewBuffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
Write(string Text, string_builder* StringBuilder)
|
||||||
|
{
|
||||||
|
string TextLeft = Text;
|
||||||
|
|
||||||
|
if (StringBuilder->Buffers == 0)
|
||||||
|
{
|
||||||
|
GrowStringBuilder(StringBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (TextLeft.Length > 0)
|
||||||
|
{
|
||||||
|
// Copy what there is room for
|
||||||
|
s32 SpaceAvailable = StringBuilder->Head->String.Max - StringBuilder->Head->String.Length;
|
||||||
|
|
||||||
|
ConcatString(TextLeft, GSMin(SpaceAvailable, TextLeft.Length), &StringBuilder->Head->String);
|
||||||
|
TextLeft.Memory += SpaceAvailable;
|
||||||
|
TextLeft.Length -= SpaceAvailable;
|
||||||
|
|
||||||
|
if (TextLeft.Length > 0)
|
||||||
|
{
|
||||||
|
GrowStringBuilder(StringBuilder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void
|
||||||
|
WriteF(string_builder* StringBuilder, char* Format, ...)
|
||||||
|
{
|
||||||
|
MakeStringBuffer(Buffer, 256);
|
||||||
|
|
||||||
|
va_list Args;
|
||||||
|
va_start(Args, Format);
|
||||||
|
Buffer.Length = PrintFArgsList(Buffer.Memory, Buffer.Max, Format, Args);
|
||||||
|
va_end(Args);
|
||||||
|
|
||||||
|
Write(Buffer, StringBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef GS_STRING_BUILDER_NO_STDIO
|
||||||
|
|
||||||
|
internal void
|
||||||
|
WriteStringBuilderToFile(string_builder StringBuilder, FILE* WriteFile)
|
||||||
|
{
|
||||||
|
string_builder_buffer* BufferAt = StringBuilder.Buffers;
|
||||||
|
while (BufferAt)
|
||||||
|
{
|
||||||
|
string String = BufferAt->String;
|
||||||
|
fwrite(String.Memory, 1, String.Length, WriteFile);
|
||||||
|
BufferAt = BufferAt->Next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // GS_STRING_BUILDER_NO_STDIO
|
||||||
|
|
||||||
|
#define GS_STRING_BUILDER_H
|
||||||
|
#endif // GS_STRING_BUILDER_H
|
File diff suppressed because it is too large
Load Diff
|
@ -16,7 +16,7 @@ set CommonLinkerFlags= -opt:ref
|
||||||
|
|
||||||
pushd build
|
pushd build
|
||||||
|
|
||||||
cl %CommonCompilerFlags% -IC:\programs-dev\gs_libs\src ..\meta\foldhaus_meta.cpp /link %CommonLinkerFlags%
|
cl %CommonCompilerFlags% ..\meta\foldhaus_meta.cpp /link %CommonLinkerFlags%
|
||||||
|
|
||||||
C:\programs\ctime\ctime.exe -end C:\projects\foldhaus\build\win32_gs_meta_build_time.ctm %LastError%
|
C:\programs\ctime\ctime.exe -end C:\projects\foldhaus\build\win32_gs_meta_build_time.ctm %LastError%
|
||||||
C:\programs\ctime\ctime.exe -stats C:\projects\foldhaus\build\win32_gs_meta_build_time.ctm
|
C:\programs\ctime\ctime.exe -stats C:\projects\foldhaus\build\win32_gs_meta_build_time.ctm
|
||||||
|
|
|
@ -24,14 +24,17 @@ int main(int ArgCount, char* Args[])
|
||||||
FinishGeneratingTypes(&TypeGenerator);
|
FinishGeneratingTypes(&TypeGenerator);
|
||||||
|
|
||||||
gsm_code_generator NodeTypeGen = BeginEnumGeneration("node_type", "NodeType", false, true);
|
gsm_code_generator NodeTypeGen = BeginEnumGeneration("node_type", "NodeType", false, true);
|
||||||
|
|
||||||
#if 0
|
|
||||||
// TODO(Peter): Create a FilterTypesByTag function to create a contiguous array
|
// TODO(Peter): Create a FilterTypesByTag function to create a contiguous array
|
||||||
// of type_definition**
|
// of type_definition**
|
||||||
printf("\n\n");
|
printf("\n\n");
|
||||||
for (u32 i = 0; i < Meta.TypeTable.Types.Used; i++)
|
for (u32 b = 0; b < Meta.TypeTable.TypeBucketsCount; b++)
|
||||||
{
|
{
|
||||||
type_definition* Decl = Meta.TypeTable.Types.GetElementAtIndex(i);
|
type_table_hash_bucket Bucket = Meta.TypeTable.Types[b];
|
||||||
|
for (u32 i = 0; i < TYPE_TABLE_BUCKET_MAX; i++)
|
||||||
|
{
|
||||||
|
if (!Bucket.Keys[i] == 0) { continue; }
|
||||||
|
|
||||||
|
type_definition* Decl = Bucket.Values + i;
|
||||||
if (HasTag(MakeStringLiteral("node_proc"), Decl->MetaTags) &&
|
if (HasTag(MakeStringLiteral("node_proc"), Decl->MetaTags) &&
|
||||||
Decl->Type == TypeDef_Function)
|
Decl->Type == TypeDef_Function)
|
||||||
{
|
{
|
||||||
|
@ -50,8 +53,8 @@ int main(int ArgCount, char* Args[])
|
||||||
printf(");\n\n");
|
printf(");\n\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
printf("\n\n");
|
printf("\n\n");
|
||||||
#endif
|
|
||||||
|
|
||||||
FinishEnumGeneration(&NodeTypeGen);
|
FinishEnumGeneration(&NodeTypeGen);
|
||||||
|
|
||||||
|
|
|
@ -20,16 +20,16 @@
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include <gs_language.h>
|
#include "..\gs_libs\gs_language.h"
|
||||||
#include <gs_bucket.h>
|
#include "..\gs_libs\gs_bucket.h"
|
||||||
#include "..\src\gs_platform.h"
|
#include "..\gs_libs\gs_platform.h"
|
||||||
#include <gs_memory_arena.h>
|
#include "..\gs_libs\gs_memory_arena.h"
|
||||||
#include <gs_string.h>
|
#include "..\gs_libs\gs_string.h"
|
||||||
|
|
||||||
#include "gs_meta_lexer.h"
|
#include "gs_meta_lexer.h"
|
||||||
#include "gs_meta_error.h"
|
#include "gs_meta_error.h"
|
||||||
|
|
||||||
#include "foldhaus_meta_type_table.h"
|
#include "gs_meta_type_table.h"
|
||||||
|
|
||||||
struct source_code_file
|
struct source_code_file
|
||||||
{
|
{
|
||||||
|
@ -404,10 +404,6 @@ ReadEntireFileAndNullTerminate (source_code_file* File, errors* Errors)
|
||||||
LengthRead = (s32)ReadSize + 1;
|
LengthRead = (s32)ReadSize + 1;
|
||||||
fclose(ReadFile);
|
fclose(ReadFile);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
PushFError(Errors, "Could Not Read File: %S", File->Path);
|
|
||||||
}
|
|
||||||
|
|
||||||
return LengthRead;
|
return LengthRead;
|
||||||
}
|
}
|
||||||
|
@ -431,7 +427,7 @@ FileAlreadyInSource(string Path, gs_bucket<source_code_file> SourceFiles)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void
|
internal void
|
||||||
AddFileToSource(string RelativePath, gs_bucket<source_code_file>* SourceFiles, errors* Errors)
|
AddFileToSource(string RelativePath, source_code_file CurrentFile, gs_bucket<source_code_file>* SourceFiles, errors* Errors)
|
||||||
{
|
{
|
||||||
source_code_file File = {0};
|
source_code_file File = {0};
|
||||||
|
|
||||||
|
@ -451,7 +447,11 @@ AddFileToSource(string RelativePath, gs_bucket<source_code_file>* SourceFiles, e
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
PushFError(Errors, "Error: Could not load file %S\n", RelativePath);
|
PushFError(Errors, "Error: Could not load file %S.\n", RelativePath);
|
||||||
|
if (CurrentFile.Path.Length > 0)
|
||||||
|
{
|
||||||
|
PushFError(Errors, " Loaded In: %S\n", CurrentFile.Path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1442,7 +1442,7 @@ PreprocessProgram (char* SourceFile)
|
||||||
string CurrentWorkingDirectory = MakeString((char*)malloc(1024), 0, 1024);
|
string CurrentWorkingDirectory = MakeString((char*)malloc(1024), 0, 1024);
|
||||||
|
|
||||||
string RootFile = MakeString(SourceFile);
|
string RootFile = MakeString(SourceFile);
|
||||||
AddFileToSource(RootFile, &Meta.SourceFiles, &Meta.Errors);
|
AddFileToSource(RootFile, {}, &Meta.SourceFiles, &Meta.Errors);
|
||||||
|
|
||||||
s32 LastSlash = ReverseSearchForCharInSet(RootFile, "\\/");
|
s32 LastSlash = ReverseSearchForCharInSet(RootFile, "\\/");
|
||||||
if (LastSlash <= 0)
|
if (LastSlash <= 0)
|
||||||
|
@ -1514,7 +1514,7 @@ PreprocessProgram (char* SourceFile)
|
||||||
ParseSuccess = true;
|
ParseSuccess = true;
|
||||||
if (!FileAlreadyInSource(TempFilePath, Meta.SourceFiles))
|
if (!FileAlreadyInSource(TempFilePath, Meta.SourceFiles))
|
||||||
{
|
{
|
||||||
AddFileToSource(TempFilePath, &Meta.SourceFiles, &Meta.Errors);
|
AddFileToSource(TempFilePath, *File, &Meta.SourceFiles, &Meta.Errors);
|
||||||
}
|
}
|
||||||
EndScope(IncludeScope);
|
EndScope(IncludeScope);
|
||||||
}
|
}
|
||||||
|
@ -1590,9 +1590,10 @@ FinishMetaprogram(gs_meta_preprocessor* Meta)
|
||||||
FinishProfiler(&Meta->Profiler);
|
FinishProfiler(&Meta->Profiler);
|
||||||
|
|
||||||
PrintAllErrors(Meta->Errors);
|
PrintAllErrors(Meta->Errors);
|
||||||
|
|
||||||
printf("\nMetaprogram Performance:\n");
|
printf("\nMetaprogram Performance:\n");
|
||||||
|
|
||||||
PrintAllCategories(&Meta->Profiler);
|
PrintAllCategories(&Meta->Profiler);
|
||||||
|
printf("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
#define GS_META_CPP
|
#define GS_META_CPP
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
//
|
//
|
||||||
#ifndef GS_META_CODE_GENERATOR_H
|
#ifndef GS_META_CODE_GENERATOR_H
|
||||||
|
|
||||||
#include <gs_string.h>
|
#include "../gs_libs/gs_string.h"
|
||||||
#include <gs_string_builder.h>
|
#include "../gs_libs/gs_string_builder.h"
|
||||||
|
|
||||||
enum gsm_code_gen_type
|
enum gsm_code_gen_type
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,164 +0,0 @@
|
||||||
#define CODE_SIZE 256
|
|
||||||
struct generated_code
|
|
||||||
{
|
|
||||||
s32 Used;
|
|
||||||
char* Code;
|
|
||||||
generated_code* Next;
|
|
||||||
};
|
|
||||||
|
|
||||||
internal void
|
|
||||||
InitGeneratedCode (generated_code* Code)
|
|
||||||
{
|
|
||||||
Code->Used = 0;
|
|
||||||
Code->Code = (char*)malloc(sizeof(char) * CODE_SIZE);
|
|
||||||
*(Code->Code + CODE_SIZE - 1) = 0;
|
|
||||||
Code->Next = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE(Peter): This ONLY supports printing strings into the buffer at the moment
|
|
||||||
static void
|
|
||||||
PrintF_ (
|
|
||||||
generated_code* Dest,
|
|
||||||
char* Format,
|
|
||||||
va_list Args
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (!Dest->Code) {
|
|
||||||
InitGeneratedCode(Dest);
|
|
||||||
}
|
|
||||||
|
|
||||||
char* Src = Format;
|
|
||||||
char* Dst = Dest->Code + Dest->Used;
|
|
||||||
|
|
||||||
while (*Src && (Dst - Dest->Code) < CODE_SIZE)
|
|
||||||
{
|
|
||||||
if (*Src == '\\' && *(Src + 1) && *(Src + 1) == '%')
|
|
||||||
{
|
|
||||||
*Src++;
|
|
||||||
*Dst++ = *Src++;
|
|
||||||
}
|
|
||||||
else if (*Src == '%')
|
|
||||||
{
|
|
||||||
Src++;
|
|
||||||
if (*Src == 's')
|
|
||||||
{
|
|
||||||
Src++;
|
|
||||||
s32 StringLength = va_arg(Args, s32);
|
|
||||||
char* String = va_arg(Args, char*);
|
|
||||||
char* C = String;
|
|
||||||
while(*C && StringLength > 0 && (Dst - Dest->Code) < CODE_SIZE)
|
|
||||||
{
|
|
||||||
StringLength--;
|
|
||||||
*Dst++ = *C++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
InvalidCodePath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
*Dst++ = *Src++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!*Dst && *Src)
|
|
||||||
{
|
|
||||||
Dest->Next = (generated_code*)malloc(sizeof(generated_code));
|
|
||||||
InitGeneratedCode(Dest->Next);
|
|
||||||
PrintF_(Dest->Next, Src, Args);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*Dst && !*Src) { *Dst = 0; }
|
|
||||||
|
|
||||||
Dest->Used = (s32)(Dst - Dest->Code);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
PrintF (
|
|
||||||
generated_code* Dest,
|
|
||||||
char* Format,
|
|
||||||
...)
|
|
||||||
{
|
|
||||||
va_list Args;
|
|
||||||
va_start(Args, Format);
|
|
||||||
|
|
||||||
PrintF_(Dest, Format, Args);
|
|
||||||
|
|
||||||
va_end(Args);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
PrintCode (generated_code* Code)
|
|
||||||
{
|
|
||||||
GS_PRINTF(Code->Code);
|
|
||||||
if (Code->Next) { PrintCode(Code->Next); }
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
GenerateFieldCode (ast_field_declaration* Field, string_buffer* CodeBuffer, string_partition* Partition,
|
|
||||||
s32 IndentLevel, char* Terminator)
|
|
||||||
{
|
|
||||||
for (s32 i = 0; i < IndentLevel; i++)
|
|
||||||
{
|
|
||||||
PrintStringBufferFormat(CodeBuffer, Partition, " ");
|
|
||||||
}
|
|
||||||
|
|
||||||
PrintStringBufferFormat(CodeBuffer, Partition, "%s%s %s%s",
|
|
||||||
Field->TypeLength, Field->Type,
|
|
||||||
(Field->TypePointer ? 1 : 0), (Field->TypePointer ? "*" : ""),
|
|
||||||
Field->NameLength, Field->Name,
|
|
||||||
(Field->TypeArray ? 2 : 0), (Field->TypeArray ? "[]" : ""));
|
|
||||||
|
|
||||||
PrintStringBufferFormat(CodeBuffer, Partition, Terminator);
|
|
||||||
}
|
|
||||||
|
|
||||||
static string_buffer*
|
|
||||||
GenerateStructCode (ast_node* Struct, string_partition* StringPartition)
|
|
||||||
{
|
|
||||||
Assert(Struct->Type == ASTNode_StructDeclaration);
|
|
||||||
|
|
||||||
string_buffer* StructCodeBuffer = GetStringBuffer(StringPartition);
|
|
||||||
|
|
||||||
PrintStringBufferFormat(StructCodeBuffer, StringPartition, "struct %s\n{\n",
|
|
||||||
Struct->StructDeclaration.NameLength, Struct->StructDeclaration.Name);
|
|
||||||
|
|
||||||
ast_field_declaration* Member = Struct->StructDeclaration.Members;
|
|
||||||
while (Member)
|
|
||||||
{
|
|
||||||
GenerateFieldCode(Member, StructCodeBuffer, StringPartition, 1, ";\n");
|
|
||||||
Member = Member->Next;
|
|
||||||
}
|
|
||||||
|
|
||||||
PrintStringBufferFormat(StructCodeBuffer, StringPartition, "}\n\n");
|
|
||||||
return StructCodeBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
static string_buffer*
|
|
||||||
GenerateFunctionDeclaration (ast_node* Function, string_partition* Partition)
|
|
||||||
{
|
|
||||||
Assert(Function->Type == ASTNode_FunctionDeclaration);
|
|
||||||
|
|
||||||
ast_function_declaration* FuncDecl = &Function->FunctionDeclaration;
|
|
||||||
|
|
||||||
string_buffer* FunctionDeclBuffer = GetStringBuffer(Partition);
|
|
||||||
|
|
||||||
PrintStringBufferFormat(FunctionDeclBuffer, Partition, "%s %s (",
|
|
||||||
FuncDecl->ReturnTypeLength, FuncDecl->ReturnType,
|
|
||||||
FuncDecl->NameLength, FuncDecl->Name);
|
|
||||||
|
|
||||||
ast_field_declaration* Param = FuncDecl->Arguments;
|
|
||||||
while (Param)
|
|
||||||
{
|
|
||||||
GenerateFieldCode(Param, FunctionDeclBuffer, Partition, 0, "");
|
|
||||||
if (Param->Next)
|
|
||||||
{
|
|
||||||
PrintStringBufferFormat(FunctionDeclBuffer, Partition, ", ");
|
|
||||||
}
|
|
||||||
Param = Param->Next;
|
|
||||||
}
|
|
||||||
|
|
||||||
PrintStringBufferFormat(FunctionDeclBuffer, Partition, ")\n");
|
|
||||||
return FunctionDeclBuffer;
|
|
||||||
}
|
|
|
@ -1,337 +0,0 @@
|
||||||
enum ast_node_type
|
|
||||||
{
|
|
||||||
ASTNode_Invalid,
|
|
||||||
|
|
||||||
ASTNode_Program,
|
|
||||||
ASTNode_StructDeclaration,
|
|
||||||
ASTNode_FunctionDeclaration,
|
|
||||||
|
|
||||||
ASTNode_Count
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ast_field_declaration
|
|
||||||
{
|
|
||||||
s32 NameLength;
|
|
||||||
char* Name;
|
|
||||||
|
|
||||||
s32 TypeLength;
|
|
||||||
char* Type;
|
|
||||||
b32 TypePointer;
|
|
||||||
b32 TypeArray;
|
|
||||||
|
|
||||||
ast_field_declaration* Next;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ast_struct_declaration
|
|
||||||
{
|
|
||||||
s32 NameLength;
|
|
||||||
char* Name;
|
|
||||||
|
|
||||||
s32 MembersCount;
|
|
||||||
ast_field_declaration* Members;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ast_function_declaration
|
|
||||||
{
|
|
||||||
s32 ReturnTypeLength;
|
|
||||||
char* ReturnType;
|
|
||||||
|
|
||||||
s32 NameLength;
|
|
||||||
char* Name;
|
|
||||||
|
|
||||||
ast_field_declaration* Arguments;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ast_node
|
|
||||||
{
|
|
||||||
ast_node_type Type;
|
|
||||||
ast_node* Children;
|
|
||||||
|
|
||||||
union
|
|
||||||
{
|
|
||||||
ast_struct_declaration StructDeclaration;
|
|
||||||
ast_function_declaration FunctionDeclaration;
|
|
||||||
};
|
|
||||||
|
|
||||||
ast_node* Next;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct parse_field_decl_result
|
|
||||||
{
|
|
||||||
ast_field_declaration* Member;
|
|
||||||
token* NextToken;
|
|
||||||
};
|
|
||||||
|
|
||||||
internal parse_field_decl_result
|
|
||||||
ParseFieldDeclaration (//ast_field_declaration** Container, ast_field_declaration* PreviousMember,
|
|
||||||
token* StartToken, token_type TerminatorToken, token_type EnclosingToken)
|
|
||||||
{
|
|
||||||
parse_field_decl_result Result = {};
|
|
||||||
|
|
||||||
ast_field_declaration* Member = 0;
|
|
||||||
|
|
||||||
token* CurrentToken = StartToken;
|
|
||||||
if (StartToken->Type == Token_Identifier)
|
|
||||||
{
|
|
||||||
Member = (ast_field_declaration*)malloc(sizeof(ast_field_declaration));
|
|
||||||
Member->Next = 0;
|
|
||||||
|
|
||||||
Member->Type = CurrentToken->Text;
|
|
||||||
Member->TypeLength = CurrentToken->TextLength;
|
|
||||||
Member->TypePointer = false;
|
|
||||||
Member->TypeArray = false;
|
|
||||||
|
|
||||||
CurrentToken = CurrentToken->Next;
|
|
||||||
if (*CurrentToken->Text == '*')
|
|
||||||
{
|
|
||||||
Member->TypePointer = true;
|
|
||||||
CurrentToken = CurrentToken->Next;
|
|
||||||
}
|
|
||||||
|
|
||||||
Member->Name = CurrentToken->Text;
|
|
||||||
Member->NameLength = CurrentToken->TextLength;
|
|
||||||
|
|
||||||
CurrentToken = CurrentToken->Next;
|
|
||||||
if (CurrentToken->Type == Token_LeftSquareBracket)
|
|
||||||
{
|
|
||||||
CurrentToken = CurrentToken->Next;
|
|
||||||
if (CurrentToken->Type != Token_RightSquareBracket)
|
|
||||||
{
|
|
||||||
GS_DEBUG_PRINTF("ALERT: Skipping Array parameter that has a size");
|
|
||||||
while (CurrentToken->Type != Token_RightSquareBracket)
|
|
||||||
{
|
|
||||||
CurrentToken = CurrentToken->Next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
CurrentToken = CurrentToken->Next;
|
|
||||||
Member->TypeArray = true;
|
|
||||||
}
|
|
||||||
else if (CurrentToken->Type != TerminatorToken &&
|
|
||||||
CurrentToken->Type != EnclosingToken)
|
|
||||||
{
|
|
||||||
GS_DEBUG_PRINTF("Error: struct member %s %.*s not followed by its terminator type: %s or %s\n",
|
|
||||||
TokenNames[CurrentToken->Type], CurrentToken->TextLength, CurrentToken->Text,
|
|
||||||
TokenNames[(int)TerminatorToken], TokenNames[(int)EnclosingToken]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (StartToken->Type == Token_Comment)
|
|
||||||
{
|
|
||||||
CurrentToken = StartToken->Next;
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE(Peter): this is here because if the current token is the enclosing type,
|
|
||||||
// ie. the ')' in a parameter list, it isn't the responsibility of this function
|
|
||||||
// to deal with that.
|
|
||||||
if (CurrentToken->Type == TerminatorToken)
|
|
||||||
{
|
|
||||||
CurrentToken = CurrentToken->Next;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result.Member = Member;
|
|
||||||
Result.NextToken = CurrentToken;
|
|
||||||
|
|
||||||
return Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct parse_result
|
|
||||||
{
|
|
||||||
b32 Parsed;
|
|
||||||
ast_node* Node;
|
|
||||||
token* NextToken;
|
|
||||||
};
|
|
||||||
|
|
||||||
internal parse_result
|
|
||||||
ParseStructDeclaration (ast_node* PreviousNode, token* StartToken)
|
|
||||||
{
|
|
||||||
parse_result Result = {};
|
|
||||||
|
|
||||||
ast_node* Struct = 0;
|
|
||||||
token* CurrentToken = StartToken;
|
|
||||||
|
|
||||||
if (PreviousNode == 0)
|
|
||||||
{
|
|
||||||
Struct = (ast_node*)malloc(sizeof(ast_node));;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
PreviousNode->Next = (ast_node*)malloc(sizeof(ast_node));
|
|
||||||
Struct = PreviousNode->Next;
|
|
||||||
}
|
|
||||||
Struct->Next = 0;
|
|
||||||
Struct->StructDeclaration.Members = 0;
|
|
||||||
|
|
||||||
Struct->Type = ASTNode_StructDeclaration;
|
|
||||||
|
|
||||||
CurrentToken = CurrentToken->Next;
|
|
||||||
// Name Before Declaration
|
|
||||||
if (CurrentToken->Type == Token_Identifier)
|
|
||||||
{
|
|
||||||
Struct->StructDeclaration.NameLength = CurrentToken->TextLength;
|
|
||||||
Struct->StructDeclaration.Name = CurrentToken->Text;
|
|
||||||
CurrentToken = CurrentToken->Next;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (CurrentToken->Type == Token_LeftCurlyBracket)
|
|
||||||
{
|
|
||||||
CurrentToken = CurrentToken->Next;
|
|
||||||
|
|
||||||
ast_field_declaration* Member = 0;
|
|
||||||
while (CurrentToken->Type != Token_RightCurlyBracket)
|
|
||||||
{
|
|
||||||
parse_field_decl_result MemberResult = ParseFieldDeclaration(CurrentToken, Token_Semicolon, Token_RightCurlyBracket);
|
|
||||||
|
|
||||||
if (!Member)
|
|
||||||
{
|
|
||||||
Member = MemberResult.Member;
|
|
||||||
Member->Next = 0;
|
|
||||||
Struct->StructDeclaration.Members = Member;
|
|
||||||
}
|
|
||||||
else if (MemberResult.Member)
|
|
||||||
{
|
|
||||||
Member->Next = MemberResult.Member;
|
|
||||||
Member = Member->Next;
|
|
||||||
}
|
|
||||||
CurrentToken = MemberResult.NextToken;
|
|
||||||
}
|
|
||||||
// Advance Past the Right Bracket
|
|
||||||
CurrentToken = CurrentToken->Next;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
GS_DEBUG_PRINTF("Error: struct <name> not followed by {");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name After Declaration
|
|
||||||
if (CurrentToken->Type == Token_Identifier)
|
|
||||||
{
|
|
||||||
Struct->StructDeclaration.NameLength = CurrentToken->TextLength;
|
|
||||||
Struct->StructDeclaration.Name = CurrentToken->Text;
|
|
||||||
CurrentToken = CurrentToken->Next;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (CurrentToken->Type == Token_Semicolon)
|
|
||||||
{
|
|
||||||
CurrentToken = CurrentToken->Next;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
GS_DEBUG_PRINTF("Error: struct declaration did not finish with a semicolon");
|
|
||||||
}
|
|
||||||
|
|
||||||
Result.Node = Struct;
|
|
||||||
Result.NextToken = CurrentToken;
|
|
||||||
Result.Parsed = true;
|
|
||||||
|
|
||||||
return Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal b32
|
|
||||||
IsFunction (token* StartToken)
|
|
||||||
{
|
|
||||||
b32 Result = true;
|
|
||||||
|
|
||||||
token* Current = StartToken;
|
|
||||||
// TODO(Peter): check a type table to see if we can do this now.
|
|
||||||
// TODO(Peter): is there a way to defer this to later?
|
|
||||||
if (Current->Type != Token_Identifier) // Return Type
|
|
||||||
{
|
|
||||||
Result = false;
|
|
||||||
return Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
Current = Current->Next;
|
|
||||||
if (Current->Type != Token_Identifier) // Function Name
|
|
||||||
{
|
|
||||||
Result = false;
|
|
||||||
return Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
Current = Current->Next;
|
|
||||||
if (Current->Type != Token_LeftParen)
|
|
||||||
{
|
|
||||||
Result = false;
|
|
||||||
return Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
Current = Current->Next;
|
|
||||||
while (Current && Current->Type != Token_RightParen)
|
|
||||||
{
|
|
||||||
Current = Current->Next;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Current->Type != Token_RightParen)
|
|
||||||
{
|
|
||||||
Result = false;
|
|
||||||
return Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal parse_result
|
|
||||||
ParseFunctionDeclaration (ast_node* PreviousNode, token* StartToken)
|
|
||||||
{
|
|
||||||
parse_result Result;
|
|
||||||
|
|
||||||
ast_node* Function = 0;
|
|
||||||
token* CurrentToken = StartToken;
|
|
||||||
|
|
||||||
if (PreviousNode == 0)
|
|
||||||
{
|
|
||||||
Function = (ast_node*)malloc(sizeof(ast_node));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
PreviousNode->Next = (ast_node*)malloc(sizeof(ast_node));
|
|
||||||
Function = PreviousNode->Next;
|
|
||||||
}
|
|
||||||
Function->Next = 0;
|
|
||||||
|
|
||||||
Function->Type = ASTNode_FunctionDeclaration;
|
|
||||||
Function->FunctionDeclaration.Arguments = 0;
|
|
||||||
|
|
||||||
Function->FunctionDeclaration.ReturnTypeLength = CurrentToken->TextLength;
|
|
||||||
Function->FunctionDeclaration.ReturnType = CurrentToken->Text;
|
|
||||||
CurrentToken = CurrentToken->Next;
|
|
||||||
|
|
||||||
Function->FunctionDeclaration.NameLength = CurrentToken->TextLength;
|
|
||||||
Function->FunctionDeclaration.Name = CurrentToken->Text;
|
|
||||||
CurrentToken = CurrentToken->Next;
|
|
||||||
|
|
||||||
if (CurrentToken->Type == Token_LeftParen)
|
|
||||||
{
|
|
||||||
CurrentToken = CurrentToken->Next;
|
|
||||||
|
|
||||||
ast_field_declaration* Param = 0;
|
|
||||||
while (CurrentToken->Type != Token_RightParen)
|
|
||||||
{
|
|
||||||
parse_field_decl_result ParamResult = ParseFieldDeclaration(CurrentToken, Token_Comma, Token_RightParen);
|
|
||||||
if (!Param)
|
|
||||||
{
|
|
||||||
Param = ParamResult.Member;
|
|
||||||
Param->Next = 0;
|
|
||||||
}
|
|
||||||
else if (ParamResult.Member)
|
|
||||||
{
|
|
||||||
Param->Next = ParamResult.Member;
|
|
||||||
Param = Param->Next;
|
|
||||||
}
|
|
||||||
CurrentToken = ParamResult.NextToken;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
CurrentToken = CurrentToken->Next;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
GS_DEBUG_PRINTF("Error: Function declaration is not followed by left parenthesis");
|
|
||||||
InvalidCodePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Result.Node = Function;
|
|
||||||
Result.NextToken = CurrentToken;
|
|
||||||
Result.Parsed = true;
|
|
||||||
|
|
||||||
return Result;
|
|
||||||
}
|
|
|
@ -9,9 +9,9 @@
|
||||||
//
|
//
|
||||||
#ifndef GS_META_TYPEINFO_GENERATOR_H
|
#ifndef GS_META_TYPEINFO_GENERATOR_H
|
||||||
|
|
||||||
#include <gs_language.h>
|
#include "..\gs_libs\gs_language.h"
|
||||||
#include <gs_string.h>
|
#include "..\gs_libs\gs_string.h"
|
||||||
#include <gs_string_builder.h>
|
#include "..\gs_libs\gs_string_builder.h"
|
||||||
#include "gs_meta_code_generator.h"
|
#include "gs_meta_code_generator.h"
|
||||||
|
|
||||||
struct typeinfo_generator
|
struct typeinfo_generator
|
||||||
|
|
|
@ -1,99 +0,0 @@
|
||||||
#define STRING_BUILDER_ARRAY_BUFFER_SIZE 32
|
|
||||||
|
|
||||||
struct string_array
|
|
||||||
{
|
|
||||||
string** Buckets;
|
|
||||||
s32 BucketSize;
|
|
||||||
s32 BucketCount;
|
|
||||||
s32 Used;
|
|
||||||
free_list FreeList;
|
|
||||||
};
|
|
||||||
|
|
||||||
internal string*
|
|
||||||
GetEntryAtIndex (s32 Index, string_array Buffer)
|
|
||||||
{
|
|
||||||
string* Result = 0;
|
|
||||||
if (Buffer.Buckets)
|
|
||||||
{
|
|
||||||
bucket_index BucketIndex = GetBucketIndexForIndex(Index, Buffer.BucketSize);
|
|
||||||
Result = Buffer.Buckets[BucketIndex.Bucket] + BucketIndex.IndexInBucket;
|
|
||||||
}
|
|
||||||
return Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal s32
|
|
||||||
PushElement (string Data, string_array* Buffer)
|
|
||||||
{
|
|
||||||
s32 Result = -1;
|
|
||||||
|
|
||||||
if (Buffer->Used >= Buffer->BucketSize * Buffer->BucketCount)
|
|
||||||
{
|
|
||||||
Buffer->Buckets = (string**)GrowBuffer(Buffer->BucketSize, sizeof(string), &Buffer->BucketCount, (void**)Buffer->Buckets);
|
|
||||||
}
|
|
||||||
|
|
||||||
s32 Index = Buffer->Used++;
|
|
||||||
s32 BucketIndex = Index / Buffer->BucketSize;
|
|
||||||
s32 IndexInBucket = Index % Buffer->BucketSize;
|
|
||||||
|
|
||||||
Buffer->Buckets[BucketIndex][IndexInBucket] = Data;
|
|
||||||
Result = Index;
|
|
||||||
return Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct string_builder
|
|
||||||
{
|
|
||||||
string_array Buffer;
|
|
||||||
s32 BufferElementSize;
|
|
||||||
};
|
|
||||||
|
|
||||||
internal string_builder
|
|
||||||
InitStringBuilder(s32 BufferSize)
|
|
||||||
{
|
|
||||||
string_builder Result = {};
|
|
||||||
Result.BufferElementSize = BufferSize;
|
|
||||||
Result.Buffer.BucketSize = STRING_BUILDER_ARRAY_BUFFER_SIZE;
|
|
||||||
Result.Buffer.FreeList.Next = &Result.Buffer.FreeList;
|
|
||||||
return Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void
|
|
||||||
GrowStringBuilder (string_builder* StringBuilder)
|
|
||||||
{
|
|
||||||
string NewSegment = {};
|
|
||||||
NewSegment.Memory = (char*)malloc(StringBuilder->BufferElementSize * sizeof(char));
|
|
||||||
NewSegment.Max = StringBuilder->BufferElementSize;
|
|
||||||
|
|
||||||
PushElement(NewSegment, &StringBuilder->Buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void
|
|
||||||
StringBuilderPrintF (string_builder* StringBuilder, char* Format, ...)
|
|
||||||
{
|
|
||||||
string Addition = {};
|
|
||||||
Addition.Max = 2048;
|
|
||||||
Addition.Memory = (char*)malloc(Addition.Max * sizeof(char));
|
|
||||||
|
|
||||||
va_list Args;
|
|
||||||
va_start(Args, Format);
|
|
||||||
Addition.Length = PrintFArgsList(Addition.Memory, Addition.Max, Format, Args);
|
|
||||||
|
|
||||||
s32 CharsCopied = 0;
|
|
||||||
while (CharsCopied < Addition.Length)
|
|
||||||
{
|
|
||||||
s32 StringBuilderTailIndex = StringBuilder->Buffer.Used - 1;
|
|
||||||
string* LastString = GetEntryAtIndex(StringBuilderTailIndex, StringBuilder->Buffer);
|
|
||||||
if (!LastString || LastString->Length >= LastString->Max)
|
|
||||||
{
|
|
||||||
GrowStringBuilder(StringBuilder);
|
|
||||||
StringBuilderTailIndex = StringBuilder->Buffer.Used - 1;
|
|
||||||
LastString = GetEntryAtIndex(StringBuilderTailIndex, StringBuilder->Buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (CharsCopied < Addition.Length && LastString->Length < LastString->Max)
|
|
||||||
{
|
|
||||||
LastString->Memory[LastString->Length++] = Addition.Memory[CharsCopied++];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
free(Addition.Memory);
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
#include "..\src\gs_language.h"
|
|
||||||
#include "..\src\foldhaus_memory.h"
|
|
||||||
#include "..\src\gs_string.h"
|
|
||||||
|
|
||||||
#include "gs_meta.cpp"
|
|
||||||
|
|
||||||
#include <windows.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
int main (int ArgCount, char* Arg[])
|
|
||||||
{
|
|
||||||
gs_meta_processor Meta = CreateMetaProcessor();
|
|
||||||
AddFile(&Meta, "C:/projects/foldhaus/meta/meta_test.cpp");
|
|
||||||
|
|
||||||
LexAllFiles(&Meta);
|
|
||||||
ParseAllFiles(&Meta);
|
|
||||||
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
identifier_table_entry* NodeStructEntry = AddDefineToIdentifierTable(&Meta, "NODE_STRUCT", "NODE_STRUCT(%name)", StructDefinition);
|
|
||||||
identifier_table_entr* NodeProcEntry = AddDefineToIdentifierTable(&Meta, "NODE_PROC", "NODE_PROC(%name, %data)", FunctionDefinition);
|
|
||||||
|
|
||||||
symbol_list NodeStructs = FindAllMatchingSymbols(Meta, NodeStructEntry);
|
|
||||||
for (s32 i = 0; i < NodeStructs.Length; i++)
|
|
||||||
{
|
|
||||||
struct_definition_symbol Struct = NodeStructs.List[i];
|
|
||||||
|
|
||||||
Struct.Size;
|
|
||||||
|
|
||||||
for (s32 m = 0; m < Struct.MembersCount; m++)
|
|
||||||
{
|
|
||||||
Struct.Members[m];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (Meta.ErrorList.TotalUsed > 0)
|
|
||||||
{
|
|
||||||
PrintErrorList(Meta.ErrorList);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
typedef float r32;
|
|
||||||
|
|
||||||
struct test_struct
|
|
||||||
{
|
|
||||||
float Float;
|
|
||||||
int Int;
|
|
||||||
char* CharPointer;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct nested_struct
|
|
||||||
{
|
|
||||||
float Hello;
|
|
||||||
test_struct NestedValue;
|
|
||||||
r32 TypedefedValue;
|
|
||||||
};
|
|
||||||
|
|
||||||
int main(char* Args, int ArgCount)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
//
|
//
|
||||||
#ifndef FOLDHAUS_APP_CPP
|
#ifndef FOLDHAUS_APP_CPP
|
||||||
|
|
||||||
#include "../meta/foldhaus_meta_include.h"
|
#include "../meta/gs_meta_include.h"
|
||||||
|
|
||||||
#include "foldhaus_platform.h"
|
#include "foldhaus_platform.h"
|
||||||
#include "foldhaus_app.h"
|
#include "foldhaus_app.h"
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
#include "../meta/gs_meta_lexer.h"
|
#include "../meta/gs_meta_lexer.h"
|
||||||
|
|
||||||
#include "gs_font.h"
|
#include "../gs_libs/gs_font.h"
|
||||||
#include "interface.h"
|
#include "interface.h"
|
||||||
|
|
||||||
#include "foldhaus_network_ordering.h"
|
#include "foldhaus_network_ordering.h"
|
||||||
|
|
|
@ -9,17 +9,17 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#define GS_LANGUAGE_NO_PROFILER_DEFINES
|
#define GS_LANGUAGE_NO_PROFILER_DEFINES
|
||||||
#include "C:\programs-dev\gs_libs\src\gs_language.h"
|
#include "..\gs_libs\gs_language.h"
|
||||||
#include "gs_platform.h"
|
#include "..\gs_libs\gs_platform.h"
|
||||||
|
|
||||||
#include "C:\programs-dev\gs_libs\src\gs_radix_sort.h"
|
#include "..\gs_libs\gs_radix_sort.h"
|
||||||
#include "C:\programs-dev\gs_libs\src\gs_list.h"
|
#include "..\gs_libs\gs_list.h"
|
||||||
#include "C:\programs-dev\gs_libs\src\gs_bucket.h"
|
#include "..\gs_libs\gs_bucket.h"
|
||||||
|
|
||||||
#define GS_MEMORY_TRACK_ALLOCATIONS
|
#define GS_MEMORY_TRACK_ALLOCATIONS
|
||||||
#include "C:\programs-dev\gs_libs\src\gs_memory_arena.h"
|
#include "..\gs_libs\gs_memory_arena.h"
|
||||||
|
|
||||||
#include "C:\programs-dev\gs_libs\src\gs_string.h"
|
#include "..\gs_libs\gs_string.h"
|
||||||
#include "foldhaus_debug.h"
|
#include "foldhaus_debug.h"
|
||||||
|
|
||||||
global_variable debug_services* GlobalDebugServices;
|
global_variable debug_services* GlobalDebugServices;
|
||||||
|
@ -27,9 +27,9 @@ global_variable debug_services* GlobalDebugServices;
|
||||||
global_variable platform_alloc* GSAlloc;
|
global_variable platform_alloc* GSAlloc;
|
||||||
global_variable platform_free* GSFree;
|
global_variable platform_free* GSFree;
|
||||||
|
|
||||||
#include "C:\programs-dev\gs_libs\src\gs_vector_matrix.h"
|
#include "..\gs_libs\gs_vector_matrix.h"
|
||||||
|
|
||||||
#include "gs_input.h"
|
#include "..\gs_libs\gs_input.h"
|
||||||
|
|
||||||
|
|
||||||
#include "foldhaus_renderer.h"
|
#include "foldhaus_renderer.h"
|
||||||
|
|
239
src/gs_array.h
239
src/gs_array.h
|
@ -1,239 +0,0 @@
|
||||||
//
|
|
||||||
// File: gs_array.h
|
|
||||||
// Author: Peter Slattery
|
|
||||||
// Creation Date: 2020-01-01
|
|
||||||
//
|
|
||||||
#ifndef GS_ARRAY_H
|
|
||||||
|
|
||||||
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; \
|
|
||||||
\
|
|
||||||
} \
|
|
||||||
|
|
||||||
// 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) \
|
|
||||||
{ \
|
|
||||||
element_type* Entry = 0; \
|
|
||||||
if (Index <= Buffer.Used) \
|
|
||||||
{ \
|
|
||||||
bucket_index BucketIndex = GetBucketIndexForIndex(Index, Buffer.BucketSize); \
|
|
||||||
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);
|
|
||||||
|
|
||||||
#define GS_ARRAY_H
|
|
||||||
#endif // GS_ARRAY_H
|
|
|
@ -1,11 +0,0 @@
|
||||||
//
|
|
||||||
// File: gs_hashtable.h
|
|
||||||
// Author: Peter Slattery
|
|
||||||
// Creation Date: 2020-01-01
|
|
||||||
//
|
|
||||||
#ifndef GS_HASHTABLE_H
|
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
#define GS_HASHTABLE_H
|
|
||||||
#endif // GS_HASHTABLE_H
|
|
|
@ -11,10 +11,10 @@
|
||||||
#include <windowsx.h>
|
#include <windowsx.h>
|
||||||
#include <gl/gl.h>
|
#include <gl/gl.h>
|
||||||
|
|
||||||
#include "../meta/foldhaus_meta_include.h"
|
#include "../meta/gs_meta_include.h"
|
||||||
#include "foldhaus_platform.h"
|
#include "foldhaus_platform.h"
|
||||||
|
|
||||||
#include "gs_win32.cpp"
|
#include "../gs_libs/gs_win32.cpp"
|
||||||
#include "foldhaus_renderer.cpp"
|
#include "foldhaus_renderer.cpp"
|
||||||
|
|
||||||
global_variable b32 Running = false;
|
global_variable b32 Running = false;
|
||||||
|
|
Loading…
Reference in New Issue