170 lines
4.7 KiB
C
170 lines
4.7 KiB
C
// Strings: UTF-16 code units, as JS counts them
|
|
#pragma once
|
|
|
|
#include "core.h"
|
|
|
|
// A string. The emitted C makes its literals as static strings of this
|
|
struct jsrt_string {
|
|
int32_t length;
|
|
uint16_t units[];
|
|
};
|
|
|
|
string_t __rt_string_lit(const char *utf8);
|
|
string_t __rt_string_from_utf8(const char *utf8, size_t size);
|
|
int32_t __rt_string_length(string_t s);
|
|
bool __rt_string_eq(string_t a, string_t b);
|
|
int32_t __rt_string_cmp(string_t a, string_t b);
|
|
|
|
// `a + b`
|
|
string_t __rt_string_concat(string_t a, string_t b);
|
|
|
|
// `s[i]`: the code unit at `i`, as a string. `i` must be in the string
|
|
string_t __rt_string_at(string_t s, int32_t i);
|
|
|
|
#ifdef JSRT_IMPLEMENTATION
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
static string_t __rt_string_new(int32_t length) {
|
|
string_t s = __rt_alloc(sizeof *s + (size_t)length * sizeof(uint16_t));
|
|
s->length = length;
|
|
return s;
|
|
}
|
|
|
|
string_t __rt_string_lit(const char *utf8) {
|
|
return __rt_string_from_utf8(utf8, strlen(utf8));
|
|
}
|
|
|
|
// The `size` bytes at `utf8`, which need not end in a 0
|
|
string_t __rt_string_from_utf8(const char *utf8, size_t size) {
|
|
const unsigned char *p = (const unsigned char *)utf8, *end = p + size;
|
|
string_t s = __rt_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 __rt_string_length(string_t s) {
|
|
return s->length;
|
|
}
|
|
|
|
bool __rt_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 __rt_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 __rt_string_concat(string_t a, string_t b) {
|
|
string_t s = __rt_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;
|
|
}
|
|
|
|
// The strings of one ASCII unit, made once each, the first time they are needed
|
|
static string_t __rt_ascii_strings[128];
|
|
|
|
string_t __rt_string_at(string_t s, int32_t i) {
|
|
uint16_t unit = s->units[i];
|
|
|
|
if (unit < 128 && __rt_ascii_strings[unit]) {
|
|
return __rt_ascii_strings[unit];
|
|
}
|
|
|
|
string_t c = __rt_string_new(1);
|
|
c->units[0] = unit;
|
|
|
|
if (unit < 128) {
|
|
__rt_ascii_strings[unit] = c;
|
|
}
|
|
|
|
return c;
|
|
}
|
|
|
|
// `s` as UTF-8, in a new buffer for the caller to free
|
|
static char *__rt_string_utf8(string_t s) {
|
|
char *utf8 = __rt_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;
|
|
}
|
|
|
|
// ── for the stdlib ──
|
|
|
|
string_t __rt_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 = __rt_string_new(end - start);
|
|
memcpy(r->units, s->units + start, (size_t)(end - start) * sizeof(uint16_t));
|
|
return r;
|
|
}
|
|
|
|
int32_t __rt_string_char_code_at(string_t s, int32_t i) {
|
|
return s->units[i];
|
|
}
|
|
|
|
string_t __rt_string_from_char_code(int32_t code) {
|
|
string_t s = __rt_string_new(1);
|
|
s->units[0] = (uint16_t)code;
|
|
return s;
|
|
}
|
|
|
|
#endif // JSRT_IMPLEMENTATION
|