1#pragma once
  2
  3#include <pw_assert.h>
  4#include <pw_interfaces.h>
  5#include <pw_utf.h>
  6
  7#ifdef __cplusplus
  8extern "C" {
  9#endif
 10
 11/****************************************************************
 12 * Low-level functions
 13 */
 14
 15static inline uint8_t* _pw_string_start(PwValuePtr str)
 16/*
 17 * Return pointer to the start of internal string data.
 18 */
 19{
 20    if (str->str_params.integral) {
 21        return str->str_1;
 22    } else if (str->str_params.allocated) {
 23        return str->string_data->data;
 24    } else {
 25        return str->char_ptr;
 26    }
 27}
 28
 29static inline uint8_t* _pw_string_start_end(PwValuePtr str, uint8_t** end)
 30/*
 31 * Return pointer to the start of internal string data
 32 * and write final pointer to `end`.
 33 */
 34{
 35    uint8_t* start_ptr;
 36    uint8_t char_size = str->str_params.char_size;
 37    if (str->str_params.integral) {
 38        start_ptr = str->str_1;
 39        *end = start_ptr + str->integral_length * char_size;
 40    } else if (str->str_params.allocated) {
 41        start_ptr = str->string_data->data;
 42        *end = start_ptr + str->length * char_size;
 43    } else {
 44        // static string
 45        start_ptr = str->char_ptr;
 46        *end = start_ptr + str->length * char_size;
 47    }
 48    return start_ptr;
 49}
 50
 51static inline uint8_t* _pw_string_start_length(PwValuePtr str, unsigned* length)
 52/*
 53 * Return pointer to the start of internal string data
 54 * and write length of string to `length`.
 55 */
 56{
 57   if (str->str_params.integral) {
 58        *length = str->integral_length;
 59        return str->str_1;
 60    } else if (str->str_params.allocated) {
 61        *length = str->length;
 62        return str->string_data->data;
 63    } else {
 64        *length = str->length;
 65        return str->char_ptr;
 66    }
 67}
 68
 69[[noreturn]]
 70void _pw_panic_bad_char_size(uint8_t char_size);
 71
 72static inline char32_t _pw_get_char(uint8_t* ptr, uint8_t char_size)
 73{
 74    if (_pw_likely(char_size == 1)) {
 75        return *ptr;
 76    }
 77    if (_pw_likely(char_size == 2)) {
 78        return *(uint16_t*) ptr;
 79    }
 80    if (_pw_likely(char_size == 0)) {
 81        return _pw_decode_utf8_char(&ptr);
 82    }
 83    if (_pw_likely(char_size == 4)) {
 84        return *(char32_t*) ptr;
 85    }
 86    if (_pw_likely(char_size == 3)) {
 87        char32_t c = *ptr++;
 88        c |= *ptr++ << 8;
 89        c |= *ptr << 16;
 90        return c;
 91    }
 92    _pw_panic_bad_char_size(char_size);
 93}
 94
 95static inline char32_t _pw_next_char(uint8_t** ptr, uint8_t char_size)
 96{
 97    if (_pw_likely(char_size == 1)) {
 98        return *(*ptr)++;
 99    }
100    if (_pw_likely(char_size == 2)) {
101        return *(* (uint16_t**) ptr)++;
102    }
103    if (_pw_likely(char_size == 0)) {
104        return _pw_decode_utf8_char(ptr);
105    }
106    if (_pw_likely(char_size == 4)) {
107        return *(* (char32_t**) ptr)++;
108    }
109    if (_pw_likely(char_size == 3)) {
110        char32_t c = *(*ptr)++;
111        c |= *(*ptr)++ << 8;
112        c |= *(*ptr)++ << 16;
113        return c;
114    }
115    _pw_panic_bad_char_size(char_size);
116}
117
118static inline char32_t _pw_prev_char(uint8_t** ptr, uint8_t char_size)
119{
120    if (_pw_likely(char_size == 1)) {
121        return *(--*ptr);
122    }
123    if (_pw_likely(char_size == 2)) {
124        return *(--* (uint16_t**) ptr);
125    }
126    if (_pw_likely(char_size == 0)) {
127        return _pw_decode_utf8_char_reverse(ptr);
128    }
129    if (_pw_likely(char_size == 4)) {
130        return *(--* (char32_t**) ptr);
131    }
132    if (_pw_likely(char_size == 3)) {
133        uint8_t* p = *ptr;
134        p -= 3;
135        *ptr = p;
136        char32_t c = *p++;
137        c |= (*p++) << 8;
138        c |= (*p) << 16;
139        return c;
140    }
141    _pw_panic_bad_char_size(char_size);
142}
143
144static inline unsigned _pw_put_char(uint8_t* ptr, char32_t chr, uint8_t char_size)
145{
146    if (_pw_likely(char_size == 1)) {
147        *ptr = (uint8_t) chr;
148    } else if (_pw_likely(char_size == 2)) {
149        *((uint16_t*) ptr) = (uint16_t) chr;
150    } else if (_pw_likely(char_size == 0)) {
151        return pw_char32_to_utf8(chr, (char*) ptr);
152    } else if (_pw_likely(char_size == 4)) {
153        *((char32_t*) ptr) = chr;
154    } else if (_pw_likely(char_size == 3)) {
155        *ptr++ = (uint8_t) chr; chr >>= 8;
156        *ptr++ = (uint8_t) chr; chr >>= 8;
157        *ptr = (uint8_t) chr;
158    } else {
159        _pw_panic_bad_char_size(char_size);
160    }
161    return char_size;
162}
163
164
165/****************************************************************
166 * String iterator.
167 *
168 * The design is TBD
169 */
170
171PW_STRUCT(PwStringIter) {
172    uint8_t* start_ptr;
173    uint8_t* end_ptr;
174    uint8_t* current_ptr;
175    uint8_t char_size;
176};
177
178static inline void _pw_string_iter(PwValuePtr str, PwStringIter* iter)
179{
180    pw_hard_assert(pw_is_string(str));
181    iter->start_ptr = iter->current_ptr = _pw_string_start_end(str, &iter->end_ptr);
182    iter->char_size = str->str_params.char_size;
183}
184
185static inline char32_t _pw_string_iter_current(PwStringIter* iter)
186{
187    return _pw_get_char(iter->current_ptr, iter->char_size);
188}
189
190static inline bool _pw_string_iter_next(PwStringIter* iter, char32_t* c)
191{
192    if (iter->current_ptr < iter->end_ptr) {
193        *c =_pw_next_char(&iter->current_ptr, iter->char_size);
194        return true;
195    } else {
196        return false;
197    }
198}
199
200static inline bool _pw_string_iter_prev(PwStringIter* iter, char32_t* c)
201{
202    if (iter->current_ptr > iter->start_ptr) {
203        *c = _pw_prev_char(&iter->current_ptr, iter->char_size);
204        return true;
205    } else {
206        return false;
207    }
208}
209
210
211/****************************************************************
212 * Basic functions
213 */
214
215char32_t pw_char_at(PwValuePtr str, unsigned position);
216/*
217 * Return character at `position`.
218 * If position is beyond end of line return 0.
219 */
220
221// check if `index` is within string length
222#define pw_string_index_valid(str, index) ((index) < pw_strlen(str))
223
224static inline unsigned pw_strlen(PwValuePtr str)
225/*
226 * Return length of string.
227 */
228{
229    pw_hard_assert(pw_is_string(str));
230    if (str->str_params.integral) {
231        return str->integral_length;
232    } else {
233        return str->length;
234    }
235}
236
237[[nodiscard]] bool pw_substr(PwValuePtr str, unsigned start_pos, unsigned end_pos, PwValuePtr result);
238/*
239 * Get substring from `start_pos` to `end_pos`.
240 */
241
242[[nodiscard]] bool pw_string_erase(PwValuePtr str, unsigned start_pos, unsigned end_pos);
243/*
244 * Erase characters from `start_pos` to `end_pos`.
245 * This may make a copy of string, so checking return value is mandatory.
246 */
247
248[[nodiscard]] bool pw_string_truncate(PwValuePtr str, unsigned position);
249/*
250 * Truncate string at given `position`.
251 * This may make a copy of string, so checking return value is mandatory.
252 */
253
254[[ nodiscard]] bool pw_strchr(PwValuePtr str, char32_t chr, unsigned start_pos, unsigned* result);
255/*
256 * Find first occurence of `chr` in `str` starting from `start_pos`.
257 *
258 * Return true if character is found and write its position to `result`.
259 * `result` can be nullptr if position is not needed and the function
260 * is called just to check if `chr` is in `str`.
261 */
262
263[[ nodiscard]] bool pw_strchr2(PwValuePtr str, char32_t chr, unsigned start_pos, unsigned* result, uint8_t* max_char_size);
264/*
265 * Same as `pw_strchr`, but also return maximal char size.
266 */
267
268[[ nodiscard]] bool pw_strrchr(PwValuePtr str, char32_t chr, unsigned end_pos, unsigned* result);
269/*
270 * Find last occurence of `chr` in `str` starting from `end_pos`.
271 *
272 * For the first search `end_pos` should be UINT_MAX.
273 *
274 * Return true if character is found and write its position to `result`.
275 * `result` can be nullptr if position is not needed and the function
276 * is called just to check if `chr` is in `str`.
277 */
278
279[[ nodiscard]] bool pw_strrchr2(PwValuePtr str, char32_t chr, unsigned end_pos, unsigned* result, uint8_t* max_char_size);
280/*
281 * Same as `pw_strrchr`, but also return maximal char size.
282 */
283
284[[ nodiscard]] bool pw_strchri(PwValuePtr str, char32_t chr, unsigned start_pos, unsigned* result);
285/*
286 * Find first case-insensitive occurence of `chr` in `str` starting from `start_pos`.
287 *
288 * Return true if character is found and write its position to `result`.
289 * `result` can be nullptr if position is not needed and the function
290 * is called just to check if `chr` is in `str`.
291 */
292
293[[nodiscard]] bool pw_string_lstrip(PwValuePtr str);
294[[nodiscard]] bool pw_string_rstrip(PwValuePtr str);
295[[nodiscard]] bool pw_string_strip(PwValuePtr str);
296
297[[nodiscard]] bool pw_string_lstrip_chars(PwValuePtr str, char32_t* chars);
298[[nodiscard]] bool pw_string_rstrip_chars(PwValuePtr str, char32_t* chars);
299[[nodiscard]] bool pw_string_strip_chars(PwValuePtr str, char32_t* chars);
300
301
302[[nodiscard]] bool pw_string_lower(PwValuePtr str);
303[[nodiscard]] bool pw_string_upper(PwValuePtr str);
304
305
306[[nodiscard]] bool pw_string_reverse(PwValuePtr str);
307
308
309#define pw_strcat(result, str, ...)  \
310    _pw_strcat_va((result), (str) __VA_OPT__(,) __VA_ARGS__, PwVaEnd())
311
312[[nodiscard]] bool _pw_strcat_va(PwValuePtr result, ...);
313[[nodiscard]] bool _pw_strcat_ap(PwValuePtr result, va_list ap);
314
315unsigned pw_string_skip_spaces(PwValuePtr str, unsigned position);
316/*
317 * Find position of the first non-space character starting from `position`.
318 * If non-space character is not found, the length is returned.
319 */
320
321unsigned pw_string_skip_chars(PwValuePtr str, unsigned position, char32_t* skipchars);
322/*
323 * Find position of the first character not belonging to `skipchars` starting from `position`.
324 * If no `skipchars` encountered, the length is returned.
325 */
326
327bool pw_string_isspace(PwValuePtr str);
328bool pw_string_isdigit(PwValuePtr str);
329bool pw_string_is_ascii_digit(PwValuePtr str);
330/*
331 * Return true if `str` is not empty and contains all digits.
332 */
333
334/****************************************************************
335 * Append functions
336 *
337 * Characters, PwValues, and null-terminated C strings:
338 *
339 *     pw_string_append(dest_string, chr);
340 *     pw_string_append(dest_string, src_string);
341 *
342 * Raw data (char*, char8_t*, char32_t*):
343 *
344 *     pw_string_append2(dest_string, start_ptr, end_ptr);
345 *
346 * C strings are slowest because strlen is called to obtain end_ptr.
347 */
348
349#define pw_string_append(dest, src, ...) _Generic((src),  \
350                  char: _pw_string_append_c32,     \
351               uint8_t: _pw_string_append_c32,     \
352              char32_t: _pw_string_append_c32,     \
353                   int: _pw_string_append_c32,     \
354                 char*: _pw_string_append1_ascii,  \
355              char8_t*: _pw_string_append1_utf8,   \
356             char32_t*: _pw_string_append1_utf32,  \
357            PwValuePtr: _pw_string_append          \
358    )((dest), (src) __VA_OPT__(,) __VA_ARGS__)
359
360[[nodiscard]] bool _pw_string_append_c32  (PwValuePtr dest, char32_t   c);
361[[nodiscard]] bool _pw_string_append      (PwValuePtr dest, PwValuePtr src);
362[[nodiscard]] bool _pw_string_append_ascii(PwValuePtr dest, char*      start_ptr, char*     end_ptr);
363[[nodiscard]] bool _pw_string_append_utf8 (PwValuePtr dest, char8_t*   start_ptr, char8_t*  end_ptr);
364[[nodiscard]] bool _pw_string_append_utf32(PwValuePtr dest, char32_t*  start_ptr, char32_t* end_ptr);
365
366[[nodiscard]] static inline bool _pw_string_append1_ascii(PwValuePtr dest, char*     start_ptr) { return _pw_string_append_ascii(dest, start_ptr, nullptr); }
367[[nodiscard]] static inline bool _pw_string_append1_utf8 (PwValuePtr dest, char8_t*  start_ptr) { return _pw_string_append_utf8 (dest, start_ptr, nullptr); }
368[[nodiscard]] static inline bool _pw_string_append1_utf32(PwValuePtr dest, char32_t* start_ptr) { return _pw_string_append_utf32(dest, start_ptr, nullptr); }
369
370#define pw_string_append2(dest, src, ...) _Generic((src),  \
371                 char*: _pw_string_append_ascii,  \
372              char8_t*: _pw_string_append_utf8,   \
373             char32_t*: _pw_string_append_utf32   \
374    )((dest), (src) __VA_OPT__(,) __VA_ARGS__)
375
376
377/****************************************************************
378 * Insert functions
379 * TODO other types
380 */
381
382[[nodiscard]] bool pw_string_insert_chars(PwValuePtr str, unsigned position, char32_t chr, unsigned n);
383
384
385#define pw_string_insert(dest, position, src, ...) _Generic((src),  \
386                  char: _pw_string_insert_c32,     \
387               uint8_t: _pw_string_insert_c32,     \
388              char32_t: _pw_string_insert_c32,     \
389                   int: _pw_string_insert_c32,     \
390                 char*: _pw_string_insert1_ascii,  \
391              char8_t*: _pw_string_insert1_utf8,   \
392             char32_t*: _pw_string_insert1_utf32,  \
393            PwValuePtr: _pw_string_insert          \
394    )((dest), (position), (src) __VA_OPT__(,) __VA_ARGS__)
395
396[[nodiscard]] static inline bool _pw_string_insert_c32(PwValuePtr dest, unsigned position, char32_t c)
397{
398    return pw_string_insert_chars(dest, position, c, 1);
399}
400[[nodiscard]] bool _pw_string_insert      (PwValuePtr dest, unsigned position, PwValuePtr src);
401[[nodiscard]] bool _pw_string_insert_ascii(PwValuePtr dest, unsigned position, char*      start_ptr, char*     end_ptr);
402[[nodiscard]] bool _pw_string_insert_utf8 (PwValuePtr dest, unsigned position, char8_t*   start_ptr, char8_t*  end_ptr);
403[[nodiscard]] bool _pw_string_insert_utf32(PwValuePtr dest, unsigned position, char32_t*  start_ptr, char32_t* end_ptr);
404
405#define pw_string_insert2(dest, position, src, ...) _Generic((src),  \
406                 char*: _pw_string_insert_ascii,  \
407              char8_t*: _pw_string_insert_utf8,   \
408             char32_t*: _pw_string_insert_utf32   \
409    )((dest), (position), (src) __VA_OPT__(,) __VA_ARGS__)
410
411[[nodiscard]] static inline bool _pw_string_insert1_ascii(PwValuePtr dest, unsigned position, char*      start_ptr) { return _pw_string_insert_ascii(dest, position, start_ptr, nullptr); }
412[[nodiscard]] static inline bool _pw_string_insert1_utf8 (PwValuePtr dest, unsigned position, char8_t*   start_ptr) { return _pw_string_insert_utf8 (dest, position, start_ptr, nullptr); }
413[[nodiscard]] static inline bool _pw_string_insert1_utf32(PwValuePtr dest, unsigned position, char32_t*  start_ptr) { return _pw_string_insert_utf32(dest, position, start_ptr, nullptr); }
414
415
416/****************************************************************
417 * Append substring.
418 *
419 * Append `src` substring starting from `src_start_pos` to `src_end_pos`.
420 */
421
422[[nodiscard]] bool pw_string_append_substring(PwValuePtr dest, PwValuePtr src, unsigned src_start_pos, unsigned src_end_pos);
423
424
425/****************************************************************
426 * String constructors
427 */
428
429[[nodiscard]] bool pw_create_empty_string(unsigned capacity, uint8_t char_size, PwValuePtr result);
430
431#define pw_create_string(result, initializer) _Generic((initializer),  \
432                 char*: _pw_create_string_ascii, \
433              char8_t*: _pw_create_string_utf8,  \
434             char32_t*: _pw_create_string_utf32, \
435            PwValuePtr: _pw_create_string        \
436    )((result), (initializer))
437
438#define CREATE_STR_IMPL  \
439    {  \
440        pw_destroy(result);  \
441        *result = PwString();  \
442        return pw_string_append(result, initializer);  \
443    }
444
445[[nodiscard]] static inline bool _pw_create_string      (PwValuePtr result, PwValuePtr initializer) CREATE_STR_IMPL
446[[nodiscard]] static inline bool _pw_create_string_ascii(PwValuePtr result, char*      initializer) CREATE_STR_IMPL
447[[nodiscard]] static inline bool _pw_create_string_utf8 (PwValuePtr result, char8_t*   initializer) CREATE_STR_IMPL
448[[nodiscard]] static inline bool _pw_create_string_utf32(PwValuePtr result, char32_t*  initializer) CREATE_STR_IMPL
449
450#undef CREATE_STR_IMPL
451
452/****************************************************************
453 * Case-insensitive comparison.
454 */
455
456#define pw_string_eqi(a, b) _Generic((b),  \
457             char*: _pw_string_eqi_ascii,  \
458          char8_t*: _pw_string_eqi_utf8,   \
459         char32_t*: _pw_string_eqi_utf32,  \
460        PwValuePtr: _pw_string_eqi         \
461    )((a), (b))
462
463[[nodiscard]] bool _pw_string_eqi  (PwValuePtr a, PwValuePtr b);
464[[nodiscard]] bool _pw_string_eqi_z(PwValuePtr a, void* b, uint8_t b_char_size);
465
466[[nodiscard]] static inline bool _pw_string_eqi_ascii(PwValuePtr a, char* b)
467{
468    return _pw_string_eqi_z(a, b, 1);
469}
470[[nodiscard]] static inline bool _pw_string_eqi_utf8 (PwValuePtr a, char8_t* b)
471{
472    return _pw_string_eqi_z(a, b, 0);
473}
474[[nodiscard]] static inline bool _pw_string_eqi_utf32(PwValuePtr a, char32_t* b)
475{
476    return _pw_string_eqi_z(a, b, 4);
477}
478
479/****************************************************************
480 * Substring comparison functions.
481 *
482 * Compare `str_a` from `start_pos` to `end_pos` with `str_b`.
483 */
484
485#define pw_substring_eq(a, start_pos, end_pos, b) _Generic((b), \
486             char*: _pw_substring_eq_ascii,  \
487          char8_t*: _pw_substring_eq_utf8,   \
488         char32_t*: _pw_substring_eq_utf32,  \
489        PwValuePtr: _pw_substring_eq         \
490    )((a), (start_pos), (end_pos), (b))
491
492[[nodiscard]] bool _pw_substring_eq  (PwValuePtr a, unsigned start_pos, unsigned end_pos, PwValuePtr b);
493[[nodiscard]] bool _pw_substring_eq_z(PwValuePtr a, unsigned start_pos, unsigned end_pos, void* b, uint8_t b_char_size);
494
495[[nodiscard]] static inline bool _pw_substring_eq_ascii(PwValuePtr a, unsigned start_pos, unsigned end_pos, char* b)
496{
497    return _pw_substring_eq_z(a, start_pos, end_pos, b, 1);
498}
499[[nodiscard]] static inline bool _pw_substring_eq_utf8 (PwValuePtr a, unsigned start_pos, unsigned end_pos, char8_t* b)
500{
501    return _pw_substring_eq_z(a, start_pos, end_pos, b, 0);
502}
503[[nodiscard]] static inline bool _pw_substring_eq_utf32(PwValuePtr a, unsigned start_pos, unsigned end_pos, char32_t* b)
504{
505    return _pw_substring_eq_z(a, start_pos, end_pos, b, 4);
506}
507
508
509#define pw_substring_eqi(a, start_pos, end_pos, b) _Generic((b), \
510             char*: _pw_substring_eqi_ascii,  \
511          char8_t*: _pw_substring_eqi_utf8,   \
512         char32_t*: _pw_substring_eqi_utf32,  \
513        PwValuePtr: _pw_substring_eqi         \
514    )((a), (start_pos), (end_pos), (b))
515
516[[nodiscard]] bool _pw_substring_eqi  (PwValuePtr a, unsigned start_pos, unsigned end_pos, PwValuePtr b);
517[[nodiscard]] bool _pw_substring_eqi_z(PwValuePtr a, unsigned start_pos, unsigned end_pos, void* b, uint8_t b_char_size);
518
519[[nodiscard]] static inline bool _pw_substring_eqi_ascii(PwValuePtr a, unsigned start_pos, unsigned end_pos, char* b)
520{
521    return _pw_substring_eqi_z(a, start_pos, end_pos, b, 1);
522}
523[[nodiscard]] static inline bool _pw_substring_eqi_utf8 (PwValuePtr a, unsigned start_pos, unsigned end_pos, char8_t* b)
524{
525    return _pw_substring_eqi_z(a, start_pos, end_pos, b, 0);
526}
527[[nodiscard]] static inline bool _pw_substring_eqi_utf32(PwValuePtr a, unsigned start_pos, unsigned end_pos, char32_t* b)
528{
529    return _pw_substring_eqi_z(a, start_pos, end_pos, b, 4);
530}
531
532
533#define pw_startswith(str, prefix) _Generic((prefix), \
534               int: _pw_startswith_c32,    \
535          char32_t: _pw_startswith_c32,    \
536             char*: _pw_startswith_ascii,  \
537          char8_t*: _pw_startswith_utf8,   \
538         char32_t*: _pw_startswith_utf32,  \
539        PwValuePtr: _pw_startswith         \
540    )((str), (prefix))
541
542[[nodiscard]] bool _pw_startswith_c32(PwValuePtr str, char32_t   prefix);
543[[nodiscard]] bool _pw_startswith    (PwValuePtr str, PwValuePtr prefix);
544[[nodiscard]] bool _pw_startswith_z  (PwValuePtr str, void*      prefix, uint8_t prefix_char_size);
545
546[[nodiscard]] static inline bool _pw_startswith_ascii(PwValuePtr str, char* prefix)
547{
548    return _pw_startswith_z(str, prefix, 1);
549}
550[[nodiscard]] static inline bool _pw_startswith_utf8(PwValuePtr str, char8_t* prefix)
551{
552    return _pw_startswith_z(str, prefix, 0);
553}
554[[nodiscard]] static inline bool _pw_startswith_utf32(PwValuePtr str, char32_t* prefix)
555{
556    return _pw_startswith_z(str, prefix, 4);
557}
558
559
560#define pw_startswithi(str, prefix) _Generic((prefix), \
561               int: _pw_startswithi_c32,    \
562          char32_t: _pw_startswithi_c32,    \
563             char*: _pw_startswithi_ascii,  \
564          char8_t*: _pw_startswithi_utf8,   \
565         char32_t*: _pw_startswithi_utf32,  \
566        PwValuePtr: _pw_startswithi         \
567    )((str), (prefix))
568
569[[nodiscard]] bool _pw_startswithi_c32(PwValuePtr str, char32_t   prefix);
570[[nodiscard]] bool _pw_startswithi    (PwValuePtr str, PwValuePtr prefix);
571[[nodiscard]] bool _pw_startswithi_z  (PwValuePtr str, void*      prefix, uint8_t prefix_char_size);
572
573[[nodiscard]] static inline bool _pw_startswithi_ascii(PwValuePtr str, char* prefix)
574{
575    return _pw_startswithi_z(str, prefix, 1);
576}
577[[nodiscard]] static inline bool _pw_startswithi_utf8(PwValuePtr str, char8_t* prefix)
578{
579    return _pw_startswithi_z(str, prefix, 0);
580}
581[[nodiscard]] static inline bool _pw_startswithi_utf32(PwValuePtr str, char32_t* prefix)
582{
583    return _pw_startswithi_z(str, prefix, 4);
584}
585
586
587#define pw_endswith(str, suffix) _Generic((suffix), \
588               int: _pw_endswith_c32,    \
589          char32_t: _pw_endswith_c32,    \
590             char*: _pw_endswith_ascii,  \
591          char8_t*: _pw_endswith_utf8,   \
592         char32_t*: _pw_endswith_utf32,  \
593        PwValuePtr: _pw_endswith         \
594    )((str), (suffix))
595
596[[nodiscard]] bool _pw_endswith_c32(PwValuePtr str, char32_t   suffix);
597[[nodiscard]] bool _pw_endswith    (PwValuePtr str, PwValuePtr suffix);
598[[nodiscard]] bool _pw_endswith_z  (PwValuePtr str, void*      suffix, uint8_t suffix_char_size);
599
600[[nodiscard]] static inline bool _pw_endswith_ascii(PwValuePtr str, char* suffix)
601{
602    return _pw_endswith_z(str, suffix, 1);
603}
604[[nodiscard]] static inline bool _pw_endswith_utf8(PwValuePtr str, char8_t* suffix)
605{
606    return _pw_endswith_z(str, suffix, 0);
607}
608[[nodiscard]] static inline bool _pw_endswith_utf32(PwValuePtr str, char32_t* suffix)
609{
610    return _pw_endswith_z(str, suffix, 4);
611}
612
613
614#define pw_endswithi(str, suffix) _Generic((suffix), \
615               int: _pw_endswithi_c32,    \
616          char32_t: _pw_endswithi_c32,    \
617             char*: _pw_endswithi_ascii,  \
618          char8_t*: _pw_endswithi_utf8,   \
619         char32_t*: _pw_endswithi_utf32,  \
620        PwValuePtr: _pw_endswithi         \
621    )((str), (suffix))
622
623[[nodiscard]] bool _pw_endswithi_c32(PwValuePtr str, char32_t   suffix);
624[[nodiscard]] bool _pw_endswithi    (PwValuePtr str, PwValuePtr suffix);
625[[nodiscard]] bool _pw_endswithi_z  (PwValuePtr str, void*      suffix, uint8_t suffix_char_size);
626
627[[nodiscard]] static inline bool _pw_endswithi_ascii(PwValuePtr str, char* suffix)
628{
629    return _pw_endswithi_z(str, suffix, 1);
630}
631[[nodiscard]] static inline bool _pw_endswithi_utf8(PwValuePtr str, char8_t* suffix)
632{
633    return _pw_endswithi_z(str, suffix, 0);
634}
635[[nodiscard]] static inline bool _pw_endswithi_utf32(PwValuePtr str, char32_t* suffix)
636{
637    return _pw_endswithi_z(str, suffix, 4);
638}
639
640
641/****************************************************************
642 * Substring scanning functions.
643 *
644 * Return true if substring is found, false if not.
645 */
646
647#define pw_strstr(str, substr, start_pos, pos) _Generic((substr), \
648                 char*: _pw_strstr_ascii,  \
649              char8_t*: _pw_strstr_utf8,   \
650             char32_t*: _pw_strstr_utf32,  \
651            PwValuePtr: _pw_strstr         \
652    )((str), (substr), (start_pos), (pos))
653
654[[nodiscard]] bool _pw_strstr(PwValuePtr str, PwValuePtr substr, unsigned start_pos, unsigned* pos);
655
656[[nodiscard]] bool _pw_strstr_z(PwValuePtr str, void* substr, uint8_t substr_char_size, unsigned start_pos, unsigned* pos);
657
658[[nodiscard]] static inline bool _pw_strstr_ascii(PwValuePtr str, char* substr, unsigned start_pos, unsigned* pos)
659{
660    return _pw_strstr_z(str, substr, 1, start_pos, pos);
661}
662[[nodiscard]] static inline bool _pw_strstr_utf8 (PwValuePtr str, char8_t* substr, unsigned start_pos, unsigned* pos)
663{
664    return _pw_strstr_z(str, substr, 0, start_pos, pos);
665}
666[[nodiscard]] static inline bool _pw_strstr_utf32(PwValuePtr str, char32_t* substr, unsigned start_pos, unsigned* pos)
667{
668    return _pw_strstr_z(str, substr, 4, start_pos, pos);
669}
670
671#define pw_strstri(str, substr, pos) _Generic((substr), \
672                 char*: _pw_strstri_ascii,  \
673              char8_t*: _pw_strstri_utf8,   \
674             char32_t*: _pw_strstri_utf32,  \
675            PwValuePtr: _pw_strstri         \
676    )((str), (substr), (start_pos), (pos))
677
678[[nodiscard]] bool _pw_strstri      (PwValuePtr str, PwValuePtr substr, unsigned start_pos, unsigned* pos);
679[[nodiscard]] bool _pw_strstri_ascii(PwValuePtr str, char*      substr, unsigned start_pos, unsigned* pos);
680[[nodiscard]] bool _pw_strstri_utf8 (PwValuePtr str, char8_t*   substr, unsigned start_pos, unsigned* pos);
681[[nodiscard]] bool _pw_strstri_utf32(PwValuePtr str, char32_t*  substr, unsigned start_pos, unsigned* pos);
682
683
684/****************************************************************
685 * Split functions.
686 * Return array of strings.
687 * maxsplit == 0 imposes no limit
688 */
689
690[[nodiscard]] bool _pw_string_split     (PwValuePtr str, /* split by spaces */ unsigned maxsplit, PwValuePtr result, uint16_t result_type);
691[[nodiscard]] bool _pw_string_split_chr (PwValuePtr str, char32_t splitter,    unsigned maxsplit, PwValuePtr result, uint16_t result_type);
692[[nodiscard]] bool _pw_string_rsplit    (PwValuePtr str, /* split by spaces */ unsigned maxsplit, PwValuePtr result, uint16_t result_type);
693[[nodiscard]] bool _pw_string_rsplit_chr(PwValuePtr str, char32_t splitter,    unsigned maxsplit, PwValuePtr result, uint16_t result_type);
694
695#define pw_string_split(...)       _pw_string_split     (__VA_ARGS__, PwTypeId_BasicArray)
696#define pw_string_split_chr(...)   _pw_string_split_chr (__VA_ARGS__, PwTypeId_BasicArray)
697#define pw_string_rsplit(...)      _pw_string_rsplit    (__VA_ARGS__, PwTypeId_BasicArray)
698#define pw_string_rsplit_chr(...)  _pw_string_rsplit_chr(__VA_ARGS__, PwTypeId_BasicArray)
699
700#define _pw_string_split_any(str, splitters, maxsplit, result_type) _Generic((splitters),  \
701                 char*: _pw_string_split_any_ascii,  \
702              char8_t*: _pw_string_split_any_utf8,   \
703             char32_t*: _pw_string_split_any_utf32,  \
704            PwValuePtr: _pw_string_split_any_pw      \
705    )((str), (splitters), (maxsplit), (result_type))
706
707#define pw_string_split_any(...)  _pw_string_split_any(__VA_ARGS__, PwTypeId_BasicArray)
708
709[[nodiscard]] bool _pw_string_split_any_pw   (PwValuePtr str, PwValuePtr splitters, unsigned maxsplit, PwValuePtr result, uint16_t result_type);
710[[nodiscard]] bool _pw_string_split_any_ascii(PwValuePtr str, char*      splitters, unsigned maxsplit, PwValuePtr result, uint16_t result_type);
711[[nodiscard]] bool _pw_string_split_any_utf8 (PwValuePtr str, char8_t*   splitters, unsigned maxsplit, PwValuePtr result, uint16_t result_type);
712[[nodiscard]] bool _pw_string_split_any_utf32(PwValuePtr str, char32_t*  splitters, unsigned maxsplit, PwValuePtr result, uint16_t result_type);
713
714#define _pw_string_rsplit_any(str, splitters, maxsplit, result_type) _Generic((splitters),  \
715                 char*: _pw_string_rsplit_any_ascii,  \
716              char8_t*: _pw_string_rsplit_any_utf8,   \
717             char32_t*: _pw_string_rsplit_any_utf32,  \
718            PwValuePtr: _pw_string_rsplit_any_pw      \
719    )((str), (splitters), (maxsplit), (result_type))
720
721#define pw_string_rsplit_any(...)  _pw_string_rsplit_any(__VA_ARGS__, PwTypeId_BasicArray)
722
723[[nodiscard]] bool _pw_string_rsplit_any_pw   (PwValuePtr str, PwValuePtr splitters, unsigned maxsplit, PwValuePtr result, uint16_t result_type);
724[[nodiscard]] bool _pw_string_rsplit_any_ascii(PwValuePtr str, char*      splitters, unsigned maxsplit, PwValuePtr result, uint16_t result_type);
725[[nodiscard]] bool _pw_string_rsplit_any_utf8 (PwValuePtr str, char8_t*   splitters, unsigned maxsplit, PwValuePtr result, uint16_t result_type);
726[[nodiscard]] bool _pw_string_rsplit_any_utf32(PwValuePtr str, char32_t*  splitters, unsigned maxsplit, PwValuePtr result, uint16_t result_type);
727
728#define _pw_string_split_substr(str, splitter, maxsplit, result_type) _Generic((splitter),  \
729                 char*: _pw_string_split_substr_ascii,  \
730              char8_t*: _pw_string_split_substr_utf8,   \
731             char32_t*: _pw_string_split_substr_utf32,  \
732            PwValuePtr: _pw_string_split_substr_pw      \
733    )((str), (splitter), (maxsplit), (result_type))
734
735#define pw_string_split_substr(...)  _pw_string_split_substr(__VA_ARGS__, PwTypeId_BasicArray)
736
737[[nodiscard]] bool _pw_string_split_substr_pw   (PwValuePtr str, PwValuePtr splitter, unsigned maxsplit, PwValuePtr result, uint16_t result_type);
738[[nodiscard]] bool _pw_string_split_substr_ascii(PwValuePtr str, char*      splitter, unsigned maxsplit, PwValuePtr result, uint16_t result_type);
739[[nodiscard]] bool _pw_string_split_substr_utf8 (PwValuePtr str, char8_t*   splitter, unsigned maxsplit, PwValuePtr result, uint16_t result_type);
740[[nodiscard]] bool _pw_string_split_substr_utf32(PwValuePtr str, char32_t*  splitter, unsigned maxsplit, PwValuePtr result, uint16_t result_type);
741
742#define _pw_string_split_substri(str, splitter, maxsplit, result_type) _Generic((splitter),  \
743                 char*: _pw_string_split_substri_ascii,  \
744              char8_t*: _pw_string_split_substri_utf8,   \
745             char32_t*: _pw_string_split_substri_utf32,  \
746            PwValuePtr: _pw_string_split_substri_pw      \
747    )((str), (splitter), (maxsplit), (result_type))
748
749#define pw_string_split_substri(...)  _pw_string_split_substri(__VA_ARGS__, PwTypeId_BasicArray)
750
751[[nodiscard]] bool _pw_string_split_substri_pw   (PwValuePtr str, PwValuePtr splitter, unsigned maxsplit, PwValuePtr result, uint16_t result_type);
752[[nodiscard]] bool _pw_string_split_substri_ascii(PwValuePtr str, char*      splitter, unsigned maxsplit, PwValuePtr result, uint16_t result_type);
753[[nodiscard]] bool _pw_string_split_substri_utf8 (PwValuePtr str, char8_t*   splitter, unsigned maxsplit, PwValuePtr result, uint16_t result_type);
754[[nodiscard]] bool _pw_string_split_substri_utf32(PwValuePtr str, char32_t*  splitter, unsigned maxsplit, PwValuePtr result, uint16_t result_type);
755
756#define _pw_string_rsplit_substr(str, splitter, maxsplit, result_type) _Generic((splitter),  \
757                 char*: _pw_string_rsplit_substr_ascii,  \
758              char8_t*: _pw_string_rsplit_substr_utf8,   \
759             char32_t*: _pw_string_rsplit_substr_utf32,  \
760            PwValuePtr: _pw_string_rsplit_substr_pw      \
761    )((str), (splitter), (maxsplit), (result_type))
762
763#define pw_string_rsplit_substr(...)  _pw_string_rsplit_substr(__VA_ARGS__, PwTypeId_BasicArray)
764
765[[nodiscard]] bool _pw_string_rsplit_substr_pw   (PwValuePtr str, PwValuePtr splitter, unsigned maxsplit, PwValuePtr result, uint16_t result_type);
766[[nodiscard]] bool _pw_string_rsplit_substr_ascii(PwValuePtr str, char*      splitter, unsigned maxsplit, PwValuePtr result, uint16_t result_type);
767[[nodiscard]] bool _pw_string_rsplit_substr_utf8 (PwValuePtr str, char8_t*   splitter, unsigned maxsplit, PwValuePtr result, uint16_t result_type);
768[[nodiscard]] bool _pw_string_rsplit_substr_utf32(PwValuePtr str, char32_t*  splitter, unsigned maxsplit, PwValuePtr result, uint16_t result_type);
769
770#define _pw_string_rsplit_substri(str, splitter, maxsplit, result_type) _Generic((splitter),  \
771                 char*: _pw_string_rsplit_substri_ascii,  \
772              char8_t*: _pw_string_rsplit_substri_utf8,   \
773             char32_t*: _pw_string_rsplit_substri_utf32,  \
774            PwValuePtr: _pw_string_rsplit_substri_pw      \
775    )((str), (splitter), (maxsplit), (result_type))
776
777#define pw_string_rsplit_substri(...)  _pw_string_rsplit_substri(__VA_ARGS__, PwTypeId_BasicArray)
778
779[[nodiscard]] bool _pw_string_rsplit_substri_pw   (PwValuePtr str, PwValuePtr splitter, unsigned maxsplit, PwValuePtr result, uint16_t result_type);
780[[nodiscard]] bool _pw_string_rsplit_substri_ascii(PwValuePtr str, char*      splitter, unsigned maxsplit, PwValuePtr result, uint16_t result_type);
781[[nodiscard]] bool _pw_string_rsplit_substri_utf8 (PwValuePtr str, char8_t*   splitter, unsigned maxsplit, PwValuePtr result, uint16_t result_type);
782[[nodiscard]] bool _pw_string_rsplit_substri_utf32(PwValuePtr str, char32_t*  splitter, unsigned maxsplit, PwValuePtr result, uint16_t result_type);
783
784
785/****************************************************************
786 * Replacement functions.
787 */
788
789#define pw_string_replace_all(str, substr, replacement) _Generic((substr),  \
790              char32_t: _pw_string_replace_all_char32, \
791                 char*: _pw_string_replace_all_ascii,  \
792              char8_t*: _pw_string_replace_all_utf8,   \
793             char32_t*: _pw_string_replace_all_utf32,  \
794            PwValuePtr: _pw_string_replace_all         \
795    )((str), (substr), (replacement))
796
797[[nodiscard]] bool _pw_string_replace_all(PwValuePtr str, PwValuePtr substr, PwValuePtr replacement);
798
799[[nodiscard]] static inline bool _pw_string_replace_all_char32(PwValuePtr str, char32_t substr, char32_t replacement)
800{
801    char32_t s[] = {substr, 0};
802    _PwValue sub = PwStaticStringUtf32(s);
803    char32_t r[] = {replacement, 0};
804    _PwValue repl = PwStaticStringUtf32(r);
805    return _pw_string_replace_all(str, &sub, &repl);
806}
807
808[[nodiscard]] static inline bool _pw_string_replace_all_ascii(PwValuePtr str, char* substr, char* replacement)
809{
810    _PwValue sub = PwStaticString(substr);
811    _PwValue repl = PwStaticString(replacement);
812    return _pw_string_replace_all(str, &sub, &repl);
813}
814
815[[nodiscard]] static inline bool _pw_string_replace_all_utf8(PwValuePtr str, char8_t*  substr, char8_t* replacement)
816{
817    PwValue sub = PW_NULL;
818    if (!pw_create_string(&sub, substr)) {
819        return false;
820    }
821    PwValue repl = PW_NULL;
822    if (!pw_create_string(&repl, replacement)) {
823        return false;
824    }
825    return _pw_string_replace_all(str, &sub, &repl);
826}
827
828[[nodiscard]] static inline bool _pw_string_replace_all_utf32(PwValuePtr str, char32_t* substr, char32_t* replacement)
829{
830    _PwValue sub = PwStaticStringUtf32(substr);
831    _PwValue repl = PwStaticStringUtf32(replacement);
832    return _pw_string_replace_all(str, &sub, &repl);
833}
834
835
836/****************************************************************
837 * printf functions
838 */
839
840[[nodiscard]] bool pw_sprintf(PwValuePtr result, char* format, ...);
841[[nodiscard]] bool pw_vsprintf(PwValuePtr result, char* format, va_list ap);
842/*
843 * printf to String
844 */
845
846[[nodiscard]] bool pw_aprintf(PwValuePtr result, PwMethod_Append_append_string_data* meth_append, char* format, ...);
847[[nodiscard]] bool pw_vaprintf(PwValuePtr result, PwMethod_Append_append_string_data* meth_append, char* format, va_list ap);
848/*
849 * printf appending to the result
850 */
851
852
853/****************************************************************
854 * C strings
855 */
856
857// somewhat ugly macro to define a local variable initialized with a copy of pw string:
858#define PW_CSTRING(variable_name, pw_str) \
859    char variable_name[pw_strlen_in_utf8(pw_str) + 1]; \
860    pw_string_to_utf8((pw_str), variable_name)
861
862
863/****************************************************************
864 * Indentation utilities
865 */
866
867[[nodiscard]] bool pw_indent(PwValuePtr lines, unsigned n);
868[[nodiscard]] bool pw_indent_rstrip(PwValuePtr lines, unsigned n);
869/*
870 * `lines` can be either string or array.
871 * The function indents lines in-place.
872 *
873 * `pw_indent_rstrip` also strips trailing spaces from each line.
874 */
875
876[[nodiscard]] bool pw_dedent(PwValuePtr lines);
877/*
878 * `lines` can be either string or array.
879 * The function dedents lines in-place.
880 *
881 * XXX the function treats tabs as single spaces.
882 */
883
884
885/****************************************************************
886 * Miscellaneous
887 */
888
889[[nodiscard]] bool pw_string_capitalize(PwValuePtr str);
890/*
891 * Capitalize `str` in-place. Make first character upper case and all the rest lower case.
892 *
893 */
894
895#ifdef __cplusplus
896}
897#endif