Skip to content

Commit

Permalink
Use native Buffer whenever available
Browse files Browse the repository at this point in the history
  • Loading branch information
appurva21 committed Aug 27, 2024
1 parent ae8fe4c commit 27f2641
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 2 deletions.
2 changes: 1 addition & 1 deletion lib/environment.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ module.exports = {
util: { preferBuiltin: true, glob: true },
stream: { preferBuiltin: true, glob: true },
string_decoder: { preferBuiltin: true, glob: true },
buffer: { resolve: 'buffer/index.js', expose: 'buffer', glob: true },
buffer: { resolve: '../vendor/buffer.js', expose: 'buffer', glob: true },
url: { preferBuiltin: true, glob: true },
punycode: { preferBuiltin: true, glob: true },
querystring: { preferBuiltin: true, glob: true },
Expand Down
7 changes: 7 additions & 0 deletions lib/sandbox/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@
// Setup Timerz before we delete the global timers
require('./timers');

// Require buffer to make sure it's available in the sandbox
// Browserify statically analyses the usage of buffers and only
// injects Buffer into the scope if it's used. `Buffer` injected
// by browserify is part of the functional scope and does not get
// deleted when we mutate the global scope below.
require('buffer');

// Although we execute the user code in a well-defined scope using the uniscope
// module but still to cutoff the reference to the globally available properties
// we sanitize the global scope by deleting the forbidden properties in this UVM
Expand Down
37 changes: 37 additions & 0 deletions lib/vendor/buffer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
const buffer = require('buffer/');

// Use the native Buffer when available.
// This is done to get better performance when operating on large buffers
// For example, when converting a large buffer to a string, the native Buffer
// is much faster than the isomorphic buffer and supports toString() operation on
// buffers of size up to 512MB , while the isomorphic buffer errors out after 100MB.
if (typeof globalThis.Buffer === 'function') {
const isomorphicBuffer = buffer.Buffer;
const nativeBuffer = globalThis.Buffer;

// For node v20 and above `new Buffer()` or `Buffer()` syntax
// does not work. Patching it here for backward compatibility
buffer.Buffer = function () {
return new isomorphicBuffer(...arguments);
};

// Ensure the exposed Buffer has same interface as the isomorphicBuffer Buffer
for (const key in isomorphicBuffer) {
buffer.Buffer[key] = nativeBuffer[key];
}

// Ensure `instanceof` works for buffers created using the `new Buffer()` or `Buffer()` syntax
Object.defineProperty(buffer.Buffer, Symbol.hasInstance, {
value: function (instance) {
return instance instanceof nativeBuffer || instance instanceof isomorphicBuffer;
}
});

// Extend `isBuffer` for buffers created using the `new Buffer()` or `Buffer()` syntax
buffer.Buffer.isBuffer = function (obj) {
return nativeBuffer.isBuffer(obj) || isomorphicBuffer.isBuffer(obj);
};
}

module.exports = buffer;

51 changes: 50 additions & 1 deletion test/unit/sandbox-libraries/buffer.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,12 +114,61 @@ describe('sandbox library - buffer', function () {
context.execute(`
var assert = require('assert'),
buf1 = new Buffer('buffer'),
buf2 = new Buffer(buf1);
buf2 = new Buffer(buf1),
buf3 = Buffer(1);
buf1[0] = 0x61;
buf3[0] = 0x61;
assert.strictEqual(buf1.toString(), 'auffer');
assert.strictEqual(buf2.toString(), 'buffer');
assert.strictEqual(buf3.toString(), 'a');
`, done);
});

it('should be able to detect Buffer instances using isBuffer', function (done) {
context.execute(`
var assert = require('assert'),
buffer = require('buffer'),
bufUsingFrom = Buffer.from('test'),
bufUsingNew = new Buffer('test'),
buf = Buffer(1);
assert.strictEqual(Buffer.isBuffer(bufUsingFrom), true);
assert.strictEqual(Buffer.isBuffer(bufUsingNew), true);
assert.strictEqual(Buffer.isBuffer(buf), true);
`, done);
});

it('should be able to detect Buffer instances using Symbol.hasInstance', function (done) {
context.execute(`
var assert = require('assert'),
buffer = require('buffer'),
bufUsingFrom = Buffer.from('test'),
bufUsingNew = new Buffer('test');
buf = Buffer(1);
assert.strictEqual(bufUsingFrom instanceof Buffer, true);
assert.strictEqual(bufUsingNew instanceof Buffer, true);
assert.strictEqual(buf instanceof Buffer, true);
`, done);
});

it('should be able to convert large buffer to string', function (done) {
// For native buffer, the max string length is ~512MB
// For browser buffer, the max string length is ~100MB
const SIZE = (window ? 100 : 512) * 1024 * 1024;

context.execute(`
var assert = require('assert'),
buffer = require('buffer'),
// Max string length is ~512MB
buf = Buffer.alloc(${SIZE}, 'a');
assert.strictEqual(buf.toString().length, ${SIZE});
`, done);
});
});

0 comments on commit 27f2641

Please sign in to comment.