mirror of
https://github.com/ClassiCube/ClassiCube.git
synced 2025-01-22 17:12:25 -05:00
277 lines
17 KiB
C
277 lines
17 KiB
C
#ifndef CC_STRING_H
|
|
#define CC_STRING_H
|
|
#include "Core.h"
|
|
CC_BEGIN_HEADER
|
|
|
|
/*
|
|
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-2025 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. */
|
|
CC_API 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. */
|
|
CC_API 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. */
|
|
CC_API void String_AppendCP1252(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. */
|
|
CC_API 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);
|
|
|
|
CC_END_HEADER
|
|
#endif
|