Bug Summary

File:String.c
Warning:line 444, column 32
Dereference of null pointer

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -triple amd64-unknown-openbsd6.8 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name String.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mthread-model posix -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -masm-verbose -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -dwarf-column-info -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/local/lib/clang/10.0.1 -I /usr/X11R6/include -I /usr/local/include -fdebug-compilation-dir /home/ben/Projects/ClassiCube/src -ferror-limit 19 -fmessage-length 0 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -fobjc-runtime=gnustep -fdiagnostics-show-option -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -o /home/ben/Projects/ClassiCube/src/scan/2021-01-09-173753-31878-1 -x c String.c
1#include "String.h"
2#include "Funcs.h"
3#include "Logger.h"
4#include "Platform.h"
5#include "Stream.h"
6#include "Utils.h"
7
8#ifdef __cplusplus
9const cc_string String_Empty = { NULL((void*)0), 0, 0 };
10#else
11const cc_string String_Empty;
12#endif
13
14int String_CalcLen(const char* raw, int capacity) {
15 int length = 0;
16 while (length < capacity && *raw) { raw++; length++; }
17 return length;
18}
19
20int String_Length(const char* raw) {
21 int length = 0;
22 while (length < UInt16_MaxValue((cc_uint16)65535) && *raw) { raw++; length++; }
23 return length;
24}
25
26cc_string String_FromRaw(STRING_REF char* buffer, int capacity) {
27 return String_Init(buffer, String_CalcLen(buffer, capacity), capacity);
28}
29
30cc_string String_FromReadonly(STRING_REF const char* buffer) {
31 int len = String_Length(buffer);
32 return String_Init((char*)buffer, len, len);
33}
34
35
36void String_Copy(cc_string* dst, const cc_string* src) {
37 dst->length = 0;
38 String_AppendString(dst, src);
39}
40
41void String_CopyToRaw(char* dst, int capacity, const cc_string* src) {
42 int i, len = min(capacity, src->length)((capacity) < (src->length) ? (capacity) : (src->length
))
;
43 for (i = 0; i < len; i++) { dst[i] = src->buffer[i]; }
44 /* add \0 to mark end of used portion of buffer */
45 if (len < capacity) dst[len] = '\0';
46}
47
48cc_string String_UNSAFE_Substring(STRING_REF const cc_string* str, int offset, int length) {
49 if (offset < 0 || offset > str->length) {
50 Logger_Abort("Offset for substring out of range");
51 }
52 if (length < 0 || length > str->length) {
53 Logger_Abort("Length for substring out of range");
54 }
55 if (offset + length > str->length) {
56 Logger_Abort("Result substring is out of range");
57 }
58 return String_Init(str->buffer + offset, length, length);
59}
60
61cc_string String_UNSAFE_SubstringAt(STRING_REF const cc_string* str, int offset) {
62 cc_string sub;
63 if (offset < 0 || offset > str->length) Logger_Abort("Sub offset out of range");
64
65 sub.buffer = str->buffer + offset;
66 sub.length = str->length - offset;
67 sub.capacity = str->length - offset; /* str->length to match String_UNSAFE_Substring */
68 return sub;
69}
70
71int String_UNSAFE_Split(STRING_REF const cc_string* str, char c, cc_string* subs, int maxSubs) {
72 int beg = 0, end, count, i;
73
74 for (i = 0; i < maxSubs && beg <= str->length; i++) {
75 end = String_IndexOfAt(str, beg, c);
76 if (end == -1) end = str->length;
77
78 subs[i] = String_UNSAFE_Substring(str, beg, end - beg);
79 beg = end + 1;
80 }
81
82 count = i;
83 /* If not enough split substrings, make remaining NULL */
84 for (; i < maxSubs; i++) { subs[i] = String_Empty; }
85 return count;
86}
87
88void String_UNSAFE_SplitBy(STRING_REF cc_string* str, char c, cc_string* part) {
89 int idx = String_IndexOf(str, c)String_IndexOfAt(str, 0, c);
90 if (idx == -1) {
91 *part = *str;
92 *str = String_Empty;
93 } else {
94 *part = String_UNSAFE_Substring(str, 0, idx); idx++;
95 *str = String_UNSAFE_SubstringAt(str, idx);
96 }
97}
98
99int String_UNSAFE_Separate(STRING_REF const cc_string* str, char c, cc_string* key, cc_string* value) {
100 int idx = String_IndexOf(str, c)String_IndexOfAt(str, 0, c);
101 if (idx == -1) {
102 *key = *str;
103 *value = String_Empty;
104 return false0;
105 }
106
107 *key = String_UNSAFE_Substring(str, 0, idx); idx++;
108 *value = String_UNSAFE_SubstringAt(str, idx);
109
110 /* Trim key [c] value to just key[c]value */
111 String_UNSAFE_TrimEnd(key);
112 String_UNSAFE_TrimStart(value);
113 return key->length > 0 && value->length > 0;
114}
115
116
117int String_Equals(const cc_string* a, const cc_string* b) {
118 return a->length == b->length && Mem_Equal(a->buffer, b->buffer, a->length);
119}
120
121int String_CaselessEquals(const cc_string* a, const cc_string* b) {
122 int i;
123 char aCur, bCur;
124 if (a->length != b->length) return false0;
125
126 for (i = 0; i < a->length; i++) {
127 aCur = a->buffer[i]; Char_MakeLower(aCur)if ((aCur) >= 'A' && (aCur) <= 'Z') { (aCur) +=
' '; }
;
128 bCur = b->buffer[i]; Char_MakeLower(bCur)if ((bCur) >= 'A' && (bCur) <= 'Z') { (bCur) +=
' '; }
;
129 if (aCur != bCur) return false0;
130 }
131 return true1;
132}
133
134int String_CaselessEqualsConst(const cc_string* a, const char* b) {
135 int i;
136 char aCur, bCur;
137
138 for (i = 0; i < a->length; i++) {
139 aCur = a->buffer[i]; Char_MakeLower(aCur)if ((aCur) >= 'A' && (aCur) <= 'Z') { (aCur) +=
' '; }
;
140 bCur = b[i]; Char_MakeLower(bCur)if ((bCur) >= 'A' && (bCur) <= 'Z') { (bCur) +=
' '; }
;
141 if (aCur != bCur || bCur == '\0') return false0;
142 }
143 /* ensure at end of string */
144 return b[a->length] == '\0';
145}
146
147
148void String_Append(cc_string* str, char c) {
149 if (str->length == str->capacity) return;
150 str->buffer[str->length++] = c;
151}
152
153void String_AppendBool(cc_string* str, cc_bool value) {
154 String_AppendConst(str, value ? "True" : "False");
155}
156
157int String_MakeUInt32(cc_uint32 num, char* digits) {
158 int len = 0;
159 do {
160 digits[len] = '0' + (num % 10); num /= 10; len++;
161 } while (num > 0);
162 return len;
163}
164
165void String_AppendInt(cc_string* str, int num) {
166 if (num < 0) {
167 num = -num;
168 String_Append(str, '-');
169 }
170 String_AppendUInt32(str, (cc_uint32)num);
171}
172
173void String_AppendUInt32(cc_string* str, cc_uint32 num) {
174 char digits[STRING_INT_CHARS24];
175 int i, count = String_MakeUInt32(num, digits);
176
177 for (i = count - 1; i >= 0; i--) {
178 String_Append(str, digits[i]);
179 }
180}
181
182void String_AppendPaddedInt(cc_string* str, int num, int minDigits) {
183 char digits[STRING_INT_CHARS24];
184 int i, count;
185 for (i = 0; i < minDigits; i++) { digits[i] = '0'; }
186
187 count = String_MakeUInt32(num, digits);
188 if (count < minDigits) count = minDigits;
189
190 for (i = count - 1; i >= 0; i--) {
191 String_Append(str, digits[i]);
192 }
193}
194
195void String_AppendFloat(cc_string* str, float num, int fracDigits) {
196 int i, whole, digit;
197 double frac;
198
199 if (num < 0.0f) {
200 String_Append(str, '-'); /* don't need to check success */
201 num = -num;
202 }
203
204 whole = (int)num;
205 String_AppendUInt32(str, whole);
206
207 frac = (double)num - (double)whole;
208 if (frac == 0.0) return;
209 String_Append(str, '.'); /* don't need to check success */
210
211 for (i = 0; i < fracDigits; i++) {
212 frac *= 10;
213 digit = (int)frac % 10;
214 String_Append(str, '0' + digit);
215 }
216}
217
218void String_AppendHex(cc_string* str, cc_uint8 value) {
219 /* 48 = index of 0, 55 = index of (A - 10) */
220 cc_uint8 hi = (value >> 4) & 0xF;
221 char c_hi = hi < 10 ? (hi + 48) : (hi + 55);
222 cc_uint8 lo = value & 0xF;
223 char c_lo = lo < 10 ? (lo + 48) : (lo + 55);
224
225 String_Append(str, c_hi);
226 String_Append(str, c_lo);
227}
228
229CC_NOINLINE__attribute__((noinline)) static void String_Hex32(cc_string* str, cc_uint32 value) {
230 int shift;
231
232 for (shift = 24; shift >= 0; shift -= 8) {
233 cc_uint8 part = (cc_uint8)(value >> shift);
234 String_AppendHex(str, part);
235 }
236}
237
238CC_NOINLINE__attribute__((noinline)) static void String_Hex64(cc_string* str, cc_uint64 value) {
239 int shift;
240
241 for (shift = 56; shift >= 0; shift -= 8) {
242 cc_uint8 part = (cc_uint8)(value >> shift);
243 String_AppendHex(str, part);
244 }
245}
246
247void String_AppendConst(cc_string* str, const char* src) {
248 for (; *src; src++) {
249 String_Append(str, *src);
250 }
251}
252
253void String_AppendAll(cc_string* str, const void* data, int len) {
254 const char* src = (const char*)data;
255 int i;
256 for (i = 0; i < len; i++) String_Append(str, src[i]);
257}
258
259void String_AppendString(cc_string* str, const cc_string* src) {
260 int i;
261 for (i = 0; i < src->length; i++) {
262 String_Append(str, src->buffer[i]);
263 }
264}
265
266void String_AppendColorless(cc_string* str, const cc_string* src) {
267 char c;
268 int i;
269
270 for (i = 0; i < src->length; i++) {
271 c = src->buffer[i];
272 if (c == '&') { i++; continue; } /* Skip over the following colour code */
273 String_Append(str, c);
274 }
275}
276
277
278int String_IndexOfAt(const cc_string* str, int offset, char c) {
279 int i;
280 for (i = offset; i < str->length; i++) {
281 if (str->buffer[i] == c) return i;
282 }
283 return -1;
284}
285
286int String_LastIndexOfAt(const cc_string* str, int offset, char c) {
287 int i;
288 for (i = (str->length - 1) - offset; i >= 0; i--) {
289 if (str->buffer[i] == c) return i;
290 }
291 return -1;
292}
293
294void String_InsertAt(cc_string* str, int offset, char c) {
295 int i;
296
297 if (offset < 0 || offset > str->length) {
298 Logger_Abort("Offset for InsertAt out of range");
299 }
300 if (str->length == str->capacity) {
301 Logger_Abort("Cannot insert character into full string");
302 }
303
304 for (i = str->length; i > offset; i--) {
305 str->buffer[i] = str->buffer[i - 1];
306 }
307 str->buffer[offset] = c;
308 str->length++;
309}
310
311void String_DeleteAt(cc_string* str, int offset) {
312 int i;
313
314 if (offset < 0 || offset >= str->length) {
315 Logger_Abort("Offset for DeleteAt out of range");
316 }
317
318 for (i = offset; i < str->length - 1; i++) {
319 str->buffer[i] = str->buffer[i + 1];
320 }
321 str->buffer[str->length - 1] = '\0';
322 str->length--;
323}
324
325void String_UNSAFE_TrimStart(cc_string* str) {
326 int i;
327 for (i = 0; i < str->length; i++) {
328 if (str->buffer[i] != ' ') break;
329
330 str->buffer++;
331 str->length--; i--;
332 }
333}
334
335void String_UNSAFE_TrimEnd(cc_string* str) {
336 int i;
337 for (i = str->length - 1; i >= 0; i--) {
338 if (str->buffer[i] != ' ') break;
339 str->length--;
340 }
341}
342
343int String_IndexOfConst(const cc_string* str, const char* sub) {
344 int i, j;
345
346 for (i = 0; i < str->length; i++) {
347 for (j = 0; sub[j] && (i + j) < str->length; j++) {
348
349 if (str->buffer[i + j] != sub[j]) break;
350 }
351 if (sub[j] == '\0') return i;
352 }
353 return -1;
354}
355
356int String_CaselessContains(const cc_string* str, const cc_string* sub) {
357 char strCur, subCur;
358 int i, j;
359
360 for (i = 0; i < str->length; i++) {
361 for (j = 0; j < sub->length && (i + j) < str->length; j++) {
362
363 strCur = str->buffer[i + j]; Char_MakeLower(strCur)if ((strCur) >= 'A' && (strCur) <= 'Z') { (strCur
) += ' '; }
;
364 subCur = sub->buffer[j]; Char_MakeLower(subCur)if ((subCur) >= 'A' && (subCur) <= 'Z') { (subCur
) += ' '; }
;
365 if (strCur != subCur) break;
366 }
367 if (j == sub->length) return true1;
368 }
369 return false0;
370}
371
372int String_CaselessStarts(const cc_string* str, const cc_string* sub) {
373 char strCur, subCur;
374 int i;
375 if (str->length < sub->length) return false0;
376
377 for (i = 0; i < sub->length; i++) {
378 strCur = str->buffer[i]; Char_MakeLower(strCur)if ((strCur) >= 'A' && (strCur) <= 'Z') { (strCur
) += ' '; }
;
379 subCur = sub->buffer[i]; Char_MakeLower(subCur)if ((subCur) >= 'A' && (subCur) <= 'Z') { (subCur
) += ' '; }
;
380 if (strCur != subCur) return false0;
381 }
382 return true1;
383}
384
385int String_CaselessEnds(const cc_string* str, const cc_string* sub) {
386 char strCur, subCur;
387 int i, j = str->length - sub->length;
388 if (j < 0) return false0; /* sub longer than str */
389
390 for (i = 0; i < sub->length; i++) {
391 strCur = str->buffer[j + i]; Char_MakeLower(strCur)if ((strCur) >= 'A' && (strCur) <= 'Z') { (strCur
) += ' '; }
;
392 subCur = sub->buffer[i]; Char_MakeLower(subCur)if ((subCur) >= 'A' && (subCur) <= 'Z') { (subCur
) += ' '; }
;
393 if (strCur != subCur) return false0;
394 }
395 return true1;
396}
397
398int String_Compare(const cc_string* a, const cc_string* b) {
399 char aCur, bCur;
400 int i, minLen = min(a->length, b->length)((a->length) < (b->length) ? (a->length) : (b->
length))
;
401
402 for (i = 0; i < minLen; i++) {
403 aCur = a->buffer[i]; Char_MakeLower(aCur)if ((aCur) >= 'A' && (aCur) <= 'Z') { (aCur) +=
' '; }
;
404 bCur = b->buffer[i]; Char_MakeLower(bCur)if ((bCur) >= 'A' && (bCur) <= 'Z') { (bCur) +=
' '; }
;
405
406 if (aCur == bCur) continue;
407 return (cc_uint8)aCur - (cc_uint8)bCur;
408 }
409
410 /* all chars are equal here - same string, or a substring */
411 return a->length - b->length;
412}
413
414void String_Format1(cc_string* str, const char* format, const void* a1) {
415 String_Format4(str, format, a1, NULL((void*)0), NULL((void*)0), NULL((void*)0));
1
Calling 'String_Format4'
416}
417void String_Format2(cc_string* str, const char* format, const void* a1, const void* a2) {
418 String_Format4(str, format, a1, a2, NULL((void*)0), NULL((void*)0));
419}
420void String_Format3(cc_string* str, const char* format, const void* a1, const void* a2, const void* a3) {
421 String_Format4(str, format, a1, a2, a3, NULL((void*)0));
422}
423void String_Format4(cc_string* str, const char* format, const void* a1, const void* a2, const void* a3, const void* a4) {
424 const void* arg;
425 int i, j = 0, digits;
426
427 const void* args[4];
428 args[0] = a1; args[1] = a2; args[2] = a3; args[3] = a4;
429
430 for (i = 0; format[i]; i++) {
2
Loop condition is true. Entering loop body
6
Loop condition is true. Entering loop body
431 if (format[i] != '%') { String_Append(str, format[i]); continue; }
3
Assuming the condition is false
4
Taking false branch
7
Assuming the condition is false
8
Taking false branch
432 arg = args[j++];
9
Null pointer value stored to 'arg'
433
434 switch (format[++i]) {
5
Control jumps to the 'default' case at line 463
10
Control jumps to 'case 112:' at line 442
435 case 'b':
436 String_AppendInt(str, *((cc_uint8*)arg)); break;
437 case 'i':
438 String_AppendInt(str, *((int*)arg)); break;
439 case 'f':
440 digits = format[++i] - '0';
441 String_AppendFloat(str, *((float*)arg), digits); break;
442 case 'p':
443 digits = format[++i] - '0';
444 String_AppendPaddedInt(str, *((int*)arg), digits); break;
11
Dereference of null pointer
445 case 't':
446 String_AppendBool(str, *((cc_bool*)arg)); break;
447 case 'c':
448 String_AppendConst(str, (char*)arg); break;
449 case 's':
450 String_AppendString(str, (cc_string*)arg); break;
451 case 'r':
452 String_Append(str, *((char*)arg)); break;
453 case 'x':
454 if (sizeof(cc_uintptr) == 4) {
455 String_Hex32(str, *((cc_uint32*)arg)); break;
456 } else {
457 String_Hex64(str, *((cc_uint64*)arg)); break;
458 }
459 case 'h':
460 String_Hex32(str, *((cc_uint32*)arg)); break;
461 case '%':
462 String_Append(str, '%'); break;
463 default:
464 Logger_Abort("Invalid type for string format");
465 }
466 }
467}
468
469
470/*########################################################################################################################*
471*------------------------------------------------Character set conversions------------------------------------------------*
472*#########################################################################################################################*/
473static const cc_unichar controlChars[32] = {
474 0x0000, 0x263A, 0x263B, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022,
475 0x25D8, 0x25CB, 0x25D9, 0x2642, 0x2640, 0x266A, 0x266B, 0x263C,
476 0x25BA, 0x25C4, 0x2195, 0x203C, 0x00B6, 0x00A7, 0x25AC, 0x21A8,
477 0x2191, 0x2193, 0x2192, 0x2190, 0x221F, 0x2194, 0x25B2, 0x25BC
478};
479
480static const cc_unichar extendedChars[129] = { 0x2302,
481 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7,
482 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
483 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9,
484 0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192,
485 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA,
486 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
487 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
488 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
489 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F,
490 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
491 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B,
492 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
493 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4,
494 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
495 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248,
496 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
497};
498
499cc_unichar Convert_CP437ToUnicode(char c) {
500 cc_uint8 raw = (cc_uint8)c;
501 if (raw < 0x20) return controlChars[raw];
502 if (raw < 0x7F) return raw;
503 return extendedChars[raw - 0x7F];
504}
505
506char Convert_CodepointToCP437(cc_codepoint cp) {
507 char c; Convert_TryCodepointToCP437(cp, &c); return c;
508}
509
510static cc_codepoint ReduceEmoji(cc_codepoint cp) {
511 if (cp == 0x1F31E) return 0x263C;
512 if (cp == 0x1F3B5) return 0x266B;
513 if (cp == 0x1F642) return 0x263A;
514
515 if (cp == 0x1F600 || cp == 0x1F601 || cp == 0x1F603) return 0x263A;
516 if (cp == 0x1F604 || cp == 0x1F606 || cp == 0x1F60A) return 0x263A;
517 return cp;
518}
519
520cc_bool Convert_TryCodepointToCP437(cc_codepoint cp, char* c) {
521 int i;
522 if (cp >= 0x20 && cp < 0x7F) { *c = (char)cp; return true1; }
523 if (cp >= 0x1F000) cp = ReduceEmoji(cp);
524
525 for (i = 0; i < Array_Elems(controlChars)(sizeof(controlChars) / sizeof(controlChars[0])); i++) {
526 if (controlChars[i] == cp) { *c = i; return true1; }
527 }
528 for (i = 0; i < Array_Elems(extendedChars)(sizeof(extendedChars) / sizeof(extendedChars[0])); i++) {
529 if (extendedChars[i] == cp) { *c = i + 0x7F; return true1; }
530 }
531
532 *c = '?'; return false0;
533}
534
535int Convert_Utf8ToCodepoint(cc_codepoint* cp, const cc_uint8* data, cc_uint32 len) {
536 *cp = '\0';
537 if (!len) return 0;
538
539 if (data[0] <= 0x7F) {
540 *cp = data[0];
541 return 1;
542 } else if ((data[0] & 0xE0) == 0xC0) {
543 if (len < 2) return 0;
544
545 *cp = ((data[0] & 0x1F) << 6) | ((data[1] & 0x3F));
546 return 2;
547 } else if ((data[0] & 0xF0) == 0xE0) {
548 if (len < 3) return 0;
549
550 *cp = ((data[0] & 0x0F) << 12) | ((data[1] & 0x3F) << 6)
551 | ((data[2] & 0x3F));
552 return 3;
553 } else {
554 if (len < 4) return 0;
555
556 *cp = ((data[0] & 0x07) << 18) | ((data[1] & 0x3F) << 12)
557 | ((data[2] & 0x3F) << 6) | (data[3] & 0x3F);
558 return 4;
559 }
560}
561
562/* Encodes a unicode character in UTF8, returning number of bytes written */
563static int Convert_UnicodeToUtf8(cc_unichar uc, cc_uint8* data) {
564 if (uc <= 0x7F) {
565 data[0] = (cc_uint8)uc;
566 return 1;
567 } else if (uc <= 0x7FF) {
568 data[0] = 0xC0 | ((uc >> 6) & 0x1F);
569 data[1] = 0x80 | ((uc) & 0x3F);
570 return 2;
571 } else {
572 data[0] = 0xE0 | ((uc >> 12) & 0x0F);
573 data[1] = 0x80 | ((uc >> 6) & 0x3F);
574 data[2] = 0x80 | ((uc) & 0x3F);
575 return 3;
576 }
577}
578
579int Convert_CP437ToUtf8(char c, cc_uint8* data) {
580 /* Common ASCII case */
581 if (c >= 0x20 && c < 0x7F) {
582 data[0] = (cc_uint8)c;
583 return 1;
584 }
585 return Convert_UnicodeToUtf8(Convert_CP437ToUnicode(c), data);
586}
587
588void String_AppendUtf16(cc_string* value, const void* data, int numBytes) {
589 const cc_unichar* chars = (const cc_unichar*)data;
590 int i; char c;
591
592 for (i = 0; i < (numBytes >> 1); i++) {
593 /* TODO: UTF16 to codepoint conversion */
594 if (Convert_TryCodepointToCP437(chars[i], &c)) String_Append(value, c);
595 }
596}
597
598void String_AppendUtf8(cc_string* value, const void* data, int numBytes) {
599 const cc_uint8* chars = (const cc_uint8*)data;
600 int len; cc_codepoint cp; char c;
601
602 for (; numBytes > 0; numBytes -= len) {
603 len = Convert_Utf8ToCodepoint(&cp, chars, numBytes);
604 if (!len) return;
605
606 if (Convert_TryCodepointToCP437(cp, &c)) String_Append(value, c);
607 chars += len;
608 }
609}
610
611void String_DecodeCP1252(cc_string* value, const void* data, int numBytes) {
612 const cc_uint8* chars = (const cc_uint8*)data;
613 int i; char c;
614
615 for (i = 0; i < numBytes; i++) {
616 if (Convert_TryCodepointToCP437(chars[i], &c)) String_Append(value, c);
617 }
618}
619
620
621/*########################################################################################################################*
622*--------------------------------------------------Numerical conversions--------------------------------------------------*
623*#########################################################################################################################*/
624cc_bool Convert_ParseUInt8(const cc_string* str, cc_uint8* value) {
625 int tmp;
626 *value = 0;
627 if (!Convert_ParseInt(str, &tmp) || tmp < 0 || tmp > UInt8_MaxValue((cc_uint8)255)) return false0;
628 *value = (cc_uint8)tmp; return true1;
629}
630
631cc_bool Convert_ParseUInt16(const cc_string* str, cc_uint16* value) {
632 int tmp;
633 *value = 0;
634 if (!Convert_ParseInt(str, &tmp) || tmp < 0 || tmp > UInt16_MaxValue((cc_uint16)65535)) return false0;
635 *value = (cc_uint16)tmp; return true1;
636}
637
638static int Convert_CompareDigits(const char* digits, const char* magnitude) {
639 int i;
640 for (i = 0; ; i++) {
641 if (magnitude[i] == '\0') return 0;
642 if (digits[i] > magnitude[i]) return 1;
643 if (digits[i] < magnitude[i]) return -1;
644 }
645 return 0;
646}
647
648static cc_bool Convert_TryParseDigits(const cc_string* str, cc_bool* negative, char* digits, int maxDigits) {
649 char* start = digits;
650 int offset = 0, i;
651
652 *negative = false0;
653 if (!str->length) return false0;
654 digits += (maxDigits - 1);
655
656 /* Handle number signs */
657 if (str->buffer[0] == '-') { *negative = true1; offset = 1; }
658 if (str->buffer[0] == '+') { offset = 1; }
659
660 /* add digits, starting at last digit */
661 for (i = str->length - 1; i >= offset; i--) {
662 char c = str->buffer[i];
663 if (c < '0' || c > '9' || digits < start) return false0;
664 *digits-- = c;
665 }
666
667 for (; digits >= start; ) { *digits-- = '0'; }
668 return true1;
669}
670
671#define INT32_DIGITS10 10
672cc_bool Convert_ParseInt(const cc_string* str, int* value) {
673 cc_bool negative;
674 char digits[INT32_DIGITS10];
675 int i, compare, sum = 0;
676
677 *value = 0;
678 if (!Convert_TryParseDigits(str, &negative, digits, INT32_DIGITS10)) return false0;
679
680 if (negative) {
681 compare = Convert_CompareDigits(digits, "2147483648");
682 /* Special case, since |largest min value| is > |largest max value| */
683 if (compare == 0) { *value = Int32_MinValue((cc_int32)-2147483647L - (cc_int32)1L); return true1; }
684 } else {
685 compare = Convert_CompareDigits(digits, "2147483647");
686 }
687 if (compare > 0) return false0;
688
689 for (i = 0; i < INT32_DIGITS10; i++) {
690 sum *= 10; sum += digits[i] - '0';
691 }
692
693 if (negative) sum = -sum;
694 *value = sum;
695 return true1;
696}
697
698#define UINT64_DIGITS20 20
699cc_bool Convert_ParseUInt64(const cc_string* str, cc_uint64* value) {
700 cc_bool negative;
701 char digits[UINT64_DIGITS20];
702 int i, compare;
703 cc_uint64 sum = 0;
704
705 *value = 0;
706 if (!Convert_TryParseDigits(str, &negative, digits, UINT64_DIGITS20)) return false0;
707
708 compare = Convert_CompareDigits(digits, "18446744073709551615");
709 if (negative || compare > 0) return false0;
710
711 for (i = 0; i < UINT64_DIGITS20; i++) {
712 sum *= 10; sum += digits[i] - '0';
713 }
714
715 *value = sum;
716 return true1;
717}
718
719cc_bool Convert_ParseFloat(const cc_string* str, float* value) {
720 int i = 0;
721 cc_bool negate = false0;
722 double sum, whole, fract, divide = 10.0;
723 *value = 0.0f;
724
725 /* Handle number signs */
726 if (!str->length) return false0;
727 if (str->buffer[0] == '-') { negate = true1; i++; }
728 if (str->buffer[0] == '+') { i++; }
729
730 for (whole = 0.0; i < str->length; i++) {
731 char c = str->buffer[i];
732 if (c == '.' || c == ',') { i++; break; }
733
734 if (c < '0' || c > '9') return false0;
735 whole *= 10; whole += (c - '0');
736 }
737
738 for (fract = 0.0; i < str->length; i++) {
739 char c = str->buffer[i];
740 if (c < '0' || c > '9') return false0;
741
742 fract += (c - '0') / divide; divide *= 10;
743 }
744
745 sum = whole + fract;
746 if (negate) sum = -sum;
747 *value = (float)sum;
748 return true1;
749}
750
751cc_bool Convert_ParseBool(const cc_string* str, cc_bool* value) {
752 if (String_CaselessEqualsConst(str, "True")) {
753 *value = true1; return true1;
754 }
755 if (String_CaselessEqualsConst(str, "False")) {
756 *value = false0; return true1;
757 }
758 *value = false0; return false0;
759}
760
761
762/*########################################################################################################################*
763*------------------------------------------------------StringsBuffer------------------------------------------------------*
764*#########################################################################################################################*/
765#define STRINGSBUFFER_BUFFER_EXPAND_SIZE8192 8192
766
767#define StringsBuffer_GetOffset(raw)((raw) >> buffer->_lenShift) ((raw) >> buffer->_lenShift)
768#define StringsBuffer_GetLength(raw)((raw) & buffer->_lenMask) ((raw) & buffer->_lenMask)
769#define StringsBuffer_PackOffset(off)((off) << buffer->_lenShift) ((off) << buffer->_lenShift)
770
771CC_NOINLINE__attribute__((noinline)) static void StringsBuffer_Init(struct StringsBuffer* buffer) {
772 buffer->count = 0;
773 buffer->totalLength = 0;
774 buffer->textBuffer = buffer->_defaultBuffer;
775 buffer->flagsBuffer = buffer->_defaultFlags;
776 buffer->_textCapacity = STRINGSBUFFER_BUFFER_DEF_SIZE4096;
777 buffer->_flagsCapacity = STRINGSBUFFER_FLAGS_DEF_ELEMS256;
778
779 if (buffer->_lenShift) return;
780 StringsBuffer_SetLengthBits(buffer, STRINGSBUFFER_DEF_LEN_SHIFT9);
781}
782
783void StringsBuffer_SetLengthBits(struct StringsBuffer* buffer, int bits) {
784 buffer->_lenShift = bits;
785 buffer->_lenMask = (1 << bits) - 1;
786}
787
788void StringsBuffer_Clear(struct StringsBuffer* buffer) {
789 /* Never initialised to begin with */
790 if (!buffer->_flagsCapacity) return;
791
792 if (buffer->textBuffer != buffer->_defaultBuffer) {
793 Mem_Free(buffer->textBuffer);
794 }
795 if (buffer->flagsBuffer != buffer->_defaultFlags) {
796 Mem_Free(buffer->flagsBuffer);
797 }
798 StringsBuffer_Init(buffer);
799}
800
801cc_string StringsBuffer_UNSAFE_Get(struct StringsBuffer* buffer, int i) {
802 cc_uint32 flags, offset, len;
803 if (i < 0 || i >= buffer->count) Logger_Abort("Tried to get String past StringsBuffer end");
804
805 flags = buffer->flagsBuffer[i];
806 offset = StringsBuffer_GetOffset(flags)((flags) >> buffer->_lenShift);
807 len = StringsBuffer_GetLength(flags)((flags) & buffer->_lenMask);
808 return String_Init(&buffer->textBuffer[offset], len, len);
809}
810
811void StringsBuffer_UNSAFE_GetRaw(struct StringsBuffer* buffer, int i, cc_string* dst) {
812 cc_uint32 flags = buffer->flagsBuffer[i];
813 dst->buffer = buffer->textBuffer + StringsBuffer_GetOffset(flags)((flags) >> buffer->_lenShift);
814 dst->length = StringsBuffer_GetLength(flags)((flags) & buffer->_lenMask);
815 dst->capacity = 0;
816}
817
818void StringsBuffer_Add(struct StringsBuffer* buffer, const cc_string* str) {
819 int textOffset;
820 /* StringsBuffer hasn't been initialised yet, do it here */
821 if (!buffer->_flagsCapacity) StringsBuffer_Init(buffer);
822
823 if (buffer->count == buffer->_flagsCapacity) {
824 Utils_Resize((void**)&buffer->flagsBuffer, &buffer->_flagsCapacity,
825 4, STRINGSBUFFER_FLAGS_DEF_ELEMS256, 512);
826 }
827
828 if (str->length > buffer->_lenMask) {
829 Logger_Abort("String too big to insert into StringsBuffer");
830 }
831
832 textOffset = buffer->totalLength;
833 if (textOffset + str->length >= buffer->_textCapacity) {
834 Utils_Resize((void**)&buffer->textBuffer, &buffer->_textCapacity,
835 1, STRINGSBUFFER_BUFFER_DEF_SIZE4096, 8192);
836 }
837
838 Mem_Copy(&buffer->textBuffer[textOffset], str->buffer, str->length);
839 buffer->flagsBuffer[buffer->count] = str->length | StringsBuffer_PackOffset(textOffset)((textOffset) << buffer->_lenShift);
840
841 buffer->count++;
842 buffer->totalLength += str->length;
843}
844
845void StringsBuffer_Remove(struct StringsBuffer* buffer, int index) {
846 cc_uint32 flags, offset, len;
847 cc_uint32 i, offsetAdj;
848 if (index < 0 || index >= buffer->count) Logger_Abort("Tried to remove String past StringsBuffer end");
849
850 flags = buffer->flagsBuffer[index];
851 offset = StringsBuffer_GetOffset(flags)((flags) >> buffer->_lenShift);
852 len = StringsBuffer_GetLength(flags)((flags) & buffer->_lenMask);
853
854 /* Imagine buffer is this: AAXXYYZZ, and want to delete XX */
855 /* We iterate from first char of Y to last char of Z, */
856 /* shifting each character two to the left. */
857 for (i = offset + len; i < buffer->totalLength; i++) {
858 buffer->textBuffer[i - len] = buffer->textBuffer[i];
859 }
860
861 /* Adjust text offset of elements after this element */
862 /* Elements may not be in order so must account for that */
863 offsetAdj = StringsBuffer_PackOffset(len)((len) << buffer->_lenShift);
864 for (i = index; i < buffer->count - 1; i++) {
865 buffer->flagsBuffer[i] = buffer->flagsBuffer[i + 1];
866 if (buffer->flagsBuffer[i] >= flags) {
867 buffer->flagsBuffer[i] -= offsetAdj;
868 }
869 }
870
871 buffer->count--;
872 buffer->totalLength -= len;
873}
874
875
876/*########################################################################################################################*
877*------------------------------------------------------Word wrapper-------------------------------------------------------*
878*#########################################################################################################################*/
879static cc_bool WordWrap_IsWrapper(char c) {
880 return c == '\0' || c == ' ' || c == '-' || c == '>' || c == '<' || c == '/' || c == '\\';
881}
882
883void WordWrap_Do(STRING_REF cc_string* text, cc_string* lines, int numLines, int lineLen) {
884 int i, lineStart, lineEnd;
885 for (i = 0; i < numLines; i++) { lines[i] = String_Empty; }
886
887 for (i = 0, lineStart = 0; i < numLines; i++) {
888 int nextLineStart = lineStart + lineLen;
889 /* No more text to wrap */
890 if (nextLineStart >= text->length) {
891 lines[i] = String_UNSAFE_SubstringAt(text, lineStart); return;
892 }
893
894 /* Find beginning of last word on current line */
895 for (lineEnd = nextLineStart; lineEnd >= lineStart; lineEnd--) {
896 if (WordWrap_IsWrapper(text->buffer[lineEnd])) break;
897 }
898 lineEnd++; /* move after wrapper char (i.e. beginning of last word) */
899
900 if (lineEnd <= lineStart || lineEnd >= nextLineStart) {
901 /* Three special cases handled by this: */
902 /* - Entire line is filled with a single word */
903 /* - Last character(s) on current line are wrapper characters */
904 /* - First character on next line is a wrapper character (last word ends at current line end) */
905 lines[i] = String_UNSAFE_Substring(text, lineStart, lineLen);
906 lineStart += lineLen;
907 } else {
908 /* Last word in current line does not end in current line (extends onto next line) */
909 /* Trim current line to end at beginning of last word */
910 /* Set next line to start at beginning of last word */
911 lines[i] = String_UNSAFE_Substring(text, lineStart, lineEnd - lineStart);
912 lineStart = lineEnd;
913 }
914 }
915}
916
917void WordWrap_GetCoords(int index, const cc_string* lines, int numLines, int* coordX, int* coordY) {
918 int y, offset = 0, lineLen;
919 if (index == -1) index = Int32_MaxValue((cc_int32)2147483647L);
920 *coordX = -1; *coordY = 0;
921
922 for (y = 0; y < numLines; y++) {
923 lineLen = lines[y].length;
924 if (!lineLen) break;
925
926 *coordY = y;
927 if (index < offset + lineLen) {
928 *coordX = index - offset; break;
929 }
930 offset += lineLen;
931 }
932 if (*coordX == -1) *coordX = lines[*coordY].length;
933}
934
935int WordWrap_GetBackLength(const cc_string* text, int index) {
936 int start = index;
937 if (index <= 0) return 0;
938 if (index >= text->length) Logger_Abort("WordWrap_GetBackLength - index past end of string");
939
940 /* Go backward to the end of the previous word */
941 while (index > 0 && text->buffer[index] == ' ') index--;
942 /* Go backward to the start of the current word */
943 while (index > 0 && text->buffer[index] != ' ') index--;
944
945 return start - index;
946}
947
948int WordWrap_GetForwardLength(const cc_string* text, int index) {
949 int start = index, length = text->length;
950 if (index == -1) return 0;
951 if (index >= text->length) Logger_Abort("WordWrap_GetForwardLength - index past end of string");
952
953 /* Go forward to the end of the word 'index' is currently in */
954 while (index < length && text->buffer[index] != ' ') index++;
955 /* Go forward to the start of the next word after 'index' */
956 while (index < length && text->buffer[index] == ' ') index++;
957
958 return index - start;
959}