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

kos-lang / kos / 6397241498

03 Oct 2023 06:56PM UTC coverage: 95.803% (-0.03%) from 95.833%
6397241498

push

github

Chris Dragan
net: raise exception on unexpected address family

5 of 5 new or added lines in 1 file covered. (100.0%)

24171 of 25230 relevant lines covered (95.8%)

1020356.76 hits per line

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

97.09
/core/kos_string.c
1
/* SPDX-License-Identifier: MIT
2
 * Copyright (c) 2014-2023 Chris Dragan
3
 */
4

5
#include "../inc/kos_string.h"
6
#include "../inc/kos_array.h"
7
#include "../inc/kos_atomic.h"
8
#include "../inc/kos_buffer.h"
9
#include "../inc/kos_constants.h"
10
#include "../inc/kos_error.h"
11
#include "../inc/kos_instance.h"
12
#include "../inc/kos_memory.h"
13
#include "../inc/kos_utf8.h"
14
#include "../inc/kos_utils.h"
15
#include "kos_heap.h"
16
#include "kos_math.h"
17
#include "kos_object_internal.h"
18
#include "kos_try.h"
19
#include "kos_unicode.h"
20
#include <assert.h>
21
#include <stdio.h>
22
#include <string.h>
23

24
KOS_DECLARE_STATIC_CONST_STRING(str_err_array_too_large,      "input array too large");
25
KOS_DECLARE_STATIC_CONST_STRING(str_err_buffer_too_large,     "input buffer too large");
26
KOS_DECLARE_STATIC_CONST_STRING(str_err_invalid_buffer_index, "buffer index is out of range");
27
KOS_DECLARE_STATIC_CONST_STRING(str_err_invalid_char_code,    "invalid character code");
28
KOS_DECLARE_STATIC_CONST_STRING(str_err_invalid_index,        "string index is out of range");
29
KOS_DECLARE_STATIC_CONST_STRING(str_err_invalid_string,       "invalid string");
30
KOS_DECLARE_STATIC_CONST_STRING(str_err_invalid_utf8,         "invalid UTF-8 sequence");
31
KOS_DECLARE_STATIC_CONST_STRING(str_err_not_array,            "object is not an array");
32
KOS_DECLARE_STATIC_CONST_STRING(str_err_not_string,           "object is not a string");
33
KOS_DECLARE_STATIC_CONST_STRING(str_err_string_too_long,      "string too long");
34
KOS_DECLARE_STATIC_CONST_STRING(str_err_too_many_repeats,     "repeated string too long");
35

36
#ifdef CONFIG_STRING16
37
#define override_elem_size(size) do { (size) = ((size) & KOS_STRING_ELEM_MASK) < KOS_STRING_ELEM_16 ? KOS_STRING_ELEM_16 : (size); } while (0)
38
#elif defined(CONFIG_STRING32)
39
#define override_elem_size(size) do { (size) = KOS_STRING_ELEM_32; } while (0)
40
#else
41
#define override_elem_size(size) do { } while (0)
42
#endif
43

44
static KOS_STRING_FLAGS string_size_from_max_code(uint32_t max_code)
1,111,720✔
45
{
46
#ifdef CONFIG_STRING16
47
    if (max_code < 0x100U)
48
        max_code = 0x100U;
49
#elif defined(CONFIG_STRING32)
50
    if (max_code < 0x10000U)
51
        max_code = 0x10000U;
52
#endif
53

54
    if (max_code < 0x80U)
1,111,720✔
55
        return KOS_STRING_ASCII;
1,105,830✔
56
    else if (max_code < 0x100U)
5,890✔
57
        return KOS_STRING_ELEM_8;
14✔
58
    else if (max_code < 0x10000U)
5,876✔
59
        return KOS_STRING_ELEM_16;
5,839✔
60
    else
61
        return KOS_STRING_ELEM_32;
37✔
62
}
63

64
static KOS_STRING *new_empty_string(KOS_CONTEXT      ctx,
20,683✔
65
                                    unsigned         length,
66
                                    KOS_STRING_FLAGS elem_size)
67
{
68
    KOS_STRING *str;
69

70
    assert(length <= 0xFFFFU);
20,683✔
71
    assert(length > 0U);
20,683✔
72

73
    str = (KOS_STRING *)kos_alloc_object(ctx,
20,683✔
74
                                         KOS_ALLOC_MOVABLE,
75
                                         OBJ_STRING,
76
                                         sizeof(KOS_STR_HEADER) + (length << elem_size));
20,683✔
77

78
    if (str) {
20,683✔
79
        assert(kos_get_object_type(str->header) == OBJ_STRING);
20,658✔
80
        str->header.flags  = (uint8_t)elem_size | (uint8_t)KOS_STRING_LOCAL;
20,658✔
81
        str->header.length = (uint16_t)length;
20,658✔
82
        str->header.hash   = 0;
20,658✔
83
    }
84

85
    return str;
20,683✔
86
}
87

88
static KOS_OBJ_ID new_string(KOS_CONTEXT     ctx,
1,130,360✔
89
                             const char     *s,
90
                             unsigned        length,
91
                             KOS_UTF8_ESCAPE escape)
92
{
93
    KOS_STRING      *str       = KOS_NULL;
1,130,360✔
94
    KOS_STRING_FLAGS elem_size = KOS_STRING_ELEM_8;
1,130,360✔
95

96
    if (length > 4U * 0xFFFFU)
1,130,360✔
97
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_string_too_long));
1✔
98

99
    else if (length) {
1,130,360✔
100
        uint32_t max_code;
101
        unsigned count = KOS_utf8_get_len(s, length, escape, &max_code);
1,127,120✔
102

103
        if (count < 0xFFFFU) {
1,125,960✔
104

105
            elem_size = string_size_from_max_code(max_code);
1,111,110✔
106

107
            str = (KOS_STRING *)kos_alloc_object(ctx,
1,111,110✔
108
                                                 KOS_ALLOC_MOVABLE,
109
                                                 OBJ_STRING,
110
                                                 sizeof(KOS_STR_HEADER) + (length << elem_size));
1,111,110✔
111
        }
112
        else
113
            KOS_raise_exception(ctx, count == ~0U ?
29,694✔
114
                                     KOS_CONST_ID(str_err_invalid_utf8) :
14,846✔
115
                                     KOS_CONST_ID(str_err_string_too_long));
1✔
116

117
        if (str) {
1,126,090✔
118

119
            void *ptr;
120

121
            assert(kos_get_object_type(str->header) == OBJ_STRING);
1,110,860✔
122

123
            str->header.flags  = (uint8_t)elem_size | (uint8_t)KOS_STRING_LOCAL;
1,110,860✔
124
            str->header.length = (uint16_t)count;
1,110,860✔
125
            str->header.hash   = 0;
1,110,860✔
126

127
            ptr = (void *)kos_get_string_buffer(str);
1,110,860✔
128

129
            if ((elem_size & KOS_STRING_ELEM_MASK) == KOS_STRING_ELEM_8)
1,110,860✔
130
                KOS_utf8_decode_8(s, length, escape, (uint8_t *)ptr);
1,104,500✔
131
            else if (elem_size == KOS_STRING_ELEM_16)
6,363✔
132
                KOS_utf8_decode_16(s, length, escape, (uint16_t *)ptr);
5,833✔
133
            else {
134
                assert(elem_size == KOS_STRING_ELEM_32);
530✔
135
                KOS_utf8_decode_32(s, length, escape, (uint32_t *)ptr);
530✔
136
            }
137
        }
138
    }
139
    else
140
        str = OBJPTR(STRING, KOS_STR_EMPTY);
3,237✔
141

142
    return OBJID(STRING, str);
1,129,870✔
143
}
144

145
KOS_OBJ_ID KOS_new_cstring(KOS_CONTEXT ctx, const char *utf8_str)
91,825✔
146
{
147
    return new_string(ctx, utf8_str, utf8_str ? (unsigned)strlen(utf8_str) : 0U, KOS_UTF8_NO_ESCAPE);
91,825✔
148
}
149

150
KOS_OBJ_ID KOS_new_string(KOS_CONTEXT ctx, const char *utf8_str, unsigned length)
728,110✔
151
{
152
    return new_string(ctx, utf8_str, length, KOS_UTF8_NO_ESCAPE);
728,110✔
153
}
154

155
KOS_OBJ_ID KOS_new_string_esc(KOS_CONTEXT ctx, const char *utf8_str, unsigned length)
310,395✔
156
{
157
    return new_string(ctx, utf8_str, length, KOS_UTF8_WITH_ESCAPE);
310,395✔
158
}
159

160
KOS_OBJ_ID KOS_new_const_ascii_cstring(KOS_CONTEXT ctx,
979✔
161
                                       const char *ascii_str)
162
{
163
    return KOS_new_const_string(ctx, ascii_str, ascii_str ? (unsigned)strlen(ascii_str) : 0U, KOS_STRING_ASCII);
979✔
164
}
165

166
KOS_OBJ_ID KOS_new_const_ascii_string(KOS_CONTEXT ctx,
3,898✔
167
                                      const char *ascii_str,
168
                                      unsigned    length)
169
{
170
    return KOS_new_const_string(ctx, ascii_str, length, KOS_STRING_ASCII);
3,898✔
171
}
172

173
KOS_OBJ_ID KOS_new_const_string(KOS_CONTEXT      ctx,
4,936✔
174
                                const void      *str_data,
175
                                unsigned         length,
176
                                KOS_STRING_FLAGS elem_size)
177
{
178
    KOS_STRING *str;
179

180
    assert(length <= 0xFFFFU);
4,936✔
181
    assert((elem_size & KOS_STRING_ELEM_MASK) <= KOS_STRING_ELEM_32);
4,936✔
182

183
    if (length) {
4,936✔
184
        str = (KOS_STRING *)kos_alloc_object(ctx,
4,918✔
185
                                             KOS_ALLOC_MOVABLE,
186
                                             OBJ_STRING,
187
                                             sizeof(struct KOS_STRING_PTR_S));
188

189
        if (str) {
4,918✔
190
            assert(kos_get_object_type(str->header) == OBJ_STRING);
4,912✔
191

192
            str->header.flags  = (uint8_t)elem_size | (uint8_t)KOS_STRING_PTR;
4,912✔
193
            str->header.length = (uint16_t)length;
4,912✔
194
            str->header.hash   = 0;
4,912✔
195
            str->ptr.data_ptr  = str_data;
4,912✔
196
        }
197
    }
198
    else
199
        str = OBJPTR(STRING, KOS_STR_EMPTY);
18✔
200

201
    return OBJID(STRING, str);
4,936✔
202
}
203

204
KOS_OBJ_ID KOS_new_string_from_codes(KOS_CONTEXT ctx,
59✔
205
                                     KOS_OBJ_ID  codes)
206
{
207
    int              error     = KOS_SUCCESS;
59✔
208
    uint32_t         length;
209
    uint32_t         i;
210
    KOS_STRING_FLAGS elem_size = KOS_STRING_ELEM_8;
59✔
211
    KOS_STRING      *ret       = KOS_NULL;
59✔
212
    void            *str_buf;
213

214
    assert(GET_OBJ_TYPE(codes) == OBJ_ARRAY);
59✔
215

216
    length = KOS_get_array_size(codes);
59✔
217

218
    if (length > 0xFFFFU)
59✔
219
        RAISE_EXCEPTION_STR(str_err_array_too_large);
1✔
220

221
    if (length) {
58✔
222
        KOS_LOCAL save_codes;
223
        uint32_t  mash_code = 0;
56✔
224

225
        codes = kos_get_array_storage(codes);
56✔
226

227
        for (i = 0; i < length; i++) {
126✔
228

229
            const KOS_OBJ_ID elem = KOS_atomic_read_relaxed_obj(OBJPTR(ARRAY_STORAGE, codes)->buf[i]);
73✔
230
            int64_t          code;
231

232
            if ( ! IS_NUMERIC_OBJ(elem))
73✔
233
                RAISE_EXCEPTION_STR(str_err_invalid_char_code);
3✔
234

235
            TRY(KOS_get_integer(ctx, elem, &code));
72✔
236

237
            if (code < 0 || code > 0x1FFFFF)
72✔
238
                RAISE_EXCEPTION_STR(str_err_invalid_char_code);
2✔
239

240
            mash_code |= (uint32_t)code;
70✔
241
        }
242

243
        elem_size = string_size_from_max_code(mash_code);
53✔
244

245
        KOS_init_local_with(ctx, &save_codes, codes);
53✔
246

247
        ret = new_empty_string(ctx, length, elem_size);
53✔
248

249
        codes = KOS_destroy_top_local(ctx, &save_codes);
53✔
250

251
        if ( ! ret)
53✔
252
            goto cleanup;
1✔
253
    }
254
    else
255
        ret = OBJPTR(STRING, KOS_STR_EMPTY);
2✔
256

257
    str_buf = (void *)kos_get_string_buffer(ret);
54✔
258

259
    switch (elem_size & KOS_STRING_ELEM_MASK) {
54✔
260

261
        case KOS_STRING_ELEM_8: {
51✔
262
            for (i = 0; i < length; i++) {
105✔
263

264
                int64_t          code;
265
                const KOS_OBJ_ID elem = KOS_atomic_read_relaxed_obj(
54✔
266
                                        OBJPTR(ARRAY_STORAGE, codes)->buf[i]);
267

268
                TRY(KOS_get_integer(ctx, elem, &code));
54✔
269

270
                ((uint8_t *)str_buf)[i] = (uint8_t)(uint64_t)code;
54✔
271
            }
272

273
            break;
51✔
274
        }
275

276
        case KOS_STRING_ELEM_16: {
1✔
277
            for (i = 0; i < length; i++) {
3✔
278

279
                int64_t          code;
280
                const KOS_OBJ_ID elem = KOS_atomic_read_relaxed_obj(
2✔
281
                                        OBJPTR(ARRAY_STORAGE, codes)->buf[i]);
282

283
                TRY(KOS_get_integer(ctx, elem, &code));
2✔
284

285
                ((uint16_t *)str_buf)[i] = (uint16_t)(uint64_t)code;
2✔
286
            }
287

288
            break;
1✔
289
        }
290

291
        default: { /* KOS_STRING_ELEM_32 */
2✔
292
            assert(kos_get_string_elem_size(ret) == KOS_STRING_ELEM_32);
2✔
293

294
            for (i = 0; i < length; i++) {
15✔
295

296
                int64_t          code;
297
                const KOS_OBJ_ID elem = KOS_atomic_read_relaxed_obj(
13✔
298
                                        OBJPTR(ARRAY_STORAGE, codes)->buf[i]);
299

300
                TRY(KOS_get_integer(ctx, elem, &code));
13✔
301

302
                ((uint32_t *)str_buf)[i] = (uint32_t)(uint64_t)code;
13✔
303
            }
304

305
            break;
2✔
306
        }
307
    }
308

309
cleanup:
59✔
310
    return error ? KOS_BADPTR : OBJID(STRING, ret);
59✔
311
}
312

313
KOS_OBJ_ID KOS_new_string_from_buffer(KOS_CONTEXT ctx,
172✔
314
                                      KOS_OBJ_ID  utf8_buf_id,
315
                                      unsigned    begin,
316
                                      unsigned    end)
317
{
318
    KOS_STRING      *str       = KOS_NULL;
172✔
319
    KOS_STRING_FLAGS elem_size = KOS_STRING_ELEM_8;
172✔
320
    KOS_LOCAL        utf8_buf;
321
    uint32_t         size;
322
    uint32_t         max_code;
323
    unsigned         length;
324
    void            *ptr;
325

326
    assert(GET_OBJ_TYPE(utf8_buf_id) == OBJ_BUFFER);
172✔
327

328
    size = KOS_get_buffer_size(utf8_buf_id);
172✔
329
    if ( ! size && begin == end)
172✔
330
        return KOS_STR_EMPTY;
20✔
331

332
    KOS_init_local_with(ctx, &utf8_buf, utf8_buf_id);
152✔
333

334
    if (begin > end || end > size) {
152✔
335
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_invalid_buffer_index));
1✔
336
        goto cleanup;
1✔
337
    }
338

339
    size = end - begin;
151✔
340

341
    utf8_buf.o = KOS_atomic_read_relaxed_obj(OBJPTR(BUFFER, utf8_buf.o)->data);
151✔
342

343
    length = KOS_utf8_get_len((const char *)&OBJPTR(BUFFER_STORAGE, utf8_buf.o)->buf[begin],
151✔
344
                              size, KOS_UTF8_NO_ESCAPE, &max_code);
345
    if (length == ~0U) {
151✔
346
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_invalid_utf8));
2✔
347
        goto cleanup;
2✔
348
    }
349

350
    elem_size = string_size_from_max_code(max_code);
149✔
351

352
    if (length > 0xFFFFU) {
149✔
353
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_buffer_too_large));
×
354
        goto cleanup;
×
355
    }
356

357
    str = new_empty_string(ctx, length, elem_size);
149✔
358
    if ( ! str)
149✔
359
        goto cleanup;
1✔
360

361
    ptr = (void *)kos_get_string_buffer(str);
148✔
362

363
    switch (elem_size) {
148✔
364
        case KOS_STRING_ASCII:
145✔
365
            memcpy(ptr, &OBJPTR(BUFFER_STORAGE, utf8_buf.o)->buf[begin], size);
145✔
366
            break;
145✔
367

368
        case KOS_STRING_ELEM_8:
1✔
369
            KOS_utf8_decode_8((const char *)&OBJPTR(BUFFER_STORAGE, utf8_buf.o)->buf[begin],
1✔
370
                              size, KOS_UTF8_NO_ESCAPE, (uint8_t *)ptr);
371
            break;
1✔
372

373
        case KOS_STRING_ELEM_16:
1✔
374
            KOS_utf8_decode_16((const char *)&OBJPTR(BUFFER_STORAGE, utf8_buf.o)->buf[begin],
1✔
375
                               size, KOS_UTF8_NO_ESCAPE, (uint16_t *)ptr);
376
            break;
1✔
377

378
        default: /* KOS_STRING_ELEM_32 */
1✔
379
            assert((elem_size & KOS_STRING_ELEM_MASK) == KOS_STRING_ELEM_32);
1✔
380
            KOS_utf8_decode_32((const char *)&OBJPTR(BUFFER_STORAGE, utf8_buf.o)->buf[begin],
1✔
381
                               size, KOS_UTF8_NO_ESCAPE, (uint32_t *)ptr);
382
            break;
1✔
383
    }
384

385
cleanup:
152✔
386
    KOS_destroy_top_local(ctx, &utf8_buf);
152✔
387

388
    return OBJID(STRING, str);
152✔
389
}
390

391
unsigned KOS_string_to_utf8(KOS_OBJ_ID obj_id,
1,488,800✔
392
                            void      *buf,
393
                            unsigned   buf_size)
394
{
395
    unsigned    num_out = 0;
1,488,800✔
396
    KOS_STRING *str     = OBJPTR(STRING, obj_id);
1,488,800✔
397
    uint8_t    *pdest   = (uint8_t *)buf;
1,488,800✔
398
    const void *src_buf;
399

400
    assert( ! IS_BAD_PTR(obj_id));
1,488,800✔
401
    assert(GET_OBJ_TYPE(obj_id) == OBJ_STRING);
1,488,800✔
402

403
    src_buf = kos_get_string_buffer(str);
1,488,800✔
404

405
    switch (str->header.flags & (KOS_STRING_ELEM_MASK | KOS_STRING_ASCII)) {
1,488,800✔
406

407
        case KOS_STRING_ASCII: {
1,488,780✔
408
            num_out = str->header.length;
1,488,780✔
409

410
            if (buf) {
1,488,780✔
411
                assert(num_out <= buf_size);
745,681✔
412
                memcpy(buf, src_buf, num_out);
745,681✔
413
            }
414
            break;
1,488,780✔
415
        }
416

417
        case KOS_STRING_ELEM_8: {
12✔
418

419
            /* Calculate how many bytes we need. */
420
            num_out = KOS_utf8_calc_buf_size_8((const uint8_t *)src_buf, str->header.length);
12✔
421

422
            /* Fill out the buffer. */
423
            if (buf) {
12✔
424
                assert(num_out <= buf_size);
6✔
425
                if (num_out == str->header.length)
6✔
426
                    memcpy(buf, src_buf, num_out);
1✔
427
                else
428
                    KOS_utf8_encode_8((const uint8_t *)src_buf, str->header.length, pdest);
5✔
429
            }
430
            break;
12✔
431
        }
432

433
        case KOS_STRING_ELEM_16: {
12✔
434

435
            /* Calculate how many bytes we need. */
436
            num_out = KOS_utf8_calc_buf_size_16((const uint16_t *)src_buf, str->header.length);
12✔
437

438
            /* Fill out the buffer. */
439
            if (buf) {
12✔
440
                assert(num_out <= buf_size);
6✔
441
                KOS_utf8_encode_16((const uint16_t *)src_buf, str->header.length, pdest);
6✔
442
            }
443
            break;
12✔
444
        }
445

446
        default: {
×
447
            assert(kos_get_string_elem_size(str) == KOS_STRING_ELEM_32);
×
448

449
            /* Calculate how many bytes we need. */
450
            num_out = KOS_utf8_calc_buf_size_32((const uint32_t *)src_buf, str->header.length);
×
451

452
            /* Fill out the buffer. */
453
            if (buf && num_out != ~0U) {
18✔
454
                assert(num_out <= buf_size);
6✔
455
                KOS_utf8_encode_32((const uint32_t *)src_buf, str->header.length, pdest);
6✔
456
            }
457
            break;
18✔
458
        }
459
    }
460

461
    return num_out;
1,488,820✔
462
}
463

464
int KOS_string_to_cstr_vec(KOS_CONTEXT ctx,
685,774✔
465
                           KOS_OBJ_ID  obj_id,
466
                           KOS_VECTOR *str_vec)
467
{
468
    int      error   = KOS_SUCCESS;
685,774✔
469
    unsigned str_len = 0;
685,774✔
470

471
    assert( ! IS_BAD_PTR(obj_id));
685,774✔
472

473
    if (GET_OBJ_TYPE(obj_id) != OBJ_STRING) {
685,774✔
474
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_not_string));
×
475
        return KOS_ERROR_EXCEPTION;
1✔
476
    }
477

478
    if (KOS_get_string_length(obj_id) > 0) {
685,781✔
479

480
        str_len = KOS_string_to_utf8(obj_id, KOS_NULL, 0);
685,777✔
481
        assert(str_len > 0);
685,568✔
482

483
        if (str_len == ~0U) {
685,568✔
484
            KOS_raise_exception(ctx, KOS_CONST_ID(str_err_invalid_string));
2✔
485
            return KOS_ERROR_EXCEPTION;
2✔
486
        }
487
    }
488

489
    error = KOS_vector_resize(str_vec, str_len+1);
685,570✔
490

491
    if (error) {
685,604✔
492
        KOS_raise_exception(ctx, KOS_STR_OUT_OF_MEMORY);
543✔
493
        return KOS_ERROR_EXCEPTION;
543✔
494
    }
495

496
    if (str_len)
685,061✔
497
        KOS_string_to_utf8(obj_id, str_vec->buffer, str_len);
685,060✔
498

499
    str_vec->buffer[str_len] = 0;
685,580✔
500

501
    return error;
685,580✔
502
}
503

504
uint32_t KOS_string_get_hash(KOS_OBJ_ID obj_id)
5,389,130✔
505
{
506
    uint32_t hash;
507

508
    KOS_STRING *str = OBJPTR(STRING, obj_id);
5,389,130✔
509

510
    assert( ! IS_BAD_PTR(obj_id));
5,389,130✔
511
    assert(GET_OBJ_TYPE(obj_id) == OBJ_STRING);
5,389,130✔
512

513
    hash = KOS_atomic_read_relaxed_u32(str->header.hash);
5,392,530✔
514

515
    if (!hash) {
5,392,530✔
516

517
        const void *buf = kos_get_string_buffer(str);
1,086,880✔
518

519
        /* djb2a algorithm */
520

521
        hash = 5381;
1,086,880✔
522

523
        switch (kos_get_string_elem_size(str)) {
1,086,880✔
524

525
            case KOS_STRING_ELEM_8: {
1,086,880✔
526
                const uint8_t *s   = (uint8_t *)buf;
1,086,880✔
527
                const uint8_t *end = s + str->header.length;
1,086,880✔
528

529
                while (s < end)
7,464,910✔
530
                    hash = (hash * 33U) ^ (uint32_t)*(s++);
6,378,030✔
531
                break;
1,086,880✔
532
            }
533

534
            case KOS_STRING_ELEM_16: {
1✔
535
                const uint16_t *s   = (uint16_t *)buf;
1✔
536
                const uint16_t *end = s + str->header.length;
1✔
537

538
                while (s < end)
4✔
539
                    hash = (hash * 33U) ^ (uint32_t)*(s++);
3✔
540
                break;
1✔
541
            }
542

543
            default: /* KOS_STRING_ELEM_32 */
3✔
544
                assert(kos_get_string_elem_size(str) == KOS_STRING_ELEM_32);
3✔
545
                {
546
                    const uint32_t *s   = (uint32_t *)buf;
3✔
547
                    const uint32_t *end = s + str->header.length;
3✔
548

549
                    while (s < end)
13✔
550
                        hash = (hash * 33U) ^ (uint32_t)*(s++);
10✔
551
                }
552
                break;
3✔
553
        }
554

555
        assert(hash);
1,086,880✔
556

557
        KOS_atomic_write_relaxed_u32(str->header.hash, hash);
1,086,880✔
558
    }
559

560
    return hash;
5,392,530✔
561
}
562

563
static void init_empty_string(KOS_STRING *dest,
83,884✔
564
                              unsigned    offs,
565
                              KOS_STRING *src,
566
                              unsigned    len)
567
{
568
    if (len) {
83,884✔
569
        void       *dest_buf = (void *)kos_get_string_buffer(dest);
83,838✔
570
        const void *src_buf  = kos_get_string_buffer(src);
83,838✔
571

572
        assert(len <= src->header.length);
83,838✔
573

574
        if (kos_get_string_elem_size(dest) == kos_get_string_elem_size(src)) {
83,838✔
575

576
            const int dest_shift = kos_get_string_elem_size(dest);
83,812✔
577

578
            memcpy((char *)dest_buf + (offs << dest_shift),
83,812✔
579
                   src_buf,
580
                   len << dest_shift);
83,812✔
581
        }
582
        else switch (kos_get_string_elem_size(dest)) {
26✔
583

584
            case KOS_STRING_ELEM_16: {
13✔
585
                uint16_t      *pdest = (uint16_t *)dest_buf + offs;
13✔
586
                const uint8_t *psrc  = (const uint8_t *)src_buf;
13✔
587
                const uint8_t *pend  = psrc + len;
13✔
588
                assert(kos_get_string_elem_size(src) == KOS_STRING_ELEM_8);
13✔
589
                for ( ; psrc != pend; ++pdest, ++psrc)
91✔
590
                    *pdest = *psrc;
78✔
591
                break;
13✔
592
            }
593

594
            default:
13✔
595
                assert(kos_get_string_elem_size(dest) == KOS_STRING_ELEM_32);
13✔
596
                assert(kos_get_string_elem_size(src) == KOS_STRING_ELEM_8 ||
13✔
597
                       kos_get_string_elem_size(src) == KOS_STRING_ELEM_16);
598
                if (kos_get_string_elem_size(src) == KOS_STRING_ELEM_8) {
13✔
599
                    uint32_t      *pdest = (uint32_t *)dest_buf + offs;
12✔
600
                    const uint8_t *psrc  = (const uint8_t *)src_buf;
12✔
601
                    const uint8_t *pend  = psrc + len;
12✔
602
                    for ( ; psrc != pend; ++pdest, ++psrc)
112✔
603
                        *pdest = *psrc;
100✔
604
                }
605
                else {
606
                    uint32_t       *pdest = (uint32_t *)dest_buf + offs;
1✔
607
                    const uint16_t *psrc  = (const uint16_t *)src_buf;
1✔
608
                    const uint16_t *pend  = psrc + len;
1✔
609
                    for ( ; psrc != pend; ++pdest, ++psrc)
2✔
610
                        *pdest = *psrc;
1✔
611
                }
612
                break;
13✔
613
        }
614
    }
46✔
615
}
83,884✔
616

617
KOS_OBJ_ID KOS_string_add_n(KOS_CONTEXT         ctx,
19,103✔
618
                            struct KOS_LOCAL_S *str_array,
619
                            unsigned            num_strings)
620
{
621
    KOS_LOCAL new_str;
622

623
    KOS_init_local(ctx, &new_str);
19,103✔
624

625
    if (num_strings == 1)
19,103✔
626
        new_str.o = str_array->o;
3✔
627

628
    else {
629
        KOS_LOCAL *const end       = str_array + num_strings;
19,100✔
630
        KOS_LOCAL       *cur_ptr;
631
        KOS_OBJ_ID       non_0_str = KOS_VOID;
19,100✔
632
        KOS_STRING_FLAGS elem_size = KOS_STRING_ELEM_8;
19,100✔
633
        unsigned         new_len   = 0;
19,100✔
634
        unsigned         num_non_0 = 0;
19,100✔
635
        unsigned         mash_size = 0;
19,100✔
636
        unsigned         ascii     = KOS_STRING_ASCII;
19,100✔
637

638
        new_str.o = KOS_STR_EMPTY;
19,100✔
639

640
        for (cur_ptr = str_array; cur_ptr != end; ++cur_ptr) {
99,684✔
641
            KOS_OBJ_ID cur_str = cur_ptr->o;
80,589✔
642
            unsigned   cur_len;
643

644
            assert( ! IS_BAD_PTR(cur_str));
80,589✔
645

646
            if (GET_OBJ_TYPE(cur_str) != OBJ_STRING) {
80,589✔
647
                new_str.o = KOS_BADPTR;
5✔
648
                new_len   = 0;
5✔
649
                KOS_raise_exception(ctx, KOS_CONST_ID(str_err_not_string));
5✔
650
                break;
5✔
651
            }
652

653
            mash_size |= OBJPTR(STRING, cur_str)->header.flags & KOS_STRING_ELEM_MASK;
80,584✔
654
            ascii     &= OBJPTR(STRING, cur_str)->header.flags & KOS_STRING_ASCII;
80,584✔
655

656
            cur_len = KOS_get_string_length(cur_str);
80,584✔
657

658
            new_len += cur_len;
80,584✔
659

660
            if (cur_len) {
80,584✔
661
                ++num_non_0;
80,416✔
662
                non_0_str = cur_str;
80,416✔
663
            }
664
        }
665

666
        if (mash_size & KOS_STRING_ELEM_32)
19,100✔
667
            elem_size = KOS_STRING_ELEM_32;
2✔
668
        else if (mash_size & KOS_STRING_ELEM_16)
19,098✔
669
            elem_size = KOS_STRING_ELEM_16;
3✔
670
        else
671
            elem_size = (KOS_STRING_FLAGS)ascii;
19,095✔
672

673
        if (num_non_0 == 1 && new_len)
19,100✔
674
            new_str.o = non_0_str;
121✔
675

676
        else if (new_len) {
18,979✔
677
            override_elem_size(elem_size);
678

679
            if (new_len <= 0xFFFFU)
18,953✔
680
                new_str.o = OBJID(STRING, new_empty_string(ctx, new_len, elem_size));
18,952✔
681
            else {
682
                new_str.o = KOS_BADPTR;
1✔
683
                KOS_raise_exception(ctx, KOS_CONST_ID(str_err_string_too_long));
1✔
684
            }
685

686
            if ( ! IS_BAD_PTR(new_str.o)) {
18,953✔
687

688
                unsigned pos = 0;
18,938✔
689

690
                for (cur_ptr = str_array; cur_ptr != end; ++cur_ptr) {
99,166✔
691
                    KOS_OBJ_ID     str_obj = cur_ptr->o;
80,228✔
692
                    const unsigned cur_len = OBJPTR(STRING, str_obj)->header.length;
80,228✔
693
                    init_empty_string(OBJPTR(STRING, new_str.o), pos, OBJPTR(STRING, str_obj), cur_len);
80,228✔
694
                    pos += cur_len;
80,228✔
695
                }
696
            }
697
        }
698
    }
699

700
    new_str.o = KOS_destroy_top_local(ctx, &new_str);
19,103✔
701

702
    return new_str.o;
19,103✔
703
}
704

705
KOS_OBJ_ID KOS_string_add(KOS_CONTEXT ctx,
1,134✔
706
                          KOS_OBJ_ID  str_array_id)
707
{
708
    KOS_LOCAL  new_str;
709
    KOS_LOCAL  str_array;
710
    unsigned   num_strings;
711

712
    if (IS_BAD_PTR(str_array_id) || GET_OBJ_TYPE(str_array_id) != OBJ_ARRAY) {
1,134✔
713
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_not_array));
2✔
714
        return KOS_BADPTR;
2✔
715
    }
716

717
    KOS_init_locals(ctx, &str_array, &new_str, kos_end_locals);
1,132✔
718

719
    str_array.o = str_array_id;
1,132✔
720

721
    num_strings = KOS_get_array_size(str_array.o);
1,132✔
722

723
    if (num_strings == 1) {
1,132✔
724
        new_str.o = KOS_array_read(ctx, str_array.o, 0);
456✔
725

726
        if ( ! IS_BAD_PTR(new_str.o) && GET_OBJ_TYPE(new_str.o) != OBJ_STRING) {
456✔
727
            KOS_raise_exception(ctx, KOS_CONST_ID(str_err_not_string));
1✔
728
            new_str.o = KOS_BADPTR;
1✔
729
        }
730
    }
731
    else {
732
        KOS_OBJ_ID       non_0_str = KOS_VOID;
676✔
733
        KOS_STRING_FLAGS elem_size = KOS_STRING_ELEM_8;
676✔
734
        unsigned         new_len   = 0;
676✔
735
        unsigned         num_non_0 = 0;
676✔
736
        unsigned         mash_size = 0;
676✔
737
        unsigned         ascii     = KOS_STRING_ASCII;
676✔
738
        unsigned         i;
739

740
        new_str.o = KOS_STR_EMPTY;
676✔
741

742
        for (i = 0; i < num_strings; ++i) {
4,506✔
743
            KOS_OBJ_ID cur_str = KOS_array_read(ctx, str_array.o, i);
3,850✔
744
            unsigned   cur_len;
745

746
            if (IS_BAD_PTR(cur_str) || GET_OBJ_TYPE(cur_str) != OBJ_STRING) {
3,850✔
747
                new_str.o = KOS_BADPTR;
20✔
748
                new_len   = 0;
20✔
749
                if ( ! IS_BAD_PTR(cur_str))
20✔
750
                    KOS_raise_exception(ctx, KOS_CONST_ID(str_err_not_string));
1✔
751
                break;
20✔
752
            }
753

754
            mash_size |= OBJPTR(STRING, cur_str)->header.flags & KOS_STRING_ELEM_MASK;
3,830✔
755
            ascii     &= OBJPTR(STRING, cur_str)->header.flags & KOS_STRING_ASCII;
3,830✔
756

757
            cur_len = KOS_get_string_length(cur_str);
3,830✔
758

759
            new_len += cur_len;
3,830✔
760

761
            if (cur_len) {
3,830✔
762
                ++num_non_0;
3,785✔
763
                non_0_str = cur_str;
3,785✔
764
            }
765
        }
766

767
        if (mash_size & KOS_STRING_ELEM_32)
676✔
768
            elem_size = KOS_STRING_ELEM_32;
×
769
        else if (mash_size & KOS_STRING_ELEM_16)
676✔
770
            elem_size = KOS_STRING_ELEM_16;
×
771
        else
772
            elem_size = (KOS_STRING_FLAGS)ascii;
676✔
773

774
        if (num_non_0 == 1 && new_len)
676✔
775
            new_str.o = non_0_str;
×
776

777
        else if (new_len) {
676✔
778
            override_elem_size(elem_size);
779

780
            if (new_len <= 0xFFFFU)
653✔
781
                new_str.o = OBJID(STRING, new_empty_string(ctx, new_len, elem_size));
652✔
782
            else {
783
                KOS_raise_exception(ctx, KOS_CONST_ID(str_err_string_too_long));
1✔
784
                new_str.o = KOS_BADPTR;
1✔
785
            }
786

787
            if ( ! IS_BAD_PTR(new_str.o)) {
653✔
788

789
                unsigned pos = 0;
647✔
790

791
                for (i = 0; i < num_strings; ++i) {
4,303✔
792
                    KOS_OBJ_ID str_obj = KOS_array_read(ctx, str_array.o, i);
3,675✔
793
                    unsigned   cur_len;
794

795
                    if (IS_BAD_PTR(str_obj) || GET_OBJ_TYPE(str_obj) != OBJ_STRING) {
3,675✔
796
                        new_str.o = KOS_BADPTR;
19✔
797
                        if ( ! IS_BAD_PTR(str_obj))
19✔
798
                            KOS_raise_exception(ctx, KOS_CONST_ID(str_err_not_string));
×
799
                        break;
19✔
800
                    }
801

802
                    cur_len = OBJPTR(STRING, str_obj)->header.length;
3,656✔
803
                    init_empty_string(OBJPTR(STRING, new_str.o), pos, OBJPTR(STRING, str_obj), cur_len);
3,656✔
804
                    pos += cur_len;
3,656✔
805
                }
806
            }
807
        }
808
    }
809

810
    new_str.o = KOS_destroy_top_locals(ctx, &str_array, &new_str);
1,132✔
811

812
    return new_str.o;
1,132✔
813
}
814

815
KOS_OBJ_ID KOS_string_slice(KOS_CONTEXT ctx,
5,467✔
816
                            KOS_OBJ_ID  obj_id,
817
                            int64_t     begin,
818
                            int64_t     end)
819
{
820
    KOS_OBJ_ID new_str = KOS_BADPTR;
5,467✔
821

822
    assert( ! IS_BAD_PTR(obj_id));
5,467✔
823

824
    if (GET_OBJ_TYPE(obj_id) != OBJ_STRING)
5,467✔
825
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_not_string));
3✔
826
    else {
827
        const KOS_STRING_FLAGS elem_size =
5,464✔
828
            kos_get_string_elem_size(OBJPTR(STRING, obj_id));
5,464✔
829

830
        const int64_t  len = OBJPTR(STRING, obj_id)->header.length;
5,464✔
831
        const uint8_t *buf;
832

833
        if (len) {
5,464✔
834
            unsigned new_len;
835

836
            if (begin < 0)
5,453✔
837
                begin += len;
10✔
838

839
            if (end < 0)
5,453✔
840
                end += len;
31✔
841

842
            if (begin < 0)
5,453✔
843
                begin = 0;
5✔
844

845
            if (end > len)
5,453✔
846
                end = len;
113✔
847

848
            if (end < begin)
5,453✔
849
                end = begin;
5✔
850

851
            {
852
                const int64_t new_len_64 = end - begin;
5,453✔
853
                assert(new_len_64 <= 0xFFFF);
5,453✔
854
                new_len = (unsigned)new_len_64;
5,453✔
855
            }
856

857
            if (new_len == len)
5,453✔
858
                new_str = obj_id;
83✔
859
            else if (new_len) {
5,370✔
860
                KOS_LOCAL in_str;
861

862
                const uint8_t size_flags = OBJPTR(STRING, obj_id)->header.flags &
5,305✔
863
                                           (KOS_STRING_ELEM_MASK | KOS_STRING_ASCII);
864

865
                KOS_init_local_with(ctx, &in_str, obj_id);
5,305✔
866

867
                if ((new_len << elem_size) <= 2 * sizeof(void *)) {
5,305✔
868
                    new_str = OBJID(STRING, new_empty_string(ctx, new_len, (KOS_STRING_FLAGS)size_flags));
374✔
869
                    buf = (const uint8_t *)kos_get_string_buffer(OBJPTR(STRING, in_str.o)) + (begin << elem_size);
374✔
870
                    if ( ! IS_BAD_PTR(new_str))
374✔
871
                        memcpy((void *)kos_get_string_buffer(OBJPTR(STRING, new_str)),
374✔
872
                               buf,
873
                               new_len << elem_size);
374✔
874
                }
875
                /* if KOS_STRING_PTR */
876
                else if ((OBJPTR(STRING, in_str.o)->header.flags & KOS_STRING_STOR_MASK) == KOS_STRING_PTR) {
4,931✔
877
                    buf = (const uint8_t *)kos_get_string_buffer(OBJPTR(STRING, in_str.o)) + (begin << elem_size);
1✔
878
                    new_str = KOS_new_const_string(ctx, buf, new_len, (KOS_STRING_FLAGS)size_flags);
1✔
879
                }
880
                else {
881

882
                    new_str = OBJID(STRING, (KOS_STRING *)
4,930✔
883
                              kos_alloc_object(ctx,
884
                                               KOS_ALLOC_MOVABLE,
885
                                               OBJ_STRING,
886
                                               sizeof(struct KOS_STRING_REF_S)));
887

888
                    if ( ! IS_BAD_PTR(new_str)) {
4,930✔
889
                        struct KOS_STRING_REF_S *const ref = &OBJPTR(STRING, new_str)->ref;
4,925✔
890

891
                        buf = (const uint8_t *)kos_get_string_buffer(OBJPTR(STRING, in_str.o)) + (begin << elem_size);
4,925✔
892

893
                        assert(READ_OBJ_TYPE(new_str) == OBJ_STRING);
4,925✔
894

895
                        OBJPTR(STRING, new_str)->header.flags  = size_flags | (uint8_t)KOS_STRING_REF;
4,925✔
896
                        OBJPTR(STRING, new_str)->header.length = (uint16_t)new_len;
4,925✔
897
                        OBJPTR(STRING, new_str)->header.hash   = 0;
4,925✔
898
                        ref->data_ptr                          = buf;
4,925✔
899

900
                        if (OBJPTR(STRING, in_str.o)->header.flags & KOS_STRING_REF)
4,925✔
901
                            ref->obj_id = OBJPTR(STRING, in_str.o)->ref.obj_id;
×
902
                        else
903
                            ref->obj_id = in_str.o;
4,925✔
904
                    }
905
                }
906

907
                KOS_destroy_top_local(ctx, &in_str);
5,305✔
908
            }
909
            else
910
                new_str = KOS_STR_EMPTY;
65✔
911
        }
912
        else
913
            new_str = KOS_STR_EMPTY;
11✔
914
    }
915

916
    return new_str;
5,467✔
917
}
918

919
KOS_OBJ_ID KOS_string_get_char(KOS_CONTEXT ctx,
428✔
920
                               KOS_OBJ_ID  obj_id,
921
                               int         idx)
922
{
923
    KOS_STRING *new_str = KOS_NULL;
428✔
924

925
    const uint32_t code = KOS_string_get_char_code(ctx, obj_id, idx);
428✔
926

927
    if (code != ~0U) {
428✔
928

929
        const KOS_STRING_FLAGS elem_size = string_size_from_max_code(code);
413✔
930

931
        new_str = new_empty_string(ctx, 1, elem_size);
413✔
932

933
        if (new_str) {
413✔
934

935
            uint8_t *const new_buf = (uint8_t *)kos_get_string_buffer(new_str);
413✔
936

937
            switch (elem_size) {
413✔
938

939
                case KOS_STRING_ELEM_16:
2✔
940
                    *(uint16_t *)new_buf = (uint16_t)code;
2✔
941
                    break;
2✔
942

943
                case KOS_STRING_ELEM_32:
1✔
944
                    *(uint32_t *)new_buf = code;
1✔
945
                    break;
1✔
946

947
                default:
410✔
948
                    assert(elem_size == KOS_STRING_ELEM_8 ||
410✔
949
                           elem_size == KOS_STRING_ASCII);
950
                    *new_buf = (uint8_t)code;
410✔
951
                    break;
410✔
952
            }
953
        }
15✔
954
    }
955

956
    return OBJID(STRING, new_str);
428✔
957
}
958

959
unsigned KOS_string_get_char_code(KOS_CONTEXT ctx,
111,219✔
960
                                  KOS_OBJ_ID  obj_id,
961
                                  int         idx)
962
{
963
    uint32_t code = ~0U;
111,219✔
964

965
    assert( ! IS_BAD_PTR(obj_id));
111,219✔
966

967
    if (GET_OBJ_TYPE(obj_id) != OBJ_STRING)
111,219✔
968
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_not_string));
6✔
969
    else {
970
        KOS_STRING            *str       = OBJPTR(STRING, obj_id);
111,213✔
971
        const KOS_STRING_FLAGS elem_size = kos_get_string_elem_size(str);
111,213✔
972
        const int              len       = (int)str->header.length;
111,213✔
973

974
        if (idx < 0)
111,213✔
975
            idx += len;
97✔
976

977
        if (idx >= 0 && idx < len) {
222,410✔
978
            const uint8_t *buf = (const uint8_t *)kos_get_string_buffer(str) + (idx << elem_size);
111,197✔
979

980
            switch (elem_size) {
111,197✔
981

982
                case KOS_STRING_ELEM_8:
111,089✔
983
                    code = *buf;
111,089✔
984
                    break;
111,089✔
985

986
                case KOS_STRING_ELEM_16:
31✔
987
                    code = *(const uint16_t*)buf;
31✔
988
                    break;
31✔
989

990
                default: /* KOS_STRING_ELEM_32 */
77✔
991
                    assert(elem_size == KOS_STRING_ELEM_32);
77✔
992
                    code = *(const uint32_t*)buf;
77✔
993
                    break;
77✔
994
            }
995
        }
996
        else
997
            KOS_raise_exception(ctx, KOS_CONST_ID(str_err_invalid_index));
16✔
998
    }
999

1000
    return code;
111,219✔
1001
}
1002

1003
static int strcmp_8_16(KOS_STRING *a,
8✔
1004
                       unsigned    a_begin,
1005
                       unsigned    a_len,
1006
                       KOS_STRING *b,
1007
                       unsigned    b_begin,
1008
                       unsigned    b_len)
1009
{
1010
    const unsigned  cmp_len = KOS_min(a_len, b_len);
8✔
1011
    const uint8_t  *pa      = (const uint8_t  *)kos_get_string_buffer(a) + a_begin;
8✔
1012
    const uint16_t *pb      = (const uint16_t *)kos_get_string_buffer(b) + b_begin;
8✔
1013
    const uint8_t  *pend    = pa + cmp_len;
8✔
1014
    int             result  = 0;
8✔
1015

1016
    for (;;) {
1017
        if (pa < pend) {
29✔
1018
            const uint8_t  ca = *pa;
25✔
1019
            const uint16_t cb = *pb;
25✔
1020
            if (ca != cb) {
25✔
1021
                result = kos_unicode_compare(ca, cb);
4✔
1022
                break;
4✔
1023
            }
1024
        }
1025
        else {
1026
            result = (int)(a_len - b_len);
4✔
1027
            break;
4✔
1028
        }
1029
        ++pa;
21✔
1030
        ++pb;
21✔
1031
    }
1032

1033
    return result;
8✔
1034
}
1035

1036
static int strcmp_8_32(KOS_STRING *a,
8✔
1037
                       unsigned    a_begin,
1038
                       unsigned    a_len,
1039
                       KOS_STRING *b,
1040
                       unsigned    b_begin,
1041
                       unsigned    b_len)
1042
{
1043
    const unsigned  cmp_len = KOS_min(a_len, b_len);
8✔
1044
    const uint8_t  *pa      = (const uint8_t  *)kos_get_string_buffer(a) + a_begin;
8✔
1045
    const uint32_t *pb      = (const uint32_t *)kos_get_string_buffer(b) + b_begin;
8✔
1046
    const uint8_t  *pend    = pa + cmp_len;
8✔
1047
    int             result  = 0;
8✔
1048

1049
    for (;;) {
1050
        if (pa < pend) {
30✔
1051
            const uint8_t  ca = *pa;
25✔
1052
            const uint32_t cb = *pb;
25✔
1053
            if (ca != cb) {
25✔
1054
                result = kos_unicode_compare(ca, cb);
3✔
1055
                break;
3✔
1056
            }
1057
        }
1058
        else {
1059
            result = (int)(a_len - b_len);
5✔
1060
            break;
5✔
1061
        }
1062
        ++pa;
22✔
1063
        ++pb;
22✔
1064
    }
1065

1066
    return result;
8✔
1067
}
1068

1069
static int strcmp_16_32(KOS_STRING *a,
5✔
1070
                        unsigned    a_begin,
1071
                        unsigned    a_len,
1072
                        KOS_STRING *b,
1073
                        unsigned    b_begin,
1074
                        unsigned    b_len)
1075
{
1076
    const unsigned  cmp_len = KOS_min(a_len, b_len);
5✔
1077
    const uint16_t *pa      = (const uint16_t *)kos_get_string_buffer(a) + a_begin;
5✔
1078
    const uint32_t *pb      = (const uint32_t *)kos_get_string_buffer(b) + b_begin;
5✔
1079
    const uint16_t *pend    = pa + cmp_len;
5✔
1080
    int             result  = 0;
5✔
1081

1082
    for (;;) {
1083
        if (pa < pend) {
18✔
1084
            const uint16_t ca = *pa;
15✔
1085
            const uint32_t cb = *pb;
15✔
1086
            if (ca != cb) {
15✔
1087
                result = kos_unicode_compare(ca, cb);
2✔
1088
                break;
2✔
1089
            }
1090
        }
1091
        else {
1092
            result = (int)(a_len - b_len);
3✔
1093
            break;
3✔
1094
        }
1095
        ++pa;
13✔
1096
        ++pb;
13✔
1097
    }
1098

1099
    return result;
5✔
1100
}
1101

1102
static int compare_slice(KOS_STRING *str_a,
938,520✔
1103
                         unsigned    a_begin,
1104
                         unsigned    a_end,
1105
                         KOS_STRING *str_b,
1106
                         unsigned    b_begin,
1107
                         unsigned    b_end)
1108
{
1109
    int      result      = 0;
938,520✔
1110
    unsigned a_len       = a_end - a_begin;
938,520✔
1111
    unsigned b_len       = b_end - b_begin;
938,520✔
1112
    unsigned a_elem_size = kos_get_string_elem_size(str_a);
938,520✔
1113
    unsigned b_elem_size = kos_get_string_elem_size(str_b);
938,520✔
1114

1115
    assert(a_end   <= str_a->header.length);
938,520✔
1116
    assert(a_begin <= a_end);
938,520✔
1117
    assert(b_end   <= str_b->header.length);
938,520✔
1118
    assert(b_begin <= b_end);
938,520✔
1119

1120
    if (a_elem_size == b_elem_size) {
938,520✔
1121

1122
        const unsigned cmp_len = KOS_min(a_len, b_len);
939,215✔
1123

1124
        const uint8_t *pa    = (const uint8_t *)kos_get_string_buffer(str_a) + (a_begin << a_elem_size);
939,215✔
1125
        const uint8_t *pb    = (const uint8_t *)kos_get_string_buffer(str_b) + (b_begin << a_elem_size);
939,215✔
1126
        const unsigned num_b = cmp_len << a_elem_size;
939,215✔
1127
        const uint8_t *pend  = pa + num_b;
939,215✔
1128
        const uint8_t *pend8 = (const uint8_t *)((uintptr_t)pend & ~(uintptr_t)7);
939,215✔
1129

1130
        uint32_t ca = 0;
939,215✔
1131
        uint32_t cb = 0;
939,215✔
1132

1133
        if (((uintptr_t)pa & 7U) == ((uintptr_t)pb & 7U) && pa + 8 < pend8) {
939,215✔
1134

1135
            while (((uintptr_t)pa & 7U) && *pa == *pb) {
192✔
1136
                ++pa;
6✔
1137
                ++pb;
6✔
1138
            }
1139

1140
            if ( ! ((uintptr_t)pa & 7U)) {
186✔
1141
                while (pa < pend8 && *(const uint64_t *)pa == *(const uint64_t *)pb) {
1,028✔
1142
                    pa += 8;
842✔
1143
                    pb += 8;
842✔
1144
                }
1145
            }
1146
        }
1147

1148
        switch (a_elem_size) {
939,215✔
1149

1150
            case KOS_STRING_ELEM_8:
938,935✔
1151
                while (pa < pend && *pa == *pb) {
3,017,930✔
1152
                    ++pa;
2,079,000✔
1153
                    ++pb;
2,079,000✔
1154
                }
1155
                if (pa < pend) {
938,935✔
1156
                    ca = *pa;
7,458✔
1157
                    cb = *pb;
7,458✔
1158
                }
1159
                break;
938,935✔
1160

1161
            case KOS_STRING_ELEM_16:
10✔
1162
                while (pa < pend && *(const uint16_t *)pa == *(const uint16_t *)pb) {
50✔
1163
                    pa += 2;
40✔
1164
                    pb += 2;
40✔
1165
                }
1166
                if (pa < pend) {
10✔
1167
                    ca = *(const uint16_t *)pa;
2✔
1168
                    cb = *(const uint16_t *)pb;
2✔
1169
                }
1170
                break;
10✔
1171

1172
            default: /* KOS_STRING_ELEM_32 */
270✔
1173
                assert(a_elem_size == KOS_STRING_ELEM_32);
270✔
1174
                while (pa < pend && *(const uint32_t *)pa == *(const uint32_t *)pb) {
278✔
1175
                    pa += 4;
8✔
1176
                    pb += 4;
8✔
1177
                }
1178
                if (pa < pend) {
270✔
1179
                    ca = *(const uint32_t *)pa;
2✔
1180
                    cb = *(const uint32_t *)pb;
2✔
1181
                }
1182
                break;
270✔
1183
        }
1184

1185
        if (pa < pend)
939,215✔
1186
            result = kos_unicode_compare(ca, cb);
7,462✔
1187
        else
1188
            result = (int)a_len - (int)b_len;
931,753✔
1189
    }
1190
    else {
1191
        const int neg = a_elem_size < b_elem_size ? 1 : -1;
×
1192
        if (neg < 0) {
×
1193
            unsigned    tmp2;
1194
            KOS_STRING *tmp = str_a;
12✔
1195
            str_a           = str_b;
12✔
1196
            str_b           = tmp;
12✔
1197

1198
            tmp2    = a_begin;
12✔
1199
            a_begin = b_begin;
12✔
1200
            b_begin = tmp2;
12✔
1201

1202
            tmp2  = a_len;
12✔
1203
            a_len = b_len;
12✔
1204
            b_len = tmp2;
12✔
1205

1206
            tmp2        = a_elem_size;
12✔
1207
            a_elem_size = b_elem_size;
12✔
1208
            b_elem_size = tmp2;
12✔
1209
        }
1210

1211
        if (a_elem_size == KOS_STRING_ELEM_8) {
×
1212
            if (b_elem_size == KOS_STRING_ELEM_16)
16✔
1213
                result = strcmp_8_16(str_a, a_begin, a_len, str_b, b_begin, b_len);
8✔
1214
            else {
1215
                assert(b_elem_size == KOS_STRING_ELEM_32);
8✔
1216
                result = strcmp_8_32(str_a, a_begin, a_len, str_b, b_begin, b_len);
8✔
1217
            }
1218
        }
1219
        else {
1220
            assert(a_elem_size == KOS_STRING_ELEM_16 && b_elem_size == KOS_STRING_ELEM_32);
×
1221
            result = strcmp_16_32(str_a, a_begin, a_len, str_b, b_begin, b_len);
5✔
1222
        }
1223

1224
        result *= neg;
21✔
1225
    }
1226

1227
    return result;
938,932✔
1228
}
1229

1230
int KOS_string_compare(KOS_OBJ_ID obj_id_a,
923,623✔
1231
                       KOS_OBJ_ID obj_id_b)
1232
{
1233
    unsigned len_a;
1234
    unsigned len_b;
1235

1236
    assert(GET_OBJ_TYPE(obj_id_a) == OBJ_STRING);
923,623✔
1237
    assert(GET_OBJ_TYPE(obj_id_b) == OBJ_STRING);
934,909✔
1238

1239
    len_a = KOS_get_string_length(obj_id_a);
935,104✔
1240
    len_b = KOS_get_string_length(obj_id_b);
935,104✔
1241

1242
    return compare_slice(OBJPTR(STRING, obj_id_a),
1,873,620✔
1243
                         0,
1244
                         len_a,
1245
                         OBJPTR(STRING, obj_id_b),
935,104✔
1246
                         0,
1247
                         len_b);
1248
}
1249

1250
int KOS_string_compare_slice(KOS_OBJ_ID obj_id_a,
291✔
1251
                             int64_t    a_begin,
1252
                             int64_t    a_end,
1253
                             KOS_OBJ_ID obj_id_b,
1254
                             int64_t    b_begin,
1255
                             int64_t    b_end)
1256
{
1257
    int64_t len_a;
1258
    int64_t len_b;
1259

1260
    assert(GET_OBJ_TYPE(obj_id_a) == OBJ_STRING);
291✔
1261
    assert(GET_OBJ_TYPE(obj_id_b) == OBJ_STRING);
291✔
1262

1263
    len_a = (int64_t)KOS_get_string_length(obj_id_a);
291✔
1264
    len_b = (int64_t)KOS_get_string_length(obj_id_b);
291✔
1265

1266
    if (a_begin < 0)
291✔
1267
        a_begin += len_a;
2✔
1268
    if (a_end < 0)
291✔
1269
        a_end += len_a;
2✔
1270
    if (a_begin < 0)
291✔
1271
        a_begin = 0;
×
1272
    if (a_end > len_a)
291✔
1273
        a_end = len_a;
×
1274
    if (a_end < a_begin)
291✔
1275
        a_end = a_begin;
1✔
1276

1277
    if (b_begin < 0)
291✔
1278
        b_begin += len_b;
2✔
1279
    if (b_end < 0)
291✔
1280
        b_end += len_b;
2✔
1281
    if (b_begin < 0)
291✔
1282
        b_begin = 0;
×
1283
    if (b_end > len_b)
291✔
1284
        b_end = len_b;
×
1285
    if (b_end < b_begin)
291✔
1286
        b_end = b_begin;
1✔
1287

1288
    return compare_slice(OBJPTR(STRING, obj_id_a),
582✔
1289
                         (unsigned)a_begin,
1290
                         (unsigned)a_end,
1291
                         OBJPTR(STRING, obj_id_b),
291✔
1292
                         (unsigned)b_begin,
1293
                         (unsigned)b_end);
1294
}
1295

1296
static void string_find_brute_force(KOS_OBJ_ID          obj_id_text,
28✔
1297
                                    KOS_OBJ_ID          obj_id_pattern,
1298
                                    enum KOS_FIND_DIR_E reverse,
1299
                                    int                *pos)
1300
{
1301
    const int text_len     = (int)KOS_get_string_length(obj_id_text);
28✔
1302
    const int pat_len      = (int)KOS_get_string_length(obj_id_pattern);
28✔
1303
    int       text_pos     = *pos;
28✔
1304
    const int end_text_pos = reverse ? -1 : text_len - pat_len + 1;
28✔
1305
    const int delta        = reverse ? -1 : 1;
28✔
1306

1307
    while (text_pos != end_text_pos) {
107✔
1308

1309
        if (compare_slice(OBJPTR(STRING, obj_id_text), (unsigned)text_pos, (unsigned)(text_pos + pat_len),
103✔
1310
                          OBJPTR(STRING, obj_id_pattern), 0, (unsigned)pat_len) == 0) {
103✔
1311

1312
            *pos = text_pos;
24✔
1313
            return;
24✔
1314
        }
1315

1316
        text_pos += delta;
79✔
1317
    }
1318

1319
    *pos = -1;
4✔
1320
}
1321

1322
int KOS_string_find(KOS_CONTEXT         ctx,
6,410✔
1323
                    KOS_OBJ_ID          obj_id_text,
1324
                    KOS_OBJ_ID          obj_id_pattern,
1325
                    enum KOS_FIND_DIR_E reverse,
1326
                    int                *pos)
1327
{
1328
    int text_len;
1329
    int pattern_len;
1330
    int cur_pos;
1331

1332
    if (GET_OBJ_TYPE(obj_id_text) != OBJ_STRING ||
6,410✔
1333
        GET_OBJ_TYPE(obj_id_pattern) != OBJ_STRING) {
6,409✔
1334

1335
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_not_string));
1✔
1336
        return KOS_ERROR_EXCEPTION;
1✔
1337
    }
1338

1339
    pattern_len = (int)KOS_get_string_length(obj_id_pattern);
6,409✔
1340

1341
    if (pattern_len == 0)
6,409✔
1342
        return KOS_SUCCESS;
10✔
1343

1344
    cur_pos  = *pos;
6,399✔
1345
    text_len = (int)KOS_get_string_length(obj_id_text);
6,399✔
1346

1347
    if (cur_pos < 0 || cur_pos + pattern_len > text_len) {
6,399✔
1348
        *pos = -1;
6✔
1349
        return KOS_SUCCESS;
6✔
1350
    }
1351

1352
    if (pattern_len == 1)
6,393✔
1353
        return KOS_string_scan(ctx, obj_id_text, obj_id_pattern, reverse, KOS_SCAN_INCLUDE, pos);
6,365✔
1354

1355
    /* TODO Optimize */
1356

1357
    string_find_brute_force(obj_id_text, obj_id_pattern, reverse, pos);
28✔
1358
    return KOS_SUCCESS;
28✔
1359
}
1360

1361
int KOS_string_scan(KOS_CONTEXT             ctx,
6,655✔
1362
                    KOS_OBJ_ID              obj_id_text,
1363
                    KOS_OBJ_ID              obj_id_pattern,
1364
                    enum KOS_FIND_DIR_E     reverse,
1365
                    enum KOS_SCAN_INCLUDE_E include,
1366
                    int                    *pos)
1367
{
1368
    int     text_len;
1369
    int     pattern_len;
1370
    int     cur_pos;
1371
    uint8_t text_elem_size;
1372
    uint8_t pattern_elem_size;
1373

1374
    if (GET_OBJ_TYPE(obj_id_text) != OBJ_STRING ||
6,655✔
1375
        GET_OBJ_TYPE(obj_id_pattern) != OBJ_STRING) {
6,654✔
1376

1377
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_not_string));
1✔
1378
        return KOS_ERROR_EXCEPTION;
1✔
1379
    }
1380

1381
    pattern_len = (int)KOS_get_string_length(obj_id_pattern);
6,654✔
1382

1383
    if (pattern_len == 0)
6,654✔
1384
        return KOS_SUCCESS;
1✔
1385

1386
    cur_pos  = *pos;
6,653✔
1387
    text_len = (int)KOS_get_string_length(obj_id_text);
6,653✔
1388

1389
    if (cur_pos < 0 || cur_pos >= text_len) {
6,653✔
1390
        *pos = -1;
29✔
1391
        return KOS_SUCCESS;
29✔
1392
    }
1393

1394
    /* TODO Optimize */
1395

1396
    text_elem_size    = (uint8_t)kos_get_string_elem_size(OBJPTR(STRING, obj_id_text));
6,624✔
1397
    pattern_elem_size = (uint8_t)kos_get_string_elem_size(OBJPTR(STRING, obj_id_pattern));
6,624✔
1398

1399
    if ( ! reverse && include && pattern_len == 1
6,624✔
1400
        && text_elem_size    == KOS_STRING_ELEM_8
6,381✔
1401
        && pattern_elem_size == KOS_STRING_ELEM_8) {
6,378✔
1402

1403
        const uint8_t *text     = (const uint8_t *)kos_get_string_buffer(OBJPTR(STRING, obj_id_text));
6,378✔
1404
        const uint8_t *pattern  = (const uint8_t *)kos_get_string_buffer(OBJPTR(STRING, obj_id_pattern));
6,378✔
1405
        const uint8_t *location = text + cur_pos;
6,378✔
1406

1407
        location = (const uint8_t *)memchr(location, (int)*pattern, (size_t)(text_len - cur_pos));
6,378✔
1408

1409
        *pos = location ? (int)(location - text) : -1;
6,378✔
1410
        return KOS_SUCCESS;
6,378✔
1411
    }
1412
    else {
1413
        const uint8_t *text     = (const uint8_t *)kos_get_string_buffer(OBJPTR(STRING, obj_id_text));
246✔
1414
        const uint8_t *pattern  = (const uint8_t *)kos_get_string_buffer(OBJPTR(STRING, obj_id_pattern));
246✔
1415
        const uint8_t *location = text + (cur_pos << text_elem_size);
246✔
1416
        const uint8_t *text_end = text + (reverse ? -((intptr_t)1 << text_elem_size) : ((intptr_t)text_len << text_elem_size));
246✔
1417
        const int      delta    = reverse ? (int)((unsigned)-1 << text_elem_size) : (1 << text_elem_size);
246✔
1418
        const uint32_t c_mask   = (pattern_elem_size == KOS_STRING_ELEM_8)  ? ~0xFFU :
434✔
1419
                                  (pattern_elem_size == KOS_STRING_ELEM_16) ? ~0xFFFFU : 0U;
188✔
1420

1421
        for ( ; location != text_end; location += delta) {
668✔
1422

1423
            uint32_t code;
1424

1425
            switch (text_elem_size) {
610✔
1426

1427
                case KOS_STRING_ELEM_8:
577✔
1428
                    code = *location;
577✔
1429
                    break;
577✔
1430

1431
                case KOS_STRING_ELEM_16:
19✔
1432
                    code = *(const uint16_t *)location;
19✔
1433
                    if (code & c_mask)
19✔
1434
                        continue;
8✔
1435
                    break;
11✔
1436

1437
                default:
14✔
1438
                    assert(text_elem_size == KOS_STRING_ELEM_32);
14✔
1439
                    code = *(const uint32_t *)location;
14✔
1440
                    if (code & c_mask)
14✔
1441
                        continue;
6✔
1442
                    break;
8✔
1443
            }
1444

1445
            switch (pattern_elem_size) {
596✔
1446

1447
                case KOS_STRING_ELEM_8: {
120✔
1448
                    const uint8_t          *pat_ptr = pattern;
120✔
1449
                    const uint8_t          *pat_end = pat_ptr + pattern_len;
120✔
1450
                    enum KOS_SCAN_INCLUDE_E match   = KOS_SCAN_EXCLUDE;
120✔
1451
                    for ( ; pat_ptr != pat_end; ++pat_ptr)
306✔
1452
                        if (*pat_ptr == code) {
256✔
1453
                            match = KOS_SCAN_INCLUDE;
70✔
1454
                            break;
70✔
1455
                        }
1456
                    if (match == include) {
120✔
1457
                        *pos = (int)((location - text) >> text_elem_size);
56✔
1458
                        return KOS_SUCCESS;
56✔
1459
                    }
1460
                    break;
64✔
1461
                }
1462

1463
                case KOS_STRING_ELEM_16: {
476✔
1464
                    const uint16_t         *pat_ptr = (const uint16_t *)pattern;
476✔
1465
                    const uint16_t         *pat_end = pat_ptr + pattern_len;
476✔
1466
                    enum KOS_SCAN_INCLUDE_E match   = KOS_SCAN_EXCLUDE;
476✔
1467
                    for ( ; pat_ptr != pat_end; ++pat_ptr)
3,343✔
1468
                        if (*pat_ptr == code) {
3,062✔
1469
                            match = KOS_SCAN_INCLUDE;
195✔
1470
                            break;
195✔
1471
                        }
1472
                    if (match == include) {
476✔
1473
                        *pos = (int)((location - text) >> text_elem_size);
132✔
1474
                        return KOS_SUCCESS;
132✔
1475
                    }
1476
                    break;
344✔
1477
                }
1478

1479
                default: {
×
1480
                    const uint32_t         *pat_ptr = (const uint32_t *)pattern;
×
1481
                    const uint32_t         *pat_end = pat_ptr + pattern_len;
×
1482
                    enum KOS_SCAN_INCLUDE_E match   = KOS_SCAN_EXCLUDE;
×
1483
                    assert(pattern_elem_size == KOS_STRING_ELEM_32);
×
1484
                    for ( ; pat_ptr != pat_end; ++pat_ptr)
×
1485
                        if (*pat_ptr == code) {
×
1486
                            match = KOS_SCAN_INCLUDE;
×
1487
                            break;
×
1488
                        }
1489
                    if (match == include) {
×
1490
                        *pos = (int)((location - text) >> text_elem_size);
×
1491
                        return KOS_SUCCESS;
×
1492
                    }
1493
                    break;
×
1494
                }
1495
            }
1496
        }
1497

1498
        *pos = -1;
58✔
1499
        return KOS_SUCCESS;
58✔
1500
    }
1501
}
1502

1503
KOS_OBJ_ID KOS_string_reverse(KOS_CONTEXT ctx,
14✔
1504
                              KOS_OBJ_ID  obj_id)
1505
{
1506
    KOS_LOCAL   save_obj_id;
1507
    KOS_STRING *ret;
1508
    unsigned    len;
1509
    uint8_t     elem_size;
1510

1511
    if (GET_OBJ_TYPE(obj_id) != OBJ_STRING) {
14✔
1512
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_not_string));
2✔
1513
        return KOS_BADPTR;
2✔
1514
    }
1515

1516
    len = KOS_get_string_length(obj_id);
12✔
1517

1518
    if (len < 2)
12✔
1519
        return obj_id;
2✔
1520

1521
    elem_size = OBJPTR(STRING, obj_id)->header.flags & (KOS_STRING_ELEM_MASK | KOS_STRING_ASCII);
10✔
1522

1523
    KOS_init_local_with(ctx, &save_obj_id, obj_id);
10✔
1524

1525
    ret = new_empty_string(ctx, len, (KOS_STRING_FLAGS)elem_size);
10✔
1526

1527
    obj_id = KOS_destroy_top_local(ctx, &save_obj_id);
10✔
1528

1529
    if ( ! ret)
10✔
1530
        return KOS_BADPTR;
1✔
1531

1532
    switch (elem_size & KOS_STRING_ELEM_MASK) {
9✔
1533

1534
        case KOS_STRING_ELEM_8: {
7✔
1535
            const uint8_t *src  = (const uint8_t *)kos_get_string_buffer(OBJPTR(STRING, obj_id));
7✔
1536
            const uint8_t *end  = src + len;
7✔
1537
            uint8_t       *dest = (uint8_t *)kos_get_string_buffer(ret) + len - 1;
7✔
1538
            for ( ; src != end; ++src, --dest)
30✔
1539
                *dest = *src;
23✔
1540
            break;
7✔
1541
        }
1542

1543
        case KOS_STRING_ELEM_16: {
1✔
1544
            const uint16_t *src  = (const uint16_t *)kos_get_string_buffer(OBJPTR(STRING, obj_id));
1✔
1545
            const uint16_t *end  = src + len;
1✔
1546
            uint16_t       *dest = (uint16_t *)kos_get_string_buffer(ret) + len - 1;
1✔
1547
            for ( ; src != end; ++src, --dest)
3✔
1548
                *dest = *src;
2✔
1549
            break;
1✔
1550
        }
1551

1552
        default: {
1✔
1553
            const uint32_t *src  = (const uint32_t *)kos_get_string_buffer(OBJPTR(STRING, obj_id));
1✔
1554
            const uint32_t *end  = src + len;
1✔
1555
            uint32_t       *dest = (uint32_t *)kos_get_string_buffer(ret) + len - 1;
1✔
1556
            assert(kos_get_string_elem_size(OBJPTR(STRING, obj_id)) == KOS_STRING_ELEM_32);
1✔
1557
            for ( ; src != end; ++src, --dest)
4✔
1558
                *dest = *src;
3✔
1559
            break;
1✔
1560
        }
1561
    }
1562

1563
    return OBJID(STRING, ret);
9✔
1564
}
1565

1566
KOS_OBJ_ID KOS_string_repeat(KOS_CONTEXT ctx,
47✔
1567
                             KOS_OBJ_ID  obj_id,
1568
                             unsigned    num_repeat)
1569
{
1570
    KOS_LOCAL   save_obj_id;
1571
    KOS_STRING *new_str;
1572
    uint8_t    *in_buf;
1573
    uint8_t    *new_buf;
1574
    uint8_t    *end_buf;
1575
    uint8_t     elem_size;
1576
    unsigned    len;
1577

1578
    if (GET_OBJ_TYPE(obj_id) != OBJ_STRING) {
47✔
1579
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_not_string));
1✔
1580
        return KOS_BADPTR;
1✔
1581
    }
1582

1583
    len = KOS_get_string_length(obj_id);
46✔
1584

1585
    if (len == 0 || num_repeat == 0)
46✔
1586
        return KOS_STR_EMPTY;
7✔
1587

1588
    if (num_repeat == 1)
39✔
1589
        return obj_id;
7✔
1590

1591
    if (num_repeat > 0xFFFFU || (len * num_repeat) > 0xFFFFU) {
32✔
1592
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_too_many_repeats));
1✔
1593
        return KOS_BADPTR;
1✔
1594
    }
1595

1596
    elem_size = OBJPTR(STRING, obj_id)->header.flags & (KOS_STRING_ELEM_MASK | KOS_STRING_ASCII);
31✔
1597

1598
    KOS_init_local_with(ctx, &save_obj_id, obj_id);
31✔
1599

1600
    new_str = new_empty_string(ctx, len * num_repeat, (KOS_STRING_FLAGS)elem_size);
31✔
1601

1602
    obj_id = KOS_destroy_top_local(ctx, &save_obj_id);
31✔
1603

1604
    if ( ! new_str)
31✔
1605
        return KOS_BADPTR;
1✔
1606

1607
    in_buf  = (uint8_t *)kos_get_string_buffer(OBJPTR(STRING, obj_id));
30✔
1608
    new_buf = (uint8_t *)kos_get_string_buffer(new_str);
30✔
1609

1610
    len <<= elem_size & KOS_STRING_ELEM_MASK;
30✔
1611

1612
    end_buf = new_buf + (len * num_repeat);
30✔
1613

1614
    while (new_buf < end_buf) {
4,258✔
1615
        memcpy(new_buf, in_buf, len);
4,228✔
1616
        new_buf += len;
4,228✔
1617
    }
1618

1619
    return OBJID(STRING, new_str);
30✔
1620
}
1621

1622
KOS_OBJ_ID KOS_string_lowercase(KOS_CONTEXT ctx, KOS_OBJ_ID obj_id)
25✔
1623
{
1624
    KOS_LOCAL   save_obj_id;
1625
    KOS_STRING *new_str;
1626
    unsigned    len;
1627
    unsigned    i;
1628
    uint8_t     elem_size;
1629

1630
    if (GET_OBJ_TYPE(obj_id) != OBJ_STRING) {
25✔
1631
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_not_string));
2✔
1632
        return KOS_BADPTR;
2✔
1633
    }
1634

1635
    len       = KOS_get_string_length(obj_id);
23✔
1636
    elem_size = OBJPTR(STRING, obj_id)->header.flags & (KOS_STRING_ELEM_MASK | KOS_STRING_ASCII);
23✔
1637

1638
    if (len == 0)
23✔
1639
        return KOS_STR_EMPTY;
1✔
1640

1641
    KOS_init_local_with(ctx, &save_obj_id, obj_id);
22✔
1642

1643
    new_str = new_empty_string(ctx, len, (KOS_STRING_FLAGS)elem_size);
22✔
1644

1645
    obj_id = KOS_destroy_top_local(ctx, &save_obj_id);
22✔
1646

1647
    if ( ! new_str)
22✔
1648
        return OBJID(STRING, new_str);
1✔
1649

1650
    switch (elem_size & KOS_STRING_ELEM_MASK) {
21✔
1651

1652
        case KOS_STRING_ELEM_8: {
19✔
1653
            const uint8_t *src  = (const uint8_t *)kos_get_string_buffer(OBJPTR(STRING, obj_id));
19✔
1654
            uint8_t       *dest = (uint8_t *)kos_get_string_buffer(new_str);
19✔
1655

1656
            for (i = 0; i < len; i++) {
64✔
1657
                const uint8_t c = *(src++);
45✔
1658
                *(dest++) = (uint8_t)kos_unicode_to_lower(c);
45✔
1659
            }
1660
            break;
19✔
1661
        }
1662

1663
        case KOS_STRING_ELEM_16: {
1✔
1664
            const uint16_t *src  = (const uint16_t *)kos_get_string_buffer(OBJPTR(STRING, obj_id));
1✔
1665
            uint16_t       *dest = (uint16_t *)kos_get_string_buffer(new_str);
1✔
1666

1667
            for (i = 0; i < len; i++) {
6✔
1668
                const uint16_t c = *(src++);
5✔
1669
                *(dest++) = (uint16_t)kos_unicode_to_lower(c);
5✔
1670
            }
1671
            break;
1✔
1672
        }
1673

1674
        default: {
1✔
1675
            const uint32_t *src  = (const uint32_t *)kos_get_string_buffer(OBJPTR(STRING, obj_id));
1✔
1676
            uint32_t       *dest = (uint32_t *)kos_get_string_buffer(new_str);
1✔
1677

1678
            assert((elem_size & KOS_STRING_ELEM_MASK) == KOS_STRING_ELEM_32);
1✔
1679

1680
            for (i = 0; i < len; i++) {
6✔
1681
                uint32_t c = *(src++);
5✔
1682

1683
                if (c < 0x10000U)
5✔
1684
                    c = (uint32_t)kos_unicode_to_lower((uint16_t)c);
4✔
1685

1686
                *(dest++) = c;
5✔
1687
            }
1688
            break;
1✔
1689
        }
1690
    }
1691

1692
    return OBJID(STRING, new_str);
21✔
1693
}
1694

1695
KOS_OBJ_ID KOS_string_uppercase(KOS_CONTEXT ctx, KOS_OBJ_ID obj_id)
30✔
1696
{
1697
    KOS_LOCAL   save_obj_id;
1698
    KOS_STRING *new_str;
1699
    unsigned    len;
1700
    unsigned    i;
1701
    uint8_t     elem_size;
1702

1703
    if (GET_OBJ_TYPE(obj_id) != OBJ_STRING) {
30✔
1704
        KOS_raise_exception(ctx, KOS_CONST_ID(str_err_not_string));
2✔
1705
        return KOS_BADPTR;
2✔
1706
    }
1707

1708
    len       = KOS_get_string_length(obj_id);
28✔
1709
    elem_size = OBJPTR(STRING, obj_id)->header.flags & (KOS_STRING_ELEM_MASK | KOS_STRING_ASCII);
28✔
1710

1711
    if (len == 0)
28✔
1712
        return KOS_STR_EMPTY;
1✔
1713

1714
    KOS_init_local_with(ctx, &save_obj_id, obj_id);
27✔
1715

1716
    new_str = new_empty_string(ctx, len, (KOS_STRING_FLAGS)elem_size);
27✔
1717

1718
    obj_id = KOS_destroy_top_local(ctx, &save_obj_id);
27✔
1719

1720
    if ( ! new_str)
27✔
1721
        return OBJID(STRING, new_str);
1✔
1722

1723
    switch (elem_size & KOS_STRING_ELEM_MASK) {
26✔
1724

1725
        case KOS_STRING_ELEM_8: {
24✔
1726
            const uint8_t *src  = (const uint8_t *)kos_get_string_buffer(OBJPTR(STRING, obj_id));
24✔
1727
            uint8_t       *dest = (uint8_t *)kos_get_string_buffer(new_str);
24✔
1728

1729
            for (i = 0; i < len; i++) {
79✔
1730
                const uint8_t c = *(src++);
55✔
1731
                *(dest++) = (uint8_t)kos_unicode_to_upper(c);
55✔
1732
            }
1733
            break;
24✔
1734
        }
1735

1736
        case KOS_STRING_ELEM_16: {
1✔
1737
            const uint16_t *src  = (const uint16_t *)kos_get_string_buffer(OBJPTR(STRING, obj_id));
1✔
1738
            uint16_t       *dest = (uint16_t *)kos_get_string_buffer(new_str);
1✔
1739

1740
            for (i = 0; i < len; i++) {
6✔
1741
                const uint16_t c = *(src++);
5✔
1742
                *(dest++) = (uint16_t)kos_unicode_to_upper(c);
5✔
1743
            }
1744
            break;
1✔
1745
        }
1746

1747
        default: {
1✔
1748
            const uint32_t *src  = (const uint32_t *)kos_get_string_buffer(OBJPTR(STRING, obj_id));
1✔
1749
            uint32_t       *dest = (uint32_t *)kos_get_string_buffer(new_str);
1✔
1750

1751
            assert((elem_size & KOS_STRING_ELEM_MASK) == KOS_STRING_ELEM_32);
1✔
1752

1753
            for (i = 0; i < len; i++) {
6✔
1754
                uint32_t c = *(src++);
5✔
1755

1756
                if (c < 0x10000U)
5✔
1757
                    c = (uint32_t)kos_unicode_to_upper((uint16_t)c);
4✔
1758

1759
                *(dest++) = c;
5✔
1760
            }
1761
            break;
1✔
1762
        }
1763
    }
1764

1765
    return OBJID(STRING, new_str);
26✔
1766
}
1767

1768
void KOS_init_string_iter(KOS_STRING_ITER *iter, KOS_OBJ_ID str_id)
1,486✔
1769
{
1770
    KOS_STRING_FLAGS elem_size;
1771
    const uint8_t   *ptr;
1772

1773
    assert( ! IS_BAD_PTR(str_id));
1,486✔
1774
    assert(GET_OBJ_TYPE(str_id) == OBJ_STRING);
1,486✔
1775

1776
    elem_size = kos_get_string_elem_size(OBJPTR(STRING, str_id));
1,486✔
1777

1778
    ptr = (const uint8_t *)kos_get_string_buffer(OBJPTR(STRING, str_id));
1,486✔
1779

1780
    iter->ptr       = ptr;
1,486✔
1781
    iter->end       = ptr + (KOS_get_string_length(str_id) << elem_size);
1,486✔
1782
    iter->elem_size = elem_size;
1,486✔
1783
}
1,486✔
1784

1785
uint32_t KOS_string_iter_peek_next_code(KOS_STRING_ITER *iter)
3,250✔
1786
{
1787
    uint32_t ret;
1788

1789
    switch (iter->elem_size) {
3,250✔
1790

1791
        case KOS_STRING_ELEM_8:
3,181✔
1792
            ret = *iter->ptr;
3,181✔
1793
            break;
3,181✔
1794

1795
        case KOS_STRING_ELEM_16:
3✔
1796
            ret = *(const uint16_t *)iter->ptr;
3✔
1797
            break;
3✔
1798

1799
        default:
66✔
1800
            assert(iter->elem_size == KOS_STRING_ELEM_32);
66✔
1801
            ret = *(const uint32_t *)iter->ptr;
66✔
1802
            break;
66✔
1803
    }
1804

1805
    return ret;
3,250✔
1806
}
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