1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
|
#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
|