complete the usage of providers for detection and async

This commit is contained in:
Adam Hathcock
2026-02-12 15:09:21 +00:00
parent ae0b4f5c4c
commit a92f82ef28
26 changed files with 617 additions and 350 deletions

View File

@@ -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 SharpCompresss 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 SharpCompresss 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

View File

@@ -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.
---

View File

@@ -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 SharpCompresss 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#

View File

@@ -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(

View File

@@ -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);
}
}

View File

@@ -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
);

View File

@@ -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);

View File

@@ -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);
}
}

View File

@@ -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));
}
}

View File

@@ -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
);
}

View File

@@ -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

View File

@@ -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.

View File

@@ -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;

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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();

View File

@@ -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))
{

View File

@@ -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

View File

@@ -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.

View File

@@ -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
);
}

View File

@@ -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
);
}

View File

@@ -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;
}
}

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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;

View File

@@ -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);
}