@@ -4,6 +4,25 @@ import { type InspectFn, defaultInspect } from './parser/utils';
44import { ByteUtils } from './utils/byte_utils' ;
55import { NumberUtils } from './utils/number_utils' ;
66
7+ const defaultPoolSize = 1000 ; // Hold 1000 ObjectId buffers in a pool
8+ let pool : Uint8Array ;
9+ let poolOffset = 0 ;
10+
11+ /** Internal pool accessors for objectId instance */
12+ /** @internal private instance pool accessor */
13+ export const _pool = Symbol ( 'pool' ) ;
14+ /** @internal private instance offset accessor */
15+ export const _offset = Symbol ( 'offset' ) ;
16+
17+ /**
18+ * Create a new ObjectId buffer pool and reset the pool offset
19+ * @internal
20+ */
21+ function createPool ( ) : void {
22+ pool = ByteUtils . allocateUnsafe ( ObjectId . poolSize * 12 ) ;
23+ poolOffset = 0 ;
24+ }
25+
726// Regular expression that checks for hex value
827const checkForHexRegExp = new RegExp ( '^[0-9a-fA-F]{24}$' ) ;
928
@@ -37,8 +56,16 @@ export class ObjectId extends BSONValue {
3756
3857 static cacheHexString : boolean ;
3958
40- /** ObjectId Bytes @internal */
41- private buffer ! : Uint8Array ;
59+ /**
60+ * The size of the buffer pool for ObjectId.
61+ */
62+ static poolSize : number = defaultPoolSize ;
63+
64+ /** ObjectId buffer pool pointer @internal */
65+ private [ _pool ] : Uint8Array ;
66+ /** Buffer pool offset @internal */
67+ private [ _offset ] : number ;
68+
4269 /** ObjectId hexString cache @internal */
4370 private __id ?: string ;
4471
@@ -72,7 +99,7 @@ export class ObjectId extends BSONValue {
7299 *
73100 * @param inputId - A 12 byte binary Buffer.
74101 */
75- constructor ( inputId : Uint8Array ) ;
102+ constructor ( inputId : Uint8Array , inputIndex ?: number ) ;
76103 /** To generate a new ObjectId, use ObjectId() with no argument. */
77104 constructor ( ) ;
78105 /**
@@ -86,7 +113,10 @@ export class ObjectId extends BSONValue {
86113 *
87114 * @param inputId - An input value to create a new ObjectId from.
88115 */
89- constructor ( inputId ?: string | number | ObjectId | ObjectIdLike | Uint8Array ) {
116+ constructor (
117+ inputId ?: string | number | ObjectId | ObjectIdLike | Uint8Array ,
118+ inputIndex ?: number
119+ ) {
90120 super ( ) ;
91121 // workingId is set based on type of input and whether valid id exists for the input
92122 let workingId ;
@@ -103,17 +133,38 @@ export class ObjectId extends BSONValue {
103133 workingId = inputId ;
104134 }
105135
136+ // If we have reached the end of the pool then create a new pool
137+ if ( ! pool || poolOffset + 12 > pool . byteLength ) {
138+ createPool ( ) ;
139+ }
140+ this [ _pool ] = pool ;
141+ this [ _offset ] = poolOffset ;
142+ poolOffset += 12 ;
143+
106144 // The following cases use workingId to construct an ObjectId
107145 if ( workingId == null || typeof workingId === 'number' ) {
108146 // The most common use case (blank id, new objectId instance)
109147 // Generate a new id
110- this . buffer = ObjectId . generate ( typeof workingId === 'number' ? workingId : undefined ) ;
111- } else if ( ArrayBuffer . isView ( workingId ) && workingId . byteLength === 12 ) {
112- // If intstanceof matches we can escape calling ensure buffer in Node.js environments
113- this . buffer = ByteUtils . toLocalBufferType ( workingId ) ;
148+ ObjectId . generate (
149+ typeof workingId === 'number' ? workingId : undefined ,
150+ this [ _pool ] ,
151+ this [ _offset ]
152+ ) ;
153+ } else if ( ArrayBuffer . isView ( workingId ) ) {
154+ if ( workingId . byteLength !== 12 && typeof inputIndex !== 'number' ) {
155+ throw new BSONError ( 'Buffer length must be 12 or offset must be specified' ) ;
156+ }
157+ if (
158+ inputIndex &&
159+ ( typeof inputIndex !== 'number' || inputIndex < 0 || workingId . byteLength < inputIndex + 12 )
160+ ) {
161+ throw new BSONError ( 'Buffer offset must be a non-negative number less than buffer length' ) ;
162+ }
163+ inputIndex ??= 0 ;
164+ for ( let i = 0 ; i < 12 ; i ++ ) this [ _pool ] [ this [ _offset ] + i ] = workingId [ inputIndex + i ] ;
114165 } else if ( typeof workingId === 'string' ) {
115166 if ( workingId . length === 24 && checkForHexRegExp . test ( workingId ) ) {
116- this . buffer = ByteUtils . fromHex ( workingId ) ;
167+ this [ _pool ] . set ( ByteUtils . fromHex ( workingId ) , this [ _offset ] ) ;
117168 } else {
118169 throw new BSONError (
119170 'input must be a 24 character hex string, 12 byte Uint8Array, or an integer'
@@ -124,20 +175,28 @@ export class ObjectId extends BSONValue {
124175 }
125176 // If we are caching the hex string
126177 if ( ObjectId . cacheHexString ) {
127- this . __id = ByteUtils . toHex ( this . id ) ;
178+ this . __id = ByteUtils . toHex ( this [ _pool ] , this [ _offset ] , this [ _offset ] + 12 ) ;
128179 }
129180 }
130181
182+ /** ObjectId bytes @internal */
183+ get buffer ( ) : Uint8Array {
184+ return this . id ;
185+ }
186+
131187 /**
132188 * The ObjectId bytes
133189 * @readonly
134190 */
135191 get id ( ) : Uint8Array {
136- return this . buffer ;
192+ return this [ _pool ] . subarray ( this [ _offset ] , this [ _offset ] + 12 ) ;
137193 }
138194
139195 set id ( value : Uint8Array ) {
140- this . buffer = value ;
196+ if ( value . byteLength !== 12 ) {
197+ throw new BSONError ( 'input must be a 12 byte Uint8Array' ) ;
198+ }
199+ this [ _pool ] . set ( value , this [ _offset ] ) ;
141200 if ( ObjectId . cacheHexString ) {
142201 this . __id = ByteUtils . toHex ( value ) ;
143202 }
@@ -149,7 +208,7 @@ export class ObjectId extends BSONValue {
149208 return this . __id ;
150209 }
151210
152- const hexString = ByteUtils . toHex ( this . id ) ;
211+ const hexString = ByteUtils . toHex ( this [ _pool ] , this [ _offset ] , this [ _offset ] + 12 ) ;
153212
154213 if ( ObjectId . cacheHexString && ! this . __id ) {
155214 this . __id = hexString ;
@@ -170,34 +229,38 @@ export class ObjectId extends BSONValue {
170229 * Generate a 12 byte id buffer used in ObjectId's
171230 *
172231 * @param time - pass in a second based timestamp.
232+ * @param buffer - Optionally pass in a buffer instance.
233+ * @param offset - Optionally pass in a buffer offset.
173234 */
174- static generate ( time ?: number ) : Uint8Array {
235+ static generate ( time ?: number , buffer ?: Uint8Array , offset : number = 0 ) : Uint8Array {
175236 if ( 'number' !== typeof time ) {
176237 time = Math . floor ( Date . now ( ) / 1000 ) ;
177238 }
178239
179240 const inc = ObjectId . getInc ( ) ;
180- const buffer = ByteUtils . allocateUnsafe ( 12 ) ;
241+ if ( ! buffer ) {
242+ buffer = ByteUtils . allocateUnsafe ( 12 ) ;
243+ }
181244
182245 // 4-byte timestamp
183- NumberUtils . setInt32BE ( buffer , 0 , time ) ;
246+ NumberUtils . setInt32BE ( buffer , offset , time ) ;
184247
185248 // set PROCESS_UNIQUE if yet not initialized
186249 if ( PROCESS_UNIQUE === null ) {
187250 PROCESS_UNIQUE = ByteUtils . randomBytes ( 5 ) ;
188251 }
189252
190253 // 5-byte process unique
191- buffer [ 4 ] = PROCESS_UNIQUE [ 0 ] ;
192- buffer [ 5 ] = PROCESS_UNIQUE [ 1 ] ;
193- buffer [ 6 ] = PROCESS_UNIQUE [ 2 ] ;
194- buffer [ 7 ] = PROCESS_UNIQUE [ 3 ] ;
195- buffer [ 8 ] = PROCESS_UNIQUE [ 4 ] ;
254+ buffer [ offset + 4 ] = PROCESS_UNIQUE [ 0 ] ;
255+ buffer [ offset + 5 ] = PROCESS_UNIQUE [ 1 ] ;
256+ buffer [ offset + 6 ] = PROCESS_UNIQUE [ 2 ] ;
257+ buffer [ offset + 7 ] = PROCESS_UNIQUE [ 3 ] ;
258+ buffer [ offset + 8 ] = PROCESS_UNIQUE [ 4 ] ;
196259
197260 // 3-byte counter
198- buffer [ 11 ] = inc & 0xff ;
199- buffer [ 10 ] = ( inc >> 8 ) & 0xff ;
200- buffer [ 9 ] = ( inc >> 16 ) & 0xff ;
261+ buffer [ offset + 11 ] = inc & 0xff ;
262+ buffer [ offset + 10 ] = ( inc >> 8 ) & 0xff ;
263+ buffer [ offset + 9 ] = ( inc >> 16 ) & 0xff ;
201264
202265 return buffer ;
203266 }
@@ -239,9 +302,16 @@ export class ObjectId extends BSONValue {
239302 }
240303
241304 if ( ObjectId . is ( otherId ) ) {
242- return (
243- this . buffer [ 11 ] === otherId . buffer [ 11 ] && ByteUtils . equals ( this . buffer , otherId . buffer )
244- ) ;
305+ if ( otherId [ _pool ] && typeof otherId [ _offset ] === 'number' ) {
306+ for ( let i = 11 ; i >= 0 ; i -- ) {
307+ if ( this [ _pool ] [ this [ _offset ] + i ] !== otherId [ _pool ] [ otherId [ _offset ] + i ] ) {
308+ return false ;
309+ }
310+ }
311+ return true ;
312+ }
313+ // If otherId does not have pool and offset, fallback to buffer comparison for compatibility
314+ return ByteUtils . equals ( this . buffer , otherId . buffer ) ;
245315 }
246316
247317 if ( typeof otherId === 'string' ) {
@@ -260,7 +330,7 @@ export class ObjectId extends BSONValue {
260330 /** Returns the generation date (accurate up to the second) that this ID was generated. */
261331 getTimestamp ( ) : Date {
262332 const timestamp = new Date ( ) ;
263- const time = NumberUtils . getUint32BE ( this . buffer , 0 ) ;
333+ const time = NumberUtils . getUint32BE ( this [ _pool ] , this [ _offset ] ) ;
264334 timestamp . setTime ( Math . floor ( time ) * 1000 ) ;
265335 return timestamp ;
266336 }
@@ -272,18 +342,18 @@ export class ObjectId extends BSONValue {
272342
273343 /** @internal */
274344 serializeInto ( uint8array : Uint8Array , index : number ) : 12 {
275- uint8array [ index ] = this . buffer [ 0 ] ;
276- uint8array [ index + 1 ] = this . buffer [ 1 ] ;
277- uint8array [ index + 2 ] = this . buffer [ 2 ] ;
278- uint8array [ index + 3 ] = this . buffer [ 3 ] ;
279- uint8array [ index + 4 ] = this . buffer [ 4 ] ;
280- uint8array [ index + 5 ] = this . buffer [ 5 ] ;
281- uint8array [ index + 6 ] = this . buffer [ 6 ] ;
282- uint8array [ index + 7 ] = this . buffer [ 7 ] ;
283- uint8array [ index + 8 ] = this . buffer [ 8 ] ;
284- uint8array [ index + 9 ] = this . buffer [ 9 ] ;
285- uint8array [ index + 10 ] = this . buffer [ 10 ] ;
286- uint8array [ index + 11 ] = this . buffer [ 11 ] ;
345+ uint8array [ index ] = this [ _pool ] [ this [ _offset ] ] ;
346+ uint8array [ index + 1 ] = this [ _pool ] [ this [ _offset ] + 1 ] ;
347+ uint8array [ index + 2 ] = this [ _pool ] [ this [ _offset ] + 2 ] ;
348+ uint8array [ index + 3 ] = this [ _pool ] [ this [ _offset ] + 3 ] ;
349+ uint8array [ index + 4 ] = this [ _pool ] [ this [ _offset ] + 4 ] ;
350+ uint8array [ index + 5 ] = this [ _pool ] [ this [ _offset ] + 5 ] ;
351+ uint8array [ index + 6 ] = this [ _pool ] [ this [ _offset ] + 6 ] ;
352+ uint8array [ index + 7 ] = this [ _pool ] [ this [ _offset ] + 7 ] ;
353+ uint8array [ index + 8 ] = this [ _pool ] [ this [ _offset ] + 8 ] ;
354+ uint8array [ index + 9 ] = this [ _pool ] [ this [ _offset ] + 9 ] ;
355+ uint8array [ index + 10 ] = this [ _pool ] [ this [ _offset ] + 10 ] ;
356+ uint8array [ index + 11 ] = this [ _pool ] [ this [ _offset ] + 11 ] ;
287357 return 12 ;
288358 }
289359
@@ -293,7 +363,7 @@ export class ObjectId extends BSONValue {
293363 * @param time - an integer number representing a number of seconds.
294364 */
295365 static createFromTime ( time : number ) : ObjectId {
296- const buffer = ByteUtils . allocate ( 12 ) ;
366+ const buffer = ByteUtils . allocateUnsafe ( 12 ) ;
297367 for ( let i = 11 ; i >= 4 ; i -- ) buffer [ i ] = 0 ;
298368 // Encode time into first 4 bytes
299369 NumberUtils . setInt32BE ( buffer , 0 , time ) ;
0 commit comments