Lumenarium/gs_string.h

1710 lines
43 KiB
C

////////////////////////////////////////////////////////////////
// String
////////////////////////////////////////////////////////////////
struct string
{
char* Memory;
s32 Length;
s32 Max;
};
////////////////////////////////////////////////////////////////
// String Tokenizing
////////////////////////////////////////////////////////////////
struct tokenizer
{
char* At;
char* Memory;
s32 MemoryLength;
};
enum token_type
{
Token_Error,
Token_LeftParen,
Token_RightParen,
Token_LeftSquareBracket,
Token_RightSquareBracket,
Token_LeftCurlyBracket,
Token_RightCurlyBracket,
Token_Semicolon,
Token_Operator,
Token_Comma,
Token_Period,
Token_PointerReference,
Token_PoundDefine,
Token_PoundUndef,
Token_PoundInclude,
Token_PoundIfDef,
Token_PoundIfNDef,
Token_PoundIf,
Token_PoundElif,
Token_PoundElse,
Token_PoundEndif,
Token_PoundError,
Token_PoundPragma,
Token_Number,
Token_Char,
Token_String,
Token_Identifier,
Token_Comment,
Token_MultilineComment,
Token_Unknown,
Token_EndOfStream,
};
char* TokenNames[] = {
"Token_Error ",
"Token_LeftParen ",
"Token_RightParen ",
"Token_LeftSquareBracket ",
"Token_RightSquareBracket ",
"Token_LeftCurlyBracket ",
"Token_RightCurlyBracket ",
"Token_Semicolon ",
"Token_Operator ",
"Token_Comma ",
"Token_Period ",
"Token_PointerReference ",
"Token_PoundDefine ",
"Token_PoundUndef ",
"Token_PoundInclude ",
"Token_PoundIfDef ",
"Token_PoundIfNDef ",
"Token_PoundIf ",
"Token_PoundElif ",
"Token_PoundElse ",
"Token_PoundEndif ",
"Token_PoundError ",
"Token_PoundPragma ",
"Token_Number ",
"Token_Char ",
"Token_String ",
"Token_Identifier ",
"Token_Comment ",
"Token_MultilineComment ",
"Token_Unknown ",
"Token_EndOfStream ",
};
struct token
{
token_type Type;
char* Text;
s32 TextLength;
token* Next;
};
////////////////////////////////////////////////////////////////
// String Memory
////////////////////////////////////////////////////////////////
struct slot_header
{
slot_header* Next;
s32 Size;
};
struct slot_arena
{
u8* Memory;
s32 SlotSize;
s32 SlotCount;
slot_header* FreeList;
};
struct contiguous_slot_count_result
{
s32 Count;
slot_header* LastContiguousSlot;
};
////////////////////////////////////////////////////////////////
// String Function Declarations
////////////////////////////////////////////////////////////////
// Utility
#if !defined GS_LANGUAGE_H
static void GSZeroMemory (u8* Memory, s32 Size);
static s32 GSMin (s32 A, s32 B);
static s32 GSAbs (s32 A);
static float GSAbsF (float A);
static float GSPowF (float N, s32 Power);
#endif
// Setup
#ifdef GS_MEMORY_H
#define PushString(str, arena, size) (str)->Memory = PushArray(arena, char, size); (str)->Length = 0; (str)->Max = size;
#endif
static void InitializeString (string* String, char* Data, s32 DataSize);
static string InitializeString (char* Data, s32 DataSize);
static void ClearString (string* String);
// Character Values
static bool IsSlash (char C);
static bool IsNewline (char C);
static bool IsWhitespace (char C);
static bool IsAlpha (char C);
static bool IsUpper (char C);
static bool IsLower (char C);
static bool IsNumeric (char C);
static bool IsNumericExtended (char C);
static bool ToUpper (char C);
static bool ToLower (char C);
static bool IsAlphaNumeric (char C);
static bool IsOperator (char C);
// Tokenizing
static b32 AtValidToken(tokenizer Tokenizer);
static char* EatToNewLine(char* C);
static void EatToNewLine(tokenizer* T);
static char* EatWhitespace(char* C);
static void EatWhitespace(tokenizer* T);
static char* EatToWhitespace(char* C);
static void EatToWhitespace(tokenizer* T);
static char* EatToCharacter(char* C, char Char);
static void EatToCharacter(tokenizer* T, char Char);
static char* EatPastCharacter(char* C, char Char);
static void EatPastCharacter(tokenizer* T, char Char);
static char* EatNumber(char* C);
static void EatNumber(tokenizer* T);
// Char/Char Array
static u32 CharToUInt (char C);
static s32 CharArrayLength (char* CharArray);
static bool CharArraysEqual (char* A, s32 ALength, char* B, s32 BLength);
static bool CharArraysEqualUnsafe (char* A, char* B);
static void ReverseCharArray (char* Array, s32 Length);
#define FirstIndexOfChar(array, find) IndexOfChar(array, 0, find)
static s32 IndexOfChar (char* Array, s32 Start, char Find);
#define FastLastIndexOfChar(array, len, find) FastReverseIndexOfChar(array, len, 0, find)
static s32 FastReverseIndexOfChar (char* Array, s32 Length, s32 OffsetFromEnd, char Find);
#define LastIndexOfChar(array, find) ReverseIndexOfChar(array, 0, find)
static s32 ReverseIndexOfChar (char* Array, s32 OffsetFromEnd, char Find);
static b32 CharArrayContains(char* Array, char* CheckFor);
static b32 CharArrayContainsSafe(char* Array, s32 ArrayLength, char* CheckFor, s32 CheckForLength);
// String
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);
static bool StringEqualsCharArray (string String, char* CharArray, s32 CharArrayLength);
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* Dest, string Source);
static void ConcatString (string* Dest, string Source, s32 Length);
static void ConcatCharArrayToString (string* Dest, char* Source);
static void ConcatCharArrayToString (string* Dest, char* Source, s32 SourceLength);
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 string Substring (string* String, s32 Start, s32 End);
static string Substring (string* String, s32 Start);
static void NullTerminate (string* String);
// Parsing
static u32 ParseUnsignedInt (char* String, s32 Length);
static s32 UIntToString (u32 Int, char* String, s32 Length);
static s32 ParseSignedInt (char* String, s32 Length);
static s32 IntToString (s32 Int, char* String, s32 Length);
static s32 IntToString (s32 Int, char* String, s32 Length, s32 Offset);
static float ParseFloat (char* String, s32 Length);
static s32 FloatToString(float Float, char *String, s32 Length, s32 AfterPoint);
// Print F
#define PrintString(str, format, ...) snprintf(str.Memory, str.Max, format, __VA_ARGS__); str.Length = CharArrayLength(str.Memory);
////////////////////////////////////////////////////////////////
// String Memory Function Declarations
////////////////////////////////////////////////////////////////
static s32 CalculateSlotCountFromSize (s32 RequestedSize, s32 SlotSize);
static bool SlotsAreContiguous (slot_header* First, slot_header* Second);
static contiguous_slot_count_result CountContiguousSlots (slot_header* First);
static slot_header* GetSlotAtOffset(slot_header* First, s32 Offset);
static slot_header* InsertSlotIntoList (slot_header* NewSlot, slot_header* ListStart);
static void AllocStringFromStringArena (string* String, s32 Size, slot_arena* Storage);
static string AllocStringFromStringArena (s32 Size, slot_arena* Storage);
static void FreeToStringArena (string* String, slot_arena* Storage);
static void ReallocFromStringArena (string* String, s32 NewSize, slot_arena* Storage);
////////////////////////////////////////////////////////////////
// String Utility Functions
////////////////////////////////////////////////////////////////
#if !defined GS_LANGUAGE_H
static void
GSZeroMemory (u8* Memory, s32 Size)
{
for (int i = 0; i < Size; i++) { Memory[i] = 0; }
}
static s32
GSMin (s32 A, s32 B)
{
return (A < B ? A : B);
}
static s32
GSAbs (s32 A)
{
return (A < 0 ? -A : A);
}
static float
GSAbs (float A)
{
return (A < 0 ? -A : A);
}
static float
GSPow (float N, s32 Power)
{
float Result = N;
for(s32 i = 1; i < Power; i++) { Result *= N; }
return Result;
}
#endif
////////////////////////////////////////////////////////////////
// Init and Clear
////////////////////////////////////////////////////////////////
static void
InitializeString (string* String, char* Data, s32 DataSize)
{
String->Memory = Data;
String->Max = DataSize;
String->Length = 0;
}
static string
InitializeString (char* Data, s32 DataSize)
{
string Result = {};
Result.Memory = Data;
Result.Max = DataSize;
Result.Length = 0;
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') || 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 == '>'));
}
////////////////////////////////////////////////////////////////
// Tokenizing
////////////////////////////////////////////////////////////////
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++; }
if (*Result) { Result++; } // NOTE(Peter): eat past the newline character
return Result;
}
static void
EatToNewLine(tokenizer* T)
{
while (*T->At && !IsNewline(*T->At)) { T->At++; }
if (*T->At) { T->At++; } // NOTE(Peter): eat past the newline character
}
static char*
EatWhitespace(char* C)
{
char* Result = C;
while (*Result && IsWhitespace(*Result)) { Result++; }
return Result;
}
static void
EatWhitespace(tokenizer* T)
{
while (*T->At && IsWhitespace(*T->At)) { T->At++; }
}
static char*
EatToWhitespace(char* C)
{
char* Result = C;
while (*Result && !IsWhitespace(*Result)) { Result++; }
return Result;
}
static void
EatToWhitespace(tokenizer* T)
{
while (*T->At && !IsWhitespace(*T->At)) { T->At++; }
}
static char*
EatToCharacter(char* C, char Char)
{
char* Result = C;
while (*Result && *Result != Char) { Result++; }
return Result;
}
static void
EatToCharacter(tokenizer* T, char Char)
{
while (*T->At && *T->At != Char) { T->At++; }
}
static char*
EatPastCharacter(char* C, char Char)
{
char* Result = EatToCharacter(C, Char);
if (*Result && *Result == Char) { Result++; }
return Result;
}
static void
EatPastCharacter(tokenizer* T, char Char)
{
EatToCharacter(T, Char);
if (*T->At && *T->At == Char) { T->At++; }
}
static char*
EatNumber(char* C)
{
char* Result = C;
while (*Result && IsNumericExtended(*Result)) { Result++; }
return Result;
}
static void
EatNumber(tokenizer* T)
{
while (*T->At && IsNumericExtended(*T->At)) { T->At++; }
}
////////////////////////////////////////////////////////////////
// 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)
{
bool Result = true;
char* S = String.Memory;
char* C = CharArray;
s32 Count = 0;
while (*C && Count < String.Length)
{
if (*C++ != *S++)
{
Result = false;
break;
}
Count++;
}
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 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* Dest, string Source)
{
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* Dest, string Source, s32 Length)
{
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
ConcatCharArrayToString (string* Dest, char* Source)
{
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 (string* Dest, char* Source, s32 SourceLength)
{
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++;
}
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++;
}
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;
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 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 void
NullTerminate (string* String)
{
Assert(String->Length + 1 <= String->Max);
*(String->Memory + String->Length) = 0;
String->Length++;
}
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
////////////////////////////////////////////////////////////////
static u32
ParseUnsignedInt (char* String, s32 Length)
{
Assert(IsNumeric(*String));
char* Iter = String;
u32 Result = 0;
for (s32 i = 0; i < Length; i++)
{
Result = CharToUInt(*Iter++) + (Result * 10);
}
return Result;
}
static u32
ParseUnisgnedIntUnsafe (char* String)
{
char* Start = String;
char* End = EatNumber(String + 1);
return ParseUnsignedInt(String, End - Start);
}
static s32
UIntToString (u32 Int, char* String, s32 Length)
{
s32 Remaining = Int;
s32 CharsCopied = 0;
char* Iter = String;
while (Remaining > 0 && CharsCopied < Length)
{
*Iter++ = '0' + (Remaining % 10);
Remaining /= 10;
CharsCopied++;
}
ReverseCharArray(String, CharsCopied);
return CharsCopied;
}
static s32
ParseSignedInt (char* String, s32 Length)
{
Assert(Length > 0);
s32 Negative = 1;
s32 LengthRemaining = Length;
s32 Result = 0;
char* Iter = String;
if (*Iter == '-') {
LengthRemaining--;
*Iter++;
Negative = -1;
}
for (s32 i = 0; i < LengthRemaining; i++)
{
Result = CharToUInt(*Iter++) + (Result * 10);
}
Result *= Negative;
return Result;
}
static s32
ParseSignedIntUnsafe (char* String)
{
char* Start = String;
char* End = EatNumber(String + 1);
return ParseSignedInt(String, End - Start);
}
static s32
IntToString (s32 Int, char* String, s32 Length)
{
s32 Remaining = Int;
s32 CharsCopied = 0;
char* Iter = String;
bool Negative = Remaining < 0;
Remaining = GSAbs(Remaining);
while (Remaining > 0 && CharsCopied < Length)
{
*Iter++ = '0' + (Remaining % 10);
Remaining /= 10;
CharsCopied++;
}
if (Negative)
{
*Iter++ = '-';
CharsCopied++;
}
ReverseCharArray(String, CharsCopied);
return CharsCopied;
}
static s32
IntToString (s32 Int, char* String, s32 Length, s32 Offset)
{
char* StringStart = String + Offset;
s32 LengthWritten = IntToString(Int, StringStart, Length - Offset);
return LengthWritten;
}
static float
ParseFloat (char* String, s32 Length)
{
s32 Negative = 1;
s32 LengthRemaining = Length;
float Result = 0;
char* Iter = String;
if (*Iter == '-') {
LengthRemaining--;
*Iter++;
Negative = -1;
}
for (s32 i = 0; i < LengthRemaining; i++)
{
if (IsNumeric(*Iter))
{
Result = (float)CharToUInt(*Iter++) + (Result * 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);
Result += AfterPoint;
}
Result *= Negative;
return Result;
}
static float
ParseFloatUnsafe (char* String)
{
char* Start = String;
char* End = EatNumber(String + 1);
return ParseFloat(String, End - Start);
}
static s32
FloatToString(float Float, char *String, s32 Length, s32 AfterPoint)
{
s32 IPart = (s32)Float;
float FPart = GSAbs(Float - (float)IPart);
s32 i = IntToString(IPart, String, Length);
if (AfterPoint > 1)
{
String[i++] = '.';
s32 FPartInt = FPart * GSPow(10, AfterPoint);
i += IntToString(FPartInt, String, Length, i);
}
return i;
}
////////////////////////////////////////////////////////////////
// PrintF
////////////////////////////////////////////////////////////////
internal s32
PrintStringF(char* Format, char* Destination, s32 DestLength, ...)
{
va_list Args;
va_start(Args, DestLength);
s32 LengthPrinted = 0;
char* DestChar = Destination;
char* C = Format;
while(*C)
{
if (*C == '%')
{
C++;
if (*C == 's') // string
{
C++;
char* InsertString = va_arg(Args, char*);
s32 LengthCopied = CopyCharArray(InsertString, DestChar, DestLength - LengthPrinted);
DestChar += LengthCopied;
LengthPrinted += LengthCopied;
}
else if(*C == 'd') // integer
{
C++;
s32 Integer = va_arg(Args, s32);
s32 IntLength = IntToString(Integer, DestChar, DestLength - LengthPrinted);
DestChar += IntLength;
LengthPrinted += IntLength;
}
else if (*C == 'f')
{
C++;
r32 Float = va_arg(Args, r32);
s32 FloatLength = FloatToString(Float, DestChar, DestLength - LengthPrinted, 5);
DestChar += FloatLength;
LengthPrinted += FloatLength;
}
else
{
*DestChar++ = *C++;
}
}
else
{
*DestChar++ = *C++;
}
}
if (LengthPrinted >= DestLength)
{
LengthPrinted = DestLength - 1;
}
*(Destination + LengthPrinted) = 0;
va_end(Args);
return LengthPrinted;
}
////////////////////////////////////////////////////////////////
// String Memory Function Definitions
////////////////////////////////////////////////////////////////
static s32
CalculateSlotCountFromSize (s32 RequestedSize, s32 SlotSize)
{
s32 SlotCount = RequestedSize / SlotSize;
if (SlotCount * SlotSize < RequestedSize)
{
SlotCount += 1;
}
return SlotCount;
}
static bool
SlotsAreContiguous (slot_header* First, slot_header* Second)
{
bool Result = false;
u8* FirstSlotNextAddress = (u8*)First + First->Size;
u8* SecondAddress = (u8*)Second;
Result = FirstSlotNextAddress == SecondAddress;
return Result;
}
static contiguous_slot_count_result
CountContiguousSlots (slot_header* First)
{
Assert(First != 0);
contiguous_slot_count_result Result = {};
Result.Count = 1;
slot_header* IterPrev = First;
slot_header* Iter = First->Next;
while (Iter && SlotsAreContiguous(IterPrev, Iter))
{
Result.Count++;
IterPrev = Iter;
Iter = Iter->Next;
}
Result.LastContiguousSlot = IterPrev;
return Result;
}
static slot_header*
GetSlotAtOffset(slot_header* First, s32 Offset)
{
slot_header* Iter = First;
s32 Count = 0;
while (Count < Offset && Iter)
{
Iter = Iter->Next;
Count++;
}
return Iter;
}
static slot_header*
InsertSlotIntoList (slot_header* NewSlot, slot_header* ListStart)
{
slot_header* List = ListStart;
if (NewSlot < List)
{
NewSlot->Next = List;
List = NewSlot;
}
else
{
slot_header* PrevIter = List;
slot_header* Iter = List->Next;
while (Iter && NewSlot > Iter)
{
PrevIter = Iter;
Iter = Iter->Next;
}
Assert(PrevIter);
if (PrevIter)
{
PrevIter->Next = NewSlot;
}
if (Iter)
{
NewSlot->Next = Iter;
}
}
return List;
}
static void
AllocStringFromStringArena (string* String, s32 Size, slot_arena* Storage)
{
s32 SlotCount = CalculateSlotCountFromSize(Size, Storage->SlotSize);
slot_header* Slot = Storage->FreeList;
slot_header* PrevSlot = 0;
while (Slot)
{
contiguous_slot_count_result ContiguousSlots = CountContiguousSlots(Slot);
if (ContiguousSlots.Count >= SlotCount)
{
slot_header* NextStartSlot = GetSlotAtOffset(Slot, SlotCount);
if (PrevSlot)
{
PrevSlot->Next = NextStartSlot;
}
else
{
Storage->FreeList = NextStartSlot;
}
break;
}
else
{
PrevSlot = Slot;
Slot = Slot->Next;
}
}
if (Slot)
{
String->Memory = (char*)Slot;
GSZeroMemory((u8*)String->Memory, SlotCount * Storage->SlotSize);
String->Max = SlotCount * Storage->SlotSize;
String->Length = 0;
}
}
static string
AllocStringFromStringArena (s32 Size, slot_arena* Storage)
{
string Result = {0};
AllocStringFromStringArena(&Result, Size, Storage);
return Result;
}
static void
FreeToStringArena (string* String, slot_arena* Storage)
{
u8* Base = (u8*)(String->Memory);
u8* End = Base + String->Max - 1;
u8* MemoryEnd = Storage->Memory + (Storage->SlotSize * Storage->SlotCount);
Assert((Base >= Storage->Memory) && (End < MemoryEnd));
Assert((String->Max % Storage->SlotSize) == 0);
s32 SizeReclaimed = 0;
slot_header* Slot = (slot_header*)Base;
while (SizeReclaimed < String->Max)
{
Slot->Size = Storage->SlotSize;
Storage->FreeList = InsertSlotIntoList(Slot, Storage->FreeList);
SizeReclaimed += Storage->SlotSize;
Slot = (slot_header*)(Base + SizeReclaimed);
}
String->Memory = 0;
String->Length = 0;
String->Max = 0;
}
static void
ReallocFromStringArena (string* String, s32 NewSize, slot_arena* Storage)
{
string NewString = AllocStringFromStringArena(NewSize, Storage);
CopyStringTo(*String, &NewString);
FreeToStringArena(String, Storage);
*String = NewString;
}
#if defined(DEBUG)
void DEBUGPrintChars (string* String, s32 Count)
{
char* Iter = String->Memory;
for (int i = 0; i < Count; i++)
{
*Iter++ = (char)('A' + i);
}
String->Length = Count;
}
#ifdef DEBUG_GS_STRING
#include <stdlib.h>
static void
TestStrings()
{
slot_arena StringArena = {};
s32 TestCount = 0;
s32 SuccessCount = 0;
DebugPrint("\n\n-------------------------------------------------\n Begin Testing Strings\n\n\n");
////////////////////////////////////////////////////////////////
// Char Functions
char ForwardArray[] = "Hello, Sailor";
char BackwardArray[] = "roliaS ,olleH";
TestClean(CharArraysEqual(ForwardArray, 13, ForwardArray, 13), "String Equality");
TestClean(!CharArraysEqual(ForwardArray, 13, BackwardArray, 13), "String Equality");
TestClean(IndexOfChar(ForwardArray, 0, ',') == 5, "Index Of Char");
TestClean(IndexOfChar(ForwardArray, 5, 'l') == 10, "Index of Char (skipping first 5)");
TestClean(FastReverseIndexOfChar(ForwardArray, 13, 0, 'o') == 11, "Fast Reverse Index Of Char");
TestClean(ReverseIndexOfChar(ForwardArray, 0, 'o') == 11, "Reverse Index of Char");
TestClean(ReverseIndexOfChar(ForwardArray, 3, 'o') == 4, "Reverse Index of Char (skipping last 3)");
TestClean(LastIndexOfChar(ForwardArray, 'o') == 11, "Last Index of Char");
ReverseCharArray(ForwardArray, 13);
TestClean(CharArraysEqual(ForwardArray, 13, BackwardArray, 13), "Reversing Char Array");
char UIntString[] = "1234";
u32 UIntValue = 1234;
u32 ParsedUInt = ParseUnsignedInt(UIntString, 4);
TestClean((ParsedUInt == UIntValue), "String To U32");
char StringifiedUInt[4] = {};
UIntToString(UIntValue, StringifiedUInt, 4);
TestClean(CharArraysEqual(UIntString, 4, StringifiedUInt, 4), "U32 To String");
char IntString[] = "-1234";
s32 IntValue = -1234;
s32 ParsedInt = ParseSignedInt(IntString, 5);
TestClean((ParsedInt == IntValue), "String To S32");
char StringifiedInt[5] = {};
IntToString(IntValue, StringifiedInt, 5);
TestClean(CharArraysEqual(IntString, 5, StringifiedInt, 5), "S32 to String");
char FloatString[] = "-1234.125";
float FloatValue = -1234.125f;
float ParsedFloat = ParseFloat(FloatString, 8);
TestClean((ParsedFloat == FloatValue), "String To Float");
char StringifiedFloat[10] = {};
FloatToString(FloatValue, StringifiedFloat, 10, 3);
TestClean(CharArraysEqual(FloatString, 8, StringifiedFloat, 8), "Float To String");
////////////////////////////////////////////////////////////////
//
StringArena.SlotSize = 256;
StringArena.SlotCount = 32;
StringArena.Memory = malloc(StringArena.SlotSize * StringArena.SlotCount);
slot_header* PrevSlotHeader = 0;
for (int i = StringArena.SlotCount - 1; i >= 0; i--)
{
u8* SlotBase = StringArena.Memory + (i * StringArena.SlotSize);
slot_header* SlotHeader = (slot_header*)SlotBase;
SlotHeader->Size = StringArena.SlotSize;
SlotHeader->Next = PrevSlotHeader;
// TEST(peter): Should be true always, except on the first iteration, when there is no next slot
bool Contiguity = SlotsAreContiguous(SlotHeader, PrevSlotHeader);
TestClean((Contiguity || SlotHeader->Next == 0), "Contiguous Arenas");
PrevSlotHeader = SlotHeader;
}
StringArena.FreeList = PrevSlotHeader;
// TEST(peter): Count Should equal StringArena.SlotCount
s32 ContiguousSlotsCountBefore = CountContiguousSlots(StringArena.FreeList).Count;
TestClean((ContiguousSlotsCountBefore == StringArena.SlotCount), "Contiguous Arenas");
// TEST(peter): Should be false
bool Contiguity = SlotsAreContiguous(StringArena.FreeList, StringArena.FreeList->Next->Next);
Contiguity = SlotsAreContiguous(StringArena.FreeList->Next->Next, StringArena.FreeList);
TestClean(!Contiguity, "Non Contiguous Arenas");
s32 Slots = CalculateSlotCountFromSize(10, 256);
TestClean(Slots == 1, "Slot Sizing");
Slots = CalculateSlotCountFromSize(256, 256);
TestClean(Slots == 1, "Slot Sizing");
Slots = CalculateSlotCountFromSize(345, 256);
TestClean(Slots == 2, "Slot Sizing");
Slots = CalculateSlotCountFromSize(1024, 256);
TestClean(Slots == 4, "Slot Sizing");
slot_header* HeaderTen = GetSlotAtOffset(StringArena.FreeList, 10);
slot_header* HeaderThree = GetSlotAtOffset(StringArena.FreeList, 3);
slot_header* HeaderFive = GetSlotAtOffset(StringArena.FreeList, 5);
string StringA = AllocStringFromStringArena(10, &StringArena);
string StringB = AllocStringFromStringArena(345, &StringArena);
#if 0
// TEST(peter): Should TestClean
u8* RandomMemory = (u8*)malloc(256);
string RandomMemString = {};
RandomMemString.Memory = (char*)RandomMemory;
RandomMemString.Max = 256;
FreeToStringArena(&RandomMemString, &StringArena);
#endif
FreeToStringArena(&StringA, &StringArena);
FreeToStringArena(&StringB, &StringArena);
// TEST(peter): After freeing both allocations, ContiguousSlotCountBefore and ContiguousSlotCountAfter should be equal
s32 ContiguousSlotCountAfter = CountContiguousSlots(StringArena.FreeList).Count;
TestClean(ContiguousSlotCountAfter == ContiguousSlotsCountBefore, "Add and REmove Slots from Arena");
// TEST(peter): Set up a free list where the first element is too small, so it has to traverse to find an appropriately
// sized block
// The slots will look list [256][used][256][256][256] etc..
StringA = AllocStringFromStringArena(256, &StringArena);
StringB = AllocStringFromStringArena(256, &StringArena);
FreeToStringArena(&StringA, &StringArena);
u32 Contiguous = CountContiguousSlots(StringArena.FreeList).Count; // Should = 1;
string StringC = AllocStringFromStringArena(512, &StringArena);
slot_header* HeaderC = (slot_header*)(StringC.Memory);
string ReallocTestString = AllocStringFromStringArena(256, &StringArena);
DEBUGPrintChars(&ReallocTestString, 24);
ReallocFromStringArena(&ReallocTestString, 512, &StringArena);
string TestString = AllocStringFromStringArena(10, &StringArena);
DEBUGPrintChars(&TestString, TestString.Max);
ReallocFromStringArena(&TestString, 20, &StringArena);
DEBUGPrintChars(&TestString, TestString.Max);
ReallocFromStringArena(&TestString, 10, &StringArena);
FreeToStringArena(&TestString, &StringArena);
string EqualityStringA = AllocStringFromStringArena(345, &StringArena);
string EqualityStringB = AllocStringFromStringArena(415, &StringArena);
// Equality should succeed despite length differences
string EqualityStringC = AllocStringFromStringArena(256, &StringArena);
string EqualityStringD = AllocStringFromStringArena(256, &StringArena); // Equality should fail
string EqualityStringEmpty = {};
DEBUGPrintChars(&EqualityStringA, 24);
DEBUGPrintChars(&EqualityStringB, 24);
DEBUGPrintChars(&EqualityStringC, 24);
DEBUGPrintChars(&EqualityStringD, 12);
bool ABEquality = StringsEqual(EqualityStringA, EqualityStringB); // Should Succeed
bool ACEquality = StringsEqual(EqualityStringA, EqualityStringC); // Should Succeed
bool ADEquality = StringsEqual(EqualityStringA, EqualityStringD); // Should Fail
bool AEEquality = StringsEqual(EqualityStringA, EqualityStringEmpty); // Should Fail
TestClean(ABEquality, "String Equality");
TestClean(ACEquality, "String Equality");
TestClean(!ADEquality, "String Equality");
TestClean(!AEEquality, "String Equality");
string CatStringA = AllocStringFromStringArena(256, &StringArena);
SetStringToCharArray(&CatStringA, "Hello ");
string CatStringB = AllocStringFromStringArena(512, &StringArena);
SetStringToCharArray(&CatStringB, "Sailor!");
string CatStringResult = AllocStringFromStringArena(512, &StringArena);
SetStringToCharArray(&CatStringResult, "Hello Sailor!");
ConcatString(&CatStringA, CatStringB);
TestClean(StringsEqual(CatStringA, CatStringResult), "Cat Strings");
s32 FirstSpaceIndex = FindFirstChar(CatStringA, ' ');
TestClean(FirstSpaceIndex == 5, "First Index");
SetStringToChar(&CatStringB, 'B', 5);
TestClean(StringEqualsCharArray(CatStringB, "BBBBB"), "SetStringToChar");
DebugPrint("Results: Passed %d / %d\n\n\n", SuccessCount, TestCount);
}
#endif // DEBUG_GS_STRING
#endif // DEBUG