Skip to content

Commit 5eccd64

Browse files
addaleaxtargos
authored andcommitted
stream: convert existing buffer when calling .setEncoding
Convert already-stored chunks when `.setEncoding()` is called so that subsequent `data` events will receive decoded strings, as they expect. Fixes: #27932 PR-URL: #27936 Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Luigi Pinca <[email protected]> Reviewed-By: Ruben Bridgewater <[email protected]> Reviewed-By: Rich Trott <[email protected]>
1 parent 6548b91 commit 5eccd64

File tree

2 files changed

+74
-1
lines changed

2 files changed

+74
-1
lines changed

lib/_stream_readable.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -321,9 +321,22 @@ Readable.prototype.isPaused = function() {
321321
Readable.prototype.setEncoding = function(enc) {
322322
if (!StringDecoder)
323323
StringDecoder = require('string_decoder').StringDecoder;
324-
this._readableState.decoder = new StringDecoder(enc);
324+
const decoder = new StringDecoder(enc);
325+
this._readableState.decoder = decoder;
325326
// If setEncoding(null), decoder.encoding equals utf8
326327
this._readableState.encoding = this._readableState.decoder.encoding;
328+
329+
// Iterate over current buffer to convert already stored Buffers:
330+
let p = this._readableState.buffer.head;
331+
let content = '';
332+
while (p !== null) {
333+
content += decoder.write(p.data);
334+
p = p.next;
335+
}
336+
this._readableState.buffer.clear();
337+
if (content !== '')
338+
this._readableState.buffer.push(content);
339+
this._readableState.length = content.length;
327340
return this;
328341
};
329342

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
'use strict';
2+
require('../common');
3+
const { Readable } = require('stream');
4+
const assert = require('assert');
5+
6+
{
7+
// Call .setEncoding() while there are bytes already in the buffer.
8+
const r = new Readable({ read() {} });
9+
10+
r.push(Buffer.from('a'));
11+
r.push(Buffer.from('b'));
12+
13+
r.setEncoding('utf8');
14+
const chunks = [];
15+
r.on('data', (chunk) => chunks.push(chunk));
16+
17+
process.nextTick(() => {
18+
assert.deepStrictEqual(chunks, ['ab']);
19+
});
20+
}
21+
22+
{
23+
// Call .setEncoding() while the buffer contains a complete,
24+
// but chunked character.
25+
const r = new Readable({ read() {} });
26+
27+
r.push(Buffer.from([0xf0]));
28+
r.push(Buffer.from([0x9f]));
29+
r.push(Buffer.from([0x8e]));
30+
r.push(Buffer.from([0x89]));
31+
32+
r.setEncoding('utf8');
33+
const chunks = [];
34+
r.on('data', (chunk) => chunks.push(chunk));
35+
36+
process.nextTick(() => {
37+
assert.deepStrictEqual(chunks, ['🎉']);
38+
});
39+
}
40+
41+
{
42+
// Call .setEncoding() while the buffer contains an incomplete character,
43+
// and finish the character later.
44+
const r = new Readable({ read() {} });
45+
46+
r.push(Buffer.from([0xf0]));
47+
r.push(Buffer.from([0x9f]));
48+
49+
r.setEncoding('utf8');
50+
51+
r.push(Buffer.from([0x8e]));
52+
r.push(Buffer.from([0x89]));
53+
54+
const chunks = [];
55+
r.on('data', (chunk) => chunks.push(chunk));
56+
57+
process.nextTick(() => {
58+
assert.deepStrictEqual(chunks, ['🎉']);
59+
});
60+
}

0 commit comments

Comments
 (0)