fix: avoid invalid UTF-16 in truncation (#567)

This commit is contained in:
Peter Steinberger
2026-01-09 14:19:25 +01:00
parent fd535a50d3
commit 63f5fa47de
7 changed files with 70 additions and 7 deletions

View File

@@ -95,6 +95,61 @@ export function sleep(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
function isHighSurrogate(codeUnit: number): boolean {
return codeUnit >= 0xd800 && codeUnit <= 0xdbff;
}
function isLowSurrogate(codeUnit: number): boolean {
return codeUnit >= 0xdc00 && codeUnit <= 0xdfff;
}
export function sliceUtf16Safe(
input: string,
start: number,
end?: number,
): string {
const len = input.length;
let from = start < 0 ? Math.max(len + start, 0) : Math.min(start, len);
let to =
end === undefined
? len
: end < 0
? Math.max(len + end, 0)
: Math.min(end, len);
if (to < from) {
const tmp = from;
from = to;
to = tmp;
}
if (from > 0 && from < len) {
const codeUnit = input.charCodeAt(from);
if (
isLowSurrogate(codeUnit) &&
isHighSurrogate(input.charCodeAt(from - 1))
) {
from += 1;
}
}
if (to > 0 && to < len) {
const codeUnit = input.charCodeAt(to - 1);
if (isHighSurrogate(codeUnit) && isLowSurrogate(input.charCodeAt(to))) {
to -= 1;
}
}
return input.slice(from, to);
}
export function truncateUtf16Safe(input: string, maxLen: number): string {
const limit = Math.max(0, Math.floor(maxLen));
if (input.length <= limit) return input;
return sliceUtf16Safe(input, 0, limit);
}
export function resolveUserPath(input: string): string {
const trimmed = input.trim();
if (!trimmed) return trimmed;