public override long Seek(long offset, SeekOrigin origin)
{
EnsureNotClosed();
EnsureCanSeek();
// If we have bytes in the write buffer, flush them out, seek and be done.
if (_writePos > 0)
{
// We should be only writing the buffer and not flushing,
// but the previous version did flush and we stick to it for back-compat reasons.
FlushWrite();
return _stream.Seek(offset, origin);
}
// The buffer is either empty or we have a buffered read.
if (_readLen - _readPos > 0 && origin == SeekOrigin.Current)
{
// If we have bytes in the read buffer, adjust the seek offset to account for the resulting difference
// between this stream's position and the underlying stream's position.
offset -= (_readLen - _readPos);
}
long oldPos = Position;
Debug.Assert(oldPos == _stream.Position + (_readPos - _readLen));
long newPos = _stream.Seek(offset, origin);
// If the seek destination is still within the data currently in the buffer, we want to keep the buffer data and continue using it.
// Otherwise we will throw away the buffer. This can only happen on read, as we flushed write data above.
// The offset of the new/updated seek pointer within _buffer:
_readPos = (int)(newPos - (oldPos - _readPos));
// If the offset of the updated seek pointer in the buffer is still legal, then we can keep using the buffer:
if (0 <= _readPos && _readPos < _readLen)
{
// Adjust the seek pointer of the underlying stream to reflect the amount of useful bytes in the read buffer:
_stream.Seek(_readLen - _readPos, SeekOrigin.Current);
}
else
{ // The offset of the updated seek pointer is not a legal offset. Loose the buffer.
_readPos = _readLen = 0;
}
Debug.Assert(newPos == Position, "newPos (=" + newPos + ") == Position (=" + Position + ")");
return newPos;
}