Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 2 additions & 56 deletions src/MongoDB.Bson/IO/BsonBinaryReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@

using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;

namespace MongoDB.Bson.IO
Expand Down Expand Up @@ -90,7 +89,7 @@ public BsonStream BsonStream
}

/// <summary>
/// Gets the settings of the writer.
/// Gets the settings of the reader.
/// </summary>
public new BsonBinaryReaderSettings Settings
{
Expand Down Expand Up @@ -214,7 +213,7 @@ public override BsonType ReadBsonType()
{
// insert the element name into the error message
var periodIndex = ex.Message.IndexOf('.');
var dottedElementName = GenerateDottedElementName();
var dottedElementName = BsonBinaryReaderUtils.GenerateDottedElementName(_context, _contextStack.ToArray(), () => _bsonStream.ReadCString(Utf8Encodings.Lenient));
var message = ex.Message.Substring(0, periodIndex) + $" for fieldname \"{dottedElementName}\"" + ex.Message.Substring(periodIndex);
throw new FormatException(message);
}
Expand Down Expand Up @@ -758,59 +757,6 @@ protected override void Dispose(bool disposing)
}

// private methods
private string GenerateDottedElementName()
{
string elementName;
if (_context.ContextType == ContextType.Document)
{
try
{
elementName = _bsonStream.ReadCString(Utf8Encodings.Lenient);
}
catch
{
elementName = "?"; // ignore exception
}
}
else if (_context.ContextType == ContextType.Array)
{
elementName = _context.ArrayIndex.ToString(NumberFormatInfo.InvariantInfo);
}
else
{
elementName = "?";
}

return GenerateDottedElementName(_contextStack.ToArray(), 0, 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(contexts, nextIndex, (context.ElementName ?? "?") + "." + elementName);
}

if (context.ContextType == ContextType.Array)
{
var indexElementName = context.ArrayIndex.ToString(NumberFormatInfo.InvariantInfo);
return GenerateDottedElementName(contexts, nextIndex, indexElementName + "." + elementName);
}

if (nextIndex < contexts.Length)
{
return GenerateDottedElementName(contexts, nextIndex, "?." + elementName);
}

return elementName;
}

private BsonReaderState GetNextState()
{
switch (_context.ContextType)
Expand Down
102 changes: 102 additions & 0 deletions src/MongoDB.Bson/IO/BsonBinaryReaderUtils.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/* Copyright 2010-present MongoDB Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

using System;
using System.Globalization;

namespace MongoDB.Bson.IO;

/// <summary>
/// Provides utility methods for working with <see cref="IBsonReader"/> instances in binary BSON format.
/// </summary>
public static class BsonBinaryReaderUtils
{
/// <summary>
/// Creates an instance of <see cref="IBsonReader"/> for the given byte buffer and reader settings.
/// For continuous single chunk buffers an optimized implementation of <see cref="IBsonReader"/> is created.
/// </summary>
/// <param name="byteBuffer">The byte buffer containing BSON data.</param>
/// <param name="settings">The settings to configure the BSON reader.</param>
/// <returns>An <see cref="IBsonReader"/>Bson reader.</returns>
public static IBsonReader CreateBinaryReader(IByteBuffer byteBuffer, BsonBinaryReaderSettings settings)
{
if (byteBuffer is ReadOnlyMemoryBuffer readOnlyMemoryBuffer)
{
return new ReadOnlyMemoryBsonReader(readOnlyMemoryBuffer.Memory, new ReadOnlyMemoryReaderSettings(settings));
}

var backingBytes = byteBuffer.AccessBackingBytes(0);
if (backingBytes.Count == byteBuffer.Length)
{
return new ReadOnlyMemoryBsonReader(backingBytes, new ReadOnlyMemoryReaderSettings(settings));
}

var stream = new ByteBufferStream(byteBuffer, ownsBuffer: false);
return new BsonBinaryReader(stream, settings);
}

internal static string GenerateDottedElementName(BsonBinaryReaderContext context, BsonBinaryReaderContext[] parentContexts, Func<string> elementNameReader)
{
string elementName;
if (context.ContextType == ContextType.Document)
{
try
{
elementName = elementNameReader();
}
catch
{
elementName = "?"; // ignore exception
}
}
else if (context.ContextType == ContextType.Array)
{
elementName = context.ArrayIndex.ToString(NumberFormatInfo.InvariantInfo);
}
else
{
elementName = "?";
}

return GenerateDottedElementName(parentContexts, 0, elementName);
}

private static 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(contexts, nextIndex, (context.ElementName ?? "?") + "." + elementName);
}

if (context.ContextType == ContextType.Array)
{
var indexElementName = context.ArrayIndex.ToString(NumberFormatInfo.InvariantInfo);
return GenerateDottedElementName(contexts, nextIndex, indexElementName + "." + elementName);
}

if (nextIndex < contexts.Length)
{
return GenerateDottedElementName(contexts, nextIndex, "?." + elementName);
}

return elementName;
}
}
17 changes: 11 additions & 6 deletions src/MongoDB.Bson/IO/BsonReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -466,9 +466,17 @@ protected void ThrowObjectDisposedException()
/// </summary>
/// <param name="methodName">The name of the method calling this one.</param>
/// <param name="requiredBsonType">The required BSON type.</param>
protected void VerifyBsonType(string methodName, BsonType requiredBsonType)
protected void VerifyBsonType(string methodName, BsonType requiredBsonType) =>
VerifyBsonType(requiredBsonType, methodName);

/// <summary>
/// Verifies the current state and BsonType of the reader.
/// </summary>
/// /// <param name="requiredBsonType">The required BSON type.</param>
/// <param name="methodName">The name of the method calling this one.</param>
protected void VerifyBsonType(BsonType requiredBsonType, [System.Runtime.CompilerServices.CallerMemberName]string methodName = null)
{
if (_state == BsonReaderState.Initial || _state == BsonReaderState.ScopeDocument || _state == BsonReaderState.Type)
if (_state is BsonReaderState.Initial or BsonReaderState.ScopeDocument or BsonReaderState.Type)
{
ReadBsonType();
}
Expand All @@ -483,10 +491,7 @@ protected void VerifyBsonType(string methodName, BsonType requiredBsonType)
}
if (_currentBsonType != requiredBsonType)
{
var message = string.Format(
"{0} can only be called when CurrentBsonType is {1}, not when CurrentBsonType is {2}.",
methodName, requiredBsonType, _currentBsonType);
throw new InvalidOperationException(message);
throw new InvalidOperationException($"{methodName} can only be called when CurrentBsonType is {requiredBsonType}, not when CurrentBsonType is {_currentBsonType}.");
}
}
}
Expand Down
5 changes: 1 addition & 4 deletions src/MongoDB.Bson/IO/BsonStream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,13 @@
*/

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MongoDB.Bson.IO
{
/// <summary>
/// Represents a Stream has additional methods to suport reading and writing BSON values.
/// Represents a Stream has additional methods to support reading and writing BSON values.
/// </summary>
public abstract class BsonStream : Stream
{
Expand Down
7 changes: 7 additions & 0 deletions src/MongoDB.Bson/IO/BsonStreamExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,13 @@ public static void BackpatchSize(this BsonStream stream, long startPosition)
stream.Position = endPosition;
}

/// <summary>
/// Determines whether the specified BSON type is valid.
/// </summary>
/// <param name="bsonType">The BSON type to validate.</param>
/// <returns>True if the BSON type is valid; otherwise, false.</returns>
public static bool IsValidBsonType(BsonType bsonType) => __validBsonTypes[(byte)bsonType];

/// <summary>
/// Reads the binary sub type.
/// </summary>
Expand Down
21 changes: 21 additions & 0 deletions src/MongoDB.Bson/IO/BsonTrie.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,27 @@ public bool TryGetNode(ArraySegment<byte> utf8, out BsonTrieNode<TValue> node)
return node != null;
}

/// <summary>
/// Gets the node associated with the specified element name.
/// </summary>
/// <param name="utf8">The element name.</param>
/// <param name="node">
/// When this method returns, contains the node associated with the specified element name, if the key is found;
/// otherwise, null. This parameter is passed uninitialized.
/// </param>
/// <returns>True if the node was found; otherwise, false.</returns>
public bool TryGetNode(ReadOnlySpan<byte> utf8, out BsonTrieNode<TValue> node)
{
node = _root;
for (var i = 0; node != null && i < utf8.Length; i++)
{
var keyByte = utf8[i];
node = node.GetChild(keyByte);
}

return node != null;
}

/// <summary>
/// Tries to get the node associated with a name read from a stream.
/// </summary>
Expand Down
14 changes: 6 additions & 8 deletions src/MongoDB.Bson/IO/BsonWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -347,15 +347,13 @@ public virtual void WriteRawBsonDocument(IByteBuffer slice)
// overridden in BsonBinaryWriter to write the raw bytes to the stream
// for all other streams, deserialize the raw bytes and serialize the resulting document instead

using (var stream = new ByteBufferStream(slice, ownsBuffer: false))
using (var bsonReader = new BsonBinaryReader(stream, BsonBinaryReaderSettings.Defaults))
{
var deserializationContext = BsonDeserializationContext.CreateRoot(bsonReader);
var document = BsonDocumentSerializer.Instance.Deserialize(deserializationContext);
using var bsonReader = BsonBinaryReaderUtils.CreateBinaryReader(slice, BsonBinaryReaderSettings.Defaults);

var serializationContext = BsonSerializationContext.CreateRoot(this);
BsonDocumentSerializer.Instance.Serialize(serializationContext, document);
}
var deserializationContext = BsonDeserializationContext.CreateRoot(bsonReader);
var document = BsonDocumentSerializer.Instance.Deserialize(deserializationContext);

var serializationContext = BsonSerializationContext.CreateRoot(this);
BsonDocumentSerializer.Instance.Serialize(serializationContext, document);
}

/// <summary>
Expand Down
6 changes: 5 additions & 1 deletion src/MongoDB.Bson/IO/INameDecoder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
*/

using System;
using System.IO;
using System.Text;

namespace MongoDB.Bson.IO
Expand All @@ -40,4 +39,9 @@ public interface INameDecoder
/// <param name="name">The name.</param>
void Inform(string name);
}

internal interface INameDecoderInternal
{
string Decode(ReadOnlySpan<byte> span, UTF8Encoding encoding);
}
}
Loading