@@ -34,6 +34,7 @@ const {
34
34
SymbolAsyncDispose,
35
35
SymbolAsyncIterator,
36
36
Symbol,
37
+ TypedArrayPrototypeSet,
37
38
} = primordials ;
38
39
39
40
module . exports = Readable ;
@@ -73,6 +74,7 @@ const {
73
74
const { validateObject } = require ( 'internal/validators' ) ;
74
75
75
76
const kState = Symbol ( 'kState' ) ;
77
+ const FastBuffer = Buffer [ Symbol . species ] ;
76
78
77
79
const { StringDecoder } = require ( 'string_decoder' ) ;
78
80
const from = require ( 'internal/streams/from' ) ;
@@ -278,7 +280,8 @@ function ReadableState(options, stream, isDuplex) {
278
280
// A linked list is used to store data chunks instead of an array because the
279
281
// linked list can remove elements from the beginning faster than
280
282
// array.shift().
281
- this . buffer = new BufferList ( ) ;
283
+ this . buffer = [ ] ;
284
+ this . bufferIndex = 0 ;
282
285
this . length = 0 ;
283
286
this . pipes = [ ] ;
284
287
@@ -546,10 +549,19 @@ function addChunk(stream, state, chunk, addToFront) {
546
549
} else {
547
550
// Update the buffer info.
548
551
state . length += ( state [ kState ] & kObjectMode ) !== 0 ? 1 : chunk . length ;
549
- if ( addToFront )
552
+ if ( addToFront ) {
553
+ state . buffer . splice ( 0 , state . bufferIndex ) ;
554
+ state . bufferIndex = 0 ;
550
555
state . buffer . unshift ( chunk ) ;
551
- else
556
+
557
+ // if (state.bufferIndex > 0) {
558
+ // state.buffer[--state.bufferIndex] = chunk;
559
+ // } else {
560
+ // state.buffer.unshift(chunk); // Slow path
561
+ // }
562
+ } else {
552
563
state . buffer . push ( chunk ) ;
564
+ }
553
565
554
566
if ( ( state [ kState ] & kNeedReadable ) !== 0 )
555
567
emitReadable ( stream ) ;
@@ -564,21 +576,24 @@ Readable.prototype.isPaused = function() {
564
576
565
577
// Backwards compatibility.
566
578
Readable . prototype . setEncoding = function ( enc ) {
579
+ const state = this . _readableState ;
580
+
567
581
const decoder = new StringDecoder ( enc ) ;
568
- this . _readableState . decoder = decoder ;
582
+ state . decoder = decoder ;
569
583
// If setEncoding(null), decoder.encoding equals utf8.
570
- this . _readableState . encoding = this . _readableState . decoder . encoding ;
584
+ state . encoding = state . decoder . encoding ;
571
585
572
- const buffer = this . _readableState . buffer ;
573
586
// Iterate over current buffer to convert already stored Buffers:
574
587
let content = '' ;
575
- for ( const data of buffer ) {
588
+ for ( const data of state . buffer . slice ( state . bufferIndex ) ) {
576
589
content += decoder . write ( data ) ;
577
590
}
578
- buffer . clear ( ) ;
591
+ state . buffer . length = 0 ;
592
+ state . bufferIndex = 0 ;
593
+
579
594
if ( content !== '' )
580
- buffer . push ( content ) ;
581
- this . _readableState . length = content . length ;
595
+ state . buffer . push ( content ) ;
596
+ state . length = content . length ;
582
597
return this ;
583
598
} ;
584
599
@@ -611,7 +626,7 @@ function howMuchToRead(n, state) {
611
626
if ( NumberIsNaN ( n ) ) {
612
627
// Only flow one buffer at a time.
613
628
if ( ( state [ kState ] & kFlowing ) !== 0 && state . length )
614
- return state . buffer . first ( ) . length ;
629
+ return state . buffer [ state . bufferIndex ] . length ;
615
630
return state . length ;
616
631
}
617
632
if ( n <= state . length )
@@ -1549,21 +1564,100 @@ function fromList(n, state) {
1549
1564
if ( state . length === 0 )
1550
1565
return null ;
1551
1566
1567
+ let idx = state . bufferIndex ;
1552
1568
let ret ;
1553
- if ( state . objectMode )
1554
- ret = state . buffer . shift ( ) ;
1555
- else if ( ! n || n >= state . length ) {
1569
+
1570
+ const buf = state . buffer ;
1571
+ const len = buf . length ;
1572
+
1573
+ if ( ( state [ kState ] & kObjectMode ) !== 0 ) {
1574
+ ret = buf [ idx ] ;
1575
+ buf [ idx ++ ] = null ;
1576
+ } else if ( ! n || n >= state . length ) {
1556
1577
// 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 ( ) ;
1578
+ if ( ( state [ kState ] & kDecoder ) !== 0 ) {
1579
+ ret = ''
1580
+ while ( idx < len ) {
1581
+ ret += buf [ idx ] ;
1582
+ buf [ idx ++ ] = null ;
1583
+ }
1584
+ } else if ( len - idx === 0 ) {
1585
+ ret = Buffer . alloc ( 0 )
1586
+ } else if ( len - idx === 1 ) {
1587
+ ret = buf [ idx ] ;
1588
+ buf [ idx ++ ] = null ;
1589
+ } else {
1590
+ ret = Buffer . allocUnsafe ( state . length ) ;
1591
+
1592
+ let i = 0 ;
1593
+ while ( idx < len ) {
1594
+ TypedArrayPrototypeSet ( ret , buf [ idx ] , i ) ;
1595
+ i += buf [ idx ] . length ;
1596
+ buf [ idx ++ ] = null ;
1597
+ }
1598
+ }
1564
1599
} else {
1565
1600
// read part of list.
1566
- ret = state . buffer . consume ( n , state . decoder ) ;
1601
+
1602
+ if ( n < buf [ idx ] . length ) {
1603
+ // `slice` is the same for buffers and strings.
1604
+ ret = buf [ idx ] . slice ( 0 , n ) ;
1605
+ buf [ idx ] = buf [ idx ] . slice ( n ) ;
1606
+ } else if ( n === buf [ idx ] . length ) {
1607
+ // First chunk is a perfect match.
1608
+ ret = buf [ idx ] ;
1609
+ buf [ idx ++ ] = null ;
1610
+ } else if ( ( state [ kState ] & kDecoder ) !== 0 ) {
1611
+ ret = '' ;
1612
+ while ( idx < len ) {
1613
+ const str = buf [ idx ] ;
1614
+ if ( n > str . length ) {
1615
+ ret += str ;
1616
+ n -= str . length ;
1617
+ buf [ idx ++ ] = null ;
1618
+ } else {
1619
+ if ( n === buf . length ) {
1620
+ ret += str ;
1621
+ buf [ idx ++ ] = null ;
1622
+ } else {
1623
+ ret += str . slice ( 0 , n ) ;
1624
+ buf [ idx ] = str . slice ( n ) ;
1625
+ }
1626
+ break ;
1627
+ }
1628
+ }
1629
+ } else {
1630
+ ret = Buffer . allocUnsafe ( n ) ;
1631
+
1632
+ const retLen = n ;
1633
+ while ( idx < len ) {
1634
+ const data = buf [ idx ] ;
1635
+ if ( n > data . length ) {
1636
+ TypedArrayPrototypeSet ( ret , data , retLen - n ) ;
1637
+ n -= data . length ;
1638
+ buf [ idx ++ ] = null ;
1639
+ } else {
1640
+ if ( n === data . length ) {
1641
+ TypedArrayPrototypeSet ( ret , data , retLen - n ) ;
1642
+ buf [ idx ++ ] = null ;
1643
+ } else {
1644
+ TypedArrayPrototypeSet ( ret , new FastBuffer ( data . buffer , data . byteOffset , n ) , retLen - n ) ;
1645
+ buf [ idx ] = new FastBuffer ( data . buffer , data . byteOffset + n , data . length - n ) ;
1646
+ }
1647
+ break ;
1648
+ }
1649
+ }
1650
+ }
1651
+ }
1652
+
1653
+ if ( idx === buf . length ) {
1654
+ state . buffer . length = 0 ;
1655
+ state . bufferIndex = 0
1656
+ } else if ( idx > 1024 ) {
1657
+ state . buffer . splice ( 0 , idx ) ;
1658
+ state . bufferIndex = 0 ;
1659
+ } else {
1660
+ state . bufferIndex = idx ;
1567
1661
}
1568
1662
1569
1663
return ret ;
0 commit comments