From 1e05ddf406e60483630f52f18df69d1de94f5dde Mon Sep 17 00:00:00 2001 From: Brian White Date: Mon, 3 Feb 2020 16:18:57 -0500 Subject: [PATCH] stream: improve writable.write() performance Backport-PR-URL: https://github.com/nodejs/node/pull/32169 PR-URL: https://github.com/nodejs/node/pull/31624 Reviewed-By: Matteo Collina Reviewed-By: James M Snell Reviewed-By: Luigi Pinca --- lib/_stream_writable.js | 75 +++++++++++++++-------------------------- 1 file changed, 27 insertions(+), 48 deletions(-) diff --git a/lib/_stream_writable.js b/lib/_stream_writable.js index 2e27ae1feae491..8e9fbe70388c9e 100644 --- a/lib/_stream_writable.js +++ b/lib/_stream_writable.js @@ -268,35 +268,8 @@ Writable.prototype.pipe = function() { }; -function writeAfterEnd(stream, cb) { - const er = new ERR_STREAM_WRITE_AFTER_END(); - // TODO: defer error events consistently everywhere, not just the cb - errorOrDestroy(stream, er); - process.nextTick(cb, er); -} - -// Checks that a user-supplied chunk is valid, especially for the particular -// mode the stream is in. Currently this means that `null` is never accepted -// and undefined/non-string values are only allowed in object mode. -function validChunk(stream, state, chunk, cb) { - var er; - - if (chunk === null) { - er = new ERR_STREAM_NULL_VALUES(); - } else if (typeof chunk !== 'string' && !state.objectMode) { - er = new ERR_INVALID_ARG_TYPE('chunk', ['string', 'Buffer'], chunk); - } - if (er) { - errorOrDestroy(stream, er); - process.nextTick(cb, er); - return false; - } - return true; -} - Writable.prototype.write = function(chunk, encoding, cb) { const state = this._writableState; - var ret = false; const isBuf = !state.objectMode && Stream._isUint8Array(chunk); // Do not use Object.getPrototypeOf as it is slower since V8 7.3. @@ -306,29 +279,42 @@ Writable.prototype.write = function(chunk, encoding, cb) { if (typeof encoding === 'function') { cb = encoding; - encoding = null; + encoding = state.defaultEncoding; + } else { + if (!encoding) + encoding = state.defaultEncoding; + if (typeof cb !== 'function') + cb = nop; } if (isBuf) encoding = 'buffer'; - else if (!encoding) - encoding = state.defaultEncoding; - - if (typeof cb !== 'function') - cb = nop; + let err; if (state.ending) { - writeAfterEnd(this, cb); + err = new ERR_STREAM_WRITE_AFTER_END(); } else if (state.destroyed) { - const err = new ERR_STREAM_DESTROYED('write'); - process.nextTick(cb, err); - errorOrDestroy(this, err); - } else if (isBuf || validChunk(this, state, chunk, cb)) { - state.pendingcb++; - ret = writeOrBuffer(this, state, chunk, encoding, cb); + err = new ERR_STREAM_DESTROYED('write'); + } else if (chunk === null) { + err = new ERR_STREAM_NULL_VALUES(); + } else { + if (!isBuf && !state.objectMode) { + if (typeof chunk !== 'string') { + err = new ERR_INVALID_ARG_TYPE('chunk', ['string', 'Buffer'], chunk); + } else if (encoding !== 'buffer' && state.decodeStrings !== false) { + chunk = Buffer.from(chunk, encoding); + encoding = 'buffer'; + } + } + if (err === undefined) { + state.pendingcb++; + return writeOrBuffer(this, state, chunk, encoding, cb); + } } - return ret; + process.nextTick(cb, err); + errorOrDestroy(this, err, true); + return false; }; Writable.prototype.cork = function() { @@ -363,13 +349,6 @@ Writable.prototype.setDefaultEncoding = function setDefaultEncoding(encoding) { // in the queue, and wait our turn. Otherwise, call _write // If we return false, then we need a drain event, so set that flag. function writeOrBuffer(stream, state, chunk, encoding, cb) { - if (!state.objectMode && - state.decodeStrings !== false && - encoding !== 'buffer' && - typeof chunk === 'string') { - chunk = Buffer.from(chunk, encoding); - encoding = 'buffer'; - } const len = state.objectMode ? 1 : chunk.length; state.length += len;