From 7638ed168ede17cf802eaed2a383d31a6ac0001d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Louren=C3=A7o?= Date: Mon, 9 Oct 2023 23:28:46 -0300 Subject: [PATCH] lib: faster blob cloning --- benchmark/blob/clone.js | 7 +++++-- lib/internal/blob.js | 22 ++++++++++++---------- test/parallel/test-blob.js | 10 ++++++++++ 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/benchmark/blob/clone.js b/benchmark/blob/clone.js index 7f66255ad9da0c..39890672fc66f8 100644 --- a/benchmark/blob/clone.js +++ b/benchmark/blob/clone.js @@ -7,14 +7,17 @@ const assert = require('assert'); const bench = common.createBenchmark(main, { n: [50e3], + bytes: [128, 1024, 1024 ** 2], }); let _cloneResult; -function main({ n }) { +function main({ n, bytes }) { + const buff = Buffer.allocUnsafe(bytes); + const blob = new Blob(buff); bench.start(); for (let i = 0; i < n; ++i) - _cloneResult = structuredClone(new Blob(['hello'])); + _cloneResult = structuredClone(blob); bench.end(n); // Avoid V8 deadcode (elimination) diff --git a/lib/internal/blob.js b/lib/internal/blob.js index a6801f3d7fba3b..314056ec140f04 100644 --- a/lib/internal/blob.js +++ b/lib/internal/blob.js @@ -200,7 +200,7 @@ class Blob { const length = this[kLength]; return { data: { handle, type, length }, - deserializeInfo: 'internal/blob:ClonedBlob', + deserializeInfo: 'internal/blob:CloneableBlob', }; } @@ -397,25 +397,27 @@ class Blob { } } -function ClonedBlob() { - return ReflectConstruct(function() { +class CloneableBlob extends Blob { + static { markTransferMode(this, true, false); - }, [], Blob); + this[kDeserialize] = () => {}; + } } -ClonedBlob.prototype[kDeserialize] = () => {}; -function TransferrableBlob(handle, length, type = '') { +CloneableBlob.prototype.constructor = Blob; + +function TransferableBlob(handle, length, type = '') { markTransferMode(this, true, false); this[kHandle] = handle; this[kType] = type; this[kLength] = length; } -ObjectSetPrototypeOf(TransferrableBlob.prototype, Blob.prototype); -ObjectSetPrototypeOf(TransferrableBlob, Blob); +ObjectSetPrototypeOf(TransferableBlob.prototype, Blob.prototype); +ObjectSetPrototypeOf(TransferableBlob, Blob); function createBlob(handle, length, type = '') { - const transferredBlob = new TransferrableBlob(handle, length, type); + const transferredBlob = new TransferableBlob(handle, length, type); // Fix issues like: https://github.com/nodejs/node/pull/49730#discussion_r1331720053 transferredBlob.constructor = Blob; @@ -489,7 +491,7 @@ function createBlobFromFilePath(path, options) { module.exports = { Blob, - ClonedBlob, + CloneableBlob, createBlob, createBlobFromFilePath, isBlob, diff --git a/test/parallel/test-blob.js b/test/parallel/test-blob.js index 04b0580202c171..cb9e2f239547f0 100644 --- a/test/parallel/test-blob.js +++ b/test/parallel/test-blob.js @@ -480,3 +480,13 @@ assert.throws(() => new Blob({}), { assert.ok(blob.slice(0, 1).constructor === Blob); assert.ok(blob.slice(0, 1) instanceof Blob); } + +(async () => { + const blob = new Blob(['hello']); + + assert.ok(structuredClone(blob).constructor === Blob); + assert.ok(structuredClone(blob) instanceof Blob); + assert.ok(structuredClone(blob).size === blob.size); + assert.ok(structuredClone(blob).size === blob.size); + assert.ok((await structuredClone(blob).text()) === (await blob.text())); +})().then(common.mustCall());