From b647c1b480d6e1ac8ece93e6e191beb68768145c Mon Sep 17 00:00:00 2001 From: Abdullah Atta Date: Mon, 9 Oct 2023 11:11:42 +0500 Subject: [PATCH] core: optimize bson objectid generation & add benchmarks --- packages/core/__benches__/objectid.bench.ts | 37 +++++++++++++++++++++ packages/core/package-lock.json | 35 ++++++++++++++++--- packages/core/package.json | 2 ++ packages/core/src/utils/object-id.ts | 35 +++++-------------- packages/core/src/utils/random.ts | 7 ++-- 5 files changed, 80 insertions(+), 36 deletions(-) create mode 100644 packages/core/__benches__/objectid.bench.ts diff --git a/packages/core/__benches__/objectid.bench.ts b/packages/core/__benches__/objectid.bench.ts new file mode 100644 index 000000000..749958c0b --- /dev/null +++ b/packages/core/__benches__/objectid.bench.ts @@ -0,0 +1,37 @@ +/* +This file is part of the Notesnook project (https://notesnook.com/) + +Copyright (C) 2023 Streetwriters (Private) Limited + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +import { bench, describe } from "vitest"; +import { createObjectId } from "../src/utils/object-id"; +import boid from "bson-objectid"; +import { nanoid } from "nanoid"; + +describe("objectid", async () => { + bench("custom", () => { + createObjectId(); + }); + + bench("bson-objectid", () => { + boid().toHexString(); + }); + + bench("nanoid", () => { + nanoid(32); + }); +}); diff --git a/packages/core/package-lock.json b/packages/core/package-lock.json index 191a068cc..00eb65f00 100644 --- a/packages/core/package-lock.json +++ b/packages/core/package-lock.json @@ -44,6 +44,7 @@ "@vitest/coverage-v8": "^0.34.1", "abortcontroller-polyfill": "^1.7.3", "better-sqlite3": "^8.6.0", + "bson-objectid": "^2.0.4", "cross-env": "^7.0.3", "dotenv": "^16.0.1", "event-source-polyfill": "^1.0.31", @@ -52,6 +53,7 @@ "isomorphic-fetch": "^3.0.0", "jsdom": "^22.1.0", "mockdate": "^3.0.5", + "nanoid": "^5.0.1", "otplib": "^12.0.1", "refractor": "^4.8.1", "vitest": "^0.34.1", @@ -2086,6 +2088,12 @@ "concat-map": "0.0.1" } }, + "node_modules/bson-objectid": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/bson-objectid/-/bson-objectid-2.0.4.tgz", + "integrity": "sha512-vgnKAUzcDoa+AeyYwXCoHyF2q6u/8H46dxu5JN+4/TZeq/Dlinn0K6GvxsCLb3LHUJl0m/TLiEK31kUwtgocMQ==", + "dev": true + }, "node_modules/buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -3150,7 +3158,9 @@ "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.6", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.0.5.tgz", + "integrity": "sha512-/Veqm+QKsyMY3kqi4faWplnY1u+VuKO3dD2binyPIybP31DRO29bPF+1mszgLnrR2KqSLceFLBNw0zmvDzN1QQ==", "dev": true, "funding": [ { @@ -3158,12 +3168,11 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "bin": { - "nanoid": "bin/nanoid.cjs" + "nanoid": "bin/nanoid.js" }, "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + "node": "^18 || >=20" } }, "node_modules/napi-build-utils": { @@ -3394,6 +3403,24 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/postcss/node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, "node_modules/prebuild-install": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", diff --git a/packages/core/package.json b/packages/core/package.json index 0812d145c..583446b02 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -21,6 +21,7 @@ "@vitest/coverage-v8": "^0.34.1", "abortcontroller-polyfill": "^1.7.3", "better-sqlite3": "^8.6.0", + "bson-objectid": "^2.0.4", "cross-env": "^7.0.3", "dotenv": "^16.0.1", "event-source-polyfill": "^1.0.31", @@ -29,6 +30,7 @@ "isomorphic-fetch": "^3.0.0", "jsdom": "^22.1.0", "mockdate": "^3.0.5", + "nanoid": "^5.0.1", "otplib": "^12.0.1", "refractor": "^4.8.1", "vitest": "^0.34.1", diff --git a/packages/core/src/utils/object-id.ts b/packages/core/src/utils/object-id.ts index a9ebe4df9..2355a602c 100644 --- a/packages/core/src/utils/object-id.ts +++ b/packages/core/src/utils/object-id.ts @@ -17,35 +17,16 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -import { BufferPool } from "./buffer-pool"; import { randomBytes, randomInt } from "./random"; -const PROCESS_UNIQUE = randomBytes(5); +const PROCESS_UNIQUE = randomBytes(5).toString("hex"); let index = ~~(randomInt() * 0xffffff); - -const objectIdPool = new BufferPool(12); export function createObjectId(date = Date.now()): string { - const buffer = objectIdPool.alloc(); - index = (index + 1) % 0xffffff; - - const time = ~~(date / 1000); - - // 4-byte timestamp - new DataView(buffer.buffer, 0, 4).setUint32(0, time); - - // 5-byte process unique - buffer[4] = PROCESS_UNIQUE[0]; - buffer[5] = PROCESS_UNIQUE[1]; - buffer[6] = PROCESS_UNIQUE[2]; - buffer[7] = PROCESS_UNIQUE[3]; - buffer[8] = PROCESS_UNIQUE[4]; - - // 3-byte counter - buffer[11] = index & 0xff; - buffer[10] = (index >> 8) & 0xff; - buffer[9] = (index >> 16) & 0xff; - - const objectId = buffer.toString("hex"); - objectIdPool.free(buffer); - return objectId; + index++; + const time = Math.floor(date / 1000); + return time.toString(16) + PROCESS_UNIQUE + swap16(index).toString(16); +} + +function swap16(val: number) { + return ((val & 0xff) << 16) | (val & 0xff00) | ((val >> 16) & 0xff); } diff --git a/packages/core/src/utils/random.ts b/packages/core/src/utils/random.ts index d87b82c05..5099af5d7 100644 --- a/packages/core/src/utils/random.ts +++ b/packages/core/src/utils/random.ts @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -export function randomBytes(size: number) { +export function randomBytes(size: number): Buffer { if (!globalThis.crypto || !crypto) throw new Error("Crypto is not supported on this platform."); if ("randomBytes" in crypto && typeof crypto.randomBytes === "function") @@ -34,8 +34,5 @@ export function randomBytes(size: number) { } export function randomInt() { - const randomBuffer = randomBytes(1); - const randomNumber = randomBuffer[0] / 0xff; // / (0xffffffff + 1); - - return Math.floor(randomNumber * 0xffffff); + return randomBytes(4).readInt32BE(); }