1#pragma once
  2
  3#include <ctype.h>
  4
  5#include <pw_assert.h>
  6#include <pw_types.h>
  7#include <pw_utf.h>
  8
  9#ifdef PW_WITH_ICU
 10    // ICU library for character classification:
 11#   include <unicode/uchar.h>
 12#endif
 13
 14#ifdef __cplusplus
 15extern "C" {
 16#endif
 17
 18/****************************************************************
 19 * String constructors
 20 */
 21
 22[[nodiscard]] bool pw_create_empty_string(unsigned capacity, uint8_t char_size, PwValuePtr result);
 23
 24#define pw_create_string(result, initializer) _Generic((initializer),  \
 25                 char*: _pw_create_string_ascii, \
 26              char8_t*: _pw_create_string_utf8,  \
 27             char32_t*: _pw_create_string_utf32, \
 28            PwValuePtr: _pw_create_string        \
 29    )((result), (initializer))
 30
 31[[nodiscard]] bool _pw_create_string      (PwValuePtr result, PwValuePtr initializer);
 32[[nodiscard]] bool _pw_create_string_ascii(PwValuePtr result, char*      initializer);
 33[[nodiscard]] bool _pw_create_string_utf8 (PwValuePtr result, char8_t*   initializer);
 34[[nodiscard]] bool _pw_create_string_utf32(PwValuePtr result, char32_t*  initializer);
 35
 36
 37/****************************************************************
 38 * Basic functions
 39 */
 40
 41static inline uint8_t* _pw_string_start(PwValuePtr str)
 42/*
 43 * Return pointer to the start of internal string data.
 44 */
 45{
 46    if (str->embedded) {
 47        return str->str_1;
 48    } else if (str->allocated) {
 49        return str->string_data->data;
 50    } else {
 51        return str->char_ptr;
 52    }
 53}
 54
 55static inline uint8_t* _pw_string_start_end(PwValuePtr str, uint8_t** end)
 56/*
 57 * Return pointer to the start of internal string data
 58 * and write final pointer to `end`.
 59 */
 60{
 61    uint8_t* start_ptr;
 62    uint8_t char_size = str->char_size;
 63    if (str->embedded) {
 64        start_ptr = str->str_1;
 65        *end = start_ptr + str->embedded_length * char_size;
 66    } else if (str->allocated) {
 67        start_ptr = str->string_data->data;
 68        *end = start_ptr + str->length * char_size;
 69    } else {
 70        // static string
 71        start_ptr = str->char_ptr;
 72        *end = start_ptr + str->length * char_size;
 73    }
 74    return start_ptr;
 75}
 76
 77static inline uint8_t* _pw_string_start_length(PwValuePtr str, unsigned* length)
 78/*
 79 * Return pointer to the start of internal string data
 80 * and write length of string to `length`.
 81 */
 82{
 83   if (str->embedded) {
 84        *length = str->embedded_length;
 85        return str->str_1;
 86    } else if (str->allocated) {
 87        *length = str->length;
 88        return str->string_data->data;
 89    } else {
 90        *length = str->length;
 91        return str->char_ptr;
 92    }
 93}
 94
 95[[noreturn]]
 96void _pw_panic_bad_char_size(uint8_t char_size);
 97
 98static inline char32_t _pw_get_char(uint8_t* ptr, uint8_t char_size)
 99{
100    if (_pw_likely(char_size == 1)) {
101        return *ptr;
102    }
103    if (_pw_likely(char_size == 2)) {
104        return *(uint16_t*) ptr;
105    }
106    if (_pw_likely(char_size == 0)) {
107        return _pw_decode_utf8_char(&ptr);
108    }
109    if (_pw_likely(char_size == 4)) {
110        return *(char32_t*) ptr;
111    }
112    if (_pw_likely(char_size == 3)) {
113        char32_t c = *ptr++;
114        c |= (*ptr++) << 8;
115        c |= (*ptr) << 16;
116        return c;
117    }
118    _pw_panic_bad_char_size(char_size);
119}
120
121static inline char32_t _pw_get_char_reverse(uint8_t** ptr, uint8_t char_size)
122{
123    if (_pw_likely(char_size == 1)) {
124        uint8_t* p = *ptr;
125        p--;
126        *ptr = p;
127        return *p;
128    }
129    if (_pw_likely(char_size == 2)) {
130        uint16_t* p = *(uint16_t**) ptr;
131        p--;
132        *ptr = (uint8_t*) p;
133        return *p;
134    }
135    if (_pw_likely(char_size == 0)) {
136        return _pw_decode_utf8_char_reverse(ptr);
137    }
138    if (_pw_likely(char_size == 4)) {
139        char32_t* p = *(char32_t**) ptr;
140        p--;
141        *ptr = (uint8_t*) p;
142        return *p;
143    }
144    if (_pw_likely(char_size == 3)) {
145        uint8_t* p = *ptr;
146        p -= 3;
147        *ptr = p;
148        char32_t c = *p++;
149        c |= (*p++) << 8;
150        c |= (*p) << 16;
151        return c;
152    }
153    _pw_panic_bad_char_size(char_size);
154}
155
156static inline unsigned _pw_put_char(uint8_t* ptr, char32_t chr, uint8_t char_size)
157{
158    if (_pw_likely(char_size == 1)) {
159        *ptr = (uint8_t) chr;
160    } else if (_pw_likely(char_size == 2)) {
161        *((uint16_t*) ptr) = (uint16_t) chr;
162    } else if (_pw_likely(char_size == 0)) {
163        return pw_char32_to_utf8(chr, (char*) ptr);
164    } else if (_pw_likely(char_size == 4)) {
165        *((char32_t*) ptr) = chr;
166    } else if (_pw_likely(char_size == 3)) {
167        *ptr++ = (uint8_t) chr; chr >>= 8;
168        *ptr++ = (uint8_t) chr; chr >>= 8;
169        *ptr = (uint8_t) chr;
170    } else {
171        _pw_panic_bad_char_size(char_size);
172    }
173    return char_size;
174}
175
176char32_t pw_char_at(PwValuePtr str, unsigned position);
177/*
178 * Return character at `position`.
179 * If position is beyond end of line return 0.
180 */
181
182// check if `index` is within string length
183#define pw_string_index_valid(str, index) ((index) < pw_strlen(str))
184
185static inline unsigned pw_strlen(PwValuePtr str)
186/*
187 * Return length of string.
188 */
189{
190    pw_assert_string(str);
191    if (str->embedded) {
192        return str->embedded_length;
193    } else {
194        return str->length;
195    }
196}
197
198[[nodiscard]] bool pw_substr(PwValuePtr str, unsigned start_pos, unsigned end_pos, PwValuePtr result);
199/*
200 * Get substring from `start_pos` to `end_pos`.
201 */
202
203[[nodiscard]] bool pw_string_erase(PwValuePtr str, unsigned start_pos, unsigned end_pos);
204/*
205 * Erase characters from `start_pos` to `end_pos`.
206 * This may make a copy of string, so checking return value is mandatory.
207 */
208
209[[nodiscard]] bool pw_string_truncate(PwValuePtr str, unsigned position);
210/*
211 * Truncate string at given `position`.
212 * This may make a copy of string, so checking return value is mandatory.
213 */
214
215[[ nodiscard]] bool pw_strchr(PwValuePtr str, char32_t chr, unsigned start_pos, unsigned* result);
216/*
217 * Find first occurence of `chr` in `str` starting from `start_pos`.
218 *
219 * Return true if character is found and write its position to `result`.
220 * `result` can be nullptr if position is not needed and the function
221 * is called just to check if `chr` is in `str`.
222 */
223
224[[ nodiscard]] bool pw_strchr2(PwValuePtr str, char32_t chr, unsigned start_pos, unsigned* result, uint8_t* max_char_size);
225/*
226 * Same as `pw_strchr` and `pw_strrchr`, but also return maximal char size.
227 */
228
229[[ nodiscard]] bool pw_strchri(PwValuePtr str, char32_t chr, unsigned start_pos, unsigned* result);
230/*
231 * Find first case-insensitive occurence of `chr` in `str` starting from `start_pos`.
232 *
233 * Return true if character is found and write its position to `result`.
234 * `result` can be nullptr if position is not needed and the function
235 * is called just to check if `chr` is in `str`.
236 */
237
238[[nodiscard]] bool pw_string_lstrip(PwValuePtr str);
239[[nodiscard]] bool pw_string_rstrip(PwValuePtr str);
240[[nodiscard]] bool pw_string_strip(PwValuePtr str);
241
242[[nodiscard]] bool pw_string_lstrip_chars(PwValuePtr str, char32_t* chars);
243[[nodiscard]] bool pw_string_rstrip_chars(PwValuePtr str, char32_t* chars);
244[[nodiscard]] bool pw_string_strip_chars(PwValuePtr str, char32_t* chars);
245
246
247[[nodiscard]] bool pw_string_lower(PwValuePtr str);
248[[nodiscard]] bool pw_string_upper(PwValuePtr str);
249
250
251#ifdef PW_WITH_ICU
252#   define pw_char_lower(c)  u_tolower(c)
253#   define pw_char_upper(c)  u_toupper(c)
254#else
255#   define pw_char_lower(c)  tolower(c)
256#   define pw_char_upper(c)  toupper(c)
257#endif
258
259#define pw_strcat(result, str, ...)  \
260    _pw_strcat_va((result), (str) __VA_OPT__(,) __VA_ARGS__, PwVaEnd())
261
262[[nodiscard]] bool _pw_strcat_va(PwValuePtr result, ...);
263[[nodiscard]] bool _pw_strcat_ap(PwValuePtr result, va_list ap);
264
265unsigned pw_string_skip_spaces(PwValuePtr str, unsigned position);
266/*
267 * Find position of the first non-space character starting from `position`.
268 * If non-space character is not found, the length is returned.
269 */
270
271unsigned pw_string_skip_chars(PwValuePtr str, unsigned position, char32_t* skipchars);
272/*
273 * Find position of the first character not belonging to `skipchars` starting from `position`.
274 * If no `skipchars` encountered, the length is returned.
275 */
276
277/****************************************************************
278 * Character classification functions
279 */
280
281#ifdef PW_WITH_ICU
282#   define pw_isspace(c)  u_isspace(c)
283#   define pw_isblank(c)  u_isblank(c)
284#   define pw_isalpha(c)  u_isalpha(c)
285#   define pw_isdigit(c)  u_isdigit(c)
286#   define pw_isalnum(c)  u_isalnum(c)
287#   define pw_ispunct(c)  u_ispunct(c)
288#   define pw_iscntrl(c)  u_iscntrl(c)
289#   define pw_isgraph(c)  u_isgraph(c)
290#   define pw_isprint(c)  u_isprint(c)
291#else
292#   define pw_isspace(c)  ((c) <= 255 && isspace((unsigned char) (c)))
293#   define pw_isblank(c)  ((c) <= 255 && isblank((unsigned char) (c)))
294#   define pw_isalpha(c)  ((c) <= 255 && isalpha((unsigned char) (c)))
295#   define pw_isdigit(c)  ((c) <= 255 && isdigit((unsigned char) (c)))
296#   define pw_isalnum(c)  ((c) <= 255 && isalnum((unsigned char) (c)))
297#   define pw_ispunct(c)  ((c) <= 255 && ispunct((unsigned char) (c)))
298#   define pw_iscntrl(c)  ((c) <= 255 && iscntrl((unsigned char) (c)))
299#   define pw_isgraph(c)  ((c) <= 255 && isgraph((unsigned char) (c)))
300#   define pw_isprint(c)  ((c) <= 255 && isprint((unsigned char) (c)))
301#endif
302/*
303 * Return true if `c` is a whitespace character.
304 */
305
306#define pw_is_ascii_digit(c)  isdigit(c)
307/*
308 * Return true if `c` is an ASCII digit.
309 * Do not consider any other unicode digits because this function
310 * is used in conjunction with standard C library (string to number conversion)
311 * that does not support unicode character classification.
312 */
313
314bool pw_string_isspace(PwValuePtr str);
315bool pw_string_isdigit(PwValuePtr str);
316bool pw_string_is_ascii_digit(PwValuePtr str);
317/*
318 * Return true if `str` is not empty and contains all digits.
319 */
320
321/****************************************************************
322 * Append functions
323 *
324 * Characters and PwValues:
325 *
326 *     pw_string_append(dest_string, chr);
327 *     pw_string_append(dest_string, src_string);
328 *
329 * Raw data (char*, char8_t*, char32_t*):
330 *
331 *     pw_string_append(dest_string, start_ptr, end_ptr);
332 *
333 * Null-terminated C strings:
334 *
335 *     pw_string_append(dest_string, start_ptr, nullptr);
336 *
337 * C strings are slowest because strlen is called to obtain end_ptr.
338 */
339
340#define pw_string_append(dest, src, ...) _Generic((src),  \
341              char32_t: _pw_string_append_c32,    \
342                   int: _pw_string_append_c32,    \
343                 char*: _pw_string_append_ascii,  \
344              char8_t*: _pw_string_append_utf8,   \
345             char32_t*: _pw_string_append_utf32,  \
346            PwValuePtr: _pw_string_append         \
347    )((dest), (src) __VA_OPT__(,) __VA_ARGS__)
348
349[[nodiscard]] bool _pw_string_append_c32  (PwValuePtr dest, char32_t   c);
350[[nodiscard]] bool _pw_string_append      (PwValuePtr dest, PwValuePtr src);
351[[nodiscard]] bool _pw_string_append_ascii(PwValuePtr dest, char*      start_ptr, char*     end_ptr);
352[[nodiscard]] bool _pw_string_append_utf8 (PwValuePtr dest, char8_t*   start_ptr, char8_t*  end_ptr);
353[[nodiscard]] bool _pw_string_append_utf32(PwValuePtr dest, char32_t*  start_ptr, char32_t* end_ptr);
354
355
356/****************************************************************
357 * Insert functions
358 * TODO other types
359 */
360
361#define pw_string_insert_chars(str, position, chr, n) _Generic((chr), \
362              char32_t: _pw_string_insert_many_c32,   \
363                   int: _pw_string_insert_many_c32    \
364    )((str), (position), (chr), (n))
365
366[[nodiscard]] bool _pw_string_insert_many_c32(PwValuePtr str, unsigned position, char32_t chr, unsigned n);
367
368/****************************************************************
369 * Append substring.
370 *
371 * Append `src` substring starting from `src_start_pos` to `src_end_pos`.
372 */
373
374[[nodiscard]] bool pw_string_append_substring(PwValuePtr dest, PwValuePtr src, unsigned src_start_pos, unsigned src_end_pos);
375
376
377/****************************************************************
378 * Substring comparison functions.
379 *
380 * Compare `str_a` from `start_pos` to `end_pos` with `str_b`.
381 */
382
383#define pw_substring_eq(a, start_pos, end_pos, b) _Generic((b), \
384             char*: _pw_substring_eq_ascii,  \
385          char8_t*: _pw_substring_eq_utf8,   \
386         char32_t*: _pw_substring_eq_utf32,  \
387        PwValuePtr: _pw_substring_eq         \
388    )((a), (start_pos), (end_pos), (b))
389
390[[nodiscard]] bool _pw_substring_eq  (PwValuePtr a, unsigned start_pos, unsigned end_pos, PwValuePtr b);
391[[nodiscard]] bool _pw_substring_eq_z(PwValuePtr a, unsigned start_pos, unsigned end_pos, void* b, uint8_t b_char_size);
392
393[[nodiscard]] static inline bool _pw_substring_eq_ascii(PwValuePtr a, unsigned start_pos, unsigned end_pos, char* b)
394{
395    return _pw_substring_eq_z(a, start_pos, end_pos, b, 1);
396}
397[[nodiscard]] static inline bool _pw_substring_eq_utf8 (PwValuePtr a, unsigned start_pos, unsigned end_pos, char8_t* b)
398{
399    return _pw_substring_eq_z(a, start_pos, end_pos, b, 0);
400}
401[[nodiscard]] static inline bool _pw_substring_eq_utf32(PwValuePtr a, unsigned start_pos, unsigned end_pos, char32_t* b)
402{
403    return _pw_substring_eq_z(a, start_pos, end_pos, b, 4);
404}
405
406
407#define pw_substring_eqi(a, start_pos, end_pos, b) _Generic((b), \
408             char*: _pw_substring_eqi_ascii,  \
409          char8_t*: _pw_substring_eqi_utf8,   \
410         char32_t*: _pw_substring_eqi_utf32,  \
411        PwValuePtr: _pw_substring_eqi         \
412    )((a), (start_pos), (end_pos), (b))
413
414[[nodiscard]] bool _pw_substring_eqi  (PwValuePtr a, unsigned start_pos, unsigned end_pos, PwValuePtr b);
415[[nodiscard]] bool _pw_substring_eqi_z(PwValuePtr a, unsigned start_pos, unsigned end_pos, void* b, uint8_t b_char_size);
416
417[[nodiscard]] static inline bool _pw_substring_eqi_ascii(PwValuePtr a, unsigned start_pos, unsigned end_pos, char* b)
418{
419    return _pw_substring_eqi_z(a, start_pos, end_pos, b, 1);
420}
421[[nodiscard]] static inline bool _pw_substring_eqi_utf8 (PwValuePtr a, unsigned start_pos, unsigned end_pos, char8_t* b)
422{
423    return _pw_substring_eqi_z(a, start_pos, end_pos, b, 0);
424}
425[[nodiscard]] static inline bool _pw_substring_eqi_utf32(PwValuePtr a, unsigned start_pos, unsigned end_pos, char32_t* b)
426{
427    return _pw_substring_eqi_z(a, start_pos, end_pos, b, 4);
428}
429
430
431#define pw_startswith(str, prefix) _Generic((prefix), \
432               int: _pw_startswith_c32,    \
433          char32_t: _pw_startswith_c32,    \
434             char*: _pw_startswith_ascii,  \
435          char8_t*: _pw_startswith_utf8,   \
436         char32_t*: _pw_startswith_utf32,  \
437        PwValuePtr: _pw_startswith         \
438    )((str), (prefix))
439
440[[nodiscard]] bool _pw_startswith_c32(PwValuePtr str, char32_t   prefix);
441[[nodiscard]] bool _pw_startswith    (PwValuePtr str, PwValuePtr prefix);
442[[nodiscard]] bool _pw_startswith_z  (PwValuePtr str, void*      prefix, uint8_t prefix_char_size);
443
444[[nodiscard]] static inline bool _pw_startswith_ascii(PwValuePtr str, char* prefix)
445{
446    return _pw_startswith_z(str, prefix, 1);
447}
448[[nodiscard]] static inline bool _pw_startswith_utf8(PwValuePtr str, char8_t* prefix)
449{
450    return _pw_startswith_z(str, prefix, 0);
451}
452[[nodiscard]] static inline bool _pw_startswith_utf32(PwValuePtr str, char32_t* prefix)
453{
454    return _pw_startswith_z(str, prefix, 4);
455}
456
457
458#define pw_startswithi(str, prefix) _Generic((prefix), \
459               int: _pw_startswithi_c32,    \
460          char32_t: _pw_startswithi_c32,    \
461             char*: _pw_startswithi_ascii,  \
462          char8_t*: _pw_startswithi_utf8,   \
463         char32_t*: _pw_startswithi_utf32,  \
464        PwValuePtr: _pw_startswithi         \
465    )((str), (prefix))
466
467[[nodiscard]] bool _pw_startswithi_c32(PwValuePtr str, char32_t   prefix);
468[[nodiscard]] bool _pw_startswithi    (PwValuePtr str, PwValuePtr prefix);
469[[nodiscard]] bool _pw_startswithi_z  (PwValuePtr str, void*      prefix, uint8_t prefix_char_size);
470
471[[nodiscard]] static inline bool _pw_startswithi_ascii(PwValuePtr str, char* prefix)
472{
473    return _pw_startswithi_z(str, prefix, 1);
474}
475[[nodiscard]] static inline bool _pw_startswithi_utf8(PwValuePtr str, char8_t* prefix)
476{
477    return _pw_startswithi_z(str, prefix, 0);
478}
479[[nodiscard]] static inline bool _pw_startswithi_utf32(PwValuePtr str, char32_t* prefix)
480{
481    return _pw_startswithi_z(str, prefix, 4);
482}
483
484
485#define pw_endswith(str, suffix) _Generic((suffix), \
486               int: _pw_endswith_c32,    \
487          char32_t: _pw_endswith_c32,    \
488             char*: _pw_endswith_ascii,  \
489          char8_t*: _pw_endswith_utf8,   \
490         char32_t*: _pw_endswith_utf32,  \
491        PwValuePtr: _pw_endswith         \
492    )((str), (suffix))
493
494[[nodiscard]] bool _pw_endswith_c32(PwValuePtr str, char32_t   suffix);
495[[nodiscard]] bool _pw_endswith    (PwValuePtr str, PwValuePtr suffix);
496[[nodiscard]] bool _pw_endswith_z  (PwValuePtr str, void*      suffix, uint8_t suffix_char_size);
497
498[[nodiscard]] static inline bool _pw_endswith_ascii(PwValuePtr str, char* suffix)
499{
500    return _pw_endswith_z(str, suffix, 1);
501}
502[[nodiscard]] static inline bool _pw_endswith_utf8(PwValuePtr str, char8_t* suffix)
503{
504    return _pw_endswith_z(str, suffix, 0);
505}
506[[nodiscard]] static inline bool _pw_endswith_utf32(PwValuePtr str, char32_t* suffix)
507{
508    return _pw_endswith_z(str, suffix, 4);
509}
510
511
512#define pw_endswithi(str, suffix) _Generic((suffix), \
513               int: _pw_endswithi_c32,    \
514          char32_t: _pw_endswithi_c32,    \
515             char*: _pw_endswithi_ascii,  \
516          char8_t*: _pw_endswithi_utf8,   \
517         char32_t*: _pw_endswithi_utf32,  \
518        PwValuePtr: _pw_endswithi         \
519    )((str), (suffix))
520
521[[nodiscard]] bool _pw_endswithi_c32(PwValuePtr str, char32_t   suffix);
522[[nodiscard]] bool _pw_endswithi    (PwValuePtr str, PwValuePtr suffix);
523[[nodiscard]] bool _pw_endswithi_z  (PwValuePtr str, void*      suffix, uint8_t suffix_char_size);
524
525[[nodiscard]] static inline bool _pw_endswithi_ascii(PwValuePtr str, char* suffix)
526{
527    return _pw_endswithi_z(str, suffix, 1);
528}
529[[nodiscard]] static inline bool _pw_endswithi_utf8(PwValuePtr str, char8_t* suffix)
530{
531    return _pw_endswithi_z(str, suffix, 0);
532}
533[[nodiscard]] static inline bool _pw_endswithi_utf32(PwValuePtr str, char32_t* suffix)
534{
535    return _pw_endswithi_z(str, suffix, 4);
536}
537
538
539/****************************************************************
540 * Substring scanning functions.
541 *
542 * Return true if substring is found, false if not.
543 */
544
545#define pw_strstr(str, substr, start_pos, pos) _Generic((substr), \
546                 char*: _pw_strstr_ascii,  \
547              char8_t*: _pw_strstr_utf8,   \
548             char32_t*: _pw_strstr_utf32,  \
549            PwValuePtr: _pw_strstr         \
550    )((str), (substr), (start_pos), (pos))
551
552[[nodiscard]] bool _pw_strstr(PwValuePtr str, PwValuePtr substr, unsigned start_pos, unsigned* pos);
553
554[[nodiscard]] bool _pw_strstr_z(PwValuePtr str, void* substr, uint8_t substr_char_size, unsigned start_pos, unsigned* pos);
555
556[[nodiscard]] static inline bool _pw_strstr_ascii(PwValuePtr str, char* substr, unsigned start_pos, unsigned* pos)
557{
558    return _pw_strstr_z(str, substr, 1, start_pos, pos);
559}
560[[nodiscard]] static inline bool _pw_strstr_utf8 (PwValuePtr str, char8_t* substr, unsigned start_pos, unsigned* pos)
561{
562    return _pw_strstr_z(str, substr, 0, start_pos, pos);
563}
564[[nodiscard]] static inline bool _pw_strstr_utf32(PwValuePtr str, char32_t* substr, unsigned start_pos, unsigned* pos)
565{
566    return _pw_strstr_z(str, substr, 4, start_pos, pos);
567}
568
569#define pw_strstri(str, substr, pos) _Generic((substr), \
570                 char*: _pw_strstri_ascii,  \
571              char8_t*: _pw_strstri_utf8,   \
572             char32_t*: _pw_strstri_utf32,  \
573            PwValuePtr: _pw_strstri         \
574    )((str), (substr), (start_pos), (pos))
575
576[[nodiscard]] bool _pw_strstri      (PwValuePtr str, PwValuePtr substr, unsigned start_pos, unsigned* pos);
577[[nodiscard]] bool _pw_strstri_ascii(PwValuePtr str, char*      substr, unsigned start_pos, unsigned* pos);
578[[nodiscard]] bool _pw_strstri_utf8 (PwValuePtr str, char8_t*   substr, unsigned start_pos, unsigned* pos);
579[[nodiscard]] bool _pw_strstri_utf32(PwValuePtr str, char32_t*  substr, unsigned start_pos, unsigned* pos);
580
581
582/****************************************************************
583 * Split functions.
584 * Return array of strings.
585 * maxsplit == 0 imposes no limit
586 */
587
588[[nodiscard]] bool _pw_string_split     (PwValuePtr str, /* split by spaces */ unsigned maxsplit, PwValuePtr result, uint16_t result_type);
589[[nodiscard]] bool _pw_string_split_chr (PwValuePtr str, char32_t splitter,    unsigned maxsplit, PwValuePtr result, uint16_t result_type);
590[[nodiscard]] bool _pw_string_rsplit    (PwValuePtr str, /* split by spaces */ unsigned maxsplit, PwValuePtr result, uint16_t result_type);
591[[nodiscard]] bool _pw_string_rsplit_chr(PwValuePtr str, char32_t splitter,    unsigned maxsplit, PwValuePtr result, uint16_t result_type);
592
593#define pw_string_split(...)       _pw_string_split     (__VA_ARGS__, PwTypeId_BasicArray)
594#define pw_string_split_chr(...)   _pw_string_split_chr (__VA_ARGS__, PwTypeId_BasicArray)
595#define pw_string_rsplit(...)      _pw_string_rsplit    (__VA_ARGS__, PwTypeId_BasicArray)
596#define pw_string_rsplit_chr(...)  _pw_string_rsplit_chr(__VA_ARGS__, PwTypeId_BasicArray)
597
598#define _pw_string_split_any(str, splitters, maxsplit, result_type) _Generic((splitters),  \
599                 char*: _pw_string_split_any_ascii,  \
600              char8_t*: _pw_string_split_any_utf8,   \
601             char32_t*: _pw_string_split_any_utf32,  \
602            PwValuePtr: _pw_string_split_any_var     \
603    )((str), (splitters), (maxsplit), (result_type))
604
605#define pw_string_split_any(...)  _pw_string_split_any(__VA_ARGS__, PwTypeId_BasicArray)
606
607[[nodiscard]] bool _pw_string_split_any_var  (PwValuePtr str, PwValuePtr splitters, unsigned maxsplit, PwValuePtr result, uint16_t result_type);
608[[nodiscard]] bool _pw_string_split_any_ascii(PwValuePtr str, char*      splitters, unsigned maxsplit, PwValuePtr result, uint16_t result_type);
609[[nodiscard]] bool _pw_string_split_any_utf8 (PwValuePtr str, char8_t*   splitters, unsigned maxsplit, PwValuePtr result, uint16_t result_type);
610[[nodiscard]] bool _pw_string_split_any_utf32(PwValuePtr str, char32_t*  splitters, unsigned maxsplit, PwValuePtr result, uint16_t result_type);
611
612#define _pw_string_rsplit_any(str, splitters, maxsplit, result_type) _Generic((splitters),  \
613                 char*: _pw_string_rsplit_any_ascii,  \
614              char8_t*: _pw_string_rsplit_any_utf8,   \
615             char32_t*: _pw_string_rsplit_any_utf32,  \
616            PwValuePtr: _pw_string_rsplit_any_var     \
617    )((str), (splitters), (maxsplit), (result_type))
618
619#define pw_string_rsplit_any(...)  _pw_string_rsplit_any(__VA_ARGS__, PwTypeId_BasicArray)
620
621[[nodiscard]] bool _pw_string_rsplit_any_var  (PwValuePtr str, PwValuePtr splitters, unsigned maxsplit, PwValuePtr result, uint16_t result_type);
622[[nodiscard]] bool _pw_string_rsplit_any_ascii(PwValuePtr str, char*      splitters, unsigned maxsplit, PwValuePtr result, uint16_t result_type);
623[[nodiscard]] bool _pw_string_rsplit_any_utf8 (PwValuePtr str, char8_t*   splitters, unsigned maxsplit, PwValuePtr result, uint16_t result_type);
624[[nodiscard]] bool _pw_string_rsplit_any_utf32(PwValuePtr str, char32_t*  splitters, unsigned maxsplit, PwValuePtr result, uint16_t result_type);
625
626#define _pw_string_split_substr(str, splitter, maxsplit, result_type) _Generic((splitter),  \
627                 char*: _pw_string_split_substr_ascii,  \
628              char8_t*: _pw_string_split_substr_utf8,   \
629             char32_t*: _pw_string_split_substr_utf32,  \
630            PwValuePtr: _pw_string_split_substr_var     \
631    )((str), (splitter), (maxsplit), (result_type))
632
633#define pw_string_split_substr(...)  _pw_string_split_substr(__VA_ARGS__, PwTypeId_BasicArray)
634
635[[nodiscard]] bool _pw_string_split_substr_var  (PwValuePtr str, PwValuePtr splitter, unsigned maxsplit, PwValuePtr result, uint16_t result_type);
636[[nodiscard]] bool _pw_string_split_substr_ascii(PwValuePtr str, char*      splitter, unsigned maxsplit, PwValuePtr result, uint16_t result_type);
637[[nodiscard]] bool _pw_string_split_substr_utf8 (PwValuePtr str, char8_t*   splitter, unsigned maxsplit, PwValuePtr result, uint16_t result_type);
638[[nodiscard]] bool _pw_string_split_substr_utf32(PwValuePtr str, char32_t*  splitter, unsigned maxsplit, PwValuePtr result, uint16_t result_type);
639
640#define _pw_string_split_substri(str, splitter, maxsplit, result_type) _Generic((splitter),  \
641                 char*: _pw_string_split_substri_ascii,  \
642              char8_t*: _pw_string_split_substri_utf8,   \
643             char32_t*: _pw_string_split_substri_utf32,  \
644            PwValuePtr: _pw_string_split_substri_var     \
645    )((str), (splitter), (maxsplit), (result_type))
646
647#define pw_string_split_substri(...)  _pw_string_split_substri(__VA_ARGS__, PwTypeId_BasicArray)
648
649[[nodiscard]] bool _pw_string_split_substri_var  (PwValuePtr str, PwValuePtr splitter, unsigned maxsplit, PwValuePtr result, uint16_t result_type);
650[[nodiscard]] bool _pw_string_split_substri_ascii(PwValuePtr str, char*      splitter, unsigned maxsplit, PwValuePtr result, uint16_t result_type);
651[[nodiscard]] bool _pw_string_split_substri_utf8 (PwValuePtr str, char8_t*   splitter, unsigned maxsplit, PwValuePtr result, uint16_t result_type);
652[[nodiscard]] bool _pw_string_split_substri_utf32(PwValuePtr str, char32_t*  splitter, unsigned maxsplit, PwValuePtr result, uint16_t result_type);
653
654#define _pw_string_rsplit_substr(str, splitter, maxsplit, result_type) _Generic((splitter),  \
655                 char*: _pw_string_rsplit_substr_ascii,  \
656              char8_t*: _pw_string_rsplit_substr_utf8,   \
657             char32_t*: _pw_string_rsplit_substr_utf32,  \
658            PwValuePtr: _pw_string_rsplit_substr_var     \
659    )((str), (splitter), (maxsplit), (result_type))
660
661#define pw_string_rsplit_substr(...)  _pw_string_rsplit_substr(__VA_ARGS__, PwTypeId_BasicArray)
662
663[[nodiscard]] bool _pw_string_rsplit_substr_var  (PwValuePtr str, PwValuePtr splitter, unsigned maxsplit, PwValuePtr result, uint16_t result_type);
664[[nodiscard]] bool _pw_string_rsplit_substr_ascii(PwValuePtr str, char*      splitter, unsigned maxsplit, PwValuePtr result, uint16_t result_type);
665[[nodiscard]] bool _pw_string_rsplit_substr_utf8 (PwValuePtr str, char8_t*   splitter, unsigned maxsplit, PwValuePtr result, uint16_t result_type);
666[[nodiscard]] bool _pw_string_rsplit_substr_utf32(PwValuePtr str, char32_t*  splitter, unsigned maxsplit, PwValuePtr result, uint16_t result_type);
667
668#define _pw_string_rsplit_substri(str, splitter, maxsplit, result_type) _Generic((splitter),  \
669                 char*: _pw_string_rsplit_substri_ascii,  \
670              char8_t*: _pw_string_rsplit_substri_utf8,   \
671             char32_t*: _pw_string_rsplit_substri_utf32,  \
672            PwValuePtr: _pw_string_rsplit_substri_var     \
673    )((str), (splitter), (maxsplit), (result_type))
674
675#define pw_string_rsplit_substri(...)  _pw_string_rsplit_substri(__VA_ARGS__, PwTypeId_BasicArray)
676
677[[nodiscard]] bool _pw_string_rsplit_substri_var  (PwValuePtr str, PwValuePtr splitter, unsigned maxsplit, PwValuePtr result, uint16_t result_type);
678[[nodiscard]] bool _pw_string_rsplit_substri_ascii(PwValuePtr str, char*      splitter, unsigned maxsplit, PwValuePtr result, uint16_t result_type);
679[[nodiscard]] bool _pw_string_rsplit_substri_utf8 (PwValuePtr str, char8_t*   splitter, unsigned maxsplit, PwValuePtr result, uint16_t result_type);
680[[nodiscard]] bool _pw_string_rsplit_substri_utf32(PwValuePtr str, char32_t*  splitter, unsigned maxsplit, PwValuePtr result, uint16_t result_type);
681
682
683/****************************************************************
684 * C strings
685 */
686
687// somewhat ugly macro to define a local variable initialized with a copy of pw string:
688#define PW_CSTRING(variable_name, pw_str) \
689    char variable_name[pw_strlen_in_utf8(pw_str) + 1]; \
690    pw_string_to_utf8((pw_str), variable_name)
691
692
693#ifdef __cplusplus
694}
695#endif