< 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: 421
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    [System.Runtime.Versioning.UnsupportedOSPlatform("wasi")]
 16    public sealed class ZstandardDecoder : IDisposable
 17    {
 18        private SafeZstdDecompressHandle _context;
 19        private bool _disposed;
 20
 21        /// <summary>
 22        /// True if we finished decompressing the entire input.
 23        /// </summary>
 24        private bool _finished;
 25
 26        /// <summary>Initializes a new instance of the <see cref="ZstandardDecoder"/> class with default settings.</summ
 27        /// <exception cref="IOException">Failed to create the <see cref="ZstandardDecoder"/> instance.</exception>
 028        public ZstandardDecoder()
 029        {
 030            _disposed = false;
 031            _finished = false;
 032            InitializeDecoder();
 033        }
 34
 35        /// <summary>Initializes a new instance of the <see cref="ZstandardDecoder"/> class with the specified maximum w
 36        /// <param name="maxWindowLog2">The maximum base-2 logarithm of the window size to use for decompression.</param
 37        /// <exception cref="ArgumentOutOfRangeException"><paramref name="maxWindowLog2"/> is not between the minimum an
 38        /// <exception cref="IOException">Failed to create the <see cref="ZstandardDecoder"/> instance.</exception>
 039        public ZstandardDecoder(int maxWindowLog2)
 040        {
 041            _disposed = false;
 42
 043            InitializeDecoder();
 44
 45            try
 046            {
 047                if (maxWindowLog2 != 0)
 048                {
 049                    SetWindowLog(maxWindowLog2);
 050                }
 051            }
 052            catch
 053            {
 054                _context.Dispose();
 055                throw;
 56            }
 057        }
 58
 59        /// <summary>Initializes a new instance of the <see cref="ZstandardDecoder"/> class with the specified dictionar
 60        /// <param name="dictionary">The decompression dictionary to use.</param>
 61        /// <exception cref="ArgumentNullException"><paramref name="dictionary"/> is null.</exception>
 62        /// <exception cref="IOException">Failed to create the <see cref="ZstandardDecoder"/> instance.</exception>
 063        public ZstandardDecoder(ZstandardDictionary dictionary)
 064        {
 065            ArgumentNullException.ThrowIfNull(dictionary);
 66
 067            _disposed = false;
 68
 069            InitializeDecoder();
 70
 71            try
 072            {
 073                SetDictionary(dictionary);
 074            }
 075            catch
 076            {
 077                _context.Dispose();
 078                throw;
 79            }
 080        }
 81
 82        /// <summary>Initializes a new instance of the <see cref="ZstandardDecoder"/> class with the specified decompres
 83        /// <param name="decompressionOptions">The options to use for Zstandard decompression.</param>
 84        /// <exception cref="ArgumentNullException"><paramref name="decompressionOptions"/> is null.</exception>
 85        /// <exception cref="IOException">Failed to create the <see cref="ZstandardDecoder"/> instance.</exception>
 086        public ZstandardDecoder(ZstandardDecompressionOptions decompressionOptions)
 087        {
 088            ArgumentNullException.ThrowIfNull(decompressionOptions);
 89
 090            _disposed = false;
 91
 092            InitializeDecoder();
 93
 94            try
 095            {
 096                if (decompressionOptions.MaxWindowLog2 != 0)
 097                {
 098                    SetWindowLog(decompressionOptions.MaxWindowLog2);
 099                }
 100
 0101                if (decompressionOptions.Dictionary is not null)
 0102                {
 0103                    SetDictionary(decompressionOptions.Dictionary);
 0104                }
 0105            }
 0106            catch
 0107            {
 0108                _context.Dispose();
 0109                throw;
 110            }
 0111        }
 112
 113        /// <summary>Initializes a new instance of the <see cref="ZstandardDecoder"/> class with the specified dictionar
 114        /// <param name="dictionary">The decompression dictionary to use.</param>
 115        /// <param name="maxWindowLog2">The maximum base-2 logarithm of the window size to use for decompression.</param
 116        /// <exception cref="ArgumentNullException"><paramref name="dictionary"/> is null.</exception>
 117        /// <exception cref="ArgumentOutOfRangeException"><paramref name="maxWindowLog2"/> is not between the minimum an
 118        /// <exception cref="IOException">Failed to create the <see cref="ZstandardDecoder"/> instance.</exception>
 0119        public ZstandardDecoder(ZstandardDictionary dictionary, int maxWindowLog2)
 0120        {
 0121            ArgumentNullException.ThrowIfNull(dictionary);
 122
 0123            _disposed = false;
 124
 0125            InitializeDecoder();
 126
 127            try
 0128            {
 0129                if (maxWindowLog2 != 0)
 0130                {
 0131                    SetWindowLog(maxWindowLog2);
 0132                }
 133
 0134                SetDictionary(dictionary);
 0135            }
 0136            catch
 0137            {
 0138                _context.Dispose();
 0139                throw;
 140            }
 0141        }
 142
 143        [MemberNotNull(nameof(_context))]
 144        private void InitializeDecoder()
 0145        {
 0146            _context = Interop.Zstd.ZSTD_createDCtx();
 0147            if (_context.IsInvalid)
 0148            {
 0149                throw new OutOfMemoryException();
 150            }
 0151        }
 152
 153        /// <summary>Decompresses the specified data.</summary>
 154        /// <param name="source">The compressed data to decompress.</param>
 155        /// <param name="destination">The buffer to write the decompressed data to.</param>
 156        /// <param name="bytesConsumed">When this method returns, contains the number of bytes consumed from the source.
 157        /// <param name="bytesWritten">When this method returns, contains the number of bytes written to the destination
 158        /// <returns>An <see cref="OperationStatus"/> indicating the result of the operation.</returns>
 159        /// <remarks>
 160        /// The method returns <see cref="OperationStatus.Done"/> after all
 161        /// contents of a single Zstandard frame are decoded and returned. However,
 162        /// Zstandard data streams may consist of multiple concatenated frames
 163        /// (some of which may even be empty). To allow processing further
 164        /// frames on the same <see cref="ZstandardDecoder" /> instance, call
 165        /// <see cref="Reset" /> before calling <see cref="Decompress" /> on the rest of the data.
 166        /// </remarks>
 167        /// <exception cref="ObjectDisposedException">The decoder has been disposed.</exception>
 168        /// <exception cref="IOException">An error occurred during decompression.</exception>
 169        public OperationStatus Decompress(ReadOnlySpan<byte> source, Span<byte> destination, out int bytesConsumed, out 
 0170        {
 0171            bytesWritten = 0;
 0172            bytesConsumed = 0;
 173
 0174            if (_finished)
 0175            {
 0176                return OperationStatus.Done;
 177            }
 178
 0179            EnsureNotDisposed();
 180
 0181            if (destination.IsEmpty)
 0182            {
 0183                return OperationStatus.DestinationTooSmall;
 184            }
 185
 186            unsafe
 0187            {
 0188                fixed (byte* sourcePtr = &MemoryMarshal.GetReference(source))
 0189                fixed (byte* destPtr = &MemoryMarshal.GetReference(destination))
 0190                {
 0191                    var input = new Interop.Zstd.ZstdInBuffer
 0192                    {
 0193                        src = sourcePtr,
 0194                        size = (nuint)source.Length,
 0195                        pos = 0
 0196                    };
 197
 0198                    var output = new Interop.Zstd.ZstdOutBuffer
 0199                    {
 0200                        dst = destPtr,
 0201                        size = (nuint)destination.Length,
 0202                        pos = 0
 0203                    };
 204
 0205                    nuint result = Interop.Zstd.ZSTD_decompressStream(_context, ref output, ref input);
 206
 0207                    if (ZstandardUtils.IsError(result, out var error))
 0208                    {
 0209                        switch (error)
 210                        {
 211                            // These specific errors are actionable by the caller and don't imply
 212                            // that the data itself is corrupt.
 213                            case Interop.Zstd.ZSTD_error.frameParameter_windowTooLarge:
 214                            case Interop.Zstd.ZSTD_error.dictionary_wrong:
 0215                                ZstandardUtils.Throw(error);
 216                                break;
 217
 218                            default:
 0219                                return OperationStatus.InvalidData;
 220                        }
 221                    }
 222
 0223                    bytesConsumed = (int)input.pos;
 0224                    bytesWritten = (int)output.pos;
 225
 0226                    if (result == 0)
 0227                    {
 0228                        _finished = true;
 0229                        return OperationStatus.Done;
 230                    }
 0231                    else if (output.pos == output.size)
 0232                    {
 0233                        return OperationStatus.DestinationTooSmall;
 234                    }
 235                    else
 0236                    {
 0237                        return OperationStatus.NeedMoreData;
 238                    }
 239                }
 240            }
 0241        }
 242
 243        /// <summary>Attempts to get the maximum decompressed length for the specified compressed data.</summary>
 244        /// <param name="data">The compressed data.</param>
 245        /// <param name="length">When this method returns <see langword="true" />, contains the maximum decompressed len
 246        /// <returns><see langword="true" /> on success; <see langword="false" /> otherwise.</returns>
 247        public static bool TryGetMaxDecompressedLength(ReadOnlySpan<byte> data, out long length)
 0248        {
 0249            if (data.IsEmpty)
 0250            {
 0251                length = 0;
 0252                return true;
 253            }
 254
 255            unsafe
 0256            {
 0257                fixed (byte* dataPtr = &MemoryMarshal.GetReference(data))
 0258                {
 0259                    ulong frameContentSize = Interop.Zstd.ZSTD_decompressBound(dataPtr, (nuint)data.Length);
 260
 261                    const ulong ZSTD_CONTENTSIZE_UNKNOWN = unchecked(0UL - 1);
 262                    const ulong ZSTD_CONTENTSIZE_ERROR = unchecked(0UL - 2);
 0263                    if (frameContentSize == ZSTD_CONTENTSIZE_UNKNOWN || frameContentSize == ZSTD_CONTENTSIZE_ERROR || fr
 0264                    {
 0265                        length = 0;
 0266                        return false;
 267                    }
 268
 0269                    length = (long)frameContentSize;
 0270                    return true;
 271                }
 272            }
 0273        }
 274
 275        /// <summary>Attempts to decompress the specified data.</summary>
 276        /// <param name="source">The compressed data to decompress.</param>
 277        /// <param name="destination">The buffer to write the decompressed data to.</param>
 278        /// <param name="bytesWritten">When this method returns <see langword="true" />, contains the number of bytes wr
 279        /// <returns><see langword="true" /> on success; <see langword="false" /> otherwise.</returns>
 280        /// <remarks>If this method returns <see langword="false" />, <paramref name="destination" /> may be empty or co
 281        public static bool TryDecompress(ReadOnlySpan<byte> source, Span<byte> destination, out int bytesWritten)
 0282        {
 0283            bytesWritten = 0;
 284
 0285            if (source.IsEmpty)
 0286            {
 0287                return false;
 288            }
 289
 290            unsafe
 0291            {
 0292                fixed (byte* sourcePtr = &MemoryMarshal.GetReference(source))
 0293                fixed (byte* destPtr = &MemoryMarshal.GetReference(destination))
 0294                {
 0295                    nuint result = Interop.Zstd.ZSTD_decompress(
 0296                        destPtr, (nuint)destination.Length,
 0297                        sourcePtr, (nuint)source.Length);
 298
 0299                    if (ZstandardUtils.IsError(result, out var error))
 0300                    {
 0301                        return false;
 302                    }
 303
 0304                    bytesWritten = (int)result;
 0305                    return true;
 306                }
 307            }
 0308        }
 309
 310        /// <summary>Attempts to decompress the specified data using the specified dictionary.</summary>
 311        /// <param name="source">The compressed data to decompress.</param>
 312        /// <param name="dictionary">The decompression dictionary to use.</param>
 313        /// <param name="destination">The buffer to write the decompressed data to.</param>
 314        /// <param name="bytesWritten">When this method returns <see langword="true" />, contains the number of bytes wr
 315        /// <returns><see langword="true" /> on success; <see langword="false" /> otherwise.</returns>
 316        /// <exception cref="ArgumentNullException"><paramref name="dictionary"/> is null.</exception>
 317        /// <remarks>If this method returns <see langword="false" />, <paramref name="destination" /> may be empty or co
 318        public static bool TryDecompress(ReadOnlySpan<byte> source, Span<byte> destination, out int bytesWritten, Zstand
 0319        {
 0320            ArgumentNullException.ThrowIfNull(dictionary);
 321
 0322            bytesWritten = 0;
 323
 0324            if (source.IsEmpty)
 0325            {
 0326                return false;
 327            }
 328
 0329            using var dctx = Interop.Zstd.ZSTD_createDCtx();
 0330            if (dctx.IsInvalid)
 0331            {
 0332                throw new OutOfMemoryException();
 333            }
 334
 335            unsafe
 0336            {
 0337                fixed (byte* sourcePtr = &MemoryMarshal.GetReference(source))
 0338                fixed (byte* destPtr = &MemoryMarshal.GetReference(destination))
 0339                {
 0340                    nuint result = Interop.Zstd.ZSTD_decompress_usingDDict(
 0341                        dctx, destPtr, (nuint)destination.Length,
 0342                        sourcePtr, (nuint)source.Length, dictionary.DecompressionDictionary);
 343
 0344                    if (ZstandardUtils.IsError(result, out var error))
 0345                    {
 0346                        return false;
 347                    }
 348
 0349                    bytesWritten = checked((int)result);
 0350                    return true;
 351                }
 352            }
 0353        }
 354
 355        /// <summary>Resets the decoder session, allowing reuse for decompressing the next Zstandard frame.</summary>
 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>The prefix will be used only for the next decompression frame and will be removed when <see cref="R
 368        /// <exception cref="ObjectDisposedException">The decoder has been disposed.</exception>
 369        /// <exception cref="InvalidOperationException">The decoder is in an invalid state for setting a prefix.</except
 370        public void SetPrefix(ReadOnlyMemory<byte> prefix)
 0371        {
 0372            EnsureNotDisposed();
 373
 0374            if (_finished)
 0375            {
 0376                throw new InvalidOperationException(SR.ZstandardEncoderDecoder_InvalidState);
 377            }
 378
 0379            nuint result = _context.SetPrefix(prefix);
 380
 0381            if (ZstandardUtils.IsError(result, out var error))
 0382            {
 0383                ZstandardUtils.Throw(error);
 384            }
 0385        }
 386
 387        /// <summary>Releases all resources used by the <see cref="ZstandardDecoder"/>.</summary>
 388        public void Dispose()
 0389        {
 0390            _disposed = true;
 0391            _context.Dispose();
 0392        }
 393
 394        private void EnsureNotDisposed()
 0395        {
 0396            ObjectDisposedException.ThrowIf(_disposed, this);
 0397        }
 398
 399        internal void SetWindowLog(int maxWindowLog2)
 0400        {
 0401            Debug.Assert(_context != null);
 402
 0403            if (maxWindowLog2 != 0)
 0404            {
 0405                ArgumentOutOfRangeException.ThrowIfLessThan(maxWindowLog2, ZstandardUtils.WindowLog_Min, nameof(maxWindo
 0406                ArgumentOutOfRangeException.ThrowIfGreaterThan(maxWindowLog2, ZstandardUtils.WindowLog_Max, nameof(maxWi
 0407            }
 408
 0409            nuint result = Interop.Zstd.ZSTD_DCtx_setParameter(_context, Interop.Zstd.ZstdDParameter.ZSTD_d_windowLogMax
 0410            ZstandardUtils.ThrowIfError(result);
 0411        }
 412
 413        internal void SetDictionary(ZstandardDictionary dictionary)
 0414        {
 0415            Debug.Assert(_context != null);
 0416            ArgumentNullException.ThrowIfNull(dictionary);
 417
 0418            _context.SetDictionary(dictionary.DecompressionDictionary);
 0419        }
 420    }
 421}