< Summary

Information
Class: System.IO.SubReadStream
Assembly: System.IO.Compression
File(s): D:\runner\runtime\src\libraries\Common\src\System\IO\SubReadStream.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 121
Coverable lines: 121
Total lines: 288
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 32
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor(...)0%220%
LimitByRemaining(...)100%110%
ThrowIfDisposed()100%110%
ThrowIfCantRead()0%220%
ThrowIfBeyondEndOfStream()0%220%
Read(...)100%110%
Read(...)0%440%
ReadByte()0%220%
ReadAsync(...)100%110%
ReadAsync(...)100%110%
ReadAsyncCore(...)0%440%
Seek(...)0%880%
SetLength(...)100%110%
Write(...)100%110%
Flush()100%110%
FlushAsync(...)0%220%
Dispose(...)100%110%

File(s)

D:\runner\runtime\src\libraries\Common\src\System\IO\SubReadStream.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System.Buffers;
 5using System.Diagnostics;
 6using System.Threading;
 7using System.Threading.Tasks;
 8
 9namespace System.IO
 10{
 11    // Stream that wraps a super stream and exposes a window [startPosition, startPosition + maxLength).
 12    // Supports both seekable and unseekable super streams. When the super stream is seekable, this stream
 13    // is seekable too and will reposition the super stream before each read if needed. When the super
 14    // stream is unseekable, reads must be sequential and Seek/Position-set throw.
 15    // Does not support writing.
 16    internal sealed class SubReadStream : Stream
 17    {
 18        private const int MaxAdvanceBufferLength = 4096;
 19
 20        private bool _hasReachedEnd;
 21        private readonly long _startInSuperStream;
 22        private long _positionInSuperStream;
 23        private readonly long _endInSuperStream;
 24        private readonly Stream _superStream;
 25        private bool _isDisposed;
 26
 027        public SubReadStream(Stream superStream, long startPosition, long maxLength)
 028        {
 029            ArgumentNullException.ThrowIfNull(superStream);
 030            if (!superStream.CanRead)
 031            {
 032                throw new ArgumentException(SR.IO_NotSupported_UnreadableStream, nameof(superStream));
 33            }
 034            ArgumentOutOfRangeException.ThrowIfNegative(startPosition);
 035            ArgumentOutOfRangeException.ThrowIfNegative(maxLength);
 036            ArgumentOutOfRangeException.ThrowIfGreaterThan(startPosition, long.MaxValue - maxLength);
 37
 038            _startInSuperStream = startPosition;
 039            _positionInSuperStream = startPosition;
 040            _endInSuperStream = startPosition + maxLength;
 041            _superStream = superStream;
 042        }
 43
 44        public override long Length
 45        {
 46            get
 047            {
 048                ThrowIfDisposed();
 049                return _endInSuperStream - _startInSuperStream;
 050            }
 51        }
 52
 53        public override long Position
 54        {
 55            get
 056            {
 057                ThrowIfDisposed();
 058                return _positionInSuperStream - _startInSuperStream;
 059            }
 60            set
 061            {
 062                ThrowIfDisposed();
 063                if (!CanSeek)
 064                {
 065                    throw new NotSupportedException(SR.IO_NotSupported_UnseekableStream);
 66                }
 067                ArgumentOutOfRangeException.ThrowIfNegative(value);
 68
 069                long newPositionInSuperStream = _startInSuperStream + value;
 070                _superStream.Position = newPositionInSuperStream;
 071                _positionInSuperStream = newPositionInSuperStream;
 072            }
 73        }
 74
 075        public override bool CanRead => !_isDisposed && _superStream.CanRead;
 76
 077        public override bool CanSeek => !_isDisposed && _superStream.CanSeek;
 78
 079        public override bool CanWrite => false;
 80
 081        private long Remaining => _endInSuperStream - _positionInSuperStream;
 82
 083        private int LimitByRemaining(int bufferSize) => (int)Math.Max(0, Math.Min(Remaining, bufferSize));
 84
 85        // Positions the super stream past the end of this stream's window. After calling this method,
 86        // subsequent reads on this stream throw <see cref="EndOfStreamException"/>.
 87        internal void AdvanceToEnd()
 88        {
 89            _hasReachedEnd = true;
 90
 91            long remaining = Remaining;
 92            _positionInSuperStream = _endInSuperStream;
 93            AdvanceSuperStream(remaining);
 94        }
 95
 96        internal ValueTask AdvanceToEndAsync(CancellationToken cancellationToken)
 97        {
 98            _hasReachedEnd = true;
 99
 100            long remaining = Remaining;
 101            _positionInSuperStream = _endInSuperStream;
 102            return AdvanceSuperStreamAsync(remaining, cancellationToken);
 103        }
 104
 105        private void AdvanceSuperStream(long bytesToDiscard)
 106        {
 107            if (_superStream.CanSeek)
 108            {
 109                _superStream.Position += bytesToDiscard;
 110            }
 111            else if (bytesToDiscard > 0)
 112            {
 113                byte[] buffer = ArrayPool<byte>.Shared.Rent((int)Math.Min(MaxAdvanceBufferLength, bytesToDiscard));
 114                try
 115                {
 116                    while (bytesToDiscard > 0)
 117                    {
 118                        int currentLengthToRead = (int)Math.Min(MaxAdvanceBufferLength, bytesToDiscard);
 119                        _superStream.ReadExactly(buffer.AsSpan(0, currentLengthToRead));
 120                        bytesToDiscard -= currentLengthToRead;
 121                    }
 122                }
 123                finally
 124                {
 125                    ArrayPool<byte>.Shared.Return(buffer);
 126                }
 127            }
 128        }
 129
 130        private async ValueTask AdvanceSuperStreamAsync(long bytesToDiscard, CancellationToken cancellationToken)
 131        {
 132            cancellationToken.ThrowIfCancellationRequested();
 133
 134            if (_superStream.CanSeek)
 135            {
 136                _superStream.Position += bytesToDiscard;
 137            }
 138            else if (bytesToDiscard > 0)
 139            {
 140                byte[] buffer = ArrayPool<byte>.Shared.Rent((int)Math.Min(MaxAdvanceBufferLength, bytesToDiscard));
 141                try
 142                {
 143                    while (bytesToDiscard > 0)
 144                    {
 145                        int currentLengthToRead = (int)Math.Min(MaxAdvanceBufferLength, bytesToDiscard);
 146                        await _superStream.ReadExactlyAsync(buffer, 0, currentLengthToRead, cancellationToken).Configure
 147                        bytesToDiscard -= currentLengthToRead;
 148                    }
 149                }
 150                finally
 151                {
 152                    ArrayPool<byte>.Shared.Return(buffer);
 153                }
 154            }
 155        }
 156
 157        private void ThrowIfDisposed()
 0158        {
 0159            ObjectDisposedException.ThrowIf(_isDisposed, this);
 0160        }
 161
 162        private void ThrowIfCantRead()
 0163        {
 0164            if (!_superStream.CanRead)
 0165            {
 0166                throw new NotSupportedException(SR.IO_NotSupported_UnreadableStream);
 167            }
 0168        }
 169
 170        private void ThrowIfBeyondEndOfStream()
 0171        {
 0172            if (_hasReachedEnd)
 0173            {
 0174                throw new EndOfStreamException();
 175            }
 0176        }
 177
 178        public override int Read(byte[] buffer, int offset, int count)
 0179        {
 0180            ValidateBufferArguments(buffer, offset, count);
 0181            return Read(buffer.AsSpan(offset, count));
 0182        }
 183
 184        public override int Read(Span<byte> destination)
 0185        {
 0186            ThrowIfDisposed();
 0187            ThrowIfCantRead();
 0188            ThrowIfBeyondEndOfStream();
 189
 0190            if (_superStream.CanSeek && _superStream.Position != _positionInSuperStream)
 0191            {
 0192                _superStream.Seek(_positionInSuperStream, SeekOrigin.Begin);
 0193            }
 194
 0195            destination = destination[..LimitByRemaining(destination.Length)];
 196
 0197            int ret = _superStream.Read(destination);
 198
 0199            _positionInSuperStream += ret;
 0200            return ret;
 0201        }
 202
 203        public override int ReadByte()
 0204        {
 0205            byte b = default;
 0206            return Read(new Span<byte>(ref b)) == 1 ? b : -1;
 0207        }
 208
 209        public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
 0210        {
 0211            ValidateBufferArguments(buffer, offset, count);
 0212            return ReadAsync(new Memory<byte>(buffer, offset, count), cancellationToken).AsTask();
 0213        }
 214
 215        public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
 0216        {
 0217            ThrowIfDisposed();
 0218            ThrowIfCantRead();
 0219            ThrowIfBeyondEndOfStream();
 0220            return ReadAsyncCore(buffer, cancellationToken);
 0221        }
 222
 223        private async ValueTask<int> ReadAsyncCore(Memory<byte> buffer, CancellationToken cancellationToken)
 0224        {
 0225            Debug.Assert(!_hasReachedEnd);
 226
 0227            cancellationToken.ThrowIfCancellationRequested();
 228
 0229            if (_superStream.CanSeek && _superStream.Position != _positionInSuperStream)
 0230            {
 0231                _superStream.Seek(_positionInSuperStream, SeekOrigin.Begin);
 0232            }
 233
 0234            buffer = buffer[..LimitByRemaining(buffer.Length)];
 235
 0236            int ret = await _superStream.ReadAsync(buffer, cancellationToken).ConfigureAwait(false);
 237
 0238            _positionInSuperStream += ret;
 0239            return ret;
 0240        }
 241
 242        public override long Seek(long offset, SeekOrigin origin)
 0243        {
 0244            ThrowIfDisposed();
 245
 0246            if (!CanSeek)
 0247            {
 0248                throw new NotSupportedException(SR.IO_NotSupported_UnseekableStream);
 249            }
 250
 0251            long newPositionInSuperStream = origin switch
 0252            {
 0253                SeekOrigin.Begin => _startInSuperStream + offset,
 0254                SeekOrigin.Current => _positionInSuperStream + offset,
 0255                SeekOrigin.End => _endInSuperStream + offset,
 0256                _ => throw new ArgumentOutOfRangeException(nameof(origin)),
 0257            };
 258
 0259            if (newPositionInSuperStream < _startInSuperStream)
 0260            {
 0261                throw new IOException(SR.IO_SeekBeforeBegin);
 262            }
 263
 0264            long actualPositionInSuperStream = _superStream.Seek(newPositionInSuperStream, SeekOrigin.Begin);
 0265            _positionInSuperStream = actualPositionInSuperStream;
 266
 0267            return _positionInSuperStream - _startInSuperStream;
 0268        }
 269
 0270        public override void SetLength(long value) => throw new NotSupportedException(SR.IO_NotSupported_UnwritableStrea
 271
 0272        public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException(SR.IO_NotSup
 273
 0274        public override void Flush() { }
 275
 276        public override Task FlushAsync(CancellationToken cancellationToken) =>
 0277            cancellationToken.IsCancellationRequested ? Task.FromCanceled(cancellationToken) :
 0278            Task.CompletedTask;
 279
 280        // Close the stream for reading. Note that this does NOT close the super stream (since
 281        // this stream is just a 'chunk' of the super stream).
 282        protected override void Dispose(bool disposing)
 0283        {
 0284            _isDisposed = true;
 0285            base.Dispose(disposing);
 0286        }
 287    }
 288}