diff --git a/src/SharpCompress/Archives/ArchiveFactory.Detection.Async.cs b/src/SharpCompress/Archives/ArchiveFactory.Detection.Async.cs
new file mode 100644
index 00000000..04cc7886
--- /dev/null
+++ b/src/SharpCompress/Archives/ArchiveFactory.Detection.Async.cs
@@ -0,0 +1,369 @@
+using System.IO;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using SharpCompress.Archives.Tar;
+using SharpCompress.Common;
+using SharpCompress.Detection;
+using SharpCompress.Factories;
+using SharpCompress.IO;
+using SharpCompress.Providers;
+using SharpCompress.Readers;
+
+namespace SharpCompress.Archives;
+
+public static partial class ArchiveFactory
+{
+ ///
+ /// Identifies the archive at the given file path without enumerating its entries.
+ ///
+ /// Path to the archive file.
+ /// Cancellation token.
+ [Zomp.SyncMethodGenerator.CreateSyncVersion]
+ public static async ValueTask DetectArchiveAsync(
+ string filePath,
+ CancellationToken cancellationToken = default
+ ) =>
+ await DetectArchiveAsync(filePath, ReaderOptions.ForFilePath, cancellationToken)
+ .ConfigureAwait(false);
+
+ ///
+ /// Identifies the archive at the given file path without enumerating its entries.
+ ///
+ /// Path to the archive file.
+ /// Options controlling archive detection.
+ /// Cancellation token.
+ [Zomp.SyncMethodGenerator.CreateSyncVersion]
+ public static async ValueTask DetectArchiveAsync(
+ string filePath,
+ ReaderOptions? readerOptions,
+ CancellationToken cancellationToken = default
+ )
+ {
+ filePath.NotNullOrEmpty(nameof(filePath));
+ using Stream stream = File.OpenRead(filePath);
+ return await DetectArchiveAsync(
+ stream,
+ readerOptions ?? ReaderOptions.ForFilePath,
+ cancellationToken
+ )
+ .ConfigureAwait(false);
+ }
+
+ ///
+ /// Identifies the archive in the given stream without enumerating its entries.
+ ///
+ /// A readable and seekable stream positioned at the start of the archive.
+ /// Cancellation token.
+ [Zomp.SyncMethodGenerator.CreateSyncVersion]
+ public static async ValueTask DetectArchiveAsync(
+ Stream stream,
+ CancellationToken cancellationToken = default
+ ) =>
+ await DetectArchiveAsync(stream, ReaderOptions.ForExternalStream, cancellationToken)
+ .ConfigureAwait(false);
+
+ ///
+ /// Identifies the archive in the given stream without enumerating its entries.
+ ///
+ /// A readable and seekable stream positioned at the start of the archive.
+ /// Options controlling archive detection.
+ /// Cancellation token.
+ [Zomp.SyncMethodGenerator.CreateSyncVersion]
+ public static async ValueTask DetectArchiveAsync(
+ Stream stream,
+ ReaderOptions? readerOptions,
+ CancellationToken cancellationToken = default
+ )
+ {
+ stream.RequireReadable();
+ stream.RequireSeekable();
+
+ return await TryDetectArchiveAsync(
+ stream,
+ readerOptions ?? ReaderOptions.ForExternalStream,
+ cancellationToken
+ )
+ .ConfigureAwait(false);
+ }
+
+ internal static ValueTask FindFactoryAsync(
+ string filePath,
+ CancellationToken cancellationToken = default
+ )
+ where T : IFactory
+ {
+ filePath.NotNullOrEmpty(nameof(filePath));
+ return FindFactoryAsync(new FileInfo(filePath), cancellationToken);
+ }
+
+ internal static async ValueTask FindFactoryAsync(
+ FileInfo fileInfo,
+ CancellationToken cancellationToken = default
+ )
+ where T : IFactory
+ {
+ fileInfo.NotNull(nameof(fileInfo));
+ using Stream stream = fileInfo.OpenRead();
+ return await FindFactoryAsync(stream, cancellationToken).ConfigureAwait(false);
+ }
+
+ [Zomp.SyncMethodGenerator.CreateSyncVersion]
+ private static async ValueTask FindFactoryAsync(
+ FileInfo fileInfo,
+ ReaderOptions readerOptions,
+ CancellationToken cancellationToken
+ )
+ where T : IFactory
+ {
+ fileInfo.NotNull(nameof(fileInfo));
+ using Stream stream = fileInfo.OpenRead();
+ return await FindFactoryAsync(stream, readerOptions, cancellationToken)
+ .ConfigureAwait(false);
+ }
+
+ internal static async ValueTask FindFactoryAsync(
+ Stream stream,
+ CancellationToken cancellationToken = default
+ )
+ where T : IFactory
+ {
+ stream.RequireReadable();
+ stream.RequireSeekable();
+
+ // Use the shared async detection loop over all factories. If the matched factory
+ // implements T we return it; otherwise (or if nothing matched) we fall through
+ // to the same "unsupported format" exception that the original code produced,
+ // listing the T-typed factories as the hint for the caller.
+ return await FindFactoryAsync(stream, ReaderOptions.ForExternalStream, cancellationToken)
+ .ConfigureAwait(false);
+ }
+
+ [Zomp.SyncMethodGenerator.CreateSyncVersion]
+ private static async ValueTask FindFactoryAsync(
+ Stream stream,
+ ReaderOptions readerOptions,
+ CancellationToken cancellationToken
+ )
+ where T : IFactory
+ {
+ stream.RequireReadable();
+ stream.RequireSeekable();
+
+ var factory = await TryFindFactoryAsync(stream, readerOptions, cancellationToken)
+ .ConfigureAwait(false);
+ if (factory is T typedFactory)
+ {
+ return typedFactory;
+ }
+
+ var extensions = string.Join(", ", Factory.Factories.OfType().Select(item => item.Name));
+
+ throw new ArchiveOperationException(
+ $"Cannot determine compressed stream type. Supported Archive Formats: {extensions}"
+ );
+ }
+
+ ///
+ /// Iterates all registered factories and returns the first one whose
+ /// recognises the stream, or .
+ /// Stream position is restored to its value at entry on both success and failure.
+ ///
+ [Zomp.SyncMethodGenerator.CreateSyncVersion]
+ private static async ValueTask TryFindFactoryAsync(
+ Stream stream,
+ ReaderOptions readerOptions,
+ CancellationToken cancellationToken
+ )
+ {
+ var startPosition = stream.Position;
+
+ try
+ {
+ foreach (var factory in Factory.Factories)
+ {
+ stream.Seek(startPosition, SeekOrigin.Begin);
+ var isArchive = await factory
+ .IsArchiveAsync(stream, readerOptions, cancellationToken)
+ .ConfigureAwait(false);
+
+ if (isArchive)
+ {
+ stream.Seek(startPosition, SeekOrigin.Begin);
+ if (
+ await IsCompressedTarAsync(
+ stream,
+ factory,
+ readerOptions,
+ cancellationToken
+ )
+ .ConfigureAwait(false)
+ )
+ {
+ continue;
+ }
+
+ return factory;
+ }
+ }
+
+ return null;
+ }
+ finally
+ {
+ stream.Seek(startPosition, SeekOrigin.Begin);
+ }
+ }
+
+ [Zomp.SyncMethodGenerator.CreateSyncVersion]
+ private static async ValueTask IsCompressedTarAsync(
+ Stream stream,
+ IFactory factory,
+ ReaderOptions readerOptions,
+ CancellationToken cancellationToken
+ ) =>
+ GetCompressedTarType(factory) is { } compressionType
+ && await IsCompressedTarAsync(stream, readerOptions, compressionType, cancellationToken)
+ .ConfigureAwait(false);
+
+ [Zomp.SyncMethodGenerator.CreateSyncVersion]
+ private static async ValueTask IsCompressedTarAsync(
+ Stream stream,
+ ReaderOptions readerOptions,
+ CompressionType compressionType,
+ CancellationToken cancellationToken
+ )
+ {
+ using var nonDisposingStream = SharpCompressStream.CreateNonDisposing(stream);
+ var testStream =
+ compressionType == CompressionType.GZip
+ ? await readerOptions
+ .Providers.CreateDecompressStreamAsync(
+ compressionType,
+ nonDisposingStream,
+ CompressionContext
+ .FromStream(nonDisposingStream)
+ .WithReaderOptions(readerOptions),
+ cancellationToken
+ )
+ .ConfigureAwait(false)
+ : await readerOptions
+ .Providers.CreateDecompressStreamAsync(
+ compressionType,
+ nonDisposingStream,
+ cancellationToken
+ )
+ .ConfigureAwait(false);
+
+ try
+ {
+ return await TarArchive
+ .IsTarFileAsync(testStream, cancellationToken)
+ .ConfigureAwait(false);
+ }
+ finally
+ {
+ DisposeProbeStream(testStream);
+ }
+ }
+
+ [Zomp.SyncMethodGenerator.CreateSyncVersion]
+ private static async ValueTask TryDetectArchiveAsync(
+ Stream stream,
+ ReaderOptions readerOptions,
+ CancellationToken cancellationToken
+ )
+ {
+ var startPosition = stream.Position;
+
+ try
+ {
+ foreach (var factory in Factory.Factories)
+ {
+ stream.Seek(startPosition, SeekOrigin.Begin);
+ if (
+ !await factory
+ .IsArchiveAsync(stream, readerOptions, cancellationToken)
+ .ConfigureAwait(false)
+ )
+ {
+ continue;
+ }
+
+ if (GetCompressedTarType(factory) is { } compressionType)
+ {
+ stream.Seek(startPosition, SeekOrigin.Begin);
+ if (
+ await IsCompressedTarAsync(
+ stream,
+ readerOptions,
+ compressionType,
+ cancellationToken
+ )
+ .ConfigureAwait(false)
+ )
+ {
+ return CreateCompressedTarDetection(compressionType);
+ }
+ }
+
+ return CreateDetection(factory);
+ }
+
+ var compressedTarType = await TryDetectCompressedTarAsync(
+ stream,
+ readerOptions,
+ startPosition,
+ cancellationToken
+ )
+ .ConfigureAwait(false);
+ return compressedTarType is { } value ? CreateCompressedTarDetection(value) : null;
+ }
+ finally
+ {
+ stream.Seek(startPosition, SeekOrigin.Begin);
+ }
+ }
+
+ [Zomp.SyncMethodGenerator.CreateSyncVersion]
+ private static async ValueTask TryDetectCompressedTarAsync(
+ Stream stream,
+ ReaderOptions readerOptions,
+ long startPosition,
+ CancellationToken cancellationToken
+ )
+ {
+ foreach (var wrapper in TarWrapper.Wrappers)
+ {
+#if !SYNC_ONLY
+ cancellationToken.ThrowIfCancellationRequested();
+#endif
+ if (wrapper.CompressionType == CompressionType.None)
+ {
+ continue;
+ }
+
+ stream.Seek(startPosition, SeekOrigin.Begin);
+ if (!await wrapper.IsMatchAsync(stream, cancellationToken).ConfigureAwait(false))
+ {
+ continue;
+ }
+
+ stream.Seek(startPosition, SeekOrigin.Begin);
+ if (
+ await IsCompressedTarAsync(
+ stream,
+ readerOptions,
+ wrapper.CompressionType,
+ cancellationToken
+ )
+ .ConfigureAwait(false)
+ )
+ {
+ return wrapper.CompressionType;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/src/SharpCompress/Archives/ArchiveFactory.Detection.cs b/src/SharpCompress/Archives/ArchiveFactory.Detection.cs
index 88168d92..85721dbf 100644
--- a/src/SharpCompress/Archives/ArchiveFactory.Detection.cs
+++ b/src/SharpCompress/Archives/ArchiveFactory.Detection.cs
@@ -1,391 +1,13 @@
using System.IO;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using SharpCompress.Archives.Tar;
using SharpCompress.Common;
using SharpCompress.Detection;
using SharpCompress.Factories;
-using SharpCompress.IO;
-using SharpCompress.Providers;
using SharpCompress.Readers;
namespace SharpCompress.Archives;
public static partial class ArchiveFactory
{
- ///
- /// Identifies the archive at the given file path without enumerating its entries.
- ///
- /// Path to the archive file.
- /// Cancellation token.
- public static async ValueTask DetectArchiveAsync(
- string filePath,
- CancellationToken cancellationToken = default
- ) =>
- await DetectArchiveAsync(filePath, ReaderOptions.ForFilePath, cancellationToken)
- .ConfigureAwait(false);
-
- ///
- /// Identifies the archive at the given file path without enumerating its entries.
- ///
- /// Path to the archive file.
- /// Options controlling archive detection.
- /// Cancellation token.
- public static async ValueTask DetectArchiveAsync(
- string filePath,
- ReaderOptions? readerOptions,
- CancellationToken cancellationToken = default
- )
- {
- filePath.NotNullOrEmpty(nameof(filePath));
- using Stream stream = File.OpenRead(filePath);
- return await DetectArchiveAsync(
- stream,
- readerOptions ?? ReaderOptions.ForFilePath,
- cancellationToken
- )
- .ConfigureAwait(false);
- }
-
- ///
- /// Identifies the archive in the given stream without enumerating its entries.
- ///
- /// A readable and seekable stream positioned at the start of the archive.
- /// Cancellation token.
- public static async ValueTask DetectArchiveAsync(
- Stream stream,
- CancellationToken cancellationToken = default
- ) =>
- await DetectArchiveAsync(stream, ReaderOptions.ForExternalStream, cancellationToken)
- .ConfigureAwait(false);
-
- ///
- /// Identifies the archive in the given stream without enumerating its entries.
- ///
- /// A readable and seekable stream positioned at the start of the archive.
- /// Options controlling archive detection.
- /// Cancellation token.
- public static async ValueTask DetectArchiveAsync(
- Stream stream,
- ReaderOptions? readerOptions,
- CancellationToken cancellationToken = default
- )
- {
- stream.RequireReadable();
- stream.RequireSeekable();
-
- return await TryDetectArchiveAsync(
- stream,
- readerOptions ?? ReaderOptions.ForExternalStream,
- cancellationToken
- )
- .ConfigureAwait(false);
- }
-
- internal static ValueTask FindFactoryAsync(
- string filePath,
- CancellationToken cancellationToken = default
- )
- where T : IFactory
- {
- filePath.NotNullOrEmpty(nameof(filePath));
- return FindFactoryAsync(new FileInfo(filePath), cancellationToken);
- }
-
- internal static async ValueTask FindFactoryAsync(
- FileInfo fileInfo,
- CancellationToken cancellationToken = default
- )
- where T : IFactory
- {
- fileInfo.NotNull(nameof(fileInfo));
- using Stream stream = fileInfo.OpenRead();
- return await FindFactoryAsync(stream, cancellationToken).ConfigureAwait(false);
- }
-
- private static async ValueTask FindFactoryAsync(
- FileInfo fileInfo,
- ReaderOptions readerOptions,
- CancellationToken cancellationToken
- )
- where T : IFactory
- {
- fileInfo.NotNull(nameof(fileInfo));
- using Stream stream = fileInfo.OpenRead();
- return await FindFactoryAsync(stream, readerOptions, cancellationToken)
- .ConfigureAwait(false);
- }
-
- internal static async ValueTask FindFactoryAsync(
- Stream stream,
- CancellationToken cancellationToken = default
- )
- where T : IFactory
- {
- stream.RequireReadable();
- stream.RequireSeekable();
-
- // Use the shared async detection loop over all factories. If the matched factory
- // implements T we return it; otherwise (or if nothing matched) we fall through
- // to the same "unsupported format" exception that the original code produced,
- // listing the T-typed factories as the hint for the caller.
- return await FindFactoryAsync(stream, ReaderOptions.ForExternalStream, cancellationToken)
- .ConfigureAwait(false);
- }
-
- private static async ValueTask FindFactoryAsync(
- Stream stream,
- ReaderOptions readerOptions,
- CancellationToken cancellationToken
- )
- where T : IFactory
- {
- stream.RequireReadable();
- stream.RequireSeekable();
-
- var factory = await TryFindFactoryAsync(stream, readerOptions, cancellationToken)
- .ConfigureAwait(false);
- if (factory is T typedFactory)
- {
- return typedFactory;
- }
-
- var extensions = string.Join(", ", Factory.Factories.OfType().Select(item => item.Name));
-
- throw new ArchiveOperationException(
- $"Cannot determine compressed stream type. Supported Archive Formats: {extensions}"
- );
- }
-
- ///
- /// Async counterpart of the synchronous factory detection path.
- /// Iterates all registered factories and returns the first one whose
- /// recognises the stream, or .
- /// Stream position is restored to its value at entry on both success and failure.
- ///
- private static async ValueTask TryFindFactoryAsync(
- Stream stream,
- ReaderOptions readerOptions,
- CancellationToken cancellationToken
- )
- {
- var startPosition = stream.Position;
-
- try
- {
- foreach (var factory in Factory.Factories)
- {
- stream.Seek(startPosition, SeekOrigin.Begin);
- var isArchive = await factory
- .IsArchiveAsync(stream, readerOptions, cancellationToken)
- .ConfigureAwait(false);
-
- if (isArchive)
- {
- stream.Seek(startPosition, SeekOrigin.Begin);
- if (
- await IsCompressedTarAsync(
- stream,
- factory,
- readerOptions,
- cancellationToken
- )
- .ConfigureAwait(false)
- )
- {
- continue;
- }
-
- return factory;
- }
- }
-
- return null;
- }
- finally
- {
- stream.Seek(startPosition, SeekOrigin.Begin);
- }
- }
-
- ///
- /// Identifies the archive at the given file path without enumerating its entries.
- ///
- /// Path to the archive file.
- public static ArchiveDetection? DetectArchive(string filePath) =>
- DetectArchive(filePath, ReaderOptions.ForFilePath);
-
- ///
- /// Identifies the archive at the given file path without enumerating its entries.
- ///
- /// Path to the archive file.
- /// Options controlling archive detection.
- public static ArchiveDetection? DetectArchive(string filePath, ReaderOptions? readerOptions)
- {
- filePath.NotNullOrEmpty(nameof(filePath));
- using Stream stream = File.OpenRead(filePath);
- return DetectArchive(stream, readerOptions ?? ReaderOptions.ForFilePath);
- }
-
- ///
- /// Identifies the archive in the given stream without enumerating its entries.
- ///
- /// A readable and seekable stream positioned at the start of the archive.
- public static ArchiveDetection? DetectArchive(Stream stream) =>
- DetectArchive(stream, ReaderOptions.ForExternalStream);
-
- ///
- /// Identifies the archive in the given stream without enumerating its entries.
- ///
- /// A readable and seekable stream positioned at the start of the archive.
- /// Options controlling archive detection.
- public static ArchiveDetection? DetectArchive(Stream stream, ReaderOptions? readerOptions)
- {
- stream.RequireReadable();
- stream.RequireSeekable();
-
- return TryDetectArchive(stream, readerOptions ?? ReaderOptions.ForExternalStream);
- }
-
- ///
- /// Iterates all registered factories and returns the first one whose
- /// recognises the stream, or .
- /// Stream position is restored to its value at entry on both success and failure.
- ///
- ///
- /// This is the shared, seekable-stream detection core used by
- /// , ,
- /// and .
- ///
- /// uses a separate code path
- /// based on rewindable buffering, which supports
- /// non-seekable streams and is therefore not unified with this helper.
- ///
- ///
- private static IFactory? TryFindFactory(Stream stream) =>
- TryFindFactory(stream, ReaderOptions.ForExternalStream);
-
- private static IFactory? TryFindFactory(Stream stream, ReaderOptions readerOptions)
- {
- var startPosition = stream.Position;
-
- try
- {
- foreach (var factory in Factory.Factories)
- {
- stream.Seek(startPosition, SeekOrigin.Begin);
- var isArchive = factory.IsArchive(stream, readerOptions);
-
- if (isArchive)
- {
- stream.Seek(startPosition, SeekOrigin.Begin);
- if (IsCompressedTar(stream, factory, readerOptions))
- {
- continue;
- }
-
- return factory;
- }
- }
-
- return null;
- }
- finally
- {
- stream.Seek(startPosition, SeekOrigin.Begin);
- }
- }
-
- private static bool IsCompressedTar(
- Stream stream,
- IFactory factory,
- ReaderOptions readerOptions
- ) =>
- GetCompressedTarType(factory) is { } compressionType
- && IsCompressedTar(stream, readerOptions, compressionType);
-
- private static bool IsCompressedTar(
- Stream stream,
- ReaderOptions readerOptions,
- CompressionType compressionType
- )
- {
- using var nonDisposingStream = SharpCompressStream.CreateNonDisposing(stream);
- var testStream =
- compressionType == CompressionType.GZip
- ? readerOptions.Providers.CreateDecompressStream(
- compressionType,
- nonDisposingStream,
- CompressionContext
- .FromStream(nonDisposingStream)
- .WithReaderOptions(readerOptions)
- )
- : readerOptions.Providers.CreateDecompressStream(
- compressionType,
- nonDisposingStream
- );
-
- try
- {
- return TarArchive.IsTarFile(testStream);
- }
- finally
- {
- DisposeProbeStream(testStream);
- }
- }
-
- private static async ValueTask IsCompressedTarAsync(
- Stream stream,
- IFactory factory,
- ReaderOptions readerOptions,
- CancellationToken cancellationToken
- ) =>
- GetCompressedTarType(factory) is { } compressionType
- && await IsCompressedTarAsync(stream, readerOptions, compressionType, cancellationToken)
- .ConfigureAwait(false);
-
- private static async ValueTask IsCompressedTarAsync(
- Stream stream,
- ReaderOptions readerOptions,
- CompressionType compressionType,
- CancellationToken cancellationToken
- )
- {
- using var nonDisposingStream = SharpCompressStream.CreateNonDisposing(stream);
- var testStream =
- compressionType == CompressionType.GZip
- ? await readerOptions
- .Providers.CreateDecompressStreamAsync(
- compressionType,
- nonDisposingStream,
- CompressionContext
- .FromStream(nonDisposingStream)
- .WithReaderOptions(readerOptions),
- cancellationToken
- )
- .ConfigureAwait(false)
- : await readerOptions
- .Providers.CreateDecompressStreamAsync(
- compressionType,
- nonDisposingStream,
- cancellationToken
- )
- .ConfigureAwait(false);
-
- try
- {
- return await TarArchive
- .IsTarFileAsync(testStream, cancellationToken)
- .ConfigureAwait(false);
- }
- finally
- {
- DisposeProbeStream(testStream);
- }
- }
-
private static void DisposeProbeStream(Stream stream)
{
try
@@ -408,169 +30,6 @@ public static partial class ArchiveFactory
_ => null,
};
- private static ArchiveDetection? TryDetectArchive(Stream stream, ReaderOptions readerOptions)
- {
- var startPosition = stream.Position;
-
- try
- {
- foreach (var factory in Factory.Factories)
- {
- stream.Seek(startPosition, SeekOrigin.Begin);
- if (!factory.IsArchive(stream, readerOptions))
- {
- continue;
- }
-
- if (GetCompressedTarType(factory) is { } compressionType)
- {
- stream.Seek(startPosition, SeekOrigin.Begin);
- if (IsCompressedTar(stream, readerOptions, compressionType))
- {
- return CreateCompressedTarDetection(compressionType);
- }
- }
-
- return CreateDetection(factory);
- }
-
- return
- TryDetectCompressedTar(stream, readerOptions, startPosition)
- is { } compressedTarType
- ? CreateCompressedTarDetection(compressedTarType)
- : null;
- }
- finally
- {
- stream.Seek(startPosition, SeekOrigin.Begin);
- }
- }
-
- private static async ValueTask TryDetectArchiveAsync(
- Stream stream,
- ReaderOptions readerOptions,
- CancellationToken cancellationToken
- )
- {
- var startPosition = stream.Position;
-
- try
- {
- foreach (var factory in Factory.Factories)
- {
- stream.Seek(startPosition, SeekOrigin.Begin);
- if (
- !await factory
- .IsArchiveAsync(stream, readerOptions, cancellationToken)
- .ConfigureAwait(false)
- )
- {
- continue;
- }
-
- if (GetCompressedTarType(factory) is { } compressionType)
- {
- stream.Seek(startPosition, SeekOrigin.Begin);
- if (
- await IsCompressedTarAsync(
- stream,
- readerOptions,
- compressionType,
- cancellationToken
- )
- .ConfigureAwait(false)
- )
- {
- return CreateCompressedTarDetection(compressionType);
- }
- }
-
- return CreateDetection(factory);
- }
-
- var compressedTarType = await TryDetectCompressedTarAsync(
- stream,
- readerOptions,
- startPosition,
- cancellationToken
- )
- .ConfigureAwait(false);
- return compressedTarType is { } value ? CreateCompressedTarDetection(value) : null;
- }
- finally
- {
- stream.Seek(startPosition, SeekOrigin.Begin);
- }
- }
-
- private static CompressionType? TryDetectCompressedTar(
- Stream stream,
- ReaderOptions readerOptions,
- long startPosition
- )
- {
- foreach (var wrapper in TarWrapper.Wrappers)
- {
- if (wrapper.CompressionType == CompressionType.None)
- {
- continue;
- }
-
- stream.Seek(startPosition, SeekOrigin.Begin);
- if (!wrapper.IsMatch(stream))
- {
- continue;
- }
-
- stream.Seek(startPosition, SeekOrigin.Begin);
- if (IsCompressedTar(stream, readerOptions, wrapper.CompressionType))
- {
- return wrapper.CompressionType;
- }
- }
-
- return null;
- }
-
- private static async ValueTask TryDetectCompressedTarAsync(
- Stream stream,
- ReaderOptions readerOptions,
- long startPosition,
- CancellationToken cancellationToken
- )
- {
- foreach (var wrapper in TarWrapper.Wrappers)
- {
- cancellationToken.ThrowIfCancellationRequested();
- if (wrapper.CompressionType == CompressionType.None)
- {
- continue;
- }
-
- stream.Seek(startPosition, SeekOrigin.Begin);
- if (!await wrapper.IsMatchAsync(stream, cancellationToken).ConfigureAwait(false))
- {
- continue;
- }
-
- stream.Seek(startPosition, SeekOrigin.Begin);
- if (
- await IsCompressedTarAsync(
- stream,
- readerOptions,
- wrapper.CompressionType,
- cancellationToken
- )
- .ConfigureAwait(false)
- )
- {
- return wrapper.CompressionType;
- }
- }
-
- return null;
- }
-
private static ArchiveDetection CreateDetection(IFactory factory)
{
var supportedApis = ArchiveAccessMode.None;
diff --git a/src/SharpCompress/Archives/ArchiveFactory.Information.Async.cs b/src/SharpCompress/Archives/ArchiveFactory.Information.Async.cs
index 1f26bd3e..8ac74f5d 100644
--- a/src/SharpCompress/Archives/ArchiveFactory.Information.Async.cs
+++ b/src/SharpCompress/Archives/ArchiveFactory.Information.Async.cs
@@ -20,11 +20,12 @@ namespace SharpCompress.Archives;
public static partial class ArchiveFactory
{
///
- /// Asynchronously collects metadata for the archive at the given file path.
+ /// Collects metadata for the archive at the given file path.
///
/// Path to the archive file.
/// Cancellation token.
/// Archive metadata, or when the file is not a supported archive.
+ [Zomp.SyncMethodGenerator.CreateSyncVersion]
public static async ValueTask InspectArchiveAsync(
string filePath,
CancellationToken cancellationToken = default
@@ -33,12 +34,13 @@ public static partial class ArchiveFactory
.ConfigureAwait(false);
///
- /// Asynchronously collects metadata for the archive at the given file path.
+ /// Collects metadata for the archive at the given file path.
///
/// Path to the archive file.
/// Options controlling archive inspection.
/// Cancellation token.
/// Archive metadata, or when the file is not a supported archive.
+ [Zomp.SyncMethodGenerator.CreateSyncVersion]
public static async ValueTask InspectArchiveAsync(
string filePath,
ReaderOptions? readerOptions,
@@ -68,11 +70,12 @@ public static partial class ArchiveFactory
}
///
- /// Asynchronously collects metadata for the archive in the given stream.
+ /// Collects metadata for the archive in the given stream.
///
/// A readable and seekable stream positioned at the start of the archive.
/// Cancellation token.
/// Archive metadata, or when the stream is not a supported archive.
+ [Zomp.SyncMethodGenerator.CreateSyncVersion]
public static async ValueTask InspectArchiveAsync(
Stream stream,
CancellationToken cancellationToken = default
@@ -81,13 +84,14 @@ public static partial class ArchiveFactory
.ConfigureAwait(false);
///
- /// Asynchronously collects metadata for the archive in the given stream.
+ /// Collects metadata for the archive in the given stream.
///
/// A readable and seekable stream positioned at the start of the archive.
/// Options controlling archive inspection.
/// Cancellation token.
/// Archive metadata, or when the stream is not a supported archive.
/// The supplied stream remains open and is restored to its original position.
+ [Zomp.SyncMethodGenerator.CreateSyncVersion]
public static async ValueTask InspectArchiveAsync(
Stream stream,
ReaderOptions? readerOptions,
@@ -96,7 +100,9 @@ public static partial class ArchiveFactory
{
stream.RequireReadable();
stream.RequireSeekable();
+#if !SYNC_ONLY
cancellationToken.ThrowIfCancellationRequested();
+#endif
var options = readerOptions ?? ReaderOptions.ForExternalStream;
var startPosition = stream.Position;
@@ -119,6 +125,10 @@ public static partial class ArchiveFactory
if ((detection.SupportedApis & ArchiveAccessMode.Archive) != 0)
{
+#if SYNC_ONLY
+ using var archive = OpenArchive(archiveStream, inspectionOptions);
+ return InspectOpenedArchive(archive, detection, physicalSize, 1);
+#else
await using var archive = await OpenAsyncArchive(
archiveStream,
inspectionOptions,
@@ -133,8 +143,14 @@ public static partial class ArchiveFactory
cancellationToken
)
.ConfigureAwait(false);
+#endif
}
+#if SYNC_ONLY
+ var aceHeader = ReadAceHeader(archiveStream, detection, inspectionOptions);
+ using var reader = ReaderFactory.OpenReader(archiveStream, inspectionOptions);
+ return InspectOpenedReader(reader, detection, physicalSize, 1, aceHeader);
+#else
var aceHeader = await ReadAceHeaderAsync(
archiveStream,
detection,
@@ -154,6 +170,7 @@ public static partial class ArchiveFactory
cancellationToken
)
.ConfigureAwait(false);
+#endif
}
catch (CryptographicException) when (string.IsNullOrEmpty(options.Password))
{
@@ -198,12 +215,13 @@ public static partial class ArchiveFactory
}
///
- /// Asynchronously collects metadata for an archive opened from multiple files.
+ /// Collects metadata for an archive opened from multiple files.
///
/// Archive source files in archive order.
/// Options controlling archive inspection.
/// Cancellation token.
/// Archive metadata, or when the files are not a supported archive.
+ [Zomp.SyncMethodGenerator.CreateSyncVersion]
public static async ValueTask InspectArchiveAsync(
IReadOnlyList fileInfos,
ReaderOptions? readerOptions = null,
@@ -242,6 +260,10 @@ public static partial class ArchiveFactory
var physicalSize = GetPhysicalSize(fileInfos);
try
{
+#if SYNC_ONLY
+ using var archive = OpenArchive(fileInfos, options);
+ return InspectOpenedArchive(archive, detection, physicalSize, fileInfos.Count);
+#else
await using var archive = await OpenAsyncArchive(fileInfos, options, cancellationToken)
.ConfigureAwait(false);
return await InspectOpenedArchiveAsync(
@@ -252,6 +274,7 @@ public static partial class ArchiveFactory
cancellationToken
)
.ConfigureAwait(false);
+#endif
}
catch (CryptographicException) when (string.IsNullOrEmpty(options.Password))
{
@@ -265,12 +288,13 @@ public static partial class ArchiveFactory
}
///
- /// Asynchronously collects metadata for an archive opened from multiple streams.
+ /// Collects metadata for an archive opened from multiple streams.
///
/// Archive source streams in archive order.
/// Options controlling archive inspection.
/// Cancellation token.
/// Archive metadata, or when the streams are not a supported archive.
+ [Zomp.SyncMethodGenerator.CreateSyncVersion]
public static async ValueTask InspectArchiveAsync(
IReadOnlyList streams,
ReaderOptions? readerOptions = null,
@@ -317,6 +341,10 @@ public static partial class ArchiveFactory
archiveStreams.AddRange(
streams.Skip(1).Select(stream => new ArchiveOffsetStream(stream))
);
+#if SYNC_ONLY
+ using var archive = OpenArchive(archiveStreams, inspectionOptions);
+ return InspectOpenedArchive(archive, detection, physicalSize, streams.Count);
+#else
await using var archive = await OpenAsyncArchive(
archiveStreams,
inspectionOptions,
@@ -331,6 +359,7 @@ public static partial class ArchiveFactory
cancellationToken
)
.ConfigureAwait(false);
+#endif
}
catch (CryptographicException) when (string.IsNullOrEmpty(options.Password))
{
@@ -466,6 +495,7 @@ public static partial class ArchiveFactory
);
}
+ [Zomp.SyncMethodGenerator.CreateSyncVersion]
private static async ValueTask ReadAceHeaderAsync(
Stream stream,
ArchiveDetection detection,
@@ -566,6 +596,7 @@ public static partial class ArchiveFactory
return (new ZipArchiveInformation(deferredSizeEntryCount > 0), deferredSizeEntryCount);
}
+ [Zomp.SyncMethodGenerator.CreateSyncVersion]
private static async ValueTask RedetectArchiveAsync(
Stream stream,
long startPosition,
diff --git a/src/SharpCompress/Archives/ArchiveFactory.Information.cs b/src/SharpCompress/Archives/ArchiveFactory.Information.cs
index b89774ef..4b12fe8d 100644
--- a/src/SharpCompress/Archives/ArchiveFactory.Information.cs
+++ b/src/SharpCompress/Archives/ArchiveFactory.Information.cs
@@ -2,8 +2,6 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
using SharpCompress.Archives.Rar;
using SharpCompress.Archives.SevenZip;
using SharpCompress.Archives.Zip;
@@ -12,7 +10,6 @@ using SharpCompress.Common.Rar;
using SharpCompress.Common.Zip;
using SharpCompress.Common.Zip.Headers;
using SharpCompress.Detection;
-using SharpCompress.IO;
using SharpCompress.Readers;
using AceMainHeader = SharpCompress.Common.Ace.Headers.AceMainHeader;
@@ -20,236 +17,6 @@ namespace SharpCompress.Archives;
public static partial class ArchiveFactory
{
- ///
- /// Collects metadata for the archive at the given file path.
- ///
- /// Path to the archive file.
- /// Archive metadata, or when the file is not a supported archive.
- public static ArchiveInformation? InspectArchive(string filePath) =>
- InspectArchive(filePath, ReaderOptions.ForFilePath);
-
- ///
- /// Collects metadata for the archive at the given file path.
- ///
- /// Path to the archive file.
- /// Options controlling archive inspection.
- /// Archive metadata, or when the file is not a supported archive.
- public static ArchiveInformation? InspectArchive(string filePath, ReaderOptions? readerOptions)
- {
- filePath.NotNullOrEmpty(nameof(filePath));
- var options = readerOptions ?? ReaderOptions.ForFilePath;
- var detection = DetectArchive(filePath, options);
- if (detection is null)
- {
- return null;
- }
- if ((detection.SupportedApis & ArchiveAccessMode.Archive) != 0)
- {
- var fileInfos = GetArchiveFileParts(new FileInfo(filePath), options);
- if (fileInfos.Length > 1)
- {
- return InspectArchive(fileInfos, options);
- }
- }
-
- using Stream stream = File.OpenRead(filePath);
- return InspectArchive(stream, options);
- }
-
- ///
- /// Collects metadata for the archive in the given stream.
- ///
- /// A readable and seekable stream positioned at the start of the archive.
- /// Archive metadata, or when the stream is not a supported archive.
- public static ArchiveInformation? InspectArchive(Stream stream) =>
- InspectArchive(stream, ReaderOptions.ForExternalStream);
-
- ///
- /// Collects metadata for the archive in the given stream.
- ///
- /// A readable and seekable stream positioned at the start of the archive.
- /// Options controlling archive inspection.
- /// Archive metadata, or when the stream is not a supported archive.
- /// The supplied stream remains open and is restored to its original position.
- public static ArchiveInformation? InspectArchive(Stream stream, ReaderOptions? readerOptions)
- {
- stream.RequireReadable();
- stream.RequireSeekable();
-
- var options = readerOptions ?? ReaderOptions.ForExternalStream;
- var startPosition = stream.Position;
- var physicalSize = GetPhysicalSize(stream, startPosition);
-
- try
- {
- using var archiveStream = new ArchiveOffsetStream(stream);
- var inspectionOptions = options with { LeaveStreamOpen = true };
- var detection = TryDetectArchive(archiveStream, inspectionOptions);
- if (detection is null)
- {
- return null;
- }
-
- if ((detection.SupportedApis & ArchiveAccessMode.Archive) != 0)
- {
- using var archive = OpenArchive(archiveStream, inspectionOptions);
- return InspectOpenedArchive(archive, detection, physicalSize, 1);
- }
-
- var aceHeader = ReadAceHeader(archiveStream, detection, inspectionOptions);
- using var reader = ReaderFactory.OpenReader(archiveStream, inspectionOptions);
- return InspectOpenedReader(reader, detection, physicalSize, 1, aceHeader);
- }
- catch (CryptographicException) when (string.IsNullOrEmpty(options.Password))
- {
- var detection = RedetectArchive(stream, startPosition, options);
- return detection is null
- ? null
- : CreatePartialInformation(
- detection,
- physicalSize,
- 1,
- ArchiveInformationLimitations.EncryptedHeaders
- );
- }
- catch (MultipartStreamRequiredException)
- {
- var detection = RedetectArchive(stream, startPosition, options);
- return detection is null
- ? null
- : CreatePartialInformation(
- detection,
- physicalSize,
- 1,
- ArchiveInformationLimitations.MissingVolumes
- );
- }
- finally
- {
- stream.Seek(startPosition, SeekOrigin.Begin);
- }
- }
-
- ///
- /// Collects metadata for an archive opened from multiple files.
- ///
- /// Archive source files in archive order.
- /// Options controlling archive inspection.
- /// Archive metadata, or when the files are not a supported archive.
- public static ArchiveInformation? InspectArchive(
- IReadOnlyList fileInfos,
- ReaderOptions? readerOptions = null
- )
- {
- fileInfos.NotNull(nameof(fileInfos));
- if (fileInfos.Count == 0)
- {
- throw new ArchiveOperationException("No files to inspect");
- }
- if (fileInfos.Count == 1)
- {
- return InspectArchive(fileInfos[0].FullName, readerOptions);
- }
-
- var options = readerOptions ?? ReaderOptions.ForFilePath;
- var detection = DetectArchive(fileInfos[0].FullName, options);
- if (detection is null)
- {
- return null;
- }
- if ((detection.SupportedApis & ArchiveAccessMode.Archive) == 0)
- {
- throw new NotSupportedException(
- "Inspecting multiple source files is supported only for formats with an Archive API."
- );
- }
-
- var physicalSize = GetPhysicalSize(fileInfos);
- try
- {
- using var archive = OpenArchive(fileInfos, options);
- return InspectOpenedArchive(archive, detection, physicalSize, fileInfos.Count);
- }
- catch (CryptographicException) when (string.IsNullOrEmpty(options.Password))
- {
- return CreatePartialInformation(
- detection,
- physicalSize,
- fileInfos.Count,
- ArchiveInformationLimitations.EncryptedHeaders
- );
- }
- }
-
- ///
- /// Collects metadata for an archive opened from multiple streams.
- ///
- /// Archive source streams in archive order.
- /// Options controlling archive inspection.
- /// Archive metadata, or when the streams are not a supported archive.
- public static ArchiveInformation? InspectArchive(
- IReadOnlyList streams,
- ReaderOptions? readerOptions = null
- )
- {
- streams.NotNull(nameof(streams));
- if (streams.Count == 0)
- {
- throw new ArchiveOperationException("No streams to inspect");
- }
- if (streams.Count == 1)
- {
- return InspectArchive(streams[0], readerOptions);
- }
-
- var options = readerOptions ?? ReaderOptions.ForExternalStream;
- var startPositions = streams.Select(stream => stream.Position).ToArray();
- var physicalSize = GetPhysicalSize(streams, startPositions);
-
- try
- {
- using var firstArchiveStream = new ArchiveOffsetStream(streams[0]);
- var inspectionOptions = options with { LeaveStreamOpen = true };
- var detection = TryDetectArchive(firstArchiveStream, inspectionOptions);
- if (detection is null)
- {
- return null;
- }
- if ((detection.SupportedApis & ArchiveAccessMode.Archive) == 0)
- {
- throw new NotSupportedException(
- "Inspecting multiple source streams is supported only for formats with an Archive API."
- );
- }
-
- var archiveStreams = new List { firstArchiveStream };
- archiveStreams.AddRange(
- streams.Skip(1).Select(stream => new ArchiveOffsetStream(stream))
- );
- using var archive = OpenArchive(archiveStreams, inspectionOptions);
- return InspectOpenedArchive(archive, detection, physicalSize, streams.Count);
- }
- catch (CryptographicException) when (string.IsNullOrEmpty(options.Password))
- {
- var detection = RedetectArchive(streams[0], startPositions[0], options);
- return detection is null
- ? null
- : CreatePartialInformation(
- detection,
- physicalSize,
- streams.Count,
- ArchiveInformationLimitations.EncryptedHeaders
- );
- }
- finally
- {
- for (var i = 0; i < streams.Count; i++)
- {
- streams[i].Seek(startPositions[i], SeekOrigin.Begin);
- }
- }
- }
-
private static ArchiveInformation InspectOpenedArchive(
IArchive archive,
ArchiveDetection detection,
@@ -331,28 +98,6 @@ public static partial class ArchiveFactory
);
}
- private static AceMainHeader? ReadAceHeader(
- Stream stream,
- ArchiveDetection detection,
- ReaderOptions options
- )
- {
- if (detection.ContainerType != ArchiveType.Ace)
- {
- return null;
- }
-
- try
- {
- stream.Position = 0;
- return new AceMainHeader(options.ArchiveEncoding).Read(stream) as AceMainHeader;
- }
- finally
- {
- stream.Position = 0;
- }
- }
-
private static ArchiveInformation CreatePartialInformation(
ArchiveDetection detection,
long? physicalSize,
@@ -486,17 +231,6 @@ public static partial class ArchiveFactory
}
}
- private static ArchiveDetection? RedetectArchive(
- Stream stream,
- long startPosition,
- ReaderOptions options
- )
- {
- stream.Seek(startPosition, SeekOrigin.Begin);
- using var archiveStream = new ArchiveOffsetStream(stream);
- return TryDetectArchive(archiveStream, options);
- }
-
private static FileInfo[] GetArchiveFileParts(FileInfo firstPart, ReaderOptions options)
{
using Stream stream = firstPart.OpenRead();
diff --git a/src/SharpCompress/Archives/ArchiveFactory.cs b/src/SharpCompress/Archives/ArchiveFactory.cs
index 63b63954..efff5604 100644
--- a/src/SharpCompress/Archives/ArchiveFactory.cs
+++ b/src/SharpCompress/Archives/ArchiveFactory.cs
@@ -123,37 +123,6 @@ public static partial class ArchiveFactory
public static T FindFactory(Stream stream)
where T : IFactory => FindFactory(stream, ReaderOptions.ForExternalStream);
- private static T FindFactory(FileInfo fileInfo, ReaderOptions readerOptions)
- where T : IFactory
- {
- fileInfo.NotNull(nameof(fileInfo));
- using Stream stream = fileInfo.OpenRead();
- return FindFactory(stream, readerOptions);
- }
-
- private static T FindFactory(Stream stream, ReaderOptions readerOptions)
- where T : IFactory
- {
- stream.RequireReadable();
- stream.RequireSeekable();
-
- // Use the shared detection loop over all factories. If the matched factory
- // implements T we return it; otherwise (or if nothing matched) we fall through
- // to the same "unsupported format" exception that the original code produced,
- // listing the T-typed factories as the hint for the caller.
- var factory = TryFindFactory(stream, readerOptions);
- if (factory is T typedFactory)
- {
- return typedFactory;
- }
-
- var extensions = string.Join(", ", Factory.Factories.OfType().Select(item => item.Name));
-
- throw new ArchiveOperationException(
- $"Cannot determine compressed stream type. Supported Archive Formats: {extensions}"
- );
- }
-
public static bool IsArchive(string filePath, out ArchiveType? type)
{
return IsArchive(filePath, ReaderOptions.ForFilePath, out type);