diff --git a/src/node_blob.cc b/src/node_blob.cc index a04bc2be0ef5..ab4267fb81f0 100644 --- a/src/node_blob.cc +++ b/src/node_blob.cc @@ -33,6 +33,7 @@ using v8::Int32; using v8::Isolate; using v8::Local; using v8::NewStringType; +using v8::Number; using v8::Object; using v8::ObjectTemplate; using v8::SnapshotCreator; @@ -125,7 +126,7 @@ void BlobFromFilePath(const FunctionCallbackInfo& args) { Blob::Create(env, DataQueue::CreateIdempotent(std::move(entries)))) { Local vals[2]{ blob->object(), - Uint32::NewFromUnsigned(env->isolate(), blob->length()), + Number::New(env->isolate(), static_cast(blob->length())), }; args.GetReturnValue().Set( Array::New(env->isolate(), &vals[0], arraysize(vals))); @@ -274,10 +275,10 @@ void Blob::ToSlice(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); Blob* blob; ASSIGN_OR_RETURN_UNWRAP(&blob, args.This()); - CHECK(args[0]->IsUint32()); - CHECK(args[1]->IsUint32()); - size_t start = args[0].As()->Value(); - size_t end = args[1].As()->Value(); + CHECK(args[0]->IsNumber()); + CHECK(args[1]->IsNumber()); + uint64_t start = static_cast(args[0].As()->Value()); + uint64_t end = static_cast(args[1].As()->Value()); BaseObjectPtr slice = blob->Slice(env, start, end); if (slice) args.GetReturnValue().Set(slice->object()); } @@ -286,9 +287,10 @@ void Blob::MemoryInfo(MemoryTracker* tracker) const { tracker->TrackField("data_queue_", data_queue_, "std::shared_ptr"); } -BaseObjectPtr Blob::Slice(Environment* env, size_t start, size_t end) { - return Create(env, - this->data_queue_->slice(start, static_cast(end))); +BaseObjectPtr Blob::Slice(Environment* env, + uint64_t start, + uint64_t end) { + return Create(env, this->data_queue_->slice(start, end)); } Blob::Blob(Environment* env, @@ -459,7 +461,7 @@ void Blob::StoreDataObject(const FunctionCallbackInfo& args) { CHECK(args[0]->IsString()); // ID key CHECK(Blob::HasInstance(realm->env(), args[1])); // Blob - CHECK(args[2]->IsUint32()); // Length + CHECK(args[2]->IsNumber()); // Length CHECK(args[3]->IsString()); // Type BlobBindingData* binding_data = realm->GetBindingData(); @@ -469,7 +471,7 @@ void Blob::StoreDataObject(const FunctionCallbackInfo& args) { Blob* blob; ASSIGN_OR_RETURN_UNWRAP(&blob, args[1]); - size_t length = args[2].As()->Value(); + uint64_t length = static_cast(args[2].As()->Value()); Utf8Value type(isolate, args[3]); binding_data->store_data_object( @@ -526,9 +528,10 @@ void Blob::GetDataObject(const FunctionCallbackInfo& args) { return; } - Local values[] = {stored.blob->object(), - Uint32::NewFromUnsigned(isolate, stored.length), - type}; + Local values[] = { + stored.blob->object(), + Number::New(isolate, static_cast(stored.length)), + type}; args.GetReturnValue().Set(Array::New(isolate, values, arraysize(values))); } @@ -540,7 +543,9 @@ void BlobBindingData::StoredDataObject::MemoryInfo( } BlobBindingData::StoredDataObject::StoredDataObject( - const BaseObjectPtr& blob_, size_t length_, const std::string& type_) + const BaseObjectPtr& blob_, + uint64_t length_, + const std::string& type_) : blob(blob_), length(length_), type(type_) {} BlobBindingData::BlobBindingData(Realm* realm, Local wrap) diff --git a/src/node_blob.h b/src/node_blob.h index 06d3a151cfa5..a3016979dfe8 100644 --- a/src/node_blob.h +++ b/src/node_blob.h @@ -51,9 +51,9 @@ class Blob : public BaseObject { SET_MEMORY_INFO_NAME(Blob) SET_SELF_SIZE(Blob) - BaseObjectPtr Slice(Environment* env, size_t start, size_t end); + BaseObjectPtr Slice(Environment* env, uint64_t start, uint64_t end); - inline size_t length() const { return this->data_queue_->size().value(); } + inline uint64_t length() const { return this->data_queue_->size().value(); } class BlobTransferData : public worker::TransferData { public: @@ -129,13 +129,13 @@ class BlobBindingData : public SnapshotableObject { struct StoredDataObject : public MemoryRetainer { BaseObjectPtr blob; - size_t length; + uint64_t length; std::string type; StoredDataObject() = default; StoredDataObject(const BaseObjectPtr& blob_, - size_t length_, + uint64_t length_, const std::string& type_); void MemoryInfo(MemoryTracker* tracker) const override; diff --git a/test/pummel/test-blob-large-file-backed.js b/test/pummel/test-blob-large-file-backed.js new file mode 100644 index 000000000000..e9204944be5c --- /dev/null +++ b/test/pummel/test-blob-large-file-backed.js @@ -0,0 +1,49 @@ +'use strict'; + +// This tests that a file-backed Blob whose size exceeds the range of a 32-bit +// unsigned integer reports its real size, can be sliced past that boundary, +// and survives a round trip through URL.createObjectURL(). + +const common = require('../common'); + +common.skipIf32Bits(); + +const assert = require('assert'); +const fs = require('fs'); +const { resolveObjectURL } = require('buffer'); + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const filepath = tmpdir.resolve('large-blob.bin'); +const size = 5 * 1024 * 1024 * 1024; // 5GB, sparse +const message = 'Large File'; + +try { + const fd = fs.openSync(filepath, 'w+'); + fs.ftruncateSync(fd, size); + const buf = Buffer.from(message); + fs.writeSync(fd, buf, 0, buf.length, size - buf.length); + fs.closeSync(fd); +} catch (e) { + if (e.code !== 'ENOSPC') { + throw e; + } + common.skip('insufficient disk space'); +} + +(async () => { + const blob = await fs.openAsBlob(filepath); + assert.strictEqual(blob.size, size); + + const tail = blob.slice(size - message.length, size); + assert.strictEqual(tail.size, message.length); + assert.strictEqual(await tail.text(), message); + + const url = URL.createObjectURL(blob); + try { + assert.strictEqual(resolveObjectURL(url).size, size); + } finally { + URL.revokeObjectURL(url); + } +})().then(common.mustCall());