1414*/
1515
1616using System ;
17+ using System . Collections . Generic ;
1718using System . Globalization ;
1819using System . IO ;
1920
@@ -30,6 +31,7 @@ public class BsonBinaryReader : BsonReader
3031#pragma warning restore CA2213 // Disposable never disposed
3132 private readonly BsonStream _bsonStream ;
3233 private BsonBinaryReaderContext _context ;
34+ private readonly Stack < BsonBinaryReaderContext > _contextStack = new ( 4 ) ;
3335
3436 // constructors
3537 /// <summary>
@@ -61,7 +63,7 @@ public BsonBinaryReader(Stream stream, BsonBinaryReaderSettings settings)
6163 _baseStream = stream ;
6264 _bsonStream = ( stream as BsonStream ) ?? new BsonStreamAdapter ( stream ) ;
6365
64- _context = new BsonBinaryReaderContext ( null , ContextType . TopLevel , 0 , 0 ) ;
66+ _context = new BsonBinaryReaderContext ( ContextType . TopLevel , 0 , 0 ) ;
6567 }
6668
6769 // public properties
@@ -109,10 +111,8 @@ public override void Close()
109111 /// Gets a bookmark to the reader's current position and state.
110112 /// </summary>
111113 /// <returns>A bookmark.</returns>
112- public override BsonReaderBookmark GetBookmark ( )
113- {
114- return new BsonBinaryReaderBookmark ( State , CurrentBsonType , CurrentName , _context , _bsonStream . Position ) ;
115- }
114+ public override BsonReaderBookmark GetBookmark ( ) =>
115+ new BsonBinaryReaderBookmark ( State , CurrentBsonType , CurrentName , _context , _contextStack , _bsonStream . Position ) ;
116116
117117 /// <summary>
118118 /// Determines whether this reader is at end of file.
@@ -201,12 +201,11 @@ public override BsonType ReadBsonType()
201201
202202 if ( _context . ContextType == ContextType . Array )
203203 {
204- _context . CurrentArrayIndex ++ ;
204+ _context . ArrayIndex ++ ;
205205 }
206206
207207 try
208208 {
209-
210209 CurrentBsonType = _bsonStream . ReadBsonType ( ) ;
211210 }
212211 catch ( FormatException ex )
@@ -342,7 +341,8 @@ public override void ReadEndArray()
342341 ThrowInvalidState ( "ReadEndArray" , BsonReaderState . EndOfArray ) ;
343342 }
344343
345- _context = _context . PopContext ( _bsonStream . Position ) ;
344+ PopContext ( ) ;
345+
346346 switch ( _context . ContextType )
347347 {
348348 case ContextType . Array : State = BsonReaderState . Type ; break ;
@@ -371,10 +371,10 @@ public override void ReadEndDocument()
371371 ThrowInvalidState ( "ReadEndDocument" , BsonReaderState . EndOfDocument ) ;
372372 }
373373
374- _context = _context . PopContext ( _bsonStream . Position ) ;
374+ PopContext ( ) ;
375375 if ( _context . ContextType == ContextType . JavaScriptWithScope )
376376 {
377- _context = _context . PopContext ( _bsonStream . Position ) ; // JavaScriptWithScope
377+ PopContext ( ) ; // JavaScriptWithScope
378378 }
379379 switch ( _context . ContextType )
380380 {
@@ -432,7 +432,9 @@ public override string ReadJavaScriptWithScope()
432432
433433 var startPosition = _bsonStream . Position ; // position of size field
434434 var size = ReadSize ( ) ;
435- _context = new BsonBinaryReaderContext ( _context , ContextType . JavaScriptWithScope , startPosition , size ) ;
435+
436+ PushContext ( new ( ContextType . JavaScriptWithScope , startPosition , size ) ) ;
437+
436438 var code = _bsonStream . ReadString ( Settings . Encoding ) ;
437439
438440 State = BsonReaderState . ScopeDocument ;
@@ -486,7 +488,7 @@ public override string ReadName(INameDecoder nameDecoder)
486488
487489 if ( _context . ContextType == ContextType . Document )
488490 {
489- _context . CurrentElementName = CurrentName ;
491+ _context . ElementName = CurrentName ;
490492 }
491493
492494 return CurrentName ;
@@ -553,7 +555,7 @@ public override IByteBuffer ReadRawBsonDocument()
553555
554556 if ( _context . ContextType == ContextType . JavaScriptWithScope )
555557 {
556- _context = _context . PopContext ( _bsonStream . Position ) ; // JavaScriptWithScope
558+ PopContext ( ) ; // JavaScriptWithScope
557559 }
558560 switch ( _context . ContextType )
559561 {
@@ -590,7 +592,9 @@ public override void ReadStartArray()
590592
591593 var startPosition = _bsonStream . Position ; // position of size field
592594 var size = ReadSize ( ) ;
593- _context = new BsonBinaryReaderContext ( _context , ContextType . Array , startPosition , size ) ;
595+
596+ PushContext ( new ( ContextType . Array , startPosition , size ) ) ;
597+
594598 State = BsonReaderState . Type ;
595599 }
596600
@@ -605,7 +609,9 @@ public override void ReadStartDocument()
605609 var contextType = ( State == BsonReaderState . ScopeDocument ) ? ContextType . ScopeDocument : ContextType . Document ;
606610 var startPosition = _bsonStream . Position ; // position of size field
607611 var size = ReadSize ( ) ;
608- _context = new BsonBinaryReaderContext ( _context , contextType , startPosition , size ) ;
612+
613+ PushContext ( new ( contextType , startPosition , size ) ) ;
614+
609615 State = BsonReaderState . Type ;
610616 }
611617
@@ -662,10 +668,11 @@ public override void ReadUndefined()
662668 public override void ReturnToBookmark ( BsonReaderBookmark bookmark )
663669 {
664670 var binaryReaderBookmark = ( BsonBinaryReaderBookmark ) bookmark ;
671+
665672 State = binaryReaderBookmark . State ;
666673 CurrentBsonType = binaryReaderBookmark . CurrentBsonType ;
667674 CurrentName = binaryReaderBookmark . CurrentName ;
668- _context = binaryReaderBookmark . CloneContext ( ) ;
675+ _context = binaryReaderBookmark . RestoreContext ( _contextStack ) ;
669676 _bsonStream . Position = binaryReaderBookmark . Position ;
670677 }
671678
@@ -686,7 +693,7 @@ public override void SkipName()
686693
687694 if ( _context . ContextType == ContextType . Document )
688695 {
689- _context . CurrentElementName = CurrentName ;
696+ _context . ElementName = CurrentName ;
690697 }
691698 }
692699
@@ -745,7 +752,7 @@ protected override void Dispose(bool disposing)
745752 {
746753 Close ( ) ;
747754 }
748- catch { } // ignore exceptions
755+ catch { /* ignore exceptions */ }
749756 }
750757 base . Dispose ( disposing ) ;
751758 }
@@ -767,35 +774,41 @@ private string GenerateDottedElementName()
767774 }
768775 else if ( _context . ContextType == ContextType . Array )
769776 {
770- elementName = _context . CurrentArrayIndex . ToString ( NumberFormatInfo . InvariantInfo ) ;
777+ elementName = _context . ArrayIndex . ToString ( NumberFormatInfo . InvariantInfo ) ;
771778 }
772779 else
773780 {
774781 elementName = "?" ;
775782 }
776783
777- return GenerateDottedElementName ( _context . ParentContext , elementName ) ;
784+ return GenerateDottedElementName ( _contextStack . ToArray ( ) , 0 , elementName ) ;
778785 }
779786
780- private string GenerateDottedElementName ( BsonBinaryReaderContext context , string elementName )
787+ private string GenerateDottedElementName ( BsonBinaryReaderContext [ ] contexts , int currentContextIndex , string elementName )
781788 {
789+ if ( currentContextIndex >= contexts . Length )
790+ return elementName ;
791+
792+ var context = contexts [ currentContextIndex ] ;
793+ var nextIndex = currentContextIndex + 1 ;
794+
782795 if ( context . ContextType == ContextType . Document )
783796 {
784- return GenerateDottedElementName ( context . ParentContext , ( context . CurrentElementName ?? "?" ) + "." + elementName ) ;
785- }
786- else if ( context . ContextType == ContextType . Array )
787- {
788- var indexElementName = context . CurrentArrayIndex . ToString ( NumberFormatInfo . InvariantInfo ) ;
789- return GenerateDottedElementName ( context . ParentContext , indexElementName + "." + elementName ) ;
797+ return GenerateDottedElementName ( contexts , nextIndex , ( context . ElementName ?? "?" ) + "." + elementName ) ;
790798 }
791- else if ( context . ParentContext != null )
799+
800+ if ( context . ContextType == ContextType . Array )
792801 {
793- return GenerateDottedElementName ( context . ParentContext , "?." + elementName ) ;
802+ var indexElementName = context . ArrayIndex . ToString ( NumberFormatInfo . InvariantInfo ) ;
803+ return GenerateDottedElementName ( contexts , nextIndex , indexElementName + "." + elementName ) ;
794804 }
795- else
805+
806+ if ( nextIndex < contexts . Length )
796807 {
797- return elementName ;
808+ return GenerateDottedElementName ( contexts , nextIndex , "?." + elementName ) ;
798809 }
810+
811+ return elementName ;
799812 }
800813
801814 private BsonReaderState GetNextState ( )
@@ -813,6 +826,23 @@ private BsonReaderState GetNextState()
813826 }
814827 }
815828
829+ private void PopContext ( )
830+ {
831+ var actualSize = _bsonStream . Position - _context . StartPosition ;
832+ if ( actualSize != _context . Size )
833+ {
834+ throw new FormatException ( $ "Expected size to be { _context . Size } , not { actualSize } .") ;
835+ }
836+
837+ _context = _contextStack . Pop ( ) ;
838+ }
839+
840+ private void PushContext ( BsonBinaryReaderContext newContext )
841+ {
842+ _contextStack . Push ( _context ) ;
843+ _context = newContext ;
844+ }
845+
816846 private int ReadSize ( )
817847 {
818848 int size = _bsonStream . ReadInt32 ( ) ;
0 commit comments