Skip to content

Commit

Permalink
Merging "Buffer read" methods of TdsParserStateObject, port dotnet#285
Browse files Browse the repository at this point in the history
…to netfx.
  • Loading branch information
panoskj committed Sep 24, 2023
1 parent 722148e commit 6c1dae1
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 234 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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<byte>();
}

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 //
/////////////////////////////////////////
Expand Down Expand Up @@ -2445,8 +2317,6 @@ partial void SetStackInternal(string value)
private PacketData _snapshotInBuffList;
private PacketData _sparePacket;

internal byte[] _plpBuffer;

private int _snapshotInBuffCount;

#if DEBUG
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -372,110 +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 = new byte[0];
}

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)
{
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)
{
return false;
}

if (_longlenleft == 0)
{
// Read the next chunk or cleanup state if hit the end
if (!TryReadPlpLength(false, out _))
{
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 //
/////////////////////////////////////////
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1762,6 +1762,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<byte>();
}

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 ;)
Expand Down Expand Up @@ -1929,6 +2052,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;
Expand Down

0 comments on commit 6c1dae1

Please sign in to comment.