From 22f6c48521c66a93a591d4c13508493a0b15f2f5 Mon Sep 17 00:00:00 2001 From: panoskj Date: Sun, 24 Sep 2023 03:15:38 +0300 Subject: [PATCH 01/10] 1. Merging "Buffer read" methods of TdsParserStateObject, port #667 to netfx. Replaced Thread.MemoryBarrier usage of netfx with Interlocked.MemoryBarrier. --- .../SqlClient/TdsParserStateObject.netcore.cs | 8 ++++++- .../SqlClient/TdsParserStateObject.netfx.cs | 23 ++++++++----------- 2 files changed, 17 insertions(+), 14 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 9ac485cbbb..a95967c32a 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 @@ -310,6 +310,9 @@ public bool TryReadByteArray(Span buff, int len) // Every time you call this method increment the offset and decrease len by the value of totalRead public bool TryReadByteArray(Span buff, int len, out int totalRead) { +#if NETFRAMEWORK + TdsParser.ReliabilitySection.Assert("unreliable call to ReadByteArray"); // you need to setup for a thread abort somewhere before you call this method +#endif totalRead = 0; #if DEBUG @@ -331,7 +334,7 @@ public bool TryReadByteArray(Span buff, int len, out int totalRead) } #endif - Debug.Assert(buff == null || buff.Length >= len, "Invalid length sent to ReadByteArray()!"); + Debug.Assert(buff.IsEmpty || buff.Length >= len, "Invalid length sent to ReadByteArray()!"); // loop through and read up to array length while (len > 0) @@ -368,6 +371,9 @@ public bool TryReadByteArray(Span buff, int len, out int totalRead) // before the byte is returned. internal bool TryReadByte(out byte value) { +#if NETFRAMEWORK + TdsParser.ReliabilitySection.Assert("unreliable call to ReadByte"); // you need to setup for a thread abort somewhere before you call this method +#endif Debug.Assert(_inBytesUsed >= 0 && _inBytesUsed <= _inBytesRead, "ERROR - TDSParser: _inBytesUsed < 0 or _inBytesUsed > _inBytesRead"); value = 0; 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 e64c25c523..59c4f1c511 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 @@ -404,14 +404,16 @@ public bool TryReadByteArray(Span buff, int len) // Every time you call this method increment the offset and decrease len by the value of totalRead public bool TryReadByteArray(Span buff, int len, out int totalRead) { +#if NETFRAMEWORK TdsParser.ReliabilitySection.Assert("unreliable call to ReadByteArray"); // you need to setup for a thread abort somewhere before you call this method +#endif totalRead = 0; #if DEBUG if (_snapshot != null && _snapshot.DoPend()) { _networkPacketTaskSource = new TaskCompletionSource(); - Thread.MemoryBarrier(); + Interlocked.MemoryBarrier(); if (s_forcePendingReadsToWaitForUser) { @@ -463,7 +465,9 @@ public bool TryReadByteArray(Span buff, int len, out int totalRead) // before the byte is returned. internal bool TryReadByte(out byte value) { +#if NETFRAMEWORK TdsParser.ReliabilitySection.Assert("unreliable call to ReadByte"); // you need to setup for a thread abort somewhere before you call this method +#endif Debug.Assert(_inBytesUsed >= 0 && _inBytesUsed <= _inBytesRead, "ERROR - TDSParser: _inBytesUsed < 0 or _inBytesUsed > _inBytesRead"); value = 0; @@ -471,7 +475,7 @@ internal bool TryReadByte(out byte value) if (_snapshot != null && _snapshot.DoPend()) { _networkPacketTaskSource = new TaskCompletionSource(); - Thread.MemoryBarrier(); + Interlocked.MemoryBarrier(); if (s_forcePendingReadsToWaitForUser) { @@ -510,35 +514,28 @@ internal bool TryReadChar(out char value) { Debug.Assert(_syncOverAsync || !_asyncReadWithoutSnapshot, "This method is not safe to call when doing sync over async"); - byte[] buffer; - int offset; + Span buffer = stackalloc byte[2]; 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(_bTmp, 2)) + if (!TryReadByteArray(buffer, 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; - offset = _inBytesUsed; - + buffer = _inBuff.AsSpan(_inBytesUsed, 2); _inBytesUsed += 2; _inBytesPacket -= 2; } AssertValidState(); - value = (char)((buffer[offset + 1] << 8) + buffer[offset]); + value = (char)((buffer[1] << 8) + buffer[0]); return true; } From baebec79d734dd5c68f090b794673ecf97a281b0 Mon Sep 17 00:00:00 2001 From: panoskj Date: Mon, 25 Sep 2023 16:49:13 +0300 Subject: [PATCH 02/10] 1. Merging "Buffer read" methods of TdsParserStateObject, port #667 to netfx. Replaced Thread.MemoryBarrier usage of netfx with Interlocked.MemoryBarrier. --- .../SqlClient/TdsParserStateObject.netcore.cs | 164 ----------------- .../SqlClient/TdsParserStateObject.netfx.cs | 164 ----------------- .../Data/SqlClient/TdsParserStateObject.cs | 169 ++++++++++++++++++ 3 files changed, 169 insertions(+), 328 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 a95967c32a..893bbcd463 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,170 +282,6 @@ internal void StartSession(object cancellationOwner) // Buffer read methods - data values // /////////////////////////////////////// - // look at the next byte without pulling it off the wire, don't just return _inBytesUsed since we may - // have to go to the network to get the next byte. - internal bool TryPeekByte(out byte value) - { - if (!TryReadByte(out value)) - { - return false; - } - - // now do fixup - _inBytesPacket++; - _inBytesUsed--; - - AssertValidState(); - return true; - } - - // Takes a byte array, an offset, and a len and fills the array from the offset to len number of - // bytes from the in buffer. - public bool TryReadByteArray(Span buff, int len) - { - return TryReadByteArray(buff, len, out _); - } - - // NOTE: This method must be retriable WITHOUT replaying a snapshot - // Every time you call this method increment the offset and decrease len by the value of totalRead - public bool TryReadByteArray(Span buff, int len, out int totalRead) - { -#if NETFRAMEWORK - TdsParser.ReliabilitySection.Assert("unreliable call to ReadByteArray"); // you need to setup for a thread abort somewhere before you call this method -#endif - totalRead = 0; - -#if DEBUG - if (_snapshot != null && _snapshot.DoPend()) - { - _networkPacketTaskSource = new TaskCompletionSource(); - Interlocked.MemoryBarrier(); - - if (s_forcePendingReadsToWaitForUser) - { - _realNetworkPacketTaskSource = new TaskCompletionSource(); - _realNetworkPacketTaskSource.SetResult(null); - } - else - { - _networkPacketTaskSource.TrySetResult(null); - } - return false; - } -#endif - - Debug.Assert(buff.IsEmpty || buff.Length >= len, "Invalid length sent to ReadByteArray()!"); - - // loop through and read up to array length - while (len > 0) - { - if ((_inBytesPacket == 0) || (_inBytesUsed == _inBytesRead)) - { - if (!TryPrepareBuffer()) - { - return false; - } - } - - int bytesToRead = Math.Min(len, Math.Min(_inBytesPacket, _inBytesRead - _inBytesUsed)); - Debug.Assert(bytesToRead > 0, "0 byte read in TryReadByteArray"); - if (!buff.IsEmpty) - { - ReadOnlySpan copyFrom = new ReadOnlySpan(_inBuff, _inBytesUsed, bytesToRead); - Span copyTo = buff.Slice(totalRead, bytesToRead); - copyFrom.CopyTo(copyTo); - } - - totalRead += bytesToRead; - _inBytesUsed += bytesToRead; - _inBytesPacket -= bytesToRead; - len -= bytesToRead; - - AssertValidState(); - } - - return true; - } - - // Takes no arguments and returns a byte from the buffer. If the buffer is empty, it is filled - // before the byte is returned. - internal bool TryReadByte(out byte value) - { -#if NETFRAMEWORK - TdsParser.ReliabilitySection.Assert("unreliable call to ReadByte"); // you need to setup for a thread abort somewhere before you call this method -#endif - Debug.Assert(_inBytesUsed >= 0 && _inBytesUsed <= _inBytesRead, "ERROR - TDSParser: _inBytesUsed < 0 or _inBytesUsed > _inBytesRead"); - value = 0; - -#if DEBUG - if (_snapshot != null && _snapshot.DoPend()) - { - _networkPacketTaskSource = new TaskCompletionSource(); - Interlocked.MemoryBarrier(); - - if (s_forcePendingReadsToWaitForUser) - { - _realNetworkPacketTaskSource = new TaskCompletionSource(); - _realNetworkPacketTaskSource.SetResult(null); - } - else - { - _networkPacketTaskSource.TrySetResult(null); - } - return false; - } -#endif - - if ((_inBytesPacket == 0) || (_inBytesUsed == _inBytesRead)) - { - if (!TryPrepareBuffer()) - { - return false; - } - } - - // decrement the number of bytes left in the packet - _inBytesPacket--; - - Debug.Assert(_inBytesPacket >= 0, "ERROR - TDSParser: _inBytesPacket < 0"); - - // return the byte from the buffer and increment the counter for number of bytes used in the in buffer - value = (_inBuff[_inBytesUsed++]); - - AssertValidState(); - return true; - } - - 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]; - 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)) - { - value = '\0'; - return false; - } - } - 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); - _inBytesUsed += 2; - _inBytesPacket -= 2; - } - - AssertValidState(); - value = (char)((buffer[1] << 8) + buffer[0]); - - return true; - } - internal bool TryReadInt16(out short value) { Debug.Assert(_syncOverAsync || !_asyncReadWithoutSnapshot, "This method is not safe to call when doing sync over async"); 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 59c4f1c511..e03d64e84d 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,170 +376,6 @@ internal void StartSession(int objectID) // Buffer read methods - data values // /////////////////////////////////////// - // look at the next byte without pulling it off the wire, don't just return _inBytesUsed since we may - // have to go to the network to get the next byte. - internal bool TryPeekByte(out byte value) - { - if (!TryReadByte(out value)) - { - return false; - } - - // now do fixup - _inBytesPacket++; - _inBytesUsed--; - - AssertValidState(); - return true; - } - - // Takes a byte array, an offset, and a len and fills the array from the offset to len number of - // bytes from the in buffer. - public bool TryReadByteArray(Span buff, int len) - { - return TryReadByteArray(buff, len, out _); - } - - // NOTE: This method must be retriable WITHOUT replaying a snapshot - // Every time you call this method increment the offset and decrease len by the value of totalRead - public bool TryReadByteArray(Span buff, int len, out int totalRead) - { -#if NETFRAMEWORK - TdsParser.ReliabilitySection.Assert("unreliable call to ReadByteArray"); // you need to setup for a thread abort somewhere before you call this method -#endif - totalRead = 0; - -#if DEBUG - if (_snapshot != null && _snapshot.DoPend()) - { - _networkPacketTaskSource = new TaskCompletionSource(); - Interlocked.MemoryBarrier(); - - if (s_forcePendingReadsToWaitForUser) - { - _realNetworkPacketTaskSource = new TaskCompletionSource(); - _realNetworkPacketTaskSource.SetResult(null); - } - else - { - _networkPacketTaskSource.TrySetResult(null); - } - return false; - } -#endif - - Debug.Assert(buff.IsEmpty || buff.Length >= len, "Invalid length sent to ReadByteArray()!"); - - // loop through and read up to array length - while (len > 0) - { - if ((_inBytesPacket == 0) || (_inBytesUsed == _inBytesRead)) - { - if (!TryPrepareBuffer()) - { - return false; - } - } - - int bytesToRead = Math.Min(len, Math.Min(_inBytesPacket, _inBytesRead - _inBytesUsed)); - Debug.Assert(bytesToRead > 0, "0 byte read in TryReadByteArray"); - if (!buff.IsEmpty) - { - ReadOnlySpan copyFrom = new ReadOnlySpan(_inBuff, _inBytesUsed, bytesToRead); - Span copyTo = buff.Slice(totalRead, bytesToRead); - copyFrom.CopyTo(copyTo); - } - - totalRead += bytesToRead; - _inBytesUsed += bytesToRead; - _inBytesPacket -= bytesToRead; - len -= bytesToRead; - - AssertValidState(); - } - - return true; - } - - // Takes no arguments and returns a byte from the buffer. If the buffer is empty, it is filled - // before the byte is returned. - internal bool TryReadByte(out byte value) - { -#if NETFRAMEWORK - TdsParser.ReliabilitySection.Assert("unreliable call to ReadByte"); // you need to setup for a thread abort somewhere before you call this method -#endif - Debug.Assert(_inBytesUsed >= 0 && _inBytesUsed <= _inBytesRead, "ERROR - TDSParser: _inBytesUsed < 0 or _inBytesUsed > _inBytesRead"); - value = 0; - -#if DEBUG - if (_snapshot != null && _snapshot.DoPend()) - { - _networkPacketTaskSource = new TaskCompletionSource(); - Interlocked.MemoryBarrier(); - - if (s_forcePendingReadsToWaitForUser) - { - _realNetworkPacketTaskSource = new TaskCompletionSource(); - _realNetworkPacketTaskSource.SetResult(null); - } - else - { - _networkPacketTaskSource.TrySetResult(null); - } - return false; - } -#endif - - if ((_inBytesPacket == 0) || (_inBytesUsed == _inBytesRead)) - { - if (!TryPrepareBuffer()) - { - return false; - } - } - - // decrement the number of bytes left in the packet - _inBytesPacket--; - - Debug.Assert(_inBytesPacket >= 0, "ERROR - TDSParser: _inBytesPacket < 0"); - - // return the byte from the buffer and increment the counter for number of bytes used in the in buffer - value = (_inBuff[_inBytesUsed++]); - - AssertValidState(); - return true; - } - - 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]; - 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)) - { - value = '\0'; - return false; - } - } - 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); - _inBytesUsed += 2; - _inBytesPacket -= 2; - } - - AssertValidState(); - value = (char)((buffer[1] << 8) + buffer[0]); - - return true; - } - internal bool TryReadInt16(out short value) { Debug.Assert(_syncOverAsync || !_asyncReadWithoutSnapshot, "This method is not safe to call when doing sync over async"); 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 7147c10e5a..618d9518ca 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs @@ -1113,6 +1113,175 @@ internal bool SetPacketSize(int size) return false; } + + /////////////////////////////////////// + // Buffer read methods - data values // + /////////////////////////////////////// + + // look at the next byte without pulling it off the wire, don't just return _inBytesUsed since we may + // have to go to the network to get the next byte. + internal bool TryPeekByte(out byte value) + { + if (!TryReadByte(out value)) + { + return false; + } + + // now do fixup + _inBytesPacket++; + _inBytesUsed--; + + AssertValidState(); + return true; + } + + // Takes a byte array, an offset, and a len and fills the array from the offset to len number of + // bytes from the in buffer. + public bool TryReadByteArray(Span buff, int len) + { + return TryReadByteArray(buff, len, out _); + } + + // NOTE: This method must be retriable WITHOUT replaying a snapshot + // Every time you call this method increment the offset and decrease len by the value of totalRead + public bool TryReadByteArray(Span buff, int len, out int totalRead) + { +#if NETFRAMEWORK + TdsParser.ReliabilitySection.Assert("unreliable call to ReadByteArray"); // you need to setup for a thread abort somewhere before you call this method +#endif + totalRead = 0; + +#if DEBUG + if (_snapshot != null && _snapshot.DoPend()) + { + _networkPacketTaskSource = new TaskCompletionSource(); + Interlocked.MemoryBarrier(); + + if (s_forcePendingReadsToWaitForUser) + { + _realNetworkPacketTaskSource = new TaskCompletionSource(); + _realNetworkPacketTaskSource.SetResult(null); + } + else + { + _networkPacketTaskSource.TrySetResult(null); + } + return false; + } +#endif + + Debug.Assert(buff.IsEmpty || buff.Length >= len, "Invalid length sent to ReadByteArray()!"); + + // loop through and read up to array length + while (len > 0) + { + if ((_inBytesPacket == 0) || (_inBytesUsed == _inBytesRead)) + { + if (!TryPrepareBuffer()) + { + return false; + } + } + + int bytesToRead = Math.Min(len, Math.Min(_inBytesPacket, _inBytesRead - _inBytesUsed)); + Debug.Assert(bytesToRead > 0, "0 byte read in TryReadByteArray"); + if (!buff.IsEmpty) + { + ReadOnlySpan copyFrom = new ReadOnlySpan(_inBuff, _inBytesUsed, bytesToRead); + Span copyTo = buff.Slice(totalRead, bytesToRead); + copyFrom.CopyTo(copyTo); + } + + totalRead += bytesToRead; + _inBytesUsed += bytesToRead; + _inBytesPacket -= bytesToRead; + len -= bytesToRead; + + AssertValidState(); + } + + return true; + } + + // Takes no arguments and returns a byte from the buffer. If the buffer is empty, it is filled + // before the byte is returned. + internal bool TryReadByte(out byte value) + { +#if NETFRAMEWORK + TdsParser.ReliabilitySection.Assert("unreliable call to ReadByte"); // you need to setup for a thread abort somewhere before you call this method +#endif + Debug.Assert(_inBytesUsed >= 0 && _inBytesUsed <= _inBytesRead, "ERROR - TDSParser: _inBytesUsed < 0 or _inBytesUsed > _inBytesRead"); + value = 0; + +#if DEBUG + if (_snapshot != null && _snapshot.DoPend()) + { + _networkPacketTaskSource = new TaskCompletionSource(); + Interlocked.MemoryBarrier(); + + if (s_forcePendingReadsToWaitForUser) + { + _realNetworkPacketTaskSource = new TaskCompletionSource(); + _realNetworkPacketTaskSource.SetResult(null); + } + else + { + _networkPacketTaskSource.TrySetResult(null); + } + return false; + } +#endif + + if ((_inBytesPacket == 0) || (_inBytesUsed == _inBytesRead)) + { + if (!TryPrepareBuffer()) + { + return false; + } + } + + // decrement the number of bytes left in the packet + _inBytesPacket--; + + Debug.Assert(_inBytesPacket >= 0, "ERROR - TDSParser: _inBytesPacket < 0"); + + // return the byte from the buffer and increment the counter for number of bytes used in the in buffer + value = (_inBuff[_inBytesUsed++]); + + AssertValidState(); + return true; + } + + 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]; + 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)) + { + value = '\0'; + return false; + } + } + 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); + _inBytesUsed += 2; + _inBytesPacket -= 2; + } + + AssertValidState(); + value = (char)((buffer[1] << 8) + buffer[0]); + + return true; + } + /* // leave this in. comes handy if you have to do Console.WriteLine style debugging ;) From bebf28c752b5c759006c9d7542ab0f972a1d0eb9 Mon Sep 17 00:00:00 2001 From: panoskj Date: Sun, 24 Sep 2023 03:21:11 +0300 Subject: [PATCH 03/10] 2. Merging "Buffer read" methods of TdsParserStateObject, port #667 to netfx. --- .../SqlClient/TdsParserStateObject.netcore.cs | 21 +++++++ .../SqlClient/TdsParserStateObject.netfx.cs | 59 +++++++++---------- 2 files changed, 48 insertions(+), 32 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 893bbcd463..89b9321cff 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 @@ -313,6 +313,9 @@ internal bool TryReadInt16(out short value) 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)) @@ -342,6 +345,9 @@ internal bool TryReadInt32(out int value) // 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()) @@ -420,6 +426,9 @@ internal bool TryReadUInt16(out ushort value) // 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()) @@ -468,6 +477,9 @@ internal bool TryReadUInt32(out uint value) 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)) { @@ -501,6 +513,9 @@ internal bool TryReadSingle(out float value) 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)) { @@ -534,6 +549,9 @@ internal bool TryReadDouble(out double value) 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; @@ -574,6 +592,9 @@ internal bool TryReadString(int length, out string value) 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) 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 e03d64e84d..eafa6b1c50 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 @@ -380,75 +380,68 @@ internal bool TryReadInt16(out short value) { Debug.Assert(_syncOverAsync || !_asyncReadWithoutSnapshot, "This method is not safe to call when doing sync over async"); - byte[] buffer; - int offset; + 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(_bTmp, 2)) + if (!TryReadByteArray(buffer, 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; - offset = _inBytesUsed; - + buffer = _inBuff.AsSpan(_inBytesUsed, 2); _inBytesUsed += 2; _inBytesPacket -= 2; } AssertValidState(); - value = (short)((buffer[offset + 1] << 8) + buffer[offset]); + 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(_bTmp, 4)) + if (!TryReadByteArray(buffer, 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. - - value = BitConverter.ToInt32(_inBuff, _inBytesUsed); - + 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()) @@ -499,43 +492,37 @@ internal bool TryReadUInt16(out ushort value) { Debug.Assert(_syncOverAsync || !_asyncReadWithoutSnapshot, "This method is not safe to call when doing sync over async"); - byte[] buffer; - int offset; + 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(_bTmp, 2)) + if (!TryReadByteArray(buffer, 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; - offset = _inBytesUsed; - + buffer = _inBuff.AsSpan(_inBytesUsed, 2); _inBytesUsed += 2; _inBytesPacket -= 2; } AssertValidState(); - value = (ushort)((buffer[offset + 1] << 8) + buffer[offset]); + 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()) @@ -584,7 +571,9 @@ internal bool TryReadUInt32(out uint value) 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)) { @@ -618,7 +607,9 @@ internal bool TryReadSingle(out float value) 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)) { @@ -652,7 +643,9 @@ internal bool TryReadDouble(out double value) 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; @@ -693,7 +686,9 @@ internal bool TryReadString(int length, out string value) 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) From ecc8c29470f8311da84f3efe0a25fc81945e87c6 Mon Sep 17 00:00:00 2001 From: panoskj Date: Mon, 25 Sep 2023 16:59:15 +0300 Subject: [PATCH 04/10] 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 89b9321cff..a22fcf2483 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 eafa6b1c50..9ac9134f1f 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 618d9518ca..f1f11c8304 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 ;) From 4d1c0eb86e869790005aba5f812fb0e5cc494dfa Mon Sep 17 00:00:00 2001 From: panoskj Date: Sun, 24 Sep 2023 03:25:00 +0300 Subject: [PATCH 05/10] 3. Merging "Buffer read" methods of TdsParserStateObject. --- .../SqlClient/TdsParserStateObject.netcore.cs | 92 ------------------- .../SqlClient/TdsParserStateObject.netfx.cs | 92 ------------------- .../Data/SqlClient/TdsParserStateObject.cs | 92 +++++++++++++++++++ 3 files changed, 92 insertions(+), 184 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 a22fcf2483..79737806a7 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,98 +282,6 @@ internal void StartSession(object cancellationOwner) // Buffer read methods - data values // /////////////////////////////////////// - internal ulong ReadPlpLength(bool returnPlpNullIfNull) - { - ulong value; - Debug.Assert(_syncOverAsync, "Should not attempt pends in a synchronous call"); - bool result = TryReadPlpLength(returnPlpNullIfNull, out value); - if (!result) - { - throw SQL.SynchronousCallMayNotPend(); - } - return value; - } - - // Reads the length of either the entire data or the length of the next chunk in a - // partially length prefixed data - // After this call, call ReadPlpBytes/ReadPlpUnicodeChars until the specified length of data - // is consumed. Repeat this until ReadPlpLength returns 0 in order to read the - // entire stream. - // When this function returns 0, it means the data stream is read completely and the - // plp state in the tdsparser is cleaned. - internal bool TryReadPlpLength(bool returnPlpNullIfNull, out ulong lengthLeft) - { - uint chunklen; - // bool firstchunk = false; - bool isNull = false; - - Debug.Assert(_longlenleft == 0, "Out of synch length read request"); - if (_longlen == 0) - { - // First chunk is being read. Find out what type of chunk it is - long value; - if (!TryReadInt64(out value)) - { - lengthLeft = 0; - return false; - } - _longlen = (ulong)value; - // firstchunk = true; - } - - if (_longlen == TdsEnums.SQL_PLP_NULL) - { - _longlen = 0; - _longlenleft = 0; - isNull = true; - } - else - { - // Data is coming in uint chunks, read length of next chunk - if (!TryReadUInt32(out chunklen)) - { - lengthLeft = 0; - return false; - } - if (chunklen == TdsEnums.SQL_PLP_CHUNK_TERMINATOR) - { - _longlenleft = 0; - _longlen = 0; - } - else - { - _longlenleft = chunklen; - } - } - - AssertValidState(); - - if (isNull && returnPlpNullIfNull) - { - lengthLeft = TdsEnums.SQL_PLP_NULL; - return true; - } - - lengthLeft = _longlenleft; - return true; - } - - internal int ReadPlpBytesChunk(byte[] buff, int offset, int len) - { - Debug.Assert(_syncOverAsync, "Should not attempt pends in a synchronous call"); - Debug.Assert(_longlenleft > 0, "Read when no data available"); - - int value; - int bytesToRead = (int)Math.Min(_longlenleft, (ulong)len); - bool result = TryReadByteArray(buff.AsSpan(offset), bytesToRead, out value); - _longlenleft -= (ulong)bytesToRead; - if (!result) - { - throw SQL.SynchronousCallMayNotPend(); - } - return value; - } - // Reads the requested number of bytes from a plp data stream, or the entire data if // requested length is -1 or larger than the actual length of data. First call to this method // should be preceeded by a call to ReadPlpLength or ReadDataLength. 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 9ac9134f1f..83ae687008 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,98 +376,6 @@ internal void StartSession(int objectID) // Buffer read methods - data values // /////////////////////////////////////// - internal ulong ReadPlpLength(bool returnPlpNullIfNull) - { - ulong value; - Debug.Assert(_syncOverAsync, "Should not attempt pends in a synchronous call"); - bool result = TryReadPlpLength(returnPlpNullIfNull, out value); - if (!result) - { - throw SQL.SynchronousCallMayNotPend(); - } - return value; - } - - // Reads the length of either the entire data or the length of the next chunk in a - // partially length prefixed data - // After this call, call ReadPlpBytes/ReadPlpUnicodeChars until the specified length of data - // is consumed. Repeat this until ReadPlpLength returns 0 in order to read the - // entire stream. - // When this function returns 0, it means the data stream is read completely and the - // plp state in the tdsparser is cleaned. - internal bool TryReadPlpLength(bool returnPlpNullIfNull, out ulong lengthLeft) - { - uint chunklen; - // bool firstchunk = false; - bool isNull = false; - - Debug.Assert(_longlenleft == 0, "Out of synch length read request"); - if (_longlen == 0) - { - // First chunk is being read. Find out what type of chunk it is - long value; - if (!TryReadInt64(out value)) - { - lengthLeft = 0; - return false; - } - _longlen = (ulong)value; - // firstchunk = true; - } - - if (_longlen == TdsEnums.SQL_PLP_NULL) - { - _longlen = 0; - _longlenleft = 0; - isNull = true; - } - else - { - // Data is coming in uint chunks, read length of next chunk - if (!TryReadUInt32(out chunklen)) - { - lengthLeft = 0; - return false; - } - if (chunklen == TdsEnums.SQL_PLP_CHUNK_TERMINATOR) - { - _longlenleft = 0; - _longlen = 0; - } - else - { - _longlenleft = chunklen; - } - } - - AssertValidState(); - - if (isNull && returnPlpNullIfNull) - { - lengthLeft = TdsEnums.SQL_PLP_NULL; - return true; - } - - lengthLeft = _longlenleft; - return true; - } - - internal int ReadPlpBytesChunk(byte[] buff, int offset, int len) - { - Debug.Assert(_syncOverAsync, "Should not attempt pends in a synchronous call"); - Debug.Assert(_longlenleft > 0, "Read when no data available"); - - int value; - int bytesToRead = (int)Math.Min(_longlenleft, (ulong)len); - bool result = TryReadByteArray(buff.AsSpan(offset), bytesToRead, out value); - _longlenleft -= (ulong)bytesToRead; - if (!result) - { - throw SQL.SynchronousCallMayNotPend(); - } - return value; - } - // Reads the requested number of bytes from a plp data stream, or the entire data if // requested length is -1 or larger than the actual length of data. First call to this method // should be preceeded by a call to ReadPlpLength or ReadDataLength. 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 f1f11c8304..c326045de0 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs @@ -1669,6 +1669,98 @@ internal bool TryReadStringWithEncoding(int length, System.Text.Encoding encodin return true; } + internal ulong ReadPlpLength(bool returnPlpNullIfNull) + { + ulong value; + Debug.Assert(_syncOverAsync, "Should not attempt pends in a synchronous call"); + bool result = TryReadPlpLength(returnPlpNullIfNull, out value); + if (!result) + { + throw SQL.SynchronousCallMayNotPend(); + } + return value; + } + + // Reads the length of either the entire data or the length of the next chunk in a + // partially length prefixed data + // After this call, call ReadPlpBytes/ReadPlpUnicodeChars until the specified length of data + // is consumed. Repeat this until ReadPlpLength returns 0 in order to read the + // entire stream. + // When this function returns 0, it means the data stream is read completely and the + // plp state in the tdsparser is cleaned. + internal bool TryReadPlpLength(bool returnPlpNullIfNull, out ulong lengthLeft) + { + uint chunklen; + // bool firstchunk = false; + bool isNull = false; + + Debug.Assert(_longlenleft == 0, "Out of synch length read request"); + if (_longlen == 0) + { + // First chunk is being read. Find out what type of chunk it is + long value; + if (!TryReadInt64(out value)) + { + lengthLeft = 0; + return false; + } + _longlen = (ulong)value; + // firstchunk = true; + } + + if (_longlen == TdsEnums.SQL_PLP_NULL) + { + _longlen = 0; + _longlenleft = 0; + isNull = true; + } + else + { + // Data is coming in uint chunks, read length of next chunk + if (!TryReadUInt32(out chunklen)) + { + lengthLeft = 0; + return false; + } + if (chunklen == TdsEnums.SQL_PLP_CHUNK_TERMINATOR) + { + _longlenleft = 0; + _longlen = 0; + } + else + { + _longlenleft = chunklen; + } + } + + AssertValidState(); + + if (isNull && returnPlpNullIfNull) + { + lengthLeft = TdsEnums.SQL_PLP_NULL; + return true; + } + + lengthLeft = _longlenleft; + return true; + } + + internal int ReadPlpBytesChunk(byte[] buff, int offset, int len) + { + Debug.Assert(_syncOverAsync, "Should not attempt pends in a synchronous call"); + Debug.Assert(_longlenleft > 0, "Read when no data available"); + + int value; + int bytesToRead = (int)Math.Min(_longlenleft, (ulong)len); + bool result = TryReadByteArray(buff.AsSpan(offset), bytesToRead, out value); + _longlenleft -= (ulong)bytesToRead; + if (!result) + { + throw SQL.SynchronousCallMayNotPend(); + } + return value; + } + /* // leave this in. comes handy if you have to do Console.WriteLine style debugging ;) From 342e41149f8fdad670be894f14a396c5dfd67ab6 Mon Sep 17 00:00:00 2001 From: panoskj Date: Sun, 24 Sep 2023 03:45:15 +0300 Subject: [PATCH 06/10] 4. Merging "Buffer read" methods of TdsParserStateObject, port #285 to netfx. --- .../SqlClient/TdsParserStateObject.netcore.cs | 2 -- .../SqlClient/TdsParserStateObject.netfx.cs | 28 +++++++++++++++++-- .../Data/SqlClient/TdsParserStateObject.cs | 2 ++ 3 files changed, 28 insertions(+), 4 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 79737806a7..22e2fc437d 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 @@ -2445,8 +2445,6 @@ partial void SetStackInternal(string value) private PacketData _snapshotInBuffList; private PacketData _sparePacket; - internal byte[] _plpBuffer; - private int _snapshotInBuffCount; #if DEBUG 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 83ae687008..7d9829ab7e 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 @@ -393,7 +393,7 @@ internal bool TryReadPlpBytes(ref byte[] buff, int offset, int len, out int tota Debug.Assert(_longlenleft == 0); if (buff == null) { - buff = new byte[0]; + buff = Array.Empty(); } AssertValidState(); @@ -409,7 +409,19 @@ internal bool TryReadPlpBytes(ref byte[] buff, int offset, int len, out int tota // If total length is known up front, allocate the whole buffer in one shot instead of realloc'ing and copying over each time if (buff == null && _longlen != TdsEnums.SQL_PLP_UNKNOWNLEN) { - buff = new byte[(Math.Min((int)_longlen, len))]; + if (_snapshot != null) + { + // if there is a snapshot and it contains a stored plp buffer take it + // and try to use it if it is the right length + buff = _snapshot._plpBuffer; + _snapshot._plpBuffer = null; + } + + if ((ulong)(buff?.Length ?? 0) != _longlen) + { + // if the buffer is null or the wrong length create one to use + buff = new byte[(Math.Min((int)_longlen, len))]; + } } if (_longlenleft == 0) @@ -454,6 +466,12 @@ internal bool TryReadPlpBytes(ref byte[] buff, int offset, int len, out int tota _longlenleft -= (ulong)bytesRead; if (!result) { + if (_snapshot != null) + { + // a partial read has happened so store the target buffer in the snapshot + // so it can be re-used when another packet arrives and we read again + _snapshot._plpBuffer = buff; + } return false; } @@ -462,6 +480,12 @@ internal bool TryReadPlpBytes(ref byte[] buff, int offset, int len, out int tota // Read the next chunk or cleanup state if hit the end if (!TryReadPlpLength(false, out _)) { + if (_snapshot != null) + { + // a partial read has happened so store the target buffer in the snapshot + // so it can be re-used when another packet arrives and we read again + _snapshot._plpBuffer = buff; + } return false; } } 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 c326045de0..0412a2eb6c 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs @@ -1928,6 +1928,8 @@ internal void Restore(TdsParserStateObject stateObj) private TdsParserStateObject _stateObj; private StateObjectData _replayStateData; + internal byte[] _plpBuffer; + #if DEBUG private int _rollingPend = 0; private int _rollingPendCount = 0; From 74f5df3c71a18cebd20d0aa8ae9350333c41f2a7 Mon Sep 17 00:00:00 2001 From: panoskj Date: Mon, 25 Sep 2023 17:23:08 +0300 Subject: [PATCH 07/10] 4. Merging "Buffer read" methods of TdsParserStateObject, port #285 to netfx. --- .../SqlClient/TdsParserStateObject.netcore.cs | 128 ------------------ .../SqlClient/TdsParserStateObject.netfx.cs | 128 ------------------ .../Data/SqlClient/TdsParserStateObject.cs | 123 +++++++++++++++++ 3 files changed, 123 insertions(+), 256 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 22e2fc437d..1abf0c0191 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 @@ -278,134 +278,6 @@ internal void StartSession(object cancellationOwner) _cancellationOwner.Target = cancellationOwner; } - /////////////////////////////////////// - // Buffer read methods - data values // - /////////////////////////////////////// - - // Reads the requested number of bytes from a plp data stream, or the entire data if - // requested length is -1 or larger than the actual length of data. First call to this method - // should be preceeded by a call to ReadPlpLength or ReadDataLength. - // Returns the actual bytes read. - // NOTE: This method must be retriable WITHOUT replaying a snapshot - // Every time you call this method increment the offset and decrease len by the value of totalBytesRead - internal bool TryReadPlpBytes(ref byte[] buff, int offset, int len, out int totalBytesRead) - { - int bytesRead; - int bytesLeft; - byte[] newbuf; - - if (_longlen == 0) - { - Debug.Assert(_longlenleft == 0); - if (buff == null) - { - buff = Array.Empty(); - } - - AssertValidState(); - totalBytesRead = 0; - return true; // No data - } - - Debug.Assert(_longlen != TdsEnums.SQL_PLP_NULL, "Out of sync plp read request"); - Debug.Assert((buff == null && offset == 0) || (buff.Length >= offset + len), "Invalid length sent to ReadPlpBytes()!"); - - bytesLeft = len; - - // If total length is known up front, allocate the whole buffer in one shot instead of realloc'ing and copying over each time - if (buff == null && _longlen != TdsEnums.SQL_PLP_UNKNOWNLEN) - { - if (_snapshot != null) - { - // if there is a snapshot and it contains a stored plp buffer take it - // and try to use it if it is the right length - buff = _snapshot._plpBuffer; - _snapshot._plpBuffer = null; - } - - if ((ulong)(buff?.Length ?? 0) != _longlen) - { - // if the buffer is null or the wrong length create one to use - buff = new byte[(Math.Min((int)_longlen, len))]; - } - } - - if (_longlenleft == 0) - { - if (!TryReadPlpLength(false, out _)) - { - totalBytesRead = 0; - return false; - } - if (_longlenleft == 0) - { // Data read complete - totalBytesRead = 0; - return true; - } - } - - if (buff == null) - { - buff = new byte[_longlenleft]; - } - - totalBytesRead = 0; - - while (bytesLeft > 0) - { - int bytesToRead = (int)Math.Min(_longlenleft, (ulong)bytesLeft); - if (buff.Length < (offset + bytesToRead)) - { - // Grow the array - newbuf = new byte[offset + bytesToRead]; - Buffer.BlockCopy(buff, 0, newbuf, 0, offset); - buff = newbuf; - } - - bool result = TryReadByteArray(buff.AsSpan(offset), bytesToRead, out bytesRead); - Debug.Assert(bytesRead <= bytesLeft, "Read more bytes than we needed"); - Debug.Assert((ulong)bytesRead <= _longlenleft, "Read more bytes than is available"); - - bytesLeft -= bytesRead; - offset += bytesRead; - totalBytesRead += bytesRead; - _longlenleft -= (ulong)bytesRead; - if (!result) - { - if (_snapshot != null) - { - // a partial read has happened so store the target buffer in the snapshot - // so it can be re-used when another packet arrives and we read again - _snapshot._plpBuffer = buff; - } - return false; - } - - if (_longlenleft == 0) - { - // Read the next chunk or cleanup state if hit the end - if (!TryReadPlpLength(false, out _)) - { - if (_snapshot != null) - { - // a partial read has happened so store the target buffer in the snapshot - // so it can be re-used when another packet arrives and we read again - _snapshot._plpBuffer = buff; - } - return false; - } - } - - AssertValidState(); - - // Catch the point where we read the entire plp data stream and clean up state - if (_longlenleft == 0) // Data read complete - break; - } - return true; - } - - ///////////////////////////////////////// // Value Skip Logic // ///////////////////////////////////////// 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 7d9829ab7e..8fa15e3a6b 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 @@ -372,134 +372,6 @@ internal void StartSession(int objectID) _allowObjectID = objectID; } - /////////////////////////////////////// - // Buffer read methods - data values // - /////////////////////////////////////// - - // Reads the requested number of bytes from a plp data stream, or the entire data if - // requested length is -1 or larger than the actual length of data. First call to this method - // should be preceeded by a call to ReadPlpLength or ReadDataLength. - // Returns the actual bytes read. - // NOTE: This method must be retriable WITHOUT replaying a snapshot - // Every time you call this method increment the offset and decrease len by the value of totalBytesRead - internal bool TryReadPlpBytes(ref byte[] buff, int offset, int len, out int totalBytesRead) - { - int bytesRead; - int bytesLeft; - byte[] newbuf; - - if (_longlen == 0) - { - Debug.Assert(_longlenleft == 0); - if (buff == null) - { - buff = Array.Empty(); - } - - AssertValidState(); - totalBytesRead = 0; - return true; // No data - } - - Debug.Assert(_longlen != TdsEnums.SQL_PLP_NULL, "Out of sync plp read request"); - Debug.Assert((buff == null && offset == 0) || (buff.Length >= offset + len), "Invalid length sent to ReadPlpBytes()!"); - - bytesLeft = len; - - // If total length is known up front, allocate the whole buffer in one shot instead of realloc'ing and copying over each time - if (buff == null && _longlen != TdsEnums.SQL_PLP_UNKNOWNLEN) - { - if (_snapshot != null) - { - // if there is a snapshot and it contains a stored plp buffer take it - // and try to use it if it is the right length - buff = _snapshot._plpBuffer; - _snapshot._plpBuffer = null; - } - - if ((ulong)(buff?.Length ?? 0) != _longlen) - { - // if the buffer is null or the wrong length create one to use - buff = new byte[(Math.Min((int)_longlen, len))]; - } - } - - if (_longlenleft == 0) - { - if (!TryReadPlpLength(false, out _)) - { - totalBytesRead = 0; - return false; - } - if (_longlenleft == 0) - { // Data read complete - totalBytesRead = 0; - return true; - } - } - - if (buff == null) - { - buff = new byte[_longlenleft]; - } - - totalBytesRead = 0; - - while (bytesLeft > 0) - { - int bytesToRead = (int)Math.Min(_longlenleft, (ulong)bytesLeft); - if (buff.Length < (offset + bytesToRead)) - { - // Grow the array - newbuf = new byte[offset + bytesToRead]; - Buffer.BlockCopy(buff, 0, newbuf, 0, offset); - buff = newbuf; - } - - bool result = TryReadByteArray(buff.AsSpan(offset), bytesToRead, out bytesRead); - Debug.Assert(bytesRead <= bytesLeft, "Read more bytes than we needed"); - Debug.Assert((ulong)bytesRead <= _longlenleft, "Read more bytes than is available"); - - bytesLeft -= bytesRead; - offset += bytesRead; - totalBytesRead += bytesRead; - _longlenleft -= (ulong)bytesRead; - if (!result) - { - if (_snapshot != null) - { - // a partial read has happened so store the target buffer in the snapshot - // so it can be re-used when another packet arrives and we read again - _snapshot._plpBuffer = buff; - } - return false; - } - - if (_longlenleft == 0) - { - // Read the next chunk or cleanup state if hit the end - if (!TryReadPlpLength(false, out _)) - { - if (_snapshot != null) - { - // a partial read has happened so store the target buffer in the snapshot - // so it can be re-used when another packet arrives and we read again - _snapshot._plpBuffer = buff; - } - return false; - } - } - - AssertValidState(); - - // Catch the point where we read the entire plp data stream and clean up state - if (_longlenleft == 0) // Data read complete - break; - } - return true; - } - - ///////////////////////////////////////// // Value Skip Logic // ///////////////////////////////////////// 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 0412a2eb6c..f2b084e7e4 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs @@ -1761,6 +1761,129 @@ internal int ReadPlpBytesChunk(byte[] buff, int offset, int len) return value; } + // Reads the requested number of bytes from a plp data stream, or the entire data if + // requested length is -1 or larger than the actual length of data. First call to this method + // should be preceeded by a call to ReadPlpLength or ReadDataLength. + // Returns the actual bytes read. + // NOTE: This method must be retriable WITHOUT replaying a snapshot + // Every time you call this method increment the offset and decrease len by the value of totalBytesRead + internal bool TryReadPlpBytes(ref byte[] buff, int offset, int len, out int totalBytesRead) + { + int bytesRead; + int bytesLeft; + byte[] newbuf; + + if (_longlen == 0) + { + Debug.Assert(_longlenleft == 0); + if (buff == null) + { + buff = Array.Empty(); + } + + AssertValidState(); + totalBytesRead = 0; + return true; // No data + } + + Debug.Assert(_longlen != TdsEnums.SQL_PLP_NULL, "Out of sync plp read request"); + Debug.Assert((buff == null && offset == 0) || (buff.Length >= offset + len), "Invalid length sent to ReadPlpBytes()!"); + + bytesLeft = len; + + // If total length is known up front, allocate the whole buffer in one shot instead of realloc'ing and copying over each time + if (buff == null && _longlen != TdsEnums.SQL_PLP_UNKNOWNLEN) + { + if (_snapshot != null) + { + // if there is a snapshot and it contains a stored plp buffer take it + // and try to use it if it is the right length + buff = _snapshot._plpBuffer; + _snapshot._plpBuffer = null; + } + + if ((ulong)(buff?.Length ?? 0) != _longlen) + { + // if the buffer is null or the wrong length create one to use + buff = new byte[(Math.Min((int)_longlen, len))]; + } + } + + if (_longlenleft == 0) + { + if (!TryReadPlpLength(false, out _)) + { + totalBytesRead = 0; + return false; + } + if (_longlenleft == 0) + { // Data read complete + totalBytesRead = 0; + return true; + } + } + + if (buff == null) + { + buff = new byte[_longlenleft]; + } + + totalBytesRead = 0; + + while (bytesLeft > 0) + { + int bytesToRead = (int)Math.Min(_longlenleft, (ulong)bytesLeft); + if (buff.Length < (offset + bytesToRead)) + { + // Grow the array + newbuf = new byte[offset + bytesToRead]; + Buffer.BlockCopy(buff, 0, newbuf, 0, offset); + buff = newbuf; + } + + bool result = TryReadByteArray(buff.AsSpan(offset), bytesToRead, out bytesRead); + Debug.Assert(bytesRead <= bytesLeft, "Read more bytes than we needed"); + Debug.Assert((ulong)bytesRead <= _longlenleft, "Read more bytes than is available"); + + bytesLeft -= bytesRead; + offset += bytesRead; + totalBytesRead += bytesRead; + _longlenleft -= (ulong)bytesRead; + if (!result) + { + if (_snapshot != null) + { + // a partial read has happened so store the target buffer in the snapshot + // so it can be re-used when another packet arrives and we read again + _snapshot._plpBuffer = buff; + } + return false; + } + + if (_longlenleft == 0) + { + // Read the next chunk or cleanup state if hit the end + if (!TryReadPlpLength(false, out _)) + { + if (_snapshot != null) + { + // a partial read has happened so store the target buffer in the snapshot + // so it can be re-used when another packet arrives and we read again + _snapshot._plpBuffer = buff; + } + return false; + } + } + + AssertValidState(); + + // Catch the point where we read the entire plp data stream and clean up state + if (_longlenleft == 0) // Data read complete + break; + } + return true; + } + /* // leave this in. comes handy if you have to do Console.WriteLine style debugging ;) From 674496fe993095ce0d4a79a94c4a3d924140600e Mon Sep 17 00:00:00 2001 From: panoskj Date: Mon, 25 Sep 2023 17:30:50 +0300 Subject: [PATCH 08/10] 5. Code formatting. --- .../src/Microsoft/Data/SqlClient/TdsParserStateObject.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 f2b084e7e4..f2852b042d 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs @@ -2013,7 +2013,7 @@ internal void Clear(TdsParserStateObject stateObj, bool trackStack = true) { stateObj._lastStack = null; } -#endif +#endif } internal void Restore(TdsParserStateObject stateObj) From 962f8c8eef3c484b77246f2baad826bf76e27065 Mon Sep 17 00:00:00 2001 From: panoskj Date: Sun, 24 Sep 2023 04:13:53 +0300 Subject: [PATCH 09/10] 6. Optional refactoring that reduces #if directives in merged TdsParserStateObject. --- .../src/Microsoft/Data/SqlClient/TdsParser.cs | 11 ++++++++++ .../Data/SqlClient/TdsParserStateObject.cs | 20 ------------------- 2 files changed, 11 insertions(+), 20 deletions(-) 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 bde99cda5e..f66ce6cfd3 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 @@ -40,6 +40,17 @@ internal struct SNIErrorDetails // and surfacing objects to the user. internal sealed partial class TdsParser { + internal struct ReliabilitySection + { + /// + /// This is a no-op in netcore version. Only needed for merging with netfx codebase. + /// + [Conditional("NETFRAMEWORK")] + internal static void Assert(string message) + { + } + } + private static int _objectTypeCount; // EventSource counter private readonly SqlClientLogger _logger = new SqlClientLogger(); 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 f2852b042d..cd8d30bf28 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs @@ -963,9 +963,7 @@ internal bool TryProcessHeader() // NOTE: This method (and all it calls) should be retryable without replaying a snapshot internal bool TryPrepareBuffer() { -#if NETFRAMEWORK TdsParser.ReliabilitySection.Assert("unreliable call to ReadBuffer"); // you need to setup for a thread abort somewhere before you call this method -#endif Debug.Assert(_inBuff != null, "packet buffer should not be null!"); // Header spans packets, or we haven't read the header yet - process header @@ -1146,9 +1144,7 @@ public bool TryReadByteArray(Span buff, int len) // Every time you call this method increment the offset and decrease len by the value of totalRead public bool TryReadByteArray(Span buff, int len, out int totalRead) { -#if NETFRAMEWORK TdsParser.ReliabilitySection.Assert("unreliable call to ReadByteArray"); // you need to setup for a thread abort somewhere before you call this method -#endif totalRead = 0; #if DEBUG @@ -1207,9 +1203,7 @@ public bool TryReadByteArray(Span buff, int len, out int totalRead) // before the byte is returned. internal bool TryReadByte(out byte value) { -#if NETFRAMEWORK TdsParser.ReliabilitySection.Assert("unreliable call to ReadByte"); // you need to setup for a thread abort somewhere before you call this method -#endif Debug.Assert(_inBytesUsed >= 0 && _inBytesUsed <= _inBytesRead, "ERROR - TDSParser: _inBytesUsed < 0 or _inBytesUsed > _inBytesRead"); value = 0; @@ -1313,9 +1307,7 @@ internal bool TryReadInt16(out short value) 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)) @@ -1345,9 +1337,7 @@ internal bool TryReadInt32(out int value) // 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()) @@ -1426,9 +1416,7 @@ internal bool TryReadUInt16(out ushort value) // 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()) @@ -1477,9 +1465,7 @@ internal bool TryReadUInt32(out uint value) 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)) { @@ -1513,9 +1499,7 @@ internal bool TryReadSingle(out float value) 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)) { @@ -1549,9 +1533,7 @@ internal bool TryReadDouble(out double value) 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; @@ -1592,9 +1574,7 @@ internal bool TryReadString(int length, out string value) 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) From 9b98c8edc714e34a5b896b5a4d226831d3109bf5 Mon Sep 17 00:00:00 2001 From: panoskj Date: Sun, 24 Sep 2023 22:48:53 +0300 Subject: [PATCH 10/10] 6. Optional refactoring that reduces #if directives in merged TdsParserStateObject also benefits merged SqlInternalTransaction. --- .../src/Microsoft/Data/SqlClient/SqlInternalTransaction.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlInternalTransaction.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlInternalTransaction.cs index 114363f5a6..03b144a33e 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlInternalTransaction.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlInternalTransaction.cs @@ -203,9 +203,7 @@ internal void CloseFromConnection() } finally { -#if NETFRAMEWORK TdsParser.ReliabilitySection.Assert("unreliable call to CloseFromConnection"); // you need to setup for a thread abort somewhere before you call this method -#endif if (processFinallyBlock) { // Always ensure we're zombied; 2005 will send an EnvChange that