// Strings: UTF-16 code units, as JS counts them, and the string natives #pragma once #include "core.h" string_t jsrt_string_lit(const char *utf8); string_t jsrt_string_from_utf8(const char *utf8, size_t size); int32_t jsrt_string_length(string_t s); bool jsrt_string_eq(string_t a, string_t b); int32_t jsrt_string_cmp(string_t a, string_t b); // `s[i]`: the code unit at `i`, as a string, or JSRT_UNDEFINED past the end string_t jsrt_string_at(string_t s, int32_t i); #ifdef JSRT_IMPLEMENTATION #include #include struct jsrt_string { int32_t length; uint16_t units[]; }; static string_t jsrt_string_new(int32_t length) { string_t s = jsrt_alloc(sizeof *s + (size_t)length * sizeof(uint16_t)); s->length = length; return s; } string_t jsrt_string_lit(const char *utf8) { return jsrt_string_from_utf8(utf8, strlen(utf8)); } // The `size` bytes at `utf8`, which need not end in a 0 string_t jsrt_string_from_utf8(const char *utf8, size_t size) { const unsigned char *p = (const unsigned char *)utf8, *end = p + size; string_t s = jsrt_string_new((int32_t)size * 2); int32_t n = 0; while (p < end) { uint32_t c = *p++; int extra = c >= 0xF0 ? 3 : c >= 0xE0 ? 2 : c >= 0xC0 ? 1 : 0; c &= extra ? 0x3F >> extra : 0x7F; for (; extra && p < end; extra--) { c = (c << 6) | (*p++ & 0x3F); } if (c >= 0x10000) { c -= 0x10000; s->units[n++] = (uint16_t)(0xD800 + (c >> 10)); s->units[n++] = (uint16_t)(0xDC00 + (c & 0x3FF)); } else { s->units[n++] = (uint16_t)c; } } s->length = n; return s; } int32_t jsrt_string_length(string_t s) { return s->length; } bool jsrt_string_eq(string_t a, string_t b) { return a->length == b->length && !memcmp(a->units, b->units, (size_t)a->length * sizeof(uint16_t)); } int32_t jsrt_string_cmp(string_t a, string_t b) { int32_t n = a->length < b->length ? a->length : b->length; for (int32_t i = 0; i < n; i++) { if (a->units[i] != b->units[i]) { return a->units[i] < b->units[i] ? -1 : 1; } } return a->length < b->length ? -1 : a->length > b->length; } string_t jsrt_string_at(string_t s, int32_t i) { if (i < 0 || i >= s->length) { return JSRT_UNDEFINED; } string_t c = jsrt_string_new(1); c->units[0] = s->units[i]; return c; } // `s` as UTF-8, in a new buffer for the caller to free static char *jsrt_string_utf8(string_t s) { char *utf8 = jsrt_alloc((size_t)s->length * 3 + 1); char *out = utf8; for (int32_t i = 0; i < s->length; i++) { uint32_t c = s->units[i]; bool pair = c >= 0xD800 && c < 0xDC00 && i + 1 < s->length && s->units[i + 1] >= 0xDC00 && s->units[i + 1] < 0xE000; if (pair) { c = 0x10000 + ((c - 0xD800) << 10) + (s->units[++i] - 0xDC00); } if (c < 0x80) { *out++ = (char)c; } else if (c < 0x800) { *out++ = (char)(0xC0 | c >> 6); *out++ = (char)(0x80 | (c & 0x3F)); } else if (c < 0x10000) { *out++ = (char)(0xE0 | c >> 12); *out++ = (char)(0x80 | (c >> 6 & 0x3F)); *out++ = (char)(0x80 | (c & 0x3F)); } else { *out++ = (char)(0xF0 | c >> 18); *out++ = (char)(0x80 | (c >> 12 & 0x3F)); *out++ = (char)(0x80 | (c >> 6 & 0x3F)); *out++ = (char)(0x80 | (c & 0x3F)); } } *out = 0; return utf8; } // ── natives ── string_t __native_string_concat(string_t a, string_t b) { string_t s = jsrt_string_new(a->length + b->length); memcpy(s->units, a->units, (size_t)a->length * sizeof(uint16_t)); memcpy(s->units + a->length, b->units, (size_t)b->length * sizeof(uint16_t)); return s; } string_t __native_string_slice(string_t s, int32_t start, int32_t end) { start = start < 0 ? 0 : start > s->length ? s->length : start; end = end < start ? start : end > s->length ? s->length : end; string_t r = jsrt_string_new(end - start); memcpy(r->units, s->units + start, (size_t)(end - start) * sizeof(uint16_t)); return r; } int32_t __native_string_char_code_at(string_t s, int32_t i) { return s->units[i]; } string_t __native_string_from_char_code(int32_t code) { string_t s = jsrt_string_new(1); s->units[0] = (uint16_t)code; return s; } #endif // JSRT_IMPLEMENTATION