Skip to content

Commit

Permalink
Add array rental capability in TryReadPlpUnicodeChars (#1866)
Browse files Browse the repository at this point in the history
  • Loading branch information
Wraith2 committed Feb 1, 2023
1 parent 1d7e01d commit e4bf520
Showing 1 changed file with 47 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5684,8 +5684,8 @@ private bool TryReadSqlStringValue(SqlBuffer value, byte type, int length, Encod
if (isPlp)
{
char[] cc = null;

if (!TryReadPlpUnicodeChars(ref cc, 0, length >> 1, stateObj, out length))
bool buffIsRented = false;
if (!TryReadPlpUnicodeChars(ref cc, 0, length >> 1, stateObj, out length, supportRentedBuff: true, rentedBuff: ref buffIsRented))
{
return false;
}
Expand All @@ -5697,6 +5697,16 @@ private bool TryReadSqlStringValue(SqlBuffer value, byte type, int length, Encod
{
s = "";
}
if (buffIsRented)
{
// do not use clearArray:true on the rented array because it can be massively larger
// than the space we've used and we would incur performance clearing memory that
// we haven't used and can't leak out information.
// clear only the length that we know we have used.
cc.AsSpan(0, length).Clear();
ArrayPool<char>.Shared.Return(cc, clearArray: false);
cc = null;
}
}
else
{
Expand Down Expand Up @@ -12512,8 +12522,9 @@ private bool TryReadPlpUnicodeCharsChunk(char[] buff, int offst, int len, TdsPar
internal int ReadPlpUnicodeChars(ref char[] buff, int offst, int len, TdsParserStateObject stateObj)
{
int charsRead;
bool rentedBuff = false;
Debug.Assert(stateObj._syncOverAsync, "Should not attempt pends in a synchronous call");
bool result = TryReadPlpUnicodeChars(ref buff, offst, len, stateObj, out charsRead);
bool result = TryReadPlpUnicodeChars(ref buff, offst, len, stateObj, out charsRead, supportRentedBuff: false, ref rentedBuff);
if (!result)
{
throw SQL.SynchronousCallMayNotPend();
Expand All @@ -12525,7 +12536,7 @@ internal int ReadPlpUnicodeChars(ref char[] buff, int offst, int len, TdsParserS
// 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 chars read.
internal bool TryReadPlpUnicodeChars(ref char[] buff, int offst, int len, TdsParserStateObject stateObj, out int totalCharsRead)
internal bool TryReadPlpUnicodeChars(ref char[] buff, int offst, int len, TdsParserStateObject stateObj, out int totalCharsRead, bool supportRentedBuff, ref bool rentedBuff)
{
int charsRead = 0;
int charsLeft = 0;
Expand All @@ -12538,16 +12549,26 @@ internal bool TryReadPlpUnicodeChars(ref char[] buff, int offst, int len, TdsPar
return true; // No data
}

Debug.Assert(((ulong)stateObj._longlen != TdsEnums.SQL_PLP_NULL),
"Out of sync plp read request");
Debug.Assert(((ulong)stateObj._longlen != TdsEnums.SQL_PLP_NULL),"Out of sync plp read request");

Debug.Assert((buff == null && offst == 0) || (buff.Length >= offst + len), "Invalid length sent to ReadPlpUnicodeChars()!");
charsLeft = 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 && stateObj._longlen != TdsEnums.SQL_PLP_UNKNOWNLEN)
// If total length is known up front, the length isn't specified as unknown
// and the caller doesn't pass int.max/2 indicating that it doesn't know the length
// allocate the whole buffer in one shot instead of realloc'ing and copying over each time
if (buff == null && stateObj._longlen != TdsEnums.SQL_PLP_UNKNOWNLEN && len < (int.MaxValue >> 1))
{
buff = new char[(int)Math.Min((int)stateObj._longlen, len)];
if (supportRentedBuff && len < 1073741824) // 1 Gib
{
buff = ArrayPool<char>.Shared.Rent((int)Math.Min((int)stateObj._longlen, len));
rentedBuff = true;
}
else
{
buff = new char[(int)Math.Min((int)stateObj._longlen, len)];
rentedBuff = false;
}
}

if (stateObj._longlenleft == 0)
Expand All @@ -12572,11 +12593,26 @@ internal bool TryReadPlpUnicodeChars(ref char[] buff, int offst, int len, TdsPar
charsRead = (int)Math.Min((stateObj._longlenleft + 1) >> 1, (ulong)charsLeft);
if ((buff == null) || (buff.Length < (offst + charsRead)))
{
// Grow the array
newbuf = new char[offst + charsRead];
bool returnRentedBufferAfterCopy = rentedBuff;
if (supportRentedBuff && (offst + charsRead) < 1073741824) // 1 Gib
{
newbuf = ArrayPool<char>.Shared.Rent(offst + charsRead);
rentedBuff = true;
}
else
{
newbuf = new char[offst + charsRead];
rentedBuff = false;
}

if (buff != null)
{
Buffer.BlockCopy(buff, 0, newbuf, 0, offst * 2);
if (returnRentedBufferAfterCopy)
{
buff.AsSpan(0, offst).Clear();
ArrayPool<char>.Shared.Return(buff, clearArray: false);
}
}
buff = newbuf;
}
Expand Down

0 comments on commit e4bf520

Please sign in to comment.