System.Security.Cryptography.CryptoStream.ReadAsyncCore C# (CSharp) Method

ReadAsyncCore() private method

private ReadAsyncCore ( byte buffer, int offset, int count, CancellationToken cancellationToken, bool useAsync ) : Task
buffer byte
offset int
count int
cancellationToken CancellationToken
useAsync bool
return Task
        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);
            }
        }