110 lines
3.3 KiB
TypeScript
110 lines
3.3 KiB
TypeScript
/**
|
|
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
|
|
* @license MIT
|
|
*/
|
|
|
|
/**
|
|
* StringToUtf32 - decodes UTF16 sequences into UTF32 codepoints.
|
|
* To keep the decoder in line with JS strings it handles single surrogates as UCS2.
|
|
*/
|
|
export class StringToUtf32 {
|
|
private _interim: number = 0;
|
|
|
|
/**
|
|
* Clears interim and resets decoder to clean state.
|
|
*/
|
|
public clear(): void {
|
|
this._interim = 0;
|
|
}
|
|
|
|
/**
|
|
* Decode JS string to UTF32 codepoints.
|
|
* The methods assumes stream input and will store partly transmitted
|
|
* surrogate pairs and decode them with the next data chunk.
|
|
* Note: The method does no bound checks for target, therefore make sure
|
|
* the provided input data does not exceed the size of `target`.
|
|
* Returns the number of written codepoints in `target`.
|
|
*/
|
|
decode(input: string, target: Uint32Array): number {
|
|
const length = input.length;
|
|
|
|
if (!length) {
|
|
return 0;
|
|
}
|
|
|
|
let size = 0;
|
|
let startPos = 0;
|
|
|
|
// handle leftover surrogate high
|
|
if (this._interim) {
|
|
const second = input.charCodeAt(startPos++);
|
|
if (0xDC00 <= second && second <= 0xDFFF) {
|
|
target[size++] = (this._interim - 0xD800) * 0x400 + second - 0xDC00 + 0x10000;
|
|
} else {
|
|
// illegal codepoint (USC2 handling)
|
|
target[size++] = this._interim;
|
|
target[size++] = second;
|
|
}
|
|
this._interim = 0;
|
|
}
|
|
|
|
for (let i = startPos; i < length; ++i) {
|
|
const code = input.charCodeAt(i);
|
|
// surrogate pair first
|
|
if (0xD800 <= code && code <= 0xDBFF) {
|
|
if (++i >= length) {
|
|
this._interim = code;
|
|
return size;
|
|
}
|
|
const second = input.charCodeAt(i);
|
|
if (0xDC00 <= second && second <= 0xDFFF) {
|
|
target[size++] = (code - 0xD800) * 0x400 + second - 0xDC00 + 0x10000;
|
|
} else {
|
|
// illegal codepoint (USC2 handling)
|
|
target[size++] = code;
|
|
target[size++] = second;
|
|
}
|
|
continue;
|
|
}
|
|
target[size++] = code;
|
|
}
|
|
return size;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Convert UTF32 codepoint into JS string.
|
|
*/
|
|
export function stringFromCodePoint(codePoint: number): string {
|
|
if (codePoint > 0xFFFF) {
|
|
// UTF32 to UTF16 conversion (see comments in utf32ToString)
|
|
codePoint -= 0x10000;
|
|
return String.fromCharCode((codePoint >> 10) + 0xD800) + String.fromCharCode((codePoint % 0x400) + 0xDC00);
|
|
}
|
|
return String.fromCharCode(codePoint);
|
|
}
|
|
|
|
/**
|
|
* Convert UTF32 char codes into JS string.
|
|
* Basically the same as `stringFromCodePoint` but for multiple codepoints
|
|
* in a loop (which is a lot faster).
|
|
*/
|
|
export function utf32ToString(data: Uint32Array, start: number = 0, end: number = data.length): string {
|
|
let result = '';
|
|
for (let i = start; i < end; ++i) {
|
|
let codepoint = data[i];
|
|
if (codepoint > 0xFFFF) {
|
|
// JS string are encoded as UTF16, thus a non BMP codepoint gets converted into a surrogate pair
|
|
// conversion rules:
|
|
// - subtract 0x10000 from code point, leaving a 20 bit number
|
|
// - add high 10 bits to 0xD800 --> first surrogate
|
|
// - add low 10 bits to 0xDC00 --> second surrogate
|
|
codepoint -= 0x10000;
|
|
result += String.fromCharCode((codepoint >> 10) + 0xD800) + String.fromCharCode((codepoint % 0x400) + 0xDC00);
|
|
} else {
|
|
result += String.fromCharCode(codepoint);
|
|
}
|
|
}
|
|
return result;
|
|
}
|