1#include "include/pw.h"
  2#include "src/types/array/array_internal.h"
  3
  4
  5[[nodiscard]] unsigned pw_array_length(PwValuePtr self)
  6{
  7    _PwArray* array = get_array(self);
  8    if (array) {
  9        return _pw_array_length(array);
 10    } else {
 11        return false;
 12    }
 13}
 14
 15[[nodiscard]] bool _pw_array_va(uint16_t result_type, PwValuePtr result, ...)
 16{
 17    va_list ap;
 18    va_start(ap);
 19    if (!pw_create(result_type, result)) {
 20        _pw_destroy_args(ap);
 21        va_end(ap);
 22        return false;
 23    }
 24    bool ret = pw_array_append_ap(result, ap);
 25    if (!ret) {
 26        pw_destroy(result);
 27    }
 28    va_end(ap);
 29    return ret;
 30}
 31
 32[[nodiscard]] bool _pw_array_append_va(PwValuePtr array, ...)
 33{
 34    va_list ap;
 35    va_start(ap);
 36    bool ret = pw_array_append_ap(array, ap);
 37    va_end(ap);
 38    return ret;
 39}
 40
 41[[nodiscard]] bool pw_array_append_ap(PwValuePtr self, va_list ap)
 42{
 43    _PwArray* array = get_array_noiter(self);
 44    if (!array) {
 45        return false;
 46    }
 47    unsigned num_appended = 0;
 48    for (;;) {
 49        PwValue arg = va_arg(ap, _PwValue);
 50        if (pw_is_status(&arg)) {
 51            if (pw_is_va_end(&arg)) {
 52                return true;
 53            }
 54            pw_set_status(pw_clone(&arg));
 55            goto failure;
 56        }
 57        if (!_pw_array_append_item(self, array, &arg)) {
 58            goto failure;
 59        }
 60        num_appended++;
 61    }
 62
 63failure:
 64    // rollback
 65    while (num_appended--) {
 66        PwValue v = PW_NULL;
 67        if (_pw_array_pop(self, array, &v)) {
 68            pw_destroy(&v);
 69        }
 70    }
 71    // consume args
 72    _pw_destroy_args(ap);
 73    return true;
 74}
 75
 76[[nodiscard]] bool pw_array_clean(PwValuePtr self)
 77{
 78    _PwArray* array = get_array_noiter(self);
 79    if (!array) {
 80        return false;
 81    }
 82    return _pw_array_del(self, array, 0, UINT_MAX);
 83}
 84
 85[[nodiscard]] bool pw_array_resize(PwValuePtr self, unsigned desired_capacity)
 86{
 87    _PwArray* array = get_array_noiter(self);
 88    if (!array) {
 89        return false;
 90    }
 91    return _pw_array_resize(self->type_id, array, desired_capacity);
 92}
 93
 94[[nodiscard]] bool _pw_array_append(PwValuePtr self, PwValuePtr item)
 95{
 96    _PwArray* array = get_array_noiter(self);
 97    if (!array) {
 98        return false;
 99    }
100    PwValue cloned_item = pw_clone(item);
101    return _pw_array_append_item(self, array, &cloned_item);
102}
103
104[[nodiscard]] bool _pw_array_insert(PwValuePtr self, unsigned index, PwValuePtr item)
105{
106    _PwArray* array = get_array_noiter(self);
107    if (!array) {
108        return false;
109    }
110    return _pw_array_ins(self, array, index, item);
111}
112
113[[nodiscard]] bool pw_array_pull(PwValuePtr self, PwValuePtr result)
114{
115    _PwArray* array = get_array_noiter(self);
116    if (!array) {
117        return false;
118    }
119    return _pw_array_pull(self, array, result);
120}
121
122[[nodiscard]] bool pw_array_del(PwValuePtr self, unsigned start_index, unsigned end_index)
123{
124    _PwArray* array = get_array_noiter(self);
125    if (!array) {
126        return false;
127    }
128    return _pw_array_del(self, array, start_index, end_index);
129}
130
131[[nodiscard]] bool pw_array_pop(PwValuePtr self, PwValuePtr result)
132{
133    _PwArray* array = get_array_noiter(self);
134    if (!array) {
135        return false;
136    }
137    return _pw_array_pop(self, array, result);
138}
139
140[[nodiscard]] bool pw_array_slice2(PwValuePtr self, unsigned start_index, unsigned end_index, PwValuePtr result, uint16_t result_type)
141// XXX make this array interface method
142{
143    _PwArray* src_array = get_array(self);
144    if (!src_array) {
145        return false;
146    }
147    unsigned length = _pw_array_length(src_array);
148
149    if (end_index > length) {
150        end_index = length;
151    }
152    if (start_index > end_index) {
153        start_index = end_index;
154    }
155    unsigned slice_len = end_index - start_index;
156    PwArrayCtorArgs args = { .capacity = slice_len };
157    if (!pw_create2(result_type, &args, result)) {
158        return false;
159    }
160    if (slice_len == 0) {
161        return true;
162    }
163
164    _PwArray* dest_array = _pw_get_struct_ptr(result, PwTypeId_BasicArray);
165
166    if (!_pw_array_resize(result->type_id, dest_array, slice_len)) {
167        return false;
168    }
169
170    PwValuePtr src_item_ptr = &src_array->items[start_index];
171    PwValuePtr dest_item_ptr = dest_array->items;
172    for (unsigned i = start_index; i < end_index; i++) {
173        __pw_clone(dest_item_ptr, src_item_ptr);  // no need to destroy dest_item, so use __pw_clone here
174        src_item_ptr++;
175        dest_item_ptr++;
176        dest_array->length++;
177    }
178    return true;
179}
180
181[[nodiscard]] bool _pw_array_join(PwValuePtr array, PwValuePtr separator, PwValuePtr result)
182{
183    if (!pw_is_string(separator)) {
184        pw_set_status(PwStatus(PW_ERROR_INCOMPATIBLE_TYPE),
185                      "Bad separator type for pw_array_join: %u, %s",
186                      separator->type_id, pw_get_type_name(separator->type_id));
187        return false;
188    }
189    unsigned num_items = pw_array_length(array);
190    if (num_items == 0) {
191        *result = PwString("");
192        return true;
193    }
194    if (num_items == 1) {
195        PwValue item = PW_NULL;
196        if (!pw_array_item(array, 0, &item)) {
197            return false;
198        }
199        if (pw_is_string(&item)) {
200            pw_move(result, &item);
201            return true;
202        } else {
203            // XXX skipping non-string values
204            *result = PwString("");
205            return true;
206        }
207    }
208
209    uint8_t  max_char_size = separator->char_size;
210    unsigned separator_len = pw_strlen(separator);
211
212    // calculate total length and max char width of string items
213    unsigned result_len = 0;
214    for (unsigned i = 0; i < num_items; i++) {
215        if (i) {
216            result_len += separator_len;
217        }
218        PwValue item = PW_NULL;
219        if (!pw_array_item(array, i, &item)) {
220            return false;
221        }
222        uint8_t char_size;
223        if (pw_is_string(&item)) {
224            char_size = item.char_size;
225            result_len += pw_strlen(&item);
226        } else {
227            // XXX skipping non-string values
228            continue;
229        }
230        if (max_char_size < char_size) {
231            max_char_size = char_size;
232        }
233    }
234
235    // join array items
236    if (!pw_create_empty_string(result_len, max_char_size, result)) {
237        return false;
238    }
239    for (unsigned i = 0; i < num_items; i++) {
240        PwValue item = PW_NULL;
241        if (!pw_array_item(array, i, &item)) {
242            return false;
243        }
244        if (pw_is_string(&item)) {
245            if (i) {
246                if (!_pw_string_append(result, separator)) {
247                    pw_destroy(result);
248                    return false;
249                }
250            }
251            if (!_pw_string_append(result, &item)) {
252                pw_destroy(result);
253                return false;
254            }
255        }
256    }
257    return true;
258}
259
260[[nodiscard]] bool _pw_array_join_c32(PwValuePtr array, char32_t separator, PwValuePtr result)
261{
262    PwValue sep = PW_STRING("");
263    if (!pw_string_append(&sep, separator)) {
264        return false;
265    }
266    return _pw_array_join(array, &sep, result);
267}
268
269[[nodiscard]] bool _pw_array_join_ascii(PwValuePtr array, char* separator, PwValuePtr result)
270{
271    PwValue sep = PW_STRING("");
272    if (!pw_string_append(&sep, separator, nullptr)) {
273        return false;
274    }
275    return _pw_array_join(array, &sep, result);
276}
277
278[[nodiscard]] bool _pw_array_join_utf8(PwValuePtr array, char8_t* separator, PwValuePtr result)
279{
280    PwValue sep = PW_STRING("");
281    if (!pw_string_append(&sep, separator, nullptr)) {
282        return false;
283    }
284    return _pw_array_join(array, &sep, result);
285}
286
287[[nodiscard]] bool _pw_array_join_utf32(PwValuePtr array, char32_t* separator, PwValuePtr result)
288{
289    PwValue sep = PW_STRING("");
290    if (!pw_string_append(&sep, separator, nullptr)) {
291        return false;
292    }
293    return _pw_array_join(array, &sep, result);
294}
295
296[[nodiscard]] bool pw_array_dedent(PwValuePtr lines)
297{
298    pw_assert_array(lines);
299
300    // dedent in-place, so access items directly to avoid cloning
301    _PwArray* array = get_array_noiter(lines);
302    if (!array) {
303        return false;
304    }
305
306    static char32_t indent_chars[] = {' ', '\t', 0};
307
308    unsigned n = pw_array_length(lines);
309
310    unsigned indent[n];
311
312    // measure indents
313    unsigned min_indent = UINT_MAX;
314    for (unsigned i = 0; i < n; i++) {
315        PwValuePtr line = &array->items[i];
316        indent[i] = 0;
317        if (pw_is_string(line)) {
318            indent[i] = pw_string_skip_chars(line, 0, indent_chars);
319            if (indent[i] && indent[i] < min_indent) {
320                min_indent = indent[i];
321            }
322        }
323    }
324    if (min_indent == UINT_MAX) {
325        // nothing to dedent
326        return true;
327    }
328
329    // dedent
330    for (unsigned i = 0; i < n; i++) {
331        if (indent[i]) {
332            PwValuePtr line = &array->items[i];
333            if (!pw_string_erase(line, 0, min_indent)) {
334                return false;
335            }
336        }
337    }
338    return true;
339}