using System; using System.Buffers; using System.IO; using System.Threading; using System.Threading.Tasks; namespace TwitchDownloaderCore.Extensions { public record struct StreamCopyProgress(long SourceLength, long BytesCopied); public static class StreamExtensions { // The default size from Stream.GetCopyBufferSize() is 81,920. private const int STREAM_DEFAULT_BUFFER_LENGTH = 81_920; public static async Task ProgressCopyToAsync(this Stream source, Stream destination, long? sourceLength, IProgress progress = null, CancellationToken cancellationToken = default) { if (!sourceLength.HasValue || progress is null) { await source.CopyToAsync(destination, cancellationToken); return; } var rentedBuffer = ArrayPool.Shared.Rent(STREAM_DEFAULT_BUFFER_LENGTH); long totalBytesRead = 0; try { int bytesRead; while ((bytesRead = await source.ReadAsync(rentedBuffer, 0, rentedBuffer.Length, cancellationToken).ConfigureAwait(false)) != 0) { await destination.WriteAsync(rentedBuffer, 0, bytesRead, cancellationToken).ConfigureAwait(false); totalBytesRead += bytesRead; progress.Report(new StreamCopyProgress(sourceLength.Value, totalBytesRead)); } } finally { ArrayPool.Shared.Return(rentedBuffer, true); } } } }