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