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