Skip to content
Draft
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
10 changes: 8 additions & 2 deletions src/SharpCompress/Archives/Tar/TarArchive.Factory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ public static IWritableArchive<TarWriterOptions> OpenArchive(
);
var compressionType = TarFactory.GetCompressionType(
sourceStream,
sourceStream.ReaderOptions.Providers
sourceStream.ReaderOptions.Providers,
sourceStream.ReaderOptions
);
sourceStream.Seek(0, SeekOrigin.Begin);
return new TarArchive(sourceStream, compressionType);
Expand All @@ -78,7 +79,8 @@ public static IWritableArchive<TarWriterOptions> OpenArchive(
);
var compressionType = TarFactory.GetCompressionType(
sourceStream,
sourceStream.ReaderOptions.Providers
sourceStream.ReaderOptions.Providers,
sourceStream.ReaderOptions
);
sourceStream.Seek(0, SeekOrigin.Begin);
return new TarArchive(sourceStream, compressionType);
Expand Down Expand Up @@ -115,6 +117,7 @@ public static async ValueTask<IWritableAsyncArchive<TarWriterOptions>> OpenAsync
.GetCompressionTypeAsync(
sourceStream,
sourceStream.ReaderOptions.Providers,
sourceStream.ReaderOptions,
cancellationToken
)
.ConfigureAwait(false);
Expand Down Expand Up @@ -147,6 +150,7 @@ public static async ValueTask<IWritableAsyncArchive<TarWriterOptions>> OpenAsync
.GetCompressionTypeAsync(
sourceStream,
sourceStream.ReaderOptions.Providers,
sourceStream.ReaderOptions,
cancellationToken
)
.ConfigureAwait(false);
Expand All @@ -172,6 +176,7 @@ public static async ValueTask<IWritableAsyncArchive<TarWriterOptions>> OpenAsync
.GetCompressionTypeAsync(
sourceStream,
sourceStream.ReaderOptions.Providers,
sourceStream.ReaderOptions,
cancellationToken
)
.ConfigureAwait(false);
Expand All @@ -197,6 +202,7 @@ public static async ValueTask<IWritableAsyncArchive<TarWriterOptions>> OpenAsync
.GetCompressionTypeAsync(
sourceStream,
sourceStream.ReaderOptions.Providers,
sourceStream.ReaderOptions,
cancellationToken
)
.ConfigureAwait(false);
Expand Down
89 changes: 84 additions & 5 deletions src/SharpCompress/Factories/TarFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,81 @@ await TarArchive

#endregion

internal override bool TryOpenReader(
SharpCompressStream stream,
ReaderOptions options,
out IReader? reader
)
{
reader = null;
foreach (var wrapper in TarWrapper.Wrappers)
{
stream.Rewind();
if (wrapper.IsMatch(stream))
{
stream.Rewind();
var decompressedStream = CreateProbeDecompressionStream(
stream,
wrapper.CompressionType,
options.Providers,
options
);
if (TarArchive.IsTarFile(decompressedStream))
{
stream.Rewind(true);
reader = new TarReader(stream, options, wrapper.CompressionType);
return true;
}
}
}
stream.Rewind();
return false;
}

internal override async ValueTask<IAsyncReader?> TryOpenReaderAsync(
SharpCompressStream stream,
ReaderOptions options,
CancellationToken cancellationToken = default
)
{
// Use an intermediate ring-buffer wrapper for the probe phase.
// This ensures decompressor constructors that use synchronous reads
// can replay already-buffered data, even when the underlying stream
// only supports async I/O (e.g. streams wrapping async-only transports).
// After StopRecording(), the ring buffer is still available for replay
// because ReadWithRingBuffer() serves data whenever _logicalPosition < streamPosition,
// regardless of whether recording is active.
var probeStream = new SharpCompressStream(stream);
probeStream.StartRecording();
foreach (var wrapper in TarWrapper.Wrappers)
{
probeStream.Rewind();
if (await wrapper.IsMatchAsync(probeStream, cancellationToken).ConfigureAwait(false))
{
probeStream.Rewind();
var decompressedStream = await CreateProbeDecompressionStreamAsync(
probeStream,
wrapper.CompressionType,
options.Providers,
options,
cancellationToken
)
.ConfigureAwait(false);
if (
await TarArchive
.IsTarFileAsync(decompressedStream, cancellationToken)
.ConfigureAwait(false)
)
{
probeStream.StopRecording();
return new TarReader(probeStream, options, wrapper.CompressionType);
}
}
}
stream.Rewind();
return null;
}

private static Stream CreateProbeDecompressionStream(
Stream stream,
CompressionType compressionType,
Expand Down Expand Up @@ -179,10 +254,11 @@ private static async ValueTask<Stream> CreateProbeDecompressionStreamAsync(

public static CompressionType GetCompressionType(
Stream stream,
CompressionProviderRegistry? providers = null
CompressionProviderRegistry? providers = null,
IReaderOptions? readerOptions = null
)
{
providers ??= CompressionProviderRegistry.Default;
providers ??= readerOptions?.Providers ?? CompressionProviderRegistry.Default;
stream.Seek(0, SeekOrigin.Begin);
foreach (var wrapper in TarWrapper.Wrappers)
{
Expand All @@ -193,7 +269,8 @@ public static CompressionType GetCompressionType(
var decompressedStream = CreateProbeDecompressionStream(
stream,
wrapper.CompressionType,
providers
providers,
readerOptions
);
if (TarArchive.IsTarFile(decompressedStream))
{
Expand All @@ -207,10 +284,11 @@ public static CompressionType GetCompressionType(
public static async ValueTask<CompressionType> GetCompressionTypeAsync(
Stream stream,
CompressionProviderRegistry? providers = null,
IReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
providers ??= CompressionProviderRegistry.Default;
providers ??= readerOptions?.Providers ?? CompressionProviderRegistry.Default;
stream.Seek(0, SeekOrigin.Begin);
foreach (var wrapper in TarWrapper.Wrappers)
{
Expand All @@ -222,7 +300,8 @@ public static async ValueTask<CompressionType> GetCompressionTypeAsync(
stream,
wrapper.CompressionType,
providers,
cancellationToken: cancellationToken
readerOptions,
cancellationToken
)
.ConfigureAwait(false);
if (
Expand Down
16 changes: 16 additions & 0 deletions tests/SharpCompress.Test/Tar/TarReaderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,22 @@ public void Tar_Skip()
[Fact]
public void Tar_GZip_OldGnu_Reader() => Read("Tar.oldgnu.tar.gz", CompressionType.GZip);

[Fact]
public void Tar_GZip_Reader_With_ExtensionHint() =>
Read("Tar.tar.gz", CompressionType.GZip, new ReaderOptions { ExtensionHint = "tar.gz" });

[Fact]
public void Tar_BZip2_Reader_With_ExtensionHint() =>
Read("Tar.tar.bz2", CompressionType.BZip2, new ReaderOptions { ExtensionHint = "tar.bz2" });

[Fact]
public void Tar_Xz_Reader_With_ExtensionHint() =>
Read("Tar.tar.xz", CompressionType.Xz, new ReaderOptions { ExtensionHint = "tar.xz" });

[Fact]
public void Tar_Plain_Reader_With_ExtensionHint() =>
Read("Tar.tar", CompressionType.None, new ReaderOptions { ExtensionHint = "tar" });

[Fact]
public void Tar_BZip2_Entry_Stream()
{
Expand Down