Skip to content

Conversation

BorisDog
Copy link
Contributor

@BorisDog BorisDog commented Oct 13, 2025

Convert BsonBinaryWriterContext and BsonBinaryReaderContext to structs, to reduces allocations on a hot path.

@BorisDog BorisDog requested a review from rstam October 13, 2025 22:26
@BorisDog BorisDog requested a review from a team as a code owner October 13, 2025 22:26
_bsonStream = (stream as BsonStream) ?? new BsonStreamAdapter(stream);

_context = null;
_context = new(ContextType.TopLevel, 0);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use TopLevel instead of null

public class BsonBinaryReaderTests
{
[Fact]
public void Bookmarks_should_work()
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added bookmarks test for better coverage of BsonBinaryReader.

GuidRepresentation.Unspecified)] GuidRepresentation guidRepresentation)
[InlineData(BsonBinarySubType.UuidStandard)]
[InlineData(BsonBinarySubType.UuidLegacy)]
public void ReadBinaryData_should_read_correct_subtype(BsonBinarySubType subType)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated cleanup.
GuidRepresentation wasn't used in ReadBinaryData_subtype_3_should_use_GuidRepresentation_from_settings and ReadBinaryData_subtype_4_should_use_GuidRepresentation_Standard tests. Both tests combined into single ReadBinaryData_should_read_correct_subtype test.

#pragma warning restore CA2213 // Disposable never disposed
private readonly BsonStream _bsonStream;
private BsonBinaryReaderContext _context;
private readonly Lazy<Stack<BsonBinaryReaderContext>> _contexts = new(() => new());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We've traded allocations of contexts for whatever allocations the stack class does.

Consider using a non-default initial capacity, probably around 4 (maybe 8), otherwise the first few calls to Push will result in memory allocations.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lazy requires an allocation also. Maybe just go ahead and allocate the Stack?

Actually maybe multiple allocations. I think the factory lambda is an allocation as well.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

private readonly Stack<BsonBinaryReaderContext> _contextStack = new(4);

return new BsonBinaryReaderBookmark(State, CurrentBsonType, CurrentName, _context, _bsonStream.Position);
}
public override BsonReaderBookmark GetBookmark() =>
new BsonBinaryReaderBookmark(State, CurrentBsonType, CurrentName, _context, _contexts.Value.ToArray(), _bsonStream.Position);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's encapsulate all logic about how to restore the context stack into the bookmark, so here we can just pass the _contextStack and let the bookmark call ToArray (or whatever it wants to do).

new BsonBinaryReaderBookmark(State, CurrentBsonType, CurrentName, _context, _contextStack, _bsonStream.Position);


_context = _context.PopContext(_bsonStream.Position);
_context = PopContext();

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's encapsulate all side effects of popping the context in the PopContext method (see below).

 PopContext();

}

_context = _context.PopContext(_bsonStream.Position);
_context = PopContext();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

 PopContext();

if (_context.ContextType == ContextType.JavaScriptWithScope)
{
_context = _context.PopContext(_bsonStream.Position); // JavaScriptWithScope
_context = PopContext(); // JavaScriptWithScope
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PopContext(); // JavaScriptWithScope

{
BackpatchSize(); // size of the JavaScript with scope value
_context = _context.ParentContext;
_context = _contexts.Value.Pop();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PopContext();

_context = new BsonBinaryWriterContext(_context, ContextType.JavaScriptWithScope, _bsonStream.Position);

_contexts.Value.Push(_context);
_context = new(ContextType.JavaScriptWithScope, _bsonStream.Position);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Single line instead of two:

PushContext(new(ContextType.JavaScriptWithScope, _bsonStream.Position));

{
BackpatchSize(); // size of the JavaScript with scope value
_context = _context.ParentContext;
_context = _contexts.Value.Pop();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

 PopContext();

_context = new BsonBinaryWriterContext(_context, ContextType.Array, _bsonStream.Position);

_contexts.Value.Push(_context);
_context = new(ContextType.Array, _bsonStream.Position);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PushContext(new(ContextType.Array, _bsonStream.Position));

var contextType = (State == BsonWriterState.ScopeDocument) ? ContextType.ScopeDocument : ContextType.Document;
_context = new BsonBinaryWriterContext(_context, contextType, _bsonStream.Position);
_contexts.Value.Push(_context);
_context = new(contextType, _bsonStream.Position);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PushContext(new(contextType, _bsonStream.Position));

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add new helper methods:

private void PopContext()
{
    _context = _contextStack.Pop();
}
 
private void PushContext(BsonBinaryWriterContext context)
{                                                        
    _contextStack.Push(_context);                        
    _context = context;                                  
}                                                        

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants