< Summary

Information
Class: System.IO.Compression.WrappedStream
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: 147
Coverable lines: 147
Total lines: 757
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 32
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%
.ctor(...)100%110%
.ctor(...)100%110%
ThrowIfDisposed()0%220%
ThrowIfCantRead()0%220%
ThrowIfCantWrite()0%220%
ThrowIfCantSeek()0%220%
Read(...)100%110%
Read(...)100%110%
ReadByte()100%110%
ReadAsync(...)100%110%
ReadAsync(...)100%110%
Seek(...)100%110%
SetLength(...)100%110%
Write(...)100%110%
Write(...)100%110%
WriteByte(...)100%110%
WriteAsync(...)100%110%
WriteAsync(...)100%110%
NotifyWrite()0%440%
Flush()100%110%
FlushAsync(...)100%110%
Dispose(...)0%880%
DisposeAsync()0%660%

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)
 027            : this(baseStream, closeBaseStream, entry: null, onClosed: null, notifyEntryOnWrite: false) { }
 28
 029        private WrappedStream(Stream baseStream, bool closeBaseStream, ZipArchiveEntry? entry, Action<ZipArchiveEntry?>?
 030        {
 031            _baseStream = baseStream;
 032            _closeBaseStream = closeBaseStream;
 033            _onClosed = onClosed;
 034            _notifyEntryOnWrite = notifyEntryOnWrite;
 035            _zipArchiveEntry = entry;
 036            _isDisposed = false;
 037        }
 38
 39        internal WrappedStream(Stream baseStream, ZipArchiveEntry entry, Action<ZipArchiveEntry?>? onClosed, bool notify
 040            : this(baseStream, false, entry, onClosed, notifyEntryOnWrite) { }
 41
 42        public override long Length
 43        {
 44            get
 045            {
 046                ThrowIfDisposed();
 047                return _baseStream.Length;
 048            }
 49        }
 50
 51        public override long Position
 52        {
 53            get
 054            {
 055                ThrowIfDisposed();
 056                return _baseStream.Position;
 057            }
 58            set
 059            {
 060                ThrowIfDisposed();
 061                ThrowIfCantSeek();
 62
 063                _baseStream.Position = value;
 064            }
 65        }
 66
 067        public override bool CanRead => !_isDisposed && _baseStream.CanRead;
 68
 069        public override bool CanSeek => !_isDisposed && _baseStream.CanSeek;
 70
 071        public override bool CanWrite => !_isDisposed && _baseStream.CanWrite;
 72
 73        private void ThrowIfDisposed()
 074        {
 075            if (_isDisposed)
 076                throw new ObjectDisposedException(GetType().ToString(), SR.HiddenStreamName);
 077        }
 78
 79        private void ThrowIfCantRead()
 080        {
 081            if (!CanRead)
 082                throw new NotSupportedException(SR.ReadingNotSupported);
 083        }
 84
 85        private void ThrowIfCantWrite()
 086        {
 087            if (!CanWrite)
 088                throw new NotSupportedException(SR.WritingNotSupported);
 089        }
 90
 91        private void ThrowIfCantSeek()
 092        {
 093            if (!CanSeek)
 094                throw new NotSupportedException(SR.SeekingNotSupported);
 095        }
 96
 97        public override int Read(byte[] buffer, int offset, int count)
 098        {
 099            ThrowIfDisposed();
 0100            ThrowIfCantRead();
 101
 0102            return _baseStream.Read(buffer, offset, count);
 0103        }
 104
 105        public override int Read(Span<byte> buffer)
 0106        {
 0107            ThrowIfDisposed();
 0108            ThrowIfCantRead();
 109
 0110            return _baseStream.Read(buffer);
 0111        }
 112
 113        public override int ReadByte()
 0114        {
 0115            ThrowIfDisposed();
 0116            ThrowIfCantRead();
 117
 0118            return _baseStream.ReadByte();
 0119        }
 120
 121        public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
 0122        {
 0123            ThrowIfDisposed();
 0124            ThrowIfCantRead();
 125
 0126            return _baseStream.ReadAsync(buffer, offset, count, cancellationToken);
 0127        }
 128
 129        public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
 0130        {
 0131            ThrowIfDisposed();
 0132            ThrowIfCantRead();
 133
 0134            return _baseStream.ReadAsync(buffer, cancellationToken);
 0135        }
 136
 137        public override long Seek(long offset, SeekOrigin origin)
 0138        {
 0139            ThrowIfDisposed();
 0140            ThrowIfCantSeek();
 141
 0142            return _baseStream.Seek(offset, origin);
 0143        }
 144
 145        public override void SetLength(long value)
 0146        {
 0147            ThrowIfDisposed();
 0148            ThrowIfCantSeek();
 0149            ThrowIfCantWrite();
 150
 0151            NotifyWrite();
 0152            _baseStream.SetLength(value);
 0153        }
 154
 155        public override void Write(byte[] buffer, int offset, int count)
 0156        {
 0157            ThrowIfDisposed();
 0158            ThrowIfCantWrite();
 159
 0160            NotifyWrite();
 0161            _baseStream.Write(buffer, offset, count);
 0162        }
 163
 164        public override void Write(ReadOnlySpan<byte> source)
 0165        {
 0166            ThrowIfDisposed();
 0167            ThrowIfCantWrite();
 168
 0169            NotifyWrite();
 0170            _baseStream.Write(source);
 0171        }
 172
 173        public override void WriteByte(byte value)
 0174        {
 0175            ThrowIfDisposed();
 0176            ThrowIfCantWrite();
 177
 0178            NotifyWrite();
 0179            _baseStream.WriteByte(value);
 0180        }
 181
 182        public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
 0183        {
 0184            ThrowIfDisposed();
 0185            ThrowIfCantWrite();
 186
 0187            NotifyWrite();
 0188            return _baseStream.WriteAsync(buffer, offset, count, cancellationToken);
 0189        }
 190
 191        public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
 0192        {
 0193            ThrowIfDisposed();
 0194            ThrowIfCantWrite();
 195
 0196            NotifyWrite();
 0197            return _baseStream.WriteAsync(buffer, cancellationToken);
 0198        }
 199
 200        private void NotifyWrite()
 0201        {
 0202            if (_notifyEntryOnWrite)
 0203            {
 0204                _zipArchiveEntry?.MarkAsModified();
 0205                _notifyEntryOnWrite = false; // Only notify once
 0206            }
 0207        }
 208
 209        public override void Flush()
 0210        {
 0211            ThrowIfDisposed();
 0212            ThrowIfCantWrite();
 213
 0214            _baseStream.Flush();
 0215        }
 216
 217        public override Task FlushAsync(CancellationToken cancellationToken)
 0218        {
 0219            ThrowIfDisposed();
 0220            ThrowIfCantWrite();
 221
 0222            return _baseStream.FlushAsync(cancellationToken);
 0223        }
 224
 225        protected override void Dispose(bool disposing)
 0226        {
 0227            if (disposing && !_isDisposed)
 0228            {
 0229                _onClosed?.Invoke(_zipArchiveEntry);
 230
 0231                if (_closeBaseStream)
 0232                    _baseStream.Dispose();
 233
 0234                _isDisposed = true;
 0235            }
 0236            base.Dispose(disposing);
 0237        }
 238
 239        public override async ValueTask DisposeAsync()
 0240        {
 0241            if (!_isDisposed)
 0242            {
 0243                _onClosed?.Invoke(_zipArchiveEntry);
 244
 0245                if (_closeBaseStream)
 0246                    await _baseStream.DisposeAsync().ConfigureAwait(false);
 247
 0248                _isDisposed = true;
 0249            }
 0250            await base.DisposeAsync().ConfigureAwait(false);
 0251        }
 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
 514        public CrcValidatingReadStream(Stream baseStream, uint expectedCrc, long expectedLength)
 515        {
 516            ArgumentNullException.ThrowIfNull(baseStream);
 517            ArgumentOutOfRangeException.ThrowIfNegative(expectedLength);
 518
 519            _baseStream = baseStream;
 520            _expectedCrc = expectedCrc;
 521            _expectedLength = expectedLength;
 522            _runningCrc = 0;
 523        }
 524
 525        public override bool CanRead => !_isDisposed && _baseStream.CanRead;
 526        public override bool CanSeek => !_isDisposed && _baseStream.CanSeek;
 527        public override bool CanWrite => false;
 528
 529        public override long Length
 530        {
 531            get
 532            {
 533                ThrowIfDisposed();
 534                return _baseStream.Length;
 535            }
 536        }
 537
 538        public override long Position
 539        {
 540            get
 541            {
 542                ThrowIfDisposed();
 543                return _baseStream.Position;
 544            }
 545            set
 546            {
 547                ThrowIfDisposed();
 548                ThrowIfCantSeek();
 549
 550                _baseStream.Position = value;
 551
 552                if (value == 0)
 553                {
 554                    ResetCrcState();
 555                }
 556                else
 557                {
 558                    _crcAbandoned = true;
 559                }
 560            }
 561        }
 562
 563        public override int Read(byte[] buffer, int offset, int count)
 564        {
 565            ThrowIfDisposed();
 566            ValidateBufferArguments(buffer, offset, count);
 567
 568            return Read(new Span<byte>(buffer, offset, count));
 569        }
 570
 571        public override int Read(Span<byte> buffer)
 572        {
 573            ThrowIfDisposed();
 574
 575            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.
 578            if (buffer.Length > 0)
 579            {
 580                ProcessBytesRead(buffer.Slice(0, bytesRead));
 581            }
 582
 583            return bytesRead;
 584        }
 585
 586        public override int ReadByte()
 587        {
 588            byte b = default;
 589            return Read(new Span<byte>(ref b)) == 1 ? b : -1;
 590        }
 591
 592        public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
 593        {
 594            ThrowIfDisposed();
 595            ValidateBufferArguments(buffer, offset, count);
 596
 597            return ReadAsync(buffer.AsMemory(offset, count), cancellationToken).AsTask();
 598        }
 599
 600        public override async ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = defaul
 601        {
 602            ThrowIfDisposed();
 603
 604            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.
 607            if (buffer.Length > 0)
 608            {
 609                ProcessBytesRead(buffer.Span.Slice(0, bytesRead));
 610            }
 611
 612            return bytesRead;
 613        }
 614
 615        private void ProcessBytesRead(ReadOnlySpan<byte> data)
 616        {
 617            if (_crcAbandoned)
 618            {
 619                return;
 620            }
 621
 622            if (data.Length == 0)
 623            {
 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.
 630                if (_totalBytesRead == _expectedLength)
 631                {
 632                    ValidateCrc();
 633                }
 634
 635                return;
 636            }
 637
 638            _runningCrc = Crc32Helper.UpdateCrc32(_runningCrc, data);
 639            _totalBytesRead += data.Length;
 640
 641            if (_totalBytesRead == _expectedLength)
 642            {
 643                ValidateCrc();
 644            }
 645            else if (_totalBytesRead > _expectedLength)
 646            {
 647                throw new InvalidDataException(SR.UnexpectedStreamLength);
 648            }
 649        }
 650
 651        private void ValidateCrc()
 652        {
 653            if (_crcValidated || _crcAbandoned)
 654            {
 655                return;
 656            }
 657
 658            if (_runningCrc != _expectedCrc)
 659            {
 660                throw new InvalidDataException(SR.CrcMismatch);
 661            }
 662            _crcValidated = true;  // only mark validated on success
 663        }
 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()
 669        {
 670            _runningCrc = 0;
 671            _totalBytesRead = 0;
 672            _crcAbandoned = false;
 673            _crcValidated = false;
 674        }
 675
 676        public override void Write(byte[] buffer, int offset, int count)
 677        {
 678            ThrowIfDisposed();
 679            throw new NotSupportedException(SR.WritingNotSupported);
 680        }
 681
 682        public override void Flush()
 683        {
 684            ThrowIfDisposed();
 685            throw new NotSupportedException(SR.WritingNotSupported);
 686        }
 687
 688        public override Task FlushAsync(CancellationToken cancellationToken)
 689        {
 690            ThrowIfDisposed();
 691            throw new NotSupportedException(SR.WritingNotSupported);
 692        }
 693
 694        public override long Seek(long offset, SeekOrigin origin)
 695        {
 696            ThrowIfDisposed();
 697            ThrowIfCantSeek();
 698
 699            long newPosition = _baseStream.Seek(offset, origin);
 700
 701            if (newPosition == 0)
 702            {
 703                ResetCrcState();
 704            }
 705            else
 706            {
 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
 709                _crcAbandoned = true;
 710            }
 711
 712            return newPosition;
 713        }
 714
 715        public override void SetLength(long value)
 716        {
 717            ThrowIfDisposed();
 718            throw new NotSupportedException(SR.SetLengthRequiresSeekingAndWriting);
 719        }
 720
 721        private void ThrowIfDisposed()
 722        {
 723            if (_isDisposed)
 724            {
 725                throw new ObjectDisposedException(GetType().ToString(), SR.HiddenStreamName);
 726            }
 727        }
 728
 729        private void ThrowIfCantSeek()
 730        {
 731            if (!CanSeek)
 732            {
 733                throw new NotSupportedException(SR.SeekingNotSupported);
 734            }
 735        }
 736
 737        protected override void Dispose(bool disposing)
 738        {
 739            if (disposing && !_isDisposed)
 740            {
 741                _baseStream.Dispose();
 742                _isDisposed = true;
 743            }
 744            base.Dispose(disposing);
 745        }
 746
 747        public override async ValueTask DisposeAsync()
 748        {
 749            if (!_isDisposed)
 750            {
 751                await _baseStream.DisposeAsync().ConfigureAwait(false);
 752                _isDisposed = true;
 753            }
 754            await base.DisposeAsync().ConfigureAwait(false);
 755        }
 756    }
 757}