Blender V4.3
string_utils.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2017 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include <cctype>
10#include <cstdlib>
11#include <cstring>
12
13#include <array>
14
15#include "MEM_guardedalloc.h"
16
17#include "BLI_array.hh"
18#include "BLI_string.h"
19#include "BLI_string_utf8.h"
20#include "BLI_string_utils.hh"
21#include "BLI_utildefines.h"
22
23#include "BLI_dynstr.h"
24
25#include "DNA_listBase.h"
26
27#include "BLI_strict_flags.h" /* Keep last. */
28
29/* -------------------------------------------------------------------- */
33char *BLI_string_replaceN(const char *__restrict str,
34 const char *__restrict substr_old,
35 const char *__restrict substr_new)
36{
37 DynStr *ds = nullptr;
38 size_t len_old = strlen(substr_old);
39 const char *match;
40
41 BLI_assert(substr_old[0] != '\0');
42
43 /* While we can still find a match for the old sub-string that we're searching for,
44 * keep dicing and replacing. */
45 while ((match = strstr(str, substr_old))) {
46 /* the assembly buffer only gets created when we actually need to rebuild the string */
47 if (ds == nullptr) {
48 ds = BLI_dynstr_new();
49 }
50
51 /* If the match position does not match the current position in the string,
52 * copy the text up to this position and advance the current position in the string. */
53 if (str != match) {
54 /* Add the segment of the string from `str` to match to the buffer,
55 * then restore the value at match. */
56 BLI_dynstr_nappend(ds, str, int(match - str));
57
58 /* now our current position should be set on the start of the match */
59 str = match;
60 }
61
62 /* Add the replacement text to the accumulation buffer. */
63 BLI_dynstr_append(ds, substr_new);
64
65 /* Advance the current position of the string up to the end of the replaced segment. */
66 str += len_old;
67 }
68
69 /* Finish off and return a new string that has had all occurrences of. */
70 if (ds) {
71 char *str_new;
72
73 /* Add what's left of the string to the assembly buffer
74 * - we've been adjusting `str` to point at the end of the replaced segments. */
76
77 /* Convert to new c-string (MEM_malloc'd), and free the buffer. */
78 str_new = BLI_dynstr_get_cstring(ds);
80
81 return str_new;
82 }
83 /* Just create a new copy of the entire string - we avoid going through the assembly buffer
84 * for what should be a bit more efficiency. */
85 return BLI_strdup(str);
86}
87
88void BLI_string_replace_char(char *str, char src, char dst)
89{
90 while (*str) {
91 if (*str == src) {
92 *str = dst;
93 }
94 str++;
95 }
96}
97
99 const size_t string_len,
100 const char *replace_table[][2],
101 int replace_table_len)
102{
103 BLI_string_debug_size_after_nil(string, string_len);
104
105 for (int i = 0; i < replace_table_len; i++) {
106 if (STREQ(string, replace_table[i][0])) {
107 BLI_strncpy(string, replace_table[i][1], string_len);
108 return true;
109 }
110 }
111 return false;
112}
113
115 char *string, size_t string_maxncpy, int src_beg, int src_end, const char *dst)
116{
117 int string_len = int(strlen(string));
118 BLI_assert(src_beg <= src_end);
119 BLI_assert(src_end <= string_len);
120 const int src_len = src_end - src_beg;
121 int dst_len = int(strlen(dst));
122
123 if (src_len < dst_len) {
124 /* Grow, first handle special cases. */
125
126 /* Special case, the src_end is entirely clipped. */
127 if (UNLIKELY(int(string_maxncpy) <= src_beg + dst_len)) {
128 /* There is only room for the destination. */
129 dst_len = (int(string_maxncpy) - src_beg) - 1;
130 string_len = src_end;
131 string[string_len] = '\0';
132 }
133
134 const int ofs = dst_len - src_len;
135 /* Clip the string when inserting the destination string exceeds `string_maxncpy`. */
136 if (string_len + ofs >= int(string_maxncpy)) {
137 string_len = (int(string_maxncpy) - ofs) - 1;
138 string[string_len] = '\0';
139 BLI_assert(src_end <= string_len);
140 }
141
142 /* Grow. */
143 memmove(string + (src_end + ofs), string + src_end, size_t(string_len - src_end) + 1);
144 string_len += ofs;
145 }
146 else if (src_len > dst_len) {
147 /* Shrink. */
148 const int ofs = src_len - dst_len;
149 memmove(string + (src_end - ofs), string + src_end, size_t(string_len - src_end) + 1);
150 string_len -= ofs;
151 }
152 else { /* Simple case, no resizing. */
153 BLI_assert(src_len == dst_len);
154 }
155
156 if (dst_len > 0) {
157 memcpy(string + src_beg, dst, size_t(dst_len));
158 }
159 BLI_assert(string[string_len] == '\0');
160 return size_t(string_len);
161}
162
165size_t BLI_string_split_name_number(const char *name,
166 const char delim,
167 char *r_name_left,
168 int *r_number)
169{
170 const size_t name_len = strlen(name);
171
172 *r_number = 0;
173 memcpy(r_name_left, name, (name_len + 1) * sizeof(char));
174
175 /* name doesn't end with a delimiter "foo." */
176 if ((name_len > 1 && name[name_len - 1] == delim) == 0) {
177 size_t a = name_len;
178 while (a--) {
179 if (name[a] == delim) {
180 r_name_left[a] = '\0'; /* truncate left part here */
181 *r_number = int(atol(name + a + 1));
182 /* casting down to an int, can overflow for large numbers */
183 if (*r_number < 0) {
184 *r_number = 0;
185 }
186 return a;
187 }
188 if (isdigit(name[a]) == 0) {
189 /* non-numeric suffix - give up */
190 break;
191 }
192 }
193 }
194
195 return name_len;
196}
197
198bool BLI_string_is_decimal(const char *string)
199{
200 if (*string == '\0') {
201 return false;
202 }
203
204 /* Keep iterating over the string until a non-digit is found. */
205 while (isdigit(*string)) {
206 string++;
207 }
208
209 /* If the non-digit we found is the terminating \0, everything was digits. */
210 return *string == '\0';
211}
212
213static bool is_char_sep(const char c)
214{
215 return ELEM(c, '.', ' ', '-', '_');
216}
217
218void BLI_string_split_suffix(const char *string,
219 const size_t string_maxlen,
220 char *r_body,
221 char *r_suf)
222{
223 BLI_string_debug_size(r_body, string_maxlen);
224 BLI_string_debug_size(r_suf, string_maxlen);
225
226 size_t len = BLI_strnlen(string, string_maxlen);
227 size_t i;
228
229 r_body[0] = r_suf[0] = '\0';
230
231 for (i = len; i > 0; i--) {
232 if (is_char_sep(string[i])) {
233 BLI_strncpy(r_body, string, i + 1);
234 BLI_strncpy(r_suf, string + i, (len + 1) - i);
235 return;
236 }
237 }
238
239 memcpy(r_body, string, len + 1);
240}
241
242void BLI_string_split_prefix(const char *string,
243 const size_t string_maxlen,
244 char *r_pre,
245 char *r_body)
246{
247 BLI_string_debug_size(r_pre, string_maxlen);
248 BLI_string_debug_size(r_body, string_maxlen);
249
250 size_t len = BLI_strnlen(string, string_maxlen);
251 size_t i;
252
253 r_body[0] = r_pre[0] = '\0';
254
255 for (i = 1; i < len; i++) {
256 if (is_char_sep(string[i])) {
257 i++;
258 BLI_strncpy(r_pre, string, i + 1);
259 BLI_strncpy(r_body, string + i, (len + 1) - i);
260 return;
261 }
262 }
263
264 BLI_strncpy(r_body, string, len);
265}
266
267size_t BLI_string_flip_side_name(char *name_dst,
268 const char *name_src,
269 const bool strip_number,
270 const size_t name_dst_maxncpy)
271{
272 BLI_string_debug_size(name_dst, name_dst_maxncpy);
273
274 size_t len;
275 char *prefix = static_cast<char *>(alloca(name_dst_maxncpy)); /* The part before the facing */
276 char *suffix = static_cast<char *>(alloca(name_dst_maxncpy)); /* The part after the facing */
277 char *number = static_cast<char *>(alloca(name_dst_maxncpy)); /* The number extension string */
278 const char *replace = nullptr;
279 char *index = nullptr;
280 bool is_set = false;
281
282 *prefix = *suffix = *number = '\0';
283
284 /* always copy the name, since this can be called with an uninitialized string */
285 len = BLI_strncpy_rlen(name_dst, name_src, name_dst_maxncpy);
286 if (len < 3) {
287 /* We don't support names such as `.R` or `.L`. */
288 return len;
289 }
290
291 /* We first check the case with a .### extension, let's find the last period */
292 if (isdigit(name_dst[len - 1])) {
293 index = strrchr(name_dst, '.'); /* Last occurrence. */
294 if (index && isdigit(index[1])) { /* Doesn't handle case `bone.1abc2` correct..., whatever! */
295 if (strip_number == false) {
296 BLI_strncpy(number, index, name_dst_maxncpy);
297 }
298 *index = 0;
299 len = BLI_strnlen(name_dst, name_dst_maxncpy);
300 }
301 }
302
303 BLI_strncpy(prefix, name_dst, name_dst_maxncpy);
304
305 /* First case; separator (`.` or `_`) with extensions in `r R l L`. */
306 if ((len > 1) && is_char_sep(name_dst[len - 2])) {
307 is_set = true;
308 switch (name_dst[len - 1]) {
309 case 'l':
310 prefix[len - 1] = 0;
311 replace = "r";
312 break;
313 case 'r':
314 prefix[len - 1] = 0;
315 replace = "l";
316 break;
317 case 'L':
318 prefix[len - 1] = 0;
319 replace = "R";
320 break;
321 case 'R':
322 prefix[len - 1] = 0;
323 replace = "L";
324 break;
325 default:
326 is_set = false;
327 }
328 }
329
330 /* case; beginning with r R l L, with separator after it */
331 if (!is_set && is_char_sep(name_dst[1])) {
332 is_set = true;
333 switch (name_dst[0]) {
334 case 'l':
335 replace = "r";
336 BLI_strncpy(suffix, name_dst + 1, name_dst_maxncpy);
337 prefix[0] = 0;
338 break;
339 case 'r':
340 replace = "l";
341 BLI_strncpy(suffix, name_dst + 1, name_dst_maxncpy);
342 prefix[0] = 0;
343 break;
344 case 'L':
345 replace = "R";
346 BLI_strncpy(suffix, name_dst + 1, name_dst_maxncpy);
347 prefix[0] = 0;
348 break;
349 case 'R':
350 replace = "L";
351 BLI_strncpy(suffix, name_dst + 1, name_dst_maxncpy);
352 prefix[0] = 0;
353 break;
354 default:
355 is_set = false;
356 }
357 }
358
359 if (!is_set && len > 5) {
360 /* Test for a separator to apply the rule: ultimate left or right. */
361 if (((index = BLI_strcasestr(prefix, "right")) == prefix) || (index == prefix + len - 5)) {
362 is_set = true;
363 if (index[0] == 'r') {
364 replace = "left";
365 }
366 else {
367 replace = (index[1] == 'I' ? "LEFT" : "Left");
368 }
369 *index = 0;
370 BLI_strncpy(suffix, index + 5, name_dst_maxncpy);
371 }
372 else if (((index = BLI_strcasestr(prefix, "left")) == prefix) || (index == prefix + len - 4)) {
373 is_set = true;
374 if (index[0] == 'l') {
375 replace = "right";
376 }
377 else {
378 replace = (index[1] == 'E' ? "RIGHT" : "Right");
379 }
380 *index = 0;
381 BLI_strncpy(suffix, index + 4, name_dst_maxncpy);
382 }
383 }
384
385 return BLI_snprintf_rlen(
386 name_dst, name_dst_maxncpy, "%s%s%s%s", prefix, replace ? replace : "", suffix, number);
387}
388
389/* Unique name utils. */
390
392 void *arg,
393 const char *defname,
394 char delim,
395 char *name,
396 size_t name_maxncpy)
397{
398 BLI_string_debug_size_after_nil(name, name_maxncpy);
399
400 if (name[0] == '\0') {
401 BLI_strncpy(name, defname, name_maxncpy);
402 }
403
404 if (unique_check(arg, name)) {
405 char numstr[16];
406 char *tempname = static_cast<char *>(alloca(name_maxncpy));
407 char *left = static_cast<char *>(alloca(name_maxncpy));
408 int number;
409 size_t len = BLI_string_split_name_number(name, delim, left, &number);
410 do {
411 const size_t numlen = SNPRINTF(numstr, "%c%03d", delim, ++number);
412
413 /* highly unlikely the string only has enough room for the number
414 * but support anyway */
415 if (UNLIKELY((len == 0) || (numlen + 1 >= name_maxncpy))) {
416 /* number is know not to be utf-8 */
417 BLI_strncpy(tempname, numstr, name_maxncpy);
418 }
419 else {
420 char *tempname_buf;
421 tempname_buf = tempname + BLI_strncpy_utf8_rlen(tempname, left, name_maxncpy - numlen);
422 memcpy(tempname_buf, numstr, numlen + 1);
423 }
424 } while (unique_check(arg, tempname));
425
426 BLI_strncpy(name, tempname, name_maxncpy);
427 }
428}
429
431 const char delim,
432 const blender::StringRef name)
433{
434 std::string new_name = name;
435
436 if (!unique_check(new_name)) {
437 return new_name;
438 }
439
440 int number;
441 blender::Array<char> left_buffer(int64_t(new_name.size()) + 1);
442 const size_t len = BLI_string_split_name_number(
443 new_name.c_str(), delim, left_buffer.data(), &number);
444
445 const std::string left = left_buffer.data();
446
447 do {
448 std::array<char, 16> num_str;
449 BLI_snprintf(num_str.data(), num_str.size(), "%c%03d", delim, ++number);
450
451 if (len == 0) {
452 new_name = num_str.data();
453 }
454 else {
455 new_name = left + num_str.data();
456 }
457 } while (unique_check(new_name));
458
459 return new_name;
460}
461
471static bool uniquename_find_dupe(const ListBase *list,
472 void *vlink,
473 const char *name,
474 int name_offset)
475{
476 for (Link *link = static_cast<Link *>(list->first); link; link = link->next) {
477 if (link != vlink) {
478 if (STREQ(static_cast<const char *>(POINTER_OFFSET((const char *)link, name_offset)), name))
479 {
480 return true;
481 }
482 }
483 }
484
485 return false;
486}
487
493
494static bool uniquename_unique_check(void *arg, const char *name)
495{
496 UniqueNameCheckData *data = static_cast<UniqueNameCheckData *>(arg);
497
498 return uniquename_find_dupe(data->lb, data->vlink, name, data->name_offset);
499}
500
501void BLI_uniquename(const ListBase *list,
502 void *vlink,
503 const char *defname,
504 char delim,
505 int name_offset,
506 size_t name_maxncpy)
507{
508 UniqueNameCheckData data{};
509 data.lb = list;
510 data.vlink = vlink;
511 data.name_offset = name_offset;
512
513 BLI_assert(name_maxncpy > 1);
514
515 /* See if we are given an empty string */
516 if (ELEM(nullptr, vlink)) {
517 return;
518 }
519
521 &data,
522 defname,
523 delim,
524 static_cast<char *>(POINTER_OFFSET(vlink, name_offset)),
525 name_maxncpy);
526}
527
528size_t BLI_string_len_array(const char *strings[], uint strings_num)
529{
530 size_t total_len = 0;
531 for (uint i = 0; i < strings_num; i++) {
532 total_len += strlen(strings[i]);
533 }
534 return total_len;
535}
536
537/* ------------------------------------------------------------------------- */
548size_t BLI_string_join_array(char *result,
549 size_t result_maxncpy,
550 const char *strings[],
551 uint strings_num)
552{
553 BLI_string_debug_size(result, result_maxncpy);
554
555 char *c = result;
556 char *c_end = &result[result_maxncpy - 1];
557 for (uint i = 0; i < strings_num; i++) {
558 const char *p = strings[i];
559 while (*p) {
560 if (UNLIKELY(!(c < c_end))) {
561 i = strings_num; /* Break outer loop. */
562 break;
563 }
564 *c++ = *p++;
565 }
566 }
567 *c = '\0';
568 return size_t(c - result);
569}
570
572 char *result, size_t result_maxncpy, char sep, const char *strings[], uint strings_num)
573{
574 BLI_string_debug_size(result, result_maxncpy);
575
576 char *c = result;
577 char *c_end = &result[result_maxncpy - 1];
578 for (uint i = 0; i < strings_num; i++) {
579 if (i != 0) {
580 if (UNLIKELY(!(c < c_end))) {
581 break;
582 }
583 *c++ = sep;
584 }
585 const char *p = strings[i];
586 while (*p) {
587 if (UNLIKELY(!(c < c_end))) {
588 i = strings_num; /* Break outer loop. */
589 break;
590 }
591 *c++ = *p++;
592 }
593 }
594 *c = '\0';
595 return size_t(c - result);
596}
597
598char *BLI_string_join_arrayN(const char *strings[], uint strings_num)
599{
600 const size_t result_size = BLI_string_len_array(strings, strings_num) + 1;
601 char *result = MEM_cnew_array<char>(result_size, __func__);
602 char *c = result;
603 for (uint i = 0; i < strings_num; i++) {
604 const size_t string_len = strlen(strings[i]);
605 memcpy(c, strings[i], string_len);
606 c += string_len;
607 }
608 /* Only needed when `strings_num == 0`. */
609 *c = '\0';
610 BLI_assert(result + result_size == c + 1);
611 return result;
612}
613
614char *BLI_string_join_array_by_sep_charN(char sep, const char *strings[], uint strings_num)
615{
616 const size_t result_size = BLI_string_len_array(strings, strings_num) +
617 (strings_num ? strings_num - 1 : 0) + 1;
618 char *result = MEM_cnew_array<char>(result_size, __func__);
619 char *c = result;
620 if (strings_num != 0) {
621 for (uint i = 0; i < strings_num; i++) {
622 const size_t string_len = strlen(strings[i]);
623 memcpy(c, strings[i], string_len);
624 c += string_len;
625 *c = sep;
626 c++;
627 }
628 c--;
629 }
630 *c = '\0';
631 BLI_assert(result + result_size == c + 1);
632 return result;
633}
634
636 char *table[],
637 const char *strings[],
638 uint strings_num)
639{
640 size_t result_size = 0;
641 for (uint i = 0; i < strings_num; i++) {
642 result_size += strlen(strings[i]) + 1;
643 }
644 if (result_size == 0) {
645 result_size = 1;
646 }
647
648 char *result = MEM_cnew_array<char>(result_size, __func__);
649 char *c = result;
650 if (strings_num != 0) {
651 for (uint i = 0; i < strings_num; i++) {
652 const size_t string_len = strlen(strings[i]);
653 memcpy(c, strings[i], string_len);
654 table[i] = c; /* <-- only difference to BLI_string_join_array_by_sep_charN. */
655 memcpy(c, strings[i], string_len);
656 c += string_len;
657 *c = sep;
658 c++;
659 }
660 c--;
661 }
662 *c = '\0';
663 BLI_assert(result + result_size == c + 1);
664 return result;
665}
666
#define BLI_assert(a)
Definition BLI_assert.h:50
A dynamically sized string ADT.
char * BLI_dynstr_get_cstring(const DynStr *ds) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition BLI_dynstr.c:149
void BLI_dynstr_nappend(DynStr *__restrict ds, const char *cstr, int len) ATTR_NONNULL()
Definition BLI_dynstr.c:81
DynStr * BLI_dynstr_new(void) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
Definition BLI_dynstr.c:37
void BLI_dynstr_free(DynStr *ds) ATTR_NONNULL()
Definition BLI_dynstr.c:174
void BLI_dynstr_append(DynStr *__restrict ds, const char *cstr) ATTR_NONNULL()
Definition BLI_dynstr.c:62
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC
Definition string.c:40
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:597
int char char int int int int size_t BLI_strnlen(const char *str, size_t maxlen) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition string.c:909
#define BLI_string_debug_size_after_nil(str, str_maxncpy)
Definition BLI_string.h:673
#define BLI_string_debug_size(str, str_maxncpy)
Definition BLI_string.h:668
size_t BLI_snprintf_rlen(char *__restrict dst, size_t dst_maxncpy, const char *__restrict format,...) ATTR_NONNULL(1
size_t BLI_snprintf(char *__restrict dst, size_t dst_maxncpy, const char *__restrict format,...) ATTR_NONNULL(1
char char size_t BLI_strncpy_rlen(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1
int char * BLI_strcasestr(const char *s, const char *find) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
char size_t BLI_strncpy_utf8_rlen(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1
bool(*)(void *arg, const char *name) UniquenameCheckCallback
unsigned int uint
#define UNLIKELY(x)
#define ELEM(...)
#define POINTER_OFFSET(v, ofs)
#define STREQ(a, b)
These structs are the foundation for all linked lists in the library system.
Read Guarded memory(de)allocation.
const T * data() const
Definition BLI_array.hh:301
int len
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
#define str(s)
__int64 int64_t
Definition stdint.h:89
size_t BLI_string_split_name_number(const char *name, const char delim, char *r_name_left, int *r_number)
bool BLI_string_replace_table_exact(char *string, const size_t string_len, const char *replace_table[][2], int replace_table_len)
char * BLI_string_replaceN(const char *__restrict str, const char *__restrict substr_old, const char *__restrict substr_new)
static bool is_char_sep(const char c)
void BLI_string_split_suffix(const char *string, const size_t string_maxlen, char *r_body, char *r_suf)
char * BLI_string_join_array_by_sep_charN(char sep, const char *strings[], uint strings_num)
static bool uniquename_find_dupe(const ListBase *list, void *vlink, const char *name, int name_offset)
char * BLI_string_join_array_by_sep_char_with_tableN(char sep, char *table[], const char *strings[], uint strings_num)
void BLI_uniquename(const ListBase *list, void *vlink, const char *defname, char delim, int name_offset, size_t name_maxncpy)
void BLI_uniquename_cb(UniquenameCheckCallback unique_check, void *arg, const char *defname, char delim, char *name, size_t name_maxncpy)
size_t BLI_string_join_array_by_sep_char(char *result, size_t result_maxncpy, char sep, const char *strings[], uint strings_num)
void BLI_string_split_prefix(const char *string, const size_t string_maxlen, char *r_pre, char *r_body)
void BLI_string_replace_char(char *str, char src, char dst)
size_t BLI_string_join_array(char *result, size_t result_maxncpy, const char *strings[], uint strings_num)
size_t BLI_string_replace_range(char *string, size_t string_maxncpy, int src_beg, int src_end, const char *dst)
char * BLI_string_join_arrayN(const char *strings[], uint strings_num)
size_t BLI_string_len_array(const char *strings[], uint strings_num)
size_t BLI_string_flip_side_name(char *name_dst, const char *name_src, const bool strip_number, const size_t name_dst_maxncpy)
bool BLI_string_is_decimal(const char *string)
static bool uniquename_unique_check(void *arg, const char *name)
const ListBase * lb