core: optimize bson objectid generation & add benchmarks

This commit is contained in:
Abdullah Atta
2023-10-09 11:11:42 +05:00
parent 94f7e8cd08
commit 07d2a701c7
5 changed files with 75 additions and 32 deletions

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
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);
});
});

View File

@@ -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",
@@ -2526,6 +2528,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",
@@ -3612,6 +3620,24 @@
"dev": true,
"license": "MIT"
},
"node_modules/nanoid": {
"version": "5.0.5",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.0.5.tgz",
"integrity": "sha512-/Veqm+QKsyMY3kqi4faWplnY1u+VuKO3dD2binyPIybP31DRO29bPF+1mszgLnrR2KqSLceFLBNw0zmvDzN1QQ==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"bin": {
"nanoid": "bin/nanoid.js"
},
"engines": {
"node": "^18 || >=20"
}
},
"node_modules/napi-build-utils": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz",

View File

@@ -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",

View File

@@ -17,35 +17,16 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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);
}

View File

@@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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();
}