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