< Summary

Information
Class: System.IO.Compression.Deflater
Assembly: System.IO.Compression
File(s): D:\runner\runtime\src\libraries\System.IO.Compression\src\System\IO\Compression\DeflateZLib\Deflater.cs
Line coverage
86%
Covered lines: 95
Uncovered lines: 15
Coverable lines: 110
Total lines: 208
Line coverage: 86.3%
Branch coverage
50%
Covered branches: 10
Total branches: 20
Branch coverage: 50%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor(...)100%11100%
Finalize()100%110%
Dispose()100%11100%
Dispose(...)50%44100%
NeedsInput()100%11100%
SetInput(...)100%11100%
SetInput(...)100%11100%
GetDeflateOutput(...)100%22100%
ReadDeflateOutput(...)50%22100%
Finish(...)100%11100%
Flush(...)100%110%
DeallocateInputBufferHandle(...)100%22100%
Deflate(...)25%8853.84%
CreateDeflater(...)50%22100%

File(s)

D:\runner\runtime\src\libraries\System.IO.Compression\src\System\IO\Compression\DeflateZLib\Deflater.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;
 6
 7using ZErrorCode = System.IO.Compression.ZLibNative.ErrorCode;
 8using ZFlushCode = System.IO.Compression.ZLibNative.FlushCode;
 9
 10namespace System.IO.Compression
 11{
 12    /// <summary>
 13    /// Provides a wrapper around the ZLib compression API.
 14    /// </summary>
 15    internal sealed class Deflater : IDisposable
 16    {
 17        private readonly ZLibNative.ZLibStreamHandle _zlibStream;
 18        private MemoryHandle _inputBufferHandle;
 19        private bool _isDisposed;
 20        private const int minWindowBits = -15;  // WindowBits must be between -8..-15 to write no header, 8..15 for a
 21        private const int maxWindowBits = 31;   // zlib header, or 24..31 for a GZip header
 22
 23        // Note, DeflateStream or the deflater do not try to be thread safe.
 24        // The lock is just used to make writing to unmanaged structures atomic to make sure
 25        // that they do not get inconsistent fields that may lead to an unmanaged memory violation.
 26        // To prevent *managed* buffer corruption or other weird behavior users need to synchronize
 27        // on the stream explicitly.
 646028        private object SyncLock => this;
 29
 129230        private Deflater(ZLibNative.ZLibStreamHandle zlibStream)
 129231        {
 129232            _zlibStream = zlibStream;
 129233        }
 34
 35        ~Deflater()
 036        {
 037            Dispose(false);
 038        }
 39
 40        public void Dispose()
 129241        {
 129242            Dispose(true);
 129243            GC.SuppressFinalize(this);
 129244        }
 45
 46        private void Dispose(bool disposing)
 129247        {
 129248            if (!_isDisposed)
 129249            {
 129250                if (disposing)
 129251                {
 129252                    _zlibStream.Dispose();
 129253                }
 54
 55                // Unpin the input buffer, but avoid modifying the ZLibStreamHandle (which may have been disposed of).
 129256                DeallocateInputBufferHandle(resetStreamHandle: false);
 129257                _isDisposed = true;
 129258            }
 129259        }
 60
 775261        public bool NeedsInput() => 0 == _zlibStream.AvailIn;
 62
 63        internal unsafe void SetInput(ReadOnlyMemory<byte> inputBuffer)
 64664        {
 64665            Debug.Assert(NeedsInput(), "We have something left in previous input!");
 64666            Debug.Assert(!inputBuffer.IsEmpty);
 67
 64668            lock (SyncLock)
 64669            {
 64670                _inputBufferHandle = inputBuffer.Pin();
 71
 64672                _zlibStream.NextIn = (IntPtr)_inputBufferHandle.Pointer;
 64673                _zlibStream.AvailIn = (uint)inputBuffer.Length;
 64674            }
 64675        }
 76
 77        internal unsafe void SetInput(byte* inputBufferPtr, int count)
 64678        {
 64679            Debug.Assert(NeedsInput(), "We have something left in previous input!");
 64680            Debug.Assert(inputBufferPtr != null);
 64681            Debug.Assert(count > 0);
 82
 64683            lock (SyncLock)
 64684            {
 64685                _zlibStream.NextIn = (IntPtr)inputBufferPtr;
 64686                _zlibStream.AvailIn = (uint)count;
 64687            }
 64688        }
 89
 90        internal int GetDeflateOutput(byte[] outputBuffer)
 129291        {
 129292            Debug.Assert(null != outputBuffer, "Can't pass in a null output buffer!");
 129293            Debug.Assert(!NeedsInput(), "GetDeflateOutput should only be called after providing input");
 94
 95            try
 129296            {
 97                int bytesRead;
 129298                ReadDeflateOutput(outputBuffer, ZFlushCode.NoFlush, out bytesRead);
 129299                return bytesRead;
 100            }
 101            finally
 1292102            {
 103                // Before returning, make sure to release input buffer if necessary:
 1292104                if (0 == _zlibStream.AvailIn)
 1292105                {
 1292106                    DeallocateInputBufferHandle(resetStreamHandle: true);
 1292107                }
 1292108            }
 1292109        }
 110
 111        private unsafe ZErrorCode ReadDeflateOutput(byte[] outputBuffer, ZFlushCode flushCode, out int bytesRead)
 2584112        {
 2584113            Debug.Assert(outputBuffer?.Length > 0);
 114
 2584115            lock (SyncLock)
 2584116            {
 2584117                fixed (byte* bufPtr = &outputBuffer[0])
 2584118                {
 2584119                    _zlibStream.NextOut = (IntPtr)bufPtr;
 2584120                    _zlibStream.AvailOut = (uint)outputBuffer.Length;
 121
 2584122                    ZErrorCode errC = Deflate(flushCode);
 2584123                    bytesRead = outputBuffer.Length - (int)_zlibStream.AvailOut;
 124
 2584125                    return errC;
 126                }
 127            }
 2584128        }
 129
 130        internal bool Finish(byte[] outputBuffer, out int bytesRead)
 1292131        {
 1292132            Debug.Assert(null != outputBuffer, "Can't pass in a null output buffer!");
 1292133            Debug.Assert(outputBuffer.Length > 0, "Can't pass in an empty output buffer!");
 134
 1292135            ZErrorCode errC = ReadDeflateOutput(outputBuffer, ZFlushCode.Finish, out bytesRead);
 1292136            return errC == ZErrorCode.StreamEnd;
 1292137        }
 138
 139        /// <summary>
 140        /// Returns true if there was something to flush. Otherwise False.
 141        /// </summary>
 142        internal bool Flush(byte[] outputBuffer, out int bytesRead)
 0143        {
 0144            Debug.Assert(null != outputBuffer, "Can't pass in a null output buffer!");
 0145            Debug.Assert(outputBuffer.Length > 0, "Can't pass in an empty output buffer!");
 0146            Debug.Assert(NeedsInput(), "We have something left in previous input!");
 147
 148
 149            // Note: we require that NeedsInput() == true, i.e. that 0 == _zlibStream.AvailIn.
 150            // If there is still input left we should never be getting here; instead we
 151            // should be calling GetDeflateOutput.
 152
 0153            return ReadDeflateOutput(outputBuffer, ZFlushCode.SyncFlush, out bytesRead) == ZErrorCode.Ok;
 0154        }
 155
 156        private void DeallocateInputBufferHandle(bool resetStreamHandle)
 2584157        {
 2584158            lock (SyncLock)
 2584159            {
 2584160                if (resetStreamHandle)
 1292161                {
 1292162                    _zlibStream.AvailIn = 0;
 1292163                    _zlibStream.NextIn = ZLibNative.ZNullPtr;
 1292164                }
 165
 2584166                _inputBufferHandle.Dispose();
 2584167            }
 2584168        }
 169
 170        private ZErrorCode Deflate(ZFlushCode flushCode)
 2584171        {
 172            ZErrorCode errC;
 173            try
 2584174            {
 2584175                errC = _zlibStream.Deflate(flushCode);
 2584176            }
 0177            catch (Exception cause)
 0178            {
 0179                throw new ZLibException(SR.ZLibErrorDLLLoadError, cause);
 180            }
 181
 2584182            switch (errC)
 183            {
 184                case ZErrorCode.Ok:
 185                case ZErrorCode.StreamEnd:
 2584186                    return errC;
 187
 188                case ZErrorCode.BufError:
 0189                    return errC;  // This is a recoverable error
 190
 191                case ZErrorCode.StreamError:
 0192                    throw new ZLibException(SR.ZLibErrorInconsistentStream, "deflate", (int)errC, _zlibStream.GetErrorMe
 193
 194                default:
 0195                    throw new ZLibException(SR.ZLibErrorUnexpected, "deflate", (int)errC, _zlibStream.GetErrorMessage())
 196            }
 2584197        }
 198
 199        public static Deflater CreateDeflater(ZLibNative.CompressionLevel compressionLevel, ZLibNative.CompressionStrate
 1292200        {
 1292201            Debug.Assert(windowBits >= minWindowBits && windowBits <= maxWindowBits);
 202
 1292203            ZLibNative.ZLibStreamHandle zlibStream = ZLibNative.ZLibStreamHandle.CreateForDeflate(compressionLevel, wind
 204
 1292205            return new Deflater(zlibStream);
 1292206        }
 207    }
 208}