private async Task<int> ReadAsyncCore(byte[] buffer, int offset, int count, CancellationToken cancellationToken, bool useAsync)
{
// read <= count bytes from the input stream, transforming as we go.
// Basic idea: first we deliver any bytes we already have in the
// _OutputBuffer, because we know they're good. Then, if asked to deliver
// more bytes, we read & transform a block at a time until either there are
// no bytes ready or we've delivered enough.
int bytesToDeliver = count;
int currentOutputIndex = offset;
if (_outputBufferIndex != 0)
{
// we have some already-transformed bytes in the output buffer
if (_outputBufferIndex <= count)
{
Buffer.BlockCopy(_outputBuffer, 0, buffer, offset, _outputBufferIndex);
bytesToDeliver -= _outputBufferIndex;
currentOutputIndex += _outputBufferIndex;
_outputBufferIndex = 0;
}
else
{
Buffer.BlockCopy(_outputBuffer, 0, buffer, offset, count);
Buffer.BlockCopy(_outputBuffer, count, _outputBuffer, 0, _outputBufferIndex - count);
_outputBufferIndex -= count;
return (count);
}
}
// _finalBlockTransformed == true implies we're at the end of the input stream
// if we got through the previous if block then _OutputBufferIndex = 0, meaning
// we have no more transformed bytes to give
// so return count-bytesToDeliver, the amount we were able to hand back
// eventually, we'll just always return 0 here because there's no more to read
if (_finalBlockTransformed)
{
return (count - bytesToDeliver);
}
// ok, now loop until we've delivered enough or there's nothing available
int amountRead = 0;
int numOutputBytes;
// OK, see first if it's a multi-block transform and we can speed up things
if (bytesToDeliver > _outputBlockSize)
{
if (_transform.CanTransformMultipleBlocks)
{
int BlocksToProcess = bytesToDeliver / _outputBlockSize;
int numWholeBlocksInBytes = BlocksToProcess * _inputBlockSize;
byte[] tempInputBuffer = new byte[numWholeBlocksInBytes];
// get first the block already read
Buffer.BlockCopy(_inputBuffer, 0, tempInputBuffer, 0, _inputBufferIndex);
amountRead = _inputBufferIndex;
amountRead += useAsync ?
await _stream.ReadAsync(tempInputBuffer, _inputBufferIndex, numWholeBlocksInBytes - _inputBufferIndex, cancellationToken) :
_stream.Read(tempInputBuffer, _inputBufferIndex, numWholeBlocksInBytes - _inputBufferIndex);
_inputBufferIndex = 0;
if (amountRead <= _inputBlockSize)
{
_inputBuffer = tempInputBuffer;
_inputBufferIndex = amountRead;
goto slow;
}
// Make amountRead an integral multiple of _InputBlockSize
int numWholeReadBlocksInBytes = (amountRead / _inputBlockSize) * _inputBlockSize;
int numIgnoredBytes = amountRead - numWholeReadBlocksInBytes;
if (numIgnoredBytes != 0)
{
_inputBufferIndex = numIgnoredBytes;
Buffer.BlockCopy(tempInputBuffer, numWholeReadBlocksInBytes, _inputBuffer, 0, numIgnoredBytes);
}
byte[] tempOutputBuffer = new byte[(numWholeReadBlocksInBytes / _inputBlockSize) * _outputBlockSize];
numOutputBytes = _transform.TransformBlock(tempInputBuffer, 0, numWholeReadBlocksInBytes, tempOutputBuffer, 0);
Buffer.BlockCopy(tempOutputBuffer, 0, buffer, currentOutputIndex, numOutputBytes);
// Now, tempInputBuffer and tempOutputBuffer are no more needed, so zeroize them to protect plain text
Array.Clear(tempInputBuffer, 0, tempInputBuffer.Length);
Array.Clear(tempOutputBuffer, 0, tempOutputBuffer.Length);
bytesToDeliver -= numOutputBytes;
currentOutputIndex += numOutputBytes;
}
}
slow:
// try to fill _InputBuffer so we have something to transform
while (bytesToDeliver > 0)
{
while (_inputBufferIndex < _inputBlockSize)
{
amountRead = useAsync ?
await _stream.ReadAsync(_inputBuffer, _inputBufferIndex, _inputBlockSize - _inputBufferIndex, cancellationToken) :
_stream.Read(_inputBuffer, _inputBufferIndex, _inputBlockSize - _inputBufferIndex);
// first, check to see if we're at the end of the input stream
if (amountRead == 0) goto ProcessFinalBlock;
_inputBufferIndex += amountRead;
}
numOutputBytes = _transform.TransformBlock(_inputBuffer, 0, _inputBlockSize, _outputBuffer, 0);
_inputBufferIndex = 0;
if (bytesToDeliver >= numOutputBytes)
{
Buffer.BlockCopy(_outputBuffer, 0, buffer, currentOutputIndex, numOutputBytes);
currentOutputIndex += numOutputBytes;
bytesToDeliver -= numOutputBytes;
}
else
{
Buffer.BlockCopy(_outputBuffer, 0, buffer, currentOutputIndex, bytesToDeliver);
_outputBufferIndex = numOutputBytes - bytesToDeliver;
Buffer.BlockCopy(_outputBuffer, bytesToDeliver, _outputBuffer, 0, _outputBufferIndex);
return count;
}
}
return count;
ProcessFinalBlock:
// if so, then call TransformFinalBlock to get whatever is left
byte[] finalBytes = _transform.TransformFinalBlock(_inputBuffer, 0, _inputBufferIndex);
// now, since _OutputBufferIndex must be 0 if we're in the while loop at this point,
// reset it to be what we just got back
_outputBuffer = finalBytes;
_outputBufferIndex = finalBytes.Length;
// set the fact that we've transformed the final block
_finalBlockTransformed = true;
// now, return either everything we just got or just what's asked for, whichever is smaller
if (bytesToDeliver < _outputBufferIndex)
{
Buffer.BlockCopy(_outputBuffer, 0, buffer, currentOutputIndex, bytesToDeliver);
_outputBufferIndex -= bytesToDeliver;
Buffer.BlockCopy(_outputBuffer, bytesToDeliver, _outputBuffer, 0, _outputBufferIndex);
return (count);
}
else
{
Buffer.BlockCopy(_outputBuffer, 0, buffer, currentOutputIndex, _outputBufferIndex);
bytesToDeliver -= _outputBufferIndex;
_outputBufferIndex = 0;
return (count - bytesToDeliver);
}
}