bson 6.3.0 → 6.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -14,7 +14,7 @@
14
14
  "vendor"
15
15
  ],
16
16
  "types": "bson.d.ts",
17
- "version": "6.3.0",
17
+ "version": "6.4.1",
18
18
  "author": {
19
19
  "name": "The MongoDB NodeJS Team",
20
20
  "email": "dbx-node@mongodb.com"
@@ -27,39 +27,39 @@
27
27
  },
28
28
  "devDependencies": {
29
29
  "@istanbuljs/nyc-config-typescript": "^1.0.2",
30
- "@microsoft/api-extractor": "^7.38.4",
31
- "@octokit/core": "^4.2.4",
30
+ "@microsoft/api-extractor": "^7.40.5",
31
+ "@octokit/core": "^5.1.0",
32
32
  "@rollup/plugin-node-resolve": "^15.2.3",
33
- "@rollup/plugin-typescript": "^11.1.5",
33
+ "@rollup/plugin-typescript": "^11.1.6",
34
34
  "@types/chai": "^4.3.11",
35
35
  "@types/mocha": "^10.0.6",
36
- "@types/node": "^18.19.2",
37
- "@types/sinon": "^10.0.20",
36
+ "@types/node": "^20.11.19",
37
+ "@types/sinon": "^17.0.3",
38
38
  "@types/sinon-chai": "^3.2.12",
39
- "@typescript-eslint/eslint-plugin": "^5.62.0",
40
- "@typescript-eslint/parser": "^5.62.0",
39
+ "@typescript-eslint/eslint-plugin": "^7.0.2",
40
+ "@typescript-eslint/parser": "^7.0.2",
41
41
  "benchmark": "^2.1.4",
42
42
  "chai": "^4.3.10",
43
43
  "chalk": "^5.3.0",
44
44
  "dbx-js-tools": "github:mongodb-js/dbx-js-tools",
45
- "eslint": "^8.55.0",
46
- "eslint-config-prettier": "^8.10.0",
45
+ "eslint": "^8.56.0",
46
+ "eslint-config-prettier": "^9.1.0",
47
47
  "eslint-plugin-no-bigint-usage": "file:etc/eslint/no-bigint-usage",
48
- "eslint-plugin-prettier": "^4.2.1",
48
+ "eslint-plugin-prettier": "^5.1.3",
49
49
  "eslint-plugin-tsdoc": "^0.2.17",
50
- "magic-string": "^0.30.5",
51
- "mocha": "10.2.0",
50
+ "magic-string": "^0.30.7",
51
+ "mocha": "10.3.0",
52
52
  "node-fetch": "^3.3.2",
53
53
  "nyc": "^15.1.0",
54
- "prettier": "^2.8.8",
55
- "rollup": "^3.29.4",
56
- "sinon": "^15.2.0",
54
+ "prettier": "^3.2.5",
55
+ "rollup": "^4.12.0",
56
+ "sinon": "^17.0.1",
57
57
  "sinon-chai": "^3.7.0",
58
58
  "source-map-support": "^0.5.21",
59
59
  "standard-version": "^9.5.0",
60
60
  "tar": "^6.2.0",
61
- "ts-node": "^10.9.1",
62
- "tsd": "^0.28.1",
61
+ "ts-node": "^10.9.2",
62
+ "tsd": "^0.30.5",
63
63
  "typescript": "^5.0.4",
64
64
  "typescript-cached-transpile": "0.0.6",
65
65
  "uuid": "^9.0.1"
package/src/bson.ts CHANGED
@@ -16,6 +16,7 @@ import { BSONRegExp } from './regexp';
16
16
  import { BSONSymbol } from './symbol';
17
17
  import { Timestamp } from './timestamp';
18
18
  import { ByteUtils } from './utils/byte_utils';
19
+ import { NumberUtils } from './utils/number_utils';
19
20
  export type { UUIDExtended, BinaryExtended, BinaryExtendedLegacy, BinarySequence } from './binary';
20
21
  export type { CodeExtended } from './code';
21
22
  export type { DBRefLike } from './db_ref';
@@ -115,7 +116,7 @@ export function serialize(object: Document, options: SerializeOptions = {}): Uin
115
116
  );
116
117
 
117
118
  // Create the final buffer
118
- const finishedBuffer = ByteUtils.allocate(serializationIndex);
119
+ const finishedBuffer = ByteUtils.allocateUnsafe(serializationIndex);
119
120
 
120
121
  // Copy into the finished buffer
121
122
  finishedBuffer.set(buffer.subarray(0, serializationIndex), 0);
@@ -232,11 +233,7 @@ export function deserializeStream(
232
233
  // Loop over all documents
233
234
  for (let i = 0; i < numberOfDocuments; i++) {
234
235
  // Find size of the document
235
- const size =
236
- bufferData[index] |
237
- (bufferData[index + 1] << 8) |
238
- (bufferData[index + 2] << 16) |
239
- (bufferData[index + 3] << 24);
236
+ const size = NumberUtils.getInt32LE(bufferData, index);
240
237
  // Update options with index
241
238
  internalOptions.index = index;
242
239
  // Parse the document at this point
package/src/db_ref.ts CHANGED
@@ -51,7 +51,6 @@ export class DBRef extends BSONValue {
51
51
  const parts = collection.split('.');
52
52
  if (parts.length === 2) {
53
53
  db = parts.shift();
54
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
55
54
  collection = parts.shift()!;
56
55
  }
57
56
 
package/src/decimal128.ts CHANGED
@@ -591,7 +591,7 @@ export class Decimal128 extends BSONValue {
591
591
  }
592
592
 
593
593
  // Encode into a buffer
594
- const buffer = ByteUtils.allocate(16);
594
+ const buffer = ByteUtils.allocateUnsafe(16);
595
595
  index = 0;
596
596
 
597
597
  // Encode the low 64 bits of the decimal
package/src/objectid.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  import { BSONValue } from './bson_value';
2
2
  import { BSONError } from './error';
3
3
  import { type InspectFn, defaultInspect } from './parser/utils';
4
- import { BSONDataView, ByteUtils } from './utils/byte_utils';
4
+ import { ByteUtils } from './utils/byte_utils';
5
+ import { NumberUtils } from './utils/number_utils';
5
6
 
6
7
  // Regular expression that checks for hex value
7
8
  const checkForHexRegExp = new RegExp('^[0-9a-fA-F]{24}$');
@@ -21,8 +22,6 @@ export interface ObjectIdExtended {
21
22
  $oid: string;
22
23
  }
23
24
 
24
- const kId = Symbol('id');
25
-
26
25
  /**
27
26
  * A class representation of the BSON ObjectId type.
28
27
  * @public
@@ -39,7 +38,7 @@ export class ObjectId extends BSONValue {
39
38
  static cacheHexString: boolean;
40
39
 
41
40
  /** ObjectId Bytes @internal */
42
- private [kId]!: Uint8Array;
41
+ private buffer!: Uint8Array;
43
42
  /** ObjectId hexString cache @internal */
44
43
  private __id?: string;
45
44
 
@@ -108,13 +107,13 @@ export class ObjectId extends BSONValue {
108
107
  if (workingId == null || typeof workingId === 'number') {
109
108
  // The most common use case (blank id, new objectId instance)
110
109
  // Generate a new id
111
- this[kId] = ObjectId.generate(typeof workingId === 'number' ? workingId : undefined);
110
+ this.buffer = ObjectId.generate(typeof workingId === 'number' ? workingId : undefined);
112
111
  } else if (ArrayBuffer.isView(workingId) && workingId.byteLength === 12) {
113
112
  // If intstanceof matches we can escape calling ensure buffer in Node.js environments
114
- this[kId] = ByteUtils.toLocalBufferType(workingId);
113
+ this.buffer = ByteUtils.toLocalBufferType(workingId);
115
114
  } else if (typeof workingId === 'string') {
116
115
  if (workingId.length === 24 && checkForHexRegExp.test(workingId)) {
117
- this[kId] = ByteUtils.fromHex(workingId);
116
+ this.buffer = ByteUtils.fromHex(workingId);
118
117
  } else {
119
118
  throw new BSONError(
120
119
  'input must be a 24 character hex string, 12 byte Uint8Array, or an integer'
@@ -134,11 +133,11 @@ export class ObjectId extends BSONValue {
134
133
  * @readonly
135
134
  */
136
135
  get id(): Uint8Array {
137
- return this[kId];
136
+ return this.buffer;
138
137
  }
139
138
 
140
139
  set id(value: Uint8Array) {
141
- this[kId] = value;
140
+ this.buffer = value;
142
141
  if (ObjectId.cacheHexString) {
143
142
  this.__id = ByteUtils.toHex(value);
144
143
  }
@@ -178,10 +177,10 @@ export class ObjectId extends BSONValue {
178
177
  }
179
178
 
180
179
  const inc = ObjectId.getInc();
181
- const buffer = ByteUtils.allocate(12);
180
+ const buffer = ByteUtils.allocateUnsafe(12);
182
181
 
183
182
  // 4-byte timestamp
184
- BSONDataView.fromUint8Array(buffer).setUint32(0, time, false);
183
+ NumberUtils.setInt32BE(buffer, 0, time);
185
184
 
186
185
  // set PROCESS_UNIQUE if yet not initialized
187
186
  if (PROCESS_UNIQUE === null) {
@@ -240,7 +239,9 @@ export class ObjectId extends BSONValue {
240
239
  }
241
240
 
242
241
  if (ObjectId.is(otherId)) {
243
- return this[kId][11] === otherId[kId][11] && ByteUtils.equals(this[kId], otherId[kId]);
242
+ return (
243
+ this.buffer[11] === otherId.buffer[11] && ByteUtils.equals(this.buffer, otherId.buffer)
244
+ );
244
245
  }
245
246
 
246
247
  if (typeof otherId === 'string') {
@@ -259,7 +260,7 @@ export class ObjectId extends BSONValue {
259
260
  /** Returns the generation date (accurate up to the second) that this ID was generated. */
260
261
  getTimestamp(): Date {
261
262
  const timestamp = new Date();
262
- const time = BSONDataView.fromUint8Array(this.id).getUint32(0, false);
263
+ const time = NumberUtils.getUint32BE(this.buffer, 0);
263
264
  timestamp.setTime(Math.floor(time) * 1000);
264
265
  return timestamp;
265
266
  }
@@ -269,15 +270,33 @@ export class ObjectId extends BSONValue {
269
270
  return new ObjectId();
270
271
  }
271
272
 
273
+ /** @internal */
274
+ 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];
287
+ return 12;
288
+ }
289
+
272
290
  /**
273
291
  * Creates an ObjectId from a second based number, with the rest of the ObjectId zeroed out. Used for comparisons or sorting the ObjectId.
274
292
  *
275
293
  * @param time - an integer number representing a number of seconds.
276
294
  */
277
295
  static createFromTime(time: number): ObjectId {
278
- const buffer = ByteUtils.fromNumberArray([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
296
+ const buffer = ByteUtils.allocate(12);
297
+ for (let i = 11; i >= 4; i--) buffer[i] = 0;
279
298
  // Encode time into first 4 bytes
280
- BSONDataView.fromUint8Array(buffer).setUint32(0, time, false);
299
+ NumberUtils.setInt32BE(buffer, 0, time);
281
300
  // Return the new objectId
282
301
  return new ObjectId(buffer);
283
302
  }
@@ -14,7 +14,8 @@ import { ObjectId } from '../objectid';
14
14
  import { BSONRegExp } from '../regexp';
15
15
  import { BSONSymbol } from '../symbol';
16
16
  import { Timestamp } from '../timestamp';
17
- import { BSONDataView, ByteUtils } from '../utils/byte_utils';
17
+ import { ByteUtils } from '../utils/byte_utils';
18
+ import { NumberUtils } from '../utils/number_utils';
18
19
  import { validateUtf8 } from '../validate_utf8';
19
20
 
20
21
  /** @public */
@@ -91,11 +92,7 @@ export function internalDeserialize(
91
92
  options = options == null ? {} : options;
92
93
  const index = options && options.index ? options.index : 0;
93
94
  // Read the document size
94
- const size =
95
- buffer[index] |
96
- (buffer[index + 1] << 8) |
97
- (buffer[index + 2] << 16) |
98
- (buffer[index + 3] << 24);
95
+ const size = NumberUtils.getInt32LE(buffer, index);
99
96
 
100
97
  if (size < 5) {
101
98
  throw new BSONError(`bson size must be >= 5, is ${size}`);
@@ -164,7 +161,7 @@ function deserializeObject(
164
161
  // Reflects utf-8 validation setting regardless of global or specific key validation
165
162
  let validationSetting: boolean;
166
163
  // Set of keys either to enable or disable validation on
167
- const utf8KeysSet = new Set();
164
+ let utf8KeysSet;
168
165
 
169
166
  // Check for boolean uniformity and empty validation option
170
167
  const utf8ValidatedKeys = validation.utf8;
@@ -190,6 +187,8 @@ function deserializeObject(
190
187
 
191
188
  // Add keys to set that will either be validated or not based on validationSetting
192
189
  if (!globalUTFValidation) {
190
+ utf8KeysSet = new Set();
191
+
193
192
  for (const key of Object.keys(utf8ValidatedKeys)) {
194
193
  utf8KeysSet.add(key);
195
194
  }
@@ -202,8 +201,8 @@ function deserializeObject(
202
201
  if (buffer.length < 5) throw new BSONError('corrupt bson message < 5 bytes long');
203
202
 
204
203
  // Read the document size
205
- const size =
206
- buffer[index++] | (buffer[index++] << 8) | (buffer[index++] << 16) | (buffer[index++] << 24);
204
+ const size = NumberUtils.getInt32LE(buffer, index);
205
+ index += 4;
207
206
 
208
207
  // Ensure buffer is valid size
209
208
  if (size < 5 || size > buffer.length) throw new BSONError('corrupt bson message');
@@ -217,7 +216,6 @@ function deserializeObject(
217
216
  let isPossibleDBRef = isArray ? false : null;
218
217
 
219
218
  // While we have more left data left keep parsing
220
- const dataview = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
221
219
  while (!done) {
222
220
  // Read the type
223
221
  const elementType = buffer[index++];
@@ -240,7 +238,7 @@ function deserializeObject(
240
238
 
241
239
  // shouldValidateKey is true if the key should be validated, false otherwise
242
240
  let shouldValidateKey = true;
243
- if (globalUTFValidation || utf8KeysSet.has(name)) {
241
+ if (globalUTFValidation || utf8KeysSet?.has(name)) {
244
242
  shouldValidateKey = validationSetting;
245
243
  } else {
246
244
  shouldValidateKey = !validationSetting;
@@ -254,11 +252,8 @@ function deserializeObject(
254
252
  index = i + 1;
255
253
 
256
254
  if (elementType === constants.BSON_DATA_STRING) {
257
- const stringSize =
258
- buffer[index++] |
259
- (buffer[index++] << 8) |
260
- (buffer[index++] << 16) |
261
- (buffer[index++] << 24);
255
+ const stringSize = NumberUtils.getInt32LE(buffer, index);
256
+ index += 4;
262
257
  if (
263
258
  stringSize <= 0 ||
264
259
  stringSize > buffer.length - index ||
@@ -269,37 +264,25 @@ function deserializeObject(
269
264
  value = ByteUtils.toUTF8(buffer, index, index + stringSize - 1, shouldValidateKey);
270
265
  index = index + stringSize;
271
266
  } else if (elementType === constants.BSON_DATA_OID) {
272
- const oid = ByteUtils.allocate(12);
273
- oid.set(buffer.subarray(index, index + 12));
267
+ const oid = ByteUtils.allocateUnsafe(12);
268
+ for (let i = 0; i < 12; i++) oid[i] = buffer[index + i];
274
269
  value = new ObjectId(oid);
275
270
  index = index + 12;
276
271
  } else if (elementType === constants.BSON_DATA_INT && promoteValues === false) {
277
- value = new Int32(
278
- buffer[index++] | (buffer[index++] << 8) | (buffer[index++] << 16) | (buffer[index++] << 24)
279
- );
272
+ value = new Int32(NumberUtils.getInt32LE(buffer, index));
273
+ index += 4;
280
274
  } else if (elementType === constants.BSON_DATA_INT) {
281
- value =
282
- buffer[index++] |
283
- (buffer[index++] << 8) |
284
- (buffer[index++] << 16) |
285
- (buffer[index++] << 24);
286
- } else if (elementType === constants.BSON_DATA_NUMBER && promoteValues === false) {
287
- value = new Double(dataview.getFloat64(index, true));
288
- index = index + 8;
275
+ value = NumberUtils.getInt32LE(buffer, index);
276
+ index += 4;
289
277
  } else if (elementType === constants.BSON_DATA_NUMBER) {
290
- value = dataview.getFloat64(index, true);
291
- index = index + 8;
278
+ value = NumberUtils.getFloat64LE(buffer, index);
279
+ index += 8;
280
+ if (promoteValues === false) value = new Double(value);
292
281
  } else if (elementType === constants.BSON_DATA_DATE) {
293
- const lowBits =
294
- buffer[index++] |
295
- (buffer[index++] << 8) |
296
- (buffer[index++] << 16) |
297
- (buffer[index++] << 24);
298
- const highBits =
299
- buffer[index++] |
300
- (buffer[index++] << 8) |
301
- (buffer[index++] << 16) |
302
- (buffer[index++] << 24);
282
+ const lowBits = NumberUtils.getInt32LE(buffer, index);
283
+ const highBits = NumberUtils.getInt32LE(buffer, index + 4);
284
+ index += 8;
285
+
303
286
  value = new Date(new Long(lowBits, highBits).toNumber());
304
287
  } else if (elementType === constants.BSON_DATA_BOOLEAN) {
305
288
  if (buffer[index] !== 0 && buffer[index] !== 1)
@@ -307,11 +290,8 @@ function deserializeObject(
307
290
  value = buffer[index++] === 1;
308
291
  } else if (elementType === constants.BSON_DATA_OBJECT) {
309
292
  const _index = index;
310
- const objectSize =
311
- buffer[index] |
312
- (buffer[index + 1] << 8) |
313
- (buffer[index + 2] << 16) |
314
- (buffer[index + 3] << 24);
293
+ const objectSize = NumberUtils.getInt32LE(buffer, index);
294
+
315
295
  if (objectSize <= 0 || objectSize > buffer.length - index)
316
296
  throw new BSONError('bad embedded document length in bson');
317
297
 
@@ -329,11 +309,7 @@ function deserializeObject(
329
309
  index = index + objectSize;
330
310
  } else if (elementType === constants.BSON_DATA_ARRAY) {
331
311
  const _index = index;
332
- const objectSize =
333
- buffer[index] |
334
- (buffer[index + 1] << 8) |
335
- (buffer[index + 2] << 16) |
336
- (buffer[index + 3] << 24);
312
+ const objectSize = NumberUtils.getInt32LE(buffer, index);
337
313
  let arrayOptions: DeserializeOptions = options;
338
314
 
339
315
  // Stop index
@@ -357,46 +333,38 @@ function deserializeObject(
357
333
  } else if (elementType === constants.BSON_DATA_NULL) {
358
334
  value = null;
359
335
  } else if (elementType === constants.BSON_DATA_LONG) {
360
- // Unpack the low and high bits
361
- const dataview = BSONDataView.fromUint8Array(buffer.subarray(index, index + 8));
362
-
363
- const lowBits =
364
- buffer[index++] |
365
- (buffer[index++] << 8) |
366
- (buffer[index++] << 16) |
367
- (buffer[index++] << 24);
368
- const highBits =
369
- buffer[index++] |
370
- (buffer[index++] << 8) |
371
- (buffer[index++] << 16) |
372
- (buffer[index++] << 24);
373
- const long = new Long(lowBits, highBits);
374
336
  if (useBigInt64) {
375
- value = dataview.getBigInt64(0, true);
376
- } else if (promoteLongs && promoteValues === true) {
377
- // Promote the long if possible
378
- value =
379
- long.lessThanOrEqual(JS_INT_MAX_LONG) && long.greaterThanOrEqual(JS_INT_MIN_LONG)
380
- ? long.toNumber()
381
- : long;
337
+ value = NumberUtils.getBigInt64LE(buffer, index);
338
+ index += 8;
382
339
  } else {
383
- value = long;
340
+ // Unpack the low and high bits
341
+ const lowBits = NumberUtils.getInt32LE(buffer, index);
342
+ const highBits = NumberUtils.getInt32LE(buffer, index + 4);
343
+ index += 8;
344
+
345
+ const long = new Long(lowBits, highBits);
346
+ // Promote the long if possible
347
+ if (promoteLongs && promoteValues === true) {
348
+ value =
349
+ long.lessThanOrEqual(JS_INT_MAX_LONG) && long.greaterThanOrEqual(JS_INT_MIN_LONG)
350
+ ? long.toNumber()
351
+ : long;
352
+ } else {
353
+ value = long;
354
+ }
384
355
  }
385
356
  } else if (elementType === constants.BSON_DATA_DECIMAL128) {
386
357
  // Buffer to contain the decimal bytes
387
- const bytes = ByteUtils.allocate(16);
358
+ const bytes = ByteUtils.allocateUnsafe(16);
388
359
  // Copy the next 16 bytes into the bytes buffer
389
- bytes.set(buffer.subarray(index, index + 16), 0);
360
+ for (let i = 0; i < 16; i++) bytes[i] = buffer[index + i];
390
361
  // Update index
391
362
  index = index + 16;
392
363
  // Assign the new Decimal128 value
393
364
  value = new Decimal128(bytes);
394
365
  } else if (elementType === constants.BSON_DATA_BINARY) {
395
- let binarySize =
396
- buffer[index++] |
397
- (buffer[index++] << 8) |
398
- (buffer[index++] << 16) |
399
- (buffer[index++] << 24);
366
+ let binarySize = NumberUtils.getInt32LE(buffer, index);
367
+ index += 4;
400
368
  const totalBinarySize = binarySize;
401
369
  const subType = buffer[index++];
402
370
 
@@ -411,11 +379,8 @@ function deserializeObject(
411
379
  if (buffer['slice'] != null) {
412
380
  // If we have subtype 2 skip the 4 bytes for the size
413
381
  if (subType === Binary.SUBTYPE_BYTE_ARRAY) {
414
- binarySize =
415
- buffer[index++] |
416
- (buffer[index++] << 8) |
417
- (buffer[index++] << 16) |
418
- (buffer[index++] << 24);
382
+ binarySize = NumberUtils.getInt32LE(buffer, index);
383
+ index += 4;
419
384
  if (binarySize < 0)
420
385
  throw new BSONError('Negative binary type element size found for subtype 0x02');
421
386
  if (binarySize > totalBinarySize - 4)
@@ -433,14 +398,10 @@ function deserializeObject(
433
398
  }
434
399
  }
435
400
  } else {
436
- const _buffer = ByteUtils.allocate(binarySize);
437
401
  // If we have subtype 2 skip the 4 bytes for the size
438
402
  if (subType === Binary.SUBTYPE_BYTE_ARRAY) {
439
- binarySize =
440
- buffer[index++] |
441
- (buffer[index++] << 8) |
442
- (buffer[index++] << 16) |
443
- (buffer[index++] << 24);
403
+ binarySize = NumberUtils.getInt32LE(buffer, index);
404
+ index += 4;
444
405
  if (binarySize < 0)
445
406
  throw new BSONError('Negative binary type element size found for subtype 0x02');
446
407
  if (binarySize > totalBinarySize - 4)
@@ -449,13 +410,12 @@ function deserializeObject(
449
410
  throw new BSONError('Binary type with subtype 0x02 contains too short binary size');
450
411
  }
451
412
 
452
- // Copy the data
453
- for (i = 0; i < binarySize; i++) {
454
- _buffer[i] = buffer[index + i];
455
- }
456
-
457
413
  if (promoteBuffers && promoteValues) {
458
- value = _buffer;
414
+ value = ByteUtils.allocateUnsafe(binarySize);
415
+ // Copy the data
416
+ for (i = 0; i < binarySize; i++) {
417
+ value[i] = buffer[index + i];
418
+ }
459
419
  } else {
460
420
  value = new Binary(buffer.slice(index, index + binarySize), subType);
461
421
  if (subType === constants.BSON_BINARY_SUBTYPE_UUID_NEW && UUID.isValid(value)) {
@@ -539,11 +499,8 @@ function deserializeObject(
539
499
  // Set the object
540
500
  value = new BSONRegExp(source, regExpOptions);
541
501
  } else if (elementType === constants.BSON_DATA_SYMBOL) {
542
- const stringSize =
543
- buffer[index++] |
544
- (buffer[index++] << 8) |
545
- (buffer[index++] << 16) |
546
- (buffer[index++] << 24);
502
+ const stringSize = NumberUtils.getInt32LE(buffer, index);
503
+ index += 4;
547
504
  if (
548
505
  stringSize <= 0 ||
549
506
  stringSize > buffer.length - index ||
@@ -555,31 +512,18 @@ function deserializeObject(
555
512
  value = promoteValues ? symbol : new BSONSymbol(symbol);
556
513
  index = index + stringSize;
557
514
  } else if (elementType === constants.BSON_DATA_TIMESTAMP) {
558
- // We intentionally **do not** use bit shifting here
559
- // Bit shifting in javascript coerces numbers to **signed** int32s
560
- // We need to keep i, and t unsigned
561
- const i =
562
- buffer[index++] +
563
- buffer[index++] * (1 << 8) +
564
- buffer[index++] * (1 << 16) +
565
- buffer[index++] * (1 << 24);
566
- const t =
567
- buffer[index++] +
568
- buffer[index++] * (1 << 8) +
569
- buffer[index++] * (1 << 16) +
570
- buffer[index++] * (1 << 24);
571
-
572
- value = new Timestamp({ i, t });
515
+ value = new Timestamp({
516
+ i: NumberUtils.getUint32LE(buffer, index),
517
+ t: NumberUtils.getUint32LE(buffer, index + 4)
518
+ });
519
+ index += 8;
573
520
  } else if (elementType === constants.BSON_DATA_MIN_KEY) {
574
521
  value = new MinKey();
575
522
  } else if (elementType === constants.BSON_DATA_MAX_KEY) {
576
523
  value = new MaxKey();
577
524
  } else if (elementType === constants.BSON_DATA_CODE) {
578
- const stringSize =
579
- buffer[index++] |
580
- (buffer[index++] << 8) |
581
- (buffer[index++] << 16) |
582
- (buffer[index++] << 24);
525
+ const stringSize = NumberUtils.getInt32LE(buffer, index);
526
+ index += 4;
583
527
  if (
584
528
  stringSize <= 0 ||
585
529
  stringSize > buffer.length - index ||
@@ -599,11 +543,8 @@ function deserializeObject(
599
543
  // Update parse index position
600
544
  index = index + stringSize;
601
545
  } else if (elementType === constants.BSON_DATA_CODE_W_SCOPE) {
602
- const totalSize =
603
- buffer[index++] |
604
- (buffer[index++] << 8) |
605
- (buffer[index++] << 16) |
606
- (buffer[index++] << 24);
546
+ const totalSize = NumberUtils.getInt32LE(buffer, index);
547
+ index += 4;
607
548
 
608
549
  // Element cannot be shorter than totalSize + stringSize + documentSize + terminator
609
550
  if (totalSize < 4 + 4 + 4 + 1) {
@@ -611,11 +552,8 @@ function deserializeObject(
611
552
  }
612
553
 
613
554
  // Get the code string size
614
- const stringSize =
615
- buffer[index++] |
616
- (buffer[index++] << 8) |
617
- (buffer[index++] << 16) |
618
- (buffer[index++] << 24);
555
+ const stringSize = NumberUtils.getInt32LE(buffer, index);
556
+ index += 4;
619
557
  // Check if we have a valid string
620
558
  if (
621
559
  stringSize <= 0 ||
@@ -637,11 +575,7 @@ function deserializeObject(
637
575
  // Parse the element
638
576
  const _index = index;
639
577
  // Decode the size of the object document
640
- const objectSize =
641
- buffer[index] |
642
- (buffer[index + 1] << 8) |
643
- (buffer[index + 2] << 16) |
644
- (buffer[index + 3] << 24);
578
+ const objectSize = NumberUtils.getInt32LE(buffer, index);
645
579
  // Decode the scope object
646
580
  const scopeObject = deserializeObject(buffer, _index, options, false);
647
581
  // Adjust the index
@@ -660,11 +594,8 @@ function deserializeObject(
660
594
  value = new Code(functionString, scopeObject);
661
595
  } else if (elementType === constants.BSON_DATA_DBPOINTER) {
662
596
  // Get the code string size
663
- const stringSize =
664
- buffer[index++] |
665
- (buffer[index++] << 8) |
666
- (buffer[index++] << 16) |
667
- (buffer[index++] << 24);
597
+ const stringSize = NumberUtils.getInt32LE(buffer, index);
598
+ index += 4;
668
599
  // Check if we have a valid string
669
600
  if (
670
601
  stringSize <= 0 ||
@@ -683,8 +614,8 @@ function deserializeObject(
683
614
  index = index + stringSize;
684
615
 
685
616
  // Read the oid
686
- const oidBuffer = ByteUtils.allocate(12);
687
- oidBuffer.set(buffer.subarray(index, index + 12), 0);
617
+ const oidBuffer = ByteUtils.allocateUnsafe(12);
618
+ for (let i = 0; i < 12; i++) oidBuffer[i] = buffer[index + i];
688
619
  const oid = new ObjectId(oidBuffer);
689
620
 
690
621
  // Update the index