#ifndef CC_STRING_H #define CC_STRING_H #include "Core.h" /* Provides various string related operations Also provides conversions betweens strings and numbers Also provides converting code page 437 indices to/from unicode Also provides wrapping a single line of text into multiple lines Copyright 2014-2023 ClassiCube | Licensed under BSD-3 */ #define STRING_INT_CHARS 24 /* Converts a character from A-Z to a-z, otherwise is left untouched. */ #define Char_MakeLower(c) if ((c) >= 'A' && (c) <= 'Z') { (c) += ' '; } /* Constant string that points to NULL and has 0 length. */ /* NOTE: Do NOT modify the contents of this string! */ extern const cc_string String_Empty; /* Constructs a string from the given arguments. */ static CC_INLINE cc_string String_Init(STRING_REF char* buffer, int length, int capacity) { cc_string s; s.buffer = buffer; s.length = length; s.capacity = capacity; return s; } /* Counts number of characters until a '\0' is found, up to capacity. */ CC_API int String_CalcLen(const char* raw, int capacity); /* Counts number of characters until a '\0' is found. */ int String_Length(const char* raw); /* Constructs a string from a compile time string constant */ #define String_FromConst(text) { (char*)(text), (sizeof(text) - 1), (sizeof(text) - 1)} /* Constructs a string from a compile time array */ #define String_FromArray(buffer) { buffer, 0, sizeof(buffer)} /* Constructs a string from a (maybe null terminated) buffer. */ CC_NOINLINE cc_string String_FromRaw(STRING_REF char* buffer, int capacity); /* Constructs a string from a null-terminated constant readonly buffer. */ CC_API cc_string String_FromReadonly(STRING_REF const char* buffer); /* Constructs a string from a compile time array, that may have arbitary actual length of data at runtime */ #define String_FromRawArray(buffer) String_FromRaw(buffer, sizeof(buffer)) /* Constructs a string from a compile time array (leaving 1 byte of room for null terminator) */ #define String_NT_Array(buffer) { buffer, 0, (sizeof(buffer) - 1)} /* Initialises a string from a compile time array. */ #define String_InitArray(str, buffr) str.buffer = buffr; str.length = 0; str.capacity = sizeof(buffr); /* Initialises a string from a compile time array. (leaving 1 byte of room for null terminator) */ #define String_InitArray_NT(str, buffr) str.buffer = buffr; str.length = 0; str.capacity = sizeof(buffr) - 1; /* Sets length of dst to 0, then appends all characters in src. */ CC_API void String_Copy(cc_string* dst, const cc_string* src); /* Copies up to capacity characters from src into dst. Appends \0 after if src->length is < capacity. */ /* NOTE: This means the buffer MAY NOT be null-terminated. Only use with String_FromRawArray. */ CC_API void String_CopyToRaw(char* dst, int capacity, const cc_string* src); /* Calls String_CopyToRaw on a compile time array. */ #define String_CopyToRawArray(buffer, src) String_CopyToRaw(buffer, sizeof(buffer), src) /* UNSAFE: Returns a substring of the given string. (sub.buffer is within str.buffer + str.length) */ CC_API cc_string String_UNSAFE_Substring(STRING_REF const cc_string* str, int offset, int length); /* UNSAFE: Returns a substring of the given string. (sub.buffer is within str.buffer + str.length) */ /* The substring returned is { str.buffer + offset, str.length - offset } */ CC_API cc_string String_UNSAFE_SubstringAt(STRING_REF const cc_string* str, int offset); /* UNSAFE: Splits a string of the form [str1][c][str2][c][str3].. into substrings. */ /* e.g., "abc:id:xyz" becomes "abc","id","xyz" */ /* Returns the number of substrings found. (always <= maxSubs) */ CC_API int String_UNSAFE_Split(STRING_REF const cc_string* str, char c, cc_string* subs, int maxSubs); /* UNSAFE: Splits a string of the form [part][c][rest], returning whether [c] was found or not. */ /* NOTE: This is intended to be repeatedly called until str->length is 0. (unbounded String_UNSAFE_Split) */ CC_API void String_UNSAFE_SplitBy(STRING_REF cc_string* str, char c, cc_string* part); /* UNSAFE: Splits a string of the form [key][c][value] into two substrings. */ /* e.g., "allowed =true" becomes "allowed" and "true", and excludes the space. */ /* If c is not found, sets key to str and value to String_Empty, returns false. */ /* Otherwise if c is found, a non-zero value is returned. */ CC_API int String_UNSAFE_Separate(STRING_REF const cc_string* str, char c, cc_string* key, cc_string* value); /* Returns non-zero if all characters of the strings are equal. */ CC_API int String_Equals( const cc_string* a, const cc_string* b); typedef int (*FP_String_Equals)(const cc_string* a, const cc_string* b); /* Returns non-zero if all characters of the strings are case-insensitively equal. */ CC_API int String_CaselessEquals( const cc_string* a, const cc_string* b); typedef int (*FP_String_CaselessEquals)(const cc_string* a, const cc_string* b); /* Returns non-zero if all characters of the strings are case-insensitively equal. */ /* NOTE: Faster than String_CaselessEquals(a, String_FromReadonly(b)) */ CC_API int String_CaselessEqualsConst( const cc_string* a, const char* b); typedef int (*FP_String_CaselessEqualsConst)(const cc_string* a, const char* b); /* Breaks down an integer into an array of digits, and returns number of digits. */ /* NOTE: Digits are in reverse order, so e.g. '200' becomes '0','0','2' */ int String_MakeUInt32(cc_uint32 num, char* digits); /* Attempts to append a character. */ /* Does nothing if str->length == str->capcity. */ CC_API void String_Append(cc_string* str, char c); /* Attempts to append "true" if value is non-zero, "false" otherwise. */ CC_API void String_AppendBool(cc_string* str, cc_bool value); /* Attempts to append the digits of an integer (and -sign if negative). */ CC_API void String_AppendInt(cc_string* str, int num); /* Attempts to append the digits of an unsigned 32 bit integer. */ CC_API void String_AppendUInt32(cc_string* str, cc_uint32 num); /* Attempts to append the digits of an integer, padding left with 0. */ CC_API void String_AppendPaddedInt(cc_string* str, int num, int minDigits); /* Attempts to append the digits of a float as a decimal. */ /* NOTE: If the number is an integer, no decimal point is added. */ /* Otherwise, fracDigits digits are added after a decimal point. */ /* e.g. 1.0f produces "1", 2.6745f produces "2.67" when fracDigits is 2 */ CC_API void String_AppendFloat(cc_string* str, float num, int fracDigits); /* TODO: Need to account for , or . for decimal */ /* Attempts to append characters. src MUST be null-terminated. */ CC_API void String_AppendConst( cc_string* str, const char* src); typedef void (*FP_String_AppendConst)(cc_string* str, const char* src); /* Attempts to append characters. */ void String_AppendAll(cc_string* str, const void* data, int len); /* Attempts to append characters of a string. */ CC_API void String_AppendString(cc_string* str, const cc_string* src); /* Attempts to append characters of a string, skipping any colour codes. */ CC_API void String_AppendColorless(cc_string* str, const cc_string* src); /* Attempts to append the two hex digits of a byte. */ CC_API void String_AppendHex(cc_string* str, cc_uint8 value); /* Returns first index of the given character in the given string, -1 if not found. */ #define String_IndexOf(str, c) String_IndexOfAt(str, 0, c) /* Returns first index of the given character in the given string, -1 if not found. */ CC_API int String_IndexOfAt(const cc_string* str, int offset, char c); /* Returns last index of the given character in the given string, -1 if not found. */ #define String_LastIndexOf(str, c) String_LastIndexOfAt(str, 0, c) /* Returns last index of the given character in the given string, -1 if not found. */ CC_API int String_LastIndexOfAt(const cc_string* str, int offset, char c); /* Inserts the given character into the given string. Exits process if this fails. */ /* e.g. inserting 'd' at offset '1' into "abc" produces "adbc" */ CC_API void String_InsertAt(cc_string* str, int offset, char c); /* Deletes a character from the given string. Exits process if this fails. */ /* e.g. deleting at offset '1' from "adbc" produces "abc" */ CC_API void String_DeleteAt(cc_string* str, int offset); /* Trims leading spaces from the given string. */ /* NOTE: Works by adjusting buffer/length, the characters in memory are not shifted. */ CC_API void String_UNSAFE_TrimStart(cc_string* str); /* Trims trailing spaces from the given string. */ /* NOTE: Works by adjusting buffer/length, the characters in memory are not shifted. */ CC_API void String_UNSAFE_TrimEnd(cc_string* str); /* Returns first index of the given substring in the given string, -1 if not found. */ /* e.g. index of "ab" within "cbabd" is 2 */ CC_API int String_IndexOfConst(const cc_string* str, const char* sub); /* Returns non-zero if given substring is inside the given string. */ #define String_ContainsConst(str, sub) (String_IndexOfConst(str, sub) >= 0) /* Returns non-zero if given substring is case-insensitively inside the given string. */ CC_API int String_CaselessContains( const cc_string* str, const cc_string* sub); typedef int (*FP_String_CaselessContains)(const cc_string* str, const cc_string* sub); /* Returns non-zero if given substring is case-insensitively equal to the beginning of the given string. */ CC_API int String_CaselessStarts( const cc_string* str, const cc_string* sub); typedef int (*FP_String_CaselessStarts)(const cc_string* str, const cc_string* sub); /* Returns non-zero if given substring is case-insensitively equal to the ending of the given string. */ CC_API int String_CaselessEnds( const cc_string* str, const cc_string* sub); typedef int (*FP_String_CaselessEnds)(const cc_string* str, const cc_string* sub); /* Compares the length of the given strings, then compares the characters if same length. Returns: */ /* -X if a.length < b.length, X if a.length > b.length */ /* -X if a.buffer[i] < b.buffer[i], X if a.buffer[i] > b.buffer[i] */ /* else returns 0. NOTE: The return value is not just in -1,0,1! */ CC_API int String_Compare(const cc_string* a, const cc_string* b); /* String_Format is provided for formatting strings (similiar to printf) Supported specifiers for string formatting: TYPE | ARGUMENT | EXAMPLE %b | cc_uint8 | format(%b, 46) = "46" %i | int | format(%i, -5) = "-5" %f[0-9] | float | format(%f2, 321.3519) = "321.35" %p[0-9] | int | format(%p3, 5) = "005" %t | cc_bool | format(%t, 1) = "true" %c | char* | format(%c, "ABCD") = "ABCD" %s | cc_string | format(%s, {"ABCD", 2, 4}) = "AB" %r | char | format(%r, 47) = "\" %x | cc_uintptr| format(%x, 31) = "000000000000002F" %h | cc_uint32 | format(%h, 11) = "0000000B" */ /* See String_Format4 */ CC_API void String_Format1( cc_string* str, const char* format, const void* a1); typedef void (*FP_String_Format1)(cc_string* str, const char* format, const void* a1); /* See String_Format4 */ CC_API void String_Format2( cc_string* str, const char* format, const void* a1, const void* a2); typedef void (*FP_String_Format2)(cc_string* str, const char* format, const void* a1, const void* a2); /* See String_Format4 */ CC_API void String_Format3( cc_string* str, const char* format, const void* a1, const void* a2, const void* a3); typedef void (*FP_String_Format3)(cc_string* str, const char* format, const void* a1, const void* a2, const void* a3); /* Formats the arguments in a string, similiar to printf or C# String.Format NOTE: This is a low level API. Argument count and types are not checked at all. */ CC_API void String_Format4( cc_string* str, const char* format, const void* a1, const void* a2, const void* a3, const void* a4); typedef void (*FP_String_Format4)(cc_string* str, const char* format, const void* a1, const void* a2, const void* a3, const void* a4); /* Converts a code page 437 character to its unicode equivalent. */ cc_unichar Convert_CP437ToUnicode(char c); /* Converts a unicode codepoint to its code page 437 equivalent, or '?' if no match. */ char Convert_CodepointToCP437(cc_codepoint cp); /* Attempts to convert a unicode codepoint to its code page 437 equivalent. */ CC_API cc_bool Convert_TryCodepointToCP437(cc_codepoint cp, char* c); /* Decodes a unicode codepoint from UTF8, returning number of bytes read. */ /* Returns 0 if not enough input data to read the character. */ int Convert_Utf8ToCodepoint(cc_codepoint* cp, const cc_uint8* data, cc_uint32 len); /* Encodes a code page 437 character in UTF8, returning number of bytes written. */ /* The number of bytes written is always either 1,2 or 3. */ int Convert_CP437ToUtf8(char c, cc_uint8* data); /* Attempts to append all characters from UTF16 encoded data to the given string. */ /* Characters not in code page 437 are omitted. */ void String_AppendUtf16(cc_string* str, const void* data, int numBytes); /* Attempts to append all characters from UTF8 encoded data to the given string. */ /* Characters not in code page 437 are omitted. */ void String_AppendUtf8(cc_string* str, const void* data, int numBytes); /* Attempts to append all characters from CP-1252 encoded data to the given string. */ /* Characters not in code page 437 are omitted. */ void String_DecodeCP1252(cc_string* str, const void* data, int numBytes); /* Encodes a string in UTF8 format, also null terminating the string. */ /* Returns the number of bytes written, excluding trailing NULL terminator. */ int String_EncodeUtf8(void* data, const cc_string* src); /* Attempts to convert the given string into an unsigned 8 bit integer. */ CC_API cc_bool Convert_ParseUInt8(const cc_string* str, cc_uint8* value); /* Attempts to convert the given string into an unsigned 16 bit integer. */ CC_API cc_bool Convert_ParseUInt16(const cc_string* str, cc_uint16* value); /* Attempts to convert the given string into an integer. */ CC_API cc_bool Convert_ParseInt(const cc_string* str, int* value); /* Attempts to convert the given string into an unsigned 64 bit integer. */ CC_API cc_bool Convert_ParseUInt64(const cc_string* str, cc_uint64* value); /* Attempts to convert the given string into a floating point number. */ CC_API cc_bool Convert_ParseFloat(const cc_string* str, float* value); /* Attempts to convert the given string into a bool. */ /* NOTE: String must case-insensitively equal "true" or "false" */ CC_API cc_bool Convert_ParseBool(const cc_string* str, cc_bool* value); #define STRINGSBUFFER_BUFFER_DEF_SIZE 4096 #define STRINGSBUFFER_FLAGS_DEF_ELEMS 256 #define STRINGSBUFFER_DEF_LEN_SHIFT 9 #define STRINGSBUFFER_DEF_LEN_MASK 0x1FFUL struct StringsBuffer { char* textBuffer; /* Raw characters of all entries */ cc_uint32* flagsBuffer; /* Private flags for each entry */ int count, totalLength; /* internal state */ int _textCapacity, _flagsCapacity; char _defaultBuffer[STRINGSBUFFER_BUFFER_DEF_SIZE]; cc_uint32 _defaultFlags[STRINGSBUFFER_FLAGS_DEF_ELEMS]; /* Value to shift a flags value by to retrieve the offset */ int _lenShift; /* Value to mask a flags value with to retrieve the length */ int _lenMask; }; /* Resets counts to 0 and other state to default */ void StringsBuffer_Init(struct StringsBuffer* buffer); /* Sets the number of bits in an entry's flags that are used to store its length */ /* (e.g. if bits is 9, then the maximum length of an entry is 2^9-1 = 511) */ void StringsBuffer_SetLengthBits(struct StringsBuffer* buffer, int bits); /* Frees any allocated memory and then called StringsBuffer_Init */ CC_NOINLINE void StringsBuffer_Clear(struct StringsBuffer* buffer); /* UNSAFE: Returns a direct pointer to the i'th string in the given buffer */ /* You MUST NOT change the characters of this string. Copy to another string if necessary.*/ CC_API STRING_REF cc_string StringsBuffer_UNSAFE_Get(struct StringsBuffer* buffer, int i); STRING_REF void StringsBuffer_UNSAFE_GetRaw(struct StringsBuffer* buffer, int i, cc_string* dst); /* Adds the given string to the end of the given buffer */ CC_API void StringsBuffer_Add(struct StringsBuffer* buffer, const cc_string* str); /* Removes the i'th string from the given buffer, shifting following strings downwards */ CC_API void StringsBuffer_Remove(struct StringsBuffer* buffer, int index); /* Sorts all the entries in the given buffer using String_Compare */ void StringsBuffer_Sort(struct StringsBuffer* buffer); /* Performs line wrapping on the given string. */ /* e.g. "some random tex|t* (| is lineLen) becomes "some random" "text" */ void WordWrap_Do(STRING_REF cc_string* text, cc_string* lines, int numLines, int lineLen); /* Calculates the position of a raw index in the wrapped lines. */ void WordWrap_GetCoords(int index, const cc_string* lines, int numLines, int* coordX, int* coordY); /* Returns number of characters from current character to end of previous word. */ int WordWrap_GetBackLength(const cc_string* text, int index); /* Returns number of characters from current character to start of next word. */ int WordWrap_GetForwardLength(const cc_string* text, int index); #endif