Skip to content

Commit e93f0f6

Browse files
committed
string_decoder: refactor to use private properties
1 parent 1323992 commit e93f0f6

File tree

2 files changed

+93
-101
lines changed

2 files changed

+93
-101
lines changed

lib/string_decoder.js

Lines changed: 91 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
const {
2525
ArrayBufferIsView,
2626
ObjectDefineProperties,
27-
Symbol,
2827
TypedArrayPrototypeSubarray,
2928
} = primordials;
3029

@@ -43,128 +42,125 @@ const {
4342
const internalUtil = require('internal/util');
4443
const {
4544
ERR_INVALID_ARG_TYPE,
46-
ERR_INVALID_THIS,
4745
ERR_UNKNOWN_ENCODING,
4846
} = require('internal/errors').codes;
4947
const isEncoding = Buffer[internalUtil.kIsEncodingSymbol];
5048

51-
const kNativeDecoder = Symbol('kNativeDecoder');
52-
53-
// Do not cache `Buffer.isEncoding` when checking encoding names as some
54-
// modules monkey-patch it to support additional encodings
55-
/**
56-
* Normalize encoding notation
57-
*
58-
* @param {string} enc
59-
* @returns {"utf8" | "utf16le" | "hex" | "ascii"
60-
* | "base64" | "latin1" | "base64url"}
61-
* @throws {TypeError} Throws an error when encoding is invalid
62-
*/
63-
function normalizeEncoding(enc) {
64-
const nenc = internalUtil.normalizeEncoding(enc);
65-
if (nenc === undefined) {
66-
if (Buffer.isEncoding === isEncoding || !Buffer.isEncoding(enc))
67-
throw new ERR_UNKNOWN_ENCODING(enc);
68-
return enc;
69-
}
70-
return nenc;
71-
}
72-
7349
const encodingsMap = {};
7450
for (let i = 0; i < encodings.length; ++i)
7551
encodingsMap[encodings[i]] = i;
7652

77-
/**
78-
* StringDecoder provides an interface for efficiently splitting a series of
79-
* buffers into a series of JS strings without breaking apart multi-byte
80-
* characters.
81-
*
82-
* @param {string} [encoding=utf-8]
83-
*/
84-
function StringDecoder(encoding) {
85-
this.encoding = normalizeEncoding(encoding);
86-
this[kNativeDecoder] = Buffer.alloc(kSize);
87-
this[kNativeDecoder][kEncodingField] = encodingsMap[this.encoding];
88-
}
53+
class StringDecoder {
54+
#nativeDecoder = Buffer.alloc(kSize);
55+
56+
/**
57+
* StringDecoder provides an interface for efficiently splitting a series of
58+
* buffers into a series of JS strings without breaking apart multi-byte
59+
* characters.
60+
*
61+
* @param {string} [encoding=utf-8]
62+
*/
63+
constructor(encoding) {
64+
this.encoding = this.#normalizeEncoding(encoding);
65+
this.#nativeDecoder[kEncodingField] = encodingsMap[this.encoding];
66+
}
67+
68+
// Do not cache `Buffer.isEncoding` when checking encoding names as some
69+
// modules monkey-patch it to support additional encodings
70+
/**
71+
* Normalize encoding notation
72+
*
73+
* @param {string} enc
74+
* @returns {"utf8" | "utf16le" | "hex" | "ascii"
75+
* | "base64" | "latin1" | "base64url"}
76+
* @throws {TypeError} Throws an error when encoding is invalid
77+
*/
78+
#normalizeEncoding(enc) {
79+
const nenc = internalUtil.normalizeEncoding(enc);
80+
if (nenc === undefined) {
81+
if (Buffer.isEncoding === isEncoding || !Buffer.isEncoding(enc))
82+
throw new ERR_UNKNOWN_ENCODING(enc);
83+
return enc;
84+
}
85+
return nenc;
86+
}
8987

90-
/**
91-
* Returns a decoded string, omitting any incomplete multi-bytes
92-
* characters at the end of the Buffer, or TypedArray, or DataView
93-
*
94-
* @param {string | Buffer | TypedArray | DataView} buf
95-
* @returns {string}
96-
* @throws {TypeError} Throws when buf is not in one of supported types
97-
*/
98-
StringDecoder.prototype.write = function write(buf) {
99-
if (typeof buf === 'string')
100-
return buf;
101-
if (!ArrayBufferIsView(buf))
102-
throw new ERR_INVALID_ARG_TYPE('buf',
103-
['Buffer', 'TypedArray', 'DataView'],
104-
buf);
105-
if (!this[kNativeDecoder]) {
106-
throw new ERR_INVALID_THIS('StringDecoder');
88+
/**
89+
* Returns a decoded string, omitting any incomplete multi-bytes
90+
* characters at the end of the Buffer, or TypedArray, or DataView
91+
*
92+
* @param {string | Buffer | TypedArray | DataView} buf
93+
* @returns {string}
94+
* @throws {TypeError} Throws when buf is not in one of supported types
95+
*/
96+
write(buf) {
97+
if (typeof buf === 'string')
98+
return buf;
99+
if (!ArrayBufferIsView(buf))
100+
throw new ERR_INVALID_ARG_TYPE('buf',
101+
['Buffer', 'TypedArray', 'DataView'],
102+
buf);
103+
return decode(this.#nativeDecoder, buf);
107104
}
108-
return decode(this[kNativeDecoder], buf);
109-
};
110105

111-
/**
112-
* Returns any remaining input stored in the internal buffer as a string.
113-
* After end() is called, the stringDecoder object can be reused for new
114-
* input.
115-
*
116-
* @param {string | Buffer | TypedArray | DataView} [buf]
117-
* @returns {string}
118-
*/
119-
StringDecoder.prototype.end = function end(buf) {
120-
let ret = '';
121-
if (buf !== undefined)
122-
ret = this.write(buf);
123-
if (this[kNativeDecoder][kBufferedBytes] > 0)
124-
ret += flush(this[kNativeDecoder]);
125-
return ret;
126-
};
106+
/**
107+
* Returns any remaining input stored in the internal buffer as a string.
108+
* After end() is called, the stringDecoder object can be reused for new
109+
* input.
110+
*
111+
* @param {string | Buffer | TypedArray | DataView} [buf]
112+
* @returns {string}
113+
*/
114+
end(buf) {
115+
let ret = '';
116+
if (buf !== undefined)
117+
ret = this.write(buf);
118+
if (this.#nativeDecoder[kBufferedBytes] > 0)
119+
ret += flush(this.#nativeDecoder);
120+
return ret;
121+
}
122+
123+
/* Everything below this line is undocumented legacy stuff. */
124+
/**
125+
*
126+
* @param {string | Buffer | TypedArray | DataView} buf
127+
* @param {number} offset
128+
* @returns {string}
129+
*/
130+
text(buf, offset) {
131+
this.#nativeDecoder[kMissingBytes] = 0;
132+
this.#nativeDecoder[kBufferedBytes] = 0;
133+
return this.write(buf.slice(offset));
134+
}
135+
136+
get lastChar() {
137+
return TypedArrayPrototypeSubarray(this.#nativeDecoder, kIncompleteCharactersStart, kIncompleteCharactersEnd);
138+
}
127139

128-
/* Everything below this line is undocumented legacy stuff. */
129-
/**
130-
*
131-
* @param {string | Buffer | TypedArray | DataView} buf
132-
* @param {number} offset
133-
* @returns {string}
134-
*/
135-
StringDecoder.prototype.text = function text(buf, offset) {
136-
this[kNativeDecoder][kMissingBytes] = 0;
137-
this[kNativeDecoder][kBufferedBytes] = 0;
138-
return this.write(buf.slice(offset));
139-
};
140+
get lastNeed() {
141+
return this.#nativeDecoder[kMissingBytes];
142+
}
143+
144+
get lastTotal() {
145+
return this.#nativeDecoder[kBufferedBytes] + this.#nativeDecoder[kMissingBytes];
146+
}
147+
}
140148

141149
ObjectDefineProperties(StringDecoder.prototype, {
142150
lastChar: {
143151
__proto__: null,
144152
configurable: true,
145153
enumerable: true,
146-
get() {
147-
return TypedArrayPrototypeSubarray(this[kNativeDecoder],
148-
kIncompleteCharactersStart,
149-
kIncompleteCharactersEnd);
150-
},
151154
},
152155
lastNeed: {
153156
__proto__: null,
154157
configurable: true,
155158
enumerable: true,
156-
get() {
157-
return this[kNativeDecoder][kMissingBytes];
158-
},
159159
},
160160
lastTotal: {
161161
__proto__: null,
162162
configurable: true,
163163
enumerable: true,
164-
get() {
165-
return this[kNativeDecoder][kBufferedBytes] +
166-
this[kNativeDecoder][kMissingBytes];
167-
},
168164
},
169165
});
170166

test/parallel/test-string-decoder.js

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,6 @@ const StringDecoder = require('string_decoder').StringDecoder;
2929
let decoder = new StringDecoder();
3030
assert.strictEqual(decoder.encoding, 'utf8');
3131

32-
// Should work without 'new' keyword
33-
const decoder2 = {};
34-
StringDecoder.call(decoder2);
35-
assert.strictEqual(decoder2.encoding, 'utf8');
36-
3732
// UTF-8
3833
test('utf-8', Buffer.from('$', 'utf-8'), '$');
3934
test('utf-8', Buffer.from('¢', 'utf-8'), '¢');
@@ -213,7 +208,8 @@ if (common.enoughTestMem) {
213208
assert.throws(
214209
() => new StringDecoder('utf8').__proto__.write(Buffer.from('abc')), // eslint-disable-line no-proto
215210
{
216-
code: 'ERR_INVALID_THIS',
211+
name: 'TypeError',
212+
message: /Cannot read private member/,
217213
}
218214
);
219215

0 commit comments

Comments
 (0)