-
Couldn't load subscription status.
- Fork 1.3k
CSHARP-5348: Avoid allocations for Bson*Context #1791
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -14,8 +14,10 @@ | |||||
| */ | ||||||
|
|
||||||
| using System; | ||||||
| using System.Collections.Generic; | ||||||
| using System.Globalization; | ||||||
| using System.IO; | ||||||
| using System.Linq; | ||||||
|
|
||||||
| namespace MongoDB.Bson.IO | ||||||
| { | ||||||
|
|
@@ -30,6 +32,7 @@ public class BsonBinaryReader : BsonReader | |||||
| #pragma warning restore CA2213 // Disposable never disposed | ||||||
| private readonly BsonStream _bsonStream; | ||||||
| private BsonBinaryReaderContext _context; | ||||||
| private readonly Lazy<Stack<BsonBinaryReaderContext>> _contexts = new(() => new()); | ||||||
|
||||||
|
|
||||||
| // constructors | ||||||
| /// <summary> | ||||||
|
|
@@ -61,7 +64,7 @@ public BsonBinaryReader(Stream stream, BsonBinaryReaderSettings settings) | |||||
| _baseStream = stream; | ||||||
| _bsonStream = (stream as BsonStream) ?? new BsonStreamAdapter(stream); | ||||||
|
|
||||||
| _context = new BsonBinaryReaderContext(null, ContextType.TopLevel, 0, 0); | ||||||
| _context = new BsonBinaryReaderContext(ContextType.TopLevel, 0, 0); | ||||||
| } | ||||||
|
|
||||||
| // public properties | ||||||
|
|
@@ -109,10 +112,8 @@ public override void Close() | |||||
| /// Gets a bookmark to the reader's current position and state. | ||||||
| /// </summary> | ||||||
| /// <returns>A bookmark.</returns> | ||||||
| public override BsonReaderBookmark GetBookmark() | ||||||
| { | ||||||
| return new BsonBinaryReaderBookmark(State, CurrentBsonType, CurrentName, _context, _bsonStream.Position); | ||||||
| } | ||||||
| public override BsonReaderBookmark GetBookmark() => | ||||||
| new BsonBinaryReaderBookmark(State, CurrentBsonType, CurrentName, _context, _contexts.Value.ToArray(), _bsonStream.Position); | ||||||
|
||||||
|
|
||||||
| /// <summary> | ||||||
| /// Determines whether this reader is at end of file. | ||||||
|
|
@@ -201,12 +202,11 @@ public override BsonType ReadBsonType() | |||||
|
|
||||||
| if (_context.ContextType == ContextType.Array) | ||||||
| { | ||||||
| _context.CurrentArrayIndex++; | ||||||
| _context.ArrayIndex++; | ||||||
| } | ||||||
|
|
||||||
| try | ||||||
| { | ||||||
|
|
||||||
| CurrentBsonType = _bsonStream.ReadBsonType(); | ||||||
| } | ||||||
| catch (FormatException ex) | ||||||
|
|
@@ -342,7 +342,8 @@ public override void ReadEndArray() | |||||
| ThrowInvalidState("ReadEndArray", BsonReaderState.EndOfArray); | ||||||
| } | ||||||
|
|
||||||
| _context = _context.PopContext(_bsonStream.Position); | ||||||
| _context = PopContext(); | ||||||
|
|
||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's encapsulate all side effects of popping the context in the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good idea, done. |
||||||
| switch (_context.ContextType) | ||||||
| { | ||||||
| case ContextType.Array: State = BsonReaderState.Type; break; | ||||||
|
|
@@ -371,10 +372,10 @@ public override void ReadEndDocument() | |||||
| ThrowInvalidState("ReadEndDocument", BsonReaderState.EndOfDocument); | ||||||
| } | ||||||
|
|
||||||
| _context = _context.PopContext(_bsonStream.Position); | ||||||
| _context = PopContext(); | ||||||
|
||||||
| if (_context.ContextType == ContextType.JavaScriptWithScope) | ||||||
| { | ||||||
| _context = _context.PopContext(_bsonStream.Position); // JavaScriptWithScope | ||||||
| _context = PopContext(); // JavaScriptWithScope | ||||||
|
||||||
| } | ||||||
| switch (_context.ContextType) | ||||||
| { | ||||||
|
|
@@ -432,7 +433,10 @@ public override string ReadJavaScriptWithScope() | |||||
|
|
||||||
| var startPosition = _bsonStream.Position; // position of size field | ||||||
| var size = ReadSize(); | ||||||
| _context = new BsonBinaryReaderContext(_context, ContextType.JavaScriptWithScope, startPosition, size); | ||||||
|
|
||||||
| PushContext(_context); | ||||||
| _context = new BsonBinaryReaderContext(ContextType.JavaScriptWithScope, startPosition, size); | ||||||
|
||||||
|
|
||||||
| var code = _bsonStream.ReadString(Settings.Encoding); | ||||||
|
|
||||||
| State = BsonReaderState.ScopeDocument; | ||||||
|
|
@@ -486,7 +490,7 @@ public override string ReadName(INameDecoder nameDecoder) | |||||
|
|
||||||
| if (_context.ContextType == ContextType.Document) | ||||||
| { | ||||||
| _context.CurrentElementName = CurrentName; | ||||||
| _context.ElementName = CurrentName; | ||||||
| } | ||||||
|
|
||||||
| return CurrentName; | ||||||
|
|
@@ -553,7 +557,7 @@ public override IByteBuffer ReadRawBsonDocument() | |||||
|
|
||||||
| if (_context.ContextType == ContextType.JavaScriptWithScope) | ||||||
| { | ||||||
| _context = _context.PopContext(_bsonStream.Position); // JavaScriptWithScope | ||||||
| _context = PopContext(); // JavaScriptWithScope | ||||||
|
||||||
| } | ||||||
| switch (_context.ContextType) | ||||||
| { | ||||||
|
|
@@ -590,7 +594,10 @@ public override void ReadStartArray() | |||||
|
|
||||||
| var startPosition = _bsonStream.Position; // position of size field | ||||||
| var size = ReadSize(); | ||||||
| _context = new BsonBinaryReaderContext(_context, ContextType.Array, startPosition, size); | ||||||
|
|
||||||
| PushContext(_context); | ||||||
| _context = new(ContextType.Array, startPosition, size); | ||||||
|
||||||
|
|
||||||
| State = BsonReaderState.Type; | ||||||
| } | ||||||
|
|
||||||
|
|
@@ -605,7 +612,8 @@ public override void ReadStartDocument() | |||||
| var contextType = (State == BsonReaderState.ScopeDocument) ? ContextType.ScopeDocument : ContextType.Document; | ||||||
| var startPosition = _bsonStream.Position; // position of size field | ||||||
| var size = ReadSize(); | ||||||
| _context = new BsonBinaryReaderContext(_context, contextType, startPosition, size); | ||||||
| PushContext(_context); | ||||||
| _context = new(contextType, startPosition, size); | ||||||
|
||||||
| State = BsonReaderState.Type; | ||||||
| } | ||||||
|
|
||||||
|
|
@@ -665,7 +673,15 @@ public override void ReturnToBookmark(BsonReaderBookmark bookmark) | |||||
| State = binaryReaderBookmark.State; | ||||||
| CurrentBsonType = binaryReaderBookmark.CurrentBsonType; | ||||||
| CurrentName = binaryReaderBookmark.CurrentName; | ||||||
| _context = binaryReaderBookmark.CloneContext(); | ||||||
|
|
||||||
| _context = binaryReaderBookmark.CurrentContext; | ||||||
|
|
||||||
| _contexts.Value.Clear(); | ||||||
| foreach (var context in binaryReaderBookmark.ContextsStack.Reverse()) | ||||||
| { | ||||||
| _contexts.Value.Push(context); | ||||||
| } | ||||||
|
|
||||||
|
||||||
| _bsonStream.Position = binaryReaderBookmark.Position; | ||||||
| } | ||||||
|
|
||||||
|
|
@@ -686,7 +702,7 @@ public override void SkipName() | |||||
|
|
||||||
| if (_context.ContextType == ContextType.Document) | ||||||
| { | ||||||
| _context.CurrentElementName = CurrentName; | ||||||
| _context.ElementName = CurrentName; | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
|
|
@@ -767,35 +783,41 @@ private string GenerateDottedElementName() | |||||
| } | ||||||
| else if (_context.ContextType == ContextType.Array) | ||||||
| { | ||||||
| elementName = _context.CurrentArrayIndex.ToString(NumberFormatInfo.InvariantInfo); | ||||||
| elementName = _context.ArrayIndex.ToString(NumberFormatInfo.InvariantInfo); | ||||||
| } | ||||||
| else | ||||||
| { | ||||||
| elementName = "?"; | ||||||
| } | ||||||
|
|
||||||
| return GenerateDottedElementName(_context.ParentContext, elementName); | ||||||
| return GenerateDottedElementName(_contexts.Value.ToArray(), 0, elementName); | ||||||
| } | ||||||
|
|
||||||
| private string GenerateDottedElementName(BsonBinaryReaderContext context, string elementName) | ||||||
| private string GenerateDottedElementName(BsonBinaryReaderContext[] contexts, int currentContextIndex, string elementName) | ||||||
| { | ||||||
| if (currentContextIndex >= contexts.Length) | ||||||
| return elementName; | ||||||
|
|
||||||
| var context = contexts[currentContextIndex]; | ||||||
| var nextIndex = currentContextIndex + 1; | ||||||
|
|
||||||
| if (context.ContextType == ContextType.Document) | ||||||
| { | ||||||
| return GenerateDottedElementName(context.ParentContext, (context.CurrentElementName ?? "?") + "." + elementName); | ||||||
| return GenerateDottedElementName(contexts, nextIndex, (context.ElementName ?? "?") + "." + elementName); | ||||||
|
||||||
| return GenerateDottedElementName(contexts, nextIndex, (context.ElementName ?? "?") + "." + elementName); | |
| return GenerateDottedElementName(contexts, nextIndex, (context.ElementName ?? "?") + "." + elementName); |
Copilot
AI
Oct 17, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Extra space after comma in method calls.
| return GenerateDottedElementName(contexts, nextIndex, indexElementName + "." + elementName); | |
| return GenerateDottedElementName(contexts, nextIndex, indexElementName + "." + elementName); |
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fully encapsulate all side effects related to popping the context into this method, changing it to void:
private void PopContext()
{
var actualSize = _bsonStream.Position - _context.StartPosition;
if (actualSize != _context.Size)
{
throw new FormatException($"Expected size to be {_context.Size}, not {actualSize}.");
}
_context = _contextStack.Pop();
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fully encapsulate all side effects related to pushing the context into this method:
private void PushContext(BsonBinaryReaderContext context)
{
_contextStack.Push(_context);
_context = context;
}
Note that the context parameter is now the NEW context.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,40 +13,28 @@ | |
| * limitations under the License. | ||
| */ | ||
|
|
||
| namespace MongoDB.Bson.IO | ||
| namespace MongoDB.Bson.IO; | ||
|
|
||
| /// <summary> | ||
| /// Represents a bookmark that can be used to return a reader to the current position and state. | ||
| /// </summary> | ||
| public class BsonBinaryReaderBookmark : BsonReaderBookmark | ||
| { | ||
| /// <summary> | ||
| /// Represents a bookmark that can be used to return a reader to the current position and state. | ||
| /// </summary> | ||
| public class BsonBinaryReaderBookmark : BsonReaderBookmark | ||
| internal BsonBinaryReaderBookmark( | ||
| BsonReaderState state, | ||
| BsonType currentBsonType, | ||
| string currentName, | ||
| BsonBinaryReaderContext currentContext, | ||
| BsonBinaryReaderContext[] contextsStack, | ||
| long position) | ||
| : base(state, currentBsonType, currentName) | ||
| { | ||
| // private fields | ||
| private BsonBinaryReaderContext _context; | ||
| private long _position; | ||
|
|
||
| // constructors | ||
| internal BsonBinaryReaderBookmark( | ||
| BsonReaderState state, | ||
| BsonType currentBsonType, | ||
| string currentName, | ||
| BsonBinaryReaderContext context, | ||
| long position) | ||
| : base(state, currentBsonType, currentName) | ||
| { | ||
| _context = context.Clone(); | ||
| _position = position; | ||
| } | ||
|
|
||
| // internal properties | ||
| internal long Position | ||
| { | ||
| get { return _position; } | ||
| } | ||
|
|
||
| // internal methods | ||
| internal BsonBinaryReaderContext CloneContext() | ||
| { | ||
| return _context.Clone(); | ||
| } | ||
| CurrentContext = currentContext; | ||
| ContextsStack = contextsStack; | ||
| Position = position; | ||
| } | ||
|
|
||
| internal BsonBinaryReaderContext CurrentContext { get; } | ||
| internal BsonBinaryReaderContext[] ContextsStack { get; } | ||
| internal long Position { get; } | ||
|
||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused using.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch, done.