1#include "include/pw.h"
 2#include "include/pwlib/ctype.h"
 3#include "src/types/string/string_internal.h"
 4
 5#define SUBSTREQI(CHAR_TYPE, CHAR_SIZE)  \
 6    static uint8_t* strchri_##CHAR_SIZE(uint8_t* start_ptr, uint8_t* end_ptr, char32_t codepoint)  \
 7    {  \
 8        while (start_ptr < end_ptr) {  \
 9            if (codepoint == (char32_t) pw_char_lower(*((CHAR_TYPE*) start_ptr))) {  \
10                return start_ptr;  \
11            }  \
12            start_ptr += CHAR_SIZE;  \
13        }  \
14        return nullptr;  \
15    }
16SUBSTREQI(uint8_t,  1)
17SUBSTREQI(uint16_t, 2)
18SUBSTREQI(char32_t, 4)
19
20static uint8_t* strchri_3(uint8_t* start_ptr, uint8_t* end_ptr, char32_t codepoint)
21{
22    while (start_ptr < end_ptr) {
23        char32_t c = *start_ptr++;
24        c |= (*start_ptr++) << 8;
25        c |= (*start_ptr++) << 16;
26
27        if (codepoint == (char32_t) pw_char_lower(c)) {
28            return start_ptr - 3;
29        }
30    }
31    return nullptr;
32}
33
34StrChr _pw_strchri_variants[5] = {
35    nullptr,
36    strchri_1,
37    strchri_2,
38    strchri_3,
39    strchri_4
40};
41
42[[nodiscard]] bool pw_strchri(PwValuePtr str, char32_t chr, unsigned start_pos, unsigned* result)
43{
44    pw_assert(pw_is_string(str));
45    uint8_t char_size = str->str_params.char_size;
46    if (_pw_unlikely(char_size < calc_char_size(chr))) {
47        return false;
48    }
49    uint8_t* end_ptr;
50    uint8_t* start_ptr = _pw_string_start_end(str, &end_ptr) + start_pos * char_size;
51    if (_pw_unlikely(start_ptr >= end_ptr)) {
52        return false;
53    }
54    StrChr fn_strchri = _pw_strchri_variants[char_size];
55    uint8_t* char_ptr = fn_strchri(start_ptr, end_ptr, pw_char_lower(chr));
56    if (_pw_unlikely(!char_ptr)) {
57        return false;
58    }
59    if (result) {
60        *result = start_pos + (char_ptr - start_ptr) / char_size;
61    }
62    return true;
63}