mirror of
https://github.com/zebrajr/node.git
synced 2026-01-15 12:15:26 +00:00
stream: use private properties for encoding
PR-URL: https://github.com/nodejs/node/pull/47218 Reviewed-By: Erick Wendel <erick.workspace@gmail.com> Reviewed-By: Minwoo Jung <nodecorelab@gmail.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Robert Nagy <ronagy@icloud.com>
This commit is contained in:
committed by
Node.js GitHub Bot
parent
4e93247079
commit
1fa084ecdf
@@ -4,7 +4,6 @@ const {
|
||||
ObjectDefineProperties,
|
||||
String,
|
||||
StringPrototypeCharCodeAt,
|
||||
Symbol,
|
||||
Uint8Array,
|
||||
} = primordials;
|
||||
|
||||
@@ -31,32 +30,19 @@ const {
|
||||
kEnumerableProperty,
|
||||
} = require('internal/util');
|
||||
|
||||
const kHandle = Symbol('kHandle');
|
||||
const kTransform = Symbol('kTransform');
|
||||
const kType = Symbol('kType');
|
||||
const kPendingHighSurrogate = Symbol('kPendingHighSurrogate');
|
||||
|
||||
/**
|
||||
* @typedef {import('./readablestream').ReadableStream} ReadableStream
|
||||
* @typedef {import('./writablestream').WritableStream} WritableStream
|
||||
*/
|
||||
|
||||
function isTextEncoderStream(value) {
|
||||
return typeof value?.[kHandle] === 'object' &&
|
||||
value?.[kType] === 'TextEncoderStream';
|
||||
}
|
||||
|
||||
function isTextDecoderStream(value) {
|
||||
return typeof value?.[kHandle] === 'object' &&
|
||||
value?.[kType] === 'TextDecoderStream';
|
||||
}
|
||||
|
||||
class TextEncoderStream {
|
||||
#pendingHighSurrogate = null;
|
||||
#handle;
|
||||
#transform;
|
||||
|
||||
constructor() {
|
||||
this[kPendingHighSurrogate] = null;
|
||||
this[kType] = 'TextEncoderStream';
|
||||
this[kHandle] = new TextEncoder();
|
||||
this[kTransform] = new TransformStream({
|
||||
this.#handle = new TextEncoder();
|
||||
this.#transform = new TransformStream({
|
||||
transform: (chunk, controller) => {
|
||||
// https://encoding.spec.whatwg.org/#encode-and-enqueue-a-chunk
|
||||
chunk = String(chunk);
|
||||
@@ -64,9 +50,9 @@ class TextEncoderStream {
|
||||
for (let i = 0; i < chunk.length; i++) {
|
||||
const item = chunk[i];
|
||||
const codeUnit = StringPrototypeCharCodeAt(item, 0);
|
||||
if (this[kPendingHighSurrogate] !== null) {
|
||||
const highSurrogate = this[kPendingHighSurrogate];
|
||||
this[kPendingHighSurrogate] = null;
|
||||
if (this.#pendingHighSurrogate !== null) {
|
||||
const highSurrogate = this.#pendingHighSurrogate;
|
||||
this.#pendingHighSurrogate = null;
|
||||
if (0xDC00 <= codeUnit && codeUnit <= 0xDFFF) {
|
||||
finalChunk += highSurrogate + item;
|
||||
continue;
|
||||
@@ -74,7 +60,7 @@ class TextEncoderStream {
|
||||
finalChunk += '\uFFFD';
|
||||
}
|
||||
if (0xD800 <= codeUnit && codeUnit <= 0xDBFF) {
|
||||
this[kPendingHighSurrogate] = item;
|
||||
this.#pendingHighSurrogate = item;
|
||||
continue;
|
||||
}
|
||||
if (0xDC00 <= codeUnit && codeUnit <= 0xDFFF) {
|
||||
@@ -84,13 +70,13 @@ class TextEncoderStream {
|
||||
finalChunk += item;
|
||||
}
|
||||
if (finalChunk) {
|
||||
const value = this[kHandle].encode(finalChunk);
|
||||
const value = this.#handle.encode(finalChunk);
|
||||
controller.enqueue(value);
|
||||
}
|
||||
},
|
||||
flush: (controller) => {
|
||||
// https://encoding.spec.whatwg.org/#encode-and-flush
|
||||
if (this[kPendingHighSurrogate] !== null) {
|
||||
if (this.#pendingHighSurrogate !== null) {
|
||||
controller.enqueue(new Uint8Array([0xEF, 0xBF, 0xBD]));
|
||||
}
|
||||
},
|
||||
@@ -102,9 +88,7 @@ class TextEncoderStream {
|
||||
* @type {string}
|
||||
*/
|
||||
get encoding() {
|
||||
if (!isTextEncoderStream(this))
|
||||
throw new ERR_INVALID_THIS('TextEncoderStream');
|
||||
return this[kHandle].encoding;
|
||||
return this.#handle.encoding;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -112,9 +96,7 @@ class TextEncoderStream {
|
||||
* @type {ReadableStream}
|
||||
*/
|
||||
get readable() {
|
||||
if (!isTextEncoderStream(this))
|
||||
throw new ERR_INVALID_THIS('TextEncoderStream');
|
||||
return this[kTransform].readable;
|
||||
return this.#transform.readable;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -122,23 +104,24 @@ class TextEncoderStream {
|
||||
* @type {WritableStream}
|
||||
*/
|
||||
get writable() {
|
||||
if (!isTextEncoderStream(this))
|
||||
throw new ERR_INVALID_THIS('TextEncoderStream');
|
||||
return this[kTransform].writable;
|
||||
return this.#transform.writable;
|
||||
}
|
||||
|
||||
[kInspect](depth, options) {
|
||||
if (!isTextEncoderStream(this))
|
||||
if (this == null)
|
||||
throw new ERR_INVALID_THIS('TextEncoderStream');
|
||||
return customInspect(depth, options, 'TextEncoderStream', {
|
||||
encoding: this[kHandle].encoding,
|
||||
readable: this[kTransform].readable,
|
||||
writable: this[kTransform].writable,
|
||||
encoding: this.#handle.encoding,
|
||||
readable: this.#transform.readable,
|
||||
writable: this.#transform.writable,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class TextDecoderStream {
|
||||
#handle;
|
||||
#transform;
|
||||
|
||||
/**
|
||||
* @param {string} [encoding]
|
||||
* @param {{
|
||||
@@ -147,16 +130,15 @@ class TextDecoderStream {
|
||||
* }} [options]
|
||||
*/
|
||||
constructor(encoding = 'utf-8', options = kEmptyObject) {
|
||||
this[kType] = 'TextDecoderStream';
|
||||
this[kHandle] = new TextDecoder(encoding, options);
|
||||
this[kTransform] = new TransformStream({
|
||||
this.#handle = new TextDecoder(encoding, options);
|
||||
this.#transform = new TransformStream({
|
||||
transform: (chunk, controller) => {
|
||||
const value = this[kHandle].decode(chunk, { stream: true });
|
||||
const value = this.#handle.decode(chunk, { stream: true });
|
||||
if (value)
|
||||
controller.enqueue(value);
|
||||
},
|
||||
flush: (controller) => {
|
||||
const value = this[kHandle].decode();
|
||||
const value = this.#handle.decode();
|
||||
if (value)
|
||||
controller.enqueue(value);
|
||||
controller.terminate();
|
||||
@@ -169,9 +151,7 @@ class TextDecoderStream {
|
||||
* @type {string}
|
||||
*/
|
||||
get encoding() {
|
||||
if (!isTextDecoderStream(this))
|
||||
throw new ERR_INVALID_THIS('TextDecoderStream');
|
||||
return this[kHandle].encoding;
|
||||
return this.#handle.encoding;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -179,9 +159,7 @@ class TextDecoderStream {
|
||||
* @type {boolean}
|
||||
*/
|
||||
get fatal() {
|
||||
if (!isTextDecoderStream(this))
|
||||
throw new ERR_INVALID_THIS('TextDecoderStream');
|
||||
return this[kHandle].fatal;
|
||||
return this.#handle.fatal;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -189,9 +167,7 @@ class TextDecoderStream {
|
||||
* @type {boolean}
|
||||
*/
|
||||
get ignoreBOM() {
|
||||
if (!isTextDecoderStream(this))
|
||||
throw new ERR_INVALID_THIS('TextDecoderStream');
|
||||
return this[kHandle].ignoreBOM;
|
||||
return this.#handle.ignoreBOM;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -199,9 +175,7 @@ class TextDecoderStream {
|
||||
* @type {ReadableStream}
|
||||
*/
|
||||
get readable() {
|
||||
if (!isTextDecoderStream(this))
|
||||
throw new ERR_INVALID_THIS('TextDecoderStream');
|
||||
return this[kTransform].readable;
|
||||
return this.#transform.readable;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -209,20 +183,18 @@ class TextDecoderStream {
|
||||
* @type {WritableStream}
|
||||
*/
|
||||
get writable() {
|
||||
if (!isTextDecoderStream(this))
|
||||
throw new ERR_INVALID_THIS('TextDecoderStream');
|
||||
return this[kTransform].writable;
|
||||
return this.#transform.writable;
|
||||
}
|
||||
|
||||
[kInspect](depth, options) {
|
||||
if (!isTextDecoderStream(this))
|
||||
if (this == null)
|
||||
throw new ERR_INVALID_THIS('TextDecoderStream');
|
||||
return customInspect(depth, options, 'TextDecoderStream', {
|
||||
encoding: this[kHandle].encoding,
|
||||
fatal: this[kHandle].fatal,
|
||||
ignoreBOM: this[kHandle].ignoreBOM,
|
||||
readable: this[kTransform].readable,
|
||||
writable: this[kTransform].writable,
|
||||
encoding: this.#handle.encoding,
|
||||
fatal: this.#handle.fatal,
|
||||
ignoreBOM: this.#handle.ignoreBOM,
|
||||
readable: this.#transform.readable,
|
||||
writable: this.#transform.writable,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,23 +48,28 @@ const kEuro = Buffer.from([0xe2, 0x82, 0xac]).toString();
|
||||
|
||||
assert.throws(
|
||||
() => Reflect.get(TextDecoderStream.prototype, 'encoding', {}), {
|
||||
code: 'ERR_INVALID_THIS',
|
||||
name: 'TypeError',
|
||||
message: /Cannot read private member/,
|
||||
});
|
||||
assert.throws(
|
||||
() => Reflect.get(TextDecoderStream.prototype, 'fatal', {}), {
|
||||
code: 'ERR_INVALID_THIS',
|
||||
name: 'TypeError',
|
||||
message: /Cannot read private member/,
|
||||
});
|
||||
assert.throws(
|
||||
() => Reflect.get(TextDecoderStream.prototype, 'ignoreBOM', {}), {
|
||||
code: 'ERR_INVALID_THIS',
|
||||
name: 'TypeError',
|
||||
message: /Cannot read private member/,
|
||||
});
|
||||
assert.throws(
|
||||
() => Reflect.get(TextDecoderStream.prototype, 'readable', {}), {
|
||||
code: 'ERR_INVALID_THIS',
|
||||
name: 'TypeError',
|
||||
message: /Cannot read private member/,
|
||||
});
|
||||
assert.throws(
|
||||
() => Reflect.get(TextDecoderStream.prototype, 'writable', {}), {
|
||||
code: 'ERR_INVALID_THIS',
|
||||
name: 'TypeError',
|
||||
message: /Cannot read private member/,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -89,14 +94,17 @@ const kEuro = Buffer.from([0xe2, 0x82, 0xac]).toString();
|
||||
|
||||
assert.throws(
|
||||
() => Reflect.get(TextEncoderStream.prototype, 'encoding', {}), {
|
||||
code: 'ERR_INVALID_THIS',
|
||||
name: 'TypeError',
|
||||
message: /Cannot read private member/,
|
||||
});
|
||||
assert.throws(
|
||||
() => Reflect.get(TextEncoderStream.prototype, 'readable', {}), {
|
||||
code: 'ERR_INVALID_THIS',
|
||||
name: 'TypeError',
|
||||
message: /Cannot read private member/,
|
||||
});
|
||||
assert.throws(
|
||||
() => Reflect.get(TextEncoderStream.prototype, 'writable', {}), {
|
||||
code: 'ERR_INVALID_THIS',
|
||||
name: 'TypeError',
|
||||
message: /Cannot read private member/,
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user