Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
200 changes: 117 additions & 83 deletions src/SharpCompress/Factories/TarFactory.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -14,6 +17,7 @@
using SharpCompress.Readers.Tar;
using SharpCompress.Writers;
using SharpCompress.Writers.Tar;
using GZipArchive = SharpCompress.Archives.GZip.GZipArchive;

namespace SharpCompress.Factories;

Expand All @@ -39,32 +43,13 @@ public class TarFactory
/// <inheritdoc/>
public override IEnumerable<string> 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;
}
}
}

/// <inheritdoc/>
Expand Down Expand Up @@ -102,6 +87,77 @@ public IArchive Open(IReadOnlyList<FileInfo> fileInfos, ReaderOptions? readerOpt

#region IReaderFactory


protected class TestOption
{
public readonly CompressionType Type;
public readonly Func<Stream, bool> CanHandle;
public readonly bool WrapInSharpCompressStream;

public readonly Func<Stream, Stream> CreateStream;

public readonly IEnumerable<string> KnownExtensions;

public TestOption(
CompressionType Type,
Func<Stream, bool> CanHandle,
Func<Stream, Stream> CreateStream,
IEnumerable<string> 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
),
];

/// <inheritdoc/>
internal override bool TryOpenReader(
SharpCompressStream rewindableStream,
Expand All @@ -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;
}

/// <inheritdoc/>
Expand Down
28 changes: 27 additions & 1 deletion src/SharpCompress/Readers/ReaderFactory.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System;
using System.IO;
using System.Linq;
using SharpCompress.Common;
using SharpCompress.Factories;
using SharpCompress.IO;

namespace SharpCompress.Readers;
Expand All @@ -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<Factories.Factory>())
var factories = Factories.Factory.Factories.OfType<Factories.Factory>();

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)
{
Expand Down
5 changes: 5 additions & 0 deletions src/SharpCompress/Readers/ReaderOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,9 @@ public class ReaderOptions : OptionsBase
public bool DisableCheckIncomplete { get; set; }

public int BufferSize { get; set; } = DefaultBufferSize;

/// <summary>
/// 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
/// </summary>
public string? ExtensionHint { get; set; }
}
Loading