private async Task WriteToUnderlyingStreamAsync(byte[] array, int offset, int count,
CancellationToken cancellationToken,
Task semaphoreLockTask)
{
Debug.Assert(array != null);
Debug.Assert(offset >= 0);
Debug.Assert(count >= 0);
Debug.Assert(array.Length - offset >= count);
Debug.Assert(_stream != null);
Debug.Assert(_stream.CanWrite);
Debug.Assert(_bufferSize > 0);
Debug.Assert(semaphoreLockTask != null);
// See the LARGE COMMENT in Write(..) for the explanation of the write buffer algorithm.
await semaphoreLockTask.ConfigureAwait(false);
try
{
// The buffer might have been changed by another async task while we were waiting on the semaphore.
// However, note that if we recalculate the sync completion condition to TRUE, then useBuffer will also be TRUE.
if (_writePos == 0)
ClearReadBufferBeforeWrite();
int totalUserBytes;
bool useBuffer;
checked
{ // We do not expect buffer sizes big enough for an overflow, but if it happens, lets fail early:
totalUserBytes = _writePos + count;
useBuffer = (totalUserBytes + count < (_bufferSize + _bufferSize));
}
if (useBuffer)
{
WriteToBuffer(array, ref offset, ref count);
if (_writePos < _bufferSize)
{
Debug.Assert(count == 0);
return;
}
Debug.Assert(count >= 0);
Debug.Assert(_writePos == _bufferSize);
Debug.Assert(_buffer != null);
await _stream.WriteAsync(_buffer, 0, _writePos, cancellationToken).ConfigureAwait(false);
_writePos = 0;
WriteToBuffer(array, ref offset, ref count);
Debug.Assert(count == 0);
Debug.Assert(_writePos < _bufferSize);
}
else
{ // if (!useBuffer)
// Write out the buffer if necessary.
if (_writePos > 0)
{
Debug.Assert(_buffer != null);
Debug.Assert(totalUserBytes >= _bufferSize);
// Try avoiding extra write to underlying stream by combining previously buffered data with current user data:
if (totalUserBytes <= (_bufferSize + _bufferSize) && totalUserBytes <= MaxShadowBufferSize)
{
EnsureShadowBufferAllocated();
Buffer.BlockCopy(array, offset, _buffer, _writePos, count);
await _stream.WriteAsync(_buffer, 0, totalUserBytes, cancellationToken).ConfigureAwait(false);
_writePos = 0;
return;
}
await _stream.WriteAsync(_buffer, 0, _writePos, cancellationToken).ConfigureAwait(false);
_writePos = 0;
}
// Write out user data.
await _stream.WriteAsync(array, offset, count, cancellationToken).ConfigureAwait(false);
}
}
finally
{
SemaphoreSlim sem = LazyEnsureAsyncActiveSemaphoreInitialized();
sem.Release();
}
}