diff --git a/src/SharpCompress/Factories/TarFactory.cs b/src/SharpCompress/Factories/TarFactory.cs index b5fc14891..d32020fd7 100644 --- a/src/SharpCompress/Factories/TarFactory.cs +++ b/src/SharpCompress/Factories/TarFactory.cs @@ -1,10 +1,13 @@ +using System; using System.Collections.Generic; using System.IO; +using System.Linq; using SharpCompress.Archives; using SharpCompress.Archives.Tar; using SharpCompress.Common; using SharpCompress.Compressors; using SharpCompress.Compressors.BZip2; +using SharpCompress.Compressors.Deflate; using SharpCompress.Compressors.LZMA; using SharpCompress.Compressors.Lzw; using SharpCompress.Compressors.Xz; @@ -14,6 +17,7 @@ using SharpCompress.Readers.Tar; using SharpCompress.Writers; using SharpCompress.Writers.Tar; +using GZipArchive = SharpCompress.Archives.GZip.GZipArchive; namespace SharpCompress.Factories; @@ -39,32 +43,13 @@ public class TarFactory /// public override IEnumerable GetSupportedExtensions() { - // from https://en.wikipedia.org/wiki/Tar_(computing)#Suffixes_for_compressed_files - - yield return "tar"; - - // gzip - yield return "taz"; - yield return "tgz"; - - // bzip2 - yield return "tb2"; - yield return "tbz"; - yield return "tbz2"; - yield return "tz2"; - - // lzma - // yield return "tlz"; // unsupported - - // xz - // yield return "txz"; // unsupported - - // compress - yield return "tZ"; - yield return "taZ"; - - // zstd - yield return "tzst"; + foreach (var testOption in compressionOptions) + { + foreach (var ext in testOption.KnownExtensions) + { + yield return ext; + } + } } /// @@ -102,6 +87,77 @@ public IArchive Open(IReadOnlyList fileInfos, ReaderOptions? readerOpt #region IReaderFactory + + protected class TestOption + { + public readonly CompressionType Type; + public readonly Func CanHandle; + public readonly bool WrapInSharpCompressStream; + + public readonly Func CreateStream; + + public readonly IEnumerable KnownExtensions; + + public TestOption( + CompressionType Type, + Func CanHandle, + Func CreateStream, + IEnumerable KnownExtensions, + bool WrapInSharpCompressStream = true + ) + { + this.Type = Type; + this.CanHandle = CanHandle; + this.WrapInSharpCompressStream = WrapInSharpCompressStream; + this.CreateStream = CreateStream; + this.KnownExtensions = KnownExtensions; + } + } + + // https://en.wikipedia.org/wiki/Tar_(computing)#Suffixes_for_compressed_files + protected TestOption[] compressionOptions = + [ + new(CompressionType.None, (stream) => true, (stream) => stream, ["tar"], false), // We always do a test for IsTarFile later + new( + CompressionType.BZip2, + BZip2Stream.IsBZip2, + (stream) => new BZip2Stream(stream, CompressionMode.Decompress, false), + ["tar.bz2", "tb2", "tbz", "tbz2", "tz2"] + ), + new( + CompressionType.GZip, + GZipArchive.IsGZipFile, + (stream) => new GZipStream(stream, CompressionMode.Decompress), + ["tar.gz", "taz", "tgz"] + ), + new( + CompressionType.ZStandard, + ZStandardStream.IsZStandard, + (stream) => new ZStandardStream(stream), + ["tar.zst", "tar.zstd", "tzst", "tzstd"] + ), + new( + CompressionType.LZip, + LZipStream.IsLZipFile, + (stream) => new LZipStream(stream, CompressionMode.Decompress), + ["tar.lz"] + ), + new( + CompressionType.Xz, + XZStream.IsXZStream, + (stream) => new XZStream(stream), + ["tar.xz", "txz"], + false + ), + new( + CompressionType.Lzw, + LzwStream.IsLzwStream, + (stream) => new LzwStream(stream), + ["tar.Z", "tZ", "taZ"], + false + ), + ]; + /// internal override bool TryOpenReader( SharpCompressStream rewindableStream, @@ -111,89 +167,67 @@ out IReader? reader { reader = null; long pos = ((IStreamStack)rewindableStream).GetPosition(); - - if (TarArchive.IsTarFile(rewindableStream)) + TestOption? testedOption = null; + if (!string.IsNullOrWhiteSpace(options.ExtensionHint)) { - ((IStreamStack)rewindableStream).StackSeek(pos); - reader = OpenReader(rewindableStream, options); - return true; - } - - ((IStreamStack)rewindableStream).StackSeek(pos); - if (BZip2Stream.IsBZip2(rewindableStream)) - { - ((IStreamStack)rewindableStream).StackSeek(pos); - var testStream = new BZip2Stream( - SharpCompressStream.Create(rewindableStream, leaveOpen: true), - CompressionMode.Decompress, - false + testedOption = compressionOptions.FirstOrDefault(a => + a.KnownExtensions.Contains( + options.ExtensionHint, + StringComparer.CurrentCultureIgnoreCase + ) ); - if (TarArchive.IsTarFile(testStream)) + if (testedOption != null) { - ((IStreamStack)rewindableStream).StackSeek(pos); - reader = new TarReader(rewindableStream, options, CompressionType.BZip2); - return true; + reader = TryOption(rewindableStream, options, pos, testedOption); + if (reader != null) + { + return true; + } } } - ((IStreamStack)rewindableStream).StackSeek(pos); - if (ZStandardStream.IsZStandard(rewindableStream)) + foreach (var testOption in compressionOptions) { - ((IStreamStack)rewindableStream).StackSeek(pos); - var testStream = new ZStandardStream( - SharpCompressStream.Create(rewindableStream, leaveOpen: true) - ); - if (TarArchive.IsTarFile(testStream)) + if (testedOption == testOption) { - ((IStreamStack)rewindableStream).StackSeek(pos); - reader = new TarReader(rewindableStream, options, CompressionType.ZStandard); - return true; + continue; // Already tested above } - } - - ((IStreamStack)rewindableStream).StackSeek(pos); - if (LZipStream.IsLZipFile(rewindableStream)) - { ((IStreamStack)rewindableStream).StackSeek(pos); - var testStream = new LZipStream( - SharpCompressStream.Create(rewindableStream, leaveOpen: true), - CompressionMode.Decompress - ); - if (TarArchive.IsTarFile(testStream)) + reader = TryOption(rewindableStream, options, pos, testOption); + if (reader != null) { - ((IStreamStack)rewindableStream).StackSeek(pos); - reader = new TarReader(rewindableStream, options, CompressionType.LZip); return true; } } - ((IStreamStack)rewindableStream).StackSeek(pos); - if (XZStream.IsXZStream(rewindableStream)) + return false; + } + + private static IReader? TryOption( + SharpCompressStream rewindableStream, + ReaderOptions options, + long pos, + TestOption testOption + ) + { + if (testOption.CanHandle(rewindableStream)) { ((IStreamStack)rewindableStream).StackSeek(pos); - var testStream = new XZStream(rewindableStream); - if (TarArchive.IsTarFile(testStream)) + var inStream = rewindableStream; + if (testOption.WrapInSharpCompressStream) { - ((IStreamStack)rewindableStream).StackSeek(pos); - reader = new TarReader(rewindableStream, options, CompressionType.Xz); - return true; + inStream = SharpCompressStream.Create(rewindableStream, leaveOpen: true); } - } + var testStream = testOption.CreateStream(rewindableStream); - ((IStreamStack)rewindableStream).StackSeek(pos); - if (LzwStream.IsLzwStream(rewindableStream)) - { - var testStream = new LzwStream(rewindableStream); - ((IStreamStack)rewindableStream).StackSeek(pos); if (TarArchive.IsTarFile(testStream)) { ((IStreamStack)rewindableStream).StackSeek(pos); - reader = new TarReader(rewindableStream, options, CompressionType.Lzw); - return true; + return new TarReader(rewindableStream, options, testOption.Type); } } - return false; + return null; } /// diff --git a/src/SharpCompress/Readers/ReaderFactory.cs b/src/SharpCompress/Readers/ReaderFactory.cs index b7b40401d..fb133d98a 100644 --- a/src/SharpCompress/Readers/ReaderFactory.cs +++ b/src/SharpCompress/Readers/ReaderFactory.cs @@ -1,6 +1,8 @@ +using System; using System.IO; using System.Linq; using SharpCompress.Common; +using SharpCompress.Factories; using SharpCompress.IO; namespace SharpCompress.Readers; @@ -22,8 +24,32 @@ public static IReader Open(Stream stream, ReaderOptions? options = null) long pos = ((IStreamStack)bStream).GetPosition(); - foreach (var factory in Factories.Factory.Factories.OfType()) + var factories = Factories.Factory.Factories.OfType(); + + Factory? testedFactory = null; + + if (!string.IsNullOrWhiteSpace(options.ExtensionHint)) { + testedFactory = factories.FirstOrDefault(a => + a.GetSupportedExtensions() + .Contains(options.ExtensionHint, StringComparer.CurrentCultureIgnoreCase) + ); + if ( + testedFactory?.TryOpenReader(bStream, options, out var reader) == true + && reader != null + ) + { + return reader; + } + ((IStreamStack)bStream).StackSeek(pos); + } + + foreach (var factory in factories) + { + if (testedFactory == factory) + { + continue; // Already tested above + } ((IStreamStack)bStream).StackSeek(pos); if (factory.TryOpenReader(bStream, options, out var reader) && reader != null) { diff --git a/src/SharpCompress/Readers/ReaderOptions.cs b/src/SharpCompress/Readers/ReaderOptions.cs index a581d51a5..4ddfe94d2 100644 --- a/src/SharpCompress/Readers/ReaderOptions.cs +++ b/src/SharpCompress/Readers/ReaderOptions.cs @@ -16,4 +16,9 @@ public class ReaderOptions : OptionsBase public bool DisableCheckIncomplete { get; set; } public int BufferSize { get; set; } = DefaultBufferSize; + + /// + /// Provide a hint for the extension of the archive being read, can speed up finding the correct decoder. Should be without the leading period in the form like: tar.gz or zip + /// + public string? ExtensionHint { get; set; } }