fix ordering and token passing

This commit is contained in:
Adam Hathcock
2025-12-23 09:22:38 +00:00
parent 0e9a4b0511
commit 87c38d6dab
5 changed files with 205 additions and 216 deletions

View File

@@ -107,7 +107,7 @@ public abstract class AbstractArchive<TEntry, TVolume> : IArchive
/// <returns></returns>
public IReader ExtractAllEntries()
{
((IArchiveExtractionListener)this).EnsureEntriesLoaded();
EnsureEntriesLoaded();
return CreateReaderForSolidExtraction();
}

View File

@@ -11,52 +11,49 @@ public static class IArchiveEntryExtensions
{
private const int BufferSize = 81920;
/// <summary>
/// Extract entry to the specified stream.
/// </summary>
/// <param name="archiveEntry">The archive entry to extract.</param>
/// <param name="streamToWriteTo">The stream to write the entry content to.</param>
/// <param name="progress">Optional progress reporter for tracking extraction progress.</param>
public static void WriteTo(
this IArchiveEntry archiveEntry,
Stream streamToWriteTo,
IProgress<ProgressReport>? progress = null
)
extension(IArchiveEntry archiveEntry)
{
if (archiveEntry.IsDirectory)
/// <summary>
/// Extract entry to the specified stream.
/// </summary>
/// <param name="streamToWriteTo">The stream to write the entry content to.</param>
/// <param name="progress">Optional progress reporter for tracking extraction progress.</param>
public void WriteTo(Stream streamToWriteTo, IProgress<ProgressReport>? 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);
}
/// <summary>
/// Extract entry to the specified stream asynchronously.
/// </summary>
/// <param name="archiveEntry">The archive entry to extract.</param>
/// <param name="streamToWriteTo">The stream to write the entry content to.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <param name="progress">Optional progress reporter for tracking extraction progress.</param>
public static async Task WriteToAsync(
this IArchiveEntry archiveEntry,
Stream streamToWriteTo,
CancellationToken cancellationToken = default,
IProgress<ProgressReport>? progress = null
)
{
if (archiveEntry.IsDirectory)
/// <summary>
/// Extract entry to the specified stream asynchronously.
/// </summary>
/// <param name="streamToWriteTo">The stream to write the entry content to.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <param name="progress">Optional progress reporter for tracking extraction progress.</param>
public async Task WriteToAsync(
Stream streamToWriteTo,
IProgress<ProgressReport>? 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
}
}
/// <summary>
/// Extract to specific directory, retaining filename
/// </summary>
public static void WriteToDirectory(
this IArchiveEntry entry,
string destinationDirectory,
ExtractionOptions? options = null
) =>
ExtractionMethods.WriteEntryToDirectory(
entry,
destinationDirectory,
options,
entry.WriteToFile
);
extension(IArchiveEntry entry)
{
/// <summary>
/// Extract to specific directory, retaining filename
/// </summary>
public void WriteToDirectory(
string destinationDirectory,
ExtractionOptions? options = null
) =>
ExtractionMethods.WriteEntryToDirectory(
entry,
destinationDirectory,
options,
entry.WriteToFile
);
/// <summary>
/// Extract to specific directory asynchronously, retaining filename
/// </summary>
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
);
/// <summary>
/// Extract to specific directory asynchronously, retaining filename
/// </summary>
public Task WriteToDirectoryAsync(
string destinationDirectory,
ExtractionOptions? options = null,
CancellationToken cancellationToken = default
) =>
ExtractionMethods.WriteEntryToDirectoryAsync(
entry,
destinationDirectory,
options,
entry.WriteToFileAsync,
cancellationToken
);
/// <summary>
/// Extract to specific file
/// </summary>
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);
}
);
/// <summary>
/// Extract to specific file
/// </summary>
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);
}
);
/// <summary>
/// Extract to specific file asynchronously
/// </summary>
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
);
/// <summary>
/// Extract to specific file asynchronously
/// </summary>
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
);
}
}

View File

@@ -128,7 +128,7 @@ internal static class ExtractionMethods
IEntry entry,
string destinationDirectory,
ExtractionOptions? options,
Func<string, ExtractionOptions?, Task> writeAsync,
Func<string, ExtractionOptions?, CancellationToken, Task> 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<string, FileMode, Task> openAndWriteAsync,
Func<string, FileMode, CancellationToken, Task> 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);
}
}

View File

@@ -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);
}
/// <summary>
/// Extract all remaining unread entries to specific directory, retaining filename
/// </summary>
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);
}
}
/// <summary>
/// Extract to specific directory, retaining filename
/// </summary>
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);
}
/// <summary>
/// Extract to specific file
/// </summary>
public static void WriteEntryToFile(
this IReader reader,
string destinationFileName,
ExtractionOptions? options = null
) =>
ExtractionMethods.WriteEntryToFile(
reader.Entry,
destinationFileName,
options,
(x, fm) =>
/// <summary>
/// Extract all remaining unread entries to specific directory, retaining filename
/// </summary>
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);
}
);
}
/// <summary>
/// Extract to specific directory asynchronously, retaining filename
/// </summary>
public static async Task WriteEntryToDirectoryAsync(
this IReader reader,
string destinationDirectory,
ExtractionOptions? options = null,
CancellationToken cancellationToken = default
) =>
await ExtractionMethods
.WriteEntryToDirectoryAsync(
/// <summary>
/// Extract to specific directory, retaining filename
/// </summary>
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
);
/// <summary>
/// Extract to specific file asynchronously
/// </summary>
public static async Task WriteEntryToFileAsync(
this IReader reader,
string destinationFileName,
ExtractionOptions? options = null,
CancellationToken cancellationToken = default
) =>
await ExtractionMethods
.WriteEntryToFileAsync(
/// <summary>
/// Extract to specific file
/// </summary>
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);
}
);
/// <summary>
/// Extract all remaining unread entries to specific directory asynchronously, retaining filename
/// </summary>
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)
/// <summary>
/// Extract to specific directory asynchronously, retaining filename
/// </summary>
public async Task WriteEntryToDirectoryAsync(
string destinationDirectory,
ExtractionOptions? options = null,
CancellationToken cancellationToken = default
) =>
await ExtractionMethods
.WriteEntryToDirectoryAsync(
reader.Entry,
destinationDirectory,
options,
reader.WriteEntryToFileAsync,
cancellationToken
)
.ConfigureAwait(false);
/// <summary>
/// Extract to specific file asynchronously
/// </summary>
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);
/// <summary>
/// Extract all remaining unread entries to specific directory asynchronously, retaining filename
/// </summary>
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);
}
}
}
}

View File

@@ -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);
}
}