Skip to content

Commit

Permalink
lib: faster blob cloning
Browse files Browse the repository at this point in the history
  • Loading branch information
H4ad committed Oct 10, 2023
1 parent 78a1570 commit 7638ed1
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 12 deletions.
7 changes: 5 additions & 2 deletions benchmark/blob/clone.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
22 changes: 12 additions & 10 deletions lib/internal/blob.js
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ class Blob {
const length = this[kLength];
return {
data: { handle, type, length },
deserializeInfo: 'internal/blob:ClonedBlob',
deserializeInfo: 'internal/blob:CloneableBlob',
};
}

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -489,7 +491,7 @@ function createBlobFromFilePath(path, options) {

module.exports = {
Blob,
ClonedBlob,
CloneableBlob,
createBlob,
createBlobFromFilePath,
isBlob,
Expand Down
10 changes: 10 additions & 0 deletions test/parallel/test-blob.js
Original file line number Diff line number Diff line change
Expand Up @@ -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());

0 comments on commit 7638ed1

Please sign in to comment.