• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

kos-lang / kos / 28804370359

06 Jul 2026 01:33PM UTC coverage: 95.093% (-1.3%) from 96.422%
28804370359

push

github

cdragan
net: use poll() instead of select()

select() has a limit of 1024 fd values, for any fds which are higher
than 1024 select() can't work.

7 of 13 new or added lines in 1 file covered. (53.85%)

884 existing lines in 19 files now uncovered.

24707 of 25982 relevant lines covered (95.09%)

939477.57 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.14
/core/kos_array.c
1
/* SPDX-License-Identifier: MIT
2
 * SPDX-FileCopyrightText: Copyright (c) 2014-2026 Chris Dragan
3
 */
4

5
#include "../inc/kos_array.h"
6
#include "../inc/kos_atomic.h"
7
#include "../inc/kos_constants.h"
8
#include "../inc/kos_instance.h"
9
#include "../inc/kos_error.h"
10
#include "../inc/kos_object.h"
11
#include "../inc/kos_utils.h"
12
#include "kos_config.h"
13
#include "kos_debug.h"
14
#include "kos_heap.h"
15
#include "kos_math.h"
16
#include "kos_object_internal.h"
17
#include "kos_perf.h"
18
#include "kos_try.h"
19
#include <assert.h>
20

21
KOS_DECLARE_STATIC_CONST_STRING(str_err_empty,         "array is empty");
22
KOS_DECLARE_STATIC_CONST_STRING(str_err_invalid_index, "array index is out of range");
23
KOS_DECLARE_STATIC_CONST_STRING(str_err_not_array,     "object is not an array");
24
KOS_DECLARE_STATIC_CONST_STRING(str_err_read_only,     "array is read-only");
25

26
DECLARE_STATIC_CONST_OBJECT(tombstone, OBJ_OPAQUE, 0xA0);
27
DECLARE_STATIC_CONST_OBJECT(closed,    OBJ_OPAQUE, 0xA1);
28

29
/* TOMBSTONE indicates that an array element has been deleted due to a resize. */
30
#define TOMBSTONE KOS_CONST_ID(tombstone)
31
/* CLOSED indicates that an array element has been moved to a new buffer. */
32
#define CLOSED    KOS_CONST_ID(closed)
33

34
#define KOS_buffer_alloc_size(cap) (KOS_align_up((uint32_t)sizeof(KOS_ARRAY_STORAGE) \
35
                                                   + (uint32_t)(((cap) - 1) * sizeof(KOS_OBJ_ID)), \
36
                                                 1U << KOS_OBJ_ALIGN_BITS))
37

38
static void atomic_fill_ptr(KOS_ATOMIC(KOS_OBJ_ID) *dest,
638,893✔
39
                            unsigned                count,
40
                            KOS_OBJ_ID              value)
41
{
42
    KOS_ATOMIC(KOS_OBJ_ID) *const end = dest + count;
638,893✔
43
    while (dest < end) {
21,091,182✔
44
        KOS_atomic_write_relaxed_ptr(*dest, value);
20,452,289✔
45
        ++dest;
20,452,289✔
46
    }
47
}
638,893✔
48

49
static KOS_ARRAY_STORAGE *alloc_buffer(KOS_CONTEXT ctx, uint32_t capacity)
199,781✔
50
{
51
    KOS_ARRAY_STORAGE *buf            = KOS_NULL;
199,781✔
52
    const uint32_t     buf_alloc_size = KOS_buffer_alloc_size(capacity);
199,781✔
53

54
    if (capacity < KOS_MAX_ARRAY_SIZE)
199,781✔
55
        buf = (KOS_ARRAY_STORAGE *)kos_alloc_object(ctx,
199,780✔
56
                                                    KOS_ALLOC_MOVABLE,
57
                                                    OBJ_ARRAY_STORAGE,
58
                                                    buf_alloc_size);
59
    else
60
        KOS_raise_exception(ctx, KOS_STR_OUT_OF_MEMORY);
1✔
61

62
    if (buf) {
199,781✔
63
        assert(kos_get_object_type(buf->header) == OBJ_ARRAY_STORAGE);
199,707✔
64

65
        capacity = 1U + (buf_alloc_size - sizeof(KOS_ARRAY_STORAGE)) / sizeof(KOS_OBJ_ID);
199,707✔
66
        KOS_atomic_write_relaxed_u32(buf->capacity,       capacity);
199,707✔
67
        KOS_atomic_write_relaxed_u32(buf->num_slots_open, capacity);
199,707✔
68
        KOS_atomic_write_relaxed_ptr(buf->next,           KOS_BADPTR);
199,707✔
69
    }
70

71
    return buf;
199,781✔
72
}
73

74
KOS_OBJ_ID KOS_new_array(KOS_CONTEXT ctx,
398,862✔
75
                         uint32_t    size)
76
{
77
    const uint32_t array_obj_size = KOS_align_up((uint32_t)sizeof(KOS_ARRAY), 1U << KOS_OBJ_ALIGN_BITS);
398,862✔
78
    const uint32_t buf_alloc_size = size ? KOS_buffer_alloc_size(size) : 0;
398,862✔
79
    const int      buf_built_in   = array_obj_size + buf_alloc_size <= 256U;
398,862✔
80
    const uint32_t alloc_size     = buf_built_in ? array_obj_size + buf_alloc_size : array_obj_size;
398,862✔
81
    KOS_ARRAY     *array          = KOS_NULL;
398,862✔
82

83
    if (size < KOS_MAX_ARRAY_SIZE)
398,862✔
84
        array = (KOS_ARRAY *)kos_alloc_object(ctx,
398,857✔
85
                                              KOS_ALLOC_MOVABLE,
86
                                              OBJ_ARRAY,
87
                                              alloc_size);
88
    else
89
        KOS_raise_exception(ctx, KOS_STR_OUT_OF_MEMORY);
5✔
90

91
    if (array) {
398,862✔
92
        KOS_ARRAY_STORAGE *storage = KOS_NULL;
398,785✔
93

94
        KOS_atomic_write_relaxed_u32(array->flags, 0);
398,785✔
95

96
        if (buf_built_in) {
398,785✔
97
            if (buf_alloc_size) {
273,784✔
98

99
                const uint32_t capacity = 1U + (buf_alloc_size - sizeof(KOS_ARRAY_STORAGE)) / sizeof(KOS_OBJ_ID);
213,933✔
100

101
                storage = (KOS_ARRAY_STORAGE *)((uintptr_t)array + array_obj_size);
213,933✔
102
                kos_set_object_type_size(storage->header, OBJ_ARRAY_STORAGE, buf_alloc_size);
213,933✔
103

104
                KOS_atomic_write_relaxed_u32(storage->capacity,       capacity);
213,933✔
105
                KOS_atomic_write_relaxed_u32(storage->num_slots_open, capacity);
213,933✔
106
                KOS_atomic_write_relaxed_ptr(storage->next,           KOS_BADPTR);
213,933✔
107

108
                kos_set_object_size(array->header, array_obj_size);
213,933✔
109

110
                KOS_atomic_write_relaxed_ptr(array->data, OBJID(ARRAY_STORAGE, storage));
213,933✔
111
            }
112
            else
113
                KOS_atomic_write_relaxed_ptr(array->data, KOS_BADPTR);
59,851✔
114
        }
115
        else {
116
            KOS_LOCAL saved_array;
117

118
            KOS_atomic_write_relaxed_ptr(array->data, KOS_BADPTR);
125,001✔
119

120
            KOS_init_local_with(ctx, &saved_array, OBJID(ARRAY, array));
125,001✔
121

122
            storage = alloc_buffer(ctx, size);
125,001✔
123

124
            if (storage) {
125,001✔
125
                array = OBJPTR(ARRAY, saved_array.o);
124,996✔
126

127
                KOS_atomic_write_relaxed_ptr(array->data, OBJID(ARRAY_STORAGE, storage));
124,996✔
128
            }
129
            else
130
                array = KOS_NULL;
5✔
131

132
            KOS_destroy_top_local(ctx, &saved_array);
125,001✔
133
        }
134

135
        if (array) {
398,785✔
136

137
            KOS_atomic_write_relaxed_u32(array->size, size);
398,780✔
138

139
            if (storage) {
398,780✔
140
                const uint32_t capacity = KOS_atomic_read_relaxed_u32(storage->capacity);
338,929✔
141

142
                if (size)
338,929✔
143
                    atomic_fill_ptr(&storage->buf[0], size, KOS_VOID);
338,929✔
144

145
                if (size < capacity)
338,929✔
146
                    atomic_fill_ptr(&storage->buf[size], capacity - size, TOMBSTONE);
225,330✔
147
            }
148
        }
149
    }
150

151
    return OBJID(ARRAY, array);
398,862✔
152
}
153

154
static KOS_ARRAY_STORAGE *get_data(KOS_OBJ_ID obj_id)
7,403,066✔
155
{
156
    const KOS_OBJ_ID buf_obj = kos_get_array_storage(obj_id);
7,403,066✔
157
    return IS_BAD_PTR(buf_obj) ? KOS_NULL : OBJPTR(ARRAY_STORAGE, buf_obj);
7,403,066✔
158
}
159

160
static KOS_ARRAY_STORAGE *get_next(KOS_ARRAY_STORAGE *storage)
2,966✔
161
{
162
    const KOS_OBJ_ID buf_obj = KOS_atomic_read_acquire_obj(storage->next);
2,966✔
163
    return IS_BAD_PTR(buf_obj) ? KOS_NULL : OBJPTR(ARRAY_STORAGE, buf_obj);
2,966✔
164
}
165

166
static void copy_buf(KOS_CONTEXT        ctx,
42,381✔
167
                     KOS_ARRAY         *array,
168
                     KOS_ARRAY_STORAGE *old_buf,
169
                     KOS_ARRAY_STORAGE *new_buf)
170
{
171
    KOS_ATOMIC(KOS_OBJ_ID) *src      = &old_buf->buf[0];
42,381✔
172
    KOS_ATOMIC(KOS_OBJ_ID) *dst      = &new_buf->buf[0];
42,381✔
173
    const uint32_t          capacity = KOS_atomic_read_relaxed_u32(old_buf->capacity);
42,381✔
174
    const uint32_t          fuzz     = KOS_atomic_read_relaxed_u32(old_buf->num_slots_open);
42,381✔
175
    uint32_t                i        = (capacity - fuzz) % capacity;
42,381✔
176

177
    for (;;) {
1,007,145✔
178
        KOS_OBJ_ID in_dst   = TOMBSTONE;
1,049,526✔
179
        int        salvaged = 0;
1,049,526✔
180

181
        /* Salvage item to the new buffer */
182
        for (;;) {
2,032✔
183
            const KOS_OBJ_ID value = KOS_atomic_read_relaxed_obj(src[i]);
1,051,558✔
184

185
            /* Another thread copied it */
186
            if (value == CLOSED)
1,051,558✔
187
                break;
47,001✔
188

189
            /* Write value to new buffer */
190
            if ( ! KOS_atomic_cas_strong_ptr(dst[i], in_dst, value))
1,004,557✔
191
                /* Another thread wrote something to dest */
192
                break;
288,703✔
193
            in_dst = value;
715,854✔
194

195
            /* Close the slot in the old buffer */
196
            if (KOS_atomic_cas_weak_ptr(src[i], value, CLOSED)) {
715,854✔
197
                salvaged = 1;
713,822✔
198
                break;
713,822✔
199
            }
200
            /* If failed to close, someone wrote a new value, try again */
201
        }
202

203
        if (salvaged)
204
            KOS_PERF_CNT(array_salvage_success);
205
        else
206
            KOS_PERF_CNT(array_salvage_fail);
207

208
        /* Exit early if another thread finished it */
209
        if ( ! salvaged && KOS_atomic_read_relaxed_u32(old_buf->num_slots_open) == 0)
1,049,526✔
210
            break;
1,759✔
211

212
        /* Update number of closed slots */
213
        if (salvaged && KOS_atomic_add_i32(old_buf->num_slots_open, -1) == 1)
1,047,767✔
214
            break;
40,622✔
215

216
        /* Try next slot */
217
        ++i;
1,007,145✔
218
        if (i == capacity)
1,007,145✔
219
            i = 0;
217✔
220
    }
221

222
    (void)KOS_atomic_cas_strong_ptr(array->data,
42,381✔
223
                                    OBJID(ARRAY_STORAGE, old_buf),
224
                                    OBJID(ARRAY_STORAGE, new_buf));
225
}
42,381✔
226

227
KOS_OBJ_ID KOS_array_read(KOS_CONTEXT ctx, KOS_OBJ_ID obj_id, int idx)
5,328,798✔
228
{
229
    KOS_OBJ_ID elem = KOS_BADPTR;
5,328,798✔
230

231
    assert( ! IS_BAD_PTR(obj_id));
5,328,798✔
232

233
    if ((GET_OBJ_TYPE(obj_id) != OBJ_ARRAY) || kos_seq_fail())
5,328,798✔
234
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_not_array));
11,741✔
235
    else {
236
        const uint32_t size   = KOS_atomic_read_relaxed_u32(OBJPTR(ARRAY, obj_id)->size);
5,317,057✔
237
        const uint32_t bufidx = (idx < 0) ? ((uint32_t)idx + size) : (uint32_t)idx;
5,317,057✔
238

239
        if (bufidx < size) {
5,317,057✔
240
            KOS_ARRAY_STORAGE *buf = get_data(obj_id);
5,315,797✔
241

242
            for (;;) {
243
                elem = KOS_atomic_read_relaxed_obj(buf->buf[bufidx]);
1,207✔
244

245
                if (elem == TOMBSTONE) {
5,317,004✔
246
                    KOS_raise_exception(ctx, KOS_CONST_ID(str_err_invalid_index));
×
247
                    elem = KOS_BADPTR;
×
248
                    break;
×
249
                }
250

251
                if (elem == CLOSED)
5,317,004✔
252
                    buf = get_next(buf);
1,207✔
253
                else
254
                    break;
5,315,797✔
255
            }
256
        }
257
        else
258
            KOS_raise_exception(ctx, KOS_CONST_ID(str_err_invalid_index));
1,260✔
259
    }
260

261
    return elem;
5,328,798✔
262
}
263

264
int KOS_array_write(KOS_CONTEXT ctx, KOS_OBJ_ID obj_id, int idx, KOS_OBJ_ID value)
1,133,699✔
265
{
266
    int error = KOS_ERROR_EXCEPTION;
1,133,699✔
267

268
    assert( ! IS_BAD_PTR(obj_id));
1,133,699✔
269

270
    if (GET_OBJ_TYPE(obj_id) != OBJ_ARRAY)
1,133,699✔
271
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_not_array));
6✔
272
    else if (KOS_atomic_read_relaxed_u32(OBJPTR(ARRAY, obj_id)->flags) & KOS_READ_ONLY)
1,133,693✔
273
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_read_only));
10✔
274
    else {
275
        const uint32_t   size   = KOS_atomic_read_relaxed_u32(OBJPTR(ARRAY, obj_id)->size);
1,133,683✔
276
        const uint32_t   bufidx = (idx < 0) ? ((uint32_t)idx + size) : (uint32_t)idx;
1,133,683✔
277

278
        if (bufidx < size) {
1,133,683✔
279

280
            KOS_ARRAY_STORAGE *buf = get_data(obj_id);
1,133,679✔
281

282
            for (;;) {
2,036✔
283

284
                KOS_OBJ_ID cur = KOS_atomic_read_relaxed_obj(buf->buf[bufidx]);
1,135,715✔
285

286
                if (cur == TOMBSTONE) {
1,135,715✔
287
                    KOS_raise_exception(ctx, KOS_CONST_ID(str_err_invalid_index));
×
288
                    break;
×
289
                }
290

291
                if (cur == CLOSED) {
1,135,715✔
292
                    KOS_ARRAY_STORAGE *new_buf = get_next(buf);
1,759✔
293
                    copy_buf(ctx, OBJPTR(ARRAY, obj_id), buf, new_buf);
1,759✔
294
                    buf = new_buf;
1,759✔
295
                }
296
                else if (KOS_atomic_cas_weak_ptr(buf->buf[bufidx], cur, value)) {
1,133,956✔
297
                    error = KOS_SUCCESS;
1,133,679✔
298
                    break;
1,133,679✔
299
                }
300
            }
301
        }
302
        else
303
            KOS_raise_exception(ctx, KOS_CONST_ID(str_err_invalid_index));
4✔
304
    }
305

306
    return error;
1,133,699✔
307
}
308

309
KOS_OBJ_ID KOS_array_cas(KOS_CONTEXT ctx,
9✔
310
                         KOS_OBJ_ID  obj_id,
311
                         int         idx,
312
                         KOS_OBJ_ID  old_value,
313
                         KOS_OBJ_ID  new_value)
314
{
315
    KOS_OBJ_ID retval = KOS_BADPTR;
9✔
316

317
    assert( ! IS_BAD_PTR(obj_id));
9✔
318

319
    if (GET_OBJ_TYPE(obj_id) != OBJ_ARRAY)
9✔
320
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_not_array));
×
321
    else if (KOS_atomic_read_relaxed_u32(OBJPTR(ARRAY, obj_id)->flags) & KOS_READ_ONLY)
9✔
322
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_read_only));
1✔
323
    else {
324
        const uint32_t size   = KOS_atomic_read_relaxed_u32(OBJPTR(ARRAY, obj_id)->size);
8✔
325
        const uint32_t bufidx = (idx < 0) ? ((uint32_t)idx + size) : (uint32_t)idx;
8✔
326

327
        if (bufidx < size) {
8✔
328

329
            KOS_ARRAY_STORAGE *buf = get_data(obj_id);
6✔
330

331
            for (;;) {
×
332

333
                KOS_OBJ_ID cur = KOS_atomic_read_relaxed_obj(buf->buf[bufidx]);
6✔
334

335
                if (cur == TOMBSTONE) {
6✔
336
                    KOS_raise_exception(ctx, KOS_CONST_ID(str_err_invalid_index));
×
337
                    break;
×
338
                }
339

340
                if (cur == CLOSED) {
6✔
341
                    KOS_ARRAY_STORAGE *new_buf = get_next(buf);
×
342
                    copy_buf(ctx, OBJPTR(ARRAY, obj_id), buf, new_buf);
×
343
                    buf = new_buf;
×
344
                }
345
                else if (cur != old_value) {
6✔
346
                    retval = cur;
2✔
347
                    break;
2✔
348
                }
349
                else if (KOS_atomic_cas_weak_ptr(buf->buf[bufidx], cur, new_value)) {
4✔
350
                    retval = cur;
4✔
351
                    break;
4✔
352
                }
353
            }
354
        }
355
        else
356
            KOS_raise_exception(ctx, KOS_CONST_ID(str_err_invalid_index));
2✔
357
    }
358

359
    return retval;
9✔
360
}
361

362
static int resize_storage(KOS_CONTEXT ctx,
74,468✔
363
                          KOS_OBJ_ID  obj_id,
364
                          uint32_t    new_capacity)
365
{
366
    KOS_ARRAY_STORAGE *old_buf;
367
    KOS_ARRAY_STORAGE *new_buf;
368
    KOS_LOCAL          array;
369
    int                error = KOS_ERROR_EXCEPTION;
74,468✔
370

371
    KOS_init_local_with(ctx, &array, obj_id);
74,468✔
372

373
    new_buf = alloc_buffer(ctx, new_capacity);
74,468✔
374

375
    if (new_buf) {
74,468✔
376

377
        old_buf = get_data(array.o);
74,400✔
378

379
        atomic_fill_ptr(&new_buf->buf[0], KOS_atomic_read_relaxed_u32(new_buf->capacity), TOMBSTONE);
74,400✔
380

381
        if ( ! old_buf)
74,400✔
382
            (void)KOS_atomic_cas_strong_ptr(OBJPTR(ARRAY, array.o)->data, KOS_BADPTR, OBJID(ARRAY_STORAGE, new_buf));
33,778✔
383
        else if (KOS_atomic_cas_strong_ptr(old_buf->next, KOS_BADPTR, OBJID(ARRAY_STORAGE, new_buf)))
40,622✔
384
            copy_buf(ctx, OBJPTR(ARRAY, array.o), old_buf, new_buf);
40,622✔
385
        else {
386
            KOS_ARRAY_STORAGE *const buf = get_next(old_buf);
×
387

388
            copy_buf(ctx, OBJPTR(ARRAY, array.o), old_buf, buf);
×
389
        }
390

391
        error = KOS_SUCCESS;
74,400✔
392
    }
393

394
    KOS_destroy_top_local(ctx, &array);
74,468✔
395

396
    return error;
74,468✔
397
}
398

399
int kos_array_copy_storage(KOS_CONTEXT ctx,
1,241✔
400
                           KOS_OBJ_ID  obj_id)
401
{
402
    KOS_ARRAY_STORAGE *buf;
403
    uint32_t           capacity;
404

405
    assert( ! IS_BAD_PTR(obj_id));
1,241✔
406
    assert(GET_OBJ_TYPE(obj_id) == OBJ_ARRAY);
1,241✔
407

408
    buf      = get_data(obj_id);
1,241✔
409
    capacity = buf ? KOS_atomic_read_relaxed_u32(buf->capacity) : 0U;
1,241✔
410

411
    return resize_storage(ctx, obj_id, capacity);
1,241✔
412
}
413

414
int KOS_array_reserve(KOS_CONTEXT ctx, KOS_OBJ_ID obj_id, uint32_t new_capacity)
73,236✔
415
{
416
    int       error = KOS_ERROR_EXCEPTION;
73,236✔
417
    KOS_LOCAL array;
418

419
    assert( ! IS_BAD_PTR(obj_id));
73,236✔
420

421
    KOS_init_local_with(ctx, &array, obj_id);
73,236✔
422

423
    if (GET_OBJ_TYPE(array.o) != OBJ_ARRAY)
73,236✔
424
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_not_array));
5✔
425
    else if (KOS_atomic_read_relaxed_u32(OBJPTR(ARRAY, array.o)->flags) & KOS_READ_ONLY)
73,231✔
426
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_read_only));
3✔
427
    else {
428
        KOS_ARRAY_STORAGE *old_buf  = get_data(array.o);
73,228✔
429
        uint32_t           capacity = old_buf ? KOS_atomic_read_relaxed_u32(old_buf->capacity) : 0U;
73,228✔
430

431
        error = KOS_SUCCESS;
73,228✔
432

433
        while (new_capacity > capacity) {
146,387✔
434
            error = resize_storage(ctx, array.o, new_capacity);
73,227✔
435
            if (error)
73,227✔
436
                break;
68✔
437

438
            old_buf = get_data(array.o);
73,159✔
439
            assert(old_buf);
73,159✔
440
            capacity = KOS_atomic_read_relaxed_u32(old_buf->capacity);
73,159✔
441
        }
442
    }
443

444
    KOS_destroy_top_local(ctx, &array);
73,236✔
445

446
    return error;
73,236✔
447
}
448

449
int KOS_array_resize(KOS_CONTEXT ctx, KOS_OBJ_ID obj_id, uint32_t size)
157,504✔
450
{
451
    int       error = KOS_ERROR_EXCEPTION;
157,504✔
452
    KOS_LOCAL array;
453

454
    assert( ! IS_BAD_PTR(obj_id));
157,504✔
455

456
    KOS_init_local_with(ctx, &array, obj_id);
157,504✔
457

458
#ifdef CONFIG_MAD_GC
459
    error = kos_trigger_mad_gc(ctx);
460
    if (error) {
461
        KOS_destroy_top_local(ctx, &array);
462
        return error;
463
    }
464
    error = KOS_ERROR_EXCEPTION;
465
#endif
466

467
    if (GET_OBJ_TYPE(array.o) != OBJ_ARRAY)
157,504✔
468
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_not_array));
5✔
469
    else if (KOS_atomic_read_relaxed_u32(OBJPTR(ARRAY, array.o)->flags) & KOS_READ_ONLY)
157,499✔
470
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_read_only));
6✔
471
    else {
472
        KOS_ARRAY_STORAGE *buf      = get_data(array.o);
157,493✔
473
        const uint32_t     capacity = buf ? KOS_atomic_read_relaxed_u32(buf->capacity) : 0U;
157,493✔
474
        uint32_t           old_size;
475

476
        if (size > capacity) {
157,493✔
477
            const uint32_t double_cap = (capacity > UINT32_MAX / 2) ? UINT32_MAX : capacity * 2;
20,460✔
478
            const uint32_t new_cap    = KOS_max(double_cap, size);
20,460✔
479

480
            TRY(KOS_array_reserve(ctx, array.o, new_cap));
20,460✔
481

482
            buf = get_data(array.o);
20,439✔
483
        }
484

485
        old_size = KOS_atomic_swap_u32(OBJPTR(ARRAY, array.o)->size, size);
157,472✔
486

487
        if (size != old_size) {
157,472✔
488
            if (size > old_size) {
155,767✔
489
                const KOS_OBJ_ID void_obj = KOS_VOID;
31,057✔
490
                /* TODO try to improve this */
491
                KOS_ATOMIC(KOS_OBJ_ID) *ptr = &buf->buf[old_size];
31,057✔
492
                KOS_ATOMIC(KOS_OBJ_ID) *end = &buf->buf[size];
31,057✔
493
                while (ptr < end)
9,924,011✔
494
                    KOS_atomic_write_relaxed_ptr(*(ptr++), void_obj);
9,892,954✔
495
            }
496
            else {
497
                /* TODO try to improve this */
498
                KOS_ATOMIC(KOS_OBJ_ID) *ptr = &buf->buf[size];
124,710✔
499
                KOS_ATOMIC(KOS_OBJ_ID) *end = &buf->buf[old_size];
124,710✔
500
                while (ptr < end)
317,114✔
501
                    KOS_atomic_write_relaxed_ptr(*(ptr++), TOMBSTONE);
192,404✔
502
            }
503
        }
504

505
        error = KOS_SUCCESS;
157,472✔
506
    }
507

508
cleanup:
157,504✔
509
    KOS_destroy_top_local(ctx, &array);
157,504✔
510

511
    return error;
157,504✔
512
}
513

514
KOS_OBJ_ID KOS_array_slice(KOS_CONTEXT ctx,
414✔
515
                           KOS_OBJ_ID  obj_id,
516
                           int64_t     begin,
517
                           int64_t     end)
518
{
519
    KOS_LOCAL array;
520
    KOS_LOCAL ret;
521

522
    assert( ! IS_BAD_PTR(obj_id));
414✔
523

524
    KOS_init_local(ctx, &ret);
414✔
525
    KOS_init_local_with(ctx, &array, obj_id);
414✔
526

527
    if (GET_OBJ_TYPE(array.o) != OBJ_ARRAY)
414✔
528
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_not_array));
6✔
529
    else {
530
        const uint32_t len = KOS_get_array_size(array.o);
408✔
531

532
        ret.o = KOS_new_array(ctx, 0);
408✔
533

534
        if (len && ! IS_BAD_PTR(ret.o)) {
408✔
535

536
            uint32_t new_len;
537
            int64_t  new_len_64;
538

539
            begin = KOS_fix_index(begin, len);
315✔
540
            end   = KOS_fix_index(end, len);
315✔
541

542
            if (end < begin)
315✔
543
                end = begin;
2✔
544

545
            new_len_64 = end - begin;
315✔
546
            assert(new_len_64 <= 0xFFFFFFFF);
315✔
547
            new_len = (uint32_t)new_len_64;
315✔
548

549
            if (new_len) {
315✔
550

551
                KOS_ARRAY_STORAGE *dest_buf = alloc_buffer(ctx, new_len);
312✔
552

553
                if (dest_buf) {
312✔
554
                    KOS_ARRAY   *const new_array = OBJPTR(ARRAY, ret.o);
311✔
555
                    KOS_ARRAY_STORAGE *src_buf   = get_data(array.o);
311✔
556
                    uint32_t           idx       = 0;
311✔
557

558
                    KOS_ATOMIC(KOS_OBJ_ID) *dest = &dest_buf->buf[0];
311✔
559

560
                    KOS_atomic_write_relaxed_ptr(new_array->data, OBJID(ARRAY_STORAGE, dest_buf));
311✔
561

562
                    while (idx < new_len) {
1,862✔
563

564
                        const KOS_OBJ_ID value =
1,551✔
565
                            KOS_atomic_read_relaxed_obj(src_buf->buf[begin + idx]);
1,551✔
566

567
                        if (value == TOMBSTONE) {
1,551✔
568
                            new_len = idx;
×
569
                            break;
×
570
                        }
571

572
                        if (value == CLOSED) {
1,551✔
573
                            src_buf = get_next(src_buf);
×
574
                            continue;
×
575
                        }
576

577
                        KOS_atomic_write_relaxed_ptr(*(dest++), value);
1,551✔
578
                        ++idx;
1,551✔
579
                    }
580

581
                    KOS_atomic_write_relaxed_u32(new_array->size, new_len);
311✔
582

583
                    if (new_len < KOS_atomic_read_relaxed_u32(dest_buf->capacity))
311✔
584
                        atomic_fill_ptr(&dest_buf->buf[new_len],
234✔
585
                                        KOS_atomic_read_relaxed_u32(dest_buf->capacity) - new_len,
234✔
586
                                        TOMBSTONE);
234✔
587
                }
588
                else
589
                    ret.o = KOS_BADPTR;
1✔
590
            }
591
        }
592
    }
593

594
    return KOS_destroy_top_locals(ctx, &array, &ret);
414✔
595
}
596

597
int KOS_array_insert(KOS_CONTEXT ctx,
17,662✔
598
                     KOS_OBJ_ID  dest_obj_id,
599
                     int64_t     dest_begin,
600
                     int64_t     dest_end,
601
                     KOS_OBJ_ID  src_obj_id,
602
                     int64_t     src_begin,
603
                     int64_t     src_end)
604
{
605
    /* TODO rewrite lock-free */
606

607
    int                error   = KOS_SUCCESS;
17,662✔
608
    uint32_t           dest_len;
609
    uint32_t           src_len = 0;
17,662✔
610
    uint32_t           dest_delta;
611
    uint32_t           src_delta;
612
    KOS_LOCAL          src;
613
    KOS_LOCAL          dest;
614
    KOS_ARRAY_STORAGE *dest_buf;
615
    KOS_ARRAY_STORAGE *src_buf = KOS_NULL;
17,662✔
616

617
    assert( ! IS_BAD_PTR(src_obj_id));
17,662✔
618
    assert( ! IS_BAD_PTR(dest_obj_id));
17,662✔
619

620
    KOS_init_local_with(ctx, &dest, dest_obj_id);
17,662✔
621
    KOS_init_local_with(ctx, &src,  src_obj_id);
17,662✔
622

623
    if (GET_OBJ_TYPE(dest.o) != OBJ_ARRAY)
17,662✔
624
        RAISE_EXCEPTION_STR(str_err_not_array);
5✔
625
    else if (KOS_atomic_read_relaxed_u32(OBJPTR(ARRAY, dest.o)->flags) & KOS_READ_ONLY)
17,657✔
626
        RAISE_EXCEPTION_STR(str_err_read_only);
4✔
627
    else if (src_begin != src_end && GET_OBJ_TYPE(src.o) != OBJ_ARRAY)
17,653✔
628
        RAISE_EXCEPTION_STR(str_err_not_array);
5✔
629

630
    dest_len = KOS_get_array_size(dest.o);
17,648✔
631

632
    dest_begin = KOS_fix_index(dest_begin, dest_len);
17,648✔
633
    dest_end   = KOS_fix_index(dest_end, dest_len);
17,648✔
634

635
    if (dest_end < dest_begin)
17,648✔
636
        dest_end = dest_begin;
1✔
637

638
    dest_delta = (uint32_t)(dest_end - dest_begin);
17,648✔
639

640
    if (src_begin != src_end) {
17,648✔
641
        src_len   = KOS_get_array_size(src.o);
17,634✔
642
        src_begin = KOS_fix_index(src_begin, src_len);
17,634✔
643
        src_end   = KOS_fix_index(src_end, src_len);
17,634✔
644

645
        if (src_end < src_begin)
17,634✔
646
            src_end = src_begin;
1✔
647
    }
648

649
    src_delta = (uint32_t)(src_end - src_begin);
17,648✔
650

651
    if (src_delta > dest_delta) {
17,648✔
652
        const uint32_t new_len = dest_len - dest_delta + src_delta;
17,531✔
653

654
        if (new_len < src_delta) {
17,531✔
UNCOV
655
            KOS_raise_exception(ctx, KOS_STR_OUT_OF_MEMORY);
×
UNCOV
656
            RAISE_ERROR(KOS_ERROR_EXCEPTION);
×
657
        }
658

659
        TRY(KOS_array_resize(ctx, dest.o, new_len));
17,531✔
660
    }
661

662
    dest_buf = get_data(dest.o);
17,647✔
663
    if (src_begin != src_end)
17,647✔
664
        src_buf = get_data(src.o);
17,614✔
665

666
    if (src.o != dest.o || src_end <= dest_begin || src_begin >= dest_end || ! src_delta) {
17,647✔
667

668
        if (src_delta != dest_delta && dest_end < dest_len)
17,635✔
669
            kos_atomic_move_ptr((KOS_ATOMIC(void *) *)&dest_buf->buf[dest_end - dest_delta + src_delta],
15,876✔
670
                                (KOS_ATOMIC(void *) *)&dest_buf->buf[dest_end],
15,876✔
671
                                (unsigned)(dest_len - dest_end));
672

673
        if (src.o == dest.o && src_begin >= dest_end)
17,635✔
674
            src_begin += src_delta - dest_delta;
3✔
675

676
        if (src_delta)
17,635✔
677
            kos_atomic_move_ptr((KOS_ATOMIC(void *) *)&dest_buf->buf[dest_begin],
17,602✔
678
                                (KOS_ATOMIC(void *) *)&src_buf->buf[src_begin],
17,602✔
679
                                src_delta);
680
    }
681
    else if (dest_delta >= src_delta) {
12✔
682

683
        if (src_begin != dest_begin)
3✔
684
            kos_atomic_move_ptr((KOS_ATOMIC(void *) *)&dest_buf->buf[dest_begin],
2✔
685
                                (KOS_ATOMIC(void *) *)&dest_buf->buf[src_begin],
2✔
686
                                src_delta);
687

688
        if (dest_end < dest_len)
3✔
689
            kos_atomic_move_ptr((KOS_ATOMIC(void *) *)&dest_buf->buf[dest_begin + src_delta],
1✔
690
                                (KOS_ATOMIC(void *) *)&dest_buf->buf[dest_end],
1✔
691
                                (unsigned)(dest_len - dest_end));
692
    }
693
    else {
694

695
        const int64_t mid = KOS_min(dest_begin + src_delta, src_end);
9✔
696

697
        if (dest_end < dest_len)
9✔
698
            kos_atomic_move_ptr((KOS_ATOMIC(void *) *)&dest_buf->buf[dest_begin + src_delta],
6✔
699
                                (KOS_ATOMIC(void *) *)&dest_buf->buf[dest_end],
6✔
700
                                (unsigned)(dest_len - dest_end));
701
        if (mid > src_begin)
9✔
702
            kos_atomic_move_ptr((KOS_ATOMIC(void *) *)&dest_buf->buf[dest_begin],
9✔
703
                                (KOS_ATOMIC(void *) *)&dest_buf->buf[src_begin],
9✔
704
                                (unsigned)(mid - src_begin));
705

706
        if (mid < src_end)
9✔
707
            kos_atomic_move_ptr((KOS_ATOMIC(void *) *)&dest_buf->buf[dest_begin + mid - src_begin],
1✔
708
                                (KOS_ATOMIC(void *) *)&dest_buf->buf[mid + src_delta - dest_delta],
1✔
709
                                (unsigned)(src_end - mid));
710
    }
711

712
    if (src_delta < dest_delta)
17,647✔
713
        TRY(KOS_array_resize(ctx, dest.o, dest_len - dest_delta + src_delta));
19✔
714

715
cleanup:
17,647✔
716
    KOS_destroy_top_locals(ctx, &src, &dest);
17,662✔
717

718
    return error;
17,662✔
719
}
720

721
int KOS_array_push(KOS_CONTEXT ctx,
465,328✔
722
                   KOS_OBJ_ID  obj_id,
723
                   KOS_OBJ_ID  value_id,
724
                   uint32_t   *idx)
725
{
726
    int                error = KOS_SUCCESS;
465,328✔
727
    KOS_LOCAL          array;
728
    KOS_LOCAL          value;
729
    KOS_ARRAY_STORAGE *buf;
730
    uint32_t           len;
731

732
    assert( ! IS_BAD_PTR(obj_id));
465,328✔
733

734
    KOS_init_local_with(ctx, &value, value_id);
465,328✔
735
    KOS_init_local_with(ctx, &array, obj_id);
465,328✔
736

737
    if (GET_OBJ_TYPE(array.o) != OBJ_ARRAY)
465,328✔
738
        RAISE_EXCEPTION_STR(str_err_not_array);
10✔
739
    else if (KOS_atomic_read_relaxed_u32(OBJPTR(ARRAY, array.o)->flags) & KOS_READ_ONLY)
465,318✔
740
        RAISE_EXCEPTION_STR(str_err_read_only);
2✔
741

742
    buf = get_data(array.o);
465,316✔
743

744
    /* Increment index */
745
    for (;;) {
52,712✔
746

747
        const uint32_t capacity = buf ? KOS_atomic_read_relaxed_u32(buf->capacity) : 0;
518,028✔
748

749
        len = KOS_get_array_size(array.o);
518,028✔
750

751
        if (len == UINT32_MAX) {
518,028✔
UNCOV
752
            KOS_raise_exception(ctx, KOS_STR_OUT_OF_MEMORY);
×
UNCOV
753
            RAISE_ERROR(KOS_ERROR_OUT_OF_MEMORY);
×
754
        }
755

756
        if (len >= capacity) {
518,028✔
757
            const uint32_t double_cap = (capacity > UINT32_MAX / 2) ? UINT32_MAX : capacity * 2;
52,759✔
758
            const uint32_t new_cap    = KOS_max(double_cap, len + 1);
52,759✔
759

760
            TRY(KOS_array_reserve(ctx, array.o, new_cap));
52,759✔
761

762
            buf = get_data(array.o);
52,712✔
763
            continue;
52,712✔
764
        }
765

766
        if (KOS_atomic_cas_weak_u32(OBJPTR(ARRAY, array.o)->size, len, len+1))
465,269✔
767
            break;
465,269✔
768
    }
769

770
    /* Write new value */
UNCOV
771
    for (;;) {
×
772

773
        const KOS_OBJ_ID cur_value = KOS_atomic_read_relaxed_ptr(buf->buf[len]);
465,269✔
774

775
        if (cur_value == CLOSED) {
465,269✔
UNCOV
776
            buf = get_next(buf);
×
UNCOV
777
            continue;
×
778
        }
779

780
        /* TODO What if cur_value != TOMBSTONE ??? ABA? */
781

782
        if (KOS_atomic_cas_weak_ptr(buf->buf[len], cur_value, value.o))
465,269✔
783
            break;
465,269✔
784
    }
785

786
    if (idx)
465,269✔
787
        *idx = len;
201,540✔
788

789
cleanup:
263,729✔
790
    KOS_destroy_top_locals(ctx, &array, &value);
465,328✔
791

792
    return error;
465,328✔
793
}
794

795
KOS_OBJ_ID KOS_array_pop(KOS_CONTEXT ctx,
49✔
796
                         KOS_OBJ_ID  obj_id)
797
{
798
    int       error = KOS_SUCCESS;
49✔
799
    uint32_t  len;
800
    KOS_LOCAL array;
801
    KOS_LOCAL ret;
802

803
    assert( ! IS_BAD_PTR(obj_id));
49✔
804

805
    KOS_init_local(ctx, &ret);
49✔
806
    KOS_init_local_with(ctx, &array, obj_id);
49✔
807

808
    if (GET_OBJ_TYPE(array.o) != OBJ_ARRAY)
49✔
809
        RAISE_EXCEPTION_STR(str_err_not_array);
7✔
810
    else if (KOS_atomic_read_relaxed_u32(OBJPTR(ARRAY, array.o)->flags) & KOS_READ_ONLY)
42✔
811
        RAISE_EXCEPTION_STR(str_err_read_only);
1✔
812

813
    len = KOS_get_array_size(array.o);
41✔
814

815
    if (len == 0)
41✔
816
        RAISE_EXCEPTION_STR(str_err_empty);
4✔
817

818
    ret.o = KOS_array_read(ctx, array.o, (int)len-1);
37✔
819
    TRY_OBJID(ret.o);
37✔
820

821
    error = KOS_array_resize(ctx, array.o, len-1);
37✔
822

823
cleanup:
49✔
824
    ret.o = KOS_destroy_top_locals(ctx, &array, &ret);
49✔
825

826
    return error ? KOS_BADPTR : ret.o;
49✔
827
}
828

829
int KOS_array_fill(KOS_CONTEXT ctx,
26✔
830
                   KOS_OBJ_ID  obj_id,
831
                   int64_t     begin,
832
                   int64_t     end,
833
                   KOS_OBJ_ID  value)
834
{
835
    uint32_t           len;
836
    KOS_ARRAY_STORAGE *buf;
837

838
    if (GET_OBJ_TYPE(obj_id) != OBJ_ARRAY) {
26✔
839
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_not_array));
1✔
840
        return KOS_ERROR_EXCEPTION;
1✔
841
    }
842
    else if (KOS_atomic_read_relaxed_u32(OBJPTR(ARRAY, obj_id)->flags) & KOS_READ_ONLY) {
25✔
843
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_read_only));
1✔
844
        return KOS_ERROR_EXCEPTION;
1✔
845
    }
846

847
    len = KOS_get_array_size(obj_id);
24✔
848

849
    begin = KOS_fix_index(begin, len);
24✔
850
    end   = KOS_fix_index(end, len);
24✔
851

852
    buf = get_data(obj_id);
24✔
853

854
    while (begin < end) {
65,636✔
855

856
        KOS_OBJ_ID cur = KOS_atomic_read_relaxed_obj(buf->buf[begin]);
65,612✔
857

858
        if (cur == TOMBSTONE)
65,612✔
UNCOV
859
            break;
×
860

861
        if (cur == CLOSED) {
65,612✔
UNCOV
862
            KOS_ARRAY_STORAGE *new_buf = get_next(buf);
×
UNCOV
863
            copy_buf(ctx, OBJPTR(ARRAY, obj_id), buf, new_buf);
×
UNCOV
864
            buf = new_buf;
×
865
        }
866
        else {
867
            if (KOS_atomic_cas_weak_ptr(buf->buf[begin], cur, value))
65,612✔
868
                ++begin;
65,612✔
869
        }
870
    }
871

872
    return KOS_SUCCESS;
24✔
873
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc