🧵 str
Painfully common string utilities for C
Loading...
Searching...
No Matches
str.h
Go to the documentation of this file.
1/*
2 * 🧵 str — str.h
3 * Copyright (c) 2025–2026 Fawn <rubiefawn@gmail.com>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
29#pragma once
30#include <stddef.h>
31#if __STDC_VERSION__ < 202311L
32 #include <stdbool.h>
33#endif
34
42typedef struct str {
43 char *data;
44 size_t len;
46
48#define str_literal(SZ_LITERAL) (str){ (SZ_LITERAL), sizeof(SZ_LITERAL) - 1 }
49
53
56str str_slice_cstr(char *sz, size_t len);
57
61
65
69
73
76str str_strip_prefix_cstr(str it, const char *prefix);
77
80str str_strip_prefix_if(str it, bool (*predicate)(char));
81
85#define str_strip_prefix(IT, PREFIX) _Generic((PREFIX),\
86 const str : str_strip_prefix_str,\
87 str : str_strip_prefix_str,\
88 const char* : str_strip_prefix_cstr,\
89 char* : str_strip_prefix_cstr,\
90 bool (*)(char c) : str_strip_prefix_if\
91)((IT), (PREFIX))
92
96
99str str_strip_postfix_cstr(str it, const char *postfix);
100
103str str_strip_postfix_if(str it, bool (*predicate)(char));
104
108#define str_strip_postfix(IT, POSTFIX) _Generic((POSTFIX),\
109 const str : str_strip_postfix_str,\
110 str : str_strip_postfix_str,\
111 const char* : str_strip_postfix_cstr,\
112 char* : str_strip_postfix_cstr,\
113 bool (*)(char c) : str_strip_postfix_if\
114)((IT), (POSTFIX))
115
119
121int str_cmp_cstr(str a, const char *b);
122
124int cstr_cmp_str(const char *a, str b);
125
128#define cstr_cmp_cstr strcmp
129
132#define str_cmp(LHS, RHS) _Generic((LHS),\
133 const str: _Generic((RHS),\
134 const str : str_cmp_str,\
135 str : str_cmp_str,\
136 const char*: str_cmp_cstr,\
137 char*: str_cmp_cstr\
138 ),\
139 str: _Generic((RHS),\
140 const str : str_cmp_str,\
141 str : str_cmp_str,\
142 const char*: str_cmp_cstr,\
143 char*: str_cmp_cstr\
144 ),\
145 const char*: _Generic((RHS),\
146 const str : cstr_cmp_str,\
147 str : cstr_cmp_str,\
148 const char*: strcmp,\
149 char*: strcmp\
150 ),\
151 char*: _Generic((RHS),\
152 const str : cstr_cmp_str,\
153 str : cstr_cmp_str,\
154 const char*: strcmp,\
155 char*: strcmp\
156 )\
157)((LHS), (RHS))
158
160bool str_eq_str(str a, str b);
161
163bool str_eq_cstr(str a, const char *b);
164
166bool cstr_eq_str(const char *a, str b);
167
169bool cstr_eq_cstr(const char *a, const char *b);
170
172#define str_eq(LHS, RHS) _Generic((LHS),\
173 const str: _Generic((RHS),\
174 const str : str_eq_str,\
175 str : str_eq_str,\
176 const char*: str_eq_cstr,\
177 char*: str_eq_cstr\
178 ),\
179 str: _Generic((RHS),\
180 const str : str_eq_str,\
181 str : str_eq_str,\
182 const char*: str_eq_cstr,\
183 char*: str_eq_cstr\
184 ),\
185 const char*: _Generic((RHS),\
186 const str : cstr_eq_str,\
187 str : cstr_eq_str,\
188 const char*: cstr_eq_cstr,\
189 char*: cstr_eq_cstr\
190 ),\
191 char*: _Generic((RHS),\
192 const str : cstr_eq_str,\
193 str : cstr_eq_str,\
194 const char*: cstr_eq_cstr,\
195 char*: cstr_eq_cstr\
196 )\
197)((LHS), (RHS))
198
207size_t str_eq_str_any(str it, size_t count, ...);
208
217size_t str_eq_strs_any(str it, size_t count, const str others[static count]);
218
220size_t str_eq_cstr_any(str it, size_t count, ...);
221
223size_t str_eq_cstrs_any(str it, size_t count, const char *others[static count]);
224
226size_t cstr_eq_str_any(const char *it, size_t count, ...);
227
229size_t cstr_eq_strs_any(const char *it, size_t count, const str others[static count]);
230
232size_t cstr_eq_cstr_any(const char *it, size_t count, ...);
233
235size_t cstr_eq_cstrs_any(const char *it, size_t count, const char *others[static count]);
236
247#define str_eq_any(LHS, COUNT, RHS, ...) _Generic((LHS),\
248 const str: _Generic((RHS),\
249 const str : str_eq_str_any,\
250 str : str_eq_str_any,\
251 const str* : str_eq_strs_any,\
252 str* : str_eq_strs_any,\
253 const char* : str_eq_cstr_any,\
254 char* : str_eq_cstr_any,\
255 const char**: str_eq_cstrs_any,\
256 char**: str_eq_cstrs_any\
257 ),\
258 str: _Generic((RHS),\
259 const str : str_eq_str_any,\
260 str : str_eq_str_any,\
261 const str* : str_eq_strs_any,\
262 str* : str_eq_strs_any,\
263 const char* : str_eq_cstr_any,\
264 char* : str_eq_cstr_any,\
265 const char**: str_eq_cstrs_any,\
266 char**: str_eq_cstrs_any\
267 ),\
268 const char*: _Generic((RHS),\
269 const str : cstr_eq_str_any,\
270 str : cstr_eq_str_any,\
271 const str* : cstr_eq_strs_any,\
272 str* : cstr_eq_strs_any,\
273 const char* : cstr_eq_cstr_any,\
274 char* : cstr_eq_cstr_any,\
275 const char**: cstr_eq_cstrs_any,\
276 char**: cstr_eq_cstrs_any\
277 ),\
278 char*: _Generic((RHS),\
279 const str : cstr_eq_str_any,\
280 str : cstr_eq_str_any,\
281 const str* : cstr_eq_strs_any,\
282 str* : cstr_eq_strs_any,\
283 const char* : cstr_eq_cstr_any,\
284 char* : cstr_eq_cstr_any,\
285 const char**: cstr_eq_cstrs_any,\
286 char**: cstr_eq_cstrs_any\
287 )\
288)((LHS), (COUNT), (RHS) __VA_OPT__(, __VA_ARGS__))
289
298size_t str_satisfies_any(str it, size_t count, ...);
299
308size_t str_satisfies_anys(str it, size_t count, bool (*predicates[static count])(str));
309
318size_t cstr_satisfies_any(const char *it, size_t count, ...);
319
321size_t cstr_satisfies_anys(const char *it, size_t count, bool (*predicates[static count])(const char*));
322
330bool str_satisfies_all(str it, size_t count, ...);
331
339bool str_satisfies_alls(str it, size_t count, bool (*predicates[static count])(str));
340
348bool cstr_satisfies_all(const char *it, size_t count, ...);
349
351bool cstr_satisfies_alls(const char *it, size_t count, bool (*predicates[static count])(const char*));
352
359bool str_chars_satisfies(str it, bool (*predicate)(char));
360
362bool cstr_chars_satisfies(const char *it, bool (*predicate)(char));
363
366size_t str_has_prefix_str(str it, str prefix);
367
369size_t str_has_prefix_cstr(str it, const char *prefix);
370
372size_t cstr_has_prefix_str(const char *it, str prefix);
373
375size_t cstr_has_prefix_cstr(const char *it, const char *prefix);
376
379size_t str_has_postfix_str(str it, str postfix);
380
382size_t str_has_postfix_cstr(str it, const char *postfix);
383
385size_t cstr_has_postfix_str(const char *it, str postfix);
386
388size_t cstr_has_postfix_cstr(const char *it, const char *postfix);
389
391#define str_has_prefix(LHS, RHS) _Generic((LHS),\
392 const str: _Generic((RHS),\
393 const str : str_has_prefix_str,\
394 str : str_has_prefix_str,\
395 const char*: str_has_prefix_cstr,\
396 char*: str_has_prefix_cstr\
397 ),\
398 str: _Generic((RHS),\
399 const str : str_has_prefix_str,\
400 str : str_has_prefix_str,\
401 const char*: str_has_prefix_cstr,\
402 char*: str_has_prefix_cstr\
403 ),\
404 const char*: _Generic((RHS),\
405 const str : cstr_has_prefix_str,\
406 str : cstr_has_prefix_str,\
407 const char*: cstr_has_prefix_cstr,\
408 char*: cstr_has_prefix_cstr\
409 ),\
410 char*: _Generic((RHS),\
411 const str : cstr_has_prefix_str,\
412 str : cstr_has_prefix_str,\
413 const char*: cstr_has_prefix_cstr,\
414 char*: cstr_has_prefix_cstr\
415 )\
416)((LHS), (RHS))
417
419#define str_has_postfix(LHS, RHS) _Generic((LHS),\
420 const str: _Generic((RHS),\
421 const str : str_has_postfix_str,\
422 str : str_has_postfix_str,\
423 const char*: str_has_postfix_cstr,\
424 char*: str_has_postfix_cstr\
425 ),\
426 str: _Generic((RHS),\
427 const str : str_has_postfix_str,\
428 str : str_has_postfix_str,\
429 const char*: str_has_postfix_cstr,\
430 char*: str_has_postfix_cstr\
431 ),\
432 const char*: _Generic((RHS),\
433 const str : cstr_has_postfix_str,\
434 str : cstr_has_postfix_str,\
435 const char*: cstr_has_postfix_cstr,\
436 char*: cstr_has_postfix_cstr\
437 ),\
438 char*: _Generic((RHS),\
439 const str : cstr_has_postfix_str,\
440 str : cstr_has_postfix_str,\
441 const char*: cstr_has_postfix_cstr,\
442 char*: cstr_has_postfix_cstr\
443 )\
444)((LHS), (RHS))
size_t cstr_has_prefix_cstr(const char *it, const char *prefix)
Checks if a string begins with another string.
size_t str_eq_cstrs_any(str it, size_t count, const char *others[static count])
Compares a string to several others to see if its contents are equal to any of them.
size_t str_eq_str_any(str it, size_t count,...)
Compares a string to several others to see if its contents are equal to any of them.
size_t str_eq_strs_any(str it, size_t count, const str others[static count])
Compares a string to several others to see if its contents are equal to any of them.
size_t cstr_has_postfix_cstr(const char *it, const char *postfix)
Checks if a string ends with another string.
str str_strip_prefix_if(str it, bool(*predicate)(char))
Returns a non-owning copy of a str with all leading characters satisfying predicate removed.
bool str_eq_cstr(str a, const char *b)
Compares two strings to see if their contents are equal.
str str_strip_prefix_cstr(str it, const char *prefix)
Returns a non-owning copy of a str with a given prefix removed, if it is present.
bool cstr_eq_str(const char *a, str b)
Compares two strings to see if their contents are equal.
str str_strip_whitespace(str it)
Returns a non-owning copy of a str with all leading and trailing whitespace removed.
str str_strip_postfix_str(str it, str postfix)
Returns a non-owning copy of a str with a given postfix removed, if it is present.
size_t cstr_satisfies_any(const char *it, size_t count,...)
Checks if a string satisfies any of several predicates.
int str_cmp_str(str a, str b)
Compares two strings lexicographically.
bool str_satisfies_alls(str it, size_t count, bool(*predicates[static count])(str))
Checks if a string satisfies all of several predicates.
size_t cstr_eq_strs_any(const char *it, size_t count, const str others[static count])
Compares a string to several others to see if its contents are equal to any of them.
bool str_eq_str(str a, str b)
Compares two strings to see if their contents are equal.
bool str_satisfies_all(str it, size_t count,...)
Checks if a string satisfies all of several predicates.
size_t cstr_has_postfix_str(const char *it, str postfix)
Checks if a string ends with another string.
str str_strip_whitespace_pre(str it)
Returns a non-owning copy of a str with all leading whitespace removed.
size_t cstr_has_prefix_str(const char *it, str prefix)
Checks if a string begins with another string.
str str_strip_prefix_str(str it, str prefix)
Returns a non-owning copy of a str with a given prefix removed, if it is present.
size_t cstr_eq_cstr_any(const char *it, size_t count,...)
Compares a string to several others to see if its contents are equal to any of them.
int str_cmp_cstr(str a, const char *b)
Compares two strings lexicographically.
str str_strip_postfix_cstr(str it, const char *postfix)
Returns a non-owning copy of a str with a given postfix removed, if it is present.
bool cstr_satisfies_all(const char *it, size_t count,...)
Checks if a string satisfies all of several predicates.
size_t str_has_prefix_str(str it, str prefix)
Checks if a string begins with another string.
str str_slice_cstr(char *sz, size_t len)
Constructs a non-owning str from part of a nil-terminated C-string.
bool cstr_chars_satisfies(const char *it, bool(*predicate)(char))
Checks if all characters in a string satisfies a predicate.
size_t cstr_eq_str_any(const char *it, size_t count,...)
Compares a string to several others to see if its contents are equal to any of them.
int cstr_cmp_str(const char *a, str b)
Compares two strings lexicographically.
size_t cstr_eq_cstrs_any(const char *it, size_t count, const char *others[static count])
Compares a string to several others to see if its contents are equal to any of them.
bool cstr_satisfies_alls(const char *it, size_t count, bool(*predicates[static count])(const char *))
Checks if a string satisfies all of several predicates.
str str_from_cstr(char *sz)
Constructs a non-owning str from a nil-terminated C-string.
size_t str_has_postfix_str(str it, str postfix)
Checks if a string ends with another string.
size_t str_has_postfix_cstr(str it, const char *postfix)
Checks if a string ends with another string.
size_t cstr_satisfies_anys(const char *it, size_t count, bool(*predicates[static count])(const char *))
Checks if a string satisfies any of several predicates.
size_t str_eq_cstr_any(str it, size_t count,...)
Compares a string to several others to see if its contents are equal to any of them.
size_t str_satisfies_any(str it, size_t count,...)
Checks if a string satisfies any of several predicates.
str str_strip_whitespace_post(str it)
Returns a non-owning copy of a str with all trailing whitespace removed.
str str_strip_postfix_if(str it, bool(*predicate)(char))
Returns a non-owning copy of a str with all trailing characters satisfying predicate removed.
bool str_chars_satisfies(str it, bool(*predicate)(char))
Checks if all characters in a string satisfies a predicate.
size_t str_has_prefix_cstr(str it, const char *prefix)
Checks if a string begins with another string.
size_t str_satisfies_anys(str it, size_t count, bool(*predicates[static count])(str))
Checks if a string satisfies any of several predicates.
bool cstr_eq_cstr(const char *a, const char *b)
Compares two strings to see if their contents are equal.
A string "slice", which is a pointer & length pair.
Definition str.h:42
char * data
Pointer to the start of the string.
Definition str.h:43
size_t len
The length of the string.
Definition str.h:44