< Summary

Information
Class: System.IO.Compression.DeflateManagedStream
Assembly: System.IO.Compression
File(s): D:\runner\runtime\src\libraries\System.IO.Compression\src\System\IO\Compression\DeflateManaged\DeflateManagedStream.cs
Line coverage
73%
Covered lines: 119
Uncovered lines: 43
Coverable lines: 162
Total lines: 313
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)

D:\runner\runtime\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
 650223        internal DeflateManagedStream(Stream stream, ZipCompressionMethod method, long uncompressedSize = -1)
 650224        {
 650225            ArgumentNullException.ThrowIfNull(stream);
 26
 650227            if (!stream.CanRead)
 028                throw new ArgumentException(SR.NotSupported_UnreadableStream, nameof(stream));
 29
 650230            Debug.Assert(method == ZipCompressionMethod.Deflate64);
 31
 650232            _inflater = new InflaterManaged(method == ZipCompressionMethod.Deflate64, uncompressedSize);
 33
 650234            _stream = stream;
 650235            _buffer = new byte[DefaultBufferSize];
 650236        }
 37
 38        public override bool CanRead
 39        {
 40            get
 650241            {
 650242                if (_stream == null)
 043                {
 044                    return false;
 45                }
 46
 650247                return _stream.CanRead;
 650248            }
 49        }
 50
 51        public override bool CanWrite
 52        {
 53            get
 054            {
 055                return false;
 056            }
 57        }
 58
 650259        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()
 650273        {
 650274            EnsureNotDisposed();
 650275        }
 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)
 808496        {
 808497            ValidateBufferArguments(buffer, offset, count);
 808498            return Read(new Span<byte>(buffer, offset, count));
 692199        }
 100
 101        public override int Read(Span<byte> buffer)
 8084102        {
 8084103            EnsureNotDisposed();
 104
 8084105            int initialLength = buffer.Length;
 106
 107            int bytesRead;
 11335108            while (true)
 11335109            {
 11335110                bytesRead = _inflater.Inflate(buffer);
 10172111                buffer = buffer.Slice(bytesRead);
 112
 10172113                if (buffer.Length == 0)
 3288114                {
 3288115                    break;
 116                }
 117
 6884118                if (_inflater.Finished())
 702119                {
 120                    // if we finished decompressing, we can't have anything left in the outputwindow.
 702121                    Debug.Assert(_inflater.AvailableOutput == 0, "We should have copied all stuff out!");
 702122                    break;
 123                }
 124
 6182125                int bytes = _stream!.Read(_buffer, 0, _buffer.Length);
 6182126                if (bytes <= 0)
 2931127                {
 2931128                    break;
 129                }
 3251130                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
 3251137                _inflater.SetInput(_buffer, 0, bytes);
 3251138            }
 139
 6921140            return initialLength - buffer.Length;
 6921141        }
 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()
 27644150        {
 27644151            ObjectDisposedException.ThrowIf(_stream is null, this);
 27644152        }
 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)
 8084161        {
 8084162            if (cancellationToken.IsCancellationRequested)
 0163            {
 0164                return ValueTask.FromCanceled<int>(cancellationToken);
 165            }
 166
 8084167            Interlocked.Increment(ref _asyncOperations);
 8084168            bool startedAsyncWork = false;
 169
 170            try
 8084171            {
 172                // Try to read decompressed data in output buffer
 8084173                int bytesRead = _inflater.Inflate(buffer.Span);
 7901174                if (bytesRead != 0)
 3576175                {
 176                    // If decompression output buffer is not empty, return immediately.
 3576177                    return ValueTask.FromResult(bytesRead);
 178                }
 179
 4325180                if (_inflater.Finished())
 337181                {
 182                    // end of compression stream
 337183                    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
 3988188                ValueTask<int> readTask = _stream!.ReadAsync(_buffer.AsMemory(), cancellationToken);
 3988189                startedAsyncWork = true;
 190
 3988191                return ReadAsyncCore(readTask, buffer, cancellationToken);
 192            }
 193            finally
 8084194            {
 195                // if we haven't started any async work, decrement the counter to end the transaction
 8084196                if (!startedAsyncWork)
 4096197                {
 4096198                    Interlocked.Decrement(ref _asyncOperations);
 4096199                }
 8084200            }
 7901201        }
 202
 203        private async ValueTask<int> ReadAsyncCore(ValueTask<int> readTask, Memory<byte> buffer, CancellationToken cance
 3988204        {
 205            try
 3988206            {
 4974207                while (true)
 4974208                {
 4974209                    int bytesRead = await readTask.ConfigureAwait(false);
 4974210                    EnsureNotDisposed();
 211
 4974212                    if (bytesRead <= 0)
 1723213                    {
 214                        // This indicates the base stream has received EOF
 1723215                        return 0;
 216                    }
 3251217                    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
 3251224                    cancellationToken.ThrowIfCancellationRequested();
 225
 226                    // Feed the data from base stream into decompression engine
 3251227                    _inflater.SetInput(_buffer, 0, bytesRead);
 3251228                    bytesRead = _inflater.Inflate(buffer.Span);
 229
 2271230                    if (bytesRead == 0 && !_inflater.Finished())
 986231                    {
 232                        // We could have read in head information and didn't get any data.
 233                        // Read from the base stream again.
 986234                        readTask = _stream!.ReadAsync(_buffer.AsMemory(), cancellationToken);
 986235                    }
 236                    else
 1285237                    {
 1285238                        return bytesRead;
 239                    }
 986240                }
 241            }
 242            finally
 3988243            {
 3988244                Interlocked.Decrement(ref _asyncOperations);
 3988245            }
 3008246        }
 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)
 8084261        {
 262            // We use this checking order for compat to earlier versions:
 8084263            if (_asyncOperations != 0)
 0264                throw new InvalidOperationException(SR.InvalidBeginCall);
 265
 8084266            EnsureNotDisposed();
 267
 8084268            return ReadAsyncInternal(buffer, cancellationToken);
 7901269        }
 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)
 6502278        {
 6502279            if (!disposing)
 0280                return;
 281
 6502282            if (_stream == null)
 0283                return;
 284
 6502285            Flush();
 6502286        }
 287
 288        protected override void Dispose(bool disposing)
 6502289        {
 290            try
 6502291            {
 6502292                PurgeBuffers(disposing);
 6502293            }
 294            finally
 6502295            {
 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
 6502300                {
 6502301                    if (disposing && _stream != null)
 6502302                        _stream.Dispose();
 6502303                }
 304                finally
 6502305                {
 6502306                    _stream = null!;
 6502307                    _inflater = null!;
 6502308                    base.Dispose(disposing);
 6502309                }
 6502310            }
 6502311        }
 312    }
 313}