< Summary

Line coverage
58%
Covered lines: 425
Uncovered lines: 302
Coverable lines: 727
Total lines: 1249
Line coverage: 58.4%
Branch coverage
50%
Covered branches: 146
Total branches: 289
Branch coverage: 50.5%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor(...)100%11100%
.ctor(...)100%110%
.ctor(...)100%110%
.ctor(...)100%110%
.ctor(...)100%11100%
.ctor(...)100%110%
.ctor(...)100%110%
.ctor(...)33.33%6673.33%
.ctor(...)100%11100%
InitializeDeflater(...)50%2290%
GetZLibNativeCompressionLevel(...)20%5550%
GetMemLevel(...)50%22100%
InitializeBuffer()100%11100%
EnsureBufferInitialized()100%22100%
Flush()0%220%
FlushAsync(...)0%440%
Core(System.Threading.CancellationToken)0%660%
Seek(...)100%110%
SetLength(...)100%110%
ReadByte()0%220%
Read(...)100%110%
Read(...)50%2271.42%
ReadCore(...)50%323255.55%
EnsureNotDisposed()100%11100%
EnsureDecompressionMode()50%2275%
ThrowCannotReadFromDeflateStreamException()100%110%
EnsureCompressionMode()50%2275%
ThrowCannotWriteToDeflateStreamException()100%110%
ThrowGenericInvalidData()100%110%
ThrowTruncatedInvalidData()100%110%
BeginRead(...)100%110%
EndRead(...)100%110%
ReadAsync(...)100%110%
ReadAsync(...)50%2271.42%
ReadAsyncMemory(...)50%2281.81%
Core(System.Memory`1<System.Byte>,System.Threading.CancellationToken)53.12%323257.77%
Write(...)100%110%
WriteByte(...)0%220%
Write(...)50%2266.66%
WriteCore(...)50%2290.47%
WriteDeflaterOutput()66.66%6672.72%
FlushBuffers()0%660%
PurgeBuffers(...)75%121286.66%
PurgeBuffersAsync()70%101092.3%
TryRewindStream(...)0%220%
Dispose(...)85.71%1414100%
DisposeAsync()50%22100%
Core()81.25%1616100%
BeginWrite(...)100%110%
EndWrite(...)100%110%
WriteAsync(...)100%110%
WriteAsync(...)50%2271.42%
WriteAsyncMemory(...)50%44100%
Core(System.ReadOnlyMemory`1<System.Byte>,System.Threading.CancellationToken)100%11100%
WriteDeflaterOutputAsync(...)66.66%6672.72%
CopyTo(...)50%22100%
CopyToAsync(...)50%4480%
.ctor(...)100%11100%
.ctor(...)100%11100%
CopyFromSourceToDestinationAsync()46.15%262657.5%
CopyFromSourceToDestination()46.15%262655.26%
WriteAsync(...)0%440%
WriteAsync(...)100%11100%
WriteAsyncCore(...)66.66%6666.66%
Write(...)60%101062.06%
Flush()100%110%
Read(...)100%110%
Seek(...)100%110%
SetLength(...)100%110%
EnsureNoActiveAsyncOperation()50%2250%
AsyncOperationStarting()50%2250%
AsyncOperationCompleting()100%11100%
ThrowInvalidBeginCall()100%110%
.cctor()50%22100%

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.IO.Compression/src/System/IO/Compression/DeflateZLib/DeflateStream.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.InteropServices;
 8using System.Threading;
 9using System.Threading.Tasks;
 10using static System.IO.Compression.ZLibNative;
 11
 12namespace System.IO.Compression
 13{
 14    public partial class DeflateStream : Stream
 15    {
 16        private const int DefaultBufferSize = 8192;
 17
 18        private Stream _stream;
 19        private CompressionMode _mode;
 20        private bool _leaveOpen;
 21        private Inflater? _inflater;
 22        private Deflater? _deflater;
 23        private byte[]? _buffer;
 24        private volatile bool _activeAsyncOperation;
 25        private volatile bool _decompressionFinished;
 26
 43827        internal DeflateStream(Stream stream, CompressionMode mode, long uncompressedSize) : this(stream, mode, leaveOpe
 43828        {
 43829        }
 30
 031        public DeflateStream(Stream stream, CompressionMode mode) : this(stream, mode, leaveOpen: false)
 032        {
 033        }
 34
 035        public DeflateStream(Stream stream, CompressionMode mode, bool leaveOpen) : this(stream, mode, leaveOpen, ZLibNa
 036        {
 037        }
 38
 39        // Implies mode = Compress
 040        public DeflateStream(Stream stream, CompressionLevel compressionLevel) : this(stream, compressionLevel, leaveOpe
 041        {
 042        }
 43
 44        // Implies mode = Compress
 43845        public DeflateStream(Stream stream, CompressionLevel compressionLevel, bool leaveOpen) : this(stream, compressio
 43846        {
 43847        }
 48
 49        /// <summary>
 50        /// Initializes a new instance of the <see cref="DeflateStream"/> class by using the specified stream, compressi
 51        /// </summary>
 52        /// <param name="stream">The stream to which compressed data is written.</param>
 53        /// <param name="compressionOptions">The options for fine tuning the compression stream.</param>
 54        /// <param name="leaveOpen"><see langword="true" /> to leave the stream object open after disposing the <see cre
 55        /// <exception cref="ArgumentNullException"><paramref name="stream"/> or <paramref name="compressionOptions"/> i
 056        public DeflateStream(Stream stream, ZLibCompressionOptions compressionOptions, bool leaveOpen = false)
 057        {
 058            ArgumentNullException.ThrowIfNull(stream);
 059            ArgumentNullException.ThrowIfNull(compressionOptions);
 60
 061            int windowBits = CompressionFormatHelper.ResolveWindowBits(compressionOptions.WindowLog2, CompressionFormat.
 62
 063            InitializeDeflater(stream, (ZLibNative.CompressionLevel)compressionOptions.CompressionLevel, (CompressionStr
 064        }
 65
 066        internal DeflateStream(Stream stream, ZLibCompressionOptions compressionOptions, bool leaveOpen, int windowBits)
 067        {
 068            ArgumentNullException.ThrowIfNull(stream);
 069            ArgumentNullException.ThrowIfNull(compressionOptions);
 70
 071            InitializeDeflater(stream, (ZLibNative.CompressionLevel)compressionOptions.CompressionLevel, (CompressionStr
 072        }
 73
 74        /// <summary>
 75        /// Internal constructor to check stream validity and call the correct initialization function depending on
 76        /// the value of the CompressionMode given.
 77        /// </summary>
 43878        internal DeflateStream(Stream stream, CompressionMode mode, bool leaveOpen, int windowBits, long uncompressedSiz
 43879        {
 43880            ArgumentNullException.ThrowIfNull(stream);
 81
 43882            switch (mode)
 83            {
 84                case CompressionMode.Decompress:
 43885                    if (!stream.CanRead)
 086                        throw new ArgumentException(SR.NotSupported_UnreadableStream, nameof(stream));
 87
 43888                    _inflater = Inflater.CreateInflater(windowBits, uncompressedSize);
 43889                    _stream = stream;
 43890                    _mode = CompressionMode.Decompress;
 43891                    _leaveOpen = leaveOpen;
 43892                    break;
 93
 94                case CompressionMode.Compress:
 095                    InitializeDeflater(stream, ZLibNative.CompressionLevel.DefaultCompression, CompressionStrategy.Defau
 096                    break;
 97
 98                default:
 099                    throw new ArgumentException(SR.ArgumentOutOfRange_Enum, nameof(mode));
 100            }
 438101        }
 102
 103        /// <summary>
 104        /// Internal constructor to specify the compressionLevel as well as the windowBits
 105        /// </summary>
 438106        internal DeflateStream(Stream stream, CompressionLevel compressionLevel, bool leaveOpen, int windowBits)
 438107        {
 438108            ArgumentNullException.ThrowIfNull(stream);
 109
 438110            InitializeDeflater(stream, GetZLibNativeCompressionLevel(compressionLevel), CompressionStrategy.DefaultStrat
 438111        }
 112
 113        /// <summary>
 114        /// Sets up this DeflateStream to be used for Zlib Deflation/Compression
 115        /// </summary>
 116        [MemberNotNull(nameof(_stream))]
 117        internal void InitializeDeflater(Stream stream, ZLibNative.CompressionLevel compressionLevel, CompressionStrateg
 438118        {
 438119            Debug.Assert(stream != null);
 438120            if (!stream.CanWrite)
 0121                throw new ArgumentException(SR.NotSupported_UnwritableStream, nameof(stream));
 122
 438123            _deflater = Deflater.CreateDeflater(compressionLevel, strategy, windowBits, GetMemLevel(compressionLevel));
 124
 438125            _stream = stream;
 438126            _mode = CompressionMode.Compress;
 438127            _leaveOpen = leaveOpen;
 438128            InitializeBuffer();
 438129        }
 130
 131        private static ZLibNative.CompressionLevel GetZLibNativeCompressionLevel(CompressionLevel compressionLevel) =>
 438132            compressionLevel switch
 438133            {
 438134                CompressionLevel.Optimal => ZLibNative.CompressionLevel.DefaultCompression,
 0135                CompressionLevel.Fastest => ZLibNative.CompressionLevel.BestSpeed,
 0136                CompressionLevel.NoCompression => ZLibNative.CompressionLevel.NoCompression,
 0137                CompressionLevel.SmallestSize => ZLibNative.CompressionLevel.BestCompression,
 0138                _ => throw new ArgumentOutOfRangeException(nameof(compressionLevel)),
 438139            };
 140
 141        private static int GetMemLevel(ZLibNative.CompressionLevel level) =>
 438142            level == ZLibNative.CompressionLevel.NoCompression ?
 438143                Deflate_NoCompressionMemLevel :
 438144                Deflate_DefaultMemLevel;
 145
 146        [MemberNotNull(nameof(_buffer))]
 147        private void InitializeBuffer()
 812148        {
 812149            Debug.Assert(_buffer == null);
 812150            _buffer = ArrayPool<byte>.Shared.Rent(DefaultBufferSize);
 812151        }
 152
 153        [MemberNotNull(nameof(_buffer))]
 154        private void EnsureBufferInitialized()
 748155        {
 748156            if (_buffer == null)
 374157            {
 374158                InitializeBuffer();
 374159            }
 748160        }
 161
 162        public Stream BaseStream
 163        {
 164            get
 0165            {
 0166                EnsureNotDisposed();
 0167                return _stream;
 0168            }
 169        }
 170
 171        public override bool CanRead
 172        {
 173            get
 438174            {
 438175                if (_stream == null)
 0176                {
 0177                    return false;
 178                }
 179
 438180                return (_mode == CompressionMode.Decompress && _stream.CanRead);
 438181            }
 182        }
 183
 184        public override bool CanWrite
 185        {
 186            get
 0187            {
 0188                if (_stream == null)
 0189                {
 0190                    return false;
 191                }
 192
 0193                return (_mode == CompressionMode.Compress && _stream.CanWrite);
 0194            }
 195        }
 196
 438197        public override bool CanSeek => false;
 198
 199        public override long Length
 200        {
 0201            get { throw new NotSupportedException(SR.NotSupported); }
 202        }
 203
 204        public override long Position
 205        {
 0206            get { throw new NotSupportedException(SR.NotSupported); }
 0207            set { throw new NotSupportedException(SR.NotSupported); }
 208        }
 209
 210        public override void Flush()
 0211        {
 0212            EnsureNotDisposed();
 0213            if (_mode == CompressionMode.Compress)
 0214                FlushBuffers();
 0215        }
 216
 217        public override Task FlushAsync(CancellationToken cancellationToken)
 0218        {
 0219            EnsureNoActiveAsyncOperation();
 0220            EnsureNotDisposed();
 221
 0222            if (cancellationToken.IsCancellationRequested)
 0223                return Task.FromCanceled(cancellationToken);
 224
 0225            return _mode != CompressionMode.Compress ?
 0226                Task.CompletedTask :
 0227                Core(cancellationToken);
 228
 229            async Task Core(CancellationToken cancellationToken)
 0230            {
 0231                AsyncOperationStarting();
 232                try
 0233                {
 0234                    Debug.Assert(_deflater != null && _buffer != null);
 235
 236                    // Compress any bytes left:
 0237                    await WriteDeflaterOutputAsync(cancellationToken).ConfigureAwait(false);
 238
 239                    // Pull out any bytes left inside deflater:
 240                    bool flushSuccessful;
 241                    do
 0242                    {
 243                        int compressedBytes;
 0244                        flushSuccessful = _deflater.Flush(_buffer, out compressedBytes);
 0245                        if (flushSuccessful)
 0246                        {
 0247                            await _stream.WriteAsync(new ReadOnlyMemory<byte>(_buffer, 0, compressedBytes), cancellation
 0248                        }
 0249                        Debug.Assert(flushSuccessful == (compressedBytes > 0));
 0250                    } while (flushSuccessful);
 251
 252                    // Always flush on the underlying stream
 0253                    await _stream.FlushAsync(cancellationToken).ConfigureAwait(false);
 0254                }
 255                finally
 0256                {
 0257                    AsyncOperationCompleting();
 0258                }
 0259            }
 0260        }
 261
 262        public override long Seek(long offset, SeekOrigin origin)
 0263        {
 0264            throw new NotSupportedException(SR.NotSupported);
 265        }
 266
 267        public override void SetLength(long value)
 0268        {
 0269            throw new NotSupportedException(SR.NotSupported);
 270        }
 271
 272        public override int ReadByte()
 0273        {
 0274            EnsureDecompressionMode();
 0275            EnsureNotDisposed();
 276
 277            // Try to read a single byte from zlib without allocating an array, pinning an array, etc.
 278            // If zlib doesn't have any data, fall back to the base stream implementation, which will do that.
 279            byte b;
 0280            Debug.Assert(_inflater != null);
 0281            return _inflater.Inflate(out b) ? b : base.ReadByte();
 0282        }
 283
 284        public override int Read(byte[] buffer, int offset, int count)
 0285        {
 0286            ValidateBufferArguments(buffer, offset, count);
 0287            return ReadCore(new Span<byte>(buffer, offset, count));
 0288        }
 289
 290        public override int Read(Span<byte> buffer)
 374291        {
 374292            if (GetType() != typeof(DeflateStream))
 0293            {
 294                // DeflateStream is not sealed, and a derived type may have overridden Read(byte[], int, int) prior
 295                // to this Read(Span<byte>) overload being introduced.  In that case, this Read(Span<byte>) overload
 296                // should use the behavior of Read(byte[],int,int) overload.
 0297                return base.Read(buffer);
 298            }
 299            else
 374300            {
 374301                return ReadCore(buffer);
 302            }
 374303        }
 304
 305        internal int ReadCore(Span<byte> buffer)
 374306        {
 374307            EnsureDecompressionMode();
 374308            EnsureNotDisposed();
 374309            EnsureBufferInitialized();
 374310            Debug.Assert(_inflater != null);
 311
 312            int bytesRead;
 561313            while (true)
 561314            {
 315                // Try to decompress any data from the inflater into the caller's buffer.
 316                // If we're able to decompress any bytes, or if decompression is completed, we're done.
 561317                bytesRead = _inflater.Inflate(buffer);
 561318                if (bytesRead != 0 || InflatorIsFinished)
 374319                {
 374320                    break;
 321                }
 322
 323                // We were unable to decompress any data.  If the inflater needs additional input
 324                // data to proceed, read some to populate it.
 187325                if (_inflater.NeedsInput())
 187326                {
 187327                    int n = _stream.Read(_buffer, 0, _buffer.Length);
 187328                    if (n <= 0)
 0329                    {
 330                        // - Inflater didn't return any data although a non-empty output buffer was passed by the caller
 331                        // - More input is needed but there is no more input available.
 332                        // - Inflation is not finished yet.
 333                        // - Provided input wasn't completely empty
 334                        // In such case, we are dealing with a truncated input stream.
 0335                        if (_inflater.HasUnconfirmedGZipProbe && _stream.CanSeek)
 0336                        {
 337                            // A lone trailing GZip ID1 (0x1F) byte was speculatively consumed as a
 338                            // possible concatenated member, but no further data followed. It was actually
 339                            // trailing content: mark the inflater finished so the byte is rewound below and
 340                            // not re-read on subsequent calls.
 0341                            _inflater.MarkEndOfStream();
 0342                        }
 0343                        else if (s_useStrictValidation && !buffer.IsEmpty && !_inflater.Finished() && _inflater.NonEmpty
 0344                        {
 0345                            ThrowTruncatedInvalidData();
 0346                        }
 0347                        break;
 348                    }
 187349                    else if (n > _buffer.Length)
 0350                    {
 0351                        ThrowGenericInvalidData();
 0352                    }
 353                    else
 187354                    {
 187355                        _inflater.SetInput(_buffer, 0, n);
 187356                    }
 187357                }
 358
 187359                if (buffer.IsEmpty)
 0360                {
 361                    // The caller provided a zero-byte buffer.  This is typically done in order to avoid allocating/rent
 362                    // a buffer until data is known to be available.  We don't have perfect knowledge here, as _inflater
 363                    // will return 0 whether or not more data is required, and having input data doesn't necessarily mea
 364                    // decompress into at least one byte of output, but it's a reasonable approximation for the 99% case
 365                    // wrong, it just means that a caller using zero-byte reads as a way to delay getting a buffer to us
 366                    // subsequent call may end up getting one earlier than otherwise preferred.
 0367                    Debug.Assert(bytesRead == 0);
 0368                    break;
 369                }
 187370            }
 371
 372            // When decompression finishes, rewind the stream to the exact end of compressed data
 374373            if (bytesRead == 0 && InflatorIsFinished && !_decompressionFinished && _stream.CanSeek)
 0374            {
 0375                TryRewindStream(_stream);
 0376                _decompressionFinished = true;
 0377            }
 378
 374379            return bytesRead;
 374380        }
 381
 382        private bool InflatorIsFinished =>
 383            // If the stream is finished then we have a few potential cases here:
 384            // 1. DeflateStream => return
 385            // 2. GZipStream that is finished but may have an additional GZipStream appended => feed more input
 386            // 3. GZipStream that is finished and appended with garbage => return
 1122387            (_inflater!.Finished() &&
 1122388            (!_inflater.IsGzipStream() || !_inflater.NeedsInput()))
 1122389            // 4. GZipStream whose lone trailing 0x1F probe has been resolved as end-of-stream => return
 1122390            || _inflater.EndOfStreamReached;
 391
 392        private void EnsureNotDisposed()
 1314393        {
 1314394            ObjectDisposedException.ThrowIf(_stream is null, this);
 1314395        }
 396
 397        private void EnsureDecompressionMode()
 748398        {
 748399            if (_mode != CompressionMode.Decompress)
 0400                ThrowCannotReadFromDeflateStreamException();
 401
 402            static void ThrowCannotReadFromDeflateStreamException() =>
 0403                throw new InvalidOperationException(SR.CannotReadFromDeflateStream);
 748404        }
 405
 406        private void EnsureCompressionMode()
 438407        {
 438408            if (_mode != CompressionMode.Compress)
 0409                ThrowCannotWriteToDeflateStreamException();
 410
 411            static void ThrowCannotWriteToDeflateStreamException() =>
 0412                throw new InvalidOperationException(SR.CannotWriteToDeflateStream);
 438413        }
 414
 415        private static void ThrowGenericInvalidData() =>
 416            // The stream is either malicious or poorly implemented and returned a number of
 417            // bytes < 0 || > than the buffer supplied to it.
 0418            throw new InvalidDataException(SR.GenericInvalidData);
 419
 420        private static void ThrowTruncatedInvalidData() =>
 0421            throw new InvalidDataException(SR.TruncatedData);
 422
 423        public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? asyncCallback, objec
 0424            TaskToAsyncResult.Begin(ReadAsync(buffer, offset, count, CancellationToken.None), asyncCallback, asyncState)
 425
 426        public override int EndRead(IAsyncResult asyncResult)
 0427        {
 0428            EnsureDecompressionMode();
 0429            EnsureNotDisposed();
 0430            return TaskToAsyncResult.End<int>(asyncResult);
 0431        }
 432
 433        public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
 0434        {
 0435            ValidateBufferArguments(buffer, offset, count);
 0436            return ReadAsyncMemory(new Memory<byte>(buffer, offset, count), cancellationToken).AsTask();
 0437        }
 438
 439        public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
 374440        {
 374441            if (GetType() != typeof(DeflateStream))
 0442            {
 443                // Ensure that existing streams derived from DeflateStream and that override ReadAsync(byte[],...)
 444                // get their existing behaviors when the newer Memory-based overload is used.
 0445                return base.ReadAsync(buffer, cancellationToken);
 446            }
 447            else
 374448            {
 374449                return ReadAsyncMemory(buffer, cancellationToken);
 450            }
 374451        }
 452
 453        internal ValueTask<int> ReadAsyncMemory(Memory<byte> buffer, CancellationToken cancellationToken)
 374454        {
 374455            EnsureDecompressionMode();
 374456            EnsureNoActiveAsyncOperation();
 374457            EnsureNotDisposed();
 458
 374459            if (cancellationToken.IsCancellationRequested)
 0460            {
 0461                return ValueTask.FromCanceled<int>(cancellationToken);
 462            }
 463
 374464            EnsureBufferInitialized();
 374465            Debug.Assert(_inflater != null);
 466
 374467            return Core(buffer, cancellationToken);
 468
 469            async ValueTask<int> Core(Memory<byte> buffer, CancellationToken cancellationToken)
 374470            {
 374471                AsyncOperationStarting();
 472                try
 374473                {
 474                    int bytesRead;
 561475                    while (true)
 561476                    {
 477                        // Try to decompress any data from the inflater into the caller's buffer.
 478                        // If we're able to decompress any bytes, or if decompression is completed, we're done.
 561479                        bytesRead = _inflater.Inflate(buffer.Span);
 561480                        if (bytesRead != 0 || InflatorIsFinished)
 374481                        {
 374482                            break;
 483                        }
 484
 485                        // We were unable to decompress any data.  If the inflater needs additional input
 486                        // data to proceed, read some to populate it.
 187487                        if (_inflater.NeedsInput())
 187488                        {
 187489                            int n = await _stream.ReadAsync(new Memory<byte>(_buffer, 0, _buffer.Length), cancellationTo
 187490                            if (n <= 0)
 0491                            {
 492                                // - Inflater didn't return any data although a non-empty output buffer was passed by th
 493                                // - More input is needed but there is no more input available.
 494                                // - Inflation is not finished yet.
 495                                // - Provided input wasn't completely empty
 496                                // In such case, we are dealing with a truncated input stream.
 0497                                if (_inflater.HasUnconfirmedGZipProbe && _stream.CanSeek)
 0498                                {
 499                                    // A lone trailing GZip ID1 (0x1F) byte was speculatively consumed as a
 500                                    // possible concatenated member, but no further data followed. It was
 501                                    // actually trailing content: mark the inflater finished so the byte is
 502                                    // rewound below and not re-read on subsequent calls.
 0503                                    _inflater.MarkEndOfStream();
 0504                                }
 0505                                else if (s_useStrictValidation && !_inflater.Finished() && _inflater.NonEmptyInput() && 
 0506                                {
 0507                                    ThrowTruncatedInvalidData();
 0508                                }
 0509                                break;
 510                            }
 187511                            else if (n > _buffer.Length)
 0512                            {
 0513                                ThrowGenericInvalidData();
 0514                            }
 515                            else
 187516                            {
 187517                                _inflater.SetInput(_buffer, 0, n);
 187518                            }
 187519                        }
 520
 187521                        if (buffer.IsEmpty)
 0522                        {
 523                            // The caller provided a zero-byte buffer.  This is typically done in order to avoid allocat
 524                            // a buffer until data is known to be available.  We don't have perfect knowledge here, as _
 525                            // will return 0 whether or not more data is required, and having input data doesn't necessa
 526                            // decompress into at least one byte of output, but it's a reasonable approximation for the 
 527                            // wrong, it just means that a caller using zero-byte reads as a way to delay getting a buff
 528                            // subsequent call may end up getting one earlier than otherwise preferred.
 0529                            break;
 530                        }
 187531                    }
 532
 533                    // When decompression finishes, rewind the stream to the exact end of compressed data
 374534                    if (bytesRead == 0 && InflatorIsFinished && !_decompressionFinished && _stream.CanSeek)
 0535                    {
 0536                        TryRewindStream(_stream);
 0537                        _decompressionFinished = true;
 0538                    }
 539
 374540                    return bytesRead;
 541                }
 542                finally
 374543                {
 374544                    AsyncOperationCompleting();
 374545                }
 374546            }
 374547        }
 548
 549        public override void Write(byte[] buffer, int offset, int count)
 0550        {
 0551            ValidateBufferArguments(buffer, offset, count);
 0552            WriteCore(new ReadOnlySpan<byte>(buffer, offset, count));
 0553        }
 554
 555        public override void WriteByte(byte value)
 0556        {
 0557            if (GetType() != typeof(DeflateStream))
 0558            {
 559                // DeflateStream is not sealed, and a derived type may have overridden Write(byte[], int, int) prior
 560                // to this WriteByte override being introduced.  In that case, this WriteByte override
 561                // should use the behavior of the Write(byte[],int,int) overload.
 0562                base.WriteByte(value);
 0563            }
 564            else
 0565            {
 0566                WriteCore(new ReadOnlySpan<byte>(in value));
 0567            }
 0568        }
 569
 570        public override void Write(ReadOnlySpan<byte> buffer)
 219571        {
 219572            if (GetType() != typeof(DeflateStream))
 0573            {
 574                // DeflateStream is not sealed, and a derived type may have overridden Write(byte[], int, int) prior
 575                // to this Write(ReadOnlySpan<byte>) overload being introduced.  In that case, this Write(ReadOnlySpan<b
 576                // should use the behavior of Write(byte[],int,int) overload.
 0577                base.Write(buffer);
 0578            }
 579            else
 219580            {
 219581                WriteCore(buffer);
 219582            }
 219583        }
 584
 585        internal void WriteCore(ReadOnlySpan<byte> buffer)
 219586        {
 219587            EnsureCompressionMode();
 219588            EnsureNotDisposed();
 589
 219590            if (buffer.IsEmpty)
 0591            {
 0592                return;
 593            }
 594
 595            // Write compressed the bytes we already passed to the deflater:
 219596            Debug.Assert(_deflater != null);
 219597            WriteDeflaterOutput();
 598
 599            unsafe
 219600            {
 601                // Pass new bytes through deflater and write them too:
 219602                fixed (byte* bufferPtr = &MemoryMarshal.GetReference(buffer))
 219603                {
 219604                    _deflater.SetInput(bufferPtr, buffer.Length);
 605                    try
 219606                    {
 219607                        WriteDeflaterOutput();
 219608                    }
 609                    finally
 219610                    {
 611                        // Discard any stale input reference so a later call (e.g. Dispose) doesn't read from a
 612                        // buffer the caller may have since mutated, reused, or freed. On the success path the
 613                        // input has already been fully consumed, so this is a no-op.
 219614                        _deflater.UnsetInput();
 219615                    }
 219616                }
 219617            }
 219618        }
 619
 620        private void WriteDeflaterOutput()
 657621        {
 657622            Debug.Assert(_deflater != null && _buffer != null);
 876623            while (!_deflater.NeedsInput())
 219624            {
 219625                int compressedBytes = _deflater.GetDeflateOutput(_buffer);
 219626                if (compressedBytes > 0)
 0627                {
 0628                    _stream.Write(_buffer, 0, compressedBytes);
 0629                }
 219630            }
 657631        }
 632
 633        // This is called by Flush:
 634        private void FlushBuffers()
 0635        {
 636            // Compress any bytes left:
 0637            WriteDeflaterOutput();
 638
 0639            Debug.Assert(_deflater != null && _buffer != null);
 640            // Pull out any bytes left inside deflater:
 641            bool flushSuccessful;
 642            do
 0643            {
 644                int compressedBytes;
 0645                flushSuccessful = _deflater.Flush(_buffer, out compressedBytes);
 0646                if (flushSuccessful)
 0647                {
 0648                    _stream.Write(_buffer, 0, compressedBytes);
 0649                }
 0650                Debug.Assert(flushSuccessful == (compressedBytes > 0));
 0651            } while (flushSuccessful);
 652
 653            // Always flush on the underlying stream
 0654            _stream.Flush();
 0655        }
 656
 657        // This is called by Dispose:
 658        private void PurgeBuffers(bool disposing)
 438659        {
 438660            if (!disposing)
 0661                return;
 662
 438663            if (_stream == null)
 0664                return;
 665
 438666            if (_mode != CompressionMode.Compress)
 219667                return;
 668
 219669            Debug.Assert(_deflater != null && _buffer != null);
 670            // Compress any bytes left
 219671            WriteDeflaterOutput();
 672
 673            // Pull out any bytes left inside deflater:
 674            bool finished;
 675            do
 219676            {
 677                int compressedBytes;
 219678                finished = _deflater.Finish(_buffer, out compressedBytes);
 679
 219680                if (compressedBytes > 0)
 219681                    _stream.Write(_buffer, 0, compressedBytes);
 438682            } while (!finished);
 438683        }
 684
 685        private async ValueTask PurgeBuffersAsync()
 438686        {
 687            // Same logic as PurgeBuffers, except with async counterparts.
 688
 438689            if (_stream == null)
 0690                return;
 691
 438692            if (_mode != CompressionMode.Compress)
 219693                return;
 694
 219695            Debug.Assert(_deflater != null && _buffer != null);
 696            // Compress any bytes left
 219697            await WriteDeflaterOutputAsync(default).ConfigureAwait(false);
 698
 699            // Pull out any bytes left inside deflater:
 700            bool finished;
 701            do
 219702            {
 703                int compressedBytes;
 219704                finished = _deflater.Finish(_buffer, out compressedBytes);
 705
 219706                if (compressedBytes > 0)
 219707                    await _stream.WriteAsync(new ReadOnlyMemory<byte>(_buffer, 0, compressedBytes)).ConfigureAwait(false
 438708            } while (!finished);
 438709        }
 710
 711        /// <summary>
 712        /// Rewinds the underlying stream to the exact end of the compressed data if there are unconsumed bytes.
 713        /// This is called when decompression finishes to reset the stream position.
 714        /// </summary>
 715        private void TryRewindStream(Stream stream)
 0716        {
 0717            Debug.Assert(stream != null);
 0718            Debug.Assert(_mode == CompressionMode.Decompress);
 0719            Debug.Assert(stream.CanSeek);
 0720            Debug.Assert(_inflater != null);
 721
 722            // Check if there are unconsumed bytes in the inflater's input buffer, plus any lone GZip ID1
 723            // (0x1F) byte that was speculatively consumed as an unconfirmed concatenated-member probe but
 724            // turned out to be trailing data.
 0725            int unconsumedBytes = _inflater.GetAvailableInput() + _inflater.UnconfirmedGZipProbeBytes;
 0726            if (unconsumedBytes > 0)
 0727            {
 728                try
 0729                {
 730                    // Rewind the stream to the exact end of the compressed data
 0731                    stream.Seek(-unconsumedBytes, SeekOrigin.Current);
 0732                }
 0733                catch
 0734                {
 735                    // If seeking fails, we don't want to throw during disposal
 0736                }
 0737            }
 0738        }
 739
 740        protected override void Dispose(bool disposing)
 438741        {
 742            try
 438743            {
 438744                PurgeBuffers(disposing);
 438745            }
 746            finally
 438747            {
 748                // Close the underlying stream even if PurgeBuffers threw.
 749                // Stream.Close() may throw here (may or may not be due to the same error).
 750                // In this case, we still need to clean up internal resources, hence the inner finally blocks.
 751                try
 438752                {
 438753                    if (disposing && !_leaveOpen)
 251754                    {
 251755                        _stream?.Dispose();
 251756                    }
 438757                }
 758                finally
 438759                {
 438760                    _stream = null!;
 761
 762                    try
 438763                    {
 438764                        _deflater?.Dispose();
 438765                        _inflater?.Dispose();
 438766                    }
 767                    finally
 438768                    {
 438769                        _deflater = null;
 438770                        _inflater = null;
 771
 438772                        byte[]? buffer = _buffer;
 438773                        if (buffer != null)
 406774                        {
 406775                            _buffer = null;
 406776                            if (!_activeAsyncOperation)
 406777                            {
 406778                                ArrayPool<byte>.Shared.Return(buffer);
 406779                            }
 406780                        }
 781
 438782                        base.Dispose(disposing);
 438783                    }
 438784                }
 438785            }
 438786        }
 787
 788        public override ValueTask DisposeAsync()
 438789        {
 438790            return GetType() == typeof(DeflateStream) ?
 438791                Core() :
 438792                base.DisposeAsync();
 793
 794            async ValueTask Core()
 438795            {
 796                // Same logic as Dispose(true), except with async counterparts.
 797                try
 438798                {
 438799                    await PurgeBuffersAsync().ConfigureAwait(false);
 438800                }
 801                finally
 438802                {
 803                    // Close the underlying stream even if PurgeBuffers threw.
 804                    // Stream.Close() may throw here (may or may not be due to the same error).
 805                    // In this case, we still need to clean up internal resources, hence the inner finally blocks.
 438806                    Stream stream = _stream;
 438807                    _stream = null!;
 808                    try
 438809                    {
 438810                        if (!_leaveOpen && stream != null)
 251811                        {
 251812                            await stream.DisposeAsync().ConfigureAwait(false);
 251813                        }
 438814                    }
 815                    finally
 438816                    {
 817                        try
 438818                        {
 438819                            _deflater?.Dispose();
 438820                            _inflater?.Dispose();
 438821                        }
 822                        finally
 438823                        {
 438824                            _deflater = null;
 438825                            _inflater = null;
 826
 438827                            byte[]? buffer = _buffer;
 438828                            if (buffer != null)
 406829                            {
 406830                                _buffer = null;
 406831                                if (!_activeAsyncOperation)
 406832                                {
 406833                                    ArrayPool<byte>.Shared.Return(buffer);
 406834                                }
 406835                            }
 438836                        }
 438837                    }
 438838                }
 839            }
 876840        }
 841
 842        public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? asyncCallback, obje
 0843            TaskToAsyncResult.Begin(WriteAsync(buffer, offset, count, CancellationToken.None), asyncCallback, asyncState
 844
 845        public override void EndWrite(IAsyncResult asyncResult)
 0846        {
 0847            EnsureCompressionMode();
 0848            EnsureNotDisposed();
 0849            TaskToAsyncResult.End(asyncResult);
 0850        }
 851
 852        public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
 0853        {
 0854            ValidateBufferArguments(buffer, offset, count);
 0855            return WriteAsyncMemory(new ReadOnlyMemory<byte>(buffer, offset, count), cancellationToken).AsTask();
 0856        }
 857
 858        public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
 219859        {
 219860            if (GetType() != typeof(DeflateStream))
 0861            {
 862                // Ensure that existing streams derived from DeflateStream and that override WriteAsync(byte[],...)
 863                // get their existing behaviors when the newer Memory-based overload is used.
 0864                return base.WriteAsync(buffer, cancellationToken);
 865            }
 866            else
 219867            {
 219868                return WriteAsyncMemory(buffer, cancellationToken);
 869            }
 219870        }
 871
 872        internal ValueTask WriteAsyncMemory(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken)
 219873        {
 219874            EnsureCompressionMode();
 219875            EnsureNoActiveAsyncOperation();
 219876            EnsureNotDisposed();
 877
 219878            return
 219879                cancellationToken.IsCancellationRequested ? ValueTask.FromCanceled(cancellationToken) :
 219880                buffer.IsEmpty ? default :
 219881                Core(buffer, cancellationToken);
 882
 883            async ValueTask Core(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken)
 219884            {
 219885                AsyncOperationStarting();
 886                try
 219887                {
 219888                    await WriteDeflaterOutputAsync(cancellationToken).ConfigureAwait(false);
 889
 890                    // Pass new bytes through deflater
 219891                    Debug.Assert(_deflater != null);
 219892                    _deflater.SetInput(buffer);
 893                    try
 219894                    {
 219895                        await WriteDeflaterOutputAsync(cancellationToken).ConfigureAwait(false);
 219896                    }
 897                    finally
 219898                    {
 899                        // Discard any stale input reference so a later call (e.g. Dispose) doesn't read from a
 900                        // buffer the caller may have since mutated, reused, or freed. On the success path the
 901                        // input has already been fully consumed, so this is a no-op.
 219902                        _deflater.UnsetInput();
 219903                    }
 219904                }
 905                finally
 219906                {
 219907                    AsyncOperationCompleting();
 219908                }
 219909            }
 219910        }
 911
 912        /// <summary>
 913        /// Writes the bytes that have already been deflated
 914        /// </summary>
 915        private async ValueTask WriteDeflaterOutputAsync(CancellationToken cancellationToken)
 657916        {
 657917            Debug.Assert(_deflater != null && _buffer != null);
 876918            while (!_deflater.NeedsInput())
 219919            {
 219920                int compressedBytes = _deflater.GetDeflateOutput(_buffer);
 219921                if (compressedBytes > 0)
 0922                {
 0923                    await _stream.WriteAsync(new ReadOnlyMemory<byte>(_buffer, 0, compressedBytes), cancellationToken).C
 0924                }
 219925            }
 657926        }
 927
 928        public override void CopyTo(Stream destination, int bufferSize)
 32929        {
 32930            ValidateCopyToArguments(destination, bufferSize);
 931
 32932            EnsureNotDisposed();
 32933            if (!CanRead) throw new NotSupportedException();
 934
 32935            new CopyToStream(this, destination, bufferSize).CopyFromSourceToDestination();
 32936        }
 937
 938        public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
 32939        {
 32940            ValidateCopyToArguments(destination, bufferSize);
 941
 32942            EnsureNotDisposed();
 32943            if (!CanRead) throw new NotSupportedException();
 32944            EnsureNoActiveAsyncOperation();
 945
 946            // Early check for cancellation
 32947            if (cancellationToken.IsCancellationRequested)
 0948            {
 0949                return Task.FromCanceled<int>(cancellationToken);
 950            }
 951
 952            // Do the copy
 32953            return new CopyToStream(this, destination, bufferSize, cancellationToken).CopyFromSourceToDestinationAsync()
 32954        }
 955
 956        private sealed class CopyToStream : Stream
 957        {
 958            private readonly DeflateStream _deflateStream;
 959            private readonly Stream _destination;
 960            private readonly CancellationToken _cancellationToken;
 961            private byte[] _arrayPoolBuffer;
 962
 963            public CopyToStream(DeflateStream deflateStream, Stream destination, int bufferSize) :
 32964                this(deflateStream, destination, bufferSize, CancellationToken.None)
 32965            {
 32966            }
 967
 64968            public CopyToStream(DeflateStream deflateStream, Stream destination, int bufferSize, CancellationToken cance
 64969            {
 64970                Debug.Assert(deflateStream != null);
 64971                Debug.Assert(destination != null);
 64972                Debug.Assert(bufferSize > 0);
 973
 64974                _deflateStream = deflateStream;
 64975                _destination = destination;
 64976                _cancellationToken = cancellationToken;
 64977                _arrayPoolBuffer = ArrayPool<byte>.Shared.Rent(bufferSize);
 64978            }
 979
 980            public async Task CopyFromSourceToDestinationAsync()
 32981            {
 32982                _deflateStream.AsyncOperationStarting();
 983                try
 32984                {
 32985                    Debug.Assert(_deflateStream._inflater != null);
 986
 987                    // If a lone trailing GZip ID1 (0x1F) probe was already resolved as end-of-stream and the
 988                    // base stream rewound, the inflater is terminally finished. A subsequent CopyToAsync would
 989                    // otherwise re-read the rewound byte from the base stream and spin (the inflater returns 0
 990                    // without consuming input while NeedsInput stays false), so return without re-reading.
 32991                    if (_deflateStream._inflater.EndOfStreamReached)
 0992                    {
 0993                        return;
 994                    }
 995
 996                    // Flush any existing data in the inflater to the destination stream.
 32997                    while (!_deflateStream._inflater.Finished())
 32998                    {
 32999                        int bytesRead = _deflateStream._inflater.Inflate(_arrayPoolBuffer, 0, _arrayPoolBuffer.Length);
 321000                        if (bytesRead > 0)
 01001                        {
 01002                            await _destination.WriteAsync(new ReadOnlyMemory<byte>(_arrayPoolBuffer, 0, bytesRead), _can
 01003                        }
 321004                        else if (_deflateStream._inflater.NeedsInput())
 321005                        {
 1006                            // only break if we read 0 and ran out of input, if input is still available it may be anoth
 321007                            break;
 1008                        }
 01009                    }
 1010
 1011                    // Now, use the source stream's CopyToAsync to push directly to our inflater via this helper stream
 321012                    await _deflateStream._stream.CopyToAsync(this, _arrayPoolBuffer.Length, _cancellationToken).Configur
 1013                    // A lone trailing GZip ID1 (0x1F) probe only suppresses the truncated-data error when the
 1014                    // base stream is seekable, since only then can the byte be rewound below. On non-seekable
 1015                    // streams strict validation still throws, preserving the existing behavior.
 321016                    if (s_useStrictValidation && !_deflateStream._inflater.Finished() &&
 321017                        !(_deflateStream._inflater.HasUnconfirmedGZipProbe && _deflateStream._stream.CanSeek))
 01018                    {
 01019                        ThrowTruncatedInvalidData();
 01020                    }
 1021
 1022                    // Rewind the stream if decompression has finished and the stream supports seeking. A lone
 1023                    // trailing GZip ID1 (0x1F) byte that was speculatively consumed as a possible concatenated
 1024                    // member also counts as finished here: it was actually trailing data.
 321025                    if ((_deflateStream._inflater.Finished() || _deflateStream._inflater.HasUnconfirmedGZipProbe) && !_d
 01026                    {
 01027                        if (_deflateStream._inflater.HasUnconfirmedGZipProbe)
 01028                        {
 01029                            _deflateStream._inflater.MarkEndOfStream();
 01030                        }
 01031                        _deflateStream.TryRewindStream(_deflateStream._stream);
 01032                        _deflateStream._decompressionFinished = true;
 01033                    }
 321034                }
 1035                finally
 321036                {
 321037                    _deflateStream.AsyncOperationCompleting();
 1038
 321039                    ArrayPool<byte>.Shared.Return(_arrayPoolBuffer);
 321040                    _arrayPoolBuffer = null!;
 321041                }
 321042            }
 1043
 1044            public void CopyFromSourceToDestination()
 321045            {
 1046                try
 321047                {
 321048                    Debug.Assert(_deflateStream._inflater != null);
 1049
 1050                    // If a lone trailing GZip ID1 (0x1F) probe was already resolved as end-of-stream and the
 1051                    // base stream rewound, the inflater is terminally finished. A subsequent CopyTo would
 1052                    // otherwise re-read the rewound byte from the base stream and spin (the inflater returns 0
 1053                    // without consuming input while NeedsInput stays false), so return without re-reading.
 321054                    if (_deflateStream._inflater.EndOfStreamReached)
 01055                    {
 01056                        return;
 1057                    }
 1058
 1059                    // Flush any existing data in the inflater to the destination stream.
 321060                    while (!_deflateStream._inflater.Finished())
 321061                    {
 321062                        int bytesRead = _deflateStream._inflater.Inflate(_arrayPoolBuffer, 0, _arrayPoolBuffer.Length);
 321063                        if (bytesRead > 0)
 01064                        {
 01065                            _destination.Write(_arrayPoolBuffer, 0, bytesRead);
 01066                        }
 321067                        else if (_deflateStream._inflater.NeedsInput())
 321068                        {
 1069                            // only break if we read 0 and ran out of input, if input is still available it may be anoth
 321070                            break;
 1071                        }
 01072                    }
 1073
 1074                    // Now, use the source stream's CopyToAsync to push directly to our inflater via this helper stream
 321075                    _deflateStream._stream.CopyTo(this, _arrayPoolBuffer.Length);
 1076                    // A lone trailing GZip ID1 (0x1F) probe only suppresses the truncated-data error when the
 1077                    // base stream is seekable, since only then can the byte be rewound below. On non-seekable
 1078                    // streams strict validation still throws, preserving the existing behavior.
 321079                    if (s_useStrictValidation && !_deflateStream._inflater.Finished() &&
 321080                        !(_deflateStream._inflater.HasUnconfirmedGZipProbe && _deflateStream._stream.CanSeek))
 01081                    {
 01082                        ThrowTruncatedInvalidData();
 01083                    }
 1084
 1085                    // Rewind the stream if decompression has finished and the stream supports seeking. A lone
 1086                    // trailing GZip ID1 (0x1F) byte that was speculatively consumed as a possible concatenated
 1087                    // member also counts as finished here: it was actually trailing data.
 321088                    if ((_deflateStream._inflater.Finished() || _deflateStream._inflater.HasUnconfirmedGZipProbe) && !_d
 01089                    {
 01090                        if (_deflateStream._inflater.HasUnconfirmedGZipProbe)
 01091                        {
 01092                            _deflateStream._inflater.MarkEndOfStream();
 01093                        }
 01094                        _deflateStream.TryRewindStream(_deflateStream._stream);
 01095                        _deflateStream._decompressionFinished = true;
 01096                    }
 321097                }
 1098                finally
 321099                {
 321100                    ArrayPool<byte>.Shared.Return(_arrayPoolBuffer);
 321101                    _arrayPoolBuffer = null!;
 321102                }
 321103            }
 1104
 1105            public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
 01106            {
 01107                Debug.Assert(buffer != _arrayPoolBuffer);
 01108                _deflateStream.EnsureNotDisposed();
 01109                if (count <= 0)
 01110                {
 01111                    return Task.CompletedTask;
 1112                }
 01113                else if (count > buffer.Length - offset)
 01114                {
 1115                    // The buffer stream is either malicious or poorly implemented and returned a number of
 1116                    // bytes larger than the buffer supplied to it.
 01117                    return Task.FromException(new InvalidDataException(SR.GenericInvalidData));
 1118                }
 1119
 01120                return WriteAsyncCore(buffer.AsMemory(offset, count), cancellationToken).AsTask();
 01121            }
 1122
 1123            public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = defa
 321124            {
 321125                _deflateStream.EnsureNotDisposed();
 1126
 321127                return WriteAsyncCore(buffer, cancellationToken);
 321128            }
 1129
 1130            private async ValueTask WriteAsyncCore(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken)
 321131            {
 321132                Debug.Assert(_deflateStream._inflater is not null);
 1133
 1134                // Feed the data from base stream into decompression engine.
 321135                _deflateStream._inflater.SetInput(buffer);
 1136
 1137                try
 321138                {
 1139                    // While there's more decompressed data available, forward it to the buffer stream.
 641140                    while (!_deflateStream._inflater.Finished())
 321141                    {
 321142                        int bytesRead = _deflateStream._inflater.Inflate(new Span<byte>(_arrayPoolBuffer));
 321143                        if (bytesRead > 0)
 321144                        {
 321145                            await _destination.WriteAsync(new ReadOnlyMemory<byte>(_arrayPoolBuffer, 0, bytesRead), canc
 321146                        }
 01147                        else if (_deflateStream._inflater.NeedsInput())
 01148                        {
 1149                            // only break if we read 0 and ran out of input, if input is still available it may be anoth
 01150                            break;
 1151                        }
 321152                    }
 321153                }
 01154                catch
 01155                {
 1156                    // Discard any stale input reference to "buffer" so the inflater doesn't retain a
 1157                    // dangling reference once this exception propagates.
 01158                    _deflateStream._inflater.UnsetInput();
 01159                    throw;
 1160                }
 321161            }
 1162
 1163            public override void Write(byte[] buffer, int offset, int count)
 321164            {
 321165                Debug.Assert(buffer != _arrayPoolBuffer);
 321166                _deflateStream.EnsureNotDisposed();
 1167
 321168                if (count <= 0)
 01169                {
 01170                    return;
 1171                }
 321172                else if (count > buffer.Length - offset)
 01173                {
 1174                    // The buffer stream is either malicious or poorly implemented and returned a number of
 1175                    // bytes larger than the buffer supplied to it.
 01176                    throw new InvalidDataException(SR.GenericInvalidData);
 1177                }
 1178
 321179                Debug.Assert(_deflateStream._inflater != null);
 1180                // Feed the data from base stream into the decompression engine.
 321181                _deflateStream._inflater.SetInput(buffer, offset, count);
 1182
 1183                try
 321184                {
 1185                    // While there's more decompressed data available, forward it to the buffer stream.
 641186                    while (!_deflateStream._inflater.Finished())
 321187                    {
 321188                        int bytesRead = _deflateStream._inflater.Inflate(new Span<byte>(_arrayPoolBuffer));
 321189                        if (bytesRead > 0)
 321190                        {
 321191                            _destination.Write(_arrayPoolBuffer, 0, bytesRead);
 321192                        }
 01193                        else if (_deflateStream._inflater.NeedsInput())
 01194                        {
 1195                            // only break if we read 0 and ran out of input, if input is still available it may be anoth
 01196                            break;
 1197                        }
 321198                    }
 321199                }
 01200                catch
 01201                {
 1202                    // Discard any stale input reference to "buffer" so the inflater doesn't retain a
 1203                    // dangling reference once this exception propagates.
 01204                    _deflateStream._inflater.UnsetInput();
 01205                    throw;
 1206                }
 321207            }
 1208
 641209            public override bool CanWrite => true;
 01210            public override void Flush() { }
 01211            public override bool CanRead => false;
 01212            public override bool CanSeek => false;
 01213            public override long Length { get { throw new NotSupportedException(); } }
 01214            public override long Position { get { throw new NotSupportedException(); } set { throw new NotSupportedExcep
 01215            public override int Read(byte[] buffer, int offset, int count) { throw new NotSupportedException(); }
 01216            public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); }
 01217            public override void SetLength(long value) { throw new NotSupportedException(); }
 1218        }
 1219
 1220        private void EnsureNoActiveAsyncOperation()
 6251221        {
 6251222            if (_activeAsyncOperation)
 01223            {
 01224                ThrowInvalidBeginCall();
 01225            }
 6251226        }
 1227
 1228        private void AsyncOperationStarting()
 6251229        {
 6251230            if (Interlocked.Exchange(ref _activeAsyncOperation, true))
 01231            {
 01232                ThrowInvalidBeginCall();
 01233            }
 6251234        }
 1235
 1236        private void AsyncOperationCompleting()
 6251237        {
 6251238            Debug.Assert(_activeAsyncOperation);
 6251239            _activeAsyncOperation = false;
 6251240        }
 1241
 1242        private static void ThrowInvalidBeginCall() =>
 01243            throw new InvalidOperationException(SR.InvalidBeginCall);
 1244
 11245        private static readonly bool s_useStrictValidation =
 11246            AppContext.TryGetSwitch("System.IO.Compression.UseStrictValidation", out bool strictValidation) ? strictVali
 1247    }
 1248}
 1249

Methods/Properties

.ctor(System.IO.Stream,System.IO.Compression.CompressionMode,System.Int64)
.ctor(System.IO.Stream,System.IO.Compression.CompressionMode)
.ctor(System.IO.Stream,System.IO.Compression.CompressionMode,System.Boolean)
.ctor(System.IO.Stream,System.IO.Compression.CompressionLevel)
.ctor(System.IO.Stream,System.IO.Compression.CompressionLevel,System.Boolean)
.ctor(System.IO.Stream,System.IO.Compression.ZLibCompressionOptions,System.Boolean)
.ctor(System.IO.Stream,System.IO.Compression.ZLibCompressionOptions,System.Boolean,System.Int32)
.ctor(System.IO.Stream,System.IO.Compression.CompressionMode,System.Boolean,System.Int32,System.Int64)
.ctor(System.IO.Stream,System.IO.Compression.CompressionLevel,System.Boolean,System.Int32)
InitializeDeflater(System.IO.Stream,System.IO.Compression.ZLibNative/CompressionLevel,System.IO.Compression.ZLibNative/CompressionStrategy,System.Boolean,System.Int32)
GetZLibNativeCompressionLevel(System.IO.Compression.CompressionLevel)
GetMemLevel(System.IO.Compression.ZLibNative/CompressionLevel)
InitializeBuffer()
EnsureBufferInitialized()
BaseStream()
CanRead()
CanWrite()
CanSeek()
Length()
Position()
Position(System.Int64)
Flush()
FlushAsync(System.Threading.CancellationToken)
Core(System.Threading.CancellationToken)
Seek(System.Int64,System.IO.SeekOrigin)
SetLength(System.Int64)
ReadByte()
Read(System.Byte[],System.Int32,System.Int32)
Read(System.Span`1<System.Byte>)
ReadCore(System.Span`1<System.Byte>)
InflatorIsFinished()
EnsureNotDisposed()
EnsureDecompressionMode()
ThrowCannotReadFromDeflateStreamException()
EnsureCompressionMode()
ThrowCannotWriteToDeflateStreamException()
ThrowGenericInvalidData()
ThrowTruncatedInvalidData()
BeginRead(System.Byte[],System.Int32,System.Int32,System.AsyncCallback,System.Object)
EndRead(System.IAsyncResult)
ReadAsync(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken)
ReadAsync(System.Memory`1<System.Byte>,System.Threading.CancellationToken)
ReadAsyncMemory(System.Memory`1<System.Byte>,System.Threading.CancellationToken)
Core(System.Memory`1<System.Byte>,System.Threading.CancellationToken)
Write(System.Byte[],System.Int32,System.Int32)
WriteByte(System.Byte)
Write(System.ReadOnlySpan`1<System.Byte>)
WriteCore(System.ReadOnlySpan`1<System.Byte>)
WriteDeflaterOutput()
FlushBuffers()
PurgeBuffers(System.Boolean)
PurgeBuffersAsync()
TryRewindStream(System.IO.Stream)
Dispose(System.Boolean)
DisposeAsync()
Core()
BeginWrite(System.Byte[],System.Int32,System.Int32,System.AsyncCallback,System.Object)
EndWrite(System.IAsyncResult)
WriteAsync(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken)
WriteAsync(System.ReadOnlyMemory`1<System.Byte>,System.Threading.CancellationToken)
WriteAsyncMemory(System.ReadOnlyMemory`1<System.Byte>,System.Threading.CancellationToken)
Core(System.ReadOnlyMemory`1<System.Byte>,System.Threading.CancellationToken)
WriteDeflaterOutputAsync(System.Threading.CancellationToken)
CopyTo(System.IO.Stream,System.Int32)
CopyToAsync(System.IO.Stream,System.Int32,System.Threading.CancellationToken)
.ctor(System.IO.Compression.DeflateStream,System.IO.Stream,System.Int32)
.ctor(System.IO.Compression.DeflateStream,System.IO.Stream,System.Int32,System.Threading.CancellationToken)
CopyFromSourceToDestinationAsync()
CopyFromSourceToDestination()
WriteAsync(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken)
WriteAsync(System.ReadOnlyMemory`1<System.Byte>,System.Threading.CancellationToken)
WriteAsyncCore(System.ReadOnlyMemory`1<System.Byte>,System.Threading.CancellationToken)
Write(System.Byte[],System.Int32,System.Int32)
CanWrite()
Flush()
CanRead()
CanSeek()
Length()
Position()
Read(System.Byte[],System.Int32,System.Int32)
Seek(System.Int64,System.IO.SeekOrigin)
SetLength(System.Int64)
EnsureNoActiveAsyncOperation()
AsyncOperationStarting()
AsyncOperationCompleting()
ThrowInvalidBeginCall()
.cctor()