From 89a8e71c38d516231e0f2266fdfef890d02b2d25 Mon Sep 17 00:00:00 2001 From: Medha Tiwari Date: Tue, 6 May 2025 07:56:40 +0200 Subject: [PATCH 1/3] fix(bson): correct deserialization of primitive arrays using BinaryPrimitives for Big Endian support Signed-off-by: Medha Tiwari --- .../Serializers/PrimitivesArrayReader.cs | 36 +++++++++---------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/src/MongoDB.Bson/Serialization/Serializers/PrimitivesArrayReader.cs b/src/MongoDB.Bson/Serialization/Serializers/PrimitivesArrayReader.cs index e811edef230..0e756d26b9e 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/PrimitivesArrayReader.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/PrimitivesArrayReader.cs @@ -1,4 +1,5 @@ using System; +using System.Buffers.Binary; using System.Collections.Generic; using System.Runtime.CompilerServices; using MongoDB.Bson.IO; @@ -77,6 +78,7 @@ private static T[] ReadBsonArray( var result = new List(); + var index = 4; // 4 first bytes are array object size in bytes var maxIndex = array.Length - 1; @@ -85,7 +87,7 @@ private static T[] ReadBsonArray( ValidateBsonType(bsonDataType); // Skip name - while (bytes[index] != 0) { index++; }; + while (bytes[index] != 0) { index++; } index++; // Skip string terminating 0 T value = default; @@ -95,85 +97,81 @@ private static T[] ReadBsonArray( { case ConversionType.DoubleToSingle: { - var v = (float)BitConverter.ToDouble(bytes, index); - + var v = (float)BitConverter.Int64BitsToDouble(BinaryPrimitives.ReadInt64LittleEndian(bytes.AsSpan(index))); value = Unsafe.As(ref v); break; } case ConversionType.DoubleToDouble: { - var v = BitConverter.ToDouble(bytes, index); + var v = BitConverter.Int64BitsToDouble(BinaryPrimitives.ReadInt64LittleEndian(bytes.AsSpan(index))); value = Unsafe.As(ref v); break; } case ConversionType.Decimal128ToDecimal128: { - var lowBits = (ulong)BitConverter.ToInt64(bytes, index); - var highBits = (ulong)BitConverter.ToInt64(bytes, index + 8); + var lowBits = BinaryPrimitives.ReadUInt64LittleEndian(bytes.AsSpan(index)); + var highBits = BinaryPrimitives.ReadUInt64LittleEndian(bytes.AsSpan(index + 8)); var v = Decimal128.ToDecimal(Decimal128.FromIEEEBits(highBits, lowBits)); - value = Unsafe.As(ref v); break; } case ConversionType.BoolToBool: { var v = bytes[index] != 0; - value = Unsafe.As(ref v); break; } case ConversionType.Int32ToInt8: { - var v = (sbyte)BitConverter.ToInt32(bytes, index); + var v = (sbyte)BinaryPrimitives.ReadInt32LittleEndian(bytes.AsSpan(index)); value = Unsafe.As(ref v); - break; } case ConversionType.Int32ToUInt8: { - var v = (byte)BitConverter.ToInt32(bytes, index); + var v = (byte)BinaryPrimitives.ReadInt32LittleEndian(bytes.AsSpan(index)); value = Unsafe.As(ref v); break; } case ConversionType.Int32ToInt16: { - var v = (short)BitConverter.ToInt32(bytes, index); + var v = (short)BinaryPrimitives.ReadInt32LittleEndian(bytes.AsSpan(index)); value = Unsafe.As(ref v); break; } case ConversionType.Int32ToUInt16: { - var v = (ushort)BitConverter.ToInt32(bytes, index); + var v = (ushort)BinaryPrimitives.ReadInt32LittleEndian(bytes.AsSpan(index)); value = Unsafe.As(ref v); break; } case ConversionType.Int32ToChar: { - var v = BitConverter.ToChar(bytes, index); + var v = (char)(ushort)BinaryPrimitives.ReadInt32LittleEndian(bytes.AsSpan(index)); value = Unsafe.As(ref v); break; } case ConversionType.Int32ToInt32: { - var v = BitConverter.ToInt32(bytes, index); + var v = BinaryPrimitives.ReadInt32LittleEndian(bytes.AsSpan(index)); value = Unsafe.As(ref v); break; } case ConversionType.Int32ToUInt32: { - var v = BitConverter.ToUInt32(bytes, index); + var v = BinaryPrimitives.ReadUInt32LittleEndian(bytes.AsSpan(index)); value = Unsafe.As(ref v); break; } case ConversionType.Int64ToInt64: { - var v = BitConverter.ToInt64(bytes, index); + var v = BinaryPrimitives.ReadInt64LittleEndian(bytes.AsSpan(index)); value = Unsafe.As(ref v); break; } case ConversionType.Int64ToUInt64: { - var v = BitConverter.ToUInt64(bytes, index); + var v = BinaryPrimitives.ReadUInt64LittleEndian(bytes.AsSpan(index)); value = Unsafe.As(ref v); break; } @@ -182,12 +180,10 @@ private static T[] ReadBsonArray( } result.Add(value); - index += bsonDataSize; } ValidateBsonType(BsonType.EndOfDocument); - return result.ToArray(); void ValidateBsonType(BsonType bsonType) From 91803387fb66f4b222c951bcfff45974eadf6c09 Mon Sep 17 00:00:00 2001 From: Medha Tiwari Date: Wed, 11 Jun 2025 20:38:47 +0200 Subject: [PATCH 2/3] CSHARP-5614: Fix deserialization of primitive arrays on Big Endian systems Signed-off-by: Medha Tiwari --- .../Serializers/PrimitivesArrayReader.cs | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/MongoDB.Bson/Serialization/Serializers/PrimitivesArrayReader.cs b/src/MongoDB.Bson/Serialization/Serializers/PrimitivesArrayReader.cs index 0e756d26b9e..53dc50a5846 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/PrimitivesArrayReader.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/PrimitivesArrayReader.cs @@ -74,20 +74,20 @@ private static T[] ReadBsonArray( using var buffer = ThreadStaticBuffer.RentBuffer(array.Length); var bytes = buffer.Bytes; - array.GetBytes(0, bytes, 0, array.Length); + array.GetBytes(0, bytes, 0, array.Length); + var span = bytes.AsSpan(); var result = new List(); - - + var index = 4; // 4 first bytes are array object size in bytes var maxIndex = array.Length - 1; while (index < maxIndex) { - ValidateBsonType(bsonDataType); + ValidateBsonType(bsonDataType, span); // Skip name - while (bytes[index] != 0) { index++; } + while (span[index] != 0) { index++; } index++; // Skip string terminating 0 T value = default; @@ -97,81 +97,81 @@ private static T[] ReadBsonArray( { case ConversionType.DoubleToSingle: { - var v = (float)BitConverter.Int64BitsToDouble(BinaryPrimitives.ReadInt64LittleEndian(bytes.AsSpan(index))); + var v = (float)BinaryPrimitivesCompat.ReadDoubleLittleEndian(span.Slice(index)); value = Unsafe.As(ref v); break; } case ConversionType.DoubleToDouble: { - var v = BitConverter.Int64BitsToDouble(BinaryPrimitives.ReadInt64LittleEndian(bytes.AsSpan(index))); + var v = BitConverter.Int64BitsToDouble(BinaryPrimitives.ReadInt64LittleEndian(span.Slice(index))); value = Unsafe.As(ref v); break; } case ConversionType.Decimal128ToDecimal128: { - var lowBits = BinaryPrimitives.ReadUInt64LittleEndian(bytes.AsSpan(index)); - var highBits = BinaryPrimitives.ReadUInt64LittleEndian(bytes.AsSpan(index + 8)); + var lowBits = BinaryPrimitives.ReadUInt64LittleEndian(span.Slice(index)); + var highBits = BinaryPrimitives.ReadUInt64LittleEndian(span.Slice(index + 8)); var v = Decimal128.ToDecimal(Decimal128.FromIEEEBits(highBits, lowBits)); value = Unsafe.As(ref v); break; } case ConversionType.BoolToBool: { - var v = bytes[index] != 0; + var v = span[index] != 0; value = Unsafe.As(ref v); break; } case ConversionType.Int32ToInt8: { - var v = (sbyte)BinaryPrimitives.ReadInt32LittleEndian(bytes.AsSpan(index)); + var v = (sbyte)BinaryPrimitives.ReadInt32LittleEndian(span.Slice(index)); value = Unsafe.As(ref v); break; } case ConversionType.Int32ToUInt8: { - var v = (byte)BinaryPrimitives.ReadInt32LittleEndian(bytes.AsSpan(index)); + var v = (byte)BinaryPrimitives.ReadInt32LittleEndian(span.Slice(index)); value = Unsafe.As(ref v); break; } case ConversionType.Int32ToInt16: { - var v = (short)BinaryPrimitives.ReadInt32LittleEndian(bytes.AsSpan(index)); + var v = (short)BinaryPrimitives.ReadInt32LittleEndian(span.Slice(index)); value = Unsafe.As(ref v); break; } case ConversionType.Int32ToUInt16: { - var v = (ushort)BinaryPrimitives.ReadInt32LittleEndian(bytes.AsSpan(index)); + var v = (ushort)BinaryPrimitives.ReadInt32LittleEndian(span.Slice(index)); value = Unsafe.As(ref v); break; } case ConversionType.Int32ToChar: { - var v = (char)(ushort)BinaryPrimitives.ReadInt32LittleEndian(bytes.AsSpan(index)); + var v = (char)(ushort)BinaryPrimitives.ReadInt32LittleEndian(span.Slice(index)); value = Unsafe.As(ref v); break; } case ConversionType.Int32ToInt32: { - var v = BinaryPrimitives.ReadInt32LittleEndian(bytes.AsSpan(index)); + var v = BinaryPrimitives.ReadInt32LittleEndian(span.Slice(index)); value = Unsafe.As(ref v); break; } case ConversionType.Int32ToUInt32: { - var v = BinaryPrimitives.ReadUInt32LittleEndian(bytes.AsSpan(index)); + var v = BinaryPrimitives.ReadUInt32LittleEndian(span.Slice(index)); value = Unsafe.As(ref v); break; } case ConversionType.Int64ToInt64: { - var v = BinaryPrimitives.ReadInt64LittleEndian(bytes.AsSpan(index)); + var v = BinaryPrimitives.ReadInt64LittleEndian(span.Slice(index)); value = Unsafe.As(ref v); break; } case ConversionType.Int64ToUInt64: { - var v = BinaryPrimitives.ReadUInt64LittleEndian(bytes.AsSpan(index)); + var v = BinaryPrimitives.ReadUInt64LittleEndian(span.Slice(index)); value = Unsafe.As(ref v); break; } @@ -183,12 +183,12 @@ private static T[] ReadBsonArray( index += bsonDataSize; } - ValidateBsonType(BsonType.EndOfDocument); + ValidateBsonType(BsonType.EndOfDocument, span); return result.ToArray(); - void ValidateBsonType(BsonType bsonType) + void ValidateBsonType(BsonType bsonType, Span span) { - if ((BsonType)bytes[index] != bsonType) + if ((BsonType)span[index] != bsonType) { throw new InvalidOperationException(); } From 31d88d57e503971bc64dc5a15418ad0afe416959 Mon Sep 17 00:00:00 2001 From: Medha Tiwari Date: Wed, 11 Jun 2025 22:05:50 +0200 Subject: [PATCH 3/3] CSHARP-5614: added BinaryPrimitivesCompat.ReadDoubleLittleEndian for d2d conversion and reverted extra spaces Signed-off-by: Medha Tiwari --- .../Serializers/PrimitivesArrayReader.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/MongoDB.Bson/Serialization/Serializers/PrimitivesArrayReader.cs b/src/MongoDB.Bson/Serialization/Serializers/PrimitivesArrayReader.cs index 53dc50a5846..c8b9bd27cd7 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/PrimitivesArrayReader.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/PrimitivesArrayReader.cs @@ -78,7 +78,7 @@ private static T[] ReadBsonArray( var span = bytes.AsSpan(); var result = new List(); - + var index = 4; // 4 first bytes are array object size in bytes var maxIndex = array.Length - 1; @@ -98,12 +98,14 @@ private static T[] ReadBsonArray( case ConversionType.DoubleToSingle: { var v = (float)BinaryPrimitivesCompat.ReadDoubleLittleEndian(span.Slice(index)); + value = Unsafe.As(ref v); break; } case ConversionType.DoubleToDouble: { - var v = BitConverter.Int64BitsToDouble(BinaryPrimitives.ReadInt64LittleEndian(span.Slice(index))); + var v = BinaryPrimitivesCompat.ReadDoubleLittleEndian(span.Slice(index)); + value = Unsafe.As(ref v); break; } @@ -112,12 +114,14 @@ private static T[] ReadBsonArray( var lowBits = BinaryPrimitives.ReadUInt64LittleEndian(span.Slice(index)); var highBits = BinaryPrimitives.ReadUInt64LittleEndian(span.Slice(index + 8)); var v = Decimal128.ToDecimal(Decimal128.FromIEEEBits(highBits, lowBits)); + value = Unsafe.As(ref v); break; } case ConversionType.BoolToBool: { var v = span[index] != 0; + value = Unsafe.As(ref v); break; } @@ -125,6 +129,7 @@ private static T[] ReadBsonArray( { var v = (sbyte)BinaryPrimitives.ReadInt32LittleEndian(span.Slice(index)); value = Unsafe.As(ref v); + break; } case ConversionType.Int32ToUInt8: @@ -180,10 +185,12 @@ private static T[] ReadBsonArray( } result.Add(value); + index += bsonDataSize; } ValidateBsonType(BsonType.EndOfDocument, span); + return result.ToArray(); void ValidateBsonType(BsonType bsonType, Span span)