Skip to content

Commit 3b7dec4

Browse files
committed
perf(napi/parser, linter/plugins): use utf8Slice for decoding UTF-8 strings (#21022)
Optimize raw transfer string decoding. Replace `TextDecoder("utf8")` with `Buffer.prototype.utf8Slice` to decode Rust UTF-8 strings to JS strings. Benchmarks show this is an average 5% speed-up in `deserializeStr`, though the reason why is unclear. It skips various checks, and avoids creating a temporary `Uint8Array` (which is required with `TextDecoder.decode`) which trims 60 bytes of the size of the assembly created for `deserializeStr`. But that's not enough to explain the speed-up 🤷. Anyway, take the win.
1 parent 012c924 commit 3b7dec4

11 files changed

Lines changed: 81 additions & 103 deletions

File tree

apps/oxlint/src-js/generated/deserialize.js

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,8 @@ let uint8,
1414
parent = null,
1515
getLoc;
1616

17-
const textDecoder = new TextDecoder("utf-8", { ignoreBOM: true }),
18-
decodeStr = textDecoder.decode.bind(textDecoder),
19-
{ fromCharCode } = String,
20-
{ latin1Slice } = Buffer.prototype,
17+
const { fromCharCode } = String,
18+
{ utf8Slice, latin1Slice } = Buffer.prototype,
2119
stringDecodeArrays = Array(65).fill(null);
2220
for (let i = 0; i <= 64; i++) stringDecodeArrays[i] = Array(i).fill(0);
2321

@@ -5892,22 +5890,22 @@ function deserializeStr(pos) {
58925890
isInSourceRegion = pos >= sourceStartPos;
58935891
if (isInSourceRegion && end <= firstNonAsciiPos)
58945892
return sourceTextLatin.substr(pos - sourceStartPos, len);
5895-
// Use `TextDecoder` for strings longer than 64 bytes
5896-
if (len > 64) return decodeStr(uint8.subarray(pos, end));
5893+
// Use `utf8Slice` for strings longer than 64 bytes
5894+
if (len > 64) return utf8Slice.call(uint8, pos, end);
58975895
// If string is in source region, use slice of `sourceTextLatin` if all ASCII
58985896
if (isInSourceRegion) {
5899-
// Check if all bytes are ASCII, use `TextDecoder` if not
5900-
for (let i = pos; i < end; i++) if (uint8[i] >= 128) return decodeStr(uint8.subarray(pos, end));
5897+
// Check if all bytes are ASCII, use `utf8Slice` if not
5898+
for (let i = pos; i < end; i++) if (uint8[i] >= 128) return utf8Slice.call(uint8, pos, end);
59015899
// String is all ASCII, so slice from `sourceTextLatin`
59025900
return sourceTextLatin.substr(pos - sourceStartPos, len);
59035901
}
59045902
// String is not in source region - use `fromCharCode.apply` with a temp array of correct length.
59055903
// Copy bytes into temp array.
5906-
// If any byte is non-ASCII, use `TextDecoder`.
5904+
// If any byte is non-ASCII, use `utf8Slice`.
59075905
let arr = stringDecodeArrays[len];
59085906
for (let i = 0; i < len; i++) {
59095907
let b = uint8[pos + i];
5910-
if (b >= 128) return decodeStr(uint8.subarray(pos, end));
5908+
if (b >= 128) return utf8Slice.call(uint8, pos, end);
59115909
arr[i] = b;
59125910
}
59135911
// Call `fromCharCode` with temp array

apps/oxlint/src-js/plugins/source_code.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,7 @@ import type { ScopeManager } from "./scope.ts";
3030
import type { Token } from "./tokens.ts";
3131
import type { BufferWithArrays, Node } from "./types.ts";
3232

33-
// Text decoder, for decoding source text from buffer
34-
const textDecoder = new TextDecoder("utf-8", { ignoreBOM: true });
33+
const { utf8Slice } = Buffer.prototype;
3534

3635
// Buffer containing AST. Set before linting a file by `setupSourceForFile`.
3736
export let buffer: BufferWithArrays | null = null;
@@ -65,7 +64,7 @@ export function initSourceText(): void {
6564
programPos = uint32[DATA_POINTER_POS_32];
6665
sourceStartPos = uint32[(programPos + SOURCE_START_OFFSET) >> 2];
6766
sourceByteLen = uint32[(programPos + SOURCE_LEN_OFFSET) >> 2];
68-
sourceText = textDecoder.decode(buffer.subarray(sourceStartPos, sourceStartPos + sourceByteLen));
67+
sourceText = utf8Slice.call(buffer, sourceStartPos, sourceStartPos + sourceByteLen);
6968
}
7069

7170
/**

napi/parser/src-js/generated/deserialize/js.js

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,8 @@ let uint8,
99
sourceEndPos = 0,
1010
firstNonAsciiPos = 0;
1111

12-
const textDecoder = new TextDecoder("utf-8", { ignoreBOM: true }),
13-
decodeStr = textDecoder.decode.bind(textDecoder),
14-
{ fromCharCode } = String,
15-
{ latin1Slice } = Buffer.prototype,
12+
const { fromCharCode } = String,
13+
{ utf8Slice, latin1Slice } = Buffer.prototype,
1614
stringDecodeArrays = Array(65).fill(null);
1715
for (let i = 0; i <= 64; i++) stringDecodeArrays[i] = Array(i).fill(0);
1816

@@ -4557,21 +4555,21 @@ function deserializeStr(pos) {
45574555
pos = uint32[pos32];
45584556
let end = pos + len;
45594557
if (end <= firstNonAsciiPos) return sourceTextLatin.substr(pos, len);
4560-
// Use `TextDecoder` for strings longer than 64 bytes
4561-
if (len > 64) return decodeStr(uint8.subarray(pos, end));
4558+
// Use `utf8Slice` for strings longer than 64 bytes
4559+
if (len > 64) return utf8Slice.call(uint8, pos, end);
45624560
if (pos < sourceEndPos) {
4563-
// Check if all bytes are ASCII, use `TextDecoder` if not
4564-
for (let i = pos; i < end; i++) if (uint8[i] >= 128) return decodeStr(uint8.subarray(pos, end));
4561+
// Check if all bytes are ASCII, use `utf8Slice` if not
4562+
for (let i = pos; i < end; i++) if (uint8[i] >= 128) return utf8Slice.call(uint8, pos, end);
45654563
// String is all ASCII, so slice from `sourceTextLatin`
45664564
return sourceTextLatin.substr(pos, len);
45674565
}
45684566
// String is not in source region - use `fromCharCode.apply` with a temp array of correct length.
45694567
// Copy bytes into temp array.
4570-
// If any byte is non-ASCII, use `TextDecoder`.
4568+
// If any byte is non-ASCII, use `utf8Slice`.
45714569
let arr = stringDecodeArrays[len];
45724570
for (let i = 0; i < len; i++) {
45734571
let b = uint8[pos + i];
4574-
if (b >= 128) return decodeStr(uint8.subarray(pos, end));
4572+
if (b >= 128) return utf8Slice.call(uint8, pos, end);
45754573
arr[i] = b;
45764574
}
45774575
// Call `fromCharCode` with temp array

napi/parser/src-js/generated/deserialize/js_parent.js

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,8 @@ let uint8,
1010
firstNonAsciiPos = 0,
1111
parent = null;
1212

13-
const textDecoder = new TextDecoder("utf-8", { ignoreBOM: true }),
14-
decodeStr = textDecoder.decode.bind(textDecoder),
15-
{ fromCharCode } = String,
16-
{ latin1Slice } = Buffer.prototype,
13+
const { fromCharCode } = String,
14+
{ utf8Slice, latin1Slice } = Buffer.prototype,
1715
stringDecodeArrays = Array(65).fill(null);
1816
for (let i = 0; i <= 64; i++) stringDecodeArrays[i] = Array(i).fill(0);
1917

@@ -5088,21 +5086,21 @@ function deserializeStr(pos) {
50885086
pos = uint32[pos32];
50895087
let end = pos + len;
50905088
if (end <= firstNonAsciiPos) return sourceTextLatin.substr(pos, len);
5091-
// Use `TextDecoder` for strings longer than 64 bytes
5092-
if (len > 64) return decodeStr(uint8.subarray(pos, end));
5089+
// Use `utf8Slice` for strings longer than 64 bytes
5090+
if (len > 64) return utf8Slice.call(uint8, pos, end);
50935091
if (pos < sourceEndPos) {
5094-
// Check if all bytes are ASCII, use `TextDecoder` if not
5095-
for (let i = pos; i < end; i++) if (uint8[i] >= 128) return decodeStr(uint8.subarray(pos, end));
5092+
// Check if all bytes are ASCII, use `utf8Slice` if not
5093+
for (let i = pos; i < end; i++) if (uint8[i] >= 128) return utf8Slice.call(uint8, pos, end);
50965094
// String is all ASCII, so slice from `sourceTextLatin`
50975095
return sourceTextLatin.substr(pos, len);
50985096
}
50995097
// String is not in source region - use `fromCharCode.apply` with a temp array of correct length.
51005098
// Copy bytes into temp array.
5101-
// If any byte is non-ASCII, use `TextDecoder`.
5099+
// If any byte is non-ASCII, use `utf8Slice`.
51025100
let arr = stringDecodeArrays[len];
51035101
for (let i = 0; i < len; i++) {
51045102
let b = uint8[pos + i];
5105-
if (b >= 128) return decodeStr(uint8.subarray(pos, end));
5103+
if (b >= 128) return utf8Slice.call(uint8, pos, end);
51065104
arr[i] = b;
51075105
}
51085106
// Call `fromCharCode` with temp array

napi/parser/src-js/generated/deserialize/js_range.js

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,8 @@ let uint8,
99
sourceEndPos = 0,
1010
firstNonAsciiPos = 0;
1111

12-
const textDecoder = new TextDecoder("utf-8", { ignoreBOM: true }),
13-
decodeStr = textDecoder.decode.bind(textDecoder),
14-
{ fromCharCode } = String,
15-
{ latin1Slice } = Buffer.prototype,
12+
const { fromCharCode } = String,
13+
{ utf8Slice, latin1Slice } = Buffer.prototype,
1614
stringDecodeArrays = Array(65).fill(null);
1715
for (let i = 0; i <= 64; i++) stringDecodeArrays[i] = Array(i).fill(0);
1816

@@ -5099,21 +5097,21 @@ function deserializeStr(pos) {
50995097
pos = uint32[pos32];
51005098
let end = pos + len;
51015099
if (end <= firstNonAsciiPos) return sourceTextLatin.substr(pos, len);
5102-
// Use `TextDecoder` for strings longer than 64 bytes
5103-
if (len > 64) return decodeStr(uint8.subarray(pos, end));
5100+
// Use `utf8Slice` for strings longer than 64 bytes
5101+
if (len > 64) return utf8Slice.call(uint8, pos, end);
51045102
if (pos < sourceEndPos) {
5105-
// Check if all bytes are ASCII, use `TextDecoder` if not
5106-
for (let i = pos; i < end; i++) if (uint8[i] >= 128) return decodeStr(uint8.subarray(pos, end));
5103+
// Check if all bytes are ASCII, use `utf8Slice` if not
5104+
for (let i = pos; i < end; i++) if (uint8[i] >= 128) return utf8Slice.call(uint8, pos, end);
51075105
// String is all ASCII, so slice from `sourceTextLatin`
51085106
return sourceTextLatin.substr(pos, len);
51095107
}
51105108
// String is not in source region - use `fromCharCode.apply` with a temp array of correct length.
51115109
// Copy bytes into temp array.
5112-
// If any byte is non-ASCII, use `TextDecoder`.
5110+
// If any byte is non-ASCII, use `utf8Slice`.
51135111
let arr = stringDecodeArrays[len];
51145112
for (let i = 0; i < len; i++) {
51155113
let b = uint8[pos + i];
5116-
if (b >= 128) return decodeStr(uint8.subarray(pos, end));
5114+
if (b >= 128) return utf8Slice.call(uint8, pos, end);
51175115
arr[i] = b;
51185116
}
51195117
// Call `fromCharCode` with temp array

napi/parser/src-js/generated/deserialize/js_range_parent.js

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,8 @@ let uint8,
1010
firstNonAsciiPos = 0,
1111
parent = null;
1212

13-
const textDecoder = new TextDecoder("utf-8", { ignoreBOM: true }),
14-
decodeStr = textDecoder.decode.bind(textDecoder),
15-
{ fromCharCode } = String,
16-
{ latin1Slice } = Buffer.prototype,
13+
const { fromCharCode } = String,
14+
{ utf8Slice, latin1Slice } = Buffer.prototype,
1715
stringDecodeArrays = Array(65).fill(null);
1816
for (let i = 0; i <= 64; i++) stringDecodeArrays[i] = Array(i).fill(0);
1917

@@ -5633,21 +5631,21 @@ function deserializeStr(pos) {
56335631
pos = uint32[pos32];
56345632
let end = pos + len;
56355633
if (end <= firstNonAsciiPos) return sourceTextLatin.substr(pos, len);
5636-
// Use `TextDecoder` for strings longer than 64 bytes
5637-
if (len > 64) return decodeStr(uint8.subarray(pos, end));
5634+
// Use `utf8Slice` for strings longer than 64 bytes
5635+
if (len > 64) return utf8Slice.call(uint8, pos, end);
56385636
if (pos < sourceEndPos) {
5639-
// Check if all bytes are ASCII, use `TextDecoder` if not
5640-
for (let i = pos; i < end; i++) if (uint8[i] >= 128) return decodeStr(uint8.subarray(pos, end));
5637+
// Check if all bytes are ASCII, use `utf8Slice` if not
5638+
for (let i = pos; i < end; i++) if (uint8[i] >= 128) return utf8Slice.call(uint8, pos, end);
56415639
// String is all ASCII, so slice from `sourceTextLatin`
56425640
return sourceTextLatin.substr(pos, len);
56435641
}
56445642
// String is not in source region - use `fromCharCode.apply` with a temp array of correct length.
56455643
// Copy bytes into temp array.
5646-
// If any byte is non-ASCII, use `TextDecoder`.
5644+
// If any byte is non-ASCII, use `utf8Slice`.
56475645
let arr = stringDecodeArrays[len];
56485646
for (let i = 0; i < len; i++) {
56495647
let b = uint8[pos + i];
5650-
if (b >= 128) return decodeStr(uint8.subarray(pos, end));
5648+
if (b >= 128) return utf8Slice.call(uint8, pos, end);
56515649
arr[i] = b;
56525650
}
56535651
// Call `fromCharCode` with temp array

napi/parser/src-js/generated/deserialize/ts.js

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,8 @@ let uint8,
99
sourceEndPos = 0,
1010
firstNonAsciiPos = 0;
1111

12-
const textDecoder = new TextDecoder("utf-8", { ignoreBOM: true }),
13-
decodeStr = textDecoder.decode.bind(textDecoder),
14-
{ fromCharCode } = String,
15-
{ latin1Slice } = Buffer.prototype,
12+
const { fromCharCode } = String,
13+
{ utf8Slice, latin1Slice } = Buffer.prototype,
1614
stringDecodeArrays = Array(65).fill(null);
1715
for (let i = 0; i <= 64; i++) stringDecodeArrays[i] = Array(i).fill(0);
1816

@@ -4866,21 +4864,21 @@ function deserializeStr(pos) {
48664864
pos = uint32[pos32];
48674865
let end = pos + len;
48684866
if (end <= firstNonAsciiPos) return sourceTextLatin.substr(pos, len);
4869-
// Use `TextDecoder` for strings longer than 64 bytes
4870-
if (len > 64) return decodeStr(uint8.subarray(pos, end));
4867+
// Use `utf8Slice` for strings longer than 64 bytes
4868+
if (len > 64) return utf8Slice.call(uint8, pos, end);
48714869
if (pos < sourceEndPos) {
4872-
// Check if all bytes are ASCII, use `TextDecoder` if not
4873-
for (let i = pos; i < end; i++) if (uint8[i] >= 128) return decodeStr(uint8.subarray(pos, end));
4870+
// Check if all bytes are ASCII, use `utf8Slice` if not
4871+
for (let i = pos; i < end; i++) if (uint8[i] >= 128) return utf8Slice.call(uint8, pos, end);
48744872
// String is all ASCII, so slice from `sourceTextLatin`
48754873
return sourceTextLatin.substr(pos, len);
48764874
}
48774875
// String is not in source region - use `fromCharCode.apply` with a temp array of correct length.
48784876
// Copy bytes into temp array.
4879-
// If any byte is non-ASCII, use `TextDecoder`.
4877+
// If any byte is non-ASCII, use `utf8Slice`.
48804878
let arr = stringDecodeArrays[len];
48814879
for (let i = 0; i < len; i++) {
48824880
let b = uint8[pos + i];
4883-
if (b >= 128) return decodeStr(uint8.subarray(pos, end));
4881+
if (b >= 128) return utf8Slice.call(uint8, pos, end);
48844882
arr[i] = b;
48854883
}
48864884
// Call `fromCharCode` with temp array

napi/parser/src-js/generated/deserialize/ts_parent.js

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,8 @@ let uint8,
1010
firstNonAsciiPos = 0,
1111
parent = null;
1212

13-
const textDecoder = new TextDecoder("utf-8", { ignoreBOM: true }),
14-
decodeStr = textDecoder.decode.bind(textDecoder),
15-
{ fromCharCode } = String,
16-
{ latin1Slice } = Buffer.prototype,
13+
const { fromCharCode } = String,
14+
{ utf8Slice, latin1Slice } = Buffer.prototype,
1715
stringDecodeArrays = Array(65).fill(null);
1816
for (let i = 0; i <= 64; i++) stringDecodeArrays[i] = Array(i).fill(0);
1917

@@ -5424,21 +5422,21 @@ function deserializeStr(pos) {
54245422
pos = uint32[pos32];
54255423
let end = pos + len;
54265424
if (end <= firstNonAsciiPos) return sourceTextLatin.substr(pos, len);
5427-
// Use `TextDecoder` for strings longer than 64 bytes
5428-
if (len > 64) return decodeStr(uint8.subarray(pos, end));
5425+
// Use `utf8Slice` for strings longer than 64 bytes
5426+
if (len > 64) return utf8Slice.call(uint8, pos, end);
54295427
if (pos < sourceEndPos) {
5430-
// Check if all bytes are ASCII, use `TextDecoder` if not
5431-
for (let i = pos; i < end; i++) if (uint8[i] >= 128) return decodeStr(uint8.subarray(pos, end));
5428+
// Check if all bytes are ASCII, use `utf8Slice` if not
5429+
for (let i = pos; i < end; i++) if (uint8[i] >= 128) return utf8Slice.call(uint8, pos, end);
54325430
// String is all ASCII, so slice from `sourceTextLatin`
54335431
return sourceTextLatin.substr(pos, len);
54345432
}
54355433
// String is not in source region - use `fromCharCode.apply` with a temp array of correct length.
54365434
// Copy bytes into temp array.
5437-
// If any byte is non-ASCII, use `TextDecoder`.
5435+
// If any byte is non-ASCII, use `utf8Slice`.
54385436
let arr = stringDecodeArrays[len];
54395437
for (let i = 0; i < len; i++) {
54405438
let b = uint8[pos + i];
5441-
if (b >= 128) return decodeStr(uint8.subarray(pos, end));
5439+
if (b >= 128) return utf8Slice.call(uint8, pos, end);
54425440
arr[i] = b;
54435441
}
54445442
// Call `fromCharCode` with temp array

napi/parser/src-js/generated/deserialize/ts_range.js

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,8 @@ let uint8,
99
sourceEndPos = 0,
1010
firstNonAsciiPos = 0;
1111

12-
const textDecoder = new TextDecoder("utf-8", { ignoreBOM: true }),
13-
decodeStr = textDecoder.decode.bind(textDecoder),
14-
{ fromCharCode } = String,
15-
{ latin1Slice } = Buffer.prototype,
12+
const { fromCharCode } = String,
13+
{ utf8Slice, latin1Slice } = Buffer.prototype,
1614
stringDecodeArrays = Array(65).fill(null);
1715
for (let i = 0; i <= 64; i++) stringDecodeArrays[i] = Array(i).fill(0);
1816

@@ -5439,21 +5437,21 @@ function deserializeStr(pos) {
54395437
pos = uint32[pos32];
54405438
let end = pos + len;
54415439
if (end <= firstNonAsciiPos) return sourceTextLatin.substr(pos, len);
5442-
// Use `TextDecoder` for strings longer than 64 bytes
5443-
if (len > 64) return decodeStr(uint8.subarray(pos, end));
5440+
// Use `utf8Slice` for strings longer than 64 bytes
5441+
if (len > 64) return utf8Slice.call(uint8, pos, end);
54445442
if (pos < sourceEndPos) {
5445-
// Check if all bytes are ASCII, use `TextDecoder` if not
5446-
for (let i = pos; i < end; i++) if (uint8[i] >= 128) return decodeStr(uint8.subarray(pos, end));
5443+
// Check if all bytes are ASCII, use `utf8Slice` if not
5444+
for (let i = pos; i < end; i++) if (uint8[i] >= 128) return utf8Slice.call(uint8, pos, end);
54475445
// String is all ASCII, so slice from `sourceTextLatin`
54485446
return sourceTextLatin.substr(pos, len);
54495447
}
54505448
// String is not in source region - use `fromCharCode.apply` with a temp array of correct length.
54515449
// Copy bytes into temp array.
5452-
// If any byte is non-ASCII, use `TextDecoder`.
5450+
// If any byte is non-ASCII, use `utf8Slice`.
54535451
let arr = stringDecodeArrays[len];
54545452
for (let i = 0; i < len; i++) {
54555453
let b = uint8[pos + i];
5456-
if (b >= 128) return decodeStr(uint8.subarray(pos, end));
5454+
if (b >= 128) return utf8Slice.call(uint8, pos, end);
54575455
arr[i] = b;
54585456
}
54595457
// Call `fromCharCode` with temp array

napi/parser/src-js/generated/deserialize/ts_range_parent.js

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,8 @@ let uint8,
1010
firstNonAsciiPos = 0,
1111
parent = null;
1212

13-
const textDecoder = new TextDecoder("utf-8", { ignoreBOM: true }),
14-
decodeStr = textDecoder.decode.bind(textDecoder),
15-
{ fromCharCode } = String,
16-
{ latin1Slice } = Buffer.prototype,
13+
const { fromCharCode } = String,
14+
{ utf8Slice, latin1Slice } = Buffer.prototype,
1715
stringDecodeArrays = Array(65).fill(null);
1816
for (let i = 0; i <= 64; i++) stringDecodeArrays[i] = Array(i).fill(0);
1917

@@ -5997,21 +5995,21 @@ function deserializeStr(pos) {
59975995
pos = uint32[pos32];
59985996
let end = pos + len;
59995997
if (end <= firstNonAsciiPos) return sourceTextLatin.substr(pos, len);
6000-
// Use `TextDecoder` for strings longer than 64 bytes
6001-
if (len > 64) return decodeStr(uint8.subarray(pos, end));
5998+
// Use `utf8Slice` for strings longer than 64 bytes
5999+
if (len > 64) return utf8Slice.call(uint8, pos, end);
60026000
if (pos < sourceEndPos) {
6003-
// Check if all bytes are ASCII, use `TextDecoder` if not
6004-
for (let i = pos; i < end; i++) if (uint8[i] >= 128) return decodeStr(uint8.subarray(pos, end));
6001+
// Check if all bytes are ASCII, use `utf8Slice` if not
6002+
for (let i = pos; i < end; i++) if (uint8[i] >= 128) return utf8Slice.call(uint8, pos, end);
60056003
// String is all ASCII, so slice from `sourceTextLatin`
60066004
return sourceTextLatin.substr(pos, len);
60076005
}
60086006
// String is not in source region - use `fromCharCode.apply` with a temp array of correct length.
60096007
// Copy bytes into temp array.
6010-
// If any byte is non-ASCII, use `TextDecoder`.
6008+
// If any byte is non-ASCII, use `utf8Slice`.
60116009
let arr = stringDecodeArrays[len];
60126010
for (let i = 0; i < len; i++) {
60136011
let b = uint8[pos + i];
6014-
if (b >= 128) return decodeStr(uint8.subarray(pos, end));
6012+
if (b >= 128) return utf8Slice.call(uint8, pos, end);
60156013
arr[i] = b;
60166014
}
60176015
// Call `fromCharCode` with temp array

0 commit comments

Comments
 (0)