< Summary

Information
Class: System.IO.Compression.ZstandardDictionary
Assembly: System.IO.Compression
File(s): D:\runner\runtime\src\libraries\System.IO.Compression\src\System\IO\Compression\Zstandard\ZstandardDictionary.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 97
Coverable lines: 97
Total lines: 206
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 28
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%
Create(...)100%110%
Create(...)0%10100%
Train(...)0%16160%
Dispose()0%220%
ThrowIfDisposed()100%110%

File(s)

D:\runner\runtime\src\libraries\System.IO.Compression\src\System\IO\Compression\Zstandard\ZstandardDictionary.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.CompilerServices;
 7using System.Runtime.InteropServices;
 8using Microsoft.Win32.SafeHandles;
 9
 10namespace System.IO.Compression
 11{
 12    /// <summary>Represents a Zstandard compression dictionary.</summary>
 13    [System.Runtime.Versioning.UnsupportedOSPlatform("browser")]
 14    [System.Runtime.Versioning.UnsupportedOSPlatform("wasi")]
 15    public sealed class ZstandardDictionary : IDisposable
 16    {
 17        private readonly SafeZstdCDictHandle _compressionDictionary;
 18        private readonly SafeZstdDDictHandle _decompressionDictionary;
 19        private readonly byte[] _dictionaryData;
 20        private bool _disposed;
 21
 022        private ZstandardDictionary(SafeZstdCDictHandle compressionDict, SafeZstdDDictHandle decompressionDict, byte[] d
 023        {
 024            _compressionDictionary = compressionDict;
 025            _decompressionDictionary = decompressionDict;
 026            _dictionaryData = data;
 027        }
 28
 29        /// <summary>Creates a Zstandard dictionary from the specified buffer.</summary>
 30        /// <param name="buffer">The buffer containing the dictionary data.</param>
 31        /// <returns>A new <see cref="ZstandardDictionary"/> instance.</returns>
 32        /// <exception cref="ArgumentException">The buffer is empty.</exception>
 033        public static ZstandardDictionary Create(ReadOnlySpan<byte> buffer) => Create(buffer, ZstandardUtils.Quality_Def
 34
 35        /// <summary>Creates a Zstandard dictionary from the specified buffer with the specified quality level and dicti
 36        /// <param name="buffer">The buffer containing the dictionary data.</param>
 37        /// <param name="quality">The quality level for dictionary creation.</param>
 38        /// <returns>A new <see cref="ZstandardDictionary"/> instance.</returns>
 39        /// <exception cref="ArgumentException">The buffer is empty.</exception>
 40        /// <exception cref="IOException">Failed to create the <see cref="ZstandardDictionary"/> instance.</exception>
 41        public static ZstandardDictionary Create(ReadOnlySpan<byte> buffer, int quality)
 042        {
 043            if (buffer.IsEmpty)
 044            {
 045                throw new ArgumentException(SR.ZstandardDictionary_EmptyBuffer, nameof(buffer));
 46            }
 47
 048            byte[] data = buffer.ToArray();
 49
 50
 51            unsafe
 052            {
 053                fixed (byte* dictPtr = data)
 054                {
 055                    SafeZstdCDictHandle compressionDict = Interop.Zstd.ZSTD_createCDict_byReference(dictPtr, (nuint)data
 56
 057                    if (compressionDict.IsInvalid)
 058                    {
 059                        throw new IOException(SR.ZstandardDictionary_CreateCompressionFailed);
 60                    }
 061                    compressionDict._pinnedData = new PinnedGCHandle<byte[]>(data);
 62
 063                    SafeZstdDDictHandle decompressionDict = Interop.Zstd.ZSTD_createDDict_byReference(dictPtr, (nuint)da
 64
 065                    if (decompressionDict.IsInvalid)
 066                    {
 067                        compressionDict.Dispose();
 068                        throw new IOException(SR.ZstandardDictionary_CreateDecompressionFailed);
 69                    }
 070                    decompressionDict._pinnedData = new PinnedGCHandle<byte[]>(data);
 71
 072                    return new ZstandardDictionary(compressionDict, decompressionDict, data);
 73                }
 74            }
 075        }
 76
 77        /// <summary>Creates a dictionary by training on the provided samples.</summary>
 78        /// <param name="samples">All training samples concatenated in one large buffer.</param>
 79        /// <param name="sampleLengths">The lengths of the individual samples. The sum of these lengths must equal the l
 80        /// <param name="maxDictionarySize">The maximum size of the dictionary to create.</param>
 81        /// <returns>A new <see cref="ZstandardDictionary"/> instance.</returns>
 82        /// <exception cref="ArgumentException">The sample data or lengths are invalid.</exception>
 83        /// <exception cref="ArgumentOutOfRangeException"><paramref name="maxDictionarySize"/> is not between the minimu
 84        /// <exception cref="IOException">Failed to train the dictionary.</exception>
 85        /// <remarks>
 86        /// The recommended maximum dictionary size is 100 KB, and the size of the training data
 87        /// should be approximately 100 times the size of the resulting dictionary.
 88        /// </remarks>
 89        public static ZstandardDictionary Train(ReadOnlySpan<byte> samples, ReadOnlySpan<int> sampleLengths, int maxDict
 090        {
 091            if (samples.IsEmpty)
 092            {
 093                throw new ArgumentException(SR.ZstandardDictionary_EmptyBuffer, nameof(samples));
 94            }
 95
 96            // this requirement is enforced by zstd native library, probably due to the underlying algorithm design
 097            if (sampleLengths.Length < 5)
 098            {
 099                throw new ArgumentException(SR.Format(SR.ZstandardDictionary_Train_MinimumSampleCount, 5), nameof(sample
 100            }
 101
 102            // the lengths need to be converted to nuint for the native call. Rent appropriately sized array from pool
 103            // This incidentally also protects against concurrent modifications of the sampleLengths that could cause
 104            // access violations later in native code.
 0105            byte[] lengthsArray = ArrayPool<byte>.Shared.Rent(sampleLengths.Length * sizeof(nuint));
 0106            byte[]? dictionaryBuffer = null;
 107            try
 0108            {
 0109                Span<nuint> lengthsAsNuint = MemoryMarshal.Cast<byte, nuint>(lengthsArray.AsSpan(0, sampleLengths.Length
 0110                Debug.Assert(lengthsAsNuint.Length == sampleLengths.Length);
 111
 0112                long totalLength = 0;
 0113                for (int i = 0; i < sampleLengths.Length; i++)
 0114                {
 0115                    int length = sampleLengths[i];
 0116                    if (length <= 0)
 0117                    {
 0118                        throw new ArgumentException(SR.ZstandardDictionary_Train_InvalidSampleLength, nameof(sampleLengt
 119                    }
 0120                    totalLength += length;
 0121                    lengthsAsNuint[i] = (nuint)length;
 0122                }
 123
 0124                if (totalLength != samples.Length)
 0125                {
 0126                    throw new ArgumentException(SR.ZstandardDictionary_SampleLengthsMismatch, nameof(sampleLengths));
 127                }
 128
 0129                ArgumentOutOfRangeException.ThrowIfLessThan(maxDictionarySize, 256, nameof(maxDictionarySize));
 130
 0131                dictionaryBuffer = ArrayPool<byte>.Shared.Rent(maxDictionarySize);
 132                nuint dictSize;
 133
 134                unsafe
 0135                {
 0136                    fixed (byte* samplesPtr = &MemoryMarshal.GetReference(samples))
 0137                    fixed (byte* dictPtr = dictionaryBuffer)
 0138                    fixed (nuint* lengthsAsNuintPtr = &MemoryMarshal.GetReference(lengthsAsNuint))
 0139                    {
 0140                        dictSize = Interop.Zstd.ZDICT_trainFromBuffer(
 0141                                dictPtr, (nuint)maxDictionarySize,
 0142                                samplesPtr, lengthsAsNuintPtr, (uint)sampleLengths.Length);
 0143                    }
 144
 0145                    ZstandardUtils.ThrowIfError(dictSize);
 0146                    return Create(dictionaryBuffer.AsSpan(0, (int)dictSize));
 147                }
 148            }
 149            finally
 0150            {
 0151                if (dictionaryBuffer is not null)
 0152                {
 0153                    ArrayPool<byte>.Shared.Return(dictionaryBuffer);
 0154                }
 0155                ArrayPool<byte>.Shared.Return(lengthsArray);
 0156            }
 0157        }
 158
 159        /// <summary>Gets the compression dictionary handle.</summary>
 160        internal SafeZstdCDictHandle CompressionDictionary
 161        {
 162            get
 0163            {
 0164                ThrowIfDisposed();
 0165                return _compressionDictionary;
 0166            }
 167        }
 168
 169        /// <summary>Gets the decompression dictionary handle.</summary>
 170        internal SafeZstdDDictHandle DecompressionDictionary
 171        {
 172            get
 0173            {
 0174                ThrowIfDisposed();
 0175                return _decompressionDictionary;
 0176            }
 177        }
 178
 179        /// <summary>Gets the dictionary data.</summary>
 180        /// <value>The raw dictionary bytes.</value>
 181        public ReadOnlyMemory<byte> Data
 182        {
 183            get
 0184            {
 0185                ThrowIfDisposed();
 0186                return _dictionaryData;
 0187            }
 188        }
 189
 190        /// <summary>Releases all resources used by the <see cref="ZstandardDictionary"/>.</summary>
 191        public void Dispose()
 0192        {
 0193            if (!_disposed)
 0194            {
 0195                _compressionDictionary.Dispose();
 0196                _decompressionDictionary.Dispose();
 0197                _disposed = true;
 0198            }
 0199        }
 200
 201        private void ThrowIfDisposed()
 0202        {
 0203            ObjectDisposedException.ThrowIf(_disposed, this);
 0204        }
 205    }
 206}