Houjie
2025-07-24 1bc8f80935add7215fa98de1ab8b375b222a2046
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var defaultEncoding_1 = require("../encoding/defaultEncoding");
var encoding_factory_1 = require("../encoding/encoding-factory");
var encodings_1 = require("../encoding/encodings");
var finished_1 = require("../encoding/finished");
var terminology_1 = require("../encoding/terminology");
var utilities_1 = require("../encoding/utilities");
var Stream_1 = require("./Stream");
/**
 * @constructor
 * @param {string=} label The label of the encoding;
 *     defaults to 'utf-8'.
 * @param {Object=} options
 */
var TextDecoder = /** @class */ (function () {
    function TextDecoder(label, options) {
        label = label !== undefined ? String(label) : defaultEncoding_1.DEFAULT_ENCODING;
        var optionsMap = utilities_1.ToDictionary(options);
        // A TextDecoder object has an associated encoding, decoder,
        // stream, ignore BOM flag (initially unset), BOM seen flag
        // (initially unset), error mode (initially replacement), and do
        // not flush flag (initially unset).
        /** @private */
        this._encoding = null;
        /** @private @type {?Decoder} */
        this._decoder = null;
        /** @private @type {boolean} */
        this._ignoreBOM = false;
        /** @private @type {boolean} */
        this._BOMseen = false;
        /** @private @type {string} */
        this._error_mode = 'replacement';
        /** @private @type {boolean} */
        this._do_not_flush = false;
        // 1. Let encoding be the result of getting an encoding from
        // label.
        var encoding = encodings_1.getEncoding(label);
        // 2. If encoding is failure or replacement, throw a RangeError.
        if (encoding === null || encoding.name === 'replacement')
            throw RangeError('Unknown encoding: ' + label);
        if (!encoding_factory_1.decoders[encoding.name]) {
            throw Error('Decoder not present.' +
                ' Did you forget to include encoding-indexes.js first?');
        }
        // 3. Let dec be a new TextDecoder object.
        // const dec = this;
        // no need to do this as this is a proper class 
        // now and TSC will handle transpilation to older platforms
        // 4. Set dec's encoding to encoding.
        this._encoding = encoding;
        // 5. If options's fatal member is true, set dec's error mode to
        // fatal.
        if (Boolean(optionsMap['fatal']))
            this._error_mode = 'fatal';
        // 6. If options's ignoreBOM member is true, set dec's ignore BOM
        // flag.
        if (Boolean(optionsMap['ignoreBOM']))
            this._ignoreBOM = true;
        // For pre-ES5 runtimes:
        // if (!Object.defineProperty) {
        //   this.encoding = dec._encoding.name.toLowerCase();
        //   this.fatal = dec._error_mode === 'fatal';
        //   this.ignoreBOM = dec._ignoreBOM;
        // }
        // 7. Return dec.
        // return dec;
    }
    Object.defineProperty(TextDecoder.prototype, "encoding", {
        // if (Object.defineProperty) {
        // The encoding attribute's getter must return encoding's name.
        //   Object.defineProperty(TextDecoder.prototype, 'encoding', {
        //     /** @this {TextDecoder} */
        //     get: function () { return this._encoding.name.toLowerCase(); }
        //   });
        get: function () {
            return this._encoding.name.toLowerCase();
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(TextDecoder.prototype, "fatal", {
        // The fatal attribute's getter must return true if error mode
        // is fatal, and false otherwise.
        //   Object.defineProperty(TextDecoder.prototype, 'fatal', {
        //     /** @this {TextDecoder} */
        //     get: function () { return this._error_mode === 'fatal'; }
        //   });
        get: function () {
            return this._error_mode === 'fatal';
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(TextDecoder.prototype, "ignoreBOM", {
        // The ignoreBOM attribute's getter must return true if ignore
        // BOM flag is set, and false otherwise.
        //   Object.defineProperty(TextDecoder.prototype, 'ignoreBOM', {
        //     /** @this {TextDecoder} */
        //     get: function () { return this._ignoreBOM; }
        //   });
        get: function () {
            return this._ignoreBOM;
        },
        enumerable: true,
        configurable: true
    });
    // }
    /**
     * @param {BufferSource=} input The buffer of bytes to decode.
     * @param {Object=} options
     * @return {string} The decoded string.
     */
    TextDecoder.prototype.decode = function (input, options) {
        var bytes = getBytesFromInput(input);
        var optionsMap = utilities_1.ToDictionary(options);
        // 1. If the do not flush flag is unset, set decoder to a new
        // encoding's decoder, set stream to a new stream, and unset the
        // BOM seen flag.
        if (!this._do_not_flush) {
            this._decoder = encoding_factory_1.decoders[this._encoding.name]({
                fatal: this._error_mode === 'fatal'
            });
            this._BOMseen = false;
        }
        // 2. If options's stream is true, set the do not flush flag, and
        // unset the do not flush flag otherwise.
        this._do_not_flush = Boolean(optionsMap['stream']);
        // 3. If input is given, push a copy of input to stream.
        // TODO: Align with spec algorithm - maintain stream on instance.
        var input_stream = new Stream_1.Stream(bytes);
        // 4. Let output be a new stream.
        var output = [];
        /** @type {?(number|!Array.<number>)} */
        var result;
        // 5. While true:
        while (true) {
            // 1. Let token be the result of reading from stream.
            var token = input_stream.read();
            // 2. If token is end-of-stream and the do not flush flag is
            // set, return output, serialized.
            // TODO: Align with spec algorithm.
            if (token === terminology_1.end_of_stream)
                break;
            // 3. Otherwise, run these subsubsteps:
            // 1. Let result be the result of processing token for decoder,
            // stream, output, and error mode.
            result = this._decoder.handler(input_stream, token);
            // 2. If result is finished, return output, serialized.
            if (result === finished_1.finished)
                break;
            if (result !== null) {
                if (Array.isArray(result))
                    output.push.apply(output, /**@type {!Array.<number>}*/ (result));
                else
                    output.push(result);
            }
            // 3. Otherwise, if result is error, throw a TypeError.
            // (Thrown in handler)
            // 4. Otherwise, do nothing.
        }
        // TODO: Align with spec algorithm.
        if (!this._do_not_flush) {
            do {
                result = this._decoder.handler(input_stream, input_stream.read());
                if (result === finished_1.finished)
                    break;
                if (!result)
                    continue;
                if (Array.isArray(result))
                    output.push.apply(output, /**@type {!Array.<number>}*/ (result));
                else
                    output.push(result);
            } while (!input_stream.endOfStream());
            this._decoder = null;
        }
        return this.serializeStream(output);
    };
    // A TextDecoder object also has an associated serialize stream
    // algorithm...
    /**
     * @param {!Array.<number>} stream
     * @return {string}
     * @this {TextDecoder}
     */
    TextDecoder.prototype.serializeStream = function (stream) {
        // 1. Let token be the result of reading from stream.
        // (Done in-place on array, rather than as a stream)
        // 2. If encoding is UTF-8, UTF-16BE, or UTF-16LE, and ignore
        // BOM flag and BOM seen flag are unset, run these subsubsteps:
        if (utilities_1.includes(['UTF-8', 'UTF-16LE', 'UTF-16BE'], this._encoding.name) &&
            !this._ignoreBOM && !this._BOMseen) {
            if (stream.length > 0 && stream[0] === 0xFEFF) {
                // 1. If token is U+FEFF, set BOM seen flag.
                this._BOMseen = true;
                stream.shift();
            }
            else if (stream.length > 0) {
                // 2. Otherwise, if token is not end-of-stream, set BOM seen
                // flag and append token to stream.
                this._BOMseen = true;
            }
            else {
                // 3. Otherwise, if token is not end-of-stream, append token
                // to output.
                // (no-op)
            }
        }
        // 4. Otherwise, return output.
        return utilities_1.codePointsToString(stream);
    };
    return TextDecoder;
}());
exports.TextDecoder = TextDecoder;
function isBufferInstance(input) {
    try {
        return input instanceof ArrayBuffer;
    }
    catch (e) {
        console.error(e);
        return false;
    }
}
function getBytesFromInput(input) {
    if (typeof input !== 'object')
        return new Uint8Array(0);
    if (isBufferInstance(input)) {
        return new Uint8Array(input);
    }
    if ('buffer' in input && isBufferInstance(input.buffer)) {
        return new Uint8Array(input.buffer, input.byteOffset, input.byteLength);
    }
    return new Uint8Array(0);
}
//# sourceMappingURL=TextDecoder.js.map