Skip to content

Commit d18e5c1

Browse files
authored
Support For Big Endian Systems (#2170)
1 parent 59080c8 commit d18e5c1

File tree

10 files changed

+131
-30
lines changed

10 files changed

+131
-30
lines changed

src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@
3333
<Compile Include="..\..\src\Microsoft\Data\Common\AdapterUtil.cs">
3434
<Link>Microsoft\Data\Common\AdapterUtil.cs</Link>
3535
</Compile>
36+
<Compile Include="..\..\src\Microsoft\Data\Common\BitConverterCompatible.cs">
37+
<Link>Microsoft\Data\Common\BitConverterCompatible.cs</Link>
38+
</Compile>
3639
<Compile Include="..\..\src\Microsoft\Data\Common\DbConnectionOptions.Common.cs">
3740
<Link>Microsoft\Data\Common\DbConnectionOptions.Common.cs</Link>
3841
</Compile>
@@ -1008,4 +1011,4 @@
10081011
<Import Project="$(ToolsDir)targets\ResolveContract.targets" Condition="'$(OSGroup)' == 'AnyOS'" />
10091012
<Import Project="$(ToolsDir)targets\NotSupported.targets" Condition="'$(OSGroup)' == 'AnyOS'" />
10101013
<Import Project="..\..\src\tools\targets\GenerateResourceStringsSource.targets" />
1011-
</Project>
1014+
</Project>

src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNICommon.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// See the LICENSE file in the project root for more information.
44

55
using System;
6+
using System.Buffers.Binary;
67
using System.Diagnostics;
78
using System.Net;
89
using System.Net.Security;
@@ -59,10 +60,11 @@ public void Read(byte[] bytes)
5960
{
6061
SMID = bytes[0];
6162
flags = bytes[1];
62-
sessionId = BitConverter.ToUInt16(bytes, 2);
63-
length = BitConverter.ToUInt32(bytes, 4) - SNISMUXHeader.HEADER_LENGTH;
64-
sequenceNumber = BitConverter.ToUInt32(bytes, 8);
65-
highwater = BitConverter.ToUInt32(bytes, 12);
63+
Span<byte> span = bytes.AsSpan();
64+
sessionId = BinaryPrimitives.ReadUInt16LittleEndian(span.Slice(2));
65+
length = BinaryPrimitives.ReadUInt32LittleEndian(span.Slice(4)) - SNISMUXHeader.HEADER_LENGTH;
66+
sequenceNumber = BinaryPrimitives.ReadUInt32LittleEndian(span.Slice(8));
67+
highwater = BinaryPrimitives.ReadUInt32LittleEndian(span.Slice(12));
6668
}
6769

6870
public void Write(Span<byte> bytes)

src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.NetCoreApp.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,17 @@
44

55
using System;
66
using System.Diagnostics;
7+
using System.Buffers.Binary;
78

89
namespace Microsoft.Data.SqlClient
910
{
1011
internal sealed partial class TdsParser
1112
{
1213
internal static void FillGuidBytes(Guid guid, Span<byte> buffer) => guid.TryWriteBytes(buffer);
1314

14-
internal static void FillDoubleBytes(double value, Span<byte> buffer) => BitConverter.TryWriteBytes(buffer, value);
15+
internal static void FillDoubleBytes(double value, Span<byte> buffer) => BinaryPrimitives.TryWriteInt64LittleEndian(buffer, BitConverter.DoubleToInt64Bits(value));
1516

16-
internal static void FillFloatBytes(float v, Span<byte> buffer) => BitConverter.TryWriteBytes(buffer, v);
17+
internal static void FillFloatBytes(float value, Span<byte> buffer) => BinaryPrimitives.TryWriteInt32LittleEndian(buffer, BitConverterCompatible.SingleToInt32Bits(value));
1718

1819
internal static Guid ConstructGuid(ReadOnlySpan<byte> bytes)
1920
{

src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
using System;
66
using System.Buffers;
7+
using System.Buffers.Binary;
78
using System.Collections.Generic;
89
using System.Data;
910
using System.Data.SqlTypes;
@@ -1760,7 +1761,7 @@ internal void WriteInt(int v, TdsParserStateObject stateObj)
17601761
internal static void WriteInt(Span<byte> buffer, int value)
17611762
{
17621763
#if NETCOREAPP
1763-
BitConverter.TryWriteBytes(buffer, value);
1764+
BinaryPrimitives.TryWriteInt32LittleEndian(buffer, value);
17641765
#else
17651766
buffer[0] = (byte)(value & 0xff);
17661767
buffer[1] = (byte)((value >> 8) & 0xff);
@@ -1779,7 +1780,9 @@ internal byte[] SerializeFloat(float v)
17791780
throw ADP.ParameterValueOutOfRange(v.ToString());
17801781
}
17811782

1782-
return BitConverter.GetBytes(v);
1783+
var bytes = new byte[4];
1784+
BinaryPrimitives.WriteInt32LittleEndian(bytes, BitConverterCompatible.SingleToInt32Bits(v));
1785+
return bytes;
17831786
}
17841787

17851788
internal void WriteFloat(float v, TdsParserStateObject stateObj)
@@ -1902,7 +1905,9 @@ internal byte[] SerializeDouble(double v)
19021905
throw ADP.ParameterValueOutOfRange(v.ToString());
19031906
}
19041907

1905-
return BitConverter.GetBytes(v);
1908+
var bytes = new byte[8];
1909+
BinaryPrimitives.WriteInt64LittleEndian(bytes, BitConverter.DoubleToInt64Bits(v));
1910+
return bytes;
19061911
}
19071912

19081913
internal void WriteDouble(double v, TdsParserStateObject stateObj)
@@ -3829,8 +3834,8 @@ private bool TryProcessFedAuthInfo(TdsParserStateObject stateObj, int tokenLen,
38293834
uint currentOptionOffset = checked(i * optionSize);
38303835

38313836
byte id = tokenData[currentOptionOffset];
3832-
uint dataLen = BitConverter.ToUInt32(tokenData, checked((int)(currentOptionOffset + 1)));
3833-
uint dataOffset = BitConverter.ToUInt32(tokenData, checked((int)(currentOptionOffset + 5)));
3837+
uint dataLen = BinaryPrimitives.ReadUInt32LittleEndian(tokenData.AsSpan(checked((int)(currentOptionOffset + 1))));
3838+
uint dataOffset = BinaryPrimitives.ReadUInt32LittleEndian(tokenData.AsSpan(checked((int)(currentOptionOffset + 5))));
38343839
if (SqlClientEventSource.Log.IsAdvancedTraceOn())
38353840
{
38363841
SqlClientEventSource.Log.AdvancedTraceEvent("<sc.TdsParser.TryProcessFedAuthInfo> FedAuthInfoOpt: ID={0}, DataLen={1}, Offset={2}", id, dataLen.ToString(CultureInfo.InvariantCulture), dataOffset.ToString(CultureInfo.InvariantCulture));
@@ -5799,7 +5804,7 @@ internal bool DeserializeUnencryptedValue(SqlBuffer value, byte[] unencryptedByt
57995804
return false;
58005805
}
58015806

5802-
longValue = BitConverter.ToInt64(unencryptedBytes, 0);
5807+
longValue = BinaryPrimitives.ReadInt64LittleEndian(unencryptedBytes);
58035808

58045809
if (tdsType == TdsEnums.SQLBIT ||
58055810
tdsType == TdsEnums.SQLBITN)
@@ -5837,7 +5842,7 @@ internal bool DeserializeUnencryptedValue(SqlBuffer value, byte[] unencryptedByt
58375842
return false;
58385843
}
58395844

5840-
singleValue = BitConverter.ToSingle(unencryptedBytes, 0);
5845+
singleValue = BitConverterCompatible.Int32BitsToSingle(BinaryPrimitives.ReadInt32LittleEndian(unencryptedBytes));
58415846
value.Single = singleValue;
58425847
break;
58435848

@@ -5848,7 +5853,7 @@ internal bool DeserializeUnencryptedValue(SqlBuffer value, byte[] unencryptedByt
58485853
return false;
58495854
}
58505855

5851-
doubleValue = BitConverter.ToDouble(unencryptedBytes, 0);
5856+
doubleValue = BitConverter.Int64BitsToDouble(BinaryPrimitives.ReadInt64LittleEndian(unencryptedBytes));
58525857
value.Double = doubleValue;
58535858
break;
58545859

@@ -5865,8 +5870,8 @@ internal bool DeserializeUnencryptedValue(SqlBuffer value, byte[] unencryptedByt
58655870
return false;
58665871
}
58675872

5868-
mid = BitConverter.ToInt32(unencryptedBytes, 0);
5869-
lo = BitConverter.ToUInt32(unencryptedBytes, 4);
5873+
mid = BinaryPrimitives.ReadInt32LittleEndian(unencryptedBytes);
5874+
lo = BinaryPrimitives.ReadUInt32LittleEndian(unencryptedBytes.AsSpan(4));
58705875

58715876
long l = (((long)mid) << 0x20) + ((long)lo);
58725877
value.SetToMoney(l);
@@ -5903,8 +5908,8 @@ internal bool DeserializeUnencryptedValue(SqlBuffer value, byte[] unencryptedByt
59035908
return false;
59045909
}
59055910

5906-
daypart = BitConverter.ToInt32(unencryptedBytes, 0);
5907-
timepart = BitConverter.ToUInt32(unencryptedBytes, 4);
5911+
daypart = BinaryPrimitives.ReadInt32LittleEndian(unencryptedBytes);
5912+
timepart = BinaryPrimitives.ReadUInt32LittleEndian(unencryptedBytes.AsSpan(4));
59085913
value.SetToDateTime(daypart, (int)timepart);
59095914
break;
59105915

@@ -5947,10 +5952,11 @@ internal bool DeserializeUnencryptedValue(SqlBuffer value, byte[] unencryptedByt
59475952
length = checked((int)length - 1);
59485953
int[] bits = new int[4];
59495954
int decLength = length >> 2;
5955+
var span = unencryptedBytes.AsSpan();
59505956
for (int i = 0; i < decLength; i++)
59515957
{
59525958
// up to 16 bytes of data following the sign byte
5953-
bits[i] = BitConverter.ToInt32(unencryptedBytes, index);
5959+
bits[i] = BinaryPrimitives.ReadInt32LittleEndian(span.Slice(index));
59545960
index += 4;
59555961
}
59565962
value.SetToDecimal(md.baseTI.precision, md.baseTI.scale, fPositive, bits);
@@ -7518,7 +7524,20 @@ internal Task WriteString(string s, int length, int offset, TdsParserStateObject
75187524

75197525
private static void CopyCharsToBytes(char[] source, int sourceOffset, byte[] dest, int destOffset, int charLength)
75207526
{
7521-
Buffer.BlockCopy(source, sourceOffset, dest, destOffset, charLength * ADP.CharSize);
7527+
if (!BitConverter.IsLittleEndian)
7528+
{
7529+
int desti = 0;
7530+
Span<byte> span = dest.AsSpan();
7531+
for (int srci = 0; srci < charLength; srci++)
7532+
{
7533+
BinaryPrimitives.WriteUInt16LittleEndian(span.Slice(desti + destOffset), (ushort)source[srci + sourceOffset]);
7534+
desti += 2;
7535+
}
7536+
}
7537+
else
7538+
{
7539+
Buffer.BlockCopy(source, sourceOffset, dest, destOffset, charLength * ADP.CharSize);
7540+
}
75227541
}
75237542

75247543
private static void CopyStringToBytes(string source, int sourceOffset, byte[] dest, int destOffset, int charLength)

src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.netcore.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
using System;
66
using System.Diagnostics;
7+
using System.Buffers.Binary;
78
using System.Runtime.InteropServices;
89
using System.Security;
910
using System.Threading;
@@ -1055,6 +1056,15 @@ private void SetBufferSecureStrings()
10551056
str = Marshal.SecureStringToBSTR(_securePasswords[i]);
10561057
byte[] data = new byte[_securePasswords[i].Length * 2];
10571058
Marshal.Copy(str, data, 0, _securePasswords[i].Length * 2);
1059+
if (!BitConverter.IsLittleEndian)
1060+
{
1061+
Span<byte> span = data.AsSpan();
1062+
for (int ii = 0; ii < _securePasswords[i].Length * 2; ii += 2)
1063+
{
1064+
short value = BinaryPrimitives.ReadInt16LittleEndian(span.Slice(ii));
1065+
BinaryPrimitives.WriteInt16BigEndian(span.Slice(ii), value);
1066+
}
1067+
}
10581068
TdsParserStaticMethods.ObfuscatePassword(data);
10591069
data.CopyTo(_outBuff, _securePasswordOffsetsInBuffer[i]);
10601070
}
@@ -1556,7 +1566,8 @@ internal Task WritePacket(byte flushMode, bool canAccumulate = false)
15561566
// So we need to avoid this check prior to login completing
15571567
state == TdsParserState.OpenLoggedIn
15581568
&& !_bulkCopyOpperationInProgress // ignore the condition checking for bulk copy
1559-
&& _outBytesUsed == (_outputHeaderLen + BitConverter.ToInt32(_outBuff, _outputHeaderLen))
1569+
&& _outBytesUsed == (_outputHeaderLen +
1570+
BinaryPrimitives.ReadInt32LittleEndian(_outBuff.AsSpan(_outputHeaderLen)))
15601571
&& _outputPacketCount == 0
15611572
|| _outBytesUsed == _outputHeaderLen
15621573
&& _outputPacketCount == 0)

src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@
9595
<Compile Include="..\..\src\Microsoft\Data\Common\AdapterUtil.Windows.cs">
9696
<Link>Microsoft\Data\Common\AdapterUtil.Windows.cs</Link>
9797
</Compile>
98+
<Compile Include="..\..\src\Microsoft\Data\Common\BitConverterCompatible.cs">
99+
<Link>Microsoft\Data\Common\BitConverterCompatible.cs</Link>
100+
</Compile>
98101
<Compile Include="..\..\src\Microsoft\Data\Common\DbConnectionStringCommon.cs">
99102
<Link>Microsoft\Data\Common\DbConnectionStringCommon.cs</Link>
100103
</Compile>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// See the LICENSE file in the project root for more information.
2+
3+
using System;
4+
5+
namespace Microsoft.Data.SqlClient
6+
{
7+
internal static class BitConverterCompatible
8+
{
9+
public static unsafe int SingleToInt32Bits(float value)
10+
{
11+
return *(int*)(&value);
12+
}
13+
public static unsafe float Int32BitsToSingle(int value)
14+
{
15+
return *(float*)(&value);
16+
}
17+
}
18+
}

src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlSequentialTextReader.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// See the LICENSE file in the project root for more information.
44

55
using System;
6+
using System.Buffers.Binary;
67
using System.Diagnostics;
78
using System.Text;
89
using System.Threading;
@@ -514,6 +515,15 @@ public override void Convert(byte[] bytes, int byteIndex, int byteCount, char[]
514515
completed = (bytesUsed == byteCount);
515516

516517
// BlockCopy uses offsets\length measured in bytes, not the actual array index
518+
if (!BitConverter.IsLittleEndian)
519+
{
520+
Span<byte> span = bytes.AsSpan();
521+
for (int ii = 0; ii < byteCount; ii += 2)
522+
{
523+
short value = BinaryPrimitives.ReadInt16LittleEndian(span.Slice(ii));
524+
BinaryPrimitives.WriteInt16BigEndian(span.Slice(ii), value);
525+
}
526+
}
517527
Buffer.BlockCopy(bytes, byteIndex, chars, charIndex * 2, bytesUsed);
518528
}
519529
}

src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// See the LICENSE file in the project root for more information.
44

55
using System;
6+
using System.Buffers.Binary;
67
using System.Collections.Generic;
78
using System.Diagnostics;
89
using System.Security;
@@ -527,6 +528,13 @@ internal bool TryReadChars(char[] chars, int charsOffset, int charsCount, out in
527528
}
528529
}
529530
}
531+
if (!BitConverter.IsLittleEndian)
532+
{
533+
for (int ii = charsOffset; ii < charsCopied + charsOffset; ii++)
534+
{
535+
chars[ii] = (char)BinaryPrimitives.ReverseEndianness((ushort)chars[ii]);
536+
}
537+
}
530538
return true;
531539
}
532540

@@ -1365,7 +1373,7 @@ internal bool TryReadInt64(out long value)
13651373
Debug.Assert(_bTmpRead + bytesRead == 8, "TryReadByteArray returned true without reading all data required");
13661374
_bTmpRead = 0;
13671375
AssertValidState();
1368-
value = BitConverter.ToInt64(_bTmp, 0);
1376+
value = BinaryPrimitives.ReadInt64LittleEndian(_bTmp);
13691377
return true;
13701378
}
13711379
}
@@ -1374,7 +1382,7 @@ internal bool TryReadInt64(out long value)
13741382
// The entire long is in the packet and in the buffer, so just return it
13751383
// and take care of the counters.
13761384

1377-
value = BitConverter.ToInt64(_inBuff, _inBytesUsed);
1385+
value = BinaryPrimitives.ReadInt64LittleEndian(_inBuff.AsSpan(_inBytesUsed));
13781386

13791387
_inBytesUsed += 8;
13801388
_inBytesPacket -= 8;
@@ -1444,7 +1452,7 @@ internal bool TryReadUInt32(out uint value)
14441452
Debug.Assert(_bTmpRead + bytesRead == 4, "TryReadByteArray returned true without reading all data required");
14451453
_bTmpRead = 0;
14461454
AssertValidState();
1447-
value = BitConverter.ToUInt32(_bTmp, 0);
1455+
value = BinaryPrimitives.ReadUInt32LittleEndian(_bTmp);
14481456
return true;
14491457
}
14501458
}
@@ -1453,7 +1461,7 @@ internal bool TryReadUInt32(out uint value)
14531461
// The entire int is in the packet and in the buffer, so just return it
14541462
// and take care of the counters.
14551463

1456-
value = BitConverter.ToUInt32(_inBuff, _inBytesUsed);
1464+
value = BinaryPrimitives.ReadUInt32LittleEndian(_inBuff.AsSpan(_inBytesUsed));
14571465

14581466
_inBytesUsed += 4;
14591467
_inBytesPacket -= 4;
@@ -1479,15 +1487,15 @@ internal bool TryReadSingle(out float value)
14791487
}
14801488

14811489
AssertValidState();
1482-
value = BitConverter.ToSingle(_bTmp, 0);
1490+
value = BitConverterCompatible.Int32BitsToSingle(BinaryPrimitives.ReadInt32LittleEndian(_bTmp));
14831491
return true;
14841492
}
14851493
else
14861494
{
14871495
// The entire float is in the packet and in the buffer, so just return it
14881496
// and take care of the counters.
14891497

1490-
value = BitConverter.ToSingle(_inBuff, _inBytesUsed);
1498+
value = BitConverterCompatible.Int32BitsToSingle(BinaryPrimitives.ReadInt32LittleEndian(_inBuff.AsSpan(_inBytesUsed)));
14911499

14921500
_inBytesUsed += 4;
14931501
_inBytesPacket -= 4;
@@ -1513,15 +1521,15 @@ internal bool TryReadDouble(out double value)
15131521
}
15141522

15151523
AssertValidState();
1516-
value = BitConverter.ToDouble(_bTmp, 0);
1524+
value = BitConverter.Int64BitsToDouble(BinaryPrimitives.ReadInt64LittleEndian(_bTmp));
15171525
return true;
15181526
}
15191527
else
15201528
{
15211529
// The entire double is in the packet and in the buffer, so just return it
15221530
// and take care of the counters.
15231531

1524-
value = BitConverter.ToDouble(_inBuff, _inBytesUsed);
1532+
value = BitConverter.Int64BitsToDouble(BinaryPrimitives.ReadInt64LittleEndian(_inBuff.AsSpan(_inBytesUsed)));
15251533

15261534
_inBytesUsed += 8;
15271535
_inBytesPacket -= 8;

0 commit comments

Comments
 (0)