From 97f9fa4ae5cda756698d819ee4bde74315ac5093 Mon Sep 17 00:00:00 2001 From: panoskj Date: Mon, 25 Sep 2023 16:59:15 +0300 Subject: [PATCH] 2. Merging "Buffer read" methods of TdsParserStateObject, port #667 to netfx. --- .../SqlClient/TdsParserStateObject.netcore.cs | 387 ------------------ .../SqlClient/TdsParserStateObject.netfx.cs | 387 ------------------ .../Data/SqlClient/TdsParserStateObject.cs | 387 ++++++++++++++++++ 3 files changed, 387 insertions(+), 774 deletions(-) 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 8497bdc1e6..ffff6abc55 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 @@ -282,393 +282,6 @@ internal void StartSession(object cancellationOwner) // Buffer read methods - data values // /////////////////////////////////////// - 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]; - 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)) - { - value = default; - return false; - } - } - 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); - _inBytesUsed += 2; - _inBytesPacket -= 2; - } - - AssertValidState(); - value = (short)((buffer[1] << 8) + buffer[0]); - return true; - } - - internal bool TryReadInt32(out int value) - { -#if NETFRAMEWORK - TdsParser.ReliabilitySection.Assert("unreliable call to ReadInt32"); // you need to setup for a thread abort somewhere before you call this method -#endif - 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)) - { - value = 0; - return false; - } - } - 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); - _inBytesUsed += 4; - _inBytesPacket -= 4; - } - - AssertValidState(); - value = (buffer[3] << 24) + (buffer[2] << 16) + (buffer[1] << 8) + buffer[0]; - return true; - } - - // This method is safe to call when doing async without snapshot - internal bool TryReadInt64(out long value) - { -#if NETFRAMEWORK - TdsParser.ReliabilitySection.Assert("unreliable call to ReadInt64"); // you need to setup for a thread abort somewhere before you call this method -#endif - if ((_inBytesPacket == 0) || (_inBytesUsed == _inBytesRead)) - { - if (!TryPrepareBuffer()) - { - value = 0; - return false; - } - } - - if ((_bTmpRead > 0) || (((_inBytesUsed + 8) > _inBytesRead) || (_inBytesPacket < 8))) - { - // If the long 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. - - int bytesRead; - if (!TryReadByteArray(_bTmp.AsSpan(start: _bTmpRead), 8 - _bTmpRead, out bytesRead)) - { - Debug.Assert(_bTmpRead + bytesRead <= 8, "Read more data than required"); - _bTmpRead += bytesRead; - value = 0; - return false; - } - else - { - Debug.Assert(_bTmpRead + bytesRead == 8, "TryReadByteArray returned true without reading all data required"); - _bTmpRead = 0; - AssertValidState(); - value = BitConverter.ToInt64(_bTmp, 0); - return true; - } - } - else - { - // 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); - - _inBytesUsed += 8; - _inBytesPacket -= 8; - - AssertValidState(); - return true; - } - } - - 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]; - 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)) - { - value = default; - return false; - } - } - 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); - _inBytesUsed += 2; - _inBytesPacket -= 2; - } - - AssertValidState(); - value = (ushort)((buffer[1] << 8) + buffer[0]); - return true; - } - - // This method is safe to call when doing async without replay - internal bool TryReadUInt32(out uint value) - { -#if NETFRAMEWORK - TdsParser.ReliabilitySection.Assert("unreliable call to ReadUInt32"); // you need to setup for a thread abort somewhere before you call this method -#endif - if ((_inBytesPacket == 0) || (_inBytesUsed == _inBytesRead)) - { - if (!TryPrepareBuffer()) - { - value = 0; - return false; - } - } - - if ((_bTmpRead > 0) || (((_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. - - int bytesRead; - if (!TryReadByteArray(_bTmp.AsSpan(start: _bTmpRead), 4 - _bTmpRead, out bytesRead)) - { - Debug.Assert(_bTmpRead + bytesRead <= 4, "Read more data than required"); - _bTmpRead += bytesRead; - value = 0; - return false; - } - else - { - Debug.Assert(_bTmpRead + bytesRead == 4, "TryReadByteArray returned true without reading all data required"); - _bTmpRead = 0; - AssertValidState(); - value = BitConverter.ToUInt32(_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. - - value = BitConverter.ToUInt32(_inBuff, _inBytesUsed); - - _inBytesUsed += 4; - _inBytesPacket -= 4; - - AssertValidState(); - return true; - } - } - - internal bool TryReadSingle(out float value) - { -#if NETFRAMEWORK - TdsParser.ReliabilitySection.Assert("unreliable call to ReadSingle"); // you need to setup for a thread abort somewhere before you call this method -#endif - Debug.Assert(_syncOverAsync || !_asyncReadWithoutSnapshot, "This method is not safe to call when doing sync over async"); - if (((_inBytesUsed + 4) > _inBytesRead) || (_inBytesPacket < 4)) - { - // If the float 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(_bTmp, 4)) - { - value = default; - return false; - } - - AssertValidState(); - value = BitConverter.ToSingle(_bTmp, 0); - return true; - } - else - { - // 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); - - _inBytesUsed += 4; - _inBytesPacket -= 4; - - AssertValidState(); - return true; - } - } - - internal bool TryReadDouble(out double value) - { -#if NETFRAMEWORK - TdsParser.ReliabilitySection.Assert("unreliable call to ReadDouble"); // you need to setup for a thread abort somewhere before you call this method -#endif - Debug.Assert(_syncOverAsync || !_asyncReadWithoutSnapshot, "This method is not safe to call when doing sync over async"); - if (((_inBytesUsed + 8) > _inBytesRead) || (_inBytesPacket < 8)) - { - // If the double 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(_bTmp, 8)) - { - value = default; - return false; - } - - AssertValidState(); - value = BitConverter.ToDouble(_bTmp, 0); - return true; - } - else - { - // 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); - - _inBytesUsed += 8; - _inBytesPacket -= 8; - - AssertValidState(); - return true; - } - } - - internal bool TryReadString(int length, out string value) - { -#if NETFRAMEWORK - TdsParser.ReliabilitySection.Assert("unreliable call to ReadString"); // you need to setup for a thread abort somewhere before you call this method -#endif - Debug.Assert(_syncOverAsync || !_asyncReadWithoutSnapshot, "This method is not safe to call when doing sync over async"); - int cBytes = length << 1; - byte[] buf; - int offset = 0; - - if (((_inBytesUsed + cBytes) > _inBytesRead) || (_inBytesPacket < cBytes)) - { - if (_bTmp == null || _bTmp.Length < cBytes) - { - _bTmp = new byte[cBytes]; - } - - if (!TryReadByteArray(_bTmp, cBytes)) - { - value = null; - return false; - } - - // assign local to point to parser scratch buffer - buf = _bTmp; - - AssertValidState(); - } - else - { - // assign local to point to _inBuff - buf = _inBuff; - offset = _inBytesUsed; - _inBytesUsed += cBytes; - _inBytesPacket -= cBytes; - - AssertValidState(); - } - - value = System.Text.Encoding.Unicode.GetString(buf, offset, cBytes); - return true; - } - - internal bool TryReadStringWithEncoding(int length, System.Text.Encoding encoding, bool isPlp, out string value) - { -#if NETFRAMEWORK - TdsParser.ReliabilitySection.Assert("unreliable call to ReadStringWithEncoding"); // you need to setup for a thread abort somewhere before you call this method -#endif - Debug.Assert(_syncOverAsync || !_asyncReadWithoutSnapshot, "This method is not safe to call when doing sync over async"); - - if (null == encoding) - { - // Need to skip the current column before throwing the error - this ensures that the state shared between this and the data reader is consistent when calling DrainData - if (isPlp) - { - if (!_parser.TrySkipPlpValue((ulong)length, this, out _)) - { - value = null; - return false; - } - } - else - { - if (!TrySkipBytes(length)) - { - value = null; - return false; - } - } - - _parser.ThrowUnsupportedCollationEncountered(this); - } - byte[] buf = null; - int offset = 0; - - if (isPlp) - { - if (!TryReadPlpBytes(ref buf, 0, int.MaxValue, out length)) - { - value = null; - return false; - } - - AssertValidState(); - } - else - { - if (((_inBytesUsed + length) > _inBytesRead) || (_inBytesPacket < length)) - { - if (_bTmp == null || _bTmp.Length < length) - { - _bTmp = new byte[length]; - } - - if (!TryReadByteArray(_bTmp, length)) - { - value = null; - return false; - } - - // assign local to point to parser scratch buffer - buf = _bTmp; - - AssertValidState(); - } - else - { - // assign local to point to _inBuff - buf = _inBuff; - offset = _inBytesUsed; - _inBytesUsed += length; - _inBytesPacket -= length; - - AssertValidState(); - } - } - - // BCL optimizes to not use char[] underneath - value = encoding.GetString(buf, offset, length); - return true; - } - internal ulong ReadPlpLength(bool returnPlpNullIfNull) { ulong value; diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.netfx.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.netfx.cs index 287f1d80f1..4ac4138bca 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.netfx.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParserStateObject.netfx.cs @@ -376,393 +376,6 @@ internal void StartSession(int objectID) // Buffer read methods - data values // /////////////////////////////////////// - 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]; - 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)) - { - value = default; - return false; - } - } - 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); - _inBytesUsed += 2; - _inBytesPacket -= 2; - } - - AssertValidState(); - value = (short)((buffer[1] << 8) + buffer[0]); - return true; - } - - internal bool TryReadInt32(out int value) - { -#if NETFRAMEWORK - TdsParser.ReliabilitySection.Assert("unreliable call to ReadInt32"); // you need to setup for a thread abort somewhere before you call this method -#endif - 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)) - { - value = 0; - return false; - } - } - 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); - _inBytesUsed += 4; - _inBytesPacket -= 4; - } - - AssertValidState(); - value = (buffer[3] << 24) + (buffer[2] << 16) + (buffer[1] << 8) + buffer[0]; - return true; - } - - // This method is safe to call when doing async without snapshot - internal bool TryReadInt64(out long value) - { -#if NETFRAMEWORK - TdsParser.ReliabilitySection.Assert("unreliable call to ReadInt64"); // you need to setup for a thread abort somewhere before you call this method -#endif - if ((_inBytesPacket == 0) || (_inBytesUsed == _inBytesRead)) - { - if (!TryPrepareBuffer()) - { - value = 0; - return false; - } - } - - if ((_bTmpRead > 0) || (((_inBytesUsed + 8) > _inBytesRead) || (_inBytesPacket < 8))) - { - // If the long 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. - - int bytesRead; - if (!TryReadByteArray(_bTmp.AsSpan(start: _bTmpRead), 8 - _bTmpRead, out bytesRead)) - { - Debug.Assert(_bTmpRead + bytesRead <= 8, "Read more data than required"); - _bTmpRead += bytesRead; - value = 0; - return false; - } - else - { - Debug.Assert(_bTmpRead + bytesRead == 8, "TryReadByteArray returned true without reading all data required"); - _bTmpRead = 0; - AssertValidState(); - value = BitConverter.ToInt64(_bTmp, 0); - return true; - } - } - else - { - // 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); - - _inBytesUsed += 8; - _inBytesPacket -= 8; - - AssertValidState(); - return true; - } - } - - 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]; - 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)) - { - value = default; - return false; - } - } - 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); - _inBytesUsed += 2; - _inBytesPacket -= 2; - } - - AssertValidState(); - value = (ushort)((buffer[1] << 8) + buffer[0]); - return true; - } - - // This method is safe to call when doing async without replay - internal bool TryReadUInt32(out uint value) - { -#if NETFRAMEWORK - TdsParser.ReliabilitySection.Assert("unreliable call to ReadUInt32"); // you need to setup for a thread abort somewhere before you call this method -#endif - if ((_inBytesPacket == 0) || (_inBytesUsed == _inBytesRead)) - { - if (!TryPrepareBuffer()) - { - value = 0; - return false; - } - } - - if ((_bTmpRead > 0) || (((_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. - - int bytesRead; - if (!TryReadByteArray(_bTmp.AsSpan(start: _bTmpRead), 4 - _bTmpRead, out bytesRead)) - { - Debug.Assert(_bTmpRead + bytesRead <= 4, "Read more data than required"); - _bTmpRead += bytesRead; - value = 0; - return false; - } - else - { - Debug.Assert(_bTmpRead + bytesRead == 4, "TryReadByteArray returned true without reading all data required"); - _bTmpRead = 0; - AssertValidState(); - value = BitConverter.ToUInt32(_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. - - value = BitConverter.ToUInt32(_inBuff, _inBytesUsed); - - _inBytesUsed += 4; - _inBytesPacket -= 4; - - AssertValidState(); - return true; - } - } - - internal bool TryReadSingle(out float value) - { -#if NETFRAMEWORK - TdsParser.ReliabilitySection.Assert("unreliable call to ReadSingle"); // you need to setup for a thread abort somewhere before you call this method -#endif - Debug.Assert(_syncOverAsync || !_asyncReadWithoutSnapshot, "This method is not safe to call when doing sync over async"); - if (((_inBytesUsed + 4) > _inBytesRead) || (_inBytesPacket < 4)) - { - // If the float 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(_bTmp, 4)) - { - value = default; - return false; - } - - AssertValidState(); - value = BitConverter.ToSingle(_bTmp, 0); - return true; - } - else - { - // 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); - - _inBytesUsed += 4; - _inBytesPacket -= 4; - - AssertValidState(); - return true; - } - } - - internal bool TryReadDouble(out double value) - { -#if NETFRAMEWORK - TdsParser.ReliabilitySection.Assert("unreliable call to ReadDouble"); // you need to setup for a thread abort somewhere before you call this method -#endif - Debug.Assert(_syncOverAsync || !_asyncReadWithoutSnapshot, "This method is not safe to call when doing sync over async"); - if (((_inBytesUsed + 8) > _inBytesRead) || (_inBytesPacket < 8)) - { - // If the double 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(_bTmp, 8)) - { - value = default; - return false; - } - - AssertValidState(); - value = BitConverter.ToDouble(_bTmp, 0); - return true; - } - else - { - // 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); - - _inBytesUsed += 8; - _inBytesPacket -= 8; - - AssertValidState(); - return true; - } - } - - internal bool TryReadString(int length, out string value) - { -#if NETFRAMEWORK - TdsParser.ReliabilitySection.Assert("unreliable call to ReadString"); // you need to setup for a thread abort somewhere before you call this method -#endif - Debug.Assert(_syncOverAsync || !_asyncReadWithoutSnapshot, "This method is not safe to call when doing sync over async"); - int cBytes = length << 1; - byte[] buf; - int offset = 0; - - if (((_inBytesUsed + cBytes) > _inBytesRead) || (_inBytesPacket < cBytes)) - { - if (_bTmp == null || _bTmp.Length < cBytes) - { - _bTmp = new byte[cBytes]; - } - - if (!TryReadByteArray(_bTmp, cBytes)) - { - value = null; - return false; - } - - // assign local to point to parser scratch buffer - buf = _bTmp; - - AssertValidState(); - } - else - { - // assign local to point to _inBuff - buf = _inBuff; - offset = _inBytesUsed; - _inBytesUsed += cBytes; - _inBytesPacket -= cBytes; - - AssertValidState(); - } - - value = System.Text.Encoding.Unicode.GetString(buf, offset, cBytes); - return true; - } - - internal bool TryReadStringWithEncoding(int length, System.Text.Encoding encoding, bool isPlp, out string value) - { -#if NETFRAMEWORK - TdsParser.ReliabilitySection.Assert("unreliable call to ReadStringWithEncoding"); // you need to setup for a thread abort somewhere before you call this method -#endif - Debug.Assert(_syncOverAsync || !_asyncReadWithoutSnapshot, "This method is not safe to call when doing sync over async"); - - if (null == encoding) - { - // Need to skip the current column before throwing the error - this ensures that the state shared between this and the data reader is consistent when calling DrainData - if (isPlp) - { - if (!_parser.TrySkipPlpValue((ulong)length, this, out _)) - { - value = null; - return false; - } - } - else - { - if (!TrySkipBytes(length)) - { - value = null; - return false; - } - } - - _parser.ThrowUnsupportedCollationEncountered(this); - } - byte[] buf = null; - int offset = 0; - - if (isPlp) - { - if (!TryReadPlpBytes(ref buf, 0, int.MaxValue, out length)) - { - value = null; - return false; - } - - AssertValidState(); - } - else - { - if (((_inBytesUsed + length) > _inBytesRead) || (_inBytesPacket < length)) - { - if (_bTmp == null || _bTmp.Length < length) - { - _bTmp = new byte[length]; - } - - if (!TryReadByteArray(_bTmp, length)) - { - value = null; - return false; - } - - // assign local to point to parser scratch buffer - buf = _bTmp; - - AssertValidState(); - } - else - { - // assign local to point to _inBuff - buf = _inBuff; - offset = _inBytesUsed; - _inBytesUsed += length; - _inBytesPacket -= length; - - AssertValidState(); - } - } - - // BCL optimizes to not use char[] underneath - value = encoding.GetString(buf, offset, length); - return true; - } - internal ulong ReadPlpLength(bool returnPlpNullIfNull) { ulong value; 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 acae24ec0f..82a9653855 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs @@ -1282,6 +1282,393 @@ internal bool TryReadChar(out char value) return true; } + 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]; + 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)) + { + value = default; + return false; + } + } + 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); + _inBytesUsed += 2; + _inBytesPacket -= 2; + } + + AssertValidState(); + value = (short)((buffer[1] << 8) + buffer[0]); + return true; + } + + internal bool TryReadInt32(out int value) + { +#if NETFRAMEWORK + TdsParser.ReliabilitySection.Assert("unreliable call to ReadInt32"); // you need to setup for a thread abort somewhere before you call this method +#endif + 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)) + { + value = 0; + return false; + } + } + 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); + _inBytesUsed += 4; + _inBytesPacket -= 4; + } + + AssertValidState(); + value = (buffer[3] << 24) + (buffer[2] << 16) + (buffer[1] << 8) + buffer[0]; + return true; + } + + // This method is safe to call when doing async without snapshot + internal bool TryReadInt64(out long value) + { +#if NETFRAMEWORK + TdsParser.ReliabilitySection.Assert("unreliable call to ReadInt64"); // you need to setup for a thread abort somewhere before you call this method +#endif + if ((_inBytesPacket == 0) || (_inBytesUsed == _inBytesRead)) + { + if (!TryPrepareBuffer()) + { + value = 0; + return false; + } + } + + if ((_bTmpRead > 0) || (((_inBytesUsed + 8) > _inBytesRead) || (_inBytesPacket < 8))) + { + // If the long 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. + + int bytesRead; + if (!TryReadByteArray(_bTmp.AsSpan(start: _bTmpRead), 8 - _bTmpRead, out bytesRead)) + { + Debug.Assert(_bTmpRead + bytesRead <= 8, "Read more data than required"); + _bTmpRead += bytesRead; + value = 0; + return false; + } + else + { + Debug.Assert(_bTmpRead + bytesRead == 8, "TryReadByteArray returned true without reading all data required"); + _bTmpRead = 0; + AssertValidState(); + value = BitConverter.ToInt64(_bTmp, 0); + return true; + } + } + else + { + // 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); + + _inBytesUsed += 8; + _inBytesPacket -= 8; + + AssertValidState(); + return true; + } + } + + 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]; + 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)) + { + value = default; + return false; + } + } + 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); + _inBytesUsed += 2; + _inBytesPacket -= 2; + } + + AssertValidState(); + value = (ushort)((buffer[1] << 8) + buffer[0]); + return true; + } + + // This method is safe to call when doing async without replay + internal bool TryReadUInt32(out uint value) + { +#if NETFRAMEWORK + TdsParser.ReliabilitySection.Assert("unreliable call to ReadUInt32"); // you need to setup for a thread abort somewhere before you call this method +#endif + if ((_inBytesPacket == 0) || (_inBytesUsed == _inBytesRead)) + { + if (!TryPrepareBuffer()) + { + value = 0; + return false; + } + } + + if ((_bTmpRead > 0) || (((_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. + + int bytesRead; + if (!TryReadByteArray(_bTmp.AsSpan(start: _bTmpRead), 4 - _bTmpRead, out bytesRead)) + { + Debug.Assert(_bTmpRead + bytesRead <= 4, "Read more data than required"); + _bTmpRead += bytesRead; + value = 0; + return false; + } + else + { + Debug.Assert(_bTmpRead + bytesRead == 4, "TryReadByteArray returned true without reading all data required"); + _bTmpRead = 0; + AssertValidState(); + value = BitConverter.ToUInt32(_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. + + value = BitConverter.ToUInt32(_inBuff, _inBytesUsed); + + _inBytesUsed += 4; + _inBytesPacket -= 4; + + AssertValidState(); + return true; + } + } + + internal bool TryReadSingle(out float value) + { +#if NETFRAMEWORK + TdsParser.ReliabilitySection.Assert("unreliable call to ReadSingle"); // you need to setup for a thread abort somewhere before you call this method +#endif + Debug.Assert(_syncOverAsync || !_asyncReadWithoutSnapshot, "This method is not safe to call when doing sync over async"); + if (((_inBytesUsed + 4) > _inBytesRead) || (_inBytesPacket < 4)) + { + // If the float 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(_bTmp, 4)) + { + value = default; + return false; + } + + AssertValidState(); + value = BitConverter.ToSingle(_bTmp, 0); + return true; + } + else + { + // 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); + + _inBytesUsed += 4; + _inBytesPacket -= 4; + + AssertValidState(); + return true; + } + } + + internal bool TryReadDouble(out double value) + { +#if NETFRAMEWORK + TdsParser.ReliabilitySection.Assert("unreliable call to ReadDouble"); // you need to setup for a thread abort somewhere before you call this method +#endif + Debug.Assert(_syncOverAsync || !_asyncReadWithoutSnapshot, "This method is not safe to call when doing sync over async"); + if (((_inBytesUsed + 8) > _inBytesRead) || (_inBytesPacket < 8)) + { + // If the double 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(_bTmp, 8)) + { + value = default; + return false; + } + + AssertValidState(); + value = BitConverter.ToDouble(_bTmp, 0); + return true; + } + else + { + // 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); + + _inBytesUsed += 8; + _inBytesPacket -= 8; + + AssertValidState(); + return true; + } + } + + internal bool TryReadString(int length, out string value) + { +#if NETFRAMEWORK + TdsParser.ReliabilitySection.Assert("unreliable call to ReadString"); // you need to setup for a thread abort somewhere before you call this method +#endif + Debug.Assert(_syncOverAsync || !_asyncReadWithoutSnapshot, "This method is not safe to call when doing sync over async"); + int cBytes = length << 1; + byte[] buf; + int offset = 0; + + if (((_inBytesUsed + cBytes) > _inBytesRead) || (_inBytesPacket < cBytes)) + { + if (_bTmp == null || _bTmp.Length < cBytes) + { + _bTmp = new byte[cBytes]; + } + + if (!TryReadByteArray(_bTmp, cBytes)) + { + value = null; + return false; + } + + // assign local to point to parser scratch buffer + buf = _bTmp; + + AssertValidState(); + } + else + { + // assign local to point to _inBuff + buf = _inBuff; + offset = _inBytesUsed; + _inBytesUsed += cBytes; + _inBytesPacket -= cBytes; + + AssertValidState(); + } + + value = System.Text.Encoding.Unicode.GetString(buf, offset, cBytes); + return true; + } + + internal bool TryReadStringWithEncoding(int length, System.Text.Encoding encoding, bool isPlp, out string value) + { +#if NETFRAMEWORK + TdsParser.ReliabilitySection.Assert("unreliable call to ReadStringWithEncoding"); // you need to setup for a thread abort somewhere before you call this method +#endif + Debug.Assert(_syncOverAsync || !_asyncReadWithoutSnapshot, "This method is not safe to call when doing sync over async"); + + if (null == encoding) + { + // Need to skip the current column before throwing the error - this ensures that the state shared between this and the data reader is consistent when calling DrainData + if (isPlp) + { + if (!_parser.TrySkipPlpValue((ulong)length, this, out _)) + { + value = null; + return false; + } + } + else + { + if (!TrySkipBytes(length)) + { + value = null; + return false; + } + } + + _parser.ThrowUnsupportedCollationEncountered(this); + } + byte[] buf = null; + int offset = 0; + + if (isPlp) + { + if (!TryReadPlpBytes(ref buf, 0, int.MaxValue, out length)) + { + value = null; + return false; + } + + AssertValidState(); + } + else + { + if (((_inBytesUsed + length) > _inBytesRead) || (_inBytesPacket < length)) + { + if (_bTmp == null || _bTmp.Length < length) + { + _bTmp = new byte[length]; + } + + if (!TryReadByteArray(_bTmp, length)) + { + value = null; + return false; + } + + // assign local to point to parser scratch buffer + buf = _bTmp; + + AssertValidState(); + } + else + { + // assign local to point to _inBuff + buf = _inBuff; + offset = _inBytesUsed; + _inBytesUsed += length; + _inBytesPacket -= length; + + AssertValidState(); + } + } + + // BCL optimizes to not use char[] underneath + value = encoding.GetString(buf, offset, length); + return true; + } + /* // leave this in. comes handy if you have to do Console.WriteLine style debugging ;)