mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-07-08 18:16:30 +00:00
complete the usage of providers for detection and async
This commit is contained in:
@@ -18,7 +18,7 @@ Check the [Supported Formats](docs/FORMATS.md) and [Basic Usage.](docs/USAGE.md)
|
||||
|
||||
## Custom Compression Providers
|
||||
|
||||
If you need to swap out SharpCompress’s built-in codecs, the `Providers` property (and `WithProviders(...)` extensions) on `ReaderOptions` and `WriterOptions` lets you supply a `CompressionProviderRegistry`. The default registry is already wired up, so customization is only necessary when you want to plug in alternatives such as `SystemGZipCompressionProvider` or a third-party `CompressionProvider`. See [docs/USAGE.md#custom-compression-providers](docs/USAGE.md#custom-compression-providers) for guided examples.
|
||||
If you need to swap out SharpCompress’s built-in codecs, the `Providers` property (and `WithProviders(...)` extensions) on `ReaderOptions` and `WriterOptions` lets you supply a `CompressionProviderRegistry`. The selected registry is used by Reader/Writer APIs, Archive APIs, and async extraction paths, so the same provider choice is applied consistently across open/read/write flows. The default registry is already wired up, so customization is only necessary when you want to plug in alternatives such as `SystemGZipCompressionProvider` or a third-party `CompressionProvider`. See [docs/USAGE.md#custom-compression-providers](docs/USAGE.md#custom-compression-providers) for guided examples.
|
||||
|
||||
## Recommended Formats
|
||||
|
||||
|
||||
@@ -320,7 +320,7 @@ ZipWriterEntryOptions: per-entry ZIP overrides (compression, level, timestamps,
|
||||
|
||||
### Compression Providers
|
||||
|
||||
`ReaderOptions` and `WriterOptions` expose a `Providers` registry that controls which `ICompressionProvider` implementations are used for each `CompressionType`. The registry defaults to `CompressionProviderRegistry.Default`, so you only need to set it if you want to swap in a custom provider (for example the `SystemGZipCompressionProvider`).
|
||||
`ReaderOptions` and `WriterOptions` expose a `Providers` registry that controls which `ICompressionProvider` implementations are used for each `CompressionType`. The registry defaults to `CompressionProviderRegistry.Default`, so you only need to set it if you want to swap in a custom provider (for example the `SystemGZipCompressionProvider`). The selected registry is honored by Reader/Writer APIs, Archive APIs, and async entry-stream extraction paths.
|
||||
|
||||
```csharp
|
||||
var registry = CompressionProviderRegistry.Default.With(new SystemGZipCompressionProvider());
|
||||
@@ -334,7 +334,7 @@ using var reader = ReaderFactory.OpenReader(input, readerOptions);
|
||||
using var writer = WriterFactory.OpenWriter(output, ArchiveType.GZip, writerOptions);
|
||||
```
|
||||
|
||||
When a format needs additional initialization/finalization data (LZMA, PPMd, etc.) the registry exposes `GetCompressingProvider` which returns the `ICompressionProviderHooks` contract; the rest of the API continues to flow through `Providers`.
|
||||
When a format needs additional initialization/finalization data (LZMA, PPMd, etc.) the registry exposes `GetCompressingProvider` which returns the `ICompressionProviderHooks` contract; the rest of the API continues to flow through `Providers`, including pre/properties/post compression hook data.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -210,6 +210,8 @@ foreach(var entry in archive.Entries)
|
||||
|
||||
By default `ReaderOptions` and `WriterOptions` already include `CompressionProviderRegistry.Default` via their `Providers` property, so you can read and write without touching the registry yet still get SharpCompress’s built-in implementations.
|
||||
|
||||
The configured registry is used consistently across Reader APIs, Writer APIs, Archive APIs, and async entry-stream extraction, including compressed TAR wrappers and ZIP async decompression.
|
||||
|
||||
To replace a specific algorithm (for example to use `System.IO.Compression` for GZip or Deflate), create a modified registry and pass it through the same options:
|
||||
|
||||
```C#
|
||||
|
||||
@@ -77,7 +77,7 @@ public partial class GZipArchive
|
||||
{
|
||||
var stream = Volumes.Single().Stream;
|
||||
stream.Position = 0;
|
||||
return new((IAsyncReader)GZipReader.OpenReader(stream));
|
||||
return new((IAsyncReader)GZipReader.OpenReader(stream, ReaderOptions));
|
||||
}
|
||||
|
||||
protected override async IAsyncEnumerable<GZipArchiveEntry> LoadEntriesAsync(
|
||||
|
||||
@@ -96,6 +96,6 @@ public partial class GZipArchive
|
||||
{
|
||||
var stream = Volumes.Single().Stream;
|
||||
stream.Position = 0;
|
||||
return GZipReader.OpenReader(stream);
|
||||
return GZipReader.OpenReader(stream, ReaderOptions);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,26 +89,32 @@ public partial class TarArchive
|
||||
{
|
||||
var stream = Volumes.Single().Stream;
|
||||
stream.Position = 0;
|
||||
return new((IAsyncReader)TarReader.OpenReader(stream));
|
||||
return new((IAsyncReader)new TarReader(stream, ReaderOptions, _compressionType));
|
||||
}
|
||||
|
||||
protected override async IAsyncEnumerable<TarArchiveEntry> LoadEntriesAsync(
|
||||
IAsyncEnumerable<TarVolume> volumes
|
||||
)
|
||||
{
|
||||
var stream = (await volumes.SingleAsync().ConfigureAwait(false)).Stream;
|
||||
var sourceStream = (await volumes.SingleAsync().ConfigureAwait(false)).Stream;
|
||||
var stream = GetStream(sourceStream);
|
||||
if (stream.CanSeek)
|
||||
{
|
||||
stream.Position = 0;
|
||||
}
|
||||
|
||||
var streamingMode =
|
||||
_compressionType == CompressionType.None
|
||||
? StreamingMode.Seekable
|
||||
: StreamingMode.Streaming;
|
||||
|
||||
// Always use async header reading in LoadEntriesAsync for consistency
|
||||
{
|
||||
// Use async header reading for async-only streams
|
||||
TarHeader? previousHeader = null;
|
||||
await foreach (
|
||||
var header in TarHeaderFactory.ReadHeaderAsync(
|
||||
StreamingMode.Seekable,
|
||||
streamingMode,
|
||||
stream,
|
||||
ReaderOptions.ArchiveEncoding
|
||||
)
|
||||
@@ -126,7 +132,10 @@ public partial class TarArchive
|
||||
{
|
||||
var entry = new TarArchiveEntry(
|
||||
this,
|
||||
new TarFilePart(previousHeader, stream),
|
||||
new TarFilePart(
|
||||
previousHeader,
|
||||
_compressionType == CompressionType.None ? stream : null
|
||||
),
|
||||
CompressionType.None,
|
||||
ReaderOptions
|
||||
);
|
||||
@@ -151,7 +160,10 @@ public partial class TarArchive
|
||||
}
|
||||
yield return new TarArchiveEntry(
|
||||
this,
|
||||
new TarFilePart(header, stream),
|
||||
new TarFilePart(
|
||||
header,
|
||||
_compressionType == CompressionType.None ? stream : null
|
||||
),
|
||||
CompressionType.None,
|
||||
ReaderOptions
|
||||
);
|
||||
|
||||
@@ -56,7 +56,10 @@ public partial class TarArchive
|
||||
i => i < files.Length ? files[i] : null,
|
||||
readerOptions ?? new ReaderOptions() { LeaveStreamOpen = false }
|
||||
);
|
||||
var compressionType = TarFactory.GetCompressionType(sourceStream);
|
||||
var compressionType = TarFactory.GetCompressionType(
|
||||
sourceStream,
|
||||
sourceStream.ReaderOptions.Providers
|
||||
);
|
||||
sourceStream.Seek(0, SeekOrigin.Begin);
|
||||
return new TarArchive(sourceStream, compressionType);
|
||||
}
|
||||
@@ -73,7 +76,10 @@ public partial class TarArchive
|
||||
i => i < strms.Length ? strms[i] : null,
|
||||
readerOptions ?? new ReaderOptions()
|
||||
);
|
||||
var compressionType = TarFactory.GetCompressionType(sourceStream);
|
||||
var compressionType = TarFactory.GetCompressionType(
|
||||
sourceStream,
|
||||
sourceStream.ReaderOptions.Providers
|
||||
);
|
||||
sourceStream.Seek(0, SeekOrigin.Begin);
|
||||
return new TarArchive(sourceStream, compressionType);
|
||||
}
|
||||
@@ -106,7 +112,11 @@ public partial class TarArchive
|
||||
readerOptions ?? new ReaderOptions()
|
||||
);
|
||||
var compressionType = await TarFactory
|
||||
.GetCompressionTypeAsync(sourceStream, cancellationToken)
|
||||
.GetCompressionTypeAsync(
|
||||
sourceStream,
|
||||
sourceStream.ReaderOptions.Providers,
|
||||
cancellationToken
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
sourceStream.Seek(0, SeekOrigin.Begin);
|
||||
return new TarArchive(sourceStream, compressionType);
|
||||
@@ -134,7 +144,11 @@ public partial class TarArchive
|
||||
readerOptions ??= new ReaderOptions() { LeaveStreamOpen = false };
|
||||
var sourceStream = new SourceStream(fileInfo, i => null, readerOptions);
|
||||
var compressionType = await TarFactory
|
||||
.GetCompressionTypeAsync(sourceStream, cancellationToken)
|
||||
.GetCompressionTypeAsync(
|
||||
sourceStream,
|
||||
sourceStream.ReaderOptions.Providers,
|
||||
cancellationToken
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
sourceStream.Seek(0, SeekOrigin.Begin);
|
||||
return new TarArchive(sourceStream, compressionType);
|
||||
@@ -155,7 +169,11 @@ public partial class TarArchive
|
||||
readerOptions ?? new ReaderOptions()
|
||||
);
|
||||
var compressionType = await TarFactory
|
||||
.GetCompressionTypeAsync(sourceStream, cancellationToken)
|
||||
.GetCompressionTypeAsync(
|
||||
sourceStream,
|
||||
sourceStream.ReaderOptions.Providers,
|
||||
cancellationToken
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
sourceStream.Seek(0, SeekOrigin.Begin);
|
||||
return new TarArchive(sourceStream, compressionType);
|
||||
@@ -176,7 +194,11 @@ public partial class TarArchive
|
||||
readerOptions ?? new ReaderOptions() { LeaveStreamOpen = false }
|
||||
);
|
||||
var compressionType = await TarFactory
|
||||
.GetCompressionTypeAsync(sourceStream, cancellationToken)
|
||||
.GetCompressionTypeAsync(
|
||||
sourceStream,
|
||||
sourceStream.ReaderOptions.Providers,
|
||||
cancellationToken
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
sourceStream.Seek(0, SeekOrigin.Begin);
|
||||
return new TarArchive(sourceStream, compressionType);
|
||||
|
||||
@@ -5,17 +5,10 @@ using System.Linq;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Common.Tar;
|
||||
using SharpCompress.Common.Tar.Headers;
|
||||
using SharpCompress.Compressors.BZip2;
|
||||
using SharpCompress.Compressors.Deflate;
|
||||
using SharpCompress.Compressors.LZMA;
|
||||
using SharpCompress.Compressors.Lzw;
|
||||
using SharpCompress.Compressors.Xz;
|
||||
using SharpCompress.Compressors.ZStandard;
|
||||
using SharpCompress.IO;
|
||||
using SharpCompress.Readers;
|
||||
using SharpCompress.Readers.Tar;
|
||||
using SharpCompress.Writers.Tar;
|
||||
using CompressionMode = SharpCompress.Compressors.CompressionMode;
|
||||
using Constants = SharpCompress.Common.Constants;
|
||||
|
||||
namespace SharpCompress.Archives.Tar;
|
||||
@@ -43,12 +36,30 @@ public partial class TarArchive
|
||||
private Stream GetStream(Stream stream) =>
|
||||
_compressionType switch
|
||||
{
|
||||
CompressionType.BZip2 => BZip2Stream.Create(stream, CompressionMode.Decompress, false),
|
||||
CompressionType.GZip => new GZipStream(stream, CompressionMode.Decompress),
|
||||
CompressionType.ZStandard => new ZStandardStream(stream),
|
||||
CompressionType.LZip => new LZipStream(stream, CompressionMode.Decompress),
|
||||
CompressionType.Xz => new XZStream(stream),
|
||||
CompressionType.Lzw => new LzwStream(stream),
|
||||
CompressionType.BZip2 => ReaderOptions.Providers.CreateDecompressStream(
|
||||
CompressionType.BZip2,
|
||||
stream
|
||||
),
|
||||
CompressionType.GZip => ReaderOptions.Providers.CreateDecompressStream(
|
||||
CompressionType.GZip,
|
||||
stream
|
||||
),
|
||||
CompressionType.ZStandard => ReaderOptions.Providers.CreateDecompressStream(
|
||||
CompressionType.ZStandard,
|
||||
stream
|
||||
),
|
||||
CompressionType.LZip => ReaderOptions.Providers.CreateDecompressStream(
|
||||
CompressionType.LZip,
|
||||
stream
|
||||
),
|
||||
CompressionType.Xz => ReaderOptions.Providers.CreateDecompressStream(
|
||||
CompressionType.Xz,
|
||||
stream
|
||||
),
|
||||
CompressionType.Lzw => ReaderOptions.Providers.CreateDecompressStream(
|
||||
CompressionType.Lzw,
|
||||
stream
|
||||
),
|
||||
CompressionType.None => stream,
|
||||
_ => throw new NotSupportedException("Invalid compression type: " + _compressionType),
|
||||
};
|
||||
@@ -83,7 +94,10 @@ public partial class TarArchive
|
||||
{
|
||||
var entry = new TarArchiveEntry(
|
||||
this,
|
||||
new TarFilePart(previousHeader, stream),
|
||||
new TarFilePart(
|
||||
previousHeader,
|
||||
_compressionType == CompressionType.None ? stream : null
|
||||
),
|
||||
CompressionType.None,
|
||||
ReaderOptions
|
||||
);
|
||||
@@ -106,7 +120,10 @@ public partial class TarArchive
|
||||
}
|
||||
yield return new TarArchiveEntry(
|
||||
this,
|
||||
new TarFilePart(header, stream),
|
||||
new TarFilePart(
|
||||
header,
|
||||
_compressionType == CompressionType.None ? stream : null
|
||||
),
|
||||
CompressionType.None,
|
||||
ReaderOptions
|
||||
);
|
||||
@@ -178,6 +195,6 @@ public partial class TarArchive
|
||||
{
|
||||
var stream = Volumes.Single().Stream;
|
||||
stream.Position = 0;
|
||||
return TarReader.OpenReader(GetStream(stream));
|
||||
return new TarReader(stream, ReaderOptions, _compressionType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,6 +176,6 @@ public partial class ZipArchive
|
||||
{
|
||||
var stream = Volumes.Single().Stream;
|
||||
stream.Position = 0;
|
||||
return new((IAsyncReader)ZipReader.OpenReader(stream));
|
||||
return new((IAsyncReader)ZipReader.OpenReader(stream, ReaderOptions, Entries));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,9 @@ public partial class LzwEntry
|
||||
)
|
||||
{
|
||||
yield return new LzwEntry(
|
||||
await LzwFilePart.CreateAsync(stream, options.ArchiveEncoding, cancellationToken),
|
||||
await LzwFilePart
|
||||
.CreateAsync(stream, options.ArchiveEncoding, options.Providers, cancellationToken)
|
||||
.ConfigureAwait(false),
|
||||
options
|
||||
);
|
||||
}
|
||||
|
||||
@@ -46,7 +46,10 @@ public partial class LzwEntry : Entry
|
||||
|
||||
internal static IEnumerable<LzwEntry> GetEntries(Stream stream, ReaderOptions options)
|
||||
{
|
||||
yield return new LzwEntry(LzwFilePart.Create(stream, options.ArchiveEncoding), options);
|
||||
yield return new LzwEntry(
|
||||
LzwFilePart.Create(stream, options.ArchiveEncoding, options.Providers),
|
||||
options
|
||||
);
|
||||
}
|
||||
|
||||
// Async methods moved to LzwEntry.Async.cs
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Providers;
|
||||
|
||||
namespace SharpCompress.Common.Lzw;
|
||||
|
||||
@@ -9,11 +10,12 @@ internal sealed partial class LzwFilePart
|
||||
internal static async ValueTask<LzwFilePart> CreateAsync(
|
||||
Stream stream,
|
||||
IArchiveEncoding archiveEncoding,
|
||||
CompressionProviderRegistry compressionProviders,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var part = new LzwFilePart(stream, archiveEncoding);
|
||||
var part = new LzwFilePart(stream, archiveEncoding, compressionProviders);
|
||||
|
||||
// For non-seekable streams, we can't track position, so use 0 since the stream will be
|
||||
// read sequentially from its current position.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.IO;
|
||||
using SharpCompress.Compressors.Lzw;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Providers;
|
||||
|
||||
namespace SharpCompress.Common.Lzw;
|
||||
|
||||
@@ -7,10 +8,15 @@ internal sealed partial class LzwFilePart : FilePart
|
||||
{
|
||||
private readonly Stream _stream;
|
||||
private readonly string? _name;
|
||||
private readonly CompressionProviderRegistry _compressionProviders;
|
||||
|
||||
internal static LzwFilePart Create(Stream stream, IArchiveEncoding archiveEncoding)
|
||||
internal static LzwFilePart Create(
|
||||
Stream stream,
|
||||
IArchiveEncoding archiveEncoding,
|
||||
CompressionProviderRegistry compressionProviders
|
||||
)
|
||||
{
|
||||
var part = new LzwFilePart(stream, archiveEncoding);
|
||||
var part = new LzwFilePart(stream, archiveEncoding, compressionProviders);
|
||||
|
||||
// For non-seekable streams, we can't track position, so use 0 since the stream will be
|
||||
// read sequentially from its current position.
|
||||
@@ -18,11 +24,16 @@ internal sealed partial class LzwFilePart : FilePart
|
||||
return part;
|
||||
}
|
||||
|
||||
private LzwFilePart(Stream stream, IArchiveEncoding archiveEncoding)
|
||||
private LzwFilePart(
|
||||
Stream stream,
|
||||
IArchiveEncoding archiveEncoding,
|
||||
CompressionProviderRegistry compressionProviders
|
||||
)
|
||||
: base(archiveEncoding)
|
||||
{
|
||||
_stream = stream;
|
||||
_name = DeriveFileName(stream);
|
||||
_compressionProviders = compressionProviders;
|
||||
}
|
||||
|
||||
internal long EntryStartPosition { get; private set; }
|
||||
@@ -30,7 +41,7 @@ internal sealed partial class LzwFilePart : FilePart
|
||||
internal override string? FilePartName => _name;
|
||||
|
||||
internal override Stream GetCompressedStream() =>
|
||||
new LzwStream(_stream) { IsStreamOwner = false };
|
||||
_compressionProviders.CreateDecompressStream(CompressionType.Lzw, _stream);
|
||||
|
||||
internal override Stream GetRawStream() => _stream;
|
||||
|
||||
|
||||
@@ -6,17 +6,8 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Common.Zip.Headers;
|
||||
using SharpCompress.Compressors;
|
||||
using SharpCompress.Compressors.BZip2;
|
||||
using SharpCompress.Compressors.Deflate;
|
||||
using SharpCompress.Compressors.Deflate64;
|
||||
using SharpCompress.Compressors.Explode;
|
||||
using SharpCompress.Compressors.LZMA;
|
||||
using SharpCompress.Compressors.PPMd;
|
||||
using SharpCompress.Compressors.Reduce;
|
||||
using SharpCompress.Compressors.Shrink;
|
||||
using SharpCompress.Compressors.Xz;
|
||||
using SharpCompress.Compressors.ZStandard;
|
||||
using SharpCompress.IO;
|
||||
using SharpCompress.Providers;
|
||||
|
||||
namespace SharpCompress.Common.Zip;
|
||||
|
||||
@@ -123,6 +114,7 @@ internal abstract partial class ZipFilePart
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
// Handle special cases first
|
||||
switch (method)
|
||||
{
|
||||
case ZipCompressionMethod.None:
|
||||
@@ -134,98 +126,24 @@ internal abstract partial class ZipFilePart
|
||||
|
||||
return stream;
|
||||
}
|
||||
case ZipCompressionMethod.Shrink:
|
||||
case ZipCompressionMethod.WinzipAes:
|
||||
{
|
||||
return await ShrinkStream
|
||||
.CreateAsync(
|
||||
stream,
|
||||
CompressionMode.Decompress,
|
||||
Header.CompressedSize,
|
||||
Header.UncompressedSize,
|
||||
cancellationToken
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
case ZipCompressionMethod.Reduce1:
|
||||
{
|
||||
return await ReduceStream
|
||||
.CreateAsync(
|
||||
stream,
|
||||
Header.CompressedSize,
|
||||
Header.UncompressedSize,
|
||||
1,
|
||||
cancellationToken
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
case ZipCompressionMethod.Reduce2:
|
||||
{
|
||||
return await ReduceStream
|
||||
.CreateAsync(
|
||||
stream,
|
||||
Header.CompressedSize,
|
||||
Header.UncompressedSize,
|
||||
2,
|
||||
cancellationToken
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
case ZipCompressionMethod.Reduce3:
|
||||
{
|
||||
return await ReduceStream
|
||||
.CreateAsync(
|
||||
stream,
|
||||
Header.CompressedSize,
|
||||
Header.UncompressedSize,
|
||||
3,
|
||||
cancellationToken
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
case ZipCompressionMethod.Reduce4:
|
||||
{
|
||||
return await ReduceStream
|
||||
.CreateAsync(
|
||||
stream,
|
||||
Header.CompressedSize,
|
||||
Header.UncompressedSize,
|
||||
4,
|
||||
cancellationToken
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
case ZipCompressionMethod.Explode:
|
||||
{
|
||||
return await ExplodeStream
|
||||
.CreateAsync(
|
||||
stream,
|
||||
Header.CompressedSize,
|
||||
Header.UncompressedSize,
|
||||
Header.Flags,
|
||||
cancellationToken
|
||||
)
|
||||
return await CreateWinzipAesDecompressionStreamAsync(stream, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
case ZipCompressionMethod.Deflate:
|
||||
{
|
||||
return new DeflateStream(stream, CompressionMode.Decompress);
|
||||
}
|
||||
case ZipCompressionMethod.Deflate64:
|
||||
{
|
||||
return new Deflate64Stream(stream, CompressionMode.Decompress);
|
||||
}
|
||||
case ZipCompressionMethod.BZip2:
|
||||
{
|
||||
return await BZip2Stream
|
||||
.CreateAsync(
|
||||
stream,
|
||||
CompressionMode.Decompress,
|
||||
false,
|
||||
cancellationToken: cancellationToken
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
var compressionType = ToCompressionType(method);
|
||||
var providers = GetProviders();
|
||||
var context = new CompressionContext
|
||||
{
|
||||
InputSize = Header.CompressedSize,
|
||||
OutputSize = Header.UncompressedSize,
|
||||
CanSeek = stream.CanSeek,
|
||||
};
|
||||
|
||||
switch (method)
|
||||
{
|
||||
case ZipCompressionMethod.LZMA:
|
||||
{
|
||||
if (FlagUtility.HasFlag(Header.Flags, HeaderFlags.Encrypted))
|
||||
@@ -234,81 +152,80 @@ internal abstract partial class ZipFilePart
|
||||
}
|
||||
var buffer = new byte[4];
|
||||
await stream.ReadFullyAsync(buffer, 0, 4, cancellationToken).ConfigureAwait(false);
|
||||
var version = BinaryPrimitives.ReadUInt16LittleEndian(buffer.AsSpan(0, 2));
|
||||
var propsSize = BinaryPrimitives.ReadUInt16LittleEndian(buffer.AsSpan(2, 2));
|
||||
var props = new byte[propsSize];
|
||||
await stream
|
||||
.ReadFullyAsync(props, 0, propsSize, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
return await LzmaStream
|
||||
.CreateAsync(
|
||||
props,
|
||||
stream,
|
||||
|
||||
context = context with
|
||||
{
|
||||
Properties = props,
|
||||
InputSize =
|
||||
Header.CompressedSize > 0 ? Header.CompressedSize - 4 - props.Length : -1,
|
||||
FlagUtility.HasFlag(Header.Flags, HeaderFlags.Bit1)
|
||||
? -1
|
||||
: Header.UncompressedSize
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
case ZipCompressionMethod.Xz:
|
||||
{
|
||||
return new XZStream(stream);
|
||||
}
|
||||
case ZipCompressionMethod.ZStandard:
|
||||
{
|
||||
return new DecompressionStream(stream);
|
||||
OutputSize = FlagUtility.HasFlag(Header.Flags, HeaderFlags.Bit1)
|
||||
? -1
|
||||
: Header.UncompressedSize,
|
||||
};
|
||||
|
||||
return providers.CreateDecompressStream(compressionType, stream, context);
|
||||
}
|
||||
case ZipCompressionMethod.PPMd:
|
||||
{
|
||||
var props = new byte[2];
|
||||
await stream.ReadFullyAsync(props, 0, 2, cancellationToken).ConfigureAwait(false);
|
||||
return await PpmdStream
|
||||
.CreateAsync(new PpmdProperties(props), stream, false, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
context = context with { Properties = props };
|
||||
return providers.CreateDecompressStream(compressionType, stream, context);
|
||||
}
|
||||
case ZipCompressionMethod.WinzipAes:
|
||||
case ZipCompressionMethod.Explode:
|
||||
{
|
||||
var data = Header.Extra.SingleOrDefault(x => x.Type == ExtraDataType.WinZipAes);
|
||||
if (data is null)
|
||||
{
|
||||
throw new InvalidFormatException("No Winzip AES extra data found.");
|
||||
}
|
||||
|
||||
if (data.Length != 7)
|
||||
{
|
||||
throw new InvalidFormatException("Winzip data length is not 7.");
|
||||
}
|
||||
|
||||
var compressedMethod = BinaryPrimitives.ReadUInt16LittleEndian(data.DataBytes);
|
||||
|
||||
if (compressedMethod != 0x01 && compressedMethod != 0x02)
|
||||
{
|
||||
throw new InvalidFormatException(
|
||||
"Unexpected vendor version number for WinZip AES metadata"
|
||||
);
|
||||
}
|
||||
|
||||
var vendorId = BinaryPrimitives.ReadUInt16LittleEndian(data.DataBytes.AsSpan(2));
|
||||
if (vendorId != 0x4541)
|
||||
{
|
||||
throw new InvalidFormatException(
|
||||
"Unexpected vendor ID for WinZip AES metadata"
|
||||
);
|
||||
}
|
||||
|
||||
return await CreateDecompressionStreamAsync(
|
||||
stream,
|
||||
(ZipCompressionMethod)
|
||||
BinaryPrimitives.ReadUInt16LittleEndian(data.DataBytes.AsSpan(5)),
|
||||
cancellationToken
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
context = context with { FormatOptions = Header.Flags };
|
||||
return providers.CreateDecompressStream(compressionType, stream, context);
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new NotSupportedException("CompressionMethod: " + Header.CompressionMethod);
|
||||
return providers.CreateDecompressStream(compressionType, stream, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async ValueTask<Stream> CreateWinzipAesDecompressionStreamAsync(
|
||||
Stream stream,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
var data = Header.Extra.SingleOrDefault(x => x.Type == ExtraDataType.WinZipAes);
|
||||
if (data is null)
|
||||
{
|
||||
throw new InvalidFormatException("No Winzip AES extra data found.");
|
||||
}
|
||||
|
||||
if (data.Length != 7)
|
||||
{
|
||||
throw new InvalidFormatException("Winzip data length is not 7.");
|
||||
}
|
||||
|
||||
var compressedMethod = BinaryPrimitives.ReadUInt16LittleEndian(data.DataBytes);
|
||||
|
||||
if (compressedMethod != 0x01 && compressedMethod != 0x02)
|
||||
{
|
||||
throw new InvalidFormatException(
|
||||
"Unexpected vendor version number for WinZip AES metadata"
|
||||
);
|
||||
}
|
||||
|
||||
var vendorId = BinaryPrimitives.ReadUInt16LittleEndian(data.DataBytes.AsSpan(2));
|
||||
if (vendorId != 0x4541)
|
||||
{
|
||||
throw new InvalidFormatException("Unexpected vendor ID for WinZip AES metadata");
|
||||
}
|
||||
|
||||
return await CreateDecompressionStreamAsync(
|
||||
stream,
|
||||
(ZipCompressionMethod)
|
||||
BinaryPrimitives.ReadUInt16LittleEndian(data.DataBytes.AsSpan(5)),
|
||||
cancellationToken
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,4 +96,28 @@ public abstract class Factory : IFactory
|
||||
stream.Rewind();
|
||||
return false;
|
||||
}
|
||||
|
||||
internal virtual async ValueTask<IAsyncReader?> TryOpenReaderAsync(
|
||||
SharpCompressStream stream,
|
||||
ReaderOptions options,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
if (this is IReaderFactory readerFactory)
|
||||
{
|
||||
stream.Rewind();
|
||||
if (
|
||||
await IsArchiveAsync(stream, options.Password, cancellationToken)
|
||||
.ConfigureAwait(false)
|
||||
)
|
||||
{
|
||||
stream.Rewind(true);
|
||||
return await readerFactory
|
||||
.OpenAsyncReader(stream, options, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
stream.Rewind();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Archives;
|
||||
@@ -128,7 +127,10 @@ public class GZipFactory
|
||||
if (GZipArchive.IsGZipFile(sharpCompressStream))
|
||||
{
|
||||
sharpCompressStream.Rewind();
|
||||
var testStream = new GZipStream(sharpCompressStream, CompressionMode.Decompress);
|
||||
using var testStream = options.Providers.CreateDecompressStream(
|
||||
CompressionType.GZip,
|
||||
SharpCompressStream.CreateNonDisposing(sharpCompressStream)
|
||||
);
|
||||
if (TarArchive.IsTarFile(testStream))
|
||||
{
|
||||
sharpCompressStream.StopRecording();
|
||||
|
||||
@@ -58,7 +58,12 @@ public class LzwFactory : Factory, IReaderFactory
|
||||
if (LzwStream.IsLzwStream(sharpCompressStream))
|
||||
{
|
||||
sharpCompressStream.Rewind();
|
||||
using (var testStream = new LzwStream(sharpCompressStream) { IsStreamOwner = false })
|
||||
using (
|
||||
var testStream = options.Providers.CreateDecompressStream(
|
||||
CompressionType.Lzw,
|
||||
SharpCompressStream.CreateNonDisposing(sharpCompressStream)
|
||||
)
|
||||
)
|
||||
{
|
||||
if (TarArchive.IsTarFile(testStream))
|
||||
{
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@@ -9,6 +8,7 @@ using SharpCompress.Archives.Tar;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Common.Options;
|
||||
using SharpCompress.IO;
|
||||
using SharpCompress.Providers;
|
||||
using SharpCompress.Readers;
|
||||
using SharpCompress.Readers.Tar;
|
||||
using SharpCompress.Writers;
|
||||
@@ -50,6 +50,7 @@ public class TarFactory
|
||||
/// <inheritdoc/>
|
||||
public override bool IsArchive(Stream stream, string? password = null)
|
||||
{
|
||||
var providers = CompressionProviderRegistry.Default;
|
||||
var sharpCompressStream = new SharpCompressStream(stream);
|
||||
sharpCompressStream.StartRecording();
|
||||
foreach (var wrapper in TarWrapper.Wrappers)
|
||||
@@ -58,7 +59,11 @@ public class TarFactory
|
||||
if (wrapper.IsMatch(sharpCompressStream))
|
||||
{
|
||||
sharpCompressStream.Rewind();
|
||||
var decompressedStream = wrapper.CreateStream(sharpCompressStream);
|
||||
var decompressedStream = CreateProbeDecompressionStream(
|
||||
sharpCompressStream,
|
||||
wrapper.CompressionType,
|
||||
providers
|
||||
);
|
||||
if (TarArchive.IsTarFile(decompressedStream))
|
||||
{
|
||||
sharpCompressStream.Rewind();
|
||||
@@ -77,6 +82,7 @@ public class TarFactory
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
var providers = CompressionProviderRegistry.Default;
|
||||
var sharpCompressStream = new SharpCompressStream(stream);
|
||||
sharpCompressStream.StartRecording();
|
||||
foreach (var wrapper in TarWrapper.Wrappers)
|
||||
@@ -89,9 +95,11 @@ public class TarFactory
|
||||
)
|
||||
{
|
||||
sharpCompressStream.Rewind();
|
||||
var decompressedStream = await wrapper
|
||||
.CreateStreamAsync(sharpCompressStream, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
var decompressedStream = CreateProbeDecompressionStream(
|
||||
sharpCompressStream,
|
||||
wrapper.CompressionType,
|
||||
providers
|
||||
);
|
||||
if (
|
||||
await TarArchive
|
||||
.IsTarFileAsync(decompressedStream, cancellationToken)
|
||||
@@ -109,8 +117,24 @@ public class TarFactory
|
||||
|
||||
#endregion
|
||||
|
||||
public static CompressionType GetCompressionType(Stream stream)
|
||||
private static Stream CreateProbeDecompressionStream(
|
||||
Stream stream,
|
||||
CompressionType compressionType,
|
||||
CompressionProviderRegistry providers
|
||||
)
|
||||
{
|
||||
var nonDisposingStream = SharpCompressStream.CreateNonDisposing(stream);
|
||||
return compressionType == CompressionType.None
|
||||
? nonDisposingStream
|
||||
: providers.CreateDecompressStream(compressionType, nonDisposingStream);
|
||||
}
|
||||
|
||||
public static CompressionType GetCompressionType(
|
||||
Stream stream,
|
||||
CompressionProviderRegistry? providers = null
|
||||
)
|
||||
{
|
||||
providers ??= CompressionProviderRegistry.Default;
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
foreach (var wrapper in TarWrapper.Wrappers)
|
||||
{
|
||||
@@ -118,7 +142,11 @@ public class TarFactory
|
||||
if (wrapper.IsMatch(stream))
|
||||
{
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
var decompressedStream = wrapper.CreateStream(stream);
|
||||
var decompressedStream = CreateProbeDecompressionStream(
|
||||
stream,
|
||||
wrapper.CompressionType,
|
||||
providers
|
||||
);
|
||||
if (TarArchive.IsTarFile(decompressedStream))
|
||||
{
|
||||
return wrapper.CompressionType;
|
||||
@@ -130,17 +158,23 @@ public class TarFactory
|
||||
|
||||
public static async ValueTask<CompressionType> GetCompressionTypeAsync(
|
||||
Stream stream,
|
||||
CompressionProviderRegistry? providers = null,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
providers ??= CompressionProviderRegistry.Default;
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
foreach (var wrapper in TarWrapper.Wrappers)
|
||||
{
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
if (wrapper.IsMatch(stream))
|
||||
if (await wrapper.IsMatchAsync(stream, cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
var decompressedStream = wrapper.CreateStream(stream);
|
||||
var decompressedStream = CreateProbeDecompressionStream(
|
||||
stream,
|
||||
wrapper.CompressionType,
|
||||
providers
|
||||
);
|
||||
if (
|
||||
await TarArchive
|
||||
.IsTarFileAsync(decompressedStream, cancellationToken)
|
||||
@@ -228,7 +262,11 @@ public class TarFactory
|
||||
if (wrapper.IsMatch(sharpCompressStream))
|
||||
{
|
||||
sharpCompressStream.Rewind();
|
||||
var decompressedStream = wrapper.CreateStream(sharpCompressStream);
|
||||
var decompressedStream = CreateProbeDecompressionStream(
|
||||
sharpCompressStream,
|
||||
wrapper.CompressionType,
|
||||
options.Providers
|
||||
);
|
||||
if (TarArchive.IsTarFile(decompressedStream))
|
||||
{
|
||||
sharpCompressStream.StopRecording();
|
||||
@@ -260,9 +298,11 @@ public class TarFactory
|
||||
)
|
||||
{
|
||||
sharpCompressStream.Rewind();
|
||||
var decompressedStream = await wrapper
|
||||
.CreateStreamAsync(sharpCompressStream, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
var decompressedStream = CreateProbeDecompressionStream(
|
||||
sharpCompressStream,
|
||||
wrapper.CompressionType,
|
||||
options.Providers
|
||||
);
|
||||
if (
|
||||
await TarArchive
|
||||
.IsTarFileAsync(decompressedStream, cancellationToken)
|
||||
@@ -275,7 +315,9 @@ public class TarFactory
|
||||
}
|
||||
}
|
||||
}
|
||||
return (IAsyncReader)TarReader.OpenReader(stream, options);
|
||||
|
||||
sharpCompressStream.Rewind();
|
||||
return (IAsyncReader)TarReader.OpenReader(sharpCompressStream, options);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -38,7 +38,9 @@ public sealed class CompressionProviderRegistry
|
||||
|
||||
private readonly Dictionary<CompressionType, ICompressionProvider> _providers;
|
||||
|
||||
private CompressionProviderRegistry(Dictionary<CompressionType, ICompressionProvider> providers) => _providers = providers;
|
||||
private CompressionProviderRegistry(
|
||||
Dictionary<CompressionType, ICompressionProvider> providers
|
||||
) => _providers = providers;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the provider for a given compression type, or null if none is registered.
|
||||
|
||||
@@ -20,7 +20,7 @@ public sealed class SystemDeflateCompressionProvider : ICompressionProvider
|
||||
public Stream CreateCompressStream(Stream destination, int compressionLevel)
|
||||
{
|
||||
var bclLevel = MapCompressionLevel(compressionLevel);
|
||||
return new DeflateStream(destination, bclLevel, leaveOpen: true);
|
||||
return new DeflateStream(destination, bclLevel, leaveOpen: false);
|
||||
}
|
||||
|
||||
public Stream CreateCompressStream(
|
||||
@@ -38,7 +38,7 @@ public sealed class SystemDeflateCompressionProvider : ICompressionProvider
|
||||
return new DeflateStream(
|
||||
source,
|
||||
global::System.IO.Compression.CompressionMode.Decompress,
|
||||
leaveOpen: true
|
||||
leaveOpen: false
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ public sealed class SystemGZipCompressionProvider : ICompressionProvider
|
||||
public Stream CreateCompressStream(Stream destination, int compressionLevel)
|
||||
{
|
||||
var bclLevel = MapCompressionLevel(compressionLevel);
|
||||
return new GZipStream(destination, bclLevel, leaveOpen: true);
|
||||
return new GZipStream(destination, bclLevel, leaveOpen: false);
|
||||
}
|
||||
|
||||
public Stream CreateCompressStream(
|
||||
@@ -38,7 +38,7 @@ public sealed class SystemGZipCompressionProvider : ICompressionProvider
|
||||
return new GZipStream(
|
||||
source,
|
||||
global::System.IO.Compression.CompressionMode.Decompress,
|
||||
leaveOpen: true
|
||||
leaveOpen: false
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -69,20 +69,14 @@ public static partial class ReaderFactory
|
||||
a.GetSupportedExtensions()
|
||||
.Contains(options.ExtensionHint, StringComparer.CurrentCultureIgnoreCase)
|
||||
);
|
||||
if (testedFactory is IReaderFactory readerFactory)
|
||||
if (testedFactory is not null)
|
||||
{
|
||||
sharpCompressStream.Rewind();
|
||||
if (
|
||||
await testedFactory
|
||||
.IsArchiveAsync(sharpCompressStream, cancellationToken: cancellationToken)
|
||||
.ConfigureAwait(false)
|
||||
)
|
||||
var reader = await testedFactory
|
||||
.TryOpenReaderAsync(sharpCompressStream, options, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
if (reader is not null)
|
||||
{
|
||||
sharpCompressStream.Rewind();
|
||||
sharpCompressStream.StopRecording();
|
||||
return await readerFactory
|
||||
.OpenAsyncReader(sharpCompressStream, options, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
return reader;
|
||||
}
|
||||
}
|
||||
sharpCompressStream.Rewind();
|
||||
@@ -94,19 +88,12 @@ public static partial class ReaderFactory
|
||||
{
|
||||
continue; // Already tested above
|
||||
}
|
||||
sharpCompressStream.Rewind();
|
||||
if (
|
||||
factory is IReaderFactory readerFactory
|
||||
&& await factory
|
||||
.IsArchiveAsync(sharpCompressStream, cancellationToken: cancellationToken)
|
||||
.ConfigureAwait(false)
|
||||
)
|
||||
var reader = await factory
|
||||
.TryOpenReaderAsync(sharpCompressStream, options, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
if (reader is not null)
|
||||
{
|
||||
sharpCompressStream.Rewind();
|
||||
sharpCompressStream.StopRecording();
|
||||
return await readerFactory
|
||||
.OpenAsyncReader(sharpCompressStream, options, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
return reader;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Archives.GZip;
|
||||
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.ZStandard;
|
||||
using SharpCompress.Factories;
|
||||
using SharpCompress.IO;
|
||||
using SharpCompress.Providers;
|
||||
|
||||
namespace SharpCompress.Readers.Tar;
|
||||
|
||||
@@ -18,6 +14,18 @@ public partial class TarReader
|
||||
: IReaderOpenable
|
||||
#endif
|
||||
{
|
||||
private static Stream CreateProbeDecompressionStream(
|
||||
Stream stream,
|
||||
CompressionType compressionType,
|
||||
CompressionProviderRegistry providers
|
||||
)
|
||||
{
|
||||
var nonDisposingStream = SharpCompressStream.CreateNonDisposing(stream);
|
||||
return compressionType == CompressionType.None
|
||||
? nonDisposingStream
|
||||
: providers.CreateDecompressStream(compressionType, nonDisposingStream);
|
||||
}
|
||||
|
||||
public static ValueTask<IAsyncReader> OpenAsyncReader(
|
||||
string path,
|
||||
ReaderOptions? readerOptions = null,
|
||||
@@ -42,81 +50,38 @@ public partial class TarReader
|
||||
bufferSize: options.RewindableBufferSize
|
||||
);
|
||||
long pos = sharpCompressStream.Position;
|
||||
if (
|
||||
await GZipArchive
|
||||
.IsGZipFileAsync(sharpCompressStream, cancellationToken)
|
||||
.ConfigureAwait(false)
|
||||
)
|
||||
foreach (var wrapper in TarWrapper.Wrappers)
|
||||
{
|
||||
sharpCompressStream.Position = pos;
|
||||
var testStream = new GZipStream(sharpCompressStream, CompressionMode.Decompress);
|
||||
if (
|
||||
await TarArchive.IsTarFileAsync(testStream, cancellationToken).ConfigureAwait(false)
|
||||
!await wrapper
|
||||
.IsMatchAsync(sharpCompressStream, cancellationToken)
|
||||
.ConfigureAwait(false)
|
||||
)
|
||||
{
|
||||
sharpCompressStream.Position = pos;
|
||||
return new TarReader(sharpCompressStream, options, CompressionType.GZip);
|
||||
continue;
|
||||
}
|
||||
throw new InvalidFormatException("Not a tar file.");
|
||||
}
|
||||
sharpCompressStream.Position = pos;
|
||||
if (
|
||||
await BZip2Stream
|
||||
.IsBZip2Async(sharpCompressStream, cancellationToken)
|
||||
.ConfigureAwait(false)
|
||||
)
|
||||
{
|
||||
|
||||
sharpCompressStream.Position = pos;
|
||||
var testStream = BZip2Stream.Create(
|
||||
var testStream = CreateProbeDecompressionStream(
|
||||
sharpCompressStream,
|
||||
CompressionMode.Decompress,
|
||||
false
|
||||
wrapper.CompressionType,
|
||||
options.Providers
|
||||
);
|
||||
if (
|
||||
await TarArchive.IsTarFileAsync(testStream, cancellationToken).ConfigureAwait(false)
|
||||
)
|
||||
{
|
||||
sharpCompressStream.Position = pos;
|
||||
return new TarReader(sharpCompressStream, options, CompressionType.BZip2);
|
||||
return new TarReader(sharpCompressStream, options, wrapper.CompressionType);
|
||||
}
|
||||
throw new InvalidFormatException("Not a tar file.");
|
||||
}
|
||||
sharpCompressStream.Position = pos;
|
||||
if (
|
||||
await ZStandardStream
|
||||
.IsZStandardAsync(sharpCompressStream, cancellationToken)
|
||||
.ConfigureAwait(false)
|
||||
)
|
||||
{
|
||||
sharpCompressStream.Position = pos;
|
||||
var testStream = new ZStandardStream(sharpCompressStream);
|
||||
if (
|
||||
await TarArchive.IsTarFileAsync(testStream, cancellationToken).ConfigureAwait(false)
|
||||
)
|
||||
|
||||
if (wrapper.CompressionType != CompressionType.None)
|
||||
{
|
||||
sharpCompressStream.Position = pos;
|
||||
return new TarReader(sharpCompressStream, options, CompressionType.ZStandard);
|
||||
throw new InvalidFormatException("Not a tar file.");
|
||||
}
|
||||
throw new InvalidFormatException("Not a tar file.");
|
||||
}
|
||||
sharpCompressStream.Position = pos;
|
||||
if (
|
||||
await LZipStream
|
||||
.IsLZipFileAsync(sharpCompressStream, cancellationToken)
|
||||
.ConfigureAwait(false)
|
||||
)
|
||||
{
|
||||
sharpCompressStream.Position = pos;
|
||||
var testStream = new LZipStream(sharpCompressStream, CompressionMode.Decompress);
|
||||
if (
|
||||
await TarArchive.IsTarFileAsync(testStream, cancellationToken).ConfigureAwait(false)
|
||||
)
|
||||
{
|
||||
sharpCompressStream.Position = pos;
|
||||
return new TarReader(sharpCompressStream, options, CompressionType.LZip);
|
||||
}
|
||||
throw new InvalidFormatException("Not a tar file.");
|
||||
}
|
||||
|
||||
sharpCompressStream.Position = pos;
|
||||
return new TarReader(sharpCompressStream, options, CompressionType.None);
|
||||
}
|
||||
@@ -159,57 +124,32 @@ public partial class TarReader
|
||||
bufferSize: options.RewindableBufferSize
|
||||
);
|
||||
long pos = sharpCompressStream.Position;
|
||||
if (GZipArchive.IsGZipFile(sharpCompressStream))
|
||||
foreach (var wrapper in TarWrapper.Wrappers)
|
||||
{
|
||||
sharpCompressStream.Position = pos;
|
||||
var testStream = new GZipStream(sharpCompressStream, CompressionMode.Decompress);
|
||||
if (TarArchive.IsTarFile(testStream))
|
||||
if (!wrapper.IsMatch(sharpCompressStream))
|
||||
{
|
||||
sharpCompressStream.Position = pos;
|
||||
return new TarReader(sharpCompressStream, options, CompressionType.GZip);
|
||||
continue;
|
||||
}
|
||||
throw new InvalidFormatException("Not a tar file.");
|
||||
}
|
||||
sharpCompressStream.Position = pos;
|
||||
if (BZip2Stream.IsBZip2(sharpCompressStream))
|
||||
{
|
||||
|
||||
sharpCompressStream.Position = pos;
|
||||
var testStream = BZip2Stream.Create(
|
||||
var testStream = CreateProbeDecompressionStream(
|
||||
sharpCompressStream,
|
||||
CompressionMode.Decompress,
|
||||
false
|
||||
wrapper.CompressionType,
|
||||
options.Providers
|
||||
);
|
||||
if (TarArchive.IsTarFile(testStream))
|
||||
{
|
||||
sharpCompressStream.Position = pos;
|
||||
return new TarReader(sharpCompressStream, options, CompressionType.BZip2);
|
||||
return new TarReader(sharpCompressStream, options, wrapper.CompressionType);
|
||||
}
|
||||
throw new InvalidFormatException("Not a tar file.");
|
||||
}
|
||||
sharpCompressStream.Position = pos;
|
||||
if (ZStandardStream.IsZStandard(sharpCompressStream))
|
||||
{
|
||||
sharpCompressStream.Position = pos;
|
||||
var testStream = new ZStandardStream(sharpCompressStream);
|
||||
if (TarArchive.IsTarFile(testStream))
|
||||
|
||||
if (wrapper.CompressionType != CompressionType.None)
|
||||
{
|
||||
sharpCompressStream.Position = pos;
|
||||
return new TarReader(sharpCompressStream, options, CompressionType.ZStandard);
|
||||
throw new InvalidFormatException("Not a tar file.");
|
||||
}
|
||||
throw new InvalidFormatException("Not a tar file.");
|
||||
}
|
||||
sharpCompressStream.Position = pos;
|
||||
if (LZipStream.IsLZipFile(sharpCompressStream))
|
||||
{
|
||||
sharpCompressStream.Position = pos;
|
||||
var testStream = new LZipStream(sharpCompressStream, CompressionMode.Decompress);
|
||||
if (TarArchive.IsTarFile(testStream))
|
||||
{
|
||||
sharpCompressStream.Position = pos;
|
||||
return new TarReader(sharpCompressStream, options, CompressionType.LZip);
|
||||
}
|
||||
throw new InvalidFormatException("Not a tar file.");
|
||||
}
|
||||
|
||||
sharpCompressStream.Position = pos;
|
||||
return new TarReader(sharpCompressStream, options, CompressionType.None);
|
||||
}
|
||||
|
||||
@@ -379,6 +379,8 @@ public partial class ZipWriter : AbstractWriter
|
||||
private readonly ZipWriter writer;
|
||||
private readonly ZipCompressionMethod zipCompressionMethod;
|
||||
private readonly int compressionLevel;
|
||||
private ICompressionProviderHooks? compressionProviderHooks;
|
||||
private CompressionContext? compressionContext;
|
||||
private CountingStream? counting;
|
||||
private ulong decompressed;
|
||||
|
||||
@@ -458,6 +460,8 @@ public partial class ZipWriter : AbstractWriter
|
||||
}
|
||||
|
||||
var context = new CompressionContext { CanSeek = originalStream.CanSeek };
|
||||
compressionProviderHooks = compressingProvider;
|
||||
compressionContext = context;
|
||||
|
||||
// Write pre-compression data (magic bytes)
|
||||
var preData = compressingProvider.GetPreCompressionData(context);
|
||||
@@ -498,6 +502,8 @@ public partial class ZipWriter : AbstractWriter
|
||||
CanSeek = originalStream.CanSeek,
|
||||
FormatOptions = writer.PpmdProperties,
|
||||
};
|
||||
compressionProviderHooks = compressingProvider;
|
||||
compressionContext = context;
|
||||
|
||||
// Write pre-compression data (properties)
|
||||
var preData = compressingProvider.GetPreCompressionData(context);
|
||||
@@ -551,6 +557,8 @@ public partial class ZipWriter : AbstractWriter
|
||||
return;
|
||||
}
|
||||
|
||||
WritePostCompressionData();
|
||||
|
||||
var countingCount = counting?.BytesWritten ?? 0;
|
||||
entry.Crc = (uint)crc.Crc32Result;
|
||||
entry.Compressed = (ulong)countingCount;
|
||||
@@ -688,6 +696,30 @@ public partial class ZipWriter : AbstractWriter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void WritePostCompressionData()
|
||||
{
|
||||
if (
|
||||
compressionProviderHooks is null
|
||||
|| compressionContext is null
|
||||
|| counting is null
|
||||
|| zipCompressionMethod == ZipCompressionMethod.None
|
||||
)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var postData = compressionProviderHooks.GetPostCompressionData(
|
||||
writeStream,
|
||||
compressionContext
|
||||
);
|
||||
if (postData is null || postData.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
counting.Write(postData, 0, postData.Length);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Nested type: ZipWritingStream
|
||||
|
||||
@@ -7,6 +7,7 @@ using SharpCompress.Archives.Tar;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Compressors;
|
||||
using SharpCompress.Providers;
|
||||
using SharpCompress.Providers.System;
|
||||
using SharpCompress.Readers;
|
||||
using SharpCompress.Writers;
|
||||
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using AwesomeAssertions;
|
||||
using SharpCompress.Archives.Tar;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Compressors;
|
||||
using SharpCompress.IO;
|
||||
@@ -18,6 +21,102 @@ namespace SharpCompress.Test;
|
||||
|
||||
public class CompressionProviderTests
|
||||
{
|
||||
private sealed class TrackingCompressionProvider : ICompressionProvider
|
||||
{
|
||||
private readonly ICompressionProvider _inner;
|
||||
|
||||
public TrackingCompressionProvider(ICompressionProvider inner)
|
||||
{
|
||||
_inner = inner;
|
||||
}
|
||||
|
||||
public int CompressionCalls { get; private set; }
|
||||
|
||||
public int DecompressionCalls { get; private set; }
|
||||
|
||||
public CompressionType CompressionType => _inner.CompressionType;
|
||||
|
||||
public bool SupportsCompression => _inner.SupportsCompression;
|
||||
|
||||
public bool SupportsDecompression => _inner.SupportsDecompression;
|
||||
|
||||
public Stream CreateCompressStream(Stream destination, int compressionLevel)
|
||||
{
|
||||
CompressionCalls++;
|
||||
return _inner.CreateCompressStream(destination, compressionLevel);
|
||||
}
|
||||
|
||||
public Stream CreateCompressStream(
|
||||
Stream destination,
|
||||
int compressionLevel,
|
||||
CompressionContext context
|
||||
)
|
||||
{
|
||||
CompressionCalls++;
|
||||
return _inner.CreateCompressStream(destination, compressionLevel, context);
|
||||
}
|
||||
|
||||
public Stream CreateDecompressStream(Stream source)
|
||||
{
|
||||
DecompressionCalls++;
|
||||
return _inner.CreateDecompressStream(source);
|
||||
}
|
||||
|
||||
public Stream CreateDecompressStream(Stream source, CompressionContext context)
|
||||
{
|
||||
DecompressionCalls++;
|
||||
return _inner.CreateDecompressStream(source, context);
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class TrackingLzmaHooksProvider : ICompressionProviderHooks
|
||||
{
|
||||
public int PreCalls { get; private set; }
|
||||
public int PropertiesCalls { get; private set; }
|
||||
public int PostCalls { get; private set; }
|
||||
|
||||
public CompressionType CompressionType => CompressionType.LZMA;
|
||||
|
||||
public bool SupportsCompression => true;
|
||||
|
||||
public bool SupportsDecompression => false;
|
||||
|
||||
public Stream CreateCompressStream(Stream destination, int compressionLevel)
|
||||
{
|
||||
CompressionContext context = new() { CanSeek = destination.CanSeek };
|
||||
return CreateCompressStream(destination, compressionLevel, context);
|
||||
}
|
||||
|
||||
public Stream CreateCompressStream(
|
||||
Stream destination,
|
||||
int compressionLevel,
|
||||
CompressionContext context
|
||||
) => SharpCompressStream.CreateNonDisposing(destination);
|
||||
|
||||
public Stream CreateDecompressStream(Stream source) => throw new NotSupportedException();
|
||||
|
||||
public Stream CreateDecompressStream(Stream source, CompressionContext context) =>
|
||||
throw new NotSupportedException();
|
||||
|
||||
public byte[]? GetPreCompressionData(CompressionContext context)
|
||||
{
|
||||
PreCalls++;
|
||||
return [];
|
||||
}
|
||||
|
||||
public byte[]? GetCompressionProperties(Stream stream, CompressionContext context)
|
||||
{
|
||||
PropertiesCalls++;
|
||||
return [];
|
||||
}
|
||||
|
||||
public byte[]? GetPostCompressionData(Stream stream, CompressionContext context)
|
||||
{
|
||||
PostCalls++;
|
||||
return [1, 2, 3];
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CompressionProviderRegistry_Default_ReturnsInternalProviders()
|
||||
{
|
||||
@@ -52,7 +151,8 @@ public class CompressionProviderTests
|
||||
|
||||
// Original should still have the default provider
|
||||
var originalProvider = original.GetProvider(CompressionType.Deflate);
|
||||
originalProvider.Should().NotBeSameAs(modified);
|
||||
var modifiedProvider = modified.GetProvider(CompressionType.Deflate);
|
||||
originalProvider.Should().NotBeSameAs(modifiedProvider);
|
||||
originalProvider.Should().NotBeSameAs(customProvider);
|
||||
}
|
||||
|
||||
@@ -219,6 +319,146 @@ public class CompressionProviderTests
|
||||
clone.LeaveStreamOpen.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TarArchive_OpenArchive_UsesCustomGZipProvider()
|
||||
{
|
||||
using var archiveStream = new MemoryStream();
|
||||
using (
|
||||
var writer = new TarWriter(
|
||||
archiveStream,
|
||||
new TarWriterOptions(CompressionType.GZip, true)
|
||||
)
|
||||
)
|
||||
{
|
||||
var data = Encoding.UTF8.GetBytes("tar archive provider usage");
|
||||
writer.Write("test.txt", new MemoryStream(data), DateTime.Now);
|
||||
}
|
||||
|
||||
var trackingProvider = new TrackingCompressionProvider(new GZipCompressionProvider());
|
||||
var registry = CompressionProviderRegistry.Default.With(trackingProvider);
|
||||
var readOptions = new ReaderOptions { Providers = registry };
|
||||
|
||||
archiveStream.Position = 0;
|
||||
using var archive = TarArchive.OpenArchive(archiveStream, readOptions);
|
||||
var entry = archive.Entries.First(x => !x.IsDirectory);
|
||||
using var entryStream = entry.OpenEntryStream();
|
||||
using var resultStream = new MemoryStream();
|
||||
entryStream.CopyTo(resultStream);
|
||||
|
||||
trackingProvider.DecompressionCalls.Should().BeGreaterThan(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TarArchive_OpenAsyncArchive_UsesCustomGZipProvider()
|
||||
{
|
||||
using var archiveStream = new MemoryStream();
|
||||
using (
|
||||
var writer = new TarWriter(
|
||||
archiveStream,
|
||||
new TarWriterOptions(CompressionType.GZip, true)
|
||||
)
|
||||
)
|
||||
{
|
||||
var data = Encoding.UTF8.GetBytes("tar async archive provider usage");
|
||||
writer.Write("test.txt", new MemoryStream(data), DateTime.Now);
|
||||
}
|
||||
|
||||
var trackingProvider = new TrackingCompressionProvider(new GZipCompressionProvider());
|
||||
var registry = CompressionProviderRegistry.Default.With(trackingProvider);
|
||||
var readOptions = new ReaderOptions { Providers = registry };
|
||||
|
||||
archiveStream.Position = 0;
|
||||
await using var archive = await TarArchive.OpenAsyncArchive(archiveStream, readOptions);
|
||||
await foreach (var entry in archive.EntriesAsync)
|
||||
{
|
||||
if (entry.IsDirectory)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
using var entryStream = await entry.OpenEntryStreamAsync();
|
||||
using var resultStream = new MemoryStream();
|
||||
await entryStream.CopyToAsync(resultStream);
|
||||
break;
|
||||
}
|
||||
|
||||
trackingProvider.DecompressionCalls.Should().BeGreaterThan(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ZipReader_OpenEntryStreamAsync_UsesCustomDeflateProvider()
|
||||
{
|
||||
using var zipStream = new MemoryStream();
|
||||
using (
|
||||
var writer = WriterFactory.OpenWriter(
|
||||
zipStream,
|
||||
ArchiveType.Zip,
|
||||
new WriterOptions(CompressionType.Deflate) { LeaveStreamOpen = true }
|
||||
)
|
||||
)
|
||||
{
|
||||
var data = Encoding.UTF8.GetBytes("zip async provider usage");
|
||||
writer.Write("test.txt", new MemoryStream(data));
|
||||
}
|
||||
|
||||
var trackingProvider = new TrackingCompressionProvider(new DeflateCompressionProvider());
|
||||
var registry = CompressionProviderRegistry.Default.With(trackingProvider);
|
||||
var options = new ReaderOptions { Providers = registry };
|
||||
|
||||
zipStream.Position = 0;
|
||||
await using var reader = await ReaderFactory.OpenAsyncReader(zipStream, options);
|
||||
(await reader.MoveToNextEntryAsync()).Should().BeTrue();
|
||||
using var entryStream = await reader.OpenEntryStreamAsync();
|
||||
using var resultStream = new MemoryStream();
|
||||
await entryStream.CopyToAsync(resultStream);
|
||||
|
||||
trackingProvider.DecompressionCalls.Should().BeGreaterThan(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LzwReader_OpenReader_UsesCustomLzwProvider()
|
||||
{
|
||||
var archivePath = Path.Combine(TestBase.TEST_ARCHIVES_PATH, "Tar.tar.Z");
|
||||
var trackingProvider = new TrackingCompressionProvider(new LzwCompressionProvider());
|
||||
var registry = CompressionProviderRegistry.Default.With(trackingProvider);
|
||||
var options = new ReaderOptions { Providers = registry };
|
||||
|
||||
using var stream = File.OpenRead(archivePath);
|
||||
using var reader = ReaderFactory.OpenReader(stream, options);
|
||||
reader.MoveToNextEntry().Should().BeTrue();
|
||||
reader.WriteEntryTo(Stream.Null);
|
||||
|
||||
trackingProvider.DecompressionCalls.Should().BeGreaterThan(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ZipWriter_LzmaProviderHook_WritesPostCompressionData()
|
||||
{
|
||||
var trackingProvider = new TrackingLzmaHooksProvider();
|
||||
var registry = CompressionProviderRegistry.Default.With(trackingProvider);
|
||||
using var zipStream = new MemoryStream();
|
||||
|
||||
using (
|
||||
var writer = WriterFactory.OpenWriter(
|
||||
zipStream,
|
||||
ArchiveType.Zip,
|
||||
new WriterOptions(CompressionType.LZMA)
|
||||
{
|
||||
LeaveStreamOpen = true,
|
||||
Providers = registry,
|
||||
}
|
||||
)
|
||||
)
|
||||
{
|
||||
var data = Encoding.UTF8.GetBytes("hook provider");
|
||||
writer.Write("test.txt", new MemoryStream(data));
|
||||
}
|
||||
|
||||
trackingProvider.PreCalls.Should().BeGreaterThan(0);
|
||||
trackingProvider.PropertiesCalls.Should().BeGreaterThan(0);
|
||||
trackingProvider.PostCalls.Should().BeGreaterThan(0);
|
||||
}
|
||||
|
||||
#region System.IO.Compression Tests
|
||||
|
||||
[Fact]
|
||||
@@ -230,8 +470,8 @@ public class CompressionProviderTests
|
||||
);
|
||||
|
||||
using var compressedStream = new MemoryStream();
|
||||
// System.IO.Compression streams leave the underlying stream open by default with leaveOpen: true
|
||||
using (var compressStream = provider.CreateCompressStream(compressedStream, 6))
|
||||
var nonDisposingStream = SharpCompressStream.CreateNonDisposing(compressedStream);
|
||||
using (var compressStream = provider.CreateCompressStream(nonDisposingStream, 6))
|
||||
{
|
||||
compressStream.Write(original, 0, original.Length);
|
||||
}
|
||||
@@ -258,7 +498,8 @@ public class CompressionProviderTests
|
||||
);
|
||||
|
||||
using var compressedStream = new MemoryStream();
|
||||
using (var compressStream = provider.CreateCompressStream(compressedStream, level))
|
||||
var nonDisposingStream = SharpCompressStream.CreateNonDisposing(compressedStream);
|
||||
using (var compressStream = provider.CreateCompressStream(nonDisposingStream, level))
|
||||
{
|
||||
compressStream.Write(original, 0, original.Length);
|
||||
}
|
||||
@@ -282,7 +523,8 @@ public class CompressionProviderTests
|
||||
);
|
||||
|
||||
using var compressedStream = new MemoryStream();
|
||||
using (var compressStream = systemProvider.CreateCompressStream(compressedStream, 6))
|
||||
var nonDisposingStream = SharpCompressStream.CreateNonDisposing(compressedStream);
|
||||
using (var compressStream = systemProvider.CreateCompressStream(nonDisposingStream, 6))
|
||||
{
|
||||
compressStream.Write(original, 0, original.Length);
|
||||
}
|
||||
@@ -377,7 +619,8 @@ public class CompressionProviderTests
|
||||
);
|
||||
|
||||
using var compressedStream = new MemoryStream();
|
||||
using (var compressStream = provider.CreateCompressStream(compressedStream, 6))
|
||||
var nonDisposingStream = SharpCompressStream.CreateNonDisposing(compressedStream);
|
||||
using (var compressStream = provider.CreateCompressStream(nonDisposingStream, 6))
|
||||
{
|
||||
compressStream.Write(original, 0, original.Length);
|
||||
}
|
||||
@@ -401,7 +644,8 @@ public class CompressionProviderTests
|
||||
);
|
||||
|
||||
using var compressedStream = new MemoryStream();
|
||||
using (var compressStream = systemProvider.CreateCompressStream(compressedStream, 6))
|
||||
var nonDisposingStream = SharpCompressStream.CreateNonDisposing(compressedStream);
|
||||
using (var compressStream = systemProvider.CreateCompressStream(nonDisposingStream, 6))
|
||||
{
|
||||
compressStream.Write(original, 0, original.Length);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user