< Summary

Information
Class: System.IO.Compression.ZstandardDecoder
Assembly: System.IO.Compression
File(s): D:\runner\runtime\src\libraries\System.IO.Compression\src\System\IO\Compression\Zstandard\ZstandardDecoder.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 215
Coverable lines: 215
Total lines: 425
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 48
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%
.ctor(...)0%220%
.ctor(...)100%110%
.ctor(...)0%440%
.ctor(...)0%220%
InitializeDecoder()0%220%
Decompress(...)0%14140%
TryGetMaxDecompressedLength(...)0%880%
TryDecompress(...)0%440%
TryDecompress(...)0%660%
Reset()100%110%
SetPrefix(...)0%440%
Dispose()100%110%
EnsureNotDisposed()100%110%
SetWindowLog(...)0%220%
SetDictionary(...)100%110%

File(s)

D:\runner\runtime\src\libraries\System.IO.Compression\src\System\IO\Compression\Zstandard\ZstandardDecoder.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.Diagnostics.CodeAnalysis;
 7using System.Runtime.CompilerServices;
 8using System.Runtime.InteropServices;
 9using Microsoft.Win32.SafeHandles;
 10
 11namespace System.IO.Compression
 12{
 13    /// <summary>Provides methods and properties to decompress data using Zstandard decompression.</summary>
 14    [System.Runtime.Versioning.UnsupportedOSPlatform("browser")]
 15    public sealed class ZstandardDecoder : IDisposable
 16    {
 17        private SafeZstdDecompressHandle _context;
 18        private bool _disposed;
 19
 20        /// <summary>
 21        /// True if we finished decompressing the entire input.
 22        /// </summary>
 23        private bool _finished;
 24
 25        /// <summary>Initializes a new instance of the <see cref="ZstandardDecoder"/> class with default settings.</summ
 26        /// <exception cref="IOException">Failed to create the <see cref="ZstandardDecoder"/> instance.</exception>
 027        public ZstandardDecoder()
 028        {
 029            _disposed = false;
 030            _finished = false;
 031            InitializeDecoder();
 032        }
 33
 34        /// <summary>Initializes a new instance of the <see cref="ZstandardDecoder"/> class with the specified maximum w
 35        /// <param name="maxWindowLog2">The maximum base-2 logarithm of the window size to use for decompression.</param
 36        /// <exception cref="ArgumentOutOfRangeException"><paramref name="maxWindowLog2"/> is not between the minimum an
 37        /// <exception cref="IOException">Failed to create the <see cref="ZstandardDecoder"/> instance.</exception>
 038        public ZstandardDecoder(int maxWindowLog2)
 039        {
 040            _disposed = false;
 41
 042            InitializeDecoder();
 43
 44            try
 045            {
 046                if (maxWindowLog2 != 0)
 047                {
 048                    SetWindowLog(maxWindowLog2);
 049                }
 050            }
 051            catch
 052            {
 053                _context.Dispose();
 054                throw;
 55            }
 056        }
 57
 58        /// <summary>Initializes a new instance of the <see cref="ZstandardDecoder"/> class with the specified dictionar
 59        /// <param name="dictionary">The decompression dictionary to use.</param>
 60        /// <exception cref="ArgumentNullException"><paramref name="dictionary"/> is null.</exception>
 61        /// <exception cref="IOException">Failed to create the <see cref="ZstandardDecoder"/> instance.</exception>
 062        public ZstandardDecoder(ZstandardDictionary dictionary)
 063        {
 064            ArgumentNullException.ThrowIfNull(dictionary);
 65
 066            _disposed = false;
 67
 068            InitializeDecoder();
 69
 70            try
 071            {
 072                SetDictionary(dictionary);
 073            }
 074            catch
 075            {
 076                _context.Dispose();
 077                throw;
 78            }
 079        }
 80
 81        /// <summary>Initializes a new instance of the <see cref="ZstandardDecoder"/> class with the specified decompres
 82        /// <param name="decompressionOptions">The options to use for Zstandard decompression.</param>
 83        /// <exception cref="ArgumentNullException"><paramref name="decompressionOptions"/> is null.</exception>
 84        /// <exception cref="IOException">Failed to create the <see cref="ZstandardDecoder"/> instance.</exception>
 085        public ZstandardDecoder(ZstandardDecompressionOptions decompressionOptions)
 086        {
 087            ArgumentNullException.ThrowIfNull(decompressionOptions);
 88
 089            _disposed = false;
 90
 091            InitializeDecoder();
 92
 93            try
 094            {
 095                if (decompressionOptions.MaxWindowLog2 != 0)
 096                {
 097                    SetWindowLog(decompressionOptions.MaxWindowLog2);
 098                }
 99
 0100                if (decompressionOptions.Dictionary is not null)
 0101                {
 0102                    SetDictionary(decompressionOptions.Dictionary);
 0103                }
 0104            }
 0105            catch
 0106            {
 0107                _context.Dispose();
 0108                throw;
 109            }
 0110        }
 111
 112        /// <summary>Initializes a new instance of the <see cref="ZstandardDecoder"/> class with the specified dictionar
 113        /// <param name="dictionary">The decompression dictionary to use.</param>
 114        /// <param name="maxWindowLog2">The maximum base-2 logarithm of the window size to use for decompression.</param
 115        /// <exception cref="ArgumentNullException"><paramref name="dictionary"/> is null.</exception>
 116        /// <exception cref="ArgumentOutOfRangeException"><paramref name="maxWindowLog2"/> is not between the minimum an
 117        /// <exception cref="IOException">Failed to create the <see cref="ZstandardDecoder"/> instance.</exception>
 0118        public ZstandardDecoder(ZstandardDictionary dictionary, int maxWindowLog2)
 0119        {
 0120            ArgumentNullException.ThrowIfNull(dictionary);
 121
 0122            _disposed = false;
 123
 0124            InitializeDecoder();
 125
 126            try
 0127            {
 0128                if (maxWindowLog2 != 0)
 0129                {
 0130                    SetWindowLog(maxWindowLog2);
 0131                }
 132
 0133                SetDictionary(dictionary);
 0134            }
 0135            catch
 0136            {
 0137                _context.Dispose();
 0138                throw;
 139            }
 0140        }
 141
 142        [MemberNotNull(nameof(_context))]
 143        private void InitializeDecoder()
 0144        {
 0145            _context = Interop.Zstd.ZSTD_createDCtx();
 0146            if (_context.IsInvalid)
 0147            {
 0148                throw new OutOfMemoryException();
 149            }
 0150        }
 151
 152        /// <summary>Decompresses the specified data.</summary>
 153        /// <param name="source">The compressed data to decompress.</param>
 154        /// <param name="destination">The buffer to write the decompressed data to.</param>
 155        /// <param name="bytesConsumed">When this method returns, contains the number of bytes consumed from the source.
 156        /// <param name="bytesWritten">When this method returns, contains the number of bytes written to the destination
 157        /// <returns>An <see cref="OperationStatus"/> indicating the result of the operation.</returns>
 158        /// <remarks>
 159        /// The method returns <see cref="OperationStatus.Done"/> after all
 160        /// contents of a single Zstandard frame are decoded and returned. However,
 161        /// Zstandard data streams may consist of multiple concatenated frames
 162        /// (some of which may even be empty). To allow processing further
 163        /// frames on the same <see cref="ZstandardDecoder" /> instance, call
 164        /// <see cref="Reset" /> before calling <see cref="Decompress" /> on the rest of the data.
 165        /// </remarks>
 166        /// <exception cref="ObjectDisposedException">The decoder has been disposed.</exception>
 167        /// <exception cref="IOException">An error occurred during decompression.</exception>
 168        public OperationStatus Decompress(ReadOnlySpan<byte> source, Span<byte> destination, out int bytesConsumed, out 
 0169        {
 0170            bytesWritten = 0;
 0171            bytesConsumed = 0;
 172
 0173            if (_finished)
 0174            {
 0175                return OperationStatus.Done;
 176            }
 177
 0178            EnsureNotDisposed();
 179
 0180            if (destination.IsEmpty)
 0181            {
 0182                return OperationStatus.DestinationTooSmall;
 183            }
 184
 185            unsafe
 0186            {
 0187                fixed (byte* sourcePtr = &MemoryMarshal.GetReference(source))
 0188                fixed (byte* destPtr = &MemoryMarshal.GetReference(destination))
 0189                {
 0190                    var input = new Interop.Zstd.ZstdInBuffer
 0191                    {
 0192                        src = sourcePtr,
 0193                        size = (nuint)source.Length,
 0194                        pos = 0
 0195                    };
 196
 0197                    var output = new Interop.Zstd.ZstdOutBuffer
 0198                    {
 0199                        dst = destPtr,
 0200                        size = (nuint)destination.Length,
 0201                        pos = 0
 0202                    };
 203
 0204                    nuint result = Interop.Zstd.ZSTD_decompressStream(_context, ref output, ref input);
 205
 0206                    if (ZstandardUtils.IsError(result, out var error))
 0207                    {
 0208                        switch (error)
 209                        {
 210                            // These specific errors are actionable by the caller and don't imply
 211                            // that the data itself is corrupt.
 212                            case Interop.Zstd.ZSTD_error.frameParameter_windowTooLarge:
 213                            case Interop.Zstd.ZSTD_error.dictionary_wrong:
 0214                                ZstandardUtils.Throw(error);
 215                                break;
 216
 217                            default:
 0218                                return OperationStatus.InvalidData;
 219                        }
 220                    }
 221
 0222                    bytesConsumed = (int)input.pos;
 0223                    bytesWritten = (int)output.pos;
 224
 0225                    if (result == 0)
 0226                    {
 0227                        _finished = true;
 0228                        return OperationStatus.Done;
 229                    }
 0230                    else if (output.pos == output.size)
 0231                    {
 0232                        return OperationStatus.DestinationTooSmall;
 233                    }
 234                    else
 0235                    {
 0236                        return OperationStatus.NeedMoreData;
 237                    }
 238                }
 239            }
 0240        }
 241
 242        /// <summary>Attempts to get the maximum decompressed length for the specified compressed data.</summary>
 243        /// <param name="data">The compressed data.</param>
 244        /// <param name="length">When this method returns <see langword="true" />, contains the maximum decompressed len
 245        /// <returns><see langword="true" /> on success; <see langword="false" /> otherwise.</returns>
 246        public static bool TryGetMaxDecompressedLength(ReadOnlySpan<byte> data, out long length)
 0247        {
 0248            if (data.IsEmpty)
 0249            {
 0250                length = 0;
 0251                return true;
 252            }
 253
 254            unsafe
 0255            {
 0256                fixed (byte* dataPtr = &MemoryMarshal.GetReference(data))
 0257                {
 0258                    ulong frameContentSize = Interop.Zstd.ZSTD_decompressBound(dataPtr, (nuint)data.Length);
 259
 260                    const ulong ZSTD_CONTENTSIZE_UNKNOWN = unchecked(0UL - 1);
 261                    const ulong ZSTD_CONTENTSIZE_ERROR = unchecked(0UL - 2);
 0262                    if (frameContentSize == ZSTD_CONTENTSIZE_UNKNOWN || frameContentSize == ZSTD_CONTENTSIZE_ERROR || fr
 0263                    {
 0264                        length = 0;
 0265                        return false;
 266                    }
 267
 0268                    length = (long)frameContentSize;
 0269                    return true;
 270                }
 271            }
 0272        }
 273
 274        /// <summary>Attempts to decompress the specified data.</summary>
 275        /// <param name="source">The compressed data to decompress.</param>
 276        /// <param name="destination">The buffer to write the decompressed data to.</param>
 277        /// <param name="bytesWritten">When this method returns <see langword="true" />, contains the number of bytes wr
 278        /// <returns><see langword="true" /> on success; <see langword="false" /> otherwise.</returns>
 279        /// <remarks>If this method returns <see langword="false" />, <paramref name="destination" /> may be empty or co
 280        public static bool TryDecompress(ReadOnlySpan<byte> source, Span<byte> destination, out int bytesWritten)
 0281        {
 0282            bytesWritten = 0;
 283
 0284            if (source.IsEmpty)
 0285            {
 0286                return false;
 287            }
 288
 289            unsafe
 0290            {
 0291                fixed (byte* sourcePtr = &MemoryMarshal.GetReference(source))
 0292                fixed (byte* destPtr = &MemoryMarshal.GetReference(destination))
 0293                {
 0294                    nuint result = Interop.Zstd.ZSTD_decompress(
 0295                        destPtr, (nuint)destination.Length,
 0296                        sourcePtr, (nuint)source.Length);
 297
 0298                    if (ZstandardUtils.IsError(result, out var error))
 0299                    {
 0300                        return false;
 301                    }
 302
 0303                    bytesWritten = (int)result;
 0304                    return true;
 305                }
 306            }
 0307        }
 308
 309        /// <summary>Attempts to decompress the specified data using the specified dictionary.</summary>
 310        /// <param name="source">The compressed data to decompress.</param>
 311        /// <param name="dictionary">The decompression dictionary to use.</param>
 312        /// <param name="destination">The buffer to write the decompressed data to.</param>
 313        /// <param name="bytesWritten">When this method returns <see langword="true" />, contains the number of bytes wr
 314        /// <returns><see langword="true" /> on success; <see langword="false" /> otherwise.</returns>
 315        /// <exception cref="ArgumentNullException"><paramref name="dictionary"/> is null.</exception>
 316        /// <remarks>If this method returns <see langword="false" />, <paramref name="destination" /> may be empty or co
 317        public static bool TryDecompress(ReadOnlySpan<byte> source, Span<byte> destination, out int bytesWritten, Zstand
 0318        {
 0319            ArgumentNullException.ThrowIfNull(dictionary);
 320
 0321            bytesWritten = 0;
 322
 0323            if (source.IsEmpty)
 0324            {
 0325                return false;
 326            }
 327
 0328            using var dctx = Interop.Zstd.ZSTD_createDCtx();
 0329            if (dctx.IsInvalid)
 0330            {
 0331                throw new OutOfMemoryException();
 332            }
 333
 334            unsafe
 0335            {
 0336                fixed (byte* sourcePtr = &MemoryMarshal.GetReference(source))
 0337                fixed (byte* destPtr = &MemoryMarshal.GetReference(destination))
 0338                {
 0339                    nuint result = Interop.Zstd.ZSTD_decompress_usingDDict(
 0340                        dctx, destPtr, (nuint)destination.Length,
 0341                        sourcePtr, (nuint)source.Length, dictionary.DecompressionDictionary);
 342
 0343                    if (ZstandardUtils.IsError(result, out var error))
 0344                    {
 0345                        return false;
 346                    }
 347
 0348                    bytesWritten = checked((int)result);
 0349                    return true;
 350                }
 351            }
 0352        }
 353
 354        /// <summary>Resets the decoder session, allowing reuse for decompressing the next Zstandard frame.</summary>
 355        /// <remarks>Clears any prefix and restores the dictionary supplied when the decoder was constructed.</remarks>
 356        /// <exception cref="ObjectDisposedException">The decoder has been disposed.</exception>
 357        /// <exception cref="IOException">Failed to reset the decoder session.</exception>
 358        public void Reset()
 0359        {
 0360            EnsureNotDisposed();
 361
 0362            _context.Reset();
 0363            _finished = false;
 0364        }
 365
 366        /// <summary>References a prefix for the next decompression operation.</summary>
 367        /// <remarks>
 368        /// The prefix will be used only for the next decompression frame and will be removed when <see cref="Reset"/> i
 369        /// The prefix replaces any dictionary supplied when the decoder was constructed until <see cref="Reset"/> resto
 370        /// The referenced data must remain valid and unmodified for the duration of the decompression operation.
 371        /// </remarks>
 372        /// <exception cref="ObjectDisposedException">The decoder has been disposed.</exception>
 373        /// <exception cref="InvalidOperationException">The decoder is in an invalid state for setting a prefix.</except
 374        public void SetPrefix(ReadOnlyMemory<byte> prefix)
 0375        {
 0376            EnsureNotDisposed();
 377
 0378            if (_finished)
 0379            {
 0380                throw new InvalidOperationException(SR.ZstandardEncoderDecoder_InvalidState);
 381            }
 382
 0383            nuint result = _context.SetPrefix(prefix);
 384
 0385            if (ZstandardUtils.IsError(result, out var error))
 0386            {
 0387                ZstandardUtils.Throw(error);
 388            }
 0389        }
 390
 391        /// <summary>Releases all resources used by the <see cref="ZstandardDecoder"/>.</summary>
 392        public void Dispose()
 0393        {
 0394            _disposed = true;
 0395            _context.Dispose();
 0396        }
 397
 398        private void EnsureNotDisposed()
 0399        {
 0400            ObjectDisposedException.ThrowIf(_disposed, this);
 0401        }
 402
 403        internal void SetWindowLog(int maxWindowLog2)
 0404        {
 0405            Debug.Assert(_context != null);
 406
 0407            if (maxWindowLog2 != 0)
 0408            {
 0409                ArgumentOutOfRangeException.ThrowIfLessThan(maxWindowLog2, ZstandardUtils.WindowLog_Min, nameof(maxWindo
 0410                ArgumentOutOfRangeException.ThrowIfGreaterThan(maxWindowLog2, ZstandardUtils.WindowLog_Max, nameof(maxWi
 0411            }
 412
 0413            nuint result = Interop.Zstd.ZSTD_DCtx_setParameter(_context, Interop.Zstd.ZstdDParameter.ZSTD_d_windowLogMax
 0414            ZstandardUtils.ThrowIfError(result);
 0415        }
 416
 417        internal void SetDictionary(ZstandardDictionary dictionary)
 0418        {
 0419            Debug.Assert(_context != null);
 0420            ArgumentNullException.ThrowIfNull(dictionary);
 421
 0422            _context.SetDictionary(dictionary.DecompressionDictionary);
 0423        }
 424    }
 425}