< 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
71%
Covered lines: 126
Uncovered lines: 50
Coverable lines: 176
Total lines: 314
Line coverage: 71.5%
Branch coverage
51%
Covered branches: 29
Total branches: 56
Branch coverage: 51.7%
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(...)0%660%
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
 322027        private object SyncLock => this;                    // Used to make writing to unmanaged structures atomic
 28
 80529        private Inflater(int windowBits, long uncompressedSize, ZLibNative.ZLibStreamHandle zlibStream)
 80530        {
 80531            _finished = false;
 80532            _nonEmptyInput = false;
 80533            _isDisposed = false;
 80534            _windowBits = windowBits;
 80535            _uncompressedSize = uncompressedSize;
 80536            _zlibStream = zlibStream;
 80537        }
 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>
 241344        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)
 057        {
 58            // If Inflate is called on an invalid or unready inflater, return 0 to indicate no bytes have been read.
 059            if (length == 0)
 060                return 0;
 61
 062            Debug.Assert(null != bytes, "Can't pass in a null output buffer!");
 063            fixed (byte* bufPtr = bytes)
 064            {
 065                return InflateVerified(bufPtr + offset, length);
 66            }
 067        }
 68
 69        public unsafe int Inflate(Span<byte> destination)
 241470        {
 71            // If Inflate is called on an invalid or unready inflater, return 0 to indicate no bytes have been read.
 241472            if (destination.Length == 0)
 073                return 0;
 74
 241475            fixed (byte* bufPtr = &MemoryMarshal.GetReference(destination))
 241476            {
 241477                return InflateVerified(bufPtr, destination.Length);
 78            }
 241379        }
 80
 81        public unsafe int InflateVerified(byte* bufPtr, int length)
 241482        {
 83            // State is valid; attempt inflation
 84            try
 241485            {
 241486                int bytesRead = 0;
 241487                if (_uncompressedSize == -1)
 088                {
 089                    ReadOutput(bufPtr, length, out bytesRead);
 090                }
 91                else
 241492                {
 241493                    if (_uncompressedSize > _currentInflatedCount)
 161094                    {
 161095                        length = (int)Math.Min(length, _uncompressedSize - _currentInflatedCount);
 161096                        ReadOutput(bufPtr, length, out bytesRead);
 160997                        _currentInflatedCount += bytesRead;
 160998                    }
 99                    else
 804100                    {
 804101                        _finished = true;
 804102                        _zlibStream.AvailIn = 0;
 804103                    }
 2413104                }
 2413105                return bytesRead;
 106            }
 107            finally
 2414108            {
 109                // Before returning, make sure to release input buffer if necessary:
 2414110                if (0 == _zlibStream.AvailIn && IsInputBufferHandleAllocated)
 804111                {
 804112                    DeallocateInputBufferHandle(resetStreamHandle: true);
 804113                }
 2414114            }
 2413115        }
 116
 117        private unsafe void ReadOutput(byte* bufPtr, int length, out int bytesRead)
 1610118        {
 1610119            if (ReadInflateOutput(bufPtr, length, ZLibNative.FlushCode.NoFlush, out bytesRead) == ZLibNative.ErrorCode.S
 804120            {
 804121                if (!NeedsInput() && IsGzipStream() && IsInputBufferHandleAllocated)
 0122                {
 0123                    _finished = ResetStreamForLeftoverInput();
 0124                }
 125                else
 804126                {
 804127                    _finished = true;
 804128                }
 804129            }
 1609130        }
 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
 1608164        internal bool IsGzipStream() => _windowBits >= 24 && _windowBits <= 31;
 165
 3219166        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)
 805173        {
 805174            Debug.Assert(NeedsInput(), "We have something left in previous input!");
 805175            Debug.Assert(inputBuffer != null);
 805176            Debug.Assert(startIndex >= 0 && count >= 0 && count + startIndex <= inputBuffer.Length);
 805177            Debug.Assert(!IsInputBufferHandleAllocated);
 178
 805179            SetInput(inputBuffer.AsMemory(startIndex, count));
 805180        }
 181
 182        public unsafe void SetInput(ReadOnlyMemory<byte> inputBuffer)
 805183        {
 805184            Debug.Assert(NeedsInput(), "We have something left in previous input!");
 805185            Debug.Assert(!IsInputBufferHandleAllocated);
 186
 805187            if (inputBuffer.IsEmpty)
 0188                return;
 189
 805190            lock (SyncLock)
 805191            {
 805192                _inputBufferHandle = inputBuffer.Pin();
 805193                _zlibStream.NextIn = (IntPtr)_inputBufferHandle.Pointer;
 805194                _zlibStream.AvailIn = (uint)inputBuffer.Length;
 805195                _finished = false;
 805196                _nonEmptyInput = true;
 805197            }
 805198        }
 199
 200        private void Dispose(bool disposing)
 805201        {
 805202            if (!_isDisposed)
 805203            {
 805204                if (disposing)
 805205                {
 805206                    _zlibStream.Dispose();
 805207                }
 208
 805209                if (IsInputBufferHandleAllocated)
 1210                {
 211                    // Unpin the input buffer, but avoid modifying the ZLibStreamHandle (which may have been disposed of
 1212                    DeallocateInputBufferHandle(resetStreamHandle: false);
 1213                }
 214
 805215                _isDisposed = true;
 805216            }
 805217        }
 218
 219        public void Dispose()
 805220        {
 805221            Dispose(true);
 805222            GC.SuppressFinalize(this);
 805223        }
 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, 
 1610234        {
 1610235            lock (SyncLock)
 1610236            {
 1610237                _zlibStream.NextOut = (IntPtr)bufPtr;
 1610238                _zlibStream.AvailOut = (uint)length;
 239
 1610240                ZLibNative.ErrorCode errC = Inflate(flushCode);
 1609241                bytesRead = length - (int)_zlibStream.AvailOut;
 242
 1609243                return errC;
 244            }
 1609245        }
 246
 247        /// <summary>
 248        /// Wrapper around the ZLib inflate function
 249        /// </summary>
 250        private ZLibNative.ErrorCode Inflate(ZLibNative.FlushCode flushCode)
 1610251        {
 252            ZLibNative.ErrorCode errC;
 253            try
 1610254            {
 1610255                errC = _zlibStream.Inflate(flushCode);
 1610256            }
 0257            catch (Exception cause) // could not load the Zlib DLL correctly
 0258            {
 0259                throw new ZLibException(SR.ZLibErrorDLLLoadError, cause);
 260            }
 1610261            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
 804265                    return errC;
 266
 267                case ZLibNative.ErrorCode.BufError:     // No room in the output buffer - inflate() can be called again 
 805268                    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 
 1274                    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            }
 1609282        }
 283
 284        /// <summary>
 285        /// Frees the GCHandle being used to store the input buffer
 286        /// </summary>
 287        private void DeallocateInputBufferHandle(bool resetStreamHandle)
 805288        {
 805289            Debug.Assert(IsInputBufferHandleAllocated);
 290
 805291            lock (SyncLock)
 805292            {
 805293                if (resetStreamHandle)
 804294                {
 804295                    _zlibStream.AvailIn = 0;
 804296                    _zlibStream.NextIn = ZLibNative.ZNullPtr;
 804297                }
 298
 805299                _inputBufferHandle.Dispose();
 805300            }
 805301        }
 302
 303        public static Inflater CreateInflater(int windowBits, long uncompressedSize = -1)
 805304        {
 805305            Debug.Assert(windowBits >= MinWindowBits && windowBits <= MaxWindowBits);
 306
 805307            ZLibNative.ZLibStreamHandle zlibStream = ZLibNative.ZLibStreamHandle.CreateForInflate(windowBits);
 308
 805309            return new Inflater(windowBits, uncompressedSize, zlibStream);
 805310        }
 311
 5633312        private unsafe bool IsInputBufferHandleAllocated => _inputBufferHandle.Pointer != default;
 313    }
 314}