@@ -53,6 +53,18 @@ Object.setPrototypeOf(Writable, Stream);
53
53
54
54
function nop ( ) { }
55
55
56
+ function bufferedDispatch ( err ) {
57
+ const index = this . index ;
58
+ for ( let i = 0 ; i < index ; i ++ ) {
59
+ this [ i ] . callback ( err ) ;
60
+ this [ i ] = null ;
61
+ }
62
+
63
+ this . splice ( 0 , index ) ;
64
+ this . index -= index ;
65
+ this . allBuffers = this . allBuffers || this . every ( ( request ) => request . isBuf ) ;
66
+ }
67
+
56
68
function WritableState ( options , stream , isDuplex ) {
57
69
options = options || { } ;
58
70
@@ -134,8 +146,10 @@ function WritableState(options, stream, isDuplex) {
134
146
// The amount that is being written when _write is called.
135
147
this . writelen = 0 ;
136
148
137
- this . bufferedRequest = null ;
138
- this . lastBufferedRequest = null ;
149
+ this . buffered = [ ] ;
150
+ this . buffered . index = 0 ;
151
+ this . buffered . allBuffers = true ;
152
+ this . buffered . dispatch = bufferedDispatch . bind ( this . buffered ) ;
139
153
140
154
// Number of pending user-supplied write callbacks
141
155
// this must be 0 before 'finish' can be emitted
@@ -153,25 +167,10 @@ function WritableState(options, stream, isDuplex) {
153
167
154
168
// Should .destroy() be called after 'finish' (and potentially 'end')
155
169
this . autoDestroy = ! ! options . autoDestroy ;
156
-
157
- // Count buffered requests
158
- this . bufferedRequestCount = 0 ;
159
-
160
- // Allocate the first CorkedRequest, there is always
161
- // one allocated and free to use, and we maintain at most two
162
- const corkReq = { next : null , entry : null , finish : undefined } ;
163
- corkReq . finish = onCorkedFinish . bind ( undefined , corkReq , this ) ;
164
- this . corkedRequestsFree = corkReq ;
165
170
}
166
171
167
172
WritableState . prototype . getBuffer = function getBuffer ( ) {
168
- var current = this . bufferedRequest ;
169
- const out = [ ] ;
170
- while ( current ) {
171
- out . push ( current ) ;
172
- current = current . next ;
173
- }
174
- return out ;
173
+ return this . buffered . slice ( this . buffered . index ) ;
175
174
} ;
176
175
177
176
Object . defineProperty ( WritableState . prototype , 'buffer' , {
@@ -314,12 +313,7 @@ Writable.prototype.uncork = function() {
314
313
315
314
if ( state . corked ) {
316
315
state . corked -- ;
317
-
318
- if ( ! state . writing &&
319
- ! state . corked &&
320
- ! state . bufferProcessing &&
321
- state . bufferedRequest )
322
- clearBuffer ( this , state ) ;
316
+ clearBuffer ( this , state ) ;
323
317
}
324
318
} ;
325
319
@@ -365,7 +359,7 @@ Object.defineProperty(Writable.prototype, 'writableHighWaterMark', {
365
359
// If we're already writing something, then just put this
366
360
// in the queue, and wait our turn. Otherwise, call _write
367
361
// If we return false, then we need a drain event, so set that flag.
368
- function writeOrBuffer ( stream , state , isBuf , chunk , encoding , cb ) {
362
+ function writeOrBuffer ( stream , state , isBuf , chunk , encoding , callback ) {
369
363
if ( ! isBuf ) {
370
364
var newChunk = decodeChunk ( state , chunk , encoding ) ;
371
365
if ( chunk !== newChunk ) {
@@ -384,22 +378,16 @@ function writeOrBuffer(stream, state, isBuf, chunk, encoding, cb) {
384
378
state . needDrain = true ;
385
379
386
380
if ( state . writing || state . corked ) {
387
- var last = state . lastBufferedRequest ;
388
- state . lastBufferedRequest = {
381
+ const buffered = state . buffered ;
382
+ buffered . push ( {
389
383
chunk,
390
384
encoding,
391
- isBuf,
392
- callback : cb ,
393
- next : null
394
- } ;
395
- if ( last ) {
396
- last . next = state . lastBufferedRequest ;
397
- } else {
398
- state . bufferedRequest = state . lastBufferedRequest ;
399
- }
400
- state . bufferedRequestCount += 1 ;
385
+ callback,
386
+ isBuf
387
+ } ) ;
388
+ buffered . allBuffers = isBuf && buffered . allBuffers ;
401
389
} else {
402
- doWrite ( stream , state , false , len , chunk , encoding , cb ) ;
390
+ doWrite ( stream , state , false , len , chunk , encoding , callback ) ;
403
391
}
404
392
405
393
return ret ;
@@ -426,21 +414,13 @@ function onwriteError(stream, state, sync, er, cb) {
426
414
// Defer the callback if we are being called synchronously
427
415
// to avoid piling up things on the stack
428
416
process . nextTick ( cb , er ) ;
429
- // This can emit finish, and it will always happen
430
- // after error
431
- process . nextTick ( finishMaybe , stream , state ) ;
432
- stream . _writableState . errorEmitted = true ;
433
- errorOrDestroy ( stream , er ) ;
434
417
} else {
435
418
// The caller expect this to happen before if
436
419
// it is async
437
420
cb ( er ) ;
438
- stream . _writableState . errorEmitted = true ;
439
- errorOrDestroy ( stream , er ) ;
440
- // This can emit finish, but finish must
441
- // always follow error
442
- finishMaybe ( stream , state ) ;
443
421
}
422
+ stream . _writableState . errorEmitted = true ;
423
+ errorOrDestroy ( stream , er ) ;
444
424
}
445
425
446
426
function onwrite ( stream , er ) {
@@ -462,10 +442,7 @@ function onwrite(stream, er) {
462
442
// Check if we're actually ready to finish, but don't emit yet
463
443
var finished = needFinish ( state ) || stream . destroyed ;
464
444
465
- if ( ! finished &&
466
- ! state . corked &&
467
- ! state . bufferProcessing &&
468
- state . bufferedRequest ) {
445
+ if ( ! finished ) {
469
446
clearBuffer ( stream , state ) ;
470
447
}
471
448
@@ -497,67 +474,34 @@ function onwriteDrain(stream, state) {
497
474
498
475
// If there's something in the buffer waiting, then process it
499
476
function clearBuffer ( stream , state ) {
500
- state . bufferProcessing = true ;
501
- var entry = state . bufferedRequest ;
502
-
503
- if ( stream . _writev && entry && entry . next ) {
504
- // Fast case, write everything using _writev()
505
- var l = state . bufferedRequestCount ;
506
- var buffer = new Array ( l ) ;
507
- var holder = state . corkedRequestsFree ;
508
- holder . entry = entry ;
509
-
510
- var count = 0 ;
511
- var allBuffers = true ;
512
- while ( entry ) {
513
- buffer [ count ] = entry ;
514
- if ( ! entry . isBuf )
515
- allBuffers = false ;
516
- entry = entry . next ;
517
- count += 1 ;
518
- }
519
- buffer . allBuffers = allBuffers ;
477
+ if ( state . writing || state . bufferProcessing || state . corked ) {
478
+ return ;
479
+ }
520
480
521
- doWrite ( stream , state , true , state . length , buffer , '' , holder . finish ) ;
481
+ const buffered = state . buffered ;
482
+ const bufferedCount = buffered . length - buffered . index ;
522
483
523
- // doWrite is almost always async, defer these to save a bit of time
524
- // as the hot path ends with doWrite
525
- state . pendingcb ++ ;
526
- state . lastBufferedRequest = null ;
527
- if ( holder . next ) {
528
- state . corkedRequestsFree = holder . next ;
529
- holder . next = null ;
530
- } else {
531
- var corkReq = { next : null , entry : null , finish : undefined } ;
532
- corkReq . finish = onCorkedFinish . bind ( undefined , corkReq , state ) ;
533
- state . corkedRequestsFree = corkReq ;
534
- }
535
- state . bufferedRequestCount = 0 ;
484
+ if ( ! bufferedCount ) {
485
+ return ;
486
+ }
487
+
488
+ state . bufferProcessing = true ;
489
+ if ( stream . _writev ) {
490
+ buffered . index += bufferedCount ;
491
+ doWrite ( stream , state , true , state . length , buffered , '' , buffered . dispatch ) ;
536
492
} else {
537
493
// Slow case, write chunks one-by-one
538
- while ( entry ) {
539
- var chunk = entry . chunk ;
540
- var encoding = entry . encoding ;
541
- var cb = entry . callback ;
542
- var len = state . objectMode ? 1 : chunk . length ;
543
-
544
- doWrite ( stream , state , false , len , chunk , encoding , cb ) ;
545
- entry = entry . next ;
546
- state . bufferedRequestCount -- ;
494
+ for ( const { chunk, encoding, length } of buffered ) {
495
+ const len = state . objectMode ? 1 : length ;
496
+ buffered . index += 1 ;
497
+ doWrite ( stream , state , false , len , chunk , encoding , buffered . dispatch ) ;
547
498
// If we didn't call the onwrite immediately, then
548
499
// it means that we need to wait until it does.
549
- // also, that means that the chunk and cb are currently
550
- // being processed, so move the buffer counter past them.
551
500
if ( state . writing ) {
552
501
break ;
553
502
}
554
503
}
555
-
556
- if ( entry === null )
557
- state . lastBufferedRequest = null ;
558
504
}
559
-
560
- state . bufferedRequest = entry ;
561
505
state . bufferProcessing = false ;
562
506
}
563
507
@@ -606,9 +550,13 @@ Object.defineProperty(Writable.prototype, 'writableLength', {
606
550
} ) ;
607
551
608
552
function needFinish ( state ) {
553
+ const buffered = state . buffered ;
554
+ const bufferedCount = buffered . length - buffered . index ;
555
+
609
556
return ( state . ending &&
610
557
state . length === 0 &&
611
- state . bufferedRequest === null &&
558
+ bufferedCount === 0 &&
559
+ ! state . errorEmitted &&
612
560
! state . finished &&
613
561
! state . writing ) ;
614
562
}
@@ -670,20 +618,6 @@ function endWritable(stream, state, cb) {
670
618
stream . writable = false ;
671
619
}
672
620
673
- function onCorkedFinish ( corkReq , state , err ) {
674
- var entry = corkReq . entry ;
675
- corkReq . entry = null ;
676
- while ( entry ) {
677
- var cb = entry . callback ;
678
- state . pendingcb -- ;
679
- cb ( err ) ;
680
- entry = entry . next ;
681
- }
682
-
683
- // Reuse the free corkReq.
684
- state . corkedRequestsFree . next = corkReq ;
685
- }
686
-
687
621
Object . defineProperty ( Writable . prototype , 'destroyed' , {
688
622
// Making it explicit this property is not enumerable
689
623
// because otherwise some prototype manipulation in
0 commit comments