< Summary

Information
Class: System.IO.Compression.Inflater
Assembly: System.IO.Compression
File(s): D:\runner\runtime\src\libraries\System.IO.Compression\src\System\IO\Compression\DeflateZLib\Inflater.cs
Line coverage
75%
Covered lines: 133
Uncovered lines: 43
Coverable lines: 176
Total lines: 314
Line coverage: 75.5%
Branch coverage
57%
Covered branches: 32
Total branches: 56
Branch coverage: 57.1%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor(...)100%11100%
Finished()100%11100%
Inflate(...)0%220%
Inflate(...)50%6687.5%
Inflate(...)50%2285.71%
InflateVerified(...)87.5%8888.88%
ReadOutput(...)62.5%8875%
ResetStreamForLeftoverInput()0%440%
IsGzipStream()50%22100%
NeedsInput()100%11100%
NonEmptyInput()100%110%
GetAvailableInput()100%110%
SetInput(...)50%44100%
SetInput(...)100%2292.85%
Dispose(...)66.66%66100%
Dispose()100%11100%
Finalize()100%110%
ReadInflateOutput(...)100%11100%
Inflate(...)50%8860%
DeallocateInputBufferHandle(...)100%22100%
CreateInflater(...)50%22100%

File(s)

D:\runner\runtime\src\libraries\System.IO.Compression\src\System\IO\Compression\DeflateZLib\Inflater.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.Runtime.InteropServices;
 7
 8namespace System.IO.Compression
 9{
 10    /// <summary>
 11    /// Provides a wrapper around the ZLib decompression API.
 12    /// </summary>
 13    internal sealed class Inflater : IDisposable
 14    {
 15        private const int MinWindowBits = -15;                      // WindowBits must be between -8..-15 to ignore the 
 16        private const int MaxWindowBits = 47;                       // zlib headers, 24..31 for GZip headers, or 40..47 
 17
 18        private bool _nonEmptyInput;                                // Whether there is any non empty input
 19        private bool _finished;                                     // Whether the end of the stream has been reached
 20        private bool _isDisposed;                                   // Prevents multiple disposals
 21        private readonly int _windowBits;                           // The WindowBits parameter passed to Inflater const
 22        private readonly ZLibNative.ZLibStreamHandle _zlibStream;   // The handle to the primary underlying zlib stream
 23        private MemoryHandle _inputBufferHandle;                    // The handle to the buffer that provides input to _
 24        private readonly long _uncompressedSize;
 25        private long _currentInflatedCount;
 26
 601227        private object SyncLock => this;                    // Used to make writing to unmanaged structures atomic
 28
 150329        private Inflater(int windowBits, long uncompressedSize, ZLibNative.ZLibStreamHandle zlibStream)
 150330        {
 150331            _finished = false;
 150332            _nonEmptyInput = false;
 150333            _isDisposed = false;
 150334            _windowBits = windowBits;
 150335            _uncompressedSize = uncompressedSize;
 150336            _zlibStream = zlibStream;
 150337        }
 38
 39        public int AvailableOutput => (int)_zlibStream.AvailOut;
 40
 41        /// <summary>
 42        /// Returns true if the end of the stream has been reached.
 43        /// </summary>
 469144        public bool Finished() => _finished;
 45
 46        public unsafe bool Inflate(out byte b)
 047        {
 048            fixed (byte* bufPtr = &b)
 049            {
 050                int bytesRead = InflateVerified(bufPtr, 1);
 051                Debug.Assert(bytesRead == 0 || bytesRead == 1);
 052                return bytesRead != 0;
 53            }
 054        }
 55
 56        public unsafe int Inflate(byte[] bytes, int offset, int length)
 19857        {
 58            // If Inflate is called on an invalid or unready inflater, return 0 to indicate no bytes have been read.
 19859            if (length == 0)
 060                return 0;
 61
 19862            Debug.Assert(null != bytes, "Can't pass in a null output buffer!");
 19863            fixed (byte* bufPtr = bytes)
 19864            {
 19865                return InflateVerified(bufPtr + offset, length);
 66            }
 19867        }
 68
 69        public unsafe int Inflate(Span<byte> destination)
 410470        {
 71            // If Inflate is called on an invalid or unready inflater, return 0 to indicate no bytes have been read.
 410472            if (destination.Length == 0)
 073                return 0;
 74
 410475            fixed (byte* bufPtr = &MemoryMarshal.GetReference(destination))
 410476            {
 410477                return InflateVerified(bufPtr, destination.Length);
 78            }
 409779        }
 80
 81        public unsafe int InflateVerified(byte* bufPtr, int length)
 430282        {
 83            // State is valid; attempt inflation
 84            try
 430285            {
 430286                int bytesRead = 0;
 430287                if (_uncompressedSize == -1)
 088                {
 089                    ReadOutput(bufPtr, length, out bytesRead);
 090                }
 91                else
 430292                {
 430293                    if (_uncompressedSize > _currentInflatedCount)
 300694                    {
 300695                        length = (int)Math.Min(length, _uncompressedSize - _currentInflatedCount);
 300696                        ReadOutput(bufPtr, length, out bytesRead);
 299997                        _currentInflatedCount += bytesRead;
 299998                    }
 99                    else
 1296100                    {
 1296101                        _finished = true;
 1296102                        _zlibStream.AvailIn = 0;
 1296103                    }
 4295104                }
 4295105                return bytesRead;
 106            }
 107            finally
 4302108            {
 109                // Before returning, make sure to release input buffer if necessary:
 4302110                if (0 == _zlibStream.AvailIn && IsInputBufferHandleAllocated)
 1496111                {
 1496112                    DeallocateInputBufferHandle(resetStreamHandle: true);
 1496113                }
 4302114            }
 4295115        }
 116
 117        private unsafe void ReadOutput(byte* bufPtr, int length, out int bytesRead)
 3006118        {
 3006119            if (ReadInflateOutput(bufPtr, length, ZLibNative.FlushCode.NoFlush, out bytesRead) == ZLibNative.ErrorCode.S
 1494120            {
 1494121                if (!NeedsInput() && IsGzipStream() && IsInputBufferHandleAllocated)
 0122                {
 0123                    _finished = ResetStreamForLeftoverInput();
 0124                }
 125                else
 1494126                {
 1494127                    _finished = true;
 1494128                }
 1494129            }
 2999130        }
 131
 132        /// <summary>
 133        /// If this stream has some input leftover that hasn't been processed then we should
 134        /// check if it is another GZip file concatenated with this one.
 135        ///
 136        /// Returns false if the leftover input is another GZip data stream.
 137        /// </summary>
 138        private unsafe bool ResetStreamForLeftoverInput()
 0139        {
 0140            Debug.Assert(!NeedsInput());
 0141            Debug.Assert(IsGzipStream());
 0142            Debug.Assert(IsInputBufferHandleAllocated);
 143
 0144            lock (SyncLock)
 0145            {
 0146                byte* nextInPointer = (byte*)_zlibStream.NextIn;
 0147                uint nextAvailIn = _zlibStream.AvailIn;
 148
 149                // Check the leftover bytes to see if they start with the gzip header ID bytes
 0150                if (*nextInPointer != ZLibNative.GZip_Header_ID1 || (nextAvailIn > 1 && *(nextInPointer + 1) != ZLibNati
 0151                {
 0152                    return true;
 153                }
 154
 155                // Reset our existing zstream.
 0156                _zlibStream.InflateReset2_(_windowBits);
 157
 0158                _finished = false;
 0159            }
 160
 0161            return false;
 0162        }
 163
 2592164        internal bool IsGzipStream() => _windowBits >= 24 && _windowBits <= 31;
 165
 5905166        public bool NeedsInput() => _zlibStream.AvailIn == 0;
 167
 0168        public bool NonEmptyInput() => _nonEmptyInput;
 169
 0170        internal int GetAvailableInput() => (int)_zlibStream.AvailIn;
 171
 172        public void SetInput(byte[] inputBuffer, int startIndex, int count)
 1404173        {
 1404174            Debug.Assert(NeedsInput(), "We have something left in previous input!");
 1404175            Debug.Assert(inputBuffer != null);
 1404176            Debug.Assert(startIndex >= 0 && count >= 0 && count + startIndex <= inputBuffer.Length);
 1404177            Debug.Assert(!IsInputBufferHandleAllocated);
 178
 1404179            SetInput(inputBuffer.AsMemory(startIndex, count));
 1404180        }
 181
 182        public unsafe void SetInput(ReadOnlyMemory<byte> inputBuffer)
 1503183        {
 1503184            Debug.Assert(NeedsInput(), "We have something left in previous input!");
 1503185            Debug.Assert(!IsInputBufferHandleAllocated);
 186
 1503187            if (inputBuffer.IsEmpty)
 0188                return;
 189
 1503190            lock (SyncLock)
 1503191            {
 1503192                _inputBufferHandle = inputBuffer.Pin();
 1503193                _zlibStream.NextIn = (IntPtr)_inputBufferHandle.Pointer;
 1503194                _zlibStream.AvailIn = (uint)inputBuffer.Length;
 1503195                _finished = false;
 1503196                _nonEmptyInput = true;
 1503197            }
 1503198        }
 199
 200        private void Dispose(bool disposing)
 1503201        {
 1503202            if (!_isDisposed)
 1503203            {
 1503204                if (disposing)
 1503205                {
 1503206                    _zlibStream.Dispose();
 1503207                }
 208
 1503209                if (IsInputBufferHandleAllocated)
 7210                {
 211                    // Unpin the input buffer, but avoid modifying the ZLibStreamHandle (which may have been disposed of
 7212                    DeallocateInputBufferHandle(resetStreamHandle: false);
 7213                }
 214
 1503215                _isDisposed = true;
 1503216            }
 1503217        }
 218
 219        public void Dispose()
 1503220        {
 1503221            Dispose(true);
 1503222            GC.SuppressFinalize(this);
 1503223        }
 224
 225        ~Inflater()
 0226        {
 0227            Dispose(false);
 0228        }
 229
 230        /// <summary>
 231        /// Wrapper around the ZLib inflate function, configuring the stream appropriately.
 232        /// </summary>
 233        private unsafe ZLibNative.ErrorCode ReadInflateOutput(byte* bufPtr, int length, ZLibNative.FlushCode flushCode, 
 3006234        {
 3006235            lock (SyncLock)
 3006236            {
 3006237                _zlibStream.NextOut = (IntPtr)bufPtr;
 3006238                _zlibStream.AvailOut = (uint)length;
 239
 3006240                ZLibNative.ErrorCode errC = Inflate(flushCode);
 2999241                bytesRead = length - (int)_zlibStream.AvailOut;
 242
 2999243                return errC;
 244            }
 2999245        }
 246
 247        /// <summary>
 248        /// Wrapper around the ZLib inflate function
 249        /// </summary>
 250        private ZLibNative.ErrorCode Inflate(ZLibNative.FlushCode flushCode)
 3006251        {
 252            ZLibNative.ErrorCode errC;
 253            try
 3006254            {
 3006255                errC = _zlibStream.Inflate(flushCode);
 3006256            }
 0257            catch (Exception cause) // could not load the Zlib DLL correctly
 0258            {
 0259                throw new ZLibException(SR.ZLibErrorDLLLoadError, cause);
 260            }
 3006261            switch (errC)
 262            {
 263                case ZLibNative.ErrorCode.Ok:           // progress has been made inflating
 264                case ZLibNative.ErrorCode.StreamEnd:    // The end of the input stream has been reached
 1496265                    return errC;
 266
 267                case ZLibNative.ErrorCode.BufError:     // No room in the output buffer - inflate() can be called again 
 1503268                    return errC;
 269
 270                case ZLibNative.ErrorCode.MemError:     // Not enough memory to complete the operation
 0271                    throw new ZLibException(SR.ZLibErrorNotEnoughMemory, "inflate_", (int)errC, _zlibStream.GetErrorMess
 272
 273                case ZLibNative.ErrorCode.DataError:    // The input data was corrupted (input stream not conforming to 
 7274                    throw new InvalidDataException(SR.UnsupportedCompression);
 275
 276                case ZLibNative.ErrorCode.StreamError:  //the stream structure was inconsistent (for example if next_in 
 0277                    throw new ZLibException(SR.ZLibErrorInconsistentStream, "inflate_", (int)errC, _zlibStream.GetErrorM
 278
 279                default:
 0280                    throw new ZLibException(SR.ZLibErrorUnexpected, "inflate_", (int)errC, _zlibStream.GetErrorMessage()
 281            }
 2999282        }
 283
 284        /// <summary>
 285        /// Frees the GCHandle being used to store the input buffer
 286        /// </summary>
 287        private void DeallocateInputBufferHandle(bool resetStreamHandle)
 1503288        {
 1503289            Debug.Assert(IsInputBufferHandleAllocated);
 290
 1503291            lock (SyncLock)
 1503292            {
 1503293                if (resetStreamHandle)
 1496294                {
 1496295                    _zlibStream.AvailIn = 0;
 1496296                    _zlibStream.NextIn = ZLibNative.ZNullPtr;
 1496297                }
 298
 1503299                _inputBufferHandle.Dispose();
 1503300            }
 1503301        }
 302
 303        public static Inflater CreateInflater(int windowBits, long uncompressedSize = -1)
 1503304        {
 1503305            Debug.Assert(windowBits >= MinWindowBits && windowBits <= MaxWindowBits);
 306
 1503307            ZLibNative.ZLibStreamHandle zlibStream = ZLibNative.ZLibStreamHandle.CreateForInflate(windowBits);
 308
 1503309            return new Inflater(windowBits, uncompressedSize, zlibStream);
 1503310        }
 311
 10208312        private unsafe bool IsInputBufferHandleAllocated => _inputBufferHandle.Pointer != default;
 313    }
 314}