Cthulhu  0.2.10
Cthulhu compiler collection
str.c
Go to the documentation of this file.
1 // SPDX-License-Identifier: LGPL-3.0-only
2 
3 #include "std/str.h"
4 
5 #include "std/map.h"
6 #include "std/typed/vector.h"
7 #include "std/vector.h"
8 
9 #include "arena/arena.h"
10 
11 #include "base/util.h"
12 #include "base/panic.h"
13 
14 #include "core/macros.h"
15 
16 #include <limits.h>
17 
18 #if CTU_STB_SPRINTF
19 # include "stb_sprintf.h"
20 # define CT_SPRINTF stbsp_snprintf
21 # define CT_SNPRINTF stbsp_snprintf
22 # define CT_VSNPRINTF stbsp_vsnprintf
23 #else
24 # include <string.h>
25 # include <stdio.h>
26 # define CT_SPRINTF snprintf
27 # define CT_SNPRINTF snprintf
28 # define CT_VSNPRINTF vsnprintf
29 #endif
30 
31 // TODO: is it worth using stbsp_vsprintfcb when its available?
32 
34 size_t str_sprintf(char *str, size_t len, const char *fmt, ...)
35 {
36  va_list args;
37  va_start(args, fmt);
38 
39  size_t result = str_vsprintf(str, len, fmt, args);
40 
41  va_end(args);
42 
43  return result;
44 }
45 
47 size_t str_vsprintf(char *str, size_t len, const char *fmt, va_list args)
48 {
49  CTASSERT(fmt != NULL);
50  CTASSERTF(len < INT_MAX, "len = %zu", len);
51 
52  return CT_VSNPRINTF(str, (int)len, fmt, args);
53 }
54 
56 text_t text_vformat(arena_t *arena, const char *fmt, va_list args)
57 {
58  CTASSERT(arena != NULL);
59  CTASSERT(fmt != NULL);
60 
61  // make a copy of the args for the second format
62  va_list again;
63  va_copy(again, args);
64 
65  // get the number of bytes needed to format
66  size_t len = str_vsprintf(NULL, 0, fmt, args);
67 
68  CTASSERTF(len > 0, "text_vformat failed to format string: %s", fmt);
69 
70  char *out = ARENA_MALLOC(len + 1, "text_vformat", NULL, arena);
71 
72  size_t result = str_vsprintf(out, len + 1, fmt, again);
73  CTASSERTF(result == len, "text_vformat failed to format string: %s expected (%zu == %zu)", fmt, result, len);
74 
75  va_end(again);
76 
77  return text_make(out, len);
78 }
79 
81 text_t text_format(arena_t *arena, const char *fmt, ...)
82 {
83  CTASSERT(arena != NULL);
84  CTASSERT(fmt != NULL);
85 
86  va_list args;
87  va_start(args, fmt);
88 
89  text_t text = text_vformat(arena, fmt, args);
90 
91  va_end(args);
92 
93  return text;
94 }
95 
97 char *str_format(arena_t *arena, const char *fmt, ...)
98 {
99  va_list args;
100  va_start(args, fmt);
101 
102  char *str = str_vformat(arena, fmt, args);
103 
104  va_end(args);
105 
106  return str;
107 }
108 
109 STA_DECL
110 char *str_vformat(arena_t *arena, const char *fmt, va_list args)
111 {
112  CTASSERT(arena != NULL);
113  CTASSERT(fmt != NULL);
114 
115  text_t text = text_vformat(arena, fmt, args);
116 
117  return text.text;
118 }
119 
120 STA_DECL
121 bool char_is_any_of(char c, const char *chars)
122 {
123  CTASSERT(chars != NULL);
124 
125  for (; *chars; chars++)
126  {
127  if (*chars == c)
128  {
129  return true;
130  }
131  }
132 
133  return false;
134 }
135 
136 STA_DECL
137 size_t str_rfind_any(const char *str, const char *letters)
138 {
139  CTASSERT(str != NULL);
140  CTASSERT(letters != NULL);
141 
142  size_t i = ctu_strlen(str);
143  while (i > 0)
144  {
145  i--;
146  if (char_is_any_of(str[i], letters))
147  {
148  return i;
149  }
150  }
151 
152  return SIZE_MAX;
153 }
154 
155 STA_DECL
156 char *str_noext(const char *path, arena_t *arena)
157 {
158  CTASSERT(path != NULL);
159  CTASSERT(arena != NULL);
160 
161  size_t idx = str_rfind(path, ".");
162  char *base = arena_strdup(path, arena);
163  if (idx == SIZE_MAX)
164  {
165  return base;
166  }
167 
168  base[idx] = '\0';
169  return base;
170 }
171 
172 STA_DECL
173 char *str_ext(const char *path, arena_t *arena)
174 {
175  CTASSERT(path != NULL);
176  CTASSERT(arena != NULL);
177 
178  size_t idx = str_rfind(path, ".");
179  if (idx == SIZE_MAX)
180  {
181  return NULL;
182  }
183 
184  return arena_strdup(path + idx + 1, arena);
185 }
186 
187 STA_DECL
188 char *str_directory(const char *path, arena_t *arena)
189 {
190  CTASSERT(path != NULL);
191  CTASSERT(arena != NULL);
192 
193  size_t idx = str_rfind_any(path, CT_PATH_SEPERATORS);
194  if (idx == SIZE_MAX)
195  {
196  return arena_strdup(".", arena);
197  }
198 
199  return arena_strndup(path, idx, arena);
200 }
201 
202 STA_DECL
203 char *str_basename(const char *path, arena_t *arena)
204 {
205  CTASSERT(path != NULL);
206  CTASSERT(arena != NULL);
207 
208  size_t idx = str_rfind_any(path, CT_PATH_SEPERATORS);
209  if (idx == SIZE_MAX)
210  {
211  return str_noext(path, arena);
212  }
213 
214  return str_noext(path + idx + 1, arena);
215 }
216 
217 STA_DECL
218 char *str_filename(const char *path, arena_t *arena)
219 {
220  CTASSERT(path != NULL);
221  CTASSERT(arena != NULL);
222 
223  size_t idx = str_rfind_any(path, CT_PATH_SEPERATORS);
224  if (idx == SIZE_MAX)
225  {
226  return arena_strdup(path, arena);
227  }
228 
229  return arena_strdup(path + idx + 1, arena);
230 }
231 
232 STA_DECL
233 bool str_startswith(const char *str, const char *prefix)
234 {
235  CTASSERT(str != NULL);
236  CTASSERT(prefix != NULL);
237 
238  return ctu_strncmp(str, prefix, ctu_strlen(prefix)) == 0;
239 }
240 
241 STA_DECL
242 bool str_endswith(const char *str, const char *suffix)
243 {
244  CTASSERT(str != NULL);
245  CTASSERT(suffix != NULL);
246 
247  size_t lenstr = ctu_strlen(str);
248  size_t lensuffix = ctu_strlen(suffix);
249  if (lensuffix > lenstr)
250  {
251  return false;
252  }
253 
254  return ctu_strncmp(str + lenstr - lensuffix, suffix, lensuffix) == 0;
255 }
256 
257 
258 STA_DECL
259 bool str_endswithn(const char *str, size_t len, const char *suffix)
260 {
261  CTASSERT(str != NULL);
262  CTASSERT(suffix != NULL);
263 
264  size_t lensuffix = ctu_strlen(suffix);
265  if (lensuffix > len)
266  {
267  return false;
268  }
269 
270  return ctu_strncmp(str + len - lensuffix, suffix, lensuffix) == 0;
271 }
272 
273 STA_DECL
274 char *str_join(const char *sep, const vector_t *parts, arena_t *arena)
275 {
276  CTASSERT(sep != NULL);
277  CTASSERT(parts != NULL);
278  CTASSERT(arena != NULL);
279 
280  size_t all = vector_len(parts);
281 
282  if (all == 0)
283  {
284  return arena_strndup("", 0, arena);
285  }
286 
287  if (all == 1)
288  {
289  char *it = vector_get(parts, 0);
290  CTASSERT(it != NULL);
291  return arena_strdup(it, arena);
292  }
293 
294  size_t len = 0;
295  size_t seplen = ctu_strlen(sep);
296  for (size_t i = 0; i < all; i++)
297  {
298  const char *part = vector_get(parts, i);
299  CTASSERTF(part != NULL, "part[%zu] = NULL", i);
300 
301  len += ctu_strlen(part);
302 
303  if (i != 0)
304  {
305  len += seplen;
306  }
307  }
308 
309  CTASSERTF(len > 0, "joined string would be empty, how did we get this far?");
310 
311  char *out = ARENA_MALLOC(len + 1, "str_join", parts, arena);
312  size_t idx = 0;
313 
314  size_t remaining = len;
315  for (size_t i = 0; i < all; i++)
316  {
317  if (i != 0)
318  {
319  ctu_memcpy(out + idx, sep, CT_MIN(remaining, seplen));
320  idx += seplen;
321  remaining -= seplen;
322  }
323 
324  const char *part = vector_get(parts, i);
325  size_t part_len = ctu_strlen(part);
326  ctu_memcpy(out + idx, part, CT_MIN(remaining, part_len));
327  idx += part_len;
328  remaining -= part_len;
329  }
330 
331  out[idx] = 0;
332  return out;
333 }
334 
335 STA_DECL
336 char *str_repeat(const char *str, size_t times, arena_t *arena)
337 {
338  CTASSERT(str != NULL);
339  CTASSERT(arena != NULL);
340 
341  if (times == 0)
342  {
343  return arena_strdup("", arena);
344  }
345 
346  size_t len = ctu_strlen(str);
347  size_t outlen = len * times;
348  char *out = ARENA_MALLOC(outlen + 1, "str_repeat", str, arena);
349  size_t remaining = outlen;
350  for (size_t i = 0; i < times; i++)
351  {
352  ctu_memcpy(out + i * len, str, CT_MIN(remaining, len));
353  remaining -= len;
354  }
355  out[outlen] = 0;
356  return out;
357 }
358 
359 static const char kEscapeChars[] = {
360  '\n', '\t', '\f', '\r', '\v', '\\', '\0', '\'', '\"',
361 };
362 
363 static bool is_escape_char(char c)
364 {
365  for (size_t i = 0; i < sizeof(kEscapeChars); i++)
366  {
367  if (kEscapeChars[i] == c)
368  {
369  return true;
370  }
371  }
372 
373  return false;
374 }
375 
376 static bool ctu_isprint(char c)
377 {
378  if (is_escape_char(c))
379  {
380  return false;
381  }
382 
383  if (c > 0x1F && c != 0x7F)
384  {
385  return true;
386  }
387 
388  return false;
389 }
390 
391 static size_t normlen(char c)
392 {
393  // TODO: this might overallocate size
394  return ctu_isprint(c) ? 1 : 4;
395 }
396 
397 static size_t normstr(char *out, char c)
398 {
399  if (ctu_isprint(c))
400  {
401  *out = c;
402  return 1;
403  }
404 
405  switch (c)
406  {
407  case '\\':
408  out[0] = '\\';
409  out[1] = '\\';
410  return 2;
411  case '\'':
412  out[0] = '\\';
413  out[1] = '\'';
414  return 2;
415  case '\"':
416  out[0] = '\\';
417  out[1] = '\"';
418  return 2;
419  case '\0':
420  out[0] = '\\';
421  out[1] = '0';
422  return 2;
423  case '\n':
424  out[0] = '\\';
425  out[1] = 'n';
426  return 2;
427  case '\t':
428  out[0] = '\\';
429  out[1] = 't';
430  return 2;
431  case '\r':
432  out[0] = '\\';
433  out[1] = 'r';
434  return 2;
435  default: {
436  int result = CT_SNPRINTF(out, 5, "\\x%02x", (c & 0xFF));
437  CTASSERT(result > 0);
438  return result;
439  }
440  }
441 }
442 
443 STA_DECL
444 size_t str_normalize_into(char *dst, size_t dstlen, const char *src, size_t srclen)
445 {
446  // preconditions
447  CTASSERT(src != NULL);
448  CTASSERT(srclen > 0);
449  if (dstlen == 0) CTASSERT(dst == NULL);
450  if (dst == NULL) CTASSERT(dstlen == 0);
451 
452 
453  // calculate required length
454  size_t outlength = 0;
455  for (size_t i = 0; i < srclen; i++)
456  outlength += normlen(src[i]);
457 
458  // if requested, return that length
459  if (dst == NULL)
460  return outlength;
461 
462  size_t avail = CT_MIN(outlength, dstlen);
463 
464  // if the string is already normalized, just copy it
465  if (outlength == srclen)
466  {
467  ctu_memcpy(dst, src, avail);
468  return avail;
469  }
470 
471  char *result = dst;
472  const char *end = dst + avail;
473  while (*src != '\0' && result < end)
474  {
475  size_t inc = normstr(result, *src++);
476  result += inc;
477  }
478 
479  *result = '\0';
480 
481  return (result - dst) - 1;
482 }
483 
484 STA_DECL
485 char *str_normalize(const char *str, arena_t *arena)
486 {
487  CTASSERT(str != NULL);
488  CTASSERT(arena != NULL);
489 
490  size_t input_length = 0;
491  size_t result_length = 0;
492 
493  // compute the length of both the input string
494  // and the required length to allocate for the normalized string
495  {
496  const char *iter = str;
497  while (*iter != '\0')
498  {
499  size_t inc = normlen(*iter++);
500  result_length += inc;
501  input_length += 1;
502  }
503  }
504 
505  // if the string is already normalized, just return a copy
506  if (input_length == result_length)
507  {
508  return arena_strndup(str, input_length, arena);
509  }
510 
511  const char *iter = str;
512  char *buf = ARENA_MALLOC(result_length + 1, "str_normalize", str, arena);
513  char *result = buf;
514  while (*iter != '\0')
515  {
516  result += normstr(result, *iter++);
517  }
518  *result = '\0';
519 
520  return buf;
521 }
522 
523 STA_DECL
524 char *str_normalizen(text_view_t text, arena_t *arena)
525 {
526  CTASSERT(text.text != NULL);
527  CTASSERT(arena != NULL);
528 
529  const char *str = text.text;
530  size_t len = text.length;
531 
532  size_t length = 1;
533  for (size_t i = 0; i < len; i++)
534  {
535  length += normlen(str[i]);
536  }
537 
538  // if the string is already normalized, just return a copy
539  if (length == len)
540  {
541  return arena_strndup(str, len, arena);
542  }
543 
544  char *buf = ARENA_MALLOC(length + 1, "str_normalizen", str, arena);
545  size_t offset = 0;
546  for (size_t i = 0; i < len; i++)
547  {
548  size_t inc = normstr(buf + offset, str[i]);
549  offset += inc;
550  }
551 
552  buf[offset] = '\0';
553  return buf;
554 }
555 
556 STA_DECL
557 vector_t *str_split(IN_STRING const char *str, IN_STRING const char *sep, arena_t *arena)
558 {
559  CTASSERT(str != NULL);
560  CTASSERT(sep != NULL);
561  CTASSERT(arena != NULL);
562 
563  if (ctu_string_empty(sep))
564  {
565  // split into individual characters
566  vector_t *result = vector_new(ctu_strlen(str), arena);
567  for (size_t i = 0; i < ctu_strlen(str); i++)
568  {
569  char *c = ARENA_MALLOC(2, "str_split", str, arena);
570  c[0] = str[i];
571  c[1] = '\0';
572  vector_push(&result, c);
573  }
574  return result;
575  }
576 
577  size_t seplen = ctu_strlen(sep);
578  vector_t *result = vector_new(4, arena);
579 
580  // store the start of the current token
581  // continue until a seperator is found
582  // then push that token to the vector
583  // and start the next token
584 
585  const char *token = str;
586  const char *cursor = str;
587 
588  while (*cursor)
589  {
590  if (!str_startswith(cursor, sep))
591  {
592  cursor += 1;
593  continue;
594  }
595 
596  vector_push(&result, arena_strndup(token, cursor - token, arena));
597  token = cursor + seplen;
598  cursor += seplen;
599  }
600 
601  vector_push(&result, arena_strndup(token, cursor - token, arena));
602 
603  return result;
604 }
605 
606 STA_DECL
607 bool str_contains(const char *str, const char *search)
608 {
609  CTASSERT(str != NULL);
610  CTASSERT(search != NULL);
611 
612  return ctu_strstr(str, search) != NULL;
613 }
614 
615 STA_DECL
616 char *str_replace(const char *str, const char *search, const char *repl, arena_t *arena)
617 {
618  CTASSERT(str != NULL);
619  CTASSERT(search != NULL);
620  CTASSERT(repl != NULL);
621 
622  if (ctu_string_empty(search))
623  {
624  return arena_strdup(str, arena);
625  }
626 
627  vector_t *split = str_split(str, search, arena);
628  return str_join(repl, split, arena);
629 }
630 
631 static const map_entry_t *find_matching_key(typevec_t *pairs, const char *str)
632 {
633  for (size_t i = 0; i < typevec_len(pairs); i++)
634  {
635  const map_entry_t *entry = typevec_offset(pairs, i);
636  CTASSERT(entry != NULL);
637  CTASSERT(entry->key != NULL);
638  CTASSERT(entry->value != NULL);
639 
640  if (str_startswith(str, entry->key))
641  {
642  return entry;
643  }
644  }
645 
646  return NULL;
647 }
648 
649 STA_DECL
650 void str_replace_inplace(text_t *text, const char *search, const char *repl)
651 {
652  CTASSERT(text != NULL);
653  CTASSERT(search != NULL);
654  CTASSERT(repl != NULL);
655  CTASSERT(text->text != NULL);
656  CTASSERT(ctu_strlen(search) > 0);
657 
658  size_t search_len = ctu_strlen(search);
659  size_t repl_len = ctu_strlen(repl);
660 
661  char *str = text->text;
662  size_t len = text->length;
663 
664  size_t offset = 0;
665 
666  while (offset < len)
667  {
668  const char *found = ctu_strstr(str + offset, search);
669  if (found == NULL)
670  {
671  break;
672  }
673 
674  size_t found_offset = (size_t)(found - str);
675  size_t found_len = ctu_strlen(found);
676 
677  // move the found string to the end of the buffer
678  ctu_memmove(str + offset + repl_len, found + search_len, found_len - search_len);
679 
680  // copy the replacement string
681  ctu_memcpy(str + offset, repl, repl_len);
682 
683  // update the length of the string
684  len -= search_len - repl_len;
685 
686  // update the offset
687  offset = found_offset + repl_len;
688  }
689 
690  text->length = len;
691 
692  // null terminate the string
693  str[len] = '\0';
694 }
695 
696 STA_DECL
697 void str_trim_back_inplace(text_t *text, const char *chars)
698 {
699  CTASSERT(text != NULL);
700  CTASSERT(chars != NULL);
701  CTASSERT(text->text != NULL);
702 
703  size_t len = text->length;
704  char *str = text->text;
705 
706  while (len > 0 && char_is_any_of(str[len - 1], chars))
707  {
708  len -= 1;
709  }
710 
711  text->length = len;
712  str[len] = '\0';
713 }
714 
715 STA_DECL
716 void str_sort_inplace(char *str, size_t len)
717 {
718  CTASSERT(str != NULL);
719 
720  // TODO: something better than bubblesort :^)
721  for (size_t i = 0; i < len; i++)
722  {
723  for (size_t j = i + 1; j < len; j++)
724  {
725  if (str[i] > str[j])
726  {
727  char temp = str[i];
728  str[i] = str[j];
729  str[j] = temp;
730  }
731  }
732  }
733 }
734 
735 STA_DECL
736 char *str_replace_many(const char *str, const map_t *repl, arena_t *arena)
737 {
738  CTASSERT(str != NULL);
739  CTASSERT(repl != NULL);
740  CTASSERT(arena != NULL);
741 
742  size_t len = 0;
743  typevec_t *pairs = map_entries((map_t*)repl); // TODO: map_entries should have a const version
744 
745  const char *iter = str;
746  while (*iter)
747  {
748  const map_entry_t *entry = find_matching_key(pairs, iter);
749  if (entry != NULL)
750  {
751  len += ctu_strlen(entry->value);
752  iter += ctu_strlen(entry->key);
753  }
754  else
755  {
756  len += 1;
757  iter += 1;
758  }
759  }
760 
761  char *out = ARENA_MALLOC(len + 1, "str_replace_many", repl, arena);
762 
763  size_t offset = 0; // offset into input string
764  for (size_t i = 0; i < len;)
765  {
766  const map_entry_t *entry = find_matching_key(pairs, str + offset);
767  if (entry != NULL)
768  {
769  ctu_memcpy(out + i, entry->value, ctu_strlen(entry->value));
770  i += ctu_strlen(entry->value);
771  offset += ctu_strlen(entry->key);
772  }
773  else
774  {
775  out[i++] = str[offset++];
776  }
777  }
778 
779  out[len] = '\0';
780 
781  return out;
782 }
783 
789 STA_DECL
790 const char *str_common_prefix(const vector_t *args, arena_t *arena)
791 {
792  CTASSERT(args != NULL);
793  CTASSERT(arena != NULL);
794 
795  size_t len = vector_len(args);
796  CTASSERT(len > 0);
797 
798  const char *result = "";
799 
800  // only one argument, return it
801  if (len == 1)
802  {
803  const char *it = vector_get(args, 0);
804  CTASSERT(it != NULL);
805  return it;
806  }
807 
808  size_t size = len * sizeof(char *);
809  char **strings = ARENA_MALLOC(size, "str_common_prefix", args, arena);
810 
811  size_t lower = SIZE_MAX;
812 
813  // find common prefix, we account for path seperators
814  // because this is used to shrink file paths
815  for (size_t i = 0; i < len; i++)
816  {
817  char *arg = vector_get(args, i);
818  CTASSERTF(arg != NULL, "args[%zu] = NULL", i);
819 
820  // find the last path seperator
821  // we find the common prefix up to the last path seperator
822  size_t find = str_rfind_any(arg, CT_PATH_SEPERATORS) + 1;
823  strings[i] = arena_strndup(arg, find, arena);
824 
825  lower = CT_MIN(lower, find);
826  }
827 
828  // no common prefix was found
829  if (lower == 0 || lower == SIZE_MAX)
830  {
831  goto finish;
832  }
833 
834  for (size_t i = 0; i < lower; i++)
835  {
836  for (size_t j = 1; j < len; j++)
837  {
838  if (strings[j][i] != strings[0][i])
839  {
840  result = i == 0 ? "" : arena_strndup(strings[0], i, arena);
841  goto finish;
842  }
843  }
844  }
845 
846  // if we get here, the common prefix is the shortest string
847  result = arena_strndup(strings[0], lower, arena);
848 
849 finish:
850  CTASSERT(strings != NULL);
851  for (size_t i = 0; i < len; i++)
852  {
853  CTASSERTF(strings[i] != NULL, "strings[%zu] = NULL", i);
854  arena_free(strings[i], CT_ALLOC_SIZE_UNKNOWN, arena);
855  }
856 
857  arena_free(strings, size, arena);
858 
859  CTASSERT(result != NULL);
860  return result;
861 }
862 
863 static size_t str_rfind_inner(const char *str, size_t len, const char *sub, size_t sublen)
864 {
865  CTASSERT(str != NULL);
866  CTASSERT(sub != NULL);
867 
868  if (len == 0) { return SIZE_MAX; }
869 
870  CTASSERTM(sublen > 0, "sub must be non-empty");
871 
872  while (len--)
873  {
874  if (ctu_strncmp(str + len, sub, sublen) == 0)
875  {
876  return len;
877  }
878  }
879 
880  return SIZE_MAX;
881 }
882 
883 STA_DECL
884 size_t str_rfind(const char *str, const char *sub)
885 {
886  CTASSERT(str != NULL);
887  CTASSERT(sub != NULL);
888 
889  size_t len = ctu_strlen(str);
890  size_t sublen = ctu_strlen(sub);
891 
892  return str_rfind_inner(str, len, sub, sublen);
893 }
894 
895 STA_DECL
896 size_t str_find(const char *str, const char *sub)
897 {
898  CTASSERT(str != NULL);
899  CTASSERT(sub != NULL);
900 
901  const char *ptr = ctu_strstr(str, sub);
902  return ptr == NULL ? SIZE_MAX : (size_t)(ptr - str);
903 }
904 
906 char *str_erase(char *str, size_t len, const char *letters)
907 {
908  CTASSERT(str != NULL);
909  CTASSERT(letters != NULL);
910 
911  // start at the back of str and remove letters,
912  // if a letter is removed then the offset doesnt get updated
913  // if a letter isnt removed then the offset gets updated
914  // push any characters that arent removed to the offset and update it
915 
916  size_t remaining = len;
917  size_t offset = len;
918  while (remaining > 0)
919  {
920  if (char_is_any_of(str[remaining - 1], letters))
921  {
922  remaining--;
923  }
924  else
925  {
926  str[--offset] = str[--remaining];
927  }
928  }
929 
930  return str + offset;
931 }
932 
933 STA_DECL
934 char *str_upper(const char *str, arena_t *arena)
935 {
936  CTASSERT(str != NULL);
937  CTASSERT(arena != NULL);
938 
939  char *result = arena_strdup(str, arena);
940  char *temp = result;
941 
942  while (*temp)
943  {
944  *temp = str_toupper(*temp);
945  temp += 1;
946  }
947 
948  return result;
949 }
950 
951 STA_DECL
952 char *str_lower(const char *str, arena_t *arena)
953 {
954  CTASSERT(str != NULL);
955  CTASSERT(arena != NULL);
956 
957  char *result = arena_strdup(str, arena);
958  char *temp = result;
959 
960  while (*temp)
961  {
962  *temp = str_tolower(*temp);
963  temp += 1;
964  }
965 
966  return result;
967 }
968 
969 STA_DECL
970 char str_tolower(char c)
971 {
972  if (c >= 'A' && c <= 'Z')
973  {
974  return (char)((c + 'a' - 'A') & CHAR_MAX);
975  }
976 
977  return c;
978 }
979 
980 STA_DECL
981 char str_toupper(char c)
982 {
983  if (c >= 'a' && c <= 'z')
984  {
985  return (char)((c + 'A' - 'a') & CHAR_MAX);
986  }
987 
988  return c;
989 }
990 
991 STA_DECL
993 {
994  if (lhs.length != rhs.length)
995  {
996  return false;
997  }
998 
999  return ctu_strncmp(lhs.text, rhs.text, lhs.length) == 0;
1000 }
CT_NODISCARD size_t size
Definition: scan.h:128
#define STA_DECL
sal2 annotation on function implementations to copy annotations from the declaration
#define IN_STRING
annotate a parameter as being a null terminated string
#define CT_NOALIAS
mark a function as only modifying pointers passed to it the same as CT_CONSTFN but allowed to modify/...
Definition: analyze.h:226
CT_NODISCARD CT_PUREFN CT_BASE_API int ctu_strncmp(const char *lhs, const char *rhs, size_t length)
compare two strings equivalent to strncmp but with safety checks
Definition: util.c:103
CT_CONSTFN CT_BASE_API text_t text_make(STA_READS(length) char *text, size_t length)
create a new owning text array text must be a valid string at least length bytes long
CT_BASE_API void * ctu_memmove(STA_WRITES(size) void *dst, STA_READS(size) const void *src, size_t size)
move memory from one location to another equivalent to memmove but with safety checks
CT_NOALIAS CT_BASE_API void * ctu_memcpy(STA_WRITES(size) void *CT_RESTRICT dst, STA_READS(size) const void *CT_RESTRICT src, size_t size)
copy memory from one location to another equivalent to memcpy but with safety checks
CT_NODISCARD CT_PUREFN CT_BASE_API bool ctu_string_empty(const char *str)
check if a string is empty equivalent to strlen(str) == 0
Definition: util.c:95
CT_NODISCARD CT_PUREFN CT_BASE_API size_t ctu_strlen(const char *str)
get the length of a string not including the null terminator equivalent to strlen but with safety che...
Definition: util.c:87
CT_NODISCARD CT_PUREFN CT_BASE_API char * ctu_strstr(const char *haystack, const char *needle)
find a substring in a string equivalent to strstr but with safety checks
Definition: util.c:156
STA_RET_STRING colour_t idx
Definition: colour.h:98
#define CT_PATH_SEPERATORS
Definition: compiler.h:89
CT_NODISCARD CT_STD_API typevec_t * map_entries(map_t *map)
collect all key-value pairs in a map into a vector returns a typevec_t<map_entry_t>
Definition: map.c:161
#define CT_MIN(L, R)
Definition: macros.h:38
#define CT_ALLOC_SIZE_UNKNOWN
unknown allocation size constant when freeing or reallocating memory, this can be used as the size to...
Definition: arena.h:37
CT_NODISCARD CT_ARENA_API char * arena_strdup(const char *str, arena_t *arena)
allocate a copy of a string from a custom allocator
Definition: arena.c:95
CT_NODISCARD CT_ARENA_API char * arena_strndup(STA_READS(len) const char *str, size_t len, arena_t *arena)
allocate a copy of a string with a maximum length from a custom allocator
#define ARENA_MALLOC(size, name, parent, arena)
allocate memory from a custom allocator
Definition: arena.h:392
CT_ARENA_API void arena_free(STA_RELEASE void *ptr, size_t size, arena_t *arena)
release memory from a custom allocator
#define CTASSERT(expr)
assert a condition, prints the condition as a message
Definition: panic.h:130
#define CTASSERTF(expr,...)
assert a condition with a message and optional format arguments
Definition: panic.h:116
#define CTASSERTM(expr, msg)
assert a condition with a message
Definition: panic.h:124
STA_DECL char * str_noext(const char *path, arena_t *arena)
remove the last file extension from a path
Definition: str.c:156
STA_DECL size_t str_rfind_any(const char *str, const char *letters)
find the first instance of a set of characters in a string
Definition: str.c:137
STA_DECL char * str_replace(const char *str, const char *search, const char *repl, arena_t *arena)
replace all instances of a substring in a string
Definition: str.c:616
STA_DECL bool text_view_equal(text_view_t lhs, text_view_t rhs)
check if two strings are equal
Definition: str.c:992
STA_DECL bool str_endswith(const char *str, const char *suffix)
check if a string ends with a substring
Definition: str.c:242
STA_DECL size_t str_find(const char *str, const char *sub)
find the first instance of a substring in a string
Definition: str.c:896
STA_DECL char * str_normalizen(text_view_t text, arena_t *arena)
turn a string with length into a C string literal
Definition: str.c:524
STA_DECL text_t text_vformat(arena_t *arena, const char *fmt, va_list args)
format a string
Definition: str.c:56
STA_DECL char * str_repeat(const char *str, size_t times, arena_t *arena)
repeat a string
Definition: str.c:336
STA_DECL char * str_upper(const char *str, arena_t *arena)
uppercase an ascii string
Definition: str.c:934
STA_DECL char * str_filename(const char *path, arena_t *arena)
get the filename from a path
Definition: str.c:218
STA_DECL bool char_is_any_of(char c, const char *chars)
check if a character is any of a set of characters
Definition: str.c:121
STA_DECL char * str_ext(const char *path, arena_t *arena)
get the last file extension from a path
Definition: str.c:173
STA_DECL vector_t * str_split(const char *str, const char *sep, arena_t *arena)
split a string into a vector by a separator
Definition: str.c:557
STA_DECL char * str_normalize(const char *str, arena_t *arena)
turn a string into a C string literal
Definition: str.c:485
STA_DECL char * str_join(const char *sep, const vector_t *parts, arena_t *arena)
join strings
Definition: str.c:274
CT_NODISCARD STA_FORMAT_STRING const char * fmt
Definition: str.h:68
STA_DECL char * str_directory(const char *path, arena_t *arena)
get the directory segment of a path
Definition: str.c:188
STA_DECL char * str_replace_many(const char *str, const map_t *repl, arena_t *arena)
replace all instances of a each substring in a string with provided replacement
Definition: str.c:736
STA_DECL const char * str_common_prefix(const vector_t *args, arena_t *arena)
find the longest common prefix of a vector of paths
Definition: str.c:790
STA_DECL size_t str_rfind(const char *str, const char *sub)
find the last instance of a substring in a string
Definition: str.c:884
STA_DECL char * str_lower(const char *str, arena_t *arena)
lowercase an ascii string this allocates a new string in the provided arena
Definition: str.c:952
STA_DECL bool str_contains(const char *str, const char *search)
check if a string contains a substring
Definition: str.c:607
STA_DECL char * str_basename(const char *path, arena_t *arena)
get the filename from a path
Definition: str.c:203
STA_DECL char * str_vformat(arena_t *arena, const char *fmt, va_list args)
format a string
Definition: str.c:110
STA_DECL char str_tolower(char c)
get the lowercase version of a character
Definition: str.c:970
STA_DECL char str_toupper(char c)
get the uppercase version of a character
Definition: str.c:981
STA_DECL bool str_startswith(const char *str, const char *prefix)
see if a string starts with a prefix
Definition: str.c:233
CT_NODISCARD CT_PUREFN CT_STD_API size_t typevec_len(const typevec_t *vec)
get the length of a vector
Definition: vector.c:120
CT_NODISCARD CT_PUREFN CT_STD_API void * typevec_offset(const typevec_t *vec, size_t index)
get a pointer to the value at the given index
Definition: vector.c:191
CT_NODISCARD CT_PUREFN CT_STD_API void * vector_get(const vector_t *vector, size_t index)
get a value from a vector
Definition: vector.c:134
CT_NODISCARD CT_STD_API vector_t * vector_new(size_t size, arena_t *arena)
create a new vector with an initial capacity
Definition: vector.c:63
CT_STD_API void vector_push(vector_t **vector, void *value)
push a value onto the end of a vector
Definition: vector.c:108
CT_NODISCARD CT_PUREFN CT_STD_API size_t vector_len(const vector_t *vector)
get the length of a vector
Definition: vector.c:152
STA_DECL size_t str_normalize_into(char *dst, size_t dstlen, const char *src, size_t srclen)
Definition: str.c:444
STA_DECL void str_sort_inplace(char *str, size_t len)
Definition: str.c:716
STA_DECL text_t text_format(arena_t *arena, const char *fmt,...)
Definition: str.c:81
STA_DECL void str_trim_back_inplace(text_t *text, const char *chars)
Definition: str.c:697
STA_DECL size_t str_sprintf(char *str, size_t len, const char *fmt,...)
Definition: str.c:34
STA_DECL size_t str_vsprintf(char *str, size_t len, const char *fmt, va_list args)
Definition: str.c:47
STA_DECL bool str_endswithn(const char *str, size_t len, const char *suffix)
Definition: str.c:259
#define CT_SNPRINTF
Definition: str.c:27
STA_DECL CT_NOALIAS char * str_erase(char *str, size_t len, const char *letters)
Definition: str.c:906
STA_DECL void str_replace_inplace(text_t *text, const char *search, const char *repl)
Definition: str.c:650
#define CT_VSNPRINTF
Definition: str.c:28
STA_DECL char * str_format(arena_t *arena, const char *fmt,...)
Definition: str.c:97
an allocator object
Definition: arena.h:86
a key-value pair in a map
Definition: map.h:175
const void * key
the key of this entry
Definition: map.h:176
void * value
the value of this entry
Definition: map.h:177
an unordered hash map
Definition: map.h:38
a range of text
Definition: text.h:14
size_t length
the number of characters in the text
Definition: text.h:19
a non-owning view of text
Definition: text.h:24
size_t length
the number of characters in the text
Definition: text.h:30
A vector with a fixed type size.
Definition: vector.h:24
a generic vector of pointers
Definition: vector.c:16