solidc
Robust collection of general-purpose cross-platform C libraries and data structures designed for rapid and safe development in C
Loading...
Searching...
No Matches
cstr.h
Go to the documentation of this file.
1
37#ifndef CSTR_H
38#define CSTR_H
39
40#include <stdbool.h>
41#include <stddef.h>
42#include <stdint.h>
43#include <string.h>
44#include "macros.h"
45
46#ifdef __cplusplus
47extern "C" {
48#endif
49
50/* -------------------------------------------------------------------------
51 * Platform / compiler helpers
52 * ---------------------------------------------------------------------- */
53
54#if defined(__GNUC__) || defined(__clang__)
55#define CSTR_LIKELY(x) __builtin_expect(!!(x), 1)
56#define CSTR_UNLIKELY(x) __builtin_expect(!!(x), 0)
57#define CSTR_INLINE static inline __attribute__((always_inline))
58#define CSTR_NONNULL(...) __attribute__((nonnull(__VA_ARGS__)))
59#define CSTR_PURE __attribute__((pure))
60#define CSTR_WARN_UNUSED __attribute__((warn_unused_result))
61#define CSTR_RESTRICT __restrict__
62#else
63#define CSTR_LIKELY(x) (x)
64#define CSTR_UNLIKELY(x) (x)
65#define CSTR_INLINE static inline
66#define CSTR_NONNULL(...)
67#define CSTR_PURE
68#define CSTR_WARN_UNUSED
69#define CSTR_RESTRICT
70#endif
71
72/* -------------------------------------------------------------------------
73 * Constants
74 * ---------------------------------------------------------------------- */
75
77#define CSTR_MAX_LEN ((uint32_t)0x7FFFFFFFu)
78
80#define CSTR_SSO_CAP 16u /* 15 usable chars + NUL */
81
83#define CSTR_HEAP_FLAG ((uint32_t)0x80000000u)
84
86#define CSTR_NPOS (-1)
87
88/* Minimum heap allocation to avoid tiny reallocs */
89#define CSTR_MIN_HEAP 32u
90
91/* -------------------------------------------------------------------------
92 * Core struct (32 bytes on 64-bit systems)
93 * ---------------------------------------------------------------------- */
94
104typedef struct cstr {
105 char* data;
106 uint32_t length;
107 uint32_t capacity;
110
111#ifndef STATIC_ASSERT
112#if defined(__cplusplus)
113#define STATIC_ASSERT(cond, msg) static_assert(cond, msg)
114#else
115#define STATIC_ASSERT(cond, msg) _Static_assert(cond, msg)
116#endif
117#endif
118
119/* Compile-time layout assertions */
120#if defined(__GNUC__) || defined(__clang__)
121STATIC_ASSERT(sizeof(cstr) == 32, "cstr must be exactly 32 bytes");
122#endif
123
124/* -------------------------------------------------------------------------
125 * Lightweight string view
126 * ---------------------------------------------------------------------- */
127
128typedef struct {
129 const char* data;
130 uint32_t length;
131} cstr_view;
132
133/* -------------------------------------------------------------------------
134 * Internal predicates (inlined, zero overhead)
135 * ---------------------------------------------------------------------- */
136
138CSTR_INLINE bool cstr_is_heap(const cstr* s) CSTR_PURE;
139CSTR_INLINE bool cstr_is_heap(const cstr* s) { return (s->capacity & CSTR_HEAP_FLAG) != 0; }
140
142CSTR_INLINE uint32_t cstr_heap_cap(const cstr* s) CSTR_PURE;
143CSTR_INLINE uint32_t cstr_heap_cap(const cstr* s) { return s->capacity & ~CSTR_HEAP_FLAG; }
144
145/* -------------------------------------------------------------------------
146 * Public API — information
147 * ---------------------------------------------------------------------- */
148
152CSTR_INLINE size_t cstr_len(const cstr* s) CSTR_PURE;
153CSTR_INLINE size_t cstr_len(const cstr* s) { return CSTR_LIKELY(s != NULL) ? (size_t)s->length : 0; }
154
159CSTR_INLINE size_t cstr_capacity(const cstr* s) CSTR_PURE;
160CSTR_INLINE size_t cstr_capacity(const cstr* s) {
161 if (CSTR_UNLIKELY(s == NULL)) return 0;
162 return cstr_is_heap(s) ? (size_t)cstr_heap_cap(s) : (size_t)(CSTR_SSO_CAP - 1);
163}
164
166CSTR_INLINE bool cstr_empty(const cstr* s) CSTR_PURE;
167CSTR_INLINE bool cstr_empty(const cstr* s) { return CSTR_UNLIKELY(s == NULL) || s->length == 0; }
168
170CSTR_INLINE bool cstr_allocated(const cstr* s) CSTR_PURE;
171CSTR_INLINE bool cstr_allocated(const cstr* s) { return CSTR_LIKELY(s != NULL) && cstr_is_heap(s); }
172
173/* -------------------------------------------------------------------------
174 * Public API — data access
175 * ---------------------------------------------------------------------- */
176
181CSTR_INLINE char* cstr_data(cstr* s) CSTR_PURE;
182CSTR_INLINE char* cstr_data(cstr* s) { return CSTR_LIKELY(s != NULL) ? s->data : NULL; }
183
187CSTR_INLINE const char* cstr_data_const(const cstr* s) CSTR_PURE;
188CSTR_INLINE const char* cstr_data_const(const cstr* s) { return CSTR_LIKELY(s != NULL) ? s->data : NULL; }
189
193CSTR_INLINE char cstr_at(const cstr* s, size_t index) CSTR_PURE;
194CSTR_INLINE char cstr_at(const cstr* s, size_t index) {
195 if (CSTR_UNLIKELY(!s) || CSTR_UNLIKELY(index >= (size_t)s->length)) return '\0';
196 return s->data[index];
197}
198
202CSTR_INLINE cstr_view cstr_as_view(const cstr* s);
203CSTR_INLINE cstr_view cstr_as_view(const cstr* s) {
204 cstr_view v = {NULL, 0};
205 if (CSTR_LIKELY(s != NULL)) {
206 v.data = s->data;
207 v.length = s->length;
208 }
209 return v;
210}
211
212/* -------------------------------------------------------------------------
213 * Lifecycle
214 * ---------------------------------------------------------------------- */
215
224CSTR_INLINE void cstr_init_inplace(cstr* s);
225CSTR_INLINE void cstr_init_inplace(cstr* s) {
226 s->buf[0] = '\0';
227 s->data = s->buf;
228 s->length = 0;
229 s->capacity = 0; /* SSO, no heap flag */
230}
231
238cstr* cstr_init(size_t initial_capacity) CSTR_WARN_UNUSED;
239
246cstr* cstr_new(const char* input) CSTR_WARN_UNUSED;
247
254cstr* cstr_new_len(const char* data, size_t length) CSTR_WARN_UNUSED;
255
260void cstr_free(cstr* s);
261
267void cstr_drop(cstr* s);
268
272void cstr_debug(const cstr* s);
273
274/* -------------------------------------------------------------------------
275 * Capacity management
276 * ---------------------------------------------------------------------- */
277
282bool cstr_reserve(cstr* s, size_t capacity) CSTR_NONNULL(1) CSTR_WARN_UNUSED;
283
285CSTR_INLINE bool cstr_resize(cstr* s, size_t capacity) CSTR_NONNULL(1) CSTR_WARN_UNUSED;
286CSTR_INLINE bool cstr_resize(cstr* s, size_t capacity) { return cstr_reserve(s, capacity); }
287
291void cstr_shrink_to_fit(cstr* s) CSTR_NONNULL(1);
292
293/* -------------------------------------------------------------------------
294 * Mutation
295 * ---------------------------------------------------------------------- */
296
300CSTR_INLINE void cstr_clear(cstr* s);
301CSTR_INLINE void cstr_clear(cstr* s) {
302 if (CSTR_LIKELY(s != NULL)) {
303 s->length = 0;
304 s->data[0] = '\0';
305 }
306}
307
311bool cstr_append(cstr* s, const char* CSTR_RESTRICT append) CSTR_NONNULL(1, 2);
312
316bool cstr_append_cstr(cstr* s, const cstr* append) CSTR_NONNULL(1, 2);
317
321bool cstr_ncat(cstr* dest, const cstr* src, size_t n) CSTR_NONNULL(1, 2);
322
324CSTR_INLINE bool cstr_cat(cstr* dest, const cstr* src) CSTR_NONNULL(1, 2);
325CSTR_INLINE bool cstr_cat(cstr* dest, const cstr* src) { return cstr_append_cstr(dest, src); }
326
332CSTR_INLINE bool cstr_append_fast(cstr* s, const char* CSTR_RESTRICT append) CSTR_NONNULL(1, 2);
333CSTR_INLINE bool cstr_append_fast(cstr* s, const char* CSTR_RESTRICT append) {
334 size_t n = strlen(append);
335 char* dst = s->data + s->length;
336 memcpy(dst, append, n + 1); /* includes NUL */
337 s->length += (uint32_t)n;
338 return true;
339}
340
344bool cstr_append_char(cstr* s, char c) CSTR_NONNULL(1);
345
349PRINTF_FORMAT(1, 2) cstr* cstr_format(const char* format, ...) CSTR_WARN_UNUSED;
350
354PRINTF_FORMAT(2, 3) bool cstr_append_fmt(cstr* s, const char* format, ...) CSTR_NONNULL(1, 2);
355
359bool cstr_prepend(cstr* s, const char* prepend) CSTR_NONNULL(1, 2);
360
364bool cstr_prepend_cstr(cstr* s, const cstr* prepend) CSTR_NONNULL(1, 2);
365
369bool cstr_prepend_fast(cstr* s, const char* prepend) CSTR_NONNULL(1, 2);
370
374bool cstr_insert(cstr* s, size_t index, const char* insert) CSTR_NONNULL(1, 3);
375
379bool cstr_insert_cstr(cstr* s, size_t index, const cstr* insert) CSTR_NONNULL(1, 3);
380
384bool cstr_remove(cstr* s, size_t index, size_t count) CSTR_NONNULL(1);
385
390size_t cstr_remove_all(cstr* s, const char* substr) CSTR_NONNULL(1, 2);
391size_t cstr_remove_all_cstr(cstr* s, const cstr* substr) CSTR_NONNULL(1, 2);
392
396void cstr_remove_char(cstr* s, char c) CSTR_NONNULL(1);
397
401void cstr_remove_substr(cstr* str, size_t start, size_t substr_length) CSTR_NONNULL(1);
402
406bool cstr_copy(cstr* dest, const cstr* src) CSTR_NONNULL(1, 2);
407
409CSTR_INLINE bool cstr_assign(cstr* dest, const cstr* src) CSTR_NONNULL(1, 2);
410CSTR_INLINE bool cstr_assign(cstr* dest, const cstr* src) { return cstr_copy(dest, src); }
411
412/* -------------------------------------------------------------------------
413 * Case conversion & formatting
414 * ---------------------------------------------------------------------- */
415
416void cstr_lower(cstr* s) CSTR_NONNULL(1);
417void cstr_upper(cstr* s) CSTR_NONNULL(1);
418bool cstr_snakecase(cstr* s) CSTR_NONNULL(1) CSTR_WARN_UNUSED;
419void cstr_camelcase(cstr* s) CSTR_NONNULL(1);
420void cstr_pascalcase(cstr* s) CSTR_NONNULL(1);
421void cstr_titlecase(cstr* s) CSTR_NONNULL(1);
422
423/* -------------------------------------------------------------------------
424 * Trim
425 * ---------------------------------------------------------------------- */
426
427void cstr_trim(cstr* s) CSTR_NONNULL(1);
428void cstr_rtrim(cstr* s) CSTR_NONNULL(1);
429void cstr_ltrim(cstr* s) CSTR_NONNULL(1);
430void cstr_trim_chars(cstr* s, const char* chars) CSTR_NONNULL(1, 2);
431
432/* -------------------------------------------------------------------------
433 * Comparison & search
434 * ---------------------------------------------------------------------- */
435
439int cstr_cmp(const cstr* s1, const cstr* s2) CSTR_PURE;
440int cstr_ncmp(const cstr* s1, const cstr* s2, size_t n) CSTR_PURE;
441
445CSTR_INLINE bool cstr_equals(const cstr* s1, const cstr* s2) CSTR_PURE;
446CSTR_INLINE bool cstr_equals(const cstr* s1, const cstr* s2) {
447 if (s1 == s2) return true;
448 if (!s1 || !s2) return false;
449 if (s1->length != s2->length) return false;
450 return memcmp(s1->data, s2->data, s1->length) == 0;
451}
452
453bool cstr_starts_with(const cstr* s, const char* prefix) CSTR_NONNULL(1, 2) CSTR_PURE;
454bool cstr_starts_with_cstr(const cstr* s, const cstr* prefix) CSTR_NONNULL(1, 2) CSTR_PURE;
455bool cstr_ends_with(const cstr* s, const char* suffix) CSTR_NONNULL(1, 2) CSTR_PURE;
456bool cstr_ends_with_cstr(const cstr* s, const cstr* suffix) CSTR_NONNULL(1, 2) CSTR_PURE;
457
462int cstr_find(const cstr* s, const char* substr) CSTR_NONNULL(1, 2) CSTR_PURE;
463int cstr_find_cstr(const cstr* s, const cstr* substr) CSTR_NONNULL(1, 2) CSTR_PURE;
464
466int cstr_rfind(const cstr* s, const char* substr) CSTR_NONNULL(1, 2) CSTR_PURE;
467int cstr_rfind_cstr(const cstr* s, const cstr* substr) CSTR_NONNULL(1, 2) CSTR_PURE;
468
469CSTR_INLINE bool cstr_contains(const cstr* s, const char* substr) CSTR_NONNULL(1, 2) CSTR_PURE;
470CSTR_INLINE bool cstr_contains(const cstr* s, const char* substr) { return cstr_find(s, substr) != CSTR_NPOS; }
471
472CSTR_INLINE bool cstr_contains_cstr(const cstr* s, const cstr* sub) CSTR_NONNULL(1, 2) CSTR_PURE;
473CSTR_INLINE bool cstr_contains_cstr(const cstr* s, const cstr* sub) { return cstr_find_cstr(s, sub) != CSTR_NPOS; }
474
476size_t cstr_count_substr(const cstr* s, const char* substr) CSTR_NONNULL(1, 2) CSTR_PURE;
477size_t cstr_count_substr_cstr(const cstr* s, const cstr* substr) CSTR_NONNULL(1, 2) CSTR_PURE;
478
479/* -------------------------------------------------------------------------
480 * Substrings & replacement (all return new cstr — caller must free)
481 * ---------------------------------------------------------------------- */
482
483cstr* cstr_substr(const cstr* s, size_t start, size_t length) CSTR_NONNULL(1) CSTR_WARN_UNUSED;
484cstr* cstr_replace(const cstr* s, const char* old_str, const char* new_str) CSTR_NONNULL(1, 2, 3) CSTR_WARN_UNUSED;
485cstr* cstr_replace_all(const cstr* s, const char* old_str, const char* new_str) CSTR_NONNULL(1, 2, 3) CSTR_WARN_UNUSED;
486
487/* -------------------------------------------------------------------------
488 * Split & join
489 * ---------------------------------------------------------------------- */
490
496cstr** cstr_split(const cstr* s, const char* delim, size_t* count_out) CSTR_NONNULL(1, 3) CSTR_WARN_UNUSED;
497
501cstr* cstr_join(const cstr** strings, size_t count, const char* delim) CSTR_WARN_UNUSED;
502
503/* -------------------------------------------------------------------------
504 * Reverse
505 * ---------------------------------------------------------------------- */
506
507cstr* cstr_reverse(const cstr* s) CSTR_NONNULL(1) CSTR_WARN_UNUSED;
508void cstr_reverse_inplace(cstr* s) CSTR_NONNULL(1);
509
510#ifdef __cplusplus
511}
512#endif
513
514#endif /* CSTR_H */
bool bool cstr_append_cstr(cstr *s, const cstr *append) CSTR_NONNULL(1
Append another cstr.
CSTR_INLINE bool cstr_is_heap(const cstr *s) CSTR_PURE
Definition cstr.h:139
CSTR_INLINE uint32_t cstr_heap_cap(const cstr *s) CSTR_PURE
Definition cstr.h:143
#define CSTR_NPOS
Definition cstr.h:86
size_t size_t void cstr_remove_char(cstr *s, char c) CSTR_NONNULL(1)
Remove a specific character from every position.
Definition cstr.c:473
cstr * cstr_new(const char *input) CSTR_WARN_UNUSED
Create a new cstr from a C string.
Definition cstr.c:146
cstr * cstr_new_len(const char *data, size_t length) CSTR_WARN_UNUSED
Create a new cstr from a buffer of known length (no strlen needed).
Definition cstr.c:151
size_t cstr_count_substr(const cstr *s, const char *substr) CSTR_NONNULL(1
size_t cstr_remove_all(cstr *s, const char *substr) CSTR_NONNULL(1
Remove all occurrences of substr (in-place, single pass).
CSTR_INLINE void cstr_clear(cstr *s)
Set length to 0 (data pointer and capacity unchanged).
Definition cstr.h:301
bool cstr_prepend(cstr *s, const char *prepend) CSTR_NONNULL(1
Prepend a NUL-terminated C string.
#define CSTR_SSO_CAP
Definition cstr.h:80
CSTR_INLINE bool cstr_resize(cstr *s, size_t capacity) CSTR_NONNULL(1) CSTR_WARN_UNUSED
Definition cstr.h:286
bool bool bool CSTR_INLINE bool cstr_cat(cstr *dest, const cstr *src) CSTR_NONNULL(1
Definition cstr.h:325
bool bool bool bool cstr_insert(cstr *s, size_t index, const char *insert) CSTR_NONNULL(1
Insert a C string at byte offset index.
bool cstr_append(cstr *s, const char *CSTR_RESTRICT append) CSTR_NONNULL(1
Append a NUL-terminated C string.
bool bool bool bool bool cstr_insert_cstr(cstr *s, size_t index, const cstr *insert) CSTR_NONNULL(1
Insert a cstr at byte offset index.
int cstr_rfind(const cstr *s, const char *substr) CSTR_NONNULL(1
void cstr_debug(const cstr *s)
Print debug information about a cstr to stderr.
Definition cstr.c:188
CSTR_INLINE void cstr_init_inplace(cstr *s)
Initialize an already-allocated cstr in SSO mode (no heap).
Definition cstr.h:225
bool bool bool cstr_prepend_fast(cstr *s, const char *prepend) CSTR_NONNULL(1
Prepend without capacity check — caller guarantees space.
void cstr_shrink_to_fit(cstr *s) CSTR_NONNULL(1)
Shrink heap allocation to fit the current length (frees wasted memory).
Definition cstr.c:207
bool bool bool bool bool bool cstr_remove(cstr *s, size_t index, size_t count) CSTR_NONNULL(1)
Remove count characters starting at index.
Definition cstr.c:344
bool bool cstr_prepend_cstr(cstr *s, const cstr *prepend) CSTR_NONNULL(1
Prepend another cstr.
bool cstr_reserve(cstr *s, size_t capacity) CSTR_NONNULL(1) CSTR_WARN_UNUSED
Ensure at least capacity usable bytes are available (NUL extra).
Definition cstr.c:205
cstr ** cstr_split(const cstr *s, const char *delim, size_t *count_out) CSTR_NONNULL(1
Split on delimiter. Returns array of cstr* (each must be freed), terminated by setting *count_out....
CSTR_INLINE bool cstr_append_fast(cstr *s, const char *CSTR_RESTRICT append) CSTR_NONNULL(1
Append without capacity check — caller guarantees space. ~20% faster for bulk building when capacity ...
Definition cstr.h:333
int cstr_find(const cstr *s, const char *substr) CSTR_NONNULL(1
Find first occurrence of substr. Uses optimised search (no memmem).
CSTR_INLINE size_t cstr_len(const cstr *s) CSTR_PURE
Length of the string, excluding NUL.
Definition cstr.h:153
void int cstr_cmp(const cstr *s1, const cstr *s2) CSTR_PURE
Lexicographic compare. NULL < non-NULL; two NULLs are equal.
Definition cstr.c:620
CSTR_INLINE bool cstr_empty(const cstr *s) CSTR_PURE
Definition cstr.h:167
CSTR_INLINE char cstr_at(const cstr *s, size_t index) CSTR_PURE
Character at index, or NUL if out of range.
Definition cstr.h:194
CSTR_INLINE cstr_view cstr_as_view(const cstr *s)
Construct a cstr_view from a cstr (zero copy).
Definition cstr.h:203
CSTR_INLINE bool cstr_equals(const cstr *s1, const cstr *s2) CSTR_PURE
Equality check (length-first — very fast for non-equal strings).
Definition cstr.h:446
CSTR_INLINE size_t cstr_capacity(const cstr *s) CSTR_PURE
Current storage capacity (bytes available before reallocation). For SSO strings this is CSTR_SSO_CAP ...
Definition cstr.h:160
void cstr_free(cstr *s)
Free a heap-allocated cstr and its storage. Safe to call with NULL.
Definition cstr.c:179
CSTR_INLINE bool cstr_allocated(const cstr *s) CSTR_PURE
Definition cstr.h:171
CSTR_INLINE const char * cstr_data_const(const cstr *s) CSTR_PURE
Const pointer to the NUL-terminated string data.
Definition cstr.h:188
bool cstr_append_char(cstr *s, char c) CSTR_NONNULL(1)
Append a single character.
Definition cstr.c:264
bool bool bool cstr_ncat(cstr *dest, const cstr *src, size_t n) CSTR_NONNULL(1
Append at most n chars from src.
cstr * cstr_init(size_t initial_capacity) CSTR_WARN_UNUSED
Create a new heap-allocated cstr with a given initial capacity.
Definition cstr.c:127
void cstr_drop(cstr *s)
Release only the internal heap buffer of an embedded cstr (one created via cstr_init_inplace)....
Definition cstr.c:170
cstr * cstr_join(const cstr **strings, size_t count, const char *delim) CSTR_WARN_UNUSED
Join an array of cstr pointers with a delimiter.
Definition cstr.c:1089
#define CSTR_HEAP_FLAG
Definition cstr.h:83
bool cstr_copy(cstr *dest, const cstr *src) CSTR_NONNULL(1
Deep copy src into dest (dest is overwritten).
void cstr_remove_substr(cstr *str, size_t start, size_t substr_length) CSTR_NONNULL(1)
Remove a run of substr_length bytes starting at start.
Definition cstr.c:485
bool CSTR_INLINE bool cstr_assign(cstr *dest, const cstr *src) CSTR_NONNULL(1
Definition cstr.h:410
CSTR_INLINE char * cstr_data(cstr *s) CSTR_PURE
Mutable pointer to the NUL-terminated string data. Never NULL for a valid cstr.
Definition cstr.h:182
Collection of utility macros for assertions, memory management, math, and debugging.
A dynamically resizable C string with SSO.
Definition cstr.h:104
uint32_t length
Definition cstr.h:106
char * data
Definition cstr.h:105
uint32_t capacity
Definition cstr.h:107
char buf[CSTR_SSO_CAP]
Definition cstr.h:108