@@ -201,12 +201,24 @@ int StreamBase::Writev(const FunctionCallbackInfo<Value>& args) {
201201 size_t offset;
202202
203203 if (!all_buffers) {
204+ // Cache per-chunk data from the first pass so the second pass avoids
205+ // redundant V8 array accesses, ToString conversions, and ParseEncoding
206+ // calls. Local<> handles remain valid for the duration of this scope.
207+ struct CachedChunk {
208+ Local<Value> value;
209+ Local<String> string; // empty for Buffer chunks
210+ enum encoding enc;
211+ };
212+ MaybeStackBuffer<CachedChunk, 16 > chunk_cache (count);
213+
204214 // Determine storage size first
205215 for (size_t i = 0 ; i < count; i++) {
206216 Local<Value> chunk;
207217 if (!chunks->Get (context, i * 2 ).ToLocal (&chunk))
208218 return -1 ;
209219
220+ chunk_cache[i].value = chunk;
221+
210222 if (Buffer::HasInstance (chunk))
211223 continue ;
212224 // Buffer chunk, no additional storage required
@@ -219,6 +231,8 @@ int StreamBase::Writev(const FunctionCallbackInfo<Value>& args) {
219231 if (!chunks->Get (context, i * 2 + 1 ).ToLocal (&next_chunk))
220232 return -1 ;
221233 enum encoding encoding = ParseEncoding (isolate, next_chunk);
234+ chunk_cache[i].string = string;
235+ chunk_cache[i].enc = encoding;
222236 size_t chunk_size;
223237 if ((encoding == UTF8 &&
224238 string->Length () > 65535 &&
@@ -230,35 +244,23 @@ int StreamBase::Writev(const FunctionCallbackInfo<Value>& args) {
230244 storage_size += chunk_size;
231245 }
232246
233- if (storage_size > INT_MAX)
234- return UV_ENOBUFS;
235- } else {
236- for (size_t i = 0 ; i < count; i++) {
237- Local<Value> chunk;
238- if (!chunks->Get (context, i).ToLocal (&chunk))
239- return -1 ;
240- bufs[i].base = Buffer::Data (chunk);
241- bufs[i].len = Buffer::Length (chunk);
242- }
243- }
247+ if (storage_size > INT_MAX) return UV_ENOBUFS;
244248
245- std::unique_ptr<BackingStore> bs;
246- if (storage_size > 0 ) {
247- bs = ArrayBuffer::NewBackingStore (
248- isolate, storage_size, BackingStoreInitializationMode::kUninitialized );
249- }
249+ std::unique_ptr<BackingStore> bs;
250+ if (storage_size > 0 ) {
251+ bs = ArrayBuffer::NewBackingStore (
252+ isolate,
253+ storage_size,
254+ BackingStoreInitializationMode::kUninitialized );
255+ }
250256
251- offset = 0 ;
252- if (!all_buffers) {
257+ offset = 0 ;
253258 for (size_t i = 0 ; i < count; i++) {
254- Local<Value> chunk;
255- if (!chunks->Get (context, i * 2 ).ToLocal (&chunk))
256- return -1 ;
257-
258- // Write buffer
259- if (Buffer::HasInstance (chunk)) {
260- bufs[i].base = Buffer::Data (chunk);
261- bufs[i].len = Buffer::Length (chunk);
259+ // string.IsEmpty() signals a Buffer chunk; enc is uninitialised in
260+ // that case so we must not read it.
261+ if (chunk_cache[i].string .IsEmpty ()) {
262+ bufs[i].base = Buffer::Data (chunk_cache[i].value );
263+ bufs[i].len = Buffer::Length (chunk_cache[i].value );
262264 continue ;
263265 }
264266
@@ -268,28 +270,32 @@ int StreamBase::Writev(const FunctionCallbackInfo<Value>& args) {
268270 static_cast <char *>(bs ? bs->Data () : nullptr ) + offset;
269271 size_t str_size = (bs ? bs->ByteLength () : 0 ) - offset;
270272
271- Local<String> string;
272- if (!chunk->ToString (context).ToLocal (&string))
273- return -1 ;
274- Local<Value> next_chunk;
275- if (!chunks->Get (context, i * 2 + 1 ).ToLocal (&next_chunk))
276- return -1 ;
277- enum encoding encoding = ParseEncoding (isolate, next_chunk);
278273 str_size = StringBytes::Write (isolate,
279274 str_storage,
280275 str_size,
281- string,
282- encoding );
276+ chunk_cache[i]. string ,
277+ chunk_cache[i]. enc );
283278 bufs[i].base = str_storage;
284279 bufs[i].len = str_size;
285280 offset += str_size;
286281 }
282+
283+ StreamWriteResult res = Write (*bufs, count, nullptr , req_wrap_obj);
284+ SetWriteResult (res);
285+ if (res.wrap != nullptr && storage_size > 0 )
286+ res.wrap ->SetBackingStore (std::move (bs));
287+ return res.err ;
288+ } else {
289+ for (size_t i = 0 ; i < count; i++) {
290+ Local<Value> chunk;
291+ if (!chunks->Get (context, i).ToLocal (&chunk)) return -1 ;
292+ bufs[i].base = Buffer::Data (chunk);
293+ bufs[i].len = Buffer::Length (chunk);
294+ }
287295 }
288296
289297 StreamWriteResult res = Write (*bufs, count, nullptr , req_wrap_obj);
290298 SetWriteResult (res);
291- if (res.wrap != nullptr && storage_size > 0 )
292- res.wrap ->SetBackingStore (std::move (bs));
293299 return res.err ;
294300}
295301
0 commit comments