@@ -13,7 +13,15 @@ import type { MinKey } from '../min_key';
13
13
import type { ObjectId } from '../objectid' ;
14
14
import type { BSONRegExp } from '../regexp' ;
15
15
import { ByteUtils } from '../utils/byte_utils' ;
16
- import { isBigInt64Array , isBigUInt64Array , isDate , isMap , isRegExp , isUint8Array } from './utils' ;
16
+ import {
17
+ isAnyArrayBuffer ,
18
+ isBigInt64Array ,
19
+ isBigUInt64Array ,
20
+ isDate ,
21
+ isMap ,
22
+ isRegExp ,
23
+ isUint8Array
24
+ } from './utils' ;
17
25
18
26
/** @public */
19
27
export interface SerializeOptions {
@@ -270,18 +278,18 @@ function serializeObject(
270
278
key : string ,
271
279
value : Document ,
272
280
index : number ,
273
- checkKeys = false ,
274
- depth = 0 ,
275
- serializeFunctions = false ,
276
- ignoreUndefined = true ,
277
- path : Document [ ] = [ ]
281
+ checkKeys : boolean ,
282
+ depth : number ,
283
+ serializeFunctions : boolean ,
284
+ ignoreUndefined : boolean ,
285
+ path : Set < Document >
278
286
) {
279
- for ( let i = 0 ; i < path . length ; i ++ ) {
280
- if ( path [ i ] === value ) throw new BSONError ( 'cyclic dependency detected ' ) ;
287
+ if ( path . has ( value ) ) {
288
+ throw new BSONError ( 'Cannot convert circular structure to BSON ' ) ;
281
289
}
282
290
283
- // Push value to stack
284
- path . push ( value ) ;
291
+ path . add ( value ) ;
292
+
285
293
// Write the type
286
294
buffer [ index ++ ] = Array . isArray ( value ) ? constants . BSON_DATA_ARRAY : constants . BSON_DATA_OBJECT ;
287
295
// Number of written bytes
@@ -299,8 +307,9 @@ function serializeObject(
299
307
ignoreUndefined ,
300
308
path
301
309
) ;
302
- // Pop stack
303
- path . pop ( ) ;
310
+
311
+ path . delete ( value ) ;
312
+
304
313
return endIndex ;
305
314
}
306
315
@@ -410,7 +419,8 @@ function serializeCode(
410
419
checkKeys = false ,
411
420
depth = 0 ,
412
421
serializeFunctions = false ,
413
- ignoreUndefined = true
422
+ ignoreUndefined = true ,
423
+ path : Set < Document >
414
424
) {
415
425
if ( value . scope && typeof value . scope === 'object' ) {
416
426
// Write the type
@@ -441,7 +451,6 @@ function serializeCode(
441
451
// Write the
442
452
index = index + codeSize + 4 ;
443
453
444
- //
445
454
// Serialize the scope value
446
455
const endIndex = serializeInto (
447
456
buffer ,
@@ -450,7 +459,8 @@ function serializeCode(
450
459
index ,
451
460
depth + 1 ,
452
461
serializeFunctions ,
453
- ignoreUndefined
462
+ ignoreUndefined ,
463
+ path
454
464
) ;
455
465
index = endIndex - 1 ;
456
466
@@ -555,7 +565,8 @@ function serializeDBRef(
555
565
value : DBRef ,
556
566
index : number ,
557
567
depth : number ,
558
- serializeFunctions : boolean
568
+ serializeFunctions : boolean ,
569
+ path : Set < Document >
559
570
) {
560
571
// Write the type
561
572
buffer [ index ++ ] = constants . BSON_DATA_OBJECT ;
@@ -577,7 +588,16 @@ function serializeDBRef(
577
588
}
578
589
579
590
output = Object . assign ( output , value . fields ) ;
580
- const endIndex = serializeInto ( buffer , output , false , index , depth + 1 , serializeFunctions ) ;
591
+ const endIndex = serializeInto (
592
+ buffer ,
593
+ output ,
594
+ false ,
595
+ index ,
596
+ depth + 1 ,
597
+ serializeFunctions ,
598
+ true ,
599
+ path
600
+ ) ;
581
601
582
602
// Calculate object size
583
603
const size = endIndex - startIndex ;
@@ -593,18 +613,48 @@ function serializeDBRef(
593
613
export function serializeInto (
594
614
buffer : Uint8Array ,
595
615
object : Document ,
596
- checkKeys = false ,
597
- startingIndex = 0 ,
598
- depth = 0 ,
599
- serializeFunctions = false ,
600
- ignoreUndefined = true ,
601
- path : Document [ ] = [ ]
616
+ checkKeys : boolean ,
617
+ startingIndex : number ,
618
+ depth : number ,
619
+ serializeFunctions : boolean ,
620
+ ignoreUndefined : boolean ,
621
+ path : Set < Document > | null
602
622
) : number {
603
- startingIndex = startingIndex || 0 ;
604
- path = path || [ ] ;
623
+ if ( path == null ) {
624
+ // We are at the root input
625
+ if ( object == null ) {
626
+ // ONLY the root should turn into an empty document
627
+ // BSON Empty document has a size of 5 (LE)
628
+ buffer [ 0 ] = 0x05 ;
629
+ buffer [ 1 ] = 0x00 ;
630
+ buffer [ 2 ] = 0x00 ;
631
+ buffer [ 3 ] = 0x00 ;
632
+ // All documents end with null terminator
633
+ buffer [ 4 ] = 0x00 ;
634
+ return 5 ;
635
+ }
636
+
637
+ if ( Array . isArray ( object ) ) {
638
+ throw new BSONError ( 'serialize does not support an array as the root input' ) ;
639
+ }
640
+ if ( typeof object !== 'object' ) {
641
+ throw new BSONError ( 'serialize does not support non-object as the root input' ) ;
642
+ } else if ( '_bsontype' in object && typeof object . _bsontype === 'string' ) {
643
+ throw new BSONError ( `BSON types cannot be serialized as a document` ) ;
644
+ } else if (
645
+ isDate ( object ) ||
646
+ isRegExp ( object ) ||
647
+ isUint8Array ( object ) ||
648
+ isAnyArrayBuffer ( object )
649
+ ) {
650
+ throw new BSONError ( `date, regexp, typedarray, and arraybuffer cannot be BSON documents` ) ;
651
+ }
652
+
653
+ path = new Set ( ) ;
654
+ }
605
655
606
656
// Push the object to the path
607
- path . push ( object ) ;
657
+ path . add ( object ) ;
608
658
609
659
// Start place to serialize into
610
660
let index = startingIndex + 4 ;
@@ -674,14 +724,15 @@ export function serializeInto(
674
724
checkKeys ,
675
725
depth ,
676
726
serializeFunctions ,
677
- ignoreUndefined
727
+ ignoreUndefined ,
728
+ path
678
729
) ;
679
730
} else if ( value [ '_bsontype' ] === 'Binary' ) {
680
731
index = serializeBinary ( buffer , key , value , index ) ;
681
732
} else if ( value [ '_bsontype' ] === 'Symbol' ) {
682
733
index = serializeSymbol ( buffer , key , value , index ) ;
683
734
} else if ( value [ '_bsontype' ] === 'DBRef' ) {
684
- index = serializeDBRef ( buffer , key , value , index , depth , serializeFunctions ) ;
735
+ index = serializeDBRef ( buffer , key , value , index , depth , serializeFunctions , path ) ;
685
736
} else if ( value [ '_bsontype' ] === 'BSONRegExp' ) {
686
737
index = serializeBSONRegExp ( buffer , key , value , index ) ;
687
738
} else if ( value [ '_bsontype' ] === 'Int32' ) {
@@ -772,7 +823,8 @@ export function serializeInto(
772
823
checkKeys ,
773
824
depth ,
774
825
serializeFunctions ,
775
- ignoreUndefined
826
+ ignoreUndefined ,
827
+ path
776
828
) ;
777
829
} else if ( typeof value === 'function' && serializeFunctions ) {
778
830
index = serializeFunction ( buffer , key , value , index ) ;
@@ -781,7 +833,7 @@ export function serializeInto(
781
833
} else if ( value [ '_bsontype' ] === 'Symbol' ) {
782
834
index = serializeSymbol ( buffer , key , value , index ) ;
783
835
} else if ( value [ '_bsontype' ] === 'DBRef' ) {
784
- index = serializeDBRef ( buffer , key , value , index , depth , serializeFunctions ) ;
836
+ index = serializeDBRef ( buffer , key , value , index , depth , serializeFunctions , path ) ;
785
837
} else if ( value [ '_bsontype' ] === 'BSONRegExp' ) {
786
838
index = serializeBSONRegExp ( buffer , key , value , index ) ;
787
839
} else if ( value [ '_bsontype' ] === 'Int32' ) {
@@ -876,7 +928,8 @@ export function serializeInto(
876
928
checkKeys ,
877
929
depth ,
878
930
serializeFunctions ,
879
- ignoreUndefined
931
+ ignoreUndefined ,
932
+ path
880
933
) ;
881
934
} else if ( typeof value === 'function' && serializeFunctions ) {
882
935
index = serializeFunction ( buffer , key , value , index ) ;
@@ -885,7 +938,7 @@ export function serializeInto(
885
938
} else if ( value [ '_bsontype' ] === 'Symbol' ) {
886
939
index = serializeSymbol ( buffer , key , value , index ) ;
887
940
} else if ( value [ '_bsontype' ] === 'DBRef' ) {
888
- index = serializeDBRef ( buffer , key , value , index , depth , serializeFunctions ) ;
941
+ index = serializeDBRef ( buffer , key , value , index , depth , serializeFunctions , path ) ;
889
942
} else if ( value [ '_bsontype' ] === 'BSONRegExp' ) {
890
943
index = serializeBSONRegExp ( buffer , key , value , index ) ;
891
944
} else if ( value [ '_bsontype' ] === 'Int32' ) {
@@ -899,7 +952,7 @@ export function serializeInto(
899
952
}
900
953
901
954
// Remove the path
902
- path . pop ( ) ;
955
+ path . delete ( object ) ;
903
956
904
957
// Final padding byte for object
905
958
buffer [ index ++ ] = 0x00 ;
0 commit comments