From 87c38d6dab814c7de6d0c01002b5aecc943cfc4e Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Tue, 23 Dec 2025 09:22:38 +0000 Subject: [PATCH] fix ordering and token passing --- src/SharpCompress/Archives/AbstractArchive.cs | 2 +- .../Archives/IArchiveEntryExtensions.cs | 211 +++++++++--------- src/SharpCompress/Common/ExtractionMethods.cs | 9 +- .../Readers/IReaderExtensions.cs | 195 ++++++++-------- .../SharpCompress.Test/ProgressReportTests.cs | 4 +- 5 files changed, 205 insertions(+), 216 deletions(-) diff --git a/src/SharpCompress/Archives/AbstractArchive.cs b/src/SharpCompress/Archives/AbstractArchive.cs index 131fcd3f..e42abfc3 100644 --- a/src/SharpCompress/Archives/AbstractArchive.cs +++ b/src/SharpCompress/Archives/AbstractArchive.cs @@ -107,7 +107,7 @@ public abstract class AbstractArchive : IArchive /// public IReader ExtractAllEntries() { - ((IArchiveExtractionListener)this).EnsureEntriesLoaded(); + EnsureEntriesLoaded(); return CreateReaderForSolidExtraction(); } diff --git a/src/SharpCompress/Archives/IArchiveEntryExtensions.cs b/src/SharpCompress/Archives/IArchiveEntryExtensions.cs index 1ec3bfec..af2c9be4 100644 --- a/src/SharpCompress/Archives/IArchiveEntryExtensions.cs +++ b/src/SharpCompress/Archives/IArchiveEntryExtensions.cs @@ -11,52 +11,49 @@ public static class IArchiveEntryExtensions { private const int BufferSize = 81920; - /// - /// Extract entry to the specified stream. - /// /// The archive entry to extract. - /// The stream to write the entry content to. - /// Optional progress reporter for tracking extraction progress. - public static void WriteTo( - this IArchiveEntry archiveEntry, - Stream streamToWriteTo, - IProgress? progress = null - ) + extension(IArchiveEntry archiveEntry) { - if (archiveEntry.IsDirectory) + /// + /// Extract entry to the specified stream. + /// + /// The stream to write the entry content to. + /// Optional progress reporter for tracking extraction progress. + public void WriteTo(Stream streamToWriteTo, IProgress? progress = null) { - throw new ExtractionException("Entry is a file directory and cannot be extracted."); + if (archiveEntry.IsDirectory) + { + throw new ExtractionException("Entry is a file directory and cannot be extracted."); + } + + using var entryStream = archiveEntry.OpenEntryStream(); + var sourceStream = WrapWithProgress(entryStream, archiveEntry, progress); + sourceStream.CopyTo(streamToWriteTo, BufferSize); } - using var entryStream = archiveEntry.OpenEntryStream(); - var sourceStream = WrapWithProgress(entryStream, archiveEntry, progress); - sourceStream.CopyTo(streamToWriteTo, BufferSize); - } - - /// - /// Extract entry to the specified stream asynchronously. - /// - /// The archive entry to extract. - /// The stream to write the entry content to. - /// Cancellation token. - /// Optional progress reporter for tracking extraction progress. - public static async Task WriteToAsync( - this IArchiveEntry archiveEntry, - Stream streamToWriteTo, - CancellationToken cancellationToken = default, - IProgress? progress = null - ) - { - if (archiveEntry.IsDirectory) + /// + /// Extract entry to the specified stream asynchronously. + /// + /// The stream to write the entry content to. + /// Cancellation token. + /// Optional progress reporter for tracking extraction progress. + public async Task WriteToAsync( + Stream streamToWriteTo, + IProgress? progress = null, + CancellationToken cancellationToken = default + ) { - throw new ExtractionException("Entry is a file directory and cannot be extracted."); - } + if (archiveEntry.IsDirectory) + { + throw new ExtractionException("Entry is a file directory and cannot be extracted."); + } - using var entryStream = archiveEntry.OpenEntryStream(); - var sourceStream = WrapWithProgress(entryStream, archiveEntry, progress); - await sourceStream - .CopyToAsync(streamToWriteTo, BufferSize, cancellationToken) - .ConfigureAwait(false); + using var entryStream = await archiveEntry.OpenEntryStreamAsync(cancellationToken); + var sourceStream = WrapWithProgress(entryStream, archiveEntry, progress); + await sourceStream + .CopyToAsync(streamToWriteTo, BufferSize, cancellationToken) + .ConfigureAwait(false); + } } private static Stream WrapWithProgress( @@ -71,7 +68,7 @@ public static class IArchiveEntryExtensions } var entryPath = entry.Key ?? string.Empty; - long? totalBytes = GetEntrySizeSafe(entry); + var totalBytes = GetEntrySizeSafe(entry); return new ProgressReportingStream( source, progress, @@ -94,77 +91,71 @@ public static class IArchiveEntryExtensions } } - /// - /// Extract to specific directory, retaining filename - /// - public static void WriteToDirectory( - this IArchiveEntry entry, - string destinationDirectory, - ExtractionOptions? options = null - ) => - ExtractionMethods.WriteEntryToDirectory( - entry, - destinationDirectory, - options, - entry.WriteToFile - ); + extension(IArchiveEntry entry) + { + /// + /// Extract to specific directory, retaining filename + /// + public void WriteToDirectory( + string destinationDirectory, + ExtractionOptions? options = null + ) => + ExtractionMethods.WriteEntryToDirectory( + entry, + destinationDirectory, + options, + entry.WriteToFile + ); - /// - /// Extract to specific directory asynchronously, retaining filename - /// - public static Task WriteToDirectoryAsync( - this IArchiveEntry entry, - string destinationDirectory, - ExtractionOptions? options = null, - CancellationToken cancellationToken = default - ) => - ExtractionMethods.WriteEntryToDirectoryAsync( - entry, - destinationDirectory, - options, - (x, opt) => entry.WriteToFileAsync(x, opt, cancellationToken), - cancellationToken - ); + /// + /// Extract to specific directory asynchronously, retaining filename + /// + public Task WriteToDirectoryAsync( + string destinationDirectory, + ExtractionOptions? options = null, + CancellationToken cancellationToken = default + ) => + ExtractionMethods.WriteEntryToDirectoryAsync( + entry, + destinationDirectory, + options, + entry.WriteToFileAsync, + cancellationToken + ); - /// - /// Extract to specific file - /// - public static void WriteToFile( - this IArchiveEntry entry, - string destinationFileName, - ExtractionOptions? options = null - ) => - ExtractionMethods.WriteEntryToFile( - entry, - destinationFileName, - options, - (x, fm) => - { - using var fs = File.Open(destinationFileName, fm); - entry.WriteTo(fs); - } - ); + /// + /// Extract to specific file + /// + public void WriteToFile(string destinationFileName, ExtractionOptions? options = null) => + ExtractionMethods.WriteEntryToFile( + entry, + destinationFileName, + options, + (x, fm) => + { + using var fs = File.Open(destinationFileName, fm); + entry.WriteTo(fs); + } + ); - /// - /// Extract to specific file asynchronously - /// - public static Task WriteToFileAsync( - this IArchiveEntry entry, - string destinationFileName, - ExtractionOptions? options = null, - CancellationToken cancellationToken = default - ) => - ExtractionMethods.WriteEntryToFileAsync( - entry, - destinationFileName, - options, - async (x, fm) => - { - using var fs = File.Open(destinationFileName, fm); - await entry - .WriteToAsync(fs, progress: null, cancellationToken: cancellationToken) - .ConfigureAwait(false); - }, - cancellationToken - ); + /// + /// Extract to specific file asynchronously + /// + public Task WriteToFileAsync( + string destinationFileName, + ExtractionOptions? options = null, + CancellationToken cancellationToken = default + ) => + ExtractionMethods.WriteEntryToFileAsync( + entry, + destinationFileName, + options, + async (x, fm, ct) => + { + using var fs = File.Open(destinationFileName, fm); + await entry.WriteToAsync(fs, null, ct).ConfigureAwait(false); + }, + cancellationToken + ); + } } diff --git a/src/SharpCompress/Common/ExtractionMethods.cs b/src/SharpCompress/Common/ExtractionMethods.cs index 485fdf4d..509524b1 100644 --- a/src/SharpCompress/Common/ExtractionMethods.cs +++ b/src/SharpCompress/Common/ExtractionMethods.cs @@ -128,7 +128,7 @@ internal static class ExtractionMethods IEntry entry, string destinationDirectory, ExtractionOptions? options, - Func writeAsync, + Func writeAsync, CancellationToken cancellationToken = default ) { @@ -189,7 +189,7 @@ internal static class ExtractionMethods "Entry is trying to write a file outside of the destination directory." ); } - await writeAsync(destinationFileName, options).ConfigureAwait(false); + await writeAsync(destinationFileName, options, cancellationToken).ConfigureAwait(false); } else if (options.ExtractFullPath && !Directory.Exists(destinationFileName)) { @@ -201,7 +201,7 @@ internal static class ExtractionMethods IEntry entry, string destinationFileName, ExtractionOptions? options, - Func openAndWriteAsync, + Func openAndWriteAsync, CancellationToken cancellationToken = default ) { @@ -225,7 +225,8 @@ internal static class ExtractionMethods fm = FileMode.CreateNew; } - await openAndWriteAsync(destinationFileName, fm).ConfigureAwait(false); + await openAndWriteAsync(destinationFileName, fm, cancellationToken) + .ConfigureAwait(false); entry.PreserveExtractionOptions(destinationFileName, options); } } diff --git a/src/SharpCompress/Readers/IReaderExtensions.cs b/src/SharpCompress/Readers/IReaderExtensions.cs index 6480df1d..65c6b1fa 100644 --- a/src/SharpCompress/Readers/IReaderExtensions.cs +++ b/src/SharpCompress/Readers/IReaderExtensions.cs @@ -7,124 +7,121 @@ namespace SharpCompress.Readers; public static class IReaderExtensions { - public static void WriteEntryTo(this IReader reader, string filePath) + extension(IReader reader) { - using Stream stream = File.Open(filePath, FileMode.Create, FileAccess.Write); - reader.WriteEntryTo(stream); - } - - public static void WriteEntryTo(this IReader reader, FileInfo filePath) - { - using Stream stream = filePath.Open(FileMode.Create); - reader.WriteEntryTo(stream); - } - - /// - /// Extract all remaining unread entries to specific directory, retaining filename - /// - public static void WriteAllToDirectory( - this IReader reader, - string destinationDirectory, - ExtractionOptions? options = null - ) - { - while (reader.MoveToNextEntry()) + public void WriteEntryTo(string filePath) { - reader.WriteEntryToDirectory(destinationDirectory, options); + using Stream stream = File.Open(filePath, FileMode.Create, FileAccess.Write); + reader.WriteEntryTo(stream); } - } - /// - /// Extract to specific directory, retaining filename - /// - public static void WriteEntryToDirectory( - this IReader reader, - string destinationDirectory, - ExtractionOptions? options = null - ) => - ExtractionMethods.WriteEntryToDirectory( - reader.Entry, - destinationDirectory, - options, - reader.WriteEntryToFile - ); + public void WriteEntryTo(FileInfo filePath) + { + using Stream stream = filePath.Open(FileMode.Create); + reader.WriteEntryTo(stream); + } - /// - /// Extract to specific file - /// - public static void WriteEntryToFile( - this IReader reader, - string destinationFileName, - ExtractionOptions? options = null - ) => - ExtractionMethods.WriteEntryToFile( - reader.Entry, - destinationFileName, - options, - (x, fm) => + /// + /// Extract all remaining unread entries to specific directory, retaining filename + /// + public void WriteAllToDirectory( + string destinationDirectory, + ExtractionOptions? options = null + ) + { + while (reader.MoveToNextEntry()) { - using var fs = File.Open(destinationFileName, fm); - reader.WriteEntryTo(fs); + reader.WriteEntryToDirectory(destinationDirectory, options); } - ); + } - /// - /// Extract to specific directory asynchronously, retaining filename - /// - public static async Task WriteEntryToDirectoryAsync( - this IReader reader, - string destinationDirectory, - ExtractionOptions? options = null, - CancellationToken cancellationToken = default - ) => - await ExtractionMethods - .WriteEntryToDirectoryAsync( + /// + /// Extract to specific directory, retaining filename + /// + public void WriteEntryToDirectory( + string destinationDirectory, + ExtractionOptions? options = null + ) => + ExtractionMethods.WriteEntryToDirectory( reader.Entry, destinationDirectory, options, - (fileName, opts) => reader.WriteEntryToFileAsync(fileName, opts, cancellationToken), - cancellationToken - ) - .ConfigureAwait(false); + reader.WriteEntryToFile + ); - /// - /// Extract to specific file asynchronously - /// - public static async Task WriteEntryToFileAsync( - this IReader reader, - string destinationFileName, - ExtractionOptions? options = null, - CancellationToken cancellationToken = default - ) => - await ExtractionMethods - .WriteEntryToFileAsync( + /// + /// Extract to specific file + /// + public void WriteEntryToFile( + string destinationFileName, + ExtractionOptions? options = null + ) => + ExtractionMethods.WriteEntryToFile( reader.Entry, destinationFileName, options, - async (x, fm) => + (x, fm) => { using var fs = File.Open(destinationFileName, fm); - await reader.WriteEntryToAsync(fs, cancellationToken).ConfigureAwait(false); - }, - cancellationToken - ) - .ConfigureAwait(false); + reader.WriteEntryTo(fs); + } + ); - /// - /// Extract all remaining unread entries to specific directory asynchronously, retaining filename - /// - public static async Task WriteAllToDirectoryAsync( - this IReader reader, - string destinationDirectory, - ExtractionOptions? options = null, - CancellationToken cancellationToken = default - ) - { - while (reader.MoveToNextEntry()) - { - await reader - .WriteEntryToDirectoryAsync(destinationDirectory, options, cancellationToken) + /// + /// Extract to specific directory asynchronously, retaining filename + /// + public async Task WriteEntryToDirectoryAsync( + string destinationDirectory, + ExtractionOptions? options = null, + CancellationToken cancellationToken = default + ) => + await ExtractionMethods + .WriteEntryToDirectoryAsync( + reader.Entry, + destinationDirectory, + options, + reader.WriteEntryToFileAsync, + cancellationToken + ) .ConfigureAwait(false); + + /// + /// Extract to specific file asynchronously + /// + public async Task WriteEntryToFileAsync( + string destinationFileName, + ExtractionOptions? options = null, + CancellationToken cancellationToken = default + ) => + await ExtractionMethods + .WriteEntryToFileAsync( + reader.Entry, + destinationFileName, + options, + async (x, fm, ct) => + { + using var fs = File.Open(destinationFileName, fm); + await reader.WriteEntryToAsync(fs, ct).ConfigureAwait(false); + }, + cancellationToken + ) + .ConfigureAwait(false); + + /// + /// Extract all remaining unread entries to specific directory asynchronously, retaining filename + /// + public async Task WriteAllToDirectoryAsync( + string destinationDirectory, + ExtractionOptions? options = null, + CancellationToken cancellationToken = default + ) + { + while (await reader.MoveToNextEntryAsync(cancellationToken)) + { + await reader + .WriteEntryToDirectoryAsync(destinationDirectory, options, cancellationToken) + .ConfigureAwait(false); + } } } } diff --git a/tests/SharpCompress.Test/ProgressReportTests.cs b/tests/SharpCompress.Test/ProgressReportTests.cs index 58ea4304..75fa2116 100644 --- a/tests/SharpCompress.Test/ProgressReportTests.cs +++ b/tests/SharpCompress.Test/ProgressReportTests.cs @@ -188,7 +188,7 @@ public class ProgressReportTests : TestBase if (!entry.IsDirectory) { using var extractedStream = new MemoryStream(); - await entry.WriteToAsync(extractedStream, CancellationToken.None, progress); + await entry.WriteToAsync(extractedStream, progress, CancellationToken.None); } } @@ -410,7 +410,7 @@ public class ProgressReportTests : TestBase if (!entry.IsDirectory) { using var extractedStream = new MemoryStream(); - await entry.WriteToAsync(extractedStream, CancellationToken.None, progress); + await entry.WriteToAsync(extractedStream, progress, CancellationToken.None); } }