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 b0b25d2e09..230509be7f 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 @@ -1619,49 +1619,47 @@ internal byte[] SerializeInt(int v, TdsParserStateObject stateObj) { if (null == stateObj._bIntBytes) { - stateObj._bIntBytes = new byte[sizeof(int)]; + stateObj._bIntBytes = new byte[4]; } else { - Debug.Assert(sizeof(int) == stateObj._bIntBytes.Length); + Debug.Assert(4 == stateObj._bIntBytes.Length); } - WriteInt(stateObj._bIntBytes.AsSpan(), v); - return stateObj._bIntBytes; + int current = 0; + byte[] bytes = stateObj._bIntBytes; + bytes[current++] = (byte)(v & 0xff); + bytes[current++] = (byte)((v >> 8) & 0xff); + bytes[current++] = (byte)((v >> 16) & 0xff); + bytes[current++] = (byte)((v >> 24) & 0xff); + return bytes; } + // + // Takes an int and writes it as an int. + // internal void WriteInt(int v, TdsParserStateObject stateObj) { - Span buffer = stackalloc byte[sizeof(int)]; - WriteInt(buffer, v); if ((stateObj._outBytesUsed + 4) > stateObj._outBuff.Length) { // if all of the int doesn't fit into the buffer - for (int index = 0; index < sizeof(int); index++) + for (int shiftValue = 0; shiftValue < sizeof(int) * 8; shiftValue += 8) { - stateObj.WriteByte(buffer[index]); + stateObj.WriteByte((byte)((v >> shiftValue) & 0xff)); } } else { // all of the int fits into the buffer - buffer.CopyTo(stateObj._outBuff.AsSpan(stateObj._outBytesUsed, sizeof(int))); + // NOTE: We don't use a loop here for performance + stateObj._outBuff[stateObj._outBytesUsed] = (byte)(v & 0xff); + stateObj._outBuff[stateObj._outBytesUsed + 1] = (byte)((v >> 8) & 0xff); + stateObj._outBuff[stateObj._outBytesUsed + 2] = (byte)((v >> 16) & 0xff); + stateObj._outBuff[stateObj._outBytesUsed + 3] = (byte)((v >> 24) & 0xff); stateObj._outBytesUsed += 4; } } - internal static void WriteInt(Span buffer, int value) - { -#if netcoreapp - BitConverter.TryWriteBytes(buffer, value); -#else - buffer[0] = (byte)(value & 0xff); - buffer[1] = (byte)((value >> 8) & 0xff); - buffer[2] = (byte)((value >> 16) & 0xff); - buffer[3] = (byte)((value >> 24) & 0xff); -#endif - } - // // Takes a float and writes it as a 32 bit float. // diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs index 7e9f5a6d67..a5e84c2a6a 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs @@ -1403,28 +1403,36 @@ internal bool TryReadChar(out char value) { Debug.Assert(_syncOverAsync || !_asyncReadWithoutSnapshot, "This method is not safe to call when doing sync over async"); - Span buffer = stackalloc byte[2]; + byte[] buffer; + int offset; if (((_inBytesUsed + 2) > _inBytesRead) || (_inBytesPacket < 2)) { // If the char isn't fully in the buffer, or if it isn't fully in the packet, // then use ReadByteArray since the logic is there to take care of that. - if (!TryReadByteArray(buffer, 2)) + + if (!TryReadByteArray(_bTmp, 2)) { value = '\0'; return false; } + + buffer = _bTmp; + offset = 0; } else { // The entire char is in the packet and in the buffer, so just return it // and take care of the counters. - buffer = _inBuff.AsSpan(_inBytesUsed, 2); + + buffer = _inBuff; + offset = _inBytesUsed; + _inBytesUsed += 2; _inBytesPacket -= 2; } AssertValidState(); - value = (char)((buffer[1] << 8) + buffer[0]); + value = (char)((buffer[offset + 1] << 8) + buffer[offset]); return true; } @@ -1432,58 +1440,70 @@ internal bool TryReadInt16(out short value) { Debug.Assert(_syncOverAsync || !_asyncReadWithoutSnapshot, "This method is not safe to call when doing sync over async"); - Span buffer = stackalloc byte[2]; + byte[] buffer; + int offset; if (((_inBytesUsed + 2) > _inBytesRead) || (_inBytesPacket < 2)) { // If the int16 isn't fully in the buffer, or if it isn't fully in the packet, // then use ReadByteArray since the logic is there to take care of that. - if (!TryReadByteArray(buffer, 2)) + + if (!TryReadByteArray(_bTmp, 2)) { value = default; return false; } + + buffer = _bTmp; + offset = 0; } else { // The entire int16 is in the packet and in the buffer, so just return it // and take care of the counters. - buffer = _inBuff.AsSpan(_inBytesUsed,2); + + buffer = _inBuff; + offset = _inBytesUsed; + _inBytesUsed += 2; _inBytesPacket -= 2; } AssertValidState(); - value = (short)((buffer[1] << 8) + buffer[0]); + value = (short)((buffer[offset + 1] << 8) + buffer[offset]); return true; } internal bool TryReadInt32(out int value) { Debug.Assert(_syncOverAsync || !_asyncReadWithoutSnapshot, "This method is not safe to call when doing sync over async"); - Span buffer = stackalloc byte[4]; if (((_inBytesUsed + 4) > _inBytesRead) || (_inBytesPacket < 4)) { // If the int isn't fully in the buffer, or if it isn't fully in the packet, // then use ReadByteArray since the logic is there to take care of that. - if (!TryReadByteArray(buffer, 4)) + + if (!TryReadByteArray(_bTmp, 4)) { value = 0; return false; } + + AssertValidState(); + value = BitConverter.ToInt32(_bTmp, 0); + return true; } else { // The entire int is in the packet and in the buffer, so just return it // and take care of the counters. - buffer = _inBuff.AsSpan(_inBytesUsed, 4); + + value = BitConverter.ToInt32(_inBuff, _inBytesUsed); + _inBytesUsed += 4; _inBytesPacket -= 4; - } - - AssertValidState(); - value = (buffer[3] << 24) + (buffer[2] <<16) + (buffer[1] << 8) + buffer[0]; - return true; + AssertValidState(); + return true; + } } // This method is safe to call when doing async without snapshot @@ -1539,28 +1559,36 @@ internal bool TryReadUInt16(out ushort value) { Debug.Assert(_syncOverAsync || !_asyncReadWithoutSnapshot, "This method is not safe to call when doing sync over async"); - Span buffer = stackalloc byte[2]; + byte[] buffer; + int offset; if (((_inBytesUsed + 2) > _inBytesRead) || (_inBytesPacket < 2)) { // If the uint16 isn't fully in the buffer, or if it isn't fully in the packet, // then use ReadByteArray since the logic is there to take care of that. - if (!TryReadByteArray(buffer, 2)) + + if (!TryReadByteArray(_bTmp, 2)) { value = default; return false; } + + buffer = _bTmp; + offset = 0; } else { // The entire uint16 is in the packet and in the buffer, so just return it // and take care of the counters. - buffer = _inBuff.AsSpan(_inBytesUsed, 2); + + buffer = _inBuff; + offset = _inBytesUsed; + _inBytesUsed += 2; _inBytesPacket -= 2; } AssertValidState(); - value = (ushort)((buffer[1] << 8) + buffer[0]); + value = (ushort)((buffer[offset + 1] << 8) + buffer[offset]); return true; } @@ -3599,8 +3627,8 @@ private void SniWriteStatisticsAndTracing() statistics.RequestNetworkServerTimer(); } } - [Conditional("DEBUG")] + private void AssertValidState() { if (_inBytesUsed < 0 || _inBytesRead < 0)