< Summary

Line coverage
0%
Covered lines: 0
Uncovered lines: 122
Coverable lines: 122
Total lines: 235
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 22
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%
Finalize()100%110%
Dispose()100%110%
Dispose(...)0%440%
NeedsInput()100%110%
SetInput(...)100%110%
SetInput(...)100%110%
GetDeflateOutput(...)0%220%
ReadDeflateOutput(...)0%220%
Finish(...)100%110%
Flush(...)100%110%
UnsetInput()0%220%
DeallocateInputBufferHandle(...)100%110%
DeallocateInputBufferHandleCore(...)0%220%
Deflate(...)0%880%
CreateDeflater(...)0%220%

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/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.
 028        private object SyncLock => this;
 29
 030        private Deflater(ZLibNative.ZLibStreamHandle zlibStream)
 031        {
 032            _zlibStream = zlibStream;
 033        }
 34
 35        ~Deflater()
 036        {
 037            Dispose(false);
 038        }
 39
 40        public void Dispose()
 041        {
 042            Dispose(true);
 043            GC.SuppressFinalize(this);
 044        }
 45
 46        private void Dispose(bool disposing)
 047        {
 048            if (!_isDisposed)
 049            {
 050                if (disposing)
 051                {
 052                    _zlibStream.Dispose();
 053                }
 54
 55                // Unpin the input buffer, but avoid modifying the ZLibStreamHandle (which may have been disposed of).
 056                DeallocateInputBufferHandle(resetStreamHandle: false);
 057                _isDisposed = true;
 058            }
 059        }
 60
 061        public bool NeedsInput() => 0 == _zlibStream.AvailIn;
 62
 63        internal unsafe void SetInput(ReadOnlyMemory<byte> inputBuffer)
 064        {
 065            Debug.Assert(NeedsInput(), "We have something left in previous input!");
 066            Debug.Assert(!inputBuffer.IsEmpty);
 67
 068            lock (SyncLock)
 069            {
 070                _inputBufferHandle = inputBuffer.Pin();
 71
 072                _zlibStream.NextIn = (IntPtr)_inputBufferHandle.Pointer;
 073                _zlibStream.AvailIn = (uint)inputBuffer.Length;
 074            }
 075        }
 76
 77        internal unsafe void SetInput(byte* inputBufferPtr, int count)
 078        {
 079            Debug.Assert(NeedsInput(), "We have something left in previous input!");
 080            Debug.Assert(inputBufferPtr != null);
 081            Debug.Assert(count > 0);
 82
 083            lock (SyncLock)
 084            {
 085                _zlibStream.NextIn = (IntPtr)inputBufferPtr;
 086                _zlibStream.AvailIn = (uint)count;
 087            }
 088        }
 89
 90        internal int GetDeflateOutput(byte[] outputBuffer)
 091        {
 092            Debug.Assert(null != outputBuffer, "Can't pass in a null output buffer!");
 093            Debug.Assert(!NeedsInput(), "GetDeflateOutput should only be called after providing input");
 94
 95            try
 096            {
 97                int bytesRead;
 098                ReadDeflateOutput(outputBuffer, ZFlushCode.NoFlush, out bytesRead);
 099                return bytesRead;
 100            }
 101            finally
 0102            {
 103                // Before returning, make sure to release input buffer if necessary:
 0104                if (0 == _zlibStream.AvailIn)
 0105                {
 0106                    DeallocateInputBufferHandle(resetStreamHandle: true);
 0107                }
 0108            }
 0109        }
 110
 111        private unsafe ZErrorCode ReadDeflateOutput(byte[] outputBuffer, ZFlushCode flushCode, out int bytesRead)
 0112        {
 0113            Debug.Assert(outputBuffer?.Length > 0);
 114
 0115            lock (SyncLock)
 0116            {
 0117                fixed (byte* bufPtr = &outputBuffer[0])
 0118                {
 0119                    _zlibStream.NextOut = (IntPtr)bufPtr;
 0120                    _zlibStream.AvailOut = (uint)outputBuffer.Length;
 121
 0122                    ZErrorCode errC = Deflate(flushCode);
 0123                    bytesRead = outputBuffer.Length - (int)_zlibStream.AvailOut;
 124
 0125                    return errC;
 126                }
 127            }
 0128        }
 129
 130        internal bool Finish(byte[] outputBuffer, out int bytesRead)
 0131        {
 0132            Debug.Assert(null != outputBuffer, "Can't pass in a null output buffer!");
 0133            Debug.Assert(outputBuffer.Length > 0, "Can't pass in an empty output buffer!");
 134
 0135            ZErrorCode errC = ReadDeflateOutput(outputBuffer, ZFlushCode.Finish, out bytesRead);
 0136            return errC == ZErrorCode.StreamEnd;
 0137        }
 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        /// <summary>
 157        /// Discards any unconsumed input previously set via SetInput, releasing the pinned reference (if any).
 158        /// Must be called if an in-progress operation is abandoned (e.g. due to an exception or cancellation) so
 159        /// the deflater doesn't retain a dangling reference to a buffer the caller may have since reused or freed.
 160        /// </summary>
 161        internal void UnsetInput()
 0162        {
 0163            lock (SyncLock)
 0164            {
 165                // On the common success path all input has already been consumed (NeedsInput() is true),
 166                // so there is nothing to abandon and no cleanup is necessary. Only when an operation is
 167                // abandoned mid-input do we need to reset state and release any pinned reference. This check
 168                // must happen under the lock so it can't race with a concurrent SetInput/Dispose call.
 0169                if (!NeedsInput())
 0170                {
 0171                    DeallocateInputBufferHandleCore(resetStreamHandle: true);
 0172                }
 0173            }
 0174        }
 175
 176        private void DeallocateInputBufferHandle(bool resetStreamHandle)
 0177        {
 0178            lock (SyncLock)
 0179            {
 0180                DeallocateInputBufferHandleCore(resetStreamHandle);
 0181            }
 0182        }
 183
 184        // Must be called while holding SyncLock.
 185        private void DeallocateInputBufferHandleCore(bool resetStreamHandle)
 0186        {
 0187            if (resetStreamHandle)
 0188            {
 0189                _zlibStream.AvailIn = 0;
 0190                _zlibStream.NextIn = ZLibNative.ZNullPtr;
 0191            }
 192
 0193            _inputBufferHandle.Dispose();
 0194        }
 195
 196        private ZErrorCode Deflate(ZFlushCode flushCode)
 0197        {
 198            ZErrorCode errC;
 199            try
 0200            {
 0201                errC = _zlibStream.Deflate(flushCode);
 0202            }
 0203            catch (Exception cause)
 0204            {
 0205                throw new ZLibException(SR.ZLibErrorDLLLoadError, cause);
 206            }
 207
 0208            switch (errC)
 209            {
 210                case ZErrorCode.Ok:
 211                case ZErrorCode.StreamEnd:
 0212                    return errC;
 213
 214                case ZErrorCode.BufError:
 0215                    return errC;  // This is a recoverable error
 216
 217                case ZErrorCode.StreamError:
 0218                    throw new ZLibException(SR.ZLibErrorInconsistentStream, "deflate", (int)errC, _zlibStream.GetErrorMe
 219
 220                default:
 0221                    throw new ZLibException(SR.ZLibErrorUnexpected, "deflate", (int)errC, _zlibStream.GetErrorMessage())
 222            }
 0223        }
 224
 225        public static Deflater CreateDeflater(ZLibNative.CompressionLevel compressionLevel, ZLibNative.CompressionStrate
 0226        {
 0227            Debug.Assert(windowBits >= minWindowBits && windowBits <= maxWindowBits);
 228
 0229            ZLibNative.ZLibStreamHandle zlibStream = ZLibNative.ZLibStreamHandle.CreateForDeflate(compressionLevel, wind
 230
 0231            return new Deflater(zlibStream);
 0232        }
 233    }
 234}
 235