bson 6.2.0 → 6.4.0

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.
@@ -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++];
@@ -236,11 +234,11 @@ function deserializeObject(
236
234
  if (i >= buffer.byteLength) throw new BSONError('Bad BSON Document: illegal CString');
237
235
 
238
236
  // Represents the key
239
- const name = isArray ? arrayIndex++ : ByteUtils.toUTF8(buffer, index, i);
237
+ const name = isArray ? arrayIndex++ : ByteUtils.toUTF8(buffer, index, i, false);
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 ||
@@ -266,40 +261,28 @@ function deserializeObject(
266
261
  ) {
267
262
  throw new BSONError('bad string length in bson');
268
263
  }
269
- value = getValidatedString(buffer, index, index + stringSize - 1, shouldValidateKey);
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)) {
@@ -476,7 +436,7 @@ function deserializeObject(
476
436
  // If are at the end of the buffer there is a problem with the document
477
437
  if (i >= buffer.length) throw new BSONError('Bad BSON Document: illegal CString');
478
438
  // Return the C string
479
- const source = ByteUtils.toUTF8(buffer, index, i);
439
+ const source = ByteUtils.toUTF8(buffer, index, i, false);
480
440
  // Create the regexp
481
441
  index = i + 1;
482
442
 
@@ -489,7 +449,7 @@ function deserializeObject(
489
449
  // If are at the end of the buffer there is a problem with the document
490
450
  if (i >= buffer.length) throw new BSONError('Bad BSON Document: illegal CString');
491
451
  // Return the C string
492
- const regExpOptions = ByteUtils.toUTF8(buffer, index, i);
452
+ const regExpOptions = ByteUtils.toUTF8(buffer, index, i, false);
493
453
  index = i + 1;
494
454
 
495
455
  // For each option add the corresponding one for javascript
@@ -521,7 +481,7 @@ function deserializeObject(
521
481
  // If are at the end of the buffer there is a problem with the document
522
482
  if (i >= buffer.length) throw new BSONError('Bad BSON Document: illegal CString');
523
483
  // Return the C string
524
- const source = ByteUtils.toUTF8(buffer, index, i);
484
+ const source = ByteUtils.toUTF8(buffer, index, i, false);
525
485
  index = i + 1;
526
486
 
527
487
  // Get the start search index
@@ -533,17 +493,14 @@ function deserializeObject(
533
493
  // If are at the end of the buffer there is a problem with the document
534
494
  if (i >= buffer.length) throw new BSONError('Bad BSON Document: illegal CString');
535
495
  // Return the C string
536
- const regExpOptions = ByteUtils.toUTF8(buffer, index, i);
496
+ const regExpOptions = ByteUtils.toUTF8(buffer, index, i, false);
537
497
  index = i + 1;
538
498
 
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 ||
@@ -551,35 +508,22 @@ function deserializeObject(
551
508
  ) {
552
509
  throw new BSONError('bad string length in bson');
553
510
  }
554
- const symbol = getValidatedString(buffer, index, index + stringSize - 1, shouldValidateKey);
511
+ const symbol = ByteUtils.toUTF8(buffer, index, index + stringSize - 1, shouldValidateKey);
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 ||
@@ -587,7 +531,7 @@ function deserializeObject(
587
531
  ) {
588
532
  throw new BSONError('bad string length in bson');
589
533
  }
590
- const functionString = getValidatedString(
534
+ const functionString = ByteUtils.toUTF8(
591
535
  buffer,
592
536
  index,
593
537
  index + stringSize - 1,
@@ -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 ||
@@ -626,7 +564,7 @@ function deserializeObject(
626
564
  }
627
565
 
628
566
  // Javascript function
629
- const functionString = getValidatedString(
567
+ const functionString = ByteUtils.toUTF8(
630
568
  buffer,
631
569
  index,
632
570
  index + stringSize - 1,
@@ -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 ||
@@ -678,13 +609,13 @@ function deserializeObject(
678
609
  throw new BSONError('Invalid UTF-8 string in BSON document');
679
610
  }
680
611
  }
681
- const namespace = ByteUtils.toUTF8(buffer, index, index + stringSize - 1);
612
+ const namespace = ByteUtils.toUTF8(buffer, index, index + stringSize - 1, false);
682
613
  // Update parse index position
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
@@ -728,24 +659,3 @@ function deserializeObject(
728
659
 
729
660
  return object;
730
661
  }
731
-
732
- function getValidatedString(
733
- buffer: Uint8Array,
734
- start: number,
735
- end: number,
736
- shouldValidateUtf8: boolean
737
- ) {
738
- const value = ByteUtils.toUTF8(buffer, start, end);
739
- // if utf8 validation is on, do the check
740
- if (shouldValidateUtf8) {
741
- for (let i = 0; i < value.length; i++) {
742
- if (value.charCodeAt(i) === 0xfffd) {
743
- if (!validateUtf8(buffer, start, end)) {
744
- throw new BSONError('Invalid UTF-8 string in BSON document');
745
- }
746
- break;
747
- }
748
- }
749
- }
750
- return value;
751
- }