Skip to content

Commit ca3438d

Browse files
committed
streams: use Array for Readable buffer
1 parent 25576b5 commit ca3438d

File tree

1 file changed

+107
-22
lines changed

1 file changed

+107
-22
lines changed

lib/internal/streams/readable.js

+107-22
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,8 @@ function ReadableState(options, stream, isDuplex) {
278278
// A linked list is used to store data chunks instead of an array because the
279279
// linked list can remove elements from the beginning faster than
280280
// array.shift().
281-
this.buffer = new BufferList();
281+
this.buffer = [];
282+
this.bufferIndex = 0;
282283
this.length = 0;
283284
this.pipes = [];
284285

@@ -546,10 +547,15 @@ function addChunk(stream, state, chunk, addToFront) {
546547
} else {
547548
// Update the buffer info.
548549
state.length += (state[kState] & kObjectMode) !== 0 ? 1 : chunk.length;
549-
if (addToFront)
550-
state.buffer.unshift(chunk);
551-
else
550+
if (addToFront) {
551+
if (state.bufferIndex > 0) {
552+
state.buffer[--state.bufferIndex] = chunk;
553+
} else {
554+
state.buffer.unshift(chunk); // Slow path
555+
}
556+
} else {
552557
state.buffer.push(chunk);
558+
}
553559

554560
if ((state[kState] & kNeedReadable) !== 0)
555561
emitReadable(stream);
@@ -564,21 +570,24 @@ Readable.prototype.isPaused = function() {
564570

565571
// Backwards compatibility.
566572
Readable.prototype.setEncoding = function(enc) {
573+
const state = this._readableState;
574+
567575
const decoder = new StringDecoder(enc);
568-
this._readableState.decoder = decoder;
576+
state.decoder = decoder;
569577
// If setEncoding(null), decoder.encoding equals utf8.
570-
this._readableState.encoding = this._readableState.decoder.encoding;
578+
state.encoding = state.decoder.encoding;
571579

572-
const buffer = this._readableState.buffer;
573580
// Iterate over current buffer to convert already stored Buffers:
574581
let content = '';
575-
for (const data of buffer) {
582+
for (const data of state.buffer.slice(state.bufferIndex)) {
576583
content += decoder.write(data);
577584
}
578-
buffer.clear();
585+
state.buffer.length = 0;
586+
state.bufferIndex = 0;
587+
579588
if (content !== '')
580589
buffer.push(content);
581-
this._readableState.length = content.length;
590+
state.length = content.length;
582591
return this;
583592
};
584593

@@ -611,7 +620,7 @@ function howMuchToRead(n, state) {
611620
if (NumberIsNaN(n)) {
612621
// Only flow one buffer at a time.
613622
if ((state[kState] & kFlowing) !== 0 && state.length)
614-
return state.buffer.first().length;
623+
return state.buffer[state.bufferIndex].length;
615624
return state.length;
616625
}
617626
if (n <= state.length)
@@ -1550,20 +1559,96 @@ function fromList(n, state) {
15501559
return null;
15511560

15521561
let ret;
1553-
if (state.objectMode)
1554-
ret = state.buffer.shift();
1555-
else if (!n || n >= state.length) {
1562+
if ((state[kState] & kObjectMode) !== 0) {
1563+
ret = state.buffer[state.bufferIndex++];
1564+
} else if (!n || n >= state.length) {
15561565
// Read it all, truncate the list.
1557-
if (state.decoder)
1558-
ret = state.buffer.join('');
1559-
else if (state.buffer.length === 1)
1560-
ret = state.buffer.first();
1561-
else
1562-
ret = state.buffer.concat(state.length);
1563-
state.buffer.clear();
1566+
if ((state[kState] & kDecoder) !== 0) {
1567+
ret = ''
1568+
for (let n = state.bufferIndex; n < state.buffer.length; n++) {
1569+
ret += state.buffer[n];
1570+
}
1571+
} else if (state.buffer.length - state.bufferIndex === 0) {
1572+
ret = Buffer.alloc(0)
1573+
} else if (state.buffer.length - state.bufferIndex === 1) {
1574+
ret = state.buffer[state.bufferIndex];
1575+
} else {
1576+
ret = Buffer.allocUnsafe(n >>> 0);
1577+
let i = 0;
1578+
for (let n = state.bufferIndex; n < state.buffer.length; n++) {
1579+
const data = state.buffer[n];
1580+
ret.set(data, i);
1581+
i += data.length;
1582+
}
1583+
}
1584+
state.buffer.length = 0;
1585+
state.bufferIndex = 0;
15641586
} else {
15651587
// read part of list.
1566-
ret = state.buffer.consume(n, state.decoder);
1588+
1589+
const data = state.buffer[state.bufferIndex];
1590+
1591+
if (n < data.length) {
1592+
// `slice` is the same for buffers and strings.
1593+
const slice = data.slice(0, n);
1594+
state.buffer[state.bufferIndex] = data.slice(n);
1595+
return slice;
1596+
}
1597+
1598+
if (n === data.length) {
1599+
// First chunk is a perfect match.
1600+
return state.buffer[state.bufferIndex++];
1601+
}
1602+
1603+
if ((state[kState] & kDecoder) !== 0) {
1604+
ret = '';
1605+
while (state.bufferIndex < state.buffer.length) {
1606+
const str = state.buffer[state.bufferIndex];
1607+
if (n > str.length) {
1608+
ret += str;
1609+
n -= str.length;
1610+
state.bufferIndex++;
1611+
} else {
1612+
if (n === buf.length) {
1613+
ret += str;
1614+
state.bufferIndex++;
1615+
} else {
1616+
ret += str.slice(0, n);
1617+
state.buffer[state.bufferIndex] = str.slice(n);
1618+
}
1619+
break;
1620+
}
1621+
}
1622+
} else {
1623+
ret = Buffer.allocUnsafe(n);
1624+
1625+
const retLen = n;
1626+
while (state.bufferIndex < state.buffer.length) {
1627+
const buf = state.buffer[state.bufferIndex];
1628+
if (n > buf.length) {
1629+
ret.set(buf, retLen - n);
1630+
n -= buf.length;
1631+
state.bufferIndex++;
1632+
} else {
1633+
if (n === buf.length) {
1634+
ret.set(buf, retLen - n);
1635+
state.bufferIndex++;
1636+
} else {
1637+
ret.set(new Uint8Array(buf.buffer, buf.byteOffset, n), retLen - n);
1638+
state.buffer[state.bufferIndex] = buf.slice(n);
1639+
}
1640+
break;
1641+
}
1642+
}
1643+
}
1644+
1645+
if (state.bufferIndex === state.buffer.length) {
1646+
state.buffer.length = 0;
1647+
state.bufferIndex = 0
1648+
} else if (state.bufferIndex > 256) {
1649+
state.buffer = state.buffer.slice(state.bufferIndex);
1650+
state.bufferIndex = 0;
1651+
}
15671652
}
15681653

15691654
return ret;

0 commit comments

Comments
 (0)