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 29, 2024
1 parent 1553e61 commit 37ed816
Show file tree
Hide file tree
Showing 7 changed files with 216 additions and 2 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
unreleased:
chores:
- Add support for configuring module resolver based on environment
new features:
- Enhanced performance when operating on buffers in Node environment

5.1.1:
date: 2024-08-01
Expand Down
7 changes: 6 additions & 1 deletion lib/environment.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@ 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/index.js',
resolveBrowser: '../vendor/buffer/index.browser.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
57 changes: 57 additions & 0 deletions lib/vendor/buffer/buffer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
function SpecificBuffer (_Buffer) {
const Buffer = function () {
if (typeof arguments[0] === 'number') {
return _Buffer.alloc(...arguments);
}

return _Buffer.from(...arguments);
}

Buffer.poolSize = _Buffer.poolSize;

Object.defineProperty(Buffer, Symbol.hasInstance, {
value: function (instance) {
return instance instanceof _Buffer;
}
});

Buffer.isBuffer = function () {
return _Buffer.isBuffer(...arguments);
}

Buffer.alloc = function () {
return _Buffer.alloc(...arguments);
}

Buffer.allocUnsafe = function () {
return _Buffer.allocUnsafe(...arguments);
}

Buffer.allocUnsafeSlow = function () {
return _Buffer.allocUnsafeSlow(...arguments);
}

Buffer.from = function () {
return _Buffer.from(...arguments);
}

Buffer.compare = function () {
return _Buffer.compare(...arguments);
}

Buffer.isEncoding = function () {
return _Buffer.isEncoding(...arguments);
}

Buffer.concat = function () {
return _Buffer.concat(...arguments);
}

Buffer.byteLength = function () {
return _Buffer.byteLength(...arguments);
}

return Buffer;
}

module.exports = SpecificBuffer;
9 changes: 9 additions & 0 deletions lib/vendor/buffer/index.browser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const SpecificBuffer = require('./buffer');
const buffer = require('buffer/');

module.exports = {
Buffer: SpecificBuffer(buffer.Buffer),
SlowBuffer: buffer.SlowBuffer,
INSPECT_MAX_BYTES: buffer.INSPECT_MAX_BYTES,
kMaxLength: buffer.kMaxLength
}
9 changes: 9 additions & 0 deletions lib/vendor/buffer/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const SpecificBuffer = require('./buffer');
const buffer = globalThis._nodeRequires.buffer;

module.exports = {
Buffer: SpecificBuffer(globalThis.Buffer),
SlowBuffer: buffer.SlowBuffer,
INSPECT_MAX_BYTES: buffer.INSPECT_MAX_BYTES,
kMaxLength: buffer.kMaxLength
}
127 changes: 126 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,137 @@ 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(`
const assert = require('assert'),
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(`
const assert = require('assert'),
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 = (typeof window === 'undefined' ? 511 : 100) * 1024 * 1024;

context.execute(`
const assert = require('assert'),
buf = Buffer.alloc(${SIZE}, 'a');
assert.strictEqual(buf.toString().length, ${SIZE});
`, done);
});

it('should implement Buffer.compare', function (done) {
context.execute(`
const assert = require('assert'),
buf1 = Buffer.from('abc'),
buf2 = Buffer.from('abc'),
buf3 = Buffer.from('abd');
assert.strictEqual(Buffer.compare(buf1, buf2), 0);
assert.strictEqual(Buffer.compare(buf1, buf3), -1);
assert.strictEqual(Buffer.compare(buf3, buf1), 1);
`, done);
});

it('should implement Buffer.byteLength', function (done) {
context.execute(`
const assert = require('assert'),
buf = Buffer.from('abc');
assert.strictEqual(Buffer.byteLength(buf), 3);
`, done);
});

it('should implement Buffer.concat', function (done) {
context.execute(`
const assert = require('assert'),
buf1 = Buffer.from('abc'),
buf2 = Buffer.from('def');
assert.strictEqual(Buffer.concat([buf1, buf2]).toString(), 'abcdef');
`, done);
});

it('should implement Buffer.isEncoding', function (done) {
context.execute(`
const assert = require('assert');
assert.strictEqual(Buffer.isEncoding('utf8'), true);
assert.strictEqual(Buffer.isEncoding('hex'), true);
assert.strictEqual(Buffer.isEncoding('ascii'), true);
assert.strictEqual(Buffer.isEncoding('utf16le'), true);
assert.strictEqual(Buffer.isEncoding('ucs2'), true);
assert.strictEqual(Buffer.isEncoding('base64'), true);
assert.strictEqual(Buffer.isEncoding('binary'), true);
assert.strictEqual(Buffer.isEncoding('utf-8'), true);
assert.strictEqual(Buffer.isEncoding('utf/8'), false);
assert.strictEqual(Buffer.isEncoding(''), false);
`, done);
});

it('should expose Buffer.poolSize', function (done) {
context.execute(`
const assert = require('assert');
assert.strictEqual(typeof Buffer.poolSize, 'number');
`, done);
});

it('should expose SlowBuffer', function (done) {
context.execute(`
const assert = require('assert'),
buffer = require('buffer');
const buf = new buffer.SlowBuffer(10);
assert.strictEqual(buf.length, 10);
`, done);
});

it('should expose constants', function (done) {
context.execute(`
const assert = require('assert'),
buffer = require('buffer');
assert.strictEqual(typeof buffer.kMaxLength, 'number');
assert.strictEqual(typeof buffer.INSPECT_MAX_BYTES, 'number');
`, done);
});
});

0 comments on commit 37ed816

Please sign in to comment.