Compare commits

..

13 Commits

Author SHA1 Message Date
Adam Hathcock
daacb93902 more pooling 2026-01-14 16:49:34 +00:00
Adam Hathcock
eb2f60fb53 rar byte[] better 2026-01-14 16:38:57 +00:00
Adam Hathcock
bd0439c424 change byte[] to memory using pool 2026-01-14 15:55:14 +00:00
Adam Hathcock
b935bcfaef reduce memory usage on headers 2026-01-14 15:33:08 +00:00
Adam Hathcock
56c22cee78 Merge branch 'copilot/add-performance-benchmarking' into adam/async-again 2026-01-14 14:53:17 +00:00
Adam Hathcock
5c4b83e501 Merge remote-tracking branch 'origin/master' into copilot/add-performance-benchmarking
# Conflicts:
#	tests/SharpCompress.Performance/packages.lock.json
2026-01-14 14:52:21 +00:00
copilot-swe-agent[bot]
80ac10a5fe Merge latest master branch and resolve conflicts
Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com>
2026-01-12 14:11:57 +00:00
copilot-swe-agent[bot]
a92ce90252 Fix path validation and add iteration cleanup to prevent file reuse
Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com>
2026-01-05 17:38:17 +00:00
copilot-swe-agent[bot]
e519f61f0f Address code review feedback: fix exception handling and initialization order
Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com>
2026-01-05 17:36:08 +00:00
copilot-swe-agent[bot]
49f2271253 Format code with CSharpier
Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com>
2026-01-05 17:33:06 +00:00
copilot-swe-agent[bot]
5b1d11bc1d Add WriteBenchmarks, BaselineComparisonBenchmarks, and comprehensive documentation
Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com>
2026-01-05 17:31:32 +00:00
copilot-swe-agent[bot]
aa3a40d968 Add BenchmarkDotNet integration with Archive and Reader benchmarks
Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com>
2026-01-05 17:27:50 +00:00
copilot-swe-agent[bot]
6125654b2e Initial plan 2026-01-05 17:21:29 +00:00
178 changed files with 2414 additions and 5894 deletions

4
.gitignore vendored
View File

@@ -20,3 +20,7 @@ artifacts/
.DS_Store
*.snupkg
# BenchmarkDotNet artifacts
BenchmarkDotNet.Artifacts/
**/BenchmarkDotNet.Artifacts/

View File

@@ -1,5 +1,6 @@
<Project>
<ItemGroup>
<PackageVersion Include="BenchmarkDotNet" Version="0.14.0" />
<PackageVersion Include="Bullseye" Version="6.1.0" />
<PackageVersion Include="AwesomeAssertions" Version="9.3.0" />
<PackageVersion Include="Glob" Version="1.1.9" />
@@ -12,7 +13,7 @@
<PackageVersion Include="System.Text.Encoding.CodePages" Version="10.0.0" />
<PackageVersion Include="System.Buffers" Version="4.6.1" />
<PackageVersion Include="System.Memory" Version="4.6.3" />
<PackageVersion Include="xunit.v3" Version="3.2.1" />
<PackageVersion Include="xunit" Version="2.9.3" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" />
<GlobalPackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
<GlobalPackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" />

View File

@@ -8,17 +8,17 @@ Quick reference for commonly used SharpCompress APIs.
```csharp
// Auto-detect format
using (var reader = ReaderFactory.OpenReader(stream))
using (var reader = ReaderFactory.Open(stream))
{
// Works with Zip, Tar, GZip, Rar, 7Zip, etc.
}
// Specific format - Archive API
using (var archive = ZipArchive.OpenArchive("file.zip"))
using (var archive = TarArchive.OpenArchive("file.tar"))
using (var archive = RarArchive.OpenArchive("file.rar"))
using (var archive = SevenZipArchive.OpenArchive("file.7z"))
using (var archive = GZipArchive.OpenArchive("file.gz"))
using (var archive = ZipArchive.Open("file.zip"))
using (var archive = TarArchive.Open("file.tar"))
using (var archive = RarArchive.Open("file.rar"))
using (var archive = SevenZipArchive.Open("file.7z"))
using (var archive = GZipArchive.Open("file.gz"))
// With options
var options = new ReaderOptions
@@ -27,30 +27,30 @@ var options = new ReaderOptions
LeaveStreamOpen = true,
ArchiveEncoding = new ArchiveEncoding { Default = Encoding.GetEncoding(932) }
};
using (var archive = ZipArchive.OpenArchive("encrypted.zip", options))
using (var archive = ZipArchive.Open("encrypted.zip", options))
```
### Creating Archives
```csharp
// Writer Factory
using (var writer = WriterFactory.OpenWriter(stream, ArchiveType.Zip, CompressionType.Deflate))
using (var writer = WriterFactory.Open(stream, ArchiveType.Zip, CompressionType.Deflate))
{
// Write entries
}
// Specific writer
using (var archive = ZipArchive.CreateArchive())
using (var archive = TarArchive.CreateArchive())
using (var archive = GZipArchive.CreateArchive())
using (var archive = ZipArchive.Create())
using (var archive = TarArchive.Create())
using (var archive = GZipArchive.Create())
// With options
var options = new WriterOptions(CompressionType.Deflate)
{
var options = new WriterOptions(CompressionType.Deflate)
{
CompressionLevel = 9,
LeaveStreamOpen = false
};
using (var archive = ZipArchive.CreateArchive())
using (var archive = ZipArchive.Create())
{
archive.SaveTo("output.zip", options);
}
@@ -63,7 +63,7 @@ using (var archive = ZipArchive.CreateArchive())
### Reading/Extracting
```csharp
using (var archive = ZipArchive.OpenArchive("file.zip"))
using (var archive = ZipArchive.Open("file.zip"))
{
// Get all entries
IEnumerable<IArchiveEntry> entries = archive.Entries;
@@ -91,7 +91,7 @@ using (var archive = ZipArchive.OpenArchive("file.zip"))
}
// Async extraction (requires IAsyncArchive)
using (var asyncArchive = await ZipArchive.OpenAsyncArchive("file.zip"))
using (var asyncArchive = await ZipArchive.OpenAsync("file.zip"))
{
await asyncArchive.WriteToDirectoryAsync(
@"C:\output",
@@ -122,7 +122,7 @@ foreach (var entry in archive.Entries)
### Creating Archives
```csharp
using (var archive = ZipArchive.CreateArchive())
using (var archive = ZipArchive.Create())
{
// Add file
archive.AddEntry("file.txt", @"C:\source\file.txt");
@@ -151,7 +151,7 @@ using (var archive = ZipArchive.CreateArchive())
```csharp
using (var stream = File.OpenRead("file.zip"))
using (var reader = ReaderFactory.OpenReader(stream))
using (var reader = ReaderFactory.Open(stream))
{
while (reader.MoveToNextEntry())
{
@@ -172,9 +172,9 @@ using (var reader = ReaderFactory.OpenReader(stream))
}
}
// Async variants (use OpenAsyncReader to get IAsyncReader)
// Async variants (use OpenAsync to get IAsyncReader)
using (var stream = File.OpenRead("file.zip"))
using (var reader = await ReaderFactory.OpenAsyncReader(stream))
using (var reader = await ReaderFactory.OpenAsync(stream))
{
while (await reader.MoveToNextEntryAsync())
{
@@ -201,7 +201,7 @@ using (var reader = await ReaderFactory.OpenAsyncReader(stream))
```csharp
using (var stream = File.Create("output.zip"))
using (var writer = WriterFactory.OpenWriter(stream, ArchiveType.Zip, CompressionType.Deflate))
using (var writer = WriterFactory.Open(stream, ArchiveType.Zip, CompressionType.Deflate))
{
// Write single file
using (var fileStream = File.OpenRead("source.txt"))
@@ -239,7 +239,7 @@ var options = new ReaderOptions
Default = Encoding.GetEncoding(932)
}
};
using (var archive = ZipArchive.OpenArchive("file.zip", options))
using (var archive = ZipArchive.Open("file.zip", options))
{
// ...
}
@@ -290,8 +290,8 @@ ArchiveType.Ace
// For Tar archives with compression
// Use WriterFactory to create compressed tar archives
using (var writer = WriterFactory.OpenWriter(stream, ArchiveType.Tar, CompressionType.GZip)) // Tar.GZip
using (var writer = WriterFactory.OpenWriter(stream, ArchiveType.Tar, CompressionType.BZip2)) // Tar.BZip2
using (var writer = WriterFactory.Open(stream, ArchiveType.Tar, CompressionType.GZip)) // Tar.GZip
using (var writer = WriterFactory.Open(stream, ArchiveType.Tar, CompressionType.BZip2)) // Tar.BZip2
```
### Archive Types
@@ -349,7 +349,7 @@ var progress = new Progress<ProgressReport>(report =>
});
var options = new ReaderOptions { Progress = progress };
using (var archive = ZipArchive.OpenArchive("archive.zip", options))
using (var archive = ZipArchive.Open("archive.zip", options))
{
archive.WriteToDirectory(@"C:\output");
}
@@ -363,7 +363,7 @@ cts.CancelAfter(TimeSpan.FromMinutes(5));
try
{
using (var archive = await ZipArchive.OpenAsyncArchive("archive.zip"))
using (var archive = await ZipArchive.OpenAsync("archive.zip"))
{
await archive.WriteToDirectoryAsync(
@"C:\output",
@@ -381,23 +381,23 @@ catch (OperationCanceledException)
### Create with Custom Compression
```csharp
using (var archive = ZipArchive.CreateArchive())
using (var archive = ZipArchive.Create())
{
archive.AddAllFromDirectory(@"D:\source");
// Fastest
archive.SaveTo("fast.zip", new WriterOptions(CompressionType.Deflate)
{
CompressionLevel = 1
archive.SaveTo("fast.zip", new WriterOptions(CompressionType.Deflate)
{
CompressionLevel = 1
});
// Balanced (default)
archive.SaveTo("normal.zip", CompressionType.Deflate);
// Best compression
archive.SaveTo("best.zip", new WriterOptions(CompressionType.Deflate)
{
CompressionLevel = 9
archive.SaveTo("best.zip", new WriterOptions(CompressionType.Deflate)
{
CompressionLevel = 9
});
}
```
@@ -406,7 +406,7 @@ using (var archive = ZipArchive.CreateArchive())
```csharp
using (var outputStream = new MemoryStream())
using (var archive = ZipArchive.CreateArchive())
using (var archive = ZipArchive.Create())
{
// Add content from memory
using (var contentStream = new MemoryStream(Encoding.UTF8.GetBytes("Hello")))
@@ -425,7 +425,7 @@ using (var archive = ZipArchive.CreateArchive())
### Extract Specific Files
```csharp
using (var archive = ZipArchive.OpenArchive("archive.zip"))
using (var archive = ZipArchive.Open("archive.zip"))
{
var filesToExtract = new[] { "file1.txt", "file2.txt" };
@@ -439,7 +439,7 @@ using (var archive = ZipArchive.OpenArchive("archive.zip"))
### List Archive Contents
```csharp
using (var archive = ZipArchive.OpenArchive("archive.zip"))
using (var archive = ZipArchive.Open("archive.zip"))
{
foreach (var entry in archive.Entries)
{
@@ -459,7 +459,7 @@ using (var archive = ZipArchive.OpenArchive("archive.zip"))
```csharp
var stream = File.OpenRead("archive.zip");
var archive = ZipArchive.OpenArchive(stream);
var archive = ZipArchive.Open(stream);
archive.WriteToDirectory(@"C:\output");
// stream not disposed - leaked resource
```
@@ -468,7 +468,7 @@ archive.WriteToDirectory(@"C:\output");
```csharp
using (var stream = File.OpenRead("archive.zip"))
using (var archive = ZipArchive.OpenArchive(stream))
using (var archive = ZipArchive.Open(stream))
{
archive.WriteToDirectory(@"C:\output");
}
@@ -479,7 +479,7 @@ using (var archive = ZipArchive.OpenArchive(stream))
```csharp
// Loading entire archive then iterating
using (var archive = ZipArchive.OpenArchive("large.zip"))
using (var archive = ZipArchive.Open("large.zip"))
{
var entries = archive.Entries.ToList(); // Loads all in memory
foreach (var e in entries)
@@ -494,7 +494,7 @@ using (var archive = ZipArchive.OpenArchive("large.zip"))
```csharp
// Streaming iteration
using (var stream = File.OpenRead("large.zip"))
using (var reader = ReaderFactory.OpenReader(stream))
using (var reader = ReaderFactory.Open(stream))
{
while (reader.MoveToNextEntry())
{

View File

@@ -76,7 +76,7 @@ Factory classes for auto-detecting archive format and creating appropriate reade
- Format-specific: `ZipFactory.cs`, `TarFactory.cs`, `RarFactory.cs`, etc.
**How It Works:**
1. `ReaderFactory.OpenReader(stream)` probes stream signatures
1. `ReaderFactory.Open(stream)` probes stream signatures
2. Identifies format by magic bytes
3. Creates appropriate reader instance
4. Returns generic `IReader` interface
@@ -142,7 +142,7 @@ Stream wrappers and utilities.
**Example:**
```csharp
// User calls factory
using (var reader = ReaderFactory.OpenReader(stream)) // Returns IReader
using (var reader = ReaderFactory.Open(stream)) // Returns IReader
{
while (reader.MoveToNextEntry())
{
@@ -175,7 +175,7 @@ CompressionType.LZMA // LZMA
CompressionType.PPMd // PPMd
// Writer uses strategy pattern
var archive = ZipArchive.CreateArchive();
var archive = ZipArchive.Create();
archive.SaveTo("output.zip", CompressionType.Deflate); // Use Deflate
archive.SaveTo("output.bz2", CompressionType.BZip2); // Use BZip2
```
@@ -248,7 +248,7 @@ foreach (var entry in entries)
}
// Reader API - provides iterator
IReader reader = ReaderFactory.OpenReader(stream);
IReader reader = ReaderFactory.Open(stream);
while (reader.MoveToNextEntry())
{
// Forward-only iteration - one entry at a time
@@ -381,7 +381,7 @@ public class NewFormatArchive : AbstractArchive
private NewFormatHeader _header;
private List<NewFormatEntry> _entries;
public static NewFormatArchive OpenArchive(Stream stream)
public static NewFormatArchive Open(Stream stream)
{
var archive = new NewFormatArchive();
archive._header = NewFormatHeader.Read(stream);
@@ -442,8 +442,8 @@ public class NewFormatFactory : Factory, IArchiveFactory, IReaderFactory
public static NewFormatFactory Instance { get; } = new();
public IArchive CreateArchive(Stream stream)
=> NewFormatArchive.OpenArchive(stream);
public IArchive CreateArchive(Stream stream)
=> NewFormatArchive.Open(stream);
public IReader CreateReader(Stream stream, ReaderOptions options)
=> new NewFormatReader(stream) { Options = options };
@@ -481,7 +481,7 @@ public class NewFormatTests : TestBase
public void NewFormat_Extracts_Successfully()
{
var archivePath = Path.Combine(TEST_ARCHIVES_PATH, "archive.newformat");
using (var archive = NewFormatArchive.OpenArchive(archivePath))
using (var archive = NewFormatArchive.Open(archivePath))
{
archive.WriteToDirectory(SCRATCH_FILES_PATH);
// Assert extraction
@@ -561,7 +561,7 @@ public class CustomStream : Stream
```csharp
// Correct: Nested using blocks
using (var fileStream = File.OpenRead("archive.zip"))
using (var archive = ZipArchive.OpenArchive(fileStream))
using (var archive = ZipArchive.Open(fileStream))
{
archive.WriteToDirectory(@"C:\output");
}
@@ -570,7 +570,7 @@ using (var archive = ZipArchive.OpenArchive(fileStream))
// Correct: Using with options
var options = new ReaderOptions { LeaveStreamOpen = true };
var stream = File.OpenRead("archive.zip");
using (var archive = ZipArchive.OpenArchive(stream, options))
using (var archive = ZipArchive.Open(stream, options))
{
archive.WriteToDirectory(@"C:\output");
}
@@ -641,7 +641,7 @@ public void Archive_Extraction_Works()
var testArchive = Path.Combine(TEST_ARCHIVES_PATH, "test.zip");
// Act
using (var archive = ZipArchive.OpenArchive(testArchive))
using (var archive = ZipArchive.Open(testArchive))
{
archive.WriteToDirectory(SCRATCH_FILES_PATH);
}

View File

@@ -27,7 +27,7 @@ var options = new ReaderOptions
}
};
using (var archive = ZipArchive.OpenArchive("japanese.zip", options))
using (var archive = ZipArchive.Open("japanese.zip", options))
{
foreach (var entry in archive.Entries)
{
@@ -51,7 +51,7 @@ var options = new ReaderOptions
{
ArchiveEncoding = new ArchiveEncoding { Default = Encoding.GetEncoding(932) }
};
using (var archive = ZipArchive.OpenArchive("file.zip", options))
using (var archive = ZipArchive.Open("file.zip", options))
{
// Use archive with correct encoding
}
@@ -64,7 +64,7 @@ var options = new ReaderOptions
ArchiveEncoding = new ArchiveEncoding { Default = Encoding.GetEncoding(932) }
};
using (var stream = File.OpenRead("file.zip"))
using (var reader = ReaderFactory.OpenReader(stream, options))
using (var reader = ReaderFactory.Open(stream, options))
{
while (reader.MoveToNextEntry())
{
@@ -89,7 +89,7 @@ var options = new ReaderOptions
Default = Encoding.GetEncoding(932)
}
};
using (var archive = ZipArchive.OpenArchive("japanese.zip", options))
using (var archive = ZipArchive.Open("japanese.zip", options))
{
// Correctly decodes Japanese filenames
}
@@ -266,7 +266,7 @@ SharpCompress attempts to auto-detect encoding, but this isn't always reliable:
```csharp
// Auto-detection (default)
using (var archive = ZipArchive.OpenArchive("file.zip")) // Uses UTF8 by default
using (var archive = ZipArchive.Open("file.zip")) // Uses UTF8 by default
{
// May show corrupted characters if archive uses different encoding
}
@@ -276,7 +276,7 @@ var options = new ReaderOptions
{
ArchiveEncoding = new ArchiveEncoding { Default = Encoding.GetEncoding(932) }
};
using (var archive = ZipArchive.OpenArchive("file.zip", options))
using (var archive = ZipArchive.Open("file.zip", options))
{
// Correct characters displayed
}
@@ -324,7 +324,7 @@ var options = new ReaderOptions
}
};
using (var archive = ZipArchive.OpenArchive("mixed.zip", options))
using (var archive = ZipArchive.Open("mixed.zip", options))
{
foreach (var entry in archive.Entries)
{
@@ -388,7 +388,7 @@ var options = new ReaderOptions
}
};
using (var archive = ZipArchive.OpenArchive("japanese_files.zip", options))
using (var archive = ZipArchive.Open("japanese_files.zip", options))
{
archive.WriteToDirectory(@"C:\output", new ExtractionOptions
{
@@ -410,7 +410,7 @@ var options = new ReaderOptions
}
};
using (var archive = ZipArchive.OpenArchive("french_files.zip", options))
using (var archive = ZipArchive.Open("french_files.zip", options))
{
archive.WriteToDirectory(@"C:\output");
}
@@ -428,7 +428,7 @@ var options = new ReaderOptions
}
};
using (var archive = ZipArchive.OpenArchive("chinese_files.zip", options))
using (var archive = ZipArchive.Open("chinese_files.zip", options))
{
archive.WriteToDirectory(@"C:\output");
}
@@ -445,7 +445,7 @@ var options = new ReaderOptions
}
};
using (var archive = ZipArchive.OpenArchive("russian_files.zip", options))
using (var archive = ZipArchive.Open("russian_files.zip", options))
{
archive.WriteToDirectory(@"C:\output");
}
@@ -463,7 +463,7 @@ var options = new ReaderOptions
};
using (var stream = File.OpenRead("japanese.zip"))
using (var reader = ReaderFactory.OpenReader(stream, options))
using (var reader = ReaderFactory.Open(stream, options))
{
while (reader.MoveToNextEntry())
{
@@ -484,7 +484,7 @@ When creating archives, SharpCompress uses UTF8 by default (recommended):
```csharp
// Create with UTF8 (default, recommended)
using (var archive = ZipArchive.CreateArchive())
using (var archive = ZipArchive.Create())
{
archive.AddAllFromDirectory(@"D:\my_files");
archive.SaveTo("output.zip", CompressionType.Deflate);

View File

@@ -24,7 +24,7 @@ Choose the right API based on your use case:
// - You need random access to entries
// - Stream is seekable (file, MemoryStream)
using (var archive = ZipArchive.OpenArchive("archive.zip"))
using (var archive = ZipArchive.Open("archive.zip"))
{
// Random access - all entries available
var specific = archive.Entries.FirstOrDefault(e => e.Key == "file.txt");
@@ -51,7 +51,7 @@ using (var archive = ZipArchive.OpenArchive("archive.zip"))
// - Forward-only processing is acceptable
using (var stream = File.OpenRead("large.zip"))
using (var reader = ReaderFactory.OpenReader(stream))
using (var reader = ReaderFactory.Open(stream))
{
while (reader.MoveToNextEntry())
{
@@ -129,7 +129,7 @@ For processing archives from downloads or pipes:
```csharp
// Download stream (non-seekable)
using (var httpStream = await httpClient.GetStreamAsync(url))
using (var reader = ReaderFactory.OpenReader(httpStream))
using (var reader = ReaderFactory.Open(httpStream))
{
// Process entries as they arrive
while (reader.MoveToNextEntry())
@@ -159,14 +159,14 @@ Choose based on your constraints:
```csharp
// Download then extract (requires disk space)
var archivePath = await DownloadFile(url, @"C:\temp\archive.zip");
using (var archive = ZipArchive.OpenArchive(archivePath))
using (var archive = ZipArchive.Open(archivePath))
{
archive.WriteToDirectory(@"C:\output");
}
// Stream during download (on-the-fly extraction)
using (var httpStream = await httpClient.GetStreamAsync(url))
using (var reader = ReaderFactory.OpenReader(httpStream))
using (var reader = ReaderFactory.Open(httpStream))
{
while (reader.MoveToNextEntry())
{
@@ -198,7 +198,7 @@ Extracting File3 requires decompressing File1 and File2 first.
**Random Extraction (Slow):**
```csharp
using (var archive = RarArchive.OpenArchive("solid.rar"))
using (var archive = RarArchive.Open("solid.rar"))
{
foreach (var entry in archive.Entries)
{
@@ -210,7 +210,7 @@ using (var archive = RarArchive.OpenArchive("solid.rar"))
**Sequential Extraction (Fast):**
```csharp
using (var archive = RarArchive.OpenArchive("solid.rar"))
using (var archive = RarArchive.Open("solid.rar"))
{
// Method 1: Use WriteToDirectory (recommended)
archive.WriteToDirectory(@"C:\output", new ExtractionOptions
@@ -256,7 +256,7 @@ using (var archive = RarArchive.OpenArchive("solid.rar"))
// Level 9 = Slowest, best compression
// Write with different compression levels
using (var archive = ZipArchive.CreateArchive())
using (var archive = ZipArchive.Create())
{
archive.AddAllFromDirectory(@"D:\data");
@@ -293,7 +293,7 @@ using (var archive = ZipArchive.CreateArchive())
// Smaller block size = lower memory, faster
// Larger block size = better compression, slower
using (var archive = TarArchive.CreateArchive())
using (var archive = TarArchive.Create())
{
archive.AddAllFromDirectory(@"D:\data");
@@ -313,7 +313,7 @@ LZMA compression is very powerful but memory-intensive:
// - Better compression: larger dictionary
// Preset via CompressionType
using (var archive = TarArchive.CreateArchive())
using (var archive = TarArchive.Create())
{
archive.AddAllFromDirectory(@"D:\data");
archive.SaveTo("archive.tar.xz", CompressionType.LZMA); // Default settings
@@ -333,7 +333,7 @@ Async is beneficial when:
```csharp
// Async extraction (non-blocking)
using (var archive = ZipArchive.OpenArchive("archive.zip"))
using (var archive = ZipArchive.Open("archive.zip"))
{
await archive.WriteToDirectoryAsync(
@"C:\output",
@@ -353,7 +353,7 @@ Async doesn't improve performance for:
```csharp
// Sync extraction (simpler, same performance on fast I/O)
using (var archive = ZipArchive.OpenArchive("archive.zip"))
using (var archive = ZipArchive.Open("archive.zip"))
{
archive.WriteToDirectory(
@"C:\output",
@@ -373,7 +373,7 @@ cts.CancelAfter(TimeSpan.FromMinutes(5));
try
{
using (var archive = ZipArchive.OpenArchive("archive.zip"))
using (var archive = ZipArchive.Open("archive.zip"))
{
await archive.WriteToDirectoryAsync(
@"C:\output",
@@ -408,14 +408,14 @@ catch (OperationCanceledException)
// ✗ Slow - opens each archive separately
foreach (var file in files)
{
using (var archive = ZipArchive.OpenArchive("archive.zip"))
using (var archive = ZipArchive.Open("archive.zip"))
{
archive.WriteToDirectory(@"C:\output");
}
}
// ✓ Better - process multiple entries at once
using (var archive = ZipArchive.OpenArchive("archive.zip"))
using (var archive = ZipArchive.Open("archive.zip"))
{
archive.WriteToDirectory(@"C:\output");
}
@@ -425,7 +425,7 @@ using (var archive = ZipArchive.OpenArchive("archive.zip"))
```csharp
var sw = Stopwatch.StartNew();
using (var archive = ZipArchive.OpenArchive("large.zip"))
using (var archive = ZipArchive.Open("large.zip"))
{
archive.WriteToDirectory(@"C:\output");
}

View File

@@ -48,7 +48,7 @@ Also, look over the tests for more thorough [examples](https://github.com/adamha
### Create Zip Archive from multiple files
```C#
using(var archive = ZipArchive.CreateArchive())
using(var archive = ZipArchive.Create())
{
archive.AddEntry("file01.txt", "C:\\file01.txt");
archive.AddEntry("file02.txt", "C:\\file02.txt");
@@ -61,7 +61,7 @@ using(var archive = ZipArchive.CreateArchive())
### Create Zip Archive from all files in a directory to a file
```C#
using (var archive = ZipArchive.CreateArchive())
using (var archive = ZipArchive.Create())
{
archive.AddAllFromDirectory("D:\\temp");
archive.SaveTo("C:\\temp.zip", CompressionType.Deflate);
@@ -72,7 +72,7 @@ using (var archive = ZipArchive.CreateArchive())
```C#
var memoryStream = new MemoryStream();
using (var archive = ZipArchive.CreateArchive())
using (var archive = ZipArchive.Create())
{
archive.AddAllFromDirectory("D:\\temp");
archive.SaveTo(memoryStream, new WriterOptions(CompressionType.Deflate)
@@ -90,7 +90,7 @@ Note: Extracting a solid rar or 7z file needs to be done in sequential order to
`ExtractAllEntries` is primarily intended for solid archives (like solid Rar) or 7Zip archives, where sequential extraction provides the best performance. For general/simple extraction with any supported archive type, use `archive.WriteToDirectory()` instead.
```C#
using (var archive = RarArchive.OpenArchive("Test.rar"))
using (var archive = RarArchive.Open("Test.rar"))
{
// Simple extraction with RarArchive; this WriteToDirectory pattern works for all archive types
archive.WriteToDirectory(@"D:\temp", new ExtractionOptions()
@@ -104,7 +104,7 @@ using (var archive = RarArchive.OpenArchive("Test.rar"))
### Iterate over all files from a Rar file using RarArchive
```C#
using (var archive = RarArchive.OpenArchive("Test.rar"))
using (var archive = RarArchive.Open("Test.rar"))
{
foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory))
{
@@ -126,7 +126,7 @@ var progress = new Progress<ProgressReport>(report =>
Console.WriteLine($"Extracting {report.EntryPath}: {report.PercentComplete}%");
});
using (var archive = RarArchive.OpenArchive("archive.rar", new ReaderOptions { Progress = progress })) // Must be solid Rar or 7Zip
using (var archive = RarArchive.Open("archive.rar", new ReaderOptions { Progress = progress })) // Must be solid Rar or 7Zip
{
archive.WriteToDirectory(@"D:\output", new ExtractionOptions()
{
@@ -140,7 +140,7 @@ using (var archive = RarArchive.OpenArchive("archive.rar", new ReaderOptions { P
```C#
using (Stream stream = File.OpenRead("Tar.tar.bz2"))
using (var reader = ReaderFactory.OpenReader(stream))
using (var reader = ReaderFactory.Open(stream))
{
while (reader.MoveToNextEntry())
{
@@ -161,7 +161,7 @@ using (var reader = ReaderFactory.OpenReader(stream))
```C#
using (Stream stream = File.OpenRead("Tar.tar.bz2"))
using (var reader = ReaderFactory.OpenReader(stream))
using (var reader = ReaderFactory.Open(stream))
{
while (reader.MoveToNextEntry())
{
@@ -180,7 +180,7 @@ using (var reader = ReaderFactory.OpenReader(stream))
```C#
using (Stream stream = File.OpenWrite("C:\\temp.tgz"))
using (var writer = WriterFactory.OpenWriter(stream, ArchiveType.Tar, new WriterOptions(CompressionType.GZip)
using (var writer = WriterFactory.Open(stream, ArchiveType.Tar, new WriterOptions(CompressionType.GZip)
{
LeaveOpenStream = true
}))
@@ -199,7 +199,7 @@ opts.ArchiveEncoding.CustomDecoder = (data, x, y) =>
{
return encoding.GetString(data);
};
var tr = SharpCompress.Archives.Zip.ZipArchive.OpenArchive("test.zip", opts);
var tr = SharpCompress.Archives.Zip.ZipArchive.Open("test.zip", opts);
foreach(var entry in tr.Entries)
{
Console.WriteLine($"{entry.Key}");
@@ -213,7 +213,7 @@ foreach(var entry in tr.Entries)
**Extract single entry asynchronously:**
```C#
using (Stream stream = File.OpenRead("archive.zip"))
using (var reader = ReaderFactory.OpenReader(stream))
using (var reader = ReaderFactory.Open(stream))
{
while (reader.MoveToNextEntry())
{
@@ -234,7 +234,7 @@ using (var reader = ReaderFactory.OpenReader(stream))
**Extract all entries asynchronously:**
```C#
using (Stream stream = File.OpenRead("archive.tar.gz"))
using (var reader = ReaderFactory.OpenReader(stream))
using (var reader = ReaderFactory.Open(stream))
{
await reader.WriteAllToDirectoryAsync(
@"D:\temp",
@@ -250,7 +250,7 @@ using (var reader = ReaderFactory.OpenReader(stream))
**Open and process entry stream asynchronously:**
```C#
using (var archive = ZipArchive.OpenArchive("archive.zip"))
using (var archive = ZipArchive.Open("archive.zip"))
{
foreach (var entry in archive.Entries.Where(e => !e.IsDirectory))
{
@@ -268,7 +268,7 @@ using (var archive = ZipArchive.OpenArchive("archive.zip"))
**Write single file asynchronously:**
```C#
using (Stream archiveStream = File.OpenWrite("output.zip"))
using (var writer = WriterFactory.OpenWriter(archiveStream, ArchiveType.Zip, CompressionType.Deflate))
using (var writer = WriterFactory.Open(archiveStream, ArchiveType.Zip, CompressionType.Deflate))
{
using (Stream fileStream = File.OpenRead("input.txt"))
{
@@ -280,7 +280,7 @@ using (var writer = WriterFactory.OpenWriter(archiveStream, ArchiveType.Zip, Com
**Write entire directory asynchronously:**
```C#
using (Stream stream = File.OpenWrite("backup.tar.gz"))
using (var writer = WriterFactory.OpenWriter(stream, ArchiveType.Tar, new WriterOptions(CompressionType.GZip)))
using (var writer = WriterFactory.Open(stream, ArchiveType.Tar, new WriterOptions(CompressionType.GZip)))
{
await writer.WriteAllAsync(
@"D:\files",
@@ -299,7 +299,7 @@ var cts = new CancellationTokenSource();
cts.CancelAfter(TimeSpan.FromMinutes(5));
using (Stream stream = File.OpenWrite("archive.zip"))
using (var writer = WriterFactory.OpenWriter(stream, ArchiveType.Zip, CompressionType.Deflate))
using (var writer = WriterFactory.Open(stream, ArchiveType.Zip, CompressionType.Deflate))
{
try
{
@@ -316,7 +316,7 @@ using (var writer = WriterFactory.OpenWriter(stream, ArchiveType.Zip, Compressio
**Extract from archive asynchronously:**
```C#
using (var archive = ZipArchive.OpenArchive("archive.zip"))
using (var archive = ZipArchive.Open("archive.zip"))
{
// Simple async extraction - works for all archive types
await archive.WriteToDirectoryAsync(

View File

@@ -198,21 +198,19 @@ public abstract class AbstractArchive<TEntry, TVolume> : IArchive, IAsyncArchive
IAsyncEnumerable<IArchiveEntry> IAsyncArchive.EntriesAsync => EntriesAsyncCast();
IAsyncEnumerable<IVolume> IAsyncArchive.VolumesAsync => VolumesAsyncCast();
private async IAsyncEnumerable<IVolume> VolumesAsyncCast()
{
await foreach (var volume in _lazyVolumesAsync)
await foreach (var volume in VolumesAsync)
{
yield return volume;
}
}
public IAsyncEnumerable<TVolume> VolumesAsync => _lazyVolumesAsync;
public IAsyncEnumerable<IVolume> VolumesAsync => VolumesAsyncCast();
public async ValueTask<IAsyncReader> ExtractAllEntriesAsync()
{
if (!await IsSolidAsync() && Type != ArchiveType.SevenZip)
if (!IsSolid && Type != ArchiveType.SevenZip)
{
throw new SharpCompressException(
"ExtractAllEntries can only be used on solid archives or 7Zip archives (which require random access)."

View File

@@ -73,25 +73,7 @@ public abstract class AbstractWritableArchive<TEntry, TVolume>
modifiedEntries.AddRange(OldEntries.Concat(newEntries));
}
private async ValueTask RebuildModifiedCollectionAsync()
{
if (pauseRebuilding)
{
return;
}
hasModifications = true;
newEntries.RemoveAll(v => removedEntries.Contains(v));
modifiedEntries.Clear();
await foreach (var entry in OldEntriesAsync)
{
modifiedEntries.Add(entry);
}
modifiedEntries.AddRange(newEntries);
}
private IEnumerable<TEntry> OldEntries => base.Entries.Where(x => !removedEntries.Contains(x));
private IAsyncEnumerable<TEntry> OldEntriesAsync =>
base.EntriesAsync.Where(x => !removedEntries.Contains(x));
public void RemoveEntry(TEntry entry)
{
@@ -102,24 +84,12 @@ public abstract class AbstractWritableArchive<TEntry, TVolume>
}
}
public async ValueTask RemoveEntryAsync(TEntry entry)
{
if (!removedEntries.Contains(entry))
{
removedEntries.Add(entry);
await RebuildModifiedCollectionAsync();
}
}
void IWritableArchive.RemoveEntry(IArchiveEntry entry) => RemoveEntry((TEntry)entry);
ValueTask IWritableAsyncArchive.RemoveEntryAsync(IArchiveEntry entry) =>
RemoveEntryAsync((TEntry)entry);
void IWritableArchiveCommon.RemoveEntry(IArchiveEntry entry) => RemoveEntry((TEntry)entry);
public TEntry AddEntry(string key, Stream source, long size = 0, DateTime? modified = null) =>
AddEntry(key, source, false, size, modified);
IArchiveEntry IWritableArchive.AddEntry(
IArchiveEntry IWritableArchiveCommon.AddEntry(
string key,
Stream source,
bool closeStream,
@@ -127,7 +97,7 @@ public abstract class AbstractWritableArchive<TEntry, TVolume>
DateTime? modified
) => AddEntry(key, source, closeStream, size, modified);
IArchiveEntry IWritableArchive.AddDirectoryEntry(string key, DateTime? modified) =>
IArchiveEntry IWritableArchiveCommon.AddDirectoryEntry(string key, DateTime? modified) =>
AddDirectoryEntry(key, modified);
public TEntry AddEntry(
@@ -170,68 +140,6 @@ public abstract class AbstractWritableArchive<TEntry, TVolume>
return false;
}
private async ValueTask<bool> DoesKeyMatchExistingAsync(
string key,
CancellationToken cancellationToken
)
{
await foreach (
var entry in EntriesAsync.WithCancellation(cancellationToken).ConfigureAwait(false)
)
{
var path = entry.Key;
if (path is null)
{
continue;
}
var p = path.Replace('/', '\\');
if (p.Length > 0 && p[0] == '\\')
{
p = p.Substring(1);
}
return string.Equals(p, key, StringComparison.OrdinalIgnoreCase);
}
return false;
}
async ValueTask<IArchiveEntry> IWritableAsyncArchive.AddEntryAsync(
string key,
Stream source,
bool closeStream,
long size,
DateTime? modified,
CancellationToken cancellationToken
) => await AddEntryAsync(key, source, closeStream, size, modified, cancellationToken);
async ValueTask<IArchiveEntry> IWritableAsyncArchive.AddDirectoryEntryAsync(
string key,
DateTime? modified,
CancellationToken cancellationToken
) => await AddDirectoryEntryAsync(key, modified, cancellationToken);
public async ValueTask<TEntry> AddEntryAsync(
string key,
Stream source,
bool closeStream,
long size = 0,
DateTime? modified = null,
CancellationToken cancellationToken = default
)
{
if (key.Length > 0 && key[0] is '/' or '\\')
{
key = key.Substring(1);
}
if (await DoesKeyMatchExistingAsync(key, cancellationToken).ConfigureAwait(false))
{
throw new ArchiveException("Cannot add entry with duplicate key: " + key);
}
var entry = CreateEntry(key, source, size, modified, closeStream);
newEntries.Add(entry);
await RebuildModifiedCollectionAsync();
return entry;
}
public TEntry AddDirectoryEntry(string key, DateTime? modified = null)
{
if (key.Length > 0 && key[0] is '/' or '\\')
@@ -248,26 +156,6 @@ public abstract class AbstractWritableArchive<TEntry, TVolume>
return entry;
}
public async ValueTask<TEntry> AddDirectoryEntryAsync(
string key,
DateTime? modified = null,
CancellationToken cancellationToken = default
)
{
if (key.Length > 0 && key[0] is '/' or '\\')
{
key = key.Substring(1);
}
if (await DoesKeyMatchExistingAsync(key, cancellationToken).ConfigureAwait(false))
{
throw new ArchiveException("Cannot add entry with duplicate key: " + key);
}
var entry = CreateDirectoryEntry(key, modified);
newEntries.Add(entry);
await RebuildModifiedCollectionAsync();
return entry;
}
public void SaveTo(Stream stream, WriterOptions options)
{
//reset streams of new entries
@@ -283,7 +171,7 @@ public abstract class AbstractWritableArchive<TEntry, TVolume>
{
//reset streams of new entries
newEntries.Cast<IWritableArchiveEntry>().ForEach(x => x.Stream.Seek(0, SeekOrigin.Begin));
await SaveToAsync(stream, options, OldEntriesAsync, newEntries, cancellationToken)
await SaveToAsync(stream, options, OldEntries, newEntries, cancellationToken)
.ConfigureAwait(false);
}
@@ -324,7 +212,7 @@ public abstract class AbstractWritableArchive<TEntry, TVolume>
protected abstract ValueTask SaveToAsync(
Stream stream,
WriterOptions options,
IAsyncEnumerable<TEntry> oldEntries,
IEnumerable<TEntry> oldEntries,
IEnumerable<TEntry> newEntries,
CancellationToken cancellationToken = default
);

View File

@@ -13,14 +13,14 @@ namespace SharpCompress.Archives;
public static class ArchiveFactory
{
public static IArchive OpenArchive(Stream stream, ReaderOptions? readerOptions = null)
public static IArchive Open(Stream stream, ReaderOptions? readerOptions = null)
{
readerOptions ??= new ReaderOptions();
stream = SharpCompressStream.Create(stream, bufferSize: readerOptions.BufferSize);
return FindFactory<IArchiveFactory>(stream).OpenArchive(stream, readerOptions);
return FindFactory<IArchiveFactory>(stream).Open(stream, readerOptions);
}
public static async ValueTask<IAsyncArchive> OpenAsyncArchive(
public static async ValueTask<IAsyncArchive> OpenAsync(
Stream stream,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
@@ -29,10 +29,10 @@ public static class ArchiveFactory
readerOptions ??= new ReaderOptions();
stream = SharpCompressStream.Create(stream, bufferSize: readerOptions.BufferSize);
var factory = await FindFactoryAsync<IArchiveFactory>(stream, cancellationToken);
return factory.OpenAsyncArchive(stream, readerOptions);
return factory.OpenAsync(stream, readerOptions);
}
public static IWritableArchive CreateArchive(ArchiveType type)
public static IWritableArchive Create(ArchiveType type)
{
var factory = Factory
.Factories.OfType<IWriteableArchiveFactory>()
@@ -40,36 +40,36 @@ public static class ArchiveFactory
if (factory != null)
{
return factory.CreateArchive();
return factory.CreateWriteableArchive();
}
throw new NotSupportedException("Cannot create Archives of type: " + type);
}
public static IArchive OpenArchive(string filePath, ReaderOptions? options = null)
public static IArchive Open(string filePath, ReaderOptions? options = null)
{
filePath.NotNullOrEmpty(nameof(filePath));
return OpenArchive(new FileInfo(filePath), options);
return Open(new FileInfo(filePath), options);
}
public static ValueTask<IAsyncArchive> OpenAsyncArchive(
public static ValueTask<IAsyncArchive> OpenAsync(
string filePath,
ReaderOptions? options = null,
CancellationToken cancellationToken = default
)
{
filePath.NotNullOrEmpty(nameof(filePath));
return OpenAsyncArchive(new FileInfo(filePath), options, cancellationToken);
return OpenAsync(new FileInfo(filePath), options, cancellationToken);
}
public static IArchive OpenArchive(FileInfo fileInfo, ReaderOptions? options = null)
public static IArchive Open(FileInfo fileInfo, ReaderOptions? options = null)
{
options ??= new ReaderOptions { LeaveStreamOpen = false };
return FindFactory<IArchiveFactory>(fileInfo).OpenArchive(fileInfo, options);
return FindFactory<IArchiveFactory>(fileInfo).Open(fileInfo, options);
}
public static async ValueTask<IAsyncArchive> OpenAsyncArchive(
public static async ValueTask<IAsyncArchive> OpenAsync(
FileInfo fileInfo,
ReaderOptions? options = null,
CancellationToken cancellationToken = default
@@ -78,13 +78,10 @@ public static class ArchiveFactory
options ??= new ReaderOptions { LeaveStreamOpen = false };
var factory = await FindFactoryAsync<IArchiveFactory>(fileInfo, cancellationToken);
return factory.OpenAsyncArchive(fileInfo, options);
return factory.OpenAsync(fileInfo, options, cancellationToken);
}
public static IArchive OpenArchive(
IEnumerable<FileInfo> fileInfos,
ReaderOptions? options = null
)
public static IArchive Open(IEnumerable<FileInfo> fileInfos, ReaderOptions? options = null)
{
fileInfos.NotNull(nameof(fileInfos));
var filesArray = fileInfos.ToArray();
@@ -96,16 +93,16 @@ public static class ArchiveFactory
var fileInfo = filesArray[0];
if (filesArray.Length == 1)
{
return OpenArchive(fileInfo, options);
return Open(fileInfo, options);
}
fileInfo.NotNull(nameof(fileInfo));
options ??= new ReaderOptions { LeaveStreamOpen = false };
return FindFactory<IMultiArchiveFactory>(fileInfo).OpenArchive(filesArray, options);
return FindFactory<IMultiArchiveFactory>(fileInfo).Open(filesArray, options);
}
public static async ValueTask<IAsyncArchive> OpenAsyncArchive(
public static async ValueTask<IAsyncArchive> OpenAsync(
IEnumerable<FileInfo> fileInfos,
ReaderOptions? options = null,
CancellationToken cancellationToken = default
@@ -121,17 +118,17 @@ public static class ArchiveFactory
var fileInfo = filesArray[0];
if (filesArray.Length == 1)
{
return await OpenAsyncArchive(fileInfo, options, cancellationToken);
return await OpenAsync(fileInfo, options, cancellationToken);
}
fileInfo.NotNull(nameof(fileInfo));
options ??= new ReaderOptions { LeaveStreamOpen = false };
var factory = await FindFactoryAsync<IMultiArchiveFactory>(fileInfo, cancellationToken);
return factory.OpenAsyncArchive(filesArray, options, cancellationToken);
return factory.OpenAsync(filesArray, options, cancellationToken);
}
public static IArchive OpenArchive(IEnumerable<Stream> streams, ReaderOptions? options = null)
public static IArchive Open(IEnumerable<Stream> streams, ReaderOptions? options = null)
{
streams.NotNull(nameof(streams));
var streamsArray = streams.ToArray();
@@ -143,16 +140,16 @@ public static class ArchiveFactory
var firstStream = streamsArray[0];
if (streamsArray.Length == 1)
{
return OpenArchive(firstStream, options);
return Open(firstStream, options);
}
firstStream.NotNull(nameof(firstStream));
options ??= new ReaderOptions();
return FindFactory<IMultiArchiveFactory>(firstStream).OpenArchive(streamsArray, options);
return FindFactory<IMultiArchiveFactory>(firstStream).Open(streamsArray, options);
}
public static async ValueTask<IAsyncArchive> OpenAsyncArchive(
public static async ValueTask<IAsyncArchive> OpenAsync(
IEnumerable<Stream> streams,
ReaderOptions? options = null,
CancellationToken cancellationToken = default
@@ -169,14 +166,14 @@ public static class ArchiveFactory
var firstStream = streamsArray[0];
if (streamsArray.Length == 1)
{
return await OpenAsyncArchive(firstStream, options, cancellationToken);
return await OpenAsync(firstStream, options, cancellationToken);
}
firstStream.NotNull(nameof(firstStream));
options ??= new ReaderOptions();
var factory = await FindFactoryAsync<IMultiArchiveFactory>(firstStream, cancellationToken);
return factory.OpenAsyncArchive(streamsArray, options);
var factory = FindFactory<IMultiArchiveFactory>(firstStream);
return factory.OpenAsync(streamsArray, options);
}
public static void WriteToDirectory(
@@ -185,29 +182,11 @@ public static class ArchiveFactory
ExtractionOptions? options = null
)
{
using var archive = OpenArchive(sourceArchive);
using var archive = Open(sourceArchive);
archive.WriteToDirectory(destinationDirectory, options);
}
public static T FindFactory<T>(string path)
where T : IFactory
{
path.NotNullOrEmpty(nameof(path));
using Stream stream = File.OpenRead(path);
return FindFactory<T>(stream);
}
public static ValueTask<T> FindFactoryAsync<T>(
string path,
CancellationToken cancellationToken = default
)
where T : IFactory
{
path.NotNullOrEmpty(nameof(path));
return FindFactoryAsync<T>(new FileInfo(path), cancellationToken);
}
public static T FindFactory<T>(FileInfo finfo)
private static T FindFactory<T>(FileInfo finfo)
where T : IFactory
{
finfo.NotNull(nameof(finfo));
@@ -215,7 +194,7 @@ public static class ArchiveFactory
return FindFactory<T>(stream);
}
public static T FindFactory<T>(Stream stream)
private static T FindFactory<T>(Stream stream)
where T : IFactory
{
stream.NotNull(nameof(stream));
@@ -363,4 +342,6 @@ public static class ArchiveFactory
}
}
}
public static IArchiveFactory AutoFactory { get; } = new AutoArchiveFactory();
}

View File

@@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
using SharpCompress.Readers;
namespace SharpCompress.Archives;
internal class AutoArchiveFactory : IArchiveFactory
{
public string Name => nameof(AutoArchiveFactory);
public ArchiveType? KnownArchiveType => null;
public IEnumerable<string> GetSupportedExtensions() => throw new NotSupportedException();
public bool IsArchive(
Stream stream,
string? password = null,
int bufferSize = ReaderOptions.DefaultBufferSize
) => throw new NotSupportedException();
public ValueTask<bool> IsArchiveAsync(
Stream stream,
string? password = null,
int bufferSize = ReaderOptions.DefaultBufferSize,
CancellationToken cancellationToken = default
) => throw new NotSupportedException();
public FileInfo? GetFilePart(int index, FileInfo part1) => throw new NotSupportedException();
public IArchive Open(Stream stream, ReaderOptions? readerOptions = null) =>
ArchiveFactory.Open(stream, readerOptions);
public IAsyncArchive OpenAsync(Stream stream, ReaderOptions? readerOptions = null) =>
(IAsyncArchive)Open(stream, readerOptions);
public IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) =>
ArchiveFactory.Open(fileInfo, readerOptions);
public IAsyncArchive OpenAsync(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncArchive)Open(fileInfo, readerOptions);
}
}

View File

@@ -16,11 +16,11 @@ namespace SharpCompress.Archives.GZip;
public partial class GZipArchive
#if NET8_0_OR_GREATER
: IWritableArchiveOpenable,
: IArchiveOpenable<IWritableArchive, IWritableAsyncArchive>,
IMultiArchiveOpenable<IWritableArchive, IWritableAsyncArchive>
#endif
{
public static IWritableAsyncArchive OpenAsyncArchive(
public static IWritableAsyncArchive OpenAsync(
string path,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
@@ -28,22 +28,19 @@ public partial class GZipArchive
{
cancellationToken.ThrowIfCancellationRequested();
path.NotNullOrEmpty(nameof(path));
return (IWritableAsyncArchive)OpenArchive(
return (IWritableAsyncArchive)Open(
new FileInfo(path),
readerOptions ?? new ReaderOptions()
);
}
public static IWritableArchive OpenArchive(string filePath, ReaderOptions? readerOptions = null)
public static IWritableArchive Open(string filePath, ReaderOptions? readerOptions = null)
{
filePath.NotNullOrEmpty(nameof(filePath));
return OpenArchive(new FileInfo(filePath), readerOptions ?? new ReaderOptions());
return Open(new FileInfo(filePath), readerOptions ?? new ReaderOptions());
}
public static IWritableArchive OpenArchive(
FileInfo fileInfo,
ReaderOptions? readerOptions = null
)
public static IWritableArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null)
{
fileInfo.NotNull(nameof(fileInfo));
return new GZipArchive(
@@ -55,7 +52,7 @@ public partial class GZipArchive
);
}
public static IWritableArchive OpenArchive(
public static IWritableArchive Open(
IEnumerable<FileInfo> fileInfos,
ReaderOptions? readerOptions = null
)
@@ -71,7 +68,7 @@ public partial class GZipArchive
);
}
public static IWritableArchive OpenArchive(
public static IWritableArchive Open(
IEnumerable<Stream> streams,
ReaderOptions? readerOptions = null
)
@@ -87,7 +84,7 @@ public partial class GZipArchive
);
}
public static IWritableArchive OpenArchive(Stream stream, ReaderOptions? readerOptions = null)
public static IWritableArchive Open(Stream stream, ReaderOptions? readerOptions = null)
{
stream.NotNull(nameof(stream));
@@ -101,49 +98,47 @@ public partial class GZipArchive
);
}
public static IWritableAsyncArchive OpenAsyncArchive(
public static IWritableAsyncArchive OpenAsync(
Stream stream,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IWritableAsyncArchive)OpenArchive(stream, readerOptions);
return (IWritableAsyncArchive)Open(stream, readerOptions);
}
public static IWritableAsyncArchive OpenAsyncArchive(
public static IWritableAsyncArchive OpenAsync(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IWritableAsyncArchive)OpenArchive(fileInfo, readerOptions);
return (IWritableAsyncArchive)Open(fileInfo, readerOptions);
}
public static IWritableAsyncArchive OpenAsyncArchive(
public static IWritableAsyncArchive OpenAsync(
IReadOnlyList<Stream> streams,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IWritableAsyncArchive)OpenArchive(streams, readerOptions);
return (IWritableAsyncArchive)Open(streams, readerOptions);
}
public static IWritableAsyncArchive OpenAsyncArchive(
public static IWritableAsyncArchive OpenAsync(
IReadOnlyList<FileInfo> fileInfos,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IWritableAsyncArchive)OpenArchive(fileInfos, readerOptions);
return (IWritableAsyncArchive)Open(fileInfos, readerOptions);
}
public static IWritableArchive CreateArchive() => new GZipArchive();
public static IWritableAsyncArchive CreateAsyncArchive() => new GZipArchive();
public static GZipArchive Create() => new();
public static bool IsGZipFile(string filePath) => IsGZipFile(new FileInfo(filePath));

View File

@@ -95,7 +95,7 @@ public partial class GZipArchive : AbstractWritableArchive<GZipArchiveEntry, GZi
protected override async ValueTask SaveToAsync(
Stream stream,
WriterOptions options,
IAsyncEnumerable<GZipArchiveEntry> oldEntries,
IEnumerable<GZipArchiveEntry> oldEntries,
IEnumerable<GZipArchiveEntry> newEntries,
CancellationToken cancellationToken = default
)
@@ -105,23 +105,7 @@ public partial class GZipArchive : AbstractWritableArchive<GZipArchiveEntry, GZi
throw new InvalidFormatException("Only one entry is allowed in a GZip Archive");
}
using var writer = new GZipWriter(stream, new GZipWriterOptions(options));
await foreach (
var entry in oldEntries.WithCancellation(cancellationToken).ConfigureAwait(false)
)
{
if (!entry.IsDirectory)
{
using var entryStream = entry.OpenEntryStream();
await writer
.WriteAsync(
entry.Key.NotNull("Entry Key is null"),
entryStream,
cancellationToken
)
.ConfigureAwait(false);
}
}
foreach (var entry in newEntries.Where(x => !x.IsDirectory))
foreach (var entry in oldEntries.Concat(newEntries).Where(x => !x.IsDirectory))
{
using var entryStream = entry.OpenEntryStream();
await writer
@@ -154,13 +138,13 @@ public partial class GZipArchive : AbstractWritableArchive<GZipArchiveEntry, GZi
{
var stream = Volumes.Single().Stream;
stream.Position = 0;
return GZipReader.OpenReader(stream);
return GZipReader.Open(stream);
}
protected override ValueTask<IAsyncReader> CreateReaderForSolidExtractionAsync()
{
var stream = Volumes.Single().Stream;
stream.Position = 0;
return new((IAsyncReader)GZipReader.OpenReader(stream));
return new((IAsyncReader)GZipReader.Open(stream));
}
}

View File

@@ -25,21 +25,21 @@ public interface IArchiveFactory : IFactory
/// </summary>
/// <param name="stream">An open, readable and seekable stream.</param>
/// <param name="readerOptions">reading options.</param>
IArchive OpenArchive(Stream stream, ReaderOptions? readerOptions = null);
IArchive Open(Stream stream, ReaderOptions? readerOptions = null);
/// <summary>
/// Opens an Archive for random access asynchronously.
/// </summary>
/// <param name="stream">An open, readable and seekable stream.</param>
/// <param name="readerOptions">reading options.</param>
IAsyncArchive OpenAsyncArchive(Stream stream, ReaderOptions? readerOptions = null);
IAsyncArchive OpenAsync(Stream stream, ReaderOptions? readerOptions = null);
/// <summary>
/// Constructor with a FileInfo object to an existing file.
/// </summary>
/// <param name="fileInfo">the file to open.</param>
/// <param name="readerOptions">reading options.</param>
IArchive OpenArchive(FileInfo fileInfo, ReaderOptions? readerOptions = null);
IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null);
/// <summary>
/// Opens an Archive from a FileInfo object asynchronously.
@@ -47,5 +47,9 @@ 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);
IAsyncArchive OpenAsync(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
);
}

View File

@@ -9,32 +9,28 @@ public interface IArchiveOpenable<TSync, TASync>
where TSync : IArchive
where TASync : IAsyncArchive
{
public static abstract TSync OpenArchive(string filePath, ReaderOptions? readerOptions = null);
public static abstract TSync Open(string filePath, ReaderOptions? readerOptions = null);
public static abstract TSync OpenArchive(
FileInfo fileInfo,
ReaderOptions? readerOptions = null
);
public static abstract TSync Open(FileInfo fileInfo, ReaderOptions? readerOptions = null);
public static abstract TSync OpenArchive(Stream stream, ReaderOptions? readerOptions = null);
public static abstract TSync Open(Stream stream, ReaderOptions? readerOptions = null);
public static abstract TASync OpenAsyncArchive(
public static abstract TASync OpenAsync(
string path,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
);
public static abstract TASync OpenAsyncArchive(
public static abstract TASync OpenAsync(
Stream stream,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
);
public static abstract TASync OpenAsyncArchive(
public static abstract TASync OpenAsync(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
);
}
#endif

View File

@@ -26,24 +26,21 @@ public interface IMultiArchiveFactory : IFactory
/// </summary>
/// <param name="streams"></param>
/// <param name="readerOptions">reading options.</param>
IArchive OpenArchive(IReadOnlyList<Stream> streams, ReaderOptions? readerOptions = null);
IArchive Open(IReadOnlyList<Stream> streams, ReaderOptions? readerOptions = null);
/// <summary>
/// Opens a multi-part archive from streams asynchronously.
/// </summary>
/// <param name="streams"></param>
/// <param name="readerOptions">reading options.</param>
IAsyncArchive OpenAsyncArchive(
IReadOnlyList<Stream> streams,
ReaderOptions? readerOptions = null
);
IAsyncArchive OpenAsync(IReadOnlyList<Stream> streams, ReaderOptions? readerOptions = null);
/// <summary>
/// Constructor with IEnumerable Stream objects, multi and split support.
/// </summary>
/// <param name="fileInfos"></param>
/// <param name="readerOptions">reading options.</param>
IArchive OpenArchive(IReadOnlyList<FileInfo> fileInfos, ReaderOptions? readerOptions = null);
IArchive Open(IReadOnlyList<FileInfo> fileInfos, ReaderOptions? readerOptions = null);
/// <summary>
/// Opens a multi-part archive from files asynchronously.
@@ -51,7 +48,7 @@ public interface IMultiArchiveFactory : IFactory
/// <param name="fileInfos"></param>
/// <param name="readerOptions">reading options.</param>
/// <param name="cancellationToken">Cancellation token.</param>
IAsyncArchive OpenAsyncArchive(
IAsyncArchive OpenAsync(
IReadOnlyList<FileInfo> fileInfos,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default

View File

@@ -10,23 +10,23 @@ public interface IMultiArchiveOpenable<TSync, TASync>
where TSync : IArchive
where TASync : IAsyncArchive
{
public static abstract TSync OpenArchive(
public static abstract TSync Open(
IEnumerable<FileInfo> fileInfos,
ReaderOptions? readerOptions = null
);
public static abstract TSync OpenArchive(
public static abstract TSync Open(
IEnumerable<Stream> streams,
ReaderOptions? readerOptions = null
);
public static abstract TASync OpenAsyncArchive(
public static abstract TASync OpenAsync(
IReadOnlyList<Stream> streams,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
);
public static abstract TASync OpenAsyncArchive(
public static abstract TASync OpenAsync(
IReadOnlyList<FileInfo> fileInfos,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default

View File

@@ -13,10 +13,12 @@ public interface IWritableArchiveCommon
/// </summary>
/// <returns>IDisposeable to resume entry rebuilding</returns>
IDisposable PauseEntryRebuilding();
}
public interface IWritableArchive : IArchive, IWritableArchiveCommon
{
/// <summary>
/// Removes the specified entry from the archive.
/// </summary>
void RemoveEntry(IArchiveEntry entry);
IArchiveEntry AddEntry(
string key,
Stream source,
@@ -26,16 +28,14 @@ public interface IWritableArchive : IArchive, IWritableArchiveCommon
);
IArchiveEntry AddDirectoryEntry(string key, DateTime? modified = null);
}
public interface IWritableArchive : IArchive, IWritableArchiveCommon
{
/// <summary>
/// Saves the archive to the specified stream using the given writer options.
/// </summary>
void SaveTo(Stream stream, WriterOptions options);
/// <summary>
/// Removes the specified entry from the archive.
/// </summary>
void RemoveEntry(IArchiveEntry entry);
}
public interface IWritableAsyncArchive : IAsyncArchive, IWritableArchiveCommon
@@ -48,30 +48,4 @@ public interface IWritableAsyncArchive : IAsyncArchive, IWritableArchiveCommon
WriterOptions options,
CancellationToken cancellationToken = default
);
/// <summary>
/// Asynchronously adds an entry to the archive with the specified key, source stream, and options.
/// </summary>
ValueTask<IArchiveEntry> AddEntryAsync(
string key,
Stream source,
bool closeStream,
long size = 0,
DateTime? modified = null,
CancellationToken cancellationToken = default
);
/// <summary>
/// Asynchronously adds a directory entry to the archive with the specified key and modification time.
/// </summary>
ValueTask<IArchiveEntry> AddDirectoryEntryAsync(
string key,
DateTime? modified = null,
CancellationToken cancellationToken = default
);
/// <summary>
/// Removes the specified entry from the archive.
/// </summary>
ValueTask RemoveEntryAsync(IArchiveEntry entry);
}

View File

@@ -0,0 +1,52 @@
using System;
using System.IO;
namespace SharpCompress.Archives;
public static class IWritableArchiveCommonExtensions
{
extension(IWritableArchiveCommon writableArchive)
{
public void AddAllFromDirectory(
string filePath,
string searchPattern = "*.*",
SearchOption searchOption = SearchOption.AllDirectories
)
{
using (writableArchive.PauseEntryRebuilding())
{
foreach (
var path in Directory.EnumerateFiles(filePath, searchPattern, searchOption)
)
{
var fileInfo = new FileInfo(path);
writableArchive.AddEntry(
path.Substring(filePath.Length),
fileInfo.OpenRead(),
true,
fileInfo.Length,
fileInfo.LastWriteTime
);
}
}
}
public IArchiveEntry AddEntry(string key, string file) =>
writableArchive.AddEntry(key, new FileInfo(file));
public IArchiveEntry AddEntry(string key, FileInfo fileInfo)
{
if (!fileInfo.Exists)
{
throw new ArgumentException("FileInfo does not exist.");
}
return writableArchive.AddEntry(
key,
fileInfo.OpenRead(),
true,
fileInfo.Length,
fileInfo.LastWriteTime
);
}
}
}

View File

@@ -1,4 +1,3 @@
using System;
using System.IO;
using SharpCompress.Common;
using SharpCompress.Writers;
@@ -9,55 +8,6 @@ public static class IWritableArchiveExtensions
{
extension(IWritableArchive writableArchive)
{
public void AddAllFromDirectory(
string filePath,
string searchPattern = "*.*",
SearchOption searchOption = SearchOption.AllDirectories
)
{
using (writableArchive.PauseEntryRebuilding())
{
foreach (
var path in Directory.EnumerateFiles(filePath, searchPattern, searchOption)
)
{
var fileInfo = new FileInfo(path);
writableArchive.AddEntry(
path.Substring(filePath.Length),
fileInfo.OpenRead(),
true,
fileInfo.Length,
fileInfo.LastWriteTime
);
}
}
}
public IArchiveEntry AddEntry(string key, string file) =>
writableArchive.AddEntry(key, new FileInfo(file));
public IArchiveEntry AddEntry(
string key,
Stream source,
long size = 0,
DateTime? modified = null
) => writableArchive.AddEntry(key, source, false, size, modified);
public IArchiveEntry AddEntry(string key, FileInfo fileInfo)
{
if (!fileInfo.Exists)
{
throw new ArgumentException("FileInfo does not exist.");
}
return writableArchive.AddEntry(
key,
fileInfo.OpenRead(),
true,
fileInfo.Length,
fileInfo.LastWriteTime
);
}
public void SaveTo(string filePath, WriterOptions? options = null) =>
writableArchive.SaveTo(new FileInfo(filePath), options ?? new(CompressionType.Deflate));

View File

@@ -1,10 +0,0 @@
#if NET8_0_OR_GREATER
namespace SharpCompress.Archives;
public interface IWritableArchiveOpenable
: IArchiveOpenable<IWritableArchive, IWritableAsyncArchive>
{
public static abstract IWritableArchive CreateArchive();
public static abstract IWritableAsyncArchive CreateAsyncArchive();
}
#endif

View File

@@ -1,4 +1,3 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
@@ -11,55 +10,6 @@ public static class IWritableAsyncArchiveExtensions
{
extension(IWritableAsyncArchive writableArchive)
{
public async ValueTask AddAllFromDirectoryAsync(
string filePath,
string searchPattern = "*.*",
SearchOption searchOption = SearchOption.AllDirectories
)
{
using (writableArchive.PauseEntryRebuilding())
{
foreach (
var path in Directory.EnumerateFiles(filePath, searchPattern, searchOption)
)
{
var fileInfo = new FileInfo(path);
await writableArchive.AddEntryAsync(
path.Substring(filePath.Length),
fileInfo.OpenRead(),
true,
fileInfo.Length,
fileInfo.LastWriteTime
);
}
}
}
public ValueTask<IArchiveEntry> AddEntryAsync(string key, string file) =>
writableArchive.AddEntryAsync(key, new FileInfo(file));
public ValueTask<IArchiveEntry> AddEntryAsync(
string key,
Stream source,
long size = 0,
DateTime? modified = null
) => writableArchive.AddEntryAsync(key, source, false, size, modified);
public ValueTask<IArchiveEntry> AddEntryAsync(string key, FileInfo fileInfo)
{
if (!fileInfo.Exists)
{
throw new ArgumentException("FileInfo does not exist.");
}
return writableArchive.AddEntryAsync(
key,
fileInfo.OpenRead(),
true,
fileInfo.Length,
fileInfo.LastWriteTime
);
}
public ValueTask SaveToAsync(
string filePath,
WriterOptions? options = null,

View File

@@ -16,5 +16,5 @@ public interface IWriteableArchiveFactory : Factories.IFactory
/// Creates a new, empty archive, ready to be written.
/// </summary>
/// <returns></returns>
IWritableArchive CreateArchive();
IWritableArchive CreateWriteableArchive();
}

View File

@@ -36,7 +36,4 @@ internal class FileInfoRarArchiveVolume : RarVolume
new FileInfoRarFilePart(this, ReaderOptions.Password, markHeader, fileHeader, FileInfo);
internal override IEnumerable<RarFilePart> ReadFileParts() => FileParts;
internal override IAsyncEnumerable<RarFilePart> ReadFilePartsAsync() =>
FileParts.ToAsyncEnumerable();
}

View File

@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
using SharpCompress.Common.Rar;
using SharpCompress.Common.Rar.Headers;
@@ -20,7 +19,7 @@ public partial class RarArchive
IMultiArchiveOpenable<IRarArchive, IRarAsyncArchive>
#endif
{
public static IRarAsyncArchive OpenAsyncArchive(
public static IRarAsyncArchive OpenAsync(
string path,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
@@ -28,10 +27,10 @@ public partial class RarArchive
{
cancellationToken.ThrowIfCancellationRequested();
path.NotNullOrEmpty(nameof(path));
return (IRarAsyncArchive)OpenArchive(new FileInfo(path), readerOptions);
return (IRarAsyncArchive)Open(new FileInfo(path), readerOptions);
}
public static IRarArchive OpenArchive(string filePath, ReaderOptions? options = null)
public static IRarArchive Open(string filePath, ReaderOptions? options = null)
{
filePath.NotNullOrEmpty(nameof(filePath));
var fileInfo = new FileInfo(filePath);
@@ -44,7 +43,7 @@ public partial class RarArchive
);
}
public static IRarArchive OpenArchive(FileInfo fileInfo, ReaderOptions? options = null)
public static IRarArchive Open(FileInfo fileInfo, ReaderOptions? options = null)
{
fileInfo.NotNull(nameof(fileInfo));
return new RarArchive(
@@ -56,7 +55,7 @@ public partial class RarArchive
);
}
public static IRarArchive OpenArchive(Stream stream, ReaderOptions? options = null)
public static IRarArchive Open(Stream stream, ReaderOptions? options = null)
{
stream.NotNull(nameof(stream));
@@ -68,7 +67,7 @@ public partial class RarArchive
return new RarArchive(new SourceStream(stream, _ => null, options ?? new ReaderOptions()));
}
public static IRarArchive OpenArchive(
public static IRarArchive Open(
IEnumerable<FileInfo> fileInfos,
ReaderOptions? readerOptions = null
)
@@ -84,10 +83,7 @@ public partial class RarArchive
);
}
public static IRarArchive OpenArchive(
IEnumerable<Stream> streams,
ReaderOptions? readerOptions = null
)
public static IRarArchive Open(IEnumerable<Stream> streams, ReaderOptions? readerOptions = null)
{
streams.NotNull(nameof(streams));
var strms = streams.ToArray();
@@ -100,44 +96,44 @@ public partial class RarArchive
);
}
public static IRarAsyncArchive OpenAsyncArchive(
public static IRarAsyncArchive OpenAsync(
Stream stream,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IRarAsyncArchive)OpenArchive(stream, readerOptions);
return (IRarAsyncArchive)Open(stream, readerOptions);
}
public static IRarAsyncArchive OpenAsyncArchive(
public static IRarAsyncArchive OpenAsync(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IRarAsyncArchive)OpenArchive(fileInfo, readerOptions);
return (IRarAsyncArchive)Open(fileInfo, readerOptions);
}
public static IRarAsyncArchive OpenAsyncArchive(
public static IRarAsyncArchive OpenAsync(
IReadOnlyList<Stream> streams,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IRarAsyncArchive)OpenArchive(streams, readerOptions);
return (IRarAsyncArchive)Open(streams, readerOptions);
}
public static IRarAsyncArchive OpenAsyncArchive(
public static IRarAsyncArchive OpenAsync(
IReadOnlyList<FileInfo> fileInfos,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IRarAsyncArchive)OpenArchive(fileInfos, readerOptions);
return (IRarAsyncArchive)Open(fileInfos, readerOptions);
}
public static bool IsRarFile(string filePath) => IsRarFile(new FileInfo(filePath));
@@ -164,24 +160,4 @@ public partial class RarArchive
return false;
}
}
public static async ValueTask<bool> IsRarFileAsync(
Stream stream,
ReaderOptions? options = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
try
{
await MarkHeader
.ReadAsync(stream, true, false, cancellationToken)
.ConfigureAwait(false);
return true;
}
catch
{
return false;
}
}
}

View File

@@ -24,7 +24,7 @@ public interface IRarArchive : IArchive, IRarArchiveCommon { }
public interface IRarAsyncArchive : IAsyncArchive, IRarArchiveCommon { }
public partial class RarArchive : AbstractArchive<RarArchiveEntry, RarVolume>, IRarArchive, IRarAsyncArchive
public partial class RarArchive : AbstractArchive<RarArchiveEntry, RarVolume>, IRarArchive
{
private bool _disposed;
internal Lazy<IRarUnpack> UnpackV2017 { get; } =
@@ -64,8 +64,6 @@ public partial class RarArchive : AbstractArchive<RarArchiveEntry, RarVolume>, I
protected override IEnumerable<RarArchiveEntry> LoadEntries(IEnumerable<RarVolume> volumes) =>
RarArchiveEntryFactory.GetEntries(this, volumes, ReaderOptions);
protected override IAsyncEnumerable<RarArchiveEntry> LoadEntriesAsync(IAsyncEnumerable<RarVolume> volumes) =>
RarArchiveEntryFactory.GetEntriesAsync(this, volumes, ReaderOptions);
protected override IEnumerable<RarVolume> LoadVolumes(SourceStream sourceStream)
{
@@ -88,25 +86,13 @@ public partial class RarArchive : AbstractArchive<RarArchiveEntry, RarVolume>, I
return new StreamRarArchiveVolume(sourceStream, ReaderOptions, i++).AsEnumerable();
}
protected override IReader CreateReaderForSolidExtraction() =>
CreateReaderForSolidExtractionInternal();
protected override async ValueTask<IAsyncReader> CreateReaderForSolidExtractionAsync()
{
if (await this.IsMultipartVolumeAsync())
{
var streams = await VolumesAsync.Select(volume =>
{
volume.Stream.Position = 0;
return volume.Stream;
}).ToListAsync();
return (RarReader)RarReader.OpenReader(streams, ReaderOptions);
}
protected override ValueTask<IAsyncReader> CreateReaderForSolidExtractionAsync() =>
new(CreateReaderForSolidExtractionInternal());
var stream = (await VolumesAsync.FirstAsync()).Stream;
stream.Position = 0;
return (RarReader)RarReader.OpenReader(stream, ReaderOptions);
}
protected override IReader CreateReaderForSolidExtraction()
private RarReader CreateReaderForSolidExtractionInternal()
{
if (this.IsMultipartVolume())
{
@@ -115,22 +101,18 @@ public partial class RarArchive : AbstractArchive<RarArchiveEntry, RarVolume>, I
volume.Stream.Position = 0;
return volume.Stream;
});
return (RarReader)RarReader.OpenReader(streams, ReaderOptions);
return (RarReader)RarReader.Open(streams, ReaderOptions);
}
var stream = Volumes.First().Stream;
stream.Position = 0;
return (RarReader)RarReader.OpenReader(stream, ReaderOptions);
return (RarReader)RarReader.Open(stream, ReaderOptions);
}
public override bool IsSolid => Volumes.First().IsSolidArchive;
public override async ValueTask<bool> IsSolidAsync() =>
await (await VolumesAsync.CastAsync<RarVolume>().FirstAsync()).IsSolidArchiveAsync();
public override bool IsEncrypted => Entries.First(x => !x.IsDirectory).IsEncrypted;
public virtual int MinVersion => Volumes.First().MinVersion;
public virtual int MaxVersion => Volumes.First().MaxVersion;
}

View File

@@ -102,9 +102,7 @@ public class RarArchiveEntry : RarEntry, IArchiveEntry
stream = new RarStream(
archive.UnpackV1.Value,
FileHeader,
await MultiVolumeReadOnlyAsyncStream.Create(
Parts.ToAsyncEnumerable().CastAsync<RarFilePart>()
)
new MultiVolumeReadOnlyStream(Parts.Cast<RarFilePart>())
);
}
else
@@ -112,9 +110,7 @@ public class RarArchiveEntry : RarEntry, IArchiveEntry
stream = new RarStream(
archive.UnpackV2017.Value,
FileHeader,
await MultiVolumeReadOnlyAsyncStream.Create(
Parts.ToAsyncEnumerable().CastAsync<RarFilePart>()
)
new MultiVolumeReadOnlyStream(Parts.Cast<RarFilePart>())
);
}

View File

@@ -17,17 +17,6 @@ internal static class RarArchiveEntryFactory
}
}
private static async IAsyncEnumerable<RarFilePart> GetFilePartsAsync(IAsyncEnumerable<RarVolume> parts)
{
await foreach (var rarPart in parts)
{
await foreach (var fp in rarPart.ReadFilePartsAsync())
{
yield return fp;
}
}
}
private static IEnumerable<IEnumerable<RarFilePart>> GetMatchedFileParts(
IEnumerable<RarVolume> parts
)
@@ -49,27 +38,6 @@ internal static class RarArchiveEntryFactory
}
}
private static async IAsyncEnumerable<IEnumerable<RarFilePart>> GetMatchedFilePartsAsync(
IAsyncEnumerable<RarVolume> parts
)
{
var groupedParts = new List<RarFilePart>();
await foreach (var fp in GetFilePartsAsync(parts))
{
groupedParts.Add(fp);
if (!fp.FileHeader.IsSplitAfter)
{
yield return groupedParts;
groupedParts = new List<RarFilePart>();
}
}
if (groupedParts.Count > 0)
{
yield return groupedParts;
}
}
internal static IEnumerable<RarArchiveEntry> GetEntries(
RarArchive archive,
IEnumerable<RarVolume> rarParts,
@@ -81,16 +49,4 @@ internal static class RarArchiveEntryFactory
yield return new RarArchiveEntry(archive, groupedParts, readerOptions);
}
}
internal static async IAsyncEnumerable<RarArchiveEntry> GetEntriesAsync(
RarArchive archive,
IAsyncEnumerable<RarVolume> rarParts,
ReaderOptions readerOptions
)
{
await foreach (var groupedParts in GetMatchedFilePartsAsync(rarParts))
{
yield return new RarArchiveEntry(archive, groupedParts, readerOptions);
}
}
}

View File

@@ -14,9 +14,6 @@ internal class StreamRarArchiveVolume : RarVolume
internal override IEnumerable<RarFilePart> ReadFileParts() => GetVolumeFileParts();
internal override IAsyncEnumerable<RarFilePart> ReadFilePartsAsync() =>
GetVolumeFilePartsAsync();
internal override RarFilePart CreateFilePart(MarkHeader markHeader, FileHeader fileHeader) =>
new SeekableFilePart(markHeader, fileHeader, Index, Stream, ReaderOptions.Password);
}

View File

@@ -1,10 +1,12 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
using SharpCompress.Common.SevenZip;
using SharpCompress.Compressors.LZMA.Utilites;
using SharpCompress.IO;
using SharpCompress.Readers;
@@ -16,7 +18,7 @@ public partial class SevenZipArchive
IMultiArchiveOpenable<IArchive, IAsyncArchive>
#endif
{
public static IAsyncArchive OpenAsyncArchive(
public static IAsyncArchive OpenAsync(
string path,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
@@ -24,16 +26,16 @@ public partial class SevenZipArchive
{
cancellationToken.ThrowIfCancellationRequested();
path.NotNullOrEmpty("path");
return (IAsyncArchive)OpenArchive(new FileInfo(path), readerOptions ?? new ReaderOptions());
return (IAsyncArchive)Open(new FileInfo(path), readerOptions ?? new ReaderOptions());
}
public static IArchive OpenArchive(string filePath, ReaderOptions? readerOptions = null)
public static IArchive Open(string filePath, ReaderOptions? readerOptions = null)
{
filePath.NotNullOrEmpty("filePath");
return OpenArchive(new FileInfo(filePath), readerOptions ?? new ReaderOptions());
return Open(new FileInfo(filePath), readerOptions ?? new ReaderOptions());
}
public static IArchive OpenArchive(FileInfo fileInfo, ReaderOptions? readerOptions = null)
public static IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null)
{
fileInfo.NotNull("fileInfo");
return new SevenZipArchive(
@@ -45,7 +47,7 @@ public partial class SevenZipArchive
);
}
public static IArchive OpenArchive(
public static IArchive Open(
IEnumerable<FileInfo> fileInfos,
ReaderOptions? readerOptions = null
)
@@ -61,10 +63,7 @@ public partial class SevenZipArchive
);
}
public static IArchive OpenArchive(
IEnumerable<Stream> streams,
ReaderOptions? readerOptions = null
)
public static IArchive Open(IEnumerable<Stream> streams, ReaderOptions? readerOptions = null)
{
streams.NotNull(nameof(streams));
var strms = streams.ToArray();
@@ -77,7 +76,7 @@ public partial class SevenZipArchive
);
}
public static IArchive OpenArchive(Stream stream, ReaderOptions? readerOptions = null)
public static IArchive Open(Stream stream, ReaderOptions? readerOptions = null)
{
stream.NotNull("stream");
@@ -91,44 +90,44 @@ public partial class SevenZipArchive
);
}
public static IAsyncArchive OpenAsyncArchive(
public static IAsyncArchive OpenAsync(
Stream stream,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncArchive)OpenArchive(stream, readerOptions);
return (IAsyncArchive)Open(stream, readerOptions);
}
public static IAsyncArchive OpenAsyncArchive(
public static IAsyncArchive OpenAsync(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncArchive)OpenArchive(fileInfo, readerOptions);
return (IAsyncArchive)Open(fileInfo, readerOptions);
}
public static IAsyncArchive OpenAsyncArchive(
public static IAsyncArchive OpenAsync(
IReadOnlyList<Stream> streams,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncArchive)OpenArchive(streams, readerOptions);
return (IAsyncArchive)Open(streams, readerOptions);
}
public static IAsyncArchive OpenAsyncArchive(
public static IAsyncArchive OpenAsync(
IReadOnlyList<FileInfo> fileInfos,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncArchive)OpenArchive(fileInfos, readerOptions);
return (IAsyncArchive)Open(fileInfos, readerOptions);
}
public static bool IsSevenZipFile(string filePath) => IsSevenZipFile(new FileInfo(filePath));
@@ -155,56 +154,13 @@ public partial class SevenZipArchive
}
}
public static async ValueTask<bool> IsSevenZipFileAsync(
Stream stream,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
try
{
return await SignatureMatchAsync(stream, cancellationToken);
}
catch
{
return false;
}
}
private static ReadOnlySpan<byte> Signature => [(byte)'7', (byte)'z', 0xBC, 0xAF, 0x27, 0x1C];
private static ReadOnlySpan<byte> Signature =>
new byte[] { (byte)'7', (byte)'z', 0xBC, 0xAF, 0x27, 0x1C };
private static bool SignatureMatch(Stream stream)
{
var buffer = ArrayPool<byte>.Shared.Rent(6);
try
{
stream.ReadExact(buffer, 0, 6);
return buffer.AsSpan().Slice(0, 6).SequenceEqual(Signature);
}
finally
{
ArrayPool<byte>.Shared.Return(buffer);
}
}
private static async ValueTask<bool> SignatureMatchAsync(
Stream stream,
CancellationToken cancellationToken
)
{
var buffer = ArrayPool<byte>.Shared.Rent(6);
try
{
if (!await stream.ReadFullyAsync(buffer, 0, 6, cancellationToken).ConfigureAwait(false))
{
return false;
}
return buffer.AsSpan().Slice(0, 6).SequenceEqual(Signature);
}
finally
{
ArrayPool<byte>.Shared.Return(buffer);
}
var reader = new BinaryReader(stream);
ReadOnlySpan<byte> signatureBytes = reader.ReadBytes(6);
return signatureBytes.SequenceEqual(Signature);
}
}

View File

@@ -32,56 +32,11 @@ public partial class SevenZipArchive : AbstractArchive<SevenZipArchiveEntry, Sev
IEnumerable<SevenZipVolume> volumes
)
{
foreach (var volume in volumes)
{
LoadFactory(volume.Stream);
if (_database is null)
{
yield break;
}
var entries = new SevenZipArchiveEntry[_database._files.Count];
for (var i = 0; i < _database._files.Count; i++)
{
var file = _database._files[i];
entries[i] = new SevenZipArchiveEntry(
this,
new SevenZipFilePart(
volume.Stream,
_database,
i,
file,
ReaderOptions.ArchiveEncoding
)
);
}
foreach (
var group in entries.Where(x => !x.IsDirectory).GroupBy(x => x.FilePart.Folder)
)
{
var isSolid = false;
foreach (var entry in group)
{
entry.IsSolid = isSolid;
isSolid = true;
}
}
foreach (var entry in entries)
{
yield return entry;
}
}
}
protected override async IAsyncEnumerable<SevenZipArchiveEntry> LoadEntriesAsync(
IAsyncEnumerable<SevenZipVolume> volumes
)
{
var stream = (await volumes.SingleAsync()).Stream;
await LoadFactoryAsync(stream);
var stream = volumes.Single().Stream;
LoadFactory(stream);
if (_database is null)
{
yield break;
return Enumerable.Empty<SevenZipArchiveEntry>();
}
var entries = new SevenZipArchiveEntry[_database._files.Count];
for (var i = 0; i < _database._files.Count; i++)
@@ -102,10 +57,7 @@ public partial class SevenZipArchive : AbstractArchive<SevenZipArchiveEntry, Sev
}
}
foreach (var entry in entries)
{
yield return entry;
}
return entries;
}
private void LoadFactory(Stream stream)
@@ -119,27 +71,6 @@ public partial class SevenZipArchive : AbstractArchive<SevenZipArchiveEntry, Sev
}
}
private async Task LoadFactoryAsync(
Stream stream,
CancellationToken cancellationToken = default
)
{
if (_database is null)
{
stream.Position = 0;
var reader = new ArchiveReader();
await reader.OpenAsync(
stream,
lookForHeader: ReaderOptions.LookForHeader,
cancellationToken
);
_database = await reader.ReadDatabaseAsync(
new PasswordProvider(ReaderOptions.Password),
cancellationToken
);
}
}
protected override IReader CreateReaderForSolidExtraction() =>
new SevenZipReader(ReaderOptions, this);

View File

@@ -2,32 +2,31 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
using SharpCompress.Common.Tar;
using SharpCompress.Common.Tar.Headers;
using SharpCompress.IO;
using SharpCompress.Readers;
using SharpCompress.Writers;
using SharpCompress.Writers.Tar;
namespace SharpCompress.Archives.Tar;
public partial class TarArchive
#if NET8_0_OR_GREATER
: IWritableArchiveOpenable,
: IArchiveOpenable<IWritableArchive, IWritableAsyncArchive>,
IMultiArchiveOpenable<IWritableArchive, IWritableAsyncArchive>
#endif
{
public static IWritableArchive OpenArchive(string filePath, ReaderOptions? readerOptions = null)
public static IWritableArchive Open(string filePath, ReaderOptions? readerOptions = null)
{
filePath.NotNullOrEmpty(nameof(filePath));
return OpenArchive(new FileInfo(filePath), readerOptions ?? new ReaderOptions());
return Open(new FileInfo(filePath), readerOptions ?? new ReaderOptions());
}
public static IWritableArchive OpenArchive(
FileInfo fileInfo,
ReaderOptions? readerOptions = null
)
public static IWritableArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null)
{
fileInfo.NotNull(nameof(fileInfo));
return new TarArchive(
@@ -39,7 +38,7 @@ public partial class TarArchive
);
}
public static IWritableArchive OpenArchive(
public static IWritableArchive Open(
IEnumerable<FileInfo> fileInfos,
ReaderOptions? readerOptions = null
)
@@ -55,7 +54,7 @@ public partial class TarArchive
);
}
public static IWritableArchive OpenArchive(
public static IWritableArchive Open(
IEnumerable<Stream> streams,
ReaderOptions? readerOptions = null
)
@@ -71,7 +70,7 @@ public partial class TarArchive
);
}
public static IWritableArchive OpenArchive(Stream stream, ReaderOptions? readerOptions = null)
public static IWritableArchive Open(Stream stream, ReaderOptions? readerOptions = null)
{
stream.NotNull(nameof(stream));
@@ -85,54 +84,54 @@ public partial class TarArchive
);
}
public static IWritableAsyncArchive OpenAsyncArchive(
public static IWritableAsyncArchive OpenAsync(
Stream stream,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IWritableAsyncArchive)OpenArchive(stream, readerOptions);
return (IWritableAsyncArchive)Open(stream, readerOptions);
}
public static IWritableAsyncArchive OpenAsyncArchive(
public static IWritableAsyncArchive OpenAsync(
string path,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IWritableAsyncArchive)OpenArchive(new FileInfo(path), readerOptions);
return (IWritableAsyncArchive)Open(new FileInfo(path), readerOptions);
}
public static IWritableAsyncArchive OpenAsyncArchive(
public static IWritableAsyncArchive OpenAsync(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IWritableAsyncArchive)OpenArchive(fileInfo, readerOptions);
return (IWritableAsyncArchive)Open(fileInfo, readerOptions);
}
public static IWritableAsyncArchive OpenAsyncArchive(
public static IWritableAsyncArchive OpenAsync(
IReadOnlyList<Stream> streams,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IWritableAsyncArchive)OpenArchive(streams, readerOptions);
return (IWritableAsyncArchive)Open(streams, readerOptions);
}
public static IWritableAsyncArchive OpenAsyncArchive(
public static IWritableAsyncArchive OpenAsync(
IReadOnlyList<FileInfo> fileInfos,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IWritableAsyncArchive)OpenArchive(fileInfos, readerOptions);
return (IWritableAsyncArchive)Open(fileInfos, readerOptions);
}
public static bool IsTarFile(string filePath) => IsTarFile(new FileInfo(filePath));
@@ -152,47 +151,16 @@ public partial class TarArchive
try
{
var tarHeader = new TarHeader(new ArchiveEncoding());
var reader = new BinaryReader(stream, Encoding.UTF8, false);
var readSucceeded = tarHeader.Read(reader);
var readSucceeded = tarHeader.Read(new BinaryReader(stream));
var isEmptyArchive =
tarHeader.Name?.Length == 0
&& tarHeader.Size == 0
&& Enum.IsDefined(typeof(EntryType), tarHeader.EntryType);
return readSucceeded || isEmptyArchive;
}
catch (Exception)
{
// Catch all exceptions during tar header reading to determine if this is a valid tar file
// Invalid tar files or corrupted streams will throw various exceptions
return false;
}
catch { }
return false;
}
public static async ValueTask<bool> IsTarFileAsync(
Stream stream,
CancellationToken cancellationToken = default
)
{
try
{
var tarHeader = new TarHeader(new ArchiveEncoding());
var reader = new AsyncBinaryReader(stream, false);
var readSucceeded = await tarHeader.ReadAsync(reader);
var isEmptyArchive =
tarHeader.Name?.Length == 0
&& tarHeader.Size == 0
&& Enum.IsDefined(typeof(EntryType), tarHeader.EntryType);
return readSucceeded || isEmptyArchive;
}
catch (Exception)
{
// Catch all exceptions during tar header reading to determine if this is a valid tar file
// Invalid tar files or corrupted streams will throw various exceptions
return false;
}
}
public static IWritableArchive CreateArchive() => new TarArchive();
public static IWritableAsyncArchive CreateAsyncArchive() => new TarArchive();
public static TarArchive Create() => new();
}

View File

@@ -32,10 +32,6 @@ public partial class TarArchive : AbstractWritableArchive<TarArchiveEntry, TarVo
protected override IEnumerable<TarArchiveEntry> LoadEntries(IEnumerable<TarVolume> volumes)
{
var stream = volumes.Single().Stream;
if (stream.CanSeek)
{
stream.Position = 0;
}
TarHeader? previousHeader = null;
foreach (
var header in TarHeaderFactory.ReadHeader(
@@ -91,77 +87,6 @@ public partial class TarArchive : AbstractWritableArchive<TarArchiveEntry, TarVo
}
}
protected override async IAsyncEnumerable<TarArchiveEntry> LoadEntriesAsync(
IAsyncEnumerable<TarVolume> volumes
)
{
var stream = (await volumes.SingleAsync()).Stream;
if (stream.CanSeek)
{
stream.Position = 0;
}
// Always use async header reading in LoadEntriesAsync for consistency
{
// Use async header reading for async-only streams
TarHeader? previousHeader = null;
await foreach (
var header in TarHeaderFactory.ReadHeaderAsync(
StreamingMode.Seekable,
stream,
ReaderOptions.ArchiveEncoding
)
)
{
if (header != null)
{
if (header.EntryType == EntryType.LongName)
{
previousHeader = header;
}
else
{
if (previousHeader != null)
{
var entry = new TarArchiveEntry(
this,
new TarFilePart(previousHeader, stream),
CompressionType.None
);
var oldStreamPos = stream.Position;
using (var entryStream = entry.OpenEntryStream())
{
using var memoryStream = new MemoryStream();
await entryStream.CopyToAsync(memoryStream);
memoryStream.Position = 0;
var bytes = memoryStream.ToArray();
header.Name = ReaderOptions
.ArchiveEncoding.Decode(bytes)
.TrimNulls();
}
stream.Position = oldStreamPos;
previousHeader = null;
}
yield return new TarArchiveEntry(
this,
new TarFilePart(header, stream),
CompressionType.None
);
}
}
else
{
throw new IncompleteArchiveException("Failed to read TAR header");
}
}
}
}
protected override TarArchiveEntry CreateEntryInternal(
string filePath,
Stream source,
@@ -217,41 +142,13 @@ public partial class TarArchive : AbstractWritableArchive<TarArchiveEntry, TarVo
protected override async ValueTask SaveToAsync(
Stream stream,
WriterOptions options,
IAsyncEnumerable<TarArchiveEntry> oldEntries,
IEnumerable<TarArchiveEntry> oldEntries,
IEnumerable<TarArchiveEntry> newEntries,
CancellationToken cancellationToken = default
)
{
using var writer = new TarWriter(stream, new TarWriterOptions(options));
await foreach (
var entry in oldEntries.WithCancellation(cancellationToken).ConfigureAwait(false)
)
{
if (entry.IsDirectory)
{
await writer
.WriteDirectoryAsync(
entry.Key.NotNull("Entry Key is null"),
entry.LastModifiedTime,
cancellationToken
)
.ConfigureAwait(false);
}
else
{
using var entryStream = entry.OpenEntryStream();
await writer
.WriteAsync(
entry.Key.NotNull("Entry Key is null"),
entryStream,
entry.LastModifiedTime,
entry.Size,
cancellationToken
)
.ConfigureAwait(false);
}
}
foreach (var entry in newEntries)
foreach (var entry in oldEntries.Concat(newEntries))
{
if (entry.IsDirectory)
{
@@ -283,13 +180,13 @@ public partial class TarArchive : AbstractWritableArchive<TarArchiveEntry, TarVo
{
var stream = Volumes.Single().Stream;
stream.Position = 0;
return TarReader.OpenReader(stream);
return TarReader.Open(stream);
}
protected override ValueTask<IAsyncReader> CreateReaderForSolidExtractionAsync()
{
var stream = Volumes.Single().Stream;
stream.Position = 0;
return new((IAsyncReader)TarReader.OpenReader(stream));
return new((IAsyncReader)TarReader.Open(stream));
}
}

View File

@@ -14,20 +14,17 @@ namespace SharpCompress.Archives.Zip;
public partial class ZipArchive
#if NET8_0_OR_GREATER
: IWritableArchiveOpenable,
: IArchiveOpenable<IWritableArchive, IWritableAsyncArchive>,
IMultiArchiveOpenable<IWritableArchive, IWritableAsyncArchive>
#endif
{
public static IWritableArchive OpenArchive(string filePath, ReaderOptions? readerOptions = null)
public static IWritableArchive Open(string filePath, ReaderOptions? readerOptions = null)
{
filePath.NotNullOrEmpty(nameof(filePath));
return OpenArchive(new FileInfo(filePath), readerOptions ?? new ReaderOptions());
return Open(new FileInfo(filePath), readerOptions ?? new ReaderOptions());
}
public static IWritableArchive OpenArchive(
FileInfo fileInfo,
ReaderOptions? readerOptions = null
)
public static IWritableArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null)
{
fileInfo.NotNull(nameof(fileInfo));
return new ZipArchive(
@@ -39,7 +36,7 @@ public partial class ZipArchive
);
}
public static IWritableArchive OpenArchive(
public static IWritableArchive Open(
IEnumerable<FileInfo> fileInfos,
ReaderOptions? readerOptions = null
)
@@ -55,7 +52,7 @@ public partial class ZipArchive
);
}
public static IWritableArchive OpenArchive(
public static IWritableArchive Open(
IEnumerable<Stream> streams,
ReaderOptions? readerOptions = null
)
@@ -71,7 +68,7 @@ public partial class ZipArchive
);
}
public static IWritableArchive OpenArchive(Stream stream, ReaderOptions? readerOptions = null)
public static IWritableArchive Open(Stream stream, ReaderOptions? readerOptions = null)
{
stream.NotNull(nameof(stream));
@@ -85,54 +82,54 @@ public partial class ZipArchive
);
}
public static IWritableAsyncArchive OpenAsyncArchive(
public static IWritableAsyncArchive OpenAsync(
string path,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IWritableAsyncArchive)OpenArchive(path, readerOptions);
return (IWritableAsyncArchive)Open(path, readerOptions);
}
public static IWritableAsyncArchive OpenAsyncArchive(
public static IWritableAsyncArchive OpenAsync(
Stream stream,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IWritableAsyncArchive)OpenArchive(stream, readerOptions);
return (IWritableAsyncArchive)Open(stream, readerOptions);
}
public static IWritableAsyncArchive OpenAsyncArchive(
public static IWritableAsyncArchive OpenAsync(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IWritableAsyncArchive)OpenArchive(fileInfo, readerOptions);
return (IWritableAsyncArchive)Open(fileInfo, readerOptions);
}
public static IWritableAsyncArchive OpenAsyncArchive(
public static IWritableAsyncArchive OpenAsync(
IReadOnlyList<Stream> streams,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IWritableAsyncArchive)OpenArchive(streams, readerOptions);
return (IWritableAsyncArchive)Open(streams, readerOptions);
}
public static IWritableAsyncArchive OpenAsyncArchive(
public static IWritableAsyncArchive OpenAsync(
IReadOnlyList<FileInfo> fileInfos,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IWritableAsyncArchive)OpenArchive(fileInfos, readerOptions);
return (IWritableAsyncArchive)Open(fileInfos, readerOptions);
}
public static bool IsZipFile(
@@ -266,9 +263,7 @@ public partial class ZipArchive
}
}
public static IWritableArchive CreateArchive() => new ZipArchive();
public static IWritableAsyncArchive CreateAsyncArchive() => new ZipArchive();
public static ZipArchive Create() => new();
public static async ValueTask<bool> IsZipMultiAsync(
Stream stream,

View File

@@ -195,39 +195,13 @@ public partial class ZipArchive : AbstractWritableArchive<ZipArchiveEntry, ZipVo
protected override async ValueTask SaveToAsync(
Stream stream,
WriterOptions options,
IAsyncEnumerable<ZipArchiveEntry> oldEntries,
IEnumerable<ZipArchiveEntry> oldEntries,
IEnumerable<ZipArchiveEntry> newEntries,
CancellationToken cancellationToken = default
)
{
using var writer = new ZipWriter(stream, new ZipWriterOptions(options));
await foreach (
var entry in oldEntries.WithCancellation(cancellationToken).ConfigureAwait(false)
)
{
if (entry.IsDirectory)
{
await writer
.WriteDirectoryAsync(
entry.Key.NotNull("Entry Key is null"),
entry.LastModifiedTime,
cancellationToken
)
.ConfigureAwait(false);
}
else
{
using var entryStream = entry.OpenEntryStream();
await writer
.WriteAsync(
entry.Key.NotNull("Entry Key is null"),
entryStream,
cancellationToken
)
.ConfigureAwait(false);
}
}
foreach (var entry in newEntries)
foreach (var entry in oldEntries.Concat(newEntries))
{
if (entry.IsDirectory)
{
@@ -270,13 +244,13 @@ public partial class ZipArchive : AbstractWritableArchive<ZipArchiveEntry, ZipVo
{
var stream = Volumes.Single().Stream;
((IStreamStack)stream).StackSeek(0);
return ZipReader.OpenReader(stream, ReaderOptions, Entries);
return ZipReader.Open(stream, ReaderOptions, Entries);
}
protected override ValueTask<IAsyncReader> CreateReaderForSolidExtractionAsync()
{
var stream = Volumes.Single().Stream;
stream.Position = 0;
return new((IAsyncReader)ZipReader.OpenReader(stream));
return new((IAsyncReader)ZipReader.Open(stream));
}
}

View File

@@ -2,8 +2,6 @@ using System;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;
using SharpCompress.Common.Arc;
@@ -149,107 +147,6 @@ namespace SharpCompress.Common.Ace.Headers
return this;
}
/// <summary>
/// Asynchronously reads the next file entry header from the stream.
/// Returns null if no more entries or end of archive.
/// Supports both ACE 1.0 and ACE 2.0 formats.
/// </summary>
public override async ValueTask<AceHeader?> ReadAsync(
Stream stream,
CancellationToken cancellationToken = default
)
{
var headerData = await ReadHeaderAsync(stream, cancellationToken);
if (headerData.Length == 0)
{
return null;
}
int offset = 0;
// Header type (1 byte)
HeaderType = headerData[offset++];
// Skip recovery record headers (ACE 2.0 feature)
if (HeaderType == (byte)SharpCompress.Common.Ace.Headers.AceHeaderType.RECOVERY32)
{
// Skip to next header
return null;
}
if (HeaderType != (byte)SharpCompress.Common.Ace.Headers.AceHeaderType.FILE)
{
// Unknown header type - skip
return null;
}
// Header flags (2 bytes)
HeaderFlags = BitConverter.ToUInt16(headerData, offset);
offset += 2;
// Packed size (4 bytes)
PackedSize = BitConverter.ToUInt32(headerData, offset);
offset += 4;
// Original size (4 bytes)
OriginalSize = BitConverter.ToUInt32(headerData, offset);
offset += 4;
// File date/time in DOS format (4 bytes)
var dosDateTime = BitConverter.ToUInt32(headerData, offset);
DateTime = ConvertDosDateTime(dosDateTime);
offset += 4;
// File attributes (4 bytes)
Attributes = (int)BitConverter.ToUInt32(headerData, offset);
offset += 4;
// CRC32 (4 bytes)
Crc32 = BitConverter.ToUInt32(headerData, offset);
offset += 4;
// Compression type (1 byte)
byte compressionType = headerData[offset++];
CompressionType = GetCompressionType(compressionType);
// Compression quality/parameter (1 byte)
byte compressionQuality = headerData[offset++];
CompressionQuality = GetCompressionQuality(compressionQuality);
// Parameters (2 bytes)
Parameters = BitConverter.ToUInt16(headerData, offset);
offset += 2;
// Reserved (2 bytes) - skip
offset += 2;
// Filename length (2 bytes)
var filenameLength = BitConverter.ToUInt16(headerData, offset);
offset += 2;
// Filename
if (offset + filenameLength <= headerData.Length)
{
Filename = ArchiveEncoding.Decode(headerData, offset, filenameLength);
offset += filenameLength;
}
// Handle comment if present
if ((HeaderFlags & SharpCompress.Common.Ace.Headers.HeaderFlags.COMMENT) != 0)
{
// Comment length (2 bytes)
if (offset + 2 <= headerData.Length)
{
ushort commentLength = BitConverter.ToUInt16(headerData, offset);
offset += 2 + commentLength; // Skip comment
}
}
// Store the data start position
DataStartPosition = stream.Position;
return this;
}
public CompressionType GetCompressionType(byte value) =>
value switch
{

View File

@@ -1,7 +1,5 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common.Arj.Headers;
using SharpCompress.Crypto;
@@ -60,11 +58,6 @@ namespace SharpCompress.Common.Ace.Headers
public abstract AceHeader? Read(Stream reader);
public abstract ValueTask<AceHeader?> ReadAsync(
Stream reader,
CancellationToken cancellationToken = default
);
public byte[] ReadHeader(Stream stream)
{
// Read header CRC (2 bytes) and header size (2 bytes)
@@ -97,41 +90,6 @@ namespace SharpCompress.Common.Ace.Headers
return body;
}
public async ValueTask<byte[]> ReadHeaderAsync(
Stream stream,
CancellationToken cancellationToken = default
)
{
// Read header CRC (2 bytes) and header size (2 bytes)
var headerBytes = new byte[4];
if (await stream.ReadAsync(headerBytes, 0, 4, cancellationToken) != 4)
{
return Array.Empty<byte>();
}
HeaderCrc = BitConverter.ToUInt16(headerBytes, 0); // CRC for validation
HeaderSize = BitConverter.ToUInt16(headerBytes, 2);
if (HeaderSize == 0)
{
return Array.Empty<byte>();
}
// Read the header data
var body = new byte[HeaderSize];
if (await stream.ReadAsync(body, 0, HeaderSize, cancellationToken) != HeaderSize)
{
return Array.Empty<byte>();
}
// Verify crc
var checksum = AceCrc.AceCrc16(body);
if (checksum != HeaderCrc)
{
throw new InvalidDataException("Header checksum is invalid");
}
return body;
}
public static bool IsArchive(Stream stream)
{
// ACE files have a specific signature
@@ -147,26 +105,6 @@ namespace SharpCompress.Common.Ace.Headers
return CheckMagicBytes(bytes, 7);
}
/// <summary>
/// Asynchronously checks if the stream is an ACE archive
/// </summary>
/// <param name="stream">The stream to read from</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>True if the stream is an ACE archive, false otherwise</returns>
public static async ValueTask<bool> IsArchiveAsync(
Stream stream,
CancellationToken cancellationToken = default
)
{
var bytes = new byte[14];
if (await stream.ReadAsync(bytes, 0, 14, cancellationToken) != 14)
{
return false;
}
return CheckMagicBytes(bytes, 7);
}
protected static bool CheckMagicBytes(byte[] headerBytes, int offset)
{
// Check for "**ACE**" at specified offset

View File

@@ -2,8 +2,6 @@ using System;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common.Ace.Headers;
using SharpCompress.Common.Zip.Headers;
using SharpCompress.Crypto;
@@ -95,77 +93,5 @@ namespace SharpCompress.Common.Ace.Headers
return this;
}
/// <summary>
/// Asynchronously reads the main archive header from the stream.
/// Returns header if this is a valid ACE archive.
/// Supports both ACE 1.0 and ACE 2.0 formats.
/// </summary>
public override async ValueTask<AceHeader?> ReadAsync(
Stream stream,
CancellationToken cancellationToken = default
)
{
var headerData = await ReadHeaderAsync(stream, cancellationToken);
if (headerData.Length == 0)
{
return null;
}
int offset = 0;
// Header type should be 0 for main header
if (headerData[offset++] != HeaderType)
{
return null;
}
// Header flags (2 bytes)
HeaderFlags = BitConverter.ToUInt16(headerData, offset);
offset += 2;
// Skip signature "**ACE**" (7 bytes)
if (!CheckMagicBytes(headerData, offset))
{
throw new InvalidDataException("Invalid ACE archive signature.");
}
offset += 7;
// ACE version (1 byte) - 10 for ACE 1.0, 20 for ACE 2.0
AceVersion = headerData[offset++];
ExtractVersion = headerData[offset++];
// Host OS (1 byte)
if (offset < headerData.Length)
{
var hostOsByte = headerData[offset++];
HostOS = hostOsByte <= 11 ? (HostOS)hostOsByte : HostOS.Unknown;
}
// Volume number (1 byte)
VolumeNumber = headerData[offset++];
// Creation date/time (4 bytes)
var dosDateTime = BitConverter.ToUInt32(headerData, offset);
DateTime = ConvertDosDateTime(dosDateTime);
offset += 4;
// Reserved fields (8 bytes)
if (offset + 8 <= headerData.Length)
{
offset += 8;
}
// Skip additional fields based on flags
// Handle comment if present
if ((HeaderFlags & SharpCompress.Common.Ace.Headers.HeaderFlags.COMMENT) != 0)
{
if (offset + 2 <= headerData.Length)
{
ushort commentLength = BitConverter.ToUInt16(headerData, offset);
offset += 2 + commentLength;
}
}
return this;
}
}
}

View File

@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common.Zip.Headers;
using SharpCompress.Crypto;
@@ -32,11 +31,6 @@ namespace SharpCompress.Common.Arj.Headers
public abstract ArjHeader? Read(Stream reader);
public abstract ValueTask<ArjHeader?> ReadAsync(
Stream reader,
CancellationToken cancellationToken = default
);
public byte[] ReadHeader(Stream stream)
{
// check for magic bytes
@@ -78,102 +72,6 @@ namespace SharpCompress.Common.Arj.Headers
return body;
}
public async ValueTask<byte[]> ReadHeaderAsync(
Stream stream,
CancellationToken cancellationToken = default
)
{
// check for magic bytes
var magic = new byte[2];
if (await stream.ReadAsync(magic, 0, 2, cancellationToken) != 2)
{
return Array.Empty<byte>();
}
if (!CheckMagicBytes(magic))
{
throw new InvalidDataException("Not an ARJ file (wrong magic bytes)");
}
// read header_size
byte[] headerBytes = new byte[2];
await stream.ReadAsync(headerBytes, 0, 2, cancellationToken);
var headerSize = (ushort)(headerBytes[0] | headerBytes[1] << 8);
if (headerSize < 1)
{
return Array.Empty<byte>();
}
var body = new byte[headerSize];
var read = await stream.ReadAsync(body, 0, headerSize, cancellationToken);
if (read < headerSize)
{
return Array.Empty<byte>();
}
byte[] crc = new byte[4];
read = await stream.ReadAsync(crc, 0, 4, cancellationToken);
var checksum = Crc32Stream.Compute(body);
// Compute the hash value
if (checksum != BitConverter.ToUInt32(crc, 0))
{
throw new InvalidDataException("Header checksum is invalid");
}
return body;
}
protected async ValueTask<List<byte[]>> ReadExtendedHeadersAsync(
Stream reader,
CancellationToken cancellationToken = default
)
{
List<byte[]> extendedHeader = new List<byte[]>();
byte[] buffer = new byte[2];
while (true)
{
int bytesRead = await reader.ReadAsync(buffer, 0, 2, cancellationToken);
if (bytesRead < 2)
{
throw new EndOfStreamException(
"Unexpected end of stream while reading extended header size."
);
}
var extHeaderSize = (ushort)(buffer[0] | (buffer[1] << 8));
if (extHeaderSize == 0)
{
return extendedHeader;
}
byte[] header = new byte[extHeaderSize];
bytesRead = await reader.ReadAsync(header, 0, extHeaderSize, cancellationToken);
if (bytesRead < extHeaderSize)
{
throw new EndOfStreamException(
"Unexpected end of stream while reading extended header data."
);
}
byte[] crc = new byte[4];
bytesRead = await reader.ReadAsync(crc, 0, 4, cancellationToken);
if (bytesRead < 4)
{
throw new EndOfStreamException(
"Unexpected end of stream while reading extended header CRC."
);
}
var checksum = Crc32Stream.Compute(header);
if (checksum != BitConverter.ToUInt32(crc, 0))
{
throw new InvalidDataException("Extended header checksum is invalid");
}
extendedHeader.Add(header);
}
}
protected List<byte[]> ReadExtendedHeaders(Stream reader)
{
List<byte[]> extendedHeader = new List<byte[]>();
@@ -251,26 +149,6 @@ namespace SharpCompress.Common.Arj.Headers
return CheckMagicBytes(bytes);
}
/// <summary>
/// Asynchronously checks if the stream is an ARJ archive
/// </summary>
/// <param name="stream">The stream to read from</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>True if the stream is an ARJ archive, false otherwise</returns>
public static async ValueTask<bool> IsArchiveAsync(
Stream stream,
CancellationToken cancellationToken = default
)
{
var bytes = new byte[2];
if (await stream.ReadAsync(bytes, 0, 2, cancellationToken) != 2)
{
return false;
}
return CheckMagicBytes(bytes);
}
protected static bool CheckMagicBytes(byte[] headerBytes)
{
var magicValue = (ushort)(headerBytes[0] | headerBytes[1] << 8);

View File

@@ -4,7 +4,6 @@ using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace SharpCompress.Common.Arj.Headers
@@ -56,22 +55,6 @@ namespace SharpCompress.Common.Arj.Headers
return null;
}
public override async ValueTask<ArjHeader?> ReadAsync(
Stream stream,
CancellationToken cancellationToken = default
)
{
var body = await ReadHeaderAsync(stream, cancellationToken);
if (body.Length > 0)
{
await ReadExtendedHeadersAsync(stream, cancellationToken);
var header = LoadFrom(body);
header.DataStartPosition = stream.Position;
return header;
}
return null;
}
public ArjLocalHeader LoadFrom(byte[] headerBytes)
{
int offset = 0;

View File

@@ -1,8 +1,6 @@
using System;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Compressors.Deflate;
using SharpCompress.Crypto;
@@ -47,16 +45,6 @@ namespace SharpCompress.Common.Arj.Headers
return LoadFrom(body);
}
public override async ValueTask<ArjHeader?> ReadAsync(
Stream stream,
CancellationToken cancellationToken = default
)
{
var body = await ReadHeaderAsync(stream, cancellationToken);
await ReadExtendedHeadersAsync(stream, cancellationToken);
return LoadFrom(body);
}
public ArjMainHeader LoadFrom(byte[] headerBytes)
{
var offset = 1;

View File

@@ -51,19 +51,14 @@ namespace SharpCompress.Common
return BinaryPrimitives.ReadUInt64LittleEndian(_buffer);
}
public async ValueTask ReadBytesAsync(
byte[] bytes,
int offset,
int count,
CancellationToken ct = default
)
public async ValueTask ReadBytesAsync(byte[] bytes, int offset, int count, CancellationToken ct = default)
{
await _stream.ReadExactAsync(bytes, offset, count, ct).ConfigureAwait(false);
}
public async ValueTask SkipAsync(int count, CancellationToken ct = default)
{
await _stream.SkipAsync(count, ct).ConfigureAwait(false);
await _stream.SkipAsync( count, ct).ConfigureAwait(false);
}
public void Dispose()

View File

@@ -1,177 +0,0 @@
using System;
using System.Buffers.Binary;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
namespace SharpCompress.Common.Rar;
internal class AsyncMarkingBinaryReader
{
private readonly AsyncBinaryReader _reader;
public AsyncMarkingBinaryReader(Stream stream)
{
_reader = new AsyncBinaryReader(stream, leaveOpen: true);
}
public Stream BaseStream => _reader.BaseStream;
public virtual long CurrentReadByteCount { get; protected set; }
public virtual void Mark() => CurrentReadByteCount = 0;
public virtual async ValueTask<bool> ReadBooleanAsync(
CancellationToken cancellationToken = default
) => await ReadByteAsync(cancellationToken).ConfigureAwait(false) != 0;
public virtual async ValueTask<byte> ReadByteAsync(
CancellationToken cancellationToken = default
)
{
CurrentReadByteCount++;
return await _reader.ReadByteAsync(cancellationToken).ConfigureAwait(false);
}
public virtual async ValueTask<byte[]> ReadBytesAsync(
int count,
CancellationToken cancellationToken = default
)
{
CurrentReadByteCount += count;
var bytes = new byte[count];
await _reader.ReadBytesAsync(bytes, 0, count, cancellationToken).ConfigureAwait(false);
return bytes;
}
public async ValueTask<ushort> ReadUInt16Async(
CancellationToken cancellationToken = default
)
{
CurrentReadByteCount += 2;
var bytes = await ReadBytesAsync( 2, cancellationToken).ConfigureAwait(false);
return BinaryPrimitives.ReadUInt16LittleEndian(bytes);
}
public async ValueTask<uint> ReadUInt32Async(
CancellationToken cancellationToken = default
)
{
CurrentReadByteCount += 4;
var bytes = await ReadBytesAsync( 4, cancellationToken).ConfigureAwait(false);
return BinaryPrimitives.ReadUInt32LittleEndian(bytes);
}
public virtual async ValueTask<ulong> ReadUInt64Async(
CancellationToken cancellationToken = default
)
{
CurrentReadByteCount += 8;
var bytes = await ReadBytesAsync( 8, cancellationToken).ConfigureAwait(false);
return BinaryPrimitives.ReadUInt64LittleEndian(bytes);
}
public virtual async ValueTask<short> ReadInt16Async(
CancellationToken cancellationToken = default
)
{
CurrentReadByteCount += 2;
var bytes = await ReadBytesAsync(2, cancellationToken).ConfigureAwait(false);
return BinaryPrimitives.ReadInt16LittleEndian(bytes);
}
public virtual async ValueTask<int> ReadInt32Async(
CancellationToken cancellationToken = default
)
{
CurrentReadByteCount += 4;
var bytes = await ReadBytesAsync( 4, cancellationToken).ConfigureAwait(false);
return BinaryPrimitives.ReadInt32LittleEndian(bytes);
}
public virtual async ValueTask<long> ReadInt64Async(
CancellationToken cancellationToken = default
)
{
CurrentReadByteCount += 8;
var bytes = await ReadBytesAsync(8, cancellationToken).ConfigureAwait(false);
return BinaryPrimitives.ReadInt64LittleEndian(bytes);
}
public async ValueTask<ulong> ReadRarVIntAsync(
CancellationToken cancellationToken = default,
int maxBytes = 10
) => await DoReadRarVIntAsync((maxBytes - 1) * 7, cancellationToken).ConfigureAwait(false);
private async ValueTask<ulong> DoReadRarVIntAsync(
int maxShift,
CancellationToken cancellationToken
)
{
var shift = 0;
ulong result = 0;
do
{
var b0 = await ReadByteAsync(cancellationToken).ConfigureAwait(false);
var b1 = ((uint)b0) & 0x7f;
ulong n = b1;
var shifted = n << shift;
if (n != shifted >> shift)
{
// overflow
break;
}
result |= shifted;
if (b0 == b1)
{
return result;
}
shift += 7;
} while (shift <= maxShift);
throw new FormatException("malformed vint");
}
public async ValueTask<uint> ReadRarVIntUInt32Async(int maxBytes = 5, CancellationToken cancellationToken = default) =>
// hopefully this gets inlined
await DoReadRarVIntUInt32Async((maxBytes - 1) * 7, cancellationToken).ConfigureAwait(false);
public async ValueTask<ushort> ReadRarVIntUInt16Async(int maxBytes = 3, CancellationToken cancellationToken = default) =>
// hopefully this gets inlined
checked((ushort)await DoReadRarVIntUInt32Async((maxBytes - 1) * 7, cancellationToken).ConfigureAwait(false));
public async ValueTask<byte> ReadRarVIntByteAsync(int maxBytes = 2, CancellationToken cancellationToken = default) =>
// hopefully this gets inlined
checked((byte)await DoReadRarVIntUInt32Async((maxBytes - 1) * 7, cancellationToken).ConfigureAwait(false));
public async ValueTask SkipAsync(int count, CancellationToken cancellationToken = default)
{
CurrentReadByteCount += count;
await _reader.SkipAsync(count, cancellationToken).ConfigureAwait(false);
}
private async ValueTask<uint> DoReadRarVIntUInt32Async(int maxShift, CancellationToken cancellationToken = default)
{
var shift = 0;
uint result = 0;
do
{
var b0 = await ReadByteAsync(cancellationToken).ConfigureAwait(false);
var b1 = ((uint)b0) & 0x7f;
var n = b1;
var shifted = n << shift;
if (n != shifted >> shift)
{
// overflow
break;
}
result |= shifted;
if (b0 == b1)
{
return result;
}
shift += 7;
} while (shift <= maxShift);
throw new FormatException("malformed vint");
}
}

View File

@@ -1,45 +0,0 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Compressors.Rar;
namespace SharpCompress.Common.Rar;
internal class AsyncRarCrcBinaryReader(Stream stream) : AsyncMarkingBinaryReader(stream)
{
private uint _currentCrc;
public uint GetCrc32() => ~_currentCrc;
public void ResetCrc() => _currentCrc = 0xffffffff;
protected void UpdateCrc(byte b) => _currentCrc = RarCRC.CheckCrc(_currentCrc, b);
protected async ValueTask<byte[]> ReadBytesNoCrcAsync(
int count,
CancellationToken cancellationToken = default
)
{
return await base.ReadBytesAsync(count, cancellationToken).ConfigureAwait(false);
}
public override async ValueTask<byte> ReadByteAsync(
CancellationToken cancellationToken = default
)
{
var b = await base.ReadByteAsync(cancellationToken).ConfigureAwait(false);
_currentCrc = RarCRC.CheckCrc(_currentCrc, b);
return b;
}
public override async ValueTask<byte[]> ReadBytesAsync(
int count,
CancellationToken cancellationToken = default
)
{
var result = await base.ReadBytesAsync(count, cancellationToken).ConfigureAwait(false);
_currentCrc = RarCRC.CheckCrc(_currentCrc, result, 0, result.Length);
return result;
}
}

View File

@@ -1,108 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common.Rar.Headers;
using SharpCompress.Crypto;
namespace SharpCompress.Common.Rar;
internal sealed class AsyncRarCryptoBinaryReader : AsyncRarCrcBinaryReader
{
private BlockTransformer _rijndael = default!;
private readonly Queue<byte> _data = new();
private long _readCount;
private AsyncRarCryptoBinaryReader(Stream stream)
: base(stream)
{
}
public static async ValueTask<AsyncRarCryptoBinaryReader> Create(Stream stream, ICryptKey cryptKey, byte[]? salt = null)
{
var binary = new AsyncRarCryptoBinaryReader(stream);
if (salt == null)
{
salt = await binary.ReadBytesAsyncBase(EncryptionConstV5.SIZE_SALT30);
binary._readCount += EncryptionConstV5.SIZE_SALT30;
}
binary._rijndael = new BlockTransformer(cryptKey.Transformer(salt));
return binary;
}
public override long CurrentReadByteCount
{
get => _readCount;
protected set
{
// ignore
}
}
public override void Mark() => _readCount = 0;
public override async ValueTask<byte> ReadByteAsync(
CancellationToken cancellationToken = default
)
{
var bytes = await ReadAndDecryptBytesAsync(1, cancellationToken).ConfigureAwait(false);
return bytes[0];
}
private ValueTask<byte[]> ReadBytesAsyncBase(int count) => base.ReadBytesAsync(count);
public override async ValueTask<byte[]> ReadBytesAsync(
int count,
CancellationToken cancellationToken = default
)
{
return await ReadAndDecryptBytesAsync(count, cancellationToken).ConfigureAwait(false);
}
private async ValueTask<byte[]> ReadAndDecryptBytesAsync(
int count,
CancellationToken cancellationToken
)
{
var queueSize = _data.Count;
var sizeToRead = count - queueSize;
if (sizeToRead > 0)
{
var alignedSize = sizeToRead + ((~sizeToRead + 1) & 0xf);
for (var i = 0; i < alignedSize / 16; i++)
{
var cipherText = await ReadBytesNoCrcAsync(16, cancellationToken)
.ConfigureAwait(false);
var readBytes = _rijndael.ProcessBlock(cipherText);
foreach (var readByte in readBytes)
{
_data.Enqueue(readByte);
}
}
}
var decryptedBytes = new byte[count];
for (var i = 0; i < count; i++)
{
var b = _data.Dequeue();
decryptedBytes[i] = b;
UpdateCrc(b);
}
_readCount += count;
return decryptedBytes;
}
public void ClearQueue() => _data.Clear();
public void SkipQueue()
{
var position = BaseStream.Position;
BaseStream.Position = position + _data.Count;
ClearQueue();
}
}

View File

@@ -4,14 +4,13 @@ namespace SharpCompress.Common.Rar.Headers;
internal class AvHeader : RarHeader
{
public static AvHeader Create(RarHeader header, RarCrcBinaryReader reader)
public AvHeader(RarHeader header, RarCrcBinaryReader reader)
: base(header, reader, HeaderType.Av)
{
var c = CreateChild<AvHeader>(header, reader, HeaderType.Av);
if (c.IsRar5)
if (IsRar5)
{
throw new InvalidFormatException("unexpected rar5 record");
}
return c;
}
protected override void ReadFinish(MarkingBinaryReader reader)

View File

@@ -1,40 +1,16 @@
#nullable disable
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common.Rar;
using SharpCompress.IO;
namespace SharpCompress.Common.Rar.Headers;
internal class ArchiveCryptHeader : RarHeader
{
public static ArchiveCryptHeader Create(RarHeader header, RarCrcBinaryReader reader) =>
CreateChild<ArchiveCryptHeader>(header, reader, HeaderType.Crypt);
public ArchiveCryptHeader(RarHeader header, RarCrcBinaryReader reader)
: base(header, reader, HeaderType.Crypt) { }
public static async ValueTask<ArchiveCryptHeader> CreateAsync(
RarHeader header,
AsyncRarCrcBinaryReader reader,
CancellationToken cancellationToken = default
) =>
await CreateChildAsync<ArchiveCryptHeader>(
header,
reader,
HeaderType.Crypt,
cancellationToken
)
.ConfigureAwait(false);
public Rar5CryptoInfo CryptInfo = default!;
public Rar5CryptoInfo CryptInfo = new();
protected override void ReadFinish(MarkingBinaryReader reader) =>
CryptInfo = Rar5CryptoInfo.Create(reader, false);
protected override async ValueTask ReadFinishAsync(
AsyncMarkingBinaryReader reader,
CancellationToken cancellationToken
)
{
CryptInfo = await Rar5CryptoInfo.CreateAsync(reader, false);
}
CryptInfo = new Rar5CryptoInfo(reader, false);
}

View File

@@ -1,22 +1,11 @@
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common.Rar;
using SharpCompress.IO;
namespace SharpCompress.Common.Rar.Headers;
internal sealed class ArchiveHeader : RarHeader
{
public static ArchiveHeader Create(RarHeader header, RarCrcBinaryReader reader) =>
CreateChild<ArchiveHeader>(header, reader, HeaderType.Archive);
public static async ValueTask<ArchiveHeader> CreateAsync(
RarHeader header,
AsyncRarCrcBinaryReader reader,
CancellationToken cancellationToken = default
) =>
await CreateChildAsync<ArchiveHeader>(header, reader, HeaderType.Archive, cancellationToken)
.ConfigureAwait(false);
public ArchiveHeader(RarHeader header, RarCrcBinaryReader reader)
: base(header, reader, HeaderType.Archive) { }
protected override void ReadFinish(MarkingBinaryReader reader)
{
@@ -44,38 +33,6 @@ internal sealed class ArchiveHeader : RarHeader
}
}
protected override async ValueTask ReadFinishAsync(
AsyncMarkingBinaryReader reader,
CancellationToken cancellationToken
)
{
if (IsRar5)
{
Flags = await reader.ReadRarVIntUInt16Async(cancellationToken: cancellationToken).ConfigureAwait(false);
if (HasFlag(ArchiveFlagsV5.HAS_VOLUME_NUMBER))
{
VolumeNumber = (int)
await reader.ReadRarVIntUInt32Async(cancellationToken: cancellationToken).ConfigureAwait(false);
}
// later: we may have a locator record if we need it
//if (ExtraSize != 0) {
// ReadLocator(reader);
//}
}
else
{
Flags = HeaderFlags;
HighPosAv = await reader.ReadInt16Async(cancellationToken).ConfigureAwait(false);
PosAv = await reader.ReadInt32Async(cancellationToken).ConfigureAwait(false);
if (HasFlag(ArchiveFlagsV4.ENCRYPT_VER))
{
EncryptionVersion = await reader
.ReadByteAsync(cancellationToken)
.ConfigureAwait(false);
}
}
}
private ushort Flags { get; set; }
private bool HasFlag(ushort flag) => (Flags & flag) == flag;

View File

@@ -4,14 +4,13 @@ namespace SharpCompress.Common.Rar.Headers;
internal class CommentHeader : RarHeader
{
public static CommentHeader Create(RarHeader header, RarCrcBinaryReader reader)
protected CommentHeader(RarHeader header, RarCrcBinaryReader reader)
: base(header, reader, HeaderType.Comment)
{
var c = CreateChild<CommentHeader>(header, reader, HeaderType.Comment);
if (c.IsRar5)
if (IsRar5)
{
throw new InvalidFormatException("unexpected rar5 record");
}
return c;
}
protected override void ReadFinish(MarkingBinaryReader reader)

View File

@@ -1,27 +1,11 @@
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common.Rar;
using SharpCompress.IO;
using SharpCompress.IO;
namespace SharpCompress.Common.Rar.Headers;
internal class EndArchiveHeader : RarHeader
{
public static EndArchiveHeader Create(RarHeader header, RarCrcBinaryReader reader) =>
CreateChild<EndArchiveHeader>(header, reader, HeaderType.EndArchive);
public static async ValueTask<EndArchiveHeader> CreateAsync(
RarHeader header,
AsyncRarCrcBinaryReader reader,
CancellationToken cancellationToken = default
) =>
await CreateChildAsync<EndArchiveHeader>(
header,
reader,
HeaderType.EndArchive,
cancellationToken
)
.ConfigureAwait(false);
public EndArchiveHeader(RarHeader header, RarCrcBinaryReader reader)
: base(header, reader, HeaderType.EndArchive) { }
protected override void ReadFinish(MarkingBinaryReader reader)
{
@@ -43,29 +27,6 @@ internal class EndArchiveHeader : RarHeader
}
}
protected override async ValueTask ReadFinishAsync(
AsyncMarkingBinaryReader reader,
CancellationToken cancellationToken
)
{
if (IsRar5)
{
Flags = await reader.ReadRarVIntUInt16Async(cancellationToken: cancellationToken).ConfigureAwait(false);
}
else
{
Flags = HeaderFlags;
if (HasFlag(EndArchiveFlagsV4.DATA_CRC))
{
ArchiveCrc = await reader.ReadInt32Async(cancellationToken).ConfigureAwait(false);
}
if (HasFlag(EndArchiveFlagsV4.VOLUME_NUMBER))
{
VolumeNumber = await reader.ReadInt16Async(cancellationToken).ConfigureAwait(false);
}
}
}
private ushort Flags { get; set; }
private bool HasFlag(ushort flag) => (Flags & flag) == flag;

View File

@@ -2,9 +2,6 @@ using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common.Rar;
using SharpCompress.IO;
#if !Rar2017_64bit
using size_t = System.UInt32;
@@ -20,20 +17,8 @@ internal class FileHeader : RarHeader
{
private byte[]? _hash;
public static FileHeader Create(
RarHeader header,
RarCrcBinaryReader reader,
HeaderType headerType
) => CreateChild<FileHeader>(header, reader, headerType);
public static async ValueTask<FileHeader> CreateAsync(
RarHeader header,
AsyncRarCrcBinaryReader reader,
HeaderType headerType,
CancellationToken cancellationToken = default
) =>
await CreateChildAsync<FileHeader>(header, reader, headerType, cancellationToken)
.ConfigureAwait(false);
public FileHeader(RarHeader header, RarCrcBinaryReader reader, HeaderType headerType)
: base(header, reader, headerType) { }
protected override void ReadFinish(MarkingBinaryReader reader)
{
@@ -47,21 +32,6 @@ internal class FileHeader : RarHeader
}
}
protected override async ValueTask ReadFinishAsync(
AsyncMarkingBinaryReader reader,
CancellationToken cancellationToken
)
{
if (IsRar5)
{
await ReadFromReaderV5Async(reader, cancellationToken).ConfigureAwait(false);
}
else
{
await ReadFromReaderV4Async(reader, cancellationToken).ConfigureAwait(false);
}
}
private void ReadFromReaderV5(MarkingBinaryReader reader)
{
Flags = reader.ReadRarVIntUInt16();
@@ -149,7 +119,7 @@ internal class FileHeader : RarHeader
{
case FHEXTRA_CRYPT: // file encryption
{
Rar5CryptoInfo = Rar5CryptoInfo.Create(reader, true);
Rar5CryptoInfo = new Rar5CryptoInfo(reader, true);
if (Rar5CryptoInfo.PswCheck.All(singleByte => singleByte == 0))
{
@@ -445,391 +415,6 @@ internal class FileHeader : RarHeader
return path;
}
private async ValueTask ReadFromReaderV5Async(
AsyncMarkingBinaryReader reader,
CancellationToken cancellationToken
)
{
Flags = await reader.ReadRarVIntUInt16Async(cancellationToken: cancellationToken).ConfigureAwait(false);
var lvalue = checked(
(long)await reader.ReadRarVIntAsync(cancellationToken: cancellationToken).ConfigureAwait(false)
);
UncompressedSize = HasFlag(FileFlagsV5.UNPACKED_SIZE_UNKNOWN) ? long.MaxValue : lvalue;
FileAttributes = await reader
.ReadRarVIntUInt32Async(cancellationToken: cancellationToken)
.ConfigureAwait(false);
if (HasFlag(FileFlagsV5.HAS_MOD_TIME))
{
FileLastModifiedTime = Utility.UnixTimeToDateTime(
await reader.ReadUInt32Async(cancellationToken).ConfigureAwait(false)
);
}
if (HasFlag(FileFlagsV5.HAS_CRC32))
{
FileCrc = await reader.ReadBytesAsync(4, cancellationToken).ConfigureAwait(false);
}
var compressionInfo = await reader
.ReadRarVIntUInt16Async(cancellationToken: cancellationToken)
.ConfigureAwait(false);
CompressionAlgorithm = (byte)((compressionInfo & 0x3f) + 50);
IsSolid = (compressionInfo & 0x40) == 0x40;
CompressionMethod = (byte)((compressionInfo >> 7) & 0x7);
WindowSize = IsDirectory ? 0 : ((size_t)0x20000) << ((compressionInfo >> 10) & 0xf);
HostOs = await reader.ReadRarVIntByteAsync(cancellationToken: cancellationToken).ConfigureAwait(false);
var nameSize = await reader.ReadRarVIntUInt16Async(cancellationToken: cancellationToken).ConfigureAwait(false);
var b = await reader.ReadBytesAsync(nameSize, cancellationToken).ConfigureAwait(false);
FileName = ConvertPathV5(Encoding.UTF8.GetString(b, 0, b.Length));
if (ExtraSize != (uint)RemainingHeaderBytesAsync(reader))
{
throw new InvalidFormatException("rar5 header size / extra size inconsistency");
}
const ushort FHEXTRA_CRYPT = 0x01;
const ushort FHEXTRA_HASH = 0x02;
const ushort FHEXTRA_HTIME = 0x03;
const ushort FHEXTRA_REDIR = 0x05;
while (reader.CurrentReadByteCount < HeaderSize)
{
var size = await reader.ReadRarVIntUInt16Async(cancellationToken: cancellationToken).ConfigureAwait(false);
var n = HeaderSize - reader.CurrentReadByteCount;
var type = await reader.ReadRarVIntUInt16Async(cancellationToken: cancellationToken).ConfigureAwait(false);
switch (type)
{
case FHEXTRA_CRYPT:
{
Rar5CryptoInfo = await Rar5CryptoInfo.CreateAsync(reader, true);
if (Rar5CryptoInfo.PswCheck.All(singleByte => singleByte == 0))
{
Rar5CryptoInfo = null;
}
}
break;
case FHEXTRA_HASH:
{
const uint FHEXTRA_HASH_BLAKE2 = 0x0;
const int BLAKE2_DIGEST_SIZE = 0x20;
if (
await reader
.ReadRarVIntUInt32Async(cancellationToken: cancellationToken)
.ConfigureAwait(false) == FHEXTRA_HASH_BLAKE2
)
{
_hash = await reader
.ReadBytesAsync(BLAKE2_DIGEST_SIZE, cancellationToken)
.ConfigureAwait(false);
}
}
break;
case FHEXTRA_HTIME:
{
var flags = await reader
.ReadRarVIntUInt16Async(cancellationToken: cancellationToken)
.ConfigureAwait(false);
var isWindowsTime = (flags & 1) == 0;
if ((flags & 0x2) == 0x2)
{
FileLastModifiedTime = await ReadExtendedTimeV5Async(
reader,
isWindowsTime,
cancellationToken
)
.ConfigureAwait(false);
}
if ((flags & 0x4) == 0x4)
{
FileCreatedTime = await ReadExtendedTimeV5Async(
reader,
isWindowsTime,
cancellationToken
)
.ConfigureAwait(false);
}
if ((flags & 0x8) == 0x8)
{
FileLastAccessedTime = await ReadExtendedTimeV5Async(
reader,
isWindowsTime,
cancellationToken
)
.ConfigureAwait(false);
}
}
break;
case FHEXTRA_REDIR:
{
RedirType = await reader
.ReadRarVIntByteAsync(cancellationToken: cancellationToken)
.ConfigureAwait(false);
RedirFlags = await reader
.ReadRarVIntByteAsync(cancellationToken: cancellationToken)
.ConfigureAwait(false);
var nn = await reader
.ReadRarVIntUInt16Async(cancellationToken: cancellationToken)
.ConfigureAwait(false);
var bb = await reader
.ReadBytesAsync(nn, cancellationToken)
.ConfigureAwait(false);
RedirTargetName = ConvertPathV5(Encoding.UTF8.GetString(bb, 0, bb.Length));
}
break;
default:
break;
}
var did = (int)(n - (HeaderSize - reader.CurrentReadByteCount));
var drain = size - did;
if (drain > 0)
{
await reader.ReadBytesAsync(drain, cancellationToken).ConfigureAwait(false);
}
}
if (AdditionalDataSize != 0)
{
CompressedSize = AdditionalDataSize;
}
}
private async ValueTask ReadFromReaderV4Async(
AsyncMarkingBinaryReader reader,
CancellationToken cancellationToken
)
{
Flags = HeaderFlags;
IsSolid = HasFlag(FileFlagsV4.SOLID);
WindowSize = IsDirectory
? 0U
: ((size_t)0x10000) << ((Flags & FileFlagsV4.WINDOW_MASK) >> 5);
var lowUncompressedSize = await reader
.ReadUInt32Async(cancellationToken)
.ConfigureAwait(false);
HostOs = await reader.ReadByteAsync(cancellationToken).ConfigureAwait(false);
FileCrc = await reader.ReadBytesAsync(4, cancellationToken).ConfigureAwait(false);
FileLastModifiedTime = Utility.DosDateToDateTime(
await reader.ReadUInt32Async(cancellationToken).ConfigureAwait(false)
);
CompressionAlgorithm = await reader.ReadByteAsync(cancellationToken).ConfigureAwait(false);
CompressionMethod = (byte)(
(await reader.ReadByteAsync(cancellationToken).ConfigureAwait(false)) - 0x30
);
var nameSize = await reader.ReadInt16Async(cancellationToken).ConfigureAwait(false);
FileAttributes = await reader.ReadUInt32Async(cancellationToken).ConfigureAwait(false);
uint highCompressedSize = 0;
uint highUncompressedkSize = 0;
if (HasFlag(FileFlagsV4.LARGE))
{
highCompressedSize = await reader
.ReadUInt32Async(cancellationToken)
.ConfigureAwait(false);
highUncompressedkSize = await reader
.ReadUInt32Async(cancellationToken)
.ConfigureAwait(false);
}
else
{
if (lowUncompressedSize == 0xffffffff)
{
lowUncompressedSize = 0xffffffff;
highUncompressedkSize = int.MaxValue;
}
}
CompressedSize = UInt32To64(highCompressedSize, checked((uint)AdditionalDataSize));
UncompressedSize = UInt32To64(highUncompressedkSize, lowUncompressedSize);
nameSize = nameSize > 4 * 1024 ? (short)(4 * 1024) : nameSize;
var fileNameBytes = await reader
.ReadBytesAsync(nameSize, cancellationToken)
.ConfigureAwait(false);
const int newLhdSize = 32;
switch (HeaderCode)
{
case HeaderCodeV.RAR4_FILE_HEADER:
{
if (HasFlag(FileFlagsV4.UNICODE))
{
var length = 0;
while (length < fileNameBytes.Length && fileNameBytes[length] != 0)
{
length++;
}
if (length != nameSize)
{
length++;
FileName = FileNameDecoder.Decode(fileNameBytes, length);
}
else
{
FileName = ArchiveEncoding.Decode(fileNameBytes);
}
}
else
{
FileName = ArchiveEncoding.Decode(fileNameBytes);
}
FileName = ConvertPathV4(FileName);
}
break;
case HeaderCodeV.RAR4_NEW_SUB_HEADER:
{
var datasize = HeaderSize - newLhdSize - nameSize;
if (HasFlag(FileFlagsV4.SALT))
{
datasize -= EncryptionConstV5.SIZE_SALT30;
}
if (datasize > 0)
{
SubData = await reader
.ReadBytesAsync(datasize, cancellationToken)
.ConfigureAwait(false);
}
if (NewSubHeaderType.SUBHEAD_TYPE_RR.Equals(fileNameBytes.Take(4).ToArray()))
{
if (SubData is null)
{
throw new InvalidFormatException();
}
RecoverySectors =
SubData[8]
+ (SubData[9] << 8)
+ (SubData[10] << 16)
+ (SubData[11] << 24);
}
}
break;
}
if (HasFlag(FileFlagsV4.SALT))
{
R4Salt = await reader.ReadBytesAsync(EncryptionConstV5.SIZE_SALT30, cancellationToken);
}
if (HasFlag(FileFlagsV4.EXT_TIME))
{
if (reader.CurrentReadByteCount >= 2)
{
var extendedFlags = await reader
.ReadUInt16Async(cancellationToken)
.ConfigureAwait(false);
if (FileLastModifiedTime is not null)
{
FileLastModifiedTime = await ProcessExtendedTimeV4Async(
extendedFlags,
FileLastModifiedTime,
reader,
0,
cancellationToken
)
.ConfigureAwait(false);
}
FileCreatedTime = await ProcessExtendedTimeV4Async(
extendedFlags,
null,
reader,
1,
cancellationToken
)
.ConfigureAwait(false);
FileLastAccessedTime = await ProcessExtendedTimeV4Async(
extendedFlags,
null,
reader,
2,
cancellationToken
)
.ConfigureAwait(false);
FileArchivedTime = await ProcessExtendedTimeV4Async(
extendedFlags,
null,
reader,
3,
cancellationToken
)
.ConfigureAwait(false);
}
}
}
private static async ValueTask<DateTime> ReadExtendedTimeV5Async(
AsyncMarkingBinaryReader reader,
bool isWindowsTime,
CancellationToken cancellationToken
)
{
if (isWindowsTime)
{
return DateTime.FromFileTime(
await reader.ReadInt64Async(cancellationToken).ConfigureAwait(false)
);
}
else
{
return Utility.UnixTimeToDateTime(
await reader.ReadUInt32Async(cancellationToken).ConfigureAwait(false)
);
}
}
private static async ValueTask<DateTime?> ProcessExtendedTimeV4Async(
ushort extendedFlags,
DateTime? time,
AsyncMarkingBinaryReader reader,
int i,
CancellationToken cancellationToken
)
{
var rmode = (uint)extendedFlags >> ((3 - i) * 4);
if ((rmode & 8) == 0)
{
return null;
}
if (i != 0)
{
var dosTime = await reader.ReadUInt32Async(cancellationToken).ConfigureAwait(false);
time = Utility.DosDateToDateTime(dosTime);
}
if ((rmode & 4) == 0 && time is not null)
{
time = time.Value.AddSeconds(1);
}
uint nanosecondHundreds = 0;
var count = (int)rmode & 3;
for (var j = 0; j < count; j++)
{
var b = await reader.ReadByteAsync(cancellationToken).ConfigureAwait(false);
nanosecondHundreds |= (((uint)b) << ((j + 3 - count) * 8));
}
if (time is not null)
{
return time.Value.AddMilliseconds(nanosecondHundreds * Math.Pow(10, -4));
}
return null;
}
public override string ToString() => FileName ?? "FileHeader";
private ushort Flags { get; set; }

View File

@@ -1,7 +1,5 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace SharpCompress.Common.Rar.Headers;
@@ -27,19 +25,6 @@ internal class MarkHeader : IRarHeader
throw new EndOfStreamException();
}
private static async Task<byte> GetByteAsync(Stream stream, CancellationToken cancellationToken)
{
var buffer = new byte[1];
var bytesRead = await stream
.ReadAsync(buffer, 0, 1, cancellationToken)
.ConfigureAwait(false);
if (bytesRead == 1)
{
return buffer[0];
}
throw new EndOfStreamException();
}
public static MarkHeader Read(Stream stream, bool leaveStreamOpen, bool lookForHeader)
{
var maxScanIndex = lookForHeader ? MAX_SFX_SIZE : 0;
@@ -144,111 +129,4 @@ internal class MarkHeader : IRarHeader
throw new InvalidFormatException("Rar signature not found");
}
public static async ValueTask<MarkHeader> ReadAsync(
Stream stream,
bool leaveStreamOpen,
bool lookForHeader,
CancellationToken cancellationToken = default
)
{
var maxScanIndex = lookForHeader ? MAX_SFX_SIZE : 0;
try
{
var start = -1;
var b = await GetByteAsync(stream, cancellationToken).ConfigureAwait(false);
start++;
while (start <= maxScanIndex)
{
if (b == 0x52)
{
b = await GetByteAsync(stream, cancellationToken).ConfigureAwait(false);
start++;
if (b == 0x61)
{
b = await GetByteAsync(stream, cancellationToken).ConfigureAwait(false);
start++;
if (b != 0x72)
{
continue;
}
b = await GetByteAsync(stream, cancellationToken).ConfigureAwait(false);
start++;
if (b != 0x21)
{
continue;
}
b = await GetByteAsync(stream, cancellationToken).ConfigureAwait(false);
start++;
if (b != 0x1a)
{
continue;
}
b = await GetByteAsync(stream, cancellationToken).ConfigureAwait(false);
start++;
if (b != 0x07)
{
continue;
}
b = await GetByteAsync(stream, cancellationToken).ConfigureAwait(false);
start++;
if (b == 1)
{
b = await GetByteAsync(stream, cancellationToken).ConfigureAwait(false);
start++;
if (b != 0)
{
continue;
}
return new MarkHeader(true); // Rar5
}
else if (b == 0)
{
return new MarkHeader(false); // Rar4
}
}
else if (b == 0x45)
{
b = await GetByteAsync(stream, cancellationToken).ConfigureAwait(false);
start++;
if (b != 0x7e)
{
continue;
}
b = await GetByteAsync(stream, cancellationToken).ConfigureAwait(false);
start++;
if (b != 0x5e)
{
continue;
}
throw new InvalidFormatException(
"Rar format version pre-4 is unsupported."
);
}
}
else
{
b = await GetByteAsync(stream, cancellationToken).ConfigureAwait(false);
start++;
}
}
}
catch (Exception e)
{
if (!leaveStreamOpen)
{
stream.Dispose();
}
throw new InvalidFormatException("Error trying to read rar signature.", e);
}
throw new InvalidFormatException("Rar signature not found");
}
}

View File

@@ -1,40 +1,17 @@
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common.Rar;
using SharpCompress.IO;
namespace SharpCompress.Common.Rar.Headers;
// ProtectHeader is part of the Recovery Record feature
internal sealed class ProtectHeader : RarHeader
{
public static ProtectHeader Create(RarHeader header, RarCrcBinaryReader reader)
public ProtectHeader(RarHeader header, RarCrcBinaryReader reader)
: base(header, reader, HeaderType.Protect)
{
var c = CreateChild<ProtectHeader>(header, reader, HeaderType.Protect);
if (c.IsRar5)
if (IsRar5)
{
throw new InvalidFormatException("unexpected rar5 record");
}
return c;
}
public static async ValueTask<ProtectHeader> CreateAsync(
RarHeader header,
AsyncRarCrcBinaryReader reader,
CancellationToken cancellationToken = default
)
{
var c = await CreateChildAsync<ProtectHeader>(
header,
reader,
HeaderType.Protect,
cancellationToken
)
.ConfigureAwait(false);
if (c.IsRar5)
{
throw new InvalidFormatException("unexpected rar5 record");
}
return c;
}
protected override void ReadFinish(MarkingBinaryReader reader)
@@ -45,17 +22,6 @@ internal sealed class ProtectHeader : RarHeader
Mark = reader.ReadBytes(8);
}
protected override async ValueTask ReadFinishAsync(
AsyncMarkingBinaryReader reader,
CancellationToken cancellationToken
)
{
Version = await reader.ReadByteAsync(cancellationToken).ConfigureAwait(false);
RecSectors = await reader.ReadUInt16Async(cancellationToken).ConfigureAwait(false);
TotalBlocks = await reader.ReadUInt32Async(cancellationToken).ConfigureAwait(false);
Mark = await reader.ReadBytesAsync(8, cancellationToken).ConfigureAwait(false);
}
internal uint DataSize => checked((uint)AdditionalDataSize);
internal byte Version { get; private set; }
internal ushort RecSectors { get; private set; }

View File

@@ -1,7 +1,4 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common.Rar;
using SharpCompress.IO;
namespace SharpCompress.Common.Rar.Headers;
@@ -10,13 +7,8 @@ namespace SharpCompress.Common.Rar.Headers;
// https://www.rarlab.com/technote.htm
internal class RarHeader : IRarHeader
{
private HeaderType _headerType;
private bool _isRar5;
protected RarHeader()
{
ArchiveEncoding = new ArchiveEncoding();
}
private readonly HeaderType _headerType;
private readonly bool _isRar5;
internal static RarHeader? TryReadBase(
RarCrcBinaryReader reader,
@@ -26,9 +18,7 @@ internal class RarHeader : IRarHeader
{
try
{
var header = new RarHeader();
header.Initialize(reader, isRar5, archiveEncoding);
return header;
return new RarHeader(reader, isRar5, archiveEncoding);
}
catch (InvalidFormatException)
{
@@ -36,32 +26,7 @@ internal class RarHeader : IRarHeader
}
}
internal static async ValueTask<RarHeader?> TryReadBaseAsync(
AsyncRarCrcBinaryReader reader,
bool isRar5,
IArchiveEncoding archiveEncoding,
CancellationToken cancellationToken = default
)
{
try
{
var header = new RarHeader();
await header
.InitializeAsync(reader, isRar5, archiveEncoding, cancellationToken)
.ConfigureAwait(false);
return header;
}
catch (InvalidFormatException)
{
return null;
}
}
private void Initialize(
RarCrcBinaryReader reader,
bool isRar5,
IArchiveEncoding archiveEncoding
)
private RarHeader(RarCrcBinaryReader reader, bool isRar5, IArchiveEncoding archiveEncoding)
{
_headerType = HeaderType.Null;
_isRar5 = isRar5;
@@ -99,128 +64,34 @@ internal class RarHeader : IRarHeader
}
}
private async ValueTask InitializeAsync(
AsyncRarCrcBinaryReader reader,
bool isRar5,
IArchiveEncoding archiveEncoding,
CancellationToken cancellationToken
)
protected RarHeader(RarHeader header, RarCrcBinaryReader reader, HeaderType headerType)
{
_headerType = HeaderType.Null;
_isRar5 = isRar5;
ArchiveEncoding = archiveEncoding;
if (IsRar5)
{
HeaderCrc = await reader.ReadUInt32Async(cancellationToken).ConfigureAwait(false);
reader.ResetCrc();
HeaderSize = (int)
await reader.ReadRarVIntUInt32Async( 3, cancellationToken).ConfigureAwait(false);
reader.Mark();
HeaderCode = await reader.ReadRarVIntByteAsync(cancellationToken: cancellationToken).ConfigureAwait(false);
HeaderFlags = await reader
.ReadRarVIntUInt16Async(2,cancellationToken)
.ConfigureAwait(false);
_headerType = headerType;
_isRar5 = header.IsRar5;
HeaderCrc = header.HeaderCrc;
HeaderCode = header.HeaderCode;
HeaderFlags = header.HeaderFlags;
HeaderSize = header.HeaderSize;
ExtraSize = header.ExtraSize;
AdditionalDataSize = header.AdditionalDataSize;
ArchiveEncoding = header.ArchiveEncoding;
ReadFinish(reader);
if (HasHeaderFlag(HeaderFlagsV5.HAS_EXTRA))
{
ExtraSize = await reader
.ReadRarVIntUInt32Async(cancellationToken: cancellationToken)
.ConfigureAwait(false);
}
if (HasHeaderFlag(HeaderFlagsV5.HAS_DATA))
{
AdditionalDataSize = (long)
await reader.ReadRarVIntAsync(cancellationToken: cancellationToken).ConfigureAwait(false);
}
}
else
{
reader.Mark();
HeaderCrc = await reader.ReadUInt16Async(cancellationToken).ConfigureAwait(false);
reader.ResetCrc();
HeaderCode = await reader.ReadByteAsync(cancellationToken).ConfigureAwait(false);
HeaderFlags = await reader.ReadUInt16Async(cancellationToken).ConfigureAwait(false);
HeaderSize = await reader.ReadInt16Async(cancellationToken).ConfigureAwait(false);
if (HasHeaderFlag(HeaderFlagsV4.HAS_DATA))
{
AdditionalDataSize = await reader
.ReadUInt32Async(cancellationToken)
.ConfigureAwait(false);
}
}
}
internal static T CreateChild<T>(
RarHeader header,
RarCrcBinaryReader reader,
HeaderType headerType
)
where T : RarHeader, new()
{
var child = new T() { ArchiveEncoding = header.ArchiveEncoding };
child._headerType = headerType;
child._isRar5 = header.IsRar5;
child.HeaderCrc = header.HeaderCrc;
child.HeaderCode = header.HeaderCode;
child.HeaderFlags = header.HeaderFlags;
child.HeaderSize = header.HeaderSize;
child.ExtraSize = header.ExtraSize;
child.AdditionalDataSize = header.AdditionalDataSize;
child.ReadFinish(reader);
var n = child.RemainingHeaderBytes(reader);
var n = RemainingHeaderBytes(reader);
if (n > 0)
{
reader.ReadBytes(n);
}
child.VerifyHeaderCrc(reader.GetCrc32());
return child;
}
internal static async ValueTask<T> CreateChildAsync<T>(
RarHeader header,
AsyncRarCrcBinaryReader reader,
HeaderType headerType,
CancellationToken cancellationToken = default
)
where T : RarHeader, new()
{
var child = new T() { ArchiveEncoding = header.ArchiveEncoding };
child._headerType = headerType;
child._isRar5 = header.IsRar5;
child.HeaderCrc = header.HeaderCrc;
child.HeaderCode = header.HeaderCode;
child.HeaderFlags = header.HeaderFlags;
child.HeaderSize = header.HeaderSize;
child.ExtraSize = header.ExtraSize;
child.AdditionalDataSize = header.AdditionalDataSize;
await child.ReadFinishAsync(reader, cancellationToken).ConfigureAwait(false);
var n = child.RemainingHeaderBytesAsync(reader);
if (n > 0)
{
await reader.ReadBytesAsync(n, cancellationToken).ConfigureAwait(false);
}
child.VerifyHeaderCrc(reader.GetCrc32());
return child;
VerifyHeaderCrc(reader.GetCrc32());
}
protected int RemainingHeaderBytes(MarkingBinaryReader reader) =>
checked(HeaderSize - (int)reader.CurrentReadByteCount);
protected int RemainingHeaderBytesAsync(AsyncMarkingBinaryReader reader) =>
checked(HeaderSize - (int)reader.CurrentReadByteCount);
protected virtual void ReadFinish(MarkingBinaryReader reader) =>
throw new NotImplementedException();
protected virtual ValueTask ReadFinishAsync(
AsyncMarkingBinaryReader reader,
CancellationToken cancellationToken = default
) => throw new NotImplementedException();
private void VerifyHeaderCrc(uint crc32)
{
var b = (IsRar5 ? crc32 : (ushort)crc32) == HeaderCrc;
@@ -232,27 +103,27 @@ internal class RarHeader : IRarHeader
public HeaderType HeaderType => _headerType;
internal bool IsRar5 => _isRar5;
protected bool IsRar5 => _isRar5;
protected uint HeaderCrc { get; private set; }
protected uint HeaderCrc { get; }
internal byte HeaderCode { get; private set; }
internal byte HeaderCode { get; }
protected ushort HeaderFlags { get; private set; }
protected ushort HeaderFlags { get; }
protected bool HasHeaderFlag(ushort flag) => (HeaderFlags & flag) == flag;
protected int HeaderSize { get; private set; }
protected int HeaderSize { get; }
internal IArchiveEncoding ArchiveEncoding { get; private set; }
internal IArchiveEncoding ArchiveEncoding { get; }
/// <summary>
/// Extra header size.
/// </summary>
protected uint ExtraSize { get; private set; }
protected uint ExtraSize { get; }
/// <summary>
/// Size of additional data (eg file contents)
/// </summary>
protected long AdditionalDataSize { get; private set; }
protected long AdditionalDataSize { get; }
}

View File

@@ -1,10 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common.Rar;
using SharpCompress.IO;
using SharpCompress.Readers;
@@ -45,37 +40,6 @@ public class RarHeaderFactory
}
}
public async IAsyncEnumerable<IRarHeader> ReadHeadersAsync(Stream stream)
{
var markHeader = await MarkHeader
.ReadAsync(
stream,
Options.LeaveStreamOpen,
Options.LookForHeader,
CancellationToken.None
)
.ConfigureAwait(false);
_isRar5 = markHeader.IsRar5;
yield return markHeader;
RarHeader? header;
while (
(
header = await TryReadNextHeaderAsync(stream, CancellationToken.None)
.ConfigureAwait(false)
) != null
)
{
yield return header;
if (header.HeaderType == HeaderType.EndArchive)
{
// End of archive marker. RAR does not read anything after this header letting to use third
// party tools to add extra information such as a digital signature to archive.
yield break;
}
}
}
private RarHeader? TryReadNextHeader(Stream stream)
{
RarCrcBinaryReader reader;
@@ -97,12 +61,12 @@ public class RarHeaderFactory
_cryptInfo.ReadInitV(new MarkingBinaryReader(stream));
var _headerKey = new CryptKey5(Options.Password!, _cryptInfo);
reader = RarCryptoBinaryReader.Create(stream, _headerKey, _cryptInfo.Salt);
reader = new RarCryptoBinaryReader(stream, _headerKey, _cryptInfo.Salt);
}
else
{
var key = new CryptKey3(Options.Password);
reader = RarCryptoBinaryReader.Create(stream, key);
reader = new RarCryptoBinaryReader(stream, key);
}
}
@@ -116,7 +80,7 @@ public class RarHeaderFactory
case HeaderCodeV.RAR5_ARCHIVE_HEADER:
case HeaderCodeV.RAR4_ARCHIVE_HEADER:
{
var ah = ArchiveHeader.Create(header, reader);
var ah = new ArchiveHeader(header, reader);
if (ah.IsEncrypted == true)
{
//!!! rar5 we don't know yet
@@ -127,7 +91,7 @@ public class RarHeaderFactory
case HeaderCodeV.RAR4_PROTECT_HEADER:
{
var ph = ProtectHeader.Create(header, reader);
var ph = new ProtectHeader(header, reader);
// skip the recovery record data, we do not use it.
switch (StreamingMode)
{
@@ -152,7 +116,7 @@ public class RarHeaderFactory
case HeaderCodeV.RAR5_SERVICE_HEADER:
{
var fh = FileHeader.Create(header, reader, HeaderType.Service);
var fh = new FileHeader(header, reader, HeaderType.Service);
if (fh.FileName == "CMT")
{
fh.PackedStream = new ReadOnlySubStream(reader.BaseStream, fh.CompressedSize);
@@ -166,7 +130,7 @@ public class RarHeaderFactory
case HeaderCodeV.RAR4_NEW_SUB_HEADER:
{
var fh = FileHeader.Create(header, reader, HeaderType.NewSub);
var fh = new FileHeader(header, reader, HeaderType.NewSub);
SkipData(fh, reader);
return fh;
}
@@ -174,7 +138,7 @@ public class RarHeaderFactory
case HeaderCodeV.RAR5_FILE_HEADER:
case HeaderCodeV.RAR4_FILE_HEADER:
{
var fh = FileHeader.Create(header, reader, HeaderType.File);
var fh = new FileHeader(header, reader, HeaderType.File);
switch (StreamingMode)
{
case StreamingMode.Seekable:
@@ -217,189 +181,11 @@ public class RarHeaderFactory
case HeaderCodeV.RAR5_END_ARCHIVE_HEADER:
case HeaderCodeV.RAR4_END_ARCHIVE_HEADER:
{
return EndArchiveHeader.Create(header, reader);
return new EndArchiveHeader(header, reader);
}
case HeaderCodeV.RAR5_ARCHIVE_ENCRYPTION_HEADER:
{
var cryptoHeader = ArchiveCryptHeader.Create(header, reader);
IsEncrypted = true;
_cryptInfo = cryptoHeader.CryptInfo;
return cryptoHeader;
}
default:
{
throw new InvalidFormatException("Unknown Rar Header: " + header.HeaderCode);
}
}
}
private async ValueTask<RarHeader?> TryReadNextHeaderAsync(
Stream stream,
CancellationToken cancellationToken
)
{
AsyncRarCrcBinaryReader reader;
if (!IsEncrypted)
{
reader = new AsyncRarCrcBinaryReader(stream);
}
else
{
if (Options.Password is null)
{
throw new CryptographicException(
"Encrypted Rar archive has no password specified."
);
}
if (_isRar5 && _cryptInfo != null)
{
await _cryptInfo.ReadInitVAsync(new AsyncMarkingBinaryReader(stream));
var _headerKey = new CryptKey5(Options.Password!, _cryptInfo);
reader = await AsyncRarCryptoBinaryReader.Create(stream, _headerKey, _cryptInfo.Salt);
}
else
{
var key = new CryptKey3(Options.Password);
reader = await AsyncRarCryptoBinaryReader.Create(stream, key);
}
}
var header = await RarHeader
.TryReadBaseAsync(reader, _isRar5, Options.ArchiveEncoding, cancellationToken)
.ConfigureAwait(false);
if (header is null)
{
return null;
}
switch (header.HeaderCode)
{
case HeaderCodeV.RAR5_ARCHIVE_HEADER:
case HeaderCodeV.RAR4_ARCHIVE_HEADER:
{
var ah = await ArchiveHeader
.CreateAsync(header, reader, cancellationToken)
.ConfigureAwait(false);
if (ah.IsEncrypted == true)
{
//!!! rar5 we don't know yet
IsEncrypted = true;
}
return ah;
}
case HeaderCodeV.RAR4_PROTECT_HEADER:
{
var ph = await ProtectHeader
.CreateAsync(header, reader, cancellationToken)
.ConfigureAwait(false);
// skip the recovery record data, we do not use it.
switch (StreamingMode)
{
case StreamingMode.Seekable:
{
reader.BaseStream.Position += ph.DataSize;
}
break;
case StreamingMode.Streaming:
{
await reader
.BaseStream.SkipAsync(ph.DataSize, cancellationToken)
.ConfigureAwait(false);
}
break;
default:
{
throw new InvalidFormatException("Invalid StreamingMode");
}
}
return ph;
}
case HeaderCodeV.RAR5_SERVICE_HEADER:
{
var fh = await FileHeader
.CreateAsync(header, reader, HeaderType.Service, cancellationToken)
.ConfigureAwait(false);
if (fh.FileName == "CMT")
{
fh.PackedStream = new ReadOnlySubStream(reader.BaseStream, fh.CompressedSize);
}
else
{
await SkipDataAsync(fh, reader, cancellationToken).ConfigureAwait(false);
}
return fh;
}
case HeaderCodeV.RAR4_NEW_SUB_HEADER:
{
var fh = await FileHeader
.CreateAsync(header, reader, HeaderType.NewSub, cancellationToken)
.ConfigureAwait(false);
await SkipDataAsync(fh, reader, cancellationToken).ConfigureAwait(false);
return fh;
}
case HeaderCodeV.RAR5_FILE_HEADER:
case HeaderCodeV.RAR4_FILE_HEADER:
{
var fh = await FileHeader
.CreateAsync(header, reader, HeaderType.File, cancellationToken)
.ConfigureAwait(false);
switch (StreamingMode)
{
case StreamingMode.Seekable:
{
fh.DataStartPosition = reader.BaseStream.Position;
reader.BaseStream.Position += fh.CompressedSize;
}
break;
case StreamingMode.Streaming:
{
var ms = new ReadOnlySubStream(reader.BaseStream, fh.CompressedSize);
if (fh.R4Salt is null && fh.Rar5CryptoInfo is null)
{
fh.PackedStream = ms;
}
else
{
fh.PackedStream = new RarCryptoWrapper(
ms,
fh.R4Salt is null
? fh.Rar5CryptoInfo.NotNull().Salt
: fh.R4Salt,
fh.R4Salt is null
? new CryptKey5(
Options.Password,
fh.Rar5CryptoInfo.NotNull()
)
: new CryptKey3(Options.Password)
);
}
}
break;
default:
{
throw new InvalidFormatException("Invalid StreamingMode");
}
}
return fh;
}
case HeaderCodeV.RAR5_END_ARCHIVE_HEADER:
case HeaderCodeV.RAR4_END_ARCHIVE_HEADER:
{
return await EndArchiveHeader
.CreateAsync(header, reader, cancellationToken)
.ConfigureAwait(false);
}
case HeaderCodeV.RAR5_ARCHIVE_ENCRYPTION_HEADER:
{
var cryptoHeader = await ArchiveCryptHeader
.CreateAsync(header, reader, cancellationToken)
.ConfigureAwait(false);
var cryptoHeader = new ArchiveCryptHeader(header, reader);
IsEncrypted = true;
_cryptInfo = cryptoHeader.CryptInfo;
@@ -434,33 +220,4 @@ public class RarHeaderFactory
}
}
}
private async ValueTask SkipDataAsync(
FileHeader fh,
AsyncRarCrcBinaryReader reader,
CancellationToken cancellationToken
)
{
switch (StreamingMode)
{
case StreamingMode.Seekable:
{
fh.DataStartPosition = reader.BaseStream.Position;
reader.BaseStream.Position += fh.CompressedSize;
}
break;
case StreamingMode.Streaming:
{
//skip the data because it's useless?
await reader
.BaseStream.SkipAsync(fh.CompressedSize, cancellationToken)
.ConfigureAwait(false);
}
break;
default:
{
throw new InvalidFormatException("Invalid StreamingMode");
}
}
}
}

View File

@@ -4,14 +4,13 @@ namespace SharpCompress.Common.Rar.Headers;
internal class SignHeader : RarHeader
{
public static SignHeader Create(RarHeader header, RarCrcBinaryReader reader)
protected SignHeader(RarHeader header, RarCrcBinaryReader reader)
: base(header, reader, HeaderType.Sign)
{
var c = CreateChild<SignHeader>(header, reader, HeaderType.Sign);
if (c.IsRar5)
if (IsRar5)
{
throw new InvalidFormatException("unexpected rar5 record");
}
return c;
}
protected override void ReadFinish(MarkingBinaryReader reader)

View File

@@ -1,7 +1,5 @@
using System;
using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common.Rar.Headers;
using SharpCompress.IO;
@@ -9,90 +7,44 @@ namespace SharpCompress.Common.Rar;
internal class Rar5CryptoInfo
{
private Rar5CryptoInfo() { }
public Rar5CryptoInfo() { }
public static Rar5CryptoInfo Create(MarkingBinaryReader reader, bool readInitV)
public Rar5CryptoInfo(MarkingBinaryReader reader, bool readInitV)
{
var cryptoInfo = new Rar5CryptoInfo();
var cryptVersion = reader.ReadRarVIntUInt32();
if (cryptVersion > EncryptionConstV5.VERSION)
{
throw new CryptographicException($"Unsupported crypto version of {cryptVersion}");
}
var encryptionFlags = reader.ReadRarVIntUInt32();
cryptoInfo.UsePswCheck = FlagUtility.HasFlag(encryptionFlags, EncryptionFlagsV5.CHFL_CRYPT_PSWCHECK);
cryptoInfo.LG2Count = reader.ReadRarVIntByte(1);
UsePswCheck = FlagUtility.HasFlag(encryptionFlags, EncryptionFlagsV5.CHFL_CRYPT_PSWCHECK);
LG2Count = reader.ReadRarVIntByte(1);
if (cryptoInfo.LG2Count > EncryptionConstV5.CRYPT5_KDF_LG2_COUNT_MAX)
if (LG2Count > EncryptionConstV5.CRYPT5_KDF_LG2_COUNT_MAX)
{
throw new CryptographicException($"Unsupported LG2 count of {cryptoInfo.LG2Count}.");
throw new CryptographicException($"Unsupported LG2 count of {LG2Count}.");
}
cryptoInfo.Salt = reader.ReadBytes(EncryptionConstV5.SIZE_SALT50);
Salt = reader.ReadBytes(EncryptionConstV5.SIZE_SALT50);
if (readInitV) // File header needs to read IV here
{
cryptoInfo.ReadInitV(reader);
ReadInitV(reader);
}
if (cryptoInfo.UsePswCheck)
if (UsePswCheck)
{
cryptoInfo.PswCheck = reader.ReadBytes(EncryptionConstV5.SIZE_PSWCHECK);
PswCheck = reader.ReadBytes(EncryptionConstV5.SIZE_PSWCHECK);
var _pswCheckCsm = reader.ReadBytes(EncryptionConstV5.SIZE_PSWCHECK_CSUM);
var sha = SHA256.Create();
cryptoInfo.UsePswCheck = sha.ComputeHash(cryptoInfo.PswCheck).AsSpan().StartsWith(_pswCheckCsm.AsSpan());
UsePswCheck = sha.ComputeHash(PswCheck).AsSpan().StartsWith(_pswCheckCsm.AsSpan());
}
return cryptoInfo;
}
public static async ValueTask<Rar5CryptoInfo> CreateAsync(AsyncMarkingBinaryReader reader, bool readInitV)
{
var cryptoInfo = new Rar5CryptoInfo();
var cryptVersion =
await reader.ReadRarVIntUInt32Async(cancellationToken: CancellationToken.None);
if (cryptVersion > EncryptionConstV5.VERSION)
{
throw new CryptographicException($"Unsupported crypto version of {cryptVersion}");
}
var encryptionFlags =
await reader.ReadRarVIntUInt32Async(cancellationToken: CancellationToken.None);
cryptoInfo.UsePswCheck = FlagUtility.HasFlag(encryptionFlags, EncryptionFlagsV5.CHFL_CRYPT_PSWCHECK);
cryptoInfo.LG2Count = (int)
await reader.ReadRarVIntUInt32Async(cancellationToken: CancellationToken.None);
if (cryptoInfo.LG2Count > EncryptionConstV5.CRYPT5_KDF_LG2_COUNT_MAX)
{
throw new CryptographicException($"Unsupported LG2 count of {cryptoInfo.LG2Count}.");
}
cryptoInfo.Salt = await reader
.ReadBytesAsync(EncryptionConstV5.SIZE_SALT50, CancellationToken.None);
if (readInitV)
{
await cryptoInfo.ReadInitVAsync(reader);
}
if (cryptoInfo.UsePswCheck)
{
cryptoInfo.PswCheck = await reader
.ReadBytesAsync(EncryptionConstV5.SIZE_PSWCHECK, CancellationToken.None);
var _pswCheckCsm = await reader
.ReadBytesAsync(EncryptionConstV5.SIZE_PSWCHECK_CSUM, CancellationToken.None);
var sha = SHA256.Create();
cryptoInfo.UsePswCheck = sha.ComputeHash(cryptoInfo.PswCheck).AsSpan().StartsWith(_pswCheckCsm.AsSpan());
}
return cryptoInfo;
}
public void ReadInitV(MarkingBinaryReader reader) =>
InitV = reader.ReadBytes(EncryptionConstV5.SIZE_INITV);
public async ValueTask ReadInitVAsync(AsyncMarkingBinaryReader reader) =>
InitV = await reader
.ReadBytesAsync(EncryptionConstV5.SIZE_INITV, CancellationToken.None);
public bool UsePswCheck = false;
public int LG2Count = 0;

View File

@@ -1,4 +1,4 @@
#nullable disable
using System.Collections.Generic;
using System.IO;
@@ -9,26 +9,20 @@ namespace SharpCompress.Common.Rar;
internal sealed class RarCryptoBinaryReader : RarCrcBinaryReader
{
private BlockTransformer _rijndael = default!;
private BlockTransformer _rijndael;
private readonly Queue<byte> _data = new();
private long _readCount;
private RarCryptoBinaryReader(Stream stream)
public RarCryptoBinaryReader(Stream stream, ICryptKey cryptKey)
: base(stream)
{
var salt = base.ReadBytes(EncryptionConstV5.SIZE_SALT30);
_readCount += EncryptionConstV5.SIZE_SALT30;
_rijndael = new BlockTransformer(cryptKey.Transformer(salt));
}
public static RarCryptoBinaryReader Create(Stream stream, ICryptKey cryptKey, byte[]? salt = null)
{
var binary = new RarCryptoBinaryReader(stream);
if (salt == null)
{
salt = binary.ReadBytesBase(EncryptionConstV5.SIZE_SALT30);
binary._readCount += EncryptionConstV5.SIZE_SALT30;
}
binary._rijndael = new BlockTransformer(cryptKey.Transformer(salt));
return binary;
}
public RarCryptoBinaryReader(Stream stream, ICryptKey cryptKey, byte[] salt)
: base(stream) => _rijndael = new BlockTransformer(cryptKey.Transformer(salt));
// track read count ourselves rather than using the underlying stream since we buffer
public override long CurrentReadByteCount
@@ -46,8 +40,6 @@ internal sealed class RarCryptoBinaryReader : RarCrcBinaryReader
public override byte[] ReadBytes(int count) => ReadAndDecryptBytes(count);
private byte[] ReadBytesBase(int count) => base.ReadBytes(count);
private byte[] ReadAndDecryptBytes(int count)
{
var queueSize = _data.Count;

View File

@@ -2,10 +2,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common.Rar.Headers;
using SharpCompress.IO;
using SharpCompress.Readers;
@@ -29,8 +26,6 @@ public abstract class RarVolume : Volume
internal abstract IEnumerable<RarFilePart> ReadFileParts();
internal abstract IAsyncEnumerable<RarFilePart> ReadFilePartsAsync();
internal abstract RarFilePart CreateFilePart(MarkHeader markHeader, FileHeader fileHeader);
internal IEnumerable<RarFilePart> GetVolumeFileParts()
@@ -76,59 +71,16 @@ public abstract class RarVolume : Volume
}
}
internal async IAsyncEnumerable<RarFilePart> GetVolumeFilePartsAsync([EnumeratorCancellation] CancellationToken cancellationToken = default)
{
MarkHeader? lastMarkHeader = null;
await foreach (var header in _headerFactory.ReadHeadersAsync(Stream).WithCancellation(cancellationToken))
{
switch (header.HeaderType)
{
case HeaderType.Mark:
{
lastMarkHeader = (MarkHeader)header;
}
break;
case HeaderType.Archive:
{
ArchiveHeader = (ArchiveHeader)header;
}
break;
case HeaderType.File:
{
var fh = (FileHeader)header;
if (_maxCompressionAlgorithm < fh.CompressionAlgorithm)
{
_maxCompressionAlgorithm = fh.CompressionAlgorithm;
}
yield return CreateFilePart(lastMarkHeader!, fh);
}
break;
case HeaderType.Service:
{
var fh = (FileHeader)header;
if (fh.FileName == "CMT")
{
var buffer = new byte[fh.CompressedSize];
fh.PackedStream.NotNull().ReadFully(buffer);
Comment = Encoding.UTF8.GetString(buffer, 0, buffer.Length - 1);
}
}
break;
}
}
}
private void EnsureArchiveHeaderLoaded()
{
if (ArchiveHeader is null)
{
if (Mode == StreamingMode.Streaming)
{
throw new InvalidOperationException(
{
throw new InvalidOperationException(
"ArchiveHeader should never been null in a streaming read."
);
}
);
}
// we only want to load the archive header to avoid overhead but have to do the nasty thing and reset the stream
GetVolumeFileParts().First();
@@ -174,12 +126,6 @@ public abstract class RarVolume : Volume
}
}
public async ValueTask<bool> IsSolidArchiveAsync(CancellationToken cancellationToken = default)
{
await EnsureArchiveHeaderLoadedAsync(cancellationToken);
return ArchiveHeader?.IsSolid ?? false;
}
public int MinVersion
{
get
@@ -228,68 +174,5 @@ public abstract class RarVolume : Volume
}
}
private async ValueTask EnsureArchiveHeaderLoadedAsync(CancellationToken cancellationToken)
{
if (ArchiveHeader is null)
{
if (Mode == StreamingMode.Streaming)
{
throw new InvalidOperationException(
"ArchiveHeader should never been null in a streaming read."
);
}
// we only want to load the archive header to avoid overhead but have to do the nasty thing and reset the stream
await GetVolumeFilePartsAsync(cancellationToken).FirstAsync();
Stream.Position = 0;
}
}
public virtual async ValueTask<int> MinVersionAsync(
CancellationToken cancellationToken = default
)
{
await EnsureArchiveHeaderLoadedAsync(cancellationToken).ConfigureAwait(false);
if (_maxCompressionAlgorithm >= 50)
{
return 5; //5-6
}
else if (_maxCompressionAlgorithm >= 29)
{
return 3; //3-4
}
else if (_maxCompressionAlgorithm >= 20)
{
return 2; //2
}
else
{
return 1;
}
}
public virtual async ValueTask<int> MaxVersionAsync(
CancellationToken cancellationToken = default
)
{
await EnsureArchiveHeaderLoadedAsync(cancellationToken).ConfigureAwait(false);
if (_maxCompressionAlgorithm >= 50)
{
return 6; //5-6
}
else if (_maxCompressionAlgorithm >= 29)
{
return 4; //3-4
}
else if (_maxCompressionAlgorithm >= 20)
{
return 2; //2
}
else
{
return 1;
}
}
public string? Comment { get; internal set; }
}

View File

@@ -5,8 +5,6 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Compressors.LZMA;
using SharpCompress.Compressors.LZMA.Utilites;
using SharpCompress.IO;
@@ -1272,46 +1270,6 @@ internal class ArchiveReader
_stream = stream;
}
public async Task OpenAsync(
Stream stream,
bool lookForHeader,
CancellationToken cancellationToken = default
)
{
Close();
_streamOrigin = stream.Position;
_streamEnding = stream.Length;
var canScan = lookForHeader ? 0x80000 - 20 : 0;
while (true)
{
// TODO: Check Signature!
_header = new byte[0x20];
await stream.ReadExactAsync(_header, 0, 0x20, cancellationToken);
if (
!lookForHeader
|| _header
.AsSpan(0, length: 6)
.SequenceEqual<byte>([0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C])
)
{
break;
}
if (canScan == 0)
{
throw new InvalidFormatException("Unable to find 7z signature");
}
canScan--;
stream.Position = ++_streamOrigin;
}
_stream = stream;
}
public void Close()
{
_stream?.Dispose();
@@ -1425,110 +1383,6 @@ internal class ArchiveReader
return db;
}
public async Task<ArchiveDatabase> ReadDatabaseAsync(
IPasswordProvider pass,
CancellationToken cancellationToken = default
)
{
var db = new ArchiveDatabase(pass);
db.Clear();
db._majorVersion = _header[6];
db._minorVersion = _header[7];
if (db._majorVersion != 0)
{
throw new InvalidOperationException();
}
var crcFromArchive = DataReader.Get32(_header, 8);
var nextHeaderOffset = (long)DataReader.Get64(_header, 0xC);
var nextHeaderSize = (long)DataReader.Get64(_header, 0x14);
var nextHeaderCrc = DataReader.Get32(_header, 0x1C);
var crc = Crc.INIT_CRC;
crc = Crc.Update(crc, nextHeaderOffset);
crc = Crc.Update(crc, nextHeaderSize);
crc = Crc.Update(crc, nextHeaderCrc);
crc = Crc.Finish(crc);
if (crc != crcFromArchive)
{
throw new InvalidOperationException();
}
db._startPositionAfterHeader = _streamOrigin + 0x20;
// empty header is ok
if (nextHeaderSize == 0)
{
db.Fill();
return db;
}
if (nextHeaderOffset < 0 || nextHeaderSize < 0 || nextHeaderSize > int.MaxValue)
{
throw new InvalidOperationException();
}
if (nextHeaderOffset > _streamEnding - db._startPositionAfterHeader)
{
throw new InvalidOperationException("nextHeaderOffset is invalid");
}
_stream.Seek(nextHeaderOffset, SeekOrigin.Current);
var header = new byte[nextHeaderSize];
await _stream.ReadExactAsync(header, 0, header.Length, cancellationToken);
if (Crc.Finish(Crc.Update(Crc.INIT_CRC, header, 0, header.Length)) != nextHeaderCrc)
{
throw new InvalidOperationException();
}
using (var streamSwitch = new CStreamSwitch())
{
streamSwitch.Set(this, header);
var type = ReadId();
if (type != BlockType.Header)
{
if (type != BlockType.EncodedHeader)
{
throw new InvalidOperationException();
}
var dataVector = ReadAndDecodePackedStreams(
db._startPositionAfterHeader,
db.PasswordProvider
);
// compressed header without content is odd but ok
if (dataVector.Count == 0)
{
db.Fill();
return db;
}
if (dataVector.Count != 1)
{
throw new InvalidOperationException();
}
streamSwitch.Set(this, dataVector[0]);
if (ReadId() != BlockType.Header)
{
throw new InvalidOperationException();
}
}
ReadHeader(db, db.PasswordProvider);
}
db.Fill();
return db;
}
internal class CExtractFolderInfo
{
internal int _fileIndex;

View File

@@ -1,10 +1,8 @@
using System;
using System.Buffers;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
namespace SharpCompress.Common.Tar.Headers;
@@ -497,90 +495,6 @@ internal sealed class TarHeader
return true;
}
internal async Task<bool> ReadAsync(AsyncBinaryReader reader)
{
string? longName = null;
string? longLinkName = null;
var hasLongValue = true;
byte[] buffer;
EntryType entryType;
do
{
buffer = await ReadBlockAsync(reader);
if (buffer.Length == 0)
{
return false;
}
entryType = ReadEntryType(buffer);
// LongName and LongLink headers can follow each other and need
// to apply to the header that follows them.
if (entryType == EntryType.LongName)
{
longName = await ReadLongNameAsync(reader, buffer);
continue;
}
else if (entryType == EntryType.LongLink)
{
longLinkName = await ReadLongNameAsync(reader, buffer);
continue;
}
hasLongValue = false;
} while (hasLongValue);
// Check header checksum
if (!checkChecksum(buffer))
{
return false;
}
Name = longName ?? ArchiveEncoding.Decode(buffer, 0, 100).TrimNulls();
EntryType = entryType;
Size = ReadSize(buffer);
// for symlinks, additionally read the linkname
if (entryType == EntryType.SymLink || entryType == EntryType.HardLink)
{
LinkName = longLinkName ?? ArchiveEncoding.Decode(buffer, 157, 100).TrimNulls();
}
Mode = ReadAsciiInt64Base8(buffer, 100, 7);
if (entryType == EntryType.Directory)
{
Mode |= 0b1_000_000_000;
}
UserId = ReadAsciiInt64Base8oldGnu(buffer, 108, 7);
GroupId = ReadAsciiInt64Base8oldGnu(buffer, 116, 7);
var unixTimeStamp = ReadAsciiInt64Base8(buffer, 136, 11);
LastModifiedTime = EPOCH.AddSeconds(unixTimeStamp).ToLocalTime();
Magic = ArchiveEncoding.Decode(buffer, 257, 6).TrimNulls();
if (!string.IsNullOrEmpty(Magic) && "ustar".Equals(Magic))
{
var namePrefix = ArchiveEncoding.Decode(buffer, 345, 157).TrimNulls();
if (!string.IsNullOrEmpty(namePrefix))
{
Name = namePrefix + "/" + Name;
}
}
if (entryType != EntryType.LongName && Name.Length == 0)
{
return false;
}
return true;
}
internal static int RecalculateChecksum(byte[] buf)
{
// Set default value for checksum. That is 8 spaces.
@@ -616,65 +530,4 @@ internal sealed class TarHeader
public long? DataStartPosition { get; set; }
public string? Magic { get; set; }
private static async ValueTask<byte[]> ReadBlockAsync(AsyncBinaryReader reader)
{
var buffer = ArrayPool<byte>.Shared.Rent(BLOCK_SIZE);
try
{
await reader.ReadBytesAsync(buffer, 0, BLOCK_SIZE);
if (buffer.Length != 0 && buffer.Length < BLOCK_SIZE)
{
throw new InvalidFormatException("Buffer is invalid size");
}
return buffer;
}
finally
{
ArrayPool<byte>.Shared.Return(buffer);
}
}
private async Task<string> ReadLongNameAsync(AsyncBinaryReader reader, byte[] buffer)
{
var size = ReadSize(buffer);
// Validate size to prevent memory exhaustion from malformed headers
if (size < 0 || size > MAX_LONG_NAME_SIZE)
{
throw new InvalidFormatException(
$"Long name size {size} is invalid or exceeds maximum allowed size of {MAX_LONG_NAME_SIZE} bytes"
);
}
var nameLength = (int)size;
var nameBytes = ArrayPool<byte>.Shared.Rent(nameLength);
try
{
await reader.ReadBytesAsync(buffer, 0, nameLength);
var remainingBytesToRead = BLOCK_SIZE - (nameLength % BLOCK_SIZE);
// Read the rest of the block and discard the data
if (remainingBytesToRead < BLOCK_SIZE)
{
var remainingBytes = ArrayPool<byte>.Shared.Rent(remainingBytesToRead);
try
{
await reader.ReadBytesAsync(remainingBytes, 0, remainingBytesToRead);
}
finally
{
ArrayPool<byte>.Shared.Return(nameBytes);
}
}
return ArchiveEncoding.Decode(nameBytes, 0, nameLength).TrimNulls();
}
finally
{
ArrayPool<byte>.Shared.Return(nameBytes);
}
}
}

View File

@@ -18,7 +18,7 @@ internal static class TarHeaderFactory
TarHeader? header = null;
try
{
var reader = new BinaryReader(stream, archiveEncoding.Default, leaveOpen: false);
var reader = new BinaryReader(stream);
header = new TarHeader(archiveEncoding);
if (!header.Read(reader))
@@ -54,52 +54,6 @@ internal static class TarHeaderFactory
}
}
internal static async IAsyncEnumerable<TarHeader?> ReadHeaderAsync(
StreamingMode mode,
Stream stream,
IArchiveEncoding archiveEncoding
)
{
while (true)
{
TarHeader? header = null;
try
{
var reader = new AsyncBinaryReader(stream, false);
header = new TarHeader(archiveEncoding);
if (!await header.ReadAsync(reader))
{
yield break;
}
switch (mode)
{
case StreamingMode.Seekable:
{
header.DataStartPosition = stream.Position;
//skip to nearest 512
stream.Position += PadTo512(header.Size);
}
break;
case StreamingMode.Streaming:
{
header.PackedStream = new TarReadOnlySubStream(stream, header.Size);
}
break;
default:
{
throw new InvalidFormatException("Invalid StreamingMode");
}
}
}
catch
{
header = null;
}
yield return header;
}
}
private static long PadTo512(long size)
{
var zeros = (int)(size % 512);

View File

@@ -56,7 +56,7 @@ internal class DirectoryEntryHeader : ZipFileEntry
var name = new byte[nameLength];
var extra = new byte[extraLength];
var comment = new byte[commentLength];
await reader.ReadBytesAsync(name, 0, nameLength);
await reader.ReadBytesAsync(name,0 ,nameLength);
await reader.ReadBytesAsync(extra, 0, extraLength);
await reader.ReadBytesAsync(comment, 0, commentLength);

View File

@@ -39,8 +39,8 @@ internal class LocalEntryHeader(IArchiveEncoding archiveEncoding)
var extraLength = await reader.ReadUInt16Async();
var name = new byte[nameLength];
var extra = new byte[extraLength];
await reader.ReadBytesAsync(name, 0, nameLength);
await reader.ReadBytesAsync(extra, 0, extraLength);
await reader.ReadBytesAsync(name,0 ,nameLength);
await reader.ReadBytesAsync(extra, 0, extraLength);
ProcessReadData(name, extra);
}

View File

@@ -39,10 +39,11 @@ internal class Zip64DirectoryEndHeader : ZipHeader
DirectorySize = (long)await reader.ReadUInt64Async();
DirectoryStartOffsetRelativeToDisk = (long)await reader.ReadUInt64Async();
var size = (int)(
SizeOfDirectoryEndRecord - SIZE_OF_FIXED_HEADER_DATA_EXCEPT_SIGNATURE_AND_SIZE_FIELDS
SizeOfDirectoryEndRecord
- SIZE_OF_FIXED_HEADER_DATA_EXCEPT_SIGNATURE_AND_SIZE_FIELDS
);
DataSector = new byte[size];
await reader.ReadBytesAsync(DataSector, 0, size);
await reader.ReadBytesAsync(DataSector, 0, size);
}
private const int SIZE_OF_FIXED_HEADER_DATA_EXCEPT_SIGNATURE_AND_SIZE_FIELDS = 44;

View File

@@ -156,6 +156,7 @@ internal sealed class SeekableZipHeaderFactory : ZipHeaderFactory
}
}
private static bool IsMatch(Span<byte> haystack, int position, byte[] needle)
{
for (var i = 0; i < needle.Length; i++)

View File

@@ -1,7 +1,5 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace SharpCompress.Compressors.Arj
{
@@ -62,53 +60,6 @@ namespace SharpCompress.Compressors.Arj
return result;
}
/// <summary>
/// Asynchronously reads a single bit from the stream. Returns 0 or 1.
/// </summary>
public async Task<int> ReadBitAsync(CancellationToken cancellationToken)
{
if (_bitCount == 0)
{
var buffer = new byte[1];
int bytesRead = await _input
.ReadAsync(buffer, 0, 1, cancellationToken)
.ConfigureAwait(false);
if (bytesRead < 1)
{
throw new EndOfStreamException("No more data available in BitReader.");
}
_bitBuffer = buffer[0];
_bitCount = 8;
}
int bit = (_bitBuffer >> (_bitCount - 1)) & 1;
_bitCount--;
return bit;
}
/// <summary>
/// Asynchronously reads n bits (up to 32) from the stream.
/// </summary>
public async Task<int> ReadBitsAsync(int count, CancellationToken cancellationToken)
{
if (count < 0 || count > 32)
{
throw new ArgumentOutOfRangeException(
nameof(count),
"Count must be between 0 and 32."
);
}
int result = 0;
for (int i = 0; i < count; i++)
{
result =
(result << 1) | await ReadBitAsync(cancellationToken).ConfigureAwait(false);
}
return result;
}
/// <summary>
/// Resets any buffered bits.
/// </summary>

View File

@@ -1,8 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.IO;
namespace SharpCompress.Compressors.Arj
@@ -116,51 +114,6 @@ namespace SharpCompress.Compressors.Arj
return true;
}
/// <summary>
/// Asynchronously decodes a single element (literal or back-reference) and appends it to _buffer.
/// Returns true if data was added, or false if all input has already been decoded.
/// </summary>
private async Task<bool> DecodeNextAsync(CancellationToken cancellationToken)
{
if (_buffer.Count >= _originalSize)
{
_finishedDecoding = true;
return false;
}
int len = await DecodeValAsync(0, 7, cancellationToken).ConfigureAwait(false);
if (len == 0)
{
byte nextChar = (byte)
await _bitReader.ReadBitsAsync(8, cancellationToken).ConfigureAwait(false);
_buffer.Add(nextChar);
}
else
{
int repCount = len + THRESHOLD - 1;
int backPtr = await DecodeValAsync(9, 13, cancellationToken).ConfigureAwait(false);
if (backPtr >= _buffer.Count)
throw new InvalidDataException("Invalid back_ptr in LH stream");
int srcIndex = _buffer.Count - 1 - backPtr;
for (int j = 0; j < repCount && _buffer.Count < _originalSize; j++)
{
byte b = _buffer[srcIndex];
_buffer.Add(b);
srcIndex++;
// srcIndex may grow; it's allowed (source region can overlap destination)
}
}
if (_buffer.Count >= _originalSize)
{
_finishedDecoding = true;
}
return true;
}
private int DecodeVal(int from, int to)
{
int add = 0;
@@ -176,31 +129,6 @@ namespace SharpCompress.Compressors.Arj
return res + add;
}
private async Task<int> DecodeValAsync(
int from,
int to,
CancellationToken cancellationToken
)
{
int add = 0;
int bit = from;
while (
bit < to
&& await _bitReader.ReadBitsAsync(1, cancellationToken).ConfigureAwait(false) == 1
)
{
add |= 1 << bit;
bit++;
}
int res =
bit > 0
? await _bitReader.ReadBitsAsync(bit, cancellationToken).ConfigureAwait(false)
: 0;
return res + add;
}
/// <summary>
/// Reads decompressed bytes into buffer[offset..offset+count].
/// The method decodes additional data on demand when needed.
@@ -250,109 +178,6 @@ namespace SharpCompress.Compressors.Arj
return totalRead;
}
public override async Task<int> ReadAsync(
byte[] buffer,
int offset,
int count,
CancellationToken cancellationToken
)
{
if (_disposed)
throw new ObjectDisposedException(nameof(LHDecoderStream));
if (buffer is null)
throw new ArgumentNullException(nameof(buffer));
if (offset < 0 || count < 0 || offset + count > buffer.Length)
throw new ArgumentOutOfRangeException("offset/count");
if (_readPosition >= _originalSize)
return 0; // EOF
int totalRead = 0;
while (totalRead < count && _readPosition < _originalSize)
{
cancellationToken.ThrowIfCancellationRequested();
if (_readPosition >= _buffer.Count)
{
bool had = await DecodeNextAsync(cancellationToken).ConfigureAwait(false);
if (!had)
{
break;
}
}
int available = _buffer.Count - (int)_readPosition;
if (available <= 0)
{
if (!_finishedDecoding)
{
continue;
}
break;
}
int toCopy = Math.Min(available, count - totalRead);
_buffer.CopyTo((int)_readPosition, buffer, offset + totalRead, toCopy);
_readPosition += toCopy;
totalRead += toCopy;
}
return totalRead;
}
#if !NETFRAMEWORK && !NETSTANDARD2_0
public override async ValueTask<int> ReadAsync(
Memory<byte> buffer,
CancellationToken cancellationToken = default
)
{
if (_disposed)
throw new ObjectDisposedException(nameof(LHDecoderStream));
if (_readPosition >= _originalSize)
return 0; // EOF
int totalRead = 0;
while (totalRead < buffer.Length && _readPosition < _originalSize)
{
cancellationToken.ThrowIfCancellationRequested();
if (_readPosition >= _buffer.Count)
{
bool had = await DecodeNextAsync(cancellationToken).ConfigureAwait(false);
if (!had)
{
break;
}
}
int available = _buffer.Count - (int)_readPosition;
if (available <= 0)
{
if (!_finishedDecoding)
{
continue;
}
break;
}
int toCopy = Math.Min(available, buffer.Length - totalRead);
for (int i = 0; i < toCopy; i++)
{
buffer.Span[totalRead + i] = _buffer[(int)_readPosition + i];
}
_readPosition += toCopy;
totalRead += toCopy;
}
return totalRead;
}
#endif
public override void Flush() => throw new NotSupportedException();
public override long Seek(long offset, SeekOrigin origin) =>

View File

@@ -149,25 +149,4 @@ public sealed class BZip2Stream : Stream, IStreamStack
}
return true;
}
/// <summary>
/// Asynchronously consumes two bytes to test if there is a BZip2 header
/// </summary>
/// <param name="stream"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public static async ValueTask<bool> IsBZip2Async(
Stream stream,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
var buffer = new byte[2];
var bytesRead = await stream.ReadAsync(buffer, 0, 2, cancellationToken);
if (bytesRead < 2 || buffer[0] != 'B' || buffer[1] != 'Z')
{
return false;
}
return true;
}
}

View File

@@ -776,16 +776,11 @@ internal sealed class InflateBlocks
// update check information
if (checkfn != null)
{
_codec._adler32 = check = Adler32.Calculate(
check,
window.Memory.Span.Slice(readAt, nBytes)
);
_codec._adler32 = check = Adler32.Calculate(check, window.Memory.Span.Slice(readAt, nBytes));
}
// copy as far as end of window
window
.Memory.Span.Slice(readAt, nBytes)
.CopyTo(_codec.OutputBuffer.AsSpan(_codec.NextOut));
window.Memory.Span.Slice(readAt, nBytes).CopyTo(_codec.OutputBuffer.AsSpan(_codec.NextOut));
_codec.NextOut += nBytes;
readAt += nBytes;
@@ -1474,8 +1469,7 @@ internal sealed class InflateCodes
}
else
{
s.window.Memory.Span.Slice(r, 2)
.CopyTo(s.window.Memory.Span.Slice(q));
s.window.Memory.Span.Slice(r, 2).CopyTo(s.window.Memory.Span.Slice(q));
q += 2;
r += 2;
c -= 2;
@@ -1503,8 +1497,7 @@ internal sealed class InflateCodes
}
else
{
s.window.Memory.Span.Slice(r, e)
.CopyTo(s.window.Memory.Span.Slice(q));
s.window.Memory.Span.Slice(r, e).CopyTo(s.window.Memory.Span.Slice(q));
q += e;
r += e;
e = 0;
@@ -1523,8 +1516,7 @@ internal sealed class InflateCodes
}
else
{
s.window.Memory.Span.Slice(r, c)
.CopyTo(s.window.Memory.Span.Slice(q));
s.window.Memory.Span.Slice(r, c).CopyTo(s.window.Memory.Span.Slice(q));
q += c;
r += c;
c = 0;

View File

@@ -222,19 +222,6 @@ public sealed class LZipStream : Stream, IStreamStack
/// <returns><c>true</c> if the given stream is an LZip file, <c>false</c> otherwise.</returns>
public static bool IsLZipFile(Stream stream) => ValidateAndReadSize(stream) != 0;
/// <summary>
/// Asynchronously determines if the given stream is positioned at the start of a v1 LZip
/// file, as indicated by the ASCII characters "LZIP" and a version byte
/// of 1, followed by at least one byte.
/// </summary>
/// <param name="stream">The stream to read from. Must not be null.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns><c>true</c> if the given stream is an LZip file, <c>false</c> otherwise.</returns>
public static async ValueTask<bool> IsLZipFileAsync(
Stream stream,
CancellationToken cancellationToken = default
) => await ValidateAndReadSizeAsync(stream, cancellationToken) != 0;
/// <summary>
/// Reads the 6-byte header of the stream, and returns 0 if either the header
/// couldn't be read or it isn't a validate LZIP header, or the dictionary
@@ -268,44 +255,6 @@ public sealed class LZipStream : Stream, IStreamStack
return (1 << basePower) - (subtractionNumerator * (1 << (basePower - 4)));
}
/// <summary>
/// Asynchronously reads the 6-byte header of the stream, and returns 0 if either the header
/// couldn't be read or it isn't a validate LZIP header, or the dictionary
/// size if it *is* a valid LZIP file.
/// </summary>
public static async ValueTask<int> ValidateAndReadSizeAsync(
Stream stream,
CancellationToken cancellationToken
)
{
// Read the header
byte[] header = new byte[6];
var n = await stream
.ReadAsync(header, 0, header.Length, cancellationToken)
.ConfigureAwait(false);
// TODO: Handle reading only part of the header?
if (n != 6)
{
return 0;
}
if (
header[0] != 'L'
|| header[1] != 'Z'
|| header[2] != 'I'
|| header[3] != 'P'
|| header[4] != 1 /* version 1 */
)
{
return 0;
}
var basePower = header[5] & 0x1F;
var subtractionNumerator = (header[5] & 0xE0) >> 5;
return (1 << basePower) - (subtractionNumerator * (1 << (basePower - 4)));
}
private static readonly byte[] headerBytes =
[
(byte)'L',

View File

@@ -1,7 +1,5 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
using SharpCompress.IO;
@@ -97,45 +95,6 @@ namespace SharpCompress.Compressors.Lzw
return true;
}
/// <summary>
/// Asynchronously checks if the stream is an LZW stream
/// </summary>
/// <param name="stream">The stream to read from</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>True if the stream is an LZW stream, false otherwise</returns>
public static async ValueTask<bool> IsLzwStreamAsync(
Stream stream,
CancellationToken cancellationToken = default
)
{
try
{
byte[] hdr = new byte[LzwConstants.HDR_SIZE];
int result = await stream.ReadAsync(hdr, 0, hdr.Length, cancellationToken);
// Check the magic marker
if (result < 0)
throw new IncompleteArchiveException("Failed to read LZW header");
if (hdr[0] != (LzwConstants.MAGIC >> 8) || hdr[1] != (LzwConstants.MAGIC & 0xff))
{
throw new IncompleteArchiveException(
String.Format(
"Wrong LZW header. Magic bytes don't match. 0x{0:x2} 0x{1:x2}",
hdr[0],
hdr[1]
)
);
}
}
catch (Exception)
{
return false;
}
return true;
}
/// <summary>
/// Gets or sets a flag indicating ownership of underlying stream.
/// When the flag is true <see cref="Stream.Dispose()" /> will close the underlying stream also.

View File

@@ -1,234 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using SharpCompress.Common;
using SharpCompress.Common.Rar;
using SharpCompress.IO;
namespace SharpCompress.Compressors.Rar;
internal sealed class MultiVolumeReadOnlyAsyncStream : MultiVolumeReadOnlyStreamBase, IStreamStack
{
#if DEBUG_STREAMS
long IStreamStack.InstanceId { get; set; }
#endif
int IStreamStack.DefaultBufferSize { get; set; }
Stream IStreamStack.BaseStream() => currentStream.NotNull();
int IStreamStack.BufferSize
{
get => 0;
set { }
}
int IStreamStack.BufferPosition
{
get => 0;
set { }
}
void IStreamStack.SetPosition(long position) { }
private long currentPosition;
private long maxPosition;
private IAsyncEnumerator<RarFilePart> filePartEnumerator;
private Stream? currentStream;
private MultiVolumeReadOnlyAsyncStream(IAsyncEnumerable<RarFilePart> parts)
{
filePartEnumerator = parts.GetAsyncEnumerator();
}
internal static async ValueTask<MultiVolumeReadOnlyAsyncStream> Create(
IAsyncEnumerable<RarFilePart> parts
)
{
var stream = new MultiVolumeReadOnlyAsyncStream(parts);
await stream.filePartEnumerator.MoveNextAsync();
stream.InitializeNextFilePart();
return stream;
}
#if NET8_0_OR_GREATER
public override async ValueTask DisposeAsync()
{
await base.DisposeAsync();
if (filePartEnumerator != null)
{
await filePartEnumerator.DisposeAsync();
}
currentStream = null;
}
#else
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
filePartEnumerator.DisposeAsync().AsTask().GetAwaiter().GetResult();
currentStream = null;
}
#endif
private void InitializeNextFilePart()
{
maxPosition = filePartEnumerator.Current.FileHeader.CompressedSize;
currentPosition = 0;
currentStream = filePartEnumerator.Current.GetCompressedStream();
CurrentCrc = filePartEnumerator.Current.FileHeader.FileCrc;
}
public override int Read(byte[] buffer, int offset, int count) =>
throw new NotSupportedException(
"Synchronous read is not supported in MultiVolumeReadOnlyAsyncStream."
);
public override async System.Threading.Tasks.Task<int> ReadAsync(
byte[] buffer,
int offset,
int count,
System.Threading.CancellationToken cancellationToken
)
{
var totalRead = 0;
var currentOffset = offset;
var currentCount = count;
while (currentCount > 0)
{
var readSize = currentCount;
if (currentCount > maxPosition - currentPosition)
{
readSize = (int)(maxPosition - currentPosition);
}
var read = await currentStream
.NotNull()
.ReadAsync(buffer, currentOffset, readSize, cancellationToken)
.ConfigureAwait(false);
if (read < 0)
{
throw new EndOfStreamException();
}
currentPosition += read;
currentOffset += read;
currentCount -= read;
totalRead += read;
if (
((maxPosition - currentPosition) == 0)
&& filePartEnumerator.Current.FileHeader.IsSplitAfter
)
{
if (filePartEnumerator.Current.FileHeader.R4Salt != null)
{
throw new InvalidFormatException(
"Sharpcompress currently does not support multi-volume decryption."
);
}
var fileName = filePartEnumerator.Current.FileHeader.FileName;
if (!await filePartEnumerator.MoveNextAsync())
{
throw new InvalidFormatException(
"Multi-part rar file is incomplete. Entry expects a new volume: "
+ fileName
);
}
InitializeNextFilePart();
}
else
{
break;
}
}
return totalRead;
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
public override async System.Threading.Tasks.ValueTask<int> ReadAsync(
Memory<byte> buffer,
System.Threading.CancellationToken cancellationToken = default
)
{
var totalRead = 0;
var currentOffset = 0;
var currentCount = buffer.Length;
while (currentCount > 0)
{
var readSize = currentCount;
if (currentCount > maxPosition - currentPosition)
{
readSize = (int)(maxPosition - currentPosition);
}
var read = await currentStream
.NotNull()
.ReadAsync(buffer.Slice(currentOffset, readSize), cancellationToken)
.ConfigureAwait(false);
if (read < 0)
{
throw new EndOfStreamException();
}
currentPosition += read;
currentOffset += read;
currentCount -= read;
totalRead += read;
if (
((maxPosition - currentPosition) == 0)
&& filePartEnumerator.Current.FileHeader.IsSplitAfter
)
{
if (filePartEnumerator.Current.FileHeader.R4Salt != null)
{
throw new InvalidFormatException(
"Sharpcompress currently does not support multi-volume decryption."
);
}
var fileName = filePartEnumerator.Current.FileHeader.FileName;
if (!await filePartEnumerator.MoveNextAsync())
{
throw new InvalidFormatException(
"Multi-part rar file is incomplete. Entry expects a new volume: "
+ fileName
);
}
InitializeNextFilePart();
}
else
{
break;
}
}
return totalRead;
}
#endif
public override bool CanRead => true;
public override bool CanSeek => false;
public override bool CanWrite => false;
public override void Flush() { }
public override long Length => throw new NotSupportedException();
public override long Position
{
get => throw new NotSupportedException();
set => throw new NotSupportedException();
}
public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();
public override void SetLength(long value) => throw new NotSupportedException();
public override void Write(byte[] buffer, int offset, int count) =>
throw new NotSupportedException();
}

View File

@@ -1,3 +1,5 @@
#nullable disable
using System;
using System.Collections.Generic;
using System.IO;
@@ -7,21 +9,20 @@ using SharpCompress.IO;
namespace SharpCompress.Compressors.Rar;
internal sealed class MultiVolumeReadOnlyStream : MultiVolumeReadOnlyStreamBase, IStreamStack
internal sealed class MultiVolumeReadOnlyStream : Stream, IStreamStack
{
#if DEBUG_STREAMS
long IStreamStack.InstanceId { get; set; }
#endif
int IStreamStack.DefaultBufferSize { get; set; }
Stream IStreamStack.BaseStream() => currentStream.NotNull();
Stream IStreamStack.BaseStream() => currentStream;
int IStreamStack.BufferSize
{
get => 0;
set { }
}
int IStreamStack.BufferPosition
{
get => 0;
@@ -34,7 +35,7 @@ internal sealed class MultiVolumeReadOnlyStream : MultiVolumeReadOnlyStreamBase,
private long maxPosition;
private IEnumerator<RarFilePart> filePartEnumerator;
private Stream? currentStream;
private Stream currentStream;
internal MultiVolumeReadOnlyStream(IEnumerable<RarFilePart> parts)
{
@@ -55,8 +56,11 @@ internal sealed class MultiVolumeReadOnlyStream : MultiVolumeReadOnlyStreamBase,
this.DebugDispose(typeof(MultiVolumeReadOnlyStream));
#endif
filePartEnumerator.Dispose();
if (filePartEnumerator != null)
{
filePartEnumerator.Dispose();
filePartEnumerator = null;
}
currentStream = null;
}
}
@@ -83,7 +87,7 @@ internal sealed class MultiVolumeReadOnlyStream : MultiVolumeReadOnlyStreamBase,
readSize = (int)(maxPosition - currentPosition);
}
var read = currentStream.NotNull().Read(buffer, currentOffset, readSize);
var read = currentStream.Read(buffer, currentOffset, readSize);
if (read < 0)
{
throw new EndOfStreamException();
@@ -104,7 +108,6 @@ internal sealed class MultiVolumeReadOnlyStream : MultiVolumeReadOnlyStreamBase,
"Sharpcompress currently does not support multi-volume decryption."
);
}
var fileName = filePartEnumerator.Current.FileHeader.FileName;
if (!filePartEnumerator.MoveNext())
{
@@ -113,7 +116,6 @@ internal sealed class MultiVolumeReadOnlyStream : MultiVolumeReadOnlyStreamBase,
+ fileName
);
}
InitializeNextFilePart();
}
else
@@ -121,7 +123,6 @@ internal sealed class MultiVolumeReadOnlyStream : MultiVolumeReadOnlyStreamBase,
break;
}
}
return totalRead;
}
@@ -144,7 +145,6 @@ internal sealed class MultiVolumeReadOnlyStream : MultiVolumeReadOnlyStreamBase,
}
var read = await currentStream
.NotNull()
.ReadAsync(buffer, currentOffset, readSize, cancellationToken)
.ConfigureAwait(false);
if (read < 0)
@@ -167,7 +167,6 @@ internal sealed class MultiVolumeReadOnlyStream : MultiVolumeReadOnlyStreamBase,
"Sharpcompress currently does not support multi-volume decryption."
);
}
var fileName = filePartEnumerator.Current.FileHeader.FileName;
if (!filePartEnumerator.MoveNext())
{
@@ -176,7 +175,6 @@ internal sealed class MultiVolumeReadOnlyStream : MultiVolumeReadOnlyStreamBase,
+ fileName
);
}
InitializeNextFilePart();
}
else
@@ -184,7 +182,6 @@ internal sealed class MultiVolumeReadOnlyStream : MultiVolumeReadOnlyStreamBase,
break;
}
}
return totalRead;
}
@@ -206,7 +203,6 @@ internal sealed class MultiVolumeReadOnlyStream : MultiVolumeReadOnlyStreamBase,
}
var read = await currentStream
.NotNull()
.ReadAsync(buffer.Slice(currentOffset, readSize), cancellationToken)
.ConfigureAwait(false);
if (read < 0)
@@ -254,6 +250,8 @@ internal sealed class MultiVolumeReadOnlyStream : MultiVolumeReadOnlyStreamBase,
public override bool CanWrite => false;
public byte[] CurrentCrc { get; private set; }
public override void Flush() { }
public override long Length => throw new NotSupportedException();

View File

@@ -1,8 +0,0 @@
using System.IO;
namespace SharpCompress.Compressors.Rar;
internal abstract class MultiVolumeReadOnlyStreamBase : Stream
{
public byte[]? CurrentCrc { get; protected set; }
}

View File

@@ -30,7 +30,7 @@ internal class RarBLAKE2spStream : RarStream, IStreamStack
void IStreamStack.SetPosition(long position) { }
private readonly MultiVolumeReadOnlyStreamBase readStream;
private readonly MultiVolumeReadOnlyStream readStream;
private readonly bool disableCRCCheck;
const uint BLAKE2S_NUM_ROUNDS = 10;
@@ -108,7 +108,7 @@ internal class RarBLAKE2spStream : RarStream, IStreamStack
private RarBLAKE2spStream(
IRarUnpack unpack,
FileHeader fileHeader,
MultiVolumeReadOnlyStreamBase readStream
MultiVolumeReadOnlyStream readStream
)
: base(unpack, fileHeader, readStream)
{
@@ -137,7 +137,7 @@ internal class RarBLAKE2spStream : RarStream, IStreamStack
public static async Task<RarBLAKE2spStream> CreateAsync(
IRarUnpack unpack,
FileHeader fileHeader,
MultiVolumeReadOnlyAsyncStream readStream,
MultiVolumeReadOnlyStream readStream,
CancellationToken cancellationToken = default
)
{

View File

@@ -29,14 +29,14 @@ internal class RarCrcStream : RarStream, IStreamStack
void IStreamStack.SetPosition(long position) { }
private readonly MultiVolumeReadOnlyStreamBase readStream;
private readonly MultiVolumeReadOnlyStream readStream;
private uint currentCrc;
private readonly bool disableCRC;
private RarCrcStream(
IRarUnpack unpack,
FileHeader fileHeader,
MultiVolumeReadOnlyStreamBase readStream
MultiVolumeReadOnlyStream readStream
)
: base(unpack, fileHeader, readStream)
{
@@ -62,7 +62,7 @@ internal class RarCrcStream : RarStream, IStreamStack
public static async Task<RarCrcStream> CreateAsync(
IRarUnpack unpack,
FileHeader fileHeader,
MultiVolumeReadOnlyStreamBase readStream,
MultiVolumeReadOnlyStream readStream,
CancellationToken cancellationToken = default
)
{
@@ -92,7 +92,7 @@ internal class RarCrcStream : RarStream, IStreamStack
}
else if (
!disableCRC
&& GetCrc() != BitConverter.ToUInt32(readStream.NotNull().CurrentCrc.NotNull(), 0)
&& GetCrc() != BitConverter.ToUInt32(readStream.CurrentCrc, 0)
&& count != 0
)
{
@@ -118,7 +118,7 @@ internal class RarCrcStream : RarStream, IStreamStack
}
else if (
!disableCRC
&& GetCrc() != BitConverter.ToUInt32(readStream.NotNull().CurrentCrc.NotNull(), 0)
&& GetCrc() != BitConverter.ToUInt32(readStream.CurrentCrc, 0)
&& count != 0
)
{
@@ -143,7 +143,7 @@ internal class RarCrcStream : RarStream, IStreamStack
}
else if (
!disableCRC
&& GetCrc() != BitConverter.ToUInt32(readStream.NotNull().CurrentCrc.NotNull(), 0)
&& GetCrc() != BitConverter.ToUInt32(readStream.CurrentCrc, 0)
&& buffer.Length != 0
)
{

View File

@@ -126,6 +126,7 @@ internal sealed partial class Unpack : BitInput, IRarUnpack
private FileHeader fileHeader;
private void Init()
{
if (this.window is null)

View File

@@ -60,22 +60,6 @@ public sealed class XZStream : XZReadOnlyStream, IStreamStack
}
}
public static async ValueTask<bool> IsXZStreamAsync(
Stream stream,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
try
{
return null != await XZHeader.FromStreamAsync(stream, cancellationToken);
}
catch (Exception)
{
return false;
}
}
private void AssertBlockCheckTypeIsSupported()
{
switch (Header.BlockCheckType)

View File

@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.IO;
@@ -44,27 +43,6 @@ internal class ZStandardStream : DecompressionStream, IStreamStack
return true;
}
internal static async ValueTask<bool> IsZStandardAsync(
Stream stream,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
var buffer = new byte[4];
var bytesRead = await stream.ReadAsync(buffer, 0, 4, cancellationToken);
if (bytesRead < 4)
{
return false;
}
var magic = BitConverter.ToUInt32(buffer, 0);
if (ZstandardConstants.MAGIC != magic)
{
return false;
}
return true;
}
public ZStandardStream(Stream baseInputStream)
: base(baseInputStream)
{

View File

@@ -29,24 +29,23 @@ namespace SharpCompress.Factories
int bufferSize = ReaderOptions.DefaultBufferSize
) => AceHeader.IsArchive(stream);
public override ValueTask<bool> IsArchiveAsync(
Stream stream,
string? password = null,
int bufferSize = ReaderOptions.DefaultBufferSize,
CancellationToken cancellationToken = default
) => AceHeader.IsArchiveAsync(stream, cancellationToken);
public IReader OpenReader(Stream stream, ReaderOptions? options) =>
AceReader.OpenReader(stream, options);
AceReader.Open(stream, options);
public ValueTask<IAsyncReader> OpenAsyncReader(
public IAsyncReader OpenReaderAsync(
Stream stream,
ReaderOptions? options,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IAsyncReader)AceReader.OpenReader(stream, options));
return (IAsyncReader)AceReader.Open(stream, options);
}
public override ValueTask<bool> IsArchiveAsync(
Stream stream,
string? password = null,
int bufferSize = ReaderOptions.DefaultBufferSize
) => new(IsArchive(stream, password, bufferSize));
}
}

View File

@@ -1,5 +1,4 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -37,54 +36,28 @@ namespace SharpCompress.Factories
//Hyper - archive, check the next two bytes for "HP" or "ST"(or look below for
//"HYP").Also the ZOO archiver also does put a 01Ah at the start of the file,
//see the ZOO entry below.
var buffer = ArrayPool<byte>.Shared.Rent(2);
try
{
stream.ReadExact(buffer, 0, 2);
return buffer[0] == 0x1A && buffer[1] < 10; //rather thin, but this is all we have
}
finally
{
ArrayPool<byte>.Shared.Return(buffer);
}
var bytes = new byte[2];
stream.Read(bytes, 0, 2);
return bytes[0] == 0x1A && bytes[1] < 10; //rather thin, but this is all we have
}
public IReader OpenReader(Stream stream, ReaderOptions? options) =>
ArcReader.OpenReader(stream, options);
ArcReader.Open(stream, options);
public ValueTask<IAsyncReader> OpenAsyncReader(
public IAsyncReader OpenReaderAsync(
Stream stream,
ReaderOptions? options,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IAsyncReader)ArcReader.OpenReader(stream, options));
return (IAsyncReader)ArcReader.Open(stream, options);
}
public override async ValueTask<bool> IsArchiveAsync(
public override ValueTask<bool> IsArchiveAsync(
Stream stream,
string? password = null,
int bufferSize = ReaderOptions.DefaultBufferSize,
CancellationToken cancellationToken = default
)
{
//You may have to use some(paranoid) checks to ensure that you actually are
//processing an ARC file, since other archivers also adopted the idea of putting
//a 01Ah byte at offset 0, namely the Hyper archiver. To check if you have a
//Hyper - archive, check the next two bytes for "HP" or "ST"(or look below for
//"HYP").Also the ZOO archiver also does put a 01Ah at the start of the file,
//see the ZOO entry below.
var buffer = ArrayPool<byte>.Shared.Rent(2);
try
{
await stream.ReadExactAsync(buffer, 0, 2, cancellationToken);
return buffer[0] == 0x1A && buffer[1] < 10; //rather thin, but this is all we have
}
finally
{
ArrayPool<byte>.Shared.Return(buffer);
}
}
int bufferSize = ReaderOptions.DefaultBufferSize
) => new(IsArchive(stream, password, bufferSize));
}
}

View File

@@ -27,26 +27,28 @@ namespace SharpCompress.Factories
Stream stream,
string? password = null,
int bufferSize = ReaderOptions.DefaultBufferSize
) => ArjHeader.IsArchive(stream);
public override ValueTask<bool> IsArchiveAsync(
Stream stream,
string? password = null,
int bufferSize = ReaderOptions.DefaultBufferSize,
CancellationToken cancellationToken = default
) => ArjHeader.IsArchiveAsync(stream, cancellationToken);
)
{
return ArjHeader.IsArchive(stream);
}
public IReader OpenReader(Stream stream, ReaderOptions? options) =>
ArjReader.OpenReader(stream, options);
ArjReader.Open(stream, options);
public ValueTask<IAsyncReader> OpenAsyncReader(
public IAsyncReader OpenReaderAsync(
Stream stream,
ReaderOptions? options,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IAsyncReader)ArjReader.OpenReader(stream, options));
return (IAsyncReader)ArjReader.Open(stream, options);
}
public override ValueTask<bool> IsArchiveAsync(
Stream stream,
string? password = null,
int bufferSize = ReaderOptions.DefaultBufferSize
) => new(IsArchive(stream, password, bufferSize));
}
}

View File

@@ -60,11 +60,22 @@ public abstract class Factory : IFactory
);
public abstract ValueTask<bool> IsArchiveAsync(
Stream stream,
string? password = null,
int bufferSize = ReaderOptions.DefaultBufferSize
);
/// <inheritdoc/>
public virtual ValueTask<bool> IsArchiveAsync(
Stream stream,
string? password = null,
int bufferSize = ReaderOptions.DefaultBufferSize,
CancellationToken cancellationToken = default
);
)
{
cancellationToken.ThrowIfCancellationRequested();
return new(IsArchive(stream, password, bufferSize));
}
/// <inheritdoc/>
public virtual FileInfo? GetFilePart(int index, FileInfo part1) => null;
@@ -101,4 +112,31 @@ public abstract class Factory : IFactory
return false;
}
internal virtual async ValueTask<(bool, IAsyncReader?)> TryOpenReaderAsync(
SharpCompressStream stream,
ReaderOptions options,
CancellationToken cancellationToken
)
{
if (this is IReaderFactory readerFactory)
{
long pos = ((IStreamStack)stream).GetPosition();
if (
await IsArchiveAsync(
stream,
options.Password,
options.BufferSize,
cancellationToken
)
)
{
((IStreamStack)stream).StackSeek(pos);
return (true, readerFactory.OpenReaderAsync(stream, options, cancellationToken));
}
}
return (false, null);
}
}

View File

@@ -61,48 +61,57 @@ public class GZipFactory
#region IArchiveFactory
/// <inheritdoc/>
public IArchive OpenArchive(Stream stream, ReaderOptions? readerOptions = null) =>
GZipArchive.OpenArchive(stream, readerOptions);
public IArchive Open(Stream stream, ReaderOptions? readerOptions = null) =>
GZipArchive.Open(stream, readerOptions);
/// <inheritdoc/>
public IAsyncArchive OpenAsyncArchive(Stream stream, ReaderOptions? readerOptions = null) =>
(IAsyncArchive)OpenArchive(stream, readerOptions);
public IAsyncArchive OpenAsync(Stream stream, ReaderOptions? readerOptions = null) =>
(IAsyncArchive)Open(stream, readerOptions);
public override ValueTask<bool> IsArchiveAsync(
Stream stream,
string? password = null,
int bufferSize = ReaderOptions.DefaultBufferSize
) => new(IsArchive(stream, password, bufferSize));
/// <inheritdoc/>
public IAsyncArchive OpenAsyncArchive(FileInfo fileInfo, ReaderOptions? readerOptions = null) =>
(IAsyncArchive)OpenArchive(fileInfo, readerOptions);
public IAsyncArchive OpenAsync(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncArchive)Open(fileInfo, readerOptions);
}
#endregion
#region IMultiArchiveFactory
/// <inheritdoc/>
public IArchive OpenArchive(
public IArchive Open(IReadOnlyList<Stream> streams, ReaderOptions? readerOptions = null) =>
GZipArchive.Open(streams, readerOptions);
/// <inheritdoc/>
public IAsyncArchive OpenAsync(
IReadOnlyList<Stream> streams,
ReaderOptions? readerOptions = null
) => GZipArchive.OpenArchive(streams, readerOptions);
) => (IAsyncArchive)Open(streams, readerOptions);
/// <inheritdoc/>
public IAsyncArchive OpenAsyncArchive(
IReadOnlyList<Stream> streams,
ReaderOptions? readerOptions = null
) => (IAsyncArchive)OpenArchive(streams, readerOptions);
public IArchive Open(IReadOnlyList<FileInfo> fileInfos, ReaderOptions? readerOptions = null) =>
GZipArchive.Open(fileInfos, readerOptions);
/// <inheritdoc/>
public IArchive OpenArchive(
IReadOnlyList<FileInfo> fileInfos,
ReaderOptions? readerOptions = null
) => GZipArchive.OpenArchive(fileInfos, readerOptions);
/// <inheritdoc/>
public IAsyncArchive OpenAsyncArchive(
public IAsyncArchive OpenAsync(
IReadOnlyList<FileInfo> fileInfos,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncArchive)OpenArchive(fileInfos, readerOptions);
return (IAsyncArchive)Open(fileInfos, readerOptions);
}
#endregion
@@ -141,29 +150,29 @@ public class GZipFactory
/// <inheritdoc/>
public IReader OpenReader(Stream stream, ReaderOptions? options) =>
GZipReader.OpenReader(stream, options);
GZipReader.Open(stream, options);
/// <inheritdoc/>
public ValueTask<IAsyncReader> OpenAsyncReader(
public IAsyncReader OpenReaderAsync(
Stream stream,
ReaderOptions? options,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IAsyncReader)GZipReader.OpenReader(stream, options));
return (IAsyncReader)GZipReader.Open(stream, options);
}
/// <inheritdoc/>
public IArchive OpenArchive(FileInfo fileInfo, ReaderOptions? readerOptions = null) =>
GZipArchive.OpenArchive(fileInfo, readerOptions);
public IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) =>
GZipArchive.Open(fileInfo, readerOptions);
#endregion
#region IWriterFactory
/// <inheritdoc/>
public IWriter OpenWriter(Stream stream, WriterOptions writerOptions)
public IWriter Open(Stream stream, WriterOptions writerOptions)
{
if (writerOptions.CompressionType != CompressionType.GZip)
{
@@ -173,7 +182,7 @@ public class GZipFactory
}
/// <inheritdoc/>
public IAsyncWriter OpenAsyncWriter(
public IAsyncWriter OpenAsync(
Stream stream,
WriterOptions writerOptions,
CancellationToken cancellationToken = default
@@ -184,7 +193,7 @@ public class GZipFactory
{
throw new InvalidFormatException("GZip archives only support GZip compression type.");
}
return (IAsyncWriter)OpenWriter(stream, writerOptions);
return (IAsyncWriter)Open(stream, writerOptions);
}
#endregion
@@ -192,7 +201,7 @@ public class GZipFactory
#region IWriteableArchiveFactory
/// <inheritdoc/>
public IWritableArchive CreateArchive() => GZipArchive.CreateArchive();
public IWritableArchive CreateWriteableArchive() => GZipArchive.Create();
#endregion
}

View File

@@ -37,14 +37,6 @@ public class RarFactory : Factory, IArchiveFactory, IMultiArchiveFactory, IReade
int bufferSize = ReaderOptions.DefaultBufferSize
) => RarArchive.IsRarFile(stream);
/// <inheritdoc/>
public override ValueTask<bool> IsArchiveAsync(
Stream stream,
string? password = null,
int bufferSize = ReaderOptions.DefaultBufferSize,
CancellationToken cancellationToken = default
) => RarArchive.IsRarFileAsync(stream, cancellationToken: cancellationToken);
/// <inheritdoc/>
public override FileInfo? GetFilePart(int index, FileInfo part1) =>
RarArchiveVolumeFactory.GetFilePart(index, part1);
@@ -54,52 +46,61 @@ public class RarFactory : Factory, IArchiveFactory, IMultiArchiveFactory, IReade
#region IArchiveFactory
/// <inheritdoc/>
public IArchive OpenArchive(Stream stream, ReaderOptions? readerOptions = null) =>
RarArchive.OpenArchive(stream, readerOptions);
public IArchive Open(Stream stream, ReaderOptions? readerOptions = null) =>
RarArchive.Open(stream, readerOptions);
/// <inheritdoc/>
public IAsyncArchive OpenAsyncArchive(Stream stream, ReaderOptions? readerOptions = null) =>
(IAsyncArchive)OpenArchive(stream, readerOptions);
public IAsyncArchive OpenAsync(Stream stream, ReaderOptions? readerOptions = null) =>
(IAsyncArchive)Open(stream, readerOptions);
/// <inheritdoc/>
public IArchive OpenArchive(FileInfo fileInfo, ReaderOptions? readerOptions = null) =>
RarArchive.OpenArchive(fileInfo, readerOptions);
public IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) =>
RarArchive.Open(fileInfo, readerOptions);
/// <inheritdoc/>
public IAsyncArchive OpenAsyncArchive(FileInfo fileInfo, ReaderOptions? readerOptions = null) =>
(IAsyncArchive)OpenArchive(fileInfo, readerOptions);
public IAsyncArchive OpenAsync(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncArchive)Open(fileInfo, readerOptions);
}
public override ValueTask<bool> IsArchiveAsync(
Stream stream,
string? password = null,
int bufferSize = ReaderOptions.DefaultBufferSize
) => new(IsArchive(stream, password, bufferSize));
#endregion
#region IMultiArchiveFactory
/// <inheritdoc/>
public IArchive OpenArchive(
public IArchive Open(IReadOnlyList<Stream> streams, ReaderOptions? readerOptions = null) =>
RarArchive.Open(streams, readerOptions);
/// <inheritdoc/>
public IAsyncArchive OpenAsync(
IReadOnlyList<Stream> streams,
ReaderOptions? readerOptions = null
) => RarArchive.OpenArchive(streams, readerOptions);
) => (IAsyncArchive)Open(streams, readerOptions);
/// <inheritdoc/>
public IAsyncArchive OpenAsyncArchive(
IReadOnlyList<Stream> streams,
ReaderOptions? readerOptions = null
) => (IAsyncArchive)OpenArchive(streams, readerOptions);
public IArchive Open(IReadOnlyList<FileInfo> fileInfos, ReaderOptions? readerOptions = null) =>
RarArchive.Open(fileInfos, readerOptions);
/// <inheritdoc/>
public IArchive OpenArchive(
IReadOnlyList<FileInfo> fileInfos,
ReaderOptions? readerOptions = null
) => RarArchive.OpenArchive(fileInfos, readerOptions);
/// <inheritdoc/>
public IAsyncArchive OpenAsyncArchive(
public IAsyncArchive OpenAsync(
IReadOnlyList<FileInfo> fileInfos,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncArchive)OpenArchive(fileInfos, readerOptions);
return (IAsyncArchive)Open(fileInfos, readerOptions);
}
#endregion
@@ -108,17 +109,17 @@ public class RarFactory : Factory, IArchiveFactory, IMultiArchiveFactory, IReade
/// <inheritdoc/>
public IReader OpenReader(Stream stream, ReaderOptions? options) =>
RarReader.OpenReader(stream, options);
RarReader.Open(stream, options);
/// <inheritdoc/>
public ValueTask<IAsyncReader> OpenAsyncReader(
public IAsyncReader OpenReaderAsync(
Stream stream,
ReaderOptions? options,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IAsyncReader)RarReader.OpenReader(stream, options));
return (IAsyncReader)RarReader.Open(stream, options);
}
#endregion

View File

@@ -36,62 +36,67 @@ public class SevenZipFactory : Factory, IArchiveFactory, IMultiArchiveFactory
int bufferSize = ReaderOptions.DefaultBufferSize
) => SevenZipArchive.IsSevenZipFile(stream);
/// <inheritdoc/>
public override ValueTask<bool> IsArchiveAsync(
Stream stream,
string? password = null,
int bufferSize = ReaderOptions.DefaultBufferSize,
CancellationToken cancellationToken = default
) => SevenZipArchive.IsSevenZipFileAsync(stream, cancellationToken);
#endregion
#region IArchiveFactory
/// <inheritdoc/>
public IArchive OpenArchive(Stream stream, ReaderOptions? readerOptions = null) =>
SevenZipArchive.OpenArchive(stream, readerOptions);
public IArchive Open(Stream stream, ReaderOptions? readerOptions = null) =>
SevenZipArchive.Open(stream, readerOptions);
/// <inheritdoc/>
public IAsyncArchive OpenAsyncArchive(Stream stream, ReaderOptions? readerOptions = null) =>
SevenZipArchive.OpenAsyncArchive(stream, readerOptions, CancellationToken.None);
public IAsyncArchive OpenAsync(Stream stream, ReaderOptions? readerOptions = null) =>
(IAsyncArchive)Open(stream, readerOptions);
/// <inheritdoc/>
public IArchive OpenArchive(FileInfo fileInfo, ReaderOptions? readerOptions = null) =>
SevenZipArchive.OpenArchive(fileInfo, readerOptions);
public IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) =>
SevenZipArchive.Open(fileInfo, readerOptions);
/// <inheritdoc/>
public IAsyncArchive OpenAsyncArchive(FileInfo fileInfo, ReaderOptions? readerOptions = null) =>
SevenZipArchive.OpenAsyncArchive(fileInfo, readerOptions, CancellationToken.None);
public IAsyncArchive OpenAsync(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncArchive)Open(fileInfo, readerOptions);
}
public override ValueTask<bool> IsArchiveAsync(
Stream stream,
string? password = null,
int bufferSize = ReaderOptions.DefaultBufferSize
) => new(IsArchive(stream, password, bufferSize));
#endregion
#region IMultiArchiveFactory
/// <inheritdoc/>
public IArchive OpenArchive(
public IArchive Open(IReadOnlyList<Stream> streams, ReaderOptions? readerOptions = null) =>
SevenZipArchive.Open(streams, readerOptions);
/// <inheritdoc/>
public IAsyncArchive OpenAsync(
IReadOnlyList<Stream> streams,
ReaderOptions? readerOptions = null
) => SevenZipArchive.OpenArchive(streams, readerOptions);
) => (IAsyncArchive)Open(streams, readerOptions);
/// <inheritdoc/>
public IAsyncArchive OpenAsyncArchive(
IReadOnlyList<Stream> streams,
ReaderOptions? readerOptions = null
) => SevenZipArchive.OpenAsyncArchive(streams, readerOptions, CancellationToken.None);
public IArchive Open(IReadOnlyList<FileInfo> fileInfos, ReaderOptions? readerOptions = null) =>
SevenZipArchive.Open(fileInfos, readerOptions);
/// <inheritdoc/>
public IArchive OpenArchive(
IReadOnlyList<FileInfo> fileInfos,
ReaderOptions? readerOptions = null
) => SevenZipArchive.OpenArchive(fileInfos, readerOptions);
/// <inheritdoc/>
public IAsyncArchive OpenAsyncArchive(
public IAsyncArchive OpenAsync(
IReadOnlyList<FileInfo> fileInfos,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
) => SevenZipArchive.OpenAsyncArchive(fileInfos, readerOptions, cancellationToken);
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncArchive)Open(fileInfos, readerOptions);
}
#endregion

View File

@@ -1,15 +1,25 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Archives;
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.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 GZipArchive = SharpCompress.Archives.GZip.GZipArchive;
namespace SharpCompress.Factories;
@@ -35,7 +45,7 @@ public class TarFactory
/// <inheritdoc/>
public override IEnumerable<string> GetSupportedExtensions()
{
foreach (var testOption in TarWrapper.Wrappers)
foreach (var testOption in compressionOptions)
{
foreach (var ext in testOption.KnownExtensions)
{
@@ -49,162 +59,230 @@ public class TarFactory
Stream stream,
string? password = null,
int bufferSize = ReaderOptions.DefaultBufferSize
)
{
var rewindableStream = new SharpCompressStream(stream);
long pos = rewindableStream.GetPosition();
foreach (var wrapper in TarWrapper.Wrappers)
{
rewindableStream.StackSeek(pos);
if (wrapper.IsMatch(rewindableStream))
{
rewindableStream.StackSeek(pos);
var decompressedStream = wrapper.CreateStream(rewindableStream);
if (TarArchive.IsTarFile(decompressedStream))
{
rewindableStream.StackSeek(pos);
return true;
}
}
}
) => TarArchive.IsTarFile(stream);
return false;
}
/// <inheritdoc/>
public override async ValueTask<bool> IsArchiveAsync(
public override ValueTask<bool> IsArchiveAsync(
Stream stream,
string? password = null,
int bufferSize = ReaderOptions.DefaultBufferSize,
CancellationToken cancellationToken = default
)
{
var rewindableStream = new SharpCompressStream(stream);
long pos = rewindableStream.GetPosition();
foreach (var wrapper in TarWrapper.Wrappers)
{
rewindableStream.StackSeek(pos);
if (await wrapper.IsMatchAsync(rewindableStream, cancellationToken))
{
rewindableStream.StackSeek(pos);
var decompressedStream = wrapper.CreateStream(rewindableStream);
if (await TarArchive.IsTarFileAsync(decompressedStream, cancellationToken))
{
rewindableStream.StackSeek(pos);
return true;
}
}
}
return false;
}
int bufferSize = ReaderOptions.DefaultBufferSize
) => new(IsArchive(stream, password, bufferSize));
#endregion
#region IArchiveFactory
/// <inheritdoc/>
public IArchive OpenArchive(Stream stream, ReaderOptions? readerOptions = null) =>
TarArchive.OpenArchive(stream, readerOptions);
public IArchive Open(Stream stream, ReaderOptions? readerOptions = null) =>
TarArchive.Open(stream, readerOptions);
/// <inheritdoc/>
public IAsyncArchive OpenAsyncArchive(Stream stream, ReaderOptions? readerOptions = null) =>
(IAsyncArchive)OpenArchive(stream, readerOptions);
public IAsyncArchive OpenAsync(Stream stream, ReaderOptions? readerOptions = null) =>
(IAsyncArchive)Open(stream, readerOptions);
/// <inheritdoc/>
public IArchive OpenArchive(FileInfo fileInfo, ReaderOptions? readerOptions = null) =>
TarArchive.OpenArchive(fileInfo, readerOptions);
public IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) =>
TarArchive.Open(fileInfo, readerOptions);
/// <inheritdoc/>
public IAsyncArchive OpenAsyncArchive(FileInfo fileInfo, ReaderOptions? readerOptions = null) =>
(IAsyncArchive)OpenArchive(fileInfo, readerOptions);
public IAsyncArchive OpenAsync(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncArchive)Open(fileInfo, readerOptions);
}
#endregion
#region IMultiArchiveFactory
/// <inheritdoc/>
public IArchive OpenArchive(
public IArchive Open(IReadOnlyList<Stream> streams, ReaderOptions? readerOptions = null) =>
TarArchive.Open(streams, readerOptions);
/// <inheritdoc/>
public IAsyncArchive OpenAsync(
IReadOnlyList<Stream> streams,
ReaderOptions? readerOptions = null
) => TarArchive.OpenArchive(streams, readerOptions);
) => (IAsyncArchive)Open(streams, readerOptions);
/// <inheritdoc/>
public IAsyncArchive OpenAsyncArchive(
IReadOnlyList<Stream> streams,
ReaderOptions? readerOptions = null
) => (IAsyncArchive)OpenArchive(streams, readerOptions);
public IArchive Open(IReadOnlyList<FileInfo> fileInfos, ReaderOptions? readerOptions = null) =>
TarArchive.Open(fileInfos, readerOptions);
/// <inheritdoc/>
public IArchive OpenArchive(
IReadOnlyList<FileInfo> fileInfos,
ReaderOptions? readerOptions = null
) => TarArchive.OpenArchive(fileInfos, readerOptions);
/// <inheritdoc/>
public IAsyncArchive OpenAsyncArchive(
public IAsyncArchive OpenAsync(
IReadOnlyList<FileInfo> fileInfos,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncArchive)OpenArchive(fileInfos, readerOptions);
return (IAsyncArchive)Open(fileInfos, readerOptions);
}
#endregion
#region IReaderFactory
/// <inheritdoc/>
public IReader OpenReader(Stream stream, ReaderOptions? options)
protected class TestOption
{
options ??= new ReaderOptions();
var rewindableStream = new SharpCompressStream(stream);
long pos = rewindableStream.GetPosition();
foreach (var wrapper in TarWrapper.Wrappers)
public readonly CompressionType Type;
public readonly Func<Stream, bool> CanHandle;
public readonly bool WrapInSharpCompressStream;
public readonly Func<Stream, Stream> CreateStream;
public readonly IEnumerable<string> KnownExtensions;
public TestOption(
CompressionType Type,
Func<Stream, bool> CanHandle,
Func<Stream, Stream> CreateStream,
IEnumerable<string> KnownExtensions,
bool WrapInSharpCompressStream = true
)
{
rewindableStream.StackSeek(pos);
if (wrapper.IsMatch(rewindableStream))
this.Type = Type;
this.CanHandle = CanHandle;
this.WrapInSharpCompressStream = WrapInSharpCompressStream;
this.CreateStream = CreateStream;
this.KnownExtensions = KnownExtensions;
}
}
// https://en.wikipedia.org/wiki/Tar_(computing)#Suffixes_for_compressed_files
protected TestOption[] compressionOptions =
[
new(CompressionType.None, (stream) => true, (stream) => stream, ["tar"], false), // We always do a test for IsTarFile later
new(
CompressionType.BZip2,
BZip2Stream.IsBZip2,
(stream) => new BZip2Stream(stream, CompressionMode.Decompress, false),
["tar.bz2", "tb2", "tbz", "tbz2", "tz2"]
),
new(
CompressionType.GZip,
GZipArchive.IsGZipFile,
(stream) => new GZipStream(stream, CompressionMode.Decompress),
["tar.gz", "taz", "tgz"]
),
new(
CompressionType.ZStandard,
ZStandardStream.IsZStandard,
(stream) => new ZStandardStream(stream),
["tar.zst", "tar.zstd", "tzst", "tzstd"]
),
new(
CompressionType.LZip,
LZipStream.IsLZipFile,
(stream) => new LZipStream(stream, CompressionMode.Decompress),
["tar.lz"]
),
new(
CompressionType.Xz,
XZStream.IsXZStream,
(stream) => new XZStream(stream),
["tar.xz", "txz"],
false
),
new(
CompressionType.Lzw,
LzwStream.IsLzwStream,
(stream) => new LzwStream(stream),
["tar.Z", "tZ", "taZ"],
false
),
];
/// <inheritdoc/>
internal override bool TryOpenReader(
SharpCompressStream rewindableStream,
ReaderOptions options,
out IReader? reader
)
{
reader = null;
long pos = ((IStreamStack)rewindableStream).GetPosition();
TestOption? testedOption = null;
if (!string.IsNullOrWhiteSpace(options.ExtensionHint))
{
testedOption = compressionOptions.FirstOrDefault(a =>
a.KnownExtensions.Contains(
options.ExtensionHint,
StringComparer.CurrentCultureIgnoreCase
)
);
if (testedOption != null)
{
rewindableStream.StackSeek(pos);
var decompressedStream = wrapper.CreateStream(rewindableStream);
if (TarArchive.IsTarFile(decompressedStream))
reader = TryOption(rewindableStream, options, pos, testedOption);
if (reader != null)
{
rewindableStream.StackSeek(pos);
return new TarReader(rewindableStream, options, wrapper.CompressionType);
return true;
}
}
}
throw new InvalidFormatException("Not a tar file.");
foreach (var testOption in compressionOptions)
{
if (testedOption == testOption)
{
continue; // Already tested above
}
((IStreamStack)rewindableStream).StackSeek(pos);
reader = TryOption(rewindableStream, options, pos, testOption);
if (reader != null)
{
return true;
}
}
return false;
}
private static IReader? TryOption(
SharpCompressStream rewindableStream,
ReaderOptions options,
long pos,
TestOption testOption
)
{
if (testOption.CanHandle(rewindableStream))
{
((IStreamStack)rewindableStream).StackSeek(pos);
var inStream = rewindableStream;
if (testOption.WrapInSharpCompressStream)
{
inStream = SharpCompressStream.Create(rewindableStream, leaveOpen: true);
}
var testStream = testOption.CreateStream(rewindableStream);
if (TarArchive.IsTarFile(testStream))
{
((IStreamStack)rewindableStream).StackSeek(pos);
return new TarReader(rewindableStream, options, testOption.Type);
}
}
return null;
}
/// <inheritdoc/>
public async ValueTask<IAsyncReader> OpenAsyncReader(
public IReader OpenReader(Stream stream, ReaderOptions? options) =>
TarReader.Open(stream, options);
/// <inheritdoc/>
public IAsyncReader OpenReaderAsync(
Stream stream,
ReaderOptions? options,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
options ??= new ReaderOptions();
var rewindableStream = new SharpCompressStream(stream);
long pos = rewindableStream.GetPosition();
foreach (var wrapper in TarWrapper.Wrappers)
{
rewindableStream.StackSeek(pos);
if (await wrapper.IsMatchAsync(rewindableStream, cancellationToken))
{
rewindableStream.StackSeek(pos);
var decompressedStream = wrapper.CreateStream(rewindableStream);
if (await TarArchive.IsTarFileAsync(decompressedStream, cancellationToken))
{
rewindableStream.StackSeek(pos);
return new TarReader(rewindableStream, options, wrapper.CompressionType);
}
}
}
return (IAsyncReader)TarReader.OpenReader(stream, options);
return (IAsyncReader)TarReader.Open(stream, options);
}
#endregion
@@ -212,18 +290,18 @@ public class TarFactory
#region IWriterFactory
/// <inheritdoc/>
public IWriter OpenWriter(Stream stream, WriterOptions writerOptions) =>
public IWriter Open(Stream stream, WriterOptions writerOptions) =>
new TarWriter(stream, new TarWriterOptions(writerOptions));
/// <inheritdoc/>
public IAsyncWriter OpenAsyncWriter(
public IAsyncWriter OpenAsync(
Stream stream,
WriterOptions writerOptions,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncWriter)OpenWriter(stream, writerOptions);
return (IAsyncWriter)Open(stream, writerOptions);
}
#endregion
@@ -231,7 +309,7 @@ public class TarFactory
#region IWriteableArchiveFactory
/// <inheritdoc/>
public IWritableArchive CreateArchive() => TarArchive.CreateArchive();
public IWritableArchive CreateWriteableArchive() => TarArchive.Create();
#endregion
}

View File

@@ -1,92 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Archives.GZip;
using SharpCompress.Common;
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;
namespace SharpCompress.Factories;
public class TarWrapper(
CompressionType type,
Func<Stream, bool> canHandle,
Func<Stream, CancellationToken, ValueTask<bool>> canHandleAsync,
Func<Stream, Stream> createStream,
IEnumerable<string> knownExtensions,
bool wrapInSharpCompressStream = true
)
{
public CompressionType CompressionType { get; } = type;
public Func<Stream, bool> IsMatch { get; } = canHandle;
public Func<Stream, CancellationToken, ValueTask<bool>> IsMatchAsync { get; } = canHandleAsync;
public bool WrapInSharpCompressStream { get; } = wrapInSharpCompressStream;
public Func<Stream, Stream> CreateStream { get; } = createStream;
public IEnumerable<string> KnownExtensions { get; } = knownExtensions;
// https://en.wikipedia.org/wiki/Tar_(computing)#Suffixes_for_compressed_files
public static TarWrapper[] Wrappers { get; } =
[
new(
CompressionType.None,
(_) => true,
(_, _) => new ValueTask<bool>(true),
(stream) => stream,
["tar"],
false
), // We always do a test for IsTarFile later
new(
CompressionType.BZip2,
BZip2Stream.IsBZip2,
BZip2Stream.IsBZip2Async,
(stream) => new BZip2Stream(stream, CompressionMode.Decompress, false),
["tar.bz2", "tb2", "tbz", "tbz2", "tz2"]
),
new(
CompressionType.GZip,
GZipArchive.IsGZipFile,
GZipArchive.IsGZipFileAsync,
(stream) => new GZipStream(stream, CompressionMode.Decompress),
["tar.gz", "taz", "tgz"]
),
new(
CompressionType.ZStandard,
ZStandardStream.IsZStandard,
ZStandardStream.IsZStandardAsync,
(stream) => new ZStandardStream(stream),
["tar.zst", "tar.zstd", "tzst", "tzstd"]
),
new(
CompressionType.LZip,
LZipStream.IsLZipFile,
LZipStream.IsLZipFileAsync,
(stream) => new LZipStream(stream, CompressionMode.Decompress),
["tar.lz"]
),
new(
CompressionType.Xz,
XZStream.IsXZStream,
XZStream.IsXZStreamAsync,
(stream) => new XZStream(stream),
["tar.xz", "txz"],
false
),
new(
CompressionType.Lzw,
LzwStream.IsLzwStream,
LzwStream.IsLzwStreamAsync,
(stream) => new LzwStream(stream),
["tar.Z", "tZ", "taZ"],
false
),
];
}

View File

@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Archives;
using SharpCompress.Compressors.ZStandard;
@@ -30,7 +29,6 @@ internal class ZStandardFactory : Factory
public override ValueTask<bool> IsArchiveAsync(
Stream stream,
string? password = null,
int bufferSize = ReaderOptions.DefaultBufferSize,
CancellationToken cancellationToken = default
) => ZStandardStream.IsZStandardAsync(stream, cancellationToken);
int bufferSize = ReaderOptions.DefaultBufferSize
) => new(IsArchive(stream, password, bufferSize));
}

View File

@@ -81,6 +81,12 @@ public class ZipFactory
return false;
}
public override ValueTask<bool> IsArchiveAsync(
Stream stream,
string? password = null,
int bufferSize = ReaderOptions.DefaultBufferSize
) => new(IsArchive(stream, password, bufferSize));
/// <inheritdoc/>
public override async ValueTask<bool> IsArchiveAsync(
Stream stream,
@@ -133,52 +139,55 @@ public class ZipFactory
#region IArchiveFactory
/// <inheritdoc/>
public IArchive OpenArchive(Stream stream, ReaderOptions? readerOptions = null) =>
ZipArchive.OpenArchive(stream, readerOptions);
public IArchive Open(Stream stream, ReaderOptions? readerOptions = null) =>
ZipArchive.Open(stream, readerOptions);
/// <inheritdoc/>
public IAsyncArchive OpenAsyncArchive(Stream stream, ReaderOptions? readerOptions = null) =>
(IAsyncArchive)OpenArchive(stream, readerOptions);
public IAsyncArchive OpenAsync(Stream stream, ReaderOptions? readerOptions = null) =>
(IAsyncArchive)Open(stream, readerOptions);
/// <inheritdoc/>
public IArchive OpenArchive(FileInfo fileInfo, ReaderOptions? readerOptions = null) =>
ZipArchive.OpenArchive(fileInfo, readerOptions);
public IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) =>
ZipArchive.Open(fileInfo, readerOptions);
/// <inheritdoc/>
public IAsyncArchive OpenAsyncArchive(FileInfo fileInfo, ReaderOptions? readerOptions = null) =>
(IAsyncArchive)OpenArchive(fileInfo, readerOptions);
public IAsyncArchive OpenAsync(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncArchive)Open(fileInfo, readerOptions);
}
#endregion
#region IMultiArchiveFactory
/// <inheritdoc/>
public IArchive OpenArchive(
public IArchive Open(IReadOnlyList<Stream> streams, ReaderOptions? readerOptions = null) =>
ZipArchive.Open(streams, readerOptions);
/// <inheritdoc/>
public IAsyncArchive OpenAsync(
IReadOnlyList<Stream> streams,
ReaderOptions? readerOptions = null
) => ZipArchive.OpenArchive(streams, readerOptions);
) => (IAsyncArchive)Open(streams, readerOptions);
/// <inheritdoc/>
public IAsyncArchive OpenAsyncArchive(
IReadOnlyList<Stream> streams,
ReaderOptions? readerOptions = null
) => (IAsyncArchive)OpenArchive(streams, readerOptions);
public IArchive Open(IReadOnlyList<FileInfo> fileInfos, ReaderOptions? readerOptions = null) =>
ZipArchive.Open(fileInfos, readerOptions);
/// <inheritdoc/>
public IArchive OpenArchive(
IReadOnlyList<FileInfo> fileInfos,
ReaderOptions? readerOptions = null
) => ZipArchive.OpenArchive(fileInfos, readerOptions);
/// <inheritdoc/>
public IAsyncArchive OpenAsyncArchive(
public IAsyncArchive OpenAsync(
IReadOnlyList<FileInfo> fileInfos,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncArchive)OpenArchive(fileInfos, readerOptions);
return (IAsyncArchive)Open(fileInfos, readerOptions);
}
#endregion
@@ -187,17 +196,17 @@ public class ZipFactory
/// <inheritdoc/>
public IReader OpenReader(Stream stream, ReaderOptions? options) =>
ZipReader.OpenReader(stream, options);
ZipReader.Open(stream, options);
/// <inheritdoc/>
public ValueTask<IAsyncReader> OpenAsyncReader(
public IAsyncReader OpenReaderAsync(
Stream stream,
ReaderOptions? options,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return new((IAsyncReader)ZipReader.OpenReader(stream, options));
return (IAsyncReader)ZipReader.Open(stream, options);
}
#endregion
@@ -205,18 +214,18 @@ public class ZipFactory
#region IWriterFactory
/// <inheritdoc/>
public IWriter OpenWriter(Stream stream, WriterOptions writerOptions) =>
public IWriter Open(Stream stream, WriterOptions writerOptions) =>
new ZipWriter(stream, new ZipWriterOptions(writerOptions));
/// <inheritdoc/>
public IAsyncWriter OpenAsyncWriter(
public IAsyncWriter OpenAsync(
Stream stream,
WriterOptions writerOptions,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncWriter)OpenWriter(stream, writerOptions);
return (IAsyncWriter)Open(stream, writerOptions);
}
#endregion
@@ -224,7 +233,7 @@ public class ZipFactory
#region IWriteableArchiveFactory
/// <inheritdoc/>
public IWritableArchive CreateArchive() => ZipArchive.CreateArchive();
public IWritableArchive CreateWriteableArchive() => ZipArchive.Create();
#endregion
}

View File

@@ -38,7 +38,7 @@ internal class BufferedSubStream : SharpCompressStream, IStreamStack
private int _cacheOffset;
private int _cacheLength;
private readonly byte[] _cache = ArrayPool<byte>.Shared.Rent(32 << 10);
private readonly byte[] _cache = ArrayPool<byte>.Shared.Rent(32 << 10);
private long origin;
private long BytesLeftToRead { get; set; }

View File

@@ -12,7 +12,7 @@ namespace SharpCompress.Readers;
/// <summary>
/// A generic push reader that reads unseekable comrpessed streams.
/// </summary>
public abstract class AbstractReader<TEntry, TVolume> : IReader, IAsyncReader
public abstract class AbstractReader<TEntry, TVolume> : IReader, IAsyncReader
where TEntry : Entry
where TVolume : Volume
{
@@ -31,8 +31,6 @@ public abstract class AbstractReader<TEntry, TVolume> : IReader, IAsyncReader
public ArchiveType ArchiveType { get; }
protected bool IsAsync => _entriesForCurrentReadStreamAsync is not null;
/// <summary>
/// Current volume that the current entry resides in
/// </summary>

View File

@@ -1,40 +1,13 @@
using System.Collections.Generic;
#if NET8_0_OR_GREATER
using System.IO;
using System.Threading;
using SharpCompress.Common;
namespace SharpCompress.Readers.Ace;
public partial class AceReader
#if NET8_0_OR_GREATER
: IReaderOpenable
#endif
public partial class AceReader : IReaderOpenable
{
/// <summary>
/// Opens an AceReader for non-seeking usage with a single volume.
/// </summary>
/// <param name="stream">The stream containing the ACE archive.</param>
/// <param name="options">Reader options.</param>
/// <returns>An AceReader instance.</returns>
public static IReader OpenReader(Stream stream, ReaderOptions? options = null)
{
stream.NotNull(nameof(stream));
return new SingleVolumeAceReader(stream, options ?? new ReaderOptions());
}
/// <summary>
/// Opens an AceReader for Non-seeking usage with multiple volumes
/// </summary>
/// <param name="streams"></param>
/// <param name="options"></param>
/// <returns></returns>
public static IReader OpenReader(IEnumerable<Stream> streams, ReaderOptions? options = null)
{
streams.NotNull(nameof(streams));
return new MultiVolumeAceReader(streams, options ?? new ReaderOptions());
}
public static IAsyncReader OpenAsyncReader(
public static IAsyncReader OpenAsync(
string path,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
@@ -42,47 +15,39 @@ public partial class AceReader
{
cancellationToken.ThrowIfCancellationRequested();
path.NotNullOrEmpty(nameof(path));
return (IAsyncReader)OpenReader(new FileInfo(path), readerOptions);
return (IAsyncReader)Open(new FileInfo(path), readerOptions);
}
public static IAsyncReader OpenAsyncReader(
public static IAsyncReader OpenAsync(
Stream stream,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncReader)OpenReader(stream, readerOptions);
return (IAsyncReader)Open(stream, readerOptions);
}
public static IAsyncReader OpenAsyncReader(
IEnumerable<Stream> streams,
ReaderOptions? options = null
)
{
streams.NotNull(nameof(streams));
return new MultiVolumeAceReader(streams, options ?? new ReaderOptions());
}
public static IAsyncReader OpenAsyncReader(
public static IAsyncReader OpenAsync(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncReader)OpenReader(fileInfo, readerOptions);
return (IAsyncReader)Open(fileInfo, readerOptions);
}
public static IReader OpenReader(string filePath, ReaderOptions? readerOptions = null)
public static IReader Open(string filePath, ReaderOptions? readerOptions = null)
{
filePath.NotNullOrEmpty(nameof(filePath));
return OpenReader(new FileInfo(filePath), readerOptions);
return Open(new FileInfo(filePath), readerOptions);
}
public static IReader OpenReader(FileInfo fileInfo, ReaderOptions? readerOptions = null)
public static IReader Open(FileInfo fileInfo, ReaderOptions? readerOptions = null)
{
fileInfo.NotNull(nameof(fileInfo));
return OpenReader(fileInfo.OpenRead(), readerOptions);
return Open(fileInfo.OpenRead(), readerOptions);
}
}
#endif

View File

@@ -1,120 +1,115 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SharpCompress.Common;
using SharpCompress.Common.Ace;
using SharpCompress.Common.Ace.Headers;
using SharpCompress.Common.Arj;
namespace SharpCompress.Readers.Ace;
/// <summary>
/// Reader for ACE archives.
/// ACE is a proprietary archive format. This implementation supports both ACE 1.0 and ACE 2.0 formats
/// and can read archive metadata and extract uncompressed (stored) entries.
/// Compressed entries require proprietary decompression algorithms that are not publicly documented.
/// </summary>
/// <remarks>
/// ACE 2.0 additions over ACE 1.0:
/// - Improved LZ77 compression (compression type 2)
/// - Recovery record support
/// - Additional header flags
/// </remarks>
public abstract partial class AceReader : AbstractReader<AceEntry, AceVolume>
namespace SharpCompress.Readers.Ace
{
private readonly IArchiveEncoding _archiveEncoding;
internal AceReader(ReaderOptions options)
: base(options, ArchiveType.Ace)
{
_archiveEncoding = Options.ArchiveEncoding;
}
private AceReader(Stream stream, ReaderOptions options)
: this(options) { }
/// <summary>
/// Derived class must create or manage the Volume itself.
/// AbstractReader.Volume is get-only, so it cannot be set here.
/// Reader for ACE archives.
/// ACE is a proprietary archive format. This implementation supports both ACE 1.0 and ACE 2.0 formats
/// and can read archive metadata and extract uncompressed (stored) entries.
/// Compressed entries require proprietary decompression algorithms that are not publicly documented.
/// </summary>
public override AceVolume? Volume => _volume;
private AceVolume? _volume;
protected abstract void ValidateArchive(AceVolume archive);
protected override IEnumerable<AceEntry> GetEntries(Stream stream)
/// <remarks>
/// ACE 2.0 additions over ACE 1.0:
/// - Improved LZ77 compression (compression type 2)
/// - Recovery record support
/// - Additional header flags
/// </remarks>
public abstract partial class AceReader : AbstractReader<AceEntry, AceVolume>
{
var mainHeaderReader = new AceMainHeader(_archiveEncoding);
var mainHeader = mainHeaderReader.Read(stream);
if (mainHeader == null)
private readonly IArchiveEncoding _archiveEncoding;
internal AceReader(ReaderOptions options)
: base(options, ArchiveType.Ace)
{
yield break;
_archiveEncoding = Options.ArchiveEncoding;
}
if (mainHeader?.IsMultiVolume == true)
private AceReader(Stream stream, ReaderOptions options)
: this(options) { }
/// <summary>
/// Derived class must create or manage the Volume itself.
/// AbstractReader.Volume is get-only, so it cannot be set here.
/// </summary>
public override AceVolume? Volume => _volume;
private AceVolume? _volume;
/// <summary>
/// Opens an AceReader for non-seeking usage with a single volume.
/// </summary>
/// <param name="stream">The stream containing the ACE archive.</param>
/// <param name="options">Reader options.</param>
/// <returns>An AceReader instance.</returns>
public static IReader Open(Stream stream, ReaderOptions? options = null)
{
throw new MultiVolumeExtractionException("Multi volumes are currently not supported");
stream.NotNull(nameof(stream));
return new SingleVolumeAceReader(stream, options ?? new ReaderOptions());
}
if (_volume == null)
/// <summary>
/// Opens an AceReader for Non-seeking usage with multiple volumes
/// </summary>
/// <param name="streams"></param>
/// <param name="options"></param>
/// <returns></returns>
public static IReader Open(IEnumerable<Stream> streams, ReaderOptions? options = null)
{
_volume = new AceVolume(stream, Options, 0);
ValidateArchive(_volume);
streams.NotNull(nameof(streams));
return new MultiVolumeAceReader(streams, options ?? new ReaderOptions());
}
var localHeaderReader = new AceFileHeader(_archiveEncoding);
while (true)
protected abstract void ValidateArchive(AceVolume archive);
protected override IEnumerable<AceEntry> GetEntries(Stream stream)
{
var localHeader = localHeaderReader.Read(stream);
if (localHeader?.IsFileEncrypted == true)
var mainHeaderReader = new AceMainHeader(_archiveEncoding);
var mainHeader = mainHeaderReader.Read(stream);
if (mainHeader == null)
{
throw new CryptographicException(
"Password protected archives are currently not supported"
yield break;
}
if (mainHeader?.IsMultiVolume == true)
{
throw new MultiVolumeExtractionException(
"Multi volumes are currently not supported"
);
}
if (localHeader == null)
break;
yield return new AceEntry(new AceFilePart((AceFileHeader)localHeader, stream));
}
}
protected override async IAsyncEnumerable<AceEntry> GetEntriesAsync(Stream stream)
{
var mainHeaderReader = new AceMainHeader(_archiveEncoding);
var mainHeader = await mainHeaderReader.ReadAsync(stream);
if (mainHeader == null)
{
yield break;
}
if (mainHeader?.IsMultiVolume == true)
{
throw new MultiVolumeExtractionException("Multi volumes are currently not supported");
}
if (_volume == null)
{
_volume = new AceVolume(stream, Options, 0);
ValidateArchive(_volume);
}
var localHeaderReader = new AceFileHeader(_archiveEncoding);
while (true)
{
var localHeader = await localHeaderReader.ReadAsync(stream);
if (localHeader?.IsFileEncrypted == true)
if (_volume == null)
{
throw new CryptographicException(
"Password protected archives are currently not supported"
);
_volume = new AceVolume(stream, Options, 0);
ValidateArchive(_volume);
}
if (localHeader == null)
break;
yield return new AceEntry(new AceFilePart((AceFileHeader)localHeader, stream));
var localHeaderReader = new AceFileHeader(_archiveEncoding);
while (true)
{
var localHeader = localHeaderReader.Read(stream);
if (localHeader?.IsFileEncrypted == true)
{
throw new CryptographicException(
"Password protected archives are currently not supported"
);
}
if (localHeader == null)
break;
yield return new AceEntry(new AceFilePart((AceFileHeader)localHeader, stream));
}
}
}
protected virtual IEnumerable<FilePart> CreateFilePartEnumerableForCurrentEntry() =>
Entry.Parts;
protected virtual IEnumerable<FilePart> CreateFilePartEnumerableForCurrentEntry() =>
Entry.Parts;
}
}

View File

@@ -7,7 +7,7 @@ namespace SharpCompress.Readers.Arc;
public partial class ArcReader : IReaderOpenable
{
public static IAsyncReader OpenAsyncReader(
public static IAsyncReader OpenAsync(
string path,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
@@ -15,39 +15,39 @@ public partial class ArcReader : IReaderOpenable
{
cancellationToken.ThrowIfCancellationRequested();
path.NotNullOrEmpty(nameof(path));
return (IAsyncReader)OpenReader(new FileInfo(path), readerOptions);
return (IAsyncReader)Open(new FileInfo(path), readerOptions);
}
public static IAsyncReader OpenAsyncReader(
public static IAsyncReader OpenAsync(
Stream stream,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncReader)OpenReader(stream, readerOptions);
return (IAsyncReader)Open(stream, readerOptions);
}
public static IAsyncReader OpenAsyncReader(
public static IAsyncReader OpenAsync(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncReader)OpenReader(fileInfo, readerOptions);
return (IAsyncReader)Open(fileInfo, readerOptions);
}
public static IReader OpenReader(string filePath, ReaderOptions? readerOptions = null)
public static IReader Open(string filePath, ReaderOptions? readerOptions = null)
{
filePath.NotNullOrEmpty(nameof(filePath));
return OpenReader(new FileInfo(filePath), readerOptions);
return Open(new FileInfo(filePath), readerOptions);
}
public static IReader OpenReader(FileInfo fileInfo, ReaderOptions? readerOptions = null)
public static IReader Open(FileInfo fileInfo, ReaderOptions? readerOptions = null)
{
fileInfo.NotNull(nameof(fileInfo));
return OpenReader(fileInfo.OpenRead(), readerOptions);
return Open(fileInfo.OpenRead(), readerOptions);
}
}
#endif

View File

@@ -22,7 +22,7 @@ namespace SharpCompress.Readers.Arc
/// <param name="stream"></param>
/// <param name="options"></param>
/// <returns></returns>
public static IReader OpenReader(Stream stream, ReaderOptions? options = null)
public static IReader Open(Stream stream, ReaderOptions? options = null)
{
stream.NotNull(nameof(stream));
return new ArcReader(stream, options ?? new ReaderOptions());

View File

@@ -7,7 +7,7 @@ namespace SharpCompress.Readers.Arj;
public partial class ArjReader : IReaderOpenable
{
public static IAsyncReader OpenAsyncReader(
public static IAsyncReader OpenAsync(
string path,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
@@ -15,39 +15,39 @@ public partial class ArjReader : IReaderOpenable
{
cancellationToken.ThrowIfCancellationRequested();
path.NotNullOrEmpty(nameof(path));
return (IAsyncReader)OpenReader(new FileInfo(path), readerOptions);
return (IAsyncReader)Open(new FileInfo(path), readerOptions);
}
public static IAsyncReader OpenAsyncReader(
public static IAsyncReader OpenAsync(
Stream stream,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncReader)OpenReader(stream, readerOptions);
return (IAsyncReader)Open(stream, readerOptions);
}
public static IAsyncReader OpenAsyncReader(
public static IAsyncReader OpenAsync(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncReader)OpenReader(fileInfo, readerOptions);
return (IAsyncReader)Open(fileInfo, readerOptions);
}
public static IReader OpenReader(string filePath, ReaderOptions? readerOptions = null)
public static IReader Open(string filePath, ReaderOptions? readerOptions = null)
{
filePath.NotNullOrEmpty(nameof(filePath));
return OpenReader(new FileInfo(filePath), readerOptions);
return Open(new FileInfo(filePath), readerOptions);
}
public static IReader OpenReader(FileInfo fileInfo, ReaderOptions? readerOptions = null)
public static IReader Open(FileInfo fileInfo, ReaderOptions? readerOptions = null)
{
fileInfo.NotNull(nameof(fileInfo));
return OpenReader(fileInfo.OpenRead(), readerOptions);
return Open(fileInfo.OpenRead(), readerOptions);
}
}
#endif

View File

@@ -1,8 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
using SharpCompress.Common.Arj;
using SharpCompress.Common.Arj.Headers;
@@ -29,7 +27,7 @@ namespace SharpCompress.Readers.Arj
/// <param name="stream"></param>
/// <param name="options"></param>
/// <returns></returns>
public static IReader OpenReader(Stream stream, ReaderOptions? options = null)
public static IReader Open(Stream stream, ReaderOptions? options = null)
{
stream.NotNull(nameof(stream));
return new SingleVolumeArjReader(stream, options ?? new ReaderOptions());
@@ -41,7 +39,7 @@ namespace SharpCompress.Readers.Arj
/// <param name="streams"></param>
/// <param name="options"></param>
/// <returns></returns>
public static IReader OpenReader(IEnumerable<Stream> streams, ReaderOptions? options = null)
public static IReader Open(IEnumerable<Stream> streams, ReaderOptions? options = null)
{
streams.NotNull(nameof(streams));
return new MultiVolumeArjReader(streams, options ?? new ReaderOptions());
@@ -85,42 +83,6 @@ namespace SharpCompress.Readers.Arj
}
}
protected override async IAsyncEnumerable<ArjEntry> GetEntriesAsync(Stream stream)
{
var encoding = new ArchiveEncoding();
var mainHeaderReader = new ArjMainHeader(encoding);
var localHeaderReader = new ArjLocalHeader(encoding);
var mainHeader = await mainHeaderReader.ReadAsync(stream);
if (mainHeader?.IsVolume == true)
{
throw new MultiVolumeExtractionException(
"Multi volumes are currently not supported"
);
}
if (mainHeader?.IsGabled == true)
{
throw new CryptographicException(
"Password protected archives are currently not supported"
);
}
if (_volume == null)
{
_volume = new ArjVolume(stream, Options, 0);
ValidateArchive(_volume);
}
while (true)
{
var localHeader = await localHeaderReader.ReadAsync(stream);
if (localHeader == null)
break;
yield return new ArjEntry(new ArjFilePart((ArjLocalHeader)localHeader, stream));
}
}
protected virtual IEnumerable<FilePart> CreateFilePartEnumerableForCurrentEntry() =>
Entry.Parts;
}

View File

@@ -7,7 +7,7 @@ namespace SharpCompress.Readers.GZip;
public partial class GZipReader : IReaderOpenable
{
public static IAsyncReader OpenAsyncReader(
public static IAsyncReader OpenAsync(
string path,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
@@ -15,39 +15,39 @@ public partial class GZipReader : IReaderOpenable
{
cancellationToken.ThrowIfCancellationRequested();
path.NotNullOrEmpty(nameof(path));
return (IAsyncReader)OpenReader(new FileInfo(path), readerOptions);
return (IAsyncReader)Open(new FileInfo(path), readerOptions);
}
public static IAsyncReader OpenAsyncReader(
public static IAsyncReader OpenAsync(
Stream stream,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncReader)OpenReader(stream, readerOptions);
return (IAsyncReader)Open(stream, readerOptions);
}
public static IAsyncReader OpenAsyncReader(
public static IAsyncReader OpenAsync(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncReader)OpenReader(fileInfo, readerOptions);
return (IAsyncReader)Open(fileInfo, readerOptions);
}
public static IReader OpenReader(string filePath, ReaderOptions? readerOptions = null)
public static IReader Open(string filePath, ReaderOptions? readerOptions = null)
{
filePath.NotNullOrEmpty(nameof(filePath));
return OpenReader(new FileInfo(filePath), readerOptions);
return Open(new FileInfo(filePath), readerOptions);
}
public static IReader OpenReader(FileInfo fileInfo, ReaderOptions? readerOptions = null)
public static IReader Open(FileInfo fileInfo, ReaderOptions? readerOptions = null)
{
fileInfo.NotNull(nameof(fileInfo));
return OpenReader(fileInfo.OpenRead(), readerOptions);
return Open(fileInfo.OpenRead(), readerOptions);
}
}
#endif

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