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