import { core, primordials } from "ext:core/mod.js";
const {
isDataView,
isSharedArrayBuffer,
isTypedArray,
} = core;
import {
op_encoding_decode,
op_encoding_decode_single,
op_encoding_decode_utf8,
op_encoding_encode_into,
op_encoding_new_decoder,
op_encoding_normalize_label,
} from "ext:core/ops";
const {
DataViewPrototypeGetBuffer,
DataViewPrototypeGetByteLength,
DataViewPrototypeGetByteOffset,
ObjectPrototypeIsPrototypeOf,
PromiseReject,
PromiseResolve,
StringPrototypeCharCodeAt,
StringPrototypeSlice,
SymbolFor,
TypedArrayPrototypeGetBuffer,
TypedArrayPrototypeGetByteLength,
TypedArrayPrototypeGetByteOffset,
TypedArrayPrototypeSubarray,
Uint32Array,
Uint8Array,
} = primordials;
import * as webidl from "ext:deno_webidl/00_webidl.js";
import { createFilteredInspectProxy } from "ext:deno_console/01_console.js";
class TextDecoder {
#encoding;
#fatal;
#ignoreBOM;
#utf8SinglePass;
#handle = null;
constructor(label = "utf-8", options = { __proto__: null }) {
const prefix = "Failed to construct 'TextDecoder'";
label = webidl.converters.DOMString(label, prefix, "Argument 1");
options = webidl.converters.TextDecoderOptions(
options,
prefix,
"Argument 2",
);
const encoding = op_encoding_normalize_label(label);
this.#encoding = encoding;
this.#fatal = options.fatal;
this.#ignoreBOM = options.ignoreBOM;
this.#utf8SinglePass = encoding === "utf-8" && !options.fatal;
this[webidl.brand] = webidl.brand;
}
get encoding() {
webidl.assertBranded(this, TextDecoderPrototype);
return this.#encoding;
}
get fatal() {
webidl.assertBranded(this, TextDecoderPrototype);
return this.#fatal;
}
get ignoreBOM() {
webidl.assertBranded(this, TextDecoderPrototype);
return this.#ignoreBOM;
}
decode(input = new Uint8Array(), options = undefined) {
webidl.assertBranded(this, TextDecoderPrototype);
const prefix = "Failed to execute 'decode' on 'TextDecoder'";
if (input !== undefined) {
input = webidl.converters.BufferSource(input, prefix, "Argument 1", {
allowShared: true,
});
}
let stream = false;
if (options !== undefined) {
options = webidl.converters.TextDecodeOptions(
options,
prefix,
"Argument 2",
);
stream = options.stream;
}
try {
let buffer = input;
if (isTypedArray(input)) {
buffer = TypedArrayPrototypeGetBuffer(
(input),
);
} else if (isDataView(input)) {
buffer = DataViewPrototypeGetBuffer( (input));
}
if (isSharedArrayBuffer(buffer)) {
if (isTypedArray(input)) {
input = new Uint8Array(
buffer,
TypedArrayPrototypeGetByteOffset(
(input),
),
TypedArrayPrototypeGetByteLength(
(input),
),
);
} else if (isDataView(input)) {
input = new Uint8Array(
buffer,
DataViewPrototypeGetByteOffset( (input)),
DataViewPrototypeGetByteLength( (input)),
);
} else {
input = new Uint8Array(buffer);
}
}
if (!stream && this.#handle === null) {
if (this.#utf8SinglePass) {
return op_encoding_decode_utf8(input, this.#ignoreBOM);
}
return op_encoding_decode_single(
input,
this.#encoding,
this.#fatal,
this.#ignoreBOM,
);
}
if (this.#handle === null) {
this.#handle = op_encoding_new_decoder(
this.#encoding,
this.#fatal,
this.#ignoreBOM,
);
}
return op_encoding_decode(input, this.#handle, stream);
} finally {
if (!stream && this.#handle !== null) {
this.#handle = null;
}
}
}
[SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
return inspect(
createFilteredInspectProxy({
object: this,
evaluate: ObjectPrototypeIsPrototypeOf(TextDecoderPrototype, this),
keys: [
"encoding",
"fatal",
"ignoreBOM",
],
}),
inspectOptions,
);
}
}
webidl.configureInterface(TextDecoder);
const TextDecoderPrototype = TextDecoder.prototype;
class TextEncoder {
constructor() {
this[webidl.brand] = webidl.brand;
}
get encoding() {
webidl.assertBranded(this, TextEncoderPrototype);
return "utf-8";
}
encode(input = "") {
webidl.assertBranded(this, TextEncoderPrototype);
input = webidl.converters.DOMString(
input,
"Failed to execute 'encode' on 'TextEncoder'",
"Argument 1",
);
return core.encode(input);
}
encodeInto(source, destination) {
webidl.assertBranded(this, TextEncoderPrototype);
const prefix = "Failed to execute 'encodeInto' on 'TextEncoder'";
source = webidl.converters.DOMString(source, prefix, "Argument 1");
destination = webidl.converters.Uint8Array(
destination,
prefix,
"Argument 2",
{
allowShared: true,
},
);
op_encoding_encode_into(source, destination, encodeIntoBuf);
return {
read: encodeIntoBuf[0],
written: encodeIntoBuf[1],
};
}
[SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
return inspect(
createFilteredInspectProxy({
object: this,
evaluate: ObjectPrototypeIsPrototypeOf(TextEncoderPrototype, this),
keys: ["encoding"],
}),
inspectOptions,
);
}
}
const encodeIntoBuf = new Uint32Array(2);
webidl.configureInterface(TextEncoder);
const TextEncoderPrototype = TextEncoder.prototype;
class TextDecoderStream {
#decoder;
#transform;
constructor(label = "utf-8", options = { __proto__: null }) {
const prefix = "Failed to construct 'TextDecoderStream'";
label = webidl.converters.DOMString(label, prefix, "Argument 1");
options = webidl.converters.TextDecoderOptions(
options,
prefix,
"Argument 2",
);
this.#decoder = new TextDecoder(label, options);
this.#transform = new TransformStream({
transform: (chunk, controller) => {
try {
chunk = webidl.converters.BufferSource(chunk, prefix, "chunk", {
allowShared: true,
});
const decoded = this.#decoder.decode(chunk, { stream: true });
if (decoded) {
controller.enqueue(decoded);
}
return PromiseResolve();
} catch (err) {
return PromiseReject(err);
}
},
flush: (controller) => {
try {
const final = this.#decoder.decode();
if (final) {
controller.enqueue(final);
}
return PromiseResolve();
} catch (err) {
return PromiseReject(err);
}
},
cancel: (_reason) => {
try {
const _ = this.#decoder.decode();
return PromiseResolve();
} catch (err) {
return PromiseReject(err);
}
},
});
this[webidl.brand] = webidl.brand;
}
get encoding() {
webidl.assertBranded(this, TextDecoderStreamPrototype);
return this.#decoder.encoding;
}
get fatal() {
webidl.assertBranded(this, TextDecoderStreamPrototype);
return this.#decoder.fatal;
}
get ignoreBOM() {
webidl.assertBranded(this, TextDecoderStreamPrototype);
return this.#decoder.ignoreBOM;
}
get readable() {
webidl.assertBranded(this, TextDecoderStreamPrototype);
return this.#transform.readable;
}
get writable() {
webidl.assertBranded(this, TextDecoderStreamPrototype);
return this.#transform.writable;
}
[SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
return inspect(
createFilteredInspectProxy({
object: this,
evaluate: ObjectPrototypeIsPrototypeOf(
TextDecoderStreamPrototype,
this,
),
keys: [
"encoding",
"fatal",
"ignoreBOM",
"readable",
"writable",
],
}),
inspectOptions,
);
}
}
webidl.configureInterface(TextDecoderStream);
const TextDecoderStreamPrototype = TextDecoderStream.prototype;
class TextEncoderStream {
#pendingHighSurrogate = null;
#transform;
constructor() {
this.#transform = new TransformStream({
transform: (chunk, controller) => {
try {
chunk = webidl.converters.DOMString(chunk);
if (chunk === "") {
return PromiseResolve();
}
if (this.#pendingHighSurrogate !== null) {
chunk = this.#pendingHighSurrogate + chunk;
}
const lastCodeUnit = StringPrototypeCharCodeAt(
chunk,
chunk.length - 1,
);
if (0xD800 <= lastCodeUnit && lastCodeUnit <= 0xDBFF) {
this.#pendingHighSurrogate = StringPrototypeSlice(chunk, -1);
chunk = StringPrototypeSlice(chunk, 0, -1);
} else {
this.#pendingHighSurrogate = null;
}
if (chunk) {
controller.enqueue(core.encode(chunk));
}
return PromiseResolve();
} catch (err) {
return PromiseReject(err);
}
},
flush: (controller) => {
try {
if (this.#pendingHighSurrogate !== null) {
controller.enqueue(new Uint8Array([0xEF, 0xBF, 0xBD]));
}
return PromiseResolve();
} catch (err) {
return PromiseReject(err);
}
},
});
this[webidl.brand] = webidl.brand;
}
get encoding() {
webidl.assertBranded(this, TextEncoderStreamPrototype);
return "utf-8";
}
get readable() {
webidl.assertBranded(this, TextEncoderStreamPrototype);
return this.#transform.readable;
}
get writable() {
webidl.assertBranded(this, TextEncoderStreamPrototype);
return this.#transform.writable;
}
[SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
return inspect(
createFilteredInspectProxy({
object: this,
evaluate: ObjectPrototypeIsPrototypeOf(
TextEncoderStreamPrototype,
this,
),
keys: [
"encoding",
"readable",
"writable",
],
}),
inspectOptions,
);
}
}
webidl.configureInterface(TextEncoderStream);
const TextEncoderStreamPrototype = TextEncoderStream.prototype;
webidl.converters.TextDecoderOptions = webidl.createDictionaryConverter(
"TextDecoderOptions",
[
{
key: "fatal",
converter: webidl.converters.boolean,
defaultValue: false,
},
{
key: "ignoreBOM",
converter: webidl.converters.boolean,
defaultValue: false,
},
],
);
webidl.converters.TextDecodeOptions = webidl.createDictionaryConverter(
"TextDecodeOptions",
[
{
key: "stream",
converter: webidl.converters.boolean,
defaultValue: false,
},
],
);
function decode(bytes, encoding) {
const BOMEncoding = BOMSniff(bytes);
if (BOMEncoding !== null) {
encoding = BOMEncoding;
const start = BOMEncoding === "UTF-8" ? 3 : 2;
bytes = TypedArrayPrototypeSubarray(bytes, start);
}
return new TextDecoder(encoding).decode(bytes);
}
function BOMSniff(bytes) {
if (bytes[0] === 0xEF && bytes[1] === 0xBB && bytes[2] === 0xBF) {
return "UTF-8";
}
if (bytes[0] === 0xFE && bytes[1] === 0xFF) return "UTF-16BE";
if (bytes[0] === 0xFF && bytes[1] === 0xFE) return "UTF-16LE";
return null;
}
export {
decode,
TextDecoder,
TextDecoderStream,
TextEncoder,
TextEncoderStream,
};