Lumenarium/gs_libs/gs_string.h

2390 lines
60 KiB
C

// TODO
/*
- InsertCharArrayIntoStringAt
- AppendChar
*/
#ifndef GS_STRING_H
#include <stdarg.h>
////////////////////////////////////////////////////////////////
// String
////////////////////////////////////////////////////////////////
struct string
{
char* Memory;
s32 Length;
s32 Max;
};
////////////////////////////////////////////////////////////////
// String Tokenizing
////////////////////////////////////////////////////////////////
struct tokenizer
{
char* At;
char* LineStart;
char* Memory;
s32 MemoryLength;
u32 LineNumber;
};
enum token_type
{
Token_Error,
Token_LeftParen,
Token_RightParen,
Token_LeftSquareBracket,
Token_RightSquareBracket,
Token_LeftCurlyBracket,
Token_RightCurlyBracket,
Token_Semicolon,
Token_Operator,
Token_Comma,
Token_Period,
Token_PointerReference,
Token_PoundDefine, // Must stay first preproc token type
Token_PoundUndef,
Token_PoundInclude,
Token_PoundIfDef,
Token_PoundIfNDef,
Token_PoundIf,
Token_PoundElif,
Token_PoundElse,
Token_PoundEndif,
Token_PoundError,
Token_PoundPragma,
Token_PoundLine, // Must stay Last Preproc Token Type
Token_Number,
Token_Char,
Token_String,
Token_Identifier,
Token_Comment,
Token_MultilineComment,
Token_EndOfLine,
Token_Unknown,
Token_EndOfStream,
};
char* TokenNames[] = {
"Token_Error",
"Token_LeftParen",
"Token_RightParen",
"Token_LeftSquareBracket",
"Token_RightSquareBracket",
"Token_LeftCurlyBracket",
"Token_RightCurlyBracket",
"Token_Semicolon",
"Token_Operator",
"Token_Comma",
"Token_Period",
"Token_PointerReference",
"Token_PoundDefine",
"Token_PoundUndef",
"Token_PoundInclude",
"Token_PoundIfDef",
"Token_PoundIfNDef",
"Token_PoundIf",
"Token_PoundElif",
"Token_PoundElse",
"Token_PoundEndif",
"Token_PoundError",
"Token_PoundPragma",
"Token_PoundLine",
"Token_Number",
"Token_Char",
"Token_String",
"Token_Identifier",
"Token_Comment",
"Token_MultilineComment",
"Token_EndOfLine",
"Token_Unknown",
"Token_EndOfStream",
};
struct token
{
token_type Type;
u32 LineNumber;
string Text;
token* Next; // TODO(Peter): Get rid of this
};
////////////////////////////////////////////////////////////////
// String Memory
////////////////////////////////////////////////////////////////
struct slot_header
{
slot_header* Next;
s32 Size;
};
struct slot_arena
{
u8* Memory;
s32 SlotSize;
s32 SlotCount;
slot_header* FreeList;
};
struct contiguous_slot_count_result
{
s32 Count;
slot_header* LastContiguousSlot;
};
////////////////////////////////////////////////////////////////
// String Function Declarations
////////////////////////////////////////////////////////////////
// Utility
#if !defined GS_LANGUAGE_H
static void GSZeroMemory (u8* Memory, s32 Size);
static s32 GSMin (s32 A, s32 B);
static s32 GSAbs (s32 A);
static float GSAbsF (float A);
static float GSPowF (float N, s32 Power);
#endif
// Setup
#ifdef GS_MEMORY_H
#define PushString(str, arena, size) (str)->Memory = PushArray(arena, char, size); (str)->Length = 0; (str)->Max = size;
#endif
static void InitializeEmptyString (string* String, char* Data, s32 DataSize);
static void InitializeString(string* String, char* Data, s32 Used, s32 Max);
static string InitializeEmptyString (char* Data, s32 DataSize);
static string InitializeString (char* Data, s32 Used, s32 Max);
static void ClearString (string* String);
// Character Values
static bool IsSlash (char C);
static bool IsNewline (char C);
static bool IsWhitespace (char C);
static bool IsNewlineOrWhitespace (char C);
static bool IsAlpha (char C);
static bool IsUpper (char C);
static bool IsLower (char C);
static char ToUpper (char C);
static char ToLower (char C);
static bool IsNumeric (char C);
static bool IsNumericExtended (char C);
static bool IsAlphaNumeric (char C);
static bool IsOperator (char C);
static bool CharsEqualCaseInsensitive(char A, char B);
// Tokenizing
static void EatChar(tokenizer* T);
static b32 AtValidPosition(tokenizer Tokenizer);
static b32 AtValidToken(tokenizer Tokenizer);
static char* EatToNewLine(char* C);
static s32 EatToNewLine(tokenizer* T);
static char* EatPastNewLine(char* C);
static s32 EatPastNewLine(tokenizer* T);
static char* EatWhitespace(char* C);
static s32 EatWhitespace(tokenizer* T);
static char* EatToNonWhitespaceOrNewline(char* C);
static s32 EatToNonWhitespaceOrNewline(tokenizer* T);
static char* EatToWhitespace(char* C);
static s32 EatToWhitespace(tokenizer* T);
static char* EatToCharacter(char* C, char Char);
static s32 EatToCharacter(tokenizer* T, char Char);
static char* EatPastCharacter(char* C, char Char);
static s32 EatPastCharacter(tokenizer* T, char Char);
static char* EatNumber(char* C);
static s32 EatNumber(tokenizer* T);
// Char/Char Array
static u32 CharToUInt (char C);
static s32 CharArrayLength (char* CharArray);
static bool CharArraysEqual (char* A, s32 ALength, char* B, s32 BLength);
static bool CharArraysEqualUnsafe (char* A, char* B);
static void ReverseCharArray (char* Array, s32 Length);
#define FirstIndexOfCharInCharArray(array, find) IndexOfChar(array, 0, find)
static s32 IndexOfChar (char* Array, s32 Start, char Find);
#define FastLastIndexOfCharInCharArray(array, len, find) FastReverseIndexOfChar(array, len, 0, find)
static s32 FastReverseIndexOfChar (char* Array, s32 Length, s32 OffsetFromEnd, char Find);
#define LastIndexOfCharInCharArray(array, find) ReverseIndexOfChar(array, 0, find)
static s32 ReverseIndexOfChar (char* Array, s32 OffsetFromEnd, char Find);
static b32 CharArrayContains(char* Array, char* CheckFor);
static b32 CharArrayContainsSafe(char* Array, s32 ArrayLength, char* CheckFor, s32 CheckForLength);
// String
#define MakeStringBuffer(name, size) \
char name##Backbuffer[(size)]; \
string name = MakeString(name##Backbuffer, size);
static string MakeString (char* Array, s32 Length, s32 Max);
static string MakeString (char* Array, s32 Length);
static string MakeString (char* Array);
static string MakeStringLiteral(char* Data);
static bool StringsEqual (string A, string B);
static bool StringEqualsCharArray (string String, char* CharArray, s32 CharArrayLength);
static bool StringEqualsCharArray (string String, char* CharArray);
static s32 FindFirstChar (string String, char C);
static void SetStringToChar (string* Dest, char C, s32 Count);
static void SetStringToCharArray (string* Dest, char* Source);
static void ConcatString (string Source, string* Dest);
static void ConcatString (string Source, s32 Length, string* Dest);
static void ConcatCharToString(string* Dest, char C);
static void ConcatCharArrayToString (char* Source, string* Dest);
static void ConcatCharArrayToString (char* Source, s32 SourceLength, string* Dest);
static void CopyStringTo (string Source, string* Dest);
static s32 CopyStringToCharArray (string Source, char* Dest, s32 DestLength);
static void CopyCharArrayToString (char* Src, string* Dest);
static void CopyCharArrayToString (char* Src, s32 SrcLength, string* Dest);
static s32 CopyCharArray (char* Source, char* Dest, s32 DestLength);
static s32 CopyCharArrayAt (char* Source, char* Dest, s32 DestLength, s32 Offset);
static void InsertChar (string* String, char Char, s32 Index);
static void InsertStringAt (string* Dest, string Source, s32 At);
static void RemoveCharAt (string* String, s32 Index);
static s32 IndexOfChar(string String, char C);
static s32 LastIndexOfChar(string String, char C);
static s32 SearchForCharInSet(string String, char* Set);
static s32 ReverseSearchForCharInSet(string String, char* Set);
static string Substring (string* String, s32 Start, s32 End);
static string Substring (string* String, s32 Start);
static b32 StringContainsCharArray(string SearchIn, char* SearchFor, s32 SearchForLength);
static b32 StringContainsString(string SearchIn, string SearchFor);
static b32 StringContainsCharArrayCaseInsensitive(string SearchIn, char* SearchFor, s32 SearchForLength);
static b32 StringContainsStringCaseInsensitive(string SearchIn, string SearchFor);
static void NullTerminate (string* String);
static u32 HashString(string String);
// Parsing
enum parse_type
{
ParseType_UnsignedInt,
ParseType_SignedInt,
ParseType_Float,
};
struct parse_result
{
parse_type Type;
char* OnePastLast;
union
{
u32 UnsignedIntValue;
s32 SignedIntValue;
r32 FloatValue;
};
};
enum format_flags
{
FormatFlags_LeftJustify = 0x1,
FormatFlags_ForceSign = 0x2,
FormatFlags_ForceSpaceInsteadOfSign = 0x4,
FormatFlags_ForceDecimalOrPrependOx = 0x8,
FormatFlags_PadWithZeroesInsteadOfSpaces = 0x16,
};
static parse_result ParseUnsignedInt (s32 Length, char* String);
static parse_result ParseSignedInt (s32 Length, char* String);
static parse_result ParseFloat (s32 Length, char* String);
// PrintF
#define StringExpand(str) (str).Length, (str).Memory
static void PrintFArgList(char* Dest, s32 DestMax, char* Format, va_list Args);
static void PrintF(string* String, char* Format, ...);
////////////////////////////////////////////////////////////////
// String Memory Function Declarations
////////////////////////////////////////////////////////////////
static s32 CalculateSlotCountFromSize (s32 RequestedSize, s32 SlotSize);
static bool SlotsAreContiguous (slot_header* First, slot_header* Second);
static contiguous_slot_count_result CountContiguousSlots (slot_header* First);
static slot_header* GetSlotAtOffset(slot_header* First, s32 Offset);
static slot_header* InsertSlotIntoList (slot_header* NewSlot, slot_header* ListStart);
static void AllocStringFromStringArena (string* String, s32 Size, slot_arena* Storage);
static string AllocStringFromStringArena (s32 Size, slot_arena* Storage);
static void FreeToStringArena (string* String, slot_arena* Storage);
static void ReallocFromStringArena (string* String, s32 NewSize, slot_arena* Storage);
////////////////////////////////////////////////////////////////
// String Utility Functions
////////////////////////////////////////////////////////////////
#if !defined GS_LANGUAGE_H
static void
GSZeroMemory (u8* Memory, s32 Size)
{
for (int i = 0; i < Size; i++) { Memory[i] = 0; }
}
static s32
GSMin (s32 A, s32 B)
{
return (A < B ? A : B);
}
static s32
GSAbs (s32 A)
{
return (A < 0 ? -A : A);
}
static float
GSAbs (float A)
{
return (A < 0 ? -A : A);
}
static float
GSPow (float N, s32 Power)
{
float Result = N;
for(s32 i = 1; i < Power; i++) { Result *= N; }
return Result;
}
#endif
////////////////////////////////////////////////////////////////
// Init and Clear
////////////////////////////////////////////////////////////////
static void
InitializeEmptyString (string* String, char* Data, s32 DataSize)
{
String->Memory = Data;
String->Max = DataSize;
String->Length = 0;
}
static void
InitializeString(string* String, char* Data, s32 Used, s32 Max)
{
String->Memory = Data;
String->Max = Max;
String->Length = Used;
}
static string
InitializeEmptyString (char* Data, s32 DataSize)
{
string Result = {};
Result.Memory = Data;
Result.Max = DataSize;
Result.Length = 0;
return Result;
}
static string
InitializeString (char* Data, s32 Used, s32 Max)
{
string Result = {};
Result.Memory = Data;
Result.Max = Max;
Result.Length = Used;
return Result;
}
static void
ClearString (string* String)
{
String->Memory = 0;
String->Max = 0;
String->Length = 0;
}
////////////////////////////////////////////////////////////////
// Char Value Types
////////////////////////////////////////////////////////////////
static bool IsSlash (char C) { return ((C == '\\') || (C == '/')); }
static bool IsNewline (char C) { return (C == '\n') || (C == '\r'); }
static bool IsWhitespace (char C) { return (C == ' ') || (C == '\t'); }
static bool IsNewlineOrWhitespace (char C) { return (IsWhitespace(C) || IsNewline(C)); }
static bool IsAlpha (char C)
{
// TODO(Peter): support UTF8 chars
return ((C >= 'A') && (C <= 'Z')) || ((C >= 'a') && (C <= 'z')) || (C == '_');
}
static bool IsUpper (char C)
{
return ((C >= 'A') && (C <= 'Z'));
}
static bool IsLower (char C)
{
return ((C >= 'a') && (C <= 'z'));
}
static bool IsNumeric (char C)
{
return (C >= '0') && (C <= '9');
}
static bool IsNumericExtended (char C)
{
return (IsNumeric(C) || (C == 'x') || (C == 'f') || (C == '.'));
}
static bool IsAlphaNumeric (char C)
{
return IsAlpha(C) || IsNumeric(C);
}
static bool IsOperator (char C)
{
return ((C == '+') ||
(C == '-') ||
(C == '*') ||
(C == '/') ||
(C == '=') ||
(C == '%') ||
(C == '<') ||
(C == '>'));
}
static char ToUpper (char A)
{
char Result = A;
if (IsLower(A))
{
Result += 'A' - 'a';
}
return Result;
}
static char ToLower (char A)
{
char Result = A;
if (IsUpper(A))
{
Result -= 'A' - 'a';
}
return Result;
}
static bool CharsEqualCaseInsensitive (char A, char B)
{
b32 Result = (ToLower(A) == ToLower(B));
return Result;
}
////////////////////////////////////////////////////////////////
// Tokenizing
////////////////////////////////////////////////////////////////
static void
EatChar (tokenizer* T)
{
if (AtValidPosition(*T))
{
if (IsNewline(*T->At))
{
T->LineNumber++;
T->At++;
T->LineStart = T->At;
}
else
{
T->At++;
}
}
}
static b32
AtValidPosition (tokenizer Tokenizer)
{
b32 Result = (Tokenizer.At - Tokenizer.Memory) <= Tokenizer.MemoryLength;
return Result;
}
static b32
AtValidToken(tokenizer Tokenizer)
{
b32 Result = *Tokenizer.At && Tokenizer.At < (Tokenizer.Memory + Tokenizer.MemoryLength);
return Result;
}
static char*
EatToNewLine(char* C)
{
char* Result = C;
while (*Result && !IsNewline(*Result))
{
Result++;
}
return Result;
}
static s32
EatToNewLine(tokenizer* T)
{
char* TStart = T->At;
while (AtValidPosition(*T) && !IsNewline(*T->At))
{
EatChar(T);
}
return T->At - TStart;
}
static char*
EatPastNewLine(char* C)
{
char* Result = EatToNewLine(C);
while(*Result && IsNewline(*Result))
{
Result++;
}
return Result;
}
static s32
EatPastNewLine(tokenizer* T)
{
char* TStart = T->At;
EatToNewLine(T);
while(AtValidPosition(*T) && IsNewline(*T->At))
{
EatChar(T);
}
return T->At - TStart;
}
static char*
EatWhitespace(char* C)
{
char* Result = C;
while (*Result && IsNewlineOrWhitespace(*Result)) { Result++; }
return Result;
}
static s32
EatWhitespace(tokenizer* T)
{
char* TStart = T->At;
while (AtValidPosition(*T) && IsNewlineOrWhitespace(*T->At)) { EatChar(T); }
return T->At - TStart;
}
static char*
EatToNonWhitespaceOrNewline(char* C)
{
char* Result = C;
while (*Result && IsWhitespace(*Result)) { Result++; }
return Result;
}
static s32
EatToNonWhitespaceOrNewline(tokenizer* T)
{
char* TStart = T->At;
while (AtValidPosition(*T) && IsWhitespace(*T->At)) { EatChar(T); }
return T->At - TStart;
}
static char*
EatToWhitespace(char* C)
{
char* Result = C;
while (*Result && !IsWhitespace(*Result)) { Result++; }
return Result;
}
static s32
EatToWhitespace(tokenizer* T)
{
char* TStart = T->At;
while (AtValidPosition(*T) && !IsWhitespace(*T->At)) { EatChar(T); }
return T->At - TStart;
}
static char*
EatToCharacter(char* C, char Char)
{
char* Result = C;
while (*Result && *Result != Char) { Result++; }
return Result;
}
static s32
EatToCharacter(tokenizer* T, char Char)
{
char* TStart = T->At;
while (AtValidPosition(*T) && *T->At != Char) { EatChar(T); }
return T->At - TStart;
}
static char*
EatPastCharacter(char* C, char Char)
{
char* Result = EatToCharacter(C, Char);
if (*Result && *Result == Char) { Result++; }
return Result;
}
static s32
EatPastCharacter(tokenizer* T, char Char)
{
char* TStart = T->At;
EatToCharacter(T, Char);
if (AtValidPosition(*T) && *T->At == Char) { EatChar(T); }
return T->At - TStart;
}
static char*
EatNumber(char* C)
{
char* Result = C;
while (*Result && IsNumericExtended(*Result)) { Result++; }
return Result;
}
static s32
EatNumber(tokenizer* T)
{
char* TStart = T->At;
while (AtValidPosition(*T) && IsNumericExtended(*T->At)) { EatChar(T); }
return T->At - TStart;
}
////////////////////////////////////////////////////////////////
// Basic Char Operations
////////////////////////////////////////////////////////////////
static u32 CharToUInt (char C) {
u32 Result = (C - '0');
return Result;
}
static s32
CharArrayLength (char* Array)
{
char* C = Array;
s32 Result = 0;
while (*C)
{
*C++;
Result++;
}
return Result;
}
static s32
NullTerminatedCharArrayLength (char* CharArray)
{
char* Iter = CharArray;
while (*Iter)
{
*Iter++;
}
return (Iter - CharArray);
}
static bool
CharArraysEqual (char* A, s32 ALength, char* B, s32 BLength)
{
bool Result = false;
if (ALength == BLength)
{
Result = true;
char* AIter = A;
char* BIter = B;
for (s32 i = 0; i < ALength; i++)
{
if(*AIter++ != *BIter++)
{
Result = false;
break;
}
}
}
return Result;
}
static bool
CharArraysEqualUnsafe (char* A, char* B)
{
bool Result = true;
char* AIter = A;
char* BIter = B;
while(*AIter && *BIter)
{
if(*AIter++ != *BIter++)
{
Result = false;
break;
}
}
if((*AIter && !*BIter) || (!*AIter && *BIter))
{
Result = false;
}
return Result;
}
static bool
CharArraysEqualUpToLength (char* A, char* B, s32 Length)
{
bool Result = true;
char* AIter = A;
char* BIter = B;
for (s32 i = 0; i < Length; i++)
{
if(*AIter++ != *BIter++)
{
Result = false;
break;
}
}
return Result;
}
static void
ReverseCharArray (char* Array, s32 Length)
{
char* ForwardIter = Array;
char* BackwardIter = Array + Length - 1;
for (s32 i = 0; i < (Length / 2); i++)
{
char F = *ForwardIter;
char B = *BackwardIter;
*ForwardIter++ = B;
*BackwardIter-- = F;
}
}
static s32
IndexOfChar (char* Array, s32 After, char Find)
{
s32 Result = -1;
s32 Counter = After;
char* Iter = Array + After;
while (*Iter)
{
if (*Iter == Find)
{
Result = Counter;
break;
}
Counter++;
*Iter++;
}
return Result;
}
static s32
FastReverseIndexOfChar (char* Array, s32 Length, s32 OffsetFromEnd, char Find)
{
s32 Result = -1;
s32 Counter = Length - OffsetFromEnd;
char* Iter = Array + Length - OffsetFromEnd;
for (int i = 0; i < (Length - OffsetFromEnd); i++)
{
if (*Iter == Find)
{
Result = Counter;
break;
}
*Iter--;
Counter--;
}
return Result;
}
static s32
ReverseIndexOfChar (char* Array, s32 OffsetFromEnd, char Find)
{
s32 StringLength = NullTerminatedCharArrayLength(Array);
return FastReverseIndexOfChar(Array, StringLength, OffsetFromEnd, Find);
}
static b32
CharArrayContains(char* Array, char* CheckFor)
{
b32 Result = false;
char* Src = Array;
while (*Src)
{
if (*Src == *CheckFor)
{
char* A = CheckFor;
char* B = Src;
while (*B && *A && *A == *B)
{
*B++; *A++;
}
if (*A == 0)
{
Result = true;
break;
}
}
Src++;
}
return Result;
}
static b32
CharArrayContainsSafe(char* Array, s32 ArrayLength, char* CheckFor, s32 CheckForLength)
{
b32 Result = false;
if (ArrayLength >= CheckForLength)
{
char* Src = Array;
for (s32 s = 0; s < ArrayLength; s++)
{
if (*Src == *CheckFor && (s + CheckForLength <= ArrayLength))
{
char* A = CheckFor;
char* B = Src;
for (s32 d = 0; d < CheckForLength; d++)
{
if (*B != *A) { break; }
*B++; *A++;
}
if (*A == 0)
{
Result = true;
break;
}
}
Src++;
}
}
return Result;
}
////////////////////////////////////////////////////////////////
// Basic String Operations
////////////////////////////////////////////////////////////////
static bool
StringsEqual (string A, string B)
{
bool Result = false;
if (A.Length == B.Length)
{
Result = true;
char* AIter = A.Memory;
char* BIter = B.Memory;
for (s32 i = 0; i < A.Length; i++)
{
if (*AIter++ != *BIter++)
{
Result = false;
break;
}
}
}
return Result;
}
static string
MakeString (char* Array, s32 Length, s32 Max)
{
string Result = {};
Result.Memory = Array;
Result.Length = Length;
Result.Max = Max;
return Result;
}
static string
MakeString (char* Array, s32 Length)
{
string Result = {};
Result.Memory = Array;
Result.Length = Length;
Result.Max = Length;
return Result;
}
static string
MakeString (char* Array)
{
s32 Length = CharArrayLength (Array);
return MakeString(Array, Length);
}
static string
MakeStringLiteral (char* String)
{
string Result = {};
Result.Memory = String;
Result.Max = CharArrayLength(String);
Result.Length = Result.Max;
return Result;
}
static bool
StringEqualsCharArray (string String, char* CharArray, s32 CharArrayLength)
{
bool Result = false;
if (CharArrayLength == String.Length)
{
Result = true;
char* S = String.Memory;
char* C = CharArray;
for (s32 i = 0; i < String.Length; i++)
{
if (*C++ != *S++)
{
Result = false;
break;
}
}
}
return Result;
}
static bool
StringEqualsCharArray (string String, char* CharArray)
{
s32 CLength = CharArrayLength(CharArray);
return StringEqualsCharArray(String, CharArray, CLength);
}
static s32
FindFirstChar (string String, char C)
{
s32 Result = -1;
char* Iter = String.Memory;
for (int i = 0; i < String.Length; i++)
{
if (*Iter++ == C)
{
Result = i;
break;
}
}
return Result;
}
static void
SetStringToChar (string* Dest, char C, s32 Count)
{
Assert(Count <= Dest->Max);
char* Iter = Dest->Memory;
for (int i = 0; i < Count; i++)
{
*Iter++ = C;
}
Dest->Length = Count;
}
static void
SetStringToCharArray (string* Dest, char* Source)
{
Dest->Length = 0;
char* Src = Source;
char* Dst = Dest->Memory;
while (*Src && Dest->Length < Dest->Max)
{
*Dst++ = *Src++;
Dest->Length++;
}
}
static void
ConcatString (string Source, string* Dest)
{
Assert((Dest->Length + Source.Length) <= Dest->Max);
char* Dst = Dest->Memory + Dest->Length;
char* Src = Source.Memory;
for (s32 i = 0; i < Source.Length; i++)
{
*Dst++ = *Src++;
Dest->Length++;
}
}
static void
ConcatString (string Source, s32 Length, string* Dest)
{
Assert(Length <= Source.Length);
Assert((Dest->Length + Length) <= Dest->Max);
char* Dst = Dest->Memory + Dest->Length;
char* Src = Source.Memory;
for (s32 i = 0; i < Length; i++)
{
*Dst++ = *Src++;
Dest->Length++;
}
}
static void
ConcatCharToString (string* Dest, char C)
{
Assert(Dest->Length + 1 <= Dest->Max);
char* Dst = Dest->Memory + Dest->Length;
*Dst = C;
Dest->Length++;
}
static void
ConcatCharArrayToString (char* Source, string* Dest)
{
Assert(CharArrayLength(Source) + Dest->Length <= Dest->Max);
char* Dst = Dest->Memory + Dest->Length;
char* Src = Source;
while (Dest->Length < Dest->Max &&
*Src)
{
*Dst++ = *Src++;
Dest->Length++;
}
}
static void
ConcatCharArrayToString (char* Source, s32 SourceLength, string* Dest)
{
Assert(SourceLength + Dest->Length <= Dest->Max);
char* Dst = Dest->Memory + Dest->Length;
char* Src = Source;
for (int i = 0; i < SourceLength && Dest->Length < Dest->Max; i++)
{
*Dst++ = *Src++;
Dest->Length++;
}
}
static void
CopyStringTo (string Source, string* Dest)
{
char* Src = Source.Memory;
char* Dst = Dest->Memory;
s32 CopyLength = GSMin(Source.Length, Dest->Max);
for (int i = 0; i < CopyLength; i++)
{
*Dst++ = *Src++;
}
Dest->Length = Source.Length;
}
static s32
CopyStringToCharArray (string Source, char* Dest, s32 DestLength)
{
char* Src = Source.Memory;
char* Dst = Dest;
s32 CopyLength = GSMin(Source.Length, DestLength);
for (int i = 0; i < CopyLength; i++)
{
*Dst++ = *Src++;
}
return CopyLength;
}
static void
CopyCharArrayToString (char* Source, string* Dest)
{
char* Src = Source;
char* Dst = Dest->Memory;
s32 Copied = 0;
while (*Src && Copied < Dest->Max)
{
*Dst++ = *Src++;
Copied++;
}
*Dst++ = 0;
Dest->Length = Copied;
}
static void
CopyCharArrayToString (char* Source, s32 SourceLength, string* Dest)
{
Assert(SourceLength <= Dest->Max);
char* Src = Source;
char* Dst = Dest->Memory;
for (s32 i = 0; i < SourceLength; i++)
{
*Dst++ = *Src++;
}
*Dst++ = 0;
Dest->Length = SourceLength;
}
static s32
CopyCharArray (char* Source, char* Dest, s32 DestLength)
{
char* Src = Source;
char* Dst = Dest;
s32 i = 0;
while (*Src && i < DestLength)
{
*Dst++ = *Src++;
i++;
}
return i;
}
static s32
CopyCharArrayAt (char* Source, char* Dest, s32 DestLength, s32 Offset)
{
Assert(Offset < DestLength);
char* Src = Source;
char* Dst = Dest + Offset;
s32 i = Offset;
while (*Src && i < DestLength)
{
*Dst++ = *Src++;
i++;
}
return i - Offset;
}
static void
InsertChar (string* String, char Char, s32 Index)
{
Assert(Index >= 0 && Index < String->Max);
Assert(String->Length < String->Max);
char* Src = String->Memory + String->Length - 1;
char* Dst = Src + 1;
for (int i = String->Length - 1; i >= Index; i--)
{
*Dst-- = *Src--;
}
*(String->Memory + Index) = Char;
String->Length++;
}
static void
RemoveCharAt (string* String, s32 Index)
{
Assert(Index >= 0 && Index < String->Max);
char* Dst = String->Memory + Index;
char* Src = Dst + 1;
for (int i = Index; i < String->Length; i++)
{
*Dst++ = *Src++;
}
*Dst = 0;
String->Length--;
}
static s32
IndexOfChar(string String, char C)
{
s32 Result = -1;
char* At = String.Memory;
for (s32 i = 0; i < String.Length; i++)
{
if (*At == C)
{
Result = i;
break;
}
At++;
}
return Result;
}
static s32
LastIndexOfChar(string String, char C)
{
s32 Result = -1;
char* At = String.Memory + String.Length - 1;
for (s32 i = 0; i < String.Length; i++)
{
if (*At == C)
{
Result = String.Length - i;
break;
}
At--;
}
return Result;
}
static s32
SearchForCharInSet(string String, char* Set)
{
s32 Index = -1;
char* At = String.Memory;
for (s32 i = 0; i < String.Length; i++)
{
char* Needle = Set;
while (*Needle)
{
if (*At == *Needle)
{
Index = String.Length - i;
break;
}
Needle++;
}
if (Index >= 0)
{
break;
}
At++;
}
return Index;
}
static s32
ReverseSearchForCharInSet(string String, char* Set)
{
s32 Index = -1;
for (s32 i = String.Length - 1; i >= 0; i--)
{
char* Needle = Set;
while (*Needle)
{
if (String.Memory[i] == *Needle)
{
Index = i;
break;
}
Needle++;
}
if (Index >= 0)
{
break;
}
}
return Index;
}
static string
Substring (string String, s32 Start, s32 End)
{
Assert(Start >= 0 && End > Start && End <= String.Length);
string Result = {};
Result.Memory = String.Memory + Start;
Result.Length = End - Start;
return Result;
}
static string
Substring (string String, s32 Start)
{
Assert(Start >= 0 && Start < String.Length);
string Result = {};
Result.Memory = String.Memory + Start;
Result.Length = String.Length - Start;
return Result;
}
static b32
StringContainsCharArray(string SearchIn, char* SearchFor, s32 SearchForLength)
{
b32 Result = false;
char* SearchInAt = SearchIn.Memory;
for (s32 i = 0; i < (SearchIn.Length - SearchForLength) + 1; i++)
{
char* InAt = SearchInAt;
char* ForAt = SearchFor;
s32 LengthMatch = 0;
while (*InAt == *ForAt)
{
InAt++;
ForAt++;
LengthMatch++;
}
if (LengthMatch == SearchForLength)
{
Result = true;
break;
}
SearchInAt++;
}
return Result;
}
static b32
StringContainsString(string SearchIn, string SearchFor)
{
return StringContainsCharArray(SearchIn, SearchFor.Memory, SearchFor.Length);
}
static b32
StringContainsCharArrayCaseInsensitive(string SearchIn, char* SearchFor, s32 SearchForLength)
{
b32 Result = false;
char* SearchInAt = SearchIn.Memory;
for (s32 i = 0; i < (SearchIn.Length - SearchForLength) + 1; i++)
{
char* InAt = SearchInAt;
char* ForAt = SearchFor;
s32 LengthMatch = 0;
while (CharsEqualCaseInsensitive(*InAt, *ForAt))
{
InAt++;
ForAt++;
LengthMatch++;
}
if (LengthMatch == SearchForLength)
{
Result = true;
break;
}
SearchInAt++;
}
return Result;
}
static b32
StringContainsStringCaseInsensitive(string SearchIn, string SearchFor)
{
return StringContainsCharArrayCaseInsensitive(SearchIn, SearchFor.Memory, SearchFor.Length);
}
static void
NullTerminate (string* String)
{
Assert(String->Length + 1 <= String->Max);
*(String->Memory + String->Length) = 0;
String->Length++;
}
// http://www.cse.yorku.ca/~oz/hash.html
// djb2 hash
static u32
HashString(string String)
{
u32 Hash = 5381;
for (s32 i = 0; i < String.Length; i++)
{
Hash = ((Hash << 5) + Hash) + (u32)String.Memory[i]; /* hash * 33 + c */
}
return Hash;
}
static void
InsertStringAt (string* Dest, string Source, s32 At)
{
Assert(At + Source.Length < Dest->Max);
Assert(At < Dest->Length);
char* Src = Dest->Memory + Dest->Length;
char* Dst = Dest->Memory + Source.Length + Dest->Length;
for (s32 i = Dest->Length - 1; i >= At; i--)
{
*--Dst = *--Src;
}
Src = Source.Memory;
Dst = Dest->Memory + At;
for (s32 j = 0; j < Source.Length; j++)
{
*Dst++ = *Src++;
}
Dest->Length += Source.Length;
}
////////////////////////////////////////////////////////////////
// String Parsing
////////////////////////////////////////////////////////////////
// NOTE(Peter): parameters are all in order Length, String because
// that matches the order of C's printf %.*s format specifier. This
// is convenient because you can use StringExpand to parse a string
// struct
// :StringExpandNote
static parse_result
ParseUnsignedInt (s32 Length, char* String)
{
Assert(IsNumeric(*String));
parse_result Result = {};
Result.Type = ParseType_UnsignedInt;
char* Iter = String;
u32 ResultValue = 0;
for (s32 i = 0; i < Length; i++)
{
ResultValue = CharToUInt(*Iter++) + (ResultValue * 10);
}
Result.UnsignedIntValue = ResultValue;
Result.OnePastLast = Iter;
return Result;
}
static parse_result
ParseUnsignedIntUnsafe (char* String)
{
char* Start = String;
char* End = EatNumber(String + 1);
return ParseUnsignedInt(End - Start, String);
}
// :StringExpandNote
static parse_result
ParseSignedInt (s32 Length, char* String)
{
Assert(Length > 0);
parse_result Result = {};
Result.Type = ParseType_SignedInt;
s32 Negative = 1;
s32 LengthRemaining = Length;
s32 ResultValue = 0;
char* Iter = String;
if (*Iter == '-') {
LengthRemaining--;
*Iter++;
Negative = -1;
}
for (s32 i = 0; i < LengthRemaining; i++)
{
ResultValue = CharToUInt(*Iter++) + (ResultValue * 10);
}
ResultValue *= Negative;
Result.SignedIntValue = ResultValue;
Result.OnePastLast = Iter;
return Result;
}
static parse_result
ParseSignedIntUnsafe (char* String)
{
char* Start = String;
char* End = EatNumber(String + 1);
return ParseSignedInt(End - Start, String);
}
// :StringExpandNote
static parse_result
ParseFloat (s32 Length, char* String)
{
parse_result Result = {};
Result.Type = ParseType_Float;
s32 Negative = 1;
s32 LengthRemaining = Length;
float ResultValue = 0;
char* Iter = String;
if (*Iter == '-') {
LengthRemaining--;
*Iter++;
Negative = -1;
}
for (s32 i = 0; i < LengthRemaining; i++)
{
if (IsNumeric(*Iter))
{
ResultValue = (float)CharToUInt(*Iter++) + (ResultValue * 10);
}
else if (*Iter == '.' || *Iter == 0)
{
LengthRemaining -= i;
break;
}
}
if (*Iter == '.')
{
*Iter++;
float AfterPoint = 0;
s32 PlacesAfterPoint = 0;
for (s32 i = 0; i < LengthRemaining; i++)
{
if (IsNumeric(*Iter))
{
AfterPoint = (float)CharToUInt(*Iter++) + (AfterPoint * 10);
PlacesAfterPoint++;
}
else
{
break;
}
}
AfterPoint = AfterPoint / GSPow(10, PlacesAfterPoint);
ResultValue += AfterPoint;
}
ResultValue *= Negative;
Result.FloatValue = ResultValue;
Result.OnePastLast = Iter;
return Result;
}
static parse_result
ParseFloatUnsafe (char* String)
{
char* Start = String;
char* End = EatNumber(String + 1);
return ParseFloat(End - Start, String);
}
static s32
UIntToString (u32 Int, char* String, s32 MaxLength, b32 FormatFlags = 0, s32 MinimumLength = 0)
{
s32 Remaining = Int;
char* Iter = String;
while (Remaining > 0 && (Iter - String) < MaxLength)
{
*Iter++ = '0' + (Remaining % 10);
Remaining /= 10;
}
s32 CharsCopied = Iter - String;
ReverseCharArray(String, CharsCopied);
return CharsCopied;
}
static s32
IntToString (s32 Int, char* String, s32 MaxLength, b32 FormatFlags = 0, s32 MinimumLength = 0)
{
s32 Remaining = Int;
s32 CharsCopied = 0;
char* Iter = String;
bool Negative = Remaining < 0;
Remaining = GSAbs(Remaining);
if (Remaining > 0)
{
while (Remaining > 0 && CharsCopied < MaxLength)
{
*Iter++ = '0' + (Remaining % 10);
Remaining /= 10;
CharsCopied++;
}
}
else if (Remaining == 0)
{
*Iter++ = '0';
}
if (Negative)
{
*Iter++ = '-';
CharsCopied++;
}
ReverseCharArray(String, CharsCopied);
return CharsCopied;
}
static s32
IntToString (s32 Int, char* String, s32 MaxLength, s32 Offset, b32 FormatFlags = 0, s32 MinimumWidth = 0)
{
char* StringStart = String + Offset;
s32 LengthWritten = IntToString(Int, StringStart, MaxLength - Offset);
return LengthWritten;
}
static s32
FloatToString(float Float, char *String, s32 MaxLength, s32 AfterPoint = 0, b32 FormatFlags = 0, s32 MinimumWidth = 0)
{
s32 IPart = (s32)Float;
float FPart = GSAbs(Float - (float)IPart);
s32 i = IntToString(IPart, String, MaxLength);
if (AfterPoint > 1)
{
String[i++] = '.';
s32 FPartInt = FPart * GSPow(10, AfterPoint);
i += IntToString(FPartInt, String, MaxLength, i, 0, 0);
}
return i;
}
////////////////////////////////////////////////////////////////
// PrintF
////////////////////////////////////////////////////////////////
static void
OutChar (string* String, char C)
{
if (String->Length < String->Max)
{
String->Memory[String->Length] = C;
String->Length++;
}
}
char OctalDigits[] = "01234567";
char DecimalDigits[] = "0123456789";
char HexDigits[] = "0123456789ABCDEF";
static void
U64ToASCII (string* String, u64 Value, s32 Base, char* Digits)
{
u64 ValueRemaining = Value;
char* Start = String->Memory + String->Length;
do {
s32 DigitsIndex = ValueRemaining % Base;
char Digit = Digits[DigitsIndex];
OutChar(String, Digit);
ValueRemaining /= Base;
}while (ValueRemaining);
char* End = String->Memory + String->Length;
while (Start < End)
{
End--;
char Temp = *End;
*End = *Start;
*Start = Temp;
*Start++;
}
}
static void
F64ToASCII (string* String, r64 Value, s32 Precision)
{
if (Value < 0)
{
OutChar(String, '-');
Value = -Value;
}
u64 IntegerPart = (u64)Value;
Value -= IntegerPart;
U64ToASCII(String, IntegerPart, 10, DecimalDigits);
OutChar(String, '.');
for (s32 i = 0; i < Precision; i++)
{
Value *= 10.f;
u32 DecimalPlace = Value;
Value -= DecimalPlace;
OutChar(String, DecimalDigits[DecimalPlace]);
}
}
internal s64
ReadVarArgsSignedInteger (s32 Width, va_list* Args)
{
s64 Result = 0;
switch (Width)
{
case 1: { Result = (s64)va_arg(*Args, s8); } break;
case 2: { Result = (s64)va_arg(*Args, s16); } break;
case 4: { Result = (s64)va_arg(*Args, s32); } break;
case 8: { Result = (s64)va_arg(*Args, s64); } break;
InvalidDefaultCase;
}
return Result;
}
internal r64
ReadVarArgsUnsignedInteger (s32 Width, va_list* Args)
{
u64 Result = 0;
switch (Width)
{
case 1: { Result = (u64)va_arg(*Args, u8); } break;
case 2: { Result = (u64)va_arg(*Args, u16); } break;
case 4: { Result = (u64)va_arg(*Args, u32); } break;
case 8: { Result = (u64)va_arg(*Args, u64); } break;
InvalidDefaultCase;
}
return Result;
}
internal r64
ReadVarArgsFloat (s32 Width, va_list* Args)
{
r64 Result = 0;
switch (Width)
{
case 4: { Result = (r64)va_arg(*Args, r64); } break;
case 8: { Result = (r64)va_arg(*Args, r64); } break;
InvalidDefaultCase;
}
return Result;
}
internal s32
PrintFArgsList (char* Dest, s32 DestMax, char* Format, va_list Args)
{
char* DestAt = Dest;
char* FormatAt = Format;
while (*FormatAt)
{
if (FormatAt[0] != '%')
{
*DestAt++ = *FormatAt++;
}
else if (FormatAt[0] == '%' && FormatAt[1] == '%') // Print the % symbol
{
*DestAt++ = *FormatAt++;
}
else
{
FormatAt++;
// Flags
if (FormatAt[0] == '-')
{
FormatAt++;
}
else if (FormatAt[0] == '+')
{
FormatAt++;
}
else if (FormatAt[0] == ' ')
{
FormatAt++;
}
else if (FormatAt[0] == '#')
{
FormatAt++;
}
else if (FormatAt[0] == '0')
{
FormatAt++;
}
// Width
b32 WidthSpecified = false;
s32 Width = 0;
if (IsNumeric(FormatAt[0]))
{
WidthSpecified = true;
parse_result Parse = ParseSignedIntUnsafe(FormatAt);
FormatAt = Parse.OnePastLast;
Width = Parse.SignedIntValue;
}
else if (FormatAt[0] == '*')
{
WidthSpecified = true;
Width = va_arg(Args, s32);
Assert(Width >= 0);
FormatAt++;
}
// Precision
b32 PrecisionSpecified = false;
s32 Precision = 0;
if (FormatAt[0] == '.')
{
FormatAt++;
if (IsNumeric(FormatAt[0]))
{
PrecisionSpecified = true;
parse_result Parse = ParseSignedIntUnsafe(FormatAt);
FormatAt = Parse.OnePastLast;
Precision = Parse.SignedIntValue;
}
else if (FormatAt[0] == '*')
{
PrecisionSpecified = true;
Precision = va_arg(Args, s32);
Assert(Precision >= 0);
FormatAt++;
}
}
// Length
b32 LengthSpecified = false;
s32 Length = 4;
if (FormatAt[0] == 'h' && FormatAt[1] == 'h')
{
LengthSpecified = true;
LengthSpecified = 1;
FormatAt += 2;
}
else if (FormatAt[0] == 'h')
{
LengthSpecified = true;
LengthSpecified = 2;
FormatAt++;
}
else if (FormatAt[0] == 'l' && FormatAt[1] == 'l')
{
LengthSpecified = true;
LengthSpecified = 8;
FormatAt += 2;
}
else if (FormatAt[0] == 'l')
{
LengthSpecified = true;
LengthSpecified = 4;
FormatAt++;
}
else if (FormatAt[0] == 'j')
{
LengthSpecified = true;
LengthSpecified = 8;
FormatAt++;
}
else if (FormatAt[0] == 'z')
{
FormatAt++;
}
else if (FormatAt[0] == 't')
{
FormatAt++;
}
else if (FormatAt[0] == 'L')
{
FormatAt++;
}
// Format Specifier
s32 DestLengthRemaining = DestMax - (DestAt - Dest);
char Temp[64];
string TempDest = MakeString(Temp, 0, 64);
if (FormatAt[0] == 'd' || FormatAt[0] == 'i')
{
s64 SignedInt = ReadVarArgsSignedInteger(Length, &Args);
if (SignedInt < 0)
{
OutChar(&TempDest, '-');
SignedInt *= -1;
}
U64ToASCII(&TempDest, (u64)SignedInt, 10, DecimalDigits);
}
else if (FormatAt[0] == 'u')
{
u32 UnsignedInt = ReadVarArgsUnsignedInteger(Length, &Args);
U64ToASCII(&TempDest, UnsignedInt, 10, DecimalDigits);
}
else if (FormatAt[0] == 'o')
{
u32 UnsignedInt = ReadVarArgsUnsignedInteger(Length, &Args);
U64ToASCII(&TempDest, UnsignedInt, 8, OctalDigits);
}
else if (FormatAt[0] == 'x' || FormatAt[0] == 'X')
{
u32 UnsignedInt = ReadVarArgsUnsignedInteger(Length, &Args);
U64ToASCII(&TempDest, UnsignedInt, 16, HexDigits);
}
else if (FormatAt[0] == 'f' || FormatAt[0] == 'F')
{
r64 Float = ReadVarArgsFloat(Length, &Args);
s32 AfterPoint = 6;
if (PrecisionSpecified)
{
AfterPoint = Precision;
}
F64ToASCII(&TempDest, Float, AfterPoint);
}
else if (FormatAt[0] == 'c')
{
char InsertChar = va_arg(Args, char);
OutChar(&TempDest, InsertChar);
}
else if (FormatAt[0] == 's')
{
char* InsertString = va_arg(Args, char*);
s32 InsertStringLength = CharArrayLength(InsertString);
if (PrecisionSpecified)
{
InsertStringLength = GSMin(InsertStringLength, Precision);
}
InsertStringLength = GSMin(DestLengthRemaining, InsertStringLength);
for (s32 c = 0; c < InsertStringLength; c++)
{
OutChar(&TempDest, *InsertString++);
}
}
else if (FormatAt[0] == 'S')
{
string InsertString = va_arg(Args, string);
for (s32 c = 0; c < InsertString.Length; c++)
{
OutChar(&TempDest, InsertString.Memory[c]);
}
}
else if (FormatAt[0] == 'p')
{
// TODO(Peter): Pointer Address
}
else
{
// NOTE(Peter): Non-specifier character found
InvalidCodePath;
}
for (s32 i = 0; i < TempDest.Length; i++)
{
*DestAt++ = TempDest.Memory[i];
}
*FormatAt++;
}
}
s32 FormattedLength = DestAt - Dest;
return FormattedLength;
}
internal void
PrintF (string* String, char* Format, ...)
{
va_list Args;
va_start(Args, Format);
String->Length = 0;
String->Length += PrintFArgsList(String->Memory + String->Length, String->Max - String->Length, Format, Args);
va_end(Args);
}
internal void
AppendPrintF (string* String, char* Format, ...)
{
va_list Args;
va_start(Args, Format);
String->Length += PrintFArgsList(String->Memory + String->Length, String->Max - String->Length, Format, Args);
va_end(Args);
}
////////////////////////////////////////////////////////////////
// String Memory Function Definitions
////////////////////////////////////////////////////////////////
static s32
CalculateSlotCountFromSize (s32 RequestedSize, s32 SlotSize)
{
s32 SlotCount = RequestedSize / SlotSize;
if (SlotCount * SlotSize < RequestedSize)
{
SlotCount += 1;
}
return SlotCount;
}
static bool
SlotsAreContiguous (slot_header* First, slot_header* Second)
{
bool Result = false;
u8* FirstSlotNextAddress = (u8*)First + First->Size;
u8* SecondAddress = (u8*)Second;
Result = FirstSlotNextAddress == SecondAddress;
return Result;
}
static contiguous_slot_count_result
CountContiguousSlots (slot_header* First)
{
Assert(First != 0);
contiguous_slot_count_result Result = {};
Result.Count = 1;
slot_header* IterPrev = First;
slot_header* Iter = First->Next;
while (Iter && SlotsAreContiguous(IterPrev, Iter))
{
Result.Count++;
IterPrev = Iter;
Iter = Iter->Next;
}
Result.LastContiguousSlot = IterPrev;
return Result;
}
static slot_header*
GetSlotAtOffset(slot_header* First, s32 Offset)
{
slot_header* Iter = First;
s32 Count = 0;
while (Count < Offset && Iter)
{
Iter = Iter->Next;
Count++;
}
return Iter;
}
static slot_header*
InsertSlotIntoList (slot_header* NewSlot, slot_header* ListStart)
{
slot_header* List = ListStart;
if (NewSlot < List)
{
NewSlot->Next = List;
List = NewSlot;
}
else
{
slot_header* PrevIter = List;
slot_header* Iter = List->Next;
while (Iter && NewSlot > Iter)
{
PrevIter = Iter;
Iter = Iter->Next;
}
Assert(PrevIter);
if (PrevIter)
{
PrevIter->Next = NewSlot;
}
if (Iter)
{
NewSlot->Next = Iter;
}
}
return List;
}
static void
AllocStringFromStringArena (string* String, s32 Size, slot_arena* Storage)
{
s32 SlotCount = CalculateSlotCountFromSize(Size, Storage->SlotSize);
slot_header* Slot = Storage->FreeList;
slot_header* PrevSlot = 0;
while (Slot)
{
contiguous_slot_count_result ContiguousSlots = CountContiguousSlots(Slot);
if (ContiguousSlots.Count >= SlotCount)
{
slot_header* NextStartSlot = GetSlotAtOffset(Slot, SlotCount);
if (PrevSlot)
{
PrevSlot->Next = NextStartSlot;
}
else
{
Storage->FreeList = NextStartSlot;
}
break;
}
else
{
PrevSlot = Slot;
Slot = Slot->Next;
}
}
if (Slot)
{
String->Memory = (char*)Slot;
GSZeroMemory((u8*)String->Memory, SlotCount * Storage->SlotSize);
String->Max = SlotCount * Storage->SlotSize;
String->Length = 0;
}
}
static string
AllocStringFromStringArena (s32 Size, slot_arena* Storage)
{
string Result = {0};
AllocStringFromStringArena(&Result, Size, Storage);
return Result;
}
static void
FreeToStringArena (string* String, slot_arena* Storage)
{
u8* Base = (u8*)(String->Memory);
u8* End = Base + String->Max - 1;
u8* MemoryEnd = Storage->Memory + (Storage->SlotSize * Storage->SlotCount);
Assert((Base >= Storage->Memory) && (End < MemoryEnd));
Assert((String->Max % Storage->SlotSize) == 0);
s32 SizeReclaimed = 0;
slot_header* Slot = (slot_header*)Base;
while (SizeReclaimed < String->Max)
{
Slot->Size = Storage->SlotSize;
Storage->FreeList = InsertSlotIntoList(Slot, Storage->FreeList);
SizeReclaimed += Storage->SlotSize;
Slot = (slot_header*)(Base + SizeReclaimed);
}
String->Memory = 0;
String->Length = 0;
String->Max = 0;
}
static void
ReallocFromStringArena (string* String, s32 NewSize, slot_arena* Storage)
{
string NewString = AllocStringFromStringArena(NewSize, Storage);
CopyStringTo(*String, &NewString);
FreeToStringArena(String, Storage);
*String = NewString;
}
#if defined(DEBUG)
void DEBUGPrintChars (string* String, s32 Count)
{
char* Iter = String->Memory;
for (int i = 0; i < Count; i++)
{
*Iter++ = (char)('A' + i);
}
String->Length = Count;
}
#ifdef DEBUG_GS_STRING
#include <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
#define GS_STRING_H
#endif // GS_STRING_H