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/bson.d.ts +2 -3
- package/lib/bson.bundle.js +302 -249
- package/lib/bson.bundle.js.map +1 -1
- package/lib/bson.cjs +302 -249
- package/lib/bson.cjs.map +1 -1
- package/lib/bson.mjs +302 -249
- package/lib/bson.mjs.map +1 -1
- package/lib/bson.rn.cjs +302 -249
- package/lib/bson.rn.cjs.map +1 -1
- package/package.json +18 -18
- package/src/bson.ts +3 -6
- package/src/db_ref.ts +0 -1
- package/src/decimal128.ts +1 -1
- package/src/objectid.ts +34 -15
- package/src/parser/deserializer.ts +75 -144
- package/src/parser/serializer.ts +41 -104
- package/src/utils/byte_utils.ts +2 -8
- package/src/utils/latin.ts +44 -1
- package/src/utils/node_byte_utils.ts +12 -6
- package/src/utils/number_utils.ts +174 -0
- package/src/utils/web_byte_utils.ts +10 -10
package/src/parser/serializer.ts
CHANGED
|
@@ -12,6 +12,7 @@ import type { MinKey } from '../min_key';
|
|
|
12
12
|
import type { ObjectId } from '../objectid';
|
|
13
13
|
import type { BSONRegExp } from '../regexp';
|
|
14
14
|
import { ByteUtils } from '../utils/byte_utils';
|
|
15
|
+
import { NumberUtils } from '../utils/number_utils';
|
|
15
16
|
import { isAnyArrayBuffer, isDate, isMap, isRegExp, isUint8Array } from './utils';
|
|
16
17
|
|
|
17
18
|
/** @public */
|
|
@@ -61,10 +62,7 @@ function serializeString(buffer: Uint8Array, key: string, value: string, index:
|
|
|
61
62
|
// Write the string
|
|
62
63
|
const size = ByteUtils.encodeUTF8Into(buffer, value, index + 4);
|
|
63
64
|
// Write the size of the string to buffer
|
|
64
|
-
buffer
|
|
65
|
-
buffer[index + 2] = ((size + 1) >> 16) & 0xff;
|
|
66
|
-
buffer[index + 1] = ((size + 1) >> 8) & 0xff;
|
|
67
|
-
buffer[index] = (size + 1) & 0xff;
|
|
65
|
+
NumberUtils.setInt32LE(buffer, index, size + 1);
|
|
68
66
|
// Update index
|
|
69
67
|
index = index + 4 + size;
|
|
70
68
|
// Write zero
|
|
@@ -72,10 +70,6 @@ function serializeString(buffer: Uint8Array, key: string, value: string, index:
|
|
|
72
70
|
return index;
|
|
73
71
|
}
|
|
74
72
|
|
|
75
|
-
const NUMBER_SPACE = new DataView(new ArrayBuffer(8), 0, 8);
|
|
76
|
-
const FOUR_BYTE_VIEW_ON_NUMBER = new Uint8Array(NUMBER_SPACE.buffer, 0, 4);
|
|
77
|
-
const EIGHT_BYTE_VIEW_ON_NUMBER = new Uint8Array(NUMBER_SPACE.buffer, 0, 8);
|
|
78
|
-
|
|
79
73
|
function serializeNumber(buffer: Uint8Array, key: string, value: number, index: number) {
|
|
80
74
|
const isNegativeZero = Object.is(value, -0);
|
|
81
75
|
|
|
@@ -87,23 +81,17 @@ function serializeNumber(buffer: Uint8Array, key: string, value: number, index:
|
|
|
87
81
|
? constants.BSON_DATA_INT
|
|
88
82
|
: constants.BSON_DATA_NUMBER;
|
|
89
83
|
|
|
90
|
-
if (type === constants.BSON_DATA_INT) {
|
|
91
|
-
NUMBER_SPACE.setInt32(0, value, true);
|
|
92
|
-
} else {
|
|
93
|
-
NUMBER_SPACE.setFloat64(0, value, true);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const bytes =
|
|
97
|
-
type === constants.BSON_DATA_INT ? FOUR_BYTE_VIEW_ON_NUMBER : EIGHT_BYTE_VIEW_ON_NUMBER;
|
|
98
|
-
|
|
99
84
|
buffer[index++] = type;
|
|
100
85
|
|
|
101
86
|
const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
|
|
102
87
|
index = index + numberOfWrittenBytes;
|
|
103
88
|
buffer[index++] = 0x00;
|
|
104
89
|
|
|
105
|
-
|
|
106
|
-
|
|
90
|
+
if (type === constants.BSON_DATA_INT) {
|
|
91
|
+
index += NumberUtils.setInt32LE(buffer, index, value);
|
|
92
|
+
} else {
|
|
93
|
+
index += NumberUtils.setFloat64LE(buffer, index, value);
|
|
94
|
+
}
|
|
107
95
|
|
|
108
96
|
return index;
|
|
109
97
|
}
|
|
@@ -115,10 +103,9 @@ function serializeBigInt(buffer: Uint8Array, key: string, value: bigint, index:
|
|
|
115
103
|
// Encode the name
|
|
116
104
|
index += numberOfWrittenBytes;
|
|
117
105
|
buffer[index++] = 0;
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
index += EIGHT_BYTE_VIEW_ON_NUMBER.byteLength;
|
|
106
|
+
|
|
107
|
+
index += NumberUtils.setBigInt64LE(buffer, index, value);
|
|
108
|
+
|
|
122
109
|
return index;
|
|
123
110
|
}
|
|
124
111
|
|
|
@@ -162,15 +149,9 @@ function serializeDate(buffer: Uint8Array, key: string, value: Date, index: numb
|
|
|
162
149
|
const lowBits = dateInMilis.getLowBits();
|
|
163
150
|
const highBits = dateInMilis.getHighBits();
|
|
164
151
|
// Encode low bits
|
|
165
|
-
|
|
166
|
-
buffer[index++] = (lowBits >> 8) & 0xff;
|
|
167
|
-
buffer[index++] = (lowBits >> 16) & 0xff;
|
|
168
|
-
buffer[index++] = (lowBits >> 24) & 0xff;
|
|
152
|
+
index += NumberUtils.setInt32LE(buffer, index, lowBits);
|
|
169
153
|
// Encode high bits
|
|
170
|
-
|
|
171
|
-
buffer[index++] = (highBits >> 8) & 0xff;
|
|
172
|
-
buffer[index++] = (highBits >> 16) & 0xff;
|
|
173
|
-
buffer[index++] = (highBits >> 24) & 0xff;
|
|
154
|
+
index += NumberUtils.setInt32LE(buffer, index, highBits);
|
|
174
155
|
return index;
|
|
175
156
|
}
|
|
176
157
|
|
|
@@ -256,16 +237,7 @@ function serializeObjectId(buffer: Uint8Array, key: string, value: ObjectId, ind
|
|
|
256
237
|
index = index + numberOfWrittenBytes;
|
|
257
238
|
buffer[index++] = 0;
|
|
258
239
|
|
|
259
|
-
|
|
260
|
-
const idValue = value.id;
|
|
261
|
-
|
|
262
|
-
if (isUint8Array(idValue)) {
|
|
263
|
-
for (let i = 0; i < 12; i++) {
|
|
264
|
-
buffer[index++] = idValue[i];
|
|
265
|
-
}
|
|
266
|
-
} else {
|
|
267
|
-
throw new BSONError('object [' + JSON.stringify(value) + '] is not a valid ObjectId');
|
|
268
|
-
}
|
|
240
|
+
index += value.serializeInto(buffer, index);
|
|
269
241
|
|
|
270
242
|
// Adjust index
|
|
271
243
|
return index;
|
|
@@ -282,14 +254,15 @@ function serializeBuffer(buffer: Uint8Array, key: string, value: Uint8Array, ind
|
|
|
282
254
|
// Get size of the buffer (current write point)
|
|
283
255
|
const size = value.length;
|
|
284
256
|
// Write the size of the string to buffer
|
|
285
|
-
|
|
286
|
-
buffer[index++] = (size >> 8) & 0xff;
|
|
287
|
-
buffer[index++] = (size >> 16) & 0xff;
|
|
288
|
-
buffer[index++] = (size >> 24) & 0xff;
|
|
257
|
+
index += NumberUtils.setInt32LE(buffer, index, size);
|
|
289
258
|
// Write the default subtype
|
|
290
259
|
buffer[index++] = constants.BSON_BINARY_SUBTYPE_DEFAULT;
|
|
291
260
|
// Copy the content form the binary field to the buffer
|
|
292
|
-
|
|
261
|
+
if (size <= 16) {
|
|
262
|
+
for (let i = 0; i < size; i++) buffer[index + i] = value[i];
|
|
263
|
+
} else {
|
|
264
|
+
buffer.set(value, index);
|
|
265
|
+
}
|
|
293
266
|
// Adjust the index
|
|
294
267
|
index = index + size;
|
|
295
268
|
return index;
|
|
@@ -343,7 +316,7 @@ function serializeDecimal128(buffer: Uint8Array, key: string, value: Decimal128,
|
|
|
343
316
|
index = index + numberOfWrittenBytes;
|
|
344
317
|
buffer[index++] = 0;
|
|
345
318
|
// Write the data from the value
|
|
346
|
-
|
|
319
|
+
for (let i = 0; i < 16; i++) buffer[index + i] = value.bytes[i];
|
|
347
320
|
return index + 16;
|
|
348
321
|
}
|
|
349
322
|
|
|
@@ -360,15 +333,9 @@ function serializeLong(buffer: Uint8Array, key: string, value: Long, index: numb
|
|
|
360
333
|
const lowBits = value.getLowBits();
|
|
361
334
|
const highBits = value.getHighBits();
|
|
362
335
|
// Encode low bits
|
|
363
|
-
|
|
364
|
-
buffer[index++] = (lowBits >> 8) & 0xff;
|
|
365
|
-
buffer[index++] = (lowBits >> 16) & 0xff;
|
|
366
|
-
buffer[index++] = (lowBits >> 24) & 0xff;
|
|
336
|
+
index += NumberUtils.setInt32LE(buffer, index, lowBits);
|
|
367
337
|
// Encode high bits
|
|
368
|
-
|
|
369
|
-
buffer[index++] = (highBits >> 8) & 0xff;
|
|
370
|
-
buffer[index++] = (highBits >> 16) & 0xff;
|
|
371
|
-
buffer[index++] = (highBits >> 24) & 0xff;
|
|
338
|
+
index += NumberUtils.setInt32LE(buffer, index, highBits);
|
|
372
339
|
return index;
|
|
373
340
|
}
|
|
374
341
|
|
|
@@ -382,10 +349,7 @@ function serializeInt32(buffer: Uint8Array, key: string, value: Int32 | number,
|
|
|
382
349
|
index = index + numberOfWrittenBytes;
|
|
383
350
|
buffer[index++] = 0;
|
|
384
351
|
// Write the int value
|
|
385
|
-
|
|
386
|
-
buffer[index++] = (value >> 8) & 0xff;
|
|
387
|
-
buffer[index++] = (value >> 16) & 0xff;
|
|
388
|
-
buffer[index++] = (value >> 24) & 0xff;
|
|
352
|
+
index += NumberUtils.setInt32LE(buffer, index, value);
|
|
389
353
|
return index;
|
|
390
354
|
}
|
|
391
355
|
|
|
@@ -401,11 +365,8 @@ function serializeDouble(buffer: Uint8Array, key: string, value: Double, index:
|
|
|
401
365
|
buffer[index++] = 0;
|
|
402
366
|
|
|
403
367
|
// Write float
|
|
404
|
-
|
|
405
|
-
buffer.set(EIGHT_BYTE_VIEW_ON_NUMBER, index);
|
|
368
|
+
index += NumberUtils.setFloat64LE(buffer, index, value.value);
|
|
406
369
|
|
|
407
|
-
// Adjust index
|
|
408
|
-
index = index + 8;
|
|
409
370
|
return index;
|
|
410
371
|
}
|
|
411
372
|
|
|
@@ -422,10 +383,7 @@ function serializeFunction(buffer: Uint8Array, key: string, value: Function, ind
|
|
|
422
383
|
// Write the string
|
|
423
384
|
const size = ByteUtils.encodeUTF8Into(buffer, functionString, index + 4) + 1;
|
|
424
385
|
// Write the size of the string to buffer
|
|
425
|
-
buffer
|
|
426
|
-
buffer[index + 1] = (size >> 8) & 0xff;
|
|
427
|
-
buffer[index + 2] = (size >> 16) & 0xff;
|
|
428
|
-
buffer[index + 3] = (size >> 24) & 0xff;
|
|
386
|
+
NumberUtils.setInt32LE(buffer, index, size);
|
|
429
387
|
// Update index
|
|
430
388
|
index = index + 4 + size - 1;
|
|
431
389
|
// Write zero
|
|
@@ -464,10 +422,7 @@ function serializeCode(
|
|
|
464
422
|
// Write string into buffer
|
|
465
423
|
const codeSize = ByteUtils.encodeUTF8Into(buffer, functionString, index + 4) + 1;
|
|
466
424
|
// Write the size of the string to buffer
|
|
467
|
-
buffer
|
|
468
|
-
buffer[index + 1] = (codeSize >> 8) & 0xff;
|
|
469
|
-
buffer[index + 2] = (codeSize >> 16) & 0xff;
|
|
470
|
-
buffer[index + 3] = (codeSize >> 24) & 0xff;
|
|
425
|
+
NumberUtils.setInt32LE(buffer, index, codeSize);
|
|
471
426
|
// Write end 0
|
|
472
427
|
buffer[index + 4 + codeSize - 1] = 0;
|
|
473
428
|
// Write the
|
|
@@ -490,10 +445,7 @@ function serializeCode(
|
|
|
490
445
|
const totalSize = endIndex - startIndex;
|
|
491
446
|
|
|
492
447
|
// Write the total size of the object
|
|
493
|
-
|
|
494
|
-
buffer[startIndex++] = (totalSize >> 8) & 0xff;
|
|
495
|
-
buffer[startIndex++] = (totalSize >> 16) & 0xff;
|
|
496
|
-
buffer[startIndex++] = (totalSize >> 24) & 0xff;
|
|
448
|
+
startIndex += NumberUtils.setInt32LE(buffer, startIndex, totalSize);
|
|
497
449
|
// Write trailing zero
|
|
498
450
|
buffer[index++] = 0;
|
|
499
451
|
} else {
|
|
@@ -508,10 +460,7 @@ function serializeCode(
|
|
|
508
460
|
// Write the string
|
|
509
461
|
const size = ByteUtils.encodeUTF8Into(buffer, functionString, index + 4) + 1;
|
|
510
462
|
// Write the size of the string to buffer
|
|
511
|
-
buffer
|
|
512
|
-
buffer[index + 1] = (size >> 8) & 0xff;
|
|
513
|
-
buffer[index + 2] = (size >> 16) & 0xff;
|
|
514
|
-
buffer[index + 3] = (size >> 24) & 0xff;
|
|
463
|
+
NumberUtils.setInt32LE(buffer, index, size);
|
|
515
464
|
// Update index
|
|
516
465
|
index = index + 4 + size - 1;
|
|
517
466
|
// Write zero
|
|
@@ -536,24 +485,21 @@ function serializeBinary(buffer: Uint8Array, key: string, value: Binary, index:
|
|
|
536
485
|
// Add the deprecated 02 type 4 bytes of size to total
|
|
537
486
|
if (value.sub_type === Binary.SUBTYPE_BYTE_ARRAY) size = size + 4;
|
|
538
487
|
// Write the size of the string to buffer
|
|
539
|
-
|
|
540
|
-
buffer[index++] = (size >> 8) & 0xff;
|
|
541
|
-
buffer[index++] = (size >> 16) & 0xff;
|
|
542
|
-
buffer[index++] = (size >> 24) & 0xff;
|
|
488
|
+
index += NumberUtils.setInt32LE(buffer, index, size);
|
|
543
489
|
// Write the subtype to the buffer
|
|
544
490
|
buffer[index++] = value.sub_type;
|
|
545
491
|
|
|
546
492
|
// If we have binary type 2 the 4 first bytes are the size
|
|
547
493
|
if (value.sub_type === Binary.SUBTYPE_BYTE_ARRAY) {
|
|
548
494
|
size = size - 4;
|
|
549
|
-
|
|
550
|
-
buffer[index++] = (size >> 8) & 0xff;
|
|
551
|
-
buffer[index++] = (size >> 16) & 0xff;
|
|
552
|
-
buffer[index++] = (size >> 24) & 0xff;
|
|
495
|
+
index += NumberUtils.setInt32LE(buffer, index, size);
|
|
553
496
|
}
|
|
554
497
|
|
|
555
|
-
|
|
556
|
-
|
|
498
|
+
if (size <= 16) {
|
|
499
|
+
for (let i = 0; i < size; i++) buffer[index + i] = data[i];
|
|
500
|
+
} else {
|
|
501
|
+
buffer.set(data, index);
|
|
502
|
+
}
|
|
557
503
|
// Adjust the index
|
|
558
504
|
index = index + value.position;
|
|
559
505
|
return index;
|
|
@@ -570,14 +516,11 @@ function serializeSymbol(buffer: Uint8Array, key: string, value: BSONSymbol, ind
|
|
|
570
516
|
// Write the string
|
|
571
517
|
const size = ByteUtils.encodeUTF8Into(buffer, value.value, index + 4) + 1;
|
|
572
518
|
// Write the size of the string to buffer
|
|
573
|
-
buffer
|
|
574
|
-
buffer[index + 1] = (size >> 8) & 0xff;
|
|
575
|
-
buffer[index + 2] = (size >> 16) & 0xff;
|
|
576
|
-
buffer[index + 3] = (size >> 24) & 0xff;
|
|
519
|
+
NumberUtils.setInt32LE(buffer, index, size);
|
|
577
520
|
// Update index
|
|
578
521
|
index = index + 4 + size - 1;
|
|
579
522
|
// Write zero
|
|
580
|
-
buffer[index++] =
|
|
523
|
+
buffer[index++] = 0;
|
|
581
524
|
return index;
|
|
582
525
|
}
|
|
583
526
|
|
|
@@ -624,10 +567,7 @@ function serializeDBRef(
|
|
|
624
567
|
// Calculate object size
|
|
625
568
|
const size = endIndex - startIndex;
|
|
626
569
|
// Write the size
|
|
627
|
-
|
|
628
|
-
buffer[startIndex++] = (size >> 8) & 0xff;
|
|
629
|
-
buffer[startIndex++] = (size >> 16) & 0xff;
|
|
630
|
-
buffer[startIndex++] = (size >> 24) & 0xff;
|
|
570
|
+
startIndex += NumberUtils.setInt32LE(buffer, index, size);
|
|
631
571
|
// Set index
|
|
632
572
|
return endIndex;
|
|
633
573
|
}
|
|
@@ -799,7 +739,7 @@ export function serializeInto(
|
|
|
799
739
|
if (checkKeys) {
|
|
800
740
|
if ('$' === key[0]) {
|
|
801
741
|
throw new BSONError('key ' + key + " must not start with '$'");
|
|
802
|
-
} else if (
|
|
742
|
+
} else if (key.includes('.')) {
|
|
803
743
|
throw new BSONError('key ' + key + " must not contain '.'");
|
|
804
744
|
}
|
|
805
745
|
}
|
|
@@ -907,7 +847,7 @@ export function serializeInto(
|
|
|
907
847
|
if (checkKeys) {
|
|
908
848
|
if ('$' === key[0]) {
|
|
909
849
|
throw new BSONError('key ' + key + " must not start with '$'");
|
|
910
|
-
} else if (
|
|
850
|
+
} else if (key.includes('.')) {
|
|
911
851
|
throw new BSONError('key ' + key + " must not contain '.'");
|
|
912
852
|
}
|
|
913
853
|
}
|
|
@@ -997,9 +937,6 @@ export function serializeInto(
|
|
|
997
937
|
// Final size
|
|
998
938
|
const size = index - startingIndex;
|
|
999
939
|
// Write the size of the object
|
|
1000
|
-
|
|
1001
|
-
buffer[startingIndex++] = (size >> 8) & 0xff;
|
|
1002
|
-
buffer[startingIndex++] = (size >> 16) & 0xff;
|
|
1003
|
-
buffer[startingIndex++] = (size >> 24) & 0xff;
|
|
940
|
+
startingIndex += NumberUtils.setInt32LE(buffer, startingIndex, size);
|
|
1004
941
|
return index;
|
|
1005
942
|
}
|
package/src/utils/byte_utils.ts
CHANGED
|
@@ -7,6 +7,8 @@ export type ByteUtils = {
|
|
|
7
7
|
toLocalBufferType(buffer: Uint8Array | ArrayBufferView | ArrayBuffer): Uint8Array;
|
|
8
8
|
/** Create empty space of size */
|
|
9
9
|
allocate: (size: number) => Uint8Array;
|
|
10
|
+
/** Create empty space of size, use pooled memory when available */
|
|
11
|
+
allocateUnsafe: (size: number) => Uint8Array;
|
|
10
12
|
/** Check if two Uint8Arrays are deep equal */
|
|
11
13
|
equals: (a: Uint8Array, b: Uint8Array) => boolean;
|
|
12
14
|
/** Check if two Uint8Arrays are deep equal */
|
|
@@ -23,8 +25,6 @@ export type ByteUtils = {
|
|
|
23
25
|
fromHex: (hex: string) => Uint8Array;
|
|
24
26
|
/** Create a lowercase hex string from bytes */
|
|
25
27
|
toHex: (buffer: Uint8Array) => string;
|
|
26
|
-
/** Create a Uint8Array containing utf8 code units from a string */
|
|
27
|
-
fromUTF8: (text: string) => Uint8Array;
|
|
28
28
|
/** Create a string from utf8 code units, fatal=true will throw an error if UTF-8 bytes are invalid, fatal=false will insert replacement characters */
|
|
29
29
|
toUTF8: (buffer: Uint8Array, start: number, end: number, fatal: boolean) => string;
|
|
30
30
|
/** Get the utf8 code unit count from a string if it were to be transformed to utf8 */
|
|
@@ -53,9 +53,3 @@ const hasGlobalBuffer = typeof Buffer === 'function' && Buffer.prototype?._isBuf
|
|
|
53
53
|
* @internal
|
|
54
54
|
*/
|
|
55
55
|
export const ByteUtils: ByteUtils = hasGlobalBuffer ? nodeJsByteUtils : webByteUtils;
|
|
56
|
-
|
|
57
|
-
export class BSONDataView extends DataView {
|
|
58
|
-
static fromUint8Array(input: Uint8Array) {
|
|
59
|
-
return new DataView(input.buffer, input.byteOffset, input.byteLength);
|
|
60
|
-
}
|
|
61
|
-
}
|
package/src/utils/latin.ts
CHANGED
|
@@ -13,7 +13,11 @@
|
|
|
13
13
|
* @param end - The index to stop searching the uint8array
|
|
14
14
|
* @returns string if all bytes are within the basic latin range, otherwise null
|
|
15
15
|
*/
|
|
16
|
-
export function
|
|
16
|
+
export function tryReadBasicLatin(
|
|
17
|
+
uint8array: Uint8Array,
|
|
18
|
+
start: number,
|
|
19
|
+
end: number
|
|
20
|
+
): string | null {
|
|
17
21
|
if (uint8array.length === 0) {
|
|
18
22
|
return '';
|
|
19
23
|
}
|
|
@@ -59,3 +63,42 @@ export function tryLatin(uint8array: Uint8Array, start: number, end: number): st
|
|
|
59
63
|
|
|
60
64
|
return String.fromCharCode(...latinBytes);
|
|
61
65
|
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* This function is an optimization for writing small basic latin strings.
|
|
69
|
+
* @internal
|
|
70
|
+
* @remarks
|
|
71
|
+
* ### Important characteristics:
|
|
72
|
+
* - If the string length is 0 return 0, do not perform any work
|
|
73
|
+
* - If a string is longer than 25 code units return null
|
|
74
|
+
* - If any code unit exceeds 128 this function returns null
|
|
75
|
+
*
|
|
76
|
+
* @param destination - The uint8array to serialize the string to
|
|
77
|
+
* @param source - The string to turn into UTF-8 bytes if it fits in the basic latin range
|
|
78
|
+
* @param offset - The position in the destination to begin writing bytes to
|
|
79
|
+
* @returns the number of bytes written to destination if all code units are below 128, otherwise null
|
|
80
|
+
*/
|
|
81
|
+
export function tryWriteBasicLatin(
|
|
82
|
+
destination: Uint8Array,
|
|
83
|
+
source: string,
|
|
84
|
+
offset: number
|
|
85
|
+
): number | null {
|
|
86
|
+
if (source.length === 0) return 0;
|
|
87
|
+
|
|
88
|
+
if (source.length > 25) return null;
|
|
89
|
+
|
|
90
|
+
if (destination.length - offset < source.length) return null;
|
|
91
|
+
|
|
92
|
+
for (
|
|
93
|
+
let charOffset = 0, destinationOffset = offset;
|
|
94
|
+
charOffset < source.length;
|
|
95
|
+
charOffset++, destinationOffset++
|
|
96
|
+
) {
|
|
97
|
+
const char = source.charCodeAt(charOffset);
|
|
98
|
+
if (char > 127) return null;
|
|
99
|
+
|
|
100
|
+
destination[destinationOffset] = char;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return source.length;
|
|
104
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BSONError } from '../error';
|
|
2
2
|
import { validateUtf8 } from '../validate_utf8';
|
|
3
|
-
import {
|
|
3
|
+
import { tryReadBasicLatin, tryWriteBasicLatin } from './latin';
|
|
4
4
|
|
|
5
5
|
type NodeJsEncoding = 'base64' | 'hex' | 'utf8' | 'binary';
|
|
6
6
|
type NodeJsBuffer = ArrayBufferView &
|
|
@@ -12,6 +12,7 @@ type NodeJsBuffer = ArrayBufferView &
|
|
|
12
12
|
};
|
|
13
13
|
type NodeJsBufferConstructor = Omit<Uint8ArrayConstructor, 'from'> & {
|
|
14
14
|
alloc: (size: number) => NodeJsBuffer;
|
|
15
|
+
allocUnsafe: (size: number) => NodeJsBuffer;
|
|
15
16
|
from(array: number[]): NodeJsBuffer;
|
|
16
17
|
from(array: Uint8Array): NodeJsBuffer;
|
|
17
18
|
from(array: ArrayBuffer): NodeJsBuffer;
|
|
@@ -89,6 +90,10 @@ export const nodeJsByteUtils = {
|
|
|
89
90
|
return Buffer.alloc(size);
|
|
90
91
|
},
|
|
91
92
|
|
|
93
|
+
allocateUnsafe(size: number): NodeJsBuffer {
|
|
94
|
+
return Buffer.allocUnsafe(size);
|
|
95
|
+
},
|
|
96
|
+
|
|
92
97
|
equals(a: Uint8Array, b: Uint8Array): boolean {
|
|
93
98
|
return nodeJsByteUtils.toLocalBufferType(a).equals(b);
|
|
94
99
|
},
|
|
@@ -123,12 +128,8 @@ export const nodeJsByteUtils = {
|
|
|
123
128
|
return nodeJsByteUtils.toLocalBufferType(buffer).toString('hex');
|
|
124
129
|
},
|
|
125
130
|
|
|
126
|
-
fromUTF8(text: string): NodeJsBuffer {
|
|
127
|
-
return Buffer.from(text, 'utf8');
|
|
128
|
-
},
|
|
129
|
-
|
|
130
131
|
toUTF8(buffer: Uint8Array, start: number, end: number, fatal: boolean): string {
|
|
131
|
-
const basicLatin = end - start <= 20 ?
|
|
132
|
+
const basicLatin = end - start <= 20 ? tryReadBasicLatin(buffer, start, end) : null;
|
|
132
133
|
if (basicLatin != null) {
|
|
133
134
|
return basicLatin;
|
|
134
135
|
}
|
|
@@ -153,6 +154,11 @@ export const nodeJsByteUtils = {
|
|
|
153
154
|
},
|
|
154
155
|
|
|
155
156
|
encodeUTF8Into(buffer: Uint8Array, source: string, byteOffset: number): number {
|
|
157
|
+
const latinBytesWritten = tryWriteBasicLatin(buffer, source, byteOffset);
|
|
158
|
+
if (latinBytesWritten != null) {
|
|
159
|
+
return latinBytesWritten;
|
|
160
|
+
}
|
|
161
|
+
|
|
156
162
|
return nodeJsByteUtils.toLocalBufferType(buffer).write(source, byteOffset, undefined, 'utf8');
|
|
157
163
|
},
|
|
158
164
|
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
const FLOAT = new Float64Array(1);
|
|
2
|
+
const FLOAT_BYTES = new Uint8Array(FLOAT.buffer, 0, 8);
|
|
3
|
+
|
|
4
|
+
FLOAT[0] = -1;
|
|
5
|
+
// Little endian [0, 0, 0, 0, 0, 0, 240, 191]
|
|
6
|
+
// Big endian [191, 240, 0, 0, 0, 0, 0, 0]
|
|
7
|
+
const isBigEndian = FLOAT_BYTES[7] === 0;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Number parsing and serializing utilities.
|
|
11
|
+
*
|
|
12
|
+
* @internal
|
|
13
|
+
*/
|
|
14
|
+
export const NumberUtils = {
|
|
15
|
+
/** Reads a little-endian 32-bit integer from source */
|
|
16
|
+
getInt32LE(source: Uint8Array, offset: number): number {
|
|
17
|
+
return (
|
|
18
|
+
source[offset] |
|
|
19
|
+
(source[offset + 1] << 8) |
|
|
20
|
+
(source[offset + 2] << 16) |
|
|
21
|
+
(source[offset + 3] << 24)
|
|
22
|
+
);
|
|
23
|
+
},
|
|
24
|
+
|
|
25
|
+
/** Reads a little-endian 32-bit unsigned integer from source */
|
|
26
|
+
getUint32LE(source: Uint8Array, offset: number): number {
|
|
27
|
+
return (
|
|
28
|
+
source[offset] +
|
|
29
|
+
source[offset + 1] * 256 +
|
|
30
|
+
source[offset + 2] * 65536 +
|
|
31
|
+
source[offset + 3] * 16777216
|
|
32
|
+
);
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
/** Reads a big-endian 32-bit integer from source */
|
|
36
|
+
getUint32BE(source: Uint8Array, offset: number): number {
|
|
37
|
+
return (
|
|
38
|
+
source[offset + 3] +
|
|
39
|
+
source[offset + 2] * 256 +
|
|
40
|
+
source[offset + 1] * 65536 +
|
|
41
|
+
source[offset] * 16777216
|
|
42
|
+
);
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
/** Reads a little-endian 64-bit integer from source */
|
|
46
|
+
getBigInt64LE(source: Uint8Array, offset: number): bigint {
|
|
47
|
+
// eslint-disable-next-line no-restricted-globals
|
|
48
|
+
const hi = BigInt(
|
|
49
|
+
source[offset + 4] +
|
|
50
|
+
source[offset + 5] * 256 +
|
|
51
|
+
source[offset + 6] * 65536 +
|
|
52
|
+
(source[offset + 7] << 24)
|
|
53
|
+
); // Overflow
|
|
54
|
+
|
|
55
|
+
// eslint-disable-next-line no-restricted-globals
|
|
56
|
+
const lo = BigInt(
|
|
57
|
+
source[offset] +
|
|
58
|
+
source[offset + 1] * 256 +
|
|
59
|
+
source[offset + 2] * 65536 +
|
|
60
|
+
source[offset + 3] * 16777216
|
|
61
|
+
);
|
|
62
|
+
// eslint-disable-next-line no-restricted-globals
|
|
63
|
+
return (hi << BigInt(32)) + lo;
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
/** Reads a little-endian 64-bit float from source */
|
|
67
|
+
getFloat64LE: isBigEndian
|
|
68
|
+
? (source: Uint8Array, offset: number) => {
|
|
69
|
+
FLOAT_BYTES[7] = source[offset];
|
|
70
|
+
FLOAT_BYTES[6] = source[offset + 1];
|
|
71
|
+
FLOAT_BYTES[5] = source[offset + 2];
|
|
72
|
+
FLOAT_BYTES[4] = source[offset + 3];
|
|
73
|
+
FLOAT_BYTES[3] = source[offset + 4];
|
|
74
|
+
FLOAT_BYTES[2] = source[offset + 5];
|
|
75
|
+
FLOAT_BYTES[1] = source[offset + 6];
|
|
76
|
+
FLOAT_BYTES[0] = source[offset + 7];
|
|
77
|
+
return FLOAT[0];
|
|
78
|
+
}
|
|
79
|
+
: (source: Uint8Array, offset: number) => {
|
|
80
|
+
FLOAT_BYTES[0] = source[offset];
|
|
81
|
+
FLOAT_BYTES[1] = source[offset + 1];
|
|
82
|
+
FLOAT_BYTES[2] = source[offset + 2];
|
|
83
|
+
FLOAT_BYTES[3] = source[offset + 3];
|
|
84
|
+
FLOAT_BYTES[4] = source[offset + 4];
|
|
85
|
+
FLOAT_BYTES[5] = source[offset + 5];
|
|
86
|
+
FLOAT_BYTES[6] = source[offset + 6];
|
|
87
|
+
FLOAT_BYTES[7] = source[offset + 7];
|
|
88
|
+
return FLOAT[0];
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
/** Writes a big-endian 32-bit integer to destination, can be signed or unsigned */
|
|
92
|
+
setInt32BE(destination: Uint8Array, offset: number, value: number): 4 {
|
|
93
|
+
destination[offset + 3] = value;
|
|
94
|
+
value >>>= 8;
|
|
95
|
+
destination[offset + 2] = value;
|
|
96
|
+
value >>>= 8;
|
|
97
|
+
destination[offset + 1] = value;
|
|
98
|
+
value >>>= 8;
|
|
99
|
+
destination[offset] = value;
|
|
100
|
+
return 4;
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
/** Writes a little-endian 32-bit integer to destination, can be signed or unsigned */
|
|
104
|
+
setInt32LE(destination: Uint8Array, offset: number, value: number): 4 {
|
|
105
|
+
destination[offset] = value;
|
|
106
|
+
value >>>= 8;
|
|
107
|
+
destination[offset + 1] = value;
|
|
108
|
+
value >>>= 8;
|
|
109
|
+
destination[offset + 2] = value;
|
|
110
|
+
value >>>= 8;
|
|
111
|
+
destination[offset + 3] = value;
|
|
112
|
+
return 4;
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
/** Write a little-endian 64-bit integer to source */
|
|
116
|
+
setBigInt64LE(destination: Uint8Array, offset: number, value: bigint): 8 {
|
|
117
|
+
/* eslint-disable-next-line no-restricted-globals -- This is allowed here as useBigInt64=true */
|
|
118
|
+
const mask32bits = BigInt(0xffff_ffff);
|
|
119
|
+
|
|
120
|
+
/** lower 32 bits */
|
|
121
|
+
let lo = Number(value & mask32bits);
|
|
122
|
+
destination[offset] = lo;
|
|
123
|
+
lo >>= 8;
|
|
124
|
+
destination[offset + 1] = lo;
|
|
125
|
+
lo >>= 8;
|
|
126
|
+
destination[offset + 2] = lo;
|
|
127
|
+
lo >>= 8;
|
|
128
|
+
destination[offset + 3] = lo;
|
|
129
|
+
|
|
130
|
+
/*
|
|
131
|
+
eslint-disable-next-line no-restricted-globals
|
|
132
|
+
-- This is allowed here as useBigInt64=true
|
|
133
|
+
|
|
134
|
+
upper 32 bits
|
|
135
|
+
*/
|
|
136
|
+
let hi = Number((value >> BigInt(32)) & mask32bits);
|
|
137
|
+
destination[offset + 4] = hi;
|
|
138
|
+
hi >>= 8;
|
|
139
|
+
destination[offset + 5] = hi;
|
|
140
|
+
hi >>= 8;
|
|
141
|
+
destination[offset + 6] = hi;
|
|
142
|
+
hi >>= 8;
|
|
143
|
+
destination[offset + 7] = hi;
|
|
144
|
+
|
|
145
|
+
return 8;
|
|
146
|
+
},
|
|
147
|
+
|
|
148
|
+
/** Writes a little-endian 64-bit float to destination */
|
|
149
|
+
setFloat64LE: isBigEndian
|
|
150
|
+
? (destination: Uint8Array, offset: number, value: number) => {
|
|
151
|
+
FLOAT[0] = value;
|
|
152
|
+
destination[offset] = FLOAT_BYTES[7];
|
|
153
|
+
destination[offset + 1] = FLOAT_BYTES[6];
|
|
154
|
+
destination[offset + 2] = FLOAT_BYTES[5];
|
|
155
|
+
destination[offset + 3] = FLOAT_BYTES[4];
|
|
156
|
+
destination[offset + 4] = FLOAT_BYTES[3];
|
|
157
|
+
destination[offset + 5] = FLOAT_BYTES[2];
|
|
158
|
+
destination[offset + 6] = FLOAT_BYTES[1];
|
|
159
|
+
destination[offset + 7] = FLOAT_BYTES[0];
|
|
160
|
+
return 8;
|
|
161
|
+
}
|
|
162
|
+
: (destination: Uint8Array, offset: number, value: number) => {
|
|
163
|
+
FLOAT[0] = value;
|
|
164
|
+
destination[offset] = FLOAT_BYTES[0];
|
|
165
|
+
destination[offset + 1] = FLOAT_BYTES[1];
|
|
166
|
+
destination[offset + 2] = FLOAT_BYTES[2];
|
|
167
|
+
destination[offset + 3] = FLOAT_BYTES[3];
|
|
168
|
+
destination[offset + 4] = FLOAT_BYTES[4];
|
|
169
|
+
destination[offset + 5] = FLOAT_BYTES[5];
|
|
170
|
+
destination[offset + 6] = FLOAT_BYTES[6];
|
|
171
|
+
destination[offset + 7] = FLOAT_BYTES[7];
|
|
172
|
+
return 8;
|
|
173
|
+
}
|
|
174
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BSONError } from '../error';
|
|
2
|
-
import {
|
|
2
|
+
import { tryReadBasicLatin } from './latin';
|
|
3
3
|
|
|
4
4
|
type TextDecoder = {
|
|
5
5
|
readonly encoding: string;
|
|
@@ -109,6 +109,10 @@ export const webByteUtils = {
|
|
|
109
109
|
return new Uint8Array(size);
|
|
110
110
|
},
|
|
111
111
|
|
|
112
|
+
allocateUnsafe(size: number): Uint8Array {
|
|
113
|
+
return webByteUtils.allocate(size);
|
|
114
|
+
},
|
|
115
|
+
|
|
112
116
|
equals(a: Uint8Array, b: Uint8Array): boolean {
|
|
113
117
|
if (a.byteLength !== b.byteLength) {
|
|
114
118
|
return false;
|
|
@@ -169,12 +173,8 @@ export const webByteUtils = {
|
|
|
169
173
|
return Array.from(uint8array, byte => byte.toString(16).padStart(2, '0')).join('');
|
|
170
174
|
},
|
|
171
175
|
|
|
172
|
-
fromUTF8(text: string): Uint8Array {
|
|
173
|
-
return new TextEncoder().encode(text);
|
|
174
|
-
},
|
|
175
|
-
|
|
176
176
|
toUTF8(uint8array: Uint8Array, start: number, end: number, fatal: boolean): string {
|
|
177
|
-
const basicLatin = end - start <= 20 ?
|
|
177
|
+
const basicLatin = end - start <= 20 ? tryReadBasicLatin(uint8array, start, end) : null;
|
|
178
178
|
if (basicLatin != null) {
|
|
179
179
|
return basicLatin;
|
|
180
180
|
}
|
|
@@ -190,12 +190,12 @@ export const webByteUtils = {
|
|
|
190
190
|
},
|
|
191
191
|
|
|
192
192
|
utf8ByteLength(input: string): number {
|
|
193
|
-
return
|
|
193
|
+
return new TextEncoder().encode(input).byteLength;
|
|
194
194
|
},
|
|
195
195
|
|
|
196
|
-
encodeUTF8Into(
|
|
197
|
-
const bytes =
|
|
198
|
-
|
|
196
|
+
encodeUTF8Into(uint8array: Uint8Array, source: string, byteOffset: number): number {
|
|
197
|
+
const bytes = new TextEncoder().encode(source);
|
|
198
|
+
uint8array.set(bytes, byteOffset);
|
|
199
199
|
return bytes.byteLength;
|
|
200
200
|
},
|
|
201
201
|
|