Skip to content

Commit

Permalink
4. Merging "Buffer read" methods of TdsParserStateObject, port dotnet…
Browse files Browse the repository at this point in the history
…#285 to netfx.
  • Loading branch information
panoskj committed Sep 25, 2023
1 parent 342e411 commit 74f5df3
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 256 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
Original file line number Diff line number Diff line change
Expand Up @@ -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<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
Original file line number Diff line number Diff line change
Expand Up @@ -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<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

0 comments on commit 74f5df3

Please sign in to comment.