1#include <string.h>
  2
  3#include "include/pw.h"
  4#include "src/types/string/string_internal.h"
  5
  6static bool substreq_n_n(uint8_t* str_start_ptr, uint8_t* substr_start_ptr, uint8_t* substr_end_ptr)
  7{
  8    return 0 == memcmp(str_start_ptr, substr_start_ptr, substr_end_ptr - substr_start_ptr);
  9}
 10
 11static bool substreq_1_2(uint8_t* str_start_ptr, uint8_t* substr_start_ptr, uint8_t* substr_end_ptr)
 12{
 13    while (substr_start_ptr < substr_end_ptr) {
 14        if (*str_start_ptr++ != *((uint16_t*) substr_start_ptr)) {
 15            return false;
 16        }
 17        substr_start_ptr += 2;
 18    }
 19    return true;
 20}
 21
 22static bool substreq_1_3(uint8_t* str_start_ptr, uint8_t* substr_start_ptr, uint8_t* substr_end_ptr)
 23{
 24    while (substr_start_ptr < substr_end_ptr) {
 25        char32_t sub_chr = *substr_start_ptr++;
 26        sub_chr |= (*substr_start_ptr++) << 8;
 27        sub_chr |= (*substr_start_ptr++) << 16;
 28        if (*str_start_ptr++ != sub_chr) {
 29            return false;
 30        }
 31    }
 32    return true;
 33}
 34
 35static bool substreq_1_4(uint8_t* str_start_ptr, uint8_t* substr_start_ptr, uint8_t* substr_end_ptr)
 36{
 37    while (substr_start_ptr < substr_end_ptr) {
 38        if (*str_start_ptr++ != *((char32_t*) substr_start_ptr)) {
 39            return false;
 40        }
 41        substr_start_ptr += 4;
 42    }
 43    return true;
 44}
 45
 46
 47static bool substreq_2_1(uint8_t* str_start_ptr, uint8_t* substr_start_ptr, uint8_t* substr_end_ptr)
 48{
 49    while (substr_start_ptr < substr_end_ptr) {
 50        if (*((uint16_t*) str_start_ptr) != *substr_start_ptr++) {
 51            return false;
 52        }
 53        str_start_ptr += 2;
 54    }
 55    return true;
 56}
 57
 58static bool substreq_2_3(uint8_t* str_start_ptr, uint8_t* substr_start_ptr, uint8_t* substr_end_ptr)
 59{
 60    while (substr_start_ptr < substr_end_ptr) {
 61        char32_t sub_chr = *substr_start_ptr++;
 62        sub_chr |= (*substr_start_ptr++) << 8;
 63        sub_chr |= (*substr_start_ptr++) << 16;
 64        if (*((uint16_t*) str_start_ptr) != sub_chr) {
 65            return false;
 66        }
 67        str_start_ptr += 2;
 68    }
 69    return true;
 70}
 71
 72static bool substreq_2_4(uint8_t* str_start_ptr, uint8_t* substr_start_ptr, uint8_t* substr_end_ptr)
 73{
 74    while (substr_start_ptr < substr_end_ptr) {
 75        if (*((uint16_t*) str_start_ptr) != *((char32_t*) substr_start_ptr)) {
 76            return false;
 77        }
 78        str_start_ptr += 2;
 79        substr_start_ptr += 4;
 80    }
 81    return true;
 82}
 83
 84
 85static bool substreq_3_1(uint8_t* str_start_ptr, uint8_t* substr_start_ptr, uint8_t* substr_end_ptr)
 86{
 87    while (substr_start_ptr < substr_end_ptr) {
 88        char32_t chr = *str_start_ptr++;
 89        chr |= (*str_start_ptr++) << 8;
 90        chr |= (*str_start_ptr++) << 16;
 91        if (chr != *substr_start_ptr++) {
 92            return false;
 93        }
 94    }
 95    return true;
 96}
 97
 98static bool substreq_3_2(uint8_t* str_start_ptr, uint8_t* substr_start_ptr, uint8_t* substr_end_ptr)
 99{
100    while (substr_start_ptr < substr_end_ptr) {
101        char32_t chr = *str_start_ptr++;
102        chr |= (*str_start_ptr++) << 8;
103        chr |= (*str_start_ptr++) << 16;
104        if (chr != *((uint16_t*) substr_start_ptr)) {
105            return false;
106        }
107        substr_start_ptr += 2;
108    }
109    return true;
110}
111
112static bool substreq_3_4(uint8_t* str_start_ptr, uint8_t* substr_start_ptr, uint8_t* substr_end_ptr)
113{
114    while (substr_start_ptr < substr_end_ptr) {
115        char32_t chr = *str_start_ptr++;
116        chr |= (*str_start_ptr++) << 8;
117        chr |= (*str_start_ptr++) << 16;
118        if (chr != *((char32_t*) substr_start_ptr)) {
119            return false;
120        }
121        str_start_ptr += 4;
122    }
123    return true;
124}
125
126
127static bool substreq_4_1(uint8_t* str_start_ptr, uint8_t* substr_start_ptr, uint8_t* substr_end_ptr)
128{
129    while (substr_start_ptr < substr_end_ptr) {
130        if (*((char32_t*) str_start_ptr) != *substr_start_ptr++) {
131            return false;
132        }
133        str_start_ptr += 4;
134    }
135    return true;
136}
137
138static bool substreq_4_2(uint8_t* str_start_ptr, uint8_t* substr_start_ptr, uint8_t* substr_end_ptr)
139{
140    while (substr_start_ptr < substr_end_ptr) {
141        if (*((char32_t*) str_start_ptr) != *((uint16_t*) substr_start_ptr)) {
142            return false;
143        }
144        str_start_ptr += 4;
145        substr_start_ptr += 2;
146    }
147    return true;
148}
149
150static bool substreq_4_3(uint8_t* str_start_ptr, uint8_t* substr_start_ptr, uint8_t* substr_end_ptr)
151{
152    while (substr_start_ptr < substr_end_ptr) {
153        char32_t sub_chr = *substr_start_ptr++;
154        sub_chr |= (*substr_start_ptr++) << 8;
155        sub_chr |= (*substr_start_ptr++) << 16;
156        if (*((char32_t*) str_start_ptr) != sub_chr) {
157            return false;
158        }
159        str_start_ptr += 4;
160    }
161    return true;
162}
163
164// Comparison with UTF-8 string.
165
166#define CMP_UTF8(CHAR_TYPE, CHAR_SIZE)  \
167    static bool substreq_##CHAR_SIZE##_0(uint8_t* str_start_ptr, uint8_t* substr_start_ptr, uint8_t* substr_end_ptr)  \
168    {  \
169        while(substr_start_ptr < substr_end_ptr) {  \
170            char32_t c = _pw_decode_utf8_char(&substr_start_ptr);  \
171            if (c != *((CHAR_TYPE*) str_start_ptr)) {  \
172                return false;  \
173            }  \
174            str_start_ptr += CHAR_SIZE;  \
175        }  \
176        return true;  \
177    }
178CMP_UTF8(uint8_t,  1)
179CMP_UTF8(uint16_t, 2)
180CMP_UTF8(char32_t, 4)
181
182static bool substreq_3_0(uint8_t* str_start_ptr, uint8_t* substr_start_ptr, uint8_t* substr_end_ptr)
183{
184    while(substr_start_ptr < substr_end_ptr) {
185        char32_t c1 = _pw_decode_utf8_char(&substr_start_ptr);
186        char32_t c2 = *str_start_ptr++;
187        c2 |= (*str_start_ptr++) << 8;
188        c2 |= (*str_start_ptr++) << 16;
189        if (c1 != c2) {
190            return false;
191        }
192    }
193    return true;
194}
195
196
197SubstrEq _pw_substreq_variants[5][5] = {
198    {
199        nullptr,
200        nullptr,
201        nullptr,
202        nullptr,
203        nullptr
204    },
205    {
206        substreq_1_0,
207        substreq_n_n,
208        substreq_1_2,
209        substreq_1_3,
210        substreq_1_4
211    },
212    {
213        substreq_2_0,
214        substreq_2_1,
215        substreq_n_n,
216        substreq_2_3,
217        substreq_2_4
218    },
219    {
220        substreq_3_0,
221        substreq_3_1,
222        substreq_3_2,
223        substreq_n_n,
224        substreq_3_4
225    },
226    {
227        substreq_4_0,
228        substreq_4_1,
229        substreq_4_2,
230        substreq_4_3,
231        substreq_n_n
232    }
233};
234
235
236/****************************************************************
237 * High-level functions
238 */
239
240[[nodiscard]] bool _pw_substring_eq(PwValuePtr a, unsigned start_pos, unsigned end_pos, PwValuePtr b)
241{
242    pw_assert_string(a);
243    unsigned a_length;
244    uint8_t* a_ptr = _pw_string_start_length(a, &a_length);
245
246    if (_pw_unlikely(end_pos > a_length)) {
247        end_pos = a_length;
248    }
249    if (_pw_unlikely(end_pos < start_pos)) {
250        return false;
251    }
252    unsigned substr_length = end_pos - start_pos;
253    unsigned b_length = pw_strlen(b);
254    if (_pw_unlikely(substr_length != b_length)) {
255        return false;
256    }
257    if (_pw_unlikely(substr_length == 0)) {
258        return true;
259    }
260    uint8_t* b_end_ptr;
261    uint8_t* b_start_ptr = _pw_string_start_end(b, &b_end_ptr);
262    SubstrEq fn_substreq = _pw_substreq_variants[a->char_size][b->char_size];
263    return fn_substreq(a_ptr + start_pos * a->char_size, b_start_ptr, b_end_ptr);
264}
265
266[[nodiscard]] bool _pw_startswith_c32(PwValuePtr str, char32_t prefix)
267{
268    pw_assert_string(str);
269    unsigned length = pw_strlen(str);
270    if (_pw_unlikely(length == 0)) {
271        return false;
272    }
273    return _pw_get_char(_pw_string_start(str), str->char_size) == prefix;
274}
275
276[[nodiscard]] bool _pw_startswith(PwValuePtr str, PwValuePtr prefix)
277{
278    return _pw_substring_eq(str, 0, pw_strlen(prefix), prefix);
279}
280
281[[nodiscard]] bool _pw_endswith_c32(PwValuePtr str, char32_t prefix)
282{
283    pw_assert_string(str);
284    unsigned length;
285    uint8_t* ptr = _pw_string_start_length(str, &length);
286    if (_pw_unlikely(length == 0)) {
287        return false;
288    }
289    uint8_t char_size = str->char_size;
290    return prefix == _pw_get_char(ptr + (length - 1) * char_size, char_size);
291}
292
293[[nodiscard]] bool _pw_endswith(PwValuePtr str, PwValuePtr suffix)
294{
295    unsigned str_len = pw_strlen(str);
296    unsigned suffix_len = pw_strlen(suffix);
297    if (_pw_unlikely(str_len < suffix_len)) {
298        return false;
299    }
300    return _pw_substring_eq(str, str_len - suffix_len, str_len, suffix);
301}