| | | 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 | | |
| | | 4 | | using System.Buffers; |
| | | 5 | | using System.Diagnostics; |
| | | 6 | | using System.Runtime.InteropServices; |
| | | 7 | | |
| | | 8 | | namespace System.IO.Compression |
| | | 9 | | { |
| | | 10 | | /// <summary> |
| | | 11 | | /// Provides methods and static methods to decode data compressed in the Deflate data format in a streamless, non-al |
| | | 12 | | /// </summary> |
| | | 13 | | public sealed class DeflateDecoder : IDisposable |
| | | 14 | | { |
| | | 15 | | private ZLibNative.ZLibStreamHandle? _state; |
| | | 16 | | private bool _disposed; |
| | | 17 | | private bool _finished; |
| | | 18 | | |
| | | 19 | | /// <summary> |
| | | 20 | | /// Initializes a new instance of the <see cref="DeflateDecoder"/> class. |
| | | 21 | | /// </summary> |
| | | 22 | | /// <exception cref="IOException">Failed to create the <see cref="DeflateDecoder"/> instance.</exception> |
| | | 23 | | public DeflateDecoder() |
| | 0 | 24 | | : this(ZLibNative.Deflate_DefaultWindowBits) |
| | 0 | 25 | | { |
| | 0 | 26 | | } |
| | | 27 | | |
| | 0 | 28 | | internal DeflateDecoder(int windowBits) |
| | 0 | 29 | | { |
| | 0 | 30 | | _state = ZLibNative.ZLibStreamHandle.CreateForInflate(windowBits); |
| | 0 | 31 | | } |
| | | 32 | | |
| | | 33 | | /// <summary> |
| | | 34 | | /// Frees and disposes unmanaged resources. |
| | | 35 | | /// </summary> |
| | | 36 | | public void Dispose() |
| | 0 | 37 | | { |
| | 0 | 38 | | _disposed = true; |
| | 0 | 39 | | _state?.Dispose(); |
| | 0 | 40 | | _state = null; |
| | 0 | 41 | | } |
| | | 42 | | |
| | | 43 | | private void EnsureNotDisposed() |
| | 0 | 44 | | { |
| | 0 | 45 | | ObjectDisposedException.ThrowIf(_disposed, this); |
| | 0 | 46 | | } |
| | | 47 | | |
| | | 48 | | /// <summary> |
| | | 49 | | /// Decompresses a read-only byte span into a destination span. |
| | | 50 | | /// </summary> |
| | | 51 | | /// <param name="source">A read-only span of bytes containing the compressed source data.</param> |
| | | 52 | | /// <param name="destination">When this method returns, a byte span where the decompressed data is stored.</para |
| | | 53 | | /// <param name="bytesConsumed">When this method returns, the total number of bytes that were read from <paramre |
| | | 54 | | /// <param name="bytesWritten">When this method returns, the total number of bytes that were written to <paramre |
| | | 55 | | /// <returns>One of the enumeration values that describes the status with which the span-based operation finishe |
| | | 56 | | public OperationStatus Decompress(ReadOnlySpan<byte> source, Span<byte> destination, out int bytesConsumed, out |
| | 0 | 57 | | { |
| | 0 | 58 | | EnsureNotDisposed(); |
| | 0 | 59 | | Debug.Assert(_state is not null); |
| | | 60 | | |
| | 0 | 61 | | bytesConsumed = 0; |
| | 0 | 62 | | bytesWritten = 0; |
| | | 63 | | |
| | 0 | 64 | | if (_finished) |
| | 0 | 65 | | { |
| | 0 | 66 | | return OperationStatus.Done; |
| | | 67 | | } |
| | | 68 | | |
| | 0 | 69 | | if (destination.IsEmpty && source.Length > 0) |
| | 0 | 70 | | { |
| | 0 | 71 | | return OperationStatus.DestinationTooSmall; |
| | | 72 | | } |
| | | 73 | | |
| | | 74 | | unsafe |
| | 0 | 75 | | { |
| | 0 | 76 | | fixed (byte* inputPtr = &MemoryMarshal.GetReference(source)) |
| | 0 | 77 | | fixed (byte* outputPtr = &MemoryMarshal.GetReference(destination)) |
| | 0 | 78 | | { |
| | 0 | 79 | | _state.NextIn = (IntPtr)inputPtr; |
| | 0 | 80 | | _state.AvailIn = (uint)source.Length; |
| | 0 | 81 | | _state.NextOut = (IntPtr)outputPtr; |
| | 0 | 82 | | _state.AvailOut = (uint)destination.Length; |
| | | 83 | | |
| | 0 | 84 | | ZLibNative.ErrorCode errorCode = _state.Inflate(ZLibNative.FlushCode.NoFlush); |
| | | 85 | | |
| | 0 | 86 | | bytesConsumed = source.Length - (int)_state.AvailIn; |
| | 0 | 87 | | bytesWritten = destination.Length - (int)_state.AvailOut; |
| | | 88 | | |
| | 0 | 89 | | OperationStatus status = errorCode switch |
| | 0 | 90 | | { |
| | 0 | 91 | | ZLibNative.ErrorCode.Ok or ZLibNative.ErrorCode.BufError => _state.AvailOut == 0 |
| | 0 | 92 | | ? OperationStatus.DestinationTooSmall |
| | 0 | 93 | | : OperationStatus.NeedMoreData, |
| | 0 | 94 | | ZLibNative.ErrorCode.StreamEnd => OperationStatus.Done, |
| | 0 | 95 | | _ => OperationStatus.InvalidData |
| | 0 | 96 | | }; |
| | | 97 | | |
| | | 98 | | // Track if decompression is finished |
| | 0 | 99 | | if (errorCode == ZLibNative.ErrorCode.StreamEnd) |
| | 0 | 100 | | { |
| | 0 | 101 | | _finished = true; |
| | 0 | 102 | | } |
| | | 103 | | |
| | 0 | 104 | | return status; |
| | | 105 | | } |
| | | 106 | | } |
| | 0 | 107 | | } |
| | | 108 | | |
| | | 109 | | /// <summary> |
| | | 110 | | /// Tries to decompress a source byte span into a destination span. |
| | | 111 | | /// </summary> |
| | | 112 | | /// <param name="source">A read-only span of bytes containing the compressed source data.</param> |
| | | 113 | | /// <param name="destination">When this method returns, a span of bytes where the decompressed data is stored.</ |
| | | 114 | | /// <param name="bytesWritten">When this method returns, the total number of bytes that were written to <paramre |
| | | 115 | | /// <returns><see langword="true"/> if the decompression operation was successful; <see langword="false"/> other |
| | | 116 | | public static bool TryDecompress(ReadOnlySpan<byte> source, Span<byte> destination, out int bytesWritten) |
| | 0 | 117 | | { |
| | 0 | 118 | | using var decoder = new DeflateDecoder(); |
| | 0 | 119 | | OperationStatus status = decoder.Decompress(source, destination, out int consumed, out bytesWritten); |
| | | 120 | | |
| | 0 | 121 | | bool success = status == OperationStatus.Done && consumed == source.Length; |
| | 0 | 122 | | if (!success) |
| | 0 | 123 | | { |
| | 0 | 124 | | bytesWritten = 0; |
| | 0 | 125 | | } |
| | | 126 | | |
| | 0 | 127 | | return success; |
| | 0 | 128 | | } |
| | | 129 | | } |
| | | 130 | | } |