GCC Code Coverage Report


Directory: ../src/
File: /home/joels/Current/lispbm/src/extensions/string_extensions.c
Date: 2025-10-28 15:15:18
Exec Total Coverage
Lines: 417 436 95.6%
Functions: 22 22 100.0%
Branches: 248 268 92.5%

Line Branch Exec Source
1 /*
2 Copyright 2022, 2023 - 2025 Joel Svensson svenssonjoel@yahoo.se
3 Copyright 2022, 2023 Benjamin Vedder
4 Copyright 2024 Rasmus Söderhielm rasmus.soderhielm@gmail.com
5
6 This program is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "extensions.h"
21 #include "lbm_memory.h"
22 #include "heap.h"
23 #include "fundamental.h"
24 #include "lbm_c_interop.h"
25 #include "eval_cps.h"
26 #include "print.h"
27
28 #include <ctype.h>
29
30 #ifdef LBM_OPT_STRING_EXTENSIONS_SIZE
31 #pragma GCC optimize ("-Os")
32 #endif
33 #ifdef LBM_OPT_STRING_EXTENSIONS_SIZE_AGGRESSIVE
34 #pragma GCC optimize ("-Oz")
35 #endif
36
37 #ifndef MIN
38 #define MIN(a,b) (((a)<(b))?(a):(b))
39 #endif
40 #ifndef MAX
41 #define MAX(a,b) (((a)>(b))?(a):(b))
42 #endif
43
44 static char print_val_buffer[256];
45
46 static lbm_uint sym_left;
47 static lbm_uint sym_case_insensitive;
48
49
50 1153328 static size_t strlen_max(const char *s, size_t maxlen) {
51 size_t i;
52
2/2
✓ Branch 0 taken 3661446 times.
✓ Branch 1 taken 85 times.
3661531 for (i = 0; i < maxlen; i ++) {
53
2/2
✓ Branch 0 taken 1153243 times.
✓ Branch 1 taken 2508203 times.
3661446 if (s[i] == 0) break;
54 }
55 1153328 return i;
56 }
57
58 578615 static bool dec_str_size(lbm_value v, char **data, size_t *size) {
59 578615 bool result = false;
60 578615 lbm_array_header_t *array = lbm_dec_array_r(v);
61
2/2
✓ Branch 0 taken 578434 times.
✓ Branch 1 taken 181 times.
578615 if (array) {
62 578434 *data = (char*)array->data;
63 578434 *size = array->size;
64 578434 result = true;
65 }
66 578615 return result;
67 }
68
69 120509 static lbm_value ext_str_from_n(lbm_value *args, lbm_uint argn) {
70
4/4
✓ Branch 0 taken 593 times.
✓ Branch 1 taken 119916 times.
✓ Branch 2 taken 252 times.
✓ Branch 3 taken 341 times.
120509 if (argn != 1 && argn != 2) {
71 252 lbm_set_error_reason((char*)lbm_error_str_num_args);
72 252 return ENC_SYM_EERROR;
73 }
74
2/2
✓ Branch 0 taken 169 times.
✓ Branch 1 taken 120088 times.
120257 if (!lbm_is_number(args[0])) {
75 169 return ENC_SYM_TERROR;
76 }
77
78
4/4
✓ Branch 0 taken 341 times.
✓ Branch 1 taken 119747 times.
✓ Branch 2 taken 86 times.
✓ Branch 3 taken 255 times.
120088 if (argn == 2 && !lbm_is_array_r(args[1])) {
79 86 return ENC_SYM_TERROR;
80 }
81
82 120002 char *format = 0;
83
2/2
✓ Branch 0 taken 255 times.
✓ Branch 1 taken 119747 times.
120002 if (argn == 2) {
84 255 format = lbm_dec_str(args[1]);
85 }
86
87 char buffer[100];
88 120002 size_t len = 0;
89
90
2/2
✓ Branch 0 taken 675 times.
✓ Branch 1 taken 119327 times.
120002 switch (lbm_type_of_functional(args[0])) {
91 675 case LBM_TYPE_DOUBLE: /* fall through */
92 case LBM_TYPE_FLOAT:
93
2/2
✓ Branch 0 taken 504 times.
✓ Branch 1 taken 171 times.
675 if (!format) {
94 504 format = "%g";
95 }
96 675 len = (size_t)snprintf(buffer, sizeof(buffer), format, lbm_dec_as_double(args[0]));
97 675 break;
98
99 119327 default:
100
2/2
✓ Branch 0 taken 119243 times.
✓ Branch 1 taken 84 times.
119327 if (!format) {
101 119243 format = "%d";
102 }
103 119327 len = (size_t)snprintf(buffer, sizeof(buffer), format, lbm_dec_as_i32(args[0]));
104 119327 break;
105 }
106
107 120002 len = MIN(len, sizeof(buffer));
108
109 lbm_value res;
110
2/2
✓ Branch 0 taken 119936 times.
✓ Branch 1 taken 66 times.
120002 if (lbm_create_array(&res, len + 1)) {
111 119936 lbm_array_header_t *arr = (lbm_array_header_t*)lbm_car(res);
112 119936 memcpy(arr->data, buffer, len);
113 119936 ((char*)(arr->data))[len] = '\0';
114 119936 return res;
115 } else {
116 66 return ENC_SYM_MERROR;
117 }
118 }
119
120 // signature: (str-join strings [delim]) -> str
121 288262 static lbm_value ext_str_join(lbm_value *args, lbm_uint argn) {
122 // This function does not check that the string arguments contain any
123 // terminating null bytes.
124
125
4/4
✓ Branch 0 taken 253049 times.
✓ Branch 1 taken 35213 times.
✓ Branch 2 taken 84 times.
✓ Branch 3 taken 252965 times.
288262 if (argn != 1 && argn != 2) {
126 84 lbm_set_error_reason((char *)lbm_error_str_num_args);
127 84 return ENC_SYM_EERROR;
128 }
129
130 288178 size_t str_len = 0;
131 288178 size_t str_count = 0;
132
2/2
✓ Branch 0 taken 85 times.
✓ Branch 1 taken 288093 times.
288178 if (!lbm_is_list(args[0])) {
133 85 lbm_set_error_reason((char *)lbm_error_str_incorrect_arg);
134 85 lbm_set_error_suspect(args[0]);
135 85 return ENC_SYM_TERROR;
136 }
137
2/2
✓ Branch 0 taken 576512 times.
✓ Branch 1 taken 288008 times.
864520 for (lbm_value current = args[0]; lbm_is_cons(current); current = lbm_cdr(current)) {
138 576512 lbm_value car_val = lbm_car(current);
139 576512 char *str = NULL;
140 576512 size_t arr_size = 0;
141
2/2
✓ Branch 0 taken 576427 times.
✓ Branch 1 taken 85 times.
576512 if (dec_str_size(car_val, &str, &arr_size)) {
142 576427 str_len += strlen_max(str, arr_size);
143 576427 str_count += 1;
144 } else {
145 85 lbm_set_error_reason((char *)lbm_error_str_incorrect_arg);
146 85 lbm_set_error_suspect(args[0]);
147 85 return ENC_SYM_TERROR;
148 }
149 }
150
151 288008 const char *delim = "";
152
2/2
✓ Branch 0 taken 252797 times.
✓ Branch 1 taken 35211 times.
288008 if (argn >= 2) {
153 252797 delim = lbm_dec_str(args[1]);
154
2/2
✓ Branch 0 taken 85 times.
✓ Branch 1 taken 252712 times.
252797 if (!delim) {
155 85 lbm_set_error_reason((char *)lbm_error_str_incorrect_arg);
156 85 lbm_set_error_suspect(args[1]);
157 85 return ENC_SYM_TERROR;
158 }
159 }
160
161 287923 size_t delim_len = strlen(delim);
162
2/2
✓ Branch 0 taken 203659 times.
✓ Branch 1 taken 84264 times.
287923 if (str_count > 0) {
163 203659 str_len += (str_count - 1) * delim_len;
164 }
165
166 lbm_value result;
167
2/2
✓ Branch 0 taken 288 times.
✓ Branch 1 taken 287635 times.
287923 if (!lbm_create_array(&result, str_len + 1)) {
168 288 return ENC_SYM_MERROR;
169 }
170 287635 char *result_str = lbm_dec_str(result);
171
172 287635 size_t i = 0;
173 287635 size_t offset = 0;
174
2/2
✓ Branch 0 taken 575680 times.
✓ Branch 1 taken 287635 times.
863315 for (lbm_value current = args[0]; lbm_is_cons(current); current = lbm_cdr(current)) {
175 575680 lbm_value car_val = lbm_car(current);
176 // All arrays have been prechecked.
177 575680 lbm_array_header_t *array = (lbm_array_header_t*) lbm_car(car_val);
178 575680 char *str = (char*)array->data;
179 575680 size_t len = strlen_max(str, array->size);
180
181 575680 memcpy(result_str + offset, str, len);
182 575680 offset += len;
183
184
2/2
✓ Branch 0 taken 372213 times.
✓ Branch 1 taken 203467 times.
575680 if (i != str_count - 1) {
185 372213 memcpy(result_str + offset, delim, delim_len);
186 372213 offset += delim_len;
187 }
188 575680 i++;
189 }
190
191 287635 result_str[str_len] = '\0';
192
193 287635 return result;
194 }
195
196 1347 static lbm_value ext_str_to_i(lbm_value *args, lbm_uint argn) {
197
4/4
✓ Branch 0 taken 169 times.
✓ Branch 1 taken 1178 times.
✓ Branch 2 taken 84 times.
✓ Branch 3 taken 85 times.
1347 if (argn != 1 && argn != 2) {
198 84 lbm_set_error_reason((char*)lbm_error_str_num_args);
199 84 return ENC_SYM_EERROR;
200 }
201
202 1263 char *str = lbm_dec_str(args[0]);
203
2/2
✓ Branch 0 taken 85 times.
✓ Branch 1 taken 1178 times.
1263 if (!str) {
204 85 return ENC_SYM_TERROR;
205 }
206
207 1178 int base = 0;
208
2/2
✓ Branch 0 taken 85 times.
✓ Branch 1 taken 1093 times.
1178 if (argn == 2) {
209
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 84 times.
85 if (!lbm_is_number(args[1])) {
210 1 return ENC_SYM_TERROR;
211 }
212
213 84 base = (int)lbm_dec_as_u32(args[1]);
214 }
215
216 1177 return lbm_enc_i32((int32_t)strtol(str, NULL, base));
217 }
218
219 337 static lbm_value ext_str_to_f(lbm_value *args, lbm_uint argn) {
220
2/2
✓ Branch 0 taken 84 times.
✓ Branch 1 taken 253 times.
337 if (argn != 1) {
221 84 lbm_set_error_reason((char*)lbm_error_str_num_args);
222 84 return ENC_SYM_EERROR;
223 }
224
225 253 char *str = lbm_dec_str(args[0]);
226
2/2
✓ Branch 0 taken 85 times.
✓ Branch 1 taken 168 times.
253 if (!str) {
227 85 return ENC_SYM_TERROR;
228 }
229
230 168 return lbm_enc_float(strtof(str, NULL));
231 }
232
233 343 static lbm_value ext_str_part(lbm_value *args, lbm_uint argn) {
234
6/6
✓ Branch 0 taken 255 times.
✓ Branch 1 taken 88 times.
✓ Branch 2 taken 171 times.
✓ Branch 3 taken 84 times.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 258 times.
343 if ((argn != 2 && argn != 3) || !lbm_is_number(args[1])) {
235 85 lbm_set_error_reason((char*)lbm_error_str_num_args);
236 85 return ENC_SYM_TERROR;
237 }
238
239 258 size_t str_arr_len = 0;
240 258 char *str = NULL;//lbm_dec_str(args[0]);
241
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 257 times.
258 if (!dec_str_size(args[0], &str, &str_arr_len)) {
242 1 return ENC_SYM_TERROR;
243 }
244
245 257 uint32_t len = (uint32_t)strlen_max(str, str_arr_len);
246
247 257 uint32_t start = lbm_dec_as_u32(args[1]);
248
249
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 255 times.
257 if (start >= len) {
250 2 return ENC_SYM_EERROR;
251 }
252
253 255 uint32_t n = len - start;
254
2/2
✓ Branch 0 taken 171 times.
✓ Branch 1 taken 84 times.
255 if (argn == 3) {
255
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 170 times.
171 if (!lbm_is_number(args[2])) {
256 1 return ENC_SYM_TERROR;
257 }
258
259
2/2
✓ Branch 0 taken 169 times.
✓ Branch 1 taken 1 times.
170 n = MIN(lbm_dec_as_u32(args[2]), n);
260 }
261
262 lbm_value res;
263
1/2
✓ Branch 0 taken 254 times.
✗ Branch 1 not taken.
254 if (lbm_create_array(&res, n + 1)) {
264 254 lbm_array_header_t *arr = (lbm_array_header_t*)lbm_car(res);
265 254 memcpy(arr->data, str + start, n);
266 254 ((char*)(arr->data))[n] = '\0';
267 254 return res;
268 } else {
269 return ENC_SYM_MERROR;
270 }
271 }
272
273 3789 static bool char_in(char c, char *delim, unsigned int max_ix) {
274 3789 char *d = delim;
275 3789 unsigned int i = 0;
276
3/4
✓ Branch 0 taken 7294 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4713 times.
✓ Branch 3 taken 2581 times.
7294 while (i < max_ix && *d != '\0' ) {
277
2/2
✓ Branch 0 taken 1208 times.
✓ Branch 1 taken 3505 times.
4713 if (c == *d) return true;
278 3505 d++; i++;
279 }
280 2581 return false;
281 }
282
283 686 static lbm_value ext_str_split(lbm_value *args, lbm_uint argn) {
284
2/2
✓ Branch 0 taken 85 times.
✓ Branch 1 taken 601 times.
686 if (argn != 2) {
285 85 lbm_set_error_reason((char*)lbm_error_str_num_args);
286 85 return ENC_SYM_TERROR;
287 }
288
289 601 size_t str_arr_size = 0;
290 601 char *str = NULL; //lbm_dec_str(args[0]);
291
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 600 times.
601 if (!dec_str_size(args[0], &str, &str_arr_size)) {
292 1 return ENC_SYM_TERROR;
293 }
294
295 600 char *delim = NULL;
296 600 size_t delim_arr_size = 0;
297
298
2/2
✓ Branch 0 taken 170 times.
✓ Branch 1 taken 430 times.
600 if (lbm_is_number(args[1])) {
299
2/2
✓ Branch 0 taken 86 times.
✓ Branch 1 taken 84 times.
170 int step = MAX(lbm_dec_as_i32(args[1]), 1);
300 170 lbm_value res = ENC_SYM_NIL;
301 170 int len = (int)strlen_max(str, str_arr_size);
302
2/2
✓ Branch 0 taken 1682 times.
✓ Branch 1 taken 170 times.
1852 for (int i = len / step;i >= 0;i--) {
303 1682 int ind_now = i * step;
304
2/2
✓ Branch 0 taken 84 times.
✓ Branch 1 taken 1598 times.
1682 if (ind_now >= len) {
305 84 continue;
306 }
307
308 1598 int step_now = step;
309
2/2
✓ Branch 0 taken 152 times.
✓ Branch 1 taken 1598 times.
1750 while ((ind_now + step_now) > len) {
310 152 step_now--;
311 }
312
313 lbm_value tok;
314
1/2
✓ Branch 0 taken 1598 times.
✗ Branch 1 not taken.
1598 if (lbm_create_array(&tok, (lbm_uint)step_now + 1)) {
315 1598 lbm_array_header_t *arr = (lbm_array_header_t*)lbm_car(tok);
316 1598 memcpy(arr->data, str + ind_now, (unsigned int)step_now);
317 1598 ((char*)(arr->data))[step_now] = '\0';
318 1598 res = lbm_cons(tok, res);
319 } else {
320 return ENC_SYM_MERROR;
321 }
322 }
323 170 return res;
324
1/2
✓ Branch 0 taken 430 times.
✗ Branch 1 not taken.
430 } else if (dec_str_size(args[1], &delim, &delim_arr_size)) {
325 430 lbm_value res = ENC_SYM_NIL;
326
327 430 unsigned int i_start = 0;
328 430 unsigned int i_end = 0;
329
330 // Abort when larger that array size. Protection against abuse
331 // with byte-arrays.
332
1/2
✓ Branch 0 taken 1638 times.
✗ Branch 1 not taken.
1638 while (i_end < str_arr_size) {
333
334
4/4
✓ Branch 0 taken 3789 times.
✓ Branch 1 taken 430 times.
✓ Branch 2 taken 2581 times.
✓ Branch 3 taken 1208 times.
4219 while (str[i_end] != '\0' && !char_in(str[i_end], delim, (unsigned int)delim_arr_size)) {
335 2581 i_end ++;
336 }
337
338 1638 unsigned int len = i_end - i_start;
339 1638 char *s = &str[i_start];
340 lbm_value tok;
341
1/2
✓ Branch 0 taken 1638 times.
✗ Branch 1 not taken.
1638 if (lbm_create_array(&tok, len + 1)) {
342 1638 lbm_array_header_t *arr = (lbm_array_header_t*)lbm_car(tok);
343 1638 memcpy(arr->data, s, len);
344 1638 ((char*)(arr->data))[len] = '\0';
345 1638 res = lbm_cons(tok, res);
346
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1638 times.
1638 if (res == ENC_SYM_MERROR) return res;
347 } else {
348 return ENC_SYM_MERROR;
349 }
350
351
2/2
✓ Branch 0 taken 430 times.
✓ Branch 1 taken 1208 times.
1638 if (str[i_end] == '\0') break;
352 1208 i_start = i_end + 1;
353 1208 i_end = i_end + 1;
354
355 }
356 430 return lbm_list_destructive_reverse(res);
357 }
358 return ENC_SYM_TERROR;
359 }
360
361 // Todo: Clean this up for 64bit
362 268 static lbm_value ext_str_replace(lbm_value *args, lbm_uint argn) {
363
4/4
✓ Branch 0 taken 182 times.
✓ Branch 1 taken 86 times.
✓ Branch 2 taken 86 times.
✓ Branch 3 taken 96 times.
268 if (argn != 2 && argn != 3) {
364 86 lbm_set_error_reason((char*)lbm_error_str_num_args);
365 86 return ENC_SYM_EERROR;
366 }
367
368 182 size_t orig_arr_size = 0;
369 182 char *orig = NULL; // lbm_dec_str(args[0]);
370
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 180 times.
182 if (!dec_str_size(args[0], &orig, &orig_arr_size)) {
371 2 return ENC_SYM_TERROR;
372 }
373
374 180 size_t rep_arr_size = 0;
375 180 char *rep = NULL; //lbm_dec_str(args[1]);
376
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 177 times.
180 if (!dec_str_size(args[1], &rep, &rep_arr_size)) {
377 3 return ENC_SYM_TERROR;
378 }
379
380 177 size_t with_arr_size = 0;
381 177 char *with = "";
382
2/2
✓ Branch 0 taken 92 times.
✓ Branch 1 taken 85 times.
177 if (argn == 3) {
383
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 90 times.
92 if (!dec_str_size(args[2], &with, &with_arr_size)) {
384 2 return ENC_SYM_TERROR;
385 }
386 }
387
388 // See https://stackoverflow.com/questions/779875/what-function-is-to-replace-a-substring-from-a-string-in-c
389 //char *result; // the return string
390 char *ins; // the next insert point
391 char *tmp; // varies
392 size_t len_rep; // length of rep (the string to remove)
393 size_t len_with; // length of with (the string to replace rep with)
394 //size_t len_front; // distance between rep and end of last rep
395 int count; // number of replacements
396
397 175 len_rep = strlen_max(rep, rep_arr_size);
398
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 173 times.
175 if (len_rep == 0) {
399 2 return args[0]; // empty rep causes infinite loop during count
400 }
401
402 173 len_with = strlen_max(with,with_arr_size);
403
404 // count the number of replacements needed
405 173 ins = orig;
406
2/2
✓ Branch 0 taken 221 times.
✓ Branch 1 taken 173 times.
394 for (count = 0; (tmp = strstr(ins, rep)); ++count) {
407 221 ins = tmp + len_rep;
408 }
409
410 173 size_t len_res = strlen_max(orig, orig_arr_size) + (len_with - len_rep) * (unsigned int)count + 1;
411 lbm_value lbm_res;
412
1/2
✓ Branch 0 taken 173 times.
✗ Branch 1 not taken.
173 if (lbm_create_array(&lbm_res, len_res)) {
413 173 lbm_array_header_t *arr = (lbm_array_header_t*)lbm_car(lbm_res);
414 173 tmp = (char*)arr->data;
415 } else {
416 return ENC_SYM_MERROR;
417 }
418
419 // first time through the loop, all the variable are set correctly
420 // from here on,
421 // tmp points to the end of the result string
422 // ins points to the next occurrence of rep in orig
423 // orig points to the remainder of orig after "end of rep"
424
2/2
✓ Branch 0 taken 221 times.
✓ Branch 1 taken 173 times.
394 while (count--) {
425 221 ins = strstr(orig, rep);
426 221 size_t len_front = (size_t)ins - (size_t)orig;
427 221 tmp = strncpy(tmp, orig, len_front) + len_front;
428 221 tmp = strncpy(tmp, with, len_with) + len_with;
429 221 orig += len_front + len_rep; // move to next "end of rep"
430 }
431 173 strcpy(tmp, orig);
432
433 173 return lbm_res;
434 }
435
436 349 static lbm_value change_case(lbm_value *args, lbm_uint argn, bool to_upper) {
437
2/2
✓ Branch 0 taken 168 times.
✓ Branch 1 taken 181 times.
349 if (argn != 1) {
438 168 lbm_set_error_reason((char*)lbm_error_str_num_args);
439 168 return ENC_SYM_EERROR;
440 }
441
442 181 size_t orig_arr_size = 0;
443 181 char *orig = NULL; //lbm_dec_str(args[0]);
444
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 179 times.
181 if (!dec_str_size(args[0], &orig, &orig_arr_size)) {
445 2 return ENC_SYM_TERROR;
446 }
447
448 179 size_t len = strlen_max(orig,orig_arr_size);
449 lbm_value lbm_res;
450
1/2
✓ Branch 0 taken 179 times.
✗ Branch 1 not taken.
179 if (lbm_create_array(&lbm_res, len + 1)) {
451 179 lbm_array_header_t *arr = (lbm_array_header_t*)lbm_car(lbm_res);
452
2/2
✓ Branch 0 taken 892 times.
✓ Branch 1 taken 179 times.
1071 for (unsigned int i = 0;i < len;i++) {
453
2/2
✓ Branch 0 taken 451 times.
✓ Branch 1 taken 441 times.
892 if (to_upper) {
454 451 ((char*)(arr->data))[i] = (char)toupper(orig[i]);
455 } else {
456 441 ((char*)(arr->data))[i] = (char)tolower(orig[i]);
457 }
458 }
459 179 ((char*)(arr->data))[len] = '\0';
460 179 return lbm_res;
461 } else {
462 return ENC_SYM_MERROR;
463 }
464 }
465
466 174 static lbm_value ext_str_to_lower(lbm_value *args, lbm_uint argn) {
467 174 return change_case(args, argn, false);
468 }
469
470 175 static lbm_value ext_str_to_upper(lbm_value *args, lbm_uint argn) {
471 175 return change_case(args,argn, true);
472 }
473
474 1036 static lbm_value ext_str_cmp(lbm_value *args, lbm_uint argn) {
475
4/4
✓ Branch 0 taken 170 times.
✓ Branch 1 taken 866 times.
✓ Branch 2 taken 84 times.
✓ Branch 3 taken 86 times.
1036 if (argn != 2 && argn != 3) {
476 84 lbm_set_error_reason((char*)lbm_error_str_num_args);
477 84 return ENC_SYM_EERROR;
478 }
479
480 952 char *str1 = lbm_dec_str(args[0]);
481
2/2
✓ Branch 0 taken 85 times.
✓ Branch 1 taken 867 times.
952 if (!str1) {
482 85 return ENC_SYM_TERROR;
483 }
484
485 867 char *str2 = lbm_dec_str(args[1]);
486
2/2
✓ Branch 0 taken 85 times.
✓ Branch 1 taken 782 times.
867 if (!str2) {
487 85 return ENC_SYM_TERROR;
488 }
489
490 782 int n = -1;
491
2/2
✓ Branch 0 taken 86 times.
✓ Branch 1 taken 696 times.
782 if (argn == 3) {
492
2/2
✓ Branch 0 taken 85 times.
✓ Branch 1 taken 1 times.
86 if (!lbm_is_number(args[2])) {
493 85 return ENC_SYM_TERROR;
494 }
495
496 1 n = lbm_dec_as_i32(args[2]);
497 }
498
499
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 696 times.
697 if (n > 0) {
500 1 return lbm_enc_i(strncmp(str1, str2, (unsigned int)n));
501 } else {
502 696 return lbm_enc_i(strcmp(str1, str2));
503 }
504 }
505
506 // TODO: This is very similar to ext-print. Maybe they can share code.
507 3276 static lbm_value to_str(char *delimiter, lbm_value *args, lbm_uint argn) {
508 3276 const int str_len = 300;
509 3276 char *str = lbm_malloc((lbm_uint)str_len);
510
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3276 times.
3276 if (!str) {
511 return ENC_SYM_MERROR;
512 }
513
514 3276 int str_ofs = 0;
515
516
2/2
✓ Branch 0 taken 4200 times.
✓ Branch 1 taken 3276 times.
7476 for (lbm_uint i = 0; i < argn; i ++) {
517 4200 lbm_value t = args[i];
518 4200 int max = str_len - str_ofs - 1;
519
520 char *arr_str;
521 4200 int chars = 0;
522
523
2/2
✓ Branch 0 taken 756 times.
✓ Branch 1 taken 3444 times.
4200 if (lbm_value_is_printable_string(t, &arr_str)) {
524
2/2
✓ Branch 0 taken 504 times.
✓ Branch 1 taken 252 times.
756 if (str_ofs == 0) {
525 504 chars = snprintf(str + str_ofs, (unsigned int)max, "%s", arr_str);
526 } else {
527 252 chars = snprintf(str + str_ofs, (unsigned int)max, "%s%s", delimiter, arr_str);
528 }
529 } else {
530 3444 lbm_print_value(print_val_buffer, 256, t);
531
2/2
✓ Branch 0 taken 2772 times.
✓ Branch 1 taken 672 times.
3444 if (str_ofs == 0) {
532 2772 chars = snprintf(str + str_ofs, (unsigned int)max, "%s", print_val_buffer);
533 } else {
534 672 chars = snprintf(str + str_ofs, (unsigned int)max, "%s%s", delimiter, print_val_buffer);
535 }
536 }
537
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4200 times.
4200 if (chars >= max) {
538 str_ofs += max;
539 } else {
540 4200 str_ofs += chars;
541 }
542 }
543
544 lbm_value res;
545
1/2
✓ Branch 0 taken 3276 times.
✗ Branch 1 not taken.
3276 if (lbm_create_array(&res, (lbm_uint)str_ofs + 1)) {
546 3276 lbm_array_header_t *arr = (lbm_array_header_t*)lbm_car(res);
547 3276 strncpy((char*)arr->data, str, (unsigned int)str_ofs + 1);
548 3276 lbm_free(str);
549 3276 return res;
550 } else {
551 lbm_free(str);
552 return ENC_SYM_MERROR;
553 }
554 }
555
556 3192 static lbm_value ext_to_str(lbm_value *args, lbm_uint argn) {
557 3192 return to_str(" ", args, argn);
558 }
559
560 86 static lbm_value ext_to_str_delim(lbm_value *args, lbm_uint argn) {
561
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 85 times.
86 if (argn < 1) {
562 1 lbm_set_error_reason((char*)lbm_error_str_num_args);
563 1 return ENC_SYM_EERROR;
564 }
565
566 85 char *delim = lbm_dec_str(args[0]);
567
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 84 times.
85 if (!delim) {
568 1 return ENC_SYM_TERROR;
569 }
570
571 84 return to_str(delim, args + 1, argn - 1);
572 }
573
574 263 static lbm_value ext_str_len(lbm_value *args, lbm_uint argn) {
575
2/2
✓ Branch 0 taken 84 times.
✓ Branch 1 taken 179 times.
263 LBM_CHECK_ARGN(1);
576
577 179 size_t str_arr_size = 0;
578 179 char *str = NULL; //lbm_dec_str(args[0]);
579
2/2
✓ Branch 0 taken 85 times.
✓ Branch 1 taken 94 times.
179 if (!dec_str_size(args[0], &str, &str_arr_size)) {
580 85 return ENC_SYM_TERROR;
581 }
582
583 94 return lbm_enc_i((int)strlen_max(str, str_arr_size));
584 }
585
586 268 static lbm_value ext_str_replicate(lbm_value *args, lbm_uint argn) {
587
2/2
✓ Branch 0 taken 168 times.
✓ Branch 1 taken 100 times.
268 if (argn != 2) {
588 168 lbm_set_error_reason((char*)lbm_error_str_num_args);
589 168 return ENC_SYM_EERROR;
590 }
591
592 100 lbm_value res = ENC_SYM_TERROR;
593
594
3/4
✓ Branch 0 taken 98 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 98 times.
✗ Branch 3 not taken.
198 if (lbm_is_number(args[0]) &&
595 98 lbm_is_number(args[1])) {
596 98 uint32_t len = lbm_dec_as_u32(args[0]);
597 98 uint8_t c = lbm_dec_as_char(args[1]);
598
599 lbm_value lbm_res;
600
1/2
✓ Branch 0 taken 98 times.
✗ Branch 1 not taken.
98 if (lbm_create_array(&lbm_res, len + 1)) {
601 98 lbm_array_header_t *arr = (lbm_array_header_t*)lbm_car(lbm_res);
602
2/2
✓ Branch 0 taken 956 times.
✓ Branch 1 taken 98 times.
1054 for (unsigned int i = 0;i < len;i++) {
603 956 ((char*)(arr->data))[i] = (char)c;
604 }
605 98 ((char*)(arr->data))[len] = '\0';
606 98 res = lbm_res;
607 } else {
608 res = ENC_SYM_MERROR;
609 }
610 }
611 100 return res;
612 }
613
614 2277 bool ci_strncmp(const char *str1, const char *str2,int n) {
615 2277 bool res = true;
616
2/2
✓ Branch 0 taken 3461 times.
✓ Branch 1 taken 758 times.
4219 for (int i = 0; i < n; i ++) {
617
2/2
✓ Branch 0 taken 1519 times.
✓ Branch 1 taken 1942 times.
3461 if (tolower(str1[i]) != tolower(str2[i])) {
618 1519 res = false;
619 1519 break;
620 }
621 }
622 2277 return res;
623 }
624
625 // signature: (str-find str:byte-array substr [start:int] [occurrence:int] [dir] [case_sensitivity]) -> int
626 // where
627 // seq = string|(..string)
628 // dir = 'left|'right
629 // case_sensitivity = 'case-sensitive | 'case-insensitive
630 2893 static lbm_value ext_str_find(lbm_value *args, lbm_uint argn) {
631
4/4
✓ Branch 0 taken 2808 times.
✓ Branch 1 taken 85 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 2807 times.
2893 if (argn < 2 || 6 < argn) {
632 86 lbm_set_error_reason((char *)lbm_error_str_num_args);
633 86 return ENC_SYM_EERROR;
634 }
635 2807 lbm_array_header_t *str_header = lbm_dec_array_r(args[0]);
636
2/2
✓ Branch 0 taken 2723 times.
✓ Branch 1 taken 84 times.
2807 if (str_header) {
637 2723 const char *str = (const char *)str_header->data;
638 2723 lbm_int str_size = (lbm_int)str_header->size;
639
640 // Guaranteed to be list containing strings.
641 lbm_value substrings;
642 2723 lbm_int min_substr_len = LBM_INT_MAX;
643
2/2
✓ Branch 0 taken 2217 times.
✓ Branch 1 taken 506 times.
2723 if (lbm_is_array_r(args[1])) {
644 2217 substrings = lbm_cons(args[1], ENC_SYM_NIL);
645
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2217 times.
2217 if (substrings == ENC_SYM_MERROR) {
646 return ENC_SYM_MERROR;
647 }
648 2217 lbm_array_header_t *header = (lbm_array_header_t *)lbm_car(args[1]);
649
650 2217 lbm_int len = (lbm_int)header->size - 1;
651
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2216 times.
2217 if (len < 0) {
652 // substr is zero length array
653 1 return lbm_enc_i(-1);
654 }
655 2216 min_substr_len = len;
656
1/2
✓ Branch 0 taken 506 times.
✗ Branch 1 not taken.
506 } else if (lbm_is_list(args[1])) {
657
2/2
✓ Branch 0 taken 675 times.
✓ Branch 1 taken 506 times.
1181 for (lbm_value current = args[1]; lbm_is_cons(current); current = lbm_cdr(current)) {
658 675 lbm_value car_val = lbm_car(current);
659 675 lbm_array_header_t *header = lbm_dec_array_r(car_val);
660
1/2
✓ Branch 0 taken 675 times.
✗ Branch 1 not taken.
675 if (header) {
661 675 lbm_int len = (lbm_int)header->size - 1;
662
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 674 times.
675 if (len < 0) {
663 // substr is zero length array
664 1 continue;
665 }
666
2/2
✓ Branch 0 taken 421 times.
✓ Branch 1 taken 253 times.
674 if (len < min_substr_len) {
667 421 min_substr_len = len;
668 }
669 } else {
670 lbm_set_error_suspect(args[1]);
671 lbm_set_error_reason((char *)lbm_error_str_incorrect_arg);
672 return ENC_SYM_TERROR;
673 }
674 }
675 506 substrings = args[1];
676 } else {
677 lbm_set_error_suspect(args[1]);
678 lbm_set_error_reason((char *)lbm_error_str_incorrect_arg);
679 return ENC_SYM_TERROR;
680 }
681
682 2722 bool to_right = true;
683 2722 bool case_sensitive = true;
684
685 2722 int nums[2] = {0, 0};
686 2722 bool nums_set[2] = {false, false};
687 2722 int num_ix = 0;
688
689
690
2/2
✓ Branch 0 taken 8900 times.
✓ Branch 1 taken 2722 times.
11622 for (int i = 0; i < (int)argn; i ++ ) {
691
3/4
✓ Branch 0 taken 1939 times.
✓ Branch 1 taken 6961 times.
✓ Branch 2 taken 1939 times.
✗ Branch 3 not taken.
8900 if (lbm_is_number(args[i]) && num_ix < 2) {
692 1939 nums_set[num_ix] = true;
693 1939 nums[num_ix++] = (int)lbm_dec_as_int(args[i]);
694 }
695
2/2
✓ Branch 0 taken 1601 times.
✓ Branch 1 taken 7299 times.
8900 if (lbm_is_symbol(args[i])) {
696 1601 lbm_uint symbol = lbm_dec_sym(args[i]);
697
2/2
✓ Branch 0 taken 843 times.
✓ Branch 1 taken 758 times.
1601 if (symbol == sym_left) {
698 843 to_right = false;
699
2/2
✓ Branch 0 taken 590 times.
✓ Branch 1 taken 168 times.
758 } else if (symbol == sym_case_insensitive) {
700 590 case_sensitive = false;
701 }
702 }
703 }
704
705 2722 uint32_t occurrence = 0;
706
2/2
✓ Branch 0 taken 1879 times.
✓ Branch 1 taken 843 times.
2722 lbm_int start = to_right ? 0 : str_size - min_substr_len;
707
2/2
✓ Branch 0 taken 1517 times.
✓ Branch 1 taken 1205 times.
2722 if (nums_set[0]) {
708 1517 start = nums[0];
709 }
710
2/2
✓ Branch 0 taken 422 times.
✓ Branch 1 taken 2300 times.
2722 if (nums_set[1]) {
711 422 occurrence = (uint32_t)nums[1];
712 }
713
714
2/2
✓ Branch 0 taken 674 times.
✓ Branch 1 taken 2048 times.
2722 if (start < 0) {
715 // start: -1 starts the search at the character index before the final null
716 // byte index.
717 674 start = str_size - 1 + start;
718 }
719
720
4/4
✓ Branch 0 taken 843 times.
✓ Branch 1 taken 1879 times.
✓ Branch 2 taken 85 times.
✓ Branch 3 taken 758 times.
2722 if (!to_right && (start > str_size - min_substr_len)) {
721 85 start = str_size - min_substr_len;
722 }
723
4/4
✓ Branch 0 taken 1879 times.
✓ Branch 1 taken 758 times.
✓ Branch 2 taken 85 times.
✓ Branch 3 taken 1794 times.
2637 else if (to_right && (start < 0)) {
724 85 start = 0;
725 }
726
727
2/2
✓ Branch 0 taken 1879 times.
✓ Branch 1 taken 843 times.
2722 lbm_int dir = to_right ? 1 : -1;
728
4/4
✓ Branch 0 taken 4915 times.
✓ Branch 1 taken 1437 times.
✓ Branch 2 taken 5929 times.
✓ Branch 3 taken 423 times.
6352 for (lbm_int i = start; to_right ? (i <= str_size - min_substr_len) : (i >= 0); i += dir) {
729
2/2
✓ Branch 0 taken 6692 times.
✓ Branch 1 taken 3630 times.
10322 for (lbm_value current = substrings; lbm_is_cons(current); current = lbm_cdr(current)) {
730 6692 lbm_array_header_t *header = (lbm_array_header_t *)lbm_car(lbm_car(current));
731 6692 lbm_int substr_len = (lbm_int)header->size - 1;
732 6692 const char *substr = (const char *)header->data;
733
734 6692 if (
735
1/2
✓ Branch 0 taken 6692 times.
✗ Branch 1 not taken.
6692 i > str_size - substr_len // substr length runs over str end.
736
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6692 times.
6692 || substr_len < 0 // empty substr substr was zero bytes in size
737 ) {
738 continue;
739 }
740
741
4/4
✓ Branch 0 taken 4415 times.
✓ Branch 1 taken 2277 times.
✓ Branch 2 taken 2452 times.
✓ Branch 3 taken 1963 times.
6692 if ((case_sensitive && memcmp(&str[i], substr, (size_t)substr_len) == 0) ||
742
4/4
✓ Branch 0 taken 2277 times.
✓ Branch 1 taken 2452 times.
✓ Branch 2 taken 758 times.
✓ Branch 3 taken 1519 times.
4729 (!case_sensitive && ci_strncmp(&str[i], substr, (int)substr_len))) {
743
2/2
✓ Branch 0 taken 2299 times.
✓ Branch 1 taken 422 times.
2721 if (occurrence == 0) {
744 2299 return lbm_enc_i(i);
745 }
746 422 occurrence -= 1;
747 }
748 }
749 }
750 423 return lbm_enc_i(-1);
751 } else {
752 84 lbm_set_error_suspect(args[0]);
753 84 lbm_set_error_reason((char *)lbm_error_str_incorrect_arg);
754 84 return ENC_SYM_TERROR;
755 }
756 }
757
758 66394 void lbm_string_extensions_init(void) {
759
760 66394 lbm_add_symbol_const("left", &sym_left);
761 66394 lbm_add_symbol_const("nocase", &sym_case_insensitive);
762
763 66394 lbm_add_extension("str-from-n", ext_str_from_n);
764 66394 lbm_add_extension("str-join", ext_str_join);
765 66394 lbm_add_extension("str-to-i", ext_str_to_i);
766 66394 lbm_add_extension("str-to-f", ext_str_to_f);
767 66394 lbm_add_extension("str-part", ext_str_part);
768 66394 lbm_add_extension("str-split", ext_str_split);
769 66394 lbm_add_extension("str-replace", ext_str_replace);
770 66394 lbm_add_extension("str-to-lower", ext_str_to_lower);
771 66394 lbm_add_extension("str-to-upper", ext_str_to_upper);
772 66394 lbm_add_extension("str-cmp", ext_str_cmp);
773 66394 lbm_add_extension("to-str", ext_to_str);
774 66394 lbm_add_extension("to-str-delim", ext_to_str_delim);
775 66394 lbm_add_extension("str-len", ext_str_len);
776 66394 lbm_add_extension("str-replicate", ext_str_replicate);
777 66394 lbm_add_extension("str-find", ext_str_find);
778 66394 }
779