File: BufferedReadStream.cs
Web Access
Project: ..\..\..\src\Build\Microsoft.Build.csproj (Microsoft.Build)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.IO;
 
#nullable disable
 
namespace Microsoft.Build.BackEnd
{
    internal class BufferedReadStream : Stream
    {
        private const int BUFFER_SIZE = 1024;
        private Stream _innerStream;
        private byte[] _buffer;
 
        // The number of bytes in the buffer that have been read from the underlying stream but not read by consumers of this stream
        private int _currentlyBufferedByteCount;
        private int _currentIndexInBuffer;
 
        public BufferedReadStream(Stream innerStream)
        {
            _innerStream = innerStream;
            _buffer = new byte[BUFFER_SIZE];
 
            _currentlyBufferedByteCount = 0;
        }
 
        public override bool CanRead { get { return _innerStream.CanRead; } }
 
        public override bool CanSeek { get { return false; } }
 
        public override bool CanWrite { get { return _innerStream.CanWrite; } }
 
        public override long Length { get { return _innerStream.Length; } }
 
        public override long Position
        {
            get { throw new NotSupportedException(); }
            set { throw new NotSupportedException(); }
        }
 
        public override void Flush()
        {
            _innerStream.Flush();
        }
 
        public override int ReadByte()
        {
            if (_currentlyBufferedByteCount > 0)
            {
                int ret = _buffer[_currentIndexInBuffer];
                _currentIndexInBuffer++;
                _currentlyBufferedByteCount--;
                return ret;
            }
            else
            {
                // Let the base class handle it, which will end up calling the Read() method
                return base.ReadByte();
            }
        }
 
        public override int Read(byte[] buffer, int offset, int count)
        {
            if (count > BUFFER_SIZE)
            {
                // Trying to read more data than the buffer can hold
                int alreadyCopied = 0;
                if (_currentlyBufferedByteCount > 0)
                {
                    Array.Copy(_buffer, _currentIndexInBuffer, buffer, offset, _currentlyBufferedByteCount);
                    alreadyCopied = _currentlyBufferedByteCount;
                    _currentIndexInBuffer = 0;
                    _currentlyBufferedByteCount = 0;
                }
                int innerReadCount = _innerStream.Read(buffer, offset + alreadyCopied, count - alreadyCopied);
                return innerReadCount + alreadyCopied;
            }
            else if (count <= _currentlyBufferedByteCount)
            {
                // Enough data buffered to satisfy read request
                Array.Copy(_buffer, _currentIndexInBuffer, buffer, offset, count);
                _currentIndexInBuffer += count;
                _currentlyBufferedByteCount -= count;
                return count;
            }
            else
            {
                // Need to read more data
                int alreadyCopied = 0;
                if (_currentlyBufferedByteCount > 0)
                {
                    Array.Copy(_buffer, _currentIndexInBuffer, buffer, offset, _currentlyBufferedByteCount);
                    alreadyCopied = _currentlyBufferedByteCount;
                    _currentIndexInBuffer = 0;
                    _currentlyBufferedByteCount = 0;
                }
 
                int innerReadCount = _innerStream.Read(_buffer, 0, BUFFER_SIZE);
                _currentIndexInBuffer = 0;
                _currentlyBufferedByteCount = innerReadCount;
 
                int remainingCopyCount;
 
                if (alreadyCopied + innerReadCount >= count)
                {
                    remainingCopyCount = count - alreadyCopied;
                }
                else
                {
                    remainingCopyCount = innerReadCount;
                }
 
                Array.Copy(_buffer, 0, buffer, offset + alreadyCopied, remainingCopyCount);
                _currentIndexInBuffer += remainingCopyCount;
                _currentlyBufferedByteCount -= remainingCopyCount;
 
                return alreadyCopied + remainingCopyCount;
            }
        }
 
        public override long Seek(long offset, SeekOrigin origin)
        {
            throw new NotSupportedException();
        }
 
        public override void SetLength(long value)
        {
            throw new NotSupportedException();
        }
 
        public override void Write(byte[] buffer, int offset, int count)
        {
            _innerStream.Write(buffer, offset, count);
        }
 
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                _innerStream.Dispose();
            }
 
            base.Dispose(disposing);
        }
    }
}