diff --git a/lib/_stream_writable.js b/lib/_stream_writable.js index c44458b9db4aaf..452445015f9a5b 100644 --- a/lib/_stream_writable.js +++ b/lib/_stream_writable.js @@ -320,6 +320,7 @@ function writeOrBuffer(stream, state, isBuf, chunk, encoding, cb) { if (!isBuf) { var newChunk = decodeChunk(state, chunk, encoding); if (chunk !== newChunk) { + isBuf = true; encoding = 'buffer'; chunk = newChunk; } @@ -335,7 +336,13 @@ function writeOrBuffer(stream, state, isBuf, chunk, encoding, cb) { if (state.writing || state.corked) { var last = state.lastBufferedRequest; - state.lastBufferedRequest = { chunk, encoding, callback: cb, next: null }; + state.lastBufferedRequest = { + chunk, + encoding, + isBuf, + callback: cb, + next: null + }; if (last) { last.next = state.lastBufferedRequest; } else { @@ -438,11 +445,15 @@ function clearBuffer(stream, state) { holder.entry = entry; var count = 0; + var allBuffers = true; while (entry) { buffer[count] = entry; + if (!entry.isBuf) + allBuffers = false; entry = entry.next; count += 1; } + buffer.allBuffers = allBuffers; doWrite(stream, state, true, state.length, buffer, '', holder.finish); diff --git a/lib/net.js b/lib/net.js index 67e191fa213329..07344ea7c7794c 100644 --- a/lib/net.js +++ b/lib/net.js @@ -726,13 +726,22 @@ Socket.prototype._writeGeneric = function(writev, data, encoding, cb) { var err; if (writev) { - var chunks = new Array(data.length << 1); - for (var i = 0; i < data.length; i++) { - var entry = data[i]; - chunks[i * 2] = entry.chunk; - chunks[i * 2 + 1] = entry.encoding; + var allBuffers = data.allBuffers; + var chunks; + var i; + if (allBuffers) { + chunks = data; + for (i = 0; i < data.length; i++) + data[i] = data[i].chunk; + } else { + chunks = new Array(data.length << 1); + for (i = 0; i < data.length; i++) { + var entry = data[i]; + chunks[i * 2] = entry.chunk; + chunks[i * 2 + 1] = entry.encoding; + } } - err = this._handle.writev(req, chunks); + err = this._handle.writev(req, chunks, allBuffers); // Retain chunks if (err === 0) req._chunks = chunks; diff --git a/src/stream_base.cc b/src/stream_base.cc index c4b59ee5ca42b5..51bad94a4fabc0 100644 --- a/src/stream_base.cc +++ b/src/stream_base.cc @@ -100,92 +100,116 @@ int StreamBase::Writev(const FunctionCallbackInfo& args) { Local req_wrap_obj = args[0].As(); Local chunks = args[1].As(); + bool all_buffers = args[2]->IsTrue(); - size_t count = chunks->Length() >> 1; + size_t count; + if (all_buffers) + count = chunks->Length(); + else + count = chunks->Length() >> 1; MaybeStackBuffer bufs(count); + uv_buf_t* buf_list = *bufs; - // Determine storage size first size_t storage_size = 0; - for (size_t i = 0; i < count; i++) { - storage_size = ROUND_UP(storage_size, WriteWrap::kAlignSize); - - Local chunk = chunks->Get(i * 2); - - if (Buffer::HasInstance(chunk)) - continue; - // Buffer chunk, no additional storage required - - // String chunk - Local string = chunk->ToString(env->isolate()); - enum encoding encoding = ParseEncoding(env->isolate(), - chunks->Get(i * 2 + 1)); - size_t chunk_size; - if (encoding == UTF8 && string->Length() > 65535) - chunk_size = StringBytes::Size(env->isolate(), string, encoding); - else - chunk_size = StringBytes::StorageSize(env->isolate(), string, encoding); - - storage_size += chunk_size; - } + uint32_t bytes = 0; + size_t offset; + AsyncWrap* wrap; + WriteWrap* req_wrap; + int err; - if (storage_size > INT_MAX) - return UV_ENOBUFS; + if (!all_buffers) { + // Determine storage size first + for (size_t i = 0; i < count; i++) { + storage_size = ROUND_UP(storage_size, WriteWrap::kAlignSize); - AsyncWrap* wrap = GetAsyncWrap(); - CHECK_NE(wrap, nullptr); - env->set_init_trigger_id(wrap->get_id()); - WriteWrap* req_wrap = WriteWrap::New(env, - req_wrap_obj, - this, - AfterWrite, - storage_size); + Local chunk = chunks->Get(i * 2); - uint32_t bytes = 0; - size_t offset = 0; - for (size_t i = 0; i < count; i++) { - Local chunk = chunks->Get(i * 2); + if (Buffer::HasInstance(chunk)) + continue; + // Buffer chunk, no additional storage required + + // String chunk + Local string = chunk->ToString(env->isolate()); + enum encoding encoding = ParseEncoding(env->isolate(), + chunks->Get(i * 2 + 1)); + size_t chunk_size; + if (encoding == UTF8 && string->Length() > 65535) + chunk_size = StringBytes::Size(env->isolate(), string, encoding); + else + chunk_size = StringBytes::StorageSize(env->isolate(), string, encoding); - // Write buffer - if (Buffer::HasInstance(chunk)) { + storage_size += chunk_size; + } + + if (storage_size > INT_MAX) + return UV_ENOBUFS; + } else { + for (size_t i = 0; i < count; i++) { + Local chunk = chunks->Get(i); bufs[i].base = Buffer::Data(chunk); bufs[i].len = Buffer::Length(chunk); bytes += bufs[i].len; - continue; } - // Write string - offset = ROUND_UP(offset, WriteWrap::kAlignSize); - CHECK_LE(offset, storage_size); - char* str_storage = req_wrap->Extra(offset); - size_t str_size = storage_size - offset; - - Local string = chunk->ToString(env->isolate()); - enum encoding encoding = ParseEncoding(env->isolate(), - chunks->Get(i * 2 + 1)); - str_size = StringBytes::Write(env->isolate(), - str_storage, - str_size, - string, - encoding); - bufs[i].base = str_storage; - bufs[i].len = str_size; - offset += str_size; - bytes += str_size; + // Try writing immediately without allocation + err = DoTryWrite(&buf_list, &count); + if (err != 0 || count == 0) + goto done; } - int err = DoWrite(req_wrap, *bufs, count, nullptr); + wrap = GetAsyncWrap(); + CHECK_NE(wrap, nullptr); + env->set_init_trigger_id(wrap->get_id()); + req_wrap = WriteWrap::New(env, req_wrap_obj, this, AfterWrite, storage_size); + offset = 0; + if (!all_buffers) { + for (size_t i = 0; i < count; i++) { + Local chunk = chunks->Get(i * 2); + + // Write buffer + if (Buffer::HasInstance(chunk)) { + bufs[i].base = Buffer::Data(chunk); + bufs[i].len = Buffer::Length(chunk); + bytes += bufs[i].len; + continue; + } + + // Write string + offset = ROUND_UP(offset, WriteWrap::kAlignSize); + CHECK_LE(offset, storage_size); + char* str_storage = req_wrap->Extra(offset); + size_t str_size = storage_size - offset; + + Local string = chunk->ToString(env->isolate()); + enum encoding encoding = ParseEncoding(env->isolate(), + chunks->Get(i * 2 + 1)); + str_size = StringBytes::Write(env->isolate(), + str_storage, + str_size, + string, + encoding); + bufs[i].base = str_storage; + bufs[i].len = str_size; + offset += str_size; + bytes += str_size; + } + } + + err = DoWrite(req_wrap, buf_list, count, nullptr); req_wrap_obj->Set(env->async(), True(env->isolate())); - req_wrap_obj->Set(env->bytes_string(), Number::New(env->isolate(), bytes)); + + if (err) + req_wrap->Dispose(); + + done: const char* msg = Error(); if (msg != nullptr) { req_wrap_obj->Set(env->error_string(), OneByteString(env->isolate(), msg)); ClearError(); } - - if (err) - req_wrap->Dispose(); + req_wrap_obj->Set(env->bytes_string(), Number::New(env->isolate(), bytes)); return err; }