< Summary

Line coverage
73%
Covered lines: 119
Uncovered lines: 43
Coverable lines: 162
Total lines: 314
Line coverage: 73.4%
Branch coverage
70%
Covered branches: 31
Total branches: 44
Branch coverage: 70.4%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor(...)50%2290%
Flush()100%11100%
FlushAsync(...)0%220%
Seek(...)100%110%
SetLength(...)100%110%
Read(...)100%11100%
Read(...)87.5%8892%
ReadByte()0%220%
EnsureNotDisposed()100%11100%
BeginRead(...)100%110%
EndRead(...)100%110%
ReadAsyncInternal(...)87.5%8891.66%
ReadAsyncCore(...)87.5%8892.3%
ReadAsync(...)0%220%
ReadAsync(...)50%2283.33%
Write(...)100%110%
PurgeBuffers(...)100%4471.42%
Dispose(...)75%44100%

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateManaged/DeflateManagedStream.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.Diagnostics;
 5using System.Runtime.InteropServices;
 6using System.Threading;
 7using System.Threading.Tasks;
 8
 9namespace System.IO.Compression
 10{
 11    // DeflateManagedStream supports decompression of Deflate64 format only.
 12    internal sealed partial class DeflateManagedStream : Stream
 13    {
 14        internal const int DefaultBufferSize = 8192;
 15
 16        private Stream? _stream;
 17        private InflaterManaged _inflater;
 18        private readonly byte[] _buffer;
 19
 20        private int _asyncOperations;
 21
 22        // A specific constructor to allow decompression of Deflate64
 471823        internal DeflateManagedStream(Stream stream, ZipCompressionMethod method, long uncompressedSize = -1)
 471824        {
 471825            ArgumentNullException.ThrowIfNull(stream);
 26
 471827            if (!stream.CanRead)
 028                throw new ArgumentException(SR.NotSupported_UnreadableStream, nameof(stream));
 29
 471830            Debug.Assert(method == ZipCompressionMethod.Deflate64);
 31
 471832            _inflater = new InflaterManaged(method == ZipCompressionMethod.Deflate64, uncompressedSize);
 33
 471834            _stream = stream;
 471835            _buffer = new byte[DefaultBufferSize];
 471836        }
 37
 38        public override bool CanRead
 39        {
 40            get
 471841            {
 471842                if (_stream == null)
 043                {
 044                    return false;
 45                }
 46
 471847                return _stream.CanRead;
 471848            }
 49        }
 50
 51        public override bool CanWrite
 52        {
 53            get
 054            {
 055                return false;
 056            }
 57        }
 58
 471859        public override bool CanSeek => false;
 60
 61        public override long Length
 62        {
 063            get { throw new NotSupportedException(SR.NotSupported); }
 64        }
 65
 66        public override long Position
 67        {
 068            get { throw new NotSupportedException(SR.NotSupported); }
 069            set { throw new NotSupportedException(SR.NotSupported); }
 70        }
 71
 72        public override void Flush()
 471873        {
 471874            EnsureNotDisposed();
 471875        }
 76
 77        public override Task FlushAsync(CancellationToken cancellationToken)
 078        {
 079            EnsureNotDisposed();
 080            return cancellationToken.IsCancellationRequested ?
 081                Task.FromCanceled(cancellationToken) :
 082                Task.CompletedTask;
 083        }
 84
 85        public override long Seek(long offset, SeekOrigin origin)
 086        {
 087            throw new NotSupportedException(SR.NotSupported);
 88        }
 89
 90        public override void SetLength(long value)
 091        {
 092            throw new NotSupportedException(SR.NotSupported);
 93        }
 94
 95        public override int Read(byte[] buffer, int offset, int count)
 456096        {
 456097            ValidateBufferArguments(buffer, offset, count);
 456098            return Read(new Span<byte>(buffer, offset, count));
 364299        }
 100
 101        public override int Read(Span<byte> buffer)
 4560102        {
 4560103            EnsureNotDisposed();
 104
 4560105            int initialLength = buffer.Length;
 106
 107            int bytesRead;
 6919108            while (true)
 6919109            {
 6919110                bytesRead = _inflater.Inflate(buffer);
 6001111                buffer = buffer.Slice(bytesRead);
 112
 6001113                if (buffer.Length == 0)
 1421114                {
 1421115                    break;
 116                }
 117
 4580118                if (_inflater.Finished())
 564119                {
 120                    // if we finished decompressing, we can't have anything left in the outputwindow.
 564121                    Debug.Assert(_inflater.AvailableOutput == 0, "We should have copied all stuff out!");
 564122                    break;
 123                }
 124
 4016125                int bytes = _stream!.Read(_buffer, 0, _buffer.Length);
 4016126                if (bytes <= 0)
 1657127                {
 1657128                    break;
 129                }
 2359130                else if (bytes > _buffer.Length)
 0131                {
 132                    // The stream is either malicious or poorly implemented and returned a number of
 133                    // bytes larger than the buffer supplied to it.
 0134                    throw new InvalidDataException(SR.GenericInvalidData);
 135                }
 136
 2359137                _inflater.SetInput(_buffer, 0, bytes);
 2359138            }
 139
 3642140            return initialLength - buffer.Length;
 3642141        }
 142
 143        public override int ReadByte()
 0144        {
 0145            byte b = default;
 0146            return Read(new Span<byte>(ref b)) == 1 ? b : -1;
 0147        }
 148
 149        private void EnsureNotDisposed()
 17349150        {
 17349151            ObjectDisposedException.ThrowIf(_stream is null, this);
 17349152        }
 153
 154        public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? asyncCallback, objec
 0155            TaskToAsyncResult.Begin(ReadAsync(buffer, offset, count, CancellationToken.None), asyncCallback, asyncState)
 156
 157        public override int EndRead(IAsyncResult asyncResult) =>
 0158            TaskToAsyncResult.End<int>(asyncResult);
 159
 160        private ValueTask<int> ReadAsyncInternal(Memory<byte> buffer, CancellationToken cancellationToken)
 4560161        {
 4560162            if (cancellationToken.IsCancellationRequested)
 0163            {
 0164                return ValueTask.FromCanceled<int>(cancellationToken);
 165            }
 166
 4560167            Interlocked.Increment(ref _asyncOperations);
 4560168            bool startedAsyncWork = false;
 169
 170            try
 4560171            {
 172                // Try to read decompressed data in output buffer
 4560173                int bytesRead = _inflater.Inflate(buffer.Span);
 4466174                if (bytesRead != 0)
 1486175                {
 176                    // If decompression output buffer is not empty, return immediately.
 1486177                    return ValueTask.FromResult(bytesRead);
 178                }
 179
 2980180                if (_inflater.Finished())
 275181                {
 182                    // end of compression stream
 275183                    return ValueTask.FromResult(0);
 184                }
 185
 186                // If there is no data on the output buffer and we are not at
 187                // the end of the stream, we need to get more data from the base stream
 2705188                ValueTask<int> readTask = _stream!.ReadAsync(_buffer.AsMemory(), cancellationToken);
 2705189                startedAsyncWork = true;
 190
 2705191                return ReadAsyncCore(readTask, buffer, cancellationToken);
 192            }
 193            finally
 4560194            {
 195                // if we haven't started any async work, decrement the counter to end the transaction
 4560196                if (!startedAsyncWork)
 1855197                {
 1855198                    Interlocked.Decrement(ref _asyncOperations);
 1855199                }
 4560200            }
 4466201        }
 202
 203        private async ValueTask<int> ReadAsyncCore(ValueTask<int> readTask, Memory<byte> buffer, CancellationToken cance
 2705204        {
 205            try
 2705206            {
 3511207                while (true)
 3511208                {
 3511209                    int bytesRead = await readTask.ConfigureAwait(false);
 3511210                    EnsureNotDisposed();
 211
 3511212                    if (bytesRead <= 0)
 1152213                    {
 214                        // This indicates the base stream has received EOF
 1152215                        return 0;
 216                    }
 2359217                    else if (bytesRead > _buffer.Length)
 0218                    {
 219                        // The stream is either malicious or poorly implemented and returned a number of
 220                        // bytes larger than the buffer supplied to it.
 0221                        throw new InvalidDataException(SR.GenericInvalidData);
 222                    }
 223
 2359224                    cancellationToken.ThrowIfCancellationRequested();
 225
 226                    // Feed the data from base stream into decompression engine
 2359227                    _inflater.SetInput(_buffer, 0, bytesRead);
 2359228                    bytesRead = _inflater.Inflate(buffer.Span);
 229
 1535230                    if (bytesRead == 0 && !_inflater.Finished())
 806231                    {
 232                        // We could have read in head information and didn't get any data.
 233                        // Read from the base stream again.
 806234                        readTask = _stream!.ReadAsync(_buffer.AsMemory(), cancellationToken);
 806235                    }
 236                    else
 729237                    {
 729238                        return bytesRead;
 239                    }
 806240                }
 241            }
 242            finally
 2705243            {
 2705244                Interlocked.Decrement(ref _asyncOperations);
 2705245            }
 1881246        }
 247
 248        public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
 0249        {
 250            // We use this checking order for compat to earlier versions:
 0251            if (_asyncOperations != 0)
 0252                throw new InvalidOperationException(SR.InvalidBeginCall);
 253
 0254            ValidateBufferArguments(buffer, offset, count);
 0255            EnsureNotDisposed();
 256
 0257            return ReadAsyncInternal(buffer.AsMemory(offset, count), cancellationToken).AsTask();
 0258        }
 259
 260        public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
 4560261        {
 262            // We use this checking order for compat to earlier versions:
 4560263            if (_asyncOperations != 0)
 0264                throw new InvalidOperationException(SR.InvalidBeginCall);
 265
 4560266            EnsureNotDisposed();
 267
 4560268            return ReadAsyncInternal(buffer, cancellationToken);
 4466269        }
 270
 271        public override void Write(byte[] buffer, int offset, int count)
 0272        {
 0273            throw new InvalidOperationException(SR.CannotWriteToDeflateStream);
 274        }
 275
 276        // This is called by Dispose:
 277        private void PurgeBuffers(bool disposing)
 4718278        {
 4718279            if (!disposing)
 0280                return;
 281
 4718282            if (_stream == null)
 0283                return;
 284
 4718285            Flush();
 4718286        }
 287
 288        protected override void Dispose(bool disposing)
 4718289        {
 290            try
 4718291            {
 4718292                PurgeBuffers(disposing);
 4718293            }
 294            finally
 4718295            {
 296                // Close the underlying stream even if PurgeBuffers threw.
 297                // Stream.Close() may throw here (may or may not be due to the same error).
 298                // In this case, we still need to clean up internal resources, hence the inner finally blocks.
 299                try
 4718300                {
 4718301                    if (disposing && _stream != null)
 4718302                        _stream.Dispose();
 4718303                }
 304                finally
 4718305                {
 4718306                    _stream = null!;
 4718307                    _inflater = null!;
 4718308                    base.Dispose(disposing);
 4718309                }
 4718310            }
 4718311        }
 312    }
 313}
 314