cwist
Modern C web framework with libttak runtime
Loading...
Searching...
No Matches
sstring.c
Go to the documentation of this file.
4#include <stdio.h>
5#include <stdlib.h>
6#include <string.h>
7#include <stdint.h>
8#include <stdbool.h>
9#include <ctype.h>
10
27
49static char *cwist_sstring_reserve(cwist_sstring *str, size_t needed, size_t preserve_len) {
50 if (!str->borrows_buffer && str->data && str->capacity >= needed) return str->data;
51
52 size_t new_cap = str->capacity ? str->capacity : 16;
53 while (new_cap < needed) {
54 if (new_cap > (SIZE_MAX - 1) / 2) {
56 break;
57 }
58 new_cap *= 2;
59 }
60
61 char *result;
62 if (str->borrows_buffer) {
63 result = (char *)cwist_alloc(new_cap + 1);
64 if (result) {
65 if (str->data && preserve_len > 0) memcpy(result, str->data, preserve_len);
66 str->base = result;
67 str->capacity = new_cap;
68 }
69 return result;
70 }
71
72 char *orig = str->base ? str->base : str->data;
73 const size_t lead = (str->base && str->data) ? (size_t)(str->data - str->base) : 0;
74 char *grown = (char *)cwist_realloc(orig, lead + new_cap + 1);
75 if (!grown) return NULL;
76 str->base = grown;
77 str->capacity = new_cap;
78 return grown + lead;
79}
80
88cwist_error_t cwist_sstring_assign_len(cwist_sstring *str, const char *data, size_t len) {
89 if (!str) {
91 err.error.err_i8 = ERR_SSTRING_NULL_STRING;
92 return err;
93 }
94
95 char *new_data = cwist_sstring_reserve(str, len, 0);
96 if (!new_data && len > 0) {
98 err.error.err_json = cJSON_CreateObject();
99 cJSON_AddStringToObject(err.error.err_json, "err", "cannot assign string: memory is full");
100 return err;
101 }
102 str->data = new_data;
103 str->borrows_buffer = false;
104 str->size = len;
105 if (data && len > 0) memcpy(str->data, data, len);
106 if (str->data) str->data[len] = '\0';
107
109 err.error.err_i8 = ERR_SSTRING_OKAY;
110 return err;
111}
112
122cwist_error_t cwist_sstring_borrow(cwist_sstring *str, const char *data, size_t len) {
124 if (!str) {
125 err.error.err_i8 = ERR_SSTRING_NULL_STRING;
126 return err;
127 }
128
129 if (str->data && !str->borrows_buffer) {
130 cwist_free(str->base ? str->base : str->data);
131 }
132 str->base = NULL;
133 str->data = (char *)(data ? data : "");
134 str->size = data ? len : 0;
135 str->capacity = 0;
136 str->borrows_buffer = true;
137 str->is_fixed = false;
138
139 err.error.err_i8 = ERR_SSTRING_OKAY;
140 return err;
141}
142
153 if (!str) {
154 err.error.err_i8 = ERR_SSTRING_NULL_STRING;
155 return err;
156 }
157
158 if (str->data && !str->borrows_buffer) {
159 cwist_free(str->base ? str->base : str->data);
160 }
161 str->base = buf;
162 str->data = buf;
163 str->size = buf ? len : 0;
164 str->capacity = 0;
165 str->borrows_buffer = false;
166 if (buf) buf[len] = '\0';
167
168 err.error.err_i8 = ERR_SSTRING_OKAY;
169 return err;
170}
171
184 size_t len) {
186 if (!str) {
187 err.error.err_i8 = ERR_SSTRING_NULL_STRING;
188 return err;
189 }
190
191 if (str->data && !str->borrows_buffer) {
192 cwist_free(str->base ? str->base : str->data);
193 }
194 str->base = base;
195 str->data = base ? base + offset : NULL;
196 str->size = base ? len : 0;
197 str->capacity = 0;
198 str->borrows_buffer = false;
199 if (base) base[offset + len] = '\0';
200
201 err.error.err_i8 = ERR_SSTRING_OKAY;
202 return err;
203}
204
212cwist_error_t cwist_sstring_append_len(cwist_sstring *str, const char *data, size_t len) {
213 if (!str) {
215 err.error.err_i8 = ERR_SSTRING_NULL_STRING;
216 return err;
217 }
218 if (!data || len == 0) {
220 err.error.err_i8 = ERR_SSTRING_OKAY;
221 return err;
222 }
223
224 size_t current_len = str->size;
225 size_t new_size = current_len + len;
226
227 char *new_data = cwist_sstring_reserve(str, new_size, current_len);
228 if (!new_data) {
230 err.error.err_json = cJSON_CreateObject();
231 cJSON_AddStringToObject(err.error.err_json, "err", "Cannot append: memory full");
232 return err;
233 }
234 str->data = new_data;
235 str->borrows_buffer = false;
236 memcpy(str->data + current_len, data, len);
237 str->size = new_size;
238 str->data[new_size] = '\0';
239
241 err.error.err_i8 = ERR_SSTRING_OKAY;
242 return err;
243}
244
252 if (!str) {
253 err.error.err_i8 = ERR_SSTRING_NULL_STRING;
254 return err;
255 }
256
257 str->data = NULL;
258 str->size = 0;
259 str->capacity = 0;
260 str->base = NULL;
261 str->is_fixed = false;
262 str->owns_storage = false;
263 str->borrows_buffer = false;
268
270 return err;
271}
272
280 if (!str) {
281 err.error.err_i8 = ERR_SSTRING_NULL_STRING;
282 return err;
283 }
284
285 str->data = NULL;
286 str->size = 0;
287 str->capacity = 0;
288 str->base = NULL;
289 str->is_fixed = false;
290 str->owns_storage = false;
291 str->borrows_buffer = false;
296
298 return err;
299}
300
307 return str ? str->size : 0;
308}
309
310static inline const char *sstring_as_text(const cwist_sstring *s) {
311 return (s && s->data) ? s->data : "";
312}
313
321 return strcmp(sstring_as_text(left), sstring_as_text(right));
322}
323
331 err.error.err_i8 = ERR_SSTRING_NULL_STRING;
332 if (!str || !str->data) return err;
333
334 size_t len = strlen(str->data);
335 size_t start = 0;
336 while (start < len && isspace((unsigned char)str->data[start])) {
337 start++;
338 }
339
340 if (start > 0) {
341 if (str->borrows_buffer) {
342 char *new_data = cwist_sstring_reserve(str, len - start, 0);
343 if (!new_data) {
344 err.error.err_i8 = ERR_SSTRING_NULL_STRING;
345 return err;
346 }
347 memcpy(new_data, str->data + start, len - start + 1);
348 str->data = new_data;
349 str->borrows_buffer = false;
350 } else {
351 memmove(str->data, str->data + start, len - start + 1);
352 }
353 str->size = len - start;
354 }
355
356 err.error.err_i8 = ERR_SSTRING_OKAY;
357 return err;
358}
359
367 err.error.err_i8 = ERR_SSTRING_NULL_STRING;
368 if (!str || !str->data) return err;
369
370 size_t len = strlen(str->data);
371
372 if (len == 0) {
373 err.error.err_i8 = ERR_SSTRING_OKAY;
374 return err;
375 }
376
377 size_t end = len - 1;
378 while (end < len && isspace((unsigned char)str->data[end])) {
379 end--;
380 }
381
382 if (str->borrows_buffer) {
383 char *new_data = cwist_sstring_reserve(str, end + 1, end + 1);
384 if (!new_data) {
385 err.error.err_i8 = ERR_SSTRING_NULL_STRING;
386 return err;
387 }
388 str->data = new_data;
389 str->borrows_buffer = false;
390 }
391
392 str->data[end + 1] = '\0';
393 str->size = end + 1;
394
395 err.error.err_i8 = ERR_SSTRING_OKAY;
396 return err;
397}
398
406 if (err.error.err_i8 != ERR_SSTRING_OKAY) return err;
407 return cwist_sstring_ltrim(str);
408}
409
419
420 if (!str) {
421 err.error.err_i8 = ERR_SSTRING_NULL_STRING;
422 return err;
423 }
424
425 if (str->is_fixed) {
426 err.error.err_i8 = ERR_SSTRING_CONSTANT;
427 return err;
428 }
429
430 size_t current_len = str->data ? strlen(str->data) : 0;
431
432 if (new_size < current_len && !blow_data) {
434 err.error.err_json = cJSON_CreateObject();
436 err.error.err_json, "err",
437 "New size is smaller than current data length and blow_data is false.");
438 return err;
439 }
440
442 bool was_borrowed = str->borrows_buffer;
443 char *new_data = cwist_sstring_reserve(str, new_size, keep);
444 if (!new_data && new_size > 0) {
445 err.error.err_i8 = ERR_SSTRING_RESIZE_TOO_LARGE;
446 return err;
447 }
448
449 str->data = new_data;
450 str->borrows_buffer = false;
451
452 if (new_size < current_len) {
453 str->size = new_size;
454 str->data[new_size] = '\0';
455 } else {
456 if (current_len == 0 || was_borrowed) {
457 /* Detached buffers carry no NUL past the preserved payload. */
458 str->data[current_len] = '\0';
459 }
460 // str->size remains current_len (existing data preserved)
461 }
462
463 err.error.err_i8 = ERR_SSTRING_OKAY;
464 return err;
465}
466
476static bool sstring_aliases(const cwist_sstring *str, const char *p) {
477 if (!p || !str->data || str->borrows_buffer) return false;
478 uintptr_t lo = (uintptr_t)(str->base ? str->base : str->data);
479 uintptr_t hi = (uintptr_t)str->data + strlen(str->data) + 1;
481 return at >= lo && at < hi;
482}
483
485static cwist_error_t sstring_with_copy(cwist_sstring *str, const char *data,
486 cwist_error_t (*op)(cwist_sstring *, const char *)) {
487 char *copy = cwist_strdup(data);
488 if (!copy) {
490 err.error.err_i8 = ERR_SSTRING_RESIZE_TOO_LARGE;
491 return err;
492 }
493 cwist_error_t err = op(str, copy);
494 cwist_free(copy);
495 return err;
496}
497
505 if (!str) {
507 err.error.err_i8 = ERR_SSTRING_NULL_STRING;
508 return err;
509 }
510 if (sstring_aliases(str, data)) return sstring_with_copy(str, data, cwist_sstring_assign);
511
513 err.error.err_json = cJSON_CreateObject();
514
515 size_t data_len = data ? strlen(data) : 0;
516
517 if (str->is_fixed) {
518 if (data_len > str->size) {
519 cJSON_AddStringToObject(err.error.err_json, "err",
520 "string's assigned size is smaller than given data");
521
523 if (err_resize.error.err_i8 == ERR_SSTRING_OKAY) {
524 return err_resize;
525 } else {
526 return err;
527 }
528 }
529 if (str->data) strcpy(str->data, data ? data : "");
530 } else {
531 char *new_data = cwist_sstring_reserve(str, data_len, 0);
532 if (!new_data) {
533 cJSON_AddStringToObject(err.error.err_json, "err",
534 "cannot assign string: memory is full");
535 return err;
536 }
537 str->data = new_data;
538 str->borrows_buffer = false;
539 str->size = data_len;
540 if (data)
541 strcpy(str->data, data);
542 else
543 str->data[0] = '\0';
544 }
545
546 cJSON_Delete(err.error.err_json);
548 err.error.err_i8 = ERR_SSTRING_OKAY;
549
550 return err;
551}
552
560 if (!str) {
562 err.error.err_i8 = ERR_SSTRING_NULL_STRING;
563 return err;
564 }
565 if (sstring_aliases(str, data)) return sstring_with_copy(str, data, cwist_sstring_append);
566 if (!data) {
567 // Appending nothing is success
569 err.error.err_i8 = ERR_SSTRING_OKAY;
570 return err;
571 }
572
573 size_t current_len = str->data ? strlen(str->data) : 0;
574 size_t append_len = strlen(data);
576
578 err.error.err_json = cJSON_CreateObject();
579
580 if (str->is_fixed) {
581 if (new_size > str->size) {
582 cJSON_AddStringToObject(err.error.err_json, "err",
583 "Cannot append: would exceed fixed size");
584 return err;
585 }
586 } else {
587 char *new_data = cwist_sstring_reserve(str, new_size, current_len);
588 if (!new_data) {
589 cJSON_AddStringToObject(err.error.err_json, "err", "Cannot append: memory full");
590 return err;
591 }
592 str->data = new_data;
593 str->borrows_buffer = false;
594 str->size = new_size;
595 }
596
597 // Append logic
598 if (str->data) {
599 // If it was empty/null before, we need to make sure we don't strcat to garbage
600 if (current_len == 0) str->data[0] = '\0';
601 strcat(str->data, data);
602 }
603
604 cJSON_Delete(err.error.err_json);
606 err.error.err_i8 = ERR_SSTRING_OKAY;
607 return err;
608}
609
617 if (!str) {
619 err.error.err_i8 = ERR_SSTRING_NULL_STRING;
620 return err;
621 }
622 if (sstring_aliases(str, data)) return sstring_with_copy(str, data, cwist_sstring_append_escaped);
623 if (!data) {
624 // Appending nothing is success
626 err.error.err_i8 = ERR_SSTRING_OKAY;
627 return err;
628 }
629
630 size_t current_len = str->data ? strlen(str->data) : 0;
631 size_t input_len = strlen(data);
632 size_t new_size = current_len + (input_len * 6) + 1;
633
634 if (str->is_fixed) {
635 if (new_size > str->size) {
637 err.error.err_json = cJSON_CreateObject();
638 if (err.error.err_json) {
639 cJSON_AddStringToObject(err.error.err_json, "err",
640 "Cannot append: would exceed fixed size");
641 }
642 return err;
643 }
644 } else {
645 char *new_data = cwist_sstring_reserve(str, new_size, current_len);
646 if (!new_data) {
648 err.error.err_json = cJSON_CreateObject();
649 if (err.error.err_json) {
650 cJSON_AddStringToObject(err.error.err_json, "err", "Cannot append: memory full");
651 }
652 return err;
653 }
654 str->data = new_data;
655 str->borrows_buffer = false;
656 str->size = new_size;
657 }
658
659 char *ptr = str->data + current_len;
660 for (size_t i = 0; i < input_len; i++) {
661 switch (data[i]) {
662 case '<':
663 memcpy(ptr, "&lt;", 4);
664 ptr += 4;
665 break;
666 case '>':
667 memcpy(ptr, "&gt;", 4);
668 ptr += 4;
669 break;
670 case '&':
671 memcpy(ptr, "&amp;", 5);
672 ptr += 5;
673 break;
674 case '"':
675 memcpy(ptr, "&quot;", 6);
676 ptr += 6;
677 break;
678 case '\'':
679 memcpy(ptr, "&#39;", 5);
680 ptr += 5;
681 break;
682 default: *ptr++ = data[i]; break;
683 }
684 }
685
686 *ptr = '\0';
687 str->size = (size_t)(ptr - str->data);
688
690 err.error.err_i8 = ERR_SSTRING_OKAY;
691 return err;
692}
693
701 if (!str) {
703 err.error.err_i8 = ERR_SSTRING_NULL_STRING;
704 return err;
705 }
706 if (!from) {
708 err.error.err_i8 = ERR_SSTRING_OKAY;
709 return err;
710 }
711 return cwist_sstring_append(str, from->data);
712}
713
721 if (!str) {
723 err.error.err_i8 = ERR_SSTRING_NULL_STRING;
724 return err;
725 }
726 if (!from) {
728 err.error.err_i8 = ERR_SSTRING_OKAY;
729 return err;
730 }
732}
733
743 if (!str || !str->data || !substr) {
744 err.error.err_i8 = ERR_SSTRING_NULL_STRING;
745 return err;
746 }
747
748 size_t len = strlen(str->data);
749 if (location < 0 || (size_t)location >= len) {
750 err.error.err_i8 = ERR_SSTRING_OUTOFBOUND;
751 return err;
752 }
753
754 strcpy(substr, str->data + location);
755
756 err.error.err_i8 = ERR_SSTRING_OKAY;
757 return err;
758}
759
767
769 if (!origin || !origin->data || !destination) {
770 err.error.err_i8 = ERR_SSTRING_NULL_STRING;
771 return err;
772 }
773
774 strcpy(destination, origin->data);
775
776 err.error.err_i8 = ERR_SSTRING_OKAY;
777 return err;
778}
779
787 if (!origin) {
789 err.error.err_i8 = ERR_SSTRING_NULL_STRING;
790 return err;
791 }
792 if (!from) {
794 }
795 return cwist_sstring_assign(origin, from->data);
796}
797
804 if (!str) return NULL;
805
806 memset(str, 0, sizeof(cwist_sstring));
807 str->is_fixed = false;
808 str->owns_storage = true;
809 str->size = 0;
810 str->data = NULL; // Initially empty
815
816 return str;
817}
818
824 if (!str) return;
825
826 if (str->data) {
827 if (!str->borrows_buffer) {
828 cwist_free(str->base ? str->base : str->data);
829 }
830 str->data = NULL;
831 }
832 str->base = NULL;
833 str->size = 0;
834 str->capacity = 0;
835
836 if (str->owns_storage) {
837 cwist_free(str);
838 } else {
839 str->get_size = NULL;
840 str->compare = NULL;
841 str->copy = NULL;
842 str->append = NULL;
843 }
844}
845
853 return strcmp(sstring_as_text(str), compare_to ? compare_to : "");
854}
855
864 if (!str || !str->data || start < 0 || length < 0) return NULL;
865
866 size_t current_len = strlen(str->data);
867 if ((size_t)start >= current_len) return NULL;
868
869 // Adjust length if it goes beyond end
870 if ((size_t)length > current_len - (size_t)start) {
871 length = (int)(current_len - (size_t)start);
872 }
873
875 if (!sub) return NULL;
876
877 sub->data = (char *)cwist_alloc(length + 1);
878 if (!sub->data) {
880 return NULL;
881 }
882 sub->base = sub->data;
883 sub->capacity = length;
884 sub->size = length;
885
886 memcpy(sub->data, str->data + start, length);
887 sub->data[length] = '\0';
888
889 return sub;
890}
Memory allocation wrappers backed by libttak.
void cwist_free(void *ptr)
Free memory obtained via cwist_alloc/cwist_strdup/etc.
Definition alloc.c:52
void * cwist_realloc(void *ptr, size_t new_size)
Resize an existing allocation (libttak tracked).
Definition alloc.c:27
char * cwist_strdup(const char *src)
Duplicate a string using libttak-backed storage.
Definition alloc.c:32
void * cwist_alloc(size_t size)
Allocate zeroed memory tracked by libttak.
Definition alloc.c:17
Safe string type with helper methods.
@ ERR_SSTRING_NULL_STRING
Definition sstring.h:188
@ ERR_SSTRING_CONSTANT
Definition sstring.h:189
@ ERR_SSTRING_OUTOFBOUND
Definition sstring.h:192
@ ERR_SSTRING_RESIZE_TOO_LARGE
Definition sstring.h:191
@ ERR_SSTRING_OKAY
Definition sstring.h:186
@ CWIST_ERR_INT8
mostly used to check a char
Definition cwist_err.h:15
@ CWIST_ERR_JSON
Definition cwist_err.h:37
cwist_error_t make_error(cwist_errtype_t type)
Creates an error object initialized with the specified type.
Definition error.c:14
size_t cap
Definition otel.c:56
int cwist_sstring_compare(cwist_sstring *str, const char *compare_to)
Compare a CWIST string against a raw C string using strcmp semantics.
Definition sstring.c:852
cwist_error_t cwist_sstring_append_len(cwist_sstring *str, const char *data, size_t len)
Append an arbitrary byte range to the end of the string.
Definition sstring.c:212
cwist_error_t cwist_sstring_adopt_region(cwist_sstring *str, char *base, size_t offset, size_t len)
Adopt a heap buffer as a region view without copying.
Definition sstring.c:183
size_t cwist_sstring_get_size(cwist_sstring *str)
Forward declaration for the method-table size callback.
Definition sstring.c:306
cwist_sstring * cwist_sstring_create(void)
Allocate and initialize a new heap-owned CWIST string object.
Definition sstring.c:802
cwist_error_t cwist_sstring_init(cwist_sstring *str)
Initialize a mutable string with the default append/copy behavior.
Definition sstring.c:250
cwist_sstring * cwist_sstring_substr(cwist_sstring *str, int start, int length)
Create a heap-owned substring spanning the requested range.
Definition sstring.c:863
cwist_error_t cwist_sstring_append(cwist_sstring *str, const char *data)
Append a NUL-terminated C string to the target string.
Definition sstring.c:559
cwist_error_t cwist_sstring_assign_len(cwist_sstring *str, const char *data, size_t len)
Replace the string contents with an arbitrary byte range.
Definition sstring.c:88
cwist_error_t cwist_sstring_assign(cwist_sstring *str, const char *data)
Replace the string contents with a NUL-terminated C string.
Definition sstring.c:504
cwist_error_t cwist_sstring_copy(cwist_sstring *origin, char *destination)
Copy the entire string into a caller-provided destination buffer.
Definition sstring.c:766
cwist_error_t cwist_sstring_append_sstring(cwist_sstring *str, const cwist_sstring *from)
Forward declaration for the method-table append callback.
Definition sstring.c:700
void cwist_sstring_destroy(cwist_sstring *str)
Destroy a heap-owned CWIST string and its backing buffer.
Definition sstring.c:823
cwist_error_t cwist_sstring_append_sstring_escaped(cwist_sstring *str, const cwist_sstring *from)
Forward declaration for the escaped-append method-table callback.
Definition sstring.c:720
cwist_error_t cwist_sstring_borrow(cwist_sstring *str, const char *data, size_t len)
Point the string at external storage without copying.
Definition sstring.c:122
cwist_error_t cwist_sstring_change_size(cwist_sstring *str, size_t new_size, bool blow_data)
Resize the string buffer, optionally allowing truncation.
Definition sstring.c:417
cwist_error_t cwist_sstring_rtrim(cwist_sstring *str)
Trim trailing ASCII whitespace from the string in place.
Definition sstring.c:365
cwist_error_t cwist_sstring_trim(cwist_sstring *str)
Trim both leading and trailing ASCII whitespace from the string.
Definition sstring.c:404
cwist_error_t cwist_sstring_ltrim(cwist_sstring *str)
Trim leading ASCII whitespace from the string in place.
Definition sstring.c:329
cwist_error_t cwist_sstring_copy_sstring(cwist_sstring *origin, const cwist_sstring *from)
Forward declaration for the method-table copy callback.
Definition sstring.c:786
cwist_error_t cwist_sstring_seek(cwist_sstring *str, char *substr, int location)
Copy a suffix of the string into a caller-provided buffer.
Definition sstring.c:741
int cwist_sstring_compare_sstring(cwist_sstring *left, const cwist_sstring *right)
Forward declaration for the method-table compare callback.
Definition sstring.c:320
cwist_error_t cwist_sstring_init_escaped(cwist_sstring *str)
Initialize a mutable string whose append helper performs HTML escaping.
Definition sstring.c:278
cwist_error_t cwist_sstring_adopt_len(cwist_sstring *str, char *buf, size_t len)
Adopt a heap buffer as the string contents without copying.
Definition sstring.c:151
cwist_error_t cwist_sstring_append_escaped(cwist_sstring *str, const char *data)
Append a string while escaping a small HTML-sensitive character set.
Definition sstring.c:616
Integrated error structure used throughout the system.
Definition cwist_err.h:72
__prim_cwist_error_t error
Definition cwist_err.h:74
int(* compare)(struct cwist_sstring *left, const struct cwist_sstring *right)
Should mimic strcmp, internally use strncmp.
Definition sstring.h:27
size_t size
Definition sstring.h:20
char * base
Definition sstring.h:24
char * data
Access directly only when raw handling is necessary.
Definition sstring.h:15
size_t capacity
Definition sstring.h:21
cwist_error_t(* append)(struct cwist_sstring *str, const struct cwist_sstring *from)
Append another sstring.
Definition sstring.h:35
cwist_error_t(* copy)(struct cwist_sstring *str, const struct cwist_sstring *from)
Definition sstring.h:30
bool owns_storage
Definition sstring.h:17
size_t(* get_size)(struct cwist_sstring *str)
Definition sstring.h:26
bool is_fixed
Definition sstring.h:16
bool borrows_buffer
data is borrowed (static/arena); never freed, detached on mutation
Definition sstring.h:18
Unified error code and type definitions for the CWIST system layer.