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}