Skip to content

Commit c7bb9b3

Browse files
committed
Handle big-endian floating-point values.
Signed-off-by: Bradley Grainger <[email protected]>
1 parent c9bd57c commit c7bb9b3

File tree

2 files changed

+88
-10
lines changed

2 files changed

+88
-10
lines changed
Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.Buffers.Binary;
12
using System.Runtime.InteropServices;
23
using MySqlConnector.Protocol.Payloads;
34

@@ -7,6 +8,30 @@ internal sealed class VectorColumnReader : ColumnReader
78
{
89
public static VectorColumnReader Instance { get; } = new();
910

10-
public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
11-
new ReadOnlyMemory<float>(MemoryMarshal.Cast<byte, float>(data).ToArray());
11+
public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
12+
{
13+
if (BitConverter.IsLittleEndian)
14+
{
15+
return new ReadOnlyMemory<float>(MemoryMarshal.Cast<byte, float>(data).ToArray());
16+
}
17+
else
18+
{
19+
var floats = new float[data.Length / 4];
20+
21+
#if !NET5_0_OR_GREATER
22+
var bytes = data.ToArray();
23+
#endif
24+
for (var i = 0; i < floats.Length; i++)
25+
{
26+
#if NET5_0_OR_GREATER
27+
floats[i] = BinaryPrimitives.ReadSingleLittleEndian(data.Slice(i * 4));
28+
#else
29+
Array.Reverse(bytes, i * 4, 4);
30+
floats[i] = BitConverter.ToSingle(bytes, i * 4);
31+
#endif
32+
}
33+
34+
return new ReadOnlyMemory<float>(floats);
35+
}
36+
}
1237
}

src/MySqlConnector/MySqlParameter.cs

Lines changed: 61 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.Buffers.Binary;
12
using System.Buffers.Text;
23
using System.Data.Common;
34
using System.Diagnostics;
@@ -294,9 +295,9 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions
294295
Memory<byte> memory => memory.Span,
295296
MySqlGeometry geometry => geometry.ValueSpan,
296297
MemoryStream memoryStream => memoryStream.TryGetBuffer(out var streamBuffer) ? streamBuffer.AsSpan() : memoryStream.ToArray().AsSpan(),
297-
float[] floatArray => MemoryMarshal.AsBytes(floatArray.AsSpan()),
298-
Memory<float> memory => MemoryMarshal.AsBytes(memory.Span),
299-
ReadOnlyMemory<float> memory => MemoryMarshal.AsBytes(memory.Span),
298+
float[] floatArray => ConvertFloatsToBytes(floatArray.AsSpan()),
299+
Memory<float> memory => ConvertFloatsToBytes(memory.Span),
300+
ReadOnlyMemory<float> memory => ConvertFloatsToBytes(memory.Span),
300301
_ => ((ReadOnlyMemory<byte>) Value).Span,
301302
};
302303

@@ -729,26 +730,52 @@ private void AppendBinary(ByteBufferWriter writer, object value, StatementPrepar
729730
}
730731
else if (value is float floatValue)
731732
{
732-
writer.Write(BitConverter.GetBytes(floatValue));
733+
#if NET5_0_OR_GREATER
734+
Span<byte> bytes = stackalloc byte[4];
735+
BinaryPrimitives.WriteSingleLittleEndian(bytes, floatValue);
736+
writer.Write(bytes);
737+
#else
738+
// convert float to bytes with correct endianness (MySQL uses little-endian)
739+
var bytes = BitConverter.GetBytes(floatValue);
740+
if (!BitConverter.IsLittleEndian)
741+
Array.Reverse(bytes);
742+
writer.Write(bytes);
743+
#endif
733744
}
734745
else if (value is double doubleValue)
735746
{
736-
writer.Write(unchecked((ulong) BitConverter.DoubleToInt64Bits(doubleValue)));
747+
#if NET5_0_OR_GREATER
748+
Span<byte> bytes = stackalloc byte[8];
749+
BinaryPrimitives.WriteDoubleLittleEndian(bytes, doubleValue);
750+
writer.Write(bytes);
751+
#else
752+
if (BitConverter.IsLittleEndian)
753+
{
754+
writer.Write(unchecked((ulong) BitConverter.DoubleToInt64Bits(doubleValue)));
755+
}
756+
else
757+
{
758+
// convert double to bytes with correct endianness (MySQL uses little-endian)
759+
var bytes = BitConverter.GetBytes(doubleValue);
760+
Array.Reverse(bytes);
761+
writer.Write(bytes);
762+
}
763+
#endif
737764
}
738765
else if (value is float[] floatArrayValue)
739766
{
740767
writer.WriteLengthEncodedInteger(unchecked((ulong) floatArrayValue.Length * 4));
741-
writer.Write(MemoryMarshal.AsBytes(floatArrayValue.AsSpan()));
768+
writer.Write(ConvertFloatsToBytes(floatArrayValue.AsSpan()));
742769
}
743770
else if (value is Memory<float> floatMemory)
744771
{
745772
writer.WriteLengthEncodedInteger(unchecked((ulong) floatMemory.Length * 4));
746-
writer.Write(MemoryMarshal.AsBytes(floatMemory.Span));
773+
writer.Write(ConvertFloatsToBytes(floatMemory.Span));
747774
}
748775
else if (value is ReadOnlyMemory<float> floatReadOnlyMemory)
749776
{
750777
writer.WriteLengthEncodedInteger(unchecked((ulong) floatReadOnlyMemory.Length * 4));
751-
writer.Write(MemoryMarshal.AsBytes(floatReadOnlyMemory.Span));
778+
writer.Write(ConvertFloatsToBytes(floatReadOnlyMemory.Span));
752779
}
753780
else if (value is decimal decimalValue)
754781
{
@@ -967,6 +994,32 @@ private static void WriteTime(ByteBufferWriter writer, TimeSpan timeSpan)
967994
}
968995
}
969996

997+
private static ReadOnlySpan<byte> ConvertFloatsToBytes(ReadOnlySpan<float> floats)
998+
{
999+
if (BitConverter.IsLittleEndian)
1000+
{
1001+
return MemoryMarshal.AsBytes(floats);
1002+
}
1003+
else
1004+
{
1005+
// for big-endian platforms, we need to convert each float individually
1006+
var bytes = new byte[floats.Length * 4];
1007+
1008+
for (var i = 0; i < floats.Length; i++)
1009+
{
1010+
#if NET5_0_OR_GREATER
1011+
BinaryPrimitives.WriteSingleLittleEndian(bytes.AsSpan(i * 4), floats[i]);
1012+
#else
1013+
var floatBytes = BitConverter.GetBytes(floats[i]);
1014+
Array.Reverse(floatBytes);
1015+
floatBytes.CopyTo(bytes, i * 4);
1016+
#endif
1017+
}
1018+
1019+
return bytes;
1020+
}
1021+
}
1022+
9701023
private static ReadOnlySpan<byte> BinaryBytes => "_binary'"u8;
9711024

9721025
private DbType m_dbType;

0 commit comments

Comments
 (0)