Files
Wellington Dias 1d51343ac1 Build and run the runtime natively on Linux.
Shared runtime code now goes through runtime/platform/platform.h, implemented by platform/win32.c and a new
platform/linux.c: futex locks, a userfaultfd and PAGEMAP_SCAN write watch, signal-based async preemption, our own
fiber switch, and a SIGSEGV handler for the thaw and crash hooks. The window, input, menu and software keyboard get
SDL3 back ends beside the Win32 ones, with the swapchain (gfx_present.c) and input merging (input.c) shared. SHA-256
replaces BCrypt, ELF builds get their own override sections, and the build tools gain Linux branches.

On Linux the selftest and the gfx-abi, astc, nvn and window probes pass. probe-vkmem still fails: imported guest
memory cannot be write-watched on Linux, which the shadow buffers in vk_memory.c will address.
2026-10-29 16:56:56 -03:00

60 lines
2.3 KiB
C

// UTF-8 <-> UTF-16 conversion for the strings the guest keeps in UTF-16 (software keyboard, probes). Portable C, no
// OS calls; malformed UTF-8 bytes and unpaired surrogates become U+FFFD.
#pragma once
#include <stddef.h>
#include <stdint.h>
// utf8 into out as NUL-terminated UTF-16, at most cap units with the NUL; returns the units written before the NUL.
static inline size_t utf8_to_utf16(const char *utf8, uint16_t *out, size_t cap) {
const unsigned char *s = (const unsigned char *)utf8;
size_t n = 0;
if (!cap) return 0;
while (*s && n + 1 < cap) {
uint32_t c = *s++;
int more = c >= 0xf0 && c < 0xf8 ? 3 : c >= 0xe0 ? 2 : c >= 0xc0 ? 1 : 0;
if (c >= 0x80 && !more) c = 0xfffd;
else if (more) c &= 0x3f >> more;
for (; more; more--) {
if ((*s & 0xc0) != 0x80) {
c = 0xfffd;
break;
}
c = c << 6 | (*s++ & 0x3f);
}
if (c > 0x10ffff || (c >= 0xd800 && c < 0xe000)) c = 0xfffd;
if (c >= 0x10000) {
if (n + 2 >= cap) break;
c -= 0x10000;
out[n++] = (uint16_t)(0xd800 + (c >> 10));
out[n++] = (uint16_t)(0xdc00 + (c & 0x3ff));
} else {
out[n++] = (uint16_t)c;
}
}
out[n] = 0;
return n;
}
// NUL-terminated (or n units, whichever comes first) UTF-16 into out as NUL-terminated UTF-8, at most cap bytes with
// the NUL; returns the bytes written before the NUL.
static inline size_t utf16_to_utf8(const uint16_t *in, size_t n, char *out, size_t cap) {
size_t k = 0;
if (!cap) return 0;
for (size_t i = 0; i < n && in[i]; i++) {
uint32_t c = in[i];
if (c >= 0xd800 && c < 0xdc00 && i + 1 < n && in[i + 1] >= 0xdc00 && in[i + 1] < 0xe000)
c = 0x10000 + ((c - 0xd800) << 10) + (in[++i] - 0xdc00);
else if (c >= 0xd800 && c < 0xe000)
c = 0xfffd;
int len = c < 0x80 ? 1 : c < 0x800 ? 2 : c < 0x10000 ? 3 : 4;
if (k + (size_t)len >= cap) break;
if (len == 1) out[k++] = (char)c;
else {
out[k++] = (char)((len == 2 ? 0xc0 : len == 3 ? 0xe0 : 0xf0) | (c >> (6 * (len - 1))));
for (int j = len - 2; j >= 0; j--) out[k++] = (char)(0x80 | ((c >> (6 * j)) & 0x3f));
}
}
out[k] = 0;
return k;
}