1#include "include/pw.h"
2#include "src/types/string/string_internal.h"
3
4/*
5 * Append variants with no dest expansion
6 */
7#define APPEND_NOEXPAND_NN(DEST_CHAR_SIZE, SRC_CHAR_SIZE) \
8 [[nodiscard]] static bool append_##DEST_CHAR_SIZE##_##SRC_CHAR_SIZE( \
9 PwValuePtr dest, unsigned dest_pos, uint8_t* src_start_ptr, uint8_t* src_end_ptr) \
10 { \
11 _pw_strcopy_n_n(_pw_string_char_ptr(dest, dest_pos), src_start_ptr, src_end_ptr); \
12 return true; \
13 }
14APPEND_NOEXPAND_NN(1, 1)
15APPEND_NOEXPAND_NN(2, 2)
16APPEND_NOEXPAND_NN(3, 3)
17APPEND_NOEXPAND_NN(4, 4)
18
19#define APPEND_NOEXPAND(DEST_CHAR_SIZE, SRC_CHAR_SIZE) \
20 [[nodiscard]] static bool append_##DEST_CHAR_SIZE##_##SRC_CHAR_SIZE( \
21 PwValuePtr dest, unsigned dest_pos, uint8_t* src_start_ptr, uint8_t* src_end_ptr) \
22 { \
23 _pw_strcopy_##DEST_CHAR_SIZE##_##SRC_CHAR_SIZE(_pw_string_char_ptr(dest, dest_pos), src_start_ptr, src_end_ptr); \
24 return true; \
25 }
26APPEND_NOEXPAND(2, 1)
27APPEND_NOEXPAND(3, 1)
28APPEND_NOEXPAND(3, 2)
29APPEND_NOEXPAND(4, 1)
30APPEND_NOEXPAND(4, 2)
31APPEND_NOEXPAND(4, 3)
32
33
34/*
35 * Append variants with dest expansion for char_size 1, 2, and 4
36 */
37#define APPEND_EXPAND(DEST_CHAR_TYPE, DEST_CHAR_SIZE, SRC_CHAR_TYPE, SRC_CHAR_SIZE) \
38 [[nodiscard]] static bool append_##DEST_CHAR_SIZE##_##SRC_CHAR_SIZE( \
39 PwValuePtr dest, unsigned dest_pos, uint8_t* src_start_ptr, uint8_t* src_end_ptr) \
40 { \
41 uint8_t* dest_ptr = _pw_string_char_ptr(dest, dest_pos); \
42 while (src_start_ptr < src_end_ptr) { \
43 char32_t c = *((SRC_CHAR_TYPE*) src_start_ptr); \
44 uint8_t char_size = calc_char_size(c); \
45 if (_pw_unlikely(char_size > DEST_CHAR_SIZE)) { \
46 if (!_pw_expand_string(dest, 0, char_size)) { \
47 return false; \
48 } \
49 StrAppend fn_append = _pw_str_append_variants[char_size][SRC_CHAR_SIZE]; \
50 return fn_append(dest, dest_pos, src_start_ptr, src_end_ptr); \
51 } \
52 *((DEST_CHAR_TYPE*) dest_ptr) = (DEST_CHAR_TYPE) c; \
53 dest_ptr += DEST_CHAR_SIZE; \
54 src_start_ptr += SRC_CHAR_SIZE; \
55 dest_pos++; \
56 } \
57 return true; \
58 }
59APPEND_EXPAND(uint8_t, 1, uint16_t, 2)
60APPEND_EXPAND(uint8_t, 1, char32_t, 4)
61APPEND_EXPAND(uint16_t, 2, char32_t, 4)
62
63/*
64 * Append variants with dest expansion for source char_size 3
65 */
66#define APPEND_EXPAND_N_3(DEST_CHAR_TYPE, DEST_CHAR_SIZE) \
67 [[nodiscard]] static bool append_##DEST_CHAR_SIZE##_3( \
68 PwValuePtr dest, unsigned dest_pos, uint8_t* src_start_ptr, uint8_t* src_end_ptr) \
69 { \
70 uint8_t* dest_ptr = _pw_string_char_ptr(dest, dest_pos); \
71 while (src_start_ptr < src_end_ptr) { \
72 char32_t c = *src_start_ptr++; \
73 c |= (*src_start_ptr++) << 8; \
74 c |= (*src_start_ptr++) << 16; \
75 uint8_t char_size = calc_char_size(c); \
76 if (_pw_unlikely(char_size > DEST_CHAR_SIZE)) { \
77 if (!_pw_expand_string(dest, 0, char_size)) { \
78 return false; \
79 } \
80 StrAppend fn_append = _pw_str_append_variants[char_size][3]; \
81 return fn_append(dest, dest_pos, src_start_ptr - 3, src_end_ptr); \
82 } \
83 *((DEST_CHAR_TYPE*) dest_ptr) = (DEST_CHAR_TYPE) c; \
84 dest_ptr += DEST_CHAR_SIZE; \
85 dest_pos++; \
86 } \
87 return true; \
88 }
89APPEND_EXPAND_N_3(uint8_t, 1)
90APPEND_EXPAND_N_3(uint16_t, 2)
91
92/*
93 * Append variant with dest expansion for source char_size 4 and dest char_size 3
94 */
95[[nodiscard]] static bool append_3_4(PwValuePtr dest, unsigned dest_pos, uint8_t* src_start_ptr, uint8_t* src_end_ptr)
96{
97 uint8_t* dest_ptr = _pw_string_char_ptr(dest, dest_pos);
98 while (src_start_ptr < src_end_ptr) {
99 char32_t c = *((char32_t*) src_start_ptr);
100 uint8_t char_size = calc_char_size(c);
101 if (_pw_unlikely(char_size > 3)) {
102 if (!_pw_expand_string(dest, 0, char_size)) {
103 return false;
104 }
105 StrAppend fn_append = _pw_str_append_variants[char_size][0];
106 return fn_append(dest, dest_pos, src_start_ptr, src_end_ptr);
107 }
108 *dest_ptr++ = (uint8_t) c; c >>= 8;
109 *dest_ptr++ = (uint8_t) c; c >>= 8;
110 *dest_ptr++ = (uint8_t) c;
111 src_start_ptr += 4;
112 dest_pos++;
113 }
114 return true;
115}
116
117/*
118 * Append variants with dest expansion for UTF-8 source and dest char_size 1, 2, and 4
119 */
120#define APPEND_UTF8(DEST_CHAR_TYPE, DEST_CHAR_SIZE) \
121 [[nodiscard]] static bool append_##DEST_CHAR_SIZE##_0( \
122 PwValuePtr dest, unsigned dest_pos, uint8_t* src_start_ptr, uint8_t* src_end_ptr) \
123 { \
124 uint8_t* dest_ptr = _pw_string_char_ptr(dest, dest_pos); \
125 while (src_start_ptr < src_end_ptr) { \
126 uint8_t* current_ptr = src_start_ptr; \
127 char32_t c = _pw_decode_utf8_char(&src_start_ptr); \
128 uint8_t char_size = calc_char_size(c); \
129 if (_pw_unlikely(char_size > DEST_CHAR_SIZE)) { \
130 if (!_pw_expand_string(dest, 0, char_size)) { \
131 return false; \
132 } \
133 StrAppend fn_append = _pw_str_append_variants[char_size][0]; \
134 return fn_append(dest, dest_pos, current_ptr, src_end_ptr); \
135 } \
136 *((DEST_CHAR_TYPE*) dest_ptr) = (DEST_CHAR_TYPE) c; \
137 dest_ptr += DEST_CHAR_SIZE; \
138 dest_pos++; \
139 } \
140 return true; \
141 }
142APPEND_UTF8(uint8_t, 1)
143APPEND_UTF8(uint16_t, 2)
144APPEND_UTF8(char32_t, 4)
145
146[[nodiscard]] static bool append_3_0(PwValuePtr dest, unsigned dest_pos, uint8_t* src_start_ptr, uint8_t* src_end_ptr)
147{
148 uint8_t* dest_ptr = _pw_string_char_ptr(dest, dest_pos);
149 while (src_start_ptr < src_end_ptr) {
150 uint8_t* current_ptr = src_start_ptr;
151 char32_t c = _pw_decode_utf8_char(&src_start_ptr);
152 uint8_t char_size = calc_char_size(c);
153 if (_pw_unlikely(char_size > 3)) {
154 if (!_pw_expand_string(dest, 0, char_size)) {
155 return false;
156 }
157 StrAppend fn_append = _pw_str_append_variants[char_size][0];
158 return fn_append(dest, dest_pos, current_ptr, src_end_ptr);
159 }
160 *dest_ptr++ = (uint8_t) c; c >>= 8;
161 *dest_ptr++ = (uint8_t) c; c >>= 8;
162 *dest_ptr++ = (uint8_t) c;
163 dest_pos++;
164 }
165 return true;
166}
167
168StrAppend _pw_str_append_variants[5][5] = {
169 {
170 nullptr,
171 nullptr,
172 nullptr,
173 nullptr,
174 nullptr
175 },
176 {
177 append_1_0,
178 append_1_1,
179 append_1_2,
180 append_1_3,
181 append_1_4
182 },
183 {
184 append_2_0,
185 append_2_1,
186 append_2_2,
187 append_2_3,
188 append_2_4
189 },
190 {
191 append_3_0,
192 append_3_1,
193 append_3_2,
194 append_3_3,
195 append_3_4
196 },
197 {
198 append_4_0,
199 append_4_1,
200 append_4_2,
201 append_4_3,
202 append_4_4
203 }
204};
205
206/****************************************************************
207 * High-level functions
208 */
209
210[[nodiscard]] bool _pw_string_append_c32(PwValuePtr dest, char32_t c)
211{
212 pw_assert_string(dest);
213 if (!_pw_expand_string(dest, 1, calc_char_size(c))) {
214 return false;
215 }
216 unsigned dest_pos = _pw_string_inc_length(dest, 1);
217 _pw_put_char(_pw_string_char_ptr(dest, dest_pos), c, dest->char_size);
218 return true;
219}
220
221[[nodiscard]] bool _pw_string_append_ascii(PwValuePtr dest, char* start_ptr, char* end_ptr)
222{
223 pw_assert_string(dest);
224
225 if (!end_ptr) {
226 end_ptr = start_ptr + strlen(start_ptr);
227 }
228 size_t src_len = end_ptr - start_ptr;
229 if (src_len == 0) {
230 return true;
231 }
232 if (src_len >= UINT_MAX) {
233 pw_set_status(PwStatus(PW_ERROR_STRING_TOO_LONG));
234 return false;
235 }
236 if (!_pw_expand_string(dest, src_len, 1)) {
237 return false;
238 }
239 unsigned dest_pos = _pw_string_inc_length(dest, src_len);
240
241 StrAppend fn_append = _pw_str_append_variants[dest->char_size][1];
242 return fn_append(dest, dest_pos, (uint8_t*) start_ptr, (uint8_t*) end_ptr);
243}
244
245[[nodiscard]] bool _pw_string_append_utf8(PwValuePtr dest, char8_t* start_ptr, char8_t* end_ptr)
246{
247 pw_assert_string(dest);
248
249 uint8_t src_char_size = 1;
250 size_t src_len;
251 if (end_ptr) {
252 src_len = end_ptr - start_ptr;
253 } else {
254 src_len = utf8_strlen3(start_ptr, &src_char_size, &end_ptr);
255 }
256 if (src_len == 0) {
257 return true;
258 }
259 if (src_len >= UINT_MAX) {
260 pw_set_status(PwStatus(PW_ERROR_STRING_TOO_LONG));
261 return false;
262 }
263 if (!_pw_expand_string(dest, src_len, src_char_size)) {
264 return false;
265 }
266 unsigned dest_pos = _pw_string_inc_length(dest, src_len);
267
268 StrAppend fn_append = _pw_str_append_variants[dest->char_size][0];
269 return fn_append(dest, dest_pos, (uint8_t*) start_ptr, (uint8_t*) end_ptr);
270}
271
272[[nodiscard]] bool _pw_string_append_utf32(PwValuePtr dest, char32_t* start_ptr, char32_t* end_ptr)
273{
274 pw_assert_string(dest);
275
276 uint8_t src_char_size = 1;
277 if (!end_ptr) {
278 end_ptr = start_ptr + utf32_strlen2(start_ptr, &src_char_size);
279 }
280 size_t src_len = end_ptr - start_ptr;
281 if (src_len == 0) {
282 return true;
283 }
284 if (src_len >= UINT_MAX / 4) {
285 pw_set_status(PwStatus(PW_ERROR_STRING_TOO_LONG));
286 return false;
287 }
288 if (!_pw_expand_string(dest, src_len, src_char_size)) {
289 return false;
290 }
291 unsigned dest_pos = _pw_string_inc_length(dest, src_len);
292
293 StrAppend fn_append = _pw_str_append_variants[dest->char_size][4];
294 return fn_append(dest, dest_pos, (uint8_t*) start_ptr, (uint8_t*) end_ptr);
295}
296
297[[nodiscard]] bool _pw_string_append(PwValuePtr dest, PwValuePtr src)
298{
299 pw_assert_string(dest);
300 unsigned src_len = pw_strlen(src);
301 if (!_pw_expand_string(dest, src_len, src->char_size)) {
302 return false;
303 }
304 unsigned dest_pos = _pw_string_inc_length(dest, src_len);
305 uint8_t* src_end_ptr;
306 uint8_t* src_start_ptr = _pw_string_start_end(src, &src_end_ptr);
307
308 StrAppend fn_append = _pw_str_append_variants[dest->char_size][src->char_size];
309 return fn_append(dest, dest_pos, src_start_ptr, src_end_ptr);
310}
311
312[[nodiscard]] bool pw_string_append_substring(PwValuePtr dest, PwValuePtr src, unsigned src_start_pos, unsigned src_end_pos)
313{
314 pw_assert_string(dest);
315 unsigned src_len = pw_strlen(src);
316 if (src_end_pos > src_len) {
317 src_end_pos = src_len;
318 }
319 if (src_start_pos >= src_end_pos) {
320 return true;
321 }
322 src_len = src_end_pos - src_start_pos;
323
324 // expand with char_size unchanged because it is unknown for substring of src
325
326 if (!_pw_expand_string(dest, src_len, 1)) {
327 return false;
328 }
329 unsigned dest_pos = _pw_string_inc_length(dest, src_len);
330 uint8_t src_char_size = src->char_size;
331 uint8_t* src_start_ptr = _pw_string_start(src);
332 uint8_t* src_end_ptr = src_start_ptr + src_end_pos * src_char_size;
333 src_start_ptr += src_start_pos * src_char_size;
334
335 StrAppend fn_append = _pw_str_append_variants[dest->char_size][src_char_size];
336 return fn_append(dest, dest_pos, src_start_ptr, src_end_ptr);
337}