From 780ccbd1a328e9c7e8f378db76b90f62e153c023 Mon Sep 17 00:00:00 2001 From: Peter Slattery Date: Sun, 1 Mar 2020 15:24:12 -0800 Subject: [PATCH] Converted thinsg over to using time ranges and implemented zoom. Also restructured how different parts of the timeline view know where they are. The timeline now constructs all the bounds, and each element just draws itself inside the provided bounds. --- gs_libs/gs_string.h | 4338 +++++++++-------- gs_libs/gs_vector_matrix.h | 50 +- src/animation/foldhaus_animation.h | 1 + .../foldhaus_panel_animation_timeline.h | 311 +- src/panels/foldhaus_panel_node_graph.h | 1 + todo.txt | 4 +- 6 files changed, 2473 insertions(+), 2232 deletions(-) diff --git a/gs_libs/gs_string.h b/gs_libs/gs_string.h index 1b1fad2..8fb9ac8 100644 --- a/gs_libs/gs_string.h +++ b/gs_libs/gs_string.h @@ -227,2164 +227,2204 @@ static b32 CharArrayContainsSafe(char* Array, s32 ArrayLength, char* CheckF #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 - + + 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 -//////////////////////////////////////////////////////////////// - + static void PrintFArgList(char* Dest, s32 DestMax, char* Format, va_list Args); + static void PrintF(string* String, char* Format, ...); + + // Printing Helper Functions + static u32 GetU32NumberOfCharactersNeeded(u32 Value, u32 Base = 10); + static u32 GetS32NumberOfCharactersNeeded(u32 Value, s32 Base = 10); + + //////////////////////////////////////////////////////////////// + // 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; -} - + + 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; -} - + + //////////////////////////////////////////////////////////////// + // 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; + } + + static 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); + } + + static 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); + } + + // Printing Helper Functions + static u32 + GetU32NumberOfCharactersNeeded(u32 Value, u32 Base) + { + u32 Result = 0; + u32 ValueLeft = Value; + // NOTE(Peter): This is in a do while loop because even if the number is 0, + // it'll still take one character to display that. + do + { + Result += 1; + ValueLeft /= Base; + }while (ValueLeft > 0); + return Result; + } + + static u32 + GetS32NumberOfCharactersNeeded(s32 Value, s32 Base) + { + u32 Result = 0; + s32 ValueLeft = Value; + if (Value < 0) + { + Result += 1; + ValueLeft = Value * -1; + } + // NOTE(Peter): This is in a do while loop because even if the number is 0, + // it'll still take one character to display that. + do + { + Result += 1; + ValueLeft /= Base; + }while (ValueLeft > 0); + return Result; + } + + //////////////////////////////////////////////////////////////// + // 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; -} - + + 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); - + + 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); + // 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); -} + 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_vector_matrix.h b/gs_libs/gs_vector_matrix.h index fb1a3b3..17167d9 100644 --- a/gs_libs/gs_vector_matrix.h +++ b/gs_libs/gs_vector_matrix.h @@ -627,20 +627,14 @@ v4 HSVToRGB (v4 In) } static bool -PointIsInRange ( - v2 _P, - v2 _Min, v2 _Max - ) +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 - ) +PointIsInRangeSafe (v2 _P, v2 _Min, v2 _Max) { s32 MinX = GSMin(_Min.x, _Max.x); s32 MinY = GSMin(_Min.y, _Max.y); @@ -666,6 +660,15 @@ PointToPercentRange (v2 P, v2 Min, v2 Max) // RECT ////////////////////////////////////// +// NOTE(Peter): This is useful when you have a function that has a call signature like: +// void Foo(v2 Min, v2 Max) +// because instead of having to do: +// Foo(MyRect.Min, MyRect.Max) +// you can just do: +// Foo(RectExpand(MyRect)) +// which makes refactoring easier as you only have to change the identifier in one place +#define RectExpand(r) (r).Min, (r).Max + inline float Width (rect Rect) { @@ -680,12 +683,43 @@ Height (rect Rect) return Result; } +inline v2 +TopLeft(rect Rect) +{ + v2 Result = {0}; + Result.x = Rect.Min.x; + Result.y = Rect.Max.y; + return Result; +} + +inline v2 +TopRight(rect Rect) +{ + return Rect.Max; +} + +inline v2 +BottomLeft(rect Rect) +{ + return Rect.Min; +} + +inline v2 +BottomRight(rect Rect) +{ + v2 Result = {0}; + Result.x = Rect.Max.x; + Result.y = Rect.Min.y; + return Result; +} + inline float AspectRatio (rect Rect) { float Result = Width(Rect) / Height(Rect); return Result; } + inline v2 CalculateRectCenter (rect Rect) { diff --git a/src/animation/foldhaus_animation.h b/src/animation/foldhaus_animation.h index 82d2b82..9b21eda 100644 --- a/src/animation/foldhaus_animation.h +++ b/src/animation/foldhaus_animation.h @@ -32,6 +32,7 @@ struct animation_system b32 TimelineShouldAdvance; // Timeline + // TODO(Peter): Reverse these. Should be FrameStart and FrameEnd for consistency r32 StartFrame; r32 EndFrame; }; diff --git a/src/panels/foldhaus_panel_animation_timeline.h b/src/panels/foldhaus_panel_animation_timeline.h index 3cee3f5..3557265 100644 --- a/src/panels/foldhaus_panel_animation_timeline.h +++ b/src/panels/foldhaus_panel_animation_timeline.h @@ -5,6 +5,70 @@ // #ifndef FOLDHAUS_PANEL_ANIMATION_TIMELINE_H +// Colors +global_variable v4 TimeSliderColor = v4{.36f, .52f, .78f, 1.f}; + +// +struct animation_timeline_state +{ + s32 VisibleFrameRangeMin; + s32 VisibleFrameRangeMax; +}; + +// TODO(Peter): Move this over to the animation system just as frame_range +struct timeline_frame_range +{ + u32 FrameMin; + u32 FrameMax; +}; + +inline b32 +FrameIsInRange(u32 Frame, timeline_frame_range Range) +{ + b32 Result = (Frame >= Range.FrameMin) && (Frame <= Range.FrameMax); + return Result; +} + +internal u32 +GetFrameCount(timeline_frame_range Range) +{ + u32 Result = Range.FrameMax - Range.FrameMin; + return Result; +} + +internal r32 +FrameToPercentRange(s32 Frame, timeline_frame_range Range) +{ + r32 Result = (r32)(Frame - Range.FrameMin); + Result = Result / GetFrameCount(Range); + return Result; +} + +internal u32 +PercentToFrameInRange(r32 Percent, timeline_frame_range Range) +{ + u32 Result = Range.FrameMin + (u32)(Percent * GetFrameCount(Range)); + return Result; +} + +internal u32 +ClampFrameToRange(u32 Frame, timeline_frame_range Range) +{ + u32 Result = Frame; + if (Result < Range.FrameMin) + { + Result = Range.FrameMin; + } + else if (Result > Range.FrameMax) + { + Result = Range.FrameMax; + } + return Result; +} + +// +// + inline u32 GetFrameFromPointInAnimationPanel(v2 Point, rect PanelBounds, u32 StartFrame, u32 EndFrame) { @@ -97,15 +161,15 @@ input_command DragTimeMarkerCommands [] = { }; internal void -StartDragTimeMarker(rect TimelineBounds, s32 PanelStartFrame, s32 PanelEndFrame, app_state* State) +StartDragTimeMarker(rect TimelineBounds, timeline_frame_range VisibleFrames, app_state* State) { operation_mode* DragTimeMarkerMode = ActivateOperationModeWithCommands(&State->Modes, DragTimeMarkerCommands, UpdateDragTimeMarker); drag_time_marker_operation_state* OpState = CreateOperationState(DragTimeMarkerMode, &State->Modes, drag_time_marker_operation_state); - OpState->StartFrame = PanelStartFrame; - OpState->EndFrame = PanelEndFrame ; + OpState->StartFrame = VisibleFrames.FrameMin; + OpState->EndFrame = VisibleFrames.FrameMax; OpState->TimelineBounds = TimelineBounds; } @@ -240,7 +304,7 @@ input_command DragAnimationClipCommands [] = { }; internal void -SelectAndBeginDragAnimationBlock(gs_list_handle BlockHandle, s32 PanelStartFrame, s32 PanelEndFrame, rect TimelineBounds, app_state* State) +SelectAndBeginDragAnimationBlock(gs_list_handle BlockHandle, timeline_frame_range VisibleRange, rect TimelineBounds, app_state* State) { SelectAnimationBlock(BlockHandle, State); @@ -250,8 +314,8 @@ SelectAndBeginDragAnimationBlock(gs_list_handle BlockHandle, s32 PanelStartFrame &State->Modes, drag_animation_clip_state); OpState->TimelineBounds = TimelineBounds; - OpState->AnimationPanel_StartFrame = PanelStartFrame; - OpState->AnimationPanel_EndFrame = PanelEndFrame ; + OpState->AnimationPanel_StartFrame = VisibleRange.FrameMin; + OpState->AnimationPanel_EndFrame = VisibleRange.FrameMax; animation_block* SelectedBlock = State->AnimationSystem.Blocks.GetElementWithHandle(BlockHandle); OpState->SelectedClip_InitialStartFrame = SelectedBlock->StartFrame; @@ -278,7 +342,11 @@ GSMetaTag(panel_type_animation_timeline); internal void AnimationTimeline_Init(panel* Panel, app_state* State) { - + // TODO: :FreePanelMemory + animation_timeline_state* TimelineState = PushStruct(&State->Permanent, animation_timeline_state); + TimelineState->VisibleFrameRangeMin = State->AnimationSystem.StartFrame; + TimelineState->VisibleFrameRangeMax = State->AnimationSystem.EndFrame; + Panel->PanelStateMemory = (u8*)TimelineState; } GSMetaTag(panel_cleanup); @@ -289,57 +357,131 @@ AnimationTimeline_Cleanup(panel* Panel, app_state* State) } -internal r32 -DrawFrameBar (animation_system* AnimationSystem, render_command_buffer* RenderBuffer, s32 StartFrame, s32 EndFrame, rect PanelBounds, mouse_state Mouse, app_state* State) +internal void +DrawFrameBar (animation_system* AnimationSystem, render_command_buffer* RenderBuffer, timeline_frame_range VisibleFrames, rect BarBounds, mouse_state Mouse, app_state* State) { MakeStringBuffer(TempString, 256); - s32 FrameCount = EndFrame - StartFrame; + s32 VisibleFrameCount = VisibleFrames.FrameMax - VisibleFrames.FrameMin; - r32 FrameBarHeight = 24; - v2 FrameBarMin = v2{PanelBounds.Min.x, PanelBounds.Max.y - FrameBarHeight}; - v2 FrameBarMax = PanelBounds.Max; + r32 BarHeight = Height(BarBounds); + r32 BarWidth = Width(BarBounds); - PushRenderQuad2D(RenderBuffer, FrameBarMin, FrameBarMax, v4{.16f, .16f, .16f, 1.f}); + PushRenderQuad2D(RenderBuffer, RectExpand(BarBounds), v4{.16f, .16f, .16f, 1.f}); // Mouse clicked inside frame nubmer bar -> change current frame on timeline - if (MouseButtonTransitionedDown(Mouse.LeftButtonState) - && PointIsInRange(Mouse.DownPos, FrameBarMin, FrameBarMax)) + if (MouseButtonTransitionedDown(Mouse.LeftButtonState) && + PointIsInRange(Mouse.DownPos, RectExpand(BarBounds))) { - StartDragTimeMarker(rect{FrameBarMin, FrameBarMax}, StartFrame, EndFrame, State); + StartDragTimeMarker(BarBounds, VisibleFrames, State); } // Frame Ticks - for (s32 f = 0; f < FrameCount; f += 10) + u32 TickCount = 10; + for (u32 Tick = 0; Tick < TickCount; Tick++) { - s32 Frame = StartFrame + f; + r32 Percent = (r32)Tick / (r32)TickCount; + u32 Frame = PercentToFrameInRange(Percent, VisibleFrames); PrintF(&TempString, "%d", Frame); - - r32 FramePercent = (r32)f / (r32)FrameCount; - r32 FrameX = GSLerp(PanelBounds.Min.x, PanelBounds.Max.x, FramePercent); - v2 FrameTextPos = v2{FrameX, FrameBarMin.y + 2}; + r32 FramePercent = FrameToPercentRange(Frame, VisibleFrames); + r32 FrameX = GSLerp(BarBounds.Min.x, BarBounds.Max.x, FramePercent); + v2 FrameTextPos = v2{FrameX, BarBounds.Min.y + 2}; DrawString(RenderBuffer, TempString, State->Interface.Font, FrameTextPos, WhiteV4); - // Frame Vertical Slices - v2 LineTop = v2{FrameX, FrameBarMin.y}; - v2 LineBottom = v2{FrameX + 1, PanelBounds.Min.y}; + v2 LineTop = v2{FrameX, BarBounds.Min.y}; + v2 LineBottom = v2{FrameX + 1, BarBounds.Min.y}; PushRenderQuad2D(RenderBuffer, LineTop, LineBottom, v4{.16f, .16f, .16f, 1.f}); } - return FrameBarMin.y; + // Time Slider + if (FrameIsInRange(AnimationSystem->CurrentFrame, VisibleFrames)) + { + r32 FrameAtPercentVisibleRange = FrameToPercentRange(AnimationSystem->CurrentFrame, VisibleFrames); + r32 SliderX = GSLerp(BarBounds.Min.x, BarBounds.Max.x, FrameAtPercentVisibleRange); + + u32 FrameNumberCharCount = GetU32NumberOfCharactersNeeded(AnimationSystem->CurrentFrame); + // space for each character + a margin on either side + r32 SliderWidth = (8 * FrameNumberCharCount) + 8; + r32 SliderHalfWidth = SliderWidth / 2.f; + v2 HeadMin = v2{SliderX - SliderHalfWidth, BarBounds.Min.y}; + v2 HeadMax = v2{SliderX + SliderHalfWidth, BarBounds.Max.y}; + PushRenderQuad2D(RenderBuffer, HeadMin, HeadMax, TimeSliderColor); + + PrintF(&TempString, "%d", AnimationSystem->CurrentFrame); + DrawString(RenderBuffer, TempString, State->Interface.Font, HeadMin + v2{6, 4}, WhiteV4); + } +} + +internal timeline_frame_range +DrawTimelineRangeBar (animation_system* AnimationSystem, animation_timeline_state* TimelineState, render_command_buffer* RenderBuffer, rect BarBounds, mouse_state Mouse) +{ + timeline_frame_range Result = {0}; + + r32 BarHeight = Height(BarBounds); + r32 BarWidth = Width(BarBounds); + PushRenderQuad2D(RenderBuffer, RectExpand(BarBounds), v4{.16f, .16f, .16f, 1.f}); + + r32 PlayableFrames = (r32)(AnimationSystem->EndFrame - AnimationSystem->StartFrame); + v2 SliderBarDim = v2{25, BarHeight}; + + // Convert Frames To Pixels + // TODO(Peter): When the animation system is storing a frame range rather than individual values + // come back and use the utility functions here + r32 RangeMinPercentPlayable = (r32)(TimelineState->VisibleFrameRangeMin - AnimationSystem->StartFrame) / PlayableFrames; + r32 RangeMaxPercentPlayable = (r32)(TimelineState->VisibleFrameRangeMax - AnimationSystem->StartFrame) / PlayableFrames; + v2 RangeMinSliderMin = v2{BarBounds.Min.x + (RangeMinPercentPlayable * Width(BarBounds)), BarBounds.Min.y}; + v2 RangeMaxSliderMin = v2{BarBounds.Min.x + (RangeMaxPercentPlayable * Width(BarBounds)) - 25, BarBounds.Min.y}; + + if (MouseButtonHeldDown(Mouse.LeftButtonState) || + MouseButtonTransitionedUp(Mouse.LeftButtonState)) + { + v2 MouseDragOffset = Mouse.Pos - Mouse.DownPos; + if (PointIsInRange(Mouse.DownPos, RangeMinSliderMin, RangeMinSliderMin + SliderBarDim)) + { + r32 NewSliderX = RangeMinSliderMin.x + MouseDragOffset.x; + RangeMinSliderMin.x = GSClamp(BarBounds.Min.x, NewSliderX, RangeMaxSliderMin.x - SliderBarDim.x); + } + if (PointIsInRange(Mouse.DownPos, RangeMaxSliderMin, RangeMaxSliderMin + SliderBarDim)) + { + r32 NewSliderX = RangeMaxSliderMin.x + MouseDragOffset.x; + RangeMaxSliderMin.x = GSClamp(RangeMinSliderMin.x + SliderBarDim.x, NewSliderX, BarBounds.Max.x - SliderBarDim.x); + } + } + + v2 RangeMinSliderMax = v2{RangeMinSliderMin.x + 25, BarBounds.Max.y}; + v2 RangeMaxSliderMax = v2{RangeMaxSliderMin.x + 25, BarBounds.Max.y}; + PushRenderQuad2D(RenderBuffer, RangeMinSliderMin, RangeMinSliderMax, v4{.8f, .8f, .8f, 1.f}); + PushRenderQuad2D(RenderBuffer, RangeMaxSliderMin, RangeMaxSliderMax, v4{.8f, .8f, .8f, 1.f}); + + // Convert Pixels Back To Frames and store + RangeMinPercentPlayable = (RangeMinSliderMin.x - BarBounds.Min.x) / BarWidth; + RangeMaxPercentPlayable = (RangeMaxSliderMax.x - BarBounds.Min.x) / BarWidth; + u32 VisibleFrameCount = AnimationSystem->EndFrame - AnimationSystem->StartFrame; + Result.FrameMin = RangeMinPercentPlayable * VisibleFrameCount; + Result.FrameMax = RangeMaxPercentPlayable * VisibleFrameCount; + + if (MouseButtonTransitionedUp(Mouse.LeftButtonState)) + { + TimelineState->VisibleFrameRangeMin = Result.FrameMin; + TimelineState->VisibleFrameRangeMax = Result.FrameMax; + } + + return Result; } internal rect -DrawAnimationBlock (animation_block AnimationBlock, v4 BlockColor, s32 FrameCount, s32 StartFrame, rect TimelineBounds, render_command_buffer* RenderBuffer) +DrawAnimationBlock (animation_block AnimationBlock, v4 BlockColor, timeline_frame_range VisibleFrames, rect TimelineBounds, render_command_buffer* RenderBuffer) { rect BlockBounds = {}; r32 TimelineWidth = Width(TimelineBounds); - r32 StartFramePercent = (r32)(AnimationBlock.StartFrame - StartFrame) / (r32)FrameCount; + u32 ClampedBlockStartFrame = ClampFrameToRange(AnimationBlock.StartFrame, VisibleFrames); + r32 StartFramePercent = FrameToPercentRange(ClampedBlockStartFrame, VisibleFrames); r32 StartPosition = TimelineWidth * StartFramePercent; - r32 EndFramePercent = (r32)(AnimationBlock.EndFrame - StartFrame) / (r32)FrameCount; + u32 ClampedBlockEndFrame = ClampFrameToRange(AnimationBlock.EndFrame, VisibleFrames); + r32 EndFramePercent = FrameToPercentRange(ClampedBlockEndFrame, VisibleFrames); r32 EndPosition = TimelineWidth * EndFramePercent; BlockBounds.Min = TimelineBounds.Min + v2{StartPosition, 25}; @@ -352,31 +494,33 @@ DrawAnimationBlock (animation_block AnimationBlock, v4 BlockColor, s32 FrameCoun } internal gs_list_handle -DrawAnimationTimeline (animation_system* AnimationSystem, s32 VisibleStartFrame, s32 VisibleEndFrame, rect PanelBounds, gs_list_handle SelectedBlockHandle, render_command_buffer* RenderBuffer, app_state* State, mouse_state Mouse) +DrawAnimationTimeline (animation_system* AnimationSystem, animation_timeline_state* TimelineState, rect PanelBounds, gs_list_handle SelectedBlockHandle, render_command_buffer* RenderBuffer, app_state* State, mouse_state Mouse) { string TempString = MakeString(PushArray(&State->Transient, char, 256), 256); - s32 VisibleFrameCount = VisibleEndFrame - VisibleStartFrame; - gs_list_handle Result = SelectedBlockHandle; r32 AnimationPanelHeight = PanelBounds.Max.y - PanelBounds.Min.y; r32 AnimationPanelWidth = PanelBounds.Max.x - PanelBounds.Min.x; - { - r32 FirstPlayablePercentX = ((r32)(AnimationSystem->StartFrame - VisibleStartFrame) / (r32)VisibleFrameCount); - r32 LastPlayablePercentX = ((r32)(AnimationSystem->EndFrame - VisibleStartFrame) / (r32)VisibleFrameCount); - - v2 PlayableMin = v2{(FirstPlayablePercentX * AnimationPanelWidth) + PanelBounds.Min.x, PanelBounds.Min.y }; - v2 PlayableMax = v2{(LastPlayablePercentX * AnimationPanelWidth) + PanelBounds.Min.x, PanelBounds.Max.y }; - - PushRenderQuad2D(RenderBuffer, PanelBounds.Min, PanelBounds.Max, v4{.16f, .16f, .16f, 1.f}); - PushRenderQuad2D(RenderBuffer, PlayableMin, PlayableMax, v4{.22f, .22f, .22f, 1.f}); - } + rect TimeRangeBarBounds = {0}; + TimeRangeBarBounds.Min = PanelBounds.Min; + TimeRangeBarBounds.Max = { PanelBounds.Max.x, PanelBounds.Min.y + 24 }; - r32 FrameBarBottom = DrawFrameBar(AnimationSystem, RenderBuffer, VisibleStartFrame, VisibleEndFrame, PanelBounds, Mouse, State); + rect FrameBarBounds = {0}; + FrameBarBounds.Min = { PanelBounds.Min.x, PanelBounds.Max.y - 32 }; + FrameBarBounds.Max = PanelBounds.Max; + + rect TimelineBounds = {0}; + TimelineBounds.Min = TopLeft(TimeRangeBarBounds); + TimelineBounds.Max = BottomRight(FrameBarBounds); + + timeline_frame_range AdjustedViewRange = {0}; + AdjustedViewRange = DrawTimelineRangeBar(AnimationSystem, TimelineState, RenderBuffer, TimeRangeBarBounds, Mouse); + s32 VisibleFrameCount = AdjustedViewRange.FrameMax - AdjustedViewRange.FrameMin; + + DrawFrameBar(AnimationSystem, RenderBuffer, AdjustedViewRange, FrameBarBounds, Mouse, State); // Animation Blocks - rect TimelineBounds = rect{ PanelBounds.Min, v2{PanelBounds.Max.x, FrameBarBottom} }; b32 MouseDownAndNotHandled = MouseButtonTransitionedDown(Mouse.LeftButtonState); for (u32 i = 0; i < AnimationSystem->Blocks.Used; i++) { @@ -386,38 +530,58 @@ DrawAnimationTimeline (animation_system* AnimationSystem, s32 VisibleStartFrame, gs_list_handle CurrentBlockHandle = AnimationBlockEntry->Handle; animation_block AnimationBlockAt = AnimationBlockEntry->Value; - v4 BlockColor = BlackV4; - if (GSListHandlesAreEqual(SelectedBlockHandle, CurrentBlockHandle)) + // If either end is in the range, we should draw it + b32 RangeIsVisible = (FrameIsInRange(AnimationBlockAt.StartFrame, AdjustedViewRange) || + FrameIsInRange(AnimationBlockAt.EndFrame, AdjustedViewRange)); + // If neither end is in the range, but the ends surround the visible range, + // we should still draw it. + RangeIsVisible |= (AnimationBlockAt.StartFrame <= AdjustedViewRange.FrameMin && + AnimationBlockAt.EndFrame >= AdjustedViewRange.FrameMax); + if (RangeIsVisible) { - BlockColor = PinkV4; - } - - rect BlockBounds = DrawAnimationBlock(AnimationBlockAt, BlockColor, VisibleFrameCount, VisibleStartFrame, TimelineBounds, RenderBuffer); - - if (PointIsInRange(Mouse.Pos, BlockBounds.Min, BlockBounds.Max) - && MouseButtonTransitionedDown(Mouse.LeftButtonState)) - { - MouseDownAndNotHandled = false; - SelectAndBeginDragAnimationBlock(CurrentBlockHandle, VisibleStartFrame, VisibleEndFrame, TimelineBounds, State); + v4 BlockColor = BlackV4; + if (GSListHandlesAreEqual(SelectedBlockHandle, CurrentBlockHandle)) + { + BlockColor = PinkV4; + } + rect BlockBounds = DrawAnimationBlock(AnimationBlockAt, BlockColor, AdjustedViewRange, TimelineBounds, RenderBuffer); + if (PointIsInRange(Mouse.Pos, BlockBounds.Min, BlockBounds.Max) + && MouseButtonTransitionedDown(Mouse.LeftButtonState)) + { + MouseDownAndNotHandled = false; + SelectAndBeginDragAnimationBlock(CurrentBlockHandle, AdjustedViewRange, TimelineBounds, State); + } } } // Time Slider - r32 TimePercent = (r32)(AnimationSystem->CurrentFrame - VisibleStartFrame) / (r32)VisibleFrameCount; - r32 SliderX = PanelBounds.Min.x + (AnimationPanelWidth * TimePercent); - v2 SliderMin = v2{SliderX, PanelBounds.Min.y}; - v2 SliderMax = v2{SliderX + 1, PanelBounds.Max.y - 25}; - v4 TimeSliderColor = v4{.36f, .52f, .78f, 1.f}; + if (FrameIsInRange(AnimationSystem->CurrentFrame, AdjustedViewRange)) + { + r32 FrameAtPercentVisibleRange = FrameToPercentRange(AnimationSystem->CurrentFrame, AdjustedViewRange); + r32 SliderX = GSLerp(TimelineBounds.Min.x, TimelineBounds.Max.x, FrameAtPercentVisibleRange); + v2 SliderMin = v2{SliderX, TimelineBounds.Min.y}; + v2 SliderMax = v2{SliderX + 1, TimelineBounds.Max.y}; + PushRenderQuad2D(RenderBuffer, SliderMin, SliderMax, TimeSliderColor); + } - PushRenderQuad2D(RenderBuffer, SliderMin, SliderMax, TimeSliderColor); + PushRenderBoundingBox2D(RenderBuffer, RectExpand(TimeRangeBarBounds), 1.f, RedV4); + PushRenderBoundingBox2D(RenderBuffer, RectExpand(FrameBarBounds), 1.f, TealV4); + PushRenderBoundingBox2D(RenderBuffer, RectExpand(TimelineBounds), 1.f, PinkV4); - r32 SliderHalfWidth = 10; - v2 HeadMin = v2{SliderX - SliderHalfWidth, SliderMax.y}; - v2 HeadMax = v2{SliderX + SliderHalfWidth, PanelBounds.Max.y}; - PushRenderQuad2D(RenderBuffer, HeadMin, HeadMax, TimeSliderColor); + return Result; - PrintF(&TempString, "%d", AnimationSystem->CurrentFrame); - DrawString(RenderBuffer, TempString, State->Interface.Font, HeadMin + v2{4, 4}, WhiteV4); + + + { + r32 FirstPlayablePercentX = FrameToPercentRange(AnimationSystem->StartFrame, AdjustedViewRange); + r32 LastPlayablePercentX = FrameToPercentRange(AnimationSystem->EndFrame, AdjustedViewRange); + + v2 PlayableMin = v2{(FirstPlayablePercentX * AnimationPanelWidth) + PanelBounds.Min.x, PanelBounds.Min.y }; + v2 PlayableMax = v2{(LastPlayablePercentX * AnimationPanelWidth) + PanelBounds.Min.x, PanelBounds.Max.y }; + + PushRenderQuad2D(RenderBuffer, PanelBounds.Min, PanelBounds.Max, v4{.16f, .16f, .16f, 1.f}); + PushRenderQuad2D(RenderBuffer, PlayableMin, PlayableMax, v4{.22f, .22f, .22f, 1.f}); + } if (MouseDownAndNotHandled && PointIsInRect(Mouse.Pos, TimelineBounds)) { @@ -484,6 +648,8 @@ GSMetaTag(panel_type_animation_timeline); internal void AnimationTimeline_Render(panel Panel, rect PanelBounds, render_command_buffer* RenderBuffer, app_state* State, context Context, mouse_state Mouse) { + animation_timeline_state* TimelineState = (animation_timeline_state*)Panel.PanelStateMemory; + gs_list_handle SelectedBlockHandle = State->SelectedAnimationBlockHandle; r32 OptionsRowHeight = 25; @@ -495,16 +661,15 @@ AnimationTimeline_Render(panel Panel, rect PanelBounds, render_command_buffer* R v2{AnimationClipListBounds.Max.x, PanelBounds.Min.y}, v2{PanelBounds.Max.x, PanelBounds.Max.y - OptionsRowHeight}, }; + if (Height(TimelineBounds) > 0) { - DrawAnimationClipsList(AnimationClipListBounds, Mouse, RenderBuffer, State); - SelectedBlockHandle = DrawAnimationTimeline(&State->AnimationSystem, - State->AnimationSystem.StartFrame - 20, - State->AnimationSystem.EndFrame + 20, + TimelineState, TimelineBounds, SelectedBlockHandle, RenderBuffer, State, Mouse); + DrawAnimationClipsList(AnimationClipListBounds, Mouse, RenderBuffer, State); } v2 OptionsRowMin = v2{ PanelBounds.Min.x, TimelineBounds.Max.y }; diff --git a/src/panels/foldhaus_panel_node_graph.h b/src/panels/foldhaus_panel_node_graph.h index 6339b3b..65affe5 100644 --- a/src/panels/foldhaus_panel_node_graph.h +++ b/src/panels/foldhaus_panel_node_graph.h @@ -180,6 +180,7 @@ NodeGraph_Init(panel* Panel, app_state* State) // TODO(Peter): We aren't able to free this memory. We need a system for // taking fixed size chunks off the Memory stack and then reusing them. THis // should probably live outside the paneling system. + // TODO: :FreePanelMemory Panel->PanelStateMemory = (u8*)PushStruct(&State->Permanent, node_graph_state); node_graph_state* GraphState = (node_graph_state*)Panel->PanelStateMemory; GraphState->LayoutIsDirty = true; diff --git a/todo.txt b/todo.txt index 630d152..bb294a1 100644 --- a/todo.txt +++ b/todo.txt @@ -65,8 +65,8 @@ Ground Up Reengineering - Animation System x snapping clips - - convert everything from time to frames - - zoom in and out + x convert everything from time to frames + x zoom in and out - blending between animation - layers - display more than one layer