Compare commits

..

4 Commits

Author SHA1 Message Date
copilot-swe-agent[bot]
c825ae91a1 Complete fix for Microsoft.Bcl.AsyncInterfaces version conflict
Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com>
2026-02-11 22:15:38 +00:00
copilot-swe-agent[bot]
6478723a8d Format code with CSharpier
Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com>
2026-02-11 22:10:48 +00:00
copilot-swe-agent[bot]
2c7e9b9c21 Fix Microsoft.Bcl.AsyncInterfaces version conflict by downgrading to 8.0.0
Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com>
2026-02-11 22:10:01 +00:00
copilot-swe-agent[bot]
7f3da598d1 Initial plan 2026-02-11 22:04:45 +00:00
147 changed files with 2696 additions and 6323 deletions

View File

@@ -53,9 +53,9 @@ jobs:
name: ${{ matrix.os }}-nuget-package
path: artifacts/*.nupkg
# Push to NuGet.org only for version tag pushes (Windows only)
# Push to NuGet.org using C# build target (Windows only, not on PRs)
- name: Push to NuGet
if: success() && matrix.os == 'windows-latest' && startsWith(github.ref, 'refs/tags/')
if: success() && matrix.os == 'windows-latest' && github.event_name != 'pull_request'
run: dotnet run --project build/build.csproj -- push-to-nuget
env:
NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}

View File

@@ -103,11 +103,8 @@ tests/
### Factory Pattern
Factory implementations can implement one or more interfaces (`IArchiveFactory`, `IReaderFactory`, `IWriterFactory`) depending on format capabilities:
- `ArchiveFactory.OpenArchive()` - Opens archive API objects from seekable streams/files
- `ArchiveFactory.OpenAsyncArchive()` - Opens async archive API objects for async archive use cases
- `ReaderFactory.OpenReader()` - Auto-detects and opens forward-only readers
- `ReaderFactory.OpenAsyncReader()` - Auto-detects and opens forward-only async readers
- `WriterFactory.OpenWriter()` - Creates a writer for a specified `ArchiveType`
- `WriterFactory.OpenAsyncWriter()` - Creates an async writer for async write scenarios
- Factories located in: `src/SharpCompress/Factories/`
## Nullable Reference Types
@@ -135,9 +132,6 @@ SharpCompress supports multiple archive and compression formats:
### Async/Await Patterns
- All I/O operations support async/await with `CancellationToken`
- Async methods follow the naming convention: `MethodNameAsync`
- For async archive scenarios, prefer `ArchiveFactory.OpenAsyncArchive(...)` over sync `OpenArchive(...)`.
- For async forward-only read scenarios, prefer `ReaderFactory.OpenAsyncReader(...)` over sync `OpenReader(...)`.
- For async write scenarios, prefer `WriterFactory.OpenAsyncWriter(...)` over sync `OpenWriter(...)`.
- Key async methods:
- `WriteEntryToAsync` - Extract entry asynchronously
- `WriteAllToDirectoryAsync` - Extract all entries asynchronously
@@ -205,8 +199,7 @@ SharpCompress supports multiple archive and compression formats:
## Common Pitfalls
1. **Don't mix Archive and Reader APIs** - Archive needs seekable stream, Reader doesn't
2. **Don't mix sync and async open paths** - For async workflows use `OpenAsyncArchive`/`OpenAsyncReader`/`OpenAsyncWriter`, not `OpenArchive`/`OpenReader`/`OpenWriter`
3. **Solid archives (Rar, 7Zip)** - Use `ExtractAllEntries()` for best performance, not individual entry extraction
4. **Stream disposal** - Always set `LeaveStreamOpen` explicitly when needed (default is to close)
5. **Tar + non-seekable stream** - Must provide file size or it will throw
6. **Format detection** - Use `ReaderFactory.OpenReader()` / `ReaderFactory.OpenAsyncReader()` for auto-detection, test with actual archive files
2. **Solid archives (Rar, 7Zip)** - Use `ExtractAllEntries()` for best performance, not individual entry extraction
3. **Stream disposal** - Always set `LeaveStreamOpen` explicitly when needed (default is to close)
4. **Tar + non-seekable stream** - Must provide file size or it will throw
5. **Format detection** - Use `ReaderFactory.OpenReader()` for auto-detection, test with actual archive files

View File

@@ -5,7 +5,7 @@
<PackageVersion Include="AwesomeAssertions" Version="9.3.0" />
<PackageVersion Include="Glob" Version="1.1.9" />
<PackageVersion Include="JetBrains.Profiler.SelfApi" Version="2.5.16" />
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="10.0.0" />
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="8.0.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageVersion Include="Mono.Posix.NETStandard" Version="1.0.0" />
<PackageVersion Include="SimpleExec" Version="13.0.0" />

View File

@@ -16,10 +16,6 @@ Post Issues on Github!
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 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
In general, I recommend GZip (Deflate)/BZip2 (BZip)/LZip (LZMA) as the simplicity of the formats lend to better long term archival as well as the streamability. Tar is often used in conjunction for multiple files in a single archive (e.g. `.tar.gz`)

View File

@@ -95,7 +95,7 @@ using (var archive = ZipArchive.OpenArchive("file.zip"))
}
// Async extraction (requires IAsyncArchive)
await using (var asyncArchive = await ZipArchive.OpenAsyncArchive("file.zip"))
using (var asyncArchive = await ZipArchive.OpenAsyncArchive("file.zip"))
{
await asyncArchive.WriteToDirectoryAsync(
@"C:\output",
@@ -177,7 +177,7 @@ using (var reader = ReaderFactory.OpenReader(stream))
// Async variants (use OpenAsyncReader to get IAsyncReader)
using (var stream = File.OpenRead("file.zip"))
await using (var reader = await ReaderFactory.OpenAsyncReader(stream))
using (var reader = await ReaderFactory.OpenAsyncReader(stream))
{
while (await reader.MoveToNextEntryAsync())
{
@@ -318,24 +318,6 @@ WriterOptions: write-time behavior (compression type/level, encoding, stream own
ZipWriterEntryOptions: per-entry ZIP overrides (compression, level, timestamps, comments, zip64)
```
### 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`). 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());
var readerOptions = ReaderOptions.ForOwnedFile().WithProviders(registry);
var writerOptions = new WriterOptions(CompressionType.GZip)
{
CompressionLevel = 6,
}.WithProviders(registry);
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`, including pre/properties/post compression hook data.
---
## Compression Types
@@ -427,7 +409,7 @@ cts.CancelAfter(TimeSpan.FromMinutes(5));
try
{
await using (var archive = await ZipArchive.OpenAsyncArchive("archive.zip"))
using (var archive = await ZipArchive.OpenAsyncArchive("archive.zip"))
{
await archive.WriteToDirectoryAsync(
@"C:\output",

View File

@@ -206,29 +206,6 @@ foreach(var entry in archive.Entries)
}
```
## Custom Compression Providers
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#
var systemGZip = new SystemGZipCompressionProvider();
var customRegistry = CompressionProviderRegistry.Default.With(systemGZip);
var readerOptions = ReaderOptions.ForOwnedFile()
.WithProviders(customRegistry);
using var reader = ReaderFactory.OpenReader(stream, readerOptions);
var writerOptions = new WriterOptions(CompressionType.GZip)
.WithProviders(customRegistry);
using var writer = WriterFactory.OpenWriter(outputStream, ArchiveType.GZip, writerOptions);
```
The registry also exposes `GetCompressingProvider` (now returning `ICompressionProviderHooks`) when a compression format needs pre- or post-stream data (e.g., LZMA/PPMd). Implementations that need extra headers can supply those bytes through the `ICompressionProviderHooks` members while the rest of the API still works through the `Providers` property.
## Async Examples
### Async Reader Examples

View File

@@ -22,9 +22,7 @@ public static partial class ArchiveFactory
readerOptions ??= ReaderOptions.ForExternalStream;
var factory = await FindFactoryAsync<IArchiveFactory>(stream, cancellationToken)
.ConfigureAwait(false);
return await factory
.OpenAsyncArchive(stream, readerOptions, cancellationToken)
.ConfigureAwait(false);
return factory.OpenAsyncArchive(stream, readerOptions);
}
public static ValueTask<IAsyncArchive> OpenAsyncArchive(
@@ -47,9 +45,7 @@ public static partial class ArchiveFactory
var factory = await FindFactoryAsync<IArchiveFactory>(fileInfo, cancellationToken)
.ConfigureAwait(false);
return await factory
.OpenAsyncArchive(fileInfo, options, cancellationToken)
.ConfigureAwait(false);
return factory.OpenAsyncArchive(fileInfo, options);
}
public static async ValueTask<IAsyncArchive> OpenAsyncArchive(

View File

@@ -2,9 +2,12 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
using SharpCompress.Common.Options;
using SharpCompress.Factories;
using SharpCompress.IO;
using SharpCompress.Readers;
namespace SharpCompress.Archives;

View File

@@ -77,7 +77,7 @@ public partial class GZipArchive
{
var stream = Volumes.Single().Stream;
stream.Position = 0;
return new((IAsyncReader)GZipReader.OpenReader(stream, ReaderOptions));
return new((IAsyncReader)GZipReader.OpenReader(stream));
}
protected override async IAsyncEnumerable<GZipArchiveEntry> LoadEntriesAsync(
@@ -88,7 +88,7 @@ public partial class GZipArchive
yield return new GZipArchiveEntry(
this,
await GZipFilePart
.CreateAsync(stream, ReaderOptions.ArchiveEncoding, ReaderOptions.Providers)
.CreateAsync(stream, ReaderOptions.ArchiveEncoding)
.ConfigureAwait(false),
ReaderOptions
);

View File

@@ -20,15 +20,14 @@ public partial class GZipArchive
>
#endif
{
public static ValueTask<IWritableAsyncArchive<GZipWriterOptions>> OpenAsyncArchive(
public static IWritableAsyncArchive<GZipWriterOptions> OpenAsyncArchive(
string path,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
ReaderOptions? readerOptions = null
)
{
cancellationToken.ThrowIfCancellationRequested();
path.NotNullOrEmpty(nameof(path));
return OpenAsyncArchive(new FileInfo(path), readerOptions, cancellationToken);
return (IWritableAsyncArchive<GZipWriterOptions>)
OpenArchive(new FileInfo(path), readerOptions ?? new ReaderOptions());
}
public static IWritableArchive<GZipWriterOptions> OpenArchive(
@@ -104,50 +103,30 @@ public partial class GZipArchive
);
}
public static ValueTask<IWritableAsyncArchive<GZipWriterOptions>> OpenAsyncArchive(
public static IWritableAsyncArchive<GZipWriterOptions> OpenAsyncArchive(
Stream stream,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IWritableAsyncArchive<GZipWriterOptions>)OpenArchive(stream, readerOptions));
}
ReaderOptions? readerOptions = null
) => (IWritableAsyncArchive<GZipWriterOptions>)OpenArchive(stream, readerOptions);
public static ValueTask<IWritableAsyncArchive<GZipWriterOptions>> OpenAsyncArchive(
public static IWritableAsyncArchive<GZipWriterOptions> OpenAsyncArchive(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IWritableAsyncArchive<GZipWriterOptions>)OpenArchive(fileInfo, readerOptions));
}
ReaderOptions? readerOptions = null
) => (IWritableAsyncArchive<GZipWriterOptions>)OpenArchive(fileInfo, readerOptions);
public static ValueTask<IWritableAsyncArchive<GZipWriterOptions>> OpenAsyncArchive(
public static IWritableAsyncArchive<GZipWriterOptions> OpenAsyncArchive(
IReadOnlyList<Stream> streams,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IWritableAsyncArchive<GZipWriterOptions>)OpenArchive(streams, readerOptions));
}
ReaderOptions? readerOptions = null
) => (IWritableAsyncArchive<GZipWriterOptions>)OpenArchive(streams, readerOptions);
public static ValueTask<IWritableAsyncArchive<GZipWriterOptions>> OpenAsyncArchive(
public static IWritableAsyncArchive<GZipWriterOptions> OpenAsyncArchive(
IReadOnlyList<FileInfo> fileInfos,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IWritableAsyncArchive<GZipWriterOptions>)OpenArchive(fileInfos, readerOptions));
}
ReaderOptions? readerOptions = null
) => (IWritableAsyncArchive<GZipWriterOptions>)OpenArchive(fileInfos, readerOptions);
public static IWritableArchive<GZipWriterOptions> CreateArchive() => new GZipArchive();
public static ValueTask<IWritableAsyncArchive<GZipWriterOptions>> CreateAsyncArchive() =>
new(new GZipArchive());
public static IWritableAsyncArchive<GZipWriterOptions> CreateAsyncArchive() =>
new GZipArchive();
public static bool IsGZipFile(string filePath) => IsGZipFile(new FileInfo(filePath));

View File

@@ -87,7 +87,7 @@ public partial class GZipArchive
var stream = volumes.Single().Stream;
yield return new GZipArchiveEntry(
this,
GZipFilePart.Create(stream, ReaderOptions.ArchiveEncoding, ReaderOptions.Providers),
GZipFilePart.Create(stream, ReaderOptions.ArchiveEncoding),
ReaderOptions
);
}
@@ -96,6 +96,6 @@ public partial class GZipArchive
{
var stream = Volumes.Single().Stream;
stream.Position = 0;
return GZipReader.OpenReader(stream, ReaderOptions);
return GZipReader.OpenReader(stream);
}
}

View File

@@ -24,20 +24,10 @@ public class GZipArchiveEntry : GZipEntry, IArchiveEntry
return Parts.Single().GetCompressedStream().NotNull();
}
public async ValueTask<Stream> OpenEntryStreamAsync(
CancellationToken cancellationToken = default
)
public ValueTask<Stream> OpenEntryStreamAsync(CancellationToken cancellationToken = default)
{
// Reset the stream position if seekable
var part = (GZipFilePart)Parts.Single();
var rawStream = part.GetRawStream();
if (rawStream.CanSeek && rawStream.Position != part.EntryStartPosition)
{
rawStream.Position = part.EntryStartPosition;
}
return (
await Parts.Single().GetCompressedStreamAsync(cancellationToken).ConfigureAwait(false)
).NotNull();
// GZip synchronous implementation is fast enough, just wrap it
return new(OpenEntryStream());
}
#region IArchiveEntry Members

View File

@@ -1,6 +1,5 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Factories;
using SharpCompress.Readers;
@@ -33,13 +32,7 @@ public interface IArchiveFactory : IFactory
/// </summary>
/// <param name="stream">An open, readable and seekable stream.</param>
/// <param name="readerOptions">reading options.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>A <see cref="ValueTask{TResult}"/> containing the opened async archive.</returns>
ValueTask<IAsyncArchive> OpenAsyncArchive(
Stream stream,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
);
IAsyncArchive OpenAsyncArchive(Stream stream, ReaderOptions? readerOptions = null);
/// <summary>
/// Constructor with a FileInfo object to an existing file.
@@ -54,10 +47,5 @@ public interface IArchiveFactory : IFactory
/// <param name="fileInfo">the file to open.</param>
/// <param name="readerOptions">reading options.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>A <see cref="ValueTask{TResult}"/> containing the opened async archive.</returns>
ValueTask<IAsyncArchive> OpenAsyncArchive(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
);
IAsyncArchive OpenAsyncArchive(FileInfo fileInfo, ReaderOptions? readerOptions = null);
}

View File

@@ -1,7 +1,6 @@
#if NET8_0_OR_GREATER
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Readers;
namespace SharpCompress.Archives;
@@ -19,22 +18,19 @@ public interface IArchiveOpenable<TSync, TASync>
public static abstract TSync OpenArchive(Stream stream, ReaderOptions? readerOptions = null);
public static abstract ValueTask<TASync> OpenAsyncArchive(
public static abstract TASync OpenAsyncArchive(
string path,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
ReaderOptions? readerOptions = null
);
public static abstract ValueTask<TASync> OpenAsyncArchive(
public static abstract TASync OpenAsyncArchive(
Stream stream,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
ReaderOptions? readerOptions = null
);
public static abstract ValueTask<TASync> OpenAsyncArchive(
public static abstract TASync OpenAsyncArchive(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
ReaderOptions? readerOptions = null
);
}

View File

@@ -2,7 +2,6 @@
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Readers;
namespace SharpCompress.Archives;
@@ -21,16 +20,14 @@ public interface IMultiArchiveOpenable<TSync, TASync>
ReaderOptions? readerOptions = null
);
public static abstract ValueTask<TASync> OpenAsyncArchive(
public static abstract TASync OpenAsyncArchive(
IReadOnlyList<Stream> streams,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
ReaderOptions? readerOptions = null
);
public static abstract ValueTask<TASync> OpenAsyncArchive(
public static abstract TASync OpenAsyncArchive(
IReadOnlyList<FileInfo> fileInfos,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
ReaderOptions? readerOptions = null
);
}
#endif

View File

@@ -1,4 +1,3 @@
using System.Threading.Tasks;
using SharpCompress.Common.Options;
#if NET8_0_OR_GREATER
@@ -9,6 +8,6 @@ public interface IWritableArchiveOpenable<TOptions>
where TOptions : IWriterOptions
{
public static abstract IWritableArchive<TOptions> CreateArchive();
public static abstract ValueTask<IWritableAsyncArchive<TOptions>> CreateAsyncArchive();
public static abstract IWritableAsyncArchive<TOptions> CreateAsyncArchive();
}
#endif

View File

@@ -20,15 +20,13 @@ public partial class RarArchive
IMultiArchiveOpenable<IRarArchive, IRarAsyncArchive>
#endif
{
public static ValueTask<IRarAsyncArchive> OpenAsyncArchive(
public static IRarAsyncArchive OpenAsyncArchive(
string path,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
ReaderOptions? readerOptions = null
)
{
cancellationToken.ThrowIfCancellationRequested();
path.NotNullOrEmpty(nameof(path));
return new((IRarAsyncArchive)OpenArchive(new FileInfo(path), readerOptions));
return (IRarAsyncArchive)OpenArchive(new FileInfo(path), readerOptions);
}
public static IRarArchive OpenArchive(string filePath, ReaderOptions? options = null)
@@ -100,44 +98,36 @@ public partial class RarArchive
);
}
public static ValueTask<IRarAsyncArchive> OpenAsyncArchive(
public static IRarAsyncArchive OpenAsyncArchive(
Stream stream,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
ReaderOptions? readerOptions = null
)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IRarAsyncArchive)OpenArchive(stream, readerOptions));
return (IRarAsyncArchive)OpenArchive(stream, readerOptions);
}
public static ValueTask<IRarAsyncArchive> OpenAsyncArchive(
public static IRarAsyncArchive OpenAsyncArchive(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
ReaderOptions? readerOptions = null
)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IRarAsyncArchive)OpenArchive(fileInfo, readerOptions));
return (IRarAsyncArchive)OpenArchive(fileInfo, readerOptions);
}
public static ValueTask<IRarAsyncArchive> OpenAsyncArchive(
public static IRarAsyncArchive OpenAsyncArchive(
IReadOnlyList<Stream> streams,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
ReaderOptions? readerOptions = null
)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IRarAsyncArchive)OpenArchive(streams, readerOptions));
return (IRarAsyncArchive)OpenArchive(streams, readerOptions);
}
public static ValueTask<IRarAsyncArchive> OpenAsyncArchive(
public static IRarAsyncArchive OpenAsyncArchive(
IReadOnlyList<FileInfo> fileInfos,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
ReaderOptions? readerOptions = null
)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IRarAsyncArchive)OpenArchive(fileInfos, readerOptions));
return (IRarAsyncArchive)OpenArchive(fileInfos, readerOptions);
}
public static bool IsRarFile(string filePath) => IsRarFile(new FileInfo(filePath));

View File

@@ -16,17 +16,10 @@ public partial class SevenZipArchive
IMultiArchiveOpenable<IArchive, IAsyncArchive>
#endif
{
public static ValueTask<IAsyncArchive> OpenAsyncArchive(
string path,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
public static IAsyncArchive OpenAsyncArchive(string path, ReaderOptions? readerOptions = null)
{
cancellationToken.ThrowIfCancellationRequested();
path.NotNullOrEmpty("path");
return new(
(IAsyncArchive)OpenArchive(new FileInfo(path), readerOptions ?? new ReaderOptions())
);
return (IAsyncArchive)OpenArchive(new FileInfo(path), readerOptions ?? new ReaderOptions());
}
public static IArchive OpenArchive(string filePath, ReaderOptions? readerOptions = null)
@@ -93,44 +86,33 @@ public partial class SevenZipArchive
);
}
public static ValueTask<IAsyncArchive> OpenAsyncArchive(
Stream stream,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
public static IAsyncArchive OpenAsyncArchive(Stream stream, ReaderOptions? readerOptions = null)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IAsyncArchive)OpenArchive(stream, readerOptions));
return (IAsyncArchive)OpenArchive(stream, readerOptions);
}
public static ValueTask<IAsyncArchive> OpenAsyncArchive(
public static IAsyncArchive OpenAsyncArchive(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
ReaderOptions? readerOptions = null
)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IAsyncArchive)OpenArchive(fileInfo, readerOptions));
return (IAsyncArchive)OpenArchive(fileInfo, readerOptions);
}
public static ValueTask<IAsyncArchive> OpenAsyncArchive(
public static IAsyncArchive OpenAsyncArchive(
IReadOnlyList<Stream> streams,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
ReaderOptions? readerOptions = null
)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IAsyncArchive)OpenArchive(streams, readerOptions));
return (IAsyncArchive)OpenArchive(streams, readerOptions);
}
public static ValueTask<IAsyncArchive> OpenAsyncArchive(
public static IAsyncArchive OpenAsyncArchive(
IReadOnlyList<FileInfo> fileInfos,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
ReaderOptions? readerOptions = null
)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IAsyncArchive)OpenArchive(fileInfos, readerOptions));
return (IAsyncArchive)OpenArchive(fileInfos, readerOptions);
}
public static bool IsSevenZipFile(string filePath) => IsSevenZipFile(new FileInfo(filePath));

View File

@@ -89,32 +89,26 @@ public partial class TarArchive
{
var stream = Volumes.Single().Stream;
stream.Position = 0;
return new((IAsyncReader)new TarReader(stream, ReaderOptions, _compressionType));
return new((IAsyncReader)TarReader.OpenReader(stream));
}
protected override async IAsyncEnumerable<TarArchiveEntry> LoadEntriesAsync(
IAsyncEnumerable<TarVolume> volumes
)
{
var sourceStream = (await volumes.SingleAsync().ConfigureAwait(false)).Stream;
var stream = await GetStreamAsync(sourceStream).ConfigureAwait(false);
var stream = (await volumes.SingleAsync().ConfigureAwait(false)).Stream;
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,
StreamingMode.Seekable,
stream,
ReaderOptions.ArchiveEncoding
)
@@ -132,10 +126,7 @@ public partial class TarArchive
{
var entry = new TarArchiveEntry(
this,
new TarFilePart(
previousHeader,
_compressionType == CompressionType.None ? stream : null
),
new TarFilePart(previousHeader, stream),
CompressionType.None,
ReaderOptions
);
@@ -160,10 +151,7 @@ public partial class TarArchive
}
yield return new TarArchiveEntry(
this,
new TarFilePart(
header,
_compressionType == CompressionType.None ? stream : null
),
new TarFilePart(header, stream),
CompressionType.None,
ReaderOptions
);

View File

@@ -7,7 +7,6 @@ using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
using SharpCompress.Common.Tar.Headers;
using SharpCompress.Factories;
using SharpCompress.IO;
using SharpCompress.Readers;
using SharpCompress.Writers.Tar;
@@ -38,9 +37,12 @@ public partial class TarArchive
)
{
fileInfo.NotNull(nameof(fileInfo));
return OpenArchive(
[fileInfo],
readerOptions ?? new ReaderOptions() { LeaveStreamOpen = false }
return new TarArchive(
new SourceStream(
fileInfo,
i => ArchiveVolumeFactory.GetFilePart(i, fileInfo),
readerOptions ?? new ReaderOptions() { LeaveStreamOpen = false }
)
);
}
@@ -51,17 +53,13 @@ public partial class TarArchive
{
fileInfos.NotNull(nameof(fileInfos));
var files = fileInfos.ToArray();
var sourceStream = new SourceStream(
files[0],
i => i < files.Length ? files[i] : null,
readerOptions ?? new ReaderOptions() { LeaveStreamOpen = false }
return new TarArchive(
new SourceStream(
files[0],
i => i < files.Length ? files[i] : null,
readerOptions ?? new ReaderOptions() { LeaveStreamOpen = false }
)
);
var compressionType = TarFactory.GetCompressionType(
sourceStream,
sourceStream.ReaderOptions.Providers
);
sourceStream.Seek(0, SeekOrigin.Begin);
return new TarArchive(sourceStream, compressionType);
}
public static IWritableArchive<TarWriterOptions> OpenArchive(
@@ -71,17 +69,13 @@ public partial class TarArchive
{
streams.NotNull(nameof(streams));
var strms = streams.ToArray();
var sourceStream = new SourceStream(
strms[0],
i => i < strms.Length ? strms[i] : null,
readerOptions ?? new ReaderOptions()
return new TarArchive(
new SourceStream(
strms[0],
i => i < strms.Length ? strms[i] : null,
readerOptions ?? new ReaderOptions()
)
);
var compressionType = TarFactory.GetCompressionType(
sourceStream,
sourceStream.ReaderOptions.Providers
);
sourceStream.Seek(0, SeekOrigin.Begin);
return new TarArchive(sourceStream, compressionType);
}
public static IWritableArchive<TarWriterOptions> OpenArchive(
@@ -96,113 +90,35 @@ public partial class TarArchive
throw new ArgumentException("Stream must be seekable", nameof(stream));
}
return OpenArchive([stream], readerOptions);
return new TarArchive(
new SourceStream(stream, i => null, readerOptions ?? new ReaderOptions())
);
}
public static async ValueTask<IWritableAsyncArchive<TarWriterOptions>> OpenAsyncArchive(
public static IWritableAsyncArchive<TarWriterOptions> OpenAsyncArchive(
Stream stream,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
stream.NotNull(nameof(stream));
var sourceStream = new SourceStream(
stream,
i => null,
readerOptions ?? new ReaderOptions()
);
var compressionType = await TarFactory
.GetCompressionTypeAsync(
sourceStream,
sourceStream.ReaderOptions.Providers,
cancellationToken
)
.ConfigureAwait(false);
sourceStream.Seek(0, SeekOrigin.Begin);
return new TarArchive(sourceStream, compressionType);
}
ReaderOptions? readerOptions = null
) => (IWritableAsyncArchive<TarWriterOptions>)OpenArchive(stream, readerOptions);
public static ValueTask<IWritableAsyncArchive<TarWriterOptions>> OpenAsyncArchive(
public static IWritableAsyncArchive<TarWriterOptions> OpenAsyncArchive(
string path,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
path.NotNullOrEmpty(nameof(path));
return OpenAsyncArchive(new FileInfo(path), readerOptions, cancellationToken);
}
ReaderOptions? readerOptions = null
) => (IWritableAsyncArchive<TarWriterOptions>)OpenArchive(new FileInfo(path), readerOptions);
public static async ValueTask<IWritableAsyncArchive<TarWriterOptions>> OpenAsyncArchive(
public static IWritableAsyncArchive<TarWriterOptions> OpenAsyncArchive(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
fileInfo.NotNull(nameof(fileInfo));
readerOptions ??= new ReaderOptions() { LeaveStreamOpen = false };
var sourceStream = new SourceStream(fileInfo, i => null, readerOptions);
var compressionType = await TarFactory
.GetCompressionTypeAsync(
sourceStream,
sourceStream.ReaderOptions.Providers,
cancellationToken
)
.ConfigureAwait(false);
sourceStream.Seek(0, SeekOrigin.Begin);
return new TarArchive(sourceStream, compressionType);
}
ReaderOptions? readerOptions = null
) => (IWritableAsyncArchive<TarWriterOptions>)OpenArchive(fileInfo, readerOptions);
public static async ValueTask<IWritableAsyncArchive<TarWriterOptions>> OpenAsyncArchive(
public static IWritableAsyncArchive<TarWriterOptions> OpenAsyncArchive(
IReadOnlyList<Stream> streams,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
streams.NotNull(nameof(streams));
var strms = streams.ToArray();
var sourceStream = new SourceStream(
strms[0],
i => i < strms.Length ? strms[i] : null,
readerOptions ?? new ReaderOptions()
);
var compressionType = await TarFactory
.GetCompressionTypeAsync(
sourceStream,
sourceStream.ReaderOptions.Providers,
cancellationToken
)
.ConfigureAwait(false);
sourceStream.Seek(0, SeekOrigin.Begin);
return new TarArchive(sourceStream, compressionType);
}
ReaderOptions? readerOptions = null
) => (IWritableAsyncArchive<TarWriterOptions>)OpenArchive(streams, readerOptions);
public static async ValueTask<IWritableAsyncArchive<TarWriterOptions>> OpenAsyncArchive(
public static IWritableAsyncArchive<TarWriterOptions> OpenAsyncArchive(
IReadOnlyList<FileInfo> fileInfos,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
fileInfos.NotNull(nameof(fileInfos));
var files = fileInfos.ToArray();
var sourceStream = new SourceStream(
files[0],
i => i < files.Length ? files[i] : null,
readerOptions ?? new ReaderOptions() { LeaveStreamOpen = false }
);
var compressionType = await TarFactory
.GetCompressionTypeAsync(
sourceStream,
sourceStream.ReaderOptions.Providers,
cancellationToken
)
.ConfigureAwait(false);
sourceStream.Seek(0, SeekOrigin.Begin);
return new TarArchive(sourceStream, compressionType);
}
ReaderOptions? readerOptions = null
) => (IWritableAsyncArchive<TarWriterOptions>)OpenArchive(fileInfos, readerOptions);
public static bool IsTarFile(string filePath) => IsTarFile(new FileInfo(filePath));
@@ -267,6 +183,5 @@ public partial class TarArchive
public static IWritableArchive<TarWriterOptions> CreateArchive() => new TarArchive();
public static ValueTask<IWritableAsyncArchive<TarWriterOptions>> CreateAsyncArchive() =>
new(new TarArchive());
public static IWritableAsyncArchive<TarWriterOptions> CreateAsyncArchive() => new TarArchive();
}

View File

@@ -5,113 +5,35 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
using SharpCompress.Common.Options;
using SharpCompress.Common.Tar;
using SharpCompress.Common.Tar.Headers;
using SharpCompress.IO;
using SharpCompress.Providers;
using SharpCompress.Readers;
using SharpCompress.Readers.Tar;
using SharpCompress.Writers;
using SharpCompress.Writers.Tar;
using Constants = SharpCompress.Common.Constants;
namespace SharpCompress.Archives.Tar;
public partial class TarArchive
: AbstractWritableArchive<TarArchiveEntry, TarVolume, TarWriterOptions>
{
private readonly CompressionType _compressionType;
protected override IEnumerable<TarVolume> LoadVolumes(SourceStream sourceStream)
{
sourceStream.NotNull("SourceStream is null").LoadAllParts();
return new TarVolume(sourceStream, ReaderOptions, 1).AsEnumerable();
}
internal TarArchive(SourceStream sourceStream, CompressionType compressionType)
: base(ArchiveType.Tar, sourceStream)
{
_compressionType = compressionType;
}
private TarArchive(SourceStream sourceStream)
: base(ArchiveType.Tar, sourceStream) { }
private TarArchive()
: base(ArchiveType.Tar) { }
private Stream GetStream(Stream stream) =>
_compressionType switch
{
CompressionType.BZip2 => ReaderOptions.Providers.CreateDecompressStream(
CompressionType.BZip2,
stream
),
CompressionType.GZip => ReaderOptions.Providers.CreateDecompressStream(
CompressionType.GZip,
stream,
CompressionContext.FromStream(stream).WithReaderOptions(ReaderOptions)
),
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),
};
private ValueTask<Stream> GetStreamAsync(
Stream stream,
CancellationToken cancellationToken = default
) =>
_compressionType switch
{
CompressionType.BZip2 => ReaderOptions.Providers.CreateDecompressStreamAsync(
CompressionType.BZip2,
stream,
cancellationToken
),
CompressionType.GZip => ReaderOptions.Providers.CreateDecompressStreamAsync(
CompressionType.GZip,
stream,
CompressionContext.FromStream(stream).WithReaderOptions(ReaderOptions),
cancellationToken
),
CompressionType.ZStandard => ReaderOptions.Providers.CreateDecompressStreamAsync(
CompressionType.ZStandard,
stream,
cancellationToken
),
CompressionType.LZip => ReaderOptions.Providers.CreateDecompressStreamAsync(
CompressionType.LZip,
stream,
cancellationToken
),
CompressionType.Xz => ReaderOptions.Providers.CreateDecompressStreamAsync(
CompressionType.Xz,
stream,
cancellationToken
),
CompressionType.Lzw => ReaderOptions.Providers.CreateDecompressStreamAsync(
CompressionType.Lzw,
stream,
cancellationToken
),
CompressionType.None => new ValueTask<Stream>(stream),
_ => throw new NotSupportedException("Invalid compression type: " + _compressionType),
};
protected override IEnumerable<TarArchiveEntry> LoadEntries(IEnumerable<TarVolume> volumes)
{
var stream = GetStream(volumes.Single().Stream);
var stream = volumes.Single().Stream;
if (stream.CanSeek)
{
stream.Position = 0;
@@ -119,9 +41,7 @@ public partial class TarArchive
TarHeader? previousHeader = null;
foreach (
var header in TarHeaderFactory.ReadHeader(
_compressionType == CompressionType.None
? StreamingMode.Seekable
: StreamingMode.Streaming,
StreamingMode.Seekable,
stream,
ReaderOptions.ArchiveEncoding
)
@@ -139,10 +59,7 @@ public partial class TarArchive
{
var entry = new TarArchiveEntry(
this,
new TarFilePart(
previousHeader,
_compressionType == CompressionType.None ? stream : null
),
new TarFilePart(previousHeader, stream),
CompressionType.None,
ReaderOptions
);
@@ -165,10 +82,7 @@ public partial class TarArchive
}
yield return new TarArchiveEntry(
this,
new TarFilePart(
header,
_compressionType == CompressionType.None ? stream : null
),
new TarFilePart(header, stream),
CompressionType.None,
ReaderOptions
);
@@ -240,6 +154,6 @@ public partial class TarArchive
{
var stream = Volumes.Single().Stream;
stream.Position = 0;
return new TarReader(stream, ReaderOptions, _compressionType);
return TarReader.OpenReader(stream);
}
}

View File

@@ -55,12 +55,7 @@ public partial class ZipArchive
yield return new ZipArchiveEntry(
this,
new SeekableZipFilePart(
headerFactory.NotNull(),
deh,
s,
ReaderOptions.Providers
),
new SeekableZipFilePart(headerFactory.NotNull(), deh, s),
ReaderOptions
);
}

View File

@@ -95,55 +95,30 @@ public partial class ZipArchive
);
}
public static ValueTask<IWritableAsyncArchive<ZipWriterOptions>> OpenAsyncArchive(
public static IWritableAsyncArchive<ZipWriterOptions> OpenAsyncArchive(
string path,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IWritableAsyncArchive<ZipWriterOptions>)OpenArchive(path, readerOptions));
}
ReaderOptions? readerOptions = null
) => (IWritableAsyncArchive<ZipWriterOptions>)OpenArchive(path, readerOptions);
public static ValueTask<IWritableAsyncArchive<ZipWriterOptions>> OpenAsyncArchive(
public static IWritableAsyncArchive<ZipWriterOptions> OpenAsyncArchive(
Stream stream,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IWritableAsyncArchive<ZipWriterOptions>)OpenArchive(stream, readerOptions));
}
ReaderOptions? readerOptions = null
) => (IWritableAsyncArchive<ZipWriterOptions>)OpenArchive(stream, readerOptions);
public static ValueTask<IWritableAsyncArchive<ZipWriterOptions>> OpenAsyncArchive(
public static IWritableAsyncArchive<ZipWriterOptions> OpenAsyncArchive(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IWritableAsyncArchive<ZipWriterOptions>)OpenArchive(fileInfo, readerOptions));
}
ReaderOptions? readerOptions = null
) => (IWritableAsyncArchive<ZipWriterOptions>)OpenArchive(fileInfo, readerOptions);
public static ValueTask<IWritableAsyncArchive<ZipWriterOptions>> OpenAsyncArchive(
public static IWritableAsyncArchive<ZipWriterOptions> OpenAsyncArchive(
IReadOnlyList<Stream> streams,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IWritableAsyncArchive<ZipWriterOptions>)OpenArchive(streams, readerOptions));
}
ReaderOptions? readerOptions = null
) => (IWritableAsyncArchive<ZipWriterOptions>)OpenArchive(streams, readerOptions);
public static ValueTask<IWritableAsyncArchive<ZipWriterOptions>> OpenAsyncArchive(
public static IWritableAsyncArchive<ZipWriterOptions> OpenAsyncArchive(
IReadOnlyList<FileInfo> fileInfos,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IWritableAsyncArchive<ZipWriterOptions>)OpenArchive(fileInfos, readerOptions));
}
ReaderOptions? readerOptions = null
) => (IWritableAsyncArchive<ZipWriterOptions>)OpenArchive(fileInfos, readerOptions);
public static bool IsZipFile(string filePath, string? password = null) =>
IsZipFile(new FileInfo(filePath), password);
@@ -248,8 +223,7 @@ public partial class ZipArchive
public static IWritableArchive<ZipWriterOptions> CreateArchive() => new ZipArchive();
public static ValueTask<IWritableAsyncArchive<ZipWriterOptions>> CreateAsyncArchive() =>
new(new ZipArchive());
public static IWritableAsyncArchive<ZipWriterOptions> CreateAsyncArchive() => new ZipArchive();
public static async ValueTask<bool> IsZipMultiAsync(
Stream stream,

View File

@@ -96,12 +96,7 @@ public partial class ZipArchive
yield return new ZipArchiveEntry(
this,
new SeekableZipFilePart(
headerFactory.NotNull(),
deh,
s,
ReaderOptions.Providers
),
new SeekableZipFilePart(headerFactory.NotNull(), deh, s),
ReaderOptions
);
}
@@ -176,6 +171,6 @@ public partial class ZipArchive
{
var stream = Volumes.Single().Stream;
stream.Position = 0;
return new((IAsyncReader)ZipReader.OpenReader(stream, ReaderOptions, Entries));
return new((IAsyncReader)ZipReader.OpenReader(stream));
}
}

View File

@@ -12,9 +12,7 @@ public partial class GZipEntry
)
{
yield return new GZipEntry(
await GZipFilePart
.CreateAsync(stream, options.ArchiveEncoding, options.Providers)
.ConfigureAwait(false),
await GZipFilePart.CreateAsync(stream, options.ArchiveEncoding).ConfigureAwait(false),
options
);
}

View File

@@ -46,10 +46,7 @@ public partial class GZipEntry : Entry
internal static IEnumerable<GZipEntry> GetEntries(Stream stream, ReaderOptions options)
{
yield return new GZipEntry(
GZipFilePart.Create(stream, options.ArchiveEncoding, options.Providers),
options
);
yield return new GZipEntry(GZipFilePart.Create(stream, options.ArchiveEncoding), options);
}
// Async methods moved to GZipEntry.Async.cs

View File

@@ -5,9 +5,7 @@ using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common.Tar.Headers;
using SharpCompress.Compressors;
using SharpCompress.Compressors.Deflate;
using SharpCompress.Providers;
namespace SharpCompress.Common.GZip;
@@ -16,11 +14,10 @@ internal sealed partial class GZipFilePart
internal static async ValueTask<GZipFilePart> CreateAsync(
Stream stream,
IArchiveEncoding archiveEncoding,
CompressionProviderRegistry compressionProviders,
CancellationToken cancellationToken = default
)
{
var part = new GZipFilePart(stream, archiveEncoding, compressionProviders);
var part = new GZipFilePart(stream, archiveEncoding);
await part.ReadAndValidateGzipHeaderAsync(cancellationToken).ConfigureAwait(false);
if (stream.CanSeek)
@@ -134,14 +131,4 @@ internal sealed partial class GZipFilePart
var buffer = list.ToArray();
return ArchiveEncoding.Decode(buffer);
}
internal override async ValueTask<Stream?> GetCompressedStreamAsync(
CancellationToken cancellationToken = default
)
{
// GZip uses Deflate compression
return await _compressionProviders
.CreateDecompressStreamAsync(CompressionType.Deflate, _stream, cancellationToken)
.ConfigureAwait(false);
}
}

View File

@@ -5,7 +5,6 @@ using System.IO;
using SharpCompress.Common.Tar.Headers;
using SharpCompress.Compressors;
using SharpCompress.Compressors.Deflate;
using SharpCompress.Providers;
namespace SharpCompress.Common.GZip;
@@ -13,15 +12,10 @@ 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,
CompressionProviderRegistry compressionProviders
)
internal static GZipFilePart Create(Stream stream, IArchiveEncoding archiveEncoding)
{
var part = new GZipFilePart(stream, archiveEncoding, compressionProviders);
var part = new GZipFilePart(stream, archiveEncoding);
part.ReadAndValidateGzipHeader();
if (stream.CanSeek)
@@ -41,16 +35,8 @@ internal sealed partial class GZipFilePart : FilePart
return part;
}
private GZipFilePart(
Stream stream,
IArchiveEncoding archiveEncoding,
CompressionProviderRegistry compressionProviders
)
: base(archiveEncoding)
{
_stream = stream;
_compressionProviders = compressionProviders;
}
private GZipFilePart(Stream stream, IArchiveEncoding archiveEncoding)
: base(archiveEncoding) => _stream = stream;
internal long EntryStartPosition { get; private set; }
@@ -60,11 +46,13 @@ internal sealed partial class GZipFilePart : FilePart
internal override string? FilePartName => _name;
internal override Stream GetCompressedStream()
{
//GZip uses Deflate compression, at this point we need a deflate stream
return _compressionProviders.CreateDecompressStream(CompressionType.Deflate, _stream);
}
internal override Stream GetCompressedStream() =>
new DeflateStream(
_stream,
CompressionMode.Decompress,
CompressionLevel.Default,
leaveOpen: true
);
internal override Stream GetRawStream() => _stream;

View File

@@ -15,9 +15,7 @@ public partial class LzwEntry
)
{
yield return new LzwEntry(
await LzwFilePart
.CreateAsync(stream, options.ArchiveEncoding, options.Providers, cancellationToken)
.ConfigureAwait(false),
await LzwFilePart.CreateAsync(stream, options.ArchiveEncoding, cancellationToken),
options
);
}

View File

@@ -46,10 +46,7 @@ public partial class LzwEntry : Entry
internal static IEnumerable<LzwEntry> GetEntries(Stream stream, ReaderOptions options)
{
yield return new LzwEntry(
LzwFilePart.Create(stream, options.ArchiveEncoding, options.Providers),
options
);
yield return new LzwEntry(LzwFilePart.Create(stream, options.ArchiveEncoding), options);
}
// Async methods moved to LzwEntry.Async.cs

View File

@@ -1,8 +1,6 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
using SharpCompress.Providers;
namespace SharpCompress.Common.Lzw;
@@ -11,25 +9,15 @@ 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, compressionProviders);
var part = new LzwFilePart(stream, archiveEncoding);
// For non-seekable streams, we can't track position, so use 0 since the stream will be
// read sequentially from its current position.
part.EntryStartPosition = stream.CanSeek ? stream.Position : 0;
return part;
}
internal override async ValueTask<Stream?> GetCompressedStreamAsync(
CancellationToken cancellationToken = default
)
{
return await _compressionProviders
.CreateDecompressStreamAsync(CompressionType.Lzw, _stream, cancellationToken)
.ConfigureAwait(false);
}
}

View File

@@ -1,6 +1,5 @@
using System.IO;
using SharpCompress.Common;
using SharpCompress.Providers;
using SharpCompress.Compressors.Lzw;
namespace SharpCompress.Common.Lzw;
@@ -8,15 +7,10 @@ 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,
CompressionProviderRegistry compressionProviders
)
internal static LzwFilePart Create(Stream stream, IArchiveEncoding archiveEncoding)
{
var part = new LzwFilePart(stream, archiveEncoding, compressionProviders);
var part = new LzwFilePart(stream, archiveEncoding);
// For non-seekable streams, we can't track position, so use 0 since the stream will be
// read sequentially from its current position.
@@ -24,16 +18,11 @@ internal sealed partial class LzwFilePart : FilePart
return part;
}
private LzwFilePart(
Stream stream,
IArchiveEncoding archiveEncoding,
CompressionProviderRegistry compressionProviders
)
private LzwFilePart(Stream stream, IArchiveEncoding archiveEncoding)
: base(archiveEncoding)
{
_stream = stream;
_name = DeriveFileName(stream);
_compressionProviders = compressionProviders;
}
internal long EntryStartPosition { get; private set; }
@@ -41,7 +30,7 @@ internal sealed partial class LzwFilePart : FilePart
internal override string? FilePartName => _name;
internal override Stream GetCompressedStream() =>
_compressionProviders.CreateDecompressStream(CompressionType.Lzw, _stream);
new LzwStream(_stream) { IsStreamOwner = false };
internal override Stream GetRawStream() => _stream;

View File

@@ -1,6 +1,3 @@
using SharpCompress.Compressors;
using SharpCompress.Providers;
namespace SharpCompress.Common.Options;
public interface IReaderOptions
@@ -9,40 +6,10 @@ public interface IReaderOptions
IProgressOptions,
IExtractionOptions
{
/// <summary>
/// Look for RarArchive (Check for self-extracting archives or cases where RarArchive isn't at the start of the file)
/// </summary>
bool LookForHeader { get; init; }
/// <summary>
/// Password for encrypted archives.
/// </summary>
string? Password { get; init; }
/// <summary>
/// Disable checking for incomplete archives.
/// </summary>
bool DisableCheckIncomplete { get; init; }
/// <summary>
/// Buffer size for stream operations.
/// </summary>
int BufferSize { get; init; }
/// <summary>
/// Provide a hint for the extension of the archive being read, can speed up finding the correct decoder.
/// </summary>
string? ExtensionHint { get; init; }
/// <summary>
/// Size of the rewindable buffer for non-seekable streams.
/// </summary>
int? RewindableBufferSize { get; init; }
/// <summary>
/// Registry of compression providers.
/// Defaults to <see cref="CompressionProviderRegistry.Default" /> but can be replaced with custom providers.
/// Use this to provide alternative decompression implementations.
/// </summary>
CompressionProviderRegistry Providers { get; init; }
}

View File

@@ -1,28 +1,9 @@
using SharpCompress.Common;
using SharpCompress.Compressors;
using SharpCompress.Providers;
namespace SharpCompress.Common.Options;
/// <summary>
/// Options for configuring writer behavior when creating archives.
/// </summary>
public interface IWriterOptions : IStreamOptions, IEncodingOptions, IProgressOptions
{
/// <summary>
/// The compression type to use for the archive.
/// </summary>
CompressionType CompressionType { get; init; }
/// <summary>
/// The compression level to be used when the compression type supports variable levels.
/// </summary>
int CompressionLevel { get; init; }
/// <summary>
/// Registry of compression providers.
/// Defaults to <see cref="CompressionProviderRegistry.Default" /> but can be replaced with custom providers, such as
/// System.IO.Compression for Deflate/GZip on modern .NET.
/// </summary>
CompressionProviderRegistry Providers { get; init; }
}

View File

@@ -1,7 +1,5 @@
using System.IO;
using SharpCompress.Common.Zip.Headers;
using SharpCompress.Compressors;
using SharpCompress.Providers;
namespace SharpCompress.Common.Zip;
@@ -13,10 +11,9 @@ internal partial class SeekableZipFilePart : ZipFilePart
internal SeekableZipFilePart(
SeekableZipHeaderFactory headerFactory,
DirectoryEntryHeader header,
Stream stream,
CompressionProviderRegistry compressionProviders
Stream stream
)
: base(header, stream, compressionProviders) => _headerFactory = headerFactory;
: base(header, stream) => _headerFactory = headerFactory;
internal override Stream GetCompressedStream()
{

View File

@@ -1,8 +1,7 @@
using System.IO;
using SharpCompress.Common.Zip.Headers;
using SharpCompress.Compressors;
using SharpCompress.Compressors.Deflate;
using SharpCompress.IO;
using SharpCompress.Providers;
namespace SharpCompress.Common.Zip;
@@ -10,12 +9,8 @@ internal sealed partial class StreamingZipFilePart : ZipFilePart
{
private Stream? _decompressionStream;
internal StreamingZipFilePart(
ZipFileEntry header,
Stream stream,
CompressionProviderRegistry compressionProviders
)
: base(header, stream, compressionProviders) { }
internal StreamingZipFilePart(ZipFileEntry header, Stream stream)
: base(header, stream) { }
protected override Stream CreateBaseStream() => Header.PackedStream.NotNull();
@@ -52,6 +47,11 @@ 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);

View File

@@ -10,10 +10,6 @@ public class ZipEntry : Entry
{
private readonly ZipFilePart? _filePart;
// WinZip AES extra data constants
private const int MinimumWinZipAesExtraDataLength = 7;
private const int WinZipAesCompressionMethodOffset = 5;
internal ZipEntry(ZipFilePart? filePart, IReaderOptions readerOptions)
: base(readerOptions)
{
@@ -37,54 +33,24 @@ public class ZipEntry : Entry
CreatedTime = times?.UnicodeTimes.Item3;
}
public override CompressionType CompressionType
{
get
public override CompressionType CompressionType =>
_filePart?.Header.CompressionMethod switch
{
var compressionMethod = GetActualCompressionMethod();
return compressionMethod switch
{
ZipCompressionMethod.BZip2 => CompressionType.BZip2,
ZipCompressionMethod.Deflate => CompressionType.Deflate,
ZipCompressionMethod.Deflate64 => CompressionType.Deflate64,
ZipCompressionMethod.LZMA => CompressionType.LZMA,
ZipCompressionMethod.PPMd => CompressionType.PPMd,
ZipCompressionMethod.None => CompressionType.None,
ZipCompressionMethod.Shrink => CompressionType.Shrink,
ZipCompressionMethod.Reduce1 => CompressionType.Reduce1,
ZipCompressionMethod.Reduce2 => CompressionType.Reduce2,
ZipCompressionMethod.Reduce3 => CompressionType.Reduce3,
ZipCompressionMethod.Reduce4 => CompressionType.Reduce4,
ZipCompressionMethod.Explode => CompressionType.Explode,
ZipCompressionMethod.ZStandard => CompressionType.ZStandard,
_ => CompressionType.Unknown,
};
}
}
private ZipCompressionMethod GetActualCompressionMethod()
{
if (_filePart?.Header.CompressionMethod != ZipCompressionMethod.WinzipAes)
{
return _filePart?.Header.CompressionMethod ?? ZipCompressionMethod.None;
}
// For WinZip AES, the actual compression method is stored in the extra data
var aesExtraData = _filePart.Header.Extra.FirstOrDefault(x =>
x.Type == ExtraDataType.WinZipAes
);
if (aesExtraData is null || aesExtraData.DataBytes.Length < MinimumWinZipAesExtraDataLength)
{
return ZipCompressionMethod.WinzipAes;
}
// The compression method is at offset 5 in the extra data
return (ZipCompressionMethod)
System.Buffers.Binary.BinaryPrimitives.ReadUInt16LittleEndian(
aesExtraData.DataBytes.AsSpan(WinZipAesCompressionMethodOffset)
);
}
ZipCompressionMethod.BZip2 => CompressionType.BZip2,
ZipCompressionMethod.Deflate => CompressionType.Deflate,
ZipCompressionMethod.Deflate64 => CompressionType.Deflate64,
ZipCompressionMethod.LZMA => CompressionType.LZMA,
ZipCompressionMethod.PPMd => CompressionType.PPMd,
ZipCompressionMethod.None => CompressionType.None,
ZipCompressionMethod.Shrink => CompressionType.Shrink,
ZipCompressionMethod.Reduce1 => CompressionType.Reduce1,
ZipCompressionMethod.Reduce2 => CompressionType.Reduce2,
ZipCompressionMethod.Reduce3 => CompressionType.Reduce3,
ZipCompressionMethod.Reduce4 => CompressionType.Reduce4,
ZipCompressionMethod.Explode => CompressionType.Explode,
ZipCompressionMethod.ZStandard => CompressionType.ZStandard,
_ => CompressionType.Unknown,
};
public override long Crc => _filePart?.Header.Crc ?? 0;

View File

@@ -6,8 +6,17 @@ 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;
@@ -114,7 +123,6 @@ internal abstract partial class ZipFilePart
CancellationToken cancellationToken = default
)
{
// Handle special cases first
switch (method)
{
case ZipCompressionMethod.None:
@@ -126,24 +134,98 @@ internal abstract partial class ZipFilePart
return stream;
}
case ZipCompressionMethod.WinzipAes:
case ZipCompressionMethod.Shrink:
{
return await CreateWinzipAesDecompressionStreamAsync(stream, cancellationToken)
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
)
.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.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);
}
case ZipCompressionMethod.LZMA:
{
if (FlagUtility.HasFlag(Header.Flags, HeaderFlags.Encrypted))
@@ -152,108 +234,81 @@ 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);
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,
};
return await providers
.CreateDecompressStreamAsync(
compressionType,
return await LzmaStream
.CreateAsync(
props,
stream,
context,
cancellationToken
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);
}
case ZipCompressionMethod.PPMd:
{
var props = new byte[2];
await stream.ReadFullyAsync(props, 0, 2, cancellationToken).ConfigureAwait(false);
context = context with { Properties = props };
return await providers
.CreateDecompressStreamAsync(
compressionType,
stream,
context,
cancellationToken
)
return await PpmdStream
.CreateAsync(new PpmdProperties(props), stream, false, cancellationToken)
.ConfigureAwait(false);
}
case ZipCompressionMethod.Explode:
case ZipCompressionMethod.WinzipAes:
{
context = context with { FormatOptions = Header.Flags };
return await providers
.CreateDecompressStreamAsync(
compressionType,
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,
context,
(ZipCompressionMethod)
BinaryPrimitives.ReadUInt16LittleEndian(data.DataBytes.AsSpan(5)),
cancellationToken
)
.ConfigureAwait(false);
}
default:
{
return await providers
.CreateDecompressStreamAsync(
compressionType,
stream,
context,
cancellationToken
)
.ConfigureAwait(false);
throw new NotSupportedException("CompressionMethod: " + Header.CompressionMethod);
}
}
}
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

@@ -15,25 +15,17 @@ using SharpCompress.Compressors.Shrink;
using SharpCompress.Compressors.Xz;
using SharpCompress.Compressors.ZStandard;
using SharpCompress.IO;
using SharpCompress.Providers;
namespace SharpCompress.Common.Zip;
internal abstract partial class ZipFilePart : FilePart
{
private readonly CompressionProviderRegistry _compressionProviders;
internal ZipFilePart(
ZipFileEntry header,
Stream stream,
CompressionProviderRegistry compressionProviders
)
internal ZipFilePart(ZipFileEntry header, Stream stream)
: base(header.ArchiveEncoding)
{
Header = header;
header.Part = this;
BaseStream = stream;
_compressionProviders = compressionProviders;
}
internal Stream BaseStream { get; }
@@ -72,37 +64,8 @@ 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;
/// <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:
@@ -111,29 +74,76 @@ internal abstract partial class ZipFilePart : FilePart
{
return new DataDescriptorStream(stream);
}
return stream;
}
case ZipCompressionMethod.WinzipAes:
case ZipCompressionMethod.Shrink:
{
return CreateWinzipAesDecompressionStream(stream);
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
);
}
}
// 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.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);
}
case ZipCompressionMethod.LZMA:
{
if (FlagUtility.HasFlag(Header.Flags, HeaderFlags.Encrypted))
@@ -148,71 +158,71 @@ internal abstract partial class ZipFilePart : FilePart
)
)
{
reader.ReadUInt16(); // LZMA version
var propsLength = reader.ReadUInt16();
var props = new byte[propsLength];
reader.ReadUInt16(); //LZMA version
var props = new byte[reader.ReadUInt16()];
reader.Read(props, 0, props.Length);
context = context with
{
Properties = props,
InputSize =
Header.CompressedSize > 0
? Header.CompressedSize - 4 - props.Length
: -1,
OutputSize = FlagUtility.HasFlag(Header.Flags, HeaderFlags.Bit1)
return LzmaStream.Create(
props,
stream,
Header.CompressedSize > 0 ? Header.CompressedSize - 4 - props.Length : -1,
FlagUtility.HasFlag(Header.Flags, HeaderFlags.Bit1)
? -1
: Header.UncompressedSize,
};
return providers.CreateDecompressStream(compressionType, stream, context);
: Header.UncompressedSize
);
}
}
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);
context = context with { Properties = props.ToArray() };
return providers.CreateDecompressStream(compressionType, stream, context);
return PpmdStream.Create(new PpmdProperties(props), stream, false);
}
case ZipCompressionMethod.Explode:
case ZipCompressionMethod.WinzipAes:
{
context = context with { FormatOptions = Header.Flags };
return providers.CreateDecompressStream(compressionType, stream, context);
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);
}
}
// 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)

View File

@@ -3,11 +3,10 @@ using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Providers;
namespace SharpCompress.Compressors.BZip2;
public sealed partial class BZip2Stream : Stream, IFinishable
public sealed partial class BZip2Stream : Stream
{
private Stream stream = default!;
private bool isDisposed;

View File

@@ -32,8 +32,6 @@ using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
using SharpCompress.Common.Options;
namespace SharpCompress.Compressors.Deflate;
@@ -55,23 +53,8 @@ public partial class GZipStream : Stream
public GZipStream(Stream stream, CompressionMode mode)
: this(stream, mode, CompressionLevel.Default, Encoding.UTF8) { }
public GZipStream(Stream stream, CompressionMode mode, IReaderOptions readerOptions)
: this(stream, mode, CompressionLevel.Default, readerOptions) { }
public GZipStream(
Stream stream,
CompressionMode mode,
CompressionLevel level,
IReaderOptions readerOptions
)
: this(
stream,
mode,
level,
(
readerOptions ?? throw new ArgumentNullException(nameof(readerOptions))
).ArchiveEncoding.GetEncoding()
) { }
public GZipStream(Stream stream, CompressionMode mode, CompressionLevel level)
: this(stream, mode, level, Encoding.UTF8) { }
public GZipStream(
Stream stream,

View File

@@ -6,7 +6,6 @@ using System.Threading.Tasks;
using SharpCompress.Common;
using SharpCompress.Crypto;
using SharpCompress.IO;
using SharpCompress.Providers;
namespace SharpCompress.Compressors.LZMA;
@@ -18,7 +17,7 @@ namespace SharpCompress.Compressors.LZMA;
/// <summary>
/// Stream supporting the LZIP format, as documented at http://www.nongnu.org/lzip/manual/lzip_manual.html
/// </summary>
public sealed partial class LZipStream : Stream, IFinishable
public sealed partial class LZipStream : Stream
{
private readonly Stream _stream;
private readonly CountingStream? _countingWritableSubStream;

View File

@@ -96,28 +96,4 @@ 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,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Archives;
@@ -9,7 +10,6 @@ using SharpCompress.Archives.Tar;
using SharpCompress.Common;
using SharpCompress.Common.Options;
using SharpCompress.IO;
using SharpCompress.Providers;
using SharpCompress.Readers;
using SharpCompress.Readers.GZip;
using SharpCompress.Readers.Tar;
@@ -63,26 +63,12 @@ public class GZipFactory
GZipArchive.OpenArchive(stream, readerOptions);
/// <inheritdoc/>
public ValueTask<IAsyncArchive> OpenAsyncArchive(
Stream stream,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IAsyncArchive)OpenArchive(stream, readerOptions));
}
public IAsyncArchive OpenAsyncArchive(Stream stream, ReaderOptions? readerOptions = null) =>
(IAsyncArchive)OpenArchive(stream, readerOptions);
/// <inheritdoc/>
public ValueTask<IAsyncArchive> OpenAsyncArchive(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IAsyncArchive)OpenArchive(fileInfo, readerOptions));
}
public IAsyncArchive OpenAsyncArchive(FileInfo fileInfo, ReaderOptions? readerOptions = null) =>
(IAsyncArchive)OpenArchive(fileInfo, readerOptions);
#endregion
@@ -128,11 +114,7 @@ public class GZipFactory
if (GZipArchive.IsGZipFile(sharpCompressStream))
{
sharpCompressStream.Rewind();
using var testStream = options.Providers.CreateDecompressStream(
CompressionType.GZip,
SharpCompressStream.CreateNonDisposing(sharpCompressStream),
CompressionContext.FromStream(sharpCompressStream).WithReaderOptions(options)
);
var testStream = new GZipStream(sharpCompressStream, CompressionMode.Decompress);
if (TarArchive.IsTarFile(testStream))
{
sharpCompressStream.StopRecording();

View File

@@ -58,12 +58,7 @@ public class LzwFactory : Factory, IReaderFactory
if (LzwStream.IsLzwStream(sharpCompressStream))
{
sharpCompressStream.Rewind();
using (
var testStream = options.Providers.CreateDecompressStream(
CompressionType.Lzw,
SharpCompressStream.CreateNonDisposing(sharpCompressStream)
)
)
using (var testStream = new LzwStream(sharpCompressStream) { IsStreamOwner = false })
{
if (TarArchive.IsTarFile(testStream))
{
@@ -92,7 +87,7 @@ public class LzwFactory : Factory, IReaderFactory
)
{
cancellationToken.ThrowIfCancellationRequested();
return LzwReader.OpenAsyncReader(stream, options);
return new(LzwReader.OpenAsyncReader(stream, options));
}
#endregion

View File

@@ -54,30 +54,16 @@ public class RarFactory : Factory, IArchiveFactory, IMultiArchiveFactory, IReade
RarArchive.OpenArchive(stream, readerOptions);
/// <inheritdoc/>
public ValueTask<IAsyncArchive> OpenAsyncArchive(
Stream stream,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IAsyncArchive)OpenArchive(stream, readerOptions));
}
public IAsyncArchive OpenAsyncArchive(Stream stream, ReaderOptions? readerOptions = null) =>
(IAsyncArchive)OpenArchive(stream, readerOptions);
/// <inheritdoc/>
public IArchive OpenArchive(FileInfo fileInfo, ReaderOptions? readerOptions = null) =>
RarArchive.OpenArchive(fileInfo, readerOptions);
/// <inheritdoc/>
public ValueTask<IAsyncArchive> OpenAsyncArchive(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IAsyncArchive)OpenArchive(fileInfo, readerOptions));
}
public IAsyncArchive OpenAsyncArchive(FileInfo fileInfo, ReaderOptions? readerOptions = null) =>
(IAsyncArchive)OpenArchive(fileInfo, readerOptions);
#endregion

View File

@@ -49,30 +49,16 @@ public class SevenZipFactory : Factory, IArchiveFactory, IMultiArchiveFactory
SevenZipArchive.OpenArchive(stream, readerOptions);
/// <inheritdoc/>
public ValueTask<IAsyncArchive> OpenAsyncArchive(
Stream stream,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IAsyncArchive)OpenArchive(stream, readerOptions));
}
public IAsyncArchive OpenAsyncArchive(Stream stream, ReaderOptions? readerOptions = null) =>
SevenZipArchive.OpenAsyncArchive(stream, readerOptions);
/// <inheritdoc/>
public IArchive OpenArchive(FileInfo fileInfo, ReaderOptions? readerOptions = null) =>
SevenZipArchive.OpenArchive(fileInfo, readerOptions);
/// <inheritdoc/>
public ValueTask<IAsyncArchive> OpenAsyncArchive(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IAsyncArchive)OpenArchive(fileInfo, readerOptions));
}
public IAsyncArchive OpenAsyncArchive(FileInfo fileInfo, ReaderOptions? readerOptions = null) =>
SevenZipArchive.OpenAsyncArchive(fileInfo, readerOptions);
#endregion
@@ -88,7 +74,7 @@ public class SevenZipFactory : Factory, IArchiveFactory, IMultiArchiveFactory
public IAsyncArchive OpenAsyncArchive(
IReadOnlyList<Stream> streams,
ReaderOptions? readerOptions = null
) => (IAsyncArchive)OpenArchive(streams, readerOptions);
) => SevenZipArchive.OpenAsyncArchive(streams, readerOptions);
/// <inheritdoc/>
public IArchive OpenArchive(
@@ -100,7 +86,7 @@ public class SevenZipFactory : Factory, IArchiveFactory, IMultiArchiveFactory
public IAsyncArchive OpenAsyncArchive(
IReadOnlyList<FileInfo> fileInfos,
ReaderOptions? readerOptions = null
) => (IAsyncArchive)OpenArchive(fileInfos, readerOptions);
) => SevenZipArchive.OpenAsyncArchive(fileInfos, readerOptions);
#endregion

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
@@ -8,7 +9,6 @@ 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,7 +50,6 @@ 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)
@@ -59,11 +58,7 @@ public class TarFactory
if (wrapper.IsMatch(sharpCompressStream))
{
sharpCompressStream.Rewind();
var decompressedStream = CreateProbeDecompressionStream(
sharpCompressStream,
wrapper.CompressionType,
providers
);
var decompressedStream = wrapper.CreateStream(sharpCompressStream);
if (TarArchive.IsTarFile(decompressedStream))
{
sharpCompressStream.Rewind();
@@ -82,7 +77,6 @@ public class TarFactory
CancellationToken cancellationToken = default
)
{
var providers = CompressionProviderRegistry.Default;
var sharpCompressStream = new SharpCompressStream(stream);
sharpCompressStream.StartRecording();
foreach (var wrapper in TarWrapper.Wrappers)
@@ -95,12 +89,8 @@ public class TarFactory
)
{
sharpCompressStream.Rewind();
var decompressedStream = await CreateProbeDecompressionStreamAsync(
sharpCompressStream,
wrapper.CompressionType,
providers,
cancellationToken: cancellationToken
)
var decompressedStream = await wrapper
.CreateStreamAsync(sharpCompressStream, cancellationToken)
.ConfigureAwait(false);
if (
await TarArchive
@@ -119,125 +109,6 @@ public class TarFactory
#endregion
private static Stream CreateProbeDecompressionStream(
Stream stream,
CompressionType compressionType,
CompressionProviderRegistry providers,
IReaderOptions? readerOptions = null
)
{
var nonDisposingStream = SharpCompressStream.CreateNonDisposing(stream);
if (compressionType == CompressionType.None)
{
return nonDisposingStream;
}
if (compressionType == CompressionType.GZip && readerOptions is not null)
{
return providers.CreateDecompressStream(
compressionType,
nonDisposingStream,
CompressionContext.FromStream(nonDisposingStream).WithReaderOptions(readerOptions)
);
}
return providers.CreateDecompressStream(compressionType, nonDisposingStream);
}
private static async ValueTask<Stream> CreateProbeDecompressionStreamAsync(
Stream stream,
CompressionType compressionType,
CompressionProviderRegistry providers,
IReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
var nonDisposingStream = SharpCompressStream.CreateNonDisposing(stream);
if (compressionType == CompressionType.None)
{
return nonDisposingStream;
}
if (compressionType == CompressionType.GZip && readerOptions is not null)
{
return await providers
.CreateDecompressStreamAsync(
compressionType,
nonDisposingStream,
CompressionContext
.FromStream(nonDisposingStream)
.WithReaderOptions(readerOptions),
cancellationToken
)
.ConfigureAwait(false);
}
return await providers
.CreateDecompressStreamAsync(compressionType, nonDisposingStream, cancellationToken)
.ConfigureAwait(false);
}
public static CompressionType GetCompressionType(
Stream stream,
CompressionProviderRegistry? providers = null
)
{
providers ??= CompressionProviderRegistry.Default;
stream.Seek(0, SeekOrigin.Begin);
foreach (var wrapper in TarWrapper.Wrappers)
{
stream.Seek(0, SeekOrigin.Begin);
if (wrapper.IsMatch(stream))
{
stream.Seek(0, SeekOrigin.Begin);
var decompressedStream = CreateProbeDecompressionStream(
stream,
wrapper.CompressionType,
providers
);
if (TarArchive.IsTarFile(decompressedStream))
{
return wrapper.CompressionType;
}
}
}
throw new InvalidFormatException("Not a tar file.");
}
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 (await wrapper.IsMatchAsync(stream, cancellationToken).ConfigureAwait(false))
{
stream.Seek(0, SeekOrigin.Begin);
var decompressedStream = await CreateProbeDecompressionStreamAsync(
stream,
wrapper.CompressionType,
providers,
cancellationToken: cancellationToken
)
.ConfigureAwait(false);
if (
await TarArchive
.IsTarFileAsync(decompressedStream, cancellationToken)
.ConfigureAwait(false)
)
{
return wrapper.CompressionType;
}
}
}
throw new InvalidFormatException("Not a tar file.");
}
#region IArchiveFactory
/// <inheritdoc/>
@@ -245,28 +116,16 @@ public class TarFactory
TarArchive.OpenArchive(stream, readerOptions);
/// <inheritdoc/>
public async ValueTask<IAsyncArchive> OpenAsyncArchive(
Stream stream,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
) =>
await TarArchive
.OpenAsyncArchive(stream, readerOptions, cancellationToken)
.ConfigureAwait(false);
public IAsyncArchive OpenAsyncArchive(Stream stream, ReaderOptions? readerOptions = null) =>
(IAsyncArchive)OpenArchive(stream, readerOptions);
/// <inheritdoc/>
public IArchive OpenArchive(FileInfo fileInfo, ReaderOptions? readerOptions = null) =>
TarArchive.OpenArchive(fileInfo, readerOptions);
/// <inheritdoc/>
public async ValueTask<IAsyncArchive> OpenAsyncArchive(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
) =>
await TarArchive
.OpenAsyncArchive(fileInfo, readerOptions, cancellationToken)
.ConfigureAwait(false);
public IAsyncArchive OpenAsyncArchive(FileInfo fileInfo, ReaderOptions? readerOptions = null) =>
(IAsyncArchive)OpenArchive(fileInfo, readerOptions);
#endregion
@@ -312,12 +171,7 @@ public class TarFactory
if (wrapper.IsMatch(sharpCompressStream))
{
sharpCompressStream.Rewind();
var decompressedStream = CreateProbeDecompressionStream(
sharpCompressStream,
wrapper.CompressionType,
options.Providers,
options
);
var decompressedStream = wrapper.CreateStream(sharpCompressStream);
if (TarArchive.IsTarFile(decompressedStream))
{
sharpCompressStream.StopRecording();
@@ -349,13 +203,8 @@ public class TarFactory
)
{
sharpCompressStream.Rewind();
var decompressedStream = await CreateProbeDecompressionStreamAsync(
sharpCompressStream,
wrapper.CompressionType,
options.Providers,
options,
cancellationToken
)
var decompressedStream = await wrapper
.CreateStreamAsync(sharpCompressStream, cancellationToken)
.ConfigureAwait(false);
if (
await TarArchive
@@ -369,9 +218,7 @@ public class TarFactory
}
}
}
sharpCompressStream.Rewind();
return (IAsyncReader)TarReader.OpenReader(sharpCompressStream, options);
return (IAsyncReader)TarReader.OpenReader(stream, options);
}
#endregion

View File

@@ -127,30 +127,16 @@ public class ZipFactory
ZipArchive.OpenArchive(stream, readerOptions);
/// <inheritdoc/>
public ValueTask<IAsyncArchive> OpenAsyncArchive(
Stream stream,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IAsyncArchive)OpenArchive(stream, readerOptions));
}
public IAsyncArchive OpenAsyncArchive(Stream stream, ReaderOptions? readerOptions = null) =>
(IAsyncArchive)OpenArchive(stream, readerOptions);
/// <inheritdoc/>
public IArchive OpenArchive(FileInfo fileInfo, ReaderOptions? readerOptions = null) =>
ZipArchive.OpenArchive(fileInfo, readerOptions);
/// <inheritdoc/>
public ValueTask<IAsyncArchive> OpenAsyncArchive(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IAsyncArchive)OpenArchive(fileInfo, readerOptions));
}
public IAsyncArchive OpenAsyncArchive(FileInfo fileInfo, ReaderOptions? readerOptions = null) =>
(IAsyncArchive)OpenArchive(fileInfo, readerOptions);
#endregion

View File

@@ -206,22 +206,8 @@ internal partial class SharpCompressStream : Stream, IStreamStack
throw new NotSupportedException();
}
public override long Length
{
get
{
if (_isPassthrough)
{
return stream.Length;
}
if (_ringBuffer is not null)
{
return _ringBuffer.Length;
}
throw new NotSupportedException();
}
}
public override long Length =>
_isPassthrough ? stream.Length : throw new NotSupportedException();
public override long Position
{

View File

@@ -1,66 +0,0 @@
using System.IO;
using SharpCompress.Common.Options;
namespace SharpCompress.Providers;
/// <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>
/// <remarks>
/// This value is consumed by provider implementations that need caller-supplied metadata
/// that is not tied to ReaderOptions. For archive header encoding, use <see cref="ReaderOptions"/> instead.
/// Examples of valid FormatOptions values include compression properties (e.g., LZMA properties),
/// format flags, or algorithm-specific configuration.
/// </remarks>
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 };
/// <summary>
/// Reader options for accessing archive metadata such as header encoding.
/// </summary>
public IReaderOptions? ReaderOptions { get; init; }
/// <summary>
/// Returns a new <see cref="CompressionContext"/> with the specified reader options.
/// </summary>
/// <param name="readerOptions">The reader options to set.</param>
/// <returns>A new <see cref="CompressionContext"/> instance.</returns>
public CompressionContext WithReaderOptions(IReaderOptions? readerOptions) =>
this with
{
ReaderOptions = readerOptions,
};
}

View File

@@ -1,18 +0,0 @@
using System.Text;
using SharpCompress.Common;
using SharpCompress.Common.Options;
namespace SharpCompress.Providers;
public static class CompressionContextExtensions
{
/// <summary>
/// Resolves the archive header encoding from <see cref="CompressionContext.ReaderOptions"/>.
/// </summary>
/// <remarks>
/// Returns <see cref="ReaderOptions.ArchiveEncoding"/> when ReaderOptions is set,
/// otherwise falls back to UTF-8.
/// </remarks>
public static Encoding ResolveArchiveEncoding(this CompressionContext context) =>
context.ReaderOptions?.ArchiveEncoding.GetEncoding() ?? Encoding.UTF8;
}

View File

@@ -1,129 +0,0 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
namespace SharpCompress.Providers;
/// <summary>
/// Base class for compression providers that provides default async implementations
/// delegating to synchronous methods. Providers can inherit from this class for
/// simpler implementations or implement ICompressionProvider directly for full control.
/// </summary>
/// <remarks>
/// <para>
/// This base class implements the async methods by calling the synchronous versions.
/// Providers that need true async implementations should override these methods.
/// </para>
/// </remarks>
public abstract class CompressionProviderBase : ICompressionProvider
{
/// <inheritdoc />
public abstract CompressionType CompressionType { get; }
/// <inheritdoc />
public abstract bool SupportsCompression { get; }
/// <inheritdoc />
public abstract bool SupportsDecompression { get; }
/// <inheritdoc />
public abstract Stream CreateCompressStream(Stream destination, int compressionLevel);
/// <inheritdoc />
public virtual Stream CreateCompressStream(
Stream destination,
int compressionLevel,
CompressionContext context
) => CreateCompressStream(destination, compressionLevel);
/// <inheritdoc />
public abstract Stream CreateDecompressStream(Stream source);
/// <inheritdoc />
public virtual Stream CreateDecompressStream(Stream source, CompressionContext context) =>
CreateDecompressStream(source);
/// <summary>
/// Asynchronously creates a compression stream.
/// Default implementation delegates to the synchronous CreateCompressStream.
/// </summary>
public virtual ValueTask<Stream> CreateCompressStreamAsync(
Stream destination,
int compressionLevel,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return new ValueTask<Stream>(CreateCompressStream(destination, compressionLevel));
}
/// <summary>
/// Asynchronously creates a compression stream with context.
/// Default implementation delegates to the synchronous CreateCompressStream with context.
/// </summary>
public virtual ValueTask<Stream> CreateCompressStreamAsync(
Stream destination,
int compressionLevel,
CompressionContext context,
CancellationToken cancellationToken = default
)
{
return CreateCompressStreamAsync(destination, compressionLevel, cancellationToken);
}
/// <summary>
/// Asynchronously creates a decompression stream.
/// Default implementation delegates to the synchronous CreateDecompressStream.
/// </summary>
public virtual ValueTask<Stream> CreateDecompressStreamAsync(
Stream source,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return new ValueTask<Stream>(CreateDecompressStream(source));
}
/// <summary>
/// Asynchronously creates a decompression stream with context.
/// Default implementation delegates to the synchronous CreateDecompressStream with context.
/// </summary>
public virtual ValueTask<Stream> CreateDecompressStreamAsync(
Stream source,
CompressionContext context,
CancellationToken cancellationToken = default
)
{
return CreateDecompressStreamAsync(source, cancellationToken);
}
protected static void ValidateRequiredSizes(CompressionContext context, string algorithmName)
{
if (context.InputSize < 0 || context.OutputSize < 0)
{
throw new ArgumentException(
$"{algorithmName} decompression requires InputSize and OutputSize in CompressionContext.",
nameof(context)
);
}
}
protected static T RequireFormatOption<T>(
CompressionContext context,
string algorithmName,
string optionName
)
{
if (context.FormatOptions is not T options)
{
throw new ArgumentException(
$"{algorithmName} decompression requires {optionName} in CompressionContext.FormatOptions.",
nameof(context)
);
}
return options;
}
}

View File

@@ -1,321 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
using SharpCompress.Providers.Default;
namespace SharpCompress.Providers;
/// <summary>
/// A registry of compression providers, keyed by CompressionType.
/// Used to resolve which implementation to use for a given compression type.
/// </summary>
/// <remarks>
/// <para>
/// This class is immutable. Use the <c>With</c> method to create modified copies
/// that add or replace providers:
/// </para>
/// <code>
/// var customRegistry = CompressionProviderRegistry.Default
/// .With(new MyCustomGZipProvider());
/// var options = new WriterOptions(CompressionType.GZip)
/// {
/// Providers = customRegistry
/// };
/// </code>
/// </remarks>
public sealed class CompressionProviderRegistry
{
/// <summary>
/// The default registry using SharpCompress internal implementations.
/// </summary>
public static CompressionProviderRegistry Default { get; } = CreateDefault();
/// <summary>
/// The empty registry for tests
/// </summary>
public static CompressionProviderRegistry Empty { get; } = CreateEmpty();
private readonly Dictionary<CompressionType, ICompressionProvider> _providers;
private CompressionProviderRegistry(
Dictionary<CompressionType, ICompressionProvider> providers
) => _providers = providers;
/// <summary>
/// Gets the provider for a given compression type, or null if none is registered.
/// </summary>
/// <param name="type">The compression type to look up.</param>
/// <returns>The provider for the type, or null if not found.</returns>
public ICompressionProvider? GetProvider(CompressionType type)
{
_providers.TryGetValue(type, out var provider);
return provider;
}
/// <summary>
/// Creates a compression stream for the specified type.
/// </summary>
/// <param name="type">The compression type.</param>
/// <param name="destination">The destination stream.</param>
/// <param name="level">The compression level.</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)
{
var provider = GetProvider(type);
if (provider is null)
{
throw new InvalidOperationException(
$"No compression provider registered for type: {type}"
);
}
return provider.CreateCompressStream(destination, level);
}
/// <summary>
/// Creates a decompression stream for the specified type.
/// </summary>
/// <param name="type">The compression type.</param>
/// <param name="source">The source stream.</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)
{
var provider = GetProvider(type);
if (provider is null)
{
throw new InvalidOperationException(
$"No compression provider registered for type: {type}"
);
}
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>
/// Asynchronously creates a compression stream for the specified type.
/// </summary>
/// <param name="type">The compression type.</param>
/// <param name="destination">The destination stream.</param>
/// <param name="level">The compression level.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>A task containing the 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 ValueTask<Stream> CreateCompressStreamAsync(
CompressionType type,
Stream destination,
int level,
CancellationToken cancellationToken = default
)
{
var provider = GetProvider(type);
if (provider is null)
{
throw new InvalidOperationException(
$"No compression provider registered for type: {type}"
);
}
return provider.CreateCompressStreamAsync(destination, level, cancellationToken);
}
/// <summary>
/// Asynchronously creates a decompression stream for the specified type.
/// </summary>
/// <param name="type">The compression type.</param>
/// <param name="source">The source stream.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>A task containing the 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 ValueTask<Stream> CreateDecompressStreamAsync(
CompressionType type,
Stream source,
CancellationToken cancellationToken = default
)
{
var provider = GetProvider(type);
if (provider is null)
{
throw new InvalidOperationException(
$"No compression provider registered for type: {type}"
);
}
return provider.CreateDecompressStreamAsync(source, cancellationToken);
}
/// <summary>
/// Asynchronously 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>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>A task containing the 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 ValueTask<Stream> CreateCompressStreamAsync(
CompressionType type,
Stream destination,
int level,
CompressionContext context,
CancellationToken cancellationToken = default
)
{
var provider = GetProvider(type);
if (provider is null)
{
throw new InvalidOperationException(
$"No compression provider registered for type: {type}"
);
}
return provider.CreateCompressStreamAsync(destination, level, context, cancellationToken);
}
/// <summary>
/// Asynchronously 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>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>A task containing the 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 ValueTask<Stream> CreateDecompressStreamAsync(
CompressionType type,
Stream source,
CompressionContext context,
CancellationToken cancellationToken = default
)
{
var provider = GetProvider(type);
if (provider is null)
{
throw new InvalidOperationException(
$"No compression provider registered for type: {type}"
);
}
return provider.CreateDecompressStreamAsync(source, context, cancellationToken);
}
/// <summary>
/// Gets the provider as an ICompressionProviderHooks 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 ICompressionProviderHooks? GetCompressingProvider(CompressionType type)
{
var provider = GetProvider(type);
return provider as ICompressionProviderHooks;
}
/// <summary>
/// Creates a new registry with the specified provider added or replaced.
/// </summary>
/// <param name="provider">The provider to add or replace.</param>
/// <returns>A new registry instance with the provider included.</returns>
/// <exception cref="ArgumentNullException">If provider is null.</exception>
public CompressionProviderRegistry With(ICompressionProvider provider)
{
if (provider is null)
{
throw new ArgumentNullException(nameof(provider));
}
var newProviders = new Dictionary<CompressionType, ICompressionProvider>(_providers)
{
[provider.CompressionType] = provider,
};
return new CompressionProviderRegistry(newProviders);
}
private static CompressionProviderRegistry CreateDefault()
{
var providers = new Dictionary<CompressionType, ICompressionProvider>
{
[CompressionType.Deflate] = new DeflateCompressionProvider(),
[CompressionType.GZip] = new GZipCompressionProvider(),
[CompressionType.BZip2] = new BZip2CompressionProvider(),
[CompressionType.ZStandard] = new ZStandardCompressionProvider(),
[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);
}
private static CompressionProviderRegistry CreateEmpty()
{
var providers = new Dictionary<CompressionType, ICompressionProvider>();
return new CompressionProviderRegistry(providers);
}
}

View File

@@ -1,29 +0,0 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace SharpCompress.Providers;
public abstract class ContextRequiredDecompressionProviderBase : DecompressionOnlyProviderBase
{
protected abstract string DecompressionContextRequirementDescription { get; }
protected virtual string DecompressionContextRequirementSuffix => string.Empty;
public sealed override Stream CreateDecompressStream(Stream source) =>
throw new InvalidOperationException(
$"{DecompressionContextRequirementDescription}. "
+ $"Use CreateDecompressStream(Stream, CompressionContext) overload{DecompressionContextRequirementSuffix}."
);
public sealed override ValueTask<Stream> CreateDecompressStreamAsync(
Stream source,
CancellationToken cancellationToken = default
) =>
throw new InvalidOperationException(
$"{DecompressionContextRequirementDescription}. "
+ "Use CreateDecompressStreamAsync(Stream, CompressionContext, CancellationToken) "
+ $"overload{DecompressionContextRequirementSuffix}."
);
}

View File

@@ -1,22 +0,0 @@
using System;
using System.IO;
using SharpCompress.Common;
namespace SharpCompress.Providers;
public abstract class DecompressionOnlyProviderBase : CompressionProviderBase
{
public override bool SupportsCompression => false;
public override bool SupportsDecompression => true;
protected abstract string CompressionNotSupportedMessage { get; }
public sealed override Stream CreateCompressStream(Stream destination, int compressionLevel) =>
throw new NotSupportedException(CompressionNotSupportedMessage);
public sealed override Stream CreateCompressStream(
Stream destination,
int compressionLevel,
CompressionContext context
) => throw new NotSupportedException(CompressionNotSupportedMessage);
}

View File

@@ -1,39 +0,0 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
using SharpCompress.Compressors;
using SharpCompress.Compressors.BZip2;
namespace SharpCompress.Providers.Default;
/// <summary>
/// Provides BZip2 compression using SharpCompress's internal implementation.
/// </summary>
public sealed class BZip2CompressionProvider : CompressionProviderBase
{
public override CompressionType CompressionType => CompressionType.BZip2;
public override bool SupportsCompression => true;
public override bool SupportsDecompression => true;
public override Stream CreateCompressStream(Stream destination, int compressionLevel)
{
// BZip2 doesn't use compressionLevel parameter in this implementation
return BZip2Stream.Create(destination, CompressionMode.Compress, false);
}
public override Stream CreateDecompressStream(Stream source)
{
return BZip2Stream.Create(source, CompressionMode.Decompress, false);
}
public override async ValueTask<Stream> CreateDecompressStreamAsync(
Stream source,
CancellationToken cancellationToken = default
)
{
return await BZip2Stream
.CreateAsync(source, CompressionMode.Decompress, false, false, cancellationToken)
.ConfigureAwait(false);
}
}

View File

@@ -1,22 +0,0 @@
using System.IO;
using SharpCompress.Common;
using SharpCompress.Compressors;
using SharpCompress.Compressors.Deflate64;
namespace SharpCompress.Providers.Default;
/// <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 : DecompressionOnlyProviderBase
{
public override CompressionType CompressionType => CompressionType.Deflate64;
protected override string CompressionNotSupportedMessage =>
"Deflate64 compression is not supported by SharpCompress's internal implementation.";
public override Stream CreateDecompressStream(Stream source)
{
return new Deflate64Stream(source, CompressionMode.Decompress);
}
}

View File

@@ -1,27 +0,0 @@
using System.IO;
using SharpCompress.Common;
using SharpCompress.Compressors;
using SharpCompress.Compressors.Deflate;
namespace SharpCompress.Providers.Default;
/// <summary>
/// Provides Deflate compression using SharpCompress's internal implementation.
/// </summary>
public sealed class DeflateCompressionProvider : CompressionProviderBase
{
public override CompressionType CompressionType => CompressionType.Deflate;
public override bool SupportsCompression => true;
public override bool SupportsDecompression => true;
public override Stream CreateCompressStream(Stream destination, int compressionLevel)
{
var level = (CompressionLevel)compressionLevel;
return new DeflateStream(destination, CompressionMode.Compress, level);
}
public override Stream CreateDecompressStream(Stream source)
{
return new DeflateStream(source, CompressionMode.Decompress);
}
}

View File

@@ -1,49 +0,0 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
using SharpCompress.Common.Zip.Headers;
using SharpCompress.Compressors.Explode;
namespace SharpCompress.Providers.Default;
/// <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 : ContextRequiredDecompressionProviderBase
{
public override CompressionType CompressionType => CompressionType.Explode;
protected override string CompressionNotSupportedMessage =>
"Explode compression is not supported by SharpCompress's internal implementation.";
protected override string DecompressionContextRequirementDescription =>
"Explode decompression requires compressed size, uncompressed size, and flags";
protected override string DecompressionContextRequirementSuffix => " with FormatOptions";
public override Stream CreateDecompressStream(Stream source, CompressionContext context)
{
ValidateRequiredSizes(context, "Explode");
var flags = RequireFormatOption<HeaderFlags>(context, "Explode", "HeaderFlags");
return ExplodeStream.Create(source, context.InputSize, context.OutputSize, flags);
}
public override async ValueTask<Stream> CreateDecompressStreamAsync(
Stream source,
CompressionContext context,
CancellationToken cancellationToken = default
)
{
ValidateRequiredSizes(context, "Explode");
var flags = RequireFormatOption<HeaderFlags>(context, "Explode", "HeaderFlags");
return await ExplodeStream
.CreateAsync(source, context.InputSize, context.OutputSize, flags, cancellationToken)
.ConfigureAwait(false);
}
}

View File

@@ -1,50 +0,0 @@
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
using SharpCompress.Compressors;
using SharpCompress.Compressors.Deflate;
namespace SharpCompress.Providers.Default;
/// <summary>
/// Provides GZip compression using SharpCompress's internal implementation.
/// </summary>
public sealed class GZipCompressionProvider : CompressionProviderBase
{
public override CompressionType CompressionType => CompressionType.GZip;
public override bool SupportsCompression => true;
public override bool SupportsDecompression => true;
public override Stream CreateCompressStream(Stream destination, int compressionLevel)
{
var level = (CompressionLevel)compressionLevel;
return new GZipStream(destination, CompressionMode.Compress, level, Encoding.UTF8);
}
public override Stream CreateDecompressStream(Stream source)
{
return new GZipStream(source, CompressionMode.Decompress);
}
public override Stream CreateDecompressStream(Stream source, CompressionContext context)
{
return new GZipStream(
source,
CompressionMode.Decompress,
CompressionLevel.Default,
context.ResolveArchiveEncoding()
);
}
public override ValueTask<Stream> CreateDecompressStreamAsync(
Stream source,
CompressionContext context,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return new ValueTask<Stream>(CreateDecompressStream(source, context));
}
}

View File

@@ -1,26 +0,0 @@
using System.IO;
using SharpCompress.Common;
using SharpCompress.Compressors;
using SharpCompress.Compressors.LZMA;
namespace SharpCompress.Providers.Default;
/// <summary>
/// Provides LZip compression using SharpCompress's internal implementation.
/// </summary>
public sealed class LZipCompressionProvider : CompressionProviderBase
{
public override CompressionType CompressionType => CompressionType.LZip;
public override bool SupportsCompression => true;
public override bool SupportsDecompression => true;
public override Stream CreateCompressStream(Stream destination, int compressionLevel)
{
return new LZipStream(destination, CompressionMode.Compress);
}
public override Stream CreateDecompressStream(Stream source)
{
return new LZipStream(source, CompressionMode.Decompress);
}
}

View File

@@ -1,116 +0,0 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
using SharpCompress.Compressors.LZMA;
namespace SharpCompress.Providers.Default;
/// <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 : CompressionProviderBase, ICompressionProviderHooks
{
public override CompressionType CompressionType => CompressionType.LZMA;
public override bool SupportsCompression => true;
public override bool SupportsDecompression => true;
public override Stream CreateCompressStream(Stream destination, int compressionLevel)
{
throw new InvalidOperationException(
"LZMA compression requires context with CanSeek information. "
+ "Use CreateCompressStream(Stream, int, CompressionContext) overload."
);
}
public override Stream CreateCompressStream(
Stream destination,
int compressionLevel,
CompressionContext context
)
{
// LZMA stream creation returns the encoder stream
// Note: Pre-compression data and properties are handled via ICompressionProviderHooks methods
var props = new LzmaEncoderProperties(!context.CanSeek);
return LzmaStream.Create(props, false, destination);
}
public override Stream CreateDecompressStream(Stream source)
{
throw new InvalidOperationException(
"LZMA decompression requires properties. "
+ "Use CreateDecompressStream(Stream, CompressionContext) overload with Properties."
);
}
public override 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 override ValueTask<Stream> CreateDecompressStreamAsync(
Stream source,
CancellationToken cancellationToken = default
) =>
throw new InvalidOperationException(
"LZMA decompression requires properties. "
+ "Use CreateDecompressStreamAsync(Stream, CompressionContext, CancellationToken) overload with Properties."
);
public override async ValueTask<Stream> CreateDecompressStreamAsync(
Stream source,
CompressionContext context,
CancellationToken cancellationToken = default
)
{
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 await LzmaStream
.CreateAsync(
context.Properties,
source,
context.InputSize,
context.OutputSize,
leaveOpen: false
)
.ConfigureAwait(false);
}
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;
}
}

View File

@@ -1,21 +0,0 @@
using System.IO;
using SharpCompress.Common;
using SharpCompress.Compressors.Lzw;
namespace SharpCompress.Providers.Default;
/// <summary>
/// Provides LZW compression decompression using SharpCompress's internal implementation.
/// Note: Compression is not supported by this provider.
/// </summary>
public sealed class LzwCompressionProvider : DecompressionOnlyProviderBase
{
public override CompressionType CompressionType => CompressionType.Lzw;
protected override string CompressionNotSupportedMessage =>
"LZW compression is not supported by SharpCompress's internal implementation.";
public override Stream CreateDecompressStream(Stream source)
{
return new LzwStream(source);
}
}

View File

@@ -1,116 +0,0 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
using SharpCompress.Compressors.PPMd;
namespace SharpCompress.Providers.Default;
/// <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 : CompressionProviderBase, ICompressionProviderHooks
{
public override CompressionType CompressionType => CompressionType.PPMd;
public override bool SupportsCompression => true;
public override bool SupportsDecompression => true;
public override 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 override 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 override Stream CreateDecompressStream(Stream source)
{
throw new InvalidOperationException(
"PPMd decompression requires properties. "
+ "Use CreateDecompressStream(Stream, CompressionContext) overload with Properties."
);
}
public override 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 override ValueTask<Stream> CreateDecompressStreamAsync(
Stream source,
CancellationToken cancellationToken = default
) =>
throw new InvalidOperationException(
"PPMd decompression requires properties. "
+ "Use CreateDecompressStreamAsync(Stream, CompressionContext, CancellationToken) overload with Properties."
);
public override async ValueTask<Stream> CreateDecompressStreamAsync(
Stream source,
CompressionContext context,
CancellationToken cancellationToken = default
)
{
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 await PpmdStream
.CreateAsync(props, source, false, cancellationToken)
.ConfigureAwait(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;
}
}

View File

@@ -1,13 +0,0 @@
using SharpCompress.Common;
namespace SharpCompress.Providers.Default;
/// <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 : ReduceCompressionProviderBase
{
public override CompressionType CompressionType => CompressionType.Reduce1;
protected override int Factor => 1;
}

View File

@@ -1,13 +0,0 @@
using SharpCompress.Common;
namespace SharpCompress.Providers.Default;
/// <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 : ReduceCompressionProviderBase
{
public override CompressionType CompressionType => CompressionType.Reduce2;
protected override int Factor => 2;
}

View File

@@ -1,13 +0,0 @@
using SharpCompress.Common;
namespace SharpCompress.Providers.Default;
/// <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 : ReduceCompressionProviderBase
{
public override CompressionType CompressionType => CompressionType.Reduce3;
protected override int Factor => 3;
}

View File

@@ -1,13 +0,0 @@
using SharpCompress.Common;
namespace SharpCompress.Providers.Default;
/// <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 : ReduceCompressionProviderBase
{
public override CompressionType CompressionType => CompressionType.Reduce4;
protected override int Factor => 4;
}

View File

@@ -1,36 +0,0 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
using SharpCompress.Compressors.Reduce;
namespace SharpCompress.Providers.Default;
public abstract class ReduceCompressionProviderBase : ContextRequiredDecompressionProviderBase
{
protected abstract int Factor { get; }
protected override string DecompressionContextRequirementDescription =>
"Reduce decompression requires compressed and uncompressed sizes";
protected override string CompressionNotSupportedMessage =>
"Reduce compression is not supported by SharpCompress's internal implementation.";
public sealed override Stream CreateDecompressStream(Stream source, CompressionContext context)
{
ValidateRequiredSizes(context, "Reduce");
return ReduceStream.Create(source, context.InputSize, context.OutputSize, Factor);
}
public sealed override async ValueTask<Stream> CreateDecompressStreamAsync(
Stream source,
CompressionContext context,
CancellationToken cancellationToken = default
)
{
ValidateRequiredSizes(context, "Reduce");
return await ReduceStream
.CreateAsync(source, context.InputSize, context.OutputSize, Factor, cancellationToken)
.ConfigureAwait(false);
}
}

View File

@@ -1,56 +0,0 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
using SharpCompress.Compressors;
using SharpCompress.Compressors.Shrink;
namespace SharpCompress.Providers.Default;
/// <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 : ContextRequiredDecompressionProviderBase
{
public override CompressionType CompressionType => CompressionType.Shrink;
protected override string CompressionNotSupportedMessage =>
"Shrink compression is not supported by SharpCompress's internal implementation.";
protected override string DecompressionContextRequirementDescription =>
"Shrink decompression requires compressed and uncompressed sizes";
public override Stream CreateDecompressStream(Stream source, CompressionContext context)
{
ValidateRequiredSizes(context, "Shrink");
return new ShrinkStream(
source,
CompressionMode.Decompress,
context.InputSize,
context.OutputSize
);
}
public override async ValueTask<Stream> CreateDecompressStreamAsync(
Stream source,
CompressionContext context,
CancellationToken cancellationToken = default
)
{
ValidateRequiredSizes(context, "Shrink");
return await ShrinkStream
.CreateAsync(
source,
CompressionMode.Decompress,
context.InputSize,
context.OutputSize,
cancellationToken
)
.ConfigureAwait(false);
}
}

View File

@@ -1,21 +0,0 @@
using System.IO;
using SharpCompress.Common;
using SharpCompress.Compressors.Xz;
namespace SharpCompress.Providers.Default;
/// <summary>
/// Provides XZ compression decompression using SharpCompress's internal implementation.
/// Note: Compression is not supported by this provider.
/// </summary>
public sealed class XzCompressionProvider : DecompressionOnlyProviderBase
{
public override CompressionType CompressionType => CompressionType.Xz;
protected override string CompressionNotSupportedMessage =>
"XZ compression is not supported by SharpCompress's internal implementation.";
public override Stream CreateDecompressStream(Stream source)
{
return new XZStream(source);
}
}

View File

@@ -1,25 +0,0 @@
using System.IO;
using SharpCompress.Common;
using ZStd = SharpCompress.Compressors.ZStandard;
namespace SharpCompress.Providers.Default;
/// <summary>
/// Provides ZStandard compression using SharpCompress's internal implementation.
/// </summary>
public sealed class ZStandardCompressionProvider : CompressionProviderBase
{
public override CompressionType CompressionType => CompressionType.ZStandard;
public override bool SupportsCompression => true;
public override bool SupportsDecompression => true;
public override Stream CreateCompressStream(Stream destination, int compressionLevel)
{
return new ZStd.CompressionStream(destination, compressionLevel);
}
public override Stream CreateDecompressStream(Stream source)
{
return new ZStd.DecompressionStream(source);
}
}

View File

@@ -1,151 +0,0 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
namespace SharpCompress.Providers;
/// <summary>
/// Provides compression and decompression stream creation for a specific compression type.
/// Implement this interface to supply alternative compression implementations.
/// </summary>
/// <remarks>
/// <para>
/// This interface abstracts the creation of compression and decompression streams,
/// allowing SharpCompress to use different implementations of the same compression type.
/// For example, you can provide an implementation that uses System.IO.Compression
/// for Deflate/GZip instead of the internal DotNetZip-derived implementation.
/// </para>
/// <para>
/// Implementations should be thread-safe for concurrent decompression operations,
/// but CreateCompressStream/CreateDecompressStream themselves return new stream instances
/// that are not shared.
/// </para>
/// <para>
/// For simpler implementations, derive from <see cref="CompressionProviderBase"/> which provides
/// default async implementations that delegate to the synchronous methods.
/// </para>
/// </remarks>
public interface ICompressionProvider
{
/// <summary>
/// The compression type this provider handles.
/// </summary>
CompressionType CompressionType { get; }
/// <summary>
/// Whether this provider supports compression (writing).
/// </summary>
bool SupportsCompression { get; }
/// <summary>
/// Whether this provider supports decompression (reading).
/// </summary>
bool SupportsDecompression { get; }
/// <summary>
/// Creates a compression stream that compresses data written to it.
/// </summary>
/// <param name="destination">The destination stream to write compressed data to.</param>
/// <param name="compressionLevel">The compression level (0-9, algorithm-specific).</param>
/// <returns>A stream that compresses data written to it.</returns>
/// <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>
/// <param name="source">The source stream to read compressed data from.</param>
/// <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. Providers may use
/// <see cref="CompressionContext.ReaderOptions"/> for archive header encoding
/// (via <see cref="ReaderOptions.ArchiveEncoding"/>) and
/// <see cref="CompressionContext.FormatOptions"/> for format-specific metadata
/// such as compression properties or algorithm-specific configuration.
/// </param>
/// <returns>A decompression stream.</returns>
/// <exception cref="NotSupportedException">Thrown if SupportsDecompression is false.</exception>
Stream CreateDecompressStream(Stream source, CompressionContext context);
/// <summary>
/// Asynchronously creates a compression stream that compresses data written to it.
/// </summary>
/// <param name="destination">The destination stream to write compressed data to.</param>
/// <param name="compressionLevel">The compression level (0-9, algorithm-specific).</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>A task containing the compression stream.</returns>
/// <exception cref="NotSupportedException">Thrown if SupportsCompression is false.</exception>
ValueTask<Stream> CreateCompressStreamAsync(
Stream destination,
int compressionLevel,
CancellationToken cancellationToken = default
);
/// <summary>
/// Asynchronously 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>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>A task containing the compression stream.</returns>
/// <exception cref="NotSupportedException">Thrown if SupportsCompression is false.</exception>
ValueTask<Stream> CreateCompressStreamAsync(
Stream destination,
int compressionLevel,
CompressionContext context,
CancellationToken cancellationToken = default
);
/// <summary>
/// Asynchronously creates a decompression stream that decompresses data read from it.
/// </summary>
/// <param name="source">The source stream to read compressed data from.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>A task containing the decompression stream.</returns>
/// <exception cref="NotSupportedException">Thrown if SupportsDecompression is false.</exception>
ValueTask<Stream> CreateDecompressStreamAsync(
Stream source,
CancellationToken cancellationToken = default
);
/// <summary>
/// Asynchronously creates a decompression stream with context information.
/// </summary>
/// <param name="source">The source stream.</param>
/// <param name="context">
/// Context information about the decompression. Providers may use
/// <see cref="CompressionContext.FormatOptions"/> for format-specific metadata
/// (for example, archive header encoding).
/// </param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>A task containing the decompression stream.</returns>
/// <exception cref="NotSupportedException">Thrown if SupportsDecompression is false.</exception>
ValueTask<Stream> CreateDecompressStreamAsync(
Stream source,
CompressionContext context,
CancellationToken cancellationToken = default
);
}

View File

@@ -1,42 +0,0 @@
using System.IO;
namespace SharpCompress.Providers;
/// <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 ICompressionProviderHooks : 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);
}

View File

@@ -1,19 +0,0 @@
namespace SharpCompress.Providers;
/// <summary>
/// Interface for compression streams that require explicit finalization
/// before disposal to ensure all compressed data is flushed properly.
/// </summary>
/// <remarks>
/// Some compression formats (notably BZip2 and LZip) require explicit
/// finalization to write trailer/footer data. Implementing this interface
/// allows generic code to handle finalization without knowing the specific stream type.
/// </remarks>
public interface IFinishable
{
/// <summary>
/// Finalizes the compression, flushing any remaining buffered data
/// and writing format-specific trailer/footer bytes.
/// </summary>
void Finish();
}

View File

@@ -1,51 +0,0 @@
using System.IO;
using System.IO.Compression;
using SharpCompress.Common;
namespace SharpCompress.Providers.System;
/// <summary>
/// Provides Deflate compression using System.IO.Compression.DeflateStream.
/// </summary>
/// <remarks>
/// On modern .NET (5+), System.IO.Compression uses hardware-accelerated zlib
/// and is significantly faster than SharpCompress's pure C# implementation.
/// </remarks>
public sealed class SystemDeflateCompressionProvider : CompressionProviderBase
{
public override CompressionType CompressionType => CompressionType.Deflate;
public override bool SupportsCompression => true;
public override bool SupportsDecompression => true;
public override Stream CreateCompressStream(Stream destination, int compressionLevel)
{
var bclLevel = MapCompressionLevel(compressionLevel);
return new DeflateStream(destination, bclLevel, leaveOpen: false);
}
public override Stream CreateDecompressStream(Stream source)
{
return new DeflateStream(
source,
global::System.IO.Compression.CompressionMode.Decompress,
leaveOpen: false
);
}
/// <summary>
/// Maps SharpCompress compression level (0-9) to BCL CompressionLevel.
/// </summary>
private static global::System.IO.Compression.CompressionLevel MapCompressionLevel(int level)
{
// Map 0-9 to appropriate BCL levels
return level switch
{
0 => global::System.IO.Compression.CompressionLevel.NoCompression,
<= 2 => global::System.IO.Compression.CompressionLevel.Fastest,
#if NET7_0_OR_GREATER
>= 8 => global::System.IO.Compression.CompressionLevel.SmallestSize,
#endif
_ => global::System.IO.Compression.CompressionLevel.Optimal,
};
}
}

View File

@@ -1,51 +0,0 @@
using System.IO;
using System.IO.Compression;
using SharpCompress.Common;
namespace SharpCompress.Providers.System;
/// <summary>
/// Provides GZip compression using System.IO.Compression.GZipStream.
/// </summary>
/// <remarks>
/// On modern .NET (5+), System.IO.Compression uses hardware-accelerated zlib
/// and is significantly faster than SharpCompress's pure C# implementation.
/// </remarks>
public sealed class SystemGZipCompressionProvider : CompressionProviderBase
{
public override CompressionType CompressionType => CompressionType.GZip;
public override bool SupportsCompression => true;
public override bool SupportsDecompression => true;
public override Stream CreateCompressStream(Stream destination, int compressionLevel)
{
var bclLevel = MapCompressionLevel(compressionLevel);
return new GZipStream(destination, bclLevel, leaveOpen: false);
}
public override Stream CreateDecompressStream(Stream source)
{
return new GZipStream(
source,
global::System.IO.Compression.CompressionMode.Decompress,
leaveOpen: false
);
}
/// <summary>
/// Maps SharpCompress compression level (0-9) to BCL CompressionLevel.
/// </summary>
private static global::System.IO.Compression.CompressionLevel MapCompressionLevel(int level)
{
// Map 0-9 to appropriate BCL levels
return level switch
{
0 => global::System.IO.Compression.CompressionLevel.NoCompression,
<= 2 => global::System.IO.Compression.CompressionLevel.Fastest,
#if NET7_0_OR_GREATER
>= 8 => global::System.IO.Compression.CompressionLevel.SmallestSize,
#endif
_ => global::System.IO.Compression.CompressionLevel.Optimal,
};
}
}

View File

@@ -43,10 +43,7 @@ public abstract partial class AbstractReader<TEntry, TVolume>
}
if (_entriesForCurrentReadStreamAsync is null)
{
return await LoadStreamForReadingAsync(
await RequestInitialStreamAsync(cancellationToken).ConfigureAwait(false)
)
.ConfigureAwait(false);
return await LoadStreamForReadingAsync(RequestInitialStream()).ConfigureAwait(false);
}
if (!_wroteCurrentEntry)
{

View File

@@ -2,8 +2,6 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
using SharpCompress.IO;
@@ -142,10 +140,6 @@ public abstract partial class AbstractReader<TEntry, TVolume> : IReader, IAsyncR
protected virtual Stream RequestInitialStream() =>
Volume.NotNull("Volume isn't loaded.").Stream;
protected virtual ValueTask<Stream> RequestInitialStreamAsync(
CancellationToken cancellationToken = default
) => new(RequestInitialStream());
internal virtual bool NextEntryForCurrentStream() =>
_entriesForCurrentReadStream.NotNull().MoveNext();

View File

@@ -1,7 +1,5 @@
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
namespace SharpCompress.Readers.Ace;
@@ -35,25 +33,15 @@ public partial class AceReader
return new MultiVolumeAceReader(streams, options ?? new ReaderOptions());
}
public static ValueTask<IAsyncReader> OpenAsyncReader(
string path,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
public static IAsyncReader OpenAsyncReader(string path, ReaderOptions? readerOptions = null)
{
cancellationToken.ThrowIfCancellationRequested();
path.NotNullOrEmpty(nameof(path));
return new((IAsyncReader)OpenReader(new FileInfo(path), readerOptions));
return (IAsyncReader)OpenReader(new FileInfo(path), readerOptions);
}
public static ValueTask<IAsyncReader> OpenAsyncReader(
Stream stream,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
public static IAsyncReader OpenAsyncReader(Stream stream, ReaderOptions? readerOptions = null)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IAsyncReader)OpenReader(stream, readerOptions));
return (IAsyncReader)OpenReader(stream, readerOptions);
}
public static IAsyncReader OpenAsyncReader(
@@ -65,14 +53,12 @@ public partial class AceReader
return new MultiVolumeAceReader(streams, options ?? new ReaderOptions());
}
public static ValueTask<IAsyncReader> OpenAsyncReader(
public static IAsyncReader OpenAsyncReader(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
ReaderOptions? readerOptions = null
)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IAsyncReader)OpenReader(fileInfo, readerOptions));
return (IAsyncReader)OpenReader(fileInfo, readerOptions);
}
public static IReader OpenReader(string filePath, ReaderOptions? readerOptions = null)

View File

@@ -1,42 +1,28 @@
#if NET8_0_OR_GREATER
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
namespace SharpCompress.Readers.Arc;
public partial class ArcReader : IReaderOpenable
{
public static ValueTask<IAsyncReader> OpenAsyncReader(
string path,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
public static IAsyncReader OpenAsyncReader(string path, ReaderOptions? readerOptions = null)
{
cancellationToken.ThrowIfCancellationRequested();
path.NotNullOrEmpty(nameof(path));
return new((IAsyncReader)OpenReader(new FileInfo(path), readerOptions));
return (IAsyncReader)OpenReader(new FileInfo(path), readerOptions);
}
public static ValueTask<IAsyncReader> OpenAsyncReader(
Stream stream,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
public static IAsyncReader OpenAsyncReader(Stream stream, ReaderOptions? readerOptions = null)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IAsyncReader)OpenReader(stream, readerOptions));
return (IAsyncReader)OpenReader(stream, readerOptions);
}
public static ValueTask<IAsyncReader> OpenAsyncReader(
public static IAsyncReader OpenAsyncReader(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
ReaderOptions? readerOptions = null
)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IAsyncReader)OpenReader(fileInfo, readerOptions));
return (IAsyncReader)OpenReader(fileInfo, readerOptions);
}
public static IReader OpenReader(string filePath, ReaderOptions? readerOptions = null)

View File

@@ -1,42 +1,28 @@
#if NET8_0_OR_GREATER
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
namespace SharpCompress.Readers.Arj;
public partial class ArjReader : IReaderOpenable
{
public static ValueTask<IAsyncReader> OpenAsyncReader(
string path,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
public static IAsyncReader OpenAsyncReader(string path, ReaderOptions? readerOptions = null)
{
cancellationToken.ThrowIfCancellationRequested();
path.NotNullOrEmpty(nameof(path));
return new((IAsyncReader)OpenReader(new FileInfo(path), readerOptions));
return (IAsyncReader)OpenReader(new FileInfo(path), readerOptions);
}
public static ValueTask<IAsyncReader> OpenAsyncReader(
Stream stream,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
public static IAsyncReader OpenAsyncReader(Stream stream, ReaderOptions? readerOptions = null)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IAsyncReader)OpenReader(stream, readerOptions));
return (IAsyncReader)OpenReader(stream, readerOptions);
}
public static ValueTask<IAsyncReader> OpenAsyncReader(
public static IAsyncReader OpenAsyncReader(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
ReaderOptions? readerOptions = null
)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IAsyncReader)OpenReader(fileInfo, readerOptions));
return (IAsyncReader)OpenReader(fileInfo, readerOptions);
}
public static IReader OpenReader(string filePath, ReaderOptions? readerOptions = null)

View File

@@ -1,6 +1,4 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace SharpCompress.Readers.GZip;
@@ -9,35 +7,23 @@ public partial class GZipReader
: IReaderOpenable
#endif
{
public static ValueTask<IAsyncReader> OpenAsyncReader(
string path,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
public static IAsyncReader OpenAsyncReader(string path, ReaderOptions? readerOptions = null)
{
cancellationToken.ThrowIfCancellationRequested();
path.NotNullOrEmpty(nameof(path));
return new((IAsyncReader)OpenReader(new FileInfo(path), readerOptions));
return (IAsyncReader)OpenReader(new FileInfo(path), readerOptions);
}
public static ValueTask<IAsyncReader> OpenAsyncReader(
Stream stream,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
public static IAsyncReader OpenAsyncReader(Stream stream, ReaderOptions? readerOptions = null)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IAsyncReader)OpenReader(stream, readerOptions));
return (IAsyncReader)OpenReader(stream, readerOptions);
}
public static ValueTask<IAsyncReader> OpenAsyncReader(
public static IAsyncReader OpenAsyncReader(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
ReaderOptions? readerOptions = null
)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IAsyncReader)OpenReader(fileInfo, readerOptions));
return (IAsyncReader)OpenReader(fileInfo, readerOptions);
}
public static IReader OpenReader(string filePath, ReaderOptions? readerOptions = null)

View File

@@ -9,18 +9,18 @@ public interface IReaderFactory : Factories.IFactory
/// <summary>
/// Opens a Reader for Non-seeking usage.
/// </summary>
/// <param name="stream">An open, readable stream.</param>
/// <param name="options">Reader options.</param>
/// <returns>The opened reader.</returns>
/// <param name="stream"></param>
/// <param name="options"></param>
/// <returns></returns>
IReader OpenReader(Stream stream, ReaderOptions? options);
/// <summary>
/// Opens a Reader for Non-seeking usage asynchronously.
/// </summary>
/// <param name="stream">An open, readable stream.</param>
/// <param name="options">Reader options.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>A <see cref="ValueTask{TResult}"/> containing the opened async reader.</returns>
/// <param name="stream"></param>
/// <param name="options"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
ValueTask<IAsyncReader> OpenAsyncReader(
Stream stream,
ReaderOptions? options,

View File

@@ -1,7 +1,6 @@
#if NET8_0_OR_GREATER
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace SharpCompress.Readers;
@@ -16,22 +15,19 @@ public interface IReaderOpenable
public static abstract IReader OpenReader(Stream stream, ReaderOptions? readerOptions = null);
public static abstract ValueTask<IAsyncReader> OpenAsyncReader(
public static abstract IAsyncReader OpenAsyncReader(
string path,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
ReaderOptions? readerOptions = null
);
public static abstract ValueTask<IAsyncReader> OpenAsyncReader(
public static abstract IAsyncReader OpenAsyncReader(
Stream stream,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
ReaderOptions? readerOptions = null
);
public static abstract ValueTask<IAsyncReader> OpenAsyncReader(
public static abstract IAsyncReader OpenAsyncReader(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
ReaderOptions? readerOptions = null
);
}
#endif

View File

@@ -1,6 +1,4 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace SharpCompress.Readers.Lzw;
@@ -9,35 +7,23 @@ public partial class LzwReader
: IReaderOpenable
#endif
{
public static ValueTask<IAsyncReader> OpenAsyncReader(
string path,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
public static IAsyncReader OpenAsyncReader(string path, ReaderOptions? readerOptions = null)
{
cancellationToken.ThrowIfCancellationRequested();
path.NotNullOrEmpty(nameof(path));
return new((IAsyncReader)OpenReader(new FileInfo(path), readerOptions));
return (IAsyncReader)OpenReader(new FileInfo(path), readerOptions);
}
public static ValueTask<IAsyncReader> OpenAsyncReader(
Stream stream,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
public static IAsyncReader OpenAsyncReader(Stream stream, ReaderOptions? readerOptions = null)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IAsyncReader)OpenReader(stream, readerOptions));
return (IAsyncReader)OpenReader(stream, readerOptions);
}
public static ValueTask<IAsyncReader> OpenAsyncReader(
public static IAsyncReader OpenAsyncReader(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
ReaderOptions? readerOptions = null
)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IAsyncReader)OpenReader(fileInfo, readerOptions));
return (IAsyncReader)OpenReader(fileInfo, readerOptions);
}
public static IReader OpenReader(string filePath, ReaderOptions? readerOptions = null)

View File

@@ -1,42 +1,28 @@
#if NET8_0_OR_GREATER
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
namespace SharpCompress.Readers.Rar;
public partial class RarReader : IReaderOpenable
{
public static ValueTask<IAsyncReader> OpenAsyncReader(
string path,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
public static IAsyncReader OpenAsyncReader(string path, ReaderOptions? readerOptions = null)
{
cancellationToken.ThrowIfCancellationRequested();
path.NotNullOrEmpty(nameof(path));
return new((IAsyncReader)OpenReader(new FileInfo(path), readerOptions));
return (IAsyncReader)OpenReader(new FileInfo(path), readerOptions);
}
public static ValueTask<IAsyncReader> OpenAsyncReader(
Stream stream,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
public static IAsyncReader OpenAsyncReader(Stream stream, ReaderOptions? readerOptions = null)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IAsyncReader)OpenReader(stream, readerOptions));
return (IAsyncReader)OpenReader(stream, readerOptions);
}
public static ValueTask<IAsyncReader> OpenAsyncReader(
public static IAsyncReader OpenAsyncReader(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
ReaderOptions? readerOptions = null
)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IAsyncReader)OpenReader(fileInfo, readerOptions));
return (IAsyncReader)OpenReader(fileInfo, readerOptions);
}
}
#endif

View File

@@ -69,14 +69,20 @@ public static partial class ReaderFactory
a.GetSupportedExtensions()
.Contains(options.ExtensionHint, StringComparer.CurrentCultureIgnoreCase)
);
if (testedFactory is not null)
if (testedFactory is IReaderFactory readerFactory)
{
var reader = await testedFactory
.TryOpenReaderAsync(sharpCompressStream, options, cancellationToken)
.ConfigureAwait(false);
if (reader is not null)
sharpCompressStream.Rewind();
if (
await testedFactory
.IsArchiveAsync(sharpCompressStream, cancellationToken: cancellationToken)
.ConfigureAwait(false)
)
{
return reader;
sharpCompressStream.Rewind();
sharpCompressStream.StopRecording();
return await readerFactory
.OpenAsyncReader(sharpCompressStream, options, cancellationToken)
.ConfigureAwait(false);
}
}
sharpCompressStream.Rewind();
@@ -88,12 +94,19 @@ public static partial class ReaderFactory
{
continue; // Already tested above
}
var reader = await factory
.TryOpenReaderAsync(sharpCompressStream, options, cancellationToken)
.ConfigureAwait(false);
if (reader is not null)
sharpCompressStream.Rewind();
if (
factory is IReaderFactory readerFactory
&& await factory
.IsArchiveAsync(sharpCompressStream, cancellationToken: cancellationToken)
.ConfigureAwait(false)
)
{
return reader;
sharpCompressStream.Rewind();
sharpCompressStream.StopRecording();
return await readerFactory
.OpenAsyncReader(sharpCompressStream, options, cancellationToken)
.ConfigureAwait(false);
}
}

View File

@@ -1,6 +1,8 @@
using System;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
using SharpCompress.Factories;
using SharpCompress.IO;

View File

@@ -1,8 +1,6 @@
using System;
using SharpCompress.Common;
using SharpCompress.Common.Options;
using SharpCompress.Compressors;
using SharpCompress.Providers;
namespace SharpCompress.Readers;
@@ -23,6 +21,16 @@ namespace SharpCompress.Readers;
/// </remarks>
public sealed record ReaderOptions : IReaderOptions
{
/// <summary>
/// The default buffer size for stream operations.
/// This value (65536 bytes) is preserved for backward compatibility.
/// New code should use Constants.BufferSize instead (81920 bytes), which matches .NET's Stream.CopyTo default.
/// </summary>
[Obsolete(
"Use Constants.BufferSize instead. This constant will be removed in a future version."
)]
public const int DefaultBufferSize = 0x10000;
/// <summary>
/// SharpCompress will keep the supplied streams open. Default is true.
/// </summary>
@@ -141,14 +149,6 @@ public sealed record ReaderOptions : IReaderOptions
/// </remarks>
public Action<string, string>? SymbolicLinkHandler { get; init; }
/// <summary>
/// Registry of compression providers.
/// Defaults to <see cref="CompressionProviderRegistry.Default" /> but can be replaced with custom implementations, such as
/// System.IO.Compression for Deflate/GZip on modern .NET.
/// </summary>
public CompressionProviderRegistry Providers { get; init; } =
CompressionProviderRegistry.Default;
/// <summary>
/// Creates a new ReaderOptions instance with default values.
/// </summary>

View File

@@ -1,8 +1,6 @@
using System;
using SharpCompress.Common;
using SharpCompress.Common.Options;
using SharpCompress.Compressors;
using SharpCompress.Providers;
namespace SharpCompress.Readers;
@@ -126,17 +124,4 @@ public static class ReaderOptionsExtensions
this ReaderOptions options,
Action<string, string>? handler
) => options with { SymbolicLinkHandler = handler };
/// <summary>
/// Creates a copy with the specified compression provider registry.
/// </summary>
/// <exception cref="ArgumentNullException">Thrown if <paramref name="providers"/> is null.</exception>
public static ReaderOptions WithProviders(
this ReaderOptions options,
CompressionProviderRegistry providers
)
{
_ = providers ?? throw new ArgumentNullException(nameof(providers));
return options with { Providers = providers };
}
}

View File

@@ -1,6 +1,16 @@
using System.Collections.Generic;
using System.IO;
using SharpCompress.Archives.GZip;
using SharpCompress.Archives.Tar;
using SharpCompress.Common;
using SharpCompress.Common.Tar;
using SharpCompress.Compressors;
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;
namespace SharpCompress.Readers.Tar;

View File

@@ -1,11 +1,5 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Archives.Tar;
using SharpCompress.Common;
using SharpCompress.Factories;
using SharpCompress.IO;
using SharpCompress.Providers;
namespace SharpCompress.Readers.Tar;
@@ -14,133 +8,23 @@ public partial class TarReader
: IReaderOpenable
#endif
{
private static Stream CreateProbeDecompressionStream(
Stream stream,
CompressionType compressionType,
CompressionProviderRegistry providers,
ReaderOptions options
)
{
var nonDisposingStream = SharpCompressStream.CreateNonDisposing(stream);
if (compressionType == CompressionType.None)
{
return nonDisposingStream;
}
if (compressionType == CompressionType.GZip)
{
return providers.CreateDecompressStream(
compressionType,
nonDisposingStream,
CompressionContext.FromStream(nonDisposingStream).WithReaderOptions(options)
);
}
return providers.CreateDecompressStream(compressionType, nonDisposingStream);
}
private static async ValueTask<Stream> CreateProbeDecompressionStreamAsync(
Stream stream,
CompressionType compressionType,
CompressionProviderRegistry providers,
ReaderOptions options,
CancellationToken cancellationToken = default
)
{
var nonDisposingStream = SharpCompressStream.CreateNonDisposing(stream);
if (compressionType == CompressionType.None)
{
return nonDisposingStream;
}
if (compressionType == CompressionType.GZip)
{
return await providers
.CreateDecompressStreamAsync(
compressionType,
nonDisposingStream,
CompressionContext.FromStream(nonDisposingStream).WithReaderOptions(options),
cancellationToken
)
.ConfigureAwait(false);
}
return await providers
.CreateDecompressStreamAsync(compressionType, nonDisposingStream, cancellationToken)
.ConfigureAwait(false);
}
public static ValueTask<IAsyncReader> OpenAsyncReader(
string path,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
public static IAsyncReader OpenAsyncReader(string path, ReaderOptions? readerOptions = null)
{
path.NotNullOrEmpty(nameof(path));
return OpenAsyncReader(new FileInfo(path), readerOptions, cancellationToken);
return (IAsyncReader)OpenReader(new FileInfo(path), readerOptions);
}
public static async ValueTask<IAsyncReader> OpenAsyncReader(
Stream stream,
ReaderOptions? options = null,
CancellationToken cancellationToken = default
)
public static IAsyncReader OpenAsyncReader(Stream stream, ReaderOptions? readerOptions = null)
{
cancellationToken.ThrowIfCancellationRequested();
stream.NotNull(nameof(stream));
options ??= new ReaderOptions();
var sharpCompressStream = SharpCompressStream.Create(
stream,
bufferSize: options.RewindableBufferSize
);
long pos = sharpCompressStream.Position;
foreach (var wrapper in TarWrapper.Wrappers)
{
sharpCompressStream.Position = pos;
if (
!await wrapper
.IsMatchAsync(sharpCompressStream, cancellationToken)
.ConfigureAwait(false)
)
{
continue;
}
sharpCompressStream.Position = pos;
var testStream = await CreateProbeDecompressionStreamAsync(
sharpCompressStream,
wrapper.CompressionType,
options.Providers,
options,
cancellationToken
)
.ConfigureAwait(false);
if (
await TarArchive.IsTarFileAsync(testStream, cancellationToken).ConfigureAwait(false)
)
{
sharpCompressStream.Position = pos;
return new TarReader(sharpCompressStream, options, wrapper.CompressionType);
}
if (wrapper.CompressionType != CompressionType.None)
{
throw new InvalidFormatException("Not a tar file.");
}
}
sharpCompressStream.Position = pos;
return new TarReader(sharpCompressStream, options, CompressionType.None);
return (IAsyncReader)OpenReader(stream, readerOptions);
}
public static ValueTask<IAsyncReader> OpenAsyncReader(
public static IAsyncReader OpenAsyncReader(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
ReaderOptions? readerOptions = null
)
{
readerOptions ??= new ReaderOptions() { LeaveStreamOpen = false };
return OpenAsyncReader(fileInfo.OpenRead(), readerOptions, cancellationToken);
return (IAsyncReader)OpenReader(fileInfo, readerOptions);
}
public static IReader OpenReader(string filePath, ReaderOptions? readerOptions = null)
@@ -152,53 +36,6 @@ public partial class TarReader
public static IReader OpenReader(FileInfo fileInfo, ReaderOptions? readerOptions = null)
{
fileInfo.NotNull(nameof(fileInfo));
readerOptions ??= new ReaderOptions() { LeaveStreamOpen = false };
return OpenReader(fileInfo.OpenRead(), readerOptions);
}
/// <summary>
/// Opens a TarReader for Non-seeking usage with a single volume
/// </summary>
/// <param name="stream"></param>
/// <param name="options"></param>
/// <returns></returns>
public static IReader OpenReader(Stream stream, ReaderOptions? options = null)
{
stream.NotNull(nameof(stream));
options ??= new ReaderOptions();
var sharpCompressStream = SharpCompressStream.Create(
stream,
bufferSize: options.RewindableBufferSize
);
long pos = sharpCompressStream.Position;
foreach (var wrapper in TarWrapper.Wrappers)
{
sharpCompressStream.Position = pos;
if (!wrapper.IsMatch(sharpCompressStream))
{
continue;
}
sharpCompressStream.Position = pos;
var testStream = CreateProbeDecompressionStream(
sharpCompressStream,
wrapper.CompressionType,
options.Providers,
options
);
if (TarArchive.IsTarFile(testStream))
{
sharpCompressStream.Position = pos;
return new TarReader(sharpCompressStream, options, wrapper.CompressionType);
}
if (wrapper.CompressionType != CompressionType.None)
{
throw new InvalidFormatException("Not a tar file.");
}
}
sharpCompressStream.Position = pos;
return new TarReader(sharpCompressStream, options, CompressionType.None);
}
}

View File

@@ -1,8 +1,8 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Archives.GZip;
using SharpCompress.Archives.Tar;
using SharpCompress.Common;
using SharpCompress.Common.Tar;
using SharpCompress.Compressors;
@@ -13,7 +13,6 @@ using SharpCompress.Compressors.Lzw;
using SharpCompress.Compressors.Xz;
using SharpCompress.Compressors.ZStandard;
using SharpCompress.IO;
using SharpCompress.Providers;
namespace SharpCompress.Readers.Tar;
@@ -33,77 +32,93 @@ public partial class TarReader : AbstractReader<TarEntry, TarVolume>
protected override Stream RequestInitialStream()
{
var stream = base.RequestInitialStream();
var providers = Options.Providers;
return compressionType switch
{
CompressionType.BZip2 => providers.CreateDecompressStream(
CompressionType.BZip2,
stream
),
CompressionType.GZip => providers.CreateDecompressStream(
CompressionType.GZip,
stream,
CompressionContext.FromStream(stream).WithReaderOptions(Options)
),
CompressionType.ZStandard => providers.CreateDecompressStream(
CompressionType.ZStandard,
stream
),
CompressionType.LZip => providers.CreateDecompressStream(CompressionType.LZip, stream),
CompressionType.Xz => providers.CreateDecompressStream(CompressionType.Xz, stream),
CompressionType.Lzw => providers.CreateDecompressStream(CompressionType.Lzw, stream),
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.None => stream,
_ => throw new NotSupportedException("Invalid compression type: " + compressionType),
};
}
protected override ValueTask<Stream> RequestInitialStreamAsync(
CancellationToken cancellationToken = default
)
{
var stream = base.RequestInitialStream();
var providers = Options.Providers;
#region OpenReader
return compressionType switch
/// <summary>
/// Opens a TarReader for Non-seeking usage with a single volume
/// </summary>
/// <param name="stream"></param>
/// <param name="options"></param>
/// <returns></returns>
public static IReader OpenReader(Stream stream, ReaderOptions? options = null)
{
stream.NotNull(nameof(stream));
options = options ?? new ReaderOptions();
var sharpCompressStream = SharpCompressStream.Create(
stream,
bufferSize: options.RewindableBufferSize
);
long pos = sharpCompressStream.Position;
if (GZipArchive.IsGZipFile(sharpCompressStream))
{
CompressionType.BZip2 => providers.CreateDecompressStreamAsync(
CompressionType.BZip2,
stream,
cancellationToken
),
CompressionType.GZip => providers.CreateDecompressStreamAsync(
CompressionType.GZip,
stream,
CompressionContext.FromStream(stream).WithReaderOptions(Options),
cancellationToken
),
CompressionType.ZStandard => providers.CreateDecompressStreamAsync(
CompressionType.ZStandard,
stream,
cancellationToken
),
CompressionType.LZip => providers.CreateDecompressStreamAsync(
CompressionType.LZip,
stream,
cancellationToken
),
CompressionType.Xz => providers.CreateDecompressStreamAsync(
CompressionType.Xz,
stream,
cancellationToken
),
CompressionType.Lzw => providers.CreateDecompressStreamAsync(
CompressionType.Lzw,
stream,
cancellationToken
),
CompressionType.None => new ValueTask<Stream>(stream),
_ => throw new NotSupportedException("Invalid compression type: " + compressionType),
};
sharpCompressStream.Position = pos;
var testStream = new GZipStream(sharpCompressStream, CompressionMode.Decompress);
if (TarArchive.IsTarFile(testStream))
{
sharpCompressStream.Position = pos;
return new TarReader(sharpCompressStream, options, CompressionType.GZip);
}
throw new InvalidFormatException("Not a tar file.");
}
sharpCompressStream.Position = pos;
if (BZip2Stream.IsBZip2(sharpCompressStream))
{
sharpCompressStream.Position = pos;
var testStream = BZip2Stream.Create(
sharpCompressStream,
CompressionMode.Decompress,
false
);
if (TarArchive.IsTarFile(testStream))
{
sharpCompressStream.Position = pos;
return new TarReader(sharpCompressStream, options, CompressionType.BZip2);
}
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))
{
sharpCompressStream.Position = pos;
return new TarReader(sharpCompressStream, options, CompressionType.ZStandard);
}
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);
}
#endregion OpenReader
protected override IEnumerable<TarEntry> GetEntries(Stream stream) =>
TarEntry.GetEntries(
StreamingMode.Streaming,
@@ -112,4 +127,6 @@ public partial class TarReader : AbstractReader<TarEntry, TarVolume>
Options.ArchiveEncoding,
Options
);
// GetEntriesAsync moved to TarReader.Async.cs
}

View File

@@ -7,7 +7,6 @@ using SharpCompress.Common;
using SharpCompress.Common.Options;
using SharpCompress.Common.Zip;
using SharpCompress.Common.Zip.Headers;
using SharpCompress.Compressors;
namespace SharpCompress.Readers.Zip;
@@ -78,11 +77,7 @@ public partial class ZipReader
{
case ZipHeaderType.LocalEntry:
_current = new ZipEntry(
new StreamingZipFilePart(
(LocalEntryHeader)header,
_stream,
_options.Providers
),
new StreamingZipFilePart((LocalEntryHeader)header, _stream),
_options
);
return true;

View File

@@ -1,42 +1,28 @@
#if NET8_0_OR_GREATER
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
namespace SharpCompress.Readers.Zip;
public partial class ZipReader : IReaderOpenable
{
public static ValueTask<IAsyncReader> OpenAsyncReader(
string path,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
public static IAsyncReader OpenAsyncReader(string path, ReaderOptions? readerOptions = null)
{
cancellationToken.ThrowIfCancellationRequested();
path.NotNullOrEmpty(nameof(path));
return new((IAsyncReader)OpenReader(new FileInfo(path), readerOptions));
return (IAsyncReader)OpenReader(new FileInfo(path), readerOptions);
}
public static ValueTask<IAsyncReader> OpenAsyncReader(
Stream stream,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
public static IAsyncReader OpenAsyncReader(Stream stream, ReaderOptions? readerOptions = null)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IAsyncReader)OpenReader(stream, readerOptions));
return (IAsyncReader)OpenReader(stream, readerOptions);
}
public static ValueTask<IAsyncReader> OpenAsyncReader(
public static IAsyncReader OpenAsyncReader(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
ReaderOptions? readerOptions = null
)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IAsyncReader)OpenReader(fileInfo, readerOptions));
return (IAsyncReader)OpenReader(fileInfo, readerOptions);
}
public static IReader OpenReader(string filePath, ReaderOptions? readerOptions = null)

View File

@@ -74,11 +74,7 @@ public partial class ZipReader : AbstractReader<ZipEntry, ZipVolume>
case ZipHeaderType.LocalEntry:
{
yield return new ZipEntry(
new StreamingZipFilePart(
(LocalEntryHeader)h,
stream,
Options.Providers
),
new StreamingZipFilePart((LocalEntryHeader)h, stream),
Options
);
}

View File

@@ -18,24 +18,14 @@ public sealed partial class GZipWriter : AbstractWriter
{
destination = SharpCompressStream.CreateNonDisposing(destination);
}
// Use the configured compression providers
var providers = WriterOptions.Providers;
// Create the GZip stream using the provider
var compressionStream = providers.CreateCompressStream(
CompressionType.GZip,
destination,
WriterOptions.CompressionLevel
InitializeStream(
new GZipStream(
destination,
CompressionMode.Compress,
(CompressionLevel)(options?.CompressionLevel ?? (int)CompressionLevel.Default),
WriterOptions.ArchiveEncoding.GetEncoding()
)
);
// If using internal GZipStream, set the encoding for header filename
if (compressionStream is GZipStream gzipStream)
{
// Note: FileName and LastModified will be set in Write()
}
InitializeStream(compressionStream);
}
protected override void Dispose(bool isDisposing)
@@ -54,16 +44,11 @@ public sealed partial class GZipWriter : AbstractWriter
{
throw new ArgumentException("Can only write a single stream to a GZip file.");
}
// Set metadata on the stream if it's the internal GZipStream
if (OutputStream is GZipStream gzipStream)
{
gzipStream.FileName = filename;
gzipStream.LastModified = modificationTime;
}
var stream = (GZipStream)OutputStream;
stream.FileName = filename;
stream.LastModified = modificationTime;
var progressStream = WrapWithProgress(source, filename);
progressStream.CopyTo(OutputStream, Constants.BufferSize);
progressStream.CopyTo(stream, Constants.BufferSize);
_wroteToStream = true;
}

View File

@@ -1,8 +1,6 @@
using System;
using SharpCompress.Common;
using SharpCompress.Common.Options;
using SharpCompress.Compressors;
using SharpCompress.Providers;
using SharpCompress.Writers;
using D = SharpCompress.Compressors.Deflate;
@@ -68,14 +66,6 @@ public sealed record GZipWriterOptions : IWriterOptions
/// </summary>
public IProgress<ProgressReport>? Progress { get; init; }
/// <summary>
/// Registry of compression providers.
/// Defaults to <see cref="CompressionProviderRegistry.Default" /> but can be replaced with custom implementations, such as
/// System.IO.Compression for GZip on modern .NET.
/// </summary>
public CompressionProviderRegistry Providers { get; init; } =
CompressionProviderRegistry.Default;
/// <summary>
/// Creates a new GZipWriterOptions instance with default values.
/// </summary>
@@ -115,7 +105,6 @@ public sealed record GZipWriterOptions : IWriterOptions
LeaveStreamOpen = options.LeaveStreamOpen;
ArchiveEncoding = options.ArchiveEncoding;
Progress = options.Progress;
Providers = options.Providers;
}
/// <summary>
@@ -128,6 +117,5 @@ public sealed record GZipWriterOptions : IWriterOptions
LeaveStreamOpen = options.LeaveStreamOpen;
ArchiveEncoding = options.ArchiveEncoding;
Progress = options.Progress;
Providers = options.Providers;
}
}

Some files were not shown because too many files have changed in this diff Show More