Branch data Line data Source code
1 : 1445 : // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
2 : : // SPDX-FileCopyrightText: 2021 Evan Welsh <contact@evanwelsh.com>
3 : :
4 : : import {getEncodingFromLabel} from './encodingMap.js';
5 : :
6 : 47 : class TextDecoder {
7 : : /**
8 : : * @type {string}
9 : : */
10 : 349 : encoding;
11 : :
12 : : /**
13 : : * @type {boolean}
14 : : */
15 : 349 : ignoreBOM;
16 : :
17 : : /**
18 : : * @type {boolean}
19 : : */
20 : 349 : fatal;
21 : :
22 : : /**
23 : : * @private
24 : : * @type {string}
25 : : */
26 : 349 : _internalEncoding;
27 : :
28 : 47 : get [Symbol.toStringTag]() {
29 : 1 : return 'TextDecoder';
30 : : }
31 : :
32 : : /**
33 : : * @param {string} encoding The encoding to decode into
34 : : * @param {object} [options] Decoding options
35 : : * @param {boolean=} options.fatal Whether to throw or substitute when invalid characters are encountered
36 : : * @param {boolean=} options.ignoreBOM Whether to ignore the byte order for UTF-8 arrays
37 : : */
38 [ + + ][ + + ]: 349 : constructor(encoding = 'utf-8', options = {}) {
39 [ + + ][ + + ]: 349 : const {fatal = false, ignoreBOM = false} = options;
40 : :
41 : 349 : const encodingDefinition = getEncodingFromLabel(`${encoding}`);
42 : :
43 [ + + ]: 349 : if (!encodingDefinition)
44 : 1 : throw new RangeError(`Invalid encoding label: '${encoding}'`);
45 : :
46 [ + - ]: 348 : if (encodingDefinition.label === 'replacement') {
47 : 0 : throw new RangeError(
48 : 0 : `Unsupported replacement encoding: '${encoding}'`
49 : : );
50 : : }
51 : :
52 : 696 : Object.defineProperty(this, '_internalEncoding', {
53 : 348 : value: encodingDefinition.internalLabel,
54 : 348 : enumerable: false,
55 : 348 : writable: false,
56 : 348 : configurable: false,
57 : : });
58 : :
59 : 696 : Object.defineProperty(this, 'encoding', {
60 : 348 : value: encodingDefinition.label,
61 : 348 : enumerable: true,
62 : 348 : writable: false,
63 : 348 : configurable: false,
64 : : });
65 : :
66 : 696 : Object.defineProperty(this, 'ignoreBOM', {
67 : 348 : value: Boolean(ignoreBOM),
68 : 348 : enumerable: true,
69 : 348 : writable: false,
70 : 348 : configurable: false,
71 : : });
72 : :
73 : 696 : Object.defineProperty(this, 'fatal', {
74 : 348 : value: Boolean(fatal),
75 : 348 : enumerable: true,
76 : 348 : writable: false,
77 : 348 : configurable: false,
78 : : });
79 : : }
80 : :
81 : : /**
82 : : * @param {unknown} bytes a typed array of bytes to decode
83 : : * @param {object} [options] Decoding options
84 : : * @param {boolean=} options.stream Unsupported option. Whether to stream the decoded bytes.
85 : : * @returns
86 : : */
87 [ - + ]: 389 : decode(bytes, options = {}) {
88 [ - + ]: 342 : const {stream = false} = options;
89 : :
90 [ + - ]: 342 : if (stream) {
91 : 0 : throw new Error(
92 : 0 : 'TextDecoder does not implement the \'stream\' option.'
93 : : );
94 : : }
95 : :
96 : : /** @type {Uint8Array} */
97 : 342 : let input;
98 : :
99 [ + - ]: 342 : if (bytes instanceof ArrayBuffer) {
100 : 0 : input = new Uint8Array(bytes);
101 [ + + ]: 342 : } else if (bytes instanceof Uint8Array) {
102 : 338 : input = bytes;
103 [ + - ]: 4 : } else if (ArrayBuffer.isView(bytes)) {
104 : 0 : let {buffer, byteLength, byteOffset} = bytes;
105 : :
106 : 0 : input = new Uint8Array(buffer, byteOffset, byteLength);
107 [ + + ]: 4 : } else if (bytes === undefined) {
108 : 1 : input = new Uint8Array(0);
109 [ + + ]: 3 : } else if (bytes instanceof import.meta.importSync('gi').GLib.Bytes) {
110 : 1 : input = bytes.toArray();
111 : : } else {
112 : 4 : throw new Error(
113 : 2 : 'Provided input cannot be converted to ArrayBufferView or ArrayBuffer'
114 : : );
115 : : }
116 : :
117 : : if (
118 [ + + ]: 340 : this.ignoreBOM &&
119 [ - + ]: 2 : input.length > 2 &&
120 [ - + ]: 2 : input[0] === 0xef &&
121 [ - + ]: 2 : input[1] === 0xbb &&
122 [ + + ]: 2 : input[2] === 0xbf
123 : 1 : ) {
124 [ + - ]: 1 : if (this.encoding !== 'utf-8')
125 : 0 : throw new Error('Cannot ignore BOM for non-UTF8 encoding.');
126 : :
127 : 1 : let {buffer, byteLength, byteOffset} = input;
128 : 1 : input = new Uint8Array(buffer, byteOffset + 3, byteLength - 3);
129 : : }
130 : :
131 : 340 : const Encoding = import.meta.importSync('_encodingNative');
132 : 340 : return Encoding.decode(input, this._internalEncoding, this.fatal);
133 : 338 : }
134 : : }
135 : :
136 : 47 : class TextEncoder {
137 : 47 : get [Symbol.toStringTag]() {
138 : 1 : return 'TextEncoder';
139 : : }
140 : :
141 : 47 : get encoding() {
142 : 0 : return 'utf-8';
143 : : }
144 : :
145 [ + - ]: 69 : encode(input = '') {
146 : 22 : const Encoding = import.meta.importSync('_encodingNative');
147 : : // The TextEncoder specification only allows for UTF-8 encoding.
148 : 22 : return Encoding.encode(`${input}`, 'utf-8');
149 : 22 : }
150 : :
151 [ + - ][ + - ]: 50 : encodeInto(input = '', output = new Uint8Array()) {
152 : 3 : const Encoding = import.meta.importSync('_encodingNative');
153 : : // The TextEncoder specification only allows for UTF-8 encoding.
154 : 3 : return Encoding.encodeInto(`${input}`, output);
155 : 3 : }
156 : : }
157 : :
158 : 94 : Object.defineProperty(globalThis, 'TextEncoder', {
159 : 47 : configurable: false,
160 : 47 : enumerable: true,
161 : 47 : writable: false,
162 : 47 : value: TextEncoder,
163 : : });
164 : :
165 : 94 : Object.defineProperty(globalThis, 'TextDecoder', {
166 : 47 : configurable: false,
167 : 47 : enumerable: true,
168 : 47 : writable: false,
169 : 47 : value: TextDecoder,
170 : : });
|