diff --git a/.gitignore b/.gitignore index a7aaaa0a..2b92723e 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,5 @@ coverage/ *.d.ts *.tgz docs/public + +*.0x diff --git a/etc/benchmarks/main.mjs b/etc/benchmarks/main.mjs new file mode 100644 index 00000000..e7b96a4e --- /dev/null +++ b/etc/benchmarks/main.mjs @@ -0,0 +1,95 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ +import { performance } from 'perf_hooks'; +import { readFile } from 'fs/promises'; +import { cpus, totalmem } from 'os'; + +const hw = cpus(); +const ram = totalmem() / 1024 ** 3; +const platform = { name: hw[0].model, cores: hw.length, ram: `${ram}GB` }; +const ITERATIONS = 1_000_000; + +const systemInfo = [ + `\n- cpu: ${platform.name}`, + `- cores: ${platform.cores}`, + `- os: ${process.platform}`, + `- ram: ${platform.ram}`, + `- iterations: ${ITERATIONS.toLocaleString()}` +].join('\n'); + +const readJSONFile = async path => + JSON.parse(await readFile(new URL(path, import.meta.url), { encoding: 'utf8' })); + +function average(array) { + let sum = 0; + for (const value of array) sum += value; + return sum / array.length; +} + +function testPerformance(fn, iterations = ITERATIONS) { + let measurements = []; + for (let i = 0; i < iterations; i++) { + const start = performance.now(); + fn(i); + const end = performance.now(); + measurements.push(end - start); + } + return average(measurements).toFixed(8); +} + +async function main() { + const [currentBSON, currentReleaseBSON, legacyBSONLib] = await Promise.all([ + (async () => ({ + lib: await import('../../lib/bson.js'), + version: 'current local' + }))(), + (async () => ({ + lib: await import('../../node_modules/bson_latest/lib/bson.js'), + version: (await readJSONFile('../../node_modules/bson_latest/package.json')).version + }))(), + (async () => { + const legacyBSON = (await import('../../node_modules/bson_legacy/index.js')).default; + return { + lib: { ...legacyBSON, ...legacyBSON.prototype }, + version: (await readJSONFile('../../node_modules/bson_legacy/package.json')).version + }; + })() + ]).catch(error => { + console.error(error); + console.error( + `Please run:\n${[ + 'npm run build', + 'npm install --no-save bson_legacy@npm:bson@1 bson_latest@npm:bson@latest' + ].join('\n')}` + ); + process.exit(1); + }); + + const documents = Array.from({ length: ITERATIONS }, () => + currentReleaseBSON.lib.serialize({ + _id: new currentReleaseBSON.lib.ObjectId(), + field1: 'value1' + }) + ); + + console.log(systemInfo); + + for (const bson of [currentBSON, currentReleaseBSON, legacyBSONLib]) { + console.log(`\nBSON@${bson.version}`); + console.log( + `deserialize({ oid, string }, { validation: { utf8: false } }) takes ${testPerformance(i => + bson.lib.deserialize(documents[i], { validation: { utf8: false } }) + )}ms on average` + ); + + const oidBuffer = Buffer.from('00'.repeat(12), 'hex'); + console.log( + `new Oid(buf) take ${testPerformance(() => new bson.lib.ObjectId(oidBuffer))}ms on average` + ); + } + + console.log(); +} + +main() + .then(() => null) + .catch(error => console.error(error)); diff --git a/package-lock.json b/package-lock.json index 9092267b..5b2e331a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "packages": { "": { "name": "bson", - "version": "4.6.2", + "version": "4.6.3", "license": "Apache-2.0", "dependencies": { "buffer": "^5.6.0" @@ -3020,6 +3020,35 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, + "node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "dev": true, + "engines": [ + "node >= 6.0" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/concat-stream/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/connect": { "version": "3.7.0", "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", @@ -3312,35 +3341,6 @@ "node": ">=10" } }, - "node_modules/conventional-recommended-bump/node_modules/concat-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", - "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", - "dev": true, - "engines": [ - "node >= 6.0" - ], - "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.0.2", - "typedarray": "^0.0.6" - } - }, - "node_modules/conventional-recommended-bump/node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/convert-source-map": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", @@ -4884,15 +4884,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/get-pkg-repo/node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true, - "engines": { - "node": ">=0.4" - } - }, "node_modules/get-stdin": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", @@ -9478,6 +9469,15 @@ } } }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, + "engines": { + "node": ">=0.4" + } + }, "node_modules/y18n": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", @@ -11700,6 +11700,31 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, + "concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, "connect": { "version": "3.7.0", "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", @@ -11929,31 +11954,6 @@ "git-semver-tags": "^4.1.1", "meow": "^8.0.0", "q": "^1.5.1" - }, - "dependencies": { - "concat-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", - "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.0.2", - "typedarray": "^0.0.6" - } - }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } } }, "convert-source-map": { @@ -13118,12 +13118,6 @@ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", "dev": true - }, - "xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true } } }, @@ -16626,6 +16620,12 @@ "dev": true, "requires": {} }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true + }, "y18n": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", diff --git a/src/objectid.ts b/src/objectid.ts index e612effd..21e1b2e0 100644 --- a/src/objectid.ts +++ b/src/objectid.ts @@ -72,7 +72,8 @@ export class ObjectId { // Generate a new id this[kId] = ObjectId.generate(typeof workingId === 'number' ? workingId : undefined); } else if (ArrayBuffer.isView(workingId) && workingId.byteLength === 12) { - this[kId] = ensureBuffer(workingId); + // If intstanceof matches we can escape calling ensure buffer in Node.js environments + this[kId] = workingId instanceof Buffer ? workingId : ensureBuffer(workingId); } else if (typeof workingId === 'string') { if (workingId.length === 12) { const bytes = Buffer.from(workingId); diff --git a/test/node/object_id_tests.js b/test/node/object_id_tests.js index ff974324..e34a1fbd 100644 --- a/test/node/object_id_tests.js +++ b/test/node/object_id_tests.js @@ -383,4 +383,17 @@ describe('ObjectId', function () { expect(propAccessRecord).to.deep.equal([oidKId, oidKId]); }); }); + + it('should return the same instance if a buffer is passed in', function () { + const inBuffer = Buffer.from('00'.repeat(12), 'hex'); + + const outBuffer = new ObjectId(inBuffer); + + // instance equality + expect(inBuffer).to.equal(outBuffer.id); + // deep equality + expect(inBuffer).to.deep.equal(outBuffer.id); + // class method equality + expect(Buffer.prototype.equals.call(inBuffer, outBuffer.id)).to.be.true; + }); }); diff --git a/test/node/tools/utils.js b/test/node/tools/utils.js index b628ec4f..788c04ea 100644 --- a/test/node/tools/utils.js +++ b/test/node/tools/utils.js @@ -1,3 +1,4 @@ +/* globals window */ 'use strict'; exports.assertArrayEqual = function (array1, array2) {