From a9070e67f6741160e6da529570ddc441f01eb28c Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 12 Sep 2018 15:42:39 +0200 Subject: [PATCH 1/2] CSHARP-2383: Provide overloads for WriteBytes/ReadBytes methods that allow buffer reuse --- src/MongoDB.Bson/IO/BsonBinaryReader.cs | 28 ++++++++++++++++++- src/MongoDB.Bson/IO/BsonBinaryWriter.cs | 22 +++++++++++++++ src/MongoDB.Bson/IO/BsonDocumentReader.cs | 26 +++++++++++++++++ src/MongoDB.Bson/IO/BsonDocumentWriter.cs | 15 ++++++++++ src/MongoDB.Bson/IO/BsonReader.cs | 8 ++++++ src/MongoDB.Bson/IO/BsonWriter.cs | 7 +++++ src/MongoDB.Bson/IO/IBsonReader.cs | 8 ++++++ src/MongoDB.Bson/IO/IBsonWriter.cs | 7 +++++ src/MongoDB.Bson/IO/JsonReader.cs | 27 ++++++++++++++++++ src/MongoDB.Bson/IO/JsonToken.cs | 3 ++ src/MongoDB.Bson/IO/JsonWriter.cs | 15 ++++++++++ src/MongoDB.Bson/IO/WrappingBsonWriter.cs | 7 +++++ .../MethodCallBinders/JoinSerializer.cs | 5 ++++ .../MongoDB.Bson.Tests/IO/JsonReaderTests.cs | 17 +++++++++++ .../IO/WrappingBsonWriterTests.cs | 14 ++++++++++ 15 files changed, 208 insertions(+), 1 deletion(-) diff --git a/src/MongoDB.Bson/IO/BsonBinaryReader.cs b/src/MongoDB.Bson/IO/BsonBinaryReader.cs index 00d59205b67..f054850350e 100644 --- a/src/MongoDB.Bson/IO/BsonBinaryReader.cs +++ b/src/MongoDB.Bson/IO/BsonBinaryReader.cs @@ -273,8 +273,34 @@ public override byte[] ReadBytes() State = GetNextState(); return _bsonStream.ReadBytes(size); } -#pragma warning restore 618 + /// + /// Reads BSON binary data from the reader to the buffer at offset and respecting the size. + /// + /// Target buffer + /// Target offset + /// Bytes read + public override int ReadBytes(byte[] bytes, int offset) { + if (Disposed) { ThrowObjectDisposedException(); } + VerifyBsonType("ReadBytes", BsonType.Binary); + + int size = ReadSize(); + + var subType = _bsonStream.ReadBinarySubType(); + if (subType != BsonBinarySubType.Binary) + { + var message = string.Format("ReadBytes requires the binary sub type to be Binary, not {0}.", subType); + throw new FormatException(message); + } + + State = GetNextState(); + + _bsonStream.ReadBytes(bytes, offset, size); + + return size; + } + +#pragma warning restore 618 /// /// Reads a BSON DateTime from the reader. /// diff --git a/src/MongoDB.Bson/IO/BsonBinaryWriter.cs b/src/MongoDB.Bson/IO/BsonBinaryWriter.cs index 8a322f9fe80..c065196242d 100644 --- a/src/MongoDB.Bson/IO/BsonBinaryWriter.cs +++ b/src/MongoDB.Bson/IO/BsonBinaryWriter.cs @@ -261,6 +261,28 @@ public override void WriteBytes(byte[] bytes) State = GetNextState(); } + /// + /// Writes BSON binary data to the writer. + /// + /// The bytes. + /// The count of bytes used in the bytes[] + public override void WriteBytes(byte[] bytes, int size) + { + if (Disposed) { throw new ObjectDisposedException("BsonBinaryWriter"); } + if (State != BsonWriterState.Value) + { + ThrowInvalidState("WriteBytes", BsonWriterState.Value); + } + + _bsonStream.WriteBsonType(BsonType.Binary); + WriteNameHelper(); + _bsonStream.WriteInt32(size); + _bsonStream.WriteBinarySubType(BsonBinarySubType.Binary); + _bsonStream.WriteBytes(bytes, 0, bytes.Length); + + State = GetNextState(); + } + /// /// Writes a BSON DateTime to the writer. /// diff --git a/src/MongoDB.Bson/IO/BsonDocumentReader.cs b/src/MongoDB.Bson/IO/BsonDocumentReader.cs index aa4a1e93252..6a4c07b7785 100644 --- a/src/MongoDB.Bson/IO/BsonDocumentReader.cs +++ b/src/MongoDB.Bson/IO/BsonDocumentReader.cs @@ -179,6 +179,32 @@ public override byte[] ReadBytes() return binaryData.Bytes; } + + /// + /// Reads BSON binary data from the reader to the buffer at offset and respecting the size. + /// + /// Target buffer + /// Target offset + /// Bytes read + public override int ReadBytes(byte[] bytes, int offset) + { + if (Disposed) { ThrowObjectDisposedException(); } + VerifyBsonType("ReadBytes", BsonType.Binary); + + State = GetNextState(); + var binaryData = _currentValue.AsBsonBinaryData; + + var subType = binaryData.SubType; + if (subType != BsonBinarySubType.Binary && subType != BsonBinarySubType.OldBinary) + { + var message = string.Format("ReadBytes requires the binary sub type to be Binary, not {0}.", subType); + throw new FormatException(message); + } + + binaryData.Bytes.CopyTo(bytes, offset); + + return binaryData.Bytes.Length; + } #pragma warning restore 618 /// diff --git a/src/MongoDB.Bson/IO/BsonDocumentWriter.cs b/src/MongoDB.Bson/IO/BsonDocumentWriter.cs index 3f77160f1c2..cb9bcbfce09 100644 --- a/src/MongoDB.Bson/IO/BsonDocumentWriter.cs +++ b/src/MongoDB.Bson/IO/BsonDocumentWriter.cs @@ -133,6 +133,21 @@ public override void WriteBytes(byte[] bytes) State = GetNextState(); } + /// + /// Writes BSON binary data to the writer. + /// + /// The bytes. + /// The count of bytes used in the bytes[] + public override void WriteBytes(byte[] bytes, int size) { + if (bytes.Length != size) { + // TODO: Better use a buffer manager here or Provide size in BsonBinaryData + var copy = new byte[size]; + Buffer.BlockCopy(bytes, 0, copy, 0, size); + WriteBytes(copy); + } else + WriteBytes(bytes); + } + /// /// Writes a BSON DateTime to the writer. /// diff --git a/src/MongoDB.Bson/IO/BsonReader.cs b/src/MongoDB.Bson/IO/BsonReader.cs index 41222a3f4ff..1b63d87653d 100644 --- a/src/MongoDB.Bson/IO/BsonReader.cs +++ b/src/MongoDB.Bson/IO/BsonReader.cs @@ -167,6 +167,14 @@ public BsonType GetCurrentBsonType() /// A byte array. public abstract byte[] ReadBytes(); + /// + /// Reads BSON binary data from the reader to the buffer at offset and respecting the size. + /// + /// Target buffer + /// Target offset + /// Bytes read + public abstract int ReadBytes(byte[] bytes, int offset); + /// /// Reads a BSON DateTime from the reader. /// diff --git a/src/MongoDB.Bson/IO/BsonWriter.cs b/src/MongoDB.Bson/IO/BsonWriter.cs index 9941a9a0b09..3d49aaa3a47 100644 --- a/src/MongoDB.Bson/IO/BsonWriter.cs +++ b/src/MongoDB.Bson/IO/BsonWriter.cs @@ -184,6 +184,13 @@ public void PushSettings(Action configurator) /// The bytes. public abstract void WriteBytes(byte[] bytes); + /// + /// Writes BSON binary data to the writer. + /// + /// The bytes. + /// + public abstract void WriteBytes(byte[] bytes, int size); + /// /// Writes a BSON DateTime to the writer. /// diff --git a/src/MongoDB.Bson/IO/IBsonReader.cs b/src/MongoDB.Bson/IO/IBsonReader.cs index 541284dae7f..d434661352f 100644 --- a/src/MongoDB.Bson/IO/IBsonReader.cs +++ b/src/MongoDB.Bson/IO/IBsonReader.cs @@ -83,6 +83,14 @@ public interface IBsonReader : IDisposable /// A byte array. byte[] ReadBytes(); + /// + /// Reads BSON binary data from the reader to the buffer at offset and respecting the size. + /// + /// Target buffer + /// Target offset + /// Bytes read + int ReadBytes(byte[] buffer, int offset); + /// /// Reads a BSON DateTime from the reader. /// diff --git a/src/MongoDB.Bson/IO/IBsonWriter.cs b/src/MongoDB.Bson/IO/IBsonWriter.cs index 70af5d2884e..e097fe85fa0 100644 --- a/src/MongoDB.Bson/IO/IBsonWriter.cs +++ b/src/MongoDB.Bson/IO/IBsonWriter.cs @@ -101,6 +101,13 @@ public interface IBsonWriter : IDisposable /// The bytes. void WriteBytes(byte[] bytes); + /// + /// Writes BSON binary data to the writer. + /// + /// The bytes. + /// + void WriteBytes(byte[] bytes, int size); + /// /// Writes a BSON DateTime to the writer. /// diff --git a/src/MongoDB.Bson/IO/JsonReader.cs b/src/MongoDB.Bson/IO/JsonReader.cs index a464d556571..1ca725fbb28 100644 --- a/src/MongoDB.Bson/IO/JsonReader.cs +++ b/src/MongoDB.Bson/IO/JsonReader.cs @@ -421,6 +421,33 @@ public override byte[] ReadBytes() } return binaryData.Bytes; + + } + + /// + /// Reads BSON binary data from the reader to the buffer at offset and respecting the size. + /// + /// Target buffer + /// Target offset + /// Bytes read + public override int ReadBytes(byte[] bytes, int offset) + { + + if (Disposed) { ThrowObjectDisposedException(); } + VerifyBsonType("ReadBinaryData", BsonType.Binary); + State = GetNextState(); + var binaryData = _currentValue.AsBsonBinaryData; + + var subType = binaryData.SubType; + if (subType != BsonBinarySubType.Binary && subType != BsonBinarySubType.OldBinary) + { + var message = string.Format("ReadBytes requires the binary sub type to be Binary, not {0}.", subType); + throw new FormatException(message); + } + + binaryData.Bytes.CopyTo(bytes, offset); + + return binaryData.Bytes.Length; #pragma warning restore } diff --git a/src/MongoDB.Bson/IO/JsonToken.cs b/src/MongoDB.Bson/IO/JsonToken.cs index afb527e62de..5446d63b94f 100644 --- a/src/MongoDB.Bson/IO/JsonToken.cs +++ b/src/MongoDB.Bson/IO/JsonToken.cs @@ -13,6 +13,9 @@ * limitations under the License. */ + + + using System; namespace MongoDB.Bson.IO diff --git a/src/MongoDB.Bson/IO/JsonWriter.cs b/src/MongoDB.Bson/IO/JsonWriter.cs index dc253e8e368..af1b35efbd4 100644 --- a/src/MongoDB.Bson/IO/JsonWriter.cs +++ b/src/MongoDB.Bson/IO/JsonWriter.cs @@ -175,6 +175,21 @@ public override void WriteBytes(byte[] bytes) WriteBinaryData(new BsonBinaryData(bytes, BsonBinarySubType.Binary)); } + /// + /// Writes BSON binary data to the writer. + /// + /// The bytes. + /// The count of bytes used in the bytes[] + public override void WriteBytes(byte[] bytes, int size) { + if (bytes.Length != size) { + // TODO: Better use a buffer manager here or Provide size in BsonBinaryData + var copy = new byte[size]; + Buffer.BlockCopy(bytes, 0, copy, 0, size); + WriteBytes(copy); + } else + WriteBytes(bytes); + } + /// /// Writes a BSON DateTime to the writer. /// diff --git a/src/MongoDB.Bson/IO/WrappingBsonWriter.cs b/src/MongoDB.Bson/IO/WrappingBsonWriter.cs index 53104f735c1..a282dde9a20 100644 --- a/src/MongoDB.Bson/IO/WrappingBsonWriter.cs +++ b/src/MongoDB.Bson/IO/WrappingBsonWriter.cs @@ -168,6 +168,13 @@ public virtual void WriteBytes(byte[] bytes) _wrapped.WriteBytes(bytes); } + /// + public virtual void WriteBytes(byte[] bytes, int size) + { + ThrowIfDisposed(); + _wrapped.WriteBytes(bytes, size); + } + /// public virtual void WriteDateTime(long value) { diff --git a/src/MongoDB.Driver/Linq/Processors/Pipeline/MethodCallBinders/JoinSerializer.cs b/src/MongoDB.Driver/Linq/Processors/Pipeline/MethodCallBinders/JoinSerializer.cs index 96c430625f2..59db317e0c9 100644 --- a/src/MongoDB.Driver/Linq/Processors/Pipeline/MethodCallBinders/JoinSerializer.cs +++ b/src/MongoDB.Driver/Linq/Processors/Pipeline/MethodCallBinders/JoinSerializer.cs @@ -188,6 +188,11 @@ public byte[] ReadBytes() return _parent.ReadBytes(); } + public int ReadBytes(byte[] bytes, int offset) + { + return _parent.ReadBytes(bytes, offset); + } + public long ReadDateTime() { return _parent.ReadDateTime(); diff --git a/tests/MongoDB.Bson.Tests/IO/JsonReaderTests.cs b/tests/MongoDB.Bson.Tests/IO/JsonReaderTests.cs index 77da4d7ad09..f889fc13cc1 100644 --- a/tests/MongoDB.Bson.Tests/IO/JsonReaderTests.cs +++ b/tests/MongoDB.Bson.Tests/IO/JsonReaderTests.cs @@ -916,6 +916,23 @@ public void TestObjectIdStrict() Assert.Equal(json, BsonSerializer.Deserialize(json).ToJson(jsonSettings)); } + [Fact] + public void TestReadWithReusedBuffer() { + var expectedBytes = new byte[] { 0x01, 0x23 }; + var json = "HexData(0, \"123\")"; + using (_bsonReader = new JsonReader(json)) + { + Assert.Equal(BsonType.Binary, _bsonReader.ReadBsonType()); + var bytes = new byte[1024]; + int readBytes = _bsonReader.ReadBytes(bytes, 50); + Assert.Equal(2, readBytes); + Assert.True(expectedBytes.SequenceEqual(bytes.Skip(50).Take(2))); + Assert.Equal(BsonReaderState.Initial, _bsonReader.State); + } + var expectedJson = "new BinData(0, \"ASM=\")"; + Assert.Equal(expectedJson, BsonSerializer.Deserialize(json).ToJson()); + } + [Theory] [InlineData("{ $regex : \"abc\", $options : \"i\" }", "abc", "i")] [InlineData("{ $regex : \"abc/\", $options : \"i\" }", "abc/", "i")] diff --git a/tests/MongoDB.Bson.Tests/IO/WrappingBsonWriterTests.cs b/tests/MongoDB.Bson.Tests/IO/WrappingBsonWriterTests.cs index 1bc534b5e47..ef3e6c0bb45 100644 --- a/tests/MongoDB.Bson.Tests/IO/WrappingBsonWriterTests.cs +++ b/tests/MongoDB.Bson.Tests/IO/WrappingBsonWriterTests.cs @@ -368,6 +368,20 @@ public void WriteBytes_should_call_wrapped() mockWrapped.Verify(m => m.WriteBytes(value), Times.Once); } + + [Fact] + public void WriteBytesPars_should_call_wrapped() + { + Mock mockWrapped; + var subject = CreateSubject(out mockWrapped); + const int size = 0; + var value = new byte[0]; + + subject.WriteBytes(value, size); + + mockWrapped.Verify(m => m.WriteBytes(value, size), Times.Once); + } + [Fact] public void WriteBytes_should_throw_when_disposed() { From 4e6258604aa5fb07126814a4bc9863acbaca7396 Mon Sep 17 00:00:00 2001 From: Benjamin Piorczig Date: Thu, 13 Sep 2018 07:54:41 +0200 Subject: [PATCH 2/2] BsonBinaryWriter does now really use provided size for the size description. Provided default values for the offset. --- src/MongoDB.Bson/IO/BsonBinaryWriter.cs | 42 +++++++++---------- src/MongoDB.Bson/IO/BsonDocumentReader.cs | 2 +- src/MongoDB.Bson/IO/BsonReader.cs | 2 +- .../MethodCallBinders/JoinSerializer.cs | 2 +- 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/MongoDB.Bson/IO/BsonBinaryWriter.cs b/src/MongoDB.Bson/IO/BsonBinaryWriter.cs index c065196242d..9103fe57de6 100644 --- a/src/MongoDB.Bson/IO/BsonBinaryWriter.cs +++ b/src/MongoDB.Bson/IO/BsonBinaryWriter.cs @@ -261,27 +261,27 @@ public override void WriteBytes(byte[] bytes) State = GetNextState(); } - /// - /// Writes BSON binary data to the writer. - /// - /// The bytes. - /// The count of bytes used in the bytes[] - public override void WriteBytes(byte[] bytes, int size) - { - if (Disposed) { throw new ObjectDisposedException("BsonBinaryWriter"); } - if (State != BsonWriterState.Value) - { - ThrowInvalidState("WriteBytes", BsonWriterState.Value); - } - - _bsonStream.WriteBsonType(BsonType.Binary); - WriteNameHelper(); - _bsonStream.WriteInt32(size); - _bsonStream.WriteBinarySubType(BsonBinarySubType.Binary); - _bsonStream.WriteBytes(bytes, 0, bytes.Length); - - State = GetNextState(); - } + /// + /// Writes BSON binary data to the writer. + /// + /// The bytes. + /// The count of bytes used in the bytes[] + public override void WriteBytes(byte[] bytes, int size) + { + if (Disposed) { throw new ObjectDisposedException("BsonBinaryWriter"); } + if (State != BsonWriterState.Value) + { + ThrowInvalidState("WriteBytes", BsonWriterState.Value); + } + + _bsonStream.WriteBsonType(BsonType.Binary); + WriteNameHelper(); + _bsonStream.WriteInt32(size); + _bsonStream.WriteBinarySubType(BsonBinarySubType.Binary); + _bsonStream.WriteBytes(bytes, 0, size); + + State = GetNextState(); + } /// /// Writes a BSON DateTime to the writer. diff --git a/src/MongoDB.Bson/IO/BsonDocumentReader.cs b/src/MongoDB.Bson/IO/BsonDocumentReader.cs index 6a4c07b7785..3868c897d5d 100644 --- a/src/MongoDB.Bson/IO/BsonDocumentReader.cs +++ b/src/MongoDB.Bson/IO/BsonDocumentReader.cs @@ -186,7 +186,7 @@ public override byte[] ReadBytes() /// Target buffer /// Target offset /// Bytes read - public override int ReadBytes(byte[] bytes, int offset) + public override int ReadBytes(byte[] bytes, int offset = 0) { if (Disposed) { ThrowObjectDisposedException(); } VerifyBsonType("ReadBytes", BsonType.Binary); diff --git a/src/MongoDB.Bson/IO/BsonReader.cs b/src/MongoDB.Bson/IO/BsonReader.cs index 1b63d87653d..c7a5e9995d4 100644 --- a/src/MongoDB.Bson/IO/BsonReader.cs +++ b/src/MongoDB.Bson/IO/BsonReader.cs @@ -173,7 +173,7 @@ public BsonType GetCurrentBsonType() /// Target buffer /// Target offset /// Bytes read - public abstract int ReadBytes(byte[] bytes, int offset); + public abstract int ReadBytes(byte[] bytes, int offset = 0); /// /// Reads a BSON DateTime from the reader. diff --git a/src/MongoDB.Driver/Linq/Processors/Pipeline/MethodCallBinders/JoinSerializer.cs b/src/MongoDB.Driver/Linq/Processors/Pipeline/MethodCallBinders/JoinSerializer.cs index 59db317e0c9..0331a7d6fd0 100644 --- a/src/MongoDB.Driver/Linq/Processors/Pipeline/MethodCallBinders/JoinSerializer.cs +++ b/src/MongoDB.Driver/Linq/Processors/Pipeline/MethodCallBinders/JoinSerializer.cs @@ -188,7 +188,7 @@ public byte[] ReadBytes() return _parent.ReadBytes(); } - public int ReadBytes(byte[] bytes, int offset) + public int ReadBytes(byte[] bytes, int offset = 0) { return _parent.ReadBytes(bytes, offset); }