public override int Read(byte[] buffer, int offset, int count)
{
#region Sanity checks
if (buffer == null) throw new ArgumentNullException(nameof(buffer));
if (offset < 0 || offset > buffer.Length) throw new ArgumentOutOfRangeException(nameof(offset));
if (count + offset > buffer.Length) throw new ArgumentOutOfRangeException(nameof(count));
#endregion
// Bytes copied to target buffer so far
int bytesCopied = 0;
// Loop until the request number of bytes have been returned
while (bytesCopied != count)
{
if (IsDisposed) throw new ObjectDisposedException("CircularBufferStream");
lock (_lock)
{
if (_relayedException != null) throw _relayedException;
// All data read and no new data coming
if (_doneWriting && _dataLength == 0) break;
}
// Block while buffer is empty
_dataAvailable.WaitOne();
if (IsDisposed) throw new ObjectDisposedException("CircularBufferStream");
lock (_lock)
{
// The index of the last byte currently stored in the buffer plus one
int dataEnd = (_dataStart + _dataLength) % _buffer.Length;
// Determine how many bytes can be read in one go
int contigousDataBytes;
if (_dataLength == 0) contigousDataBytes = 0; // Empty
else if (_dataLength == _buffer.Length) contigousDataBytes = _buffer.Length - _dataStart; // Full, potentially wrap around, partial read
else if (_dataStart < dataEnd) contigousDataBytes = dataEnd - _dataStart; // No wrap around, read all
else contigousDataBytes = _buffer.Length - _dataStart; // Wrap around, partial read
Debug.Assert(contigousDataBytes <= _dataLength, "Contigous data length can not exceed total data length");
int bytesToCopy = Math.Min(contigousDataBytes, count - bytesCopied);
Buffer.BlockCopy(_buffer, _dataStart, buffer, offset + bytesCopied, bytesToCopy);
// Update counters
bytesCopied += bytesToCopy;
_positionRead += bytesToCopy;
_dataLength -= bytesToCopy;
_dataStart += bytesToCopy;
// Roll over start pointer
_dataStart %= _buffer.Length;
// Start blocking when buffer becomes empty
if (_dataLength == 0) _dataAvailable.Reset();
// Stop blocking when space becomes available
if (_dataLength < _buffer.Length) _spaceAvailable.Set();
}
}
return bytesCopied;
}