From f3b770bb3846a74ef8adec432ff62ec4ef8a9ecd Mon Sep 17 00:00:00 2001 From: Sean Reece Date: Tue, 6 Aug 2024 14:34:41 -0400 Subject: [PATCH 1/8] Initial commit --- src/objectid.ts | 7 +++++++ src/utils/node_byte_utils.ts | 2 +- test/node/object_id.test.ts | 8 +++++++- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/objectid.ts b/src/objectid.ts index c3b8c615e..3b8413a9e 100644 --- a/src/objectid.ts +++ b/src/objectid.ts @@ -212,6 +212,11 @@ export class ObjectId extends BSONValue { return this.id; } + /** ObjectId bytes @internal */ + get buffer(): Uint8Array { + return this.id; + } + /** * The ObjectId bytes * @readonly @@ -283,6 +288,8 @@ export class ObjectId extends BSONValue { * Generate a 12 byte id buffer used in ObjectId's * * @param time - pass in a second based timestamp. + * @param buffer - Optionally pass in a buffer instance. + * @param offset - Optionally pass in a buffer offset. */ static generate(time?: number): Uint8Array; /** diff --git a/src/utils/node_byte_utils.ts b/src/utils/node_byte_utils.ts index ba9ffd5db..90d612c7c 100644 --- a/src/utils/node_byte_utils.ts +++ b/src/utils/node_byte_utils.ts @@ -6,7 +6,7 @@ type NodeJsEncoding = 'base64' | 'hex' | 'utf8' | 'binary'; type NodeJsBuffer = ArrayBufferView & Uint8Array & { write(string: string, offset: number, length: undefined, encoding: 'utf8'): number; - copy(target: Uint8Array, targetStart: number, sourceStart: number, sourceEnd: number): number; + copy(target: Uint8Array, targetStart: number, sourceStart?: number, sourceEnd?: number): number; toString: (this: Uint8Array, encoding: NodeJsEncoding, start?: number, end?: number) => string; equals: (this: Uint8Array, other: Uint8Array) => boolean; swap32: (this: NodeJsBuffer) => NodeJsBuffer; diff --git a/test/node/object_id.test.ts b/test/node/object_id.test.ts index f9f1b5290..bd6f8f370 100644 --- a/test/node/object_id.test.ts +++ b/test/node/object_id.test.ts @@ -4,6 +4,9 @@ import * as util from 'util'; import { expect } from 'chai'; import { bufferFromHexArray } from './tools/utils'; import { isBufferOrUint8Array } from './tools/utils'; +import { _pool, _offset } from '../../src/objectid'; + +ObjectId.poolSize = 100; describe('ObjectId', function () { describe('static createFromTime()', () => { @@ -539,9 +542,12 @@ describe('ObjectId', function () { let equalId = { _bsontype: 'ObjectId', [oidKId]: oid.id }; const propAccessRecord: string[] = []; + equalId = new Proxy(equalId, { get(target, prop: string, recv) { - if (prop !== '_bsontype') { + if (typeof prop === 'symbol') { + propAccessRecord.push((prop as symbol).toString()); + } else if (prop !== '_bsontype') { propAccessRecord.push(prop); } return Reflect.get(target, prop, recv); From 2f3ece489d06e1816fd2d3fe3fe3177812dddf7f Mon Sep 17 00:00:00 2001 From: Sean Reece Date: Wed, 14 Aug 2024 23:42:53 -0400 Subject: [PATCH 2/8] Improvements --- src/utils/node_byte_utils.ts | 2 +- test/node/object_id.test.ts | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/utils/node_byte_utils.ts b/src/utils/node_byte_utils.ts index 90d612c7c..ba9ffd5db 100644 --- a/src/utils/node_byte_utils.ts +++ b/src/utils/node_byte_utils.ts @@ -6,7 +6,7 @@ type NodeJsEncoding = 'base64' | 'hex' | 'utf8' | 'binary'; type NodeJsBuffer = ArrayBufferView & Uint8Array & { write(string: string, offset: number, length: undefined, encoding: 'utf8'): number; - copy(target: Uint8Array, targetStart: number, sourceStart?: number, sourceEnd?: number): number; + copy(target: Uint8Array, targetStart: number, sourceStart: number, sourceEnd: number): number; toString: (this: Uint8Array, encoding: NodeJsEncoding, start?: number, end?: number) => string; equals: (this: Uint8Array, other: Uint8Array) => boolean; swap32: (this: NodeJsBuffer) => NodeJsBuffer; diff --git a/test/node/object_id.test.ts b/test/node/object_id.test.ts index bd6f8f370..7d72a236b 100644 --- a/test/node/object_id.test.ts +++ b/test/node/object_id.test.ts @@ -4,10 +4,16 @@ import * as util from 'util'; import { expect } from 'chai'; import { bufferFromHexArray } from './tools/utils'; import { isBufferOrUint8Array } from './tools/utils'; -import { _pool, _offset } from '../../src/objectid'; ObjectId.poolSize = 100; +declare module '../register-bson' { + interface ObjectId { + pool: Uint8Array; + offset: number; + } +} + describe('ObjectId', function () { describe('static createFromTime()', () => { it('creates an objectId with user defined value in the timestamp field', function () { @@ -545,9 +551,7 @@ describe('ObjectId', function () { equalId = new Proxy(equalId, { get(target, prop: string, recv) { - if (typeof prop === 'symbol') { - propAccessRecord.push((prop as symbol).toString()); - } else if (prop !== '_bsontype') { + if (prop !== '_bsontype') { propAccessRecord.push(prop); } return Reflect.get(target, prop, recv); From 4b773e6035dddc068f25e1eb3f41aea5276c4ba8 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Thu, 12 Dec 2024 10:30:57 -0500 Subject: [PATCH 3/8] Disable pool and improve perf for poolSize=1 --- src/objectid.ts | 2 +- test/node/object_id.test.ts | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/objectid.ts b/src/objectid.ts index 3b8413a9e..a6ef274dd 100644 --- a/src/objectid.ts +++ b/src/objectid.ts @@ -35,7 +35,7 @@ function incrementPool(): void { let PROCESS_UNIQUE: Uint8Array | null = null; /** ObjectId hexString cache @internal */ -const __idCache = new WeakMap(); // TODO(NODE-6549): convert this to #__id private field when target updated to ES2022 +const __idCache = new WeakMap(); // TODO(NODE-6549): convert this to #__id private field when target updated to ES2022 /** @public */ export interface ObjectIdLike { diff --git a/test/node/object_id.test.ts b/test/node/object_id.test.ts index 7d72a236b..9ea4c19d3 100644 --- a/test/node/object_id.test.ts +++ b/test/node/object_id.test.ts @@ -548,7 +548,6 @@ describe('ObjectId', function () { let equalId = { _bsontype: 'ObjectId', [oidKId]: oid.id }; const propAccessRecord: string[] = []; - equalId = new Proxy(equalId, { get(target, prop: string, recv) { if (prop !== '_bsontype') { From 30beb4427e7a78631b99ce1b4767b713a0699f11 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Thu, 12 Dec 2024 11:24:15 -0500 Subject: [PATCH 4/8] fix: use weakmap private --- src/objectid.ts | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/objectid.ts b/src/objectid.ts index a6ef274dd..e8a475d7c 100644 --- a/src/objectid.ts +++ b/src/objectid.ts @@ -36,6 +36,8 @@ let PROCESS_UNIQUE: Uint8Array | null = null; /** ObjectId hexString cache @internal */ const __idCache = new WeakMap(); // TODO(NODE-6549): convert this to #__id private field when target updated to ES2022 +const __pool = new WeakMap(); // TODO(NODE-6549): convert this to #pool private field when target updated to ES2022 +const __offset = new WeakMap(); // TODO(NODE-6549): convert this to #offset private field when target updated to ES2022 /** @public */ export interface ObjectIdLike { @@ -76,12 +78,20 @@ export class ObjectId extends BSONValue { } /** ObjectId buffer pool pointer @internal */ - private pool: Uint8Array; - /** Buffer pool offset @internal */ - private offset?: number; + private get pool(): Uint8Array { + return __pool.get(this) as Uint8Array; + } + private set pool(value: Uint8Array) { + __pool.set(this, value); + } - /** ObjectId hexString cache @internal */ - private __id?: string; + /** Buffer pool offset @internal */ + private get offset(): number | undefined { + return __offset.get(this); + } + private set offset(value: number) { + __offset.set(this, value); + } /** * Create ObjectId from a number. From 951207c935adedcd169dd16150eafc8c4825bbf6 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Thu, 12 Dec 2024 11:45:37 -0500 Subject: [PATCH 5/8] test: use before and after to set poolSize --- test/node/object_id.test.ts | 226 +++++++++++++++++------------------- 1 file changed, 107 insertions(+), 119 deletions(-) diff --git a/test/node/object_id.test.ts b/test/node/object_id.test.ts index 9ea4c19d3..9e2b33fac 100644 --- a/test/node/object_id.test.ts +++ b/test/node/object_id.test.ts @@ -267,91 +267,121 @@ describe('ObjectId', function () { ).to.be.true; }); - it('should correctly use buffer pool for ObjectId creation', function () { - const oldPoolSize = ObjectId.poolSize; - ObjectId.poolSize = 2; - const obj = new ObjectId(); - const obj2 = new ObjectId(); - - expect(obj.offset).to.equal(0); - expect(obj2.offset).to.equal(12); - expect(obj.offset).to.not.equal(obj2.offset); - expect(obj.pool).to.equal(obj2.pool); - - expect(obj.id).to.not.equal(obj2.id); - ObjectId.poolSize = oldPoolSize; - }); - - it('should respect buffer pool size for ObjectId creation', function () { - const oldPoolSize = ObjectId.poolSize; - ObjectId.poolSize = 2; - const test = new ObjectId(); - // Must fill current (large) pool first - const num = (test.pool.byteLength - test.offset) / 12; - for (let i = 0; i < num + 1; i++) { - new ObjectId(); - } + it('ObjectId.poolSize should be 1 by default', function () { + expect(ObjectId.poolSize).to.equal(1); + }); - const obj = new ObjectId(); - const obj2 = new ObjectId(); - const obj3 = new ObjectId(); - - expect(obj.offset).to.equal(0); - expect(obj2.offset).to.equal(12); - expect(obj3.offset).to.equal(0); - expect(obj.pool).to.equal(obj2.pool); - expect(obj2.pool).to.not.equal(obj3.pool); - - expect(obj.id).to.not.equal(obj2.id); - expect(obj2.id).to.not.equal(obj3.id); - ObjectId.poolSize = oldPoolSize; - }); - - it('should allow poolSize of 1', function () { - const oldPoolSize = ObjectId.poolSize; - ObjectId.poolSize = 1; - const test = new ObjectId(); - // Must fill current (large) pool first - const num = (test.pool.byteLength - test.offset) / 12; - for (let i = 0; i < num + 1; i++) { - new ObjectId(); - } + describe('when poolSize is greater than 1', function () { + let oldPoolSize; + before(function () { + oldPoolSize = ObjectId.poolSize; + ObjectId.poolSize = 2; + }); + + after(function () { + ObjectId.poolSize = oldPoolSize; + }); + + it('should correctly use buffer pool for ObjectId creation', function () { + const obj = new ObjectId(); + const obj2 = new ObjectId(); + + expect(obj.offset).to.equal(0); + expect(obj2.offset).to.equal(12); + expect(obj.offset).to.not.equal(obj2.offset); + expect(obj.pool).to.equal(obj2.pool); + + expect(obj.id).to.not.equal(obj2.id); + }); + + it('should respect buffer pool size for ObjectId creation', function () { + const oldPoolSize = ObjectId.poolSize; + ObjectId.poolSize = 2; + const test = new ObjectId(); + // Must fill current (large) pool first + const num = (test.pool.byteLength - test.offset) / 12; + for (let i = 0; i < num + 1; i++) { + new ObjectId(); + } - const obj = new ObjectId(); - const obj2 = new ObjectId(); - const obj3 = new ObjectId(); + const obj = new ObjectId(); + const obj2 = new ObjectId(); + const obj3 = new ObjectId(); - expect(obj.offset).to.equal(undefined); - expect(obj2.offset).to.equal(undefined); - expect(obj3.offset).to.equal(undefined); - expect(obj.pool).to.not.equal(obj2.pool); - expect(obj2.pool).to.not.equal(obj3.pool); + expect(obj.offset).to.equal(0); + expect(obj2.offset).to.equal(12); + expect(obj3.offset).to.equal(0); + expect(obj.pool).to.equal(obj2.pool); + expect(obj2.pool).to.not.equal(obj3.pool); - expect(obj.id).to.not.equal(obj2.id); - expect(obj2.id).to.not.equal(obj3.id); - ObjectId.poolSize = oldPoolSize; + expect(obj.id).to.not.equal(obj2.id); + expect(obj2.id).to.not.equal(obj3.id); + ObjectId.poolSize = oldPoolSize; + }); }); - it('should default to poolSize = 1 when invalid poolSize set', function () { - const oldPoolSize = ObjectId.poolSize; + describe('when poolSize is 1', function () { + let oldPoolSize; + before(function () { + oldPoolSize = ObjectId.poolSize; + ObjectId.poolSize = 1; + }); - ObjectId.poolSize = 0; - expect(ObjectId.poolSize).to.equal(1); - ObjectId.poolSize = -1; - expect(ObjectId.poolSize).to.equal(1); - ObjectId.poolSize = 0n; - expect(ObjectId.poolSize).to.equal(1); - ObjectId.poolSize = ''; - expect(ObjectId.poolSize).to.equal(1); - ObjectId.poolSize = NaN; - expect(ObjectId.poolSize).to.equal(1); - ObjectId.poolSize = {}; - expect(ObjectId.poolSize).to.equal(1); - ObjectId.poolSize = false; - expect(ObjectId.poolSize).to.equal(1); - ObjectId.poolSize = '1'; + after(function () { + ObjectId.poolSize = oldPoolSize; + }); + + it('should allow poolSize of 1', function () { + const test = new ObjectId(); + // Must fill current (large) pool first + const num = (test.pool.byteLength - test.offset) / 12; + for (let i = 0; i < num + 1; i++) { + new ObjectId(); + } + + const obj = new ObjectId(); + const obj2 = new ObjectId(); + const obj3 = new ObjectId(); + + expect(obj.offset).to.equal(undefined); + expect(obj2.offset).to.equal(undefined); + expect(obj3.offset).to.equal(undefined); + expect(obj.pool).to.not.equal(obj2.pool); + expect(obj2.pool).to.not.equal(obj3.pool); + + expect(obj.id).to.not.equal(obj2.id); + expect(obj2.id).to.not.equal(obj3.id); + }); + }); + + describe('when poolSize is modified', function () { + let oldPoolSize; + beforeEach(function () { + oldPoolSize = ObjectId.poolSize; + ObjectId.poolSize = 1; + }); - ObjectId.poolSize = oldPoolSize; + afterEach(function () { + ObjectId.poolSize = oldPoolSize; + }); + + it('should default to poolSize = 1 when invalid poolSize set', function () { + ObjectId.poolSize = 0; + expect(ObjectId.poolSize).to.equal(1); + ObjectId.poolSize = -1; + expect(ObjectId.poolSize).to.equal(1); + ObjectId.poolSize = 0n; + expect(ObjectId.poolSize).to.equal(1); + ObjectId.poolSize = ''; + expect(ObjectId.poolSize).to.equal(1); + ObjectId.poolSize = NaN; + expect(ObjectId.poolSize).to.equal(1); + ObjectId.poolSize = {}; + expect(ObjectId.poolSize).to.equal(1); + ObjectId.poolSize = false; + expect(ObjectId.poolSize).to.equal(1); + ObjectId.poolSize = '1'; + }); }); it('should throw error if non-12 byte non-24 hex string passed in', function () { @@ -543,48 +573,6 @@ describe('ObjectId', function () { expect(oid.toString()).to.not.equal(equalId.toString()); expect(oid.equals(equalId)).to.be.true; }); - - it('should use otherId[kId] Buffer for equality when otherId has _bsontype === ObjectId', () => { - let equalId = { _bsontype: 'ObjectId', [oidKId]: oid.id }; - - const propAccessRecord: string[] = []; - equalId = new Proxy(equalId, { - get(target, prop: string, recv) { - if (prop !== '_bsontype') { - propAccessRecord.push(prop); - } - return Reflect.get(target, prop, recv); - } - }); - - expect(oid.equals(equalId)).to.be.true; - // once for the 11th byte shortcut - // once for the total equality - expect(propAccessRecord).to.deep.equal(['pool', oidKId, oidKId]); - }); - - it('should use otherId[kId] Pool for equality when otherId has _bsontype === ObjectId when using pool', () => { - const oldPoolSize = ObjectId.poolSize; - ObjectId.poolSize = 2; - const oid = new ObjectId(oidString); - let equalId = new ObjectId(oidString); - - const propAccessRecord: string[] = []; - equalId = new Proxy(equalId, { - get(target, prop: string, recv) { - if (prop !== '_bsontype') { - propAccessRecord.push(prop); - } - return Reflect.get(target, prop, recv); - } - }); - - expect(oid.equals(equalId)).to.be.true; - // once for the 11th byte shortcut - // once for the total equality - expect(propAccessRecord).to.contain('pool').contain('offset'); - ObjectId.poolSize = oldPoolSize; - }); }); context('createFromHexString()', () => { From be3b64606ff754a4d73cadb744396dde1d796a62 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Thu, 12 Dec 2024 16:01:01 -0500 Subject: [PATCH 6/8] refactor: encapsulate pooling helpers --- src/objectid.ts | 74 +++++++++++++++++++------------------ test/node/object_id.test.ts | 9 ----- 2 files changed, 39 insertions(+), 44 deletions(-) diff --git a/src/objectid.ts b/src/objectid.ts index e8a475d7c..1aaa41c88 100644 --- a/src/objectid.ts +++ b/src/objectid.ts @@ -4,32 +4,39 @@ import { type InspectFn, defaultInspect } from './parser/utils'; import { ByteUtils } from './utils/byte_utils'; import { NumberUtils } from './utils/number_utils'; -// Settings for ObjectId Buffer pool -// Disable pool by default in order to ensure compatibility -// Specify larger poolSize to enable pool -let currentPool: Uint8Array | null = null; -let poolSize = 1; // Disable pool by default. -let currentPoolOffset = 0; +const ObjectIdPooling: { + currentPool: Uint8Array | null; + poolSize: number; + currentPoolOffset: number; + getPool(): { readonly currentPool: Uint8Array; readonly currentPoolOffset: number }; + incrementPool(): void; +} = { + // Settings for ObjectId Buffer pool + // Disable pool by default in order to ensure compatibility + // Specify larger poolSize to enable pool + currentPool: null, + poolSize: 1, // Disable pool by default. + currentPoolOffset: 0, + /** + * Retrieves a ObjectId pool and offset. This function may create a new ObjectId buffer pool and reset the pool offset + * @internal + */ + getPool() { + if (!this.currentPool || this.currentPoolOffset + 12 > this.currentPool.length) { + this.currentPool = ByteUtils.allocateUnsafe(this.poolSize * 12); + this.currentPoolOffset = 0; + } + return { currentPool: this.currentPool, currentPoolOffset: this.currentPoolOffset } as const; + }, -/** - * Retrieves a ObjectId pool and offset. This function may create a new ObjectId buffer pool and reset the pool offset - * @internal - */ -function getPool(): [Uint8Array, number] { - if (!currentPool || currentPoolOffset + 12 > currentPool.length) { - currentPool = ByteUtils.allocateUnsafe(poolSize * 12); - currentPoolOffset = 0; + /** + * Increments the pool offset by 12 bytes + * @internal + */ + incrementPool(): void { + this.currentPoolOffset += 12; } - return [currentPool, currentPoolOffset]; -} - -/** - * Increments the pool offset by 12 bytes - * @internal - */ -function incrementPool(): void { - currentPoolOffset += 12; -} +}; // Unique sequence for the current process (initialized on first use) let PROCESS_UNIQUE: Uint8Array | null = null; @@ -70,11 +77,11 @@ export class ObjectId extends BSONValue { * The size of the current ObjectId buffer pool. */ static get poolSize(): number { - return poolSize; + return ObjectIdPooling.poolSize; } static set poolSize(size: number) { - poolSize = Math.max(Math.abs(Number(size)) >>> 0, 1); + ObjectIdPooling.poolSize = Math.max(Math.abs(Number(size)) >>> 0, 1); } /** ObjectId buffer pool pointer @internal */ @@ -168,11 +175,13 @@ export class ObjectId extends BSONValue { let offset: number; // Special case when poolSize === 1 and a 12 byte buffer is passed in - just persist buffer - if (poolSize === 1 && ArrayBuffer.isView(workingId) && workingId.length === 12) { + if (ObjectId.poolSize === 1 && ArrayBuffer.isView(workingId) && workingId.length === 12) { pool = ByteUtils.toLocalBufferType(workingId); offset = 0; } else { - [pool, offset] = getPool(); + const currentPool = ObjectIdPooling.getPool(); + pool = currentPool.currentPool; + offset = currentPool.currentPoolOffset; // The following cases use workingId to construct an ObjectId if (workingId == null || typeof workingId === 'number') { @@ -211,15 +220,10 @@ export class ObjectId extends BSONValue { // Increment pool offset once we have completed initialization this.pool = pool; // Only set offset if pool is used - if (poolSize > 1) { + if (ObjectId.poolSize > 1) { this.offset = offset; } - incrementPool(); - } - - /** ObjectId bytes @internal */ - get buffer(): Uint8Array { - return this.id; + ObjectIdPooling.incrementPool(); } /** ObjectId bytes @internal */ diff --git a/test/node/object_id.test.ts b/test/node/object_id.test.ts index 9e2b33fac..09f545b6a 100644 --- a/test/node/object_id.test.ts +++ b/test/node/object_id.test.ts @@ -5,15 +5,6 @@ import { expect } from 'chai'; import { bufferFromHexArray } from './tools/utils'; import { isBufferOrUint8Array } from './tools/utils'; -ObjectId.poolSize = 100; - -declare module '../register-bson' { - interface ObjectId { - pool: Uint8Array; - offset: number; - } -} - describe('ObjectId', function () { describe('static createFromTime()', () => { it('creates an objectId with user defined value in the timestamp field', function () { From 129038f679cc5fcb0462f6a19b19c0d9e119f83b Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Fri, 13 Dec 2024 13:34:36 -0500 Subject: [PATCH 7/8] refactor: remove WeakMap private pool and offset properties --- src/objectid.ts | 19 ++----------------- test/node/object_id.test.ts | 12 ++++++++++++ 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/src/objectid.ts b/src/objectid.ts index 1aaa41c88..15ec130fd 100644 --- a/src/objectid.ts +++ b/src/objectid.ts @@ -43,8 +43,6 @@ let PROCESS_UNIQUE: Uint8Array | null = null; /** ObjectId hexString cache @internal */ const __idCache = new WeakMap(); // TODO(NODE-6549): convert this to #__id private field when target updated to ES2022 -const __pool = new WeakMap(); // TODO(NODE-6549): convert this to #pool private field when target updated to ES2022 -const __offset = new WeakMap(); // TODO(NODE-6549): convert this to #offset private field when target updated to ES2022 /** @public */ export interface ObjectIdLike { @@ -84,21 +82,8 @@ export class ObjectId extends BSONValue { ObjectIdPooling.poolSize = Math.max(Math.abs(Number(size)) >>> 0, 1); } - /** ObjectId buffer pool pointer @internal */ - private get pool(): Uint8Array { - return __pool.get(this) as Uint8Array; - } - private set pool(value: Uint8Array) { - __pool.set(this, value); - } - - /** Buffer pool offset @internal */ - private get offset(): number | undefined { - return __offset.get(this); - } - private set offset(value: number) { - __offset.set(this, value); - } + private pool: Uint8Array; + private offset: number | undefined; /** * Create ObjectId from a number. diff --git a/test/node/object_id.test.ts b/test/node/object_id.test.ts index 09f545b6a..6a8da7e13 100644 --- a/test/node/object_id.test.ts +++ b/test/node/object_id.test.ts @@ -309,6 +309,12 @@ describe('ObjectId', function () { expect(obj2.id).to.not.equal(obj3.id); ObjectId.poolSize = oldPoolSize; }); + + it('two identical ObjectId values are not deep equal', function () { + const oid0 = new ObjectId('00'.repeat(12)); + const oid1 = new ObjectId('00'.repeat(12)); + expect(oid0).to.not.deep.equal(oid1); + }); }); describe('when poolSize is 1', function () { @@ -343,6 +349,12 @@ describe('ObjectId', function () { expect(obj.id).to.not.equal(obj2.id); expect(obj2.id).to.not.equal(obj3.id); }); + + it('two identical ObjectId values are deep equal', function () { + const oid0 = new ObjectId('00'.repeat(12)); + const oid1 = new ObjectId('00'.repeat(12)); + expect(oid0).to.deep.equal(oid1); + }); }); describe('when poolSize is modified', function () { From 04317bd1abc1f88d9f588932d356853209083d95 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Tue, 7 Jan 2025 15:13:05 -0500 Subject: [PATCH 8/8] chore: don't change src --- src/objectid.ts | 80 +++++++++++++++++++++++-------------------------- 1 file changed, 37 insertions(+), 43 deletions(-) diff --git a/src/objectid.ts b/src/objectid.ts index 15ec130fd..c3b8c615e 100644 --- a/src/objectid.ts +++ b/src/objectid.ts @@ -4,45 +4,38 @@ import { type InspectFn, defaultInspect } from './parser/utils'; import { ByteUtils } from './utils/byte_utils'; import { NumberUtils } from './utils/number_utils'; -const ObjectIdPooling: { - currentPool: Uint8Array | null; - poolSize: number; - currentPoolOffset: number; - getPool(): { readonly currentPool: Uint8Array; readonly currentPoolOffset: number }; - incrementPool(): void; -} = { - // Settings for ObjectId Buffer pool - // Disable pool by default in order to ensure compatibility - // Specify larger poolSize to enable pool - currentPool: null, - poolSize: 1, // Disable pool by default. - currentPoolOffset: 0, - /** - * Retrieves a ObjectId pool and offset. This function may create a new ObjectId buffer pool and reset the pool offset - * @internal - */ - getPool() { - if (!this.currentPool || this.currentPoolOffset + 12 > this.currentPool.length) { - this.currentPool = ByteUtils.allocateUnsafe(this.poolSize * 12); - this.currentPoolOffset = 0; - } - return { currentPool: this.currentPool, currentPoolOffset: this.currentPoolOffset } as const; - }, +// Settings for ObjectId Buffer pool +// Disable pool by default in order to ensure compatibility +// Specify larger poolSize to enable pool +let currentPool: Uint8Array | null = null; +let poolSize = 1; // Disable pool by default. +let currentPoolOffset = 0; - /** - * Increments the pool offset by 12 bytes - * @internal - */ - incrementPool(): void { - this.currentPoolOffset += 12; +/** + * Retrieves a ObjectId pool and offset. This function may create a new ObjectId buffer pool and reset the pool offset + * @internal + */ +function getPool(): [Uint8Array, number] { + if (!currentPool || currentPoolOffset + 12 > currentPool.length) { + currentPool = ByteUtils.allocateUnsafe(poolSize * 12); + currentPoolOffset = 0; } -}; + return [currentPool, currentPoolOffset]; +} + +/** + * Increments the pool offset by 12 bytes + * @internal + */ +function incrementPool(): void { + currentPoolOffset += 12; +} // Unique sequence for the current process (initialized on first use) let PROCESS_UNIQUE: Uint8Array | null = null; /** ObjectId hexString cache @internal */ -const __idCache = new WeakMap(); // TODO(NODE-6549): convert this to #__id private field when target updated to ES2022 +const __idCache = new WeakMap(); // TODO(NODE-6549): convert this to #__id private field when target updated to ES2022 /** @public */ export interface ObjectIdLike { @@ -75,15 +68,20 @@ export class ObjectId extends BSONValue { * The size of the current ObjectId buffer pool. */ static get poolSize(): number { - return ObjectIdPooling.poolSize; + return poolSize; } static set poolSize(size: number) { - ObjectIdPooling.poolSize = Math.max(Math.abs(Number(size)) >>> 0, 1); + poolSize = Math.max(Math.abs(Number(size)) >>> 0, 1); } + /** ObjectId buffer pool pointer @internal */ private pool: Uint8Array; - private offset: number | undefined; + /** Buffer pool offset @internal */ + private offset?: number; + + /** ObjectId hexString cache @internal */ + private __id?: string; /** * Create ObjectId from a number. @@ -160,13 +158,11 @@ export class ObjectId extends BSONValue { let offset: number; // Special case when poolSize === 1 and a 12 byte buffer is passed in - just persist buffer - if (ObjectId.poolSize === 1 && ArrayBuffer.isView(workingId) && workingId.length === 12) { + if (poolSize === 1 && ArrayBuffer.isView(workingId) && workingId.length === 12) { pool = ByteUtils.toLocalBufferType(workingId); offset = 0; } else { - const currentPool = ObjectIdPooling.getPool(); - pool = currentPool.currentPool; - offset = currentPool.currentPoolOffset; + [pool, offset] = getPool(); // The following cases use workingId to construct an ObjectId if (workingId == null || typeof workingId === 'number') { @@ -205,10 +201,10 @@ export class ObjectId extends BSONValue { // Increment pool offset once we have completed initialization this.pool = pool; // Only set offset if pool is used - if (ObjectId.poolSize > 1) { + if (poolSize > 1) { this.offset = offset; } - ObjectIdPooling.incrementPool(); + incrementPool(); } /** ObjectId bytes @internal */ @@ -287,8 +283,6 @@ export class ObjectId extends BSONValue { * Generate a 12 byte id buffer used in ObjectId's * * @param time - pass in a second based timestamp. - * @param buffer - Optionally pass in a buffer instance. - * @param offset - Optionally pass in a buffer offset. */ static generate(time?: number): Uint8Array; /**