Skip to content

Commit 02579c1

Browse files
committed
resolved all the comments
1 parent 530ecda commit 02579c1

File tree

5 files changed

+84
-73
lines changed

5 files changed

+84
-73
lines changed

src/MongoDB.Bson/IO/BinaryPrimitivesCompat.cs

Lines changed: 2 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -43,29 +43,8 @@ public static float ReadSingleLittleEndian(ReadOnlySpan<byte> source)
4343
throw new ArgumentOutOfRangeException(nameof(source), "Source span is too small to contain a float.");
4444
}
4545

46-
// Manually construct a 32-bit integer from 4 bytes in Little Endian order.
47-
// BSON mandates that all multibyte values-including float32-must be encoded
48-
// using Little Endian byte order, regardless of the system architecture.
49-
//
50-
// This method ensures platform-agnostic behavior by explicitly assembling
51-
// the bytes in the correct order, rather than relying on the system's native endianness.
52-
//
53-
// Given a byte sequence [a, b, c, d], representing a float encoded in Little Endian,
54-
// the expression below constructs the 32-bit integer as:
55-
// intValue = a + (b << 8) + (c << 16) + (d << 24)
56-
//
57-
// This preserves the intended bit pattern when converting back to float using
58-
// BitConverter.Int32BitsToSingle.
59-
//
60-
// Example:
61-
// A float value of 1.0f is represented in IEEE-754 binary32 format as:
62-
// [0x00, 0x00, 0x80, 0x3F] (Little Endian)
63-
// On a Big Endian system, naive interpretation would yield an incorrect value,
64-
// but this method assembles the int as:
65-
// 0x00 + (0x00 << 8) + (0x80 << 16) + (0x3F << 24) = 0x3F800000,
66-
// which correctly maps to 1.0f.
67-
//
68-
// This guarantees BSON-compliant serialization across all platforms.
46+
// Constructs a 32-bit float from 4 Little Endian bytes in a platform-agnostic way.
47+
// Ensures correct bit pattern regardless of system endianness.
6948
int intValue =
7049
source[0] |
7150
(source[1] << 8) |
@@ -97,9 +76,6 @@ public static void WriteSingleLittleEndian(Span<byte> destination, float value)
9776
#endif
9877
}
9978

100-
// This layout trick allows safely reinterpreting float as int and vice versa.
101-
// It ensures identical memory layout for both fields, used for low-level bit conversion
102-
// in environments like net472 which lack BitConverter.SingleToInt32Bits and its inverse.
10379
[StructLayout(LayoutKind.Explicit)]
10480
private struct FloatIntUnion
10581
{

src/MongoDB.Bson/Serialization/BinaryVectorReader.cs

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,6 @@ public static (TItem[] Items, byte Padding, BinaryVectorDataType VectorDataType)
4242
switch (vectorDataType)
4343
{
4444
case BinaryVectorDataType.Float32:
45-
46-
if ((vectorDataBytes.Span.Length & 3) != 0)
47-
{
48-
throw new FormatException("Data length of binary vector of type Float32 must be a multiple of 4 bytes.");
49-
}
50-
5145
var floatArray = ReadSinglesArrayLittleEndian(vectorDataBytes.Span);
5246
items = (TItem[])(object)floatArray;
5347
break;
@@ -124,15 +118,17 @@ private static float[] ReadSinglesArrayLittleEndian(ReadOnlySpan<byte> span)
124118
{
125119
throw new FormatException("Data length of binary vector of type Float32 must be a multiple of 4 bytes.");
126120
}
127-
int count = span.Length / 4;
128-
float[] result = new float[count];
121+
122+
float[] result;
129123
if (BitConverter.IsLittleEndian)
130124
{
131-
MemoryMarshal.Cast<byte, float>(span).CopyTo(result);
125+
result = MemoryMarshal.Cast<byte, float>(span).ToArray();
132126
}
133127
else
134128
{
135-
for (int i = 0; i < count; i++)
129+
var count = span.Length / 4;
130+
result = new float[count];
131+
for (int i = 0; i < count; i++)
136132
{
137133
result[i] = BinaryPrimitivesCompat.ReadSingleLittleEndian(span.Slice(i * 4, 4));
138134
}

src/MongoDB.Bson/Serialization/BinaryVectorWriter.cs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,18 +36,17 @@ public static byte[] WriteToBytes<TItem>(BinaryVector<TItem> binaryVector)
3636
public static byte[] WriteToBytes<TItem>(ReadOnlySpan<TItem> vectorData, BinaryVectorDataType binaryVectorDataType, byte padding)
3737
where TItem : struct
3838
{
39-
byte[] resultBytes;
40-
4139
switch (binaryVectorDataType)
4240
{
4341
case BinaryVectorDataType.Float32:
44-
var length = vectorData.Length * sizeof(float);
45-
resultBytes = new byte[2 + length];
46-
resultBytes[0] = (byte)binaryVectorDataType;
47-
resultBytes[1] = padding;
42+
byte[] result;
43+
var length = vectorData.Length * 4;
44+
result = new byte[2 + length];
45+
result[0] = (byte)binaryVectorDataType;
46+
result[1] = padding;
4847

4948
var floatSpan = MemoryMarshal.Cast<TItem, float>(vectorData);
50-
Span<byte> floatOutput = resultBytes.AsSpan(2);
49+
var floatOutput = result.AsSpan(2);
5150

5251
if (BitConverter.IsLittleEndian)
5352
{
@@ -61,7 +60,7 @@ public static byte[] WriteToBytes<TItem>(ReadOnlySpan<TItem> vectorData, BinaryV
6160
}
6261
}
6362

64-
return resultBytes;
63+
return result;
6564

6665
case BinaryVectorDataType.Int8:
6766
case BinaryVectorDataType.PackedBit:

tests/MongoDB.Bson.Tests/IO/BinaryPrimitivesCompatTests.cs

Lines changed: 54 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,65 @@
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+
116
using System;
217
using Xunit;
18+
using FluentAssertions;
319
using MongoDB.Bson.IO;
420

521
namespace MongoDB.Bson.Tests.IO
622
{
723
public class BinaryPrimitivesCompatTests
824
{
25+
[Fact]
26+
public void ReadSingleLittleEndian_should_read_correctly()
27+
{
28+
var bytes = new byte[] { 0x00, 0x00, 0x80, 0x3F }; // 1.0f in little endian
29+
var result = BinaryPrimitivesCompat.ReadSingleLittleEndian(bytes);
30+
result.Should().Be(1.0f);
31+
}
32+
33+
[Fact]
34+
public void ReadSingleLittleEndian_should_throw_on_insufficient_length()
35+
{
36+
var shortBuffer = new byte[3];
37+
var exception = Record.Exception(() =>
38+
BinaryPrimitivesCompat.ReadSingleLittleEndian(shortBuffer));
39+
40+
exception.Should().BeOfType<ArgumentOutOfRangeException>();
41+
exception.Message.Should().Contain("length");
42+
}
43+
44+
[Fact]
45+
public void WriteSingleLittleEndian_should_throw_on_insufficient_length()
46+
{
47+
var shortBuffer = new byte[3];
48+
var exception = Record.Exception(() =>
49+
BinaryPrimitivesCompat.WriteSingleLittleEndian(shortBuffer, 1.23f));
50+
51+
exception.Should().BeOfType<ArgumentOutOfRangeException>();
52+
exception.Message.Should().Contain("length");
53+
}
54+
55+
[Fact]
56+
public void WriteSingleLittleEndian_should_write_correctly()
57+
{
58+
Span<byte> buffer = new byte[4];
59+
BinaryPrimitivesCompat.WriteSingleLittleEndian(buffer, 1.0f);
60+
buffer.ToArray().Should().Equal(0x00, 0x00, 0x80, 0x3F); // 1.0f little-endian
61+
}
62+
963
[Theory]
1064
[InlineData(0f)]
1165
[InlineData(1.0f)]
@@ -31,22 +85,5 @@ public void WriteAndReadSingleLittleEndian_should_roundtrip_correctly(float valu
3185
Assert.Equal(value, result);
3286
}
3387
}
34-
35-
[Fact]
36-
public void ReadSingleLittleEndian_should_throw_on_insufficient_length()
37-
{
38-
var shortBuffer = new byte[3];
39-
Assert.Throws<ArgumentOutOfRangeException>(() =>
40-
BinaryPrimitivesCompat.ReadSingleLittleEndian(shortBuffer));
41-
}
42-
43-
[Fact]
44-
public void WriteSingleLittleEndian_should_throw_on_insufficient_length()
45-
{
46-
var shortBuffer = new byte[3];
47-
Assert.Throws<ArgumentOutOfRangeException>(() =>
48-
BinaryPrimitivesCompat.WriteSingleLittleEndian(shortBuffer, 1.23f));
49-
}
5088
}
5189
}
52-

tests/MongoDB.Bson.Tests/Serialization/Serializers/BinaryVectorSerializerTests.cs

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,7 @@ private static (T[], byte[] VectorBson) GetTestData<T>(BinaryVectorDataType data
371371
.ToArray());
372372
var elementsBytesLittleEndian = BitConverter.IsLittleEndian
373373
? MemoryMarshal.Cast<T, byte>(elementsSpan)
374-
: ToLittleEndian(elementsSpan, dataType);
374+
: BigEndianToLittleEndian(elementsSpan, dataType);
375375

376376
byte[] vectorBsonData = [(byte)dataType, bitsPadding, .. elementsBytesLittleEndian];
377377
return (elementsSpan.ToArray(), vectorBsonData);
@@ -415,31 +415,34 @@ private static IBsonSerializer CreateBinaryVectorSerializer<T>(BinaryVectorDataT
415415
return serializer;
416416
}
417417

418-
public class BinaryVectorNoAttributeHolder
419-
{
420-
public BinaryVectorInt8 ValuesInt8 { get; set; }
421-
422-
public BinaryVectorPackedBit ValuesPackedBit { get; set; }
423-
424-
public BinaryVectorFloat32 ValuesFloat { get; set; }
425-
}
426-
427-
private static byte[] ToLittleEndian<T>(ReadOnlySpan<T> span, BinaryVectorDataType dataType) where T : struct
418+
private static byte[] BigEndianToLittleEndian<T>(ReadOnlySpan<T> span, BinaryVectorDataType dataType) where T : struct
428419
{
429420
// Types that do NOT need conversion safe on BE
430421
if (dataType == BinaryVectorDataType.Int8 || dataType == BinaryVectorDataType.PackedBit)
431422
{
432423
return MemoryMarshal.Cast<T, byte>(span).ToArray();
433424
}
434-
int elementSize = Marshal.SizeOf<T>();
425+
426+
var elementSize = Marshal.SizeOf<T>();
435427
byte[] result = new byte[span.Length * elementSize];
428+
436429
for (int i = 0; i < span.Length; i++)
437430
{
438431
byte[] bytes = BitConverter.GetBytes((dynamic)span[i]);
439432
Array.Reverse(bytes); // Ensure LE order
440433
Buffer.BlockCopy(bytes, 0, result, i * elementSize, elementSize);
441434
}
435+
442436
return result;
443437
}
438+
439+
public class BinaryVectorNoAttributeHolder
440+
{
441+
public BinaryVectorInt8 ValuesInt8 { get; set; }
442+
443+
public BinaryVectorPackedBit ValuesPackedBit { get; set; }
444+
445+
public BinaryVectorFloat32 ValuesFloat { get; set; }
446+
}
444447
}
445448
}

0 commit comments

Comments
 (0)