Skip to content

Commit ed93901

Browse files
Manas Sahurstam
authored andcommitted
CSHARP-4676: Add MongoDB.Driver support for big-endian clients.
1 parent e70abd8 commit ed93901

File tree

15 files changed

+139
-96
lines changed

15 files changed

+139
-96
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
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+
using System.Buffers.Binary;
18+
19+
namespace MongoDB.Bson.IO
20+
{
21+
// this class implements a few BinaryPrimitives methods we need that don't exist in some of our target frameworks
22+
// this class can be deleted once all our targeted frameworks have the needed methods
23+
internal static class BinaryPrimitivesCompat
24+
{
25+
public static double ReadDoubleLittleEndian(ReadOnlySpan<byte> source)
26+
{
27+
return BitConverter.Int64BitsToDouble(BinaryPrimitives.ReadInt64LittleEndian(source));
28+
}
29+
30+
public static void WriteDoubleLittleEndian(Span<byte> destination, double value)
31+
{
32+
BinaryPrimitives.WriteInt64LittleEndian(destination, BitConverter.DoubleToInt64Bits(value));
33+
}
34+
}
35+
}

src/MongoDB.Bson/IO/BsonStreamAdapter.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
using System;
1717
using System.IO;
1818
using System.Text;
19+
using System.Buffers.Binary;
1920
using System.Threading;
2021
using System.Threading.Tasks;
2122

@@ -307,7 +308,7 @@ public override double ReadDouble()
307308
{
308309
ThrowIfDisposed();
309310
this.ReadBytes(_temp, 0, 8);
310-
return BitConverter.ToDouble(_temp, 0);
311+
return BinaryPrimitivesCompat.ReadDoubleLittleEndian(_temp);
311312
}
312313

313314
/// <inheritdoc/>
@@ -323,7 +324,7 @@ public override long ReadInt64()
323324
{
324325
ThrowIfDisposed();
325326
this.ReadBytes(_temp, 0, 8);
326-
return BitConverter.ToInt64(_temp, 0);
327+
return BinaryPrimitives.ReadInt64LittleEndian(_temp);
327328
}
328329

329330
/// <inheritdoc/>
@@ -494,7 +495,8 @@ public override void WriteDecimal128(Decimal128 value)
494495
public override void WriteDouble(double value)
495496
{
496497
ThrowIfDisposed();
497-
var bytes = BitConverter.GetBytes(value);
498+
var bytes = new byte[8];
499+
BinaryPrimitivesCompat.WriteDoubleLittleEndian(bytes, value);
498500
_stream.Write(bytes, 0, 8);
499501
}
500502

@@ -513,7 +515,8 @@ public override void WriteInt32(int value)
513515
public override void WriteInt64(long value)
514516
{
515517
ThrowIfDisposed();
516-
var bytes = BitConverter.GetBytes(value);
518+
var bytes = new byte[8];
519+
BinaryPrimitives.WriteInt64LittleEndian(bytes, value);
517520
_stream.Write(bytes, 0, 8);
518521
}
519522

src/MongoDB.Bson/IO/ByteBufferStream.cs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
using System;
1717
using System.IO;
1818
using System.Text;
19+
using System.Buffers.Binary;
1920

2021
namespace MongoDB.Bson.IO
2122
{
@@ -418,12 +419,12 @@ public override double ReadDouble()
418419
if (segment.Count >= 8)
419420
{
420421
_position += 8;
421-
return BitConverter.ToDouble(segment.Array, segment.Offset);
422+
return BinaryPrimitivesCompat.ReadDoubleLittleEndian(new ReadOnlySpan<byte>(segment.Array, segment.Offset, 8));
422423
}
423424
else
424425
{
425426
this.ReadBytes(_temp, 0, 8);
426-
return BitConverter.ToDouble(_temp, 0);
427+
return BinaryPrimitivesCompat.ReadDoubleLittleEndian(_temp);
427428
}
428429
}
429430

@@ -458,12 +459,12 @@ public override long ReadInt64()
458459
if (segment.Count >= 8)
459460
{
460461
_position += 8;
461-
return BitConverter.ToInt64(segment.Array, segment.Offset);
462+
return BinaryPrimitives.ReadInt64LittleEndian(new ReadOnlySpan<byte>(segment.Array, segment.Offset, 8));
462463
}
463464
else
464465
{
465466
this.ReadBytes(_temp, 0, 8);
466-
return BitConverter.ToInt64(_temp, 0);
467+
return BinaryPrimitives.ReadInt64LittleEndian(_temp);
467468
}
468469
}
469470

@@ -637,7 +638,8 @@ public override void WriteDouble(double value)
637638

638639
PrepareToWrite(8);
639640

640-
var bytes = BitConverter.GetBytes(value);
641+
var bytes = new byte[8];
642+
BinaryPrimitivesCompat.WriteDoubleLittleEndian(bytes, value);
641643
_buffer.SetBytes(_position, bytes, 0, 8);
642644

643645
SetPositionAfterWrite(_position + 8);
@@ -677,7 +679,8 @@ public override void WriteInt64(long value)
677679

678680
PrepareToWrite(8);
679681

680-
var bytes = BitConverter.GetBytes(value);
682+
var bytes = new byte[8];
683+
BinaryPrimitives.WriteInt64LittleEndian(bytes, value);
681684
_buffer.SetBytes(_position, bytes, 0, 8);
682685

683686
SetPositionAfterWrite(_position + 8);
@@ -731,7 +734,8 @@ public override void WriteString(string value, UTF8Encoding encoding)
731734
var bytes = rentedSegmentEncoded.Segment.Array;
732735
actualLength = rentedSegmentEncoded.Segment.Count;
733736

734-
var lengthPlusOneBytes = BitConverter.GetBytes(actualLength + 1);
737+
var lengthPlusOneBytes = new byte[4];
738+
BinaryPrimitives.WriteInt32LittleEndian(lengthPlusOneBytes, actualLength + 1);
735739

736740
_buffer.SetBytes(_position, lengthPlusOneBytes, 0, 4);
737741
_buffer.SetBytes(_position + 4, bytes, 0, actualLength);

src/MongoDB.Bson/IO/ElementAppendingBsonWriter.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
using System;
1717
using System.Collections.Generic;
1818
using System.Linq;
19+
using System.Buffers.Binary;
1920
using MongoDB.Bson.Serialization;
2021
using MongoDB.Bson.Serialization.Serializers;
2122

@@ -83,7 +84,7 @@ public override void WriteRawBsonDocument(IByteBuffer slice)
8384
// just copy the bytes (without the length and terminating null)
8485
var lengthBytes = new byte[4];
8586
slice.GetBytes(0, lengthBytes, 0, 4);
86-
var length = BitConverter.ToInt32(lengthBytes, 0);
87+
var length = BinaryPrimitives.ReadInt32LittleEndian(lengthBytes);
8788
using (var elements = slice.GetSlice(4, length - 5))
8889
{
8990
var stream = binaryWriter.BsonStream;

src/MongoDB.Bson/MongoDB.Bson.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
<ItemGroup>
1616
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="5.0.0" />
17+
<PackageReference Include="System.Memory" Version="4.5.5" />
1718
</ItemGroup>
1819

1920
<ItemGroup>

src/MongoDB.Bson/ObjectModel/GuidConverter.cs

Lines changed: 12 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -39,31 +39,19 @@ public static Guid FromBytes(byte[] bytes, GuidRepresentation representation)
3939
switch (representation)
4040
{
4141
case GuidRepresentation.CSharpLegacy:
42-
if (!BitConverter.IsLittleEndian)
43-
{
44-
Array.Reverse(bytes, 0, 4);
45-
Array.Reverse(bytes, 4, 2);
46-
Array.Reverse(bytes, 6, 2);
47-
}
4842
break;
4943
case GuidRepresentation.JavaLegacy:
5044
Array.Reverse(bytes, 0, 8);
5145
Array.Reverse(bytes, 8, 8);
52-
if (BitConverter.IsLittleEndian)
53-
{
54-
Array.Reverse(bytes, 0, 4);
55-
Array.Reverse(bytes, 4, 2);
56-
Array.Reverse(bytes, 6, 2);
57-
}
46+
Array.Reverse(bytes, 0, 4);
47+
Array.Reverse(bytes, 4, 2);
48+
Array.Reverse(bytes, 6, 2);
5849
break;
5950
case GuidRepresentation.PythonLegacy:
6051
case GuidRepresentation.Standard:
61-
if (BitConverter.IsLittleEndian)
62-
{
63-
Array.Reverse(bytes, 0, 4);
64-
Array.Reverse(bytes, 4, 2);
65-
Array.Reverse(bytes, 6, 2);
66-
}
52+
Array.Reverse(bytes, 0, 4);
53+
Array.Reverse(bytes, 4, 2);
54+
Array.Reverse(bytes, 6, 2);
6755
break;
6856
case GuidRepresentation.Unspecified:
6957
throw new InvalidOperationException("Unable to convert byte array to Guid because GuidRepresentation is Unspecified.");
@@ -107,31 +95,19 @@ public static byte[] ToBytes(Guid guid, GuidRepresentation guidRepresentation)
10795
switch (guidRepresentation)
10896
{
10997
case GuidRepresentation.CSharpLegacy:
110-
if (!BitConverter.IsLittleEndian)
111-
{
112-
Array.Reverse(bytes, 0, 4);
113-
Array.Reverse(bytes, 4, 2);
114-
Array.Reverse(bytes, 6, 2);
115-
}
11698
break;
11799
case GuidRepresentation.JavaLegacy:
118-
if (BitConverter.IsLittleEndian)
119-
{
120-
Array.Reverse(bytes, 0, 4);
121-
Array.Reverse(bytes, 4, 2);
122-
Array.Reverse(bytes, 6, 2);
123-
}
100+
Array.Reverse(bytes, 0, 4);
101+
Array.Reverse(bytes, 4, 2);
102+
Array.Reverse(bytes, 6, 2);
124103
Array.Reverse(bytes, 0, 8);
125104
Array.Reverse(bytes, 8, 8);
126105
break;
127106
case GuidRepresentation.PythonLegacy:
128107
case GuidRepresentation.Standard:
129-
if (BitConverter.IsLittleEndian)
130-
{
131-
Array.Reverse(bytes, 0, 4);
132-
Array.Reverse(bytes, 4, 2);
133-
Array.Reverse(bytes, 6, 2);
134-
}
108+
Array.Reverse(bytes, 0, 4);
109+
Array.Reverse(bytes, 4, 2);
110+
Array.Reverse(bytes, 6, 2);
135111
break;
136112
case GuidRepresentation.Unspecified:
137113
throw new InvalidOperationException("Unable to convert Guid to byte array because GuidRepresentation is Unspecified.");

src/MongoDB.Bson/Serialization/IdGenerators/AscendingGuidGenerator.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
using System;
1717
using System.Threading;
18+
using System.Buffers.Binary;
1819

1920
namespace MongoDB.Bson.Serialization.IdGenerators
2021
{
@@ -39,7 +40,8 @@ public class AscendingGuidGenerator : IIdGenerator
3940
static AscendingGuidGenerator()
4041
{
4142
var random = ObjectId.CalculateRandomValue();
42-
var random8Bytes = BitConverter.GetBytes(random);
43+
var random8Bytes = new byte[8];
44+
BinaryPrimitives.WriteInt64LittleEndian(random8Bytes, random);
4345
__random = new byte[5];
4446
Array.Copy(random8Bytes, __random, 5); // the 5 bytes we need are the first 5 bytes assuming little-endian
4547
}

src/MongoDB.Bson/Serialization/IdGenerators/CombGuidGenerator.cs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
*/
1515

1616
using System;
17+
using System.Buffers.Binary;
1718

1819
namespace MongoDB.Bson.Serialization.IdGenerators
1920
{
@@ -87,13 +88,10 @@ public Guid NewCombGuid(Guid guid, DateTime timestamp)
8788

8889
var bytes = guid.ToByteArray();
8990

90-
Array.Copy(BitConverter.GetBytes(days), 0, bytes, 10, 2);
91-
Array.Copy(BitConverter.GetBytes(timeTicks), 0, bytes, 12, 4);
92-
if (BitConverter.IsLittleEndian)
93-
{
94-
Array.Reverse(bytes, 10, 2);
95-
Array.Reverse(bytes, 12, 4);
96-
}
91+
BinaryPrimitives.WriteUInt16LittleEndian(new Span<byte>(bytes, 10, 2), days);
92+
BinaryPrimitives.WriteInt32LittleEndian(new Span<byte>(bytes, 12, 4), timeTicks);
93+
Array.Reverse(bytes, 10, 2);
94+
Array.Reverse(bytes, 12, 4);
9795

9896
return new Guid(bytes);
9997
}

src/MongoDB.Driver.Core/Core/Connections/BinaryConnection.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
using System.Net;
2323
using System.Threading;
2424
using System.Threading.Tasks;
25+
using System.Buffers.Binary;
2526
using Microsoft.Extensions.Logging;
2627
using MongoDB.Bson.IO;
2728
using MongoDB.Driver.Core.Compression;
@@ -315,7 +316,7 @@ private IByteBuffer ReceiveBuffer(CancellationToken cancellationToken)
315316
{
316317
var messageSizeBytes = new byte[4];
317318
_stream.ReadBytes(messageSizeBytes, 0, 4, cancellationToken);
318-
var messageSize = BitConverter.ToInt32(messageSizeBytes, 0);
319+
var messageSize = BinaryPrimitives.ReadInt32LittleEndian(messageSizeBytes);
319320
EnsureMessageSizeIsValid(messageSize);
320321
var inputBufferChunkSource = new InputBufferChunkSource(BsonChunkPool.Default);
321322
var buffer = ByteBufferFactory.Create(inputBufferChunkSource, messageSize);
@@ -385,7 +386,7 @@ private async Task<IByteBuffer> ReceiveBufferAsync(CancellationToken cancellatio
385386
var messageSizeBytes = new byte[4];
386387
var readTimeout = _stream.CanTimeout ? TimeSpan.FromMilliseconds(_stream.ReadTimeout) : Timeout.InfiniteTimeSpan;
387388
await _stream.ReadBytesAsync(messageSizeBytes, 0, 4, readTimeout, cancellationToken).ConfigureAwait(false);
388-
var messageSize = BitConverter.ToInt32(messageSizeBytes, 0);
389+
var messageSize = BinaryPrimitives.ReadInt32LittleEndian(messageSizeBytes);
389390
EnsureMessageSizeIsValid(messageSize);
390391
var inputBufferChunkSource = new InputBufferChunkSource(BsonChunkPool.Default);
391392
var buffer = ByteBufferFactory.Create(inputBufferChunkSource, messageSize);
@@ -797,7 +798,7 @@ public IByteBuffer RemoveMessage(int responseTo)
797798
private int GetResponseTo(IByteBuffer message)
798799
{
799800
var backingBytes = message.AccessBackingBytes(8);
800-
return BitConverter.ToInt32(backingBytes.Array, backingBytes.Offset);
801+
return BinaryPrimitives.ReadInt32LittleEndian(new ReadOnlySpan<byte>(backingBytes.Array, backingBytes.Offset, 4));
801802
}
802803
}
803804

src/MongoDB.Driver.Core/Core/Connections/KeepAliveValues.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
*/
1515

1616
using System;
17+
using System.Buffers.Binary;
1718

1819
namespace MongoDB.Driver.Core.Connections
1920
{
@@ -30,9 +31,9 @@ internal struct KeepAliveValues
3031
public byte[] ToBytes()
3132
{
3233
var bytes = new byte[12];
33-
Array.Copy(BitConverter.GetBytes(OnOff), 0, bytes, 0, 4);
34-
Array.Copy(BitConverter.GetBytes(KeepAliveTime), 0, bytes, 4, 4);
35-
Array.Copy(BitConverter.GetBytes(KeepAliveInterval), 0, bytes, 8, 4);
34+
BinaryPrimitives.WriteUInt32LittleEndian(new Span<byte>(bytes, 0, 4), OnOff);
35+
BinaryPrimitives.WriteUInt32LittleEndian(new Span<byte>(bytes, 4, 4), KeepAliveTime);
36+
BinaryPrimitives.WriteUInt32LittleEndian(new Span<byte>(bytes, 8, 4), KeepAliveInterval);
3637
return bytes;
3738
}
3839
}

0 commit comments

Comments
 (0)