< 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
0%
Covered lines: 0
Uncovered lines: 176
Coverable lines: 176
Total lines: 314
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 56
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(...)100%110%
Finished()100%110%
Inflate(...)0%220%
Inflate(...)0%660%
Inflate(...)0%220%
InflateVerified(...)0%880%
ReadOutput(...)0%880%
ResetStreamForLeftoverInput()0%440%
IsGzipStream()0%220%
NeedsInput()100%110%
NonEmptyInput()100%110%
GetAvailableInput()100%110%
SetInput(...)0%440%
SetInput(...)0%220%
Dispose(...)0%660%
Dispose()100%110%
Finalize()100%110%
ReadInflateOutput(...)100%110%
Inflate(...)0%880%
DeallocateInputBufferHandle(...)0%220%
CreateInflater(...)0%220%

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
 027        private object SyncLock => this;                    // Used to make writing to unmanaged structures atomic
 28
 029        private Inflater(int windowBits, long uncompressedSize, ZLibNative.ZLibStreamHandle zlibStream)
 030        {
 031            _finished = false;
 032            _nonEmptyInput = false;
 033            _isDisposed = false;
 034            _windowBits = windowBits;
 035            _uncompressedSize = uncompressedSize;
 036            _zlibStream = zlibStream;
 037        }
 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>
 044        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)
 070        {
 71            // If Inflate is called on an invalid or unready inflater, return 0 to indicate no bytes have been read.
 072            if (destination.Length == 0)
 073                return 0;
 74
 075            fixed (byte* bufPtr = &MemoryMarshal.GetReference(destination))
 076            {
 077                return InflateVerified(bufPtr, destination.Length);
 78            }
 079        }
 80
 81        public unsafe int InflateVerified(byte* bufPtr, int length)
 082        {
 83            // State is valid; attempt inflation
 84            try
 085            {
 086                int bytesRead = 0;
 087                if (_uncompressedSize == -1)
 088                {
 089                    ReadOutput(bufPtr, length, out bytesRead);
 090                }
 91                else
 092                {
 093                    if (_uncompressedSize > _currentInflatedCount)
 094                    {
 095                        length = (int)Math.Min(length, _uncompressedSize - _currentInflatedCount);
 096                        ReadOutput(bufPtr, length, out bytesRead);
 097                        _currentInflatedCount += bytesRead;
 098                    }
 99                    else
 0100                    {
 0101                        _finished = true;
 0102                        _zlibStream.AvailIn = 0;
 0103                    }
 0104                }
 0105                return bytesRead;
 106            }
 107            finally
 0108            {
 109                // Before returning, make sure to release input buffer if necessary:
 0110                if (0 == _zlibStream.AvailIn && IsInputBufferHandleAllocated)
 0111                {
 0112                    DeallocateInputBufferHandle(resetStreamHandle: true);
 0113                }
 0114            }
 0115        }
 116
 117        private unsafe void ReadOutput(byte* bufPtr, int length, out int bytesRead)
 0118        {
 0119            if (ReadInflateOutput(bufPtr, length, ZLibNative.FlushCode.NoFlush, out bytesRead) == ZLibNative.ErrorCode.S
 0120            {
 0121                if (!NeedsInput() && IsGzipStream() && IsInputBufferHandleAllocated)
 0122                {
 0123                    _finished = ResetStreamForLeftoverInput();
 0124                }
 125                else
 0126                {
 0127                    _finished = true;
 0128                }
 0129            }
 0130        }
 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
 0164        internal bool IsGzipStream() => _windowBits >= 24 && _windowBits <= 31;
 165
 0166        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)
 0173        {
 0174            Debug.Assert(NeedsInput(), "We have something left in previous input!");
 0175            Debug.Assert(inputBuffer != null);
 0176            Debug.Assert(startIndex >= 0 && count >= 0 && count + startIndex <= inputBuffer.Length);
 0177            Debug.Assert(!IsInputBufferHandleAllocated);
 178
 0179            SetInput(inputBuffer.AsMemory(startIndex, count));
 0180        }
 181
 182        public unsafe void SetInput(ReadOnlyMemory<byte> inputBuffer)
 0183        {
 0184            Debug.Assert(NeedsInput(), "We have something left in previous input!");
 0185            Debug.Assert(!IsInputBufferHandleAllocated);
 186
 0187            if (inputBuffer.IsEmpty)
 0188                return;
 189
 0190            lock (SyncLock)
 0191            {
 0192                _inputBufferHandle = inputBuffer.Pin();
 0193                _zlibStream.NextIn = (IntPtr)_inputBufferHandle.Pointer;
 0194                _zlibStream.AvailIn = (uint)inputBuffer.Length;
 0195                _finished = false;
 0196                _nonEmptyInput = true;
 0197            }
 0198        }
 199
 200        private void Dispose(bool disposing)
 0201        {
 0202            if (!_isDisposed)
 0203            {
 0204                if (disposing)
 0205                {
 0206                    _zlibStream.Dispose();
 0207                }
 208
 0209                if (IsInputBufferHandleAllocated)
 0210                {
 211                    // Unpin the input buffer, but avoid modifying the ZLibStreamHandle (which may have been disposed of
 0212                    DeallocateInputBufferHandle(resetStreamHandle: false);
 0213                }
 214
 0215                _isDisposed = true;
 0216            }
 0217        }
 218
 219        public void Dispose()
 0220        {
 0221            Dispose(true);
 0222            GC.SuppressFinalize(this);
 0223        }
 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, 
 0234        {
 0235            lock (SyncLock)
 0236            {
 0237                _zlibStream.NextOut = (IntPtr)bufPtr;
 0238                _zlibStream.AvailOut = (uint)length;
 239
 0240                ZLibNative.ErrorCode errC = Inflate(flushCode);
 0241                bytesRead = length - (int)_zlibStream.AvailOut;
 242
 0243                return errC;
 244            }
 0245        }
 246
 247        /// <summary>
 248        /// Wrapper around the ZLib inflate function
 249        /// </summary>
 250        private ZLibNative.ErrorCode Inflate(ZLibNative.FlushCode flushCode)
 0251        {
 252            ZLibNative.ErrorCode errC;
 253            try
 0254            {
 0255                errC = _zlibStream.Inflate(flushCode);
 0256            }
 0257            catch (Exception cause) // could not load the Zlib DLL correctly
 0258            {
 0259                throw new ZLibException(SR.ZLibErrorDLLLoadError, cause);
 260            }
 0261            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
 0265                    return errC;
 266
 267                case ZLibNative.ErrorCode.BufError:     // No room in the output buffer - inflate() can be called again 
 0268                    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 
 0274                    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            }
 0282        }
 283
 284        /// <summary>
 285        /// Frees the GCHandle being used to store the input buffer
 286        /// </summary>
 287        private void DeallocateInputBufferHandle(bool resetStreamHandle)
 0288        {
 0289            Debug.Assert(IsInputBufferHandleAllocated);
 290
 0291            lock (SyncLock)
 0292            {
 0293                if (resetStreamHandle)
 0294                {
 0295                    _zlibStream.AvailIn = 0;
 0296                    _zlibStream.NextIn = ZLibNative.ZNullPtr;
 0297                }
 298
 0299                _inputBufferHandle.Dispose();
 0300            }
 0301        }
 302
 303        public static Inflater CreateInflater(int windowBits, long uncompressedSize = -1)
 0304        {
 0305            Debug.Assert(windowBits >= MinWindowBits && windowBits <= MaxWindowBits);
 306
 0307            ZLibNative.ZLibStreamHandle zlibStream = ZLibNative.ZLibStreamHandle.CreateForInflate(windowBits);
 308
 0309            return new Inflater(windowBits, uncompressedSize, zlibStream);
 0310        }
 311
 0312        private unsafe bool IsInputBufferHandleAllocated => _inputBufferHandle.Pointer != default;
 313    }
 314}