Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 8 additions & 51 deletions src/util/utf8.ts
Original file line number Diff line number Diff line change
@@ -1,60 +1,17 @@
// UTF-8 helpers, so the library doesn't depend on TextEncoder/TextDecoder being available
// TextEncoder and TextDecoder are web APIs, not part of the es2022 lib: declare the parts used here
declare const TextEncoder: { new(): { encode(text: string): Uint8Array } };
declare const TextDecoder: { new(): { decode(bytes: Uint8Array): string } };

export function encode(text: string): Uint8Array {
const out: number[] = [];

for (const char of text) {
const c = char.codePointAt(0) as number;
if (c < 0x80) out.push(c);
else if (c < 0x800) out.push(0xc0 | c >> 6, 0x80 | c & 63);
else if (c < 0x10000) out.push(0xe0 | c >> 12, 0x80 | c >> 6 & 63, 0x80 | c & 63);
else out.push(0xf0 | c >> 18, 0x80 | c >> 12 & 63, 0x80 | c >> 6 & 63, 0x80 | c & 63);
}
const encoder = new TextEncoder();
const decoder = new TextDecoder();

return Uint8Array.from(out);
export function encode(text: string): Uint8Array {
return encoder.encode(text);
}

/**
* Decodes UTF-8, replacing invalid sequences with U+FFFD and skipping a leading byte order mark
*/
export function decode(bytes: Uint8Array): string {
let out = '';
let i = bytes[0] === 0xef && bytes[1] === 0xbb && bytes[2] === 0xbf ? 3 : 0;

while (i < bytes.length) {
const b = bytes[i++];
let c: number, extra: number;

if (b < 0x80) {
c = b;
extra = 0;
} else if (b >= 0xc2 && b < 0xe0) {
c = b & 31;
extra = 1;
} else if (b >= 0xe0 && b < 0xf0) {
c = b & 15;
extra = 2;
} else if (b >= 0xf0 && b < 0xf5) {
c = b & 7;
extra = 3;
} else {
out += '�';
continue;
}

let valid = true;
for (let k = 0; k < extra; k++) {
const next = bytes[i];
if (next === undefined || (next & 0xc0) !== 0x80) {
valid = false;
break;
}
c = c << 6 | next & 63;
i++;
}

out += valid && c <= 0x10ffff ? String.fromCodePoint(c) : '�';
}

return out;
return decoder.decode(bytes);
}
Loading