Skip to content

Commit 8415d63

Browse files
committed
src: introduce PullAll method to speed up blob.text/arrayBuffer
Refs: nodejs/performance#118
1 parent 0debfa4 commit 8415d63

File tree

3 files changed

+98
-22
lines changed

3 files changed

+98
-22
lines changed

lib/internal/blob.js

+11-22
Original file line numberDiff line numberDiff line change
@@ -276,28 +276,17 @@ class Blob {
276276

277277
const { promise, resolve, reject } = createDeferredPromise();
278278
const reader = this[kHandle].getReader();
279-
const buffers = [];
280-
const readNext = () => {
281-
reader.pull((status, buffer) => {
282-
if (status === 0) {
283-
// EOS, concat & resolve
284-
// buffer should be undefined here
285-
resolve(concat(buffers));
286-
return;
287-
} else if (status < 0) {
288-
// The read could fail for many different reasons when reading
289-
// from a non-memory resident blob part (e.g. file-backed blob).
290-
// The error details the system error code.
291-
const error = lazyDOMException('The blob could not be read', 'NotReadableError');
292-
reject(error);
293-
return;
294-
}
295-
if (buffer !== undefined)
296-
buffers.push(buffer);
297-
queueMicrotask(() => readNext());
298-
});
299-
};
300-
readNext();
279+
reader.pullAll((status, buffer) => {
280+
if (status === 0) {
281+
resolve(buffer);
282+
} else if (status < 0) {
283+
// The read could fail for many different reasons when reading
284+
// from a non-memory resident blob part (e.g. file-backed blob).
285+
// The error details the system error code.
286+
const error = lazyDOMException('The blob could not be read', 'NotReadableError');
287+
reject(error);
288+
}
289+
});
301290
return promise;
302291
}
303292

src/node_blob.cc

+86
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,7 @@ Local<FunctionTemplate> Blob::Reader::GetConstructorTemplate(Environment* env) {
305305
BaseObject::kInternalFieldCount);
306306
tmpl->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "BlobReader"));
307307
SetProtoMethod(env->isolate(), tmpl, "pull", Pull);
308+
SetProtoMethod(env->isolate(), tmpl, "pullAll", PullAll);
308309
env->set_blob_reader_constructor_template(tmpl);
309310
}
310311
return tmpl;
@@ -393,6 +394,90 @@ void Blob::Reader::Pull(const FunctionCallbackInfo<Value>& args) {
393394
std::move(next), node::bob::OPTIONS_END, nullptr, 0));
394395
}
395396

397+
void Blob::Reader::PullAll(const FunctionCallbackInfo<Value>& args) {
398+
Environment* env = Environment::GetCurrent(args);
399+
Blob::Reader* reader;
400+
ASSIGN_OR_RETURN_UNWRAP(&reader, args.Holder());
401+
402+
CHECK(args[0]->IsFunction());
403+
Local<Function> fn = args[0].As<Function>();
404+
CHECK(!fn->IsConstructor());
405+
406+
if (reader->eos_) {
407+
Local<Value> arg = Int32::New(env->isolate(), bob::STATUS_EOS);
408+
reader->MakeCallback(fn, 1, &arg);
409+
return;
410+
}
411+
412+
struct View {
413+
std::shared_ptr<BackingStore> store;
414+
size_t length;
415+
size_t offset = 0;
416+
};
417+
418+
struct Impl {
419+
BaseObjectPtr<Blob::Reader> reader;
420+
Global<Function> callback;
421+
Environment* env;
422+
std::function<void()> enqueue_cb;
423+
std::vector<View> views;
424+
size_t total_size = 0;
425+
};
426+
427+
std::shared_ptr<Impl> impl_ptr = std::make_shared<Impl>();
428+
impl_ptr->reader = BaseObjectPtr<Blob::Reader>(reader);
429+
impl_ptr->callback.Reset(env->isolate(), fn);
430+
impl_ptr->env = env;
431+
432+
auto next = [impl_ptr](int status, const DataQueue::Vec* vecs, size_t count, bob::Done doneCb) mutable {
433+
Environment* env = impl_ptr->env;
434+
HandleScope handleScope(env->isolate());
435+
Local<Function> fn = impl_ptr->callback.Get(env->isolate());
436+
437+
if (status == bob::STATUS_EOS) impl_ptr->reader->eos_ = true;
438+
439+
if (count > 0) {
440+
size_t total = 0;
441+
for (size_t n = 0; n < count; n++) total += vecs[n].len;
442+
443+
// contructing array buffer requires a shared_ptr
444+
std::shared_ptr<BackingStore> store = v8::ArrayBuffer::NewBackingStore(env->isolate(), total);
445+
auto ptr = static_cast<uint8_t*>(store->Data());
446+
for (size_t n = 0; n < count; n++) {
447+
std::copy(vecs[n].base, vecs[n].base + vecs[n].len, ptr);
448+
ptr += vecs[n].len;
449+
}
450+
doneCb(0);
451+
impl_ptr->views.emplace_back(View{store, total});
452+
impl_ptr->total_size += total;
453+
}
454+
455+
if (status > 0) {
456+
impl_ptr->enqueue_cb();
457+
} else {
458+
std::shared_ptr<BackingStore> store = ArrayBuffer::NewBackingStore(env->isolate(), impl_ptr->total_size);
459+
auto ptr = static_cast<uint8_t*>(store->Data());
460+
auto views = impl_ptr->views;
461+
for (const auto& view : views) {
462+
uint8_t* from = static_cast<uint8_t*>(view.store->Data()) + view.offset;
463+
std::copy(from, from + view.length, ptr);
464+
ptr += view.length;
465+
}
466+
Local<Value> argv[2] = {Int32::New(env->isolate(), status), ArrayBuffer::New(env->isolate(), store)};
467+
impl_ptr->reader->MakeCallback(fn, arraysize(argv), argv);
468+
impl_ptr->reader.reset();
469+
}
470+
};
471+
472+
impl_ptr->enqueue_cb = [next, impl_ptr]() {
473+
if (impl_ptr->reader && impl_ptr->reader->inner_) {
474+
impl_ptr->reader->inner_->Pull(next, node::bob::OPTIONS_END, nullptr, 0);
475+
}
476+
};
477+
478+
impl_ptr->enqueue_cb();
479+
}
480+
396481
BaseObjectPtr<BaseObject>
397482
Blob::BlobTransferData::Deserialize(
398483
Environment* env,
@@ -574,6 +659,7 @@ void Blob::RegisterExternalReferences(ExternalReferenceRegistry* registry) {
574659
registry->Register(Blob::GetDataObject);
575660
registry->Register(Blob::RevokeObjectURL);
576661
registry->Register(Blob::Reader::Pull);
662+
registry->Register(Blob::Reader::PullAll);
577663
registry->Register(Concat);
578664
registry->Register(BlobFromFilePath);
579665
}

src/node_blob.h

+1
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ class Blob : public BaseObject {
8282
static BaseObjectPtr<Reader> Create(Environment* env,
8383
BaseObjectPtr<Blob> blob);
8484
static void Pull(const v8::FunctionCallbackInfo<v8::Value>& args);
85+
static void PullAll(const v8::FunctionCallbackInfo<v8::Value>& args);
8586

8687
explicit Reader(Environment* env,
8788
v8::Local<v8::Object> obj,

0 commit comments

Comments
 (0)