From d23a2a68d4fef0edcec599609a016c574ddf3487 Mon Sep 17 00:00:00 2001 From: Peter Slattery Date: Mon, 20 Jan 2020 22:56:36 -0800 Subject: [PATCH] File Cleanup, removed unused files, and pulled gs_libs into the codebase so that its a standalone codebase --- gs_libs/gs_bucket.h | 130 + {src => gs_libs}/gs_font.h | 0 {src => gs_libs}/gs_input.h | 0 gs_libs/gs_language.h | 468 ++++ gs_libs/gs_list.h | 247 ++ gs_libs/gs_memory_arena.h | 614 +++++ {src => gs_libs}/gs_platform.h | 0 gs_libs/gs_radix_sort.h | 81 + gs_libs/gs_string.h | 2390 +++++++++++++++++ gs_libs/gs_string_builder.h | 104 + gs_libs/gs_vector_matrix.h | 1493 ++++++++++ {src => gs_libs}/gs_win32.cpp | 0 {src => gs_libs}/gs_win32.h | 0 meta/build.bat | 2 +- meta/foldhaus_meta.cpp | 37 +- meta/gs_meta.cpp | 31 +- meta/gs_meta_code_generator.h | 4 +- meta/gs_meta_generator.h | 164 -- ...dhaus_meta_include.h => gs_meta_include.h} | 0 meta/gs_meta_parser.h | 337 --- ...meta_type_table.h => gs_meta_type_table.h} | 0 meta/gs_meta_typeinfo_generator.h | 6 +- meta/gs_string_builder.h | 99 - meta/main_meta.cpp | 42 - meta/meta_test.cpp | 21 - src/foldhaus_app.cpp | 2 +- src/foldhaus_app.h | 2 +- src/foldhaus_platform.h | 18 +- src/gs_array.h | 239 -- src/gs_hashtable.h | 11 - src/win32_foldhaus.cpp | 4 +- 31 files changed, 5582 insertions(+), 964 deletions(-) create mode 100644 gs_libs/gs_bucket.h rename {src => gs_libs}/gs_font.h (100%) rename {src => gs_libs}/gs_input.h (100%) create mode 100644 gs_libs/gs_language.h create mode 100644 gs_libs/gs_list.h create mode 100644 gs_libs/gs_memory_arena.h rename {src => gs_libs}/gs_platform.h (100%) create mode 100644 gs_libs/gs_radix_sort.h create mode 100644 gs_libs/gs_string.h create mode 100644 gs_libs/gs_string_builder.h create mode 100644 gs_libs/gs_vector_matrix.h rename {src => gs_libs}/gs_win32.cpp (100%) rename {src => gs_libs}/gs_win32.h (100%) delete mode 100644 meta/gs_meta_generator.h rename meta/{foldhaus_meta_include.h => gs_meta_include.h} (100%) delete mode 100644 meta/gs_meta_parser.h rename meta/{foldhaus_meta_type_table.h => gs_meta_type_table.h} (100%) delete mode 100644 meta/gs_string_builder.h delete mode 100644 meta/main_meta.cpp delete mode 100644 meta/meta_test.cpp delete mode 100644 src/gs_array.h delete mode 100644 src/gs_hashtable.h diff --git a/gs_libs/gs_bucket.h b/gs_libs/gs_bucket.h new file mode 100644 index 0000000..ffeb9c7 --- /dev/null +++ b/gs_libs/gs_bucket.h @@ -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 +struct gs_bucket_bucket +{ + T* Contents; +}; + +#define GS_LIST_DEFAULT_BUCKET_SIZE 256 + +template +class gs_bucket { + public: + u32 BucketSize; + u32 BucketCount; + gs_bucket_bucket* 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 +gs_bucket::gs_bucket() +{ + this->BucketSize = 0; + this->BucketCount = 0; + this->Buckets = 0; + this->Used = 0; +} + +template +gs_bucket::gs_bucket(u32 BucketSize) +{ + this->BucketSize = BucketSize; + this->BucketCount = 0; + this->Buckets = 0; + this->Used = 0; +} + +template +void gs_bucket::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*)realloc(this->Buckets, sizeof(gs_bucket_bucket) * this->BucketCount); + gs_bucket_bucket* NewBucket = this->Buckets + (this->BucketCount - 1); + NewBucket->Contents = (T*)malloc(sizeof(T) * this->BucketSize); +} + +template +T* gs_bucket::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 +T* gs_bucket::TakeElement() +{ + if (this->Used >= this->BucketSize * this->BucketCount) + { + this->GrowBucket(); + } + + u32 Index = this->Used++; + T* Result = this->GetElementAtIndex(Index); + + return Result; +} + +template +u32 gs_bucket::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 +void gs_bucket::FreeElementAtIndex(u32 Index) +{ + Assert(Index < this->BucketSize * this->BucketCount); + + T* ToFillIn = this->GetElementAtIndex(Index); + T* ToFree = this->GetElementAtIndex(--this->Used); + *ToFillIn = *ToFree; +} diff --git a/src/gs_font.h b/gs_libs/gs_font.h similarity index 100% rename from src/gs_font.h rename to gs_libs/gs_font.h diff --git a/src/gs_input.h b/gs_libs/gs_input.h similarity index 100% rename from src/gs_input.h rename to gs_libs/gs_input.h diff --git a/gs_libs/gs_language.h b/gs_libs/gs_language.h new file mode 100644 index 0000000..9240fcf --- /dev/null +++ b/gs_libs/gs_language.h @@ -0,0 +1,468 @@ +#ifndef GS_LANGUAGE_H + +#if defined(_WIN32) || defined(_WIN64) || defined(__WIN32__) +#include + +// TODO(Peter): Get rid of math.h +#include + +#elif defined(__APPLE__) && defined(__MAC__) +// TODO(Peter): + +#else // Std lib +#include + +#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 \ No newline at end of file diff --git a/gs_libs/gs_list.h b/gs_libs/gs_list.h new file mode 100644 index 0000000..20926f0 --- /dev/null +++ b/gs_libs/gs_list.h @@ -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 +struct gs_list_entry +{ + gs_list_handle Handle; + gs_free_list Free; + T Value; +}; + +#define EntryIsFree(entry) ((entry)->Free.NextFreeEntry != 0) + +template +struct gs_list_bucket +{ + gs_list_entry* Contents; +}; + +#define GS_LIST_DEFAULT_BUCKET_SIZE 256 + +template +class gs_list { + public: + u32 BucketCapacity; + u32 BucketCount; + gs_list_bucket* Buckets; + gs_list_entry 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* GetEntryAtIndex(u32 Index); + + T* GetElementAtIndex(u32 Index); + T* GetElementWithHandle(gs_list_handle Handle); + + gs_list_entry* TakeFreeEntry(); + T* TakeElement(); + gs_list_handle PushElementOnList(T Ele); + + void FreeElementAtIndex(u32 Index); + void FreeElementWithHandle(gs_list_handle Handle); +}; + +template +gs_list::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 +gs_list::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 +void gs_list::GrowList() +{ + if (this->BucketCapacity == 0) + { + this->BucketCapacity = GS_LIST_DEFAULT_BUCKET_SIZE; + } + + this->BucketCount += 1; + this->Buckets = (gs_list_bucket*)realloc(this->Buckets, sizeof(gs_list_bucket) * this->BucketCount); + gs_list_bucket* NewBucket = this->Buckets + (this->BucketCount - 1); + NewBucket->Contents = (gs_list_entry*)malloc(sizeof(gs_list_entry) * 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 +gs_list_entry* gs_list::GetEntryAtIndex(u32 Index) +{ + gs_list_entry* 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 +T* gs_list::GetElementAtIndex(u32 Index) +{ + T* Result = 0; + gs_list_entry* Entry = this->GetEntryAtIndex(Index); + Result = &Entry->Value; + return Result; +} + +template +T* gs_list::GetElementWithHandle(gs_list_handle Handle) +{ + T* Result = 0; + gs_list_entry* Entry = this->GetEntryAtIndex(Handle.Index); + if (Entry->Handle.Generation == Handle.Generation) + { + Result = &Entry->Value; + } + return Result; +} + +template +gs_list_entry* gs_list::TakeFreeEntry() +{ + gs_list_entry* FreeEntry = (gs_list_entry*)this->FreeList.Free.NextFreeEntry; + if (FreeEntry == 0 || this->BucketCapacity == 0 || this->BucketCount == 0) + { + this->FreeList.Free.NextFreeEntry = (u8*)&this->FreeList; + FreeEntry = (gs_list_entry*)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 +T* gs_list::TakeElement() +{ + T* Result = 0; + + gs_list_entry* FreeEntry = this->TakeFreeEntry(); + Result = &FreeEntry->Value; + + return Result; +} + +template +gs_list_handle gs_list::PushElementOnList(T Ele) +{ + gs_list_handle Result = {0}; + + gs_list_entry* FreeEntry = this->TakeFreeEntry(); + FreeEntry->Value = Ele; + Result = FreeEntry->Handle; + + return Result; +} + +template +void gs_list::FreeElementAtIndex(u32 Index) +{ + Assert(Index < this->BucketCapacity * this->BucketCount); + + gs_list_entry* 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 +void gs_list::FreeElementWithHandle(gs_list_handle Handle) +{ + Assert(Handle.Index < this->BucketCapacity * this->BucketCount); + + gs_list_entry* 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--; + } +} diff --git a/gs_libs/gs_memory_arena.h b/gs_libs/gs_memory_arena.h new file mode 100644 index 0000000..b8b0a8e --- /dev/null +++ b/gs_libs/gs_memory_arena.h @@ -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 + +#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 \ No newline at end of file diff --git a/src/gs_platform.h b/gs_libs/gs_platform.h similarity index 100% rename from src/gs_platform.h rename to gs_libs/gs_platform.h diff --git a/gs_libs/gs_radix_sort.h b/gs_libs/gs_radix_sort.h new file mode 100644 index 0000000..8e62a27 --- /dev/null +++ b/gs_libs/gs_radix_sort.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 \ No newline at end of file diff --git a/gs_libs/gs_string.h b/gs_libs/gs_string.h new file mode 100644 index 0000000..1b1fad2 --- /dev/null +++ b/gs_libs/gs_string.h @@ -0,0 +1,2390 @@ +// TODO +/* +- InsertCharArrayIntoStringAt +- AppendChar +*/ +#ifndef GS_STRING_H +#include + +//////////////////////////////////////////////////////////////// +// String +//////////////////////////////////////////////////////////////// + +struct string +{ + char* Memory; + s32 Length; + s32 Max; +}; + +//////////////////////////////////////////////////////////////// +// String Tokenizing +//////////////////////////////////////////////////////////////// + +struct tokenizer +{ + char* At; + char* LineStart; + + char* Memory; + s32 MemoryLength; + u32 LineNumber; +}; + +enum token_type +{ + Token_Error, + + Token_LeftParen, + Token_RightParen, + Token_LeftSquareBracket, + Token_RightSquareBracket, + Token_LeftCurlyBracket, + Token_RightCurlyBracket, + Token_Semicolon, + Token_Operator, + Token_Comma, + Token_Period, + Token_PointerReference, + + Token_PoundDefine, // Must stay first preproc token type + Token_PoundUndef, + Token_PoundInclude, + Token_PoundIfDef, + Token_PoundIfNDef, + Token_PoundIf, + Token_PoundElif, + Token_PoundElse, + Token_PoundEndif, + Token_PoundError, + Token_PoundPragma, + Token_PoundLine, // Must stay Last Preproc Token Type + + Token_Number, + Token_Char, + Token_String, + Token_Identifier, + + Token_Comment, + Token_MultilineComment, + + Token_EndOfLine, + + Token_Unknown, + Token_EndOfStream, +}; + +char* TokenNames[] = { + "Token_Error", + "Token_LeftParen", + "Token_RightParen", + "Token_LeftSquareBracket", + "Token_RightSquareBracket", + "Token_LeftCurlyBracket", + "Token_RightCurlyBracket", + "Token_Semicolon", + "Token_Operator", + "Token_Comma", + "Token_Period", + "Token_PointerReference", + "Token_PoundDefine", + "Token_PoundUndef", + "Token_PoundInclude", + "Token_PoundIfDef", + "Token_PoundIfNDef", + "Token_PoundIf", + "Token_PoundElif", + "Token_PoundElse", + "Token_PoundEndif", + "Token_PoundError", + "Token_PoundPragma", + "Token_PoundLine", + "Token_Number", + "Token_Char", + "Token_String", + "Token_Identifier", + "Token_Comment", + "Token_MultilineComment", + "Token_EndOfLine", + "Token_Unknown", + "Token_EndOfStream", +}; + +struct token +{ + token_type Type; + u32 LineNumber; + string Text; + token* Next; // TODO(Peter): Get rid of this +}; + +//////////////////////////////////////////////////////////////// +// String Memory +//////////////////////////////////////////////////////////////// + +struct slot_header +{ + slot_header* Next; + s32 Size; +}; + +struct slot_arena +{ + u8* Memory; + s32 SlotSize; + s32 SlotCount; + slot_header* FreeList; +}; + +struct contiguous_slot_count_result +{ + s32 Count; + slot_header* LastContiguousSlot; +}; + + +//////////////////////////////////////////////////////////////// +// String Function Declarations +//////////////////////////////////////////////////////////////// + +// Utility +#if !defined GS_LANGUAGE_H + +static void GSZeroMemory (u8* Memory, s32 Size); +static s32 GSMin (s32 A, s32 B); +static s32 GSAbs (s32 A); +static float GSAbsF (float A); +static float GSPowF (float N, s32 Power); + +#endif + +// Setup + +#ifdef GS_MEMORY_H +#define PushString(str, arena, size) (str)->Memory = PushArray(arena, char, size); (str)->Length = 0; (str)->Max = size; +#endif + +static void InitializeEmptyString (string* String, char* Data, s32 DataSize); +static void InitializeString(string* String, char* Data, s32 Used, s32 Max); +static string InitializeEmptyString (char* Data, s32 DataSize); +static string InitializeString (char* Data, s32 Used, s32 Max); +static void ClearString (string* String); + +// Character Values +static bool IsSlash (char C); +static bool IsNewline (char C); +static bool IsWhitespace (char C); +static bool IsNewlineOrWhitespace (char C); +static bool IsAlpha (char C); +static bool IsUpper (char C); +static bool IsLower (char C); +static char ToUpper (char C); +static char ToLower (char C); +static bool IsNumeric (char C); +static bool IsNumericExtended (char C); +static bool IsAlphaNumeric (char C); +static bool IsOperator (char C); +static bool CharsEqualCaseInsensitive(char A, char B); + +// Tokenizing +static void EatChar(tokenizer* T); +static b32 AtValidPosition(tokenizer Tokenizer); +static b32 AtValidToken(tokenizer Tokenizer); +static char* EatToNewLine(char* C); +static s32 EatToNewLine(tokenizer* T); +static char* EatPastNewLine(char* C); +static s32 EatPastNewLine(tokenizer* T); +static char* EatWhitespace(char* C); +static s32 EatWhitespace(tokenizer* T); +static char* EatToNonWhitespaceOrNewline(char* C); +static s32 EatToNonWhitespaceOrNewline(tokenizer* T); +static char* EatToWhitespace(char* C); +static s32 EatToWhitespace(tokenizer* T); +static char* EatToCharacter(char* C, char Char); +static s32 EatToCharacter(tokenizer* T, char Char); +static char* EatPastCharacter(char* C, char Char); +static s32 EatPastCharacter(tokenizer* T, char Char); +static char* EatNumber(char* C); +static s32 EatNumber(tokenizer* T); + +// Char/Char Array +static u32 CharToUInt (char C); +static s32 CharArrayLength (char* CharArray); +static bool CharArraysEqual (char* A, s32 ALength, char* B, s32 BLength); +static bool CharArraysEqualUnsafe (char* A, char* B); +static void ReverseCharArray (char* Array, s32 Length); +#define FirstIndexOfCharInCharArray(array, find) IndexOfChar(array, 0, find) +static s32 IndexOfChar (char* Array, s32 Start, char Find); +#define FastLastIndexOfCharInCharArray(array, len, find) FastReverseIndexOfChar(array, len, 0, find) +static s32 FastReverseIndexOfChar (char* Array, s32 Length, s32 OffsetFromEnd, char Find); +#define LastIndexOfCharInCharArray(array, find) ReverseIndexOfChar(array, 0, find) +static s32 ReverseIndexOfChar (char* Array, s32 OffsetFromEnd, char Find); +static b32 CharArrayContains(char* Array, char* CheckFor); +static b32 CharArrayContainsSafe(char* Array, s32 ArrayLength, char* CheckFor, s32 CheckForLength); + +// String + +#define MakeStringBuffer(name, size) \ +char name##Backbuffer[(size)]; \ +string name = MakeString(name##Backbuffer, size); + +static string MakeString (char* Array, s32 Length, s32 Max); +static string MakeString (char* Array, s32 Length); +static string MakeString (char* Array); +static string MakeStringLiteral(char* Data); + +static bool StringsEqual (string A, string B); +static bool StringEqualsCharArray (string String, char* CharArray, s32 CharArrayLength); +static bool StringEqualsCharArray (string String, char* CharArray); +static s32 FindFirstChar (string String, char C); + +static void SetStringToChar (string* Dest, char C, s32 Count); +static void SetStringToCharArray (string* Dest, char* Source); + +static void ConcatString (string Source, string* Dest); +static void ConcatString (string Source, s32 Length, string* Dest); +static void ConcatCharToString(string* Dest, char C); +static void ConcatCharArrayToString (char* Source, string* Dest); +static void ConcatCharArrayToString (char* Source, s32 SourceLength, string* Dest); + +static void CopyStringTo (string Source, string* Dest); +static s32 CopyStringToCharArray (string Source, char* Dest, s32 DestLength); +static void CopyCharArrayToString (char* Src, string* Dest); +static void CopyCharArrayToString (char* Src, s32 SrcLength, string* Dest); +static s32 CopyCharArray (char* Source, char* Dest, s32 DestLength); +static s32 CopyCharArrayAt (char* Source, char* Dest, s32 DestLength, s32 Offset); + +static void InsertChar (string* String, char Char, s32 Index); +static void InsertStringAt (string* Dest, string Source, s32 At); +static void RemoveCharAt (string* String, s32 Index); + +static s32 IndexOfChar(string String, char C); +static s32 LastIndexOfChar(string String, char C); +static s32 SearchForCharInSet(string String, char* Set); +static s32 ReverseSearchForCharInSet(string String, char* Set); +static string Substring (string* String, s32 Start, s32 End); +static string Substring (string* String, s32 Start); + +static b32 StringContainsCharArray(string SearchIn, char* SearchFor, s32 SearchForLength); +static b32 StringContainsString(string SearchIn, string SearchFor); +static b32 StringContainsCharArrayCaseInsensitive(string SearchIn, char* SearchFor, s32 SearchForLength); +static b32 StringContainsStringCaseInsensitive(string SearchIn, string SearchFor); + +static void NullTerminate (string* String); + +static u32 HashString(string String); + +// Parsing +enum parse_type +{ + ParseType_UnsignedInt, + ParseType_SignedInt, + ParseType_Float, +}; + +struct parse_result +{ + parse_type Type; + char* OnePastLast; + union + { + u32 UnsignedIntValue; + s32 SignedIntValue; + r32 FloatValue; + }; +}; + +enum format_flags +{ + FormatFlags_LeftJustify = 0x1, + FormatFlags_ForceSign = 0x2, + FormatFlags_ForceSpaceInsteadOfSign = 0x4, + FormatFlags_ForceDecimalOrPrependOx = 0x8, + FormatFlags_PadWithZeroesInsteadOfSpaces = 0x16, +}; + +static parse_result ParseUnsignedInt (s32 Length, char* String); +static parse_result ParseSignedInt (s32 Length, char* String); +static parse_result ParseFloat (s32 Length, char* String); + +// PrintF + +#define StringExpand(str) (str).Length, (str).Memory +static void PrintFArgList(char* Dest, s32 DestMax, char* Format, va_list Args); +static void PrintF(string* String, char* Format, ...); + +//////////////////////////////////////////////////////////////// +// String Memory Function Declarations +//////////////////////////////////////////////////////////////// + +static s32 CalculateSlotCountFromSize (s32 RequestedSize, s32 SlotSize); +static bool SlotsAreContiguous (slot_header* First, slot_header* Second); +static contiguous_slot_count_result CountContiguousSlots (slot_header* First); +static slot_header* GetSlotAtOffset(slot_header* First, s32 Offset); +static slot_header* InsertSlotIntoList (slot_header* NewSlot, slot_header* ListStart); +static void AllocStringFromStringArena (string* String, s32 Size, slot_arena* Storage); +static string AllocStringFromStringArena (s32 Size, slot_arena* Storage); +static void FreeToStringArena (string* String, slot_arena* Storage); +static void ReallocFromStringArena (string* String, s32 NewSize, slot_arena* Storage); + +//////////////////////////////////////////////////////////////// +// String Utility Functions +//////////////////////////////////////////////////////////////// + +#if !defined GS_LANGUAGE_H + +static void +GSZeroMemory (u8* Memory, s32 Size) +{ + for (int i = 0; i < Size; i++) { Memory[i] = 0; } +} + +static s32 +GSMin (s32 A, s32 B) +{ + return (A < B ? A : B); +} + +static s32 +GSAbs (s32 A) +{ + return (A < 0 ? -A : A); +} + +static float +GSAbs (float A) +{ + return (A < 0 ? -A : A); +} + +static float +GSPow (float N, s32 Power) +{ + float Result = N; + for(s32 i = 1; i < Power; i++) { Result *= N; } + return Result; +} + +#endif + +//////////////////////////////////////////////////////////////// +// Init and Clear +//////////////////////////////////////////////////////////////// + +static void +InitializeEmptyString (string* String, char* Data, s32 DataSize) +{ + String->Memory = Data; + String->Max = DataSize; + String->Length = 0; +} + +static void +InitializeString(string* String, char* Data, s32 Used, s32 Max) +{ + String->Memory = Data; + String->Max = Max; + String->Length = Used; +} + +static string +InitializeEmptyString (char* Data, s32 DataSize) +{ + string Result = {}; + Result.Memory = Data; + Result.Max = DataSize; + Result.Length = 0; + return Result; +} + +static string +InitializeString (char* Data, s32 Used, s32 Max) +{ + string Result = {}; + Result.Memory = Data; + Result.Max = Max; + Result.Length = Used; + return Result; +} + +static void +ClearString (string* String) +{ + String->Memory = 0; + String->Max = 0; + String->Length = 0; +} + +//////////////////////////////////////////////////////////////// +// Char Value Types +//////////////////////////////////////////////////////////////// + +static bool IsSlash (char C) { return ((C == '\\') || (C == '/')); } +static bool IsNewline (char C) { return (C == '\n') || (C == '\r'); } +static bool IsWhitespace (char C) { return (C == ' ') || (C == '\t'); } +static bool IsNewlineOrWhitespace (char C) { return (IsWhitespace(C) || IsNewline(C)); } +static bool IsAlpha (char C) +{ + // TODO(Peter): support UTF8 chars + return ((C >= 'A') && (C <= 'Z')) || ((C >= 'a') && (C <= 'z')) || (C == '_'); +} +static bool IsUpper (char C) +{ + return ((C >= 'A') && (C <= 'Z')); +} +static bool IsLower (char C) +{ + return ((C >= 'a') && (C <= 'z')); +} +static bool IsNumeric (char C) +{ + return (C >= '0') && (C <= '9'); +} +static bool IsNumericExtended (char C) +{ + return (IsNumeric(C) || (C == 'x') || (C == 'f') || (C == '.')); +} +static bool IsAlphaNumeric (char C) +{ + return IsAlpha(C) || IsNumeric(C); +} +static bool IsOperator (char C) +{ + return ((C == '+') || + (C == '-') || + (C == '*') || + (C == '/') || + (C == '=') || + (C == '%') || + (C == '<') || + (C == '>')); +} +static char ToUpper (char A) +{ + char Result = A; + if (IsLower(A)) + { + Result += 'A' - 'a'; + } + return Result; +} +static char ToLower (char A) +{ + char Result = A; + if (IsUpper(A)) + { + Result -= 'A' - 'a'; + } + return Result; +} +static bool CharsEqualCaseInsensitive (char A, char B) +{ + b32 Result = (ToLower(A) == ToLower(B)); + return Result; +} + +//////////////////////////////////////////////////////////////// +// Tokenizing +//////////////////////////////////////////////////////////////// + +static void +EatChar (tokenizer* T) +{ + if (AtValidPosition(*T)) + { + if (IsNewline(*T->At)) + { + T->LineNumber++; + T->At++; + T->LineStart = T->At; + } + else + { + T->At++; + } + } +} + +static b32 +AtValidPosition (tokenizer Tokenizer) +{ + b32 Result = (Tokenizer.At - Tokenizer.Memory) <= Tokenizer.MemoryLength; + return Result; +} + +static b32 +AtValidToken(tokenizer Tokenizer) +{ + b32 Result = *Tokenizer.At && Tokenizer.At < (Tokenizer.Memory + Tokenizer.MemoryLength); + return Result; +} + +static char* +EatToNewLine(char* C) +{ + char* Result = C; + while (*Result && !IsNewline(*Result)) + { + Result++; + } + return Result; +} + +static s32 +EatToNewLine(tokenizer* T) +{ + char* TStart = T->At; + while (AtValidPosition(*T) && !IsNewline(*T->At)) + { + EatChar(T); + } + return T->At - TStart; +} + +static char* +EatPastNewLine(char* C) +{ + char* Result = EatToNewLine(C); + while(*Result && IsNewline(*Result)) + { + Result++; + } + return Result; +} + +static s32 +EatPastNewLine(tokenizer* T) +{ + char* TStart = T->At; + + EatToNewLine(T); + while(AtValidPosition(*T) && IsNewline(*T->At)) + { + EatChar(T); + } + + return T->At - TStart; +} + +static char* +EatWhitespace(char* C) +{ + char* Result = C; + while (*Result && IsNewlineOrWhitespace(*Result)) { Result++; } + return Result; +} + +static s32 +EatWhitespace(tokenizer* T) +{ + char* TStart = T->At; + while (AtValidPosition(*T) && IsNewlineOrWhitespace(*T->At)) { EatChar(T); } + return T->At - TStart; +} + +static char* +EatToNonWhitespaceOrNewline(char* C) +{ + char* Result = C; + while (*Result && IsWhitespace(*Result)) { Result++; } + return Result; +} + +static s32 +EatToNonWhitespaceOrNewline(tokenizer* T) +{ + char* TStart = T->At; + while (AtValidPosition(*T) && IsWhitespace(*T->At)) { EatChar(T); } + return T->At - TStart; +} + +static char* +EatToWhitespace(char* C) +{ + char* Result = C; + while (*Result && !IsWhitespace(*Result)) { Result++; } + return Result; +} + +static s32 +EatToWhitespace(tokenizer* T) +{ + char* TStart = T->At; + while (AtValidPosition(*T) && !IsWhitespace(*T->At)) { EatChar(T); } + return T->At - TStart; +} + +static char* +EatToCharacter(char* C, char Char) +{ + char* Result = C; + while (*Result && *Result != Char) { Result++; } + return Result; +} + +static s32 +EatToCharacter(tokenizer* T, char Char) +{ + char* TStart = T->At; + while (AtValidPosition(*T) && *T->At != Char) { EatChar(T); } + return T->At - TStart; +} + +static char* +EatPastCharacter(char* C, char Char) +{ + char* Result = EatToCharacter(C, Char); + if (*Result && *Result == Char) { Result++; } + return Result; +} + +static s32 +EatPastCharacter(tokenizer* T, char Char) +{ + char* TStart = T->At; + EatToCharacter(T, Char); + if (AtValidPosition(*T) && *T->At == Char) { EatChar(T); } + return T->At - TStart; +} + +static char* +EatNumber(char* C) +{ + char* Result = C; + while (*Result && IsNumericExtended(*Result)) { Result++; } + return Result; +} + +static s32 +EatNumber(tokenizer* T) +{ + char* TStart = T->At; + while (AtValidPosition(*T) && IsNumericExtended(*T->At)) { EatChar(T); } + return T->At - TStart; +} + +//////////////////////////////////////////////////////////////// +// Basic Char Operations +//////////////////////////////////////////////////////////////// + +static u32 CharToUInt (char C) { + u32 Result = (C - '0'); + return Result; +} + +static s32 +CharArrayLength (char* Array) +{ + char* C = Array; + s32 Result = 0; + while (*C) + { + *C++; + Result++; + } + return Result; +} + +static s32 +NullTerminatedCharArrayLength (char* CharArray) +{ + char* Iter = CharArray; + while (*Iter) + { + *Iter++; + } + return (Iter - CharArray); +} + +static bool +CharArraysEqual (char* A, s32 ALength, char* B, s32 BLength) +{ + bool Result = false; + if (ALength == BLength) + { + Result = true; + char* AIter = A; + char* BIter = B; + for (s32 i = 0; i < ALength; i++) + { + if(*AIter++ != *BIter++) + { + Result = false; + break; + } + } + } + return Result; +} + +static bool +CharArraysEqualUnsafe (char* A, char* B) +{ + bool Result = true; + + char* AIter = A; + char* BIter = B; + while(*AIter && *BIter) + { + if(*AIter++ != *BIter++) + { + Result = false; + break; + } + } + + if((*AIter && !*BIter) || (!*AIter && *BIter)) + { + Result = false; + } + + return Result; +} + +static bool +CharArraysEqualUpToLength (char* A, char* B, s32 Length) +{ + bool Result = true; + + char* AIter = A; + char* BIter = B; + for (s32 i = 0; i < Length; i++) + { + if(*AIter++ != *BIter++) + { + Result = false; + break; + } + } + + return Result; +} + +static void +ReverseCharArray (char* Array, s32 Length) +{ + char* ForwardIter = Array; + char* BackwardIter = Array + Length - 1; + for (s32 i = 0; i < (Length / 2); i++) + { + char F = *ForwardIter; + char B = *BackwardIter; + *ForwardIter++ = B; + *BackwardIter-- = F; + } +} + +static s32 +IndexOfChar (char* Array, s32 After, char Find) +{ + s32 Result = -1; + + s32 Counter = After; + char* Iter = Array + After; + while (*Iter) + { + if (*Iter == Find) + { + Result = Counter; + break; + } + Counter++; + *Iter++; + } + + return Result; +} + +static s32 +FastReverseIndexOfChar (char* Array, s32 Length, s32 OffsetFromEnd, char Find) +{ + s32 Result = -1; + + s32 Counter = Length - OffsetFromEnd; + char* Iter = Array + Length - OffsetFromEnd; + for (int i = 0; i < (Length - OffsetFromEnd); i++) + { + if (*Iter == Find) + { + Result = Counter; + break; + } + + *Iter--; + Counter--; + } + + return Result; +} + +static s32 +ReverseIndexOfChar (char* Array, s32 OffsetFromEnd, char Find) +{ + s32 StringLength = NullTerminatedCharArrayLength(Array); + return FastReverseIndexOfChar(Array, StringLength, OffsetFromEnd, Find); +} + +static b32 +CharArrayContains(char* Array, char* CheckFor) +{ + b32 Result = false; + + char* Src = Array; + while (*Src) + { + if (*Src == *CheckFor) + { + char* A = CheckFor; + char* B = Src; + while (*B && *A && *A == *B) + { + *B++; *A++; + } + + if (*A == 0) + { + Result = true; + break; + } + } + + Src++; + } + + return Result; +} + +static b32 +CharArrayContainsSafe(char* Array, s32 ArrayLength, char* CheckFor, s32 CheckForLength) +{ + b32 Result = false; + + if (ArrayLength >= CheckForLength) + { + char* Src = Array; + for (s32 s = 0; s < ArrayLength; s++) + { + if (*Src == *CheckFor && (s + CheckForLength <= ArrayLength)) + { + char* A = CheckFor; + char* B = Src; + for (s32 d = 0; d < CheckForLength; d++) + { + if (*B != *A) { break; } + *B++; *A++; + } + + if (*A == 0) + { + Result = true; + break; + } + } + + Src++; + } + } + + return Result; +} + +//////////////////////////////////////////////////////////////// +// Basic String Operations +//////////////////////////////////////////////////////////////// + +static bool +StringsEqual (string A, string B) +{ + bool Result = false; + + if (A.Length == B.Length) + { + Result = true; + char* AIter = A.Memory; + char* BIter = B.Memory; + for (s32 i = 0; i < A.Length; i++) + { + if (*AIter++ != *BIter++) + { + Result = false; + break; + } + } + } + + return Result; +} + +static string +MakeString (char* Array, s32 Length, s32 Max) +{ + string Result = {}; + Result.Memory = Array; + Result.Length = Length; + Result.Max = Max; + return Result; +} + +static string +MakeString (char* Array, s32 Length) +{ + string Result = {}; + Result.Memory = Array; + Result.Length = Length; + Result.Max = Length; + return Result; +} + +static string +MakeString (char* Array) +{ + s32 Length = CharArrayLength (Array); + return MakeString(Array, Length); +} + +static string +MakeStringLiteral (char* String) +{ + string Result = {}; + Result.Memory = String; + Result.Max = CharArrayLength(String); + Result.Length = Result.Max; + return Result; +} + +static bool +StringEqualsCharArray (string String, char* CharArray, s32 CharArrayLength) +{ + bool Result = false; + + if (CharArrayLength == String.Length) + { + Result = true; + + char* S = String.Memory; + char* C = CharArray; + for (s32 i = 0; i < String.Length; i++) + { + if (*C++ != *S++) + { + Result = false; + break; + } + } + } + + return Result; +} + +static bool +StringEqualsCharArray (string String, char* CharArray) +{ + s32 CLength = CharArrayLength(CharArray); + return StringEqualsCharArray(String, CharArray, CLength); +} + +static s32 +FindFirstChar (string String, char C) +{ + s32 Result = -1; + + char* Iter = String.Memory; + for (int i = 0; i < String.Length; i++) + { + if (*Iter++ == C) + { + Result = i; + break; + } + } + + return Result; +} + +static void +SetStringToChar (string* Dest, char C, s32 Count) +{ + Assert(Count <= Dest->Max); + + char* Iter = Dest->Memory; + for (int i = 0; i < Count; i++) + { + *Iter++ = C; + } + Dest->Length = Count; +} + +static void +SetStringToCharArray (string* Dest, char* Source) +{ + Dest->Length = 0; + + char* Src = Source; + char* Dst = Dest->Memory; + while (*Src && Dest->Length < Dest->Max) + { + *Dst++ = *Src++; + Dest->Length++; + } +} + +static void +ConcatString (string Source, string* Dest) +{ + Assert((Dest->Length + Source.Length) <= Dest->Max); + + char* Dst = Dest->Memory + Dest->Length; + char* Src = Source.Memory; + for (s32 i = 0; i < Source.Length; i++) + { + *Dst++ = *Src++; + Dest->Length++; + } +} + +static void +ConcatString (string Source, s32 Length, string* Dest) +{ + Assert(Length <= Source.Length); + Assert((Dest->Length + Length) <= Dest->Max); + + char* Dst = Dest->Memory + Dest->Length; + char* Src = Source.Memory; + for (s32 i = 0; i < Length; i++) + { + *Dst++ = *Src++; + Dest->Length++; + } +} + +static void +ConcatCharToString (string* Dest, char C) +{ + Assert(Dest->Length + 1 <= Dest->Max); + + char* Dst = Dest->Memory + Dest->Length; + *Dst = C; + Dest->Length++; +} + +static void +ConcatCharArrayToString (char* Source, string* Dest) +{ + Assert(CharArrayLength(Source) + Dest->Length <= Dest->Max); + + char* Dst = Dest->Memory + Dest->Length; + char* Src = Source; + while (Dest->Length < Dest->Max && + *Src) + { + *Dst++ = *Src++; + Dest->Length++; + } +} + +static void +ConcatCharArrayToString (char* Source, s32 SourceLength, string* Dest) +{ + Assert(SourceLength + Dest->Length <= Dest->Max); + + char* Dst = Dest->Memory + Dest->Length; + char* Src = Source; + for (int i = 0; i < SourceLength && Dest->Length < Dest->Max; i++) + { + *Dst++ = *Src++; + Dest->Length++; + } +} + +static void +CopyStringTo (string Source, string* Dest) +{ + char* Src = Source.Memory; + char* Dst = Dest->Memory; + s32 CopyLength = GSMin(Source.Length, Dest->Max); + for (int i = 0; i < CopyLength; i++) + { + *Dst++ = *Src++; + } + Dest->Length = Source.Length; +} + +static s32 +CopyStringToCharArray (string Source, char* Dest, s32 DestLength) +{ + char* Src = Source.Memory; + char* Dst = Dest; + s32 CopyLength = GSMin(Source.Length, DestLength); + for (int i = 0; i < CopyLength; i++) + { + *Dst++ = *Src++; + } + return CopyLength; +} + +static void +CopyCharArrayToString (char* Source, string* Dest) +{ + char* Src = Source; + char* Dst = Dest->Memory; + s32 Copied = 0; + while (*Src && Copied < Dest->Max) + { + *Dst++ = *Src++; + Copied++; + } + *Dst++ = 0; + Dest->Length = Copied; +} + +static void +CopyCharArrayToString (char* Source, s32 SourceLength, string* Dest) +{ + Assert(SourceLength <= Dest->Max); + + char* Src = Source; + char* Dst = Dest->Memory; + for (s32 i = 0; i < SourceLength; i++) + { + *Dst++ = *Src++; + } + *Dst++ = 0; + Dest->Length = SourceLength; +} + +static s32 +CopyCharArray (char* Source, char* Dest, s32 DestLength) +{ + char* Src = Source; + char* Dst = Dest; + s32 i = 0; + while (*Src && i < DestLength) + { + *Dst++ = *Src++; + i++; + } + return i; +} + +static s32 +CopyCharArrayAt (char* Source, char* Dest, s32 DestLength, s32 Offset) +{ + Assert(Offset < DestLength); + + char* Src = Source; + char* Dst = Dest + Offset; + s32 i = Offset; + while (*Src && i < DestLength) + { + *Dst++ = *Src++; + i++; + } + return i - Offset; +} + +static void +InsertChar (string* String, char Char, s32 Index) +{ + Assert(Index >= 0 && Index < String->Max); + Assert(String->Length < String->Max); + + char* Src = String->Memory + String->Length - 1; + char* Dst = Src + 1; + for (int i = String->Length - 1; i >= Index; i--) + { + *Dst-- = *Src--; + } + + *(String->Memory + Index) = Char; + String->Length++; +} + +static void +RemoveCharAt (string* String, s32 Index) +{ + Assert(Index >= 0 && Index < String->Max); + + char* Dst = String->Memory + Index; + char* Src = Dst + 1; + for (int i = Index; i < String->Length; i++) + { + *Dst++ = *Src++; + } + *Dst = 0; + String->Length--; +} + +static s32 +IndexOfChar(string String, char C) +{ + s32 Result = -1; + char* At = String.Memory; + for (s32 i = 0; i < String.Length; i++) + { + if (*At == C) + { + Result = i; + break; + } + At++; + } + return Result; +} + +static s32 +LastIndexOfChar(string String, char C) +{ + s32 Result = -1; + char* At = String.Memory + String.Length - 1; + for (s32 i = 0; i < String.Length; i++) + { + if (*At == C) + { + Result = String.Length - i; + break; + } + At--; + } + return Result; +} + +static s32 +SearchForCharInSet(string String, char* Set) +{ + s32 Index = -1; + + char* At = String.Memory; + for (s32 i = 0; i < String.Length; i++) + { + char* Needle = Set; + while (*Needle) + { + if (*At == *Needle) + { + Index = String.Length - i; + break; + } + + Needle++; + } + + if (Index >= 0) + { + break; + } + + At++; + } + + return Index; +} + +static s32 +ReverseSearchForCharInSet(string String, char* Set) +{ + s32 Index = -1; + + for (s32 i = String.Length - 1; i >= 0; i--) + { + char* Needle = Set; + while (*Needle) + { + if (String.Memory[i] == *Needle) + { + Index = i; + break; + } + + Needle++; + } + + if (Index >= 0) + { + break; + } + } + + return Index; +} + +static string +Substring (string String, s32 Start, s32 End) +{ + Assert(Start >= 0 && End > Start && End <= String.Length); + + string Result = {}; + Result.Memory = String.Memory + Start; + Result.Length = End - Start; + return Result; +} + +static string +Substring (string String, s32 Start) +{ + Assert(Start >= 0 && Start < String.Length); + + string Result = {}; + Result.Memory = String.Memory + Start; + Result.Length = String.Length - Start; + return Result; +} + +static b32 +StringContainsCharArray(string SearchIn, char* SearchFor, s32 SearchForLength) +{ + b32 Result = false; + + char* SearchInAt = SearchIn.Memory; + for (s32 i = 0; i < (SearchIn.Length - SearchForLength) + 1; i++) + { + char* InAt = SearchInAt; + char* ForAt = SearchFor; + s32 LengthMatch = 0; + while (*InAt == *ForAt) + { + InAt++; + ForAt++; + LengthMatch++; + } + if (LengthMatch == SearchForLength) + { + Result = true; + break; + } + SearchInAt++; + } + + return Result; +} + +static b32 +StringContainsString(string SearchIn, string SearchFor) +{ + return StringContainsCharArray(SearchIn, SearchFor.Memory, SearchFor.Length); +} + +static b32 +StringContainsCharArrayCaseInsensitive(string SearchIn, char* SearchFor, s32 SearchForLength) +{ + b32 Result = false; + + char* SearchInAt = SearchIn.Memory; + for (s32 i = 0; i < (SearchIn.Length - SearchForLength) + 1; i++) + { + char* InAt = SearchInAt; + char* ForAt = SearchFor; + s32 LengthMatch = 0; + while (CharsEqualCaseInsensitive(*InAt, *ForAt)) + { + InAt++; + ForAt++; + LengthMatch++; + } + if (LengthMatch == SearchForLength) + { + Result = true; + break; + } + SearchInAt++; + } + + return Result; +} + +static b32 +StringContainsStringCaseInsensitive(string SearchIn, string SearchFor) +{ + return StringContainsCharArrayCaseInsensitive(SearchIn, SearchFor.Memory, SearchFor.Length); +} + +static void +NullTerminate (string* String) +{ + Assert(String->Length + 1 <= String->Max); + *(String->Memory + String->Length) = 0; + String->Length++; +} + +// http://www.cse.yorku.ca/~oz/hash.html +// djb2 hash +static u32 +HashString(string String) +{ + u32 Hash = 5381; + for (s32 i = 0; i < String.Length; i++) + { + Hash = ((Hash << 5) + Hash) + (u32)String.Memory[i]; /* hash * 33 + c */ + } + return Hash; +} + +static void +InsertStringAt (string* Dest, string Source, s32 At) +{ + Assert(At + Source.Length < Dest->Max); + Assert(At < Dest->Length); + + char* Src = Dest->Memory + Dest->Length; + char* Dst = Dest->Memory + Source.Length + Dest->Length; + for (s32 i = Dest->Length - 1; i >= At; i--) + { + *--Dst = *--Src; + } + + Src = Source.Memory; + Dst = Dest->Memory + At; + for (s32 j = 0; j < Source.Length; j++) + { + *Dst++ = *Src++; + } + + Dest->Length += Source.Length; +} + +//////////////////////////////////////////////////////////////// +// String Parsing +//////////////////////////////////////////////////////////////// + +// NOTE(Peter): parameters are all in order Length, String because +// that matches the order of C's printf %.*s format specifier. This +// is convenient because you can use StringExpand to parse a string +// struct +// :StringExpandNote +static parse_result +ParseUnsignedInt (s32 Length, char* String) +{ + Assert(IsNumeric(*String)); + parse_result Result = {}; + Result.Type = ParseType_UnsignedInt; + + char* Iter = String; + u32 ResultValue = 0; + for (s32 i = 0; i < Length; i++) + { + ResultValue = CharToUInt(*Iter++) + (ResultValue * 10); + } + + Result.UnsignedIntValue = ResultValue; + Result.OnePastLast = Iter; + + return Result; +} + +static parse_result +ParseUnsignedIntUnsafe (char* String) +{ + char* Start = String; + char* End = EatNumber(String + 1); + return ParseUnsignedInt(End - Start, String); +} + +// :StringExpandNote +static parse_result +ParseSignedInt (s32 Length, char* String) +{ + Assert(Length > 0); + parse_result Result = {}; + Result.Type = ParseType_SignedInt; + + s32 Negative = 1; + s32 LengthRemaining = Length; + s32 ResultValue = 0; + char* Iter = String; + + if (*Iter == '-') { + LengthRemaining--; + *Iter++; + Negative = -1; + } + + for (s32 i = 0; i < LengthRemaining; i++) + { + ResultValue = CharToUInt(*Iter++) + (ResultValue * 10); + } + + ResultValue *= Negative; + + Result.SignedIntValue = ResultValue; + Result.OnePastLast = Iter; + + return Result; +} + +static parse_result +ParseSignedIntUnsafe (char* String) +{ + char* Start = String; + char* End = EatNumber(String + 1); + return ParseSignedInt(End - Start, String); +} + +// :StringExpandNote +static parse_result +ParseFloat (s32 Length, char* String) +{ + parse_result Result = {}; + Result.Type = ParseType_Float; + + s32 Negative = 1; + s32 LengthRemaining = Length; + float ResultValue = 0; + char* Iter = String; + + if (*Iter == '-') { + LengthRemaining--; + *Iter++; + Negative = -1; + } + + for (s32 i = 0; i < LengthRemaining; i++) + { + if (IsNumeric(*Iter)) + { + ResultValue = (float)CharToUInt(*Iter++) + (ResultValue * 10); + } + else if (*Iter == '.' || *Iter == 0) + { + LengthRemaining -= i; + break; + } + } + + if (*Iter == '.') + { + *Iter++; + float AfterPoint = 0; + s32 PlacesAfterPoint = 0; + + for (s32 i = 0; i < LengthRemaining; i++) + { + if (IsNumeric(*Iter)) + { + AfterPoint = (float)CharToUInt(*Iter++) + (AfterPoint * 10); + PlacesAfterPoint++; + } + else + { + break; + } + } + + AfterPoint = AfterPoint / GSPow(10, PlacesAfterPoint); + ResultValue += AfterPoint; + } + + ResultValue *= Negative; + + Result.FloatValue = ResultValue; + Result.OnePastLast = Iter; + + return Result; +} + +static parse_result +ParseFloatUnsafe (char* String) +{ + char* Start = String; + char* End = EatNumber(String + 1); + return ParseFloat(End - Start, String); +} + +static s32 +UIntToString (u32 Int, char* String, s32 MaxLength, b32 FormatFlags = 0, s32 MinimumLength = 0) +{ + s32 Remaining = Int; + char* Iter = String; + while (Remaining > 0 && (Iter - String) < MaxLength) + { + *Iter++ = '0' + (Remaining % 10); + Remaining /= 10; + } + s32 CharsCopied = Iter - String; + ReverseCharArray(String, CharsCopied); + return CharsCopied; +} + +static s32 +IntToString (s32 Int, char* String, s32 MaxLength, b32 FormatFlags = 0, s32 MinimumLength = 0) +{ + s32 Remaining = Int; + s32 CharsCopied = 0; + + char* Iter = String; + + bool Negative = Remaining < 0; + Remaining = GSAbs(Remaining); + + if (Remaining > 0) + { + while (Remaining > 0 && CharsCopied < MaxLength) + { + *Iter++ = '0' + (Remaining % 10); + Remaining /= 10; + CharsCopied++; + } + } + else if (Remaining == 0) + { + *Iter++ = '0'; + } + + if (Negative) + { + *Iter++ = '-'; + CharsCopied++; + } + + ReverseCharArray(String, CharsCopied); + return CharsCopied; +} + +static s32 +IntToString (s32 Int, char* String, s32 MaxLength, s32 Offset, b32 FormatFlags = 0, s32 MinimumWidth = 0) +{ + char* StringStart = String + Offset; + s32 LengthWritten = IntToString(Int, StringStart, MaxLength - Offset); + return LengthWritten; +} + +static s32 +FloatToString(float Float, char *String, s32 MaxLength, s32 AfterPoint = 0, b32 FormatFlags = 0, s32 MinimumWidth = 0) +{ + s32 IPart = (s32)Float; + float FPart = GSAbs(Float - (float)IPart); + + s32 i = IntToString(IPart, String, MaxLength); + + if (AfterPoint > 1) + { + String[i++] = '.'; + + s32 FPartInt = FPart * GSPow(10, AfterPoint); + i += IntToString(FPartInt, String, MaxLength, i, 0, 0); + } + + return i; +} + +//////////////////////////////////////////////////////////////// +// PrintF +//////////////////////////////////////////////////////////////// + +static void +OutChar (string* String, char C) +{ + if (String->Length < String->Max) + { + String->Memory[String->Length] = C; + String->Length++; + } +} + +char OctalDigits[] = "01234567"; +char DecimalDigits[] = "0123456789"; +char HexDigits[] = "0123456789ABCDEF"; + +static void +U64ToASCII (string* String, u64 Value, s32 Base, char* Digits) +{ + u64 ValueRemaining = Value; + char* Start = String->Memory + String->Length; + do { + s32 DigitsIndex = ValueRemaining % Base; + char Digit = Digits[DigitsIndex]; + OutChar(String, Digit); + ValueRemaining /= Base; + }while (ValueRemaining); + char* End = String->Memory + String->Length; + + while (Start < End) + { + End--; + char Temp = *End; + *End = *Start; + *Start = Temp; + *Start++; + } +} + +static void +F64ToASCII (string* String, r64 Value, s32 Precision) +{ + if (Value < 0) + { + OutChar(String, '-'); + Value = -Value; + } + + u64 IntegerPart = (u64)Value; + Value -= IntegerPart; + + U64ToASCII(String, IntegerPart, 10, DecimalDigits); + + OutChar(String, '.'); + + for (s32 i = 0; i < Precision; i++) + { + Value *= 10.f; + u32 DecimalPlace = Value; + Value -= DecimalPlace; + OutChar(String, DecimalDigits[DecimalPlace]); + } +} + +internal s64 +ReadVarArgsSignedInteger (s32 Width, va_list* Args) +{ + s64 Result = 0; + switch (Width) + { + case 1: { Result = (s64)va_arg(*Args, s8); } break; + case 2: { Result = (s64)va_arg(*Args, s16); } break; + case 4: { Result = (s64)va_arg(*Args, s32); } break; + case 8: { Result = (s64)va_arg(*Args, s64); } break; + InvalidDefaultCase; + } + return Result; +} + +internal r64 +ReadVarArgsUnsignedInteger (s32 Width, va_list* Args) +{ + u64 Result = 0; + switch (Width) + { + case 1: { Result = (u64)va_arg(*Args, u8); } break; + case 2: { Result = (u64)va_arg(*Args, u16); } break; + case 4: { Result = (u64)va_arg(*Args, u32); } break; + case 8: { Result = (u64)va_arg(*Args, u64); } break; + InvalidDefaultCase; + } + return Result; +} + +internal r64 +ReadVarArgsFloat (s32 Width, va_list* Args) +{ + r64 Result = 0; + switch (Width) + { + case 4: { Result = (r64)va_arg(*Args, r64); } break; + case 8: { Result = (r64)va_arg(*Args, r64); } break; + InvalidDefaultCase; + } + return Result; +} + +internal s32 +PrintFArgsList (char* Dest, s32 DestMax, char* Format, va_list Args) +{ + char* DestAt = Dest; + + char* FormatAt = Format; + while (*FormatAt) + { + if (FormatAt[0] != '%') + { + *DestAt++ = *FormatAt++; + } + else if (FormatAt[0] == '%' && FormatAt[1] == '%') // Print the % symbol + { + *DestAt++ = *FormatAt++; + } + else + { + FormatAt++; + + // Flags + if (FormatAt[0] == '-') + { + FormatAt++; + } + else if (FormatAt[0] == '+') + { + FormatAt++; + } + else if (FormatAt[0] == ' ') + { + FormatAt++; + } + else if (FormatAt[0] == '#') + { + FormatAt++; + } + else if (FormatAt[0] == '0') + { + FormatAt++; + } + + // Width + b32 WidthSpecified = false; + s32 Width = 0; + + if (IsNumeric(FormatAt[0])) + { + WidthSpecified = true; + parse_result Parse = ParseSignedIntUnsafe(FormatAt); + FormatAt = Parse.OnePastLast; + Width = Parse.SignedIntValue; + } + else if (FormatAt[0] == '*') + { + WidthSpecified = true; + Width = va_arg(Args, s32); + Assert(Width >= 0); + FormatAt++; + } + + // Precision + b32 PrecisionSpecified = false; + s32 Precision = 0; + + if (FormatAt[0] == '.') + { + FormatAt++; + if (IsNumeric(FormatAt[0])) + { + PrecisionSpecified = true; + parse_result Parse = ParseSignedIntUnsafe(FormatAt); + FormatAt = Parse.OnePastLast; + Precision = Parse.SignedIntValue; + } + else if (FormatAt[0] == '*') + { + PrecisionSpecified = true; + Precision = va_arg(Args, s32); + Assert(Precision >= 0); + FormatAt++; + } + } + + // Length + b32 LengthSpecified = false; + s32 Length = 4; + + if (FormatAt[0] == 'h' && FormatAt[1] == 'h') + { + LengthSpecified = true; + LengthSpecified = 1; + FormatAt += 2; + } + else if (FormatAt[0] == 'h') + { + LengthSpecified = true; + LengthSpecified = 2; + FormatAt++; + } + else if (FormatAt[0] == 'l' && FormatAt[1] == 'l') + { + LengthSpecified = true; + LengthSpecified = 8; + FormatAt += 2; + } + else if (FormatAt[0] == 'l') + { + LengthSpecified = true; + LengthSpecified = 4; + FormatAt++; + } + else if (FormatAt[0] == 'j') + { + LengthSpecified = true; + LengthSpecified = 8; + FormatAt++; + } + else if (FormatAt[0] == 'z') + { + FormatAt++; + } + else if (FormatAt[0] == 't') + { + FormatAt++; + } + else if (FormatAt[0] == 'L') + { + FormatAt++; + } + + // Format Specifier + s32 DestLengthRemaining = DestMax - (DestAt - Dest); + + char Temp[64]; + string TempDest = MakeString(Temp, 0, 64); + + if (FormatAt[0] == 'd' || FormatAt[0] == 'i') + { + s64 SignedInt = ReadVarArgsSignedInteger(Length, &Args); + if (SignedInt < 0) + { + OutChar(&TempDest, '-'); + SignedInt *= -1; + } + U64ToASCII(&TempDest, (u64)SignedInt, 10, DecimalDigits); + } + else if (FormatAt[0] == 'u') + { + u32 UnsignedInt = ReadVarArgsUnsignedInteger(Length, &Args); + U64ToASCII(&TempDest, UnsignedInt, 10, DecimalDigits); + } + else if (FormatAt[0] == 'o') + { + u32 UnsignedInt = ReadVarArgsUnsignedInteger(Length, &Args); + U64ToASCII(&TempDest, UnsignedInt, 8, OctalDigits); + } + else if (FormatAt[0] == 'x' || FormatAt[0] == 'X') + { + u32 UnsignedInt = ReadVarArgsUnsignedInteger(Length, &Args); + U64ToASCII(&TempDest, UnsignedInt, 16, HexDigits); + } + else if (FormatAt[0] == 'f' || FormatAt[0] == 'F') + { + r64 Float = ReadVarArgsFloat(Length, &Args); + s32 AfterPoint = 6; + if (PrecisionSpecified) + { + AfterPoint = Precision; + } + F64ToASCII(&TempDest, Float, AfterPoint); + } + else if (FormatAt[0] == 'c') + { + char InsertChar = va_arg(Args, char); + OutChar(&TempDest, InsertChar); + } + else if (FormatAt[0] == 's') + { + char* InsertString = va_arg(Args, char*); + + s32 InsertStringLength = CharArrayLength(InsertString); + if (PrecisionSpecified) + { + InsertStringLength = GSMin(InsertStringLength, Precision); + } + InsertStringLength = GSMin(DestLengthRemaining, InsertStringLength); + + for (s32 c = 0; c < InsertStringLength; c++) + { + OutChar(&TempDest, *InsertString++); + } + } + else if (FormatAt[0] == 'S') + { + string InsertString = va_arg(Args, string); + + for (s32 c = 0; c < InsertString.Length; c++) + { + OutChar(&TempDest, InsertString.Memory[c]); + } + } + else if (FormatAt[0] == 'p') + { + // TODO(Peter): Pointer Address + } + else + { + // NOTE(Peter): Non-specifier character found + InvalidCodePath; + } + + for (s32 i = 0; i < TempDest.Length; i++) + { + *DestAt++ = TempDest.Memory[i]; + } + + *FormatAt++; + } + } + + s32 FormattedLength = DestAt - Dest; + return FormattedLength; +} + +internal void +PrintF (string* String, char* Format, ...) +{ + va_list Args; + va_start(Args, Format); + String->Length = 0; + String->Length += PrintFArgsList(String->Memory + String->Length, String->Max - String->Length, Format, Args); + va_end(Args); +} + +internal void +AppendPrintF (string* String, char* Format, ...) +{ + va_list Args; + va_start(Args, Format); + String->Length += PrintFArgsList(String->Memory + String->Length, String->Max - String->Length, Format, Args); + va_end(Args); +} + +//////////////////////////////////////////////////////////////// +// String Memory Function Definitions +//////////////////////////////////////////////////////////////// + +static s32 +CalculateSlotCountFromSize (s32 RequestedSize, s32 SlotSize) +{ + s32 SlotCount = RequestedSize / SlotSize; + if (SlotCount * SlotSize < RequestedSize) + { + SlotCount += 1; + } + return SlotCount; +} + +static bool +SlotsAreContiguous (slot_header* First, slot_header* Second) +{ + bool Result = false; + u8* FirstSlotNextAddress = (u8*)First + First->Size; + u8* SecondAddress = (u8*)Second; + Result = FirstSlotNextAddress == SecondAddress; + return Result; +} + +static contiguous_slot_count_result +CountContiguousSlots (slot_header* First) +{ + Assert(First != 0); + + contiguous_slot_count_result Result = {}; + Result.Count = 1; + + slot_header* IterPrev = First; + slot_header* Iter = First->Next; + while (Iter && SlotsAreContiguous(IterPrev, Iter)) + { + Result.Count++; + IterPrev = Iter; + Iter = Iter->Next; + } + + Result.LastContiguousSlot = IterPrev; + return Result; +} + +static slot_header* +GetSlotAtOffset(slot_header* First, s32 Offset) +{ + slot_header* Iter = First; + s32 Count = 0; + while (Count < Offset && Iter) + { + Iter = Iter->Next; + Count++; + } + return Iter; +} + +static slot_header* +InsertSlotIntoList (slot_header* NewSlot, slot_header* ListStart) +{ + slot_header* List = ListStart; + if (NewSlot < List) + { + NewSlot->Next = List; + List = NewSlot; + } + else + { + slot_header* PrevIter = List; + slot_header* Iter = List->Next; + while (Iter && NewSlot > Iter) + { + PrevIter = Iter; + Iter = Iter->Next; + } + + Assert(PrevIter); + if (PrevIter) + { + PrevIter->Next = NewSlot; + } + + if (Iter) + { + NewSlot->Next = Iter; + } + } + return List; +} + +static void +AllocStringFromStringArena (string* String, s32 Size, slot_arena* Storage) +{ + s32 SlotCount = CalculateSlotCountFromSize(Size, Storage->SlotSize); + slot_header* Slot = Storage->FreeList; + slot_header* PrevSlot = 0; + while (Slot) + { + contiguous_slot_count_result ContiguousSlots = CountContiguousSlots(Slot); + if (ContiguousSlots.Count >= SlotCount) + { + slot_header* NextStartSlot = GetSlotAtOffset(Slot, SlotCount); + if (PrevSlot) + { + PrevSlot->Next = NextStartSlot; + } + else + { + Storage->FreeList = NextStartSlot; + } + break; + } + else + { + PrevSlot = Slot; + Slot = Slot->Next; + } + } + + if (Slot) + { + String->Memory = (char*)Slot; + GSZeroMemory((u8*)String->Memory, SlotCount * Storage->SlotSize); + String->Max = SlotCount * Storage->SlotSize; + String->Length = 0; + } +} + +static string +AllocStringFromStringArena (s32 Size, slot_arena* Storage) +{ + string Result = {0}; + AllocStringFromStringArena(&Result, Size, Storage); + return Result; +} + +static void +FreeToStringArena (string* String, slot_arena* Storage) +{ + u8* Base = (u8*)(String->Memory); + u8* End = Base + String->Max - 1; + u8* MemoryEnd = Storage->Memory + (Storage->SlotSize * Storage->SlotCount); + Assert((Base >= Storage->Memory) && (End < MemoryEnd)); + Assert((String->Max % Storage->SlotSize) == 0); + + s32 SizeReclaimed = 0; + slot_header* Slot = (slot_header*)Base; + while (SizeReclaimed < String->Max) + { + Slot->Size = Storage->SlotSize; + Storage->FreeList = InsertSlotIntoList(Slot, Storage->FreeList); + SizeReclaimed += Storage->SlotSize; + Slot = (slot_header*)(Base + SizeReclaimed); + } + + String->Memory = 0; + String->Length = 0; + String->Max = 0; +} + +static void +ReallocFromStringArena (string* String, s32 NewSize, slot_arena* Storage) +{ + string NewString = AllocStringFromStringArena(NewSize, Storage); + CopyStringTo(*String, &NewString); + FreeToStringArena(String, Storage); + *String = NewString; +} + +#if defined(DEBUG) + +void DEBUGPrintChars (string* String, s32 Count) +{ + char* Iter = String->Memory; + for (int i = 0; i < Count; i++) + { + *Iter++ = (char)('A' + i); + } + String->Length = Count; +} + +#ifdef DEBUG_GS_STRING + +#include + +static void +TestStrings() +{ + + slot_arena StringArena = {}; + + s32 TestCount = 0; + s32 SuccessCount = 0; + + DebugPrint("\n\n-------------------------------------------------\n Begin Testing Strings\n\n\n"); + + //////////////////////////////////////////////////////////////// + // Char Functions + + char ForwardArray[] = "Hello, Sailor"; + char BackwardArray[] = "roliaS ,olleH"; + TestClean(CharArraysEqual(ForwardArray, 13, ForwardArray, 13), "String Equality"); + TestClean(!CharArraysEqual(ForwardArray, 13, BackwardArray, 13), "String Equality"); + + TestClean(IndexOfChar(ForwardArray, 0, ',') == 5, "Index Of Char"); + TestClean(IndexOfChar(ForwardArray, 5, 'l') == 10, "Index of Char (skipping first 5)"); + TestClean(FastReverseIndexOfChar(ForwardArray, 13, 0, 'o') == 11, "Fast Reverse Index Of Char"); + TestClean(ReverseIndexOfChar(ForwardArray, 0, 'o') == 11, "Reverse Index of Char"); + TestClean(ReverseIndexOfChar(ForwardArray, 3, 'o') == 4, "Reverse Index of Char (skipping last 3)"); + TestClean(LastIndexOfChar(ForwardArray, 'o') == 11, "Last Index of Char"); + + ReverseCharArray(ForwardArray, 13); + TestClean(CharArraysEqual(ForwardArray, 13, BackwardArray, 13), "Reversing Char Array"); + + char UIntString[] = "1234"; + u32 UIntValue = 1234; + u32 ParsedUInt = ParseUnsignedInt(UIntString, 4); + TestClean((ParsedUInt == UIntValue), "String To U32"); + char StringifiedUInt[4] = {}; + UIntToString(UIntValue, StringifiedUInt, 4); + TestClean(CharArraysEqual(UIntString, 4, StringifiedUInt, 4), "U32 To String"); + + char IntString[] = "-1234"; + s32 IntValue = -1234; + s32 ParsedInt = ParseSignedInt(IntString, 5); + TestClean((ParsedInt == IntValue), "String To S32"); + char StringifiedInt[5] = {}; + IntToString(IntValue, StringifiedInt, 5); + TestClean(CharArraysEqual(IntString, 5, StringifiedInt, 5), "S32 to String"); + + char FloatString[] = "-1234.125"; + float FloatValue = -1234.125f; + float ParsedFloat = ParseFloat(FloatString, 8); + TestClean((ParsedFloat == FloatValue), "String To Float"); + char StringifiedFloat[10] = {}; + FloatToString(FloatValue, StringifiedFloat, 10, 3); + TestClean(CharArraysEqual(FloatString, 8, StringifiedFloat, 8), "Float To String"); + + + //////////////////////////////////////////////////////////////// + // + + StringArena.SlotSize = 256; + StringArena.SlotCount = 32; + StringArena.Memory = malloc(StringArena.SlotSize * StringArena.SlotCount); + slot_header* PrevSlotHeader = 0; + for (int i = StringArena.SlotCount - 1; i >= 0; i--) + { + u8* SlotBase = StringArena.Memory + (i * StringArena.SlotSize); + slot_header* SlotHeader = (slot_header*)SlotBase; + SlotHeader->Size = StringArena.SlotSize; + SlotHeader->Next = PrevSlotHeader; + + // TEST(peter): Should be true always, except on the first iteration, when there is no next slot + bool Contiguity = SlotsAreContiguous(SlotHeader, PrevSlotHeader); + TestClean((Contiguity || SlotHeader->Next == 0), "Contiguous Arenas"); + + PrevSlotHeader = SlotHeader; + } + StringArena.FreeList = PrevSlotHeader; + + // TEST(peter): Count Should equal StringArena.SlotCount + s32 ContiguousSlotsCountBefore = CountContiguousSlots(StringArena.FreeList).Count; + TestClean((ContiguousSlotsCountBefore == StringArena.SlotCount), "Contiguous Arenas"); + + // TEST(peter): Should be false + bool Contiguity = SlotsAreContiguous(StringArena.FreeList, StringArena.FreeList->Next->Next); + Contiguity = SlotsAreContiguous(StringArena.FreeList->Next->Next, StringArena.FreeList); + TestClean(!Contiguity, "Non Contiguous Arenas"); + + s32 Slots = CalculateSlotCountFromSize(10, 256); + TestClean(Slots == 1, "Slot Sizing"); + Slots = CalculateSlotCountFromSize(256, 256); + TestClean(Slots == 1, "Slot Sizing"); + Slots = CalculateSlotCountFromSize(345, 256); + TestClean(Slots == 2, "Slot Sizing"); + Slots = CalculateSlotCountFromSize(1024, 256); + TestClean(Slots == 4, "Slot Sizing"); + + slot_header* HeaderTen = GetSlotAtOffset(StringArena.FreeList, 10); + slot_header* HeaderThree = GetSlotAtOffset(StringArena.FreeList, 3); + slot_header* HeaderFive = GetSlotAtOffset(StringArena.FreeList, 5); + + string StringA = AllocStringFromStringArena(10, &StringArena); + string StringB = AllocStringFromStringArena(345, &StringArena); + +#if 0 + // TEST(peter): Should TestClean + u8* RandomMemory = (u8*)malloc(256); + string RandomMemString = {}; + RandomMemString.Memory = (char*)RandomMemory; + RandomMemString.Max = 256; + FreeToStringArena(&RandomMemString, &StringArena); +#endif + FreeToStringArena(&StringA, &StringArena); + FreeToStringArena(&StringB, &StringArena); + // TEST(peter): After freeing both allocations, ContiguousSlotCountBefore and ContiguousSlotCountAfter should be equal + s32 ContiguousSlotCountAfter = CountContiguousSlots(StringArena.FreeList).Count; + TestClean(ContiguousSlotCountAfter == ContiguousSlotsCountBefore, "Add and REmove Slots from Arena"); + + // TEST(peter): Set up a free list where the first element is too small, so it has to traverse to find an appropriately + // sized block + // The slots will look list [256][used][256][256][256] etc.. + StringA = AllocStringFromStringArena(256, &StringArena); + StringB = AllocStringFromStringArena(256, &StringArena); + FreeToStringArena(&StringA, &StringArena); + u32 Contiguous = CountContiguousSlots(StringArena.FreeList).Count; // Should = 1; + string StringC = AllocStringFromStringArena(512, &StringArena); + slot_header* HeaderC = (slot_header*)(StringC.Memory); + + string ReallocTestString = AllocStringFromStringArena(256, &StringArena); + DEBUGPrintChars(&ReallocTestString, 24); + ReallocFromStringArena(&ReallocTestString, 512, &StringArena); + + + string TestString = AllocStringFromStringArena(10, &StringArena); + DEBUGPrintChars(&TestString, TestString.Max); + ReallocFromStringArena(&TestString, 20, &StringArena); + DEBUGPrintChars(&TestString, TestString.Max); + ReallocFromStringArena(&TestString, 10, &StringArena); + FreeToStringArena(&TestString, &StringArena); + + string EqualityStringA = AllocStringFromStringArena(345, &StringArena); + string EqualityStringB = AllocStringFromStringArena(415, &StringArena); + // Equality should succeed despite length differences + string EqualityStringC = AllocStringFromStringArena(256, &StringArena); + string EqualityStringD = AllocStringFromStringArena(256, &StringArena); // Equality should fail + string EqualityStringEmpty = {}; + + DEBUGPrintChars(&EqualityStringA, 24); + DEBUGPrintChars(&EqualityStringB, 24); + DEBUGPrintChars(&EqualityStringC, 24); + DEBUGPrintChars(&EqualityStringD, 12); + + bool ABEquality = StringsEqual(EqualityStringA, EqualityStringB); // Should Succeed + bool ACEquality = StringsEqual(EqualityStringA, EqualityStringC); // Should Succeed + bool ADEquality = StringsEqual(EqualityStringA, EqualityStringD); // Should Fail + bool AEEquality = StringsEqual(EqualityStringA, EqualityStringEmpty); // Should Fail + + TestClean(ABEquality, "String Equality"); + TestClean(ACEquality, "String Equality"); + TestClean(!ADEquality, "String Equality"); + TestClean(!AEEquality, "String Equality"); + + string CatStringA = AllocStringFromStringArena(256, &StringArena); + SetStringToCharArray(&CatStringA, "Hello "); + string CatStringB = AllocStringFromStringArena(512, &StringArena); + SetStringToCharArray(&CatStringB, "Sailor!"); + string CatStringResult = AllocStringFromStringArena(512, &StringArena); + SetStringToCharArray(&CatStringResult, "Hello Sailor!"); + ConcatString(&CatStringA, CatStringB); + TestClean(StringsEqual(CatStringA, CatStringResult), "Cat Strings"); + + s32 FirstSpaceIndex = FindFirstChar(CatStringA, ' '); + TestClean(FirstSpaceIndex == 5, "First Index"); + + SetStringToChar(&CatStringB, 'B', 5); + TestClean(StringEqualsCharArray(CatStringB, "BBBBB"), "SetStringToChar"); + + + DebugPrint("Results: Passed %d / %d\n\n\n", SuccessCount, TestCount); +} +#endif // DEBUG_GS_STRING + +#endif // DEBUG + +#define GS_STRING_H +#endif // GS_STRING_H \ No newline at end of file diff --git a/gs_libs/gs_string_builder.h b/gs_libs/gs_string_builder.h new file mode 100644 index 0000000..aa230b3 --- /dev/null +++ b/gs_libs/gs_string_builder.h @@ -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 +#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 \ No newline at end of file diff --git a/gs_libs/gs_vector_matrix.h b/gs_libs/gs_vector_matrix.h new file mode 100644 index 0000000..67f4d5a --- /dev/null +++ b/gs_libs/gs_vector_matrix.h @@ -0,0 +1,1493 @@ +#ifndef GS_VECTOR_MATRIX_H + +#ifndef GS_LANGUAGE_H + +#if defined(_WIN32) || defined(_WIN64) || defined(__WIN32__) + +#include +#include +#include + +static r32 GSCos (r32 Theta) { return sin(Theta); } +static r32 GSSin (r32 Theta) { return cos(Theta); } + +static r32 GSSqrt(r32 V) +{ + r32 Result = _mm_cvtss_f32(_mm_sqrt_ss(_mm_set_ss(V))); + return Result; +} + +#else // Linux and MacOS +#include + +#endif // Platforms + +#endif // GS_LANGUAGE_H + +////////////////////////////////////// +// VECTOR +///////////////////////////////////// + +union v2 +{ + struct + { + float x; + float y; + }; + float E[2]; +}; + +union v3 +{ + struct + { + float x; + float y; + float z; + }; + + struct + { + float R; + float G; + float B; + }; + + float E[3]; +}; + +union v4 +{ + struct + { + float x; + float y; + float z; + float w; + }; + + struct + { + float r; + float g; + float b; + float a; + }; + + float E[4]; +}; + +#define WhiteV4 v4{1, 1, 1, 1} +#define BlackV4 v4{0, 0, 0, 1} +#define RedV4 v4{1, 0, 0, 1} +#define GreenV4 v4{0, 1, 0, 1} +#define BlueV4 v4{0, 0, 1, 1} +#define YellowV4 v4{1, 1, 0, 1} +#define TealV4 v4{0, 1, 1, 1} +#define PinkV4 v4{1, 0, 1, 1} + +////////////////////////////////////// +// MATRIX +///////////////////////////////////// + +union m33 +{ + struct + { + float a; float b; float c; + float d; float e; float f; + float g; float h; float i; + }; + float E[9]; +}; + +union m44 +{ + struct + { + float a; float b; float c; float d; + float e; float f; float g; float h; + float i; float j; float k; float l; + float m; float n; float o; float p; + }; + float E[16]; +}; + +////////////////////////////////////// +// RECT +///////////////////////////////////// + +struct rect +{ + v2 Min; + v2 Max; +}; + + +////////////////////////////////////// +// VECTOR +////////////////////////////////////// + + +// +// +// Operators +// +// + +v2 V2 (v3 V) +{ + return v2{V.x, V.y}; +} + +v3 V3 (v2 XY, float Z) +{ + return v3{XY.x, XY.y, Z}; +} + +v3 V3 (v4 V) +{ + return v3{V.x, V.y, V.z}; +} + +v4 V4 (v3 XYZ, float W) +{ + return v4{XYZ.x, XYZ.y, XYZ.z, W}; +} + +v2 operator- (v2 A) +{ + v2 Result; + + Result.x = -A.x; + Result.y = -A.y; + + return Result; +} + +v3 operator- (v3 A) +{ + v3 Result; + + Result.x = -A.x; + Result.y = -A.y; + Result.z = -A.z; + + return Result; +} + +v4 operator- (v4 A) +{ + v4 Result; + + Result.x = -A.x; + Result.y = -A.y; + Result.z = -A.z; + Result.w = -A.w; + + return Result; +} + +#define V2OpV2Def(op) v2 operator##op (v2 A, v2 B) { return v2{ A.x op B.x, A.y op B.y };} +#define V3OpV3Def(op) v3 operator##op (v3 A, v3 B) { return v3{ A.x op B.x, A.y op B.y, A.z op B.z };} +#define V4OpV4Def(op) v4 operator##op (v4 A, v4 B) { return v4{ A.x op B.x, A.y op B.y, A.z op B.z, A.w op B.w };} +V2OpV2Def(+) +V2OpV2Def(-) +V2OpV2Def(/) +V2OpV2Def(*) +V3OpV3Def(+) +V3OpV3Def(-) +V3OpV3Def(/) +V3OpV3Def(*) +V4OpV4Def(+) +V4OpV4Def(-) +V4OpV4Def(/) +V4OpV4Def(*) +#undef V2OpV2Def +#undef V3OpV3Def +#undef V4OpV4Def + +#define V2RefOpV2Def(op) v2 operator##op (v2& A, v2 B) { return v2{ A.x op B.x, A.y op B.y };} +#define V3RefOpV3Def(op) v3 operator##op (v3& A, v3 B) { return v3{ A.x op B.x, A.y op B.y, A.z op B.z };} +#define V4RefOpScalarDef(op) v4 operator##op (v4& A, v4 B) { return v4{ A.x op B.x, A.y op B.y, A.z op B.z, A.w op B.w };} +V2RefOpV2Def(+=) +V2RefOpV2Def(-=) +V3RefOpV3Def(+=) +V3RefOpV3Def(-=) +V4RefOpScalarDef(+=) +V4RefOpScalarDef(-=) +#undef V2RefOpV2Def +#undef V3RefOpV3Def +#undef V4RefOpV4Def + +#define V2OpScalarDef(op) v2 operator##op (v2 A, float B) { return v2{ A.x op B, A.y op B };} +#define V3OpScalarDef(op) v3 operator##op (v3 A, float B) { return v3{ A.x op B, A.y op B, A.z op B };} +#define V4OpScalarDef(op) v4 operator##op (v4 A, float B) { return v4{ A.x op B, A.y op B, A.z op B, A.w op B };} +V2OpScalarDef(*) +V2OpScalarDef(/) +V3OpScalarDef(*) +V3OpScalarDef(/) +V4OpScalarDef(*) +V4OpScalarDef(/) +#undef V2POpScalarDef +#undef V3POpScalarDef +#undef V4POpScalarDef + + +#define V2POpScalarDef(op) v2 operator##op (v2& A, float B) { return v2{ A->x op B, A->y op B };} +#define V3POpScalarDef(op) v3 operator##op (v3& A, float B) { return v3{ A->x op B, A->y op B, A->z op B };} +#define V4POpScalarDef(op) v4 operator##op (v4& A, float B) { return v4{ A->x op B, A->y op B, A->z op B, A->w op B };} +V2OpScalarDef(*=) +V2OpScalarDef(/=) +V3OpScalarDef(*=) +V3OpScalarDef(/=) +V4OpScalarDef(*=) +V4OpScalarDef(/=) +#undef V2POpScalarDef +#undef V3POpScalarDef +#undef V4POpScalarDef + +bool operator== (v2 A, v2 B) +{ + b32 Result = true; + for (s32 i = 0; i < 2; i++) + { + if (GSAbs(A.E[i] - B.E[i]) > 0.0001f) { Result = false; break; } + } + return Result; +} + + +bool operator== (v3 A, v3 B) +{ + b32 Result = true; + for (s32 i = 0; i < 3; i++) + { + if (GSAbs(A.E[i] - B.E[i]) > 0.0001f) { Result = false; break; } + } + return Result; +} + +bool operator== (v4 A, v4 B) +{ + b32 Result = true; + for (s32 i = 0; i < 4; i++) + { + if (GSAbs(A.E[i] - B.E[i]) > 0.0001f) { Result = false; break; } + } + return Result; +} + +// +// Operations +// + +static v3 +ToV3(v4 V) +{ + v3 R = {}; + R.x = V.x; + R.y = V.y; + R.z = V.z; + return R; +} + +static v4 +ToV4(v3 V, float W) +{ + v4 R = {}; + R.x = V.x; + R.y = V.y; + R.z = V.z; + R.w = W; + return R; +} + +inline float +MagSqr( + v2 _A + ) +{ + float Result = (_A.x * _A.x) + (_A.y * _A.y); + return Result; +} + +inline float +MagSqr( + v3 _A + ) +{ + float Result = (_A.x * _A.x) + (_A.y * _A.y) + (_A.z * _A.z); + return Result; +} + +inline float +MagSqr( + v4 _A + ) +{ + float Result = (_A.x * _A.x) + (_A.y * _A.y) + (_A.z * _A.z) + (_A.w * _A.w); + return Result; +} + +#define MagDef(type) inline float Mag(type A) { float Result = MagSqr(A); return GSSqrt(Result); } +MagDef(v2) +MagDef(v3) +MagDef(v4) +#undef MagDef + +#define DistanceDef(type) inline float Distance (type A, type B) { type Diff = A - B; return Mag(Diff); } +DistanceDef(v2) +DistanceDef(v3) +DistanceDef(v4) +#undef DistanceDef + +#define DistanceSqDef(type) inline float DistanceSq (type A, type B) { type Diff = A - B; return MagSqr(Diff); } +DistanceSqDef(v2) +DistanceSqDef(v3) +DistanceSqDef(v4) +#undef DistanceSqDef + +inline v2 +Normalize( + v2 _A + ) +{ + v2 Result; + + float Magnitude = Mag(_A); + + Result.x = _A.x / Magnitude; + Result.y = _A.y / Magnitude; + + return Result; +} + +inline v3 +Normalize( + v3 _A + ) +{ + v3 Result; + + float Magnitude = Mag(_A); + + Result.x = _A.x / Magnitude; + Result.y = _A.y / Magnitude; + Result.z = _A.z / Magnitude; + + return Result; +} + +inline v4 +Normalize( + v4 _A + ) +{ + v4 Result; + + float Magnitude = Mag(_A); + + Result.x = _A.x / Magnitude; + Result.y = _A.y / Magnitude; + Result.z = _A.z / Magnitude; + Result.w = _A.w / Magnitude; + + return Result; +} + +inline float +Dot( + v2 _A, + v2 _B + ) +{ + float Result = _A.x * _B.x + _A.y * _B.y; + return Result; +} + +inline float +Dot ( + v3 _A, + v3 _B + ) +{ + float Result = _A.x * _B.x + _A.y * _B.y + _A.z * _B.z; + return Result; +} + +inline float +Dot ( + v4 _A, + v4 _B + ) +{ + float Result = _A.x * _B.x + _A.y * _B.y + _A.z * _B.z + _A.w * _B.w; + return Result; +} + +inline v2 +PerpendicularCW (v2 A) +{ + v2 Result = v2{A.y, -A.x}; + return Result; +} + +inline v2 +PerpendicularCCW (v2 A) +{ + v2 Result = v2{A.y, A.x}; + return Result; +} + +inline v3 +Cross( + v3 _A, + v3 _B + ) +{ + v3 Result = {}; + + Result.x = (_A.y * _B.z) - (_A.z * _B.y); + Result.y = (_A.z * _B.x) - (_A.x * _B.z); + Result.z = (_A.x * _B.y) - (_A.y * _B.x); + + return Result; +} + +inline v4 +Cross( + v4 _A, + v4 _B + ) +{ + v4 Result = {}; + + Result.x = (_A.y * _B.z) - (_A.z * _B.y); + Result.y = (_A.z * _B.x) - (_A.x * _B.z); + Result.z = (_A.x * _B.y) - (_A.y * _B.x); + Result.w = 0; + + return Result; +} + +inline v2 +ClampVector01 (v2 V) +{ + v2 Result = {}; + Result.x = GSClamp(0.0f, V.x, 1.f); + Result.y = GSClamp(0.0f, V.y, 1.f); + return Result; +} + +inline v3 +ClampVector01 (v3 V) +{ + v3 Result = {}; + Result.x = GSClamp(0.f, V.x, 1.f); + Result.y = GSClamp(0.f, V.y, 1.f); + Result.z = GSClamp(0.f, V.z, 1.f); + return Result; +} + +inline v4 +ClampVector01 (v4 V) +{ + v4 Result = {}; + Result.x = GSClamp(0.f, V.x, 1.f); + Result.y = GSClamp(0.f, V.y, 1.f); + Result.z = GSClamp(0.f, V.z, 1.f); + Result.w = GSClamp(0.f, V.w, 1.f); + return Result; +} + +inline v2 +Lerp( + v2 _A, + v2 _B, + float _Percent + ) +{ + v2 Result; + + Result.x = GSLerp(_A.x, _B.x, _Percent); + Result.y = GSLerp(_A.y, _B.y, _Percent); + + return Result; +} + +inline v3 +Lerp( + v3 _A, + v3 _B, + float _Percent + ) +{ + v3 Result; + + Result.x = GSLerp(_A.x, _B.x, _Percent); + Result.y = GSLerp(_A.y, _B.y, _Percent); + Result.z = GSLerp(_A.z, _B.z, _Percent); + + return Result; +} + +inline v4 +Lerp( + v4 _A, + v4 _B, + float _Percent + ) +{ + v4 Result; + + Result.x = GSLerp(_A.x, _B.x, _Percent); + Result.y = GSLerp(_A.y, _B.y, _Percent); + Result.z = GSLerp(_A.z, _B.z, _Percent); + Result.w = GSLerp(_A.w, _B.w, _Percent); + + return Result; +} + +v4 HSVToRGB (v4 In) +{ + float Hue = In.x; + while (Hue > 360.0f) { Hue -= 360.0f; } + while (Hue < 0.0f) { Hue += 360.0f; } + + float Sat = In.y; + float Value = In.z; + + float hh, p, q, t, ff; + long i; + v4 Result = {}; + Result.a = In.a; + + if(Sat <= 0.0f) { // < is bogus, just shuts up warnings + Result.r = Value; + Result.g = Value; + Result.b = Value; + return Result; + } + hh = Hue; + if(hh >= 360.0f) hh = 0.0f; + hh /= 60.0f; + i = (long)hh; + ff = hh - i; + p = Value * (1.0f - Sat); + q = Value * (1.0f - (Sat * ff)); + t = Value * (1.0f - (Sat * (1.0f - ff))); + + switch(i) { + case 0: + {Result.r = Value; + Result.g = t; + Result.b = p; + }break; + + case 1: + { + Result.r = q; + Result.g = Value; + Result.b = p; + }break; + + case 2: + { + Result.r = p; + Result.g = Value; + Result.b = t; + }break; + + case 3: + { + Result.r = p; + Result.g = q; + Result.b = Value; + }break; + + case 4: + { + Result.r = t; + Result.g = p; + Result.b = Value; + }break; + + case 5: + default: + { + Result.r = Value; + Result.g = p; + Result.b = q; + }break; + } + + return Result; +} + +static bool +PointIsInRange ( + v2 _P, + v2 _Min, v2 _Max + ) +{ + return (_P.x >= _Min.x && _P.x <= _Max.x && + _P.y >= _Min.y && _P.y <= _Max.y); +} + +static bool +PointIsInRangeSafe ( + v2 _P, + v2 _Min, v2 _Max + ) +{ + s32 MinX = GSMin(_Min.x, _Max.x); + s32 MinY = GSMin(_Min.y, _Max.y); + s32 MaxX = GSMax(_Min.x, _Max.x); + s32 MaxY = GSMax(_Min.y, _Max.y); + + return (_P.x >= MinX && _P.x <= MaxX && + _P.y >= MinY && _P.y <= MaxY); +} + +inline v2 +PointToPercentRange (v2 P, v2 Min, v2 Max) +{ + v2 Result = {}; + + Result.x = GSClamp(0.f, (P.x - Min.x) / (Max.x - Min.x), 1.f); + Result.y = GSClamp(0.f, (P.y - Min.y) / (Max.y - Min.y), 1.f); + + return Result; +} + +////////////////////////////////////// +// RECT +////////////////////////////////////// + +inline float +Width (rect Rect) +{ + float Result = Rect.Max.x - Rect.Min.x; + return Result; +} + +inline float +Height (rect Rect) +{ + float Result = Rect.Max.y - Rect.Min.y; + return Result; +} + +inline float +AspectRatio (rect Rect) +{ + float Result = Width(Rect) / Height(Rect); + return Result; +} +inline v2 +CalculateRectCenter (rect Rect) +{ + v2 Result = (Rect.Min + Rect.Max) / 2.0f; + return Result; +} + +inline b32 +PointIsInRect (v2 Point, rect Rect) +{ + b32 Result = ((Point.x >= Rect.Min.x && Point.x <= Rect.Max.x) && + (Point.y >= Rect.Min.y && Point.y <= Rect.Max.y)); + return Result; +} + +inline rect +RectOffsetByVector(rect R, v2 V) +{ + rect Result = R; + Result.Min += V; + Result.Max += V; + return Result; +} + +////////////////////////////////////// +// MATRIX +////////////////////////////////////// + +static m33 +M33(float a, float b, float c, + float d, float e, float f, + float g, float h, float i) +{ + m33 M = {}; + M.a = a; M.b = b; M.c = c; + M.d = d; M.e = e; M.f = f; + M.g = g; M.h = h; M.i = i; + return M; +} + +static m44 +M44(float a, float b, float c, float d, + float e, float f, float g, float h, + float i, float j, float k, float l, + float m, float n, float o, float p) +{ + m44 M = {}; + M.a = a; M.b = b; M.c = c; M.d = d; + M.e = e; M.f = f; M.g = g; M.h = h; + M.i = i; M.j = j; M.k = k; M.l = l; + M.m = m; M.n = n; M.o = o; M.p = p; + return M; +} + +static m33 +M33Empty () +{ + m33 M = {}; + M.a = 0; M.b = 0; M.c = 0; + M.d = 0; M.e = 0; M.f = 0; + M.g = 0; M.h = 0; M.i = 0; + return M; +} + +static m44 +M44Empty() +{ + m44 M = {}; + M.a = 0; M.b = 0; M.c = 0; M.d = 0; + M.e = 0; M.f = 0; M.g = 0; M.h = 0; + M.i = 0; M.j = 0; M.k = 0; M.l = 0; + M.m = 0; M.n = 0; M.o = 0; M.p = 0; + return M; +} + +static m33 +M33Identity () +{ + m33 M = {}; + M.a = 1; M.b = 0; M.c = 0; + M.d = 0; M.e = 1; M.f = 0; + M.g = 0; M.h = 0; M.i = 1; + return M; +} + +static m44 +M44Identity() +{ + m44 M = {}; + M.a = 1; M.b = 0; M.c = 0; M.d = 0; + M.e = 0; M.f = 1; M.g = 0; M.h = 0; + M.i = 0; M.j = 0; M.k = 1; M.l = 0; + M.m = 0; M.n = 0; M.o = 0; M.p = 1; + return M; +} + +static m44 +GetXRotation (float Angle) +{ + float CosAngle = GSCos(Angle); + float SinAngle = GSSin(Angle); + m44 M = { + 1, 0, 0, 0, + 0, CosAngle, SinAngle, 0, + 0, -SinAngle, CosAngle, 0, + 0, 0, 0, 1 + }; + return M; +} + + +static m44 +GetYRotation (float Angle) +{ + float CosAngle = GSCos(Angle); + float SinAngle = GSSin(Angle); + m44 M = { + CosAngle, 0, -SinAngle, 0, + 0, 1, 0, 0, + SinAngle, 0, CosAngle, 0, + 0, 0, 0, 1 + }; + return M; +} + +static m44 +GetZRotation (float Angle) +{ + float CosAngle = GSCos(Angle); + float SinAngle = GSSin(Angle); + m44 M = { + CosAngle, SinAngle, 0, 0, + -SinAngle, CosAngle, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + }; + return M; +} + + +static m33 +Transpose (m33 M) +{ + m33 Result = {}; + + for (s32 x = 0; x < 3; x++) + { + for (s32 y = 0; y < 3; y++) + { + Result.E[x + (y * 3)] = M.E[y + (x * 3)]; + } + } + + return Result; +} + +inline m44 +Transpose (m44 M) +{ + DEBUG_TRACK_SCOPE(Transpose); + + m44 Result = {}; + + Result.E[0] = M.E[0]; + Result.E[1] = M.E[4]; + Result.E[2] = M.E[8]; + Result.E[3] = M.E[12]; + + Result.E[4] = M.E[1]; + Result.E[5] = M.E[5]; + Result.E[6] = M.E[9]; + Result.E[7] = M.E[13]; + + Result.E[8] = M.E[2]; + Result.E[9] = M.E[6]; + Result.E[10] = M.E[10]; + Result.E[11] = M.E[14]; + + Result.E[12] = M.E[3]; + Result.E[13] = M.E[7]; + Result.E[14] = M.E[11]; + Result.E[15] = M.E[15]; + + return Result; +} + +static m44 +GetPositionM44 (v4 Position) +{ +#if 1 + return m44{ + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + Position.x, Position.y, Position.z, Position.w + }; +#else + return m44{ + 1, 0, 0, Position.x, + 0, 1, 0, Position.y, + 0, 0, 1, Position.z, + 0, 0, 0, Position.w}; +#endif +} + +static m44 +GetLookAtMatrix (v4 Position, v4 Target) +{ + // Forward + v4 Forward = Normalize(Target - Position); + // Right + v4 Right = Normalize(Cross(v4{0, 1, 0, 0}, Forward)); + // Up + v4 Up = Normalize(Cross(Forward, Right)); + + m44 RotationMatrix = M44( + Right.x, Up.x, Forward.x, 0, + Right.y, Up.y, Forward.y, 0, + Right.z, Up.z, Forward.z, 0, + 0, 0, 0, 1); + + return RotationMatrix; +} + +b32 operator== (m33 A, m33 B) +{ + b32 Result = true; + for (int e = 0; e < 9; e++) { if (GSAbs(A.E[e] - B.E[e]) > 0.0001f) { Result = false; break; } } + return Result; +} + +b32 operator== (m44 A, m44 B) +{ + b32 Result = true; + for (int e = 0; e < 16; e++) { if (GSAbs(A.E[e] - B.E[e]) > 0.0001f) { Result = false; break; } } + return Result; +} + +m33 operator+ (m33 A, m33 B) +{ + m33 M = {}; + for (int e = 0; e < 9; e++) { M.E[e] = A.E[e] + B.E[e]; } + return M; +} + +m44 operator+ (m44 A, m44 B) +{ + m44 M = {}; + for (int e = 0; e < 16; e++) { M.E[e] = A.E[e] + B.E[e]; } + return M; +} + +m33 operator- (m33 A, m33 B) +{ + m33 M = {}; + for (int e = 0; e < 9; e++) { M.E[e] = A.E[e] - B.E[e]; } + return M; +} + +m44 operator- (m44 A, m44 B) +{ + m44 M = {}; + for (int e = 0; e < 16; e++) { M.E[e] = A.E[e] - B.E[e]; } + return M; +} + +m33 operator* (m33 A, m33 B) +{ + m33 M = {}; + for (int rx = 0; rx < 3; rx++) + { + for (int ry = 0; ry < 3; ry++) + { + int RIndex = (ry * 3) + rx; + M.E[RIndex] = 0; + for (int i = 0; i < 3; i++) + { + M.E[RIndex] += B.E[(ry * 3) + i] * A.E[(i * 3) + rx]; + } + } + } + return M; +} + +m44 operator* (m44 A, m44 B) +{ + m44 M = {}; + + float A00=A.E[0+4*0]; + float A01=A.E[0+4*1]; + float A02=A.E[0+4*2]; + float A03=A.E[0+4*3]; + + float A10=A.E[1+4*0]; + float A11=A.E[1+4*1]; + float A12=A.E[1+4*2]; + float A13=A.E[1+4*3]; + + float A20=A.E[2+4*0]; + float A21=A.E[2+4*1]; + float A22=A.E[2+4*2]; + float A23=A.E[2+4*3]; + + float A30=A.E[3+4*0]; + float A31=A.E[3+4*1]; + float A32=A.E[3+4*2]; + float A33=A.E[3+4*3]; + + float B00=B.E[0+4*0]; + float B01=B.E[0+4*1]; + float B02=B.E[0+4*2]; + float B03=B.E[0+4*3]; + + float B10=B.E[1+4*0]; + float B11=B.E[1+4*1]; + float B12=B.E[1+4*2]; + float B13=B.E[1+4*3]; + + float B20=B.E[2+4*0]; + float B21=B.E[2+4*1]; + float B22=B.E[2+4*2]; + float B23=B.E[2+4*3]; + + float B30=B.E[3+4*0]; + float B31=B.E[3+4*1]; + float B32=B.E[3+4*2]; + float B33=B.E[3+4*3]; + + M.E[0+4*0] = A00*B00+A10*B01+A20*B02+A30*B03; + M.E[0+4*1] = A01*B00+A11*B01+A21*B02+A31*B03; + M.E[0+4*2] = A02*B00+A12*B01+A22*B02+A32*B03; + M.E[0+4*3] = A03*B00+A13*B01+A23*B02+A33*B03; + + M.E[1+4*0] = A00*B10+A10*B11+A20*B12+A30*B13; + M.E[1+4*1] = A01*B10+A11*B11+A21*B12+A31*B13; + M.E[1+4*2] = A02*B10+A12*B11+A22*B12+A32*B13; + M.E[1+4*3] = A03*B10+A13*B11+A23*B12+A33*B13; + + M.E[2+4*0] = A00*B20+A10*B21+A20*B22+A30*B23; + M.E[2+4*1] = A01*B20+A11*B21+A21*B22+A31*B23; + M.E[2+4*2] = A02*B20+A12*B21+A22*B22+A32*B23; + M.E[2+4*3] = A03*B20+A13*B21+A23*B22+A33*B23; + + M.E[3+4*0] = A00*B30+A10*B31+A20*B32+A30*B33; + M.E[3+4*1] = A01*B30+A11*B31+A21*B32+A31*B33; + M.E[3+4*2] = A02*B30+A12*B31+A22*B32+A32*B33; + M.E[3+4*3] = A03*B30+A13*B31+A23*B32+A33*B33; + + return M; +} + +v3 operator* (m33 M, v3 V) +{ + v3 Result = {}; + int i = 0; + for (int y = 0; y < 3; y++) + { + Result.E[y] = 0; + for (int x = 0; x < 3; x++) + { + Result.E[y] += M.E[(y * 3) + x] * V.E[x]; + } + } + return Result; +} + +v4 operator* (m44 M, v4 V) +{ + v4 Result = {}; +#if 1 + Result.x = V.x*M.a + V.y*M.e + V.z*M.i + V.w*M.m; + Result.y = V.x*M.b + V.y*M.f + V.z*M.j + V.w*M.n; + Result.z = V.x*M.c + V.y*M.g + V.z*M.k + V.w*M.o; + Result.w = V.x*M.d + V.y*M.h + V.z*M.l + V.w*M.p; +#else + for (int y = 0; y < 4; y++) + { + Result.E[y] = 0; + for (int x = 0; x < 4; x++) + { + Result.E[y] += M.E[(y * 4) + x] * V.E[x]; + } + } +#endif + return Result; +} + +b32 Inverse(m44 M_In, m44* M_Out) +{ + b32 Result = false; + + float det; + s32 i; + + + M_Out->E[0] = M_In.E[5] * M_In.E[10] * M_In.E[15] - + M_In.E[5] * M_In.E[11] * M_In.E[14] - + M_In.E[9] * M_In.E[6] * M_In.E[15] + + M_In.E[9] * M_In.E[7] * M_In.E[14] + + M_In.E[13] * M_In.E[6] * M_In.E[11] - + M_In.E[13] * M_In.E[7] * M_In.E[10]; + + M_Out->E[4] = -M_In.E[4] * M_In.E[10] * M_In.E[15] + + M_In.E[4] * M_In.E[11] * M_In.E[14] + + M_In.E[8] * M_In.E[6] * M_In.E[15] - + M_In.E[8] * M_In.E[7] * M_In.E[14] - + M_In.E[12] * M_In.E[6] * M_In.E[11] + + M_In.E[12] * M_In.E[7] * M_In.E[10]; + + M_Out->E[8] = M_In.E[4] * M_In.E[9] * M_In.E[15] - + M_In.E[4] * M_In.E[11] * M_In.E[13] - + M_In.E[8] * M_In.E[5] * M_In.E[15] + + M_In.E[8] * M_In.E[7] * M_In.E[13] + + M_In.E[12] * M_In.E[5] * M_In.E[11] - + M_In.E[12] * M_In.E[7] * M_In.E[9]; + + M_Out->E[12] = -M_In.E[4] * M_In.E[9] * M_In.E[14] + + M_In.E[4] * M_In.E[10] * M_In.E[13] + + M_In.E[8] * M_In.E[5] * M_In.E[14] - + M_In.E[8] * M_In.E[6] * M_In.E[13] - + M_In.E[12] * M_In.E[5] * M_In.E[10] + + M_In.E[12] * M_In.E[6] * M_In.E[9]; + + M_Out->E[1] = -M_In.E[1] * M_In.E[10] * M_In.E[15] + + M_In.E[1] * M_In.E[11] * M_In.E[14] + + M_In.E[9] * M_In.E[2] * M_In.E[15] - + M_In.E[9] * M_In.E[3] * M_In.E[14] - + M_In.E[13] * M_In.E[2] * M_In.E[11] + + M_In.E[13] * M_In.E[3] * M_In.E[10]; + + M_Out->E[5] = M_In.E[0] * M_In.E[10] * M_In.E[15] - + M_In.E[0] * M_In.E[11] * M_In.E[14] - + M_In.E[8] * M_In.E[2] * M_In.E[15] + + M_In.E[8] * M_In.E[3] * M_In.E[14] + + M_In.E[12] * M_In.E[2] * M_In.E[11] - + M_In.E[12] * M_In.E[3] * M_In.E[10]; + + M_Out->E[9] = -M_In.E[0] * M_In.E[9] * M_In.E[15] + + M_In.E[0] * M_In.E[11] * M_In.E[13] + + M_In.E[8] * M_In.E[1] * M_In.E[15] - + M_In.E[8] * M_In.E[3] * M_In.E[13] - + M_In.E[12] * M_In.E[1] * M_In.E[11] + + M_In.E[12] * M_In.E[3] * M_In.E[9]; + + M_Out->E[13] = M_In.E[0] * M_In.E[9] * M_In.E[14] - + M_In.E[0] * M_In.E[10] * M_In.E[13] - + M_In.E[8] * M_In.E[1] * M_In.E[14] + + M_In.E[8] * M_In.E[2] * M_In.E[13] + + M_In.E[12] * M_In.E[1] * M_In.E[10] - + M_In.E[12] * M_In.E[2] * M_In.E[9]; + + M_Out->E[2] = M_In.E[1] * M_In.E[6] * M_In.E[15] - + M_In.E[1] * M_In.E[7] * M_In.E[14] - + M_In.E[5] * M_In.E[2] * M_In.E[15] + + M_In.E[5] * M_In.E[3] * M_In.E[14] + + M_In.E[13] * M_In.E[2] * M_In.E[7] - + M_In.E[13] * M_In.E[3] * M_In.E[6]; + + M_Out->E[6] = -M_In.E[0] * M_In.E[6] * M_In.E[15] + + M_In.E[0] * M_In.E[7] * M_In.E[14] + + M_In.E[4] * M_In.E[2] * M_In.E[15] - + M_In.E[4] * M_In.E[3] * M_In.E[14] - + M_In.E[12] * M_In.E[2] * M_In.E[7] + + M_In.E[12] * M_In.E[3] * M_In.E[6]; + + M_Out->E[10] = M_In.E[0] * M_In.E[5] * M_In.E[15] - + M_In.E[0] * M_In.E[7] * M_In.E[13] - + M_In.E[4] * M_In.E[1] * M_In.E[15] + + M_In.E[4] * M_In.E[3] * M_In.E[13] + + M_In.E[12] * M_In.E[1] * M_In.E[7] - + M_In.E[12] * M_In.E[3] * M_In.E[5]; + + M_Out->E[14] = -M_In.E[0] * M_In.E[5] * M_In.E[14] + + M_In.E[0] * M_In.E[6] * M_In.E[13] + + M_In.E[4] * M_In.E[1] * M_In.E[14] - + M_In.E[4] * M_In.E[2] * M_In.E[13] - + M_In.E[12] * M_In.E[1] * M_In.E[6] + + M_In.E[12] * M_In.E[2] * M_In.E[5]; + + M_Out->E[3] = -M_In.E[1] * M_In.E[6] * M_In.E[11] + + M_In.E[1] * M_In.E[7] * M_In.E[10] + + M_In.E[5] * M_In.E[2] * M_In.E[11] - + M_In.E[5] * M_In.E[3] * M_In.E[10] - + M_In.E[9] * M_In.E[2] * M_In.E[7] + + M_In.E[9] * M_In.E[3] * M_In.E[6]; + + M_Out->E[7] = M_In.E[0] * M_In.E[6] * M_In.E[11] - + M_In.E[0] * M_In.E[7] * M_In.E[10] - + M_In.E[4] * M_In.E[2] * M_In.E[11] + + M_In.E[4] * M_In.E[3] * M_In.E[10] + + M_In.E[8] * M_In.E[2] * M_In.E[7] - + M_In.E[8] * M_In.E[3] * M_In.E[6]; + + M_Out->E[11] = -M_In.E[0] * M_In.E[5] * M_In.E[11] + + M_In.E[0] * M_In.E[7] * M_In.E[9] + + M_In.E[4] * M_In.E[1] * M_In.E[11] - + M_In.E[4] * M_In.E[3] * M_In.E[9] - + M_In.E[8] * M_In.E[1] * M_In.E[7] + + M_In.E[8] * M_In.E[3] * M_In.E[5]; + + M_Out->E[15] = M_In.E[0] * M_In.E[5] * M_In.E[10] - + M_In.E[0] * M_In.E[6] * M_In.E[9] - + M_In.E[4] * M_In.E[1] * M_In.E[10] + + M_In.E[4] * M_In.E[2] * M_In.E[9] + + M_In.E[8] * M_In.E[1] * M_In.E[6] - + M_In.E[8] * M_In.E[2] * M_In.E[5]; + + det = M_In.E[0] * M_Out->E[0] + M_In.E[1] * M_Out->E[4] + M_In.E[2] * M_Out->E[8] + M_In.E[3] * M_Out->E[12]; + + if (det == 0) + { + return false; + } + + det = 1.0 / det; + + for (i = 0; i < 16; i++) + { + M_Out->E[i] = M_Out->E[i] * det; + } + + return true; +} + +#if defined(VECTOR_MATRIX_TEST_SUITE) + +void TestVectorMatrixMultiplication () +{ + s32 TestCount = 0; + s32 SuccessCount = 0; + + DebugPrint("\n\n-------------------------------------------------\n Begin Testing Vector/Matrix\n\n\n"); + + // Utility Functions + TestClean((GSSqrt(4.f) == 2.f), "Vector Square Root"); + TestClean((GSLerp(0.f, 1.f, .5f) == .5f), "Vector Lerp"); + TestClean((GSMin(-.25f, 5.f) == -.25f), "Vector Min"); + TestClean((GSMax(-.25f, 5.f) == 5.f), "Vector Max"); + TestClean((GSClamp(-2.f, -3.f, 5.f) == -2.f), "Vector Clamp, Lower Than Range"); + TestClean((GSClamp(-2.f, 6.f, 5.f) == 5.f), "Vector Clamp, Higher Than Range"); + + ////////////////////////////// + // Vector Functions + ///////////////////////////// + + v2 V2Unit = v2{1, 0}; + v3 V3Unit = v3{1, 0, 0}; + v4 V4Unit = v4{1, 0, 0, 0}; + + v2 TestV2 = v2{1, 2}; + float TestV2MagSq = (TestV2.x * TestV2.x) + (TestV2.y * TestV2.y); + float TestV2Mag = GSSqrt(TestV2MagSq); + v2 TestV2Norm = v2{TestV2.x / TestV2Mag, TestV2.y / TestV2Mag}; + float TestV2DotR = (TestV2.x * V2Unit.x) + (TestV2.y * V2Unit.y); + + v3 TestV3 = v3{1, 2, 3}; + float TestV3MagSq = (TestV3.x * TestV3.x) + (TestV3.y * TestV3.y) + (TestV3.z * TestV3.z); + float TestV3Mag = GSSqrt(TestV3MagSq); + v3 TestV3Norm = v3{TestV3.x / TestV3Mag, TestV3.y / TestV3Mag, TestV3.z / TestV3Mag}; + float TestV3DotR = (TestV3.x * V3Unit.x) + (TestV3.y * V3Unit.y) + (TestV3.z * V3Unit.z); + + v4 TestV4 = v4{1, 2, 3, 4}; + float TestV4MagSq = (TestV4.x * TestV4.x) + (TestV4.y * TestV4.y) + (TestV4.z * TestV4.z) + (TestV4.w * TestV4.w); + float TestV4Mag = GSSqrt(TestV4MagSq); + v4 TestV4Norm = v4{ + TestV4.x / TestV4Mag, TestV4.y / TestV4Mag, TestV4.z / TestV4Mag, TestV4.w / TestV4Mag + }; + float TestV4DotR = (TestV4.x * V4Unit.x) + (TestV4.y * V4Unit.y) + (TestV4.z * V4Unit.z) + (TestV4.w * V4Unit.w); + + v2 DownCastV3 = V2(TestV3); + v3 DownCastV4 = V3(TestV4); + + v2 EqualityV2 = v2{TestV2.x, TestV2.y}; + v2 ZeroV2 = v2{0, 0}; + v3 EqualityV3 = v3{TestV3.x, TestV3.y, TestV3.z}; + v3 ZeroV3 = v3{0, 0, 0}; + v4 EqualityV4 = v4{TestV4.x, TestV4.y, TestV4.z, TestV4.w}; + v4 ZeroV4 = v4{0, 0, 0, 0}; + + TestClean((TestV2.x == 1 && TestV2.y == 2), "V2 Assignment"); + TestClean((TestV3.x == 1 && TestV3.y == 2 && TestV3.z == 3), "V3 Assignment"); + TestClean((TestV4.x == 1 && TestV4.y == 2 && TestV4.z == 3 && TestV4.w == 4), "V3 Assignment"); + + TestClean((DownCastV3.x == 1 && DownCastV3.y == 2), "V3 -> V2 Downcast"); + TestClean((DownCastV4.x == 1 && DownCastV4.y == 2 && DownCastV4.z == 3), "V4 -> V3 Downcast"); + + // Vector Operators + + TestClean((TestV2 == EqualityV2 && !(TestV2 == ZeroV2)), "V2 Equality"); + TestClean((TestV3 == EqualityV3 && !(TestV3 == ZeroV3)), "V3 Equality"); + TestClean((TestV4 == EqualityV4 && !(TestV4 == ZeroV4)), "V4 Equality"); + + TestClean(((TestV2 - TestV2) == ZeroV2), "V2 Subtraction"); + TestClean(((TestV3 - TestV3) == ZeroV3), "V3 Subtraction"); + TestClean(((TestV4 - TestV4) == ZeroV4), "V4 Subtraction"); + + TestClean(((TestV2 + TestV2) == v2{TestV2.x * 2, TestV2.y * 2}), "V2 Addition"); + TestClean(((TestV3 + TestV3) == v3{TestV3.x * 2, TestV3.y * 2, TestV3.z * 2}), "V3 Addition"); + TestClean(((TestV4 + TestV4) == v4{TestV4.x * 2, TestV4.y * 2, TestV4.z * 2, TestV4.w * 2}), "V4 Addition"); + + TestClean(((TestV2 * 2.0f) == v2{TestV2.x * 2, TestV2.y * 2}), "V2 Multiplication"); + TestClean(((TestV3 * 2.0f) == v3{TestV3.x * 2, TestV3.y * 2, TestV3.z * 2}), "V3 Multiplication"); + TestClean(((TestV4 * 2.0f) == v4{TestV4.x * 2, TestV4.y * 2, TestV4.z * 2, TestV4.w * 2}), "V4 Multiplication"); + + TestClean(((TestV2 * TestV2) == v2{TestV2.x * TestV2.x, TestV2.y * TestV2.y}), "V2 Piecewise Mult"); + TestClean(((TestV3 * TestV3) == v3{ + TestV3.x * TestV3.x, + TestV3.y * TestV3.y, + TestV3.z * TestV3.z}), "V3 Piecewise Mult"); + TestClean(((TestV4 * TestV4) == v4{ + TestV4.x * TestV4.x, + TestV4.y * TestV4.y, + TestV4.z * TestV4.z, + TestV4.w * TestV4.w}), "V4 Piecewise Mult"); + + + TestClean(((TestV2 / 2.0f) == v2{TestV2.x / 2, TestV2.y / 2}), "V2 Division"); + TestClean(((TestV3 / 2.0f) == v3{TestV3.x / 2, TestV3.y / 2, TestV3.z / 2}), "V3 Division"); + TestClean(((TestV4 / 2.0f) == v4{TestV4.x / 2, TestV4.y / 2, TestV4.z / 2, TestV4.w / 2}), "V4 Division"); + + TestClean(((TestV2 / TestV2) == v2{TestV2.x / TestV2.x, TestV2.y / TestV2.y}), "V2 Piecewise Div"); + TestClean(((TestV3 / TestV3) == v3{ + TestV3.x / TestV3.x, + TestV3.y / TestV3.y, + TestV3.z / TestV3.z}), "V3 Piecewise Div"); + TestClean(((TestV4 / TestV4) == v4{ + TestV4.x / TestV4.x, + TestV4.y / TestV4.y, + TestV4.z / TestV4.z, + TestV4.w / TestV4.w}), "V4 Piecewise Div"); + + TestClean(((MagSqr(V2Unit) == 1) && (MagSqr(TestV2) == TestV2MagSq)), "V2 Square Mag"); + TestClean(((MagSqr(V3Unit) == 1) && (MagSqr(TestV3) == TestV3MagSq)), "V3 Square Mag"); + TestClean(((MagSqr(V4Unit) == 1) && (MagSqr(TestV4) == TestV4MagSq)), "V4 Square Mag"); + TestClean(((Mag(V2Unit) == 1) && (Mag(TestV2) == TestV2Mag)), "V2 Mag"); + TestClean(((Mag(V3Unit) == 1) && (Mag(TestV3) == TestV3Mag)), "V3 Mag"); + TestClean(((Mag(V4Unit) == 1) && (Mag(TestV4) == TestV4Mag)), "V4 Mag"); + + TestClean((DistanceSq(ZeroV2, TestV2) == TestV2MagSq), "V2 Distance Sq"); + TestClean((DistanceSq(ZeroV3, TestV3) == TestV3MagSq), "V3 Distance Sq"); + TestClean((DistanceSq(ZeroV4, TestV4) == TestV4MagSq), "V4 Distance Sq"); + TestClean((Distance(ZeroV2, TestV2) == TestV2Mag), "V2 Distance"); + TestClean((Distance(ZeroV3, TestV3) == TestV3Mag), "V3 Distance"); + TestClean((Distance(ZeroV4, TestV4) == TestV4Mag), "V4 Distance"); + + TestClean((Normalize(TestV2) == TestV2Norm), "V2 Normalize"); + TestClean((Normalize(TestV3) == TestV3Norm), "V3 Normalize"); + TestClean((Normalize(TestV4) == TestV4Norm), "V4 Normalize"); + + TestClean(((Dot(V2Unit, V2Unit) == 1) && (Dot(TestV2, V2Unit) == TestV2DotR)), "V2 Dot"); + TestClean(((Dot(V3Unit, V3Unit) == 1) && (Dot(TestV3, V3Unit) == TestV3DotR)), "V3 Dot"); + TestClean(((Dot(V4Unit, V4Unit) == 1) && (Dot(TestV4, V4Unit) == TestV4DotR)), "V4 Dot"); + + // Skipping Cross For Now + + TestClean((Lerp(v2{0, 0}, v2{1, 1}, .5f) == v2{.5f, .5f}), "V2 Lerp"); + TestClean((Lerp(v3{0, 0, 0}, v3{1, 1, 1}, .5f) == v3{.5f, .5f, .5f}), "V3 Lerp"); + TestClean((Lerp(v4{0, 0, 0, 0}, v4{1, 1, 1, 1}, .5f) == v4{.5f, .5f, .5f, .5f}), "V4 Lerp"); + + ///////////////////////////// + // Matrix + //////////////////////////// + + m33 TestM33 = m33{ + 0, 1, 2, + 3, 4, 5, + 6, 7, 8}; + + m33 EqualityM33 = {}; + for (s32 i = 0; i < 16; i++) { EqualityM33.E[i] = TestM33.E[i]; } + + m33 TransposeM33 = m33{ + 0, 3, 6, + 1, 4, 7, + 2, 5, 8}; + + m33 IdentityM33 = m33{ + 1, 0, 0, + 0, 1, 0, + 0, 0, 1}; + + m33 TestM33Squared = m33{ + 15, 18, 21, + 42, 54, 66, + 69, 90, 111 + }; + + m44 TestM44 = m44{ + 0, 1, 2, 3, + 4, 5, 6, 7, + 8, 9, 10, 11, + 12, 13, 14, 15 + }; + + m44 EqualityM44 = {}; + for (s32 i = 0; i < 16; i++) { EqualityM44.E[i] = TestM44.E[i]; } + + m44 TransposeM44 = m44{ + 0, 4, 8, 12, + 1, 5, 9, 13, + 2, 6, 10, 14, + 3, 7, 11, 15 + }; + + m44 IdentityM44 = m44{ + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + }; + + m44 TestM44Squared = m44{ + 56, 62, 68, 74, + 152, 174, 196, 218, + 248, 286, 324, 362, + 344, 398, 452, 506, + }; + + TestClean(((IdentityM33 == IdentityM33) && (TestM33 == EqualityM33)), "M33 Equality"); + TestClean(((IdentityM44 == IdentityM44) && (TestM44 == EqualityM44)), "M44 Equality"); + + TestClean(((Transpose(IdentityM33) == IdentityM33) && + (Transpose(TestM33) == TransposeM33)), "M33 Transpose"); + TestClean(((Transpose(IdentityM44) == IdentityM44) && + (Transpose(TestM44) == TransposeM44)), "M44 Transpose"); + + TestClean(((TestM33 * IdentityM33) == TestM33), "M33 Identity Mult"); + TestClean(((TestM44 * IdentityM44) == TestM44), "M44 Identity Mult"); + TestClean(((TestM33 * TestM33) == TestM33Squared), "M33 Mult"); + TestClean(((TestM44 * TestM44) == TestM44Squared), "M44 Mult"); + + + // Useful Tests + v4 Right = v4{1, 0, 0, 0}; + v4 Forward = v4{0, 0, 1, 0}; + v4 Up = v4{0, 1, 0, 0}; + v4 Left = v4{-1, 0, 0, 0}; + v4 Back = v4{0, 0, -1, 0}; + v4 Down = v4{0, -1, 0, 0}; + + m44 NinetyDegreesAboutX = GetXRotation(M_PI / 2); + v4 Rotated = NinetyDegreesAboutX * Forward; + TestClean((Rotated == Up), "Rotation About X"); + + m44 NinetyDegreesAboutY = GetYRotation(M_PI / 2); + Rotated = NinetyDegreesAboutY * Right; + TestClean((Rotated == Forward), "Rotation About Y"); + + m44 NinetyDegreesAboutZ = GetZRotation(M_PI / 2); + Rotated = NinetyDegreesAboutZ * Right; + TestClean((Rotated == Down), "Rotation About Z"); + + + v4 A = v4{1, 2, 3, 4}; + m44 B = m44{ + 1, 2, 3, 4, + 5, 6, 7, 8, + 9, 1, 2, 3, + 4, 5, 6, 7}; + v4 VTest = v4{30, 70, 29, 60}; + TestClean(((B * A) == VTest), "V4 M44 Multiplication"); + + m44 C = m44{ + 9, 8, 7, 6, + 5, 4, 3, 2, + 1, 0, 9, 8, + 7, 6, 5, 4 + }; + m44 MResult = B * C; + m44 MTest = m44{ + 50, 40, 60, 50, + 138, 112, 156, 130, + 109, 94, 99, 84, + 116, 94, 132, 110 + }; + TestClean(((B * C) == MTest), "M44 Mult Test 2"); + + m44 Identity = M44Identity(); + m44 InvIdentity = {}; + Inverse(Identity, &InvIdentity); + TestClean((Identity == InvIdentity), "Inverse Identity"); + + m44 Test = m44{ + 2, 4, 6, 7, + 5, 1, 8, 8, + 1, 7, 3, 1, + 3, 9, 2, 4 + }; + m44 PreCalcTestInv = m44{ + -0.3904761904761904762f, 0.26190476190476190475f, -0.02857142857142857139f, 0.16666666666666666668f, + 0.022222222222222222212f, -0.055555555555555555549f, 0.06666666666666666667f, 0.055555555555555555547f, + -0.00317460317460317458f, 0.07936507936507936506f, 0.27619047619047619045f, -0.2222222222222222222f, + 0.24444444444444444444f, -0.1111111111111111111f, -0.26666666666666666667f, 0.1111111111111111111f + }; + m44 InvTest = {}; + Inverse(Test, &InvTest); + //TestClean((PreCalcTestInv == InvTest), "Inverse M44"); + + DebugPrint("Results: Passed %d / %d\n\n\no", SuccessCount, TestCount); +} + +#endif + +#define GS_VECTOR_MATRIX_H +#endif \ No newline at end of file diff --git a/src/gs_win32.cpp b/gs_libs/gs_win32.cpp similarity index 100% rename from src/gs_win32.cpp rename to gs_libs/gs_win32.cpp diff --git a/src/gs_win32.h b/gs_libs/gs_win32.h similarity index 100% rename from src/gs_win32.h rename to gs_libs/gs_win32.h diff --git a/meta/build.bat b/meta/build.bat index b4b0d8f..5e21f70 100644 --- a/meta/build.bat +++ b/meta/build.bat @@ -16,7 +16,7 @@ set CommonLinkerFlags= -opt:ref 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 -stats C:\projects\foldhaus\build\win32_gs_meta_build_time.ctm diff --git a/meta/foldhaus_meta.cpp b/meta/foldhaus_meta.cpp index a96ca69..0c67490 100644 --- a/meta/foldhaus_meta.cpp +++ b/meta/foldhaus_meta.cpp @@ -24,34 +24,37 @@ int main(int ArgCount, char* Args[]) FinishGeneratingTypes(&TypeGenerator); gsm_code_generator NodeTypeGen = BeginEnumGeneration("node_type", "NodeType", false, true); - -#if 0 // TODO(Peter): Create a FilterTypesByTag function to create a contiguous array // of type_definition** 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); - if (HasTag(MakeStringLiteral("node_proc"), Decl->MetaTags) && - Decl->Type == TypeDef_Function) + type_table_hash_bucket Bucket = Meta.TypeTable.Types[b]; + for (u32 i = 0; i < TYPE_TABLE_BUCKET_MAX; i++) { - AddEnumElement(&NodeTypeGen, Decl->Identifier); + if (!Bucket.Keys[i] == 0) { continue; } - type_table_handle ReturnTypeHandle = Decl->Function.ReturnTypeHandle; - type_definition* ReturnType = GetTypeDefinition(ReturnTypeHandle, Meta.TypeTable); - printf("%.*s %.*s(\n", StringExpand(ReturnType->Identifier), StringExpand(Decl->Identifier)); - for (u32 j = 0; j < Decl->Function.Parameters.Used; j++) + type_definition* Decl = Bucket.Values + i; + if (HasTag(MakeStringLiteral("node_proc"), Decl->MetaTags) && + Decl->Type == TypeDef_Function) { - variable_decl* Param = Decl->Function.Parameters.GetElementAtIndex(j); - type_table_handle ParamTypeHandle = Param->TypeHandle; - type_definition* ParamType = GetTypeDefinition(ParamTypeHandle, Meta.TypeTable); - printf(" %.*s %.*s,\n", StringExpand(ParamType->Identifier), StringExpand(Param->Identifier)); + AddEnumElement(&NodeTypeGen, Decl->Identifier); + + type_table_handle ReturnTypeHandle = Decl->Function.ReturnTypeHandle; + type_definition* ReturnType = GetTypeDefinition(ReturnTypeHandle, Meta.TypeTable); + printf("%.*s %.*s(\n", StringExpand(ReturnType->Identifier), StringExpand(Decl->Identifier)); + for (u32 j = 0; j < Decl->Function.Parameters.Used; j++) + { + variable_decl* Param = Decl->Function.Parameters.GetElementAtIndex(j); + type_table_handle ParamTypeHandle = Param->TypeHandle; + type_definition* ParamType = GetTypeDefinition(ParamTypeHandle, Meta.TypeTable); + printf(" %.*s %.*s,\n", StringExpand(ParamType->Identifier), StringExpand(Param->Identifier)); + } + printf(");\n\n"); } - printf(");\n\n"); } } printf("\n\n"); -#endif FinishEnumGeneration(&NodeTypeGen); diff --git a/meta/gs_meta.cpp b/meta/gs_meta.cpp index 260b082..afca8ba 100644 --- a/meta/gs_meta.cpp +++ b/meta/gs_meta.cpp @@ -20,16 +20,16 @@ #include #include -#include -#include -#include "..\src\gs_platform.h" -#include -#include +#include "..\gs_libs\gs_language.h" +#include "..\gs_libs\gs_bucket.h" +#include "..\gs_libs\gs_platform.h" +#include "..\gs_libs\gs_memory_arena.h" +#include "..\gs_libs\gs_string.h" #include "gs_meta_lexer.h" #include "gs_meta_error.h" -#include "foldhaus_meta_type_table.h" +#include "gs_meta_type_table.h" struct source_code_file { @@ -404,10 +404,6 @@ ReadEntireFileAndNullTerminate (source_code_file* File, errors* Errors) LengthRead = (s32)ReadSize + 1; fclose(ReadFile); } - else - { - PushFError(Errors, "Could Not Read File: %S", File->Path); - } return LengthRead; } @@ -431,7 +427,7 @@ FileAlreadyInSource(string Path, gs_bucket SourceFiles) } internal void -AddFileToSource(string RelativePath, gs_bucket* SourceFiles, errors* Errors) +AddFileToSource(string RelativePath, source_code_file CurrentFile, gs_bucket* SourceFiles, errors* Errors) { source_code_file File = {0}; @@ -451,7 +447,11 @@ AddFileToSource(string RelativePath, gs_bucket* SourceFiles, e } 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 RootFile = MakeString(SourceFile); - AddFileToSource(RootFile, &Meta.SourceFiles, &Meta.Errors); + AddFileToSource(RootFile, {}, &Meta.SourceFiles, &Meta.Errors); s32 LastSlash = ReverseSearchForCharInSet(RootFile, "\\/"); if (LastSlash <= 0) @@ -1514,7 +1514,7 @@ PreprocessProgram (char* SourceFile) ParseSuccess = true; if (!FileAlreadyInSource(TempFilePath, Meta.SourceFiles)) { - AddFileToSource(TempFilePath, &Meta.SourceFiles, &Meta.Errors); + AddFileToSource(TempFilePath, *File, &Meta.SourceFiles, &Meta.Errors); } EndScope(IncludeScope); } @@ -1590,9 +1590,10 @@ FinishMetaprogram(gs_meta_preprocessor* Meta) FinishProfiler(&Meta->Profiler); PrintAllErrors(Meta->Errors); - printf("\nMetaprogram Performance:\n"); + PrintAllCategories(&Meta->Profiler); + printf("\n"); } #define GS_META_CPP diff --git a/meta/gs_meta_code_generator.h b/meta/gs_meta_code_generator.h index b8e5ed0..ba431ad 100644 --- a/meta/gs_meta_code_generator.h +++ b/meta/gs_meta_code_generator.h @@ -5,8 +5,8 @@ // #ifndef GS_META_CODE_GENERATOR_H -#include -#include +#include "../gs_libs/gs_string.h" +#include "../gs_libs/gs_string_builder.h" enum gsm_code_gen_type { diff --git a/meta/gs_meta_generator.h b/meta/gs_meta_generator.h deleted file mode 100644 index c2be5e6..0000000 --- a/meta/gs_meta_generator.h +++ /dev/null @@ -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; -} \ No newline at end of file diff --git a/meta/foldhaus_meta_include.h b/meta/gs_meta_include.h similarity index 100% rename from meta/foldhaus_meta_include.h rename to meta/gs_meta_include.h diff --git a/meta/gs_meta_parser.h b/meta/gs_meta_parser.h deleted file mode 100644 index 3d33168..0000000 --- a/meta/gs_meta_parser.h +++ /dev/null @@ -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 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; -} \ No newline at end of file diff --git a/meta/foldhaus_meta_type_table.h b/meta/gs_meta_type_table.h similarity index 100% rename from meta/foldhaus_meta_type_table.h rename to meta/gs_meta_type_table.h diff --git a/meta/gs_meta_typeinfo_generator.h b/meta/gs_meta_typeinfo_generator.h index 49015df..19c4cd2 100644 --- a/meta/gs_meta_typeinfo_generator.h +++ b/meta/gs_meta_typeinfo_generator.h @@ -9,9 +9,9 @@ // #ifndef GS_META_TYPEINFO_GENERATOR_H -#include -#include -#include +#include "..\gs_libs\gs_language.h" +#include "..\gs_libs\gs_string.h" +#include "..\gs_libs\gs_string_builder.h" #include "gs_meta_code_generator.h" struct typeinfo_generator diff --git a/meta/gs_string_builder.h b/meta/gs_string_builder.h deleted file mode 100644 index 519ed0c..0000000 --- a/meta/gs_string_builder.h +++ /dev/null @@ -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); -} \ No newline at end of file diff --git a/meta/main_meta.cpp b/meta/main_meta.cpp deleted file mode 100644 index a426edf..0000000 --- a/meta/main_meta.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#include "..\src\gs_language.h" -#include "..\src\foldhaus_memory.h" -#include "..\src\gs_string.h" - -#include "gs_meta.cpp" - -#include -#include - -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; -} \ No newline at end of file diff --git a/meta/meta_test.cpp b/meta/meta_test.cpp deleted file mode 100644 index 45abfef..0000000 --- a/meta/meta_test.cpp +++ /dev/null @@ -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; -} - diff --git a/src/foldhaus_app.cpp b/src/foldhaus_app.cpp index e98815e..f33be26 100644 --- a/src/foldhaus_app.cpp +++ b/src/foldhaus_app.cpp @@ -5,7 +5,7 @@ // #ifndef FOLDHAUS_APP_CPP -#include "../meta/foldhaus_meta_include.h" +#include "../meta/gs_meta_include.h" #include "foldhaus_platform.h" #include "foldhaus_app.h" diff --git a/src/foldhaus_app.h b/src/foldhaus_app.h index 31cb0ab..015ae51 100644 --- a/src/foldhaus_app.h +++ b/src/foldhaus_app.h @@ -7,7 +7,7 @@ #include "../meta/gs_meta_lexer.h" -#include "gs_font.h" +#include "../gs_libs/gs_font.h" #include "interface.h" #include "foldhaus_network_ordering.h" diff --git a/src/foldhaus_platform.h b/src/foldhaus_platform.h index f38a992..1e599ae 100644 --- a/src/foldhaus_platform.h +++ b/src/foldhaus_platform.h @@ -9,17 +9,17 @@ #include #define GS_LANGUAGE_NO_PROFILER_DEFINES -#include "C:\programs-dev\gs_libs\src\gs_language.h" -#include "gs_platform.h" +#include "..\gs_libs\gs_language.h" +#include "..\gs_libs\gs_platform.h" -#include "C:\programs-dev\gs_libs\src\gs_radix_sort.h" -#include "C:\programs-dev\gs_libs\src\gs_list.h" -#include "C:\programs-dev\gs_libs\src\gs_bucket.h" +#include "..\gs_libs\gs_radix_sort.h" +#include "..\gs_libs\gs_list.h" +#include "..\gs_libs\gs_bucket.h" #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" global_variable debug_services* GlobalDebugServices; @@ -27,9 +27,9 @@ global_variable debug_services* GlobalDebugServices; global_variable platform_alloc* GSAlloc; 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" diff --git a/src/gs_array.h b/src/gs_array.h deleted file mode 100644 index 73ed151..0000000 --- a/src/gs_array.h +++ /dev/null @@ -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 \ No newline at end of file diff --git a/src/gs_hashtable.h b/src/gs_hashtable.h deleted file mode 100644 index a3d733c..0000000 --- a/src/gs_hashtable.h +++ /dev/null @@ -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 \ No newline at end of file diff --git a/src/win32_foldhaus.cpp b/src/win32_foldhaus.cpp index d6a0096..bbb7b1b 100644 --- a/src/win32_foldhaus.cpp +++ b/src/win32_foldhaus.cpp @@ -11,10 +11,10 @@ #include #include -#include "../meta/foldhaus_meta_include.h" +#include "../meta/gs_meta_include.h" #include "foldhaus_platform.h" -#include "gs_win32.cpp" +#include "../gs_libs/gs_win32.cpp" #include "foldhaus_renderer.cpp" global_variable b32 Running = false;