< Summary

Information
Class: System.IO.Compression.CrcValidatingReadStream
Assembly: System.IO.Compression
File(s): D:\runner\runtime\src\libraries\System.IO.Compression\src\System\IO\Compression\ZipCustomStreams.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 151
Coverable lines: 151
Total lines: 757
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 40
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%
Read(...)100%110%
Read(...)0%220%
ReadByte()0%220%
ReadAsync(...)100%110%
ReadAsync(...)0%220%
ProcessBytesRead(...)0%10100%
ValidateCrc()0%660%
ResetCrcState()100%110%
Write(...)100%110%
Flush()100%110%
FlushAsync(...)100%110%
Seek(...)0%220%
SetLength(...)100%110%
ThrowIfDisposed()0%220%
ThrowIfCantSeek()0%220%
Dispose(...)0%440%
DisposeAsync()0%220%

File(s)

D:\runner\runtime\src\libraries\System.IO.Compression\src\System\IO\Compression\ZipCustomStreams.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.Diagnostics;
 5using System.Runtime.InteropServices;
 6using System.Threading;
 7using System.Threading.Tasks;
 8
 9namespace System.IO.Compression
 10{
 11    internal sealed class WrappedStream : Stream
 12    {
 13        private readonly Stream _baseStream;
 14        private readonly bool _closeBaseStream;
 15
 16        // Delegate that will be invoked on stream disposing
 17        private readonly Action<ZipArchiveEntry?>? _onClosed;
 18
 19        // When true, notifies the entry on first write operation
 20        private bool _notifyEntryOnWrite;
 21
 22        // Instance that will be passed to _onClose delegate
 23        private readonly ZipArchiveEntry? _zipArchiveEntry;
 24        private bool _isDisposed;
 25
 26        internal WrappedStream(Stream baseStream, bool closeBaseStream)
 27            : this(baseStream, closeBaseStream, entry: null, onClosed: null, notifyEntryOnWrite: false) { }
 28
 29        private WrappedStream(Stream baseStream, bool closeBaseStream, ZipArchiveEntry? entry, Action<ZipArchiveEntry?>?
 30        {
 31            _baseStream = baseStream;
 32            _closeBaseStream = closeBaseStream;
 33            _onClosed = onClosed;
 34            _notifyEntryOnWrite = notifyEntryOnWrite;
 35            _zipArchiveEntry = entry;
 36            _isDisposed = false;
 37        }
 38
 39        internal WrappedStream(Stream baseStream, ZipArchiveEntry entry, Action<ZipArchiveEntry?>? onClosed, bool notify
 40            : this(baseStream, false, entry, onClosed, notifyEntryOnWrite) { }
 41
 42        public override long Length
 43        {
 44            get
 45            {
 46                ThrowIfDisposed();
 47                return _baseStream.Length;
 48            }
 49        }
 50
 51        public override long Position
 52        {
 53            get
 54            {
 55                ThrowIfDisposed();
 56                return _baseStream.Position;
 57            }
 58            set
 59            {
 60                ThrowIfDisposed();
 61                ThrowIfCantSeek();
 62
 63                _baseStream.Position = value;
 64            }
 65        }
 66
 67        public override bool CanRead => !_isDisposed && _baseStream.CanRead;
 68
 69        public override bool CanSeek => !_isDisposed && _baseStream.CanSeek;
 70
 71        public override bool CanWrite => !_isDisposed && _baseStream.CanWrite;
 72
 73        private void ThrowIfDisposed()
 74        {
 75            if (_isDisposed)
 76                throw new ObjectDisposedException(GetType().ToString(), SR.HiddenStreamName);
 77        }
 78
 79        private void ThrowIfCantRead()
 80        {
 81            if (!CanRead)
 82                throw new NotSupportedException(SR.ReadingNotSupported);
 83        }
 84
 85        private void ThrowIfCantWrite()
 86        {
 87            if (!CanWrite)
 88                throw new NotSupportedException(SR.WritingNotSupported);
 89        }
 90
 91        private void ThrowIfCantSeek()
 92        {
 93            if (!CanSeek)
 94                throw new NotSupportedException(SR.SeekingNotSupported);
 95        }
 96
 97        public override int Read(byte[] buffer, int offset, int count)
 98        {
 99            ThrowIfDisposed();
 100            ThrowIfCantRead();
 101
 102            return _baseStream.Read(buffer, offset, count);
 103        }
 104
 105        public override int Read(Span<byte> buffer)
 106        {
 107            ThrowIfDisposed();
 108            ThrowIfCantRead();
 109
 110            return _baseStream.Read(buffer);
 111        }
 112
 113        public override int ReadByte()
 114        {
 115            ThrowIfDisposed();
 116            ThrowIfCantRead();
 117
 118            return _baseStream.ReadByte();
 119        }
 120
 121        public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
 122        {
 123            ThrowIfDisposed();
 124            ThrowIfCantRead();
 125
 126            return _baseStream.ReadAsync(buffer, offset, count, cancellationToken);
 127        }
 128
 129        public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
 130        {
 131            ThrowIfDisposed();
 132            ThrowIfCantRead();
 133
 134            return _baseStream.ReadAsync(buffer, cancellationToken);
 135        }
 136
 137        public override long Seek(long offset, SeekOrigin origin)
 138        {
 139            ThrowIfDisposed();
 140            ThrowIfCantSeek();
 141
 142            return _baseStream.Seek(offset, origin);
 143        }
 144
 145        public override void SetLength(long value)
 146        {
 147            ThrowIfDisposed();
 148            ThrowIfCantSeek();
 149            ThrowIfCantWrite();
 150
 151            NotifyWrite();
 152            _baseStream.SetLength(value);
 153        }
 154
 155        public override void Write(byte[] buffer, int offset, int count)
 156        {
 157            ThrowIfDisposed();
 158            ThrowIfCantWrite();
 159
 160            NotifyWrite();
 161            _baseStream.Write(buffer, offset, count);
 162        }
 163
 164        public override void Write(ReadOnlySpan<byte> source)
 165        {
 166            ThrowIfDisposed();
 167            ThrowIfCantWrite();
 168
 169            NotifyWrite();
 170            _baseStream.Write(source);
 171        }
 172
 173        public override void WriteByte(byte value)
 174        {
 175            ThrowIfDisposed();
 176            ThrowIfCantWrite();
 177
 178            NotifyWrite();
 179            _baseStream.WriteByte(value);
 180        }
 181
 182        public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
 183        {
 184            ThrowIfDisposed();
 185            ThrowIfCantWrite();
 186
 187            NotifyWrite();
 188            return _baseStream.WriteAsync(buffer, offset, count, cancellationToken);
 189        }
 190
 191        public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
 192        {
 193            ThrowIfDisposed();
 194            ThrowIfCantWrite();
 195
 196            NotifyWrite();
 197            return _baseStream.WriteAsync(buffer, cancellationToken);
 198        }
 199
 200        private void NotifyWrite()
 201        {
 202            if (_notifyEntryOnWrite)
 203            {
 204                _zipArchiveEntry?.MarkAsModified();
 205                _notifyEntryOnWrite = false; // Only notify once
 206            }
 207        }
 208
 209        public override void Flush()
 210        {
 211            ThrowIfDisposed();
 212            ThrowIfCantWrite();
 213
 214            _baseStream.Flush();
 215        }
 216
 217        public override Task FlushAsync(CancellationToken cancellationToken)
 218        {
 219            ThrowIfDisposed();
 220            ThrowIfCantWrite();
 221
 222            return _baseStream.FlushAsync(cancellationToken);
 223        }
 224
 225        protected override void Dispose(bool disposing)
 226        {
 227            if (disposing && !_isDisposed)
 228            {
 229                _onClosed?.Invoke(_zipArchiveEntry);
 230
 231                if (_closeBaseStream)
 232                    _baseStream.Dispose();
 233
 234                _isDisposed = true;
 235            }
 236            base.Dispose(disposing);
 237        }
 238
 239        public override async ValueTask DisposeAsync()
 240        {
 241            if (!_isDisposed)
 242            {
 243                _onClosed?.Invoke(_zipArchiveEntry);
 244
 245                if (_closeBaseStream)
 246                    await _baseStream.DisposeAsync().ConfigureAwait(false);
 247
 248                _isDisposed = true;
 249            }
 250            await base.DisposeAsync().ConfigureAwait(false);
 251        }
 252    }
 253
 254    internal sealed class CheckSumAndSizeWriteStream : Stream
 255    {
 256        private readonly Func<Stream> _baseStreamFactory;
 257        private Stream? _baseStream;
 258        private readonly Stream _baseBaseStream;
 259        private long _position;
 260        private uint _checksum;
 261
 262        private readonly bool _leaveOpenOnClose;
 263        private readonly bool _canWrite;
 264        private bool _isDisposed;
 265
 266        private bool _everWritten;
 267
 268        // this is the position in BaseBaseStream
 269        private long _initialPosition;
 270        private readonly ZipArchiveEntry _zipArchiveEntry;
 271        private readonly EventHandler? _onClose;
 272        // Called when the stream is closed.
 273        // parameters are initialPosition, currentPosition, checkSum, baseBaseStream, zipArchiveEntry and onClose handle
 274        private readonly Action<long, long, uint, Stream, ZipArchiveEntry, EventHandler?> _saveCrcAndSizes;
 275
 276        // parameters to saveCrcAndSizes are
 277        // initialPosition (initialPosition in baseBaseStream),
 278        // currentPosition (in this CheckSumAndSizeWriteStream),
 279        // checkSum (of data passed into this CheckSumAndSizeWriteStream),
 280        // baseBaseStream it's a backingStream, passed here so as to avoid closure allocation,
 281        // zipArchiveEntry passed here so as to avoid closure allocation,
 282        // onClose handler passed here so as to avoid closure allocation
 283        public CheckSumAndSizeWriteStream(Func<Stream> baseStreamFactory, Stream baseBaseStream, bool leaveOpenOnClose,
 284            ZipArchiveEntry entry, EventHandler? onClose,
 285            Action<long, long, uint, Stream, ZipArchiveEntry, EventHandler?> saveCrcAndSizes)
 286        {
 287            _baseStreamFactory = baseStreamFactory;
 288            _baseBaseStream = baseBaseStream;
 289            _position = 0;
 290            _checksum = 0;
 291            _leaveOpenOnClose = leaveOpenOnClose;
 292            _canWrite = true;
 293            _isDisposed = false;
 294            _initialPosition = 0;
 295            _zipArchiveEntry = entry;
 296            _onClose = onClose;
 297            _saveCrcAndSizes = saveCrcAndSizes;
 298        }
 299
 300        public override long Length
 301        {
 302            get
 303            {
 304                ThrowIfDisposed();
 305                throw new NotSupportedException(SR.SeekingNotSupported);
 306            }
 307        }
 308
 309        public override long Position
 310        {
 311            get
 312            {
 313                ThrowIfDisposed();
 314                return _position;
 315            }
 316            set
 317            {
 318                ThrowIfDisposed();
 319                throw new NotSupportedException(SR.SeekingNotSupported);
 320            }
 321        }
 322
 323        public override bool CanRead => false;
 324
 325        public override bool CanSeek => false;
 326
 327        public override bool CanWrite => _canWrite;
 328
 329        private void ThrowIfDisposed()
 330        {
 331            if (_isDisposed)
 332                throw new ObjectDisposedException(GetType().ToString(), SR.HiddenStreamName);
 333        }
 334
 335        public override int Read(byte[] buffer, int offset, int count)
 336        {
 337            ThrowIfDisposed();
 338            throw new NotSupportedException(SR.ReadingNotSupported);
 339        }
 340
 341        public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
 342        {
 343            ThrowIfDisposed();
 344            throw new NotSupportedException(SR.ReadingNotSupported);
 345        }
 346
 347        public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
 348        {
 349            ThrowIfDisposed();
 350            throw new NotSupportedException(SR.ReadingNotSupported);
 351        }
 352
 353        public override long Seek(long offset, SeekOrigin origin)
 354        {
 355            ThrowIfDisposed();
 356            throw new NotSupportedException(SR.SeekingNotSupported);
 357        }
 358
 359        public override void SetLength(long value)
 360        {
 361            ThrowIfDisposed();
 362            throw new NotSupportedException(SR.SetLengthRequiresSeekingAndWriting);
 363        }
 364
 365        public override void Write(byte[] buffer, int offset, int count)
 366        {
 367            // we can't pass the argument checking down a level
 368            ValidateBufferArguments(buffer, offset, count);
 369
 370            // if we're not actually writing anything, we don't want to trigger as if we did write something
 371            ThrowIfDisposed();
 372            Debug.Assert(CanWrite);
 373
 374            if (count == 0)
 375                return;
 376
 377            if (!_everWritten)
 378            {
 379                Debug.Assert(_baseStream == null);
 380                _baseStream = _baseStreamFactory();
 381
 382                _initialPosition = _baseBaseStream.Position;
 383                _everWritten = true;
 384            }
 385
 386            Debug.Assert(_baseStream != null);
 387
 388            _checksum = Crc32Helper.UpdateCrc32(_checksum, buffer, offset, count);
 389            _baseStream.Write(buffer, offset, count);
 390            _position += count;
 391        }
 392
 393        public override void Write(ReadOnlySpan<byte> source)
 394        {
 395            // if we're not actually writing anything, we don't want to trigger as if we did write something
 396            ThrowIfDisposed();
 397            Debug.Assert(CanWrite);
 398
 399            if (source.Length == 0)
 400                return;
 401
 402            if (!_everWritten)
 403            {
 404                Debug.Assert(_baseStream == null);
 405                _baseStream = _baseStreamFactory();
 406
 407                _initialPosition = _baseBaseStream.Position;
 408                _everWritten = true;
 409            }
 410
 411            Debug.Assert(_baseStream != null);
 412
 413            _checksum = Crc32Helper.UpdateCrc32(_checksum, source);
 414            _baseStream.Write(source);
 415            _position += source.Length;
 416        }
 417
 418        public override void WriteByte(byte value) =>
 419            Write(new ReadOnlySpan<byte>(in value));
 420
 421        public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
 422        {
 423            ValidateBufferArguments(buffer, offset, count);
 424            return WriteAsync(new ReadOnlyMemory<byte>(buffer, offset, count), cancellationToken).AsTask();
 425        }
 426
 427        public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
 428        {
 429            ThrowIfDisposed();
 430            Debug.Assert(CanWrite);
 431
 432            return !buffer.IsEmpty ?
 433                Core(buffer, cancellationToken) :
 434                default;
 435
 436            async ValueTask Core(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
 437            {
 438                if (!_everWritten)
 439                {
 440                    Debug.Assert(_baseStream == null);
 441                    _baseStream = _baseStreamFactory();
 442
 443                    _initialPosition = _baseBaseStream.Position;
 444                    _everWritten = true;
 445                }
 446
 447                Debug.Assert(_baseStream != null);
 448
 449                _checksum = Crc32Helper.UpdateCrc32(_checksum, buffer.Span);
 450
 451                await _baseStream.WriteAsync(buffer, cancellationToken).ConfigureAwait(false);
 452                _position += buffer.Length;
 453            }
 454        }
 455
 456        public override void Flush()
 457        {
 458            ThrowIfDisposed();
 459
 460            // assume writable if not disposed
 461            Debug.Assert(CanWrite);
 462
 463            _baseStream?.Flush();
 464        }
 465
 466        public override Task FlushAsync(CancellationToken cancellationToken)
 467        {
 468            ThrowIfDisposed();
 469            return _baseStream?.FlushAsync(cancellationToken) ?? Task.CompletedTask;
 470        }
 471
 472        protected override void Dispose(bool disposing)
 473        {
 474            if (disposing && !_isDisposed)
 475            {
 476                // if we never wrote through here, save the position
 477                if (!_everWritten)
 478                    _initialPosition = _baseBaseStream.Position;
 479                if (!_leaveOpenOnClose)
 480                    _baseStream?.Dispose(); // Close my super-stream (flushes the last data if we ever wrote any)
 481                _saveCrcAndSizes?.Invoke(_initialPosition, Position, _checksum, _baseBaseStream, _zipArchiveEntry, _onCl
 482                _isDisposed = true;
 483            }
 484            base.Dispose(disposing);
 485        }
 486
 487        public override async ValueTask DisposeAsync()
 488        {
 489            if (!_isDisposed)
 490            {
 491                // if we never wrote through here, save the position
 492                if (!_everWritten)
 493                    _initialPosition = _baseBaseStream.Position;
 494                if (!_leaveOpenOnClose && _baseStream != null)
 495                    await _baseStream.DisposeAsync().ConfigureAwait(false); // Close my super-stream (flushes the last d
 496                _saveCrcAndSizes?.Invoke(_initialPosition, Position, _checksum, _baseBaseStream, _zipArchiveEntry, _onCl
 497                _isDisposed = true;
 498            }
 499            await base.DisposeAsync().ConfigureAwait(false);
 500        }
 501    }
 502
 503    internal sealed class CrcValidatingReadStream : Stream
 504    {
 505        private readonly Stream _baseStream;
 506        private uint _runningCrc;       // CRC32 computed incrementally over bytes read
 507        private readonly uint _expectedCrc;
 508        private long _totalBytesRead;
 509        private readonly long _expectedLength;
 510        private bool _isDisposed;
 511        private bool _crcValidated;     // Whether CRC check has been performed
 512        private bool _crcAbandoned;     // Set when seeking makes CRC validation unreliable
 513
 0514        public CrcValidatingReadStream(Stream baseStream, uint expectedCrc, long expectedLength)
 0515        {
 0516            ArgumentNullException.ThrowIfNull(baseStream);
 0517            ArgumentOutOfRangeException.ThrowIfNegative(expectedLength);
 518
 0519            _baseStream = baseStream;
 0520            _expectedCrc = expectedCrc;
 0521            _expectedLength = expectedLength;
 0522            _runningCrc = 0;
 0523        }
 524
 0525        public override bool CanRead => !_isDisposed && _baseStream.CanRead;
 0526        public override bool CanSeek => !_isDisposed && _baseStream.CanSeek;
 0527        public override bool CanWrite => false;
 528
 529        public override long Length
 530        {
 531            get
 0532            {
 0533                ThrowIfDisposed();
 0534                return _baseStream.Length;
 0535            }
 536        }
 537
 538        public override long Position
 539        {
 540            get
 0541            {
 0542                ThrowIfDisposed();
 0543                return _baseStream.Position;
 0544            }
 545            set
 0546            {
 0547                ThrowIfDisposed();
 0548                ThrowIfCantSeek();
 549
 0550                _baseStream.Position = value;
 551
 0552                if (value == 0)
 0553                {
 0554                    ResetCrcState();
 0555                }
 556                else
 0557                {
 0558                    _crcAbandoned = true;
 0559                }
 0560            }
 561        }
 562
 563        public override int Read(byte[] buffer, int offset, int count)
 0564        {
 0565            ThrowIfDisposed();
 0566            ValidateBufferArguments(buffer, offset, count);
 567
 0568            return Read(new Span<byte>(buffer, offset, count));
 0569        }
 570
 571        public override int Read(Span<byte> buffer)
 0572        {
 0573            ThrowIfDisposed();
 574
 0575            int bytesRead = _baseStream.Read(buffer);
 576            // Only process when a real read occurred or EOF was signaled
 577            // (EOF = requested > 0 but got 0 back). Skip zero-length requests.
 0578            if (buffer.Length > 0)
 0579            {
 0580                ProcessBytesRead(buffer.Slice(0, bytesRead));
 0581            }
 582
 0583            return bytesRead;
 0584        }
 585
 586        public override int ReadByte()
 0587        {
 0588            byte b = default;
 0589            return Read(new Span<byte>(ref b)) == 1 ? b : -1;
 0590        }
 591
 592        public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
 0593        {
 0594            ThrowIfDisposed();
 0595            ValidateBufferArguments(buffer, offset, count);
 596
 0597            return ReadAsync(buffer.AsMemory(offset, count), cancellationToken).AsTask();
 0598        }
 599
 600        public override async ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = defaul
 0601        {
 0602            ThrowIfDisposed();
 603
 0604            int bytesRead = await _baseStream.ReadAsync(buffer, cancellationToken).ConfigureAwait(false);
 605            // Only process when a real read occurred or EOF was signaled
 606            // (EOF = requested > 0 but got 0 back). Skip zero-length requests.
 0607            if (buffer.Length > 0)
 0608            {
 0609                ProcessBytesRead(buffer.Span.Slice(0, bytesRead));
 0610            }
 611
 0612            return bytesRead;
 0613        }
 614
 615        private void ProcessBytesRead(ReadOnlySpan<byte> data)
 0616        {
 0617            if (_crcAbandoned)
 0618            {
 0619                return;
 620            }
 621
 0622            if (data.Length == 0)
 0623            {
 624                // EOF reached. Only validate CRC if we've read exactly the expected number of bytes.
 625                // If _totalBytesRead < _expectedLength the declared size was larger than the actual
 626                // data (e.g. a tampered-but-not-truncated entry);
 627                // We don't throw here because the caller (decompressor) will surface that as an error!
 628                // If _totalBytesRead == _expectedLength we can validate the CRC now, which covers
 629                // zero-length entries and the final EOF read after all expected bytes were consumed.
 0630                if (_totalBytesRead == _expectedLength)
 0631                {
 0632                    ValidateCrc();
 0633                }
 634
 0635                return;
 636            }
 637
 0638            _runningCrc = Crc32Helper.UpdateCrc32(_runningCrc, data);
 0639            _totalBytesRead += data.Length;
 640
 0641            if (_totalBytesRead == _expectedLength)
 0642            {
 0643                ValidateCrc();
 0644            }
 0645            else if (_totalBytesRead > _expectedLength)
 0646            {
 0647                throw new InvalidDataException(SR.UnexpectedStreamLength);
 648            }
 0649        }
 650
 651        private void ValidateCrc()
 0652        {
 0653            if (_crcValidated || _crcAbandoned)
 0654            {
 0655                return;
 656            }
 657
 0658            if (_runningCrc != _expectedCrc)
 0659            {
 0660                throw new InvalidDataException(SR.CrcMismatch);
 661            }
 0662            _crcValidated = true;  // only mark validated on success
 0663        }
 664
 665        /// <summary>
 666        /// Resets CRC tracking state so validation can be recomputed from the beginning of the stream.
 667        /// </summary>
 668        private void ResetCrcState()
 0669        {
 0670            _runningCrc = 0;
 0671            _totalBytesRead = 0;
 0672            _crcAbandoned = false;
 0673            _crcValidated = false;
 0674        }
 675
 676        public override void Write(byte[] buffer, int offset, int count)
 0677        {
 0678            ThrowIfDisposed();
 0679            throw new NotSupportedException(SR.WritingNotSupported);
 680        }
 681
 682        public override void Flush()
 0683        {
 0684            ThrowIfDisposed();
 0685            throw new NotSupportedException(SR.WritingNotSupported);
 686        }
 687
 688        public override Task FlushAsync(CancellationToken cancellationToken)
 0689        {
 0690            ThrowIfDisposed();
 0691            throw new NotSupportedException(SR.WritingNotSupported);
 692        }
 693
 694        public override long Seek(long offset, SeekOrigin origin)
 0695        {
 0696            ThrowIfDisposed();
 0697            ThrowIfCantSeek();
 698
 0699            long newPosition = _baseStream.Seek(offset, origin);
 700
 0701            if (newPosition == 0)
 0702            {
 0703                ResetCrcState();
 0704            }
 705            else
 0706            {
 707                // we should always start from the beginning of the stream
 708                // so any seek that doesn't put us back at the start means we can't reliably validate CRC anymore
 0709                _crcAbandoned = true;
 0710            }
 711
 0712            return newPosition;
 0713        }
 714
 715        public override void SetLength(long value)
 0716        {
 0717            ThrowIfDisposed();
 0718            throw new NotSupportedException(SR.SetLengthRequiresSeekingAndWriting);
 719        }
 720
 721        private void ThrowIfDisposed()
 0722        {
 0723            if (_isDisposed)
 0724            {
 0725                throw new ObjectDisposedException(GetType().ToString(), SR.HiddenStreamName);
 726            }
 0727        }
 728
 729        private void ThrowIfCantSeek()
 0730        {
 0731            if (!CanSeek)
 0732            {
 0733                throw new NotSupportedException(SR.SeekingNotSupported);
 734            }
 0735        }
 736
 737        protected override void Dispose(bool disposing)
 0738        {
 0739            if (disposing && !_isDisposed)
 0740            {
 0741                _baseStream.Dispose();
 0742                _isDisposed = true;
 0743            }
 0744            base.Dispose(disposing);
 0745        }
 746
 747        public override async ValueTask DisposeAsync()
 0748        {
 0749            if (!_isDisposed)
 0750            {
 0751                await _baseStream.DisposeAsync().ConfigureAwait(false);
 0752                _isDisposed = true;
 0753            }
 0754            await base.DisposeAsync().ConfigureAwait(false);
 0755        }
 756    }
 757}