Skip to content

Commit 2f904bc

Browse files
cola119ruyadorno
authored andcommitted
stream: update TextEncoderStream to align the latest spec
PR-URL: #44101 Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
1 parent 123b2d6 commit 2f904bc

File tree

2 files changed

+66
-8
lines changed

2 files changed

+66
-8
lines changed

lib/internal/webstreams/encoding.js

+37-6
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22

33
const {
44
ObjectDefineProperties,
5+
String,
6+
StringPrototypeCharCodeAt,
57
Symbol,
8+
Uint8Array,
69
} = primordials;
710

811
const {
@@ -31,6 +34,7 @@ const {
3134
const kHandle = Symbol('kHandle');
3235
const kTransform = Symbol('kTransform');
3336
const kType = Symbol('kType');
37+
const kPendingHighSurrogate = Symbol('kPendingHighSurrogate');
3438

3539
/**
3640
* @typedef {import('./readablestream').ReadableStream} ReadableStream
@@ -49,19 +53,46 @@ function isTextDecoderStream(value) {
4953

5054
class TextEncoderStream {
5155
constructor() {
56+
this[kPendingHighSurrogate] = null;
5257
this[kType] = 'TextEncoderStream';
5358
this[kHandle] = new TextEncoder();
5459
this[kTransform] = new TransformStream({
5560
transform: (chunk, controller) => {
56-
const value = this[kHandle].encode(chunk);
57-
if (value)
61+
// https://encoding.spec.whatwg.org/#encode-and-enqueue-a-chunk
62+
chunk = String(chunk);
63+
let finalChunk = '';
64+
for (let i = 0; i < chunk.length; i++) {
65+
const item = chunk[i];
66+
const codeUnit = StringPrototypeCharCodeAt(item, 0);
67+
if (this[kPendingHighSurrogate] !== null) {
68+
const highSurrogate = this[kPendingHighSurrogate];
69+
this[kPendingHighSurrogate] = null;
70+
if (0xDC00 <= codeUnit && codeUnit <= 0xDFFF) {
71+
finalChunk += highSurrogate + item;
72+
continue;
73+
}
74+
finalChunk += '\uFFFD';
75+
}
76+
if (0xD800 <= codeUnit && codeUnit <= 0xDBFF) {
77+
this[kPendingHighSurrogate] = item;
78+
continue;
79+
}
80+
if (0xDC00 <= codeUnit && codeUnit <= 0xDFFF) {
81+
finalChunk += '\uFFFD';
82+
continue;
83+
}
84+
finalChunk += item;
85+
}
86+
if (finalChunk) {
87+
const value = this[kHandle].encode(finalChunk);
5888
controller.enqueue(value);
89+
}
5990
},
6091
flush: (controller) => {
61-
const value = this[kHandle].encode();
62-
if (value.byteLength > 0)
63-
controller.enqueue(value);
64-
controller.terminate();
92+
// https://encoding.spec.whatwg.org/#encode-and-flush
93+
if (this[kPendingHighSurrogate] !== null) {
94+
controller.enqueue(new Uint8Array([0xEF, 0xBF, 0xBD]));
95+
}
6596
},
6697
});
6798
}

test/wpt/status/encoding.json

+29-2
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,35 @@
4848
"unsupported-encodings.any.js": {
4949
"skip": "decoding-helpers.js needs XMLHttpRequest"
5050
},
51-
"streams/*.js": {
52-
"fail": "No implementation of TextDecoderStream and TextEncoderStream"
51+
"streams/decode-ignore-bom.any.js": {
52+
"requires": ["small-icu"]
53+
},
54+
"streams/realms.window.js": {
55+
"skip": "window is not defined"
56+
},
57+
"streams/decode-attributes.any.js": {
58+
"requires": ["full-icu"]
59+
},
60+
"streams/decode-incomplete-input.any.js": {
61+
"requires": ["small-icu"]
62+
},
63+
"streams/decode-utf8.any.js": {
64+
"requires": ["small-icu"],
65+
"fail": {
66+
"unexpected": [
67+
"promise_test: Unhandled rejection with value: object 'TypeError: Cannot perform Construct on a detached ArrayBuffer'"
68+
]
69+
}
70+
},
71+
"streams/decode-bad-chunks.any.js": {
72+
"fail": {
73+
"unexpected": [
74+
"assert_unreached: Should have rejected: write should reject Reached unreachable code"
75+
]
76+
}
77+
},
78+
"streams/decode-non-utf8.any.js": {
79+
"requires": ["full-icu"]
5380
},
5481
"encodeInto.any.js": {
5582
"requires": ["small-icu"]

0 commit comments

Comments
 (0)