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__
63#define CSTR_LIKELY(x) (x)
64#define CSTR_UNLIKELY(x) (x)
65#define CSTR_INLINE static inline
66#define CSTR_NONNULL(...)
68#define CSTR_WARN_UNUSED
77#define CSTR_MAX_LEN ((uint32_t)0x7FFFFFFFu)
80#define CSTR_SSO_CAP 16u
83#define CSTR_HEAP_FLAG ((uint32_t)0x80000000u)
89#define CSTR_MIN_HEAP 32u
112#if defined(__cplusplus)
113#define STATIC_ASSERT(cond, msg) static_assert(cond, msg)
115#define STATIC_ASSERT(cond, msg) _Static_assert(cond, msg)
120#if defined(__GNUC__) || defined(__clang__)
121STATIC_ASSERT(
sizeof(
cstr) == 32,
"cstr must be exactly 32 bytes");
161 if (CSTR_UNLIKELY(s == NULL))
return 0;
193CSTR_INLINE
char cstr_at(
const cstr* s,
size_t index) CSTR_PURE;
195 if (CSTR_UNLIKELY(!s) || CSTR_UNLIKELY(index >= (
size_t)s->
length))
return '\0';
196 return s->
data[index];
204 cstr_view v = {NULL, 0};
205 if (CSTR_LIKELY(s != NULL)) {
282bool cstr_reserve(
cstr* s,
size_t capacity) CSTR_NONNULL(1) CSTR_WARN_UNUSED;
285CSTR_INLINE
bool cstr_resize(
cstr* s,
size_t capacity) CSTR_NONNULL(1) CSTR_WARN_UNUSED;
302 if (CSTR_LIKELY(s != NULL)) {
334 size_t n = strlen(append);
336 memcpy(dst, append, n + 1);
349PRINTF_FORMAT(1, 2)
cstr* cstr_format(const
char* format, ...) CSTR_WARN_UNUSED;
354PRINTF_FORMAT(2, 3)
bool cstr_append_fmt(
cstr* s, const
char* format, ...) CSTR_NONNULL(1, 2);
391size_t cstr_remove_all_cstr(
cstr* s, const
cstr* substr) CSTR_NONNULL(1, 2);
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);
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);
440int cstr_ncmp(const
cstr* s1, const
cstr* s2,
size_t n) CSTR_PURE;
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;
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;
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;
467int cstr_rfind_cstr(const
cstr* s, const
cstr* substr) CSTR_NONNULL(1, 2) CSTR_PURE;
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; }
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; }
477size_t cstr_count_substr_cstr(const
cstr* s, const
cstr* substr) CSTR_NONNULL(1, 2) CSTR_PURE;
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;
496cstr**
cstr_split(const
cstr* s, const
char* delim,
size_t* count_out) CSTR_NONNULL(1, 3) CSTR_WARN_UNUSED;
501cstr*
cstr_join(const
cstr** strings,
size_t count, const
char* delim) CSTR_WARN_UNUSED;
507cstr* cstr_reverse(const
cstr* s) CSTR_NONNULL(1) CSTR_WARN_UNUSED;
508void cstr_reverse_inplace(
cstr* s) CSTR_NONNULL(1);
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
CSTR_INLINE uint32_t cstr_heap_cap(const cstr *s) CSTR_PURE
size_t size_t void cstr_remove_char(cstr *s, char c) CSTR_NONNULL(1)
Remove a specific character from every position.
cstr * cstr_new(const char *input) CSTR_WARN_UNUSED
Create a new cstr from a C string.
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).
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).
bool cstr_prepend(cstr *s, const char *prepend) CSTR_NONNULL(1
Prepend a NUL-terminated C string.
CSTR_INLINE bool cstr_resize(cstr *s, size_t capacity) CSTR_NONNULL(1) CSTR_WARN_UNUSED
bool bool bool CSTR_INLINE bool cstr_cat(cstr *dest, const cstr *src) CSTR_NONNULL(1
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.
CSTR_INLINE void cstr_init_inplace(cstr *s)
Initialize an already-allocated cstr in SSO mode (no heap).
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).
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.
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).
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 ...
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.
void int cstr_cmp(const cstr *s1, const cstr *s2) CSTR_PURE
Lexicographic compare. NULL < non-NULL; two NULLs are equal.
CSTR_INLINE bool cstr_empty(const cstr *s) CSTR_PURE
CSTR_INLINE char cstr_at(const cstr *s, size_t index) CSTR_PURE
Character at index, or NUL if out of range.
CSTR_INLINE cstr_view cstr_as_view(const cstr *s)
Construct a cstr_view from a cstr (zero copy).
CSTR_INLINE bool cstr_equals(const cstr *s1, const cstr *s2) CSTR_PURE
Equality check (length-first — very fast for non-equal strings).
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 ...
void cstr_free(cstr *s)
Free a heap-allocated cstr and its storage. Safe to call with NULL.
CSTR_INLINE bool cstr_allocated(const cstr *s) CSTR_PURE
CSTR_INLINE const char * cstr_data_const(const cstr *s) CSTR_PURE
Const pointer to the NUL-terminated string data.
bool cstr_append_char(cstr *s, char c) CSTR_NONNULL(1)
Append a single character.
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.
void cstr_drop(cstr *s)
Release only the internal heap buffer of an embedded cstr (one created via cstr_init_inplace)....
cstr * cstr_join(const cstr **strings, size_t count, const char *delim) CSTR_WARN_UNUSED
Join an array of cstr pointers with a delimiter.
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.
bool CSTR_INLINE bool cstr_assign(cstr *dest, const cstr *src) CSTR_NONNULL(1
CSTR_INLINE char * cstr_data(cstr *s) CSTR_PURE
Mutable pointer to the NUL-terminated string data. Never NULL for a valid cstr.
Collection of utility macros for assertions, memory management, math, and debugging.
A dynamically resizable C string with SSO.