mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-09-25 16:34:45 +00:00
more providers
This commit is contained in:
@@ -84,7 +84,11 @@ public partial class GZipArchive
|
||||
var stream = (await volumes.SingleAsync()).Stream;
|
||||
yield return new GZipArchiveEntry(
|
||||
this,
|
||||
await GZipFilePart.CreateAsync(stream, ReaderOptions.ArchiveEncoding)
|
||||
await GZipFilePart.CreateAsync(
|
||||
stream,
|
||||
ReaderOptions.ArchiveEncoding,
|
||||
ReaderOptions.CompressionProviders
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,7 +88,11 @@ public partial class GZipArchive : AbstractWritableArchive<GZipArchiveEntry, GZi
|
||||
var stream = volumes.Single().Stream;
|
||||
yield return new GZipArchiveEntry(
|
||||
this,
|
||||
GZipFilePart.Create(stream, ReaderOptions.ArchiveEncoding)
|
||||
GZipFilePart.Create(
|
||||
stream,
|
||||
ReaderOptions.ArchiveEncoding,
|
||||
ReaderOptions.CompressionProviders
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -55,7 +55,12 @@ public partial class ZipArchive
|
||||
|
||||
yield return new ZipArchiveEntry(
|
||||
this,
|
||||
new SeekableZipFilePart(headerFactory.NotNull(), deh, s)
|
||||
new SeekableZipFilePart(
|
||||
headerFactory.NotNull(),
|
||||
deh,
|
||||
s,
|
||||
ReaderOptions.CompressionProviders
|
||||
)
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -95,7 +95,12 @@ public partial class ZipArchive : AbstractWritableArchive<ZipArchiveEntry, ZipVo
|
||||
|
||||
yield return new ZipArchiveEntry(
|
||||
this,
|
||||
new SeekableZipFilePart(headerFactory.NotNull(), deh, s)
|
||||
new SeekableZipFilePart(
|
||||
headerFactory.NotNull(),
|
||||
deh,
|
||||
s,
|
||||
ReaderOptions.CompressionProviders
|
||||
)
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -11,6 +11,12 @@ public partial class GZipEntry
|
||||
ReaderOptions options
|
||||
)
|
||||
{
|
||||
yield return new GZipEntry(await GZipFilePart.CreateAsync(stream, options.ArchiveEncoding));
|
||||
yield return new GZipEntry(
|
||||
await GZipFilePart.CreateAsync(
|
||||
stream,
|
||||
options.ArchiveEncoding,
|
||||
options.CompressionProviders
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,9 @@ public partial class GZipEntry : Entry
|
||||
|
||||
internal static IEnumerable<GZipEntry> GetEntries(Stream stream, ReaderOptions options)
|
||||
{
|
||||
yield return new GZipEntry(GZipFilePart.Create(stream, options.ArchiveEncoding));
|
||||
yield return new GZipEntry(
|
||||
GZipFilePart.Create(stream, options.ArchiveEncoding, options.CompressionProviders)
|
||||
);
|
||||
}
|
||||
|
||||
// Async methods moved to GZipEntry.Async.cs
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Common.Tar.Headers;
|
||||
using SharpCompress.Compressors;
|
||||
using SharpCompress.Compressors.Deflate;
|
||||
|
||||
namespace SharpCompress.Common.GZip;
|
||||
@@ -17,7 +18,17 @@ internal sealed partial class GZipFilePart
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
var part = new GZipFilePart(stream, archiveEncoding);
|
||||
return await CreateAsync(stream, archiveEncoding, null, cancellationToken);
|
||||
}
|
||||
|
||||
internal static async ValueTask<GZipFilePart> CreateAsync(
|
||||
Stream stream,
|
||||
IArchiveEncoding archiveEncoding,
|
||||
CompressionProviderRegistry? compressionProviders,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
var part = new GZipFilePart(stream, archiveEncoding, compressionProviders);
|
||||
|
||||
await part.ReadAndValidateGzipHeaderAsync(cancellationToken);
|
||||
if (stream.CanSeek)
|
||||
|
||||
@@ -12,10 +12,37 @@ internal sealed partial class GZipFilePart : FilePart
|
||||
{
|
||||
private string? _name;
|
||||
private readonly Stream _stream;
|
||||
private readonly CompressionProviderRegistry? _compressionProviders;
|
||||
|
||||
internal static GZipFilePart Create(Stream stream, IArchiveEncoding archiveEncoding)
|
||||
{
|
||||
var part = new GZipFilePart(stream, archiveEncoding);
|
||||
var part = new GZipFilePart(stream, archiveEncoding, null);
|
||||
|
||||
part.ReadAndValidateGzipHeader();
|
||||
if (stream.CanSeek)
|
||||
{
|
||||
var position = stream.Position;
|
||||
stream.Position = stream.Length - 8;
|
||||
part.ReadTrailer();
|
||||
stream.Position = position;
|
||||
part.EntryStartPosition = position;
|
||||
}
|
||||
else
|
||||
{
|
||||
// For non-seekable streams, we can't read the trailer or track position.
|
||||
// Set to 0 since the stream will be read sequentially from its current position.
|
||||
part.EntryStartPosition = 0;
|
||||
}
|
||||
return part;
|
||||
}
|
||||
|
||||
internal static GZipFilePart Create(
|
||||
Stream stream,
|
||||
IArchiveEncoding archiveEncoding,
|
||||
CompressionProviderRegistry? compressionProviders
|
||||
)
|
||||
{
|
||||
var part = new GZipFilePart(stream, archiveEncoding, compressionProviders);
|
||||
|
||||
part.ReadAndValidateGzipHeader();
|
||||
if (stream.CanSeek)
|
||||
@@ -38,6 +65,17 @@ internal sealed partial class GZipFilePart : FilePart
|
||||
private GZipFilePart(Stream stream, IArchiveEncoding archiveEncoding)
|
||||
: base(archiveEncoding) => _stream = stream;
|
||||
|
||||
private GZipFilePart(
|
||||
Stream stream,
|
||||
IArchiveEncoding archiveEncoding,
|
||||
CompressionProviderRegistry? compressionProviders
|
||||
)
|
||||
: base(archiveEncoding)
|
||||
{
|
||||
_stream = stream;
|
||||
_compressionProviders = compressionProviders;
|
||||
}
|
||||
|
||||
internal long EntryStartPosition { get; private set; }
|
||||
|
||||
internal DateTime? DateModified { get; private set; }
|
||||
@@ -46,13 +84,11 @@ internal sealed partial class GZipFilePart : FilePart
|
||||
|
||||
internal override string? FilePartName => _name;
|
||||
|
||||
internal override Stream GetCompressedStream() =>
|
||||
new DeflateStream(
|
||||
_stream,
|
||||
CompressionMode.Decompress,
|
||||
CompressionLevel.Default,
|
||||
leaveOpen: true
|
||||
);
|
||||
internal override Stream GetCompressedStream()
|
||||
{
|
||||
var providers = _compressionProviders ?? CompressionProviderRegistry.Default;
|
||||
return providers.CreateDecompressStream(CompressionType.Deflate, _stream);
|
||||
}
|
||||
|
||||
internal override Stream GetRawStream() => _stream;
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.IO;
|
||||
using SharpCompress.Common.Zip.Headers;
|
||||
using SharpCompress.Compressors;
|
||||
|
||||
namespace SharpCompress.Common.Zip;
|
||||
|
||||
@@ -15,6 +16,14 @@ internal partial class SeekableZipFilePart : ZipFilePart
|
||||
)
|
||||
: base(header, stream) => _headerFactory = headerFactory;
|
||||
|
||||
internal SeekableZipFilePart(
|
||||
SeekableZipHeaderFactory headerFactory,
|
||||
DirectoryEntryHeader header,
|
||||
Stream stream,
|
||||
CompressionProviderRegistry? compressionProviders
|
||||
)
|
||||
: base(header, stream, compressionProviders) => _headerFactory = headerFactory;
|
||||
|
||||
internal override Stream GetCompressedStream()
|
||||
{
|
||||
if (!_isLocalHeaderLoaded)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.IO;
|
||||
using SharpCompress.Common.Zip.Headers;
|
||||
using SharpCompress.Compressors.Deflate;
|
||||
using SharpCompress.Compressors;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Common.Zip;
|
||||
@@ -12,6 +12,13 @@ internal sealed partial class StreamingZipFilePart : ZipFilePart
|
||||
internal StreamingZipFilePart(ZipFileEntry header, Stream stream)
|
||||
: base(header, stream) { }
|
||||
|
||||
internal StreamingZipFilePart(
|
||||
ZipFileEntry header,
|
||||
Stream stream,
|
||||
CompressionProviderRegistry? compressionProviders
|
||||
)
|
||||
: base(header, stream, compressionProviders) { }
|
||||
|
||||
protected override Stream CreateBaseStream() => Header.PackedStream.NotNull();
|
||||
|
||||
internal override Stream GetCompressedStream()
|
||||
@@ -47,11 +54,6 @@ internal sealed partial class StreamingZipFilePart : ZipFilePart
|
||||
// If we had TotalIn / TotalOut we could have used them
|
||||
Header.CompressedSize = _decompressionStream.Position;
|
||||
|
||||
if (_decompressionStream is DeflateStream deflateStream)
|
||||
{
|
||||
stream.Position = 0;
|
||||
}
|
||||
|
||||
Skipped = true;
|
||||
}
|
||||
var reader = new BinaryReader(stream, System.Text.Encoding.Default, leaveOpen: true);
|
||||
|
||||
@@ -20,6 +20,8 @@ namespace SharpCompress.Common.Zip;
|
||||
|
||||
internal abstract partial class ZipFilePart : FilePart
|
||||
{
|
||||
private CompressionProviderRegistry? _compressionProviders;
|
||||
|
||||
internal ZipFilePart(ZipFileEntry header, Stream stream)
|
||||
: base(header.ArchiveEncoding)
|
||||
{
|
||||
@@ -28,9 +30,29 @@ internal abstract partial class ZipFilePart : FilePart
|
||||
BaseStream = stream;
|
||||
}
|
||||
|
||||
internal ZipFilePart(
|
||||
ZipFileEntry header,
|
||||
Stream stream,
|
||||
CompressionProviderRegistry? compressionProviders
|
||||
)
|
||||
: this(header, stream)
|
||||
{
|
||||
_compressionProviders = compressionProviders;
|
||||
}
|
||||
|
||||
internal Stream BaseStream { get; }
|
||||
internal ZipFileEntry Header { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the compression provider registry.
|
||||
/// If null, uses the default registry.
|
||||
/// </summary>
|
||||
internal CompressionProviderRegistry? CompressionProviders
|
||||
{
|
||||
get => _compressionProviders;
|
||||
set => _compressionProviders = value;
|
||||
}
|
||||
|
||||
internal override string? FilePartName => Header.Name;
|
||||
|
||||
internal override Stream GetCompressedStream()
|
||||
@@ -64,8 +86,38 @@ internal abstract partial class ZipFilePart : FilePart
|
||||
protected bool LeaveStreamOpen =>
|
||||
FlagUtility.HasFlag(Header.Flags, HeaderFlags.UsePostDataDescriptor) || Header.IsZip64;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the compression provider registry, falling back to default if not set.
|
||||
/// </summary>
|
||||
protected CompressionProviderRegistry GetProviders() =>
|
||||
_compressionProviders ?? CompressionProviderRegistry.Default;
|
||||
|
||||
/// <summary>
|
||||
/// Converts ZipCompressionMethod to CompressionType.
|
||||
/// </summary>
|
||||
protected static CompressionType ToCompressionType(ZipCompressionMethod method) =>
|
||||
method switch
|
||||
{
|
||||
ZipCompressionMethod.None => CompressionType.None,
|
||||
ZipCompressionMethod.Deflate => CompressionType.Deflate,
|
||||
ZipCompressionMethod.Deflate64 => CompressionType.Deflate64,
|
||||
ZipCompressionMethod.BZip2 => CompressionType.BZip2,
|
||||
ZipCompressionMethod.LZMA => CompressionType.LZMA,
|
||||
ZipCompressionMethod.PPMd => CompressionType.PPMd,
|
||||
ZipCompressionMethod.ZStandard => CompressionType.ZStandard,
|
||||
ZipCompressionMethod.Xz => CompressionType.Xz,
|
||||
ZipCompressionMethod.Shrink => CompressionType.Shrink,
|
||||
ZipCompressionMethod.Reduce1 => CompressionType.Reduce1,
|
||||
ZipCompressionMethod.Reduce2 => CompressionType.Reduce2,
|
||||
ZipCompressionMethod.Reduce3 => CompressionType.Reduce3,
|
||||
ZipCompressionMethod.Reduce4 => CompressionType.Reduce4,
|
||||
ZipCompressionMethod.Explode => CompressionType.Explode,
|
||||
_ => throw new NotSupportedException($"Unsupported compression method: {method}"),
|
||||
};
|
||||
|
||||
protected Stream CreateDecompressionStream(Stream stream, ZipCompressionMethod method)
|
||||
{
|
||||
// Handle special cases first
|
||||
switch (method)
|
||||
{
|
||||
case ZipCompressionMethod.None:
|
||||
@@ -74,76 +126,29 @@ internal abstract partial class ZipFilePart : FilePart
|
||||
{
|
||||
return new DataDescriptorStream(stream);
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
case ZipCompressionMethod.Shrink:
|
||||
case ZipCompressionMethod.WinzipAes:
|
||||
{
|
||||
return new ShrinkStream(
|
||||
stream,
|
||||
CompressionMode.Decompress,
|
||||
Header.CompressedSize,
|
||||
Header.UncompressedSize
|
||||
);
|
||||
}
|
||||
case ZipCompressionMethod.Reduce1:
|
||||
{
|
||||
return ReduceStream.Create(
|
||||
stream,
|
||||
Header.CompressedSize,
|
||||
Header.UncompressedSize,
|
||||
1
|
||||
);
|
||||
}
|
||||
case ZipCompressionMethod.Reduce2:
|
||||
{
|
||||
return ReduceStream.Create(
|
||||
stream,
|
||||
Header.CompressedSize,
|
||||
Header.UncompressedSize,
|
||||
2
|
||||
);
|
||||
}
|
||||
case ZipCompressionMethod.Reduce3:
|
||||
{
|
||||
return ReduceStream.Create(
|
||||
stream,
|
||||
Header.CompressedSize,
|
||||
Header.UncompressedSize,
|
||||
3
|
||||
);
|
||||
}
|
||||
case ZipCompressionMethod.Reduce4:
|
||||
{
|
||||
return ReduceStream.Create(
|
||||
stream,
|
||||
Header.CompressedSize,
|
||||
Header.UncompressedSize,
|
||||
4
|
||||
);
|
||||
}
|
||||
case ZipCompressionMethod.Explode:
|
||||
{
|
||||
return ExplodeStream.Create(
|
||||
stream,
|
||||
Header.CompressedSize,
|
||||
Header.UncompressedSize,
|
||||
Header.Flags
|
||||
);
|
||||
return CreateWinzipAesDecompressionStream(stream);
|
||||
}
|
||||
}
|
||||
|
||||
case ZipCompressionMethod.Deflate:
|
||||
{
|
||||
return new DeflateStream(stream, CompressionMode.Decompress);
|
||||
}
|
||||
case ZipCompressionMethod.Deflate64:
|
||||
{
|
||||
return new Deflate64Stream(stream, CompressionMode.Decompress);
|
||||
}
|
||||
case ZipCompressionMethod.BZip2:
|
||||
{
|
||||
return BZip2Stream.Create(stream, CompressionMode.Decompress, false);
|
||||
}
|
||||
// Get the compression type and providers
|
||||
var compressionType = ToCompressionType(method);
|
||||
var providers = GetProviders();
|
||||
|
||||
// Build context with header information
|
||||
var context = new CompressionContext
|
||||
{
|
||||
InputSize = Header.CompressedSize,
|
||||
OutputSize = Header.UncompressedSize,
|
||||
CanSeek = stream.CanSeek,
|
||||
};
|
||||
|
||||
// Handle methods that need special context
|
||||
switch (method)
|
||||
{
|
||||
case ZipCompressionMethod.LZMA:
|
||||
{
|
||||
if (FlagUtility.HasFlag(Header.Flags, HeaderFlags.Encrypted))
|
||||
@@ -158,71 +163,71 @@ internal abstract partial class ZipFilePart : FilePart
|
||||
)
|
||||
)
|
||||
{
|
||||
reader.ReadUInt16(); //LZMA version
|
||||
var props = new byte[reader.ReadUInt16()];
|
||||
reader.ReadUInt16(); // LZMA version
|
||||
var propsLength = reader.ReadUInt16();
|
||||
var props = new byte[propsLength];
|
||||
reader.Read(props, 0, props.Length);
|
||||
return LzmaStream.Create(
|
||||
props,
|
||||
stream,
|
||||
Header.CompressedSize > 0 ? Header.CompressedSize - 4 - props.Length : -1,
|
||||
FlagUtility.HasFlag(Header.Flags, HeaderFlags.Bit1)
|
||||
context = context with
|
||||
{
|
||||
Properties = props,
|
||||
InputSize =
|
||||
Header.CompressedSize > 0
|
||||
? Header.CompressedSize - 4 - props.Length
|
||||
: -1,
|
||||
OutputSize = FlagUtility.HasFlag(Header.Flags, HeaderFlags.Bit1)
|
||||
? -1
|
||||
: Header.UncompressedSize
|
||||
);
|
||||
: Header.UncompressedSize,
|
||||
};
|
||||
return providers.CreateDecompressStream(compressionType, stream, context);
|
||||
}
|
||||
}
|
||||
case ZipCompressionMethod.Xz:
|
||||
{
|
||||
return new XZStream(stream);
|
||||
}
|
||||
case ZipCompressionMethod.ZStandard:
|
||||
{
|
||||
return new DecompressionStream(stream);
|
||||
}
|
||||
case ZipCompressionMethod.PPMd:
|
||||
{
|
||||
Span<byte> props = stackalloc byte[2];
|
||||
stream.ReadFully(props);
|
||||
return PpmdStream.Create(new PpmdProperties(props), stream, false);
|
||||
context = context with { Properties = props.ToArray() };
|
||||
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 CreateDecompressionStream(
|
||||
stream,
|
||||
(ZipCompressionMethod)
|
||||
BinaryPrimitives.ReadUInt16LittleEndian(data.DataBytes.AsSpan(5))
|
||||
);
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new NotSupportedException("CompressionMethod: " + Header.CompressionMethod);
|
||||
context = context with { FormatOptions = Header.Flags };
|
||||
return providers.CreateDecompressStream(compressionType, stream, context);
|
||||
}
|
||||
}
|
||||
|
||||
// For simple methods, use the basic decompress
|
||||
return providers.CreateDecompressStream(compressionType, stream, context);
|
||||
}
|
||||
|
||||
private Stream CreateWinzipAesDecompressionStream(Stream stream)
|
||||
{
|
||||
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 CreateDecompressionStream(
|
||||
stream,
|
||||
(ZipCompressionMethod)BinaryPrimitives.ReadUInt16LittleEndian(data.DataBytes.AsSpan(5))
|
||||
);
|
||||
}
|
||||
|
||||
protected Stream GetCryptoStream(Stream plainStream)
|
||||
|
||||
44
src/SharpCompress/Compressors/CompressionContext.cs
Normal file
44
src/SharpCompress/Compressors/CompressionContext.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace SharpCompress.Compressors;
|
||||
|
||||
/// <summary>
|
||||
/// Provides context information for compression operations.
|
||||
/// Carries format-specific parameters that some compression types require.
|
||||
/// </summary>
|
||||
public sealed record CompressionContext
|
||||
{
|
||||
/// <summary>
|
||||
/// The size of the input data, or -1 if unknown.
|
||||
/// </summary>
|
||||
public long InputSize { get; init; } = -1;
|
||||
|
||||
/// <summary>
|
||||
/// The expected output size, or -1 if unknown.
|
||||
/// </summary>
|
||||
public long OutputSize { get; init; } = -1;
|
||||
|
||||
/// <summary>
|
||||
/// Properties bytes for the compression format (e.g., LZMA properties).
|
||||
/// </summary>
|
||||
public byte[]? Properties { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the underlying stream supports seeking.
|
||||
/// </summary>
|
||||
public bool CanSeek { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Additional format-specific options.
|
||||
/// </summary>
|
||||
public object? FormatOptions { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a CompressionContext from a stream.
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream to extract context from.</param>
|
||||
/// <returns>A CompressionContext populated from the stream.</returns>
|
||||
public static CompressionContext FromStream(Stream stream) =>
|
||||
new() { CanSeek = stream.CanSeek, InputSize = stream.CanSeek ? stream.Length : -1 };
|
||||
}
|
||||
@@ -90,6 +90,69 @@ public sealed class CompressionProviderRegistry
|
||||
return provider.CreateDecompressStream(source);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a compression stream for the specified type with context.
|
||||
/// </summary>
|
||||
/// <param name="type">The compression type.</param>
|
||||
/// <param name="destination">The destination stream.</param>
|
||||
/// <param name="level">The compression level.</param>
|
||||
/// <param name="context">Context information for the compression.</param>
|
||||
/// <returns>A compression stream.</returns>
|
||||
/// <exception cref="InvalidOperationException">If no provider is registered for the type.</exception>
|
||||
/// <exception cref="NotSupportedException">If the provider does not support compression.</exception>
|
||||
public Stream CreateCompressStream(
|
||||
CompressionType type,
|
||||
Stream destination,
|
||||
int level,
|
||||
CompressionContext context
|
||||
)
|
||||
{
|
||||
var provider = GetProvider(type);
|
||||
if (provider is null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"No compression provider registered for type: {type}"
|
||||
);
|
||||
}
|
||||
return provider.CreateCompressStream(destination, level, context);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a decompression stream for the specified type with context.
|
||||
/// </summary>
|
||||
/// <param name="type">The compression type.</param>
|
||||
/// <param name="source">The source stream.</param>
|
||||
/// <param name="context">Context information for the decompression.</param>
|
||||
/// <returns>A decompression stream.</returns>
|
||||
/// <exception cref="InvalidOperationException">If no provider is registered for the type.</exception>
|
||||
/// <exception cref="NotSupportedException">If the provider does not support decompression.</exception>
|
||||
public Stream CreateDecompressStream(
|
||||
CompressionType type,
|
||||
Stream source,
|
||||
CompressionContext context
|
||||
)
|
||||
{
|
||||
var provider = GetProvider(type);
|
||||
if (provider is null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"No compression provider registered for type: {type}"
|
||||
);
|
||||
}
|
||||
return provider.CreateDecompressStream(source, context);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the provider as an ICompressingProvider if it supports complex initialization.
|
||||
/// </summary>
|
||||
/// <param name="type">The compression type.</param>
|
||||
/// <returns>The compressing provider, or null if the provider doesn't support complex initialization.</returns>
|
||||
public ICompressingProvider? GetCompressingProvider(CompressionType type)
|
||||
{
|
||||
var provider = GetProvider(type);
|
||||
return provider as ICompressingProvider;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new registry with the specified provider added or replaced.
|
||||
/// </summary>
|
||||
@@ -122,6 +185,15 @@ public sealed class CompressionProviderRegistry
|
||||
[CompressionType.LZip] = new LZipCompressionProvider(),
|
||||
[CompressionType.Xz] = new XzCompressionProvider(),
|
||||
[CompressionType.Lzw] = new LzwCompressionProvider(),
|
||||
[CompressionType.Deflate64] = new Deflate64CompressionProvider(),
|
||||
[CompressionType.Shrink] = new ShrinkCompressionProvider(),
|
||||
[CompressionType.Reduce1] = new Reduce1CompressionProvider(),
|
||||
[CompressionType.Reduce2] = new Reduce2CompressionProvider(),
|
||||
[CompressionType.Reduce3] = new Reduce3CompressionProvider(),
|
||||
[CompressionType.Reduce4] = new Reduce4CompressionProvider(),
|
||||
[CompressionType.Explode] = new ExplodeCompressionProvider(),
|
||||
[CompressionType.LZMA] = new LzmaCompressingProvider(),
|
||||
[CompressionType.PPMd] = new PpmdCompressingProvider(),
|
||||
};
|
||||
|
||||
return new CompressionProviderRegistry(providers);
|
||||
|
||||
43
src/SharpCompress/Compressors/ICompressingProvider.cs
Normal file
43
src/SharpCompress/Compressors/ICompressingProvider.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using System.IO;
|
||||
using SharpCompress.Common;
|
||||
|
||||
namespace SharpCompress.Compressors;
|
||||
|
||||
/// <summary>
|
||||
/// Extended compression provider interface for formats that require initialization/finalization data.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Some compression formats (like LZMA and PPMd in Zip) require special handling:
|
||||
/// - Data written before compression starts (magic bytes, properties headers)
|
||||
/// - Data written after compression completes (properties, footers)
|
||||
/// This interface extends ICompressionProvider to support these complex initialization patterns
|
||||
/// while keeping the simple ICompressionProvider interface for formats that don't need it.
|
||||
/// </remarks>
|
||||
public interface ICompressingProvider : ICompressionProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets initialization data to write before compression starts.
|
||||
/// Returns null if no pre-compression data is needed.
|
||||
/// </summary>
|
||||
/// <param name="context">Context information.</param>
|
||||
/// <returns>Bytes to write before compression, or null.</returns>
|
||||
byte[]? GetPreCompressionData(CompressionContext context);
|
||||
|
||||
/// <summary>
|
||||
/// Gets properties/data to write after creating the compression stream but before writing data.
|
||||
/// Returns null if no properties are needed.
|
||||
/// </summary>
|
||||
/// <param name="stream">The compression stream that was created.</param>
|
||||
/// <param name="context">Context information.</param>
|
||||
/// <returns>Bytes to write after stream creation, or null.</returns>
|
||||
byte[]? GetCompressionProperties(Stream stream, CompressionContext context);
|
||||
|
||||
/// <summary>
|
||||
/// Gets data to write after compression is complete.
|
||||
/// Returns null if no post-compression data is needed.
|
||||
/// </summary>
|
||||
/// <param name="stream">The compression stream.</param>
|
||||
/// <param name="context">Context information.</param>
|
||||
/// <returns>Bytes to write after compression, or null.</returns>
|
||||
byte[]? GetPostCompressionData(Stream stream, CompressionContext context);
|
||||
}
|
||||
@@ -46,6 +46,20 @@ public interface ICompressionProvider
|
||||
/// <exception cref="NotSupportedException">Thrown if SupportsCompression is false.</exception>
|
||||
Stream CreateCompressStream(Stream destination, int compressionLevel);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a compression stream with context information.
|
||||
/// </summary>
|
||||
/// <param name="destination">The destination stream.</param>
|
||||
/// <param name="compressionLevel">The compression level.</param>
|
||||
/// <param name="context">Context information about the compression.</param>
|
||||
/// <returns>A compression stream.</returns>
|
||||
/// <exception cref="NotSupportedException">Thrown if SupportsCompression is false.</exception>
|
||||
Stream CreateCompressStream(
|
||||
Stream destination,
|
||||
int compressionLevel,
|
||||
CompressionContext context
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a decompression stream that decompresses data read from it.
|
||||
/// </summary>
|
||||
@@ -53,4 +67,13 @@ public interface ICompressionProvider
|
||||
/// <returns>A stream that decompresses data read from it.</returns>
|
||||
/// <exception cref="NotSupportedException">Thrown if SupportsDecompression is false.</exception>
|
||||
Stream CreateDecompressStream(Stream source);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a decompression stream with context information.
|
||||
/// </summary>
|
||||
/// <param name="source">The source stream.</param>
|
||||
/// <param name="context">Context information about the decompression.</param>
|
||||
/// <returns>A decompression stream.</returns>
|
||||
/// <exception cref="NotSupportedException">Thrown if SupportsDecompression is false.</exception>
|
||||
Stream CreateDecompressStream(Stream source, CompressionContext context);
|
||||
}
|
||||
|
||||
@@ -19,8 +19,24 @@ public sealed class BZip2CompressionProvider : ICompressionProvider
|
||||
return BZip2Stream.Create(destination, CompressionMode.Compress, false);
|
||||
}
|
||||
|
||||
public Stream CreateCompressStream(
|
||||
Stream destination,
|
||||
int compressionLevel,
|
||||
CompressionContext context
|
||||
)
|
||||
{
|
||||
// Context not used for BZip2 compression
|
||||
return CreateCompressStream(destination, compressionLevel);
|
||||
}
|
||||
|
||||
public Stream CreateDecompressStream(Stream source)
|
||||
{
|
||||
return BZip2Stream.Create(source, CompressionMode.Decompress, false);
|
||||
}
|
||||
|
||||
public Stream CreateDecompressStream(Stream source, CompressionContext context)
|
||||
{
|
||||
// Context not used for BZip2 decompression
|
||||
return CreateDecompressStream(source);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Compressors.Deflate64;
|
||||
|
||||
namespace SharpCompress.Compressors.Providers;
|
||||
|
||||
/// <summary>
|
||||
/// Provides Deflate64 decompression using SharpCompress's internal implementation.
|
||||
/// Note: Deflate64 compression is not supported; this provider is decompression-only.
|
||||
/// </summary>
|
||||
public sealed class Deflate64CompressionProvider : ICompressionProvider
|
||||
{
|
||||
public CompressionType CompressionType => CompressionType.Deflate64;
|
||||
public bool SupportsCompression => false;
|
||||
public bool SupportsDecompression => true;
|
||||
|
||||
public Stream CreateCompressStream(Stream destination, int compressionLevel)
|
||||
{
|
||||
throw new NotSupportedException(
|
||||
"Deflate64 compression is not supported by SharpCompress's internal implementation."
|
||||
);
|
||||
}
|
||||
|
||||
public Stream CreateCompressStream(
|
||||
Stream destination,
|
||||
int compressionLevel,
|
||||
CompressionContext context
|
||||
)
|
||||
{
|
||||
throw new NotSupportedException(
|
||||
"Deflate64 compression is not supported by SharpCompress's internal implementation."
|
||||
);
|
||||
}
|
||||
|
||||
public Stream CreateDecompressStream(Stream source)
|
||||
{
|
||||
return new Deflate64Stream(source, CompressionMode.Decompress);
|
||||
}
|
||||
|
||||
public Stream CreateDecompressStream(Stream source, CompressionContext context)
|
||||
{
|
||||
// Context not used for Deflate64 decompression
|
||||
return CreateDecompressStream(source);
|
||||
}
|
||||
}
|
||||
@@ -19,8 +19,24 @@ public sealed class DeflateCompressionProvider : ICompressionProvider
|
||||
return new DeflateStream(destination, CompressionMode.Compress, level);
|
||||
}
|
||||
|
||||
public Stream CreateCompressStream(
|
||||
Stream destination,
|
||||
int compressionLevel,
|
||||
CompressionContext context
|
||||
)
|
||||
{
|
||||
// Context not used for simple Deflate compression
|
||||
return CreateCompressStream(destination, compressionLevel);
|
||||
}
|
||||
|
||||
public Stream CreateDecompressStream(Stream source)
|
||||
{
|
||||
return new DeflateStream(source, CompressionMode.Decompress);
|
||||
}
|
||||
|
||||
public Stream CreateDecompressStream(Stream source, CompressionContext context)
|
||||
{
|
||||
// Context not used for simple Deflate decompression
|
||||
return CreateDecompressStream(source);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Common.Zip.Headers;
|
||||
using SharpCompress.Compressors.Explode;
|
||||
|
||||
namespace SharpCompress.Compressors.Providers;
|
||||
|
||||
/// <summary>
|
||||
/// Provides Explode decompression using SharpCompress's internal implementation.
|
||||
/// Note: Explode compression is not supported; this provider is decompression-only.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Explode requires compressed size, uncompressed size, and flags which must be provided via CompressionContext.
|
||||
/// </remarks>
|
||||
public sealed class ExplodeCompressionProvider : ICompressionProvider
|
||||
{
|
||||
public CompressionType CompressionType => CompressionType.Explode;
|
||||
public bool SupportsCompression => false;
|
||||
public bool SupportsDecompression => true;
|
||||
|
||||
public Stream CreateCompressStream(Stream destination, int compressionLevel)
|
||||
{
|
||||
throw new NotSupportedException(
|
||||
"Explode compression is not supported by SharpCompress's internal implementation."
|
||||
);
|
||||
}
|
||||
|
||||
public Stream CreateCompressStream(
|
||||
Stream destination,
|
||||
int compressionLevel,
|
||||
CompressionContext context
|
||||
)
|
||||
{
|
||||
throw new NotSupportedException(
|
||||
"Explode compression is not supported by SharpCompress's internal implementation."
|
||||
);
|
||||
}
|
||||
|
||||
public Stream CreateDecompressStream(Stream source)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Explode decompression requires compressed size, uncompressed size, and flags. "
|
||||
+ "Use CreateDecompressStream(Stream, CompressionContext) overload with FormatOptions."
|
||||
);
|
||||
}
|
||||
|
||||
public Stream CreateDecompressStream(Stream source, CompressionContext context)
|
||||
{
|
||||
if (context.InputSize < 0 || context.OutputSize < 0)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"Explode decompression requires InputSize and OutputSize in CompressionContext.",
|
||||
nameof(context)
|
||||
);
|
||||
}
|
||||
|
||||
if (context.FormatOptions is not HeaderFlags flags)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"Explode decompression requires HeaderFlags in CompressionContext.FormatOptions.",
|
||||
nameof(context)
|
||||
);
|
||||
}
|
||||
|
||||
return ExplodeStream.Create(source, context.InputSize, context.OutputSize, flags);
|
||||
}
|
||||
}
|
||||
@@ -19,8 +19,24 @@ public sealed class GZipCompressionProvider : ICompressionProvider
|
||||
return new GZipStream(destination, CompressionMode.Compress, level);
|
||||
}
|
||||
|
||||
public Stream CreateCompressStream(
|
||||
Stream destination,
|
||||
int compressionLevel,
|
||||
CompressionContext context
|
||||
)
|
||||
{
|
||||
// Context not used for simple GZip compression
|
||||
return CreateCompressStream(destination, compressionLevel);
|
||||
}
|
||||
|
||||
public Stream CreateDecompressStream(Stream source)
|
||||
{
|
||||
return new GZipStream(source, CompressionMode.Decompress);
|
||||
}
|
||||
|
||||
public Stream CreateDecompressStream(Stream source, CompressionContext context)
|
||||
{
|
||||
// Context not used for simple GZip decompression
|
||||
return CreateDecompressStream(source);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,8 +18,24 @@ public sealed class LZipCompressionProvider : ICompressionProvider
|
||||
return new LZipStream(destination, CompressionMode.Compress);
|
||||
}
|
||||
|
||||
public Stream CreateCompressStream(
|
||||
Stream destination,
|
||||
int compressionLevel,
|
||||
CompressionContext context
|
||||
)
|
||||
{
|
||||
// Context not used for LZip compression
|
||||
return CreateCompressStream(destination, compressionLevel);
|
||||
}
|
||||
|
||||
public Stream CreateDecompressStream(Stream source)
|
||||
{
|
||||
return new LZipStream(source, CompressionMode.Decompress);
|
||||
}
|
||||
|
||||
public Stream CreateDecompressStream(Stream source, CompressionContext context)
|
||||
{
|
||||
// Context not used for LZip decompression
|
||||
return CreateDecompressStream(source);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Compressors.LZMA;
|
||||
|
||||
namespace SharpCompress.Compressors.Providers;
|
||||
|
||||
/// <summary>
|
||||
/// Provides LZMA compression and decompression using SharpCompress's internal implementation.
|
||||
/// This is a complex provider that requires initialization data for compression.
|
||||
/// </summary>
|
||||
public sealed class LzmaCompressingProvider : ICompressingProvider
|
||||
{
|
||||
public CompressionType CompressionType => CompressionType.LZMA;
|
||||
public bool SupportsCompression => true;
|
||||
public bool SupportsDecompression => true;
|
||||
|
||||
public Stream CreateCompressStream(Stream destination, int compressionLevel)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"LZMA compression requires context with CanSeek information. "
|
||||
+ "Use CreateCompressStream(Stream, int, CompressionContext) overload."
|
||||
);
|
||||
}
|
||||
|
||||
public Stream CreateCompressStream(
|
||||
Stream destination,
|
||||
int compressionLevel,
|
||||
CompressionContext context
|
||||
)
|
||||
{
|
||||
// LZMA stream creation returns the encoder stream
|
||||
// Note: Pre-compression data and properties are handled via ICompressingProvider methods
|
||||
var props = new LzmaEncoderProperties(!context.CanSeek);
|
||||
return LzmaStream.Create(props, false, destination);
|
||||
}
|
||||
|
||||
public Stream CreateDecompressStream(Stream source)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"LZMA decompression requires properties. "
|
||||
+ "Use CreateDecompressStream(Stream, CompressionContext) overload with Properties."
|
||||
);
|
||||
}
|
||||
|
||||
public Stream CreateDecompressStream(Stream source, CompressionContext context)
|
||||
{
|
||||
if (context.Properties is null || context.Properties.Length < 5)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"LZMA decompression requires Properties (at least 5 bytes) in CompressionContext.",
|
||||
nameof(context)
|
||||
);
|
||||
}
|
||||
|
||||
return LzmaStream.Create(context.Properties, source, context.InputSize, context.OutputSize);
|
||||
}
|
||||
|
||||
public byte[]? GetPreCompressionData(CompressionContext context)
|
||||
{
|
||||
// Zip format writes these magic bytes before the LZMA stream
|
||||
return new byte[] { 9, 20, 5, 0 };
|
||||
}
|
||||
|
||||
public byte[]? GetCompressionProperties(Stream stream, CompressionContext context)
|
||||
{
|
||||
// The LZMA stream exposes its properties after creation
|
||||
if (stream is LzmaStream lzmaStream)
|
||||
{
|
||||
return lzmaStream.Properties;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public byte[]? GetPostCompressionData(Stream stream, CompressionContext context)
|
||||
{
|
||||
// No post-compression data needed for LZMA in Zip
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -22,8 +22,25 @@ public sealed class LzwCompressionProvider : ICompressionProvider
|
||||
);
|
||||
}
|
||||
|
||||
public Stream CreateCompressStream(
|
||||
Stream destination,
|
||||
int compressionLevel,
|
||||
CompressionContext context
|
||||
)
|
||||
{
|
||||
throw new NotSupportedException(
|
||||
"LZW compression is not supported by SharpCompress's internal implementation."
|
||||
);
|
||||
}
|
||||
|
||||
public Stream CreateDecompressStream(Stream source)
|
||||
{
|
||||
return new LzwStream(source);
|
||||
}
|
||||
|
||||
public Stream CreateDecompressStream(Stream source, CompressionContext context)
|
||||
{
|
||||
// Context not used for LZW decompression
|
||||
return CreateDecompressStream(source);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Compressors.PPMd;
|
||||
|
||||
namespace SharpCompress.Compressors.Providers;
|
||||
|
||||
/// <summary>
|
||||
/// Provides PPMd compression and decompression using SharpCompress's internal implementation.
|
||||
/// This is a complex provider that requires initialization data for compression.
|
||||
/// </summary>
|
||||
public sealed class PpmdCompressingProvider : ICompressingProvider
|
||||
{
|
||||
public CompressionType CompressionType => CompressionType.PPMd;
|
||||
public bool SupportsCompression => true;
|
||||
public bool SupportsDecompression => true;
|
||||
|
||||
public Stream CreateCompressStream(Stream destination, int compressionLevel)
|
||||
{
|
||||
// Ppmd doesn't use compressionLevel, uses PpmdProperties instead
|
||||
var props = new PpmdProperties();
|
||||
return PpmdStream.Create(props, destination, true);
|
||||
}
|
||||
|
||||
public Stream CreateCompressStream(
|
||||
Stream destination,
|
||||
int compressionLevel,
|
||||
CompressionContext context
|
||||
)
|
||||
{
|
||||
// Context not used for Ppmd compression, but we could use FormatOptions for custom properties
|
||||
if (context.FormatOptions is PpmdProperties customProps)
|
||||
{
|
||||
return PpmdStream.Create(customProps, destination, true);
|
||||
}
|
||||
|
||||
return CreateCompressStream(destination, compressionLevel);
|
||||
}
|
||||
|
||||
public Stream CreateDecompressStream(Stream source)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"PPMd decompression requires properties. "
|
||||
+ "Use CreateDecompressStream(Stream, CompressionContext) overload with Properties."
|
||||
);
|
||||
}
|
||||
|
||||
public Stream CreateDecompressStream(Stream source, CompressionContext context)
|
||||
{
|
||||
if (context.Properties is null || context.Properties.Length < 2)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"PPMd decompression requires Properties (at least 2 bytes) in CompressionContext.",
|
||||
nameof(context)
|
||||
);
|
||||
}
|
||||
|
||||
var props = new PpmdProperties(context.Properties);
|
||||
return PpmdStream.Create(props, source, false);
|
||||
}
|
||||
|
||||
public byte[]? GetPreCompressionData(CompressionContext context)
|
||||
{
|
||||
// Ppmd writes its properties before the compressed data
|
||||
if (context.FormatOptions is PpmdProperties customProps)
|
||||
{
|
||||
return customProps.Properties;
|
||||
}
|
||||
|
||||
var defaultProps = new PpmdProperties();
|
||||
return defaultProps.Properties;
|
||||
}
|
||||
|
||||
public byte[]? GetCompressionProperties(Stream stream, CompressionContext context)
|
||||
{
|
||||
// Properties are already written in GetPreCompressionData
|
||||
return null;
|
||||
}
|
||||
|
||||
public byte[]? GetPostCompressionData(Stream stream, CompressionContext context)
|
||||
{
|
||||
// No post-compression data needed for Ppmd
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Compressors.Reduce;
|
||||
|
||||
namespace SharpCompress.Compressors.Providers;
|
||||
|
||||
/// <summary>
|
||||
/// Provides Reduce1 decompression using SharpCompress's internal implementation.
|
||||
/// Note: Reduce compression is not supported; this provider is decompression-only.
|
||||
/// </summary>
|
||||
public sealed class Reduce1CompressionProvider : ICompressionProvider
|
||||
{
|
||||
public CompressionType CompressionType => CompressionType.Reduce1;
|
||||
public bool SupportsCompression => false;
|
||||
public bool SupportsDecompression => true;
|
||||
|
||||
public Stream CreateCompressStream(Stream destination, int compressionLevel)
|
||||
{
|
||||
throw new NotSupportedException(
|
||||
"Reduce compression is not supported by SharpCompress's internal implementation."
|
||||
);
|
||||
}
|
||||
|
||||
public Stream CreateCompressStream(
|
||||
Stream destination,
|
||||
int compressionLevel,
|
||||
CompressionContext context
|
||||
)
|
||||
{
|
||||
throw new NotSupportedException(
|
||||
"Reduce compression is not supported by SharpCompress's internal implementation."
|
||||
);
|
||||
}
|
||||
|
||||
public Stream CreateDecompressStream(Stream source)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Reduce decompression requires compressed and uncompressed sizes. "
|
||||
+ "Use CreateDecompressStream(Stream, CompressionContext) overload."
|
||||
);
|
||||
}
|
||||
|
||||
public Stream CreateDecompressStream(Stream source, CompressionContext context)
|
||||
{
|
||||
if (context.InputSize < 0 || context.OutputSize < 0)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"Reduce decompression requires InputSize and OutputSize in CompressionContext.",
|
||||
nameof(context)
|
||||
);
|
||||
}
|
||||
|
||||
return ReduceStream.Create(source, context.InputSize, context.OutputSize, 1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Compressors.Reduce;
|
||||
|
||||
namespace SharpCompress.Compressors.Providers;
|
||||
|
||||
/// <summary>
|
||||
/// Provides Reduce2 decompression using SharpCompress's internal implementation.
|
||||
/// Note: Reduce compression is not supported; this provider is decompression-only.
|
||||
/// </summary>
|
||||
public sealed class Reduce2CompressionProvider : ICompressionProvider
|
||||
{
|
||||
public CompressionType CompressionType => CompressionType.Reduce2;
|
||||
public bool SupportsCompression => false;
|
||||
public bool SupportsDecompression => true;
|
||||
|
||||
public Stream CreateCompressStream(Stream destination, int compressionLevel)
|
||||
{
|
||||
throw new NotSupportedException(
|
||||
"Reduce compression is not supported by SharpCompress's internal implementation."
|
||||
);
|
||||
}
|
||||
|
||||
public Stream CreateCompressStream(
|
||||
Stream destination,
|
||||
int compressionLevel,
|
||||
CompressionContext context
|
||||
)
|
||||
{
|
||||
throw new NotSupportedException(
|
||||
"Reduce compression is not supported by SharpCompress's internal implementation."
|
||||
);
|
||||
}
|
||||
|
||||
public Stream CreateDecompressStream(Stream source)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Reduce decompression requires compressed and uncompressed sizes. "
|
||||
+ "Use CreateDecompressStream(Stream, CompressionContext) overload."
|
||||
);
|
||||
}
|
||||
|
||||
public Stream CreateDecompressStream(Stream source, CompressionContext context)
|
||||
{
|
||||
if (context.InputSize < 0 || context.OutputSize < 0)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"Reduce decompression requires InputSize and OutputSize in CompressionContext.",
|
||||
nameof(context)
|
||||
);
|
||||
}
|
||||
|
||||
return ReduceStream.Create(source, context.InputSize, context.OutputSize, 2);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Compressors.Reduce;
|
||||
|
||||
namespace SharpCompress.Compressors.Providers;
|
||||
|
||||
/// <summary>
|
||||
/// Provides Reduce3 decompression using SharpCompress's internal implementation.
|
||||
/// Note: Reduce compression is not supported; this provider is decompression-only.
|
||||
/// </summary>
|
||||
public sealed class Reduce3CompressionProvider : ICompressionProvider
|
||||
{
|
||||
public CompressionType CompressionType => CompressionType.Reduce3;
|
||||
public bool SupportsCompression => false;
|
||||
public bool SupportsDecompression => true;
|
||||
|
||||
public Stream CreateCompressStream(Stream destination, int compressionLevel)
|
||||
{
|
||||
throw new NotSupportedException(
|
||||
"Reduce compression is not supported by SharpCompress's internal implementation."
|
||||
);
|
||||
}
|
||||
|
||||
public Stream CreateCompressStream(
|
||||
Stream destination,
|
||||
int compressionLevel,
|
||||
CompressionContext context
|
||||
)
|
||||
{
|
||||
throw new NotSupportedException(
|
||||
"Reduce compression is not supported by SharpCompress's internal implementation."
|
||||
);
|
||||
}
|
||||
|
||||
public Stream CreateDecompressStream(Stream source)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Reduce decompression requires compressed and uncompressed sizes. "
|
||||
+ "Use CreateDecompressStream(Stream, CompressionContext) overload."
|
||||
);
|
||||
}
|
||||
|
||||
public Stream CreateDecompressStream(Stream source, CompressionContext context)
|
||||
{
|
||||
if (context.InputSize < 0 || context.OutputSize < 0)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"Reduce decompression requires InputSize and OutputSize in CompressionContext.",
|
||||
nameof(context)
|
||||
);
|
||||
}
|
||||
|
||||
return ReduceStream.Create(source, context.InputSize, context.OutputSize, 3);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Compressors.Reduce;
|
||||
|
||||
namespace SharpCompress.Compressors.Providers;
|
||||
|
||||
/// <summary>
|
||||
/// Provides Reduce4 decompression using SharpCompress's internal implementation.
|
||||
/// Note: Reduce compression is not supported; this provider is decompression-only.
|
||||
/// </summary>
|
||||
public sealed class Reduce4CompressionProvider : ICompressionProvider
|
||||
{
|
||||
public CompressionType CompressionType => CompressionType.Reduce4;
|
||||
public bool SupportsCompression => false;
|
||||
public bool SupportsDecompression => true;
|
||||
|
||||
public Stream CreateCompressStream(Stream destination, int compressionLevel)
|
||||
{
|
||||
throw new NotSupportedException(
|
||||
"Reduce compression is not supported by SharpCompress's internal implementation."
|
||||
);
|
||||
}
|
||||
|
||||
public Stream CreateCompressStream(
|
||||
Stream destination,
|
||||
int compressionLevel,
|
||||
CompressionContext context
|
||||
)
|
||||
{
|
||||
throw new NotSupportedException(
|
||||
"Reduce compression is not supported by SharpCompress's internal implementation."
|
||||
);
|
||||
}
|
||||
|
||||
public Stream CreateDecompressStream(Stream source)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Reduce decompression requires compressed and uncompressed sizes. "
|
||||
+ "Use CreateDecompressStream(Stream, CompressionContext) overload."
|
||||
);
|
||||
}
|
||||
|
||||
public Stream CreateDecompressStream(Stream source, CompressionContext context)
|
||||
{
|
||||
if (context.InputSize < 0 || context.OutputSize < 0)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"Reduce decompression requires InputSize and OutputSize in CompressionContext.",
|
||||
nameof(context)
|
||||
);
|
||||
}
|
||||
|
||||
return ReduceStream.Create(source, context.InputSize, context.OutputSize, 4);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Compressors.Shrink;
|
||||
|
||||
namespace SharpCompress.Compressors.Providers;
|
||||
|
||||
/// <summary>
|
||||
/// Provides Shrink decompression using SharpCompress's internal implementation.
|
||||
/// Note: Shrink compression is not supported; this provider is decompression-only.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Shrink requires compressed and uncompressed sizes which must be provided via CompressionContext.
|
||||
/// </remarks>
|
||||
public sealed class ShrinkCompressionProvider : ICompressionProvider
|
||||
{
|
||||
public CompressionType CompressionType => CompressionType.Shrink;
|
||||
public bool SupportsCompression => false;
|
||||
public bool SupportsDecompression => true;
|
||||
|
||||
public Stream CreateCompressStream(Stream destination, int compressionLevel)
|
||||
{
|
||||
throw new NotSupportedException(
|
||||
"Shrink compression is not supported by SharpCompress's internal implementation."
|
||||
);
|
||||
}
|
||||
|
||||
public Stream CreateCompressStream(
|
||||
Stream destination,
|
||||
int compressionLevel,
|
||||
CompressionContext context
|
||||
)
|
||||
{
|
||||
throw new NotSupportedException(
|
||||
"Shrink compression is not supported by SharpCompress's internal implementation."
|
||||
);
|
||||
}
|
||||
|
||||
public Stream CreateDecompressStream(Stream source)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Shrink decompression requires compressed and uncompressed sizes. "
|
||||
+ "Use CreateDecompressStream(Stream, CompressionContext) overload."
|
||||
);
|
||||
}
|
||||
|
||||
public Stream CreateDecompressStream(Stream source, CompressionContext context)
|
||||
{
|
||||
if (context.InputSize < 0 || context.OutputSize < 0)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
"Shrink decompression requires InputSize and OutputSize in CompressionContext.",
|
||||
nameof(context)
|
||||
);
|
||||
}
|
||||
|
||||
return new ShrinkStream(
|
||||
source,
|
||||
CompressionMode.Decompress,
|
||||
context.InputSize,
|
||||
context.OutputSize
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,16 @@ public sealed class SystemDeflateCompressionProvider : ICompressionProvider
|
||||
return new DeflateStream(destination, bclLevel, leaveOpen: true);
|
||||
}
|
||||
|
||||
public Stream CreateCompressStream(
|
||||
Stream destination,
|
||||
int compressionLevel,
|
||||
CompressionContext context
|
||||
)
|
||||
{
|
||||
// Context not used for simple Deflate compression
|
||||
return CreateCompressStream(destination, compressionLevel);
|
||||
}
|
||||
|
||||
public Stream CreateDecompressStream(Stream source)
|
||||
{
|
||||
return new DeflateStream(
|
||||
@@ -33,6 +43,12 @@ public sealed class SystemDeflateCompressionProvider : ICompressionProvider
|
||||
);
|
||||
}
|
||||
|
||||
public Stream CreateDecompressStream(Stream source, CompressionContext context)
|
||||
{
|
||||
// Context not used for simple Deflate decompression
|
||||
return CreateDecompressStream(source);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps SharpCompress compression level (0-9) to BCL CompressionLevel.
|
||||
/// </summary>
|
||||
|
||||
@@ -24,6 +24,16 @@ public sealed class SystemGZipCompressionProvider : ICompressionProvider
|
||||
return new GZipStream(destination, bclLevel, leaveOpen: true);
|
||||
}
|
||||
|
||||
public Stream CreateCompressStream(
|
||||
Stream destination,
|
||||
int compressionLevel,
|
||||
CompressionContext context
|
||||
)
|
||||
{
|
||||
// Context not used for simple GZip compression
|
||||
return CreateCompressStream(destination, compressionLevel);
|
||||
}
|
||||
|
||||
public Stream CreateDecompressStream(Stream source)
|
||||
{
|
||||
return new GZipStream(
|
||||
@@ -33,6 +43,12 @@ public sealed class SystemGZipCompressionProvider : ICompressionProvider
|
||||
);
|
||||
}
|
||||
|
||||
public Stream CreateDecompressStream(Stream source, CompressionContext context)
|
||||
{
|
||||
// Context not used for simple GZip decompression
|
||||
return CreateDecompressStream(source);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps SharpCompress compression level (0-9) to BCL CompressionLevel.
|
||||
/// </summary>
|
||||
|
||||
@@ -22,8 +22,25 @@ public sealed class XzCompressionProvider : ICompressionProvider
|
||||
);
|
||||
}
|
||||
|
||||
public Stream CreateCompressStream(
|
||||
Stream destination,
|
||||
int compressionLevel,
|
||||
CompressionContext context
|
||||
)
|
||||
{
|
||||
throw new NotSupportedException(
|
||||
"XZ compression is not supported by SharpCompress's internal implementation."
|
||||
);
|
||||
}
|
||||
|
||||
public Stream CreateDecompressStream(Stream source)
|
||||
{
|
||||
return new XZStream(source);
|
||||
}
|
||||
|
||||
public Stream CreateDecompressStream(Stream source, CompressionContext context)
|
||||
{
|
||||
// Context not used for XZ decompression
|
||||
return CreateDecompressStream(source);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,8 +18,24 @@ public sealed class ZStandardCompressionProvider : ICompressionProvider
|
||||
return new ZStd.CompressionStream(destination, compressionLevel);
|
||||
}
|
||||
|
||||
public Stream CreateCompressStream(
|
||||
Stream destination,
|
||||
int compressionLevel,
|
||||
CompressionContext context
|
||||
)
|
||||
{
|
||||
// Context not used for ZStandard compression
|
||||
return CreateCompressStream(destination, compressionLevel);
|
||||
}
|
||||
|
||||
public Stream CreateDecompressStream(Stream source)
|
||||
{
|
||||
return new ZStd.DecompressionStream(source);
|
||||
}
|
||||
|
||||
public Stream CreateDecompressStream(Stream source, CompressionContext context)
|
||||
{
|
||||
// Context not used for ZStandard decompression
|
||||
return CreateDecompressStream(source);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Threading.Tasks;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Common.Zip;
|
||||
using SharpCompress.Common.Zip.Headers;
|
||||
using SharpCompress.Compressors;
|
||||
|
||||
namespace SharpCompress.Readers.Zip;
|
||||
|
||||
@@ -18,16 +19,28 @@ public partial class ZipReader
|
||||
{
|
||||
private readonly StreamingZipHeaderFactory _headerFactory;
|
||||
private readonly Stream _stream;
|
||||
private readonly CompressionProviderRegistry? _compressionProviders;
|
||||
|
||||
public ZipEntryAsyncEnumerable(StreamingZipHeaderFactory headerFactory, Stream stream)
|
||||
public ZipEntryAsyncEnumerable(
|
||||
StreamingZipHeaderFactory headerFactory,
|
||||
Stream stream,
|
||||
CompressionProviderRegistry? compressionProviders
|
||||
)
|
||||
{
|
||||
_headerFactory = headerFactory;
|
||||
_stream = stream;
|
||||
_compressionProviders = compressionProviders;
|
||||
}
|
||||
|
||||
public IAsyncEnumerator<ZipEntry> GetAsyncEnumerator(
|
||||
CancellationToken cancellationToken = default
|
||||
) => new ZipEntryAsyncEnumerator(_headerFactory, _stream, cancellationToken);
|
||||
) =>
|
||||
new ZipEntryAsyncEnumerator(
|
||||
_headerFactory,
|
||||
_stream,
|
||||
_compressionProviders,
|
||||
cancellationToken
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -37,15 +50,18 @@ public partial class ZipReader
|
||||
{
|
||||
private readonly Stream _stream;
|
||||
private readonly IAsyncEnumerator<ZipHeader> _headerEnumerator;
|
||||
private readonly CompressionProviderRegistry? _compressionProviders;
|
||||
private ZipEntry? _current;
|
||||
|
||||
public ZipEntryAsyncEnumerator(
|
||||
StreamingZipHeaderFactory headerFactory,
|
||||
Stream stream,
|
||||
CompressionProviderRegistry? compressionProviders,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
_stream = stream;
|
||||
_compressionProviders = compressionProviders;
|
||||
_headerEnumerator = headerFactory
|
||||
.ReadStreamHeaderAsync(stream)
|
||||
.GetAsyncEnumerator(cancellationToken);
|
||||
@@ -67,7 +83,11 @@ public partial class ZipReader
|
||||
{
|
||||
case ZipHeaderType.LocalEntry:
|
||||
_current = new ZipEntry(
|
||||
new StreamingZipFilePart((LocalEntryHeader)header, _stream)
|
||||
new StreamingZipFilePart(
|
||||
(LocalEntryHeader)header,
|
||||
_stream,
|
||||
_compressionProviders
|
||||
)
|
||||
);
|
||||
return true;
|
||||
case ZipHeaderType.DirectoryEntry:
|
||||
|
||||
@@ -74,7 +74,11 @@ public partial class ZipReader : AbstractReader<ZipEntry, ZipVolume>
|
||||
case ZipHeaderType.LocalEntry:
|
||||
{
|
||||
yield return new ZipEntry(
|
||||
new StreamingZipFilePart((LocalEntryHeader)h, stream)
|
||||
new StreamingZipFilePart(
|
||||
(LocalEntryHeader)h,
|
||||
stream,
|
||||
Options.CompressionProviders
|
||||
)
|
||||
);
|
||||
}
|
||||
break;
|
||||
@@ -99,7 +103,7 @@ public partial class ZipReader : AbstractReader<ZipEntry, ZipVolume>
|
||||
/// Returns entries asynchronously for streams that only support async reads.
|
||||
/// </summary>
|
||||
protected override IAsyncEnumerable<ZipEntry> GetEntriesAsync(Stream stream) =>
|
||||
new ZipEntryAsyncEnumerable(_headerFactory, stream);
|
||||
new ZipEntryAsyncEnumerable(_headerFactory, stream, Options.CompressionProviders);
|
||||
|
||||
// Async nested classes moved to ZipReader.Async.cs
|
||||
}
|
||||
|
||||
@@ -448,25 +448,70 @@ public partial class ZipWriter : AbstractWriter
|
||||
}
|
||||
case ZipCompressionMethod.LZMA:
|
||||
{
|
||||
// LZMA has complex initialization that doesn't fit the provider pattern
|
||||
counting.WriteByte(9);
|
||||
counting.WriteByte(20);
|
||||
counting.WriteByte(5);
|
||||
counting.WriteByte(0);
|
||||
|
||||
var lzmaStream = LzmaStream.Create(
|
||||
new LzmaEncoderProperties(!originalStream.CanSeek),
|
||||
false,
|
||||
counting
|
||||
// Use ICompressingProvider for complex initialization
|
||||
var compressingProvider = providers.GetCompressingProvider(
|
||||
CompressionType.LZMA
|
||||
);
|
||||
counting.Write(lzmaStream.Properties, 0, lzmaStream.Properties.Length);
|
||||
if (compressingProvider is null)
|
||||
{
|
||||
throw new InvalidOperationException("LZMA compression provider not found.");
|
||||
}
|
||||
|
||||
var context = new CompressionContext { CanSeek = originalStream.CanSeek };
|
||||
|
||||
// Write pre-compression data (magic bytes)
|
||||
var preData = compressingProvider.GetPreCompressionData(context);
|
||||
if (preData != null)
|
||||
{
|
||||
counting.Write(preData, 0, preData.Length);
|
||||
}
|
||||
|
||||
// Create compression stream
|
||||
var lzmaStream = compressingProvider.CreateCompressStream(
|
||||
counting,
|
||||
compressionLevel,
|
||||
context
|
||||
);
|
||||
|
||||
// Write compression properties
|
||||
var props = compressingProvider.GetCompressionProperties(lzmaStream, context);
|
||||
if (props != null)
|
||||
{
|
||||
counting.Write(props, 0, props.Length);
|
||||
}
|
||||
|
||||
return lzmaStream;
|
||||
}
|
||||
case ZipCompressionMethod.PPMd:
|
||||
{
|
||||
// PPMd has complex initialization that doesn't fit the provider pattern
|
||||
counting.Write(writer.PpmdProperties.Properties, 0, 2);
|
||||
return PpmdStream.Create(writer.PpmdProperties, counting, true);
|
||||
// Use ICompressingProvider for complex initialization
|
||||
var compressingProvider = providers.GetCompressingProvider(
|
||||
CompressionType.PPMd
|
||||
);
|
||||
if (compressingProvider is null)
|
||||
{
|
||||
throw new InvalidOperationException("PPMd compression provider not found.");
|
||||
}
|
||||
|
||||
var context = new CompressionContext
|
||||
{
|
||||
CanSeek = originalStream.CanSeek,
|
||||
FormatOptions = writer.PpmdProperties,
|
||||
};
|
||||
|
||||
// Write pre-compression data (properties)
|
||||
var preData = compressingProvider.GetPreCompressionData(context);
|
||||
if (preData != null)
|
||||
{
|
||||
counting.Write(preData, 0, preData.Length);
|
||||
}
|
||||
|
||||
// Create compression stream
|
||||
return compressingProvider.CreateCompressStream(
|
||||
counting,
|
||||
compressionLevel,
|
||||
context
|
||||
);
|
||||
}
|
||||
case ZipCompressionMethod.ZStandard:
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user