bson 6.0.0-alpha.1 → 6.1.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.
package/package.json CHANGED
@@ -14,7 +14,7 @@
14
14
  "vendor"
15
15
  ],
16
16
  "types": "bson.d.ts",
17
- "version": "6.0.0-alpha.1",
17
+ "version": "6.1.0",
18
18
  "author": {
19
19
  "name": "The MongoDB NodeJS Team",
20
20
  "email": "dbx-node@mongodb.com"
package/src/decimal128.ts CHANGED
@@ -158,6 +158,32 @@ export class Decimal128 extends BSONValue {
158
158
  * @param representation - a numeric string representation.
159
159
  */
160
160
  static fromString(representation: string): Decimal128 {
161
+ return Decimal128._fromString(representation, { allowRounding: false });
162
+ }
163
+
164
+ /**
165
+ * Create a Decimal128 instance from a string representation, allowing for rounding to 34
166
+ * significant digits
167
+ *
168
+ * @example Example of a number that will be rounded
169
+ * ```ts
170
+ * > let d = Decimal128.fromString('37.499999999999999196428571428571375')
171
+ * Uncaught:
172
+ * BSONError: "37.499999999999999196428571428571375" is not a valid Decimal128 string - inexact rounding
173
+ * at invalidErr (/home/wajames/js-bson/lib/bson.cjs:1402:11)
174
+ * at Decimal128.fromStringInternal (/home/wajames/js-bson/lib/bson.cjs:1633:25)
175
+ * at Decimal128.fromString (/home/wajames/js-bson/lib/bson.cjs:1424:27)
176
+ *
177
+ * > d = Decimal128.fromStringWithRounding('37.499999999999999196428571428571375')
178
+ * new Decimal128("37.49999999999999919642857142857138")
179
+ * ```
180
+ * @param representation - a numeric string representation.
181
+ */
182
+ static fromStringWithRounding(representation: string): Decimal128 {
183
+ return Decimal128._fromString(representation, { allowRounding: true });
184
+ }
185
+
186
+ private static _fromString(representation: string, options: { allowRounding: boolean }) {
161
187
  // Parse state tracking
162
188
  let isNegative = false;
163
189
  let sawSign = false;
@@ -351,59 +377,147 @@ export class Decimal128 extends BSONValue {
351
377
  exponent = exponent - 1;
352
378
  }
353
379
 
354
- while (exponent < EXPONENT_MIN || nDigitsStored < nDigits) {
355
- // Shift last digit. can only do this if < significant digits than # stored.
356
- if (lastDigit === 0) {
357
- if (significantDigits === 0) {
380
+ if (options.allowRounding) {
381
+ while (exponent < EXPONENT_MIN || nDigitsStored < nDigits) {
382
+ // Shift last digit. can only do this if < significant digits than # stored.
383
+ if (lastDigit === 0 && significantDigits < nDigitsStored) {
358
384
  exponent = EXPONENT_MIN;
385
+ significantDigits = 0;
359
386
  break;
360
387
  }
361
388
 
362
- invalidErr(representation, 'exponent underflow');
389
+ if (nDigitsStored < nDigits) {
390
+ // adjust to match digits not stored
391
+ nDigits = nDigits - 1;
392
+ } else {
393
+ // adjust to round
394
+ lastDigit = lastDigit - 1;
395
+ }
396
+
397
+ if (exponent < EXPONENT_MAX) {
398
+ exponent = exponent + 1;
399
+ } else {
400
+ // Check if we have a zero then just hard clamp, otherwise fail
401
+ const digitsString = digits.join('');
402
+ if (digitsString.match(/^0+$/)) {
403
+ exponent = EXPONENT_MAX;
404
+ break;
405
+ }
406
+ invalidErr(representation, 'overflow');
407
+ }
363
408
  }
364
409
 
365
- if (nDigitsStored < nDigits) {
366
- if (
367
- representation[nDigits - 1 + Number(sawSign) + Number(sawRadix)] !== '0' &&
368
- significantDigits !== 0
369
- ) {
370
- invalidErr(representation, 'inexact rounding');
410
+ // Round
411
+ // We've normalized the exponent, but might still need to round.
412
+ if (lastDigit + 1 < significantDigits) {
413
+ let endOfString = nDigitsRead;
414
+
415
+ // If we have seen a radix point, 'string' is 1 longer than we have
416
+ // documented with ndigits_read, so inc the position of the first nonzero
417
+ // digit and the position that digits are read to.
418
+ if (sawRadix) {
419
+ firstNonZero = firstNonZero + 1;
420
+ endOfString = endOfString + 1;
371
421
  }
372
- // adjust to match digits not stored
373
- nDigits = nDigits - 1;
374
- } else {
375
- if (digits[lastDigit] !== 0) {
376
- invalidErr(representation, 'inexact rounding');
422
+ // if negative, we need to increment again to account for - sign at start.
423
+ if (sawSign) {
424
+ firstNonZero = firstNonZero + 1;
425
+ endOfString = endOfString + 1;
377
426
  }
378
- // adjust to round
379
- lastDigit = lastDigit - 1;
380
- }
381
427
 
382
- if (exponent < EXPONENT_MAX) {
383
- exponent = exponent + 1;
384
- } else {
385
- invalidErr(representation, 'overflow');
386
- }
387
- }
428
+ const roundDigit = parseInt(representation[firstNonZero + lastDigit + 1], 10);
429
+ let roundBit = 0;
430
+
431
+ if (roundDigit >= 5) {
432
+ roundBit = 1;
433
+ if (roundDigit === 5) {
434
+ roundBit = digits[lastDigit] % 2 === 1 ? 1 : 0;
435
+ for (let i = firstNonZero + lastDigit + 2; i < endOfString; i++) {
436
+ if (parseInt(representation[i], 10)) {
437
+ roundBit = 1;
438
+ break;
439
+ }
440
+ }
441
+ }
442
+ }
388
443
 
389
- // Round
390
- // We've normalized the exponent, but might still need to round.
391
- if (lastDigit + 1 < significantDigits) {
392
- // If we have seen a radix point, 'string' is 1 longer than we have
393
- // documented with ndigits_read, so inc the position of the first nonzero
394
- // digit and the position that digits are read to.
395
- if (sawRadix) {
396
- firstNonZero = firstNonZero + 1;
444
+ if (roundBit) {
445
+ let dIdx = lastDigit;
446
+
447
+ for (; dIdx >= 0; dIdx--) {
448
+ if (++digits[dIdx] > 9) {
449
+ digits[dIdx] = 0;
450
+
451
+ // overflowed most significant digit
452
+ if (dIdx === 0) {
453
+ if (exponent < EXPONENT_MAX) {
454
+ exponent = exponent + 1;
455
+ digits[dIdx] = 1;
456
+ } else {
457
+ return new Decimal128(isNegative ? INF_NEGATIVE_BUFFER : INF_POSITIVE_BUFFER);
458
+ }
459
+ }
460
+ } else {
461
+ break;
462
+ }
463
+ }
464
+ }
397
465
  }
398
- // if saw sign, we need to increment again to account for - or + sign at start.
399
- if (sawSign) {
400
- firstNonZero = firstNonZero + 1;
466
+ } else {
467
+ while (exponent < EXPONENT_MIN || nDigitsStored < nDigits) {
468
+ // Shift last digit. can only do this if < significant digits than # stored.
469
+ if (lastDigit === 0) {
470
+ if (significantDigits === 0) {
471
+ exponent = EXPONENT_MIN;
472
+ break;
473
+ }
474
+
475
+ invalidErr(representation, 'exponent underflow');
476
+ }
477
+
478
+ if (nDigitsStored < nDigits) {
479
+ if (
480
+ representation[nDigits - 1 + Number(sawSign) + Number(sawRadix)] !== '0' &&
481
+ significantDigits !== 0
482
+ ) {
483
+ invalidErr(representation, 'inexact rounding');
484
+ }
485
+ // adjust to match digits not stored
486
+ nDigits = nDigits - 1;
487
+ } else {
488
+ if (digits[lastDigit] !== 0) {
489
+ invalidErr(representation, 'inexact rounding');
490
+ }
491
+ // adjust to round
492
+ lastDigit = lastDigit - 1;
493
+ }
494
+
495
+ if (exponent < EXPONENT_MAX) {
496
+ exponent = exponent + 1;
497
+ } else {
498
+ invalidErr(representation, 'overflow');
499
+ }
401
500
  }
402
501
 
403
- const roundDigit = parseInt(representation[firstNonZero + lastDigit + 1], 10);
502
+ // Round
503
+ // We've normalized the exponent, but might still need to round.
504
+ if (lastDigit + 1 < significantDigits) {
505
+ // If we have seen a radix point, 'string' is 1 longer than we have
506
+ // documented with ndigits_read, so inc the position of the first nonzero
507
+ // digit and the position that digits are read to.
508
+ if (sawRadix) {
509
+ firstNonZero = firstNonZero + 1;
510
+ }
511
+ // if saw sign, we need to increment again to account for - or + sign at start.
512
+ if (sawSign) {
513
+ firstNonZero = firstNonZero + 1;
514
+ }
515
+
516
+ const roundDigit = parseInt(representation[firstNonZero + lastDigit + 1], 10);
404
517
 
405
- if (roundDigit !== 0) {
406
- invalidErr(representation, 'inexact rounding');
518
+ if (roundDigit !== 0) {
519
+ invalidErr(representation, 'inexact rounding');
520
+ }
407
521
  }
408
522
  }
409
523
 
@@ -507,7 +621,6 @@ export class Decimal128 extends BSONValue {
507
621
  // Return the new Decimal128
508
622
  return new Decimal128(buffer);
509
623
  }
510
-
511
624
  /** Create a string representation of the raw Decimal128 value */
512
625
  toString(): string {
513
626
  // Note: bits in this routine are referred to starting at 0,
@@ -257,14 +257,18 @@ function serializeObjectId(buffer: Uint8Array, key: string, value: ObjectId, ind
257
257
  buffer[index++] = 0;
258
258
 
259
259
  // Write the objectId into the shared buffer
260
- if (isUint8Array(value.id)) {
261
- buffer.set(value.id.subarray(0, 12), index);
260
+ const idValue = value.id;
261
+
262
+ if (isUint8Array(idValue)) {
263
+ for (let i = 0; i < 12; i++) {
264
+ buffer[index++] = idValue[i];
265
+ }
262
266
  } else {
263
267
  throw new BSONError('object [' + JSON.stringify(value) + '] is not a valid ObjectId');
264
268
  }
265
269
 
266
270
  // Adjust index
267
- return index + 12;
271
+ return index;
268
272
  }
269
273
 
270
274
  function serializeBuffer(buffer: Uint8Array, key: string, value: Uint8Array, index: number) {