| File: System\IO\Compression\DeflateDecoder.cs | Web Access |
| Project: src\runtime\src\libraries\System.IO.Compression\src\System.IO.Compression.csproj (System.IO.Compression) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Buffers; using System.Diagnostics; using System.Runtime.InteropServices; namespace System.IO.Compression { /// <summary> /// Provides methods and static methods to decode data compressed in the Deflate data format in a streamless, non-allocating, and performant manner. /// </summary> public sealed class DeflateDecoder : IDisposable { private ZLibNative.ZLibStreamHandle? _state; private bool _disposed; private bool _finished; /// <summary> /// Initializes a new instance of the <see cref="DeflateDecoder"/> class. /// </summary> /// <exception cref="IOException">Failed to create the <see cref="DeflateDecoder"/> instance.</exception> public DeflateDecoder() : this(ZLibNative.Deflate_DefaultWindowBits) { } internal DeflateDecoder(int windowBits) { _state = ZLibNative.ZLibStreamHandle.CreateForInflate(windowBits); } /// <summary> /// Frees and disposes unmanaged resources. /// </summary> public void Dispose() { _disposed = true; _state?.Dispose(); _state = null; } private void EnsureNotDisposed() { ObjectDisposedException.ThrowIf(_disposed, this); } /// <summary> /// Decompresses a read-only byte span into a destination span. /// </summary> /// <param name="source">A read-only span of bytes containing the compressed source data.</param> /// <param name="destination">When this method returns, a byte span where the decompressed data is stored.</param> /// <param name="bytesConsumed">When this method returns, the total number of bytes that were read from <paramref name="source"/>.</param> /// <param name="bytesWritten">When this method returns, the total number of bytes that were written to <paramref name="destination"/>.</param> /// <returns>One of the enumeration values that describes the status with which the span-based operation finished.</returns> public OperationStatus Decompress(ReadOnlySpan<byte> source, Span<byte> destination, out int bytesConsumed, out int bytesWritten) { EnsureNotDisposed(); Debug.Assert(_state is not null); bytesConsumed = 0; bytesWritten = 0; if (_finished) { return OperationStatus.Done; } if (destination.IsEmpty && source.Length > 0) { return OperationStatus.DestinationTooSmall; } unsafe { fixed (byte* inputPtr = &MemoryMarshal.GetReference(source)) fixed (byte* outputPtr = &MemoryMarshal.GetReference(destination)) { _state.NextIn = (IntPtr)inputPtr; _state.AvailIn = (uint)source.Length; _state.NextOut = (IntPtr)outputPtr; _state.AvailOut = (uint)destination.Length; ZLibNative.ErrorCode errorCode = _state.Inflate(ZLibNative.FlushCode.NoFlush); bytesConsumed = source.Length - (int)_state.AvailIn; bytesWritten = destination.Length - (int)_state.AvailOut; OperationStatus status = errorCode switch { ZLibNative.ErrorCode.Ok or ZLibNative.ErrorCode.BufError => _state.AvailOut == 0 ? OperationStatus.DestinationTooSmall : OperationStatus.NeedMoreData, ZLibNative.ErrorCode.StreamEnd => OperationStatus.Done, _ => OperationStatus.InvalidData }; // Track if decompression is finished if (errorCode == ZLibNative.ErrorCode.StreamEnd) { _finished = true; } return status; } } } /// <summary> /// Tries to decompress a source byte span into a destination span. /// </summary> /// <param name="source">A read-only span of bytes containing the compressed source data.</param> /// <param name="destination">When this method returns, a span of bytes where the decompressed data is stored.</param> /// <param name="bytesWritten">When this method returns, the total number of bytes that were written to <paramref name="destination"/>.</param> /// <returns><see langword="true"/> if the decompression operation was successful; <see langword="false"/> otherwise.</returns> public static bool TryDecompress(ReadOnlySpan<byte> source, Span<byte> destination, out int bytesWritten) { using var decoder = new DeflateDecoder(); OperationStatus status = decoder.Decompress(source, destination, out int consumed, out bytesWritten); bool success = status == OperationStatus.Done && consumed == source.Length; if (!success) { bytesWritten = 0; } return success; } } }