1#include "include/pw.h"
  2#include "src/types/string/string_internal.h"
  3
  4#define SUBSTREQI(STR_CHAR_TYPE, STR_CHAR_SIZE, SUBSTR_CHAR_TYPE, SUBSTR_CHAR_SIZE)  \
  5    static bool substreqi_##STR_CHAR_SIZE##_##SUBSTR_CHAR_SIZE(uint8_t* str_start_ptr, uint8_t* substr_start_ptr, uint8_t* substr_end_ptr)  \
  6    {  \
  7        while (substr_start_ptr < substr_end_ptr) {  \
  8            if (pw_char_lower(*((STR_CHAR_TYPE*) str_start_ptr)) != pw_char_lower(*((SUBSTR_CHAR_TYPE*) substr_start_ptr))) {  \
  9                return false;  \
 10            }  \
 11            str_start_ptr += STR_CHAR_SIZE;  \
 12            substr_start_ptr += SUBSTR_CHAR_SIZE;  \
 13        }  \
 14        return true;  \
 15    }
 16SUBSTREQI(uint8_t,  1,  uint8_t,  1)
 17SUBSTREQI(uint8_t,  1,  uint16_t, 2)
 18SUBSTREQI(uint8_t,  1,  char32_t, 4)
 19SUBSTREQI(uint16_t, 2,  uint8_t,  1)
 20SUBSTREQI(uint16_t, 2,  uint16_t, 2)
 21SUBSTREQI(uint16_t, 2,  char32_t, 4)
 22SUBSTREQI(char32_t, 4,  uint8_t,  1)
 23SUBSTREQI(char32_t, 4,  uint16_t, 2)
 24SUBSTREQI(char32_t, 4,  char32_t, 4)
 25
 26#define SUBSTREQI_N_3(STR_CHAR_TYPE, STR_CHAR_SIZE)  \
 27    static bool substreqi_##STR_CHAR_SIZE##_3(uint8_t* str_start_ptr, uint8_t* substr_start_ptr, uint8_t* substr_end_ptr)  \
 28    {  \
 29        while (substr_start_ptr < substr_end_ptr) {  \
 30            char32_t c = *substr_start_ptr++;  \
 31            c |= (*substr_start_ptr++) << 8;  \
 32            c |= (*substr_start_ptr++) << 16;  \
 33            if (pw_char_lower(*((STR_CHAR_TYPE*) str_start_ptr)) != pw_char_lower(c)) {  \
 34                return false;  \
 35            }  \
 36            str_start_ptr += STR_CHAR_SIZE;  \
 37        }  \
 38        return true;  \
 39    }
 40SUBSTREQI_N_3(uint8_t,  1)
 41SUBSTREQI_N_3(uint16_t, 2)
 42SUBSTREQI_N_3(char32_t, 4)
 43
 44#define SUBSTREQI_3_N(SUBSTR_CHAR_TYPE, SUBSTR_CHAR_SIZE)  \
 45    static bool substreqi_3_##SUBSTR_CHAR_SIZE(uint8_t* str_start_ptr, uint8_t* substr_start_ptr, uint8_t* substr_end_ptr)  \
 46    {  \
 47        while (substr_start_ptr < substr_end_ptr) {  \
 48            char32_t c = *str_start_ptr++;  \
 49            c |= (*str_start_ptr++) << 8;  \
 50            c |= (*str_start_ptr++) << 16;  \
 51            if (pw_char_lower(c) != pw_char_lower(*((SUBSTR_CHAR_TYPE*) substr_start_ptr))) {  \
 52                return false;  \
 53            }  \
 54            substr_start_ptr += SUBSTR_CHAR_SIZE;  \
 55        }  \
 56        return true;  \
 57    }
 58SUBSTREQI_3_N(uint8_t,  1)
 59SUBSTREQI_3_N(uint16_t, 2)
 60SUBSTREQI_3_N(char32_t, 4)
 61
 62static bool substreqi_3_3(uint8_t* str_start_ptr, uint8_t* substr_start_ptr, uint8_t* substr_end_ptr)
 63{
 64    while (substr_start_ptr < substr_end_ptr) {
 65        char32_t c1 = *str_start_ptr++;
 66        c1 |= (*str_start_ptr++) << 8;
 67        c1 |= (*str_start_ptr++) << 16;
 68        char32_t c2 = *substr_start_ptr++;  \
 69        c2 |= (*substr_start_ptr++) << 8;  \
 70        c2 |= (*substr_start_ptr++) << 16;  \
 71        if (pw_char_lower(c1) != pw_char_lower(c2)) {
 72            return false;
 73        }
 74    }
 75    return true;
 76}
 77
 78// Comparison with UTF-8 string.
 79
 80#define SUBSTREQ_UTF8(CHAR_TYPE, CHAR_SIZE)  \
 81    static bool substreqi_##CHAR_SIZE##_0(uint8_t* str_start_ptr, uint8_t* substr_start_ptr, uint8_t* substr_end_ptr)  \
 82    {  \
 83        while(substr_start_ptr < substr_end_ptr) {  \
 84            char32_t c = _pw_decode_utf8_char(&substr_start_ptr);  \
 85            if (pw_char_lower(c) != pw_char_lower(*((CHAR_TYPE*) str_start_ptr))) {  \
 86                return false;  \
 87            }  \
 88            str_start_ptr += CHAR_SIZE;  \
 89        }  \
 90        return true;  \
 91    }
 92SUBSTREQ_UTF8(uint8_t,  1)
 93SUBSTREQ_UTF8(uint16_t, 2)
 94SUBSTREQ_UTF8(char32_t, 4)
 95
 96static bool substreqi_3_0(uint8_t* str_start_ptr, uint8_t* substr_start_ptr, uint8_t* substr_end_ptr)
 97{
 98    while(substr_start_ptr < substr_end_ptr) {
 99        char32_t c1 = _pw_decode_utf8_char(&substr_start_ptr);
100        char32_t c2 = *str_start_ptr++;
101        c2 |= (*str_start_ptr++) << 8;
102        c2 |= (*str_start_ptr++) << 16;
103        if (pw_char_lower(c1) != pw_char_lower(c2)) {
104            return false;
105        }
106    }
107    return true;
108}
109
110
111SubstrEq _pw_substreqi_variants[5][5] = {
112    {
113        nullptr,
114        nullptr,
115        nullptr,
116        nullptr,
117        nullptr
118    },
119    {
120        substreqi_1_0,
121        substreqi_1_1,
122        substreqi_1_2,
123        substreqi_1_3,
124        substreqi_1_4
125    },
126    {
127        substreqi_2_0,
128        substreqi_2_1,
129        substreqi_2_2,
130        substreqi_2_3,
131        substreqi_2_4
132    },
133    {
134        substreqi_3_0,
135        substreqi_3_1,
136        substreqi_3_2,
137        substreqi_3_3,
138        substreqi_3_4
139    },
140    {
141        substreqi_4_0,
142        substreqi_4_1,
143        substreqi_4_2,
144        substreqi_4_3,
145        substreqi_4_4
146    }
147};
148
149
150/****************************************************************
151 * High-level functions
152 */
153
154[[nodiscard]] bool _pw_substring_eqi(PwValuePtr a, unsigned start_pos, unsigned end_pos, PwValuePtr b)
155{
156    pw_assert_string(a);
157    unsigned a_length;
158    uint8_t* a_ptr = _pw_string_start_length(a, &a_length);
159
160    if (_pw_unlikely(end_pos > a_length)) {
161        end_pos = a_length;
162    }
163    if (_pw_unlikely(end_pos < start_pos)) {
164        return false;
165    }
166    unsigned substr_length = end_pos - start_pos;
167    unsigned b_length = pw_strlen(b);
168    if (_pw_unlikely(substr_length != b_length)) {
169        return false;
170    }
171    if (_pw_unlikely(substr_length == 0)) {
172        return true;
173    }
174    uint8_t* b_end_ptr;
175    uint8_t* b_start_ptr = _pw_string_start_end(b, &b_end_ptr);
176    SubstrEq fn_substreqi = _pw_substreqi_variants[a->char_size][b->char_size];
177    return fn_substreqi(a_ptr + start_pos * a->char_size, b_start_ptr, b_end_ptr);
178}
179
180[[nodiscard]] bool _pw_startswithi_c32(PwValuePtr str, char32_t prefix)
181{
182    pw_assert_string(str);
183    unsigned length = pw_strlen(str);
184    if (_pw_unlikely(length == 0)) {
185        return false;
186    }
187    return pw_char_lower(_pw_get_char(_pw_string_start(str), str->char_size)) == pw_char_lower(prefix);
188}
189
190[[nodiscard]] bool _pw_startswithi(PwValuePtr str, PwValuePtr prefix)
191{
192    return _pw_substring_eqi(str, 0, pw_strlen(prefix), prefix);
193}
194
195[[nodiscard]] bool _pw_endswithi_c32(PwValuePtr str, char32_t prefix)
196{
197    pw_assert_string(str);
198    unsigned length;
199    uint8_t* ptr = _pw_string_start_length(str, &length);
200    if (_pw_unlikely(length == 0)) {
201        return false;
202    }
203    uint8_t char_size = str->char_size;
204    return pw_char_lower(prefix) == pw_char_lower(_pw_get_char(ptr + (length - 1) * char_size, char_size));
205}
206
207[[nodiscard]] bool _pw_endswithi(PwValuePtr str, PwValuePtr suffix)
208{
209    unsigned str_len = pw_strlen(str);
210    unsigned suffix_len = pw_strlen(suffix);
211    if (_pw_unlikely(str_len < suffix_len)) {
212        return false;
213    }
214    return _pw_substring_eqi(str, str_len - suffix_len, str_len, suffix);
215}