private async Task WriteAsyncCore(byte[] buffer, int offset, int count, CancellationToken cancellationToken, bool useAsync)
{
// write <= count bytes to the output stream, transforming as we go.
// Basic idea: using bytes in the _InputBuffer first, make whole blocks,
// transform them, and write them out. Cache any remaining bytes in the _InputBuffer.
int bytesToWrite = count;
int currentInputIndex = offset;
// if we have some bytes in the _InputBuffer, we have to deal with those first,
// so let's try to make an entire block out of it
if (_inputBufferIndex > 0)
{
if (count >= _inputBlockSize - _inputBufferIndex)
{
// we have enough to transform at least a block, so fill the input block
Buffer.BlockCopy(buffer, offset, _inputBuffer, _inputBufferIndex, _inputBlockSize - _inputBufferIndex);
currentInputIndex += (_inputBlockSize - _inputBufferIndex);
bytesToWrite -= (_inputBlockSize - _inputBufferIndex);
_inputBufferIndex = _inputBlockSize;
// Transform the block and write it out
}
else
{
// not enough to transform a block, so just copy the bytes into the _InputBuffer
// and return
Buffer.BlockCopy(buffer, offset, _inputBuffer, _inputBufferIndex, count);
_inputBufferIndex += count;
return;
}
}
// If the OutputBuffer has anything in it, write it out
if (_outputBufferIndex > 0)
{
if (useAsync)
await _stream.WriteAsync(_outputBuffer, 0, _outputBufferIndex, cancellationToken);
else
_stream.Write(_outputBuffer, 0, _outputBufferIndex);
_outputBufferIndex = 0;
}
// At this point, either the _InputBuffer is full, empty, or we've already returned.
// If full, let's process it -- we now know the _OutputBuffer is empty
int numOutputBytes;
if (_inputBufferIndex == _inputBlockSize)
{
numOutputBytes = _transform.TransformBlock(_inputBuffer, 0, _inputBlockSize, _outputBuffer, 0);
// write out the bytes we just got
if (useAsync)
await _stream.WriteAsync(_outputBuffer, 0, numOutputBytes, cancellationToken);
else
_stream.Write(_outputBuffer, 0, numOutputBytes);
// reset the _InputBuffer
_inputBufferIndex = 0;
}
while (bytesToWrite > 0)
{
if (bytesToWrite >= _inputBlockSize)
{
// We have at least an entire block's worth to transform
// If the transform will handle multiple blocks at once, do that
if (_transform.CanTransformMultipleBlocks)
{
int numWholeBlocks = bytesToWrite / _inputBlockSize;
int numWholeBlocksInBytes = numWholeBlocks * _inputBlockSize;
byte[] _tempOutputBuffer = new byte[numWholeBlocks * _outputBlockSize];
numOutputBytes = _transform.TransformBlock(buffer, currentInputIndex, numWholeBlocksInBytes, _tempOutputBuffer, 0);
if (useAsync)
await _stream.WriteAsync(_tempOutputBuffer, 0, numOutputBytes, cancellationToken);
else
_stream.Write(_tempOutputBuffer, 0, numOutputBytes);
currentInputIndex += numWholeBlocksInBytes;
bytesToWrite -= numWholeBlocksInBytes;
}
else
{
// do it the slow way
numOutputBytes = _transform.TransformBlock(buffer, currentInputIndex, _inputBlockSize, _outputBuffer, 0);
if (useAsync)
await _stream.WriteAsync(_outputBuffer, 0, numOutputBytes, cancellationToken);
else
_stream.Write(_outputBuffer, 0, numOutputBytes);
currentInputIndex += _inputBlockSize;
bytesToWrite -= _inputBlockSize;
}
}
else
{
// In this case, we don't have an entire block's worth left, so store it up in the
// input buffer, which by now must be empty.
Buffer.BlockCopy(buffer, currentInputIndex, _inputBuffer, 0, bytesToWrite);
_inputBufferIndex += bytesToWrite;
return;
}
}
return;
}