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}