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 7b700a1edd..49327a4cdd 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 @@ -12501,18 +12501,18 @@ private bool TryReadPlpUnicodeCharsChunk(char[] buff, int offst, int len, TdsPar return true; } - charsRead = len; + int charsToRead = len; // stateObj._longlenleft is in bytes - if ((stateObj._longlenleft >> 1) < (ulong)len) - charsRead = (int)(stateObj._longlenleft >> 1); + if ((stateObj._longlenleft / 2) < (ulong)len) + { + charsToRead = (int)(stateObj._longlenleft >> 1); + } - for (int ii = 0; ii < charsRead; ii++) + if (!stateObj.TryReadChars(buff, offst, charsToRead, out charsRead)) { - if (!stateObj.TryReadChar(out buff[offst + ii])) - { - return false; - } + charsRead = 0; + return false; } stateObj._longlenleft -= ((ulong)charsRead << 1); diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs index 1565a618a2..bc7cd362a8 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs @@ -3,12 +3,9 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; using System.Diagnostics; -using System.Linq; using System.Runtime.InteropServices; using System.Security; -using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.Data.Common; @@ -487,6 +484,7 @@ internal bool TryReadChar(out char value) AssertValidState(); value = (char)((buffer[1] << 8) + buffer[0]); + return true; } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs index 70673ef343..a15da77757 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -13528,7 +13528,6 @@ internal void WriteParameterVarLen(MetaType type, int size, bool isNull, TdsPars // Returns the actual chars read private bool TryReadPlpUnicodeCharsChunk(char[] buff, int offst, int len, TdsParserStateObject stateObj, out int charsRead) { - Debug.Assert((buff == null && len == 0) || (buff.Length >= offst + len), "Invalid length sent to ReadPlpUnicodeChars()!"); Debug.Assert((stateObj._longlen != 0) && (stateObj._longlen != TdsEnums.SQL_PLP_NULL), "Out of sync plp read request"); @@ -13539,18 +13538,18 @@ private bool TryReadPlpUnicodeCharsChunk(char[] buff, int offst, int len, TdsPar return true; } - charsRead = len; + int charsToRead = len; // stateObj._longlenleft is in bytes - if ((stateObj._longlenleft >> 1) < (ulong)len) - charsRead = (int)(stateObj._longlenleft >> 1); + if ((stateObj._longlenleft / 2) < (ulong)len) + { + charsToRead = (int)(stateObj._longlenleft >> 1); + } - for (int ii = 0; ii < charsRead; ii++) + if (!stateObj.TryReadChars(buff, offst, charsToRead, out charsRead)) { - if (!stateObj.TryReadChar(out buff[offst + ii])) - { - return false; - } + charsRead = 0; + return false; } stateObj._longlenleft -= ((ulong)charsRead << 1); 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 86f51fba2a..62c02e4db7 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/TdsParserStateObject.cs @@ -436,6 +436,51 @@ internal bool TryStartNewRow(bool isNullCompressed, int nullBitmapColumnsCount = return true; } + internal bool TryReadChars(char[] chars, int charsOffset, int charsCount, out int charsCopied) + { + charsCopied = 0; + while (charsCopied < charsCount) + { + // check if the current buffer contains some bytes we need to copy and copy them + // in a block + int bytesToRead = Math.Min( + (charsCount - charsCopied) * 2, + unchecked((_inBytesRead - _inBytesUsed) & (int)0xFFFFFFFE) // it the result is odd take off the 0 to make it even + ); + if (bytesToRead > 0) + { + Buffer.BlockCopy( + _inBuff, + _inBytesUsed, + chars, + (charsOffset + charsCopied) * 2, // offset in bytes, + bytesToRead + ); + charsCopied += (bytesToRead / 2); + _inBytesUsed += bytesToRead; + _inBytesPacket -= bytesToRead; + } + + // if the number of chars requested is lower than the number copied then we need + // to request a new packet, use TryReadChar() to do this then loop back to see + // if we can copy another bulk of chars from the new buffer + + if (charsCopied < charsCount) + { + bool result = TryReadChar(out chars[charsOffset + charsCopied]); + if (result) + { + charsCopied += 1; + } + else + { + return false; + } + } + } + return true; + } + internal bool IsRowTokenReady() { // Removing one byte since TryReadByteArray\TryReadByte will aggressively read the next packet if there is no data left - so we need to ensure there is a spare byte