GCC Code Coverage Report


Directory: ./
File: string.c
Date: 2025-11-30 14:18:36
Exec Total Coverage
Lines: 492 492 100.0%
Functions: 58 58 100.0%
Branches: 307 326 94.2%

Line Branch Exec Source
1 /*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3 *
4 * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28 #ifdef MEMRCHR_NEED_GNU
29 #define _GNU_SOURCE
30 #endif
31
32 #include "cx/string.h"
33
34 #include <string.h>
35 #include <stdarg.h>
36 #include <assert.h>
37 #include <errno.h>
38 #include <limits.h>
39 #include <float.h>
40 #include <ctype.h>
41
42 #ifdef _WIN32
43 #define cx_strcasecmp_impl _strnicmp
44 #else
45 #include <strings.h>
46 #define cx_strcasecmp_impl strncasecmp
47 #endif
48
49 6 cxmutstr cx_mutstr(char *cstring) {
50
2/2
✓ Branch 0 (2→3) taken 5 times.
✓ Branch 1 (2→4) taken 1 times.
6 return (cxmutstr) {cstring, cstring == NULL ? 0 : strlen(cstring)};
51 }
52
53 190 cxmutstr cx_mutstrn(
54 char *cstring,
55 size_t length
56 ) {
57 190 return (cxmutstr) {cstring, length};
58 }
59
60 1664 cxstring cx_str(const char *cstring) {
61
2/2
✓ Branch 0 (2→3) taken 1660 times.
✓ Branch 1 (2→4) taken 4 times.
1664 return (cxstring) {cstring, cstring == NULL ? 0 : strlen(cstring)};
62 }
63
64 792 cxstring cx_strn(
65 const char *cstring,
66 size_t length
67 ) {
68 792 return (cxstring) {cstring, length};
69 }
70
71 126 void cx_strfree(cxmutstr *str) {
72
2/2
✓ Branch 0 (2→3) taken 1 times.
✓ Branch 1 (2→4) taken 125 times.
126 if (str == NULL) return;
73 125 cxFreeDefault(str->ptr);
74 125 str->ptr = NULL;
75 125 str->length = 0;
76 }
77
78 449 void cx_strfree_a(
79 const CxAllocator *alloc,
80 cxmutstr *str
81 ) {
82
2/2
✓ Branch 0 (2→3) taken 1 times.
✓ Branch 1 (2→4) taken 448 times.
449 if (str == NULL) return;
83 448 cxFree(alloc, str->ptr);
84 448 str->ptr = NULL;
85 448 str->length = 0;
86 }
87
88 4 int cx_strcpy_a(
89 const CxAllocator *alloc,
90 cxmutstr *dest,
91 cxstring src
92 ) {
93
1/2
✗ Branch 0 (3→4) not taken.
✓ Branch 1 (3→5) taken 4 times.
4 if (cxReallocate(alloc, &dest->ptr, src.length + 1)) {
94 return 1; // LCOV_EXCL_LINE
95 }
96
97 4 memcpy(dest->ptr, src.ptr, src.length);
98 4 dest->length = src.length;
99 4 dest->ptr[dest->length] = '\0';
100
101 4 return 0;
102 }
103
104 4 size_t cx_strlen(
105 size_t count,
106 ...
107 ) {
108
2/2
✓ Branch 0 (2→3) taken 1 times.
✓ Branch 1 (2→4) taken 3 times.
4 if (count == 0) return 0;
109
110 va_list ap;
111 3 va_start(ap, count);
112 3 size_t size = 0;
113
2/2
✓ Branch 0 (9→5) taken 6 times.
✓ Branch 1 (9→10) taken 3 times.
9 for (size_t i = 0; i < count; i++) {
114 6 cxstring str = va_arg(ap, cxstring);
115
1/2
✗ Branch 0 (6→7) not taken.
✓ Branch 1 (6→8) taken 6 times.
6 if (size > SIZE_MAX - str.length) errno = EOVERFLOW;
116 6 size += str.length;
117 }
118 3 va_end(ap);
119
120 3 return size;
121 }
122
123 21 cxmutstr cx_strcat_ma(
124 const CxAllocator *alloc,
125 cxmutstr str,
126 size_t count,
127 ...
128 ) {
129
1/2
✗ Branch 0 (2→3) not taken.
✓ Branch 1 (2→4) taken 21 times.
21 if (count == 0) return str;
130 va_list ap;
131 21 va_start(ap, count);
132 va_list ap2;
133 21 va_copy(ap2, ap);
134
135 // compute overall length
136 21 bool overflow = false;
137 21 size_t slen = str.length;
138
2/2
✓ Branch 0 (9→5) taken 43 times.
✓ Branch 1 (9→10) taken 21 times.
64 for (size_t i = 0; i < count; i++) {
139 43 cxstring s = va_arg(ap, cxstring);
140
2/2
✓ Branch 0 (6→7) taken 1 times.
✓ Branch 1 (6→8) taken 42 times.
43 if (slen > SIZE_MAX - s.length) overflow = true;
141 43 slen += s.length;
142 }
143 21 va_end(ap);
144
145 // abort in case of overflow
146
2/2
✓ Branch 0 (10→11) taken 1 times.
✓ Branch 1 (10→12) taken 20 times.
21 if (overflow) {
147 1 va_end(ap2);
148 1 errno = EOVERFLOW;
149 1 return (cxmutstr) { NULL, 0 };
150 }
151
152 // reallocate or create new string
153 char *newstr;
154
2/2
✓ Branch 0 (12→13) taken 6 times.
✓ Branch 1 (12→14) taken 14 times.
20 if (str.ptr == NULL) {
155 6 newstr = cxMalloc(alloc, slen + 1);
156 } else {
157 14 newstr = cxRealloc(alloc, str.ptr, slen + 1);
158 }
159 if (newstr == NULL) { // LCOV_EXCL_START
160 va_end(ap2);
161 return (cxmutstr) {NULL, 0};
162 } // LCOV_EXCL_STOP
163 20 str.ptr = newstr;
164
165 // concatenate strings
166 20 size_t pos = str.length;
167 20 str.length = slen;
168
2/2
✓ Branch 0 (20→18) taken 40 times.
✓ Branch 1 (20→21) taken 20 times.
60 for (size_t i = 0; i < count; i++) {
169 40 cxstring s = va_arg(ap2, cxstring);
170 40 memcpy(str.ptr + pos, s.ptr, s.length);
171 40 pos += s.length;
172 }
173 20 va_end(ap2);
174
175 // terminate string
176 20 str.ptr[str.length] = '\0';
177
178 20 return str;
179 }
180
181 327 cxstring cx_strsubs(
182 cxstring string,
183 size_t start
184 ) {
185 327 return cx_strsubsl(string, start, string.length - start);
186 }
187
188 1 cxmutstr cx_strsubs_m(
189 cxmutstr string,
190 size_t start
191 ) {
192 1 return cx_strsubsl_m(string, start, string.length - start);
193 }
194
195 347 cxstring cx_strsubsl(
196 cxstring string,
197 size_t start,
198 size_t length
199 ) {
200
2/2
✓ Branch 0 (2→3) taken 1 times.
✓ Branch 1 (2→4) taken 346 times.
347 if (start > string.length) {
201 1 return (cxstring) {NULL, 0};
202 }
203
204 346 size_t rem_len = string.length - start;
205
2/2
✓ Branch 0 (4→5) taken 2 times.
✓ Branch 1 (4→6) taken 344 times.
346 if (length > rem_len) {
206 2 length = rem_len;
207 }
208
209 346 return (cxstring) {string.ptr + start, length};
210 }
211
212 1 cxmutstr cx_strsubsl_m(
213 cxmutstr string,
214 size_t start,
215 size_t length
216 ) {
217 1 cxstring result = cx_strsubsl(cx_strcast(string), start, length);
218 1 return (cxmutstr) {(char *) result.ptr, result.length};
219 }
220
221 365 cxstring cx_strchr(
222 cxstring string,
223 int chr
224 ) {
225 365 char *ret = memchr(string.ptr, 0xFF & chr, string.length);
226
2/2
✓ Branch 0 (2→3) taken 29 times.
✓ Branch 1 (2→5) taken 336 times.
365 if (ret == NULL) return (cxstring) {NULL, 0};
227 336 return (cxstring) {ret, string.length - (ret - string.ptr)};
228 }
229
230 1 cxmutstr cx_strchr_m(
231 cxmutstr string,
232 int chr
233 ) {
234 1 cxstring result = cx_strchr(cx_strcast(string), chr);
235 1 return (cxmutstr) {(char *) result.ptr, result.length};
236 }
237
238 5 cxstring cx_strrchr(
239 cxstring string,
240 int chr
241 ) {
242 #ifdef WITH_MEMRCHR
243 5 char *ret = memrchr(string.ptr, 0xFF & chr, string.length);
244
2/2
✓ Branch 0 (2→3) taken 2 times.
✓ Branch 1 (2→5) taken 3 times.
5 if (ret == NULL) return (cxstring) {NULL, 0};
245 3 return (cxstring) {ret, string.length - (ret - string.ptr)};
246 #else
247 chr = 0xFF & chr;
248 size_t i = string.length;
249 while (i > 0) {
250 i--;
251 if (string.ptr[i] == chr) {
252 return cx_strsubs(string, i);
253 }
254 }
255 return (cxstring) {NULL, 0};
256 #endif
257 }
258
259 1 cxmutstr cx_strrchr_m(
260 cxmutstr string,
261 int chr
262 ) {
263 1 cxstring result = cx_strrchr(cx_strcast(string), chr);
264 1 return (cxmutstr) {(char *) result.ptr, result.length};
265 }
266
267 #ifndef CX_STRSTR_SBO_SIZE
268 #define CX_STRSTR_SBO_SIZE 128
269 #endif
270 const unsigned cx_strstr_sbo_size = CX_STRSTR_SBO_SIZE;
271
272 439 cxstring cx_strstr(
273 cxstring haystack,
274 cxstring needle
275 ) {
276
2/2
✓ Branch 0 (2→3) taken 9 times.
✓ Branch 1 (2→4) taken 430 times.
439 if (needle.length == 0) {
277 9 return haystack;
278 }
279
280 // optimize for single-char needles
281
2/2
✓ Branch 0 (4→5) taken 338 times.
✓ Branch 1 (4→7) taken 92 times.
430 if (needle.length == 1) {
282 338 return cx_strchr(haystack, *needle.ptr);
283 }
284
285 /*
286 * IMPORTANT:
287 * Our prefix table contains the prefix length PLUS ONE
288 * this is our decision, because we want to use the full range of size_t.
289 * The original algorithm needs a (-1) at one single place,
290 * and we want to avoid that.
291 */
292
293 // local prefix table
294 size_t s_prefix_table[CX_STRSTR_SBO_SIZE];
295
296 // check needle length and use appropriate prefix table
297 // if the pattern exceeds static prefix table, allocate on the heap
298 92 const bool useheap = needle.length >= CX_STRSTR_SBO_SIZE;
299 91 register size_t *ptable = useheap
300 1 ? cxCallocDefault(needle.length + 1, sizeof(size_t))
301
2/2
✓ Branch 0 (7→8) taken 1 times.
✓ Branch 1 (7→9) taken 91 times.
92 : s_prefix_table;
302
303 // keep counter in registers
304 register size_t i, j;
305
306 // fill prefix table
307 92 i = 0;
308 92 j = 0;
309 92 ptable[i] = j;
310
2/2
✓ Branch 0 (16→11) taken 614 times.
✓ Branch 1 (16→17) taken 92 times.
706 while (i < needle.length) {
311
4/4
✓ Branch 0 (13→14) taken 536 times.
✓ Branch 1 (13→15) taken 384 times.
✓ Branch 2 (14→12) taken 306 times.
✓ Branch 3 (14→15) taken 230 times.
920 while (j >= 1 && needle.ptr[j - 1] != needle.ptr[i]) {
312 306 j = ptable[j - 1];
313 }
314 614 i++;
315 614 j++;
316 614 ptable[i] = j;
317 }
318
319 // search
320 92 cxstring result = {NULL, 0};
321 92 i = 0;
322 92 j = 1;
323
2/2
✓ Branch 0 (24→18) taken 1211 times.
✓ Branch 1 (24→25) taken 32 times.
1243 while (i < haystack.length) {
324
4/4
✓ Branch 0 (20→21) taken 1269 times.
✓ Branch 1 (20→22) taken 664 times.
✓ Branch 2 (21→19) taken 722 times.
✓ Branch 3 (21→22) taken 547 times.
1933 while (j >= 1 && haystack.ptr[i] != needle.ptr[j - 1]) {
325 722 j = ptable[j - 1];
326 }
327 1211 i++;
328 1211 j++;
329
2/2
✓ Branch 0 (22→23) taken 60 times.
✓ Branch 1 (22→24) taken 1151 times.
1211 if (j - 1 == needle.length) {
330 60 size_t start = i - needle.length;
331 60 result.ptr = haystack.ptr + start;
332 60 result.length = haystack.length - start;
333 60 break;
334 }
335 }
336
337 // if prefix table was allocated on the heap, free it
338
2/2
✓ Branch 0 (25→26) taken 1 times.
✓ Branch 1 (25→27) taken 91 times.
92 if (useheap) {
339 1 cxFreeDefault(ptable);
340 }
341
342 92 return result;
343 }
344
345 1 cxmutstr cx_strstr_m(
346 cxmutstr haystack,
347 cxstring needle
348 ) {
349 1 cxstring result = cx_strstr(cx_strcast(haystack), needle);
350 1 return (cxmutstr) {(char *) result.ptr, result.length};
351 }
352
353 28 size_t cx_strsplit(
354 cxstring string,
355 cxstring delim,
356 size_t limit,
357 cxstring *output
358 ) {
359 // special case: output limit is zero
360
1/2
✗ Branch 0 (2→3) not taken.
✓ Branch 1 (2→4) taken 28 times.
28 if (limit == 0) return 0;
361
362 // special case: delimiter is empty
363
2/2
✓ Branch 0 (4→5) taken 2 times.
✓ Branch 1 (4→6) taken 26 times.
28 if (delim.length == 0) {
364 2 output[0] = string;
365 2 return 1;
366 }
367
368 // special cases: delimiter is at least as large as the string
369
2/2
✓ Branch 0 (6→7) taken 4 times.
✓ Branch 1 (6→17) taken 22 times.
26 if (delim.length >= string.length) {
370 // exact match
371
2/2
✓ Branch 0 (12→13) taken 2 times.
✓ Branch 1 (12→16) taken 2 times.
8 if (cx_strcmp(string, delim) == 0) {
372 2 output[0] = cx_strn(string.ptr, 0);
373 2 output[1] = cx_strn(string.ptr + string.length, 0);
374 2 return 2;
375 } else {
376 // no match possible
377 2 output[0] = string;
378 2 return 1;
379 }
380 }
381
382 22 size_t n = 0;
383 22 cxstring curpos = string;
384 34 while (1) {
385 56 ++n;
386 56 cxstring match = cx_strstr(curpos, delim);
387
2/2
✓ Branch 0 (19→20) taken 40 times.
✓ Branch 1 (19→24) taken 16 times.
56 if (match.length > 0) {
388 // is the limit reached?
389
2/2
✓ Branch 0 (20→21) taken 34 times.
✓ Branch 1 (20→23) taken 6 times.
40 if (n < limit) {
390 // copy the current string to the array
391 34 cxstring item = cx_strn(curpos.ptr, match.ptr - curpos.ptr);
392 34 output[n - 1] = item;
393 34 size_t processed = item.length + delim.length;
394 34 curpos.ptr += processed;
395 34 curpos.length -= processed;
396 } else {
397 // limit reached, copy the _full_ remaining string
398 6 output[n - 1] = curpos;
399 6 break;
400 }
401 } else {
402 // no more matches, copy last string
403 16 output[n - 1] = curpos;
404 16 break;
405 }
406 }
407
408 22 return n;
409 }
410
411 14 size_t cx_strsplit_a(
412 const CxAllocator *allocator,
413 cxstring string,
414 cxstring delim,
415 size_t limit,
416 cxstring **output
417 ) {
418 // find out how many splits we're going to make and allocate memory
419 14 size_t n = 0;
420 14 cxstring curpos = string;
421 25 while (1) {
422 39 ++n;
423 39 cxstring match = cx_strstr(curpos, delim);
424
2/2
✓ Branch 0 (4→5) taken 29 times.
✓ Branch 1 (4→8) taken 10 times.
39 if (match.length > 0) {
425 // is the limit reached?
426
2/2
✓ Branch 0 (5→6) taken 25 times.
✓ Branch 1 (5→7) taken 4 times.
29 if (n < limit) {
427 25 size_t processed = match.ptr - curpos.ptr + delim.length;
428 25 curpos.ptr += processed;
429 25 curpos.length -= processed;
430 } else {
431 // limit reached
432 4 break;
433 }
434 } else {
435 // no more matches
436 10 break;
437 }
438 }
439 14 *output = cxCalloc(allocator, n, sizeof(cxstring));
440 14 return cx_strsplit(string, delim, n, *output);
441 }
442
443 1 size_t cx_strsplit_m(
444 cxmutstr string,
445 cxstring delim,
446 size_t limit,
447 cxmutstr *output
448 ) {
449 1 return cx_strsplit(cx_strcast(string),
450 delim, limit, (cxstring *) output);
451 }
452
453 1 size_t cx_strsplit_ma(
454 const CxAllocator *allocator,
455 cxmutstr string,
456 cxstring delim,
457 size_t limit,
458 cxmutstr **output
459 ) {
460 1 return cx_strsplit_a(allocator, cx_strcast(string),
461 delim, limit, (cxstring **) output);
462 }
463
464 4081 int cx_strcmp_(
465 cxstring s1,
466 cxstring s2
467 ) {
468
2/2
✓ Branch 0 (2→3) taken 1747 times.
✓ Branch 1 (2→4) taken 2334 times.
4081 if (s1.length == s2.length) {
469 1747 return strncmp(s1.ptr, s2.ptr, s1.length);
470
2/2
✓ Branch 0 (4→5) taken 1728 times.
✓ Branch 1 (4→8) taken 606 times.
2334 } else if (s1.length > s2.length) {
471 1728 int r = strncmp(s1.ptr, s2.ptr, s2.length);
472
2/2
✓ Branch 0 (5→6) taken 1646 times.
✓ Branch 1 (5→7) taken 82 times.
1728 if (r != 0) return r;
473 82 return 1;
474 } else {
475 606 int r = strncmp(s1.ptr, s2.ptr, s1.length);
476
2/2
✓ Branch 0 (8→9) taken 573 times.
✓ Branch 1 (8→10) taken 33 times.
606 if (r != 0) return r;
477 33 return -1;
478 }
479 }
480
481 14 int cx_strcasecmp_(
482 cxstring s1,
483 cxstring s2
484 ) {
485
2/2
✓ Branch 0 (2→3) taken 7 times.
✓ Branch 1 (2→4) taken 7 times.
14 if (s1.length == s2.length) {
486 7 return cx_strcasecmp_impl(s1.ptr, s2.ptr, s1.length);
487
2/2
✓ Branch 0 (4→5) taken 4 times.
✓ Branch 1 (4→8) taken 3 times.
7 } else if (s1.length > s2.length) {
488 4 int r = cx_strcasecmp_impl(s1.ptr, s2.ptr, s2.length);
489
2/2
✓ Branch 0 (5→6) taken 2 times.
✓ Branch 1 (5→7) taken 2 times.
4 if (r != 0) return r;
490 2 return 1;
491 } else {
492 3 int r = cx_strcasecmp_impl(s1.ptr, s2.ptr, s1.length);
493
2/2
✓ Branch 0 (8→9) taken 2 times.
✓ Branch 1 (8→10) taken 1 times.
3 if (r != 0) return r;
494 1 return -1;
495 }
496 }
497
498 4 int cx_strcmp_p(
499 const void *s1,
500 const void *s2
501 ) {
502 4 const cxstring *left = s1;
503 4 const cxstring *right = s2;
504 8 return cx_strcmp(*left, *right);
505 }
506
507 2 int cx_strcasecmp_p(
508 const void *s1,
509 const void *s2
510 ) {
511 2 const cxstring *left = s1;
512 2 const cxstring *right = s2;
513 4 return cx_strcasecmp(*left, *right);
514 }
515
516 612 cxmutstr cx_strdup_a_(
517 const CxAllocator *allocator,
518 cxstring string
519 ) {
520 612 cxmutstr result = {
521 612 cxMalloc(allocator, string.length + 1),
522 612 string.length
523 };
524 // LCOV_EXCL_START
525 if (result.ptr == NULL) {
526 result.length = 0;
527 return result;
528 }
529 // LCOV_EXCL_STOP
530 612 memcpy(result.ptr, string.ptr, string.length);
531 612 result.ptr[string.length] = '\0';
532 612 return result;
533 }
534
535 202 cxstring cx_strtrim(cxstring string) {
536 202 cxstring result = string;
537
4/4
✓ Branch 0 (4→5) taken 269 times.
✓ Branch 1 (4→6) taken 74 times.
✓ Branch 2 (5→3) taken 141 times.
✓ Branch 3 (5→6) taken 128 times.
343 while (result.length > 0 && isspace((unsigned char)(result.ptr[0]))) {
538 141 result.ptr++;
539 141 result.length--;
540 }
541
4/4
✓ Branch 0 (8→9) taken 228 times.
✓ Branch 1 (8→10) taken 74 times.
✓ Branch 2 (9→7) taken 100 times.
✓ Branch 3 (9→10) taken 128 times.
302 while (result.length > 0 && isspace((unsigned char)result.ptr[result.length - 1])) {
542 100 result.length--;
543 }
544 202 return result;
545 }
546
547 1 cxmutstr cx_strtrim_m(cxmutstr string) {
548 1 cxstring result = cx_strtrim(cx_strcast(string));
549 1 return (cxmutstr) {(char *) result.ptr, result.length};
550 }
551
552 5 bool cx_strprefix_(
553 cxstring string,
554 cxstring prefix
555 ) {
556
2/2
✓ Branch 0 (2→3) taken 1 times.
✓ Branch 1 (2→4) taken 4 times.
5 if (string.length < prefix.length) return false;
557 4 return memcmp(string.ptr, prefix.ptr, prefix.length) == 0;
558 }
559
560 5 bool cx_strsuffix_(
561 cxstring string,
562 cxstring suffix
563 ) {
564
2/2
✓ Branch 0 (2→3) taken 1 times.
✓ Branch 1 (2→4) taken 4 times.
5 if (string.length < suffix.length) return false;
565 4 return memcmp(string.ptr + string.length - suffix.length,
566 4 suffix.ptr, suffix.length) == 0;
567 }
568
569 5 bool cx_strcaseprefix_(
570 cxstring string,
571 cxstring prefix
572 ) {
573
2/2
✓ Branch 0 (2→3) taken 1 times.
✓ Branch 1 (2→4) taken 4 times.
5 if (string.length < prefix.length) return false;
574 #ifdef _WIN32
575 return _strnicmp(string.ptr, prefix.ptr, prefix.length) == 0;
576 #else
577 4 return strncasecmp(string.ptr, prefix.ptr, prefix.length) == 0;
578 #endif
579 }
580
581 5 bool cx_strcasesuffix_(
582 cxstring string,
583 cxstring suffix
584 ) {
585
2/2
✓ Branch 0 (2→3) taken 1 times.
✓ Branch 1 (2→4) taken 4 times.
5 if (string.length < suffix.length) return false;
586 #ifdef _WIN32
587 return _strnicmp(string.ptr+string.length-suffix.length,
588 suffix.ptr, suffix.length) == 0;
589 #else
590 4 return strncasecmp(string.ptr + string.length - suffix.length,
591 4 suffix.ptr, suffix.length) == 0;
592 #endif
593 }
594
595 15 cxmutstr cx_strreplacen_a(
596 const CxAllocator *allocator,
597 cxstring str,
598 cxstring search,
599 cxstring replacement,
600 size_t replmax
601 ) {
602 // special cases
603
4/6
✓ Branch 0 (2→3) taken 15 times.
✗ Branch 1 (2→5) not taken.
✓ Branch 2 (3→4) taken 14 times.
✓ Branch 3 (3→5) taken 1 times.
✗ Branch 4 (4→5) not taken.
✓ Branch 5 (4→9) taken 14 times.
15 if (search.length == 0 || search.length > str.length || replmax == 0) {
604 1 return cx_strdup_a(allocator, str);
605 }
606
607 14 size_t in_len = str.length;
608 14 size_t search_len = search.length;
609 14 size_t repl_len = replacement.length;
610
611 // first run, count the occurrences
612 // and remember where the first is
613 14 size_t occurrences = 1;
614 14 cxstring first = cx_strstr(str, search);
615
2/2
✓ Branch 0 (10→11) taken 1 times.
✓ Branch 1 (10→15) taken 13 times.
14 if (first.length == 0) {
616 // special case, no replacements
617 1 return cx_strdup_a(allocator, str);
618 }
619 13 cxstring tmp = cx_strsubs(first, search_len);
620
2/2
✓ Branch 0 (18→19) taken 149 times.
✓ Branch 1 (18→21) taken 5 times.
154 while (occurrences < replmax &&
621
2/2
✓ Branch 0 (20→17) taken 141 times.
✓ Branch 1 (20→21) taken 8 times.
149 (tmp = cx_strstr(tmp, search)).length > 0) {
622 141 occurrences++;
623 141 tmp = cx_strsubs(tmp, search_len);
624 }
625
626 // calculate necessary memory
627 13 signed long long diff_len = (signed long long) repl_len - search_len;
628 13 size_t out_len = in_len + diff_len * occurrences;
629 13 cxmutstr out = {
630 13 cxMalloc(allocator, out_len + 1),
631 out_len
632 };
633
1/2
✗ Branch 0 (22→23) not taken.
✓ Branch 1 (22→24) taken 13 times.
13 if (out.ptr == NULL) return out;
634
635 // second run: perform the replacements
636 // but start where we found the first occurrence
637 13 const char *inp = str.ptr;
638 13 tmp = first;
639 13 char *outp = out.ptr;
640
3/4
✓ Branch 0 (26→27) taken 154 times.
✓ Branch 1 (26→29) taken 13 times.
✓ Branch 2 (28→25) taken 154 times.
✗ Branch 3 (28→29) not taken.
167 while (occurrences-- > 0 && (tmp = cx_strstr(tmp, search)).length > 0) {
641 154 size_t copylen = tmp.ptr - inp;
642 154 memcpy(outp, inp, copylen);
643 154 outp += copylen;
644 154 memcpy(outp, replacement.ptr, repl_len);
645 154 outp += repl_len;
646 154 inp += copylen + search_len;
647 154 tmp = cx_strsubs(tmp, search_len);
648 }
649
650 // add the remaining string
651 13 size_t copylen = in_len - (inp - str.ptr);
652 13 memcpy(outp, inp, copylen);
653 13 out.ptr[out_len] = '\0';
654
655 13 return out;
656 }
657
658 5 CxStrtokCtx cx_strtok_(
659 cxstring str,
660 cxstring delim,
661 size_t limit
662 ) {
663 CxStrtokCtx ctx;
664 5 ctx.str = str;
665 5 ctx.delim = delim;
666 5 ctx.limit = limit;
667 5 ctx.pos = 0;
668 5 ctx.next_pos = 0;
669 5 ctx.delim_pos = 0;
670 5 ctx.found = 0;
671 5 ctx.delim_more = NULL;
672 5 ctx.delim_more_count = 0;
673 5 return ctx;
674 }
675
676 16 bool cx_strtok_next(
677 CxStrtokCtx *ctx,
678 cxstring *token
679 ) {
680 // abortion criteria
681
4/4
✓ Branch 0 (2→3) taken 15 times.
✓ Branch 1 (2→4) taken 1 times.
✓ Branch 2 (3→4) taken 2 times.
✓ Branch 3 (3→5) taken 13 times.
16 if (ctx->found >= ctx->limit || ctx->delim_pos >= ctx->str.length) {
682 3 return false;
683 }
684
685 // determine the search start
686 13 cxstring haystack = cx_strsubs(ctx->str, ctx->next_pos);
687
688 // search the next delimiter
689 13 cxstring delim = cx_strstr(haystack, ctx->delim);
690
691 // if found, make delim capture exactly the delimiter
692
2/2
✓ Branch 0 (7→8) taken 8 times.
✓ Branch 1 (7→9) taken 5 times.
13 if (delim.length > 0) {
693 8 delim.length = ctx->delim.length;
694 }
695
696 // if more delimiters are specified, check them now
697
2/2
✓ Branch 0 (9→10) taken 5 times.
✓ Branch 1 (9→18) taken 8 times.
13 if (ctx->delim_more_count > 0) {
698
2/2
✓ Branch 0 (17→11) taken 10 times.
✓ Branch 1 (17→18) taken 5 times.
15 for (size_t i = 0; i < ctx->delim_more_count; i++) {
699 10 cxstring d = cx_strstr(haystack, ctx->delim_more[i]);
700
6/6
✓ Branch 0 (12→13) taken 7 times.
✓ Branch 1 (12→16) taken 3 times.
✓ Branch 2 (13→14) taken 4 times.
✓ Branch 3 (13→15) taken 3 times.
✓ Branch 4 (14→15) taken 1 times.
✓ Branch 5 (14→16) taken 3 times.
10 if (d.length > 0 && (delim.length == 0 || d.ptr < delim.ptr)) {
701 4 delim.ptr = d.ptr;
702 4 delim.length = ctx->delim_more[i].length;
703 }
704 }
705 }
706
707 // store the token information and adjust the context
708 13 ctx->found++;
709 13 ctx->pos = ctx->next_pos;
710 13 token->ptr = &ctx->str.ptr[ctx->pos];
711 26 ctx->delim_pos = delim.length == 0 ?
712
2/2
✓ Branch 0 (18→19) taken 2 times.
✓ Branch 1 (18→20) taken 11 times.
13 ctx->str.length : (size_t) (delim.ptr - ctx->str.ptr);
713 13 token->length = ctx->delim_pos - ctx->pos;
714 13 ctx->next_pos = ctx->delim_pos + delim.length;
715
716 13 return true;
717 }
718
719 6 bool cx_strtok_next_m(
720 CxStrtokCtx *ctx,
721 cxmutstr *token
722 ) {
723 6 return cx_strtok_next(ctx, (cxstring *) token);
724 }
725
726 2 void cx_strtok_delim(
727 CxStrtokCtx *ctx,
728 const cxstring *delim,
729 size_t count
730 ) {
731 2 ctx->delim_more = delim;
732 2 ctx->delim_more_count = count;
733 2 }
734
735 #define cx_strtoX_signed_impl(rtype, rmin, rmax) \
736 long long result; \
737 if (cx_strtoll_lc(str, &result, base, groupsep)) { \
738 return -1; \
739 } \
740 if (result < rmin || result > rmax) { \
741 errno = ERANGE; \
742 return -1; \
743 } \
744 *output = (rtype) result; \
745 return 0
746
747 52 int cx_strtos_lc_(cxstring str, short *output, int base, const char *groupsep) {
748
5/6
✗ Branch 0 (5→6) not taken.
✓ Branch 1 (5→7) taken 52 times.
✓ Branch 2 (7→8) taken 35 times.
✓ Branch 3 (7→9) taken 17 times.
✓ Branch 4 (8→9) taken 17 times.
✓ Branch 5 (8→10) taken 18 times.
52 cx_strtoX_signed_impl(short, SHRT_MIN, SHRT_MAX);
749 }
750
751 52 int cx_strtoi_lc_(cxstring str, int *output, int base, const char *groupsep) {
752
5/6
✗ Branch 0 (5→6) not taken.
✓ Branch 1 (5→7) taken 52 times.
✓ Branch 2 (7→8) taken 44 times.
✓ Branch 3 (7→9) taken 8 times.
✓ Branch 4 (8→9) taken 8 times.
✓ Branch 5 (8→10) taken 36 times.
52 cx_strtoX_signed_impl(int, INT_MIN, INT_MAX);
753 }
754
755 52 int cx_strtol_lc_(cxstring str, long *output, int base, const char *groupsep) {
756
1/2
✗ Branch 0 (5→6) not taken.
✓ Branch 1 (5→7) taken 52 times.
52 cx_strtoX_signed_impl(long, LONG_MIN, LONG_MAX);
757 }
758
759 479 int cx_strtoll_lc_(cxstring str, long long *output, int base, const char *groupsep) {
760 // strategy: parse as unsigned, check range, negate if required
761 479 bool neg = false;
762 479 size_t start_unsigned = 0;
763
764 // emptiness check
765
2/2
✓ Branch 0 (2→3) taken 2 times.
✓ Branch 1 (2→4) taken 477 times.
479 if (str.length == 0) {
766 2 errno = EINVAL;
767 2 return -1;
768 }
769
770 // test if we have a negative sign character
771
2/2
✓ Branch 0 (4→5) taken 223 times.
✓ Branch 1 (4→8) taken 254 times.
477 if (str.ptr[start_unsigned] == '-') {
772 223 neg = true;
773 223 start_unsigned++;
774 // must not be followed by positive sign character
775
4/4
✓ Branch 0 (5→6) taken 222 times.
✓ Branch 1 (5→7) taken 1 times.
✓ Branch 2 (6→7) taken 1 times.
✓ Branch 3 (6→8) taken 221 times.
223 if (str.length == 1 || str.ptr[start_unsigned] == '+') {
776 2 errno = EINVAL;
777 2 return -1;
778 }
779 }
780
781 // now parse the number with strtoull
782 unsigned long long v;
783 254 cxstring ustr = start_unsigned == 0 ? str
784
2/2
✓ Branch 0 (8→9) taken 221 times.
✓ Branch 1 (8→10) taken 254 times.
475 : cx_strn(str.ptr + start_unsigned, str.length - start_unsigned);
785 475 int ret = cx_strtoull_lc(ustr, &v, base, groupsep);
786
2/2
✓ Branch 0 (14→15) taken 9 times.
✓ Branch 1 (14→16) taken 466 times.
475 if (ret != 0) return ret;
787
2/2
✓ Branch 0 (16→17) taken 218 times.
✓ Branch 1 (16→20) taken 248 times.
466 if (neg) {
788
2/2
✓ Branch 0 (17→18) taken 1 times.
✓ Branch 1 (17→19) taken 217 times.
218 if (v - 1 > LLONG_MAX) {
789 1 errno = ERANGE;
790 1 return -1;
791 }
792 217 *output = -(long long) v;
793 217 return 0;
794 } else {
795
2/2
✓ Branch 0 (20→21) taken 2 times.
✓ Branch 1 (20→22) taken 246 times.
248 if (v > LLONG_MAX) {
796 2 errno = ERANGE;
797 2 return -1;
798 }
799 246 *output = (long long) v;
800 246 return 0;
801 }
802 }
803
804 55 int cx_strtoi8_lc_(cxstring str, int8_t *output, int base, const char *groupsep) {
805
5/6
✗ Branch 0 (5→6) not taken.
✓ Branch 1 (5→7) taken 55 times.
✓ Branch 2 (7→8) taken 32 times.
✓ Branch 3 (7→9) taken 23 times.
✓ Branch 4 (8→9) taken 23 times.
✓ Branch 5 (8→10) taken 9 times.
55 cx_strtoX_signed_impl(int8_t, INT8_MIN, INT8_MAX);
806 }
807
808 57 int cx_strtoi16_lc_(cxstring str, int16_t *output, int base, const char *groupsep) {
809
6/6
✓ Branch 0 (5→6) taken 1 times.
✓ Branch 1 (5→7) taken 56 times.
✓ Branch 2 (7→8) taken 38 times.
✓ Branch 3 (7→9) taken 18 times.
✓ Branch 4 (8→9) taken 18 times.
✓ Branch 5 (8→10) taken 20 times.
57 cx_strtoX_signed_impl(int16_t, INT16_MIN, INT16_MAX);
810 }
811
812 53 int cx_strtoi32_lc_(cxstring str, int32_t *output, int base, const char *groupsep) {
813
5/6
✗ Branch 0 (5→6) not taken.
✓ Branch 1 (5→7) taken 53 times.
✓ Branch 2 (7→8) taken 45 times.
✓ Branch 3 (7→9) taken 8 times.
✓ Branch 4 (8→9) taken 8 times.
✓ Branch 5 (8→10) taken 37 times.
53 cx_strtoX_signed_impl(int32_t, INT32_MIN, INT32_MAX);
814 }
815
816 91 int cx_strtoi64_lc_(cxstring str, int64_t *output, int base, const char *groupsep) {
817 assert(sizeof(long long) == sizeof(int64_t)); // should be true on all platforms
818 91 return cx_strtoll_lc(str, (long long*) output, base, groupsep);
819 }
820
821 #define cx_strtoX_unsigned_impl(rtype, rmax) \
822 uint64_t result; \
823 if (cx_strtou64_lc(str, &result, base, groupsep)) { \
824 return -1; \
825 } \
826 if (result > rmax) { \
827 errno = ERANGE; \
828 return -1; \
829 } \
830 *output = (rtype) result; \
831 return 0
832
833 32 int cx_strtous_lc_(cxstring str, unsigned short *output, int base, const char *groupsep) {
834
3/4
✗ Branch 0 (5→6) not taken.
✓ Branch 1 (5→7) taken 32 times.
✓ Branch 2 (7→8) taken 14 times.
✓ Branch 3 (7→9) taken 18 times.
32 cx_strtoX_unsigned_impl(unsigned short, USHRT_MAX);
835 }
836
837 32 int cx_strtou_lc_(cxstring str, unsigned int *output, int base, const char *groupsep) {
838
3/4
✗ Branch 0 (5→6) not taken.
✓ Branch 1 (5→7) taken 32 times.
✓ Branch 2 (7→8) taken 5 times.
✓ Branch 3 (7→9) taken 27 times.
32 cx_strtoX_unsigned_impl(unsigned int, UINT_MAX);
839 }
840
841 32 int cx_strtoul_lc_(cxstring str, unsigned long *output, int base, const char *groupsep) {
842
1/2
✗ Branch 0 (5→6) not taken.
✓ Branch 1 (5→7) taken 32 times.
32 cx_strtoX_unsigned_impl(unsigned long, ULONG_MAX);
843 }
844
845 808 int cx_strtoull_lc_(cxstring str, unsigned long long *output, int base, const char *groupsep) {
846 // some sanity checks
847
2/2
✓ Branch 0 (2→3) taken 2 times.
✓ Branch 1 (2→4) taken 806 times.
808 if (str.length == 0) {
848 2 errno = EINVAL;
849 2 return -1;
850 }
851
8/8
✓ Branch 0 (4→5) taken 706 times.
✓ Branch 1 (4→9) taken 100 times.
✓ Branch 2 (5→6) taken 497 times.
✓ Branch 3 (5→9) taken 209 times.
✓ Branch 4 (6→7) taken 245 times.
✓ Branch 5 (6→9) taken 252 times.
✓ Branch 6 (7→8) taken 2 times.
✓ Branch 7 (7→9) taken 243 times.
806 if (!(base == 2 || base == 8 || base == 10 || base == 16)) {
852 2 errno = EINVAL;
853 2 return -1;
854 }
855
1/2
✗ Branch 0 (9→10) not taken.
✓ Branch 1 (9→11) taken 804 times.
804 if (groupsep == NULL) groupsep = "";
856
857 // find the actual start of the number
858
2/2
✓ Branch 0 (11→12) taken 4 times.
✓ Branch 1 (11→14) taken 800 times.
804 if (str.ptr[0] == '+') {
859 4 str.ptr++;
860 4 str.length--;
861
2/2
✓ Branch 0 (12→13) taken 1 times.
✓ Branch 1 (12→14) taken 3 times.
4 if (str.length == 0) {
862 1 errno = EINVAL;
863 1 return -1;
864 }
865 }
866 803 size_t start = 0;
867
868 // if base is 2 or 16, some leading stuff may appear
869
2/2
✓ Branch 0 (14→15) taken 100 times.
✓ Branch 1 (14→21) taken 703 times.
803 if (base == 2) {
870
2/2
✓ Branch 0 (15→16) taken 1 times.
✓ Branch 1 (15→17) taken 99 times.
100 if ((str.ptr[0] | 32) == 'b') {
871 1 start = 1;
872
4/4
✓ Branch 0 (17→18) taken 95 times.
✓ Branch 1 (17→29) taken 4 times.
✓ Branch 2 (18→19) taken 86 times.
✓ Branch 3 (18→29) taken 9 times.
99 } else if (str.ptr[0] == '0' && str.length > 1) {
873
2/2
✓ Branch 0 (19→20) taken 85 times.
✓ Branch 1 (19→29) taken 1 times.
86 if ((str.ptr[1] | 32) == 'b') {
874 85 start = 2;
875 }
876 }
877
2/2
✓ Branch 0 (21→22) taken 243 times.
✓ Branch 1 (21→29) taken 460 times.
703 } else if (base == 16) {
878
4/4
✓ Branch 0 (22→23) taken 237 times.
✓ Branch 1 (22→24) taken 6 times.
✓ Branch 2 (23→24) taken 1 times.
✓ Branch 3 (23→25) taken 236 times.
243 if ((str.ptr[0] | 32) == 'x' || str.ptr[0] == '#') {
879 7 start = 1;
880
4/4
✓ Branch 0 (25→26) taken 219 times.
✓ Branch 1 (25→29) taken 17 times.
✓ Branch 2 (26→27) taken 210 times.
✓ Branch 3 (26→29) taken 9 times.
236 } else if (str.ptr[0] == '0' && str.length > 1) {
881
2/2
✓ Branch 0 (27→28) taken 197 times.
✓ Branch 1 (27→29) taken 13 times.
210 if ((str.ptr[1] | 32) == 'x') {
882 197 start = 2;
883 }
884 }
885 }
886
887 // check if there are digits left
888
2/2
✓ Branch 0 (29→30) taken 5 times.
✓ Branch 1 (29→31) taken 798 times.
803 if (start >= str.length) {
889 5 errno = EINVAL;
890 5 return -1;
891 }
892
893 // now parse the number
894 798 unsigned long long result = 0;
895
2/2
✓ Branch 0 (47→32) taken 7281 times.
✓ Branch 1 (47→48) taken 786 times.
8067 for (size_t i = start; i < str.length; i++) {
896 // ignore group separators
897
2/2
✓ Branch 0 (32→33) taken 13 times.
✓ Branch 1 (32→34) taken 7268 times.
7281 if (strchr(groupsep, str.ptr[i])) continue;
898
899 // determine the digit value of the character
900 7268 unsigned char c = str.ptr[i];
901
2/2
✓ Branch 0 (34→35) taken 275 times.
✓ Branch 1 (34→36) taken 6993 times.
7268 if (c >= 'a') c = 10 + (c - 'a');
902
2/2
✓ Branch 0 (36→37) taken 425 times.
✓ Branch 1 (36→38) taken 6568 times.
6993 else if (c >= 'A') c = 10 + (c - 'A');
903
2/2
✓ Branch 0 (38→39) taken 6557 times.
✓ Branch 1 (38→40) taken 11 times.
6568 else if (c >= '0') c = c - '0';
904 11 else c = 255;
905
2/2
✓ Branch 0 (41→42) taken 11 times.
✓ Branch 1 (41→43) taken 7257 times.
7268 if (c >= base) {
906 11 errno = EINVAL;
907 11 return -1;
908 }
909
910 // now combine the digit with what we already have
911 7257 unsigned long right = (result & 0xff) * base + c;
912 7257 unsigned long long left = (result >> 8) * base + (right >> 8);
913
2/2
✓ Branch 0 (43→44) taken 1 times.
✓ Branch 1 (43→45) taken 7256 times.
7257 if (left > (ULLONG_MAX >> 8)) {
914 1 errno = ERANGE;
915 1 return -1;
916 }
917 7256 result = (left << 8) + (right & 0xff);
918 }
919
920 786 *output = result;
921 786 return 0;
922 }
923
924 34 int cx_strtou8_lc_(cxstring str, uint8_t *output, int base, const char *groupsep) {
925
3/4
✗ Branch 0 (5→6) not taken.
✓ Branch 1 (5→7) taken 34 times.
✓ Branch 2 (7→8) taken 21 times.
✓ Branch 3 (7→9) taken 13 times.
34 cx_strtoX_unsigned_impl(uint8_t, UINT8_MAX);
926 }
927
928 62 int cx_strtou16_lc_(cxstring str, uint16_t *output, int base, const char *groupsep) {
929
4/4
✓ Branch 0 (5→6) taken 3 times.
✓ Branch 1 (5→7) taken 59 times.
✓ Branch 2 (7→8) taken 14 times.
✓ Branch 3 (7→9) taken 45 times.
62 cx_strtoX_unsigned_impl(uint16_t, UINT16_MAX);
930 }
931
932 34 int cx_strtou32_lc_(cxstring str, uint32_t *output, int base, const char *groupsep) {
933
3/4
✗ Branch 0 (5→6) not taken.
✓ Branch 1 (5→7) taken 34 times.
✓ Branch 2 (7→8) taken 5 times.
✓ Branch 3 (7→9) taken 29 times.
34 cx_strtoX_unsigned_impl(uint32_t, UINT32_MAX);
934 }
935
936 258 int cx_strtou64_lc_(cxstring str, uint64_t *output, int base, const char *groupsep) {
937 assert(sizeof(unsigned long long) == sizeof(uint64_t)); // should be true on all platforms
938 258 return cx_strtoull_lc(str, (unsigned long long*) output, base, groupsep);
939 }
940
941 32 int cx_strtoz_lc_(cxstring str, size_t *output, int base, const char *groupsep) {
942 #if SIZE_MAX == UINT32_MAX
943 return cx_strtou32_lc_(str, (uint32_t*) output, base, groupsep);
944 #elif SIZE_MAX == UINT64_MAX
945 32 return cx_strtoull_lc_(str, (unsigned long long *) output, base, groupsep);
946 #else
947 #error "unsupported size_t size"
948 #endif
949 }
950
951 18 int cx_strtof_lc_(cxstring str, float *output, char decsep, const char *groupsep) {
952 // use string to double and add a range check
953 double d;
954 18 int ret = cx_strtod_lc_(str, &d, decsep, groupsep);
955
2/2
✓ Branch 0 (3→4) taken 5 times.
✓ Branch 1 (3→5) taken 13 times.
18 if (ret != 0) return ret;
956 // note: FLT_MIN is the smallest POSITIVE number that can be represented
957
2/2
✓ Branch 0 (5→6) taken 3 times.
✓ Branch 1 (5→7) taken 10 times.
13 double test = d < 0 ? -d : d;
958
4/4
✓ Branch 0 (8→9) taken 12 times.
✓ Branch 1 (8→10) taken 1 times.
✓ Branch 2 (9→10) taken 2 times.
✓ Branch 3 (9→11) taken 10 times.
13 if (test < FLT_MIN || test > FLT_MAX) {
959 3 errno = ERANGE;
960 3 return -1;
961 }
962 10 *output = (float) d;
963 10 return 0;
964 }
965
966 47 int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupsep) {
967 // TODO: overflow check
968 // TODO: increase precision
969
970 // emptiness check
971
2/2
✓ Branch 0 (2→3) taken 2 times.
✓ Branch 1 (2→4) taken 45 times.
47 if (str.length == 0) {
972 2 errno = EINVAL;
973 2 return -1;
974 }
975
976 45 double result = 0.;
977 45 int sign = 1;
978
979 // check if there is a sign
980
2/2
✓ Branch 0 (4→5) taken 10 times.
✓ Branch 1 (4→6) taken 35 times.
45 if (str.ptr[0] == '-') {
981 10 sign = -1;
982 10 str.ptr++;
983 10 str.length--;
984
2/2
✓ Branch 0 (6→7) taken 3 times.
✓ Branch 1 (6→8) taken 32 times.
35 } else if (str.ptr[0] == '+') {
985 3 str.ptr++;
986 3 str.length--;
987 }
988
989 // there must be at least one char to parse
990
2/2
✓ Branch 0 (8→9) taken 2 times.
✓ Branch 1 (8→10) taken 43 times.
45 if (str.length == 0) {
991 2 errno = EINVAL;
992 2 return -1;
993 }
994
995 // parse all digits until we find the decsep
996 43 size_t pos = 0;
997 do {
998
2/2
✓ Branch 0 (11→12) taken 89 times.
✓ Branch 1 (11→13) taken 45 times.
134 if (isdigit((unsigned char)str.ptr[pos])) {
999 89 result = result * 10 + (str.ptr[pos] - '0');
1000
2/2
✓ Branch 0 (13→14) taken 42 times.
✓ Branch 1 (13→15) taken 3 times.
45 } else if (strchr(groupsep, str.ptr[pos]) == NULL) {
1001 42 break;
1002 }
1003
2/2
✓ Branch 0 (15→11) taken 91 times.
✓ Branch 1 (15→16) taken 1 times.
92 } while (++pos < str.length);
1004
1005 // already done?
1006
2/2
✓ Branch 0 (16→17) taken 1 times.
✓ Branch 1 (16→18) taken 42 times.
43 if (pos == str.length) {
1007 1 *output = result * sign;
1008 1 return 0;
1009 }
1010
1011 // is the next char the decsep?
1012
2/2
✓ Branch 0 (18→19) taken 26 times.
✓ Branch 1 (18→29) taken 16 times.
42 if (str.ptr[pos] == decsep) {
1013 26 pos++;
1014 // it may end with the decsep, if it did not start with it
1015
2/2
✓ Branch 0 (19→20) taken 2 times.
✓ Branch 1 (19→23) taken 24 times.
26 if (pos == str.length) {
1016
2/2
✓ Branch 0 (20→21) taken 1 times.
✓ Branch 1 (20→22) taken 1 times.
2 if (str.length == 1) {
1017 1 errno = EINVAL;
1018 1 return -1;
1019 } else {
1020 1 *output = result * sign;
1021 1 return 0;
1022 }
1023 }
1024 // parse everything until exponent or end
1025 24 double factor = 1.;
1026 do {
1027
2/2
✓ Branch 0 (24→25) taken 83 times.
✓ Branch 1 (24→26) taken 13 times.
96 if (isdigit((unsigned char)str.ptr[pos])) {
1028 83 factor *= 0.1;
1029 83 result = result + factor * (str.ptr[pos] - '0');
1030
2/2
✓ Branch 0 (26→27) taken 11 times.
✓ Branch 1 (26→28) taken 2 times.
13 } else if (strchr(groupsep, str.ptr[pos]) == NULL) {
1031 11 break;
1032 }
1033
2/2
✓ Branch 0 (28→24) taken 72 times.
✓ Branch 1 (28→29) taken 13 times.
85 } while (++pos < str.length);
1034 }
1035
1036 // no exponent?
1037
2/2
✓ Branch 0 (29→30) taken 13 times.
✓ Branch 1 (29→31) taken 27 times.
40 if (pos == str.length) {
1038 13 *output = result * sign;
1039 13 return 0;
1040 }
1041
1042 // now the next separator MUST be the exponent separator
1043 // and at least one char must follow
1044
4/4
✓ Branch 0 (31→32) taken 19 times.
✓ Branch 1 (31→33) taken 8 times.
✓ Branch 2 (32→33) taken 1 times.
✓ Branch 3 (32→34) taken 18 times.
27 if ((str.ptr[pos] | 32) != 'e' || str.length <= pos + 1) {
1045 9 errno = EINVAL;
1046 9 return -1;
1047 }
1048 18 pos++;
1049
1050 // check if we have a sign for the exponent
1051 18 double factor = 10.;
1052
2/2
✓ Branch 0 (34→35) taken 7 times.
✓ Branch 1 (34→36) taken 11 times.
18 if (str.ptr[pos] == '-') {
1053 7 factor = .1;
1054 7 pos++;
1055
2/2
✓ Branch 0 (36→37) taken 3 times.
✓ Branch 1 (36→38) taken 8 times.
11 } else if (str.ptr[pos] == '+') {
1056 3 pos++;
1057 }
1058
1059 // at least one digit must follow
1060
2/2
✓ Branch 0 (38→39) taken 2 times.
✓ Branch 1 (38→40) taken 16 times.
18 if (pos == str.length) {
1061 2 errno = EINVAL;
1062 2 return -1;
1063 }
1064
1065 // parse the exponent
1066 16 unsigned int exp = 0;
1067 do {
1068
2/2
✓ Branch 0 (41→42) taken 26 times.
✓ Branch 1 (41→43) taken 2 times.
28 if (isdigit((unsigned char)str.ptr[pos])) {
1069 26 exp = 10 * exp + (str.ptr[pos] - '0');
1070
2/2
✓ Branch 0 (43→44) taken 1 times.
✓ Branch 1 (43→45) taken 1 times.
2 } else if (strchr(groupsep, str.ptr[pos]) == NULL) {
1071 1 errno = EINVAL;
1072 1 return -1;
1073 }
1074
2/2
✓ Branch 0 (45→41) taken 12 times.
✓ Branch 1 (45→46) taken 15 times.
27 } while (++pos < str.length);
1075
1076 // apply the exponent by fast exponentiation
1077 do {
1078
2/2
✓ Branch 0 (46→47) taken 39 times.
✓ Branch 1 (46→48) taken 23 times.
62 if (exp & 1) {
1079 39 result *= factor;
1080 }
1081 62 factor *= factor;
1082
2/2
✓ Branch 0 (48→46) taken 47 times.
✓ Branch 1 (48→49) taken 15 times.
62 } while ((exp >>= 1) > 0);
1083
1084 // store the result and exit
1085 15 *output = result * sign;
1086 15 return 0;
1087 }
1088