Skip to content

Commit c4715c1

Browse files
committed
Merge branch 'main' into NODE-4892-version-tag
2 parents 9132939 + 9679ec3 commit c4715c1

15 files changed

+438
-159
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,12 +238,13 @@ Serialize a Javascript object using a predefined Buffer and index into the buffe
238238
| buffer | <code>Buffer</code> | | the buffer containing the serialized set of BSON documents. |
239239
| [options.evalFunctions] | <code>Object</code> | <code>false</code> | evaluate functions in the BSON document scoped to the object deserialized. |
240240
| [options.cacheFunctions] | <code>Object</code> | <code>false</code> | cache evaluated functions for reuse. |
241+
| [options.useBigInt64] | <code>Object</code> | <code>false</code> | when deserializing a Long will return a BigInt. |
241242
| [options.promoteLongs] | <code>Object</code> | <code>true</code> | when deserializing a Long will fit it into a Number if it's smaller than 53 bits |
242243
| [options.promoteBuffers] | <code>Object</code> | <code>false</code> | when deserializing a Binary will return it as a node.js Buffer instance. |
243244
| [options.promoteValues] | <code>Object</code> | <code>false</code> | when deserializing will promote BSON values to their Node.js closest equivalent types. |
244245
| [options.fieldsAsRaw] | <code>Object</code> | <code></code> | allow to specify if there what fields we wish to return as unserialized raw buffer. |
245246
| [options.bsonRegExp] | <code>Object</code> | <code>false</code> | return BSON regular expressions as BSONRegExp instances. |
246-
| [options.allowObjectSmallerThanBufferSize] | <code>boolean</code> | <code>false</code> | allows the buffer to be larger than the parsed BSON object |
247+
| [options.allowObjectSmallerThanBufferSize] | <code>boolean</code> | <code>false</code> | allows the buffer to be larger than the parsed BSON object. |
247248

248249
Deserialize data as BSON.
249250

src/double.ts

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -57,23 +57,17 @@ export class Double {
5757
return this.value;
5858
}
5959

60-
// NOTE: JavaScript has +0 and -0, apparently to model limit calculations. If a user
61-
// explicitly provided `-0` then we need to ensure the sign makes it into the output
6260
if (Object.is(Math.sign(this.value), -0)) {
63-
return { $numberDouble: `-${this.value.toFixed(1)}` };
61+
// NOTE: JavaScript has +0 and -0, apparently to model limit calculations. If a user
62+
// explicitly provided `-0` then we need to ensure the sign makes it into the output
63+
return { $numberDouble: '-0.0' };
6464
}
6565

66-
let $numberDouble: string;
6766
if (Number.isInteger(this.value)) {
68-
$numberDouble = this.value.toFixed(1);
69-
if ($numberDouble.length >= 13) {
70-
$numberDouble = this.value.toExponential(13).toUpperCase();
71-
}
67+
return { $numberDouble: `${this.value}.0` };
7268
} else {
73-
$numberDouble = this.value.toString();
69+
return { $numberDouble: `${this.value}` };
7470
}
75-
76-
return { $numberDouble };
7771
}
7872

7973
/** @internal */

src/parser/deserializer.ts

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,14 @@ import { ObjectId } from '../objectid';
1414
import { BSONRegExp } from '../regexp';
1515
import { BSONSymbol } from '../symbol';
1616
import { Timestamp } from '../timestamp';
17-
import { ByteUtils } from '../utils/byte_utils';
17+
import { BSONDataView, ByteUtils } from '../utils/byte_utils';
1818
import { validateUtf8 } from '../validate_utf8';
1919

2020
/** @public */
2121
export interface DeserializeOptions {
22-
/** when deserializing a Long will fit it into a Number if it's smaller than 53 bits */
22+
/** when deserializing a Long will return as a BigInt. */
23+
useBigInt64?: boolean;
24+
/** when deserializing a Long will fit it into a Number if it's smaller than 53 bits. */
2325
promoteLongs?: boolean;
2426
/** when deserializing a Binary will return it as a node.js Buffer instance. */
2527
promoteBuffers?: boolean;
@@ -29,7 +31,7 @@ export interface DeserializeOptions {
2931
fieldsAsRaw?: Document;
3032
/** return BSON regular expressions as BSONRegExp instances. */
3133
bsonRegExp?: boolean;
32-
/** allows the buffer to be larger than the parsed BSON object */
34+
/** allows the buffer to be larger than the parsed BSON object. */
3335
allowObjectSmallerThanBufferSize?: boolean;
3436
/** Offset into buffer to begin reading document from */
3537
index?: number;
@@ -96,7 +98,7 @@ export function internalDeserialize(
9698
);
9799
}
98100

99-
// Start deserializtion
101+
// Start deserialization
100102
return deserializeObject(buffer, index, options, isArray);
101103
}
102104

@@ -117,9 +119,18 @@ function deserializeObject(
117119
const bsonRegExp = typeof options['bsonRegExp'] === 'boolean' ? options['bsonRegExp'] : false;
118120

119121
// Controls the promotion of values vs wrapper classes
120-
const promoteBuffers = options['promoteBuffers'] == null ? false : options['promoteBuffers'];
121-
const promoteLongs = options['promoteLongs'] == null ? true : options['promoteLongs'];
122-
const promoteValues = options['promoteValues'] == null ? true : options['promoteValues'];
122+
const promoteBuffers = options.promoteBuffers ?? false;
123+
const promoteLongs = options.promoteLongs ?? true;
124+
const promoteValues = options.promoteValues ?? true;
125+
const useBigInt64 = options.useBigInt64 ?? false;
126+
127+
if (useBigInt64 && !promoteValues) {
128+
throw new BSONError('Must either request bigint or Long for int64 deserialization');
129+
}
130+
131+
if (useBigInt64 && !promoteLongs) {
132+
throw new BSONError('Must either request bigint or Long for int64 deserialization');
133+
}
123134

124135
// Ensures default validation option if none given
125136
const validation = options.validation == null ? { utf8: true } : options.validation;
@@ -323,6 +334,8 @@ function deserializeObject(
323334
value = null;
324335
} else if (elementType === constants.BSON_DATA_LONG) {
325336
// Unpack the low and high bits
337+
const dataview = BSONDataView.fromUint8Array(buffer.subarray(index, index + 8));
338+
326339
const lowBits =
327340
buffer[index++] |
328341
(buffer[index++] << 8) |
@@ -334,8 +347,10 @@ function deserializeObject(
334347
(buffer[index++] << 16) |
335348
(buffer[index++] << 24);
336349
const long = new Long(lowBits, highBits);
337-
// Promote the long if possible
338-
if (promoteLongs && promoteValues === true) {
350+
if (useBigInt64) {
351+
value = dataview.getBigInt64(0, true);
352+
} else if (promoteLongs && promoteValues === true) {
353+
// Promote the long if possible
339354
value =
340355
long.lessThanOrEqual(JS_INT_MAX_LONG) && long.greaterThanOrEqual(JS_INT_MIN_LONG)
341356
? long.toNumber()

src/parser/serializer.ts

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,7 @@ import type { MinKey } from '../min_key';
1212
import type { ObjectId } from '../objectid';
1313
import type { BSONRegExp } from '../regexp';
1414
import { ByteUtils } from '../utils/byte_utils';
15-
import {
16-
isAnyArrayBuffer,
17-
isBigInt64Array,
18-
isBigUInt64Array,
19-
isDate,
20-
isMap,
21-
isRegExp,
22-
isUint8Array
23-
} from './utils';
15+
import { isAnyArrayBuffer, isDate, isMap, isRegExp, isUint8Array } from './utils';
2416

2517
/** @public */
2618
export interface SerializeOptions {
@@ -103,6 +95,20 @@ function serializeNumber(buffer: Uint8Array, key: string, value: number, index:
10395
return index;
10496
}
10597

98+
function serializeBigInt(buffer: Uint8Array, key: string, value: bigint, index: number) {
99+
buffer[index++] = constants.BSON_DATA_LONG;
100+
// Number of written bytes
101+
const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
102+
// Encode the name
103+
index += numberOfWrittenBytes;
104+
buffer[index++] = 0;
105+
NUMBER_SPACE.setBigInt64(0, value, true);
106+
// Write BigInt value
107+
buffer.set(EIGHT_BYTE_VIEW_ON_NUMBER, index);
108+
index += EIGHT_BYTE_VIEW_ON_NUMBER.byteLength;
109+
return index;
110+
}
111+
106112
function serializeNull(buffer: Uint8Array, key: string, _: unknown, index: number) {
107113
// Set long type
108114
buffer[index++] = constants.BSON_DATA_NULL;
@@ -675,7 +681,7 @@ export function serializeInto(
675681
} else if (typeof value === 'number') {
676682
index = serializeNumber(buffer, key, value, index);
677683
} else if (typeof value === 'bigint') {
678-
throw new BSONError('Unsupported type BigInt, please use Decimal128');
684+
index = serializeBigInt(buffer, key, value, index);
679685
} else if (typeof value === 'boolean') {
680686
index = serializeBoolean(buffer, key, value, index);
681687
} else if (value instanceof Date || isDate(value)) {
@@ -779,8 +785,8 @@ export function serializeInto(
779785
index = serializeString(buffer, key, value, index);
780786
} else if (type === 'number') {
781787
index = serializeNumber(buffer, key, value, index);
782-
} else if (type === 'bigint' || isBigInt64Array(value) || isBigUInt64Array(value)) {
783-
throw new BSONError('Unsupported type BigInt, please use Decimal128');
788+
} else if (type === 'bigint') {
789+
index = serializeBigInt(buffer, key, value, index);
784790
} else if (type === 'boolean') {
785791
index = serializeBoolean(buffer, key, value, index);
786792
} else if (value instanceof Date || isDate(value)) {
@@ -885,7 +891,7 @@ export function serializeInto(
885891
} else if (type === 'number') {
886892
index = serializeNumber(buffer, key, value, index);
887893
} else if (type === 'bigint') {
888-
throw new BSONError('Unsupported type BigInt, please use Decimal128');
894+
index = serializeBigInt(buffer, key, value, index);
889895
} else if (type === 'boolean') {
890896
index = serializeBoolean(buffer, key, value, index);
891897
} else if (value instanceof Date || isDate(value)) {

0 commit comments

Comments
 (0)