fix(runtime): handle multi-byte UTF-8 in JSON string escaping
Validate UTF-8 continuation bytes in jb_emit_escaped; pass valid sequences through and escape orphaned/invalid start bytes as \u00xx. Pre-existing change found uncommitted in the working tree; committed here so it is reviewable rather than lost. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3135,23 +3135,49 @@ static void jb_puts(JsonBuf* b, const char* s) {
|
||||
|
||||
static void jb_emit_escaped(JsonBuf* b, const char* s) {
|
||||
jb_putc(b, '"');
|
||||
for (; *s; s++) {
|
||||
unsigned char c = (unsigned char)*s;
|
||||
const unsigned char* p = (const unsigned char*)s;
|
||||
while (*p) {
|
||||
unsigned char c = *p;
|
||||
switch (c) {
|
||||
case '"': jb_puts(b, "\\\""); break;
|
||||
case '\\': jb_puts(b, "\\\\"); break;
|
||||
case '\b': jb_puts(b, "\\b"); break;
|
||||
case '\f': jb_puts(b, "\\f"); break;
|
||||
case '\n': jb_puts(b, "\\n"); break;
|
||||
case '\r': jb_puts(b, "\\r"); break;
|
||||
case '\t': jb_puts(b, "\\t"); break;
|
||||
case '"': jb_puts(b, "\\\""); p++; break;
|
||||
case '\\': jb_puts(b, "\\\\"); p++; break;
|
||||
case '\b': jb_puts(b, "\\b"); p++; break;
|
||||
case '\f': jb_puts(b, "\\f"); p++; break;
|
||||
case '\n': jb_puts(b, "\\n"); p++; break;
|
||||
case '\r': jb_puts(b, "\\r"); p++; break;
|
||||
case '\t': jb_puts(b, "\\t"); p++; break;
|
||||
default:
|
||||
if (c < 0x20) {
|
||||
char tmp[8];
|
||||
snprintf(tmp, sizeof(tmp), "\\u%04x", c);
|
||||
jb_puts(b, tmp);
|
||||
} else {
|
||||
p++;
|
||||
} else if (c < 0x80) {
|
||||
jb_putc(b, (char)c);
|
||||
p++;
|
||||
} else {
|
||||
/* Multi-byte UTF-8: validate sequence, pass through if valid,
|
||||
* escape as \u00xx if the start byte is invalid/orphaned. */
|
||||
int seq_len = 0;
|
||||
if ((c & 0xE0) == 0xC0) seq_len = 2;
|
||||
else if ((c & 0xF0) == 0xE0) seq_len = 3;
|
||||
else if ((c & 0xF8) == 0xF0) seq_len = 4;
|
||||
if (seq_len >= 2) {
|
||||
int valid = 1;
|
||||
for (int i = 1; i < seq_len; i++) {
|
||||
if ((p[i] & 0xC0) != 0x80) { valid = 0; break; }
|
||||
}
|
||||
if (valid) {
|
||||
for (int i = 0; i < seq_len; i++) jb_putc(b, (char)p[i]);
|
||||
p += seq_len;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Invalid start byte or truncated sequence — escape it */
|
||||
char tmp[8];
|
||||
snprintf(tmp, sizeof(tmp), "\\u%04x", c);
|
||||
jb_puts(b, tmp);
|
||||
p++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user