diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
index 21454d70ec..8e7db8df77 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
@@ -33,6 +33,9 @@
Microsoft\Data\Common\AdapterUtil.cs
+
+ Microsoft\Data\Common\BitConverterCompatible.cs
+
Microsoft\Data\Common\DbConnectionOptions.Common.cs
@@ -1008,4 +1011,4 @@
-
\ No newline at end of file
+
diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNICommon.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNICommon.cs
index b93ba0749c..964d332ae4 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNICommon.cs
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SNI/SNICommon.cs
@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System;
+using System.Buffers.Binary;
using System.Diagnostics;
using System.Net;
using System.Net.Security;
@@ -59,10 +60,11 @@ public void Read(byte[] bytes)
{
SMID = bytes[0];
flags = bytes[1];
- sessionId = BitConverter.ToUInt16(bytes, 2);
- length = BitConverter.ToUInt32(bytes, 4) - SNISMUXHeader.HEADER_LENGTH;
- sequenceNumber = BitConverter.ToUInt32(bytes, 8);
- highwater = BitConverter.ToUInt32(bytes, 12);
+ Span span = bytes.AsSpan();
+ sessionId = BinaryPrimitives.ReadUInt16LittleEndian(span.Slice(2));
+ length = BinaryPrimitives.ReadUInt32LittleEndian(span.Slice(4)) - SNISMUXHeader.HEADER_LENGTH;
+ sequenceNumber = BinaryPrimitives.ReadUInt32LittleEndian(span.Slice(8));
+ highwater = BinaryPrimitives.ReadUInt32LittleEndian(span.Slice(12));
}
public void Write(Span bytes)
diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.NetCoreApp.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.NetCoreApp.cs
index d8026b36e3..95dd0d9731 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.NetCoreApp.cs
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.NetCoreApp.cs
@@ -4,6 +4,7 @@
using System;
using System.Diagnostics;
+using System.Buffers.Binary;
namespace Microsoft.Data.SqlClient
{
@@ -11,9 +12,9 @@ internal sealed partial class TdsParser
{
internal static void FillGuidBytes(Guid guid, Span buffer) => guid.TryWriteBytes(buffer);
- internal static void FillDoubleBytes(double value, Span buffer) => BitConverter.TryWriteBytes(buffer, value);
+ internal static void FillDoubleBytes(double value, Span buffer) => BinaryPrimitives.TryWriteInt64LittleEndian(buffer, BitConverter.DoubleToInt64Bits(value));
- internal static void FillFloatBytes(float v, Span buffer) => BitConverter.TryWriteBytes(buffer, v);
+ internal static void FillFloatBytes(float value, Span buffer) => BinaryPrimitives.TryWriteInt32LittleEndian(buffer, BitConverterCompatible.SingleToInt32Bits(value));
internal static Guid ConstructGuid(ReadOnlySpan bytes)
{
diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs
index e111a8e789..5405c5b757 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs
@@ -4,6 +4,7 @@
using System;
using System.Buffers;
+using System.Buffers.Binary;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlTypes;
@@ -1760,7 +1761,7 @@ internal void WriteInt(int v, TdsParserStateObject stateObj)
internal static void WriteInt(Span buffer, int value)
{
#if NETCOREAPP
- BitConverter.TryWriteBytes(buffer, value);
+ BinaryPrimitives.TryWriteInt32LittleEndian(buffer, value);
#else
buffer[0] = (byte)(value & 0xff);
buffer[1] = (byte)((value >> 8) & 0xff);
@@ -1779,7 +1780,9 @@ internal byte[] SerializeFloat(float v)
throw ADP.ParameterValueOutOfRange(v.ToString());
}
- return BitConverter.GetBytes(v);
+ var bytes = new byte[4];
+ BinaryPrimitives.WriteInt32LittleEndian(bytes, BitConverterCompatible.SingleToInt32Bits(v));
+ return bytes;
}
internal void WriteFloat(float v, TdsParserStateObject stateObj)
@@ -1902,7 +1905,9 @@ internal byte[] SerializeDouble(double v)
throw ADP.ParameterValueOutOfRange(v.ToString());
}
- return BitConverter.GetBytes(v);
+ var bytes = new byte[8];
+ BinaryPrimitives.WriteInt64LittleEndian(bytes, BitConverter.DoubleToInt64Bits(v));
+ return bytes;
}
internal void WriteDouble(double v, TdsParserStateObject stateObj)
@@ -3829,8 +3834,8 @@ private bool TryProcessFedAuthInfo(TdsParserStateObject stateObj, int tokenLen,
uint currentOptionOffset = checked(i * optionSize);
byte id = tokenData[currentOptionOffset];
- uint dataLen = BitConverter.ToUInt32(tokenData, checked((int)(currentOptionOffset + 1)));
- uint dataOffset = BitConverter.ToUInt32(tokenData, checked((int)(currentOptionOffset + 5)));
+ uint dataLen = BinaryPrimitives.ReadUInt32LittleEndian(tokenData.AsSpan(checked((int)(currentOptionOffset + 1))));
+ uint dataOffset = BinaryPrimitives.ReadUInt32LittleEndian(tokenData.AsSpan(checked((int)(currentOptionOffset + 5))));
if (SqlClientEventSource.Log.IsAdvancedTraceOn())
{
SqlClientEventSource.Log.AdvancedTraceEvent(" 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
return false;
}
- longValue = BitConverter.ToInt64(unencryptedBytes, 0);
+ longValue = BinaryPrimitives.ReadInt64LittleEndian(unencryptedBytes);
if (tdsType == TdsEnums.SQLBIT ||
tdsType == TdsEnums.SQLBITN)
@@ -5837,7 +5842,7 @@ internal bool DeserializeUnencryptedValue(SqlBuffer value, byte[] unencryptedByt
return false;
}
- singleValue = BitConverter.ToSingle(unencryptedBytes, 0);
+ singleValue = BitConverterCompatible.Int32BitsToSingle(BinaryPrimitives.ReadInt32LittleEndian(unencryptedBytes));
value.Single = singleValue;
break;
@@ -5848,7 +5853,7 @@ internal bool DeserializeUnencryptedValue(SqlBuffer value, byte[] unencryptedByt
return false;
}
- doubleValue = BitConverter.ToDouble(unencryptedBytes, 0);
+ doubleValue = BitConverter.Int64BitsToDouble(BinaryPrimitives.ReadInt64LittleEndian(unencryptedBytes));
value.Double = doubleValue;
break;
@@ -5865,8 +5870,8 @@ internal bool DeserializeUnencryptedValue(SqlBuffer value, byte[] unencryptedByt
return false;
}
- mid = BitConverter.ToInt32(unencryptedBytes, 0);
- lo = BitConverter.ToUInt32(unencryptedBytes, 4);
+ mid = BinaryPrimitives.ReadInt32LittleEndian(unencryptedBytes);
+ lo = BinaryPrimitives.ReadUInt32LittleEndian(unencryptedBytes.AsSpan(4));
long l = (((long)mid) << 0x20) + ((long)lo);
value.SetToMoney(l);
@@ -5903,8 +5908,8 @@ internal bool DeserializeUnencryptedValue(SqlBuffer value, byte[] unencryptedByt
return false;
}
- daypart = BitConverter.ToInt32(unencryptedBytes, 0);
- timepart = BitConverter.ToUInt32(unencryptedBytes, 4);
+ daypart = BinaryPrimitives.ReadInt32LittleEndian(unencryptedBytes);
+ timepart = BinaryPrimitives.ReadUInt32LittleEndian(unencryptedBytes.AsSpan(4));
value.SetToDateTime(daypart, (int)timepart);
break;
@@ -5947,10 +5952,11 @@ internal bool DeserializeUnencryptedValue(SqlBuffer value, byte[] unencryptedByt
length = checked((int)length - 1);
int[] bits = new int[4];
int decLength = length >> 2;
+ var span = unencryptedBytes.AsSpan();
for (int i = 0; i < decLength; i++)
{
// up to 16 bytes of data following the sign byte
- bits[i] = BitConverter.ToInt32(unencryptedBytes, index);
+ bits[i] = BinaryPrimitives.ReadInt32LittleEndian(span.Slice(index));
index += 4;
}
value.SetToDecimal(md.baseTI.precision, md.baseTI.scale, fPositive, bits);
@@ -7518,7 +7524,20 @@ internal Task WriteString(string s, int length, int offset, TdsParserStateObject
private static void CopyCharsToBytes(char[] source, int sourceOffset, byte[] dest, int destOffset, int charLength)
{
- Buffer.BlockCopy(source, sourceOffset, dest, destOffset, charLength * ADP.CharSize);
+ if (!BitConverter.IsLittleEndian)
+ {
+ int desti = 0;
+ Span span = dest.AsSpan();
+ for (int srci = 0; srci < charLength; srci++)
+ {
+ BinaryPrimitives.WriteUInt16LittleEndian(span.Slice(desti + destOffset), (ushort)source[srci + sourceOffset]);
+ desti += 2;
+ }
+ }
+ else
+ {
+ Buffer.BlockCopy(source, sourceOffset, dest, destOffset, charLength * ADP.CharSize);
+ }
}
private static void CopyStringToBytes(string source, int sourceOffset, byte[] dest, int destOffset, int charLength)
diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.netcore.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.netcore.cs
index 7e35e5a122..6c32e9594d 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.netcore.cs
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.netcore.cs
@@ -4,6 +4,7 @@
using System;
using System.Diagnostics;
+using System.Buffers.Binary;
using System.Runtime.InteropServices;
using System.Security;
using System.Threading;
@@ -1055,6 +1056,15 @@ private void SetBufferSecureStrings()
str = Marshal.SecureStringToBSTR(_securePasswords[i]);
byte[] data = new byte[_securePasswords[i].Length * 2];
Marshal.Copy(str, data, 0, _securePasswords[i].Length * 2);
+ if (!BitConverter.IsLittleEndian)
+ {
+ Span span = data.AsSpan();
+ for (int ii = 0; ii < _securePasswords[i].Length * 2; ii += 2)
+ {
+ short value = BinaryPrimitives.ReadInt16LittleEndian(span.Slice(ii));
+ BinaryPrimitives.WriteInt16BigEndian(span.Slice(ii), value);
+ }
+ }
TdsParserStaticMethods.ObfuscatePassword(data);
data.CopyTo(_outBuff, _securePasswordOffsetsInBuffer[i]);
}
@@ -1556,7 +1566,8 @@ internal Task WritePacket(byte flushMode, bool canAccumulate = false)
// So we need to avoid this check prior to login completing
state == TdsParserState.OpenLoggedIn
&& !_bulkCopyOpperationInProgress // ignore the condition checking for bulk copy
- && _outBytesUsed == (_outputHeaderLen + BitConverter.ToInt32(_outBuff, _outputHeaderLen))
+ && _outBytesUsed == (_outputHeaderLen +
+ BinaryPrimitives.ReadInt32LittleEndian(_outBuff.AsSpan(_outputHeaderLen)))
&& _outputPacketCount == 0
|| _outBytesUsed == _outputHeaderLen
&& _outputPacketCount == 0)
diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
index c81604f3c3..f17622f303 100644
--- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
@@ -95,6 +95,9 @@
Microsoft\Data\Common\AdapterUtil.Windows.cs
+
+ Microsoft\Data\Common\BitConverterCompatible.cs
+
Microsoft\Data\Common\DbConnectionStringCommon.cs
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/BitConverterCompatible.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/BitConverterCompatible.cs
new file mode 100644
index 0000000000..5e93a2f336
--- /dev/null
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/Common/BitConverterCompatible.cs
@@ -0,0 +1,18 @@
+// See the LICENSE file in the project root for more information.
+
+using System;
+
+namespace Microsoft.Data.SqlClient
+{
+ internal static class BitConverterCompatible
+ {
+ public static unsafe int SingleToInt32Bits(float value)
+ {
+ return *(int*)(&value);
+ }
+ public static unsafe float Int32BitsToSingle(int value)
+ {
+ return *(float*)(&value);
+ }
+ }
+}
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlSequentialTextReader.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlSequentialTextReader.cs
index 436ad67c8e..9ea820000e 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlSequentialTextReader.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlSequentialTextReader.cs
@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System;
+using System.Buffers.Binary;
using System.Diagnostics;
using System.Text;
using System.Threading;
@@ -514,6 +515,15 @@ public override void Convert(byte[] bytes, int byteIndex, int byteCount, char[]
completed = (bytesUsed == byteCount);
// BlockCopy uses offsets\length measured in bytes, not the actual array index
+ if (!BitConverter.IsLittleEndian)
+ {
+ Span span = bytes.AsSpan();
+ for (int ii = 0; ii < byteCount; ii += 2)
+ {
+ short value = BinaryPrimitives.ReadInt16LittleEndian(span.Slice(ii));
+ BinaryPrimitives.WriteInt16BigEndian(span.Slice(ii), value);
+ }
+ }
Buffer.BlockCopy(bytes, byteIndex, chars, charIndex * 2, bytesUsed);
}
}
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs
index 0e6655a8b2..8f405cc85f 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs
@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System;
+using System.Buffers.Binary;
using System.Collections.Generic;
using System.Diagnostics;
using System.Security;
@@ -527,6 +528,13 @@ internal bool TryReadChars(char[] chars, int charsOffset, int charsCount, out in
}
}
}
+ if (!BitConverter.IsLittleEndian)
+ {
+ for (int ii = charsOffset; ii < charsCopied + charsOffset; ii++)
+ {
+ chars[ii] = (char)BinaryPrimitives.ReverseEndianness((ushort)chars[ii]);
+ }
+ }
return true;
}
@@ -1365,7 +1373,7 @@ internal bool TryReadInt64(out long value)
Debug.Assert(_bTmpRead + bytesRead == 8, "TryReadByteArray returned true without reading all data required");
_bTmpRead = 0;
AssertValidState();
- value = BitConverter.ToInt64(_bTmp, 0);
+ value = BinaryPrimitives.ReadInt64LittleEndian(_bTmp);
return true;
}
}
@@ -1374,7 +1382,7 @@ internal bool TryReadInt64(out long value)
// The entire long is in the packet and in the buffer, so just return it
// and take care of the counters.
- value = BitConverter.ToInt64(_inBuff, _inBytesUsed);
+ value = BinaryPrimitives.ReadInt64LittleEndian(_inBuff.AsSpan(_inBytesUsed));
_inBytesUsed += 8;
_inBytesPacket -= 8;
@@ -1444,7 +1452,7 @@ internal bool TryReadUInt32(out uint value)
Debug.Assert(_bTmpRead + bytesRead == 4, "TryReadByteArray returned true without reading all data required");
_bTmpRead = 0;
AssertValidState();
- value = BitConverter.ToUInt32(_bTmp, 0);
+ value = BinaryPrimitives.ReadUInt32LittleEndian(_bTmp);
return true;
}
}
@@ -1453,7 +1461,7 @@ internal bool TryReadUInt32(out uint value)
// The entire int is in the packet and in the buffer, so just return it
// and take care of the counters.
- value = BitConverter.ToUInt32(_inBuff, _inBytesUsed);
+ value = BinaryPrimitives.ReadUInt32LittleEndian(_inBuff.AsSpan(_inBytesUsed));
_inBytesUsed += 4;
_inBytesPacket -= 4;
@@ -1479,7 +1487,7 @@ internal bool TryReadSingle(out float value)
}
AssertValidState();
- value = BitConverter.ToSingle(_bTmp, 0);
+ value = BitConverterCompatible.Int32BitsToSingle(BinaryPrimitives.ReadInt32LittleEndian(_bTmp));
return true;
}
else
@@ -1487,7 +1495,7 @@ internal bool TryReadSingle(out float value)
// The entire float is in the packet and in the buffer, so just return it
// and take care of the counters.
- value = BitConverter.ToSingle(_inBuff, _inBytesUsed);
+ value = BitConverterCompatible.Int32BitsToSingle(BinaryPrimitives.ReadInt32LittleEndian(_inBuff.AsSpan(_inBytesUsed)));
_inBytesUsed += 4;
_inBytesPacket -= 4;
@@ -1513,7 +1521,7 @@ internal bool TryReadDouble(out double value)
}
AssertValidState();
- value = BitConverter.ToDouble(_bTmp, 0);
+ value = BitConverter.Int64BitsToDouble(BinaryPrimitives.ReadInt64LittleEndian(_bTmp));
return true;
}
else
@@ -1521,7 +1529,7 @@ internal bool TryReadDouble(out double value)
// The entire double is in the packet and in the buffer, so just return it
// and take care of the counters.
- value = BitConverter.ToDouble(_inBuff, _inBytesUsed);
+ value = BitConverter.Int64BitsToDouble(BinaryPrimitives.ReadInt64LittleEndian(_inBuff.AsSpan(_inBytesUsed)));
_inBytesUsed += 8;
_inBytesPacket -= 8;
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsValueSetter.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsValueSetter.cs
index fd50f68a0b..f2c33824a2 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsValueSetter.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsValueSetter.cs
@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System;
+using System.Buffers.Binary;
using System.Data;
using System.Data.SqlTypes;
using System.Diagnostics;
@@ -586,9 +587,21 @@ internal void SetDateTime(DateTime value)
if (SqlDbType.DateTime2 == _metaData.SqlDbType)
{
long time = value.TimeOfDay.Ticks / TdsEnums.TICKS_FROM_SCALE[_metaData.Scale];
+#if NETCOREAPP
+ Span result_time = stackalloc byte[8];
+ BinaryPrimitives.WriteInt64LittleEndian(result_time, time);
+ _stateObj.WriteByteSpan(result_time.Slice(0, (int)_metaData.MaxLength - 3));
+#else
_stateObj.WriteByteArray(BitConverter.GetBytes(time), (int)_metaData.MaxLength - 3, 0);
+#endif
}
+#if NETCOREAPP
+ Span result_date = stackalloc byte[4];
+ BinaryPrimitives.WriteInt32LittleEndian(result_date, days);
+ _stateObj.WriteByteSpan(result_date.Slice(0, 3));
+#else
_stateObj.WriteByteArray(BitConverter.GetBytes(days), 3, 0);
+#endif
}
}
}
@@ -646,7 +659,13 @@ internal void SetTimeSpan(TimeSpan value)
_stateObj.WriteByte(length);
}
long time = value.Ticks / TdsEnums.TICKS_FROM_SCALE[scale];
+#if NETCOREAPP
+ Span result_time = stackalloc byte[8];
+ BinaryPrimitives.WriteInt64LittleEndian(result_time, time);
+ _stateObj.WriteByteSpan(result_time.Slice(0, length));
+#else
_stateObj.WriteByteArray(BitConverter.GetBytes(time), length, 0);
+#endif
}
// valid for DateTimeOffset
@@ -677,8 +696,15 @@ internal void SetDateTimeOffset(DateTimeOffset value)
int days = utcDateTime.Subtract(DateTime.MinValue).Days;
short offset = (short)value.Offset.TotalMinutes;
+#if NETCOREAPP
+ Span result = stackalloc byte[9];
+ BinaryPrimitives.WriteInt64LittleEndian(result, time);
+ BinaryPrimitives.WriteInt32LittleEndian(result.Slice(5), days);
+ _stateObj.WriteByteSpan(result.Slice(0, 8));
+#else
_stateObj.WriteByteArray(BitConverter.GetBytes(time), length - 5, 0); // time
_stateObj.WriteByteArray(BitConverter.GetBytes(days), 3, 0); // date
+#endif
_stateObj.WriteByte((byte)(offset & 0xff)); // offset byte 1
_stateObj.WriteByte((byte)((offset >> 8) & 0xff)); // offset byte 2
}