Bug Summary

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