using System; using System.IO; using System.Threading; using System.Threading.Tasks; using SharpCompress.Common; namespace SharpCompress.IO; /// /// A stream wrapper that reports progress as data is read from the source. /// Used to track compression progress by wrapping the source stream. /// internal sealed class ProgressReportingStream : Stream { private readonly Stream _baseStream; private readonly IProgress _progress; private readonly string _entryPath; private readonly long? _totalBytes; private long _bytesRead; private readonly bool _leaveOpen; public ProgressReportingStream( Stream baseStream, IProgress progress, string entryPath, long? totalBytes, bool leaveOpen = false ) { _baseStream = baseStream; _progress = progress; _entryPath = entryPath; _totalBytes = totalBytes; _leaveOpen = leaveOpen; } public override bool CanRead => _baseStream.CanRead; public override bool CanSeek => _baseStream.CanSeek; public override bool CanWrite => _baseStream.CanWrite; public override long Length => _baseStream.Length; public override long Position { get => _baseStream.Position; set => _baseStream.Position = value; } public override void Flush() => _baseStream.Flush(); public override int Read(byte[] buffer, int offset, int count) { var bytesRead = _baseStream.Read(buffer, offset, count); if (bytesRead > 0) { _bytesRead += bytesRead; ReportProgress(); } return bytesRead; } public override int Read(Span buffer) { var bytesRead = _baseStream.Read(buffer); if (bytesRead > 0) { _bytesRead += bytesRead; ReportProgress(); } return bytesRead; } public override async Task ReadAsync( byte[] buffer, int offset, int count, CancellationToken cancellationToken ) { var bytesRead = await _baseStream .ReadAsync(buffer, offset, count, cancellationToken) .ConfigureAwait(false); if (bytesRead > 0) { _bytesRead += bytesRead; ReportProgress(); } return bytesRead; } public override async ValueTask ReadAsync( Memory buffer, CancellationToken cancellationToken = default ) { var bytesRead = await _baseStream .ReadAsync(buffer, cancellationToken) .ConfigureAwait(false); if (bytesRead > 0) { _bytesRead += bytesRead; ReportProgress(); } return bytesRead; } public override int ReadByte() { var value = _baseStream.ReadByte(); if (value != -1) { _bytesRead++; ReportProgress(); } return value; } public override long Seek(long offset, SeekOrigin origin) => _baseStream.Seek(offset, origin); public override void SetLength(long value) => _baseStream.SetLength(value); public override void Write(byte[] buffer, int offset, int count) => _baseStream.Write(buffer, offset, count); private void ReportProgress() { _progress.Report(new CompressionProgress(_entryPath, _bytesRead, _totalBytes)); } protected override void Dispose(bool disposing) { if (disposing && !_leaveOpen) { _baseStream.Dispose(); } base.Dispose(disposing); } public override async ValueTask DisposeAsync() { if (!_leaveOpen) { await _baseStream.DisposeAsync().ConfigureAwait(false); } await base.DisposeAsync().ConfigureAwait(false); } }