Skip to content

Commit 59c76e0

Browse files
committed
- Added IBufferSlicer for slicing the underlying byteBuffer in ReadOnlyMemoryBsonReader and ReadOnlyMemoryBuffer
1 parent 0504b0f commit 59c76e0

11 files changed

Lines changed: 310 additions & 33 deletions

src/MongoDB.Bson/IO/BsonBinaryReaderUtils.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ public static class BsonBinaryReaderUtils
2525
{
2626
/// <summary>
2727
/// Creates an instance of <see cref="IBsonReader"/> for the given byte buffer and reader settings.
28+
/// The result <see cref="IBsonReader"/> instance does not own the byte buffer, and will not Dispose it.
2829
/// For continuous single chunk buffers an optimized implementation of <see cref="IBsonReader"/> is created.
2930
/// </summary>
3031
/// <param name="byteBuffer">The byte buffer containing BSON data.</param>
@@ -40,7 +41,7 @@ public static IBsonReader CreateBinaryReader(IByteBuffer byteBuffer, BsonBinaryR
4041
var backingBytes = byteBuffer.AccessBackingBytes(0);
4142
if (backingBytes.Count == byteBuffer.Length)
4243
{
43-
return new ReadOnlyMemoryBsonReader(backingBytes, new ReadOnlyMemoryReaderSettings(settings));
44+
return new ReadOnlyMemoryBsonReader(backingBytes, new ByteBufferSlicer(byteBuffer), new ReadOnlyMemoryReaderSettings(settings));
4445
}
4546

4647
var stream = new ByteBufferStream(byteBuffer, ownsBuffer: false);
@@ -99,4 +100,4 @@ private static string GenerateDottedElementName(BsonBinaryReaderContext[] contex
99100

100101
return elementName;
101102
}
102-
}
103+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/* Copyright 2010-present MongoDB Inc.
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
using System;
17+
18+
namespace MongoDB.Bson.IO;
19+
20+
internal interface IByteBufferSlicer
21+
{
22+
public IByteBuffer GetSlice(int position, int length);
23+
}
24+
25+
internal sealed class ReadOnlyMemorySlicer(ReadOnlyMemory<byte> ReadOnlyMemory) : IByteBufferSlicer
26+
{
27+
public IByteBuffer GetSlice(int position, int length)
28+
{
29+
var slice = ReadOnlyMemory.Slice(position, length);
30+
return new ReadOnlyMemoryBuffer(slice, new ReadOnlyMemorySlicer(slice));
31+
}
32+
}
33+
34+
internal sealed class ByteBufferSlicer(IByteBuffer ByteBuffer) : IByteBufferSlicer
35+
{
36+
public IByteBuffer GetSlice(int position, int length) => ByteBuffer.GetSlice(position, length);
37+
}

src/MongoDB.Bson/IO/ReadOnlyMemoryBsonReader.cs

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,11 @@
2121

2222
namespace MongoDB.Bson.IO;
2323

24-
/// <summary>
25-
/// Represents a BSON reader for a ReadOnlyMemory containing a binary BSON byte array.
26-
/// </summary>
27-
public sealed class ReadOnlyMemoryBsonReader : BsonReader
24+
internal sealed class ReadOnlyMemoryBsonReader : BsonReader
2825
{
2926
private static readonly BsonReaderState[] __stateMap;
3027

28+
private readonly IByteBufferSlicer _byteBufferSlicer;
3129
private readonly ReadOnlyMemory<byte> _memory;
3230
private int _position;
3331

@@ -44,24 +42,26 @@ static ReadOnlyMemoryBsonReader()
4442
__stateMap[(int)ContextType.TopLevel] = BsonReaderState.Initial;
4543
}
4644

47-
/// <summary>
48-
/// Initializes a new instance of the ReadOnlyMemoryBsonReader class.
49-
/// </summary>
50-
/// <param name="memory">Memory containing binary BSON.</param>
5145
public ReadOnlyMemoryBsonReader(ReadOnlyMemory<byte> memory)
5246
: this(memory, ReadOnlyMemoryReaderSettings.Defaults)
5347
{
5448
}
5549

56-
/// <summary>
57-
/// Initializes a new instance of the ReadOnlyMemoryBsonReader class.
58-
/// </summary>
59-
/// <param name="memory">Memory containing binary BSON.</param>
60-
/// <param name="settings">A ReadOnlyMemoryReaderSettings.</param>
6150
public ReadOnlyMemoryBsonReader(ReadOnlyMemory<byte> memory, ReadOnlyMemoryReaderSettings settings)
51+
: this(memory, new ReadOnlyMemorySlicer(memory), settings)
52+
{
53+
}
54+
55+
public ReadOnlyMemoryBsonReader(ReadOnlyMemory<byte> memory, IByteBufferSlicer byteBufferSlicer, ReadOnlyMemoryReaderSettings settings)
6256
: base(settings)
6357
{
58+
if (byteBufferSlicer == null)
59+
{
60+
throw new ArgumentNullException(nameof(byteBufferSlicer));
61+
}
62+
6463
_memory = memory;
64+
_byteBufferSlicer = byteBufferSlicer;
6565
_position = 0;
6666

6767
_context = new BsonBinaryReaderContext(ContextType.TopLevel, 0, 0);
@@ -700,8 +700,7 @@ private IByteBuffer ReadSliceFromMemory()
700700
var memoryAtPosition = _memory.Slice(_position);
701701
var length = BinaryPrimitives.ReadInt32LittleEndian(memoryAtPosition.Span);
702702

703-
var result = new ReadOnlyMemoryBuffer(memoryAtPosition.Slice(0, length));
704-
703+
var result = _byteBufferSlicer.GetSlice(_position, length);
705704
_position += length;
706705

707706
return result;

src/MongoDB.Bson/IO/ReadOnlyMemoryBuffer.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,17 @@ namespace MongoDB.Bson.IO;
2121
internal sealed class ReadOnlyMemoryBuffer : IByteBuffer
2222
{
2323
private readonly ReadOnlyMemory<byte> _memory;
24+
private readonly IByteBufferSlicer _bufferSlicer;
2425

25-
public ReadOnlyMemoryBuffer(ReadOnlyMemory<byte> memory)
26+
public ReadOnlyMemoryBuffer(ReadOnlyMemory<byte> memory, IByteBufferSlicer bufferSlicer)
2627
{
28+
if (bufferSlicer == null)
29+
{
30+
throw new ArgumentNullException(nameof(bufferSlicer));
31+
}
32+
2733
_memory = memory;
34+
_bufferSlicer = bufferSlicer;
2835
}
2936

3037
/// <inheritdoc/>
@@ -77,7 +84,7 @@ public void GetBytes(int position, byte[] destination, int offset, int count) =>
7784

7885
/// <inheritdoc/>
7986
public IByteBuffer GetSlice(int position, int length) =>
80-
new ReadOnlyMemoryBuffer(_memory.Slice(position, length));
87+
_bufferSlicer.GetSlice(position, length);
8188

8289
/// <inheritdoc/>
8390
public void MakeReadOnly()

src/MongoDB.Bson/IO/SingleChunkBuffer.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -260,15 +260,15 @@ private void EnsureIsReadOnly()
260260
{
261261
if (!_isReadOnly)
262262
{
263-
throw new InvalidOperationException("MultiChunkBuffer is not read only.");
263+
throw new InvalidOperationException($"{nameof(SingleChunkBuffer)} is not read only.");
264264
}
265265
}
266266

267267
private void EnsureIsWritable()
268268
{
269269
if (_isReadOnly)
270270
{
271-
throw new InvalidOperationException("MultiChunkBuffer is not writable.");
271+
throw new InvalidOperationException($"{nameof(SingleChunkBuffer)} is not writable.");
272272
}
273273
}
274274

src/MongoDB.Bson/Properties/AssemblyInfo.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,6 @@
2626
// as Xamarin.iOS/Xamarin.Mac.
2727
[assembly: Preserve(AllMembers = true)]
2828

29+
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]
2930
[assembly: InternalsVisibleTo("MongoDB.Bson.Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010035287f0d3883c0a075c88e0cda3ce93b621003ecbd5e920d4a8c7238564f4d2f4f68116aca28c9b21341dc3a877679c14556192b2b2f5fe2c11d624e0894d308ff7b94bf6fd72aef1b41017ffe2572e99019d1c61963e68cd0ed67734a42cb333b808e3867cbe631937214e32e409fb1fa62fdb69d494c2530e64a40e417d6ee")]
3031
[assembly: InternalsVisibleTo("MongoDB.Analyzer.MQLGenerator, PublicKey=002400000480000094000000060200000024000052534131000400000100010035287f0d3883c0a075c88e0cda3ce93b621003ecbd5e920d4a8c7238564f4d2f4f68116aca28c9b21341dc3a877679c14556192b2b2f5fe2c11d624e0894d308ff7b94bf6fd72aef1b41017ffe2572e99019d1c61963e68cd0ed67734a42cb333b808e3867cbe631937214e32e409fb1fa62fdb69d494c2530e64a40e417d6ee")]
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/* Copyright 2010-present MongoDB Inc.
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
using System.Linq;
17+
using FluentAssertions;
18+
using MongoDB.Bson.IO;
19+
using Xunit;
20+
21+
namespace MongoDB.Bson.Tests.IO;
22+
23+
public class BsonBinaryReaderUtilsTests
24+
{
25+
[Fact]
26+
public void CreateBinaryReader_should_create_ReadOnlyMemoryBsonReader_for_ReadOnlyMemoryBuffer()
27+
{
28+
var bytes = new byte[] { 29, 0, 0, 0, 5, 120, 0, 16, 0, 0, 0, 4, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0 };
29+
using var buffer = new ReadOnlyMemoryBuffer(bytes, new ReadOnlyMemorySlicer(bytes));
30+
using var reader = BsonBinaryReaderUtils.CreateBinaryReader(buffer, new());
31+
32+
reader.Should().BeOfType<ReadOnlyMemoryBsonReader>();
33+
34+
ValidateSliceType<ReadOnlyMemoryBuffer>(reader, bytes.Length);
35+
}
36+
37+
[Fact]
38+
public void CreateBinaryReader_should_create_ReadOnlyMemoryBsonReader_for_SingleChunkBuffer()
39+
{
40+
var bytes = new byte[] { 29, 0, 0, 0, 5, 120, 0, 16, 0, 0, 0, 4, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0 };
41+
42+
var buffer = new SingleChunkBuffer(new ByteArrayChunk(bytes), bytes.Length, true);
43+
var reader = BsonBinaryReaderUtils.CreateBinaryReader(buffer, new());
44+
45+
reader.Should().BeOfType<ReadOnlyMemoryBsonReader>();
46+
47+
ValidateSliceType<ByteBufferSlice>(reader, bytes.Length);
48+
}
49+
50+
[Fact]
51+
public void CreateBinaryReader_should_create_ReadOnlyMemoryBsonReader_for_MultiChunkBuffer_with_single_chunk()
52+
{
53+
var bytes = new byte[] { 29, 0, 0, 0, 5, 120, 0, 16, 0, 0, 0, 4, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0 };
54+
55+
var buffer = new MultiChunkBuffer([new ByteArrayChunk(bytes)], bytes.Length, true);
56+
var reader = BsonBinaryReaderUtils.CreateBinaryReader(buffer, new());
57+
58+
reader.Should().BeOfType<ReadOnlyMemoryBsonReader>();
59+
60+
ValidateSliceType<ByteBufferSlice>(reader, bytes.Length);
61+
}
62+
63+
[Fact]
64+
public void CreateBinaryReader_should_create_BsonBinaryReader_for_MultiChunkBuffer_with_multiple_chunks()
65+
{
66+
var bytes = new byte[] { 29, 0, 0, 0, 5, 120, 0, 16, 0, 0, 0, 4, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0 };
67+
68+
var buffer = new MultiChunkBuffer([new ByteArrayChunk(bytes.Take(10).ToArray()), new ByteArrayChunk(bytes.Skip(10).ToArray())], bytes.Length, true);
69+
var reader = BsonBinaryReaderUtils.CreateBinaryReader(buffer, new());
70+
71+
reader.Should().BeOfType<BsonBinaryReader>();
72+
73+
ValidateSliceType<ByteBufferSlice>(reader, bytes.Length);
74+
}
75+
76+
private static void ValidateSliceType<T>(IBsonReader reader, int expectedSliceSize)
77+
where T : IByteBuffer
78+
{
79+
// Check the usage of the correct slicer by reading a slice
80+
var slice = reader.ReadRawBsonDocument();
81+
82+
slice.Length.Should().Be(expectedSliceSize);
83+
slice.Should().BeOfType<T>();
84+
}
85+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/* Copyright 2010-present MongoDB Inc.
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
using FluentAssertions;
17+
using MongoDB.Bson.IO;
18+
using Moq;
19+
using Xunit;
20+
21+
namespace MongoDB.Bson.Tests.IO;
22+
23+
public class ByteBufferSlicerTests
24+
{
25+
[Fact]
26+
public void ByteBufferSlicer_should_use_underlying_buffer_slice()
27+
{
28+
using var byteBufferExpected = new ByteArrayBuffer([1, 2, 3]);
29+
30+
var byteBufferMock = new Mock<IByteBuffer>();
31+
byteBufferMock.Setup(b => b.GetSlice(0, 3)).Returns(byteBufferExpected);
32+
33+
var slicer = new ByteBufferSlicer(byteBufferMock.Object);
34+
35+
using var slice = slicer.GetSlice(0, 3);
36+
37+
slice.Should().Be(byteBufferExpected);
38+
byteBufferMock.Verify(b => b.GetSlice(0, 3), Times.Once);
39+
}
40+
41+
[Fact]
42+
public void ReadOnlyMemorySlicer_should_return_ReadOnlyMemoryBuffer()
43+
{
44+
byte[] bytes = [1, 2, 3];
45+
46+
var slicer = new ReadOnlyMemorySlicer(bytes);
47+
48+
using var slice = slicer.GetSlice(0, 1);
49+
50+
var buffer = slice.Should().BeOfType<ReadOnlyMemoryBuffer>().Subject;
51+
buffer.Memory.Length.Should().Be(1);
52+
buffer.Memory.Span[0].Should().Be(1);
53+
}
54+
}

0 commit comments

Comments
 (0)