@nberlette/utf8
Version:
Blazing fast universal ponyfills for TextEncoder and TextDecoder.
238 lines • 11 kB
JavaScript
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
if (kind === "m") throw new TypeError("Private method is not writable");
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
};
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var _TextDecoder_encoding, _TextDecoder_fatal, _TextDecoder_ignoreBOM, _TextDecoder_buffer;
/**
* This module provides a high performance dependency-free ponyfill for the
* `TextDecoder` Web API, allowing you to decode UTF-8 encoded `BufferSource`
* buffers into strings in any ES2015+ environment.
*
* Currently only UTF-8 encoding is supported, but additional encodings will be
* added in the near future. Encodings that are currently being implemented are
* UTF-16, ISO-8859-1, Windows-1252, and ASCII.
*
* @module text-decoder
*/
import { normalizeEncoding, StringFromCharCode, toUint8Array, TypeError, Uint8Array, Uint8ArrayPrototypeSubarray, undefined, } from "./_internal.js";
/**
* Decodes an encoded sequence of bytes into a string, using the specified
* encoding standard. Currently, only UTF-8 encoding is supported.
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder
* @category Encoding
* @tags utf-8, decoder
*/
export class TextDecoder {
/**
* Creates a new TextDecoder instance.
* @param label The encoding to use. Currently, only "utf-8" is supported.
* @param options Configuration options.
*/
constructor(label = "utf-8", options = {}) {
_TextDecoder_encoding.set(this, void 0);
_TextDecoder_fatal.set(this, void 0);
_TextDecoder_ignoreBOM.set(this, void 0);
_TextDecoder_buffer.set(this, new Uint8Array(0));
__classPrivateFieldSet(this, _TextDecoder_encoding, normalizeEncoding(label), "f");
__classPrivateFieldSet(this, _TextDecoder_fatal, !!options.fatal, "f");
__classPrivateFieldSet(this, _TextDecoder_ignoreBOM, !!options.ignoreBOM, "f");
if (__classPrivateFieldGet(this, _TextDecoder_encoding, "f") !== "utf-8") {
throw new TypeError(`The encoding "${label}" is not supported.`);
}
}
/** The encoding standard to use. */
get encoding() {
return __classPrivateFieldGet(this, _TextDecoder_encoding, "f");
}
/** If true, invalid bytes will throw a TypeError. */
get fatal() {
return __classPrivateFieldGet(this, _TextDecoder_fatal, "f");
}
/** If true, the BOM (Byte Order Mark) will be ignored. */
get ignoreBOM() {
return __classPrivateFieldGet(this, _TextDecoder_ignoreBOM, "f");
}
/**
* Decodes a BufferSource into a string using UTF-8 decoding.
*
* @param input The bytes to decode. Defaults to an empty Uint8Array.
* @param [options] Decoding options.
* @returns The decoded string.
* @throws if the input is not a BufferSource.
* @throws if fatal is true and an invalid byte sequence is encountered.
*/
decode(input, options) {
const stream = options?.stream ?? false;
let bytes = toUint8Array(input);
// Concatenate any leftover bytes from the previous decode call
if (__classPrivateFieldGet(this, _TextDecoder_buffer, "f").length > 0) {
const combined = new Uint8Array(__classPrivateFieldGet(this, _TextDecoder_buffer, "f").length + bytes.length);
combined.set(__classPrivateFieldGet(this, _TextDecoder_buffer, "f"), 0);
combined.set(bytes, __classPrivateFieldGet(this, _TextDecoder_buffer, "f").length);
bytes = combined;
__classPrivateFieldSet(this, _TextDecoder_buffer, new Uint8Array(), "f");
}
let string = "", i = 0;
// Handle BOM
if (!this.ignoreBOM &&
bytes.length >= 3 &&
bytes[0] === 0xef &&
bytes[1] === 0xbb &&
bytes[2] === 0xbf)
i += 3;
while (i < bytes.length) {
const startIndex = i;
const byte1 = bytes[i++];
if (byte1 <= 0x7f) {
string += StringFromCharCode(byte1);
}
else if (byte1 >= 0xc0 && byte1 <= 0xdf) {
// 2-byte sequence
const byte2 = bytes[i++];
if (byte2 === undefined) {
// Incomplete sequence
if (stream) {
__classPrivateFieldSet(this, _TextDecoder_buffer, Uint8ArrayPrototypeSubarray(bytes, startIndex), "f");
break;
}
else {
if (this.fatal)
throw new TypeError("Incomplete byte sequence");
string += "\uFFFD";
break;
}
}
if ((byte2 & 0xc0) !== 0x80) {
if (this.fatal)
throw new TypeError("Invalid continuation byte");
string += "\uFFFD";
i = startIndex + 1;
continue;
}
const codePoint = ((byte1 & 0x1f) << 6) | (byte2 & 0x3f);
string += StringFromCharCode(codePoint);
}
else if (byte1 >= 0xe0 && byte1 <= 0xef) {
// 3-byte sequence
const byte2 = bytes[i++];
if (byte2 === undefined) {
// Incomplete sequence
if (stream) {
__classPrivateFieldSet(this, _TextDecoder_buffer, Uint8ArrayPrototypeSubarray(bytes, startIndex), "f");
break;
}
else {
if (this.fatal)
throw new TypeError("Incomplete byte sequence");
string += "\uFFFD";
break;
}
}
const byte3 = bytes[i++];
if (byte3 === undefined) {
// Incomplete sequence
if (stream) {
__classPrivateFieldSet(this, _TextDecoder_buffer, Uint8ArrayPrototypeSubarray(bytes, startIndex), "f");
break;
}
else {
if (this.fatal)
throw new TypeError("Incomplete byte sequence");
string += "\uFFFD";
break;
}
}
if ((byte2 & 0xc0) !== 0x80 || (byte3 & 0xc0) !== 0x80) {
if (this.fatal)
throw new TypeError("Invalid continuation bytes");
string += "\uFFFD";
i = startIndex + 1;
continue;
}
const codePoint = ((byte1 & 0x0f) << 12) |
((byte2 & 0x3f) << 6) |
(byte3 & 0x3f);
string += StringFromCharCode(codePoint);
}
else if (byte1 >= 0xf0 && byte1 <= 0xf7) {
// 4-byte sequence
const byte2 = bytes[i++];
if (byte2 === undefined) {
// Incomplete sequence
if (stream) {
__classPrivateFieldSet(this, _TextDecoder_buffer, Uint8ArrayPrototypeSubarray(bytes, startIndex), "f");
break;
}
else {
if (this.fatal)
throw new TypeError("Incomplete byte sequence");
string += "\uFFFD";
break;
}
}
const byte3 = bytes[i++];
if (byte3 === undefined) {
// Incomplete sequence
if (stream) {
__classPrivateFieldSet(this, _TextDecoder_buffer, Uint8ArrayPrototypeSubarray(bytes, startIndex), "f");
break;
}
else {
if (this.fatal)
throw new TypeError("Incomplete byte sequence");
string += "\uFFFD";
break;
}
}
const byte4 = bytes[i++];
if (byte4 === undefined) {
// Incomplete sequence
if (stream) {
__classPrivateFieldSet(this, _TextDecoder_buffer, Uint8ArrayPrototypeSubarray(bytes, startIndex), "f");
break;
}
else {
if (this.fatal)
throw new TypeError("Incomplete byte sequence");
string += "\uFFFD";
break;
}
}
if ((byte2 & 0xc0) !== 0x80 ||
(byte3 & 0xc0) !== 0x80 ||
(byte4 & 0xc0) !== 0x80) {
if (this.fatal)
throw new TypeError("Invalid continuation bytes");
string += "\uFFFD";
i = startIndex + 1;
continue;
}
let codePoint = ((byte1 & 0x07) << 18) |
((byte2 & 0x3f) << 12) |
((byte3 & 0x3f) << 6) |
(byte4 & 0x3f);
codePoint -= 0x10000;
string += StringFromCharCode(0xd800 + ((codePoint >> 10) & 0x3ff), 0xdc00 + (codePoint & 0x3ff));
}
else {
if (this.fatal)
throw new TypeError("Invalid byte");
string += "\uFFFD";
}
}
// If not streaming, reset the buffer
if (!stream)
__classPrivateFieldSet(this, _TextDecoder_buffer, new Uint8Array(), "f");
return string;
}
}
_TextDecoder_encoding = new WeakMap(), _TextDecoder_fatal = new WeakMap(), _TextDecoder_ignoreBOM = new WeakMap(), _TextDecoder_buffer = new WeakMap();
//# sourceMappingURL=text_decoder.js.map