🧵 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@proton.me>
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
25#pragma once
26#include <stddef.h>
27#if __STDC_VERSION__ < 202311L
28 #include <stdbool.h>
29#endif
30
38typedef struct str {
39 char *data;
40 size_t len;
42
44#define str_literal(SZ_LITERAL) (str){ (SZ_LITERAL), sizeof(SZ_LITERAL) - 1 }
45
49
52str str_slice_cstr(char *sz, size_t len);
53
57
61
65
69
72str str_strip_prefix_cstr(str it, const char *prefix);
73
76str str_strip_prefix_if(str it, bool (*predicate)(char));
77
81#define str_strip_prefix(IT, PREFIX) _Generic((PREFIX),\
82 const str : str_strip_prefix_str,\
83 str : str_strip_prefix_str,\
84 const char* : str_strip_prefix_cstr,\
85 char* : str_strip_prefix_cstr,\
86 bool (*)(char c) : str_strip_prefix_if\
87)((IT), (PREFIX))
88
92
95str str_strip_postfix_cstr(str it, const char *postfix);
96
99str str_strip_postfix_if(str it, bool (*predicate)(char));
100
104#define str_strip_postfix(IT, POSTFIX) _Generic((POSTFIX),\
105 const str : str_strip_postfix_str,\
106 str : str_strip_postfix_str,\
107 const char* : str_strip_postfix_cstr,\
108 char* : str_strip_postfix_cstr,\
109 bool (*)(char c) : str_strip_postfix_if\
110)((IT), (POSTFIX))
111
115
117int str_cmp_cstr(str a, const char *b);
118
120int cstr_cmp_str(const char *a, str b);
121
124#define cstr_cmp_cstr strcmp
125
128#define str_cmp(LHS, RHS) _Generic((LHS),\
129 const str: _Generic((RHS),\
130 const str : str_cmp_str,\
131 str : str_cmp_str,\
132 const char*: str_cmp_cstr,\
133 char*: str_cmp_cstr\
134 ),\
135 str: _Generic((RHS),\
136 const str : str_cmp_str,\
137 str : str_cmp_str,\
138 const char*: str_cmp_cstr,\
139 char*: str_cmp_cstr\
140 ),\
141 const char*: _Generic((RHS),\
142 const str : cstr_cmp_str,\
143 str : cstr_cmp_str,\
144 const char*: strcmp,\
145 char*: strcmp\
146 ),\
147 char*: _Generic((RHS),\
148 const str : cstr_cmp_str,\
149 str : cstr_cmp_str,\
150 const char*: strcmp,\
151 char*: strcmp\
152 )\
153)((LHS), (RHS))
154
156bool str_eq_str(str a, str b);
157
159bool str_eq_cstr(str a, const char *b);
160
162bool cstr_eq_str(const char *a, str b);
163
165bool cstr_eq_cstr(const char *a, const char *b);
166
168#define str_eq(LHS, RHS) _Generic((LHS),\
169 const str: _Generic((RHS),\
170 const str : str_eq_str,\
171 str : str_eq_str,\
172 const char*: str_eq_cstr,\
173 char*: str_eq_cstr\
174 ),\
175 str: _Generic((RHS),\
176 const str : str_eq_str,\
177 str : str_eq_str,\
178 const char*: str_eq_cstr,\
179 char*: str_eq_cstr\
180 ),\
181 const char*: _Generic((RHS),\
182 const str : cstr_eq_str,\
183 str : cstr_eq_str,\
184 const char*: cstr_eq_cstr,\
185 char*: cstr_eq_cstr\
186 ),\
187 char*: _Generic((RHS),\
188 const str : cstr_eq_str,\
189 str : cstr_eq_str,\
190 const char*: cstr_eq_cstr,\
191 char*: cstr_eq_cstr\
192 )\
193)((LHS), (RHS))
194
203size_t str_eq_str_any(str it, size_t count, ...);
204
213size_t str_eq_strs_any(str it, size_t count, const str others[static count]);
214
216size_t str_eq_cstr_any(str it, size_t count, ...);
217
219size_t str_eq_cstrs_any(str it, size_t count, const char *others[static count]);
220
222size_t cstr_eq_str_any(const char *it, size_t count, ...);
223
225size_t cstr_eq_strs_any(const char *it, size_t count, const str others[static count]);
226
228size_t cstr_eq_cstr_any(const char *it, size_t count, ...);
229
231size_t cstr_eq_cstrs_any(const char *it, size_t count, const char *others[static count]);
232
244#if __STDC_VERSION__ >= 202311L
245 #define str_eq_any(LHS, COUNT, RHS, ...) _Generic((LHS),\
246 const str: _Generic((RHS),\
247 const str : str_eq_str_any,\
248 str : str_eq_str_any,\
249 const str* : str_eq_strs_any,\
250 str* : str_eq_strs_any,\
251 const char* : str_eq_cstr_any,\
252 char* : str_eq_cstr_any,\
253 const char**: str_eq_cstrs_any,\
254 char**: str_eq_cstrs_any\
255 ),\
256 str: _Generic((RHS),\
257 const str : str_eq_str_any,\
258 str : str_eq_str_any,\
259 const str* : str_eq_strs_any,\
260 str* : str_eq_strs_any,\
261 const char* : str_eq_cstr_any,\
262 char* : str_eq_cstr_any,\
263 const char**: str_eq_cstrs_any,\
264 char**: str_eq_cstrs_any\
265 ),\
266 const char*: _Generic((RHS),\
267 const str : cstr_eq_str_any,\
268 str : cstr_eq_str_any,\
269 const str* : cstr_eq_strs_any,\
270 str* : cstr_eq_strs_any,\
271 const char* : cstr_eq_cstr_any,\
272 char* : cstr_eq_cstr_any,\
273 const char**: cstr_eq_cstrs_any,\
274 char**: cstr_eq_cstrs_any\
275 ),\
276 char*: _Generic((RHS),\
277 const str : cstr_eq_str_any,\
278 str : cstr_eq_str_any,\
279 const str* : cstr_eq_strs_any,\
280 str* : cstr_eq_strs_any,\
281 const char* : cstr_eq_cstr_any,\
282 char* : cstr_eq_cstr_any,\
283 const char**: cstr_eq_cstrs_any,\
284 char**: cstr_eq_cstrs_any\
285 )\
286 )((LHS), (COUNT), (RHS) __VA_OPT__(, __VA_ARGS__))
287#elif defined(__GNUC__)
288 #define str_eq_any(LHS, COUNT, RHS, ...) _Generic((LHS),\
289 const str: _Generic((RHS),\
290 const str : str_eq_str_any,\
291 str : str_eq_str_any,\
292 const str* : str_eq_strs_any,\
293 str* : str_eq_strs_any,\
294 const char* : str_eq_cstr_any,\
295 char* : str_eq_cstr_any,\
296 const char**: str_eq_cstrs_any,\
297 char**: str_eq_cstrs_any\
298 ),\
299 str: _Generic((RHS),\
300 const str : str_eq_str_any,\
301 str : str_eq_str_any,\
302 const str* : str_eq_strs_any,\
303 str* : str_eq_strs_any,\
304 const char* : str_eq_cstr_any,\
305 char* : str_eq_cstr_any,\
306 const char**: str_eq_cstrs_any,\
307 char**: str_eq_cstrs_any\
308 ),\
309 const char*: _Generic((RHS),\
310 const str : cstr_eq_str_any,\
311 str : cstr_eq_str_any,\
312 const str* : cstr_eq_strs_any,\
313 str* : cstr_eq_strs_any,\
314 const char* : cstr_eq_cstr_any,\
315 char* : cstr_eq_cstr_any,\
316 const char**: cstr_eq_cstrs_any,\
317 char**: cstr_eq_cstrs_any\
318 ),\
319 char*: _Generic((RHS),\
320 const str : cstr_eq_str_any,\
321 str : cstr_eq_str_any,\
322 const str* : cstr_eq_strs_any,\
323 str* : cstr_eq_strs_any,\
324 const char* : cstr_eq_cstr_any,\
325 char* : cstr_eq_cstr_any,\
326 const char**: cstr_eq_cstrs_any,\
327 char**: cstr_eq_cstrs_any\
328 )\
329 )((LHS), (COUNT), (RHS), ##__VA_ARGS__)
330#else
331 #warning Neither C23's __VA_OPT__ nor GNU's ##__VA_ARGS__ are available, str_eq_any() will be unavailable
332#endif
333
342size_t str_satisfies_any(str it, size_t count, ...);
343
352size_t str_satisfies_anys(str it, size_t count, bool (*predicates[static count])(str));
353
362size_t cstr_satisfies_any(const char *it, size_t count, ...);
363
365size_t cstr_satisfies_anys(const char *it, size_t count, bool (*predicates[static count])(const char*));
366
374bool str_satisfies_all(str it, size_t count, ...);
375
383bool str_satisfies_alls(str it, size_t count, bool (*predicates[static count])(str));
384
392bool cstr_satisfies_all(const char *it, size_t count, ...);
393
395bool cstr_satisfies_alls(const char *it, size_t count, bool (*predicates[static count])(const char*));
396
403bool str_chars_satisfies(str it, bool (*predicate)(char));
404
406bool cstr_chars_satisfies(const char *it, bool (*predicate)(char));
407
410size_t str_has_prefix_str(str it, str prefix);
411
413size_t str_has_prefix_cstr(str it, const char *prefix);
414
416size_t cstr_has_prefix_str(const char *it, str prefix);
417
419size_t cstr_has_prefix_cstr(const char *it, const char *prefix);
420
423size_t str_has_postfix_str(str it, str postfix);
424
426size_t str_has_postfix_cstr(str it, const char *postfix);
427
429size_t cstr_has_postfix_str(const char *it, str postfix);
430
432size_t cstr_has_postfix_cstr(const char *it, const char *postfix);
433
435#define str_has_prefix(LHS, RHS) _Generic((LHS),\
436 const str: _Generic((RHS),\
437 const str : str_has_prefix_str,\
438 str : str_has_prefix_str,\
439 const char*: str_has_prefix_cstr,\
440 char*: str_has_prefix_cstr\
441 ),\
442 str: _Generic((RHS),\
443 const str : str_has_prefix_str,\
444 str : str_has_prefix_str,\
445 const char*: str_has_prefix_cstr,\
446 char*: str_has_prefix_cstr\
447 ),\
448 const char*: _Generic((RHS),\
449 const str : cstr_has_prefix_str,\
450 str : cstr_has_prefix_str,\
451 const char*: cstr_has_prefix_cstr,\
452 char*: cstr_has_prefix_cstr\
453 ),\
454 char*: _Generic((RHS),\
455 const str : cstr_has_prefix_str,\
456 str : cstr_has_prefix_str,\
457 const char*: cstr_has_prefix_cstr,\
458 char*: cstr_has_prefix_cstr\
459 )\
460)((LHS), (RHS))
461
463#define str_has_postfix(LHS, RHS) _Generic((LHS),\
464 const str: _Generic((RHS),\
465 const str : str_has_postfix_str,\
466 str : str_has_postfix_str,\
467 const char*: str_has_postfix_cstr,\
468 char*: str_has_postfix_cstr\
469 ),\
470 str: _Generic((RHS),\
471 const str : str_has_postfix_str,\
472 str : str_has_postfix_str,\
473 const char*: str_has_postfix_cstr,\
474 char*: str_has_postfix_cstr\
475 ),\
476 const char*: _Generic((RHS),\
477 const str : cstr_has_postfix_str,\
478 str : cstr_has_postfix_str,\
479 const char*: cstr_has_postfix_cstr,\
480 char*: cstr_has_postfix_cstr\
481 ),\
482 char*: _Generic((RHS),\
483 const str : cstr_has_postfix_str,\
484 str : cstr_has_postfix_str,\
485 const char*: cstr_has_postfix_cstr,\
486 char*: cstr_has_postfix_cstr\
487 )\
488)((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:38
char * data
Pointer to the start of the string.
Definition str.h:39
size_t len
The length of the string.
Definition str.h:40