< 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
0%
Covered lines: 0
Uncovered lines: 162
Coverable lines: 162
Total lines: 313
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 44
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%
Flush()100%110%
FlushAsync(...)0%220%
Seek(...)100%110%
SetLength(...)100%110%
Read(...)100%110%
Read(...)0%880%
ReadByte()0%220%
EnsureNotDisposed()100%110%
BeginRead(...)100%110%
EndRead(...)100%110%
ReadAsyncInternal(...)0%880%
ReadAsyncCore(...)0%880%
ReadAsync(...)0%220%
ReadAsync(...)0%220%
Write(...)100%110%
PurgeBuffers(...)0%440%
Dispose(...)0%440%

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
 023        internal DeflateManagedStream(Stream stream, ZipCompressionMethod method, long uncompressedSize = -1)
 024        {
 025            ArgumentNullException.ThrowIfNull(stream);
 26
 027            if (!stream.CanRead)
 028                throw new ArgumentException(SR.NotSupported_UnreadableStream, nameof(stream));
 29
 030            Debug.Assert(method == ZipCompressionMethod.Deflate64);
 31
 032            _inflater = new InflaterManaged(method == ZipCompressionMethod.Deflate64, uncompressedSize);
 33
 034            _stream = stream;
 035            _buffer = new byte[DefaultBufferSize];
 036        }
 37
 38        public override bool CanRead
 39        {
 40            get
 041            {
 042                if (_stream == null)
 043                {
 044                    return false;
 45                }
 46
 047                return _stream.CanRead;
 048            }
 49        }
 50
 51        public override bool CanWrite
 52        {
 53            get
 054            {
 055                return false;
 056            }
 57        }
 58
 059        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()
 073        {
 074            EnsureNotDisposed();
 075        }
 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)
 096        {
 097            ValidateBufferArguments(buffer, offset, count);
 098            return Read(new Span<byte>(buffer, offset, count));
 099        }
 100
 101        public override int Read(Span<byte> buffer)
 0102        {
 0103            EnsureNotDisposed();
 104
 0105            int initialLength = buffer.Length;
 106
 107            int bytesRead;
 0108            while (true)
 0109            {
 0110                bytesRead = _inflater.Inflate(buffer);
 0111                buffer = buffer.Slice(bytesRead);
 112
 0113                if (buffer.Length == 0)
 0114                {
 0115                    break;
 116                }
 117
 0118                if (_inflater.Finished())
 0119                {
 120                    // if we finished decompressing, we can't have anything left in the outputwindow.
 0121                    Debug.Assert(_inflater.AvailableOutput == 0, "We should have copied all stuff out!");
 0122                    break;
 123                }
 124
 0125                int bytes = _stream!.Read(_buffer, 0, _buffer.Length);
 0126                if (bytes <= 0)
 0127                {
 0128                    break;
 129                }
 0130                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
 0137                _inflater.SetInput(_buffer, 0, bytes);
 0138            }
 139
 0140            return initialLength - buffer.Length;
 0141        }
 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()
 0150        {
 0151            ObjectDisposedException.ThrowIf(_stream is null, this);
 0152        }
 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)
 0161        {
 0162            if (cancellationToken.IsCancellationRequested)
 0163            {
 0164                return ValueTask.FromCanceled<int>(cancellationToken);
 165            }
 166
 0167            Interlocked.Increment(ref _asyncOperations);
 0168            bool startedAsyncWork = false;
 169
 170            try
 0171            {
 172                // Try to read decompressed data in output buffer
 0173                int bytesRead = _inflater.Inflate(buffer.Span);
 0174                if (bytesRead != 0)
 0175                {
 176                    // If decompression output buffer is not empty, return immediately.
 0177                    return ValueTask.FromResult(bytesRead);
 178                }
 179
 0180                if (_inflater.Finished())
 0181                {
 182                    // end of compression stream
 0183                    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
 0188                ValueTask<int> readTask = _stream!.ReadAsync(_buffer.AsMemory(), cancellationToken);
 0189                startedAsyncWork = true;
 190
 0191                return ReadAsyncCore(readTask, buffer, cancellationToken);
 192            }
 193            finally
 0194            {
 195                // if we haven't started any async work, decrement the counter to end the transaction
 0196                if (!startedAsyncWork)
 0197                {
 0198                    Interlocked.Decrement(ref _asyncOperations);
 0199                }
 0200            }
 0201        }
 202
 203        private async ValueTask<int> ReadAsyncCore(ValueTask<int> readTask, Memory<byte> buffer, CancellationToken cance
 0204        {
 205            try
 0206            {
 0207                while (true)
 0208                {
 0209                    int bytesRead = await readTask.ConfigureAwait(false);
 0210                    EnsureNotDisposed();
 211
 0212                    if (bytesRead <= 0)
 0213                    {
 214                        // This indicates the base stream has received EOF
 0215                        return 0;
 216                    }
 0217                    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
 0224                    cancellationToken.ThrowIfCancellationRequested();
 225
 226                    // Feed the data from base stream into decompression engine
 0227                    _inflater.SetInput(_buffer, 0, bytesRead);
 0228                    bytesRead = _inflater.Inflate(buffer.Span);
 229
 0230                    if (bytesRead == 0 && !_inflater.Finished())
 0231                    {
 232                        // We could have read in head information and didn't get any data.
 233                        // Read from the base stream again.
 0234                        readTask = _stream!.ReadAsync(_buffer.AsMemory(), cancellationToken);
 0235                    }
 236                    else
 0237                    {
 0238                        return bytesRead;
 239                    }
 0240                }
 241            }
 242            finally
 0243            {
 0244                Interlocked.Decrement(ref _asyncOperations);
 0245            }
 0246        }
 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)
 0261        {
 262            // We use this checking order for compat to earlier versions:
 0263            if (_asyncOperations != 0)
 0264                throw new InvalidOperationException(SR.InvalidBeginCall);
 265
 0266            EnsureNotDisposed();
 267
 0268            return ReadAsyncInternal(buffer, cancellationToken);
 0269        }
 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)
 0278        {
 0279            if (!disposing)
 0280                return;
 281
 0282            if (_stream == null)
 0283                return;
 284
 0285            Flush();
 0286        }
 287
 288        protected override void Dispose(bool disposing)
 0289        {
 290            try
 0291            {
 0292                PurgeBuffers(disposing);
 0293            }
 294            finally
 0295            {
 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
 0300                {
 0301                    if (disposing && _stream != null)
 0302                        _stream.Dispose();
 0303                }
 304                finally
 0305                {
 0306                    _stream = null!;
 0307                    _inflater = null!;
 0308                    base.Dispose(disposing);
 0309                }
 0310            }
 0311        }
 312    }
 313}