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

kos-lang / kos / 28430187190

30 Jun 2026 08:12AM UTC coverage: 95.174% (-0.006%) from 95.18%
28430187190

push

github

cdragan
re: fix too many character class slots

8 of 10 new or added lines in 1 file covered. (80.0%)

10 existing lines in 2 files now uncovered.

24672 of 25923 relevant lines covered (95.17%)

937162.14 hits per line

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

93.57
/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,
615,507✔
39
                            unsigned                count,
40
                            KOS_OBJ_ID              value)
41
{
42
    KOS_ATOMIC(KOS_OBJ_ID) *const end = dest + count;
615,507✔
43
    while (dest < end) {
20,968,173✔
44
        KOS_atomic_write_relaxed_ptr(*dest, value);
20,352,666✔
45
        ++dest;
20,352,666✔
46
    }
47
}
615,507✔
48

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

54
    if (capacity < KOS_MAX_ARRAY_SIZE)
199,765✔
55
        buf = (KOS_ARRAY_STORAGE *)kos_alloc_object(ctx,
199,764✔
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,765✔
63
        assert(kos_get_object_type(buf->header) == OBJ_ARRAY_STORAGE);
199,691✔
64

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

71
    return buf;
199,765✔
72
}
73

74
KOS_OBJ_ID KOS_new_array(KOS_CONTEXT ctx,
387,175✔
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);
387,175✔
78
    const uint32_t buf_alloc_size = size ? KOS_buffer_alloc_size(size) : 0;
387,175✔
79
    const int      buf_built_in   = array_obj_size + buf_alloc_size <= 256U;
387,175✔
80
    const uint32_t alloc_size     = buf_built_in ? array_obj_size + buf_alloc_size : array_obj_size;
387,175✔
81
    KOS_ARRAY     *array          = KOS_NULL;
387,175✔
82

83
    if (size < KOS_MAX_ARRAY_SIZE)
387,175✔
84
        array = (KOS_ARRAY *)kos_alloc_object(ctx,
387,170✔
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) {
387,175✔
92
        KOS_ARRAY_STORAGE *storage = KOS_NULL;
387,098✔
93

94
        KOS_atomic_write_relaxed_u32(array->flags, 0);
387,098✔
95

96
        if (buf_built_in) {
387,098✔
97
            if (buf_alloc_size) {
262,095✔
98

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

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

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

108
                kos_set_object_size(array->header, array_obj_size);
202,242✔
109

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

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

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

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

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

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

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

135
        if (array) {
387,098✔
136

137
            KOS_atomic_write_relaxed_u32(array->size, size);
387,093✔
138

139
            if (storage) {
387,093✔
140
                const uint32_t capacity = KOS_atomic_read_relaxed_u32(storage->capacity);
327,240✔
141

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

145
                if (size < capacity)
327,240✔
146
                    atomic_fill_ptr(&storage->buf[size], capacity - size, TOMBSTONE);
213,651✔
147
            }
148
        }
149
    }
150

151
    return OBJID(ARRAY, array);
387,175✔
152
}
153

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

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

166
static void copy_buf(KOS_CONTEXT        ctx,
42,326✔
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,326✔
172
    KOS_ATOMIC(KOS_OBJ_ID) *dst      = &new_buf->buf[0];
42,326✔
173
    const uint32_t          capacity = KOS_atomic_read_relaxed_u32(old_buf->capacity);
42,326✔
174
    const uint32_t          fuzz     = KOS_atomic_read_relaxed_u32(old_buf->num_slots_open);
42,326✔
175
    uint32_t                i        = (capacity - fuzz) % capacity;
42,326✔
176

177
    for (;;) {
990,006✔
178
        KOS_OBJ_ID in_dst   = TOMBSTONE;
1,032,332✔
179
        int        salvaged = 0;
1,032,332✔
180

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

185
            /* Another thread copied it */
186
            if (value == CLOSED)
1,034,341✔
187
                break;
45,877✔
188

189
            /* Write value to new buffer */
190
            if ( ! KOS_atomic_cas_strong_ptr(dst[i], in_dst, value))
988,464✔
191
                /* Another thread wrote something to dest */
192
                break;
286,717✔
193
            in_dst = value;
701,747✔
194

195
            /* Close the slot in the old buffer */
196
            if (KOS_atomic_cas_weak_ptr(src[i], value, CLOSED)) {
701,747✔
197
                salvaged = 1;
699,738✔
198
                break;
699,738✔
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,032,332✔
210
            break;
1,728✔
211

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

216
        /* Try next slot */
217
        ++i;
990,006✔
218
        if (i == capacity)
990,006✔
219
            i = 0;
209✔
220
    }
221

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

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

231
    assert( ! IS_BAD_PTR(obj_id));
5,200,327✔
232

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

239
        if (bufidx < size) {
5,188,580✔
240
            KOS_ARRAY_STORAGE *buf = get_data(obj_id);
5,187,320✔
241

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

245
                if (elem == TOMBSTONE) {
5,188,777✔
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,188,777✔
252
                    buf = get_next(buf);
1,457✔
253
                else
254
                    break;
5,187,320✔
255
            }
256
        }
257
        else
258
            KOS_raise_exception(ctx, KOS_CONST_ID(str_err_invalid_index));
1,260✔
259
    }
260

261
    return elem;
5,200,327✔
262
}
263

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

268
    assert( ! IS_BAD_PTR(obj_id));
1,098,639✔
269

270
    if (GET_OBJ_TYPE(obj_id) != OBJ_ARRAY)
1,098,639✔
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,098,633✔
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,098,623✔
276
        const uint32_t   bufidx = (idx < 0) ? ((uint32_t)idx + size) : (uint32_t)idx;
1,098,623✔
277

278
        if (bufidx < size) {
1,098,623✔
279

280
            KOS_ARRAY_STORAGE *buf = get_data(obj_id);
1,098,619✔
281

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

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

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

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

306
    return error;
1,098,639✔
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,450✔
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,450✔
370

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

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

375
    if (new_buf) {
74,450✔
376

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

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

381
        if ( ! old_buf)
74,382✔
382
            (void)KOS_atomic_cas_strong_ptr(OBJPTR(ARRAY, array.o)->data, KOS_BADPTR, OBJID(ARRAY_STORAGE, new_buf));
33,784✔
383
        else if (KOS_atomic_cas_strong_ptr(old_buf->next, KOS_BADPTR, OBJID(ARRAY_STORAGE, new_buf)))
40,598✔
384
            copy_buf(ctx, OBJPTR(ARRAY, array.o), old_buf, new_buf);
40,598✔
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,382✔
392
    }
393

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

396
    return error;
74,450✔
397
}
398

399
int kos_array_copy_storage(KOS_CONTEXT ctx,
1,221✔
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,221✔
406
    assert(GET_OBJ_TYPE(obj_id) == OBJ_ARRAY);
1,221✔
407

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

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

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

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

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

423
    if (GET_OBJ_TYPE(array.o) != OBJ_ARRAY)
73,238✔
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,233✔
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,230✔
429
        uint32_t           capacity = old_buf ? KOS_atomic_read_relaxed_u32(old_buf->capacity) : 0U;
73,230✔
430

431
        error = KOS_SUCCESS;
73,230✔
432

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

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

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

446
    return error;
73,238✔
447
}
448

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

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

456
    KOS_init_local_with(ctx, &array, obj_id);
157,555✔
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,555✔
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,550✔
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,544✔
473
        const uint32_t     capacity = buf ? KOS_atomic_read_relaxed_u32(buf->capacity) : 0U;
157,544✔
474
        uint32_t           old_size;
475

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

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

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

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

487
        if (size != old_size) {
157,523✔
488
            if (size > old_size) {
155,821✔
489
                const KOS_OBJ_ID void_obj = KOS_VOID;
31,065✔
490
                /* TODO try to improve this */
491
                KOS_ATOMIC(KOS_OBJ_ID) *ptr = &buf->buf[old_size];
31,065✔
492
                KOS_ATOMIC(KOS_OBJ_ID) *end = &buf->buf[size];
31,065✔
493
                while (ptr < end)
9,914,079✔
494
                    KOS_atomic_write_relaxed_ptr(*(ptr++), void_obj);
9,883,014✔
495
            }
496
            else {
497
                /* TODO try to improve this */
498
                KOS_ATOMIC(KOS_OBJ_ID) *ptr = &buf->buf[size];
124,756✔
499
                KOS_ATOMIC(KOS_OBJ_ID) *end = &buf->buf[old_size];
124,756✔
500
                while (ptr < end)
317,232✔
501
                    KOS_atomic_write_relaxed_ptr(*(ptr++), TOMBSTONE);
192,476✔
502
            }
503
        }
504

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

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

511
    return error;
157,555✔
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,667✔
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,667✔
608
    uint32_t           dest_len;
609
    uint32_t           src_len = 0;
17,667✔
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,667✔
616

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

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

623
    if (GET_OBJ_TYPE(dest.o) != OBJ_ARRAY)
17,667✔
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,662✔
626
        RAISE_EXCEPTION_STR(str_err_read_only);
4✔
627
    else if (src_begin != src_end && GET_OBJ_TYPE(src.o) != OBJ_ARRAY)
17,658✔
628
        RAISE_EXCEPTION_STR(str_err_not_array);
5✔
629

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

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

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

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

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

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

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

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

654
        if (new_len < src_delta) {
17,536✔
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,536✔
660
    }
661

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

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

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

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

676
        if (src_delta)
17,640✔
677
            kos_atomic_move_ptr((KOS_ATOMIC(void *) *)&dest_buf->buf[dest_begin],
17,607✔
678
                                (KOS_ATOMIC(void *) *)&src_buf->buf[src_begin],
17,607✔
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,652✔
713
        TRY(KOS_array_resize(ctx, dest.o, dest_len - dest_delta + src_delta));
19✔
714

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

718
    return error;
17,667✔
719
}
720

721
int KOS_array_push(KOS_CONTEXT ctx,
450,446✔
722
                   KOS_OBJ_ID  obj_id,
723
                   KOS_OBJ_ID  value_id,
724
                   uint32_t   *idx)
725
{
726
    int                error = KOS_SUCCESS;
450,446✔
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));
450,446✔
733

734
    KOS_init_local_with(ctx, &value, value_id);
450,446✔
735
    KOS_init_local_with(ctx, &array, obj_id);
450,446✔
736

737
    if (GET_OBJ_TYPE(array.o) != OBJ_ARRAY)
450,446✔
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)
450,436✔
740
        RAISE_EXCEPTION_STR(str_err_read_only);
2✔
741

742
    buf = get_data(array.o);
450,434✔
743

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

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

749
        len = KOS_get_array_size(array.o);
503,144✔
750

751
        if (len >= capacity) {
503,144✔
752
            const uint32_t double_cap = (capacity > UINT32_MAX / 2) ? UINT32_MAX : capacity * 2;
52,757✔
753
            const uint32_t new_cap    = KOS_max(double_cap, len + 1);
52,757✔
754

755
            TRY(KOS_array_reserve(ctx, array.o, new_cap));
52,757✔
756

757
            buf = get_data(array.o);
52,710✔
758
            continue;
52,710✔
759
        }
760

761
        if (KOS_atomic_cas_weak_u32(OBJPTR(ARRAY, array.o)->size, len, len+1))
450,387✔
762
            break;
450,387✔
763
    }
764

765
    /* Write new value */
UNCOV
766
    for (;;) {
×
767

768
        const KOS_OBJ_ID cur_value = KOS_atomic_read_relaxed_ptr(buf->buf[len]);
450,387✔
769

770
        if (cur_value == CLOSED) {
450,387✔
UNCOV
771
            buf = get_next(buf);
×
UNCOV
772
            continue;
×
773
        }
774

775
        /* TODO What if cur_value != TOMBSTONE ??? ABA? */
776

777
        if (KOS_atomic_cas_weak_ptr(buf->buf[len], cur_value, value.o))
450,387✔
778
            break;
450,387✔
779
    }
780

781
    if (idx)
450,387✔
782
        *idx = len;
186,583✔
783

784
cleanup:
263,804✔
785
    KOS_destroy_top_locals(ctx, &array, &value);
450,446✔
786

787
    return error;
450,446✔
788
}
789

790
KOS_OBJ_ID KOS_array_pop(KOS_CONTEXT ctx,
49✔
791
                         KOS_OBJ_ID  obj_id)
792
{
793
    int       error = KOS_SUCCESS;
49✔
794
    uint32_t  len;
795
    KOS_LOCAL array;
796
    KOS_LOCAL ret;
797

798
    assert( ! IS_BAD_PTR(obj_id));
49✔
799

800
    KOS_init_local(ctx, &ret);
49✔
801
    KOS_init_local_with(ctx, &array, obj_id);
49✔
802

803
    if (GET_OBJ_TYPE(array.o) != OBJ_ARRAY)
49✔
804
        RAISE_EXCEPTION_STR(str_err_not_array);
7✔
805
    else if (KOS_atomic_read_relaxed_u32(OBJPTR(ARRAY, array.o)->flags) & KOS_READ_ONLY)
42✔
806
        RAISE_EXCEPTION_STR(str_err_read_only);
1✔
807

808
    len = KOS_get_array_size(array.o);
41✔
809

810
    if (len == 0)
41✔
811
        RAISE_EXCEPTION_STR(str_err_empty);
4✔
812

813
    ret.o = KOS_array_read(ctx, array.o, (int)len-1);
37✔
814
    TRY_OBJID(ret.o);
37✔
815

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

818
cleanup:
49✔
819
    ret.o = KOS_destroy_top_locals(ctx, &array, &ret);
49✔
820

821
    return error ? KOS_BADPTR : ret.o;
49✔
822
}
823

824
int KOS_array_fill(KOS_CONTEXT ctx,
26✔
825
                   KOS_OBJ_ID  obj_id,
826
                   int64_t     begin,
827
                   int64_t     end,
828
                   KOS_OBJ_ID  value)
829
{
830
    uint32_t           len;
831
    KOS_ARRAY_STORAGE *buf;
832

833
    if (GET_OBJ_TYPE(obj_id) != OBJ_ARRAY) {
26✔
834
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_not_array));
1✔
835
        return KOS_ERROR_EXCEPTION;
1✔
836
    }
837
    else if (KOS_atomic_read_relaxed_u32(OBJPTR(ARRAY, obj_id)->flags) & KOS_READ_ONLY) {
25✔
838
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_read_only));
1✔
839
        return KOS_ERROR_EXCEPTION;
1✔
840
    }
841

842
    len = KOS_get_array_size(obj_id);
24✔
843

844
    begin = KOS_fix_index(begin, len);
24✔
845
    end   = KOS_fix_index(end, len);
24✔
846

847
    buf = get_data(obj_id);
24✔
848

849
    while (begin < end) {
65,636✔
850

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

853
        if (cur == TOMBSTONE)
65,612✔
UNCOV
854
            break;
×
855

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

867
    return KOS_SUCCESS;
24✔
868
}
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