Compare commits

..

15 Commits

Author SHA1 Message Date
Adam Hathcock
c81e78b5bb fix async benchmarks 2026-02-12 11:07:11 +00:00
Adam Hathcock
fb707aa676 push to nuget only on tags 2026-02-12 10:56:26 +00:00
Adam Hathcock
147dbc878a Merge pull request #1210 from adamhathcock/adam/issue-1206
OpenAsyncReader, OpenAsyncArchive and others must be async for Tar detection
2026-02-12 10:43:10 +00:00
Adam Hathcock
06bd6d9bed Merge pull request #1202 from adamhathcock/adam/async-benchmarks
update benchmarks to include async paths
2026-02-12 10:37:21 +00:00
Adam Hathcock
7f6272807d update docs 2026-02-12 10:32:20 +00:00
Adam Hathcock
89d948b4e1 use configure await false 2026-02-12 10:29:15 +00:00
Adam Hathcock
51c42b89b4 OpenAsyncArchive has to be async 2026-02-12 10:26:18 +00:00
Adam Hathcock
5a319ffe2c create/open always has to be async for detection 2026-02-12 10:18:43 +00:00
Adam Hathcock
bae660381c TarArchive should use a compression method like TarReader 2026-02-12 09:48:06 +00:00
Adam Hathcock
b2f1d007c6 Clean up some code paths 2026-02-12 08:50:18 +00:00
Adam Hathcock
98d0f1913e make sure things compile adam 2026-02-11 14:19:21 +00:00
Adam Hathcock
8aa93f4e34 fix fmt 2026-02-11 14:02:27 +00:00
Adam Hathcock
3689b893db Update tests/SharpCompress.Performance/Benchmarks/SevenZipBenchmarks.cs
Co-authored-by: kiloconnect[bot] <240665456+kiloconnect[bot]@users.noreply.github.com>
2026-02-11 14:00:37 +00:00
Adam Hathcock
fbec7dc083 generate github actions baseline 2026-02-11 13:57:06 +00:00
Adam Hathcock
7cf7623438 update benchmarks to include async paths 2026-02-11 13:36:26 +00:00
58 changed files with 1223 additions and 483 deletions

View File

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

View File

@@ -103,8 +103,11 @@ 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
@@ -132,6 +135,9 @@ 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
@@ -199,7 +205,8 @@ 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. **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
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

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="8.0.0" />
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="10.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

@@ -95,7 +95,7 @@ using (var archive = ZipArchive.OpenArchive("file.zip"))
}
// Async extraction (requires IAsyncArchive)
using (var asyncArchive = await ZipArchive.OpenAsyncArchive("file.zip"))
await 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"))
using (var reader = await ReaderFactory.OpenAsyncReader(stream))
await using (var reader = await ReaderFactory.OpenAsyncReader(stream))
{
while (await reader.MoveToNextEntryAsync())
{
@@ -409,7 +409,7 @@ cts.CancelAfter(TimeSpan.FromMinutes(5));
try
{
using (var archive = await ZipArchive.OpenAsyncArchive("archive.zip"))
await using (var archive = await ZipArchive.OpenAsyncArchive("archive.zip"))
{
await archive.WriteToDirectoryAsync(
@"C:\output",

View File

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

View File

@@ -2,12 +2,9 @@ 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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -7,6 +7,7 @@ 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;
@@ -37,12 +38,9 @@ public partial class TarArchive
)
{
fileInfo.NotNull(nameof(fileInfo));
return new TarArchive(
new SourceStream(
fileInfo,
i => ArchiveVolumeFactory.GetFilePart(i, fileInfo),
readerOptions ?? new ReaderOptions() { LeaveStreamOpen = false }
)
return OpenArchive(
[fileInfo],
readerOptions ?? new ReaderOptions() { LeaveStreamOpen = false }
);
}
@@ -53,13 +51,14 @@ public partial class TarArchive
{
fileInfos.NotNull(nameof(fileInfos));
var files = fileInfos.ToArray();
return new TarArchive(
new SourceStream(
files[0],
i => i < files.Length ? files[i] : null,
readerOptions ?? new ReaderOptions() { LeaveStreamOpen = false }
)
var sourceStream = new SourceStream(
files[0],
i => i < files.Length ? files[i] : null,
readerOptions ?? new ReaderOptions() { LeaveStreamOpen = false }
);
var compressionType = TarFactory.GetCompressionType(sourceStream);
sourceStream.Seek(0, SeekOrigin.Begin);
return new TarArchive(sourceStream, compressionType);
}
public static IWritableArchive<TarWriterOptions> OpenArchive(
@@ -69,13 +68,14 @@ public partial class TarArchive
{
streams.NotNull(nameof(streams));
var strms = streams.ToArray();
return new TarArchive(
new SourceStream(
strms[0],
i => i < strms.Length ? strms[i] : null,
readerOptions ?? new ReaderOptions()
)
var sourceStream = new SourceStream(
strms[0],
i => i < strms.Length ? strms[i] : null,
readerOptions ?? new ReaderOptions()
);
var compressionType = TarFactory.GetCompressionType(sourceStream);
sourceStream.Seek(0, SeekOrigin.Begin);
return new TarArchive(sourceStream, compressionType);
}
public static IWritableArchive<TarWriterOptions> OpenArchive(
@@ -90,35 +90,97 @@ public partial class TarArchive
throw new ArgumentException("Stream must be seekable", nameof(stream));
}
return new TarArchive(
new SourceStream(stream, i => null, readerOptions ?? new ReaderOptions())
);
return OpenArchive([stream], readerOptions);
}
public static IWritableAsyncArchive<TarWriterOptions> OpenAsyncArchive(
public static async ValueTask<IWritableAsyncArchive<TarWriterOptions>> OpenAsyncArchive(
Stream stream,
ReaderOptions? readerOptions = null
) => (IWritableAsyncArchive<TarWriterOptions>)OpenArchive(stream, readerOptions);
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, cancellationToken)
.ConfigureAwait(false);
sourceStream.Seek(0, SeekOrigin.Begin);
return new TarArchive(sourceStream, compressionType);
}
public static IWritableAsyncArchive<TarWriterOptions> OpenAsyncArchive(
public static ValueTask<IWritableAsyncArchive<TarWriterOptions>> OpenAsyncArchive(
string path,
ReaderOptions? readerOptions = null
) => (IWritableAsyncArchive<TarWriterOptions>)OpenArchive(new FileInfo(path), readerOptions);
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
path.NotNullOrEmpty(nameof(path));
return OpenAsyncArchive(new FileInfo(path), readerOptions, cancellationToken);
}
public static IWritableAsyncArchive<TarWriterOptions> OpenAsyncArchive(
public static async ValueTask<IWritableAsyncArchive<TarWriterOptions>> OpenAsyncArchive(
FileInfo fileInfo,
ReaderOptions? readerOptions = null
) => (IWritableAsyncArchive<TarWriterOptions>)OpenArchive(fileInfo, readerOptions);
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, cancellationToken)
.ConfigureAwait(false);
sourceStream.Seek(0, SeekOrigin.Begin);
return new TarArchive(sourceStream, compressionType);
}
public static IWritableAsyncArchive<TarWriterOptions> OpenAsyncArchive(
public static async ValueTask<IWritableAsyncArchive<TarWriterOptions>> OpenAsyncArchive(
IReadOnlyList<Stream> streams,
ReaderOptions? readerOptions = null
) => (IWritableAsyncArchive<TarWriterOptions>)OpenArchive(streams, readerOptions);
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, cancellationToken)
.ConfigureAwait(false);
sourceStream.Seek(0, SeekOrigin.Begin);
return new TarArchive(sourceStream, compressionType);
}
public static IWritableAsyncArchive<TarWriterOptions> OpenAsyncArchive(
public static async ValueTask<IWritableAsyncArchive<TarWriterOptions>> OpenAsyncArchive(
IReadOnlyList<FileInfo> fileInfos,
ReaderOptions? readerOptions = null
) => (IWritableAsyncArchive<TarWriterOptions>)OpenArchive(fileInfos, readerOptions);
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, cancellationToken)
.ConfigureAwait(false);
sourceStream.Seek(0, SeekOrigin.Begin);
return new TarArchive(sourceStream, compressionType);
}
public static bool IsTarFile(string filePath) => IsTarFile(new FileInfo(filePath));
@@ -183,5 +245,6 @@ public partial class TarArchive
public static IWritableArchive<TarWriterOptions> CreateArchive() => new TarArchive();
public static IWritableAsyncArchive<TarWriterOptions> CreateAsyncArchive() => new TarArchive();
public static ValueTask<IWritableAsyncArchive<TarWriterOptions>> CreateAsyncArchive() =>
new(new TarArchive());
}

View File

@@ -2,38 +2,60 @@ 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.Common.Tar;
using SharpCompress.Common.Tar.Headers;
using SharpCompress.Compressors.BZip2;
using SharpCompress.Compressors.Deflate;
using SharpCompress.Compressors.LZMA;
using SharpCompress.Compressors.Lzw;
using SharpCompress.Compressors.Xz;
using SharpCompress.Compressors.ZStandard;
using SharpCompress.IO;
using SharpCompress.Readers;
using SharpCompress.Readers.Tar;
using SharpCompress.Writers;
using SharpCompress.Writers.Tar;
using CompressionMode = SharpCompress.Compressors.CompressionMode;
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();
}
private TarArchive(SourceStream sourceStream)
: base(ArchiveType.Tar, sourceStream) { }
internal TarArchive(SourceStream sourceStream, CompressionType compressionType)
: base(ArchiveType.Tar, sourceStream)
{
_compressionType = compressionType;
}
private TarArchive()
: base(ArchiveType.Tar) { }
private Stream GetStream(Stream stream) =>
_compressionType switch
{
CompressionType.BZip2 => BZip2Stream.Create(stream, CompressionMode.Decompress, false),
CompressionType.GZip => new GZipStream(stream, CompressionMode.Decompress),
CompressionType.ZStandard => new ZStandardStream(stream),
CompressionType.LZip => new LZipStream(stream, CompressionMode.Decompress),
CompressionType.Xz => new XZStream(stream),
CompressionType.Lzw => new LzwStream(stream),
CompressionType.None => stream,
_ => throw new NotSupportedException("Invalid compression type: " + _compressionType),
};
protected override IEnumerable<TarArchiveEntry> LoadEntries(IEnumerable<TarVolume> volumes)
{
var stream = volumes.Single().Stream;
var stream = GetStream(volumes.Single().Stream);
if (stream.CanSeek)
{
stream.Position = 0;
@@ -41,7 +63,9 @@ public partial class TarArchive
TarHeader? previousHeader = null;
foreach (
var header in TarHeaderFactory.ReadHeader(
StreamingMode.Seekable,
_compressionType == CompressionType.None
? StreamingMode.Seekable
: StreamingMode.Streaming,
stream,
ReaderOptions.ArchiveEncoding
)
@@ -154,6 +178,6 @@ public partial class TarArchive
{
var stream = Volumes.Single().Stream;
stream.Position = 0;
return TarReader.OpenReader(stream);
return TarReader.OpenReader(GetStream(stream));
}
}

View File

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

View File

@@ -63,12 +63,26 @@ public class GZipFactory
GZipArchive.OpenArchive(stream, readerOptions);
/// <inheritdoc/>
public IAsyncArchive OpenAsyncArchive(Stream stream, ReaderOptions? readerOptions = null) =>
(IAsyncArchive)OpenArchive(stream, readerOptions);
public ValueTask<IAsyncArchive> OpenAsyncArchive(
Stream stream,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IAsyncArchive)OpenArchive(stream, readerOptions));
}
/// <inheritdoc/>
public IAsyncArchive OpenAsyncArchive(FileInfo fileInfo, ReaderOptions? readerOptions = null) =>
(IAsyncArchive)OpenArchive(fileInfo, readerOptions);
public ValueTask<IAsyncArchive> OpenAsyncArchive(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IAsyncArchive)OpenArchive(fileInfo, readerOptions));
}
#endregion

View File

@@ -87,7 +87,7 @@ public class LzwFactory : Factory, IReaderFactory
)
{
cancellationToken.ThrowIfCancellationRequested();
return new(LzwReader.OpenAsyncReader(stream, options));
return LzwReader.OpenAsyncReader(stream, options);
}
#endregion

View File

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

View File

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

View File

@@ -109,6 +109,51 @@ public class TarFactory
#endregion
public static CompressionType GetCompressionType(Stream stream)
{
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 = wrapper.CreateStream(stream);
if (TarArchive.IsTarFile(decompressedStream))
{
return wrapper.CompressionType;
}
}
}
throw new InvalidFormatException("Not a tar file.");
}
public static async ValueTask<CompressionType> GetCompressionTypeAsync(
Stream stream,
CancellationToken cancellationToken = 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 = wrapper.CreateStream(stream);
if (
await TarArchive
.IsTarFileAsync(decompressedStream, cancellationToken)
.ConfigureAwait(false)
)
{
return wrapper.CompressionType;
}
}
}
throw new InvalidFormatException("Not a tar file.");
}
#region IArchiveFactory
/// <inheritdoc/>
@@ -116,16 +161,28 @@ public class TarFactory
TarArchive.OpenArchive(stream, readerOptions);
/// <inheritdoc/>
public IAsyncArchive OpenAsyncArchive(Stream stream, ReaderOptions? readerOptions = null) =>
(IAsyncArchive)OpenArchive(stream, readerOptions);
public async ValueTask<IAsyncArchive> OpenAsyncArchive(
Stream stream,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
) =>
await TarArchive
.OpenAsyncArchive(stream, readerOptions, cancellationToken)
.ConfigureAwait(false);
/// <inheritdoc/>
public IArchive OpenArchive(FileInfo fileInfo, ReaderOptions? readerOptions = null) =>
TarArchive.OpenArchive(fileInfo, readerOptions);
/// <inheritdoc/>
public IAsyncArchive OpenAsyncArchive(FileInfo fileInfo, ReaderOptions? readerOptions = null) =>
(IAsyncArchive)OpenArchive(fileInfo, readerOptions);
public async ValueTask<IAsyncArchive> OpenAsyncArchive(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
) =>
await TarArchive
.OpenAsyncArchive(fileInfo, readerOptions, cancellationToken)
.ConfigureAwait(false);
#endregion

View File

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

View File

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

View File

@@ -1,28 +1,42 @@
#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 IAsyncReader OpenAsyncReader(string path, ReaderOptions? readerOptions = null)
{
path.NotNullOrEmpty(nameof(path));
return (IAsyncReader)OpenReader(new FileInfo(path), readerOptions);
}
public static IAsyncReader OpenAsyncReader(Stream stream, ReaderOptions? readerOptions = null)
{
return (IAsyncReader)OpenReader(stream, readerOptions);
}
public static IAsyncReader OpenAsyncReader(
FileInfo fileInfo,
ReaderOptions? readerOptions = null
public static ValueTask<IAsyncReader> OpenAsyncReader(
string path,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
return (IAsyncReader)OpenReader(fileInfo, readerOptions);
cancellationToken.ThrowIfCancellationRequested();
path.NotNullOrEmpty(nameof(path));
return new((IAsyncReader)OpenReader(new FileInfo(path), readerOptions));
}
public static ValueTask<IAsyncReader> OpenAsyncReader(
Stream stream,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IAsyncReader)OpenReader(stream, readerOptions));
}
public static ValueTask<IAsyncReader> OpenAsyncReader(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IAsyncReader)OpenReader(fileInfo, readerOptions));
}
public static IReader OpenReader(string filePath, ReaderOptions? readerOptions = null)

View File

@@ -1,28 +1,42 @@
#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 IAsyncReader OpenAsyncReader(string path, ReaderOptions? readerOptions = null)
{
path.NotNullOrEmpty(nameof(path));
return (IAsyncReader)OpenReader(new FileInfo(path), readerOptions);
}
public static IAsyncReader OpenAsyncReader(Stream stream, ReaderOptions? readerOptions = null)
{
return (IAsyncReader)OpenReader(stream, readerOptions);
}
public static IAsyncReader OpenAsyncReader(
FileInfo fileInfo,
ReaderOptions? readerOptions = null
public static ValueTask<IAsyncReader> OpenAsyncReader(
string path,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
return (IAsyncReader)OpenReader(fileInfo, readerOptions);
cancellationToken.ThrowIfCancellationRequested();
path.NotNullOrEmpty(nameof(path));
return new((IAsyncReader)OpenReader(new FileInfo(path), readerOptions));
}
public static ValueTask<IAsyncReader> OpenAsyncReader(
Stream stream,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IAsyncReader)OpenReader(stream, readerOptions));
}
public static ValueTask<IAsyncReader> OpenAsyncReader(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IAsyncReader)OpenReader(fileInfo, readerOptions));
}
public static IReader OpenReader(string filePath, ReaderOptions? readerOptions = null)

View File

@@ -1,4 +1,6 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace SharpCompress.Readers.GZip;
@@ -7,23 +9,35 @@ public partial class GZipReader
: IReaderOpenable
#endif
{
public static IAsyncReader OpenAsyncReader(string path, ReaderOptions? readerOptions = null)
{
path.NotNullOrEmpty(nameof(path));
return (IAsyncReader)OpenReader(new FileInfo(path), readerOptions);
}
public static IAsyncReader OpenAsyncReader(Stream stream, ReaderOptions? readerOptions = null)
{
return (IAsyncReader)OpenReader(stream, readerOptions);
}
public static IAsyncReader OpenAsyncReader(
FileInfo fileInfo,
ReaderOptions? readerOptions = null
public static ValueTask<IAsyncReader> OpenAsyncReader(
string path,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
return (IAsyncReader)OpenReader(fileInfo, readerOptions);
cancellationToken.ThrowIfCancellationRequested();
path.NotNullOrEmpty(nameof(path));
return new((IAsyncReader)OpenReader(new FileInfo(path), readerOptions));
}
public static ValueTask<IAsyncReader> OpenAsyncReader(
Stream stream,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IAsyncReader)OpenReader(stream, readerOptions));
}
public static ValueTask<IAsyncReader> OpenAsyncReader(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return new((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"></param>
/// <param name="options"></param>
/// <returns></returns>
/// <param name="stream">An open, readable stream.</param>
/// <param name="options">Reader options.</param>
/// <returns>The opened reader.</returns>
IReader OpenReader(Stream stream, ReaderOptions? options);
/// <summary>
/// Opens a Reader for Non-seeking usage asynchronously.
/// </summary>
/// <param name="stream"></param>
/// <param name="options"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
/// <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>
ValueTask<IAsyncReader> OpenAsyncReader(
Stream stream,
ReaderOptions? options,

View File

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

View File

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

View File

@@ -1,28 +1,42 @@
#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 IAsyncReader OpenAsyncReader(string path, ReaderOptions? readerOptions = null)
{
path.NotNullOrEmpty(nameof(path));
return (IAsyncReader)OpenReader(new FileInfo(path), readerOptions);
}
public static IAsyncReader OpenAsyncReader(Stream stream, ReaderOptions? readerOptions = null)
{
return (IAsyncReader)OpenReader(stream, readerOptions);
}
public static IAsyncReader OpenAsyncReader(
FileInfo fileInfo,
ReaderOptions? readerOptions = null
public static ValueTask<IAsyncReader> OpenAsyncReader(
string path,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
return (IAsyncReader)OpenReader(fileInfo, readerOptions);
cancellationToken.ThrowIfCancellationRequested();
path.NotNullOrEmpty(nameof(path));
return new((IAsyncReader)OpenReader(new FileInfo(path), readerOptions));
}
public static ValueTask<IAsyncReader> OpenAsyncReader(
Stream stream,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IAsyncReader)OpenReader(stream, readerOptions));
}
public static ValueTask<IAsyncReader> OpenAsyncReader(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IAsyncReader)OpenReader(fileInfo, readerOptions));
}
}
#endif

View File

@@ -1,8 +1,6 @@
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,16 +1,6 @@
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,5 +1,15 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Archives.GZip;
using SharpCompress.Archives.Tar;
using SharpCompress.Common;
using SharpCompress.Compressors;
using SharpCompress.Compressors.BZip2;
using SharpCompress.Compressors.Deflate;
using SharpCompress.Compressors.LZMA;
using SharpCompress.Compressors.ZStandard;
using SharpCompress.IO;
namespace SharpCompress.Readers.Tar;
@@ -8,23 +18,117 @@ public partial class TarReader
: IReaderOpenable
#endif
{
public static IAsyncReader OpenAsyncReader(string path, ReaderOptions? readerOptions = null)
{
path.NotNullOrEmpty(nameof(path));
return (IAsyncReader)OpenReader(new FileInfo(path), readerOptions);
}
public static IAsyncReader OpenAsyncReader(Stream stream, ReaderOptions? readerOptions = null)
{
return (IAsyncReader)OpenReader(stream, readerOptions);
}
public static IAsyncReader OpenAsyncReader(
FileInfo fileInfo,
ReaderOptions? readerOptions = null
public static ValueTask<IAsyncReader> OpenAsyncReader(
string path,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
return (IAsyncReader)OpenReader(fileInfo, readerOptions);
path.NotNullOrEmpty(nameof(path));
return OpenAsyncReader(new FileInfo(path), readerOptions, cancellationToken);
}
public static async ValueTask<IAsyncReader> OpenAsyncReader(
Stream stream,
ReaderOptions? options = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
stream.NotNull(nameof(stream));
options ??= new ReaderOptions();
var sharpCompressStream = SharpCompressStream.Create(
stream,
bufferSize: options.RewindableBufferSize
);
long pos = sharpCompressStream.Position;
if (
await GZipArchive
.IsGZipFileAsync(sharpCompressStream, cancellationToken)
.ConfigureAwait(false)
)
{
sharpCompressStream.Position = pos;
var testStream = new GZipStream(sharpCompressStream, CompressionMode.Decompress);
if (
await TarArchive.IsTarFileAsync(testStream, cancellationToken).ConfigureAwait(false)
)
{
sharpCompressStream.Position = pos;
return new TarReader(sharpCompressStream, options, CompressionType.GZip);
}
throw new InvalidFormatException("Not a tar file.");
}
sharpCompressStream.Position = pos;
if (
await BZip2Stream
.IsBZip2Async(sharpCompressStream, cancellationToken)
.ConfigureAwait(false)
)
{
sharpCompressStream.Position = pos;
var testStream = BZip2Stream.Create(
sharpCompressStream,
CompressionMode.Decompress,
false
);
if (
await TarArchive.IsTarFileAsync(testStream, cancellationToken).ConfigureAwait(false)
)
{
sharpCompressStream.Position = pos;
return new TarReader(sharpCompressStream, options, CompressionType.BZip2);
}
throw new InvalidFormatException("Not a tar file.");
}
sharpCompressStream.Position = pos;
if (
await ZStandardStream
.IsZStandardAsync(sharpCompressStream, cancellationToken)
.ConfigureAwait(false)
)
{
sharpCompressStream.Position = pos;
var testStream = new ZStandardStream(sharpCompressStream);
if (
await TarArchive.IsTarFileAsync(testStream, cancellationToken).ConfigureAwait(false)
)
{
sharpCompressStream.Position = pos;
return new TarReader(sharpCompressStream, options, CompressionType.ZStandard);
}
throw new InvalidFormatException("Not a tar file.");
}
sharpCompressStream.Position = pos;
if (
await LZipStream
.IsLZipFileAsync(sharpCompressStream, cancellationToken)
.ConfigureAwait(false)
)
{
sharpCompressStream.Position = pos;
var testStream = new LZipStream(sharpCompressStream, CompressionMode.Decompress);
if (
await TarArchive.IsTarFileAsync(testStream, cancellationToken).ConfigureAwait(false)
)
{
sharpCompressStream.Position = pos;
return new TarReader(sharpCompressStream, options, CompressionType.LZip);
}
throw new InvalidFormatException("Not a tar file.");
}
sharpCompressStream.Position = pos;
return new TarReader(sharpCompressStream, options, CompressionType.None);
}
public static ValueTask<IAsyncReader> OpenAsyncReader(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
readerOptions ??= new ReaderOptions() { LeaveStreamOpen = false };
return OpenAsyncReader(fileInfo.OpenRead(), readerOptions, cancellationToken);
}
public static IReader OpenReader(string filePath, ReaderOptions? readerOptions = null)
@@ -36,6 +140,77 @@ 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;
if (GZipArchive.IsGZipFile(sharpCompressStream))
{
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);
}
}

View File

@@ -1,8 +1,6 @@
using System;
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;
@@ -45,80 +43,6 @@ public partial class TarReader : AbstractReader<TarEntry, TarVolume>
};
}
#region OpenReader
/// <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))
{
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,
@@ -127,6 +51,4 @@ public partial class TarReader : AbstractReader<TarEntry, TarVolume>
Options.ArchiveEncoding,
Options
);
// GetEntriesAsync moved to TarReader.Async.cs
}

View File

@@ -1,28 +1,42 @@
#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 IAsyncReader OpenAsyncReader(string path, ReaderOptions? readerOptions = null)
{
path.NotNullOrEmpty(nameof(path));
return (IAsyncReader)OpenReader(new FileInfo(path), readerOptions);
}
public static IAsyncReader OpenAsyncReader(Stream stream, ReaderOptions? readerOptions = null)
{
return (IAsyncReader)OpenReader(stream, readerOptions);
}
public static IAsyncReader OpenAsyncReader(
FileInfo fileInfo,
ReaderOptions? readerOptions = null
public static ValueTask<IAsyncReader> OpenAsyncReader(
string path,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
return (IAsyncReader)OpenReader(fileInfo, readerOptions);
cancellationToken.ThrowIfCancellationRequested();
path.NotNullOrEmpty(nameof(path));
return new((IAsyncReader)OpenReader(new FileInfo(path), readerOptions));
}
public static ValueTask<IAsyncReader> OpenAsyncReader(
Stream stream,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IAsyncReader)OpenReader(stream, readerOptions));
}
public static ValueTask<IAsyncReader> OpenAsyncReader(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IAsyncReader)OpenReader(fileInfo, readerOptions));
}
public static IReader OpenReader(string filePath, ReaderOptions? readerOptions = null)

View File

@@ -4,11 +4,11 @@
".NETFramework,Version=v4.8": {
"Microsoft.Bcl.AsyncInterfaces": {
"type": "Direct",
"requested": "[8.0.0, )",
"resolved": "8.0.0",
"contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==",
"requested": "[10.0.0, )",
"resolved": "10.0.0",
"contentHash": "vFuwSLj9QJBbNR0NeNO4YVASUbokxs+i/xbuu8B+Fs4FAZg5QaFa6eGrMaRqTzzNI5tAb97T7BhSxtLckFyiRA==",
"dependencies": {
"System.Threading.Tasks.Extensions": "4.5.4"
"System.Threading.Tasks.Extensions": "4.6.3"
}
},
"Microsoft.NETFramework.ReferenceAssemblies": {
@@ -91,10 +91,10 @@
},
"System.Threading.Tasks.Extensions": {
"type": "Transitive",
"resolved": "4.5.4",
"contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
"resolved": "4.6.3",
"contentHash": "7sCiwilJLYbTZELaKnc7RecBBXWXA+xMLQWZKWawBxYjp6DBlSE3v9/UcvKBvr1vv2tTOhipiogM8rRmxlhrVA==",
"dependencies": {
"System.Runtime.CompilerServices.Unsafe": "4.5.3"
"System.Runtime.CompilerServices.Unsafe": "6.1.2"
}
},
"System.ValueTuple": {
@@ -106,11 +106,11 @@
".NETStandard,Version=v2.0": {
"Microsoft.Bcl.AsyncInterfaces": {
"type": "Direct",
"requested": "[8.0.0, )",
"resolved": "8.0.0",
"contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==",
"requested": "[10.0.0, )",
"resolved": "10.0.0",
"contentHash": "vFuwSLj9QJBbNR0NeNO4YVASUbokxs+i/xbuu8B+Fs4FAZg5QaFa6eGrMaRqTzzNI5tAb97T7BhSxtLckFyiRA==",
"dependencies": {
"System.Threading.Tasks.Extensions": "4.5.4"
"System.Threading.Tasks.Extensions": "4.6.3"
}
},
"Microsoft.NETFramework.ReferenceAssemblies": {
@@ -206,10 +206,10 @@
},
"System.Threading.Tasks.Extensions": {
"type": "Transitive",
"resolved": "4.5.4",
"contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
"resolved": "4.6.3",
"contentHash": "7sCiwilJLYbTZELaKnc7RecBBXWXA+xMLQWZKWawBxYjp6DBlSE3v9/UcvKBvr1vv2tTOhipiogM8rRmxlhrVA==",
"dependencies": {
"System.Runtime.CompilerServices.Unsafe": "4.5.3"
"System.Runtime.CompilerServices.Unsafe": "6.1.2"
}
}
},

View File

@@ -1,5 +1,6 @@
using System;
using System.IO;
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;
using SharpCompress.Compressors;
using SharpCompress.Compressors.Deflate;
@@ -36,6 +37,14 @@ public class GZipBenchmarks
gzipStream.Write(_sourceData, 0, _sourceData.Length);
}
[Benchmark(Description = "GZip: Compress 100KB (Async)")]
public async Task GZipCompressAsync()
{
using var outputStream = new MemoryStream();
using var gzipStream = new GZipStream(outputStream, CompressionMode.Compress);
await gzipStream.WriteAsync(_sourceData, 0, _sourceData.Length).ConfigureAwait(false);
}
[Benchmark(Description = "GZip: Decompress 100KB")]
public void GZipDecompress()
{
@@ -43,4 +52,12 @@ public class GZipBenchmarks
using var gzipStream = new GZipStream(inputStream, CompressionMode.Decompress);
gzipStream.CopyTo(Stream.Null);
}
[Benchmark(Description = "GZip: Decompress 100KB (Async)")]
public async Task GZipDecompressAsync()
{
using var inputStream = new MemoryStream(_compressedData);
using var gzipStream = new GZipStream(inputStream, CompressionMode.Decompress);
await gzipStream.CopyToAsync(Stream.Null).ConfigureAwait(false);
}
}

View File

@@ -1,6 +1,7 @@
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;
using SharpCompress.Archives.Rar;
using SharpCompress.Readers;
@@ -30,6 +31,18 @@ public class RarBenchmarks : ArchiveBenchmarkBase
}
}
[Benchmark(Description = "Rar: Extract all entries (Archive API, Async)")]
public async Task RarExtractArchiveApiAsync()
{
using var stream = new MemoryStream(_rarBytes);
await using var archive = await RarArchive.OpenAsyncArchive(stream).ConfigureAwait(false);
await foreach (var entry in archive.EntriesAsync.Where(e => !e.IsDirectory))
{
await using var entryStream = await entry.OpenEntryStreamAsync().ConfigureAwait(false);
await entryStream.CopyToAsync(Stream.Null).ConfigureAwait(false);
}
}
[Benchmark(Description = "Rar: Extract all entries (Reader API)")]
public void RarExtractReaderApi()
{
@@ -43,4 +56,18 @@ public class RarBenchmarks : ArchiveBenchmarkBase
}
}
}
[Benchmark(Description = "Rar: Extract all entries (Reader API, Async)")]
public async Task RarExtractReaderApiAsync()
{
using var stream = new MemoryStream(_rarBytes);
await using var reader = await ReaderFactory.OpenAsyncReader(stream).ConfigureAwait(false);
while (await reader.MoveToNextEntryAsync().ConfigureAwait(false))
{
if (!reader.Entry.IsDirectory)
{
await reader.WriteEntryToAsync(Stream.Null).ConfigureAwait(false);
}
}
}
}

View File

@@ -1,6 +1,7 @@
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;
using SharpCompress.Archives.SevenZip;
@@ -31,6 +32,20 @@ public class SevenZipBenchmarks : ArchiveBenchmarkBase
}
}
[Benchmark(Description = "7Zip LZMA: Extract all entries (Async)")]
public async Task SevenZipLzmaExtractAsync()
{
using var stream = new MemoryStream(_lzmaBytes);
await using var archive = await SevenZipArchive
.OpenAsyncArchive(stream)
.ConfigureAwait(false);
await foreach (var entry in archive.EntriesAsync.Where(e => !e.IsDirectory))
{
await using var entryStream = await entry.OpenEntryStreamAsync().ConfigureAwait(false);
await entryStream.CopyToAsync(Stream.Null).ConfigureAwait(false);
}
}
[Benchmark(Description = "7Zip LZMA2: Extract all entries")]
public void SevenZipLzma2Extract()
{
@@ -42,4 +57,46 @@ public class SevenZipBenchmarks : ArchiveBenchmarkBase
entryStream.CopyTo(Stream.Null);
}
}
[Benchmark(Description = "7Zip LZMA2: Extract all entries (Async)")]
public async Task SevenZipLzma2ExtractAsync()
{
using var stream = new MemoryStream(_lzma2Bytes);
await using var archive = await SevenZipArchive
.OpenAsyncArchive(stream)
.ConfigureAwait(false);
await foreach (var entry in archive.EntriesAsync.Where(e => !e.IsDirectory))
{
await using var entryStream = await entry.OpenEntryStreamAsync().ConfigureAwait(false);
await entryStream.CopyToAsync(Stream.Null).ConfigureAwait(false);
}
}
[Benchmark(Description = "7Zip LZMA2 Reader: Extract all entries")]
public void SevenZipLzma2Extract_Reader()
{
using var stream = new MemoryStream(_lzma2Bytes);
using var archive = SevenZipArchive.OpenArchive(stream);
using var reader = archive.ExtractAllEntries();
foreach (var entry in archive.Entries.Where(e => !e.IsDirectory))
{
using var entryStream = entry.OpenEntryStream();
entryStream.CopyTo(Stream.Null);
}
}
[Benchmark(Description = "7Zip LZMA2 Reader: Extract all entries (Async)")]
public async Task SevenZipLzma2ExtractAsync_Reader()
{
using var stream = new MemoryStream(_lzma2Bytes);
await using var archive = await SevenZipArchive
.OpenAsyncArchive(stream)
.ConfigureAwait(false);
await using var reader = await archive.ExtractAllEntriesAsync();
while (await reader.MoveToNextEntryAsync().ConfigureAwait(false))
{
await using var entryStream = await reader.OpenEntryStreamAsync().ConfigureAwait(false);
await entryStream.CopyToAsync(Stream.Null).ConfigureAwait(false);
}
}
}

View File

@@ -1,6 +1,7 @@
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;
using SharpCompress.Archives.Tar;
using SharpCompress.Common;
@@ -34,6 +35,18 @@ public class TarBenchmarks : ArchiveBenchmarkBase
}
}
[Benchmark(Description = "Tar: Extract all entries (Archive API, Async)")]
public async Task TarExtractArchiveApiAsync()
{
using var stream = new MemoryStream(_tarBytes);
await using var archive = await TarArchive.OpenAsyncArchive(stream).ConfigureAwait(false);
await foreach (var entry in archive.EntriesAsync.Where(e => !e.IsDirectory))
{
await using var entryStream = await entry.OpenEntryStreamAsync().ConfigureAwait(false);
await entryStream.CopyToAsync(Stream.Null).ConfigureAwait(false);
}
}
[Benchmark(Description = "Tar: Extract all entries (Reader API)")]
public void TarExtractReaderApi()
{
@@ -48,6 +61,20 @@ public class TarBenchmarks : ArchiveBenchmarkBase
}
}
[Benchmark(Description = "Tar: Extract all entries (Reader API, Async)")]
public async Task TarExtractReaderApiAsync()
{
using var stream = new MemoryStream(_tarBytes);
await using var reader = await ReaderFactory.OpenAsyncReader(stream).ConfigureAwait(false);
while (await reader.MoveToNextEntryAsync().ConfigureAwait(false))
{
if (!reader.Entry.IsDirectory)
{
await reader.WriteEntryToAsync(Stream.Null).ConfigureAwait(false);
}
}
}
[Benchmark(Description = "Tar.GZip: Extract all entries")]
public void TarGzipExtract()
{
@@ -60,6 +87,18 @@ public class TarBenchmarks : ArchiveBenchmarkBase
}
}
[Benchmark(Description = "Tar.GZip: Extract all entries (Async)")]
public async Task TarGzipExtractAsync()
{
using var stream = new MemoryStream(_tarGzBytes);
await using var archive = await TarArchive.OpenAsyncArchive(stream).ConfigureAwait(false);
await foreach (var entry in archive.EntriesAsync.Where(e => !e.IsDirectory))
{
await using var entryStream = await entry.OpenEntryStreamAsync().ConfigureAwait(false);
await entryStream.CopyToAsync(Stream.Null).ConfigureAwait(false);
}
}
[Benchmark(Description = "Tar: Create archive with small files")]
public void TarCreateSmallFiles()
{
@@ -78,4 +117,22 @@ public class TarBenchmarks : ArchiveBenchmarkBase
writer.Write($"file{i}.txt", entryStream);
}
}
[Benchmark(Description = "Tar: Create archive with small files (Async)")]
public async Task TarCreateSmallFilesAsync()
{
using var outputStream = new MemoryStream();
await using var writer = WriterFactory.OpenAsyncWriter(
outputStream,
ArchiveType.Tar,
new WriterOptions(CompressionType.None) { LeaveStreamOpen = true }
);
for (int i = 0; i < 10; i++)
{
var data = new byte[1024];
using var entryStream = new MemoryStream(data);
await writer.WriteAsync($"file{i}.txt", entryStream).ConfigureAwait(false);
}
}
}

View File

@@ -1,6 +1,7 @@
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;
using SharpCompress.Archives.Zip;
using SharpCompress.Common;
@@ -34,6 +35,18 @@ public class ZipBenchmarks : ArchiveBenchmarkBase
}
}
[Benchmark(Description = "Zip: Extract all entries (Archive API, Async)")]
public async Task ZipExtractArchiveApiAsync()
{
using var stream = new MemoryStream(_archiveBytes);
await using var archive = await ZipArchive.OpenAsyncArchive(stream).ConfigureAwait(false);
await foreach (var entry in archive.EntriesAsync.Where(e => !e.IsDirectory))
{
await using var entryStream = await entry.OpenEntryStreamAsync().ConfigureAwait(false);
await entryStream.CopyToAsync(Stream.Null).ConfigureAwait(false);
}
}
[Benchmark(Description = "Zip: Extract all entries (Reader API)")]
public void ZipExtractReaderApi()
{
@@ -48,6 +61,20 @@ public class ZipBenchmarks : ArchiveBenchmarkBase
}
}
[Benchmark(Description = "Zip: Extract all entries (Reader API, Async)")]
public async Task ZipExtractReaderApiAsync()
{
using var stream = new MemoryStream(_archiveBytes);
await using var reader = await ReaderFactory.OpenAsyncReader(stream).ConfigureAwait(false);
while (await reader.MoveToNextEntryAsync().ConfigureAwait(false))
{
if (!reader.Entry.IsDirectory)
{
await reader.WriteEntryToAsync(Stream.Null).ConfigureAwait(false);
}
}
}
[Benchmark(Description = "Zip: Create archive with small files")]
public void ZipCreateSmallFiles()
{
@@ -66,4 +93,22 @@ public class ZipBenchmarks : ArchiveBenchmarkBase
writer.Write($"file{i}.txt", entryStream);
}
}
[Benchmark(Description = "Zip: Create archive with small files (Async)")]
public async Task ZipCreateSmallFilesAsync()
{
using var outputStream = new MemoryStream();
await using var writer = WriterFactory.OpenAsyncWriter(
outputStream,
ArchiveType.Zip,
new WriterOptions(CompressionType.Deflate) { LeaveStreamOpen = true }
);
for (int i = 0; i < 10; i++)
{
var data = new byte[1024];
using var entryStream = new MemoryStream(data);
await writer.WriteAsync($"file{i}.txt", entryStream).ConfigureAwait(false);
}
}
}

View File

@@ -20,10 +20,10 @@ public class Program
// Default: Run BenchmarkDotNet
var config = DefaultConfig.Instance.AddJob(
Job.Default.WithToolchain(InProcessEmitToolchain.Instance)
.WithWarmupCount(3) // Minimal warmup iterations for CI
.WithIterationCount(10) // Minimal measurement iterations for CI
.WithInvocationCount(10)
.WithUnrollFactor(1)
.WithWarmupCount(5) // Minimal warmup iterations for CI
.WithIterationCount(30) // Minimal measurement iterations for CI
.WithInvocationCount(30)
.WithUnrollFactor(2)
);
BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args, config);

View File

@@ -5,11 +5,11 @@ This project contains performance benchmarks for SharpCompress using [BenchmarkD
## Overview
The benchmarks test all major archive formats supported by SharpCompress:
- **Zip**: Read (Archive & Reader API) and Write operations
- **Tar**: Read (Archive & Reader API) and Write operations, including Tar.GZip
- **Rar**: Read operations (Archive & Reader API)
- **7Zip**: Read operations for LZMA and LZMA2 compression
- **GZip**: Compression and decompression
- **Zip**: Read (Archive & Reader API) and Write operations, each with sync and async variants
- **Tar**: Read (Archive & Reader API) and Write operations, including Tar.GZip, each with sync and async variants
- **Rar**: Read operations (Archive & Reader API), each with sync and async variants
- **7Zip**: Read operations for LZMA and LZMA2 compression, each with sync and async variants
- **GZip**: Compression and decompression, each with sync and async variants
## Running Benchmarks

View File

@@ -1,23 +1,49 @@
| Method | Mean | Error | StdDev | Allocated |
|------------------------- |-----------:|---------:|---------:|----------:|
| &#39;GZip: Compress 100KB&#39; | 3,268.7 μs | 28.50 μs | 16.96 μs | 519.2 KB |
| &#39;GZip: Decompress 100KB&#39; | 436.6 μs | 3.23 μs | 1.69 μs | 34.18 KB |
| Method | Mean | Error | StdDev | Allocated |
|----------------------------------------- |---------:|----------:|----------:|----------:|
| &#39;Rar: Extract all entries (Archive API)&#39; | 2.054 ms | 0.3927 ms | 0.2598 ms | 91.09 KB |
| &#39;Rar: Extract all entries (Reader API)&#39; | 2.235 ms | 0.0253 ms | 0.0132 ms | 149.48 KB |
| Method | Mean | Error | StdDev | Allocated |
|---------------------------------- |---------:|----------:|----------:|----------:|
| &#39;7Zip LZMA: Extract all entries&#39; | 9.124 ms | 2.1930 ms | 1.4505 ms | 272.8 KB |
| &#39;7Zip LZMA2: Extract all entries&#39; | 7.810 ms | 0.1323 ms | 0.0788 ms | 272.58 KB |
| Method | Mean | Error | StdDev | Allocated |
|----------------------------------------- |----------:|---------:|---------:|----------:|
| &#39;Tar: Extract all entries (Archive API)&#39; | 56.36 μs | 3.312 μs | 1.971 μs | 16.65 KB |
| &#39;Tar: Extract all entries (Reader API)&#39; | 175.34 μs | 2.616 μs | 1.557 μs | 213.36 KB |
| &#39;Tar.GZip: Extract all entries&#39; | NA | NA | NA | NA |
| &#39;Tar: Create archive with small files&#39; | 51.38 μs | 2.349 μs | 1.398 μs | 68.7 KB |
| Method | Mean | Error | StdDev | Gen0 | Allocated |
|----------------------------------------- |-----------:|---------:|---------:|---------:|-----------:|
| &#39;Zip: Extract all entries (Archive API)&#39; | 1,188.4 μs | 28.62 μs | 14.97 μs | - | 181.66 KB |
| &#39;Zip: Extract all entries (Reader API)&#39; | 1,137.0 μs | 5.58 μs | 2.92 μs | - | 123.19 KB |
| &#39;Zip: Create archive with small files&#39; | 258.2 μs | 8.98 μs | 4.70 μs | 100.0000 | 2806.93 KB |
| Method | Mean | Error | StdDev | Allocated |
|---------------------------- |---------:|---------:|---------:|----------:|
| SharpCompress_0_44_Original | 581.8 ms | 11.56 ms | 17.65 ms | 48.77 MB |
| Method | Mean | Error | StdDev | Median | Gen0 | Gen1 | Gen2 | Allocated |
|-------------------- |-----------:|----------:|----------:|-----------:|---------:|---------:|---------:|----------:|
| ZipArchiveRead | 959.2 μs | 52.16 μs | 153.78 μs | 928.7 μs | 27.3438 | 5.8594 | - | 345.75 KB |
| TarArchiveRead | 252.1 μs | 20.97 μs | 61.82 μs | 251.9 μs | 12.2070 | 5.8594 | - | 154.78 KB |
| TarGzArchiveRead | 600.9 μs | 19.25 μs | 53.98 μs | 607.8 μs | 16.6016 | 6.8359 | - | 204.95 KB |
| TarBz2ArchiveRead | NA | NA | NA | NA | NA | NA | NA | NA |
| SevenZipArchiveRead | 8,354.4 μs | 273.01 μs | 747.35 μs | 8,093.2 μs | 109.3750 | 109.3750 | 109.3750 | 787.99 KB |
| RarArchiveRead | 1,648.6 μs | 131.91 μs | 388.94 μs | 1,617.6 μs | 17.5781 | 5.8594 | - | 222.62 KB |
| Method | Mean | Error | StdDev | Gen0 | Gen1 | Gen2 | Allocated |
|--------------------------------- |-----------:|--------:|---------:|--------:|--------:|--------:|----------:|
| &#39;GZip: Compress 100KB&#39; | 3,317.1 μs | 7.15 μs | 10.02 μs | 33.3333 | 33.3333 | 33.3333 | 519.31 KB |
| &#39;GZip: Compress 100KB (Async)&#39; | 3,280.3 μs | 8.30 μs | 11.63 μs | 33.3333 | 33.3333 | 33.3333 | 519.46 KB |
| &#39;GZip: Decompress 100KB&#39; | 432.5 μs | 2.43 μs | 3.56 μs | - | - | - | 33.92 KB |
| &#39;GZip: Decompress 100KB (Async)&#39; | 442.8 μs | 1.20 μs | 1.76 μs | - | - | - | 34.24 KB |
| Method | Mean | Error | StdDev | Gen0 | Gen1 | Gen2 | Allocated |
|------------------------------------------------ |-----------:|----------:|----------:|---------:|---------:|---------:|-----------:|
| &#39;Rar: Extract all entries (Archive API)&#39; | 908.2 μs | 12.42 μs | 17.01 μs | - | - | - | 90.68 KB |
| &#39;Rar: Extract all entries (Archive API, Async)&#39; | 1,175.4 μs | 118.74 μs | 177.72 μs | - | - | - | 96.09 KB |
| &#39;Rar: Extract all entries (Reader API)&#39; | 1,215.1 μs | 2.26 μs | 3.09 μs | - | - | - | 148.85 KB |
| &#39;Rar: Extract all entries (Reader API, Async)&#39; | 1,592.0 μs | 22.58 μs | 33.10 μs | 500.0000 | 500.0000 | 500.0000 | 4776.76 KB |
| Method | Mean | Error | StdDev | Gen0 | Gen1 | Gen2 | Allocated |
|------------------------------------------------- |----------:|----------:|----------:|---------:|---------:|---------:|-----------:|
| &#39;7Zip LZMA: Extract all entries&#39; | 7.723 ms | 0.0111 ms | 0.0152 ms | 33.3333 | 33.3333 | 33.3333 | 272.68 KB |
| &#39;7Zip LZMA: Extract all entries (Async)&#39; | 35.827 ms | 0.0381 ms | 0.0546 ms | 200.0000 | 33.3333 | 33.3333 | 3402.82 KB |
| &#39;7Zip LZMA2: Extract all entries&#39; | 7.758 ms | 0.0074 ms | 0.0104 ms | 33.3333 | 33.3333 | 33.3333 | 272.46 KB |
| &#39;7Zip LZMA2: Extract all entries (Async)&#39; | 36.317 ms | 0.0345 ms | 0.0506 ms | 200.0000 | 33.3333 | 33.3333 | 3409.72 KB |
| &#39;7Zip LZMA2 Reader: Extract all entries&#39; | 7.706 ms | 0.0114 ms | 0.0163 ms | 33.3333 | 33.3333 | 33.3333 | 273.03 KB |
| &#39;7Zip LZMA2 Reader: Extract all entries (Async)&#39; | 22.951 ms | 0.0973 ms | 0.1426 ms | 100.0000 | 100.0000 | 100.0000 | 2420.81 KB |
| Method | Mean | Error | StdDev | Gen0 | Gen1 | Gen2 | Allocated |
|------------------------------------------------ |----------:|---------:|---------:|--------:|--------:|--------:|----------:|
| &#39;Tar: Extract all entries (Archive API)&#39; | 40.82 μs | 0.292 μs | 0.427 μs | - | - | - | 16.36 KB |
| &#39;Tar: Extract all entries (Archive API, Async)&#39; | 105.12 μs | 6.183 μs | 9.254 μs | - | - | - | 14.57 KB |
| &#39;Tar: Extract all entries (Reader API)&#39; | 187.89 μs | 1.571 μs | 2.254 μs | 66.6667 | 66.6667 | 66.6667 | 341.24 KB |
| &#39;Tar: Extract all entries (Reader API, Async)&#39; | 229.78 μs | 4.852 μs | 6.802 μs | 66.6667 | 66.6667 | 66.6667 | 376.64 KB |
| &#39;Tar.GZip: Extract all entries&#39; | NA | NA | NA | NA | NA | NA | NA |
| &#39;Tar.GZip: Extract all entries (Async)&#39; | NA | NA | NA | NA | NA | NA | NA |
| &#39;Tar: Create archive with small files&#39; | 46.98 μs | 0.287 μs | 0.394 μs | - | - | - | 68.11 KB |
| &#39;Tar: Create archive with small files (Async)&#39; | 53.14 μs | 0.352 μs | 0.493 μs | - | - | - | 68.11 KB |
| Method | Mean | Error | StdDev | Gen0 | Gen1 | Allocated |
|------------------------------------------------ |---------:|---------:|---------:|---------:|--------:|-----------:|
| &#39;Zip: Extract all entries (Archive API)&#39; | 556.7 μs | 3.38 μs | 4.74 μs | - | - | 180.22 KB |
| &#39;Zip: Extract all entries (Archive API, Async)&#39; | 615.7 μs | 15.98 μs | 22.92 μs | - | - | 125.52 KB |
| &#39;Zip: Extract all entries (Reader API)&#39; | 542.2 μs | 1.10 μs | 1.46 μs | - | - | 121.04 KB |
| &#39;Zip: Extract all entries (Reader API, Async)&#39; | 562.8 μs | 2.42 μs | 3.55 μs | - | - | 123.34 KB |
| &#39;Zip: Create archive with small files&#39; | 271.1 μs | 12.93 μs | 18.95 μs | 166.6667 | 33.3333 | 2806.28 KB |
| &#39;Zip: Create archive with small files (Async)&#39; | 394.3 μs | 25.59 μs | 36.71 μs | 166.6667 | 33.3333 | 2811.42 KB |

View File

@@ -614,7 +614,7 @@ public class ArchiveTests : ReaderTests
{
using (var stream = SharpCompressStream.CreateNonDisposing(File.OpenRead(path)))
await using (
var archive = archiveFactory.OpenAsyncArchive(
var archive = await archiveFactory.OpenAsyncArchive(
new AsyncOnlyStream(stream),
readerOptions
)

View File

@@ -72,7 +72,7 @@ public class AsyncTests : TestBase
public async ValueTask Archive_Entry_Async_Open_Stream()
{
var testArchive = Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz");
await using var archive = GZipArchive.OpenAsyncArchive(
await using var archive = await GZipArchive.OpenAsyncArchive(
new AsyncOnlyStream(File.OpenRead(testArchive))
);
@@ -123,7 +123,7 @@ public class AsyncTests : TestBase
// Verify the archive was created and contains the entry
Assert.True(File.Exists(outputPath));
await using var archive = ZipArchive.OpenAsyncArchive(outputPath);
await using var archive = await ZipArchive.OpenAsyncArchive(outputPath);
Assert.Single(await archive.EntriesAsync.Where(e => !e.IsDirectory).ToListAsync());
}

View File

@@ -24,7 +24,7 @@ public class GZipArchiveAsyncTests : ArchiveTests
#else
await using (Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz")))
#endif
await using (var archive = GZipArchive.OpenAsyncArchive(new AsyncOnlyStream(stream)))
await using (var archive = await GZipArchive.OpenAsyncArchive(new AsyncOnlyStream(stream)))
{
var entry = await archive.EntriesAsync.FirstAsync();
await entry.WriteToFileAsync(Path.Combine(SCRATCH_FILES_PATH, entry.Key.NotNull()));
@@ -51,7 +51,9 @@ public class GZipArchiveAsyncTests : ArchiveTests
await using (Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz")))
#endif
{
await using (var archive = GZipArchive.OpenAsyncArchive(new AsyncOnlyStream(stream)))
await using (
var archive = await GZipArchive.OpenAsyncArchive(new AsyncOnlyStream(stream))
)
{
var entry = await archive.EntriesAsync.FirstAsync();
await entry.WriteToFileAsync(Path.Combine(SCRATCH_FILES_PATH, entry.Key.NotNull()));
@@ -79,7 +81,7 @@ public class GZipArchiveAsyncTests : ArchiveTests
#else
await using Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz"));
#endif
await using (var archive = GZipArchive.OpenAsyncArchive(new AsyncOnlyStream(stream)))
await using (var archive = await GZipArchive.OpenAsyncArchive(new AsyncOnlyStream(stream)))
{
await Assert.ThrowsAsync<NotSupportedException>(async () =>
await archive.AddEntryAsync("jpg\\test.jpg", File.OpenRead(jpg), closeStream: true)
@@ -105,7 +107,9 @@ public class GZipArchiveAsyncTests : ArchiveTests
inputStream.Position = 0;
}
await using var archive = GZipArchive.OpenAsyncArchive(new AsyncOnlyStream(inputStream));
await using var archive = await GZipArchive.OpenAsyncArchive(
new AsyncOnlyStream(inputStream)
);
var archiveEntry = await archive.EntriesAsync.FirstAsync();
MemoryStream tarStream;
@@ -159,7 +163,7 @@ public class GZipArchiveAsyncTests : ArchiveTests
#else
await using var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz"));
#endif
await using var archive = GZipArchive.OpenAsyncArchive(new AsyncOnlyStream(stream));
await using var archive = await GZipArchive.OpenAsyncArchive(new AsyncOnlyStream(stream));
await foreach (var entry in archive.EntriesAsync.Where(entry => !entry.IsDirectory))
{
Assert.InRange(entry.Crc, 0L, 0xFFFFFFFFL);
@@ -174,7 +178,7 @@ public class GZipArchiveAsyncTests : ArchiveTests
#else
await using var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz"));
#endif
await using var archive = GZipArchive.OpenAsyncArchive(new AsyncOnlyStream(stream));
await using var archive = await GZipArchive.OpenAsyncArchive(new AsyncOnlyStream(stream));
Assert.Equal(archive.Type, ArchiveType.GZip);
}
}

View File

@@ -22,7 +22,7 @@ public class GZipReaderAsyncTests : ReaderTests
{
//read only as GZip item
using Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz"));
await using var reader = GZipReader.OpenAsyncReader(new AsyncOnlyStream(stream));
await using var reader = await GZipReader.OpenAsyncReader(new AsyncOnlyStream(stream));
while (await reader.MoveToNextEntryAsync())
{
Assert.NotEqual(0, reader.Entry.Size);

View File

@@ -69,7 +69,7 @@ public class RarArchiveAsyncTests : ArchiveTests
{
using (Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, testArchive)))
await using (
var archive = RarArchive.OpenAsyncArchive(
var archive = await RarArchive.OpenAsyncArchive(
stream,
new ReaderOptions { Password = password, LeaveStreamOpen = true }
)
@@ -691,7 +691,7 @@ public class RarArchiveAsyncTests : ArchiveTests
{
var testFile = "Rar.issue1050.rar";
using var fileStream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, testFile));
await using var archive = RarArchive.OpenAsyncArchive(fileStream);
await using var archive = await RarArchive.OpenAsyncArchive(fileStream);
// Extract using archive.WriteToDirectoryAsync without explicit options
await archive.WriteToDirectoryAsync(SCRATCH_FILES_PATH);

View File

@@ -178,7 +178,8 @@ public abstract class ReaderTests : TestBase
await using (
var reader = await ReaderFactory.OpenAsyncReader(
new AsyncOnlyStream(testStream),
options
options,
cancellationToken
)
)
{

View File

@@ -233,7 +233,7 @@ public class SevenZipArchiveAsyncTests : ArchiveTests
// This test verifies that solid archives iterate entries as contiguous streams
// rather than recreating the decompression stream for each entry
var testArchive = Path.Combine(TEST_ARCHIVES_PATH, "7Zip.solid.7z");
await using var archive = SevenZipArchive.OpenAsyncArchive(testArchive);
await using var archive = await SevenZipArchive.OpenAsyncArchive(testArchive);
Assert.True(((SevenZipArchive)archive).IsSolid);
await using var reader = await archive.ExtractAllEntriesAsync();
@@ -254,7 +254,7 @@ public class SevenZipArchiveAsyncTests : ArchiveTests
// This test verifies that the folder stream is reused within each folder
// and not recreated for each entry in solid archives
var testArchive = Path.Combine(TEST_ARCHIVES_PATH, "7Zip.solid.7z");
await using var archive = SevenZipArchive.OpenAsyncArchive(testArchive);
await using var archive = await SevenZipArchive.OpenAsyncArchive(testArchive);
Assert.True(((SevenZipArchive)archive).IsSolid);
await using var reader = await archive.ExtractAllEntriesAsync();

View File

@@ -344,21 +344,4 @@ public class SevenZipArchiveTests : ArchiveTests
// The critical check: within a single folder, the stream should NEVER be recreated
Assert.Equal(0, streamRecreationsWithinFolder); // Folder stream should remain the same for all entries in the same folder
}
[Fact]
public void SevenZipArchive_Cheat_CanBeOpenedWithArchiveFactory()
{
// Regression test for issue #1204: 7zip file couldn't be opened with ArchiveFactory.OpenArchive()
// due to Microsoft.Bcl.AsyncInterfaces version conflict (version 10.0.0 was too new)
var testArchive = Path.Combine(TEST_ARCHIVES_PATH, "cheat.7z");
// Test that ArchiveFactory can open the file
using var archive = ArchiveFactory.OpenArchive(testArchive);
Assert.NotNull(archive);
Assert.True(archive.Entries.Any());
// Verify we can read entries
var entry = archive.Entries.First();
Assert.NotNull(entry);
}
}

View File

@@ -55,7 +55,7 @@ public class TarArchiveAsyncTests : ArchiveTests
// Step 2: check if the written tar file can be read correctly
var unmodified = Path.Combine(SCRATCH2_FILES_PATH, archive);
await using (
var archive2 = TarArchive.OpenAsyncArchive(
var archive2 = await TarArchive.OpenAsyncArchive(
new AsyncOnlyStream(File.OpenRead(unmodified)),
new ReaderOptions() { LeaveStreamOpen = false }
)
@@ -113,7 +113,7 @@ public class TarArchiveAsyncTests : ArchiveTests
// Step 2: check if the written tar file can be read correctly
var unmodified = Path.Combine(SCRATCH2_FILES_PATH, archive);
await using (
var archive2 = TarArchive.OpenAsyncArchive(
var archive2 = await TarArchive.OpenAsyncArchive(
new AsyncOnlyStream(File.OpenRead(unmodified)),
new ReaderOptions() { LeaveStreamOpen = false }
)
@@ -145,7 +145,7 @@ public class TarArchiveAsyncTests : ArchiveTests
var scratchPath = Path.Combine(SCRATCH_FILES_PATH, "Tar.tar");
var unmodified = Path.Combine(TEST_ARCHIVES_PATH, "Tar.noEmptyDirs.tar");
await using (var archive = TarArchive.CreateAsyncArchive())
await using (var archive = await TarArchive.CreateAsyncArchive())
{
await archive.AddAllFromDirectoryAsync(ORIGINAL_FILES_PATH);
var twopt = new TarWriterOptions(CompressionType.None, true)
@@ -165,7 +165,7 @@ public class TarArchiveAsyncTests : ArchiveTests
var unmodified = Path.Combine(TEST_ARCHIVES_PATH, "Tar.mod.tar");
var modified = Path.Combine(TEST_ARCHIVES_PATH, "Tar.noEmptyDirs.tar");
await using (var archive = TarArchive.OpenAsyncArchive(unmodified))
await using (var archive = await TarArchive.OpenAsyncArchive(unmodified))
{
await archive.AddEntryAsync("jpg\\test.jpg", jpg);
await archive.SaveToAsync(
@@ -183,7 +183,7 @@ public class TarArchiveAsyncTests : ArchiveTests
var modified = Path.Combine(TEST_ARCHIVES_PATH, "Tar.mod.tar");
var unmodified = Path.Combine(TEST_ARCHIVES_PATH, "Tar.noEmptyDirs.tar");
await using (var archive = TarArchive.OpenAsyncArchive(unmodified))
await using (var archive = await TarArchive.OpenAsyncArchive(unmodified))
{
var entry = await archive.EntriesAsync.SingleAsync(x =>
x.Key.NotNull().EndsWith("jpg", StringComparison.OrdinalIgnoreCase)
@@ -253,7 +253,9 @@ public class TarArchiveAsyncTests : ArchiveTests
var numberOfEntries = 0;
await using (
var archiveFactory = TarArchive.OpenAsyncArchive(new AsyncOnlyStream(memoryStream))
var archiveFactory = await ArchiveFactory.OpenAsyncArchive(
new AsyncOnlyStream(memoryStream)
)
)
{
await foreach (var entry in archiveFactory.EntriesAsync)

View File

@@ -7,6 +7,7 @@ using SharpCompress.Archives.Tar;
using SharpCompress.Common;
using SharpCompress.Readers;
using SharpCompress.Readers.Tar;
using SharpCompress.Test.Mocks;
using SharpCompress.Writers;
using SharpCompress.Writers.Tar;
using Xunit;
@@ -23,6 +24,16 @@ public class TarArchiveTests : ArchiveTests
[Fact]
public void TarArchivePathRead() => ArchiveFileRead("Tar.tar");
[Fact]
public void TarArchiveStreamRead_Throws_On_NonSeekable_Stream()
{
using Stream stream = new ForwardOnlyStream(
File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar"))
);
Assert.Throws<ArgumentException>(() => ArchiveFactory.OpenArchive(stream));
}
[Fact]
public void Tar_FileName_Exactly_100_Characters()
{
@@ -53,7 +64,7 @@ public class TarArchiveTests : ArchiveTests
// Step 2: check if the written tar file can be read correctly
var unmodified = Path.Combine(SCRATCH2_FILES_PATH, archive);
using (var archive2 = TarArchive.OpenArchive(unmodified))
using (var archive2 = ArchiveFactory.OpenArchive(unmodified))
{
Assert.Equal(1, archive2.Entries.Count());
Assert.Contains(filename, archive2.Entries.Select(entry => entry.Key));
@@ -72,7 +83,7 @@ public class TarArchiveTests : ArchiveTests
public void Tar_NonUstarArchiveWithLongNameDoesNotSkipEntriesAfterTheLongOne()
{
var unmodified = Path.Combine(TEST_ARCHIVES_PATH, "very long filename.tar");
using var archive = TarArchive.OpenArchive(unmodified);
using var archive = ArchiveFactory.OpenArchive(unmodified);
Assert.Equal(5, archive.Entries.Count());
Assert.Contains("very long filename/", archive.Entries.Select(entry => entry.Key));
Assert.Contains(
@@ -119,7 +130,7 @@ public class TarArchiveTests : ArchiveTests
// Step 2: check if the written tar file can be read correctly
var unmodified = Path.Combine(SCRATCH2_FILES_PATH, archive);
using (var archive2 = TarArchive.OpenArchive(unmodified))
using (var archive2 = ArchiveFactory.OpenArchive(unmodified))
{
Assert.Equal(1, archive2.Entries.Count());
Assert.Contains(longFilename, archive2.Entries.Select(entry => entry.Key));
@@ -138,7 +149,7 @@ public class TarArchiveTests : ArchiveTests
public void Tar_UstarArchivePathReadLongName()
{
var unmodified = Path.Combine(TEST_ARCHIVES_PATH, "ustar with long names.tar");
using var archive = TarArchive.OpenArchive(unmodified);
using var archive = ArchiveFactory.OpenArchive(unmodified);
Assert.Equal(6, archive.Entries.Count());
Assert.Contains("Directory/", archive.Entries.Select(entry => entry.Key));
Assert.Contains(
@@ -285,9 +296,9 @@ public class TarArchiveTests : ArchiveTests
var numberOfEntries = 0;
using (var archiveFactory = TarArchive.OpenArchive(memoryStream))
using (var archive = ArchiveFactory.OpenArchive(memoryStream))
{
foreach (var entry in archiveFactory.Entries)
foreach (var entry in archive.Entries)
{
++numberOfEntries;
@@ -308,4 +319,24 @@ public class TarArchiveTests : ArchiveTests
Assert.False(isTar);
}
[Fact]
public void TarArchiveStreamRead_Autodetect_CompressedTar()
{
using Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz"));
using var archive = ArchiveFactory.OpenArchive(stream);
Assert.Equal(ArchiveType.Tar, archive.Type);
Assert.NotEmpty(archive.Entries);
}
[Fact]
public void TarReaderStreamRead_Autodetect_CompressedTar()
{
using Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz"));
using var reader = ReaderFactory.OpenReader(stream);
Assert.Equal(ArchiveType.Tar, reader.ArchiveType);
Assert.True(reader.MoveToNextEntry());
}
}

View File

@@ -74,7 +74,7 @@ public class TarReaderAsyncTests : ReaderTests
public async ValueTask Tar_BZip2_Entry_Stream_Async()
{
using Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.bz2"));
await using var reader = TarReader.OpenAsyncReader(stream);
await using var reader = await TarReader.OpenAsyncReader(stream);
while (await reader.MoveToNextEntryAsync())
{
if (!reader.Entry.IsDirectory)
@@ -135,7 +135,7 @@ public class TarReaderAsyncTests : ReaderTests
public async ValueTask Tar_BZip2_Skip_Entry_Stream_Async()
{
using Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.bz2"));
await using var reader = TarReader.OpenAsyncReader(stream);
await using var reader = await TarReader.OpenAsyncReader(stream);
var names = new List<string>();
while (await reader.MoveToNextEntryAsync())
{

View File

@@ -126,7 +126,7 @@ public class ZipArchiveAsyncTests : ArchiveTests
var unmodified = Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.noEmptyDirs.zip");
var modified = Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.mod.zip");
await using (var archive = ZipArchive.OpenAsyncArchive(unmodified))
await using (var archive = await ZipArchive.OpenAsyncArchive(unmodified))
{
var entry = await archive.EntriesAsync.SingleAsync(x =>
x.Key.NotNull().EndsWith("jpg", StringComparison.OrdinalIgnoreCase)
@@ -151,7 +151,7 @@ public class ZipArchiveAsyncTests : ArchiveTests
var unmodified = Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.mod.zip");
var modified = Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.noEmptyDirs.zip");
await using (var archive = ZipArchive.OpenAsyncArchive(unmodified))
await using (var archive = await ZipArchive.OpenAsyncArchive(unmodified))
{
await archive.AddEntryAsync("jpg\\test.jpg", jpg);
@@ -171,7 +171,7 @@ public class ZipArchiveAsyncTests : ArchiveTests
var scratchPath = Path.Combine(SCRATCH_FILES_PATH, "Zip.deflate.noEmptyDirs.zip");
var unmodified = Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.noEmptyDirs.zip");
await using (var archive = (ZipArchive)ZipArchive.CreateAsyncArchive())
await using (var archive = (ZipArchive)await ZipArchive.CreateAsyncArchive())
{
archive.DeflateCompressionLevel = CompressionLevel.BestSpeed;
archive.AddAllFromDirectory(ORIGINAL_FILES_PATH);
@@ -191,7 +191,7 @@ public class ZipArchiveAsyncTests : ArchiveTests
{
using (Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.zip")))
{
IAsyncArchive archive = ZipArchive.OpenAsyncArchive(new AsyncOnlyStream(stream));
IAsyncArchive archive = await ZipArchive.OpenAsyncArchive(new AsyncOnlyStream(stream));
try
{
await foreach (var entry in archive.EntriesAsync.Where(entry => !entry.IsDirectory))
@@ -212,7 +212,7 @@ public class ZipArchiveAsyncTests : ArchiveTests
{
using (Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.zip")))
{
IAsyncArchive archive = ZipArchive.OpenAsyncArchive(new AsyncOnlyStream(stream));
IAsyncArchive archive = await ZipArchive.OpenAsyncArchive(new AsyncOnlyStream(stream));
try
{
await archive.WriteToDirectoryAsync(SCRATCH_FILES_PATH);
@@ -239,7 +239,7 @@ public class ZipArchiveAsyncTests : ArchiveTests
)
#endif
{
await using IAsyncArchive archive = ZipArchive.OpenAsyncArchive(
await using IAsyncArchive archive = await ZipArchive.OpenAsyncArchive(
new AsyncOnlyStream(stream)
);
await archive.WriteToDirectoryAsync(SCRATCH_FILES_PATH, progress);

View File

@@ -190,10 +190,10 @@
},
"System.Threading.Tasks.Extensions": {
"type": "Transitive",
"resolved": "4.5.4",
"contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
"resolved": "4.6.3",
"contentHash": "7sCiwilJLYbTZELaKnc7RecBBXWXA+xMLQWZKWawBxYjp6DBlSE3v9/UcvKBvr1vv2tTOhipiogM8rRmxlhrVA==",
"dependencies": {
"System.Runtime.CompilerServices.Unsafe": "4.5.3"
"System.Runtime.CompilerServices.Unsafe": "6.1.2"
}
},
"System.ValueTuple": {
@@ -275,7 +275,7 @@
"sharpcompress": {
"type": "Project",
"dependencies": {
"Microsoft.Bcl.AsyncInterfaces": "[8.0.0, )",
"Microsoft.Bcl.AsyncInterfaces": "[10.0.0, )",
"System.Buffers": "[4.6.1, )",
"System.Memory": "[4.6.3, )",
"System.Text.Encoding.CodePages": "[10.0.0, )"
@@ -283,11 +283,11 @@
},
"Microsoft.Bcl.AsyncInterfaces": {
"type": "CentralTransitive",
"requested": "[8.0.0, )",
"resolved": "8.0.0",
"contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==",
"requested": "[10.0.0, )",
"resolved": "10.0.0",
"contentHash": "vFuwSLj9QJBbNR0NeNO4YVASUbokxs+i/xbuu8B+Fs4FAZg5QaFa6eGrMaRqTzzNI5tAb97T7BhSxtLckFyiRA==",
"dependencies": {
"System.Threading.Tasks.Extensions": "4.5.4"
"System.Threading.Tasks.Extensions": "4.6.3"
}
},
"System.Buffers": {
@@ -527,9 +527,9 @@
},
"Microsoft.Bcl.AsyncInterfaces": {
"type": "CentralTransitive",
"requested": "[8.0.0, )",
"resolved": "8.0.0",
"contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw=="
"requested": "[10.0.0, )",
"resolved": "10.0.0",
"contentHash": "vFuwSLj9QJBbNR0NeNO4YVASUbokxs+i/xbuu8B+Fs4FAZg5QaFa6eGrMaRqTzzNI5tAb97T7BhSxtLckFyiRA=="
}
}
}

Binary file not shown.