Merge remote-tracking branch 'origin/master' into adam/add-alternate-compressions
# Conflicts: # src/SharpCompress/Archives/GZip/GZipArchive.Async.cs # src/SharpCompress/Archives/GZip/GZipArchive.cs # src/SharpCompress/Archives/Zip/ZipArchive.Async.cs # src/SharpCompress/Archives/Zip/ZipArchive.cs # src/SharpCompress/Common/GZip/GZipEntry.Async.cs # src/SharpCompress/Common/GZip/GZipEntry.cs # src/SharpCompress/Common/Options/IReaderOptions.cs # src/SharpCompress/Readers/ReaderOptions.cs # src/SharpCompress/Readers/Zip/ZipReader.Async.cs # src/SharpCompress/Readers/Zip/ZipReader.cs # src/SharpCompress/Writers/GZip/GZipWriterOptions.cs
This commit is contained in:
commit
a9f2d3cf7f
219 changed files with 3874 additions and 1921 deletions
|
|
@ -3,7 +3,7 @@
|
|||
"isRoot": true,
|
||||
"tools": {
|
||||
"csharpier": {
|
||||
"version": "1.2.5",
|
||||
"version": "1.2.6",
|
||||
"commands": [
|
||||
"csharpier"
|
||||
],
|
||||
|
|
|
|||
50
.github/workflows/performance-benchmarks.yml
vendored
Normal file
50
.github/workflows/performance-benchmarks.yml
vendored
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
name: Performance Benchmarks
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
- 'release'
|
||||
pull_request:
|
||||
branches:
|
||||
- 'master'
|
||||
- 'release'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
benchmark:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: actions/setup-dotnet@v5
|
||||
with:
|
||||
dotnet-version: 10.0.x
|
||||
|
||||
- name: Build Performance Project
|
||||
run: dotnet build tests/SharpCompress.Performance/SharpCompress.Performance.csproj --configuration Release
|
||||
|
||||
- name: Run Benchmarks
|
||||
run: dotnet run --project tests/SharpCompress.Performance/SharpCompress.Performance.csproj --configuration Release --no-build -- --filter "*" --exporters json markdown --artifacts benchmark-results
|
||||
continue-on-error: true
|
||||
|
||||
- name: Display Benchmark Results
|
||||
if: always()
|
||||
run: dotnet run --project build/build.csproj -- display-benchmark-results
|
||||
|
||||
- name: Compare with Baseline
|
||||
if: always()
|
||||
run: dotnet run --project build/build.csproj -- compare-benchmark-results
|
||||
|
||||
- name: Upload Benchmark Results
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: benchmark-results
|
||||
path: benchmark-results/
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -17,6 +17,10 @@ tests/TestArchives/*/Scratch2
|
|||
tools
|
||||
.idea/
|
||||
artifacts/
|
||||
BenchmarkDotNet.Artifacts/
|
||||
baseline-artifacts/
|
||||
profiler-snapshots/
|
||||
|
||||
.DS_Store
|
||||
*.snupkg
|
||||
benchmark-results/
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<Project>
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="BenchmarkDotNet" Version="0.15.8" />
|
||||
<PackageVersion Include="Bullseye" Version="6.1.0" />
|
||||
<PackageVersion Include="AwesomeAssertions" Version="9.3.0" />
|
||||
<PackageVersion Include="Glob" Version="1.1.9" />
|
||||
|
|
|
|||
385
build/Program.cs
385
build/Program.cs
|
|
@ -19,6 +19,9 @@ const string Publish = "publish";
|
|||
const string DetermineVersion = "determine-version";
|
||||
const string UpdateVersion = "update-version";
|
||||
const string PushToNuGet = "push-to-nuget";
|
||||
const string DisplayBenchmarkResults = "display-benchmark-results";
|
||||
const string CompareBenchmarkResults = "compare-benchmark-results";
|
||||
const string GenerateBaseline = "generate-baseline";
|
||||
|
||||
Target(
|
||||
Clean,
|
||||
|
|
@ -210,6 +213,249 @@ Target(
|
|||
}
|
||||
);
|
||||
|
||||
Target(
|
||||
DisplayBenchmarkResults,
|
||||
() =>
|
||||
{
|
||||
var githubStepSummary = Environment.GetEnvironmentVariable("GITHUB_STEP_SUMMARY");
|
||||
var resultsDir = "benchmark-results/results";
|
||||
|
||||
if (!Directory.Exists(resultsDir))
|
||||
{
|
||||
Console.WriteLine("No benchmark results found.");
|
||||
return;
|
||||
}
|
||||
|
||||
var markdownFiles = Directory
|
||||
.GetFiles(resultsDir, "*-report-github.md")
|
||||
.OrderBy(f => f)
|
||||
.ToList();
|
||||
|
||||
if (markdownFiles.Count == 0)
|
||||
{
|
||||
Console.WriteLine("No benchmark markdown reports found.");
|
||||
return;
|
||||
}
|
||||
|
||||
var output = new List<string> { "## Benchmark Results", "" };
|
||||
|
||||
foreach (var file in markdownFiles)
|
||||
{
|
||||
Console.WriteLine($"Processing {Path.GetFileName(file)}");
|
||||
var content = File.ReadAllText(file);
|
||||
output.Add(content);
|
||||
output.Add("");
|
||||
}
|
||||
|
||||
// Write to GitHub Step Summary if available
|
||||
if (!string.IsNullOrEmpty(githubStepSummary))
|
||||
{
|
||||
File.AppendAllLines(githubStepSummary, output);
|
||||
Console.WriteLine($"Benchmark results written to GitHub Step Summary");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Write to console if not in GitHub Actions
|
||||
foreach (var line in output)
|
||||
{
|
||||
Console.WriteLine(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
Target(
|
||||
CompareBenchmarkResults,
|
||||
() =>
|
||||
{
|
||||
var githubStepSummary = Environment.GetEnvironmentVariable("GITHUB_STEP_SUMMARY");
|
||||
var baselinePath = "tests/SharpCompress.Performance/baseline-results.md";
|
||||
var resultsDir = "benchmark-results/results";
|
||||
|
||||
var output = new List<string> { "## Comparison with Baseline", "" };
|
||||
|
||||
if (!File.Exists(baselinePath))
|
||||
{
|
||||
Console.WriteLine("Baseline file not found");
|
||||
output.Add("⚠️ Baseline file not found. Run `generate-baseline` to create it.");
|
||||
WriteOutput(output, githubStepSummary);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Directory.Exists(resultsDir))
|
||||
{
|
||||
Console.WriteLine("No current benchmark results found.");
|
||||
output.Add("⚠️ No current benchmark results found. Showing baseline only.");
|
||||
output.Add("");
|
||||
output.Add("### Baseline Results");
|
||||
output.AddRange(File.ReadAllLines(baselinePath));
|
||||
WriteOutput(output, githubStepSummary);
|
||||
return;
|
||||
}
|
||||
|
||||
var markdownFiles = Directory
|
||||
.GetFiles(resultsDir, "*-report-github.md")
|
||||
.OrderBy(f => f)
|
||||
.ToList();
|
||||
|
||||
if (markdownFiles.Count == 0)
|
||||
{
|
||||
Console.WriteLine("No current benchmark markdown reports found.");
|
||||
output.Add("⚠️ No current benchmark results found. Showing baseline only.");
|
||||
output.Add("");
|
||||
output.Add("### Baseline Results");
|
||||
output.AddRange(File.ReadAllLines(baselinePath));
|
||||
WriteOutput(output, githubStepSummary);
|
||||
return;
|
||||
}
|
||||
|
||||
Console.WriteLine("Parsing baseline results...");
|
||||
var baselineMetrics = ParseBenchmarkResults(File.ReadAllText(baselinePath));
|
||||
|
||||
Console.WriteLine("Parsing current results...");
|
||||
var currentText = string.Join("\n", markdownFiles.Select(f => File.ReadAllText(f)));
|
||||
var currentMetrics = ParseBenchmarkResults(currentText);
|
||||
|
||||
Console.WriteLine("Comparing results...");
|
||||
output.Add("### Performance Comparison");
|
||||
output.Add("");
|
||||
output.Add(
|
||||
"| Benchmark | Baseline Mean | Current Mean | Change | Baseline Memory | Current Memory | Change |"
|
||||
);
|
||||
output.Add(
|
||||
"|-----------|---------------|--------------|--------|-----------------|----------------|--------|"
|
||||
);
|
||||
|
||||
var hasRegressions = false;
|
||||
var hasImprovements = false;
|
||||
|
||||
foreach (var method in currentMetrics.Keys.Union(baselineMetrics.Keys).OrderBy(k => k))
|
||||
{
|
||||
var hasCurrent = currentMetrics.TryGetValue(method, out var current);
|
||||
var hasBaseline = baselineMetrics.TryGetValue(method, out var baseline);
|
||||
|
||||
if (!hasCurrent)
|
||||
{
|
||||
output.Add(
|
||||
$"| {method} | {baseline!.Mean} | ❌ Missing | N/A | {baseline.Memory} | N/A | N/A |"
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!hasBaseline)
|
||||
{
|
||||
output.Add(
|
||||
$"| {method} | ❌ New | {current!.Mean} | N/A | N/A | {current.Memory} | N/A |"
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
var timeChange = CalculateChange(baseline!.MeanValue, current!.MeanValue);
|
||||
var memChange = CalculateChange(baseline.MemoryValue, current.MemoryValue);
|
||||
|
||||
var timeIcon =
|
||||
timeChange > 25 ? "🔴"
|
||||
: timeChange < -25 ? "🟢"
|
||||
: "⚪";
|
||||
var memIcon =
|
||||
memChange > 25 ? "🔴"
|
||||
: memChange < -25 ? "🟢"
|
||||
: "⚪";
|
||||
|
||||
if (timeChange > 25 || memChange > 25)
|
||||
hasRegressions = true;
|
||||
if (timeChange < -25 || memChange < -25)
|
||||
hasImprovements = true;
|
||||
|
||||
output.Add(
|
||||
$"| {method} | {baseline.Mean} | {current.Mean} | {timeIcon} {timeChange:+0.0;-0.0;0}% | {baseline.Memory} | {current.Memory} | {memIcon} {memChange:+0.0;-0.0;0}% |"
|
||||
);
|
||||
}
|
||||
|
||||
output.Add("");
|
||||
output.Add("**Legend:**");
|
||||
output.Add("- 🔴 Regression (>25% slower/more memory)");
|
||||
output.Add("- 🟢 Improvement (>25% faster/less memory)");
|
||||
output.Add("- ⚪ No significant change");
|
||||
|
||||
if (hasRegressions)
|
||||
{
|
||||
output.Add("");
|
||||
output.Add(
|
||||
"⚠️ **Warning**: Performance regressions detected. Review the changes carefully."
|
||||
);
|
||||
}
|
||||
else if (hasImprovements)
|
||||
{
|
||||
output.Add("");
|
||||
output.Add("✅ Performance improvements detected!");
|
||||
}
|
||||
else
|
||||
{
|
||||
output.Add("");
|
||||
output.Add("✅ Performance is stable compared to baseline.");
|
||||
}
|
||||
|
||||
WriteOutput(output, githubStepSummary);
|
||||
}
|
||||
);
|
||||
|
||||
Target(
|
||||
GenerateBaseline,
|
||||
() =>
|
||||
{
|
||||
var perfProject = "tests/SharpCompress.Performance/SharpCompress.Performance.csproj";
|
||||
var baselinePath = "tests/SharpCompress.Performance/baseline-results.md";
|
||||
var artifactsDir = "baseline-artifacts";
|
||||
|
||||
Console.WriteLine("Building performance project...");
|
||||
Run("dotnet", $"build {perfProject} --configuration Release");
|
||||
|
||||
Console.WriteLine("Running benchmarks to generate baseline...");
|
||||
Run(
|
||||
"dotnet",
|
||||
$"run --project {perfProject} --configuration Release --no-build -- --filter \"*\" --exporters markdown --artifacts {artifactsDir}"
|
||||
);
|
||||
|
||||
var resultsDir = Path.Combine(artifactsDir, "results");
|
||||
if (!Directory.Exists(resultsDir))
|
||||
{
|
||||
Console.WriteLine("ERROR: No benchmark results generated.");
|
||||
return;
|
||||
}
|
||||
|
||||
var markdownFiles = Directory
|
||||
.GetFiles(resultsDir, "*-report-github.md")
|
||||
.OrderBy(f => f)
|
||||
.ToList();
|
||||
|
||||
if (markdownFiles.Count == 0)
|
||||
{
|
||||
Console.WriteLine("ERROR: No markdown reports found.");
|
||||
return;
|
||||
}
|
||||
|
||||
Console.WriteLine($"Combining {markdownFiles.Count} benchmark reports...");
|
||||
var baselineContent = new List<string>();
|
||||
|
||||
foreach (var file in markdownFiles)
|
||||
{
|
||||
var lines = File.ReadAllLines(file);
|
||||
baselineContent.AddRange(lines.Select(l => l.Trim()).Where(l => l.StartsWith('|')));
|
||||
}
|
||||
|
||||
File.WriteAllText(baselinePath, string.Join(Environment.NewLine, baselineContent));
|
||||
Console.WriteLine($"Baseline written to {baselinePath}");
|
||||
|
||||
// Clean up artifacts directory
|
||||
if (Directory.Exists(artifactsDir))
|
||||
{
|
||||
Directory.Delete(artifactsDir, true);
|
||||
Console.WriteLine("Cleaned up artifacts directory.");
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
Target("default", [Publish], () => Console.WriteLine("Done!"));
|
||||
|
||||
await RunTargetsAndExitAsync(args);
|
||||
|
|
@ -302,3 +548,142 @@ static async Task<string> GetGitOutput(string command, string args)
|
|||
throw new Exception($"Git command failed: git {command} {args}\n{ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
static void WriteOutput(List<string> output, string? githubStepSummary)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(githubStepSummary))
|
||||
{
|
||||
File.AppendAllLines(githubStepSummary, output);
|
||||
Console.WriteLine("Comparison written to GitHub Step Summary");
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var line in output)
|
||||
{
|
||||
Console.WriteLine(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Dictionary<string, BenchmarkMetric> ParseBenchmarkResults(string markdown)
|
||||
{
|
||||
var metrics = new Dictionary<string, BenchmarkMetric>();
|
||||
var lines = markdown.Split('\n');
|
||||
|
||||
for (int i = 0; i < lines.Length; i++)
|
||||
{
|
||||
var line = lines[i].Trim();
|
||||
|
||||
// Look for table rows with benchmark data
|
||||
if (line.StartsWith("|") && line.Contains("'") && i > 0)
|
||||
{
|
||||
var parts = line.Split('|', StringSplitOptions.TrimEntries);
|
||||
if (parts.Length >= 5)
|
||||
{
|
||||
var method = parts[1].Replace("'", "'");
|
||||
var meanStr = parts[2];
|
||||
|
||||
// Find Allocated column - it's usually the last column or labeled "Allocated"
|
||||
string memoryStr = "N/A";
|
||||
for (int j = parts.Length - 2; j >= 2; j--)
|
||||
{
|
||||
if (
|
||||
parts[j].Contains("KB")
|
||||
|| parts[j].Contains("MB")
|
||||
|| parts[j].Contains("GB")
|
||||
|| parts[j].Contains("B")
|
||||
)
|
||||
{
|
||||
memoryStr = parts[j];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
!method.Equals("Method", StringComparison.OrdinalIgnoreCase)
|
||||
&& !string.IsNullOrWhiteSpace(method)
|
||||
)
|
||||
{
|
||||
var metric = new BenchmarkMetric
|
||||
{
|
||||
Method = method,
|
||||
Mean = meanStr,
|
||||
MeanValue = ParseTimeValue(meanStr),
|
||||
Memory = memoryStr,
|
||||
MemoryValue = ParseMemoryValue(memoryStr),
|
||||
};
|
||||
metrics[method] = metric;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return metrics;
|
||||
}
|
||||
|
||||
static double ParseTimeValue(string timeStr)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(timeStr) || timeStr == "N/A" || timeStr == "NA")
|
||||
return 0;
|
||||
|
||||
// Remove thousands separators and parse
|
||||
timeStr = timeStr.Replace(",", "").Trim();
|
||||
|
||||
var match = Regex.Match(timeStr, @"([\d.]+)\s*(\w+)");
|
||||
if (!match.Success)
|
||||
return 0;
|
||||
|
||||
var value = double.Parse(match.Groups[1].Value);
|
||||
var unit = match.Groups[2].Value.ToLower();
|
||||
|
||||
// Convert to microseconds for comparison
|
||||
return unit switch
|
||||
{
|
||||
"s" => value * 1_000_000,
|
||||
"ms" => value * 1_000,
|
||||
"μs" or "us" => value,
|
||||
"ns" => value / 1_000,
|
||||
_ => value,
|
||||
};
|
||||
}
|
||||
|
||||
static double ParseMemoryValue(string memStr)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(memStr) || memStr == "N/A" || memStr == "NA")
|
||||
return 0;
|
||||
|
||||
memStr = memStr.Replace(",", "").Trim();
|
||||
|
||||
var match = Regex.Match(memStr, @"([\d.]+)\s*(\w+)");
|
||||
if (!match.Success)
|
||||
return 0;
|
||||
|
||||
var value = double.Parse(match.Groups[1].Value);
|
||||
var unit = match.Groups[2].Value.ToUpper();
|
||||
|
||||
// Convert to KB for comparison
|
||||
return unit switch
|
||||
{
|
||||
"GB" => value * 1_024 * 1_024,
|
||||
"MB" => value * 1_024,
|
||||
"KB" => value,
|
||||
"B" => value / 1_024,
|
||||
_ => value,
|
||||
};
|
||||
}
|
||||
|
||||
static double CalculateChange(double baseline, double current)
|
||||
{
|
||||
if (baseline == 0)
|
||||
return 0;
|
||||
return ((current - baseline) / baseline) * 100;
|
||||
}
|
||||
|
||||
record BenchmarkMetric
|
||||
{
|
||||
public string Method { get; init; } = "";
|
||||
public string Mean { get; init; } = "";
|
||||
public double MeanValue { get; init; }
|
||||
public string Memory { get; init; } = "";
|
||||
public double MemoryValue { get; init; }
|
||||
}
|
||||
|
|
|
|||
123
docs/API.md
123
docs/API.md
|
|
@ -20,14 +20,18 @@ using (var archive = RarArchive.OpenArchive("file.rar"))
|
|||
using (var archive = SevenZipArchive.OpenArchive("file.7z"))
|
||||
using (var archive = GZipArchive.OpenArchive("file.gz"))
|
||||
|
||||
// With options
|
||||
var options = new ReaderOptions
|
||||
// With fluent options (preferred)
|
||||
var options = ReaderOptions.ForEncryptedArchive("password")
|
||||
.WithArchiveEncoding(new ArchiveEncoding { Default = Encoding.GetEncoding(932) });
|
||||
using (var archive = ZipArchive.OpenArchive("encrypted.zip", options))
|
||||
|
||||
// Alternative: object initializer
|
||||
var options2 = new ReaderOptions
|
||||
{
|
||||
Password = "password",
|
||||
LeaveStreamOpen = true,
|
||||
ArchiveEncoding = new ArchiveEncoding { Default = Encoding.GetEncoding(932) }
|
||||
};
|
||||
using (var archive = ZipArchive.OpenArchive("encrypted.zip", options))
|
||||
```
|
||||
|
||||
### Creating Archives
|
||||
|
|
@ -44,16 +48,21 @@ using (var archive = ZipArchive.CreateArchive())
|
|||
using (var archive = TarArchive.CreateArchive())
|
||||
using (var archive = GZipArchive.CreateArchive())
|
||||
|
||||
// With options
|
||||
var options = new WriterOptions(CompressionType.Deflate)
|
||||
{
|
||||
CompressionLevel = 9,
|
||||
LeaveStreamOpen = false
|
||||
};
|
||||
// With fluent options (preferred)
|
||||
var options = WriterOptions.ForZip()
|
||||
.WithCompressionLevel(9)
|
||||
.WithLeaveStreamOpen(false);
|
||||
using (var archive = ZipArchive.CreateArchive())
|
||||
{
|
||||
archive.SaveTo("output.zip", options);
|
||||
}
|
||||
|
||||
// Alternative: constructor with object initializer
|
||||
var options2 = new WriterOptions(CompressionType.Deflate)
|
||||
{
|
||||
CompressionLevel = 9,
|
||||
LeaveStreamOpen = false
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
|
@ -72,16 +81,11 @@ using (var archive = ZipArchive.OpenArchive("file.zip"))
|
|||
var entry = archive.Entries.FirstOrDefault(e => e.Key == "file.txt");
|
||||
|
||||
// Extract all
|
||||
archive.WriteToDirectory(@"C:\output", new ExtractionOptions
|
||||
{
|
||||
ExtractFullPath = true,
|
||||
Overwrite = true
|
||||
});
|
||||
archive.WriteToDirectory(@"C:\output");
|
||||
|
||||
// Extract single entry
|
||||
var entry = archive.Entries.First();
|
||||
entry.WriteToFile(@"C:\output\file.txt");
|
||||
entry.WriteToFile(@"C:\output\file.txt", new ExtractionOptions { Overwrite = true });
|
||||
|
||||
// Get entry stream
|
||||
using (var stream = entry.OpenEntryStream())
|
||||
|
|
@ -95,7 +99,6 @@ using (var asyncArchive = await ZipArchive.OpenAsyncArchive("file.zip"))
|
|||
{
|
||||
await asyncArchive.WriteToDirectoryAsync(
|
||||
@"C:\output",
|
||||
new ExtractionOptions { ExtractFullPath = true, Overwrite = true },
|
||||
cancellationToken: cancellationToken
|
||||
);
|
||||
}
|
||||
|
|
@ -187,7 +190,6 @@ using (var reader = await ReaderFactory.OpenAsyncReader(stream))
|
|||
// Async extraction of all entries
|
||||
await reader.WriteAllToDirectoryAsync(
|
||||
@"C:\output",
|
||||
new ExtractionOptions { ExtractFullPath = true, Overwrite = true },
|
||||
cancellationToken
|
||||
);
|
||||
}
|
||||
|
|
@ -229,43 +231,91 @@ using (var writer = WriterFactory.OpenWriter(stream, ArchiveType.Zip, Compressio
|
|||
|
||||
### ReaderOptions
|
||||
|
||||
Use factory presets and fluent helpers for common configurations:
|
||||
|
||||
```csharp
|
||||
var options = new ReaderOptions
|
||||
{
|
||||
Password = "password", // For encrypted archives
|
||||
LeaveStreamOpen = true, // Don't close wrapped stream
|
||||
ArchiveEncoding = new ArchiveEncoding // Custom character encoding
|
||||
{
|
||||
Default = Encoding.GetEncoding(932)
|
||||
}
|
||||
};
|
||||
// External stream with password and custom encoding
|
||||
var options = ReaderOptions.ForExternalStream()
|
||||
.WithPassword("password")
|
||||
.WithArchiveEncoding(new ArchiveEncoding { Default = Encoding.GetEncoding(932) });
|
||||
|
||||
using (var archive = ZipArchive.OpenArchive("file.zip", options))
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
// Common presets
|
||||
var safeOptions = ReaderOptions.SafeExtract; // No overwrite
|
||||
var flatOptions = ReaderOptions.FlatExtract; // No directory structure
|
||||
|
||||
// Factory defaults:
|
||||
// - file path / FileInfo overloads use LeaveStreamOpen = false
|
||||
// - stream overloads use LeaveStreamOpen = true
|
||||
```
|
||||
|
||||
Alternative: traditional object initializer:
|
||||
|
||||
```csharp
|
||||
var options = new ReaderOptions
|
||||
{
|
||||
Password = "password",
|
||||
LeaveStreamOpen = true,
|
||||
ArchiveEncoding = new ArchiveEncoding { Default = Encoding.GetEncoding(932) }
|
||||
};
|
||||
```
|
||||
|
||||
### WriterOptions
|
||||
|
||||
Factory methods provide a clean, discoverable way to create writer options:
|
||||
|
||||
```csharp
|
||||
// Factory methods for common archive types
|
||||
var zipOptions = WriterOptions.ForZip() // ZIP with Deflate
|
||||
.WithCompressionLevel(9) // 0-9 for Deflate
|
||||
.WithLeaveStreamOpen(false); // Close stream when done
|
||||
|
||||
var tarOptions = WriterOptions.ForTar(CompressionType.GZip) // TAR with GZip
|
||||
.WithLeaveStreamOpen(false);
|
||||
|
||||
var gzipOptions = WriterOptions.ForGZip() // GZip file
|
||||
.WithCompressionLevel(6);
|
||||
|
||||
archive.SaveTo("output.zip", zipOptions);
|
||||
```
|
||||
|
||||
Alternative: traditional constructor with object initializer:
|
||||
|
||||
```csharp
|
||||
var options = new WriterOptions(CompressionType.Deflate)
|
||||
{
|
||||
CompressionLevel = 9, // 0-9 for Deflate
|
||||
LeaveStreamOpen = true, // Don't close stream
|
||||
CompressionLevel = 9,
|
||||
LeaveStreamOpen = true,
|
||||
};
|
||||
archive.SaveTo("output.zip", options);
|
||||
```
|
||||
|
||||
### ExtractionOptions
|
||||
### Extraction behavior
|
||||
|
||||
```csharp
|
||||
var options = new ExtractionOptions
|
||||
var options = new ReaderOptions
|
||||
{
|
||||
ExtractFullPath = true, // Recreate directory structure
|
||||
Overwrite = true, // Overwrite existing files
|
||||
PreserveFileTime = true // Keep original timestamps
|
||||
};
|
||||
archive.WriteToDirectory(@"C:\output", options);
|
||||
|
||||
using (var archive = ZipArchive.OpenArchive("file.zip", options))
|
||||
{
|
||||
archive.WriteToDirectory(@"C:\output");
|
||||
}
|
||||
```
|
||||
|
||||
### Options matrix
|
||||
|
||||
```text
|
||||
ReaderOptions: open-time behavior (password, encoding, stream ownership, extraction defaults)
|
||||
WriterOptions: write-time behavior (compression type/level, encoding, stream ownership)
|
||||
ZipWriterEntryOptions: per-entry ZIP overrides (compression, level, timestamps, comments, zip64)
|
||||
```
|
||||
|
||||
---
|
||||
|
|
@ -317,13 +367,9 @@ ArchiveType.ZStandard
|
|||
try
|
||||
{
|
||||
using (var archive = ZipArchive.Open("archive.zip",
|
||||
new ReaderOptions { Password = "password" }))
|
||||
ReaderOptions.ForEncryptedArchive("password")))
|
||||
{
|
||||
archive.WriteToDirectory(@"C:\output", new ExtractionOptions
|
||||
{
|
||||
ExtractFullPath = true,
|
||||
Overwrite = true
|
||||
});
|
||||
archive.WriteToDirectory(@"C:\output");
|
||||
}
|
||||
}
|
||||
catch (PasswordRequiredException)
|
||||
|
|
@ -348,7 +394,7 @@ var progress = new Progress<ProgressReport>(report =>
|
|||
Console.WriteLine($"Extracting {report.EntryPath}: {report.PercentComplete}%");
|
||||
});
|
||||
|
||||
var options = new ReaderOptions { Progress = progress };
|
||||
var options = ReaderOptions.ForOwnedFile().WithProgress(progress);
|
||||
using (var archive = ZipArchive.OpenArchive("archive.zip", options))
|
||||
{
|
||||
archive.WriteToDirectory(@"C:\output");
|
||||
|
|
@ -367,7 +413,6 @@ try
|
|||
{
|
||||
await archive.WriteToDirectoryAsync(
|
||||
@"C:\output",
|
||||
new ExtractionOptions { ExtractFullPath = true, Overwrite = true },
|
||||
cancellationToken: cts.Token
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ Common types, options, and enumerations used across formats.
|
|||
- `ArchiveType.cs` - Enum for archive formats
|
||||
- `CompressionType.cs` - Enum for compression methods
|
||||
- `ArchiveEncoding.cs` - Character encoding configuration
|
||||
- `ExtractionOptions.cs` - Extraction configuration
|
||||
- `IExtractionOptions.cs` - Extraction configuration exposed through `ReaderOptions`
|
||||
- Format-specific headers: `Zip/Headers/`, `Tar/Headers/`, `Rar/Headers/`, etc.
|
||||
|
||||
#### `Compressors/` - Compression Algorithms
|
||||
|
|
@ -215,13 +215,13 @@ using (var compressor = new DeflateStream(nonDisposingStream))
|
|||
public abstract class AbstractArchive : IArchive
|
||||
{
|
||||
// Template methods
|
||||
public virtual void WriteToDirectory(string destinationDirectory, ExtractionOptions options)
|
||||
public virtual void WriteToDirectory(string destinationDirectory)
|
||||
{
|
||||
// Common extraction logic
|
||||
foreach (var entry in Entries)
|
||||
{
|
||||
// Call subclass method
|
||||
entry.WriteToFile(destinationPath, options);
|
||||
entry.WriteToFile(destinationPath);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -267,8 +267,7 @@ public interface IArchive : IDisposable
|
|||
{
|
||||
IEnumerable<IEntry> Entries { get; }
|
||||
|
||||
void WriteToDirectory(string destinationDirectory,
|
||||
ExtractionOptions options = null);
|
||||
void WriteToDirectory(string destinationDirectory);
|
||||
|
||||
IEntry FirstOrDefault(Func<IEntry, bool> predicate);
|
||||
|
||||
|
|
@ -287,8 +286,7 @@ public interface IReader : IDisposable
|
|||
|
||||
bool MoveToNextEntry();
|
||||
|
||||
void WriteEntryToDirectory(string destinationDirectory,
|
||||
ExtractionOptions options = null);
|
||||
void WriteEntryToDirectory(string destinationDirectory);
|
||||
|
||||
Stream OpenEntryStream();
|
||||
|
||||
|
|
@ -327,7 +325,7 @@ public interface IEntry
|
|||
DateTime? LastModifiedTime { get; }
|
||||
CompressionType CompressionType { get; }
|
||||
|
||||
void WriteToFile(string fullPath, ExtractionOptions options = null);
|
||||
void WriteToFile(string fullPath);
|
||||
void WriteToStream(Stream destinationStream);
|
||||
Stream OpenEntryStream();
|
||||
|
||||
|
|
|
|||
|
|
@ -18,14 +18,9 @@ Most archive formats store filenames and metadata as bytes. SharpCompress must c
|
|||
using SharpCompress.Common;
|
||||
using SharpCompress.Readers;
|
||||
|
||||
// Configure encoding before opening archive
|
||||
var options = new ReaderOptions
|
||||
{
|
||||
ArchiveEncoding = new ArchiveEncoding
|
||||
{
|
||||
Default = Encoding.GetEncoding(932) // cp932 for Japanese
|
||||
}
|
||||
};
|
||||
// Configure encoding using fluent factory method (preferred)
|
||||
var options = ReaderOptions.ForEncoding(
|
||||
new ArchiveEncoding { Default = Encoding.GetEncoding(932) }); // cp932 for Japanese
|
||||
|
||||
using (var archive = ZipArchive.OpenArchive("japanese.zip", options))
|
||||
{
|
||||
|
|
@ -34,6 +29,12 @@ using (var archive = ZipArchive.OpenArchive("japanese.zip", options))
|
|||
Console.WriteLine(entry.Key); // Now shows correct characters
|
||||
}
|
||||
}
|
||||
|
||||
// Alternative: object initializer
|
||||
var options2 = new ReaderOptions
|
||||
{
|
||||
ArchiveEncoding = new ArchiveEncoding { Default = Encoding.GetEncoding(932) }
|
||||
};
|
||||
```
|
||||
|
||||
### ArchiveEncoding Properties
|
||||
|
|
@ -47,10 +48,8 @@ using (var archive = ZipArchive.OpenArchive("japanese.zip", options))
|
|||
|
||||
**Archive API:**
|
||||
```csharp
|
||||
var options = new ReaderOptions
|
||||
{
|
||||
ArchiveEncoding = new ArchiveEncoding { Default = Encoding.GetEncoding(932) }
|
||||
};
|
||||
var options = ReaderOptions.ForEncoding(
|
||||
new ArchiveEncoding { Default = Encoding.GetEncoding(932) });
|
||||
using (var archive = ZipArchive.OpenArchive("file.zip", options))
|
||||
{
|
||||
// Use archive with correct encoding
|
||||
|
|
@ -59,10 +58,8 @@ using (var archive = ZipArchive.OpenArchive("file.zip", options))
|
|||
|
||||
**Reader API:**
|
||||
```csharp
|
||||
var options = new ReaderOptions
|
||||
{
|
||||
ArchiveEncoding = new ArchiveEncoding { Default = Encoding.GetEncoding(932) }
|
||||
};
|
||||
var options = ReaderOptions.ForEncoding(
|
||||
new ArchiveEncoding { Default = Encoding.GetEncoding(932) });
|
||||
using (var stream = File.OpenRead("file.zip"))
|
||||
using (var reader = ReaderFactory.OpenReader(stream, options))
|
||||
{
|
||||
|
|
@ -390,11 +387,7 @@ var options = new ReaderOptions
|
|||
|
||||
using (var archive = ZipArchive.OpenArchive("japanese_files.zip", options))
|
||||
{
|
||||
archive.WriteToDirectory(@"C:\output", new ExtractionOptions
|
||||
{
|
||||
ExtractFullPath = true,
|
||||
Overwrite = true
|
||||
});
|
||||
archive.WriteToDirectory(@"C:\output");
|
||||
}
|
||||
// Files extracted with correct Japanese names
|
||||
```
|
||||
|
|
|
|||
|
|
@ -213,11 +213,7 @@ using (var archive = RarArchive.OpenArchive("solid.rar"))
|
|||
using (var archive = RarArchive.OpenArchive("solid.rar"))
|
||||
{
|
||||
// Method 1: Use WriteToDirectory (recommended)
|
||||
archive.WriteToDirectory(@"C:\output", new ExtractionOptions
|
||||
{
|
||||
ExtractFullPath = true,
|
||||
Overwrite = true
|
||||
});
|
||||
archive.WriteToDirectory(@"C:\output");
|
||||
|
||||
// Method 2: Use ExtractAllEntries
|
||||
archive.ExtractAllEntries();
|
||||
|
|
@ -337,7 +333,6 @@ using (var archive = ZipArchive.OpenArchive("archive.zip"))
|
|||
{
|
||||
await archive.WriteToDirectoryAsync(
|
||||
@"C:\output",
|
||||
new ExtractionOptions { ExtractFullPath = true, Overwrite = true },
|
||||
cancellationToken
|
||||
);
|
||||
}
|
||||
|
|
@ -355,10 +350,7 @@ Async doesn't improve performance for:
|
|||
// Sync extraction (simpler, same performance on fast I/O)
|
||||
using (var archive = ZipArchive.OpenArchive("archive.zip"))
|
||||
{
|
||||
archive.WriteToDirectory(
|
||||
@"C:\output",
|
||||
new ExtractionOptions { ExtractFullPath = true, Overwrite = true }
|
||||
);
|
||||
archive.WriteToDirectory(@"C:\output");
|
||||
}
|
||||
// Simple and fast - no async needed
|
||||
```
|
||||
|
|
@ -377,7 +369,6 @@ try
|
|||
{
|
||||
await archive.WriteToDirectoryAsync(
|
||||
@"C:\output",
|
||||
new ExtractionOptions { ExtractFullPath = true, Overwrite = true },
|
||||
cts.Token
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ SharpCompress now provides full async/await support for all I/O operations. All
|
|||
|
||||
**Key Async Methods:**
|
||||
- `reader.WriteEntryToAsync(stream, cancellationToken)` - Extract entry asynchronously
|
||||
- `reader.WriteAllToDirectoryAsync(path, options, cancellationToken)` - Extract all asynchronously
|
||||
- `reader.WriteAllToDirectoryAsync(path, cancellationToken)` - Extract all asynchronously
|
||||
- `writer.WriteAsync(filename, stream, modTime, cancellationToken)` - Write entry asynchronously
|
||||
- `writer.WriteAllAsync(directory, pattern, searchOption, cancellationToken)` - Write directory asynchronously
|
||||
- `entry.OpenEntryStreamAsync(cancellationToken)` - Open entry stream asynchronously
|
||||
|
|
@ -40,6 +40,10 @@ To deal with the "correct" rules as well as the expectations of users, I've deci
|
|||
|
||||
To be explicit though, consider always using the overloads that use `ReaderOptions` or `WriterOptions` and explicitly set `LeaveStreamOpen` the way you want.
|
||||
|
||||
Default behavior in factory APIs:
|
||||
- File path / `FileInfo` overloads set `LeaveStreamOpen = false`.
|
||||
- Caller-provided `Stream` overloads set `LeaveStreamOpen = true`.
|
||||
|
||||
If using Compression Stream classes directly and you don't want the wrapped stream to be closed. Use the `NonDisposingStream` as a wrapper to prevent the stream being disposed. The change in 0.21 simplified a lot even though the usage is a bit more convoluted.
|
||||
|
||||
## Samples
|
||||
|
|
@ -90,14 +94,14 @@ 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 fluent factory method for extraction options
|
||||
using (var archive = RarArchive.OpenArchive("Test.rar",
|
||||
ReaderOptions.ForOwnedFile()
|
||||
.WithExtractFullPath(true)
|
||||
.WithOverwrite(true)))
|
||||
{
|
||||
// Simple extraction with RarArchive; this WriteToDirectory pattern works for all archive types
|
||||
archive.WriteToDirectory(@"D:\temp", new ExtractionOptions()
|
||||
{
|
||||
ExtractFullPath = true,
|
||||
Overwrite = true
|
||||
});
|
||||
archive.WriteToDirectory(@"D:\temp");
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -126,13 +130,13 @@ 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.OpenArchive("archive.rar",
|
||||
ReaderOptions.ForOwnedFile()
|
||||
.WithProgress(progress)
|
||||
.WithExtractFullPath(true)
|
||||
.WithOverwrite(true))) // Must be solid Rar or 7Zip
|
||||
{
|
||||
archive.WriteToDirectory(@"D:\output", new ExtractionOptions()
|
||||
{
|
||||
ExtractFullPath = true,
|
||||
Overwrite = true
|
||||
});
|
||||
archive.WriteToDirectory(@"D:\output");
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -147,11 +151,7 @@ using (var reader = ReaderFactory.OpenReader(stream))
|
|||
if (!reader.Entry.IsDirectory)
|
||||
{
|
||||
Console.WriteLine(reader.Entry.Key);
|
||||
reader.WriteEntryToDirectory(@"C:\temp", new ExtractionOptions()
|
||||
{
|
||||
ExtractFullPath = true,
|
||||
Overwrite = true
|
||||
});
|
||||
reader.WriteEntryToDirectory(@"C:\temp");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -180,10 +180,10 @@ 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)
|
||||
{
|
||||
LeaveOpenStream = true
|
||||
}))
|
||||
using (var writer = WriterFactory.OpenWriter(
|
||||
stream,
|
||||
ArchiveType.Tar,
|
||||
WriterOptions.ForTar(CompressionType.GZip).WithLeaveStreamOpen(true)))
|
||||
{
|
||||
writer.WriteAll("D:\\temp", "*", SearchOption.AllDirectories);
|
||||
}
|
||||
|
|
@ -192,15 +192,15 @@ using (var writer = WriterFactory.OpenWriter(stream, ArchiveType.Tar, new Writer
|
|||
### Extract zip which has non-utf8 encoded filename(cp932)
|
||||
|
||||
```C#
|
||||
var opts = new SharpCompress.Readers.ReaderOptions();
|
||||
var encoding = Encoding.GetEncoding(932);
|
||||
opts.ArchiveEncoding = new SharpCompress.Common.ArchiveEncoding();
|
||||
opts.ArchiveEncoding.CustomDecoder = (data, x, y) =>
|
||||
{
|
||||
return encoding.GetString(data);
|
||||
};
|
||||
var tr = SharpCompress.Archives.Zip.ZipArchive.OpenArchive("test.zip", opts);
|
||||
foreach(var entry in tr.Entries)
|
||||
var opts = new ReaderOptions()
|
||||
.WithArchiveEncoding(new ArchiveEncoding
|
||||
{
|
||||
CustomDecoder = (data, x, y) => encoding.GetString(data)
|
||||
});
|
||||
|
||||
using var archive = ZipArchive.OpenArchive("test.zip", opts);
|
||||
foreach(var entry in archive.Entries)
|
||||
{
|
||||
Console.WriteLine($"{entry.Key}");
|
||||
}
|
||||
|
|
@ -238,11 +238,6 @@ using (var reader = ReaderFactory.OpenReader(stream))
|
|||
{
|
||||
await reader.WriteAllToDirectoryAsync(
|
||||
@"D:\temp",
|
||||
new ExtractionOptions()
|
||||
{
|
||||
ExtractFullPath = true,
|
||||
Overwrite = true
|
||||
},
|
||||
cancellationToken
|
||||
);
|
||||
}
|
||||
|
|
@ -321,7 +316,6 @@ using (var archive = ZipArchive.OpenArchive("archive.zip"))
|
|||
// Simple async extraction - works for all archive types
|
||||
await archive.WriteToDirectoryAsync(
|
||||
@"C:\output",
|
||||
new ExtractionOptions() { ExtractFullPath = true, Overwrite = true },
|
||||
cancellationToken
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ public abstract partial class AbstractArchive<TEntry, TVolume>
|
|||
IAsyncEnumerable<TVolume> volumes
|
||||
)
|
||||
{
|
||||
foreach (var item in LoadEntries(await volumes.ToListAsync()))
|
||||
foreach (var item in LoadEntries(await volumes.ToListAsync().ConfigureAwait(false)))
|
||||
{
|
||||
yield return item;
|
||||
}
|
||||
|
|
@ -47,8 +47,8 @@ public abstract partial class AbstractArchive<TEntry, TVolume>
|
|||
|
||||
private async ValueTask EnsureEntriesLoadedAsync()
|
||||
{
|
||||
await _lazyEntriesAsync.EnsureFullyLoaded();
|
||||
await _lazyVolumesAsync.EnsureFullyLoaded();
|
||||
await _lazyEntriesAsync.EnsureFullyLoaded().ConfigureAwait(false);
|
||||
await _lazyVolumesAsync.EnsureFullyLoaded().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async IAsyncEnumerable<IArchiveEntry> EntriesAsyncCast()
|
||||
|
|
@ -73,29 +73,31 @@ public abstract partial class AbstractArchive<TEntry, TVolume>
|
|||
|
||||
public async ValueTask<IAsyncReader> ExtractAllEntriesAsync()
|
||||
{
|
||||
if (!await IsSolidAsync() && Type != ArchiveType.SevenZip)
|
||||
if (!await IsSolidAsync().ConfigureAwait(false) && Type != ArchiveType.SevenZip)
|
||||
{
|
||||
throw new SharpCompressException(
|
||||
"ExtractAllEntries can only be used on solid archives or 7Zip archives (which require random access)."
|
||||
);
|
||||
}
|
||||
await EnsureEntriesLoadedAsync();
|
||||
return await CreateReaderForSolidExtractionAsync();
|
||||
await EnsureEntriesLoadedAsync().ConfigureAwait(false);
|
||||
return await CreateReaderForSolidExtractionAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public virtual ValueTask<bool> IsSolidAsync() => new(false);
|
||||
|
||||
public async ValueTask<bool> IsCompleteAsync()
|
||||
{
|
||||
await EnsureEntriesLoadedAsync();
|
||||
return await EntriesAsync.AllAsync(x => x.IsComplete);
|
||||
await EnsureEntriesLoadedAsync().ConfigureAwait(false);
|
||||
return await EntriesAsync.AllAsync(x => x.IsComplete).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async ValueTask<long> TotalSizeAsync() =>
|
||||
await EntriesAsync.AggregateAsync(0L, (total, cf) => total + cf.CompressedSize);
|
||||
await EntriesAsync
|
||||
.AggregateAsync(0L, (total, cf) => total + cf.CompressedSize)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
public async ValueTask<long> TotalUncompressedSizeAsync() =>
|
||||
await EntriesAsync.AggregateAsync(0L, (total, cf) => total + cf.Size);
|
||||
await EntriesAsync.AggregateAsync(0L, (total, cf) => total + cf.Size).ConfigureAwait(false);
|
||||
|
||||
public ValueTask<bool> IsEncryptedAsync() => new(IsEncrypted);
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ public abstract partial class AbstractArchive<TEntry, TVolume> : IArchive, IAsyn
|
|||
private readonly LazyAsyncReadOnlyCollection<TVolume> _lazyVolumesAsync;
|
||||
private readonly LazyAsyncReadOnlyCollection<TEntry> _lazyEntriesAsync;
|
||||
|
||||
protected ReaderOptions ReaderOptions { get; }
|
||||
public ReaderOptions ReaderOptions { get; protected set; }
|
||||
|
||||
internal AbstractArchive(ArchiveType type, SourceStream sourceStream)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ using System.Threading;
|
|||
using System.Threading.Tasks;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Common.Options;
|
||||
using SharpCompress.Writers;
|
||||
|
||||
namespace SharpCompress.Archives;
|
||||
|
||||
public abstract partial class AbstractWritableArchive<TEntry, TVolume>
|
||||
public abstract partial class AbstractWritableArchive<TEntry, TVolume, TOptions>
|
||||
where TEntry : IArchiveEntry
|
||||
where TVolume : IVolume
|
||||
where TOptions : IWriterOptions
|
||||
{
|
||||
// Async property moved from main file
|
||||
private IAsyncEnumerable<TEntry> OldEntriesAsync =>
|
||||
|
|
@ -39,7 +39,7 @@ public abstract partial class AbstractWritableArchive<TEntry, TVolume>
|
|||
if (!removedEntries.Contains(entry))
|
||||
{
|
||||
removedEntries.Add(entry);
|
||||
await RebuildModifiedCollectionAsync();
|
||||
await RebuildModifiedCollectionAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -86,7 +86,7 @@ public abstract partial class AbstractWritableArchive<TEntry, TVolume>
|
|||
}
|
||||
var entry = CreateEntry(key, source, size, modified, closeStream);
|
||||
newEntries.Add(entry);
|
||||
await RebuildModifiedCollectionAsync();
|
||||
await RebuildModifiedCollectionAsync().ConfigureAwait(false);
|
||||
return entry;
|
||||
}
|
||||
|
||||
|
|
@ -106,13 +106,13 @@ public abstract partial class AbstractWritableArchive<TEntry, TVolume>
|
|||
}
|
||||
var entry = CreateDirectoryEntry(key, modified);
|
||||
newEntries.Add(entry);
|
||||
await RebuildModifiedCollectionAsync();
|
||||
await RebuildModifiedCollectionAsync().ConfigureAwait(false);
|
||||
return entry;
|
||||
}
|
||||
|
||||
public async ValueTask SaveToAsync(
|
||||
Stream stream,
|
||||
IWriterOptions options,
|
||||
TOptions options,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
|
|
@ -121,4 +121,12 @@ public abstract partial class AbstractWritableArchive<TEntry, TVolume>
|
|||
await SaveToAsync(stream, options, OldEntriesAsync, newEntries, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
protected abstract ValueTask SaveToAsync(
|
||||
Stream stream,
|
||||
TOptions options,
|
||||
IAsyncEnumerable<TEntry> oldEntries,
|
||||
IEnumerable<TEntry> newEntries,
|
||||
CancellationToken cancellationToken = default
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,18 +11,19 @@ using SharpCompress.Writers;
|
|||
|
||||
namespace SharpCompress.Archives;
|
||||
|
||||
public abstract partial class AbstractWritableArchive<TEntry, TVolume>
|
||||
public abstract partial class AbstractWritableArchive<TEntry, TVolume, TOptions>
|
||||
: AbstractArchive<TEntry, TVolume>,
|
||||
IWritableArchive,
|
||||
IWritableAsyncArchive
|
||||
IWritableArchive<TOptions>,
|
||||
IWritableAsyncArchive<TOptions>
|
||||
where TEntry : IArchiveEntry
|
||||
where TVolume : IVolume
|
||||
where TOptions : IWriterOptions
|
||||
{
|
||||
private class RebuildPauseDisposable : IDisposable
|
||||
{
|
||||
private readonly AbstractWritableArchive<TEntry, TVolume> archive;
|
||||
private readonly AbstractWritableArchive<TEntry, TVolume, TOptions> archive;
|
||||
|
||||
public RebuildPauseDisposable(AbstractWritableArchive<TEntry, TVolume> archive)
|
||||
public RebuildPauseDisposable(AbstractWritableArchive<TEntry, TVolume, TOptions> archive)
|
||||
{
|
||||
this.archive = archive;
|
||||
archive.pauseRebuilding = true;
|
||||
|
|
@ -151,13 +152,15 @@ public abstract partial class AbstractWritableArchive<TEntry, TVolume>
|
|||
long size,
|
||||
DateTime? modified,
|
||||
CancellationToken cancellationToken
|
||||
) => await AddEntryAsync(key, source, closeStream, size, modified, cancellationToken);
|
||||
) =>
|
||||
await AddEntryAsync(key, source, closeStream, size, modified, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
async ValueTask<IArchiveEntry> IWritableAsyncArchive.AddDirectoryEntryAsync(
|
||||
string key,
|
||||
DateTime? modified,
|
||||
CancellationToken cancellationToken
|
||||
) => await AddDirectoryEntryAsync(key, modified, cancellationToken);
|
||||
) => await AddDirectoryEntryAsync(key, modified, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
public TEntry AddDirectoryEntry(string key, DateTime? modified = null)
|
||||
{
|
||||
|
|
@ -175,7 +178,7 @@ public abstract partial class AbstractWritableArchive<TEntry, TVolume>
|
|||
return entry;
|
||||
}
|
||||
|
||||
public void SaveTo(Stream stream, IWriterOptions options)
|
||||
public void SaveTo(Stream stream, TOptions options)
|
||||
{
|
||||
//reset streams of new entries
|
||||
newEntries.Cast<IWritableArchiveEntry>().ForEach(x => x.Stream.Seek(0, SeekOrigin.Begin));
|
||||
|
|
@ -211,19 +214,11 @@ public abstract partial class AbstractWritableArchive<TEntry, TVolume>
|
|||
|
||||
protected abstract void SaveTo(
|
||||
Stream stream,
|
||||
IWriterOptions options,
|
||||
TOptions options,
|
||||
IEnumerable<TEntry> oldEntries,
|
||||
IEnumerable<TEntry> newEntries
|
||||
);
|
||||
|
||||
protected abstract ValueTask SaveToAsync(
|
||||
Stream stream,
|
||||
IWriterOptions options,
|
||||
IAsyncEnumerable<TEntry> oldEntries,
|
||||
IEnumerable<TEntry> newEntries,
|
||||
CancellationToken cancellationToken = default
|
||||
);
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
base.Dispose();
|
||||
|
|
|
|||
|
|
@ -19,8 +19,9 @@ public static partial class ArchiveFactory
|
|||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
readerOptions ??= new ReaderOptions();
|
||||
var factory = await FindFactoryAsync<IArchiveFactory>(stream, cancellationToken);
|
||||
readerOptions ??= ReaderOptions.ForExternalStream;
|
||||
var factory = await FindFactoryAsync<IArchiveFactory>(stream, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
return factory.OpenAsyncArchive(stream, readerOptions);
|
||||
}
|
||||
|
||||
|
|
@ -40,9 +41,10 @@ public static partial class ArchiveFactory
|
|||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
options ??= new ReaderOptions { LeaveStreamOpen = false };
|
||||
options ??= ReaderOptions.ForOwnedFile;
|
||||
|
||||
var factory = await FindFactoryAsync<IArchiveFactory>(fileInfo, cancellationToken);
|
||||
var factory = await FindFactoryAsync<IArchiveFactory>(fileInfo, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
return factory.OpenAsyncArchive(fileInfo, options);
|
||||
}
|
||||
|
||||
|
|
@ -62,14 +64,16 @@ public static partial class ArchiveFactory
|
|||
var fileInfo = filesArray[0];
|
||||
if (filesArray.Length == 1)
|
||||
{
|
||||
return await OpenAsyncArchive(fileInfo, options, cancellationToken);
|
||||
return await OpenAsyncArchive(fileInfo, options, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
fileInfo.NotNull(nameof(fileInfo));
|
||||
options ??= new ReaderOptions { LeaveStreamOpen = false };
|
||||
options ??= ReaderOptions.ForOwnedFile;
|
||||
|
||||
var factory = await FindFactoryAsync<IMultiArchiveFactory>(fileInfo, cancellationToken);
|
||||
return factory.OpenAsyncArchive(filesArray, options, cancellationToken);
|
||||
var factory = await FindFactoryAsync<IMultiArchiveFactory>(fileInfo, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
return factory.OpenAsyncArchive(filesArray, options);
|
||||
}
|
||||
|
||||
public static async ValueTask<IAsyncArchive> OpenAsyncArchive(
|
||||
|
|
@ -89,13 +93,15 @@ public static partial class ArchiveFactory
|
|||
var firstStream = streamsArray[0];
|
||||
if (streamsArray.Length == 1)
|
||||
{
|
||||
return await OpenAsyncArchive(firstStream, options, cancellationToken);
|
||||
return await OpenAsyncArchive(firstStream, options, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
firstStream.NotNull(nameof(firstStream));
|
||||
options ??= new ReaderOptions();
|
||||
options ??= ReaderOptions.ForExternalStream;
|
||||
|
||||
var factory = await FindFactoryAsync<IMultiArchiveFactory>(firstStream, cancellationToken);
|
||||
var factory = await FindFactoryAsync<IMultiArchiveFactory>(firstStream, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
return factory.OpenAsyncArchive(streamsArray, options);
|
||||
}
|
||||
|
||||
|
|
@ -117,7 +123,7 @@ public static partial class ArchiveFactory
|
|||
{
|
||||
finfo.NotNull(nameof(finfo));
|
||||
using Stream stream = finfo.OpenRead();
|
||||
return await FindFactoryAsync<T>(stream, cancellationToken);
|
||||
return await FindFactoryAsync<T>(stream, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static async ValueTask<T> FindFactoryAsync<T>(
|
||||
|
|
@ -140,7 +146,11 @@ public static partial class ArchiveFactory
|
|||
{
|
||||
stream.Seek(startPosition, SeekOrigin.Begin);
|
||||
|
||||
if (await factory.IsArchiveAsync(stream, cancellationToken: cancellationToken))
|
||||
if (
|
||||
await factory
|
||||
.IsArchiveAsync(stream, cancellationToken: cancellationToken)
|
||||
.ConfigureAwait(false)
|
||||
)
|
||||
{
|
||||
stream.Seek(startPosition, SeekOrigin.Begin);
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using System.Linq;
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Common.Options;
|
||||
using SharpCompress.Factories;
|
||||
using SharpCompress.IO;
|
||||
using SharpCompress.Readers;
|
||||
|
|
@ -15,22 +16,23 @@ public static partial class ArchiveFactory
|
|||
{
|
||||
public static IArchive OpenArchive(Stream stream, ReaderOptions? readerOptions = null)
|
||||
{
|
||||
readerOptions ??= new ReaderOptions();
|
||||
readerOptions ??= ReaderOptions.ForExternalStream;
|
||||
return FindFactory<IArchiveFactory>(stream).OpenArchive(stream, readerOptions);
|
||||
}
|
||||
|
||||
public static IWritableArchive CreateArchive(ArchiveType type)
|
||||
public static IWritableArchive<TOptions> CreateArchive<TOptions>()
|
||||
where TOptions : IWriterOptions
|
||||
{
|
||||
var factory = Factory
|
||||
.Factories.OfType<IWriteableArchiveFactory>()
|
||||
.FirstOrDefault(item => item.KnownArchiveType == type);
|
||||
.Factories.OfType<IWriteableArchiveFactory<TOptions>>()
|
||||
.FirstOrDefault();
|
||||
|
||||
if (factory != null)
|
||||
{
|
||||
return factory.CreateArchive();
|
||||
}
|
||||
|
||||
throw new NotSupportedException("Cannot create Archives of type: " + type);
|
||||
throw new NotSupportedException("Cannot create Archives of type: " + typeof(TOptions));
|
||||
}
|
||||
|
||||
public static IArchive OpenArchive(string filePath, ReaderOptions? options = null)
|
||||
|
|
@ -41,7 +43,7 @@ public static partial class ArchiveFactory
|
|||
|
||||
public static IArchive OpenArchive(FileInfo fileInfo, ReaderOptions? options = null)
|
||||
{
|
||||
options ??= new ReaderOptions { LeaveStreamOpen = false };
|
||||
options ??= ReaderOptions.ForOwnedFile;
|
||||
|
||||
return FindFactory<IArchiveFactory>(fileInfo).OpenArchive(fileInfo, options);
|
||||
}
|
||||
|
|
@ -65,7 +67,7 @@ public static partial class ArchiveFactory
|
|||
}
|
||||
|
||||
fileInfo.NotNull(nameof(fileInfo));
|
||||
options ??= new ReaderOptions { LeaveStreamOpen = false };
|
||||
options ??= ReaderOptions.ForOwnedFile;
|
||||
|
||||
return FindFactory<IMultiArchiveFactory>(fileInfo).OpenArchive(filesArray, options);
|
||||
}
|
||||
|
|
@ -86,7 +88,7 @@ public static partial class ArchiveFactory
|
|||
}
|
||||
|
||||
firstStream.NotNull(nameof(firstStream));
|
||||
options ??= new ReaderOptions();
|
||||
options ??= ReaderOptions.ForExternalStream;
|
||||
|
||||
return FindFactory<IMultiArchiveFactory>(firstStream).OpenArchive(streamsArray, options);
|
||||
}
|
||||
|
|
@ -94,11 +96,11 @@ public static partial class ArchiveFactory
|
|||
public static void WriteToDirectory(
|
||||
string sourceArchive,
|
||||
string destinationDirectory,
|
||||
ExtractionOptions? options = null
|
||||
ReaderOptions? options = null
|
||||
)
|
||||
{
|
||||
using var archive = OpenArchive(sourceArchive);
|
||||
archive.WriteToDirectory(destinationDirectory, options);
|
||||
using var archive = OpenArchive(sourceArchive, options);
|
||||
archive.WriteToDirectory(destinationDirectory);
|
||||
}
|
||||
|
||||
public static T FindFactory<T>(string path)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ using System.Threading.Tasks;
|
|||
using SharpCompress.Common;
|
||||
using SharpCompress.Common.GZip;
|
||||
using SharpCompress.Common.Options;
|
||||
using SharpCompress.IO;
|
||||
using SharpCompress.Readers;
|
||||
using SharpCompress.Readers.GZip;
|
||||
using SharpCompress.Writers;
|
||||
|
|
@ -25,13 +24,13 @@ public partial class GZipArchive
|
|||
)
|
||||
{
|
||||
using var stream = fileInfo.Open(FileMode.Create, FileAccess.Write);
|
||||
await SaveToAsync(stream, new WriterOptions(CompressionType.GZip), cancellationToken)
|
||||
await SaveToAsync(stream, new GZipWriterOptions(CompressionType.GZip), cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
protected override async ValueTask SaveToAsync(
|
||||
Stream stream,
|
||||
IWriterOptions options,
|
||||
GZipWriterOptions options,
|
||||
IAsyncEnumerable<GZipArchiveEntry> oldEntries,
|
||||
IEnumerable<GZipArchiveEntry> newEntries,
|
||||
CancellationToken cancellationToken = default
|
||||
|
|
@ -41,7 +40,7 @@ public partial class GZipArchive
|
|||
{
|
||||
throw new InvalidFormatException("Only one entry is allowed in a GZip Archive");
|
||||
}
|
||||
using var writer = new GZipWriter(
|
||||
await using var writer = new GZipWriter(
|
||||
stream,
|
||||
options as GZipWriterOptions ?? new GZipWriterOptions(options)
|
||||
);
|
||||
|
|
@ -51,7 +50,9 @@ public partial class GZipArchive
|
|||
{
|
||||
if (!entry.IsDirectory)
|
||||
{
|
||||
using var entryStream = entry.OpenEntryStream();
|
||||
using var entryStream = await entry
|
||||
.OpenEntryStreamAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
await writer
|
||||
.WriteAsync(
|
||||
entry.Key.NotNull("Entry Key is null"),
|
||||
|
|
@ -63,7 +64,9 @@ public partial class GZipArchive
|
|||
}
|
||||
foreach (var entry in newEntries.Where(x => !x.IsDirectory))
|
||||
{
|
||||
using var entryStream = entry.OpenEntryStream();
|
||||
using var entryStream = await entry
|
||||
.OpenEntryStreamAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
await writer
|
||||
.WriteAsync(entry.Key.NotNull("Entry Key is null"), entryStream, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
|
@ -81,14 +84,13 @@ public partial class GZipArchive
|
|||
IAsyncEnumerable<GZipVolume> volumes
|
||||
)
|
||||
{
|
||||
var stream = (await volumes.SingleAsync()).Stream;
|
||||
var stream = (await volumes.SingleAsync().ConfigureAwait(false)).Stream;
|
||||
yield return new GZipArchiveEntry(
|
||||
this,
|
||||
await GZipFilePart.CreateAsync(
|
||||
stream,
|
||||
ReaderOptions.ArchiveEncoding,
|
||||
ReaderOptions.CompressionProviders
|
||||
)
|
||||
await GZipFilePart
|
||||
.CreateAsync(stream, ReaderOptions.ArchiveEncoding)
|
||||
.ConfigureAwait(false),
|
||||
ReaderOptions
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,43 +5,41 @@ using System.IO;
|
|||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Common.GZip;
|
||||
using SharpCompress.IO;
|
||||
using SharpCompress.Readers;
|
||||
using SharpCompress.Readers.GZip;
|
||||
using SharpCompress.Writers;
|
||||
using SharpCompress.Writers.GZip;
|
||||
|
||||
namespace SharpCompress.Archives.GZip;
|
||||
|
||||
public partial class GZipArchive
|
||||
#if NET8_0_OR_GREATER
|
||||
: IWritableArchiveOpenable,
|
||||
IMultiArchiveOpenable<IWritableArchive, IWritableAsyncArchive>
|
||||
: IWritableArchiveOpenable<GZipWriterOptions>,
|
||||
IMultiArchiveOpenable<
|
||||
IWritableArchive<GZipWriterOptions>,
|
||||
IWritableAsyncArchive<GZipWriterOptions>
|
||||
>
|
||||
#endif
|
||||
{
|
||||
public static IWritableAsyncArchive OpenAsyncArchive(
|
||||
public static IWritableAsyncArchive<GZipWriterOptions> OpenAsyncArchive(
|
||||
string path,
|
||||
ReaderOptions? readerOptions = null,
|
||||
CancellationToken cancellationToken = default
|
||||
ReaderOptions? readerOptions = null
|
||||
)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
path.NotNullOrEmpty(nameof(path));
|
||||
return (IWritableAsyncArchive)OpenArchive(
|
||||
new FileInfo(path),
|
||||
readerOptions ?? new ReaderOptions()
|
||||
);
|
||||
return (IWritableAsyncArchive<GZipWriterOptions>)
|
||||
OpenArchive(new FileInfo(path), readerOptions ?? new ReaderOptions());
|
||||
}
|
||||
|
||||
public static IWritableArchive OpenArchive(string filePath, ReaderOptions? readerOptions = null)
|
||||
public static IWritableArchive<GZipWriterOptions> OpenArchive(
|
||||
string filePath,
|
||||
ReaderOptions? readerOptions = null
|
||||
)
|
||||
{
|
||||
filePath.NotNullOrEmpty(nameof(filePath));
|
||||
return OpenArchive(new FileInfo(filePath), readerOptions ?? new ReaderOptions());
|
||||
}
|
||||
|
||||
public static IWritableArchive OpenArchive(
|
||||
public static IWritableArchive<GZipWriterOptions> OpenArchive(
|
||||
FileInfo fileInfo,
|
||||
ReaderOptions? readerOptions = null
|
||||
)
|
||||
|
|
@ -56,7 +54,7 @@ public partial class GZipArchive
|
|||
);
|
||||
}
|
||||
|
||||
public static IWritableArchive OpenArchive(
|
||||
public static IWritableArchive<GZipWriterOptions> OpenArchive(
|
||||
IEnumerable<FileInfo> fileInfos,
|
||||
ReaderOptions? readerOptions = null
|
||||
)
|
||||
|
|
@ -72,7 +70,7 @@ public partial class GZipArchive
|
|||
);
|
||||
}
|
||||
|
||||
public static IWritableArchive OpenArchive(
|
||||
public static IWritableArchive<GZipWriterOptions> OpenArchive(
|
||||
IEnumerable<Stream> streams,
|
||||
ReaderOptions? readerOptions = null
|
||||
)
|
||||
|
|
@ -88,7 +86,10 @@ public partial class GZipArchive
|
|||
);
|
||||
}
|
||||
|
||||
public static IWritableArchive OpenArchive(Stream stream, ReaderOptions? readerOptions = null)
|
||||
public static IWritableArchive<GZipWriterOptions> OpenArchive(
|
||||
Stream stream,
|
||||
ReaderOptions? readerOptions = null
|
||||
)
|
||||
{
|
||||
stream.NotNull(nameof(stream));
|
||||
|
||||
|
|
@ -102,49 +103,30 @@ public partial class GZipArchive
|
|||
);
|
||||
}
|
||||
|
||||
public static IWritableAsyncArchive OpenAsyncArchive(
|
||||
public static IWritableAsyncArchive<GZipWriterOptions> OpenAsyncArchive(
|
||||
Stream stream,
|
||||
ReaderOptions? readerOptions = null,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
return (IWritableAsyncArchive)OpenArchive(stream, readerOptions);
|
||||
}
|
||||
ReaderOptions? readerOptions = null
|
||||
) => (IWritableAsyncArchive<GZipWriterOptions>)OpenArchive(stream, readerOptions);
|
||||
|
||||
public static IWritableAsyncArchive OpenAsyncArchive(
|
||||
public static IWritableAsyncArchive<GZipWriterOptions> OpenAsyncArchive(
|
||||
FileInfo fileInfo,
|
||||
ReaderOptions? readerOptions = null,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
return (IWritableAsyncArchive)OpenArchive(fileInfo, readerOptions);
|
||||
}
|
||||
ReaderOptions? readerOptions = null
|
||||
) => (IWritableAsyncArchive<GZipWriterOptions>)OpenArchive(fileInfo, readerOptions);
|
||||
|
||||
public static IWritableAsyncArchive OpenAsyncArchive(
|
||||
public static IWritableAsyncArchive<GZipWriterOptions> OpenAsyncArchive(
|
||||
IReadOnlyList<Stream> streams,
|
||||
ReaderOptions? readerOptions = null,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
return (IWritableAsyncArchive)OpenArchive(streams, readerOptions);
|
||||
}
|
||||
ReaderOptions? readerOptions = null
|
||||
) => (IWritableAsyncArchive<GZipWriterOptions>)OpenArchive(streams, readerOptions);
|
||||
|
||||
public static IWritableAsyncArchive OpenAsyncArchive(
|
||||
public static IWritableAsyncArchive<GZipWriterOptions> OpenAsyncArchive(
|
||||
IReadOnlyList<FileInfo> fileInfos,
|
||||
ReaderOptions? readerOptions = null,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
return (IWritableAsyncArchive)OpenArchive(fileInfos, readerOptions);
|
||||
}
|
||||
ReaderOptions? readerOptions = null
|
||||
) => (IWritableAsyncArchive<GZipWriterOptions>)OpenArchive(fileInfos, readerOptions);
|
||||
|
||||
public static IWritableArchive CreateArchive() => new GZipArchive();
|
||||
public static IWritableArchive<GZipWriterOptions> CreateArchive() => new GZipArchive();
|
||||
|
||||
public static IWritableAsyncArchive CreateAsyncArchive() => new GZipArchive();
|
||||
public static IWritableAsyncArchive<GZipWriterOptions> CreateAsyncArchive() =>
|
||||
new GZipArchive();
|
||||
|
||||
public static bool IsGZipFile(string filePath) => IsGZipFile(new FileInfo(filePath));
|
||||
|
||||
|
|
|
|||
|
|
@ -2,8 +2,6 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Common.GZip;
|
||||
using SharpCompress.Common.Options;
|
||||
|
|
@ -15,7 +13,8 @@ using SharpCompress.Writers.GZip;
|
|||
|
||||
namespace SharpCompress.Archives.GZip;
|
||||
|
||||
public partial class GZipArchive : AbstractWritableArchive<GZipArchiveEntry, GZipVolume>
|
||||
public partial class GZipArchive
|
||||
: AbstractWritableArchive<GZipArchiveEntry, GZipVolume, GZipWriterOptions>
|
||||
{
|
||||
private GZipArchive(SourceStream sourceStream)
|
||||
: base(ArchiveType.GZip, sourceStream) { }
|
||||
|
|
@ -34,7 +33,7 @@ public partial class GZipArchive : AbstractWritableArchive<GZipArchiveEntry, GZi
|
|||
public void SaveTo(FileInfo fileInfo)
|
||||
{
|
||||
using var stream = fileInfo.Open(FileMode.Create, FileAccess.Write);
|
||||
SaveTo(stream, new WriterOptions(CompressionType.GZip));
|
||||
SaveTo(stream, new GZipWriterOptions(CompressionType.GZip));
|
||||
}
|
||||
|
||||
protected override GZipArchiveEntry CreateEntryInternal(
|
||||
|
|
@ -59,7 +58,7 @@ public partial class GZipArchive : AbstractWritableArchive<GZipArchiveEntry, GZi
|
|||
|
||||
protected override void SaveTo(
|
||||
Stream stream,
|
||||
IWriterOptions options,
|
||||
GZipWriterOptions options,
|
||||
IEnumerable<GZipArchiveEntry> oldEntries,
|
||||
IEnumerable<GZipArchiveEntry> newEntries
|
||||
)
|
||||
|
|
@ -88,11 +87,8 @@ public partial class GZipArchive : AbstractWritableArchive<GZipArchiveEntry, GZi
|
|||
var stream = volumes.Single().Stream;
|
||||
yield return new GZipArchiveEntry(
|
||||
this,
|
||||
GZipFilePart.Create(
|
||||
stream,
|
||||
ReaderOptions.ArchiveEncoding,
|
||||
ReaderOptions.CompressionProviders
|
||||
)
|
||||
GZipFilePart.Create(stream, ReaderOptions.ArchiveEncoding),
|
||||
ReaderOptions
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,15 +1,16 @@
|
|||
using System.IO;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Common.GZip;
|
||||
using SharpCompress.Common.Options;
|
||||
|
||||
namespace SharpCompress.Archives.GZip;
|
||||
|
||||
public class GZipArchiveEntry : GZipEntry, IArchiveEntry
|
||||
{
|
||||
internal GZipArchiveEntry(GZipArchive archive, GZipFilePart? part)
|
||||
: base(part) => Archive = archive;
|
||||
internal GZipArchiveEntry(GZipArchive archive, GZipFilePart? part, IReaderOptions readerOptions)
|
||||
: base(part, readerOptions) => Archive = archive;
|
||||
|
||||
public virtual Stream OpenEntryStream()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ internal sealed class GZipWritableArchiveEntry : GZipArchiveEntry, IWritableArch
|
|||
DateTime? lastModified,
|
||||
bool closeStream
|
||||
)
|
||||
: base(archive, null)
|
||||
: base(archive, null, archive.ReaderOptions)
|
||||
{
|
||||
this.stream = stream;
|
||||
Key = path;
|
||||
|
|
|
|||
|
|
@ -12,6 +12,11 @@ public interface IArchive : IDisposable
|
|||
|
||||
ArchiveType Type { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The options used when opening this archive, including extraction behavior settings.
|
||||
/// </summary>
|
||||
ReaderOptions ReaderOptions { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Use this method to extract all entries in an archive in order.
|
||||
/// This is primarily for SOLID Rar Archives or 7Zip Archives as they need to be
|
||||
|
|
|
|||
|
|
@ -47,11 +47,13 @@ public static class IArchiveEntryExtensions
|
|||
}
|
||||
|
||||
#if LEGACY_DOTNET
|
||||
using var entryStream = await archiveEntry.OpenEntryStreamAsync(cancellationToken);
|
||||
using var entryStream = await archiveEntry
|
||||
.OpenEntryStreamAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
#else
|
||||
await using var entryStream = await archiveEntry.OpenEntryStreamAsync(
|
||||
cancellationToken
|
||||
);
|
||||
await using var entryStream = await archiveEntry
|
||||
.OpenEntryStreamAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
#endif
|
||||
var sourceStream = WrapWithProgress(entryStream, archiveEntry, progress);
|
||||
await sourceStream
|
||||
|
|
@ -100,15 +102,11 @@ public static class IArchiveEntryExtensions
|
|||
/// <summary>
|
||||
/// Extract to specific directory, retaining filename
|
||||
/// </summary>
|
||||
public void WriteToDirectory(
|
||||
string destinationDirectory,
|
||||
ExtractionOptions? options = null
|
||||
) =>
|
||||
public void WriteToDirectory(string destinationDirectory) =>
|
||||
ExtractionMethods.WriteEntryToDirectory(
|
||||
entry,
|
||||
destinationDirectory,
|
||||
options,
|
||||
entry.WriteToFile
|
||||
(path) => entry.WriteToFile(path)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -116,15 +114,14 @@ public static class IArchiveEntryExtensions
|
|||
/// </summary>
|
||||
public async ValueTask WriteToDirectoryAsync(
|
||||
string destinationDirectory,
|
||||
ExtractionOptions? options = null,
|
||||
CancellationToken cancellationToken = default
|
||||
) =>
|
||||
await ExtractionMethods
|
||||
.WriteEntryToDirectoryAsync(
|
||||
entry,
|
||||
destinationDirectory,
|
||||
options,
|
||||
entry.WriteToFileAsync,
|
||||
async (path, ct) =>
|
||||
await entry.WriteToFileAsync(path, ct).ConfigureAwait(false),
|
||||
cancellationToken
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
|
|
@ -132,11 +129,10 @@ public static class IArchiveEntryExtensions
|
|||
/// <summary>
|
||||
/// Extract to specific file
|
||||
/// </summary>
|
||||
public void WriteToFile(string destinationFileName, ExtractionOptions? options = null) =>
|
||||
public void WriteToFile(string destinationFileName) =>
|
||||
ExtractionMethods.WriteEntryToFile(
|
||||
entry,
|
||||
destinationFileName,
|
||||
options,
|
||||
(x, fm) =>
|
||||
{
|
||||
using var fs = File.Open(destinationFileName, fm);
|
||||
|
|
@ -149,14 +145,12 @@ public static class IArchiveEntryExtensions
|
|||
/// </summary>
|
||||
public async ValueTask WriteToFileAsync(
|
||||
string destinationFileName,
|
||||
ExtractionOptions? options = null,
|
||||
CancellationToken cancellationToken = default
|
||||
) =>
|
||||
await ExtractionMethods
|
||||
.WriteEntryToFileAsync(
|
||||
entry,
|
||||
destinationFileName,
|
||||
options,
|
||||
async (x, fm, ct) =>
|
||||
{
|
||||
using var fs = File.Open(destinationFileName, fm);
|
||||
|
|
|
|||
|
|
@ -14,28 +14,25 @@ public static class IArchiveExtensions
|
|||
/// Extract to specific directory with progress reporting
|
||||
/// </summary>
|
||||
/// <param name="destinationDirectory">The folder to extract into.</param>
|
||||
/// <param name="options">Extraction options.</param>
|
||||
/// <param name="progress">Optional progress reporter for tracking extraction progress.</param>
|
||||
public void WriteToDirectory(
|
||||
string destinationDirectory,
|
||||
ExtractionOptions? options = null,
|
||||
IProgress<ProgressReport>? progress = null
|
||||
)
|
||||
{
|
||||
if (archive.IsSolid || archive.Type == ArchiveType.SevenZip)
|
||||
{
|
||||
using var reader = archive.ExtractAllEntries();
|
||||
reader.WriteAllToDirectory(destinationDirectory, options);
|
||||
reader.WriteAllToDirectory(destinationDirectory);
|
||||
}
|
||||
else
|
||||
{
|
||||
archive.WriteToDirectoryInternal(destinationDirectory, options, progress);
|
||||
archive.WriteToDirectoryInternal(destinationDirectory, progress);
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteToDirectoryInternal(
|
||||
string destinationDirectory,
|
||||
ExtractionOptions? options,
|
||||
IProgress<ProgressReport>? progress
|
||||
)
|
||||
{
|
||||
|
|
@ -61,7 +58,7 @@ public static class IArchiveExtensions
|
|||
continue;
|
||||
}
|
||||
|
||||
entry.WriteToDirectory(destinationDirectory, options);
|
||||
entry.WriteToDirectory(destinationDirectory);
|
||||
|
||||
bytesRead += entry.Size;
|
||||
progress?.Report(
|
||||
|
|
|
|||
|
|
@ -20,20 +20,17 @@ public interface IArchiveOpenable<TSync, TASync>
|
|||
|
||||
public static abstract TASync OpenAsyncArchive(
|
||||
string path,
|
||||
ReaderOptions? readerOptions = null,
|
||||
CancellationToken cancellationToken = default
|
||||
ReaderOptions? readerOptions = null
|
||||
);
|
||||
|
||||
public static abstract TASync OpenAsyncArchive(
|
||||
Stream stream,
|
||||
ReaderOptions? readerOptions = null,
|
||||
CancellationToken cancellationToken = default
|
||||
ReaderOptions? readerOptions = null
|
||||
);
|
||||
|
||||
public static abstract TASync OpenAsyncArchive(
|
||||
FileInfo fileInfo,
|
||||
ReaderOptions? readerOptions = null,
|
||||
CancellationToken cancellationToken = default
|
||||
ReaderOptions? readerOptions = null
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,44 +17,45 @@ public static class IAsyncArchiveExtensions
|
|||
/// </summary>
|
||||
/// <param name="archive">The archive to extract.</param>
|
||||
/// <param name="destinationDirectory">The folder to extract into.</param>
|
||||
/// <param name="options">Extraction options.</param>
|
||||
/// <param name="progress">Optional progress reporter for tracking extraction progress.</param>
|
||||
/// <param name="cancellationToken">Optional cancellation token.</param>
|
||||
public async ValueTask WriteToDirectoryAsync(
|
||||
string destinationDirectory,
|
||||
ExtractionOptions? options = null,
|
||||
IProgress<ProgressReport>? progress = null,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
if (await archive.IsSolidAsync() || archive.Type == ArchiveType.SevenZip)
|
||||
if (
|
||||
await archive.IsSolidAsync().ConfigureAwait(false)
|
||||
|| archive.Type == ArchiveType.SevenZip
|
||||
)
|
||||
{
|
||||
await using var reader = await archive.ExtractAllEntriesAsync();
|
||||
await reader.WriteAllToDirectoryAsync(
|
||||
destinationDirectory,
|
||||
options,
|
||||
cancellationToken
|
||||
);
|
||||
await using var reader = await archive
|
||||
.ExtractAllEntriesAsync()
|
||||
.ConfigureAwait(false);
|
||||
await reader
|
||||
.WriteAllToDirectoryAsync(destinationDirectory, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await archive.WriteToDirectoryAsyncInternal(
|
||||
destinationDirectory,
|
||||
options,
|
||||
progress,
|
||||
cancellationToken
|
||||
);
|
||||
await archive
|
||||
.WriteToDirectoryAsyncInternal(
|
||||
destinationDirectory,
|
||||
progress,
|
||||
cancellationToken
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
private async ValueTask WriteToDirectoryAsyncInternal(
|
||||
string destinationDirectory,
|
||||
ExtractionOptions? options,
|
||||
IProgress<ProgressReport>? progress,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
var totalBytes = await archive.TotalUncompressedSizeAsync();
|
||||
var totalBytes = await archive.TotalUncompressedSizeAsync().ConfigureAwait(false);
|
||||
var bytesRead = 0L;
|
||||
var seenDirectories = new HashSet<string>();
|
||||
|
||||
|
|
@ -79,7 +80,7 @@ public static class IAsyncArchiveExtensions
|
|||
}
|
||||
|
||||
await entry
|
||||
.WriteToDirectoryAsync(destinationDirectory, options, cancellationToken)
|
||||
.WriteToDirectoryAsync(destinationDirectory, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
bytesRead += entry.Size;
|
||||
|
|
|
|||
|
|
@ -50,10 +50,8 @@ public interface IMultiArchiveFactory : IFactory
|
|||
/// </summary>
|
||||
/// <param name="fileInfos"></param>
|
||||
/// <param name="readerOptions">reading options.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
IAsyncArchive OpenAsyncArchive(
|
||||
IReadOnlyList<FileInfo> fileInfos,
|
||||
ReaderOptions? readerOptions = null,
|
||||
CancellationToken cancellationToken = default
|
||||
ReaderOptions? readerOptions = null
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,14 +22,12 @@ public interface IMultiArchiveOpenable<TSync, TASync>
|
|||
|
||||
public static abstract TASync OpenAsyncArchive(
|
||||
IReadOnlyList<Stream> streams,
|
||||
ReaderOptions? readerOptions = null,
|
||||
CancellationToken cancellationToken = default
|
||||
ReaderOptions? readerOptions = null
|
||||
);
|
||||
|
||||
public static abstract TASync OpenAsyncArchive(
|
||||
IReadOnlyList<FileInfo> fileInfos,
|
||||
ReaderOptions? readerOptions = null,
|
||||
CancellationToken cancellationToken = default
|
||||
ReaderOptions? readerOptions = null
|
||||
);
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -28,28 +28,23 @@ public interface IWritableArchive : IArchive, IWritableArchiveCommon
|
|||
|
||||
IArchiveEntry AddDirectoryEntry(string key, DateTime? modified = null);
|
||||
|
||||
/// <summary>
|
||||
/// Saves the archive to the specified stream using the given writer options.
|
||||
/// </summary>
|
||||
void SaveTo(Stream stream, IWriterOptions options);
|
||||
|
||||
/// <summary>
|
||||
/// Removes the specified entry from the archive.
|
||||
/// </summary>
|
||||
void RemoveEntry(IArchiveEntry entry);
|
||||
}
|
||||
|
||||
public interface IWritableAsyncArchive : IAsyncArchive, IWritableArchiveCommon
|
||||
public interface IWritableArchive<TOptions> : IWritableArchive
|
||||
where TOptions : IWriterOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Asynchronously saves the archive to the specified stream using the given writer options.
|
||||
/// Saves the archive to the specified stream using the given writer options.
|
||||
/// </summary>
|
||||
ValueTask SaveToAsync(
|
||||
Stream stream,
|
||||
IWriterOptions options,
|
||||
CancellationToken cancellationToken = default
|
||||
);
|
||||
void SaveTo(Stream stream, TOptions options);
|
||||
}
|
||||
|
||||
public interface IWritableAsyncArchive : IAsyncArchive, IWritableArchiveCommon
|
||||
{
|
||||
/// <summary>
|
||||
/// Asynchronously adds an entry to the archive with the specified key, source stream, and options.
|
||||
/// </summary>
|
||||
|
|
@ -76,3 +71,16 @@ public interface IWritableAsyncArchive : IAsyncArchive, IWritableArchiveCommon
|
|||
/// </summary>
|
||||
ValueTask RemoveEntryAsync(IArchiveEntry entry);
|
||||
}
|
||||
|
||||
public interface IWritableAsyncArchive<TOptions> : IWritableAsyncArchive
|
||||
where TOptions : IWriterOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Asynchronously saves the archive to the specified stream using the given writer options.
|
||||
/// </summary>
|
||||
ValueTask SaveToAsync(
|
||||
Stream stream,
|
||||
TOptions options,
|
||||
CancellationToken cancellationToken = default
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,17 +58,23 @@ public static class IWritableArchiveExtensions
|
|||
fileInfo.LastWriteTime
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveTo(string filePath, IWriterOptions? options = null) =>
|
||||
writableArchive.SaveTo(
|
||||
new FileInfo(filePath),
|
||||
options ?? new WriterOptions(CompressionType.Deflate)
|
||||
);
|
||||
public static void SaveTo<TOptions>(
|
||||
this IWritableArchive<TOptions> writableArchive,
|
||||
string filePath,
|
||||
TOptions options
|
||||
)
|
||||
where TOptions : IWriterOptions => writableArchive.SaveTo(new FileInfo(filePath), options);
|
||||
|
||||
public void SaveTo(FileInfo fileInfo, IWriterOptions? options = null)
|
||||
{
|
||||
using var stream = fileInfo.Open(FileMode.Create, FileAccess.Write);
|
||||
writableArchive.SaveTo(stream, options ?? new WriterOptions(CompressionType.Deflate));
|
||||
}
|
||||
public static void SaveTo<TOptions>(
|
||||
this IWritableArchive<TOptions> writableArchive,
|
||||
FileInfo fileInfo,
|
||||
TOptions options
|
||||
)
|
||||
where TOptions : IWriterOptions
|
||||
{
|
||||
using var stream = fileInfo.Open(FileMode.Create, FileAccess.Write);
|
||||
writableArchive.SaveTo(stream, options);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
using SharpCompress.Common.Options;
|
||||
|
||||
#if NET8_0_OR_GREATER
|
||||
namespace SharpCompress.Archives;
|
||||
|
||||
public interface IWritableArchiveOpenable
|
||||
: IArchiveOpenable<IWritableArchive, IWritableAsyncArchive>
|
||||
public interface IWritableArchiveOpenable<TOptions>
|
||||
: IArchiveOpenable<IWritableArchive<TOptions>, IWritableAsyncArchive<TOptions>>
|
||||
where TOptions : IWriterOptions
|
||||
{
|
||||
public static abstract IWritableArchive CreateArchive();
|
||||
public static abstract IWritableAsyncArchive CreateAsyncArchive();
|
||||
public static abstract IWritableArchive<TOptions> CreateArchive();
|
||||
public static abstract IWritableAsyncArchive<TOptions> CreateAsyncArchive();
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -25,13 +25,15 @@ public static class IWritableAsyncArchiveExtensions
|
|||
)
|
||||
{
|
||||
var fileInfo = new FileInfo(path);
|
||||
await writableArchive.AddEntryAsync(
|
||||
path.Substring(filePath.Length),
|
||||
fileInfo.OpenRead(),
|
||||
true,
|
||||
fileInfo.Length,
|
||||
fileInfo.LastWriteTime
|
||||
);
|
||||
await writableArchive
|
||||
.AddEntryAsync(
|
||||
path.Substring(filePath.Length),
|
||||
fileInfo.OpenRead(),
|
||||
true,
|
||||
fileInfo.Length,
|
||||
fileInfo.LastWriteTime
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -60,32 +62,26 @@ public static class IWritableAsyncArchiveExtensions
|
|||
fileInfo.LastWriteTime
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public ValueTask SaveToAsync(
|
||||
string filePath,
|
||||
IWriterOptions? options = null,
|
||||
CancellationToken cancellationToken = default
|
||||
) =>
|
||||
writableArchive.SaveToAsync(
|
||||
new FileInfo(filePath),
|
||||
options ?? new WriterOptions(CompressionType.Deflate),
|
||||
cancellationToken
|
||||
);
|
||||
public static ValueTask SaveToAsync<TOptions>(
|
||||
this IWritableAsyncArchive<TOptions> writableArchive,
|
||||
string filePath,
|
||||
TOptions options,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
where TOptions : IWriterOptions =>
|
||||
writableArchive.SaveToAsync(new FileInfo(filePath), options, cancellationToken);
|
||||
|
||||
public async ValueTask SaveToAsync(
|
||||
FileInfo fileInfo,
|
||||
IWriterOptions? options = null,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
using var stream = fileInfo.Open(FileMode.Create, FileAccess.Write);
|
||||
await writableArchive
|
||||
.SaveToAsync(
|
||||
stream,
|
||||
options ?? new WriterOptions(CompressionType.Deflate),
|
||||
cancellationToken
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
public static async ValueTask SaveToAsync<TOptions>(
|
||||
this IWritableAsyncArchive<TOptions> writableArchive,
|
||||
FileInfo fileInfo,
|
||||
TOptions options,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
where TOptions : IWriterOptions
|
||||
{
|
||||
using var stream = fileInfo.Open(FileMode.Create, FileAccess.Write);
|
||||
await writableArchive.SaveToAsync(stream, options, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
using SharpCompress.Common.Options;
|
||||
|
||||
namespace SharpCompress.Archives;
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -10,11 +12,12 @@ namespace SharpCompress.Archives;
|
|||
/// <item><see cref="Factories.ZipFactory"/></item>
|
||||
/// <item><see cref="Factories.GZipFactory"/></item>
|
||||
/// </list>
|
||||
public interface IWriteableArchiveFactory : Factories.IFactory
|
||||
public interface IWriteableArchiveFactory<TOptions> : Factories.IFactory
|
||||
where TOptions : IWriterOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new, empty archive, ready to be written.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
IWritableArchive CreateArchive();
|
||||
IWritableArchive<TOptions> CreateArchive();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,13 +25,13 @@ public partial class RarArchive
|
|||
}
|
||||
|
||||
_disposed = true;
|
||||
await base.DisposeAsync();
|
||||
await base.DisposeAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
protected override async ValueTask<IAsyncReader> CreateReaderForSolidExtractionAsync()
|
||||
{
|
||||
if (await this.IsMultipartVolumeAsync())
|
||||
if (await this.IsMultipartVolumeAsync().ConfigureAwait(false))
|
||||
{
|
||||
var streams = await VolumesAsync
|
||||
.Select(volume =>
|
||||
|
|
@ -39,15 +39,18 @@ public partial class RarArchive
|
|||
volume.Stream.Position = 0;
|
||||
return volume.Stream;
|
||||
})
|
||||
.ToListAsync();
|
||||
.ToListAsync()
|
||||
.ConfigureAwait(false);
|
||||
return (RarReader)RarReader.OpenReader(streams, ReaderOptions);
|
||||
}
|
||||
|
||||
var stream = (await VolumesAsync.FirstAsync()).Stream;
|
||||
var stream = (await VolumesAsync.FirstAsync().ConfigureAwait(false)).Stream;
|
||||
stream.Position = 0;
|
||||
return (RarReader)RarReader.OpenReader(stream, ReaderOptions);
|
||||
}
|
||||
|
||||
public override async ValueTask<bool> IsSolidAsync() =>
|
||||
await (await VolumesAsync.CastAsync<RarVolume>().FirstAsync()).IsSolidArchiveAsync();
|
||||
await (await VolumesAsync.CastAsync<RarVolume>().FirstAsync().ConfigureAwait(false))
|
||||
.IsSolidArchiveAsync()
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,12 +25,16 @@ public static class RarArchiveExtensions
|
|||
/// RarArchive is the first volume of a multi-part archive. If MultipartVolume is true and IsFirstVolume is false then the first volume file must be missing.
|
||||
/// </summary>
|
||||
public async ValueTask<bool> IsFirstVolumeAsync() =>
|
||||
(await archive.VolumesAsync.CastAsync<RarVolume>().FirstAsync()).IsFirstVolume;
|
||||
(
|
||||
await archive.VolumesAsync.CastAsync<RarVolume>().FirstAsync().ConfigureAwait(false)
|
||||
).IsFirstVolume;
|
||||
|
||||
/// <summary>
|
||||
/// RarArchive is part of a multi-part archive.
|
||||
/// </summary>
|
||||
public async ValueTask<bool> IsMultipartVolumeAsync() =>
|
||||
(await archive.VolumesAsync.CastAsync<RarVolume>().FirstAsync()).IsMultiVolume;
|
||||
(
|
||||
await archive.VolumesAsync.CastAsync<RarVolume>().FirstAsync().ConfigureAwait(false)
|
||||
).IsMultiVolume;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,11 +22,9 @@ public partial class RarArchive
|
|||
{
|
||||
public static IRarAsyncArchive OpenAsyncArchive(
|
||||
string path,
|
||||
ReaderOptions? readerOptions = null,
|
||||
CancellationToken cancellationToken = default
|
||||
ReaderOptions? readerOptions = null
|
||||
)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
path.NotNullOrEmpty(nameof(path));
|
||||
return (IRarAsyncArchive)OpenArchive(new FileInfo(path), readerOptions);
|
||||
}
|
||||
|
|
@ -102,41 +100,33 @@ public partial class RarArchive
|
|||
|
||||
public static IRarAsyncArchive OpenAsyncArchive(
|
||||
Stream stream,
|
||||
ReaderOptions? readerOptions = null,
|
||||
CancellationToken cancellationToken = default
|
||||
ReaderOptions? readerOptions = null
|
||||
)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
return (IRarAsyncArchive)OpenArchive(stream, readerOptions);
|
||||
}
|
||||
|
||||
public static IRarAsyncArchive OpenAsyncArchive(
|
||||
FileInfo fileInfo,
|
||||
ReaderOptions? readerOptions = null,
|
||||
CancellationToken cancellationToken = default
|
||||
ReaderOptions? readerOptions = null
|
||||
)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
return (IRarAsyncArchive)OpenArchive(fileInfo, readerOptions);
|
||||
}
|
||||
|
||||
public static IRarAsyncArchive OpenAsyncArchive(
|
||||
IReadOnlyList<Stream> streams,
|
||||
ReaderOptions? readerOptions = null,
|
||||
CancellationToken cancellationToken = default
|
||||
ReaderOptions? readerOptions = null
|
||||
)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
return (IRarAsyncArchive)OpenArchive(streams, readerOptions);
|
||||
}
|
||||
|
||||
public static IRarAsyncArchive OpenAsyncArchive(
|
||||
IReadOnlyList<FileInfo> fileInfos,
|
||||
ReaderOptions? readerOptions = null,
|
||||
CancellationToken cancellationToken = default
|
||||
ReaderOptions? readerOptions = null
|
||||
)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
return (IRarAsyncArchive)OpenArchive(fileInfos, readerOptions);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,9 +21,9 @@ public partial class RarArchiveEntry
|
|||
stream = new RarStream(
|
||||
archive.UnpackV1.Value,
|
||||
FileHeader,
|
||||
await MultiVolumeReadOnlyAsyncStream.Create(
|
||||
Parts.ToAsyncEnumerable().CastAsync<RarFilePart>()
|
||||
)
|
||||
await MultiVolumeReadOnlyAsyncStream
|
||||
.Create(Parts.ToAsyncEnumerable().CastAsync<RarFilePart>())
|
||||
.ConfigureAwait(false)
|
||||
);
|
||||
}
|
||||
else
|
||||
|
|
@ -31,13 +31,13 @@ public partial class RarArchiveEntry
|
|||
stream = new RarStream(
|
||||
archive.UnpackV2017.Value,
|
||||
FileHeader,
|
||||
await MultiVolumeReadOnlyAsyncStream.Create(
|
||||
Parts.ToAsyncEnumerable().CastAsync<RarFilePart>()
|
||||
)
|
||||
await MultiVolumeReadOnlyAsyncStream
|
||||
.Create(Parts.ToAsyncEnumerable().CastAsync<RarFilePart>())
|
||||
.ConfigureAwait(false)
|
||||
);
|
||||
}
|
||||
|
||||
await stream.InitializeAsync(cancellationToken);
|
||||
await stream.InitializeAsync(cancellationToken).ConfigureAwait(false);
|
||||
return stream;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ public partial class RarArchiveEntry : RarEntry, IArchiveEntry
|
|||
IEnumerable<RarFilePart> parts,
|
||||
ReaderOptions readerOptions
|
||||
)
|
||||
: base(readerOptions)
|
||||
{
|
||||
this.parts = parts.ToList();
|
||||
this.archive = archive;
|
||||
|
|
|
|||
|
|
@ -21,15 +21,12 @@ public partial class SevenZipArchive
|
|||
{
|
||||
stream.Position = 0;
|
||||
var reader = new ArchiveReader();
|
||||
await reader.OpenAsync(
|
||||
stream,
|
||||
lookForHeader: ReaderOptions.LookForHeader,
|
||||
cancellationToken
|
||||
);
|
||||
_database = await reader.ReadDatabaseAsync(
|
||||
new PasswordProvider(ReaderOptions.Password),
|
||||
cancellationToken
|
||||
);
|
||||
await reader
|
||||
.OpenAsync(stream, lookForHeader: ReaderOptions.LookForHeader, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
_database = await reader
|
||||
.ReadDatabaseAsync(new PasswordProvider(ReaderOptions.Password), cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -37,8 +34,8 @@ public partial class SevenZipArchive
|
|||
IAsyncEnumerable<SevenZipVolume> volumes
|
||||
)
|
||||
{
|
||||
var stream = (await volumes.SingleAsync()).Stream;
|
||||
await LoadFactoryAsync(stream);
|
||||
var stream = (await volumes.SingleAsync().ConfigureAwait(false)).Stream;
|
||||
await LoadFactoryAsync(stream).ConfigureAwait(false);
|
||||
if (_database is null)
|
||||
{
|
||||
yield break;
|
||||
|
|
@ -49,7 +46,8 @@ public partial class SevenZipArchive
|
|||
var file = _database._files[i];
|
||||
entries[i] = new SevenZipArchiveEntry(
|
||||
this,
|
||||
new SevenZipFilePart(stream, _database, i, file, ReaderOptions.ArchiveEncoding)
|
||||
new SevenZipFilePart(stream, _database, i, file, ReaderOptions.ArchiveEncoding),
|
||||
ReaderOptions
|
||||
);
|
||||
}
|
||||
foreach (var group in entries.Where(x => !x.IsDirectory).GroupBy(x => x.FilePart.Folder))
|
||||
|
|
|
|||
|
|
@ -16,13 +16,8 @@ public partial class SevenZipArchive
|
|||
IMultiArchiveOpenable<IArchive, IAsyncArchive>
|
||||
#endif
|
||||
{
|
||||
public static IAsyncArchive OpenAsyncArchive(
|
||||
string path,
|
||||
ReaderOptions? readerOptions = null,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
public static IAsyncArchive OpenAsyncArchive(string path, ReaderOptions? readerOptions = null)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
path.NotNullOrEmpty("path");
|
||||
return (IAsyncArchive)OpenArchive(new FileInfo(path), readerOptions ?? new ReaderOptions());
|
||||
}
|
||||
|
|
@ -91,43 +86,32 @@ public partial class SevenZipArchive
|
|||
);
|
||||
}
|
||||
|
||||
public static IAsyncArchive OpenAsyncArchive(
|
||||
Stream stream,
|
||||
ReaderOptions? readerOptions = null,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
public static IAsyncArchive OpenAsyncArchive(Stream stream, ReaderOptions? readerOptions = null)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
return (IAsyncArchive)OpenArchive(stream, readerOptions);
|
||||
}
|
||||
|
||||
public static IAsyncArchive OpenAsyncArchive(
|
||||
FileInfo fileInfo,
|
||||
ReaderOptions? readerOptions = null,
|
||||
CancellationToken cancellationToken = default
|
||||
ReaderOptions? readerOptions = null
|
||||
)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
return (IAsyncArchive)OpenArchive(fileInfo, readerOptions);
|
||||
}
|
||||
|
||||
public static IAsyncArchive OpenAsyncArchive(
|
||||
IReadOnlyList<Stream> streams,
|
||||
ReaderOptions? readerOptions = null,
|
||||
CancellationToken cancellationToken = default
|
||||
ReaderOptions? readerOptions = null
|
||||
)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
return (IAsyncArchive)OpenArchive(streams, readerOptions);
|
||||
}
|
||||
|
||||
public static IAsyncArchive OpenAsyncArchive(
|
||||
IReadOnlyList<FileInfo> fileInfos,
|
||||
ReaderOptions? readerOptions = null,
|
||||
CancellationToken cancellationToken = default
|
||||
ReaderOptions? readerOptions = null
|
||||
)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
return (IAsyncArchive)OpenArchive(fileInfos, readerOptions);
|
||||
}
|
||||
|
||||
|
|
@ -163,7 +147,7 @@ public partial class SevenZipArchive
|
|||
cancellationToken.ThrowIfCancellationRequested();
|
||||
try
|
||||
{
|
||||
return await SignatureMatchAsync(stream, cancellationToken);
|
||||
return await SignatureMatchAsync(stream, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
|
|
|||
|
|
@ -55,7 +55,8 @@ public partial class SevenZipArchive : AbstractArchive<SevenZipArchiveEntry, Sev
|
|||
i,
|
||||
file,
|
||||
ReaderOptions.ArchiveEncoding
|
||||
)
|
||||
),
|
||||
ReaderOptions
|
||||
);
|
||||
}
|
||||
foreach (
|
||||
|
|
|
|||
|
|
@ -1,20 +1,28 @@
|
|||
using System.IO;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Common.Options;
|
||||
using SharpCompress.Common.SevenZip;
|
||||
|
||||
namespace SharpCompress.Archives.SevenZip;
|
||||
|
||||
public class SevenZipArchiveEntry : SevenZipEntry, IArchiveEntry
|
||||
{
|
||||
internal SevenZipArchiveEntry(SevenZipArchive archive, SevenZipFilePart part)
|
||||
: base(part) => Archive = archive;
|
||||
internal SevenZipArchiveEntry(
|
||||
SevenZipArchive archive,
|
||||
SevenZipFilePart part,
|
||||
IReaderOptions readerOptions
|
||||
)
|
||||
: base(part, readerOptions) => Archive = archive;
|
||||
|
||||
public Stream OpenEntryStream() => FilePart.GetCompressedStream();
|
||||
|
||||
public async ValueTask<Stream> OpenEntryStreamAsync(
|
||||
CancellationToken cancellationToken = default
|
||||
) => (await FilePart.GetCompressedStreamAsync(cancellationToken)).NotNull();
|
||||
) =>
|
||||
(
|
||||
await FilePart.GetCompressedStreamAsync(cancellationToken).ConfigureAwait(false)
|
||||
).NotNull();
|
||||
|
||||
public IArchive Archive { get; }
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ public partial class TarArchive
|
|||
{
|
||||
protected override async ValueTask SaveToAsync(
|
||||
Stream stream,
|
||||
IWriterOptions options,
|
||||
TarWriterOptions options,
|
||||
IAsyncEnumerable<TarArchiveEntry> oldEntries,
|
||||
IEnumerable<TarArchiveEntry> newEntries,
|
||||
CancellationToken cancellationToken = default
|
||||
|
|
@ -96,7 +96,7 @@ public partial class TarArchive
|
|||
IAsyncEnumerable<TarVolume> volumes
|
||||
)
|
||||
{
|
||||
var stream = (await volumes.SingleAsync()).Stream;
|
||||
var stream = (await volumes.SingleAsync().ConfigureAwait(false)).Stream;
|
||||
if (stream.CanSeek)
|
||||
{
|
||||
stream.Position = 0;
|
||||
|
|
@ -127,7 +127,8 @@ public partial class TarArchive
|
|||
var entry = new TarArchiveEntry(
|
||||
this,
|
||||
new TarFilePart(previousHeader, stream),
|
||||
CompressionType.None
|
||||
CompressionType.None,
|
||||
ReaderOptions
|
||||
);
|
||||
|
||||
var oldStreamPos = stream.Position;
|
||||
|
|
@ -135,7 +136,7 @@ public partial class TarArchive
|
|||
using (var entryStream = entry.OpenEntryStream())
|
||||
{
|
||||
using var memoryStream = new MemoryStream();
|
||||
await entryStream.CopyToAsync(memoryStream);
|
||||
await entryStream.CopyToAsync(memoryStream).ConfigureAwait(false);
|
||||
memoryStream.Position = 0;
|
||||
var bytes = memoryStream.ToArray();
|
||||
|
||||
|
|
@ -151,7 +152,8 @@ public partial class TarArchive
|
|||
yield return new TarArchiveEntry(
|
||||
this,
|
||||
new TarFilePart(header, stream),
|
||||
CompressionType.None
|
||||
CompressionType.None,
|
||||
ReaderOptions
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,22 +9,29 @@ using SharpCompress.Common;
|
|||
using SharpCompress.Common.Tar.Headers;
|
||||
using SharpCompress.IO;
|
||||
using SharpCompress.Readers;
|
||||
using SharpCompress.Writers.Tar;
|
||||
|
||||
namespace SharpCompress.Archives.Tar;
|
||||
|
||||
public partial class TarArchive
|
||||
#if NET8_0_OR_GREATER
|
||||
: IWritableArchiveOpenable,
|
||||
IMultiArchiveOpenable<IWritableArchive, IWritableAsyncArchive>
|
||||
: IWritableArchiveOpenable<TarWriterOptions>,
|
||||
IMultiArchiveOpenable<
|
||||
IWritableArchive<TarWriterOptions>,
|
||||
IWritableAsyncArchive<TarWriterOptions>
|
||||
>
|
||||
#endif
|
||||
{
|
||||
public static IWritableArchive OpenArchive(string filePath, ReaderOptions? readerOptions = null)
|
||||
public static IWritableArchive<TarWriterOptions> OpenArchive(
|
||||
string filePath,
|
||||
ReaderOptions? readerOptions = null
|
||||
)
|
||||
{
|
||||
filePath.NotNullOrEmpty(nameof(filePath));
|
||||
return OpenArchive(new FileInfo(filePath), readerOptions);
|
||||
}
|
||||
|
||||
public static IWritableArchive OpenArchive(
|
||||
public static IWritableArchive<TarWriterOptions> OpenArchive(
|
||||
FileInfo fileInfo,
|
||||
ReaderOptions? readerOptions = null
|
||||
)
|
||||
|
|
@ -39,7 +46,7 @@ public partial class TarArchive
|
|||
);
|
||||
}
|
||||
|
||||
public static IWritableArchive OpenArchive(
|
||||
public static IWritableArchive<TarWriterOptions> OpenArchive(
|
||||
IEnumerable<FileInfo> fileInfos,
|
||||
ReaderOptions? readerOptions = null
|
||||
)
|
||||
|
|
@ -55,7 +62,7 @@ public partial class TarArchive
|
|||
);
|
||||
}
|
||||
|
||||
public static IWritableArchive OpenArchive(
|
||||
public static IWritableArchive<TarWriterOptions> OpenArchive(
|
||||
IEnumerable<Stream> streams,
|
||||
ReaderOptions? readerOptions = null
|
||||
)
|
||||
|
|
@ -71,7 +78,10 @@ public partial class TarArchive
|
|||
);
|
||||
}
|
||||
|
||||
public static IWritableArchive OpenArchive(Stream stream, ReaderOptions? readerOptions = null)
|
||||
public static IWritableArchive<TarWriterOptions> OpenArchive(
|
||||
Stream stream,
|
||||
ReaderOptions? readerOptions = null
|
||||
)
|
||||
{
|
||||
stream.NotNull(nameof(stream));
|
||||
|
||||
|
|
@ -85,55 +95,30 @@ public partial class TarArchive
|
|||
);
|
||||
}
|
||||
|
||||
public static IWritableAsyncArchive OpenAsyncArchive(
|
||||
public static IWritableAsyncArchive<TarWriterOptions> OpenAsyncArchive(
|
||||
Stream stream,
|
||||
ReaderOptions? readerOptions = null,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
return (IWritableAsyncArchive)OpenArchive(stream, readerOptions);
|
||||
}
|
||||
ReaderOptions? readerOptions = null
|
||||
) => (IWritableAsyncArchive<TarWriterOptions>)OpenArchive(stream, readerOptions);
|
||||
|
||||
public static IWritableAsyncArchive OpenAsyncArchive(
|
||||
public static IWritableAsyncArchive<TarWriterOptions> OpenAsyncArchive(
|
||||
string path,
|
||||
ReaderOptions? readerOptions = null,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
return (IWritableAsyncArchive)OpenArchive(new FileInfo(path), readerOptions);
|
||||
}
|
||||
ReaderOptions? readerOptions = null
|
||||
) => (IWritableAsyncArchive<TarWriterOptions>)OpenArchive(new FileInfo(path), readerOptions);
|
||||
|
||||
public static IWritableAsyncArchive OpenAsyncArchive(
|
||||
public static IWritableAsyncArchive<TarWriterOptions> OpenAsyncArchive(
|
||||
FileInfo fileInfo,
|
||||
ReaderOptions? readerOptions = null,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
return (IWritableAsyncArchive)OpenArchive(fileInfo, readerOptions);
|
||||
}
|
||||
ReaderOptions? readerOptions = null
|
||||
) => (IWritableAsyncArchive<TarWriterOptions>)OpenArchive(fileInfo, readerOptions);
|
||||
|
||||
public static IWritableAsyncArchive OpenAsyncArchive(
|
||||
public static IWritableAsyncArchive<TarWriterOptions> OpenAsyncArchive(
|
||||
IReadOnlyList<Stream> streams,
|
||||
ReaderOptions? readerOptions = null,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
return (IWritableAsyncArchive)OpenArchive(streams, readerOptions);
|
||||
}
|
||||
ReaderOptions? readerOptions = null
|
||||
) => (IWritableAsyncArchive<TarWriterOptions>)OpenArchive(streams, readerOptions);
|
||||
|
||||
public static IWritableAsyncArchive OpenAsyncArchive(
|
||||
public static IWritableAsyncArchive<TarWriterOptions> OpenAsyncArchive(
|
||||
IReadOnlyList<FileInfo> fileInfos,
|
||||
ReaderOptions? readerOptions = null,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
return (IWritableAsyncArchive)OpenArchive(fileInfos, readerOptions);
|
||||
}
|
||||
ReaderOptions? readerOptions = null
|
||||
) => (IWritableAsyncArchive<TarWriterOptions>)OpenArchive(fileInfos, readerOptions);
|
||||
|
||||
public static bool IsTarFile(string filePath) => IsTarFile(new FileInfo(filePath));
|
||||
|
||||
|
|
@ -181,7 +166,7 @@ public partial class TarArchive
|
|||
#else
|
||||
using var reader = new AsyncBinaryReader(stream, leaveOpen: true);
|
||||
#endif
|
||||
var readSucceeded = await tarHeader.ReadAsync(reader);
|
||||
var readSucceeded = await tarHeader.ReadAsync(reader).ConfigureAwait(false);
|
||||
var isEmptyArchive =
|
||||
tarHeader.Name?.Length == 0
|
||||
&& tarHeader.Size == 0
|
||||
|
|
@ -196,7 +181,7 @@ public partial class TarArchive
|
|||
}
|
||||
}
|
||||
|
||||
public static IWritableArchive CreateArchive() => new TarArchive();
|
||||
public static IWritableArchive<TarWriterOptions> CreateArchive() => new TarArchive();
|
||||
|
||||
public static IWritableAsyncArchive CreateAsyncArchive() => new TarArchive();
|
||||
public static IWritableAsyncArchive<TarWriterOptions> CreateAsyncArchive() => new TarArchive();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,8 @@ using SharpCompress.Writers.Tar;
|
|||
|
||||
namespace SharpCompress.Archives.Tar;
|
||||
|
||||
public partial class TarArchive : AbstractWritableArchive<TarArchiveEntry, TarVolume>
|
||||
public partial class TarArchive
|
||||
: AbstractWritableArchive<TarArchiveEntry, TarVolume, TarWriterOptions>
|
||||
{
|
||||
protected override IEnumerable<TarVolume> LoadVolumes(SourceStream sourceStream)
|
||||
{
|
||||
|
|
@ -59,7 +60,8 @@ public partial class TarArchive : AbstractWritableArchive<TarArchiveEntry, TarVo
|
|||
var entry = new TarArchiveEntry(
|
||||
this,
|
||||
new TarFilePart(previousHeader, stream),
|
||||
CompressionType.None
|
||||
CompressionType.None,
|
||||
ReaderOptions
|
||||
);
|
||||
|
||||
var oldStreamPos = stream.Position;
|
||||
|
|
@ -81,7 +83,8 @@ public partial class TarArchive : AbstractWritableArchive<TarArchiveEntry, TarVo
|
|||
yield return new TarArchiveEntry(
|
||||
this,
|
||||
new TarFilePart(header, stream),
|
||||
CompressionType.None
|
||||
CompressionType.None,
|
||||
ReaderOptions
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -116,7 +119,7 @@ public partial class TarArchive : AbstractWritableArchive<TarArchiveEntry, TarVo
|
|||
|
||||
protected override void SaveTo(
|
||||
Stream stream,
|
||||
IWriterOptions options,
|
||||
TarWriterOptions options,
|
||||
IEnumerable<TarArchiveEntry> oldEntries,
|
||||
IEnumerable<TarArchiveEntry> newEntries
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,22 +1,31 @@
|
|||
using System.IO;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Common.Options;
|
||||
using SharpCompress.Common.Tar;
|
||||
|
||||
namespace SharpCompress.Archives.Tar;
|
||||
|
||||
public class TarArchiveEntry : TarEntry, IArchiveEntry
|
||||
{
|
||||
internal TarArchiveEntry(TarArchive archive, TarFilePart? part, CompressionType compressionType)
|
||||
: base(part, compressionType) => Archive = archive;
|
||||
internal TarArchiveEntry(
|
||||
TarArchive archive,
|
||||
TarFilePart? part,
|
||||
CompressionType compressionType,
|
||||
IReaderOptions readerOptions
|
||||
)
|
||||
: base(part, compressionType, readerOptions) => Archive = archive;
|
||||
|
||||
public virtual Stream OpenEntryStream() => Parts.Single().GetCompressedStream().NotNull();
|
||||
|
||||
public async ValueTask<Stream> OpenEntryStreamAsync(
|
||||
CancellationToken cancellationToken = default
|
||||
) => (await Parts.Single().GetCompressedStreamAsync(cancellationToken)).NotNull();
|
||||
) =>
|
||||
(
|
||||
await Parts.Single().GetCompressedStreamAsync(cancellationToken).ConfigureAwait(false)
|
||||
).NotNull();
|
||||
|
||||
#region IArchiveEntry Members
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ internal sealed class TarWritableArchiveEntry : TarArchiveEntry, IWritableArchiv
|
|||
DateTime? lastModified,
|
||||
bool closeStream
|
||||
)
|
||||
: base(archive, null, compressionType)
|
||||
: base(archive, null, compressionType, archive.ReaderOptions)
|
||||
{
|
||||
this.stream = stream;
|
||||
Key = path;
|
||||
|
|
@ -36,7 +36,7 @@ internal sealed class TarWritableArchiveEntry : TarArchiveEntry, IWritableArchiv
|
|||
string directoryPath,
|
||||
DateTime? lastModified
|
||||
)
|
||||
: base(archive, null, CompressionType.None)
|
||||
: base(archive, null, CompressionType.None, archive.ReaderOptions)
|
||||
{
|
||||
stream = null;
|
||||
Key = directoryPath;
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ public partial class ZipArchive
|
|||
IAsyncEnumerable<ZipVolume> volumes
|
||||
)
|
||||
{
|
||||
var vols = await volumes.ToListAsync();
|
||||
var vols = await volumes.ToListAsync().ConfigureAwait(false);
|
||||
var volsArray = vols.ToArray();
|
||||
|
||||
await foreach (
|
||||
|
|
@ -55,12 +55,8 @@ public partial class ZipArchive
|
|||
|
||||
yield return new ZipArchiveEntry(
|
||||
this,
|
||||
new SeekableZipFilePart(
|
||||
headerFactory.NotNull(),
|
||||
deh,
|
||||
s,
|
||||
ReaderOptions.CompressionProviders
|
||||
)
|
||||
new SeekableZipFilePart(headerFactory.NotNull(), deh, s),
|
||||
ReaderOptions
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
|
@ -77,7 +73,7 @@ public partial class ZipArchive
|
|||
|
||||
protected override async ValueTask SaveToAsync(
|
||||
Stream stream,
|
||||
IWriterOptions options,
|
||||
ZipWriterOptions options,
|
||||
IAsyncEnumerable<ZipArchiveEntry> oldEntries,
|
||||
IEnumerable<ZipArchiveEntry> newEntries,
|
||||
CancellationToken cancellationToken = default
|
||||
|
|
|
|||
|
|
@ -9,22 +9,29 @@ using SharpCompress.Common.Zip;
|
|||
using SharpCompress.Common.Zip.Headers;
|
||||
using SharpCompress.IO;
|
||||
using SharpCompress.Readers;
|
||||
using SharpCompress.Writers.Zip;
|
||||
|
||||
namespace SharpCompress.Archives.Zip;
|
||||
|
||||
public partial class ZipArchive
|
||||
#if NET8_0_OR_GREATER
|
||||
: IWritableArchiveOpenable,
|
||||
IMultiArchiveOpenable<IWritableArchive, IWritableAsyncArchive>
|
||||
: IWritableArchiveOpenable<ZipWriterOptions>,
|
||||
IMultiArchiveOpenable<
|
||||
IWritableArchive<ZipWriterOptions>,
|
||||
IWritableAsyncArchive<ZipWriterOptions>
|
||||
>
|
||||
#endif
|
||||
{
|
||||
public static IWritableArchive OpenArchive(string filePath, ReaderOptions? readerOptions = null)
|
||||
public static IWritableArchive<ZipWriterOptions> OpenArchive(
|
||||
string filePath,
|
||||
ReaderOptions? readerOptions = null
|
||||
)
|
||||
{
|
||||
filePath.NotNullOrEmpty(nameof(filePath));
|
||||
return OpenArchive(new FileInfo(filePath), readerOptions);
|
||||
}
|
||||
|
||||
public static IWritableArchive OpenArchive(
|
||||
public static IWritableArchive<ZipWriterOptions> OpenArchive(
|
||||
FileInfo fileInfo,
|
||||
ReaderOptions? readerOptions = null
|
||||
)
|
||||
|
|
@ -39,7 +46,7 @@ public partial class ZipArchive
|
|||
);
|
||||
}
|
||||
|
||||
public static IWritableArchive OpenArchive(
|
||||
public static IWritableArchive<ZipWriterOptions> OpenArchive(
|
||||
IEnumerable<FileInfo> fileInfos,
|
||||
ReaderOptions? readerOptions = null
|
||||
)
|
||||
|
|
@ -55,7 +62,7 @@ public partial class ZipArchive
|
|||
);
|
||||
}
|
||||
|
||||
public static IWritableArchive OpenArchive(
|
||||
public static IWritableArchive<ZipWriterOptions> OpenArchive(
|
||||
IEnumerable<Stream> streams,
|
||||
ReaderOptions? readerOptions = null
|
||||
)
|
||||
|
|
@ -71,7 +78,10 @@ public partial class ZipArchive
|
|||
);
|
||||
}
|
||||
|
||||
public static IWritableArchive OpenArchive(Stream stream, ReaderOptions? readerOptions = null)
|
||||
public static IWritableArchive<ZipWriterOptions> OpenArchive(
|
||||
Stream stream,
|
||||
ReaderOptions? readerOptions = null
|
||||
)
|
||||
{
|
||||
stream.NotNull(nameof(stream));
|
||||
|
||||
|
|
@ -85,55 +95,30 @@ public partial class ZipArchive
|
|||
);
|
||||
}
|
||||
|
||||
public static IWritableAsyncArchive OpenAsyncArchive(
|
||||
public static IWritableAsyncArchive<ZipWriterOptions> OpenAsyncArchive(
|
||||
string path,
|
||||
ReaderOptions? readerOptions = null,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
return (IWritableAsyncArchive)OpenArchive(path, readerOptions);
|
||||
}
|
||||
ReaderOptions? readerOptions = null
|
||||
) => (IWritableAsyncArchive<ZipWriterOptions>)OpenArchive(path, readerOptions);
|
||||
|
||||
public static IWritableAsyncArchive OpenAsyncArchive(
|
||||
public static IWritableAsyncArchive<ZipWriterOptions> OpenAsyncArchive(
|
||||
Stream stream,
|
||||
ReaderOptions? readerOptions = null,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
return (IWritableAsyncArchive)OpenArchive(stream, readerOptions);
|
||||
}
|
||||
ReaderOptions? readerOptions = null
|
||||
) => (IWritableAsyncArchive<ZipWriterOptions>)OpenArchive(stream, readerOptions);
|
||||
|
||||
public static IWritableAsyncArchive OpenAsyncArchive(
|
||||
public static IWritableAsyncArchive<ZipWriterOptions> OpenAsyncArchive(
|
||||
FileInfo fileInfo,
|
||||
ReaderOptions? readerOptions = null,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
return (IWritableAsyncArchive)OpenArchive(fileInfo, readerOptions);
|
||||
}
|
||||
ReaderOptions? readerOptions = null
|
||||
) => (IWritableAsyncArchive<ZipWriterOptions>)OpenArchive(fileInfo, readerOptions);
|
||||
|
||||
public static IWritableAsyncArchive OpenAsyncArchive(
|
||||
public static IWritableAsyncArchive<ZipWriterOptions> OpenAsyncArchive(
|
||||
IReadOnlyList<Stream> streams,
|
||||
ReaderOptions? readerOptions = null,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
return (IWritableAsyncArchive)OpenArchive(streams, readerOptions);
|
||||
}
|
||||
ReaderOptions? readerOptions = null
|
||||
) => (IWritableAsyncArchive<ZipWriterOptions>)OpenArchive(streams, readerOptions);
|
||||
|
||||
public static IWritableAsyncArchive OpenAsyncArchive(
|
||||
public static IWritableAsyncArchive<ZipWriterOptions> OpenAsyncArchive(
|
||||
IReadOnlyList<FileInfo> fileInfos,
|
||||
ReaderOptions? readerOptions = null,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
return (IWritableAsyncArchive)OpenArchive(fileInfos, readerOptions);
|
||||
}
|
||||
ReaderOptions? readerOptions = null
|
||||
) => (IWritableAsyncArchive<ZipWriterOptions>)OpenArchive(fileInfos, readerOptions);
|
||||
|
||||
public static bool IsZipFile(string filePath, string? password = null) =>
|
||||
IsZipFile(new FileInfo(filePath), password);
|
||||
|
|
@ -218,7 +203,8 @@ public partial class ZipArchive
|
|||
var header = await headerFactory
|
||||
.ReadStreamHeaderAsync(stream)
|
||||
.Where(x => x.ZipHeaderType != ZipHeaderType.Split)
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
.FirstOrDefaultAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
if (header is null)
|
||||
{
|
||||
return false;
|
||||
|
|
@ -235,9 +221,9 @@ public partial class ZipArchive
|
|||
}
|
||||
}
|
||||
|
||||
public static IWritableArchive CreateArchive() => new ZipArchive();
|
||||
public static IWritableArchive<ZipWriterOptions> CreateArchive() => new ZipArchive();
|
||||
|
||||
public static IWritableAsyncArchive CreateAsyncArchive() => new ZipArchive();
|
||||
public static IWritableAsyncArchive<ZipWriterOptions> CreateAsyncArchive() => new ZipArchive();
|
||||
|
||||
public static async ValueTask<bool> IsZipMultiAsync(
|
||||
Stream stream,
|
||||
|
|
@ -261,6 +247,7 @@ public partial class ZipArchive
|
|||
await foreach (
|
||||
var h in z.ReadSeekableHeaderAsync(stream)
|
||||
.WithCancellation(cancellationToken)
|
||||
.ConfigureAwait(false)
|
||||
)
|
||||
{
|
||||
x = h;
|
||||
|
|
|
|||
|
|
@ -17,7 +17,8 @@ using SharpCompress.Writers.Zip;
|
|||
|
||||
namespace SharpCompress.Archives.Zip;
|
||||
|
||||
public partial class ZipArchive : AbstractWritableArchive<ZipArchiveEntry, ZipVolume>
|
||||
public partial class ZipArchive
|
||||
: AbstractWritableArchive<ZipArchiveEntry, ZipVolume, ZipWriterOptions>
|
||||
{
|
||||
private readonly SeekableZipHeaderFactory? headerFactory;
|
||||
|
||||
|
|
@ -95,12 +96,8 @@ public partial class ZipArchive : AbstractWritableArchive<ZipArchiveEntry, ZipVo
|
|||
|
||||
yield return new ZipArchiveEntry(
|
||||
this,
|
||||
new SeekableZipFilePart(
|
||||
headerFactory.NotNull(),
|
||||
deh,
|
||||
s,
|
||||
ReaderOptions.CompressionProviders
|
||||
)
|
||||
new SeekableZipFilePart(headerFactory.NotNull(), deh, s),
|
||||
ReaderOptions
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
|
@ -115,11 +112,12 @@ public partial class ZipArchive : AbstractWritableArchive<ZipArchiveEntry, ZipVo
|
|||
}
|
||||
}
|
||||
|
||||
public void SaveTo(Stream stream) => SaveTo(stream, new WriterOptions(CompressionType.Deflate));
|
||||
public void SaveTo(Stream stream) =>
|
||||
SaveTo(stream, new ZipWriterOptions(CompressionType.Deflate));
|
||||
|
||||
protected override void SaveTo(
|
||||
Stream stream,
|
||||
IWriterOptions options,
|
||||
ZipWriterOptions options,
|
||||
IEnumerable<ZipArchiveEntry> oldEntries,
|
||||
IEnumerable<ZipArchiveEntry> newEntries
|
||||
)
|
||||
|
|
|
|||
|
|
@ -15,7 +15,9 @@ public partial class ZipArchiveEntry
|
|||
var part = Parts.Single();
|
||||
if (part is SeekableZipFilePart seekablePart)
|
||||
{
|
||||
return (await seekablePart.GetCompressedStreamAsync(cancellationToken)).NotNull();
|
||||
return (
|
||||
await seekablePart.GetCompressedStreamAsync(cancellationToken).ConfigureAwait(false)
|
||||
).NotNull();
|
||||
}
|
||||
return OpenEntryStream();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,20 @@
|
|||
using System.IO;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Common.Options;
|
||||
using SharpCompress.Common.Zip;
|
||||
|
||||
namespace SharpCompress.Archives.Zip;
|
||||
|
||||
public partial class ZipArchiveEntry : ZipEntry, IArchiveEntry
|
||||
{
|
||||
internal ZipArchiveEntry(ZipArchive archive, SeekableZipFilePart? part)
|
||||
: base(part) => Archive = archive;
|
||||
internal ZipArchiveEntry(
|
||||
ZipArchive archive,
|
||||
SeekableZipFilePart? part,
|
||||
IReaderOptions readerOptions
|
||||
)
|
||||
: base(part, readerOptions) => Archive = archive;
|
||||
|
||||
public virtual Stream OpenEntryStream() => Parts.Single().GetCompressedStream().NotNull();
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ internal class ZipWritableArchiveEntry : ZipArchiveEntry, IWritableArchiveEntry
|
|||
DateTime? lastModified,
|
||||
bool closeStream
|
||||
)
|
||||
: base(archive, null)
|
||||
: base(archive, null, archive.ReaderOptions)
|
||||
{
|
||||
this.stream = stream;
|
||||
Key = path;
|
||||
|
|
@ -36,7 +36,7 @@ internal class ZipWritableArchiveEntry : ZipArchiveEntry, IWritableArchiveEntry
|
|||
string directoryPath,
|
||||
DateTime? lastModified
|
||||
)
|
||||
: base(archive, null)
|
||||
: base(archive, null, archive.ReaderOptions)
|
||||
{
|
||||
stream = null;
|
||||
Key = directoryPath;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using System.Linq;
|
|||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Common.Ace.Headers;
|
||||
using SharpCompress.Common.Options;
|
||||
|
||||
namespace SharpCompress.Common.Ace;
|
||||
|
||||
|
|
@ -12,7 +13,8 @@ public class AceEntry : Entry
|
|||
{
|
||||
private readonly AceFilePart _filePart;
|
||||
|
||||
internal AceEntry(AceFilePart filePart)
|
||||
internal AceEntry(AceFilePart filePart, IReaderOptions readerOptions)
|
||||
: base(readerOptions)
|
||||
{
|
||||
_filePart = filePart;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ public sealed partial class AceFileHeader
|
|||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
var headerData = await ReadHeaderAsync(stream, cancellationToken);
|
||||
var headerData = await ReadHeaderAsync(stream, cancellationToken).ConfigureAwait(false);
|
||||
if (headerData.Length == 0)
|
||||
{
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -19,7 +19,9 @@ public abstract partial class AceHeader
|
|||
{
|
||||
// Read header CRC (2 bytes) and header size (2 bytes)
|
||||
var headerBytes = new byte[4];
|
||||
if (!await stream.ReadFullyAsync(headerBytes, 0, 4, cancellationToken))
|
||||
if (
|
||||
!await stream.ReadFullyAsync(headerBytes, 0, 4, cancellationToken).ConfigureAwait(false)
|
||||
)
|
||||
{
|
||||
return Array.Empty<byte>();
|
||||
}
|
||||
|
|
@ -33,7 +35,11 @@ public abstract partial class AceHeader
|
|||
|
||||
// Read the header data
|
||||
var body = new byte[HeaderSize];
|
||||
if (!await stream.ReadFullyAsync(body, 0, HeaderSize, cancellationToken))
|
||||
if (
|
||||
!await stream
|
||||
.ReadFullyAsync(body, 0, HeaderSize, cancellationToken)
|
||||
.ConfigureAwait(false)
|
||||
)
|
||||
{
|
||||
return Array.Empty<byte>();
|
||||
}
|
||||
|
|
@ -59,7 +65,7 @@ public abstract partial class AceHeader
|
|||
)
|
||||
{
|
||||
var bytes = new byte[14];
|
||||
if (!await stream.ReadFullyAsync(bytes, 0, 14, cancellationToken))
|
||||
if (!await stream.ReadFullyAsync(bytes, 0, 14, cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ public sealed partial class AceMainHeader
|
|||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
var headerData = await ReadHeaderAsync(stream, cancellationToken);
|
||||
var headerData = await ReadHeaderAsync(stream, cancellationToken).ConfigureAwait(false);
|
||||
if (headerData.Length == 0)
|
||||
{
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using System.Linq;
|
|||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Common.GZip;
|
||||
using SharpCompress.Common.Options;
|
||||
using SharpCompress.Common.Tar;
|
||||
|
||||
namespace SharpCompress.Common.Arc;
|
||||
|
|
@ -13,7 +14,8 @@ public class ArcEntry : Entry
|
|||
{
|
||||
private readonly ArcFilePart? _filePart;
|
||||
|
||||
internal ArcEntry(ArcFilePart? filePart)
|
||||
internal ArcEntry(ArcFilePart? filePart, IReaderOptions readerOptions)
|
||||
: base(readerOptions)
|
||||
{
|
||||
_filePart = filePart;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,8 +41,9 @@ public class ArcEntryHeader
|
|||
{
|
||||
byte[] headerBytes = new byte[29];
|
||||
if (
|
||||
await stream.ReadAsync(headerBytes, 0, headerBytes.Length, cancellationToken)
|
||||
!= headerBytes.Length
|
||||
await stream
|
||||
.ReadAsync(headerBytes, 0, headerBytes.Length, cancellationToken)
|
||||
.ConfigureAwait(false) != headerBytes.Length
|
||||
)
|
||||
{
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -31,11 +31,9 @@ public partial class ArcFilePart
|
|||
compressedStream = new RunLength90Stream(_stream, (int)Header.CompressedSize);
|
||||
break;
|
||||
case CompressionType.Squeezed:
|
||||
compressedStream = await SqueezeStream.CreateAsync(
|
||||
_stream,
|
||||
(int)Header.CompressedSize,
|
||||
cancellationToken
|
||||
);
|
||||
compressedStream = await SqueezeStream
|
||||
.CreateAsync(_stream, (int)Header.CompressedSize, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
break;
|
||||
case CompressionType.Crunched:
|
||||
if (Header.OriginalSize > 128 * 1024)
|
||||
|
|
|
|||
|
|
@ -10,4 +10,5 @@ public enum ArchiveType
|
|||
Arc,
|
||||
Arj,
|
||||
Ace,
|
||||
Lzw,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using System.Text;
|
|||
using System.Threading.Tasks;
|
||||
using SharpCompress.Common.Arc;
|
||||
using SharpCompress.Common.Arj.Headers;
|
||||
using SharpCompress.Common.Options;
|
||||
|
||||
namespace SharpCompress.Common.Arj;
|
||||
|
||||
|
|
@ -12,7 +13,8 @@ public class ArjEntry : Entry
|
|||
{
|
||||
private readonly ArjFilePart _filePart;
|
||||
|
||||
internal ArjEntry(ArjFilePart filePart)
|
||||
internal ArjEntry(ArjFilePart filePart, IReaderOptions readerOptions)
|
||||
: base(readerOptions)
|
||||
{
|
||||
_filePart = filePart;
|
||||
}
|
||||
|
|
@ -41,9 +43,9 @@ public class ArjEntry : Entry
|
|||
|
||||
public override DateTime? LastModifiedTime => _filePart.Header.DateTimeModified.DateTime;
|
||||
|
||||
public override DateTime? CreatedTime => _filePart.Header.DateTimeCreated.DateTime;
|
||||
public override DateTime? CreatedTime => _filePart.Header.DateTimeCreated?.DateTime;
|
||||
|
||||
public override DateTime? LastAccessedTime => _filePart.Header.DateTimeAccessed.DateTime;
|
||||
public override DateTime? LastAccessedTime => _filePart.Header.DateTimeAccessed?.DateTime;
|
||||
|
||||
public override DateTime? ArchivedTime => null;
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ public abstract partial class ArjHeader
|
|||
{
|
||||
// check for magic bytes
|
||||
var magic = new byte[2];
|
||||
if (await stream.ReadAsync(magic, 0, 2, cancellationToken) != 2)
|
||||
if (await stream.ReadAsync(magic, 0, 2, cancellationToken).ConfigureAwait(false) != 2)
|
||||
{
|
||||
return Array.Empty<byte>();
|
||||
}
|
||||
|
|
@ -33,7 +33,7 @@ public abstract partial class ArjHeader
|
|||
|
||||
// read header_size
|
||||
byte[] headerBytes = new byte[2];
|
||||
await stream.ReadAsync(headerBytes, 0, 2, cancellationToken);
|
||||
await stream.ReadAsync(headerBytes, 0, 2, cancellationToken).ConfigureAwait(false);
|
||||
var headerSize = (ushort)(headerBytes[0] | headerBytes[1] << 8);
|
||||
if (headerSize < 1)
|
||||
{
|
||||
|
|
@ -41,14 +41,16 @@ public abstract partial class ArjHeader
|
|||
}
|
||||
|
||||
var body = new byte[headerSize];
|
||||
var read = await stream.ReadAsync(body, 0, headerSize, cancellationToken);
|
||||
var read = await stream
|
||||
.ReadAsync(body, 0, headerSize, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
if (read < headerSize)
|
||||
{
|
||||
return Array.Empty<byte>();
|
||||
}
|
||||
|
||||
byte[] crc = new byte[4];
|
||||
read = await stream.ReadAsync(crc, 0, 4, cancellationToken);
|
||||
await stream.ReadFullyAsync(crc, 0, 4, cancellationToken).ConfigureAwait(false);
|
||||
var checksum = Crc32Stream.Compute(body);
|
||||
// Compute the hash value
|
||||
if (checksum != BitConverter.ToUInt32(crc, 0))
|
||||
|
|
@ -68,7 +70,9 @@ public abstract partial class ArjHeader
|
|||
|
||||
while (true)
|
||||
{
|
||||
int bytesRead = await reader.ReadAsync(buffer, 0, 2, cancellationToken);
|
||||
int bytesRead = await reader
|
||||
.ReadAsync(buffer, 0, 2, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
if (bytesRead < 2)
|
||||
{
|
||||
throw new EndOfStreamException(
|
||||
|
|
@ -83,7 +87,9 @@ public abstract partial class ArjHeader
|
|||
}
|
||||
|
||||
byte[] header = new byte[extHeaderSize];
|
||||
bytesRead = await reader.ReadAsync(header, 0, extHeaderSize, cancellationToken);
|
||||
bytesRead = await reader
|
||||
.ReadAsync(header, 0, extHeaderSize, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
if (bytesRead < extHeaderSize)
|
||||
{
|
||||
throw new EndOfStreamException(
|
||||
|
|
@ -92,7 +98,9 @@ public abstract partial class ArjHeader
|
|||
}
|
||||
|
||||
byte[] crcextended = new byte[4];
|
||||
bytesRead = await reader.ReadAsync(crcextended, 0, 4, cancellationToken);
|
||||
bytesRead = await reader
|
||||
.ReadAsync(crcextended, 0, 4, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
if (bytesRead < 4)
|
||||
{
|
||||
throw new EndOfStreamException(
|
||||
|
|
@ -122,7 +130,7 @@ public abstract partial class ArjHeader
|
|||
)
|
||||
{
|
||||
var bytes = new byte[2];
|
||||
if (await stream.ReadAsync(bytes, 0, 2, cancellationToken) != 2)
|
||||
if (await stream.ReadAsync(bytes, 0, 2, cancellationToken).ConfigureAwait(false) != 2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,10 +11,10 @@ public partial class ArjLocalHeader
|
|||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
var body = await ReadHeaderAsync(stream, cancellationToken);
|
||||
var body = await ReadHeaderAsync(stream, cancellationToken).ConfigureAwait(false);
|
||||
if (body.Length > 0)
|
||||
{
|
||||
await ReadExtendedHeadersAsync(stream, cancellationToken);
|
||||
await ReadExtendedHeadersAsync(stream, cancellationToken).ConfigureAwait(false);
|
||||
var header = LoadFrom(body);
|
||||
header.DataStartPosition = stream.Position;
|
||||
return header;
|
||||
|
|
|
|||
|
|
@ -27,8 +27,8 @@ public partial class ArjLocalHeader : ArjHeader
|
|||
public byte FirstChapter { get; set; }
|
||||
public byte LastChapter { get; set; }
|
||||
public long ExtendedFilePosition { get; set; }
|
||||
public DosDateTime DateTimeAccessed { get; set; } = new DosDateTime(0);
|
||||
public DosDateTime DateTimeCreated { get; set; } = new DosDateTime(0);
|
||||
public DosDateTime? DateTimeAccessed { get; set; }
|
||||
public DosDateTime? DateTimeCreated { get; set; }
|
||||
public long OriginalSizeEvenForVolumes { get; set; }
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string Comment { get; set; } = string.Empty;
|
||||
|
|
@ -119,11 +119,9 @@ public partial class ArjLocalHeader : ArjHeader
|
|||
if (headerSize >= R9HdrSize)
|
||||
{
|
||||
rawTimestamp = ReadInt32();
|
||||
DateTimeAccessed =
|
||||
rawTimestamp != 0 ? new DosDateTime(rawTimestamp) : new DosDateTime(0);
|
||||
DateTimeAccessed = rawTimestamp != 0 ? new DosDateTime(rawTimestamp) : null;
|
||||
rawTimestamp = ReadInt32();
|
||||
DateTimeCreated =
|
||||
rawTimestamp != 0 ? new DosDateTime(rawTimestamp) : new DosDateTime(0);
|
||||
DateTimeCreated = rawTimestamp != 0 ? new DosDateTime(rawTimestamp) : null;
|
||||
OriginalSizeEvenForVolumes = ReadInt32();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ public partial class ArjMainHeader
|
|||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
var body = await ReadHeaderAsync(stream, cancellationToken);
|
||||
await ReadExtendedHeadersAsync(stream, cancellationToken);
|
||||
var body = await ReadHeaderAsync(stream, cancellationToken).ConfigureAwait(false);
|
||||
await ReadExtendedHeadersAsync(stream, cancellationToken).ConfigureAwait(false);
|
||||
return LoadFrom(body);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using SharpCompress.Common.Options;
|
||||
|
||||
namespace SharpCompress.Common;
|
||||
|
||||
|
|
@ -87,4 +88,14 @@ public abstract class Entry : IEntry
|
|||
/// Entry file attribute.
|
||||
/// </summary>
|
||||
public virtual int? Attrib => throw new NotImplementedException();
|
||||
|
||||
/// <summary>
|
||||
/// The options used when opening this entry's source (reader or archive).
|
||||
/// </summary>
|
||||
public IReaderOptions Options { get; protected set; }
|
||||
|
||||
protected Entry(IReaderOptions readerOptions)
|
||||
{
|
||||
Options = readerOptions;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ using System;
|
|||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Readers;
|
||||
|
||||
namespace SharpCompress.Common;
|
||||
|
||||
|
|
@ -10,8 +11,7 @@ internal static partial class ExtractionMethods
|
|||
public static async ValueTask WriteEntryToDirectoryAsync(
|
||||
IEntry entry,
|
||||
string destinationDirectory,
|
||||
ExtractionOptions? options,
|
||||
Func<string, ExtractionOptions?, CancellationToken, ValueTask> writeAsync,
|
||||
Func<string, CancellationToken, ValueTask> writeAsync,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
|
|
@ -34,11 +34,9 @@ internal static partial class ExtractionMethods
|
|||
);
|
||||
}
|
||||
|
||||
options ??= new ExtractionOptions() { Overwrite = true };
|
||||
|
||||
var file = Path.GetFileName(entry.Key.NotNull("Entry Key is null")).NotNull("File is null");
|
||||
file = Utility.ReplaceInvalidFileNameChars(file);
|
||||
if (options.ExtractFullPath)
|
||||
if (entry.Options.ExtractFullPath)
|
||||
{
|
||||
var folder = Path.GetDirectoryName(entry.Key.NotNull("Entry Key is null"))
|
||||
.NotNull("Directory is null");
|
||||
|
|
@ -72,9 +70,9 @@ internal static partial class ExtractionMethods
|
|||
"Entry is trying to write a file outside of the destination directory."
|
||||
);
|
||||
}
|
||||
await writeAsync(destinationFileName, options, cancellationToken).ConfigureAwait(false);
|
||||
await writeAsync(destinationFileName, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
else if (options.ExtractFullPath && !Directory.Exists(destinationFileName))
|
||||
else if (entry.Options.ExtractFullPath && !Directory.Exists(destinationFileName))
|
||||
{
|
||||
Directory.CreateDirectory(destinationFileName);
|
||||
}
|
||||
|
|
@ -83,36 +81,34 @@ internal static partial class ExtractionMethods
|
|||
public static async ValueTask WriteEntryToFileAsync(
|
||||
IEntry entry,
|
||||
string destinationFileName,
|
||||
ExtractionOptions? options,
|
||||
Func<string, FileMode, CancellationToken, ValueTask> openAndWriteAsync,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
if (entry.LinkTarget != null)
|
||||
{
|
||||
if (options?.SymbolicLinkHandler is not null)
|
||||
if (entry.Options.SymbolicLinkHandler is not null)
|
||||
{
|
||||
options.SymbolicLinkHandler(destinationFileName, entry.LinkTarget);
|
||||
entry.Options.SymbolicLinkHandler(destinationFileName, entry.LinkTarget);
|
||||
}
|
||||
else
|
||||
{
|
||||
ExtractionOptions.DefaultSymbolicLinkHandler(destinationFileName, entry.LinkTarget);
|
||||
ReaderOptions.DefaultSymbolicLinkHandler(destinationFileName, entry.LinkTarget);
|
||||
}
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
var fm = FileMode.Create;
|
||||
options ??= new ExtractionOptions() { Overwrite = true };
|
||||
|
||||
if (!options.Overwrite)
|
||||
if (!entry.Options.Overwrite)
|
||||
{
|
||||
fm = FileMode.CreateNew;
|
||||
}
|
||||
|
||||
await openAndWriteAsync(destinationFileName, fm, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
entry.PreserveExtractionOptions(destinationFileName, options);
|
||||
entry.PreserveExtractionOptions(destinationFileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ using System.IO;
|
|||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Readers;
|
||||
|
||||
namespace SharpCompress.Common;
|
||||
|
||||
|
|
@ -23,8 +24,7 @@ internal static partial class ExtractionMethods
|
|||
public static void WriteEntryToDirectory(
|
||||
IEntry entry,
|
||||
string destinationDirectory,
|
||||
ExtractionOptions? options,
|
||||
Action<string, ExtractionOptions?> write
|
||||
Action<string> write
|
||||
)
|
||||
{
|
||||
string destinationFileName;
|
||||
|
|
@ -46,11 +46,9 @@ internal static partial class ExtractionMethods
|
|||
);
|
||||
}
|
||||
|
||||
options ??= new ExtractionOptions() { Overwrite = true };
|
||||
|
||||
var file = Path.GetFileName(entry.Key.NotNull("Entry Key is null")).NotNull("File is null");
|
||||
file = Utility.ReplaceInvalidFileNameChars(file);
|
||||
if (options.ExtractFullPath)
|
||||
if (entry.Options.ExtractFullPath)
|
||||
{
|
||||
var folder = Path.GetDirectoryName(entry.Key.NotNull("Entry Key is null"))
|
||||
.NotNull("Directory is null");
|
||||
|
|
@ -84,9 +82,9 @@ internal static partial class ExtractionMethods
|
|||
"Entry is trying to write a file outside of the destination directory."
|
||||
);
|
||||
}
|
||||
write(destinationFileName, options);
|
||||
write(destinationFileName);
|
||||
}
|
||||
else if (options.ExtractFullPath && !Directory.Exists(destinationFileName))
|
||||
else if (entry.Options.ExtractFullPath && !Directory.Exists(destinationFileName))
|
||||
{
|
||||
Directory.CreateDirectory(destinationFileName);
|
||||
}
|
||||
|
|
@ -95,34 +93,32 @@ internal static partial class ExtractionMethods
|
|||
public static void WriteEntryToFile(
|
||||
IEntry entry,
|
||||
string destinationFileName,
|
||||
ExtractionOptions? options,
|
||||
Action<string, FileMode> openAndWrite
|
||||
)
|
||||
{
|
||||
if (entry.LinkTarget != null)
|
||||
{
|
||||
if (options?.SymbolicLinkHandler is not null)
|
||||
if (entry.Options.SymbolicLinkHandler is not null)
|
||||
{
|
||||
options.SymbolicLinkHandler(destinationFileName, entry.LinkTarget);
|
||||
entry.Options.SymbolicLinkHandler(destinationFileName, entry.LinkTarget);
|
||||
}
|
||||
else
|
||||
{
|
||||
ExtractionOptions.DefaultSymbolicLinkHandler(destinationFileName, entry.LinkTarget);
|
||||
ReaderOptions.DefaultSymbolicLinkHandler(destinationFileName, entry.LinkTarget);
|
||||
}
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
var fm = FileMode.Create;
|
||||
options ??= new ExtractionOptions() { Overwrite = true };
|
||||
|
||||
if (!options.Overwrite)
|
||||
if (!entry.Options.Overwrite)
|
||||
{
|
||||
fm = FileMode.CreateNew;
|
||||
}
|
||||
|
||||
openAndWrite(destinationFileName, fm);
|
||||
entry.PreserveExtractionOptions(destinationFileName, options);
|
||||
entry.PreserveExtractionOptions(destinationFileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,108 +0,0 @@
|
|||
using System;
|
||||
|
||||
namespace SharpCompress.Common;
|
||||
|
||||
/// <summary>
|
||||
/// Options for configuring extraction behavior when extracting archive entries.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This class is immutable. Use the <c>with</c> expression to create modified copies:
|
||||
/// <code>
|
||||
/// var options = new ExtractionOptions { Overwrite = false };
|
||||
/// options = options with { PreserveFileTime = true };
|
||||
/// </code>
|
||||
/// </remarks>
|
||||
public sealed record ExtractionOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Overwrite target if it exists.
|
||||
/// <para><b>Breaking change:</b> Default changed from false to true in version 0.40.0.</para>
|
||||
/// </summary>
|
||||
public bool Overwrite { get; init; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Extract with internal directory structure.
|
||||
/// <para><b>Breaking change:</b> Default changed from false to true in version 0.40.0.</para>
|
||||
/// </summary>
|
||||
public bool ExtractFullPath { get; init; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Preserve file time.
|
||||
/// <para><b>Breaking change:</b> Default changed from false to true in version 0.40.0.</para>
|
||||
/// </summary>
|
||||
public bool PreserveFileTime { get; init; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Preserve windows file attributes.
|
||||
/// </summary>
|
||||
public bool PreserveAttributes { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Delegate for writing symbolic links to disk.
|
||||
/// The first parameter is the source path (where the symlink is created).
|
||||
/// The second parameter is the target path (what the symlink refers to).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <b>Breaking change:</b> Changed from field to init-only property in version 0.40.0.
|
||||
/// The default handler logs a warning message.
|
||||
/// </remarks>
|
||||
public Action<string, string>? SymbolicLinkHandler { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new ExtractionOptions instance with default values.
|
||||
/// </summary>
|
||||
public ExtractionOptions() { }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new ExtractionOptions instance with the specified overwrite behavior.
|
||||
/// </summary>
|
||||
/// <param name="overwrite">Whether to overwrite existing files.</param>
|
||||
public ExtractionOptions(bool overwrite)
|
||||
{
|
||||
Overwrite = overwrite;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new ExtractionOptions instance with the specified extraction path and overwrite behavior.
|
||||
/// </summary>
|
||||
/// <param name="extractFullPath">Whether to preserve directory structure.</param>
|
||||
/// <param name="overwrite">Whether to overwrite existing files.</param>
|
||||
public ExtractionOptions(bool extractFullPath, bool overwrite)
|
||||
{
|
||||
ExtractFullPath = extractFullPath;
|
||||
Overwrite = overwrite;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new ExtractionOptions instance with the specified extraction path, overwrite behavior, and file time preservation.
|
||||
/// </summary>
|
||||
/// <param name="extractFullPath">Whether to preserve directory structure.</param>
|
||||
/// <param name="overwrite">Whether to overwrite existing files.</param>
|
||||
/// <param name="preserveFileTime">Whether to preserve file modification times.</param>
|
||||
public ExtractionOptions(bool extractFullPath, bool overwrite, bool preserveFileTime)
|
||||
{
|
||||
ExtractFullPath = extractFullPath;
|
||||
Overwrite = overwrite;
|
||||
PreserveFileTime = preserveFileTime;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an ExtractionOptions instance configured for safe extraction (no overwrite).
|
||||
/// </summary>
|
||||
public static ExtractionOptions SafeExtract => new(overwrite: false);
|
||||
|
||||
/// <summary>
|
||||
/// Gets an ExtractionOptions instance configured for flat extraction (no directory structure).
|
||||
/// </summary>
|
||||
public static ExtractionOptions FlatExtract => new(extractFullPath: false, overwrite: true);
|
||||
|
||||
/// <summary>
|
||||
/// Default symbolic link handler that logs a warning message.
|
||||
/// </summary>
|
||||
public static void DefaultSymbolicLinkHandler(string sourcePath, string targetPath)
|
||||
{
|
||||
Console.WriteLine(
|
||||
$"Could not write symlink {sourcePath} -> {targetPath}, for more information please see https://github.com/dotnet/runtime/issues/24271"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -12,11 +12,8 @@ public partial class GZipEntry
|
|||
)
|
||||
{
|
||||
yield return new GZipEntry(
|
||||
await GZipFilePart.CreateAsync(
|
||||
stream,
|
||||
options.ArchiveEncoding,
|
||||
options.CompressionProviders
|
||||
)
|
||||
await GZipFilePart.CreateAsync(stream, options.ArchiveEncoding).ConfigureAwait(false),
|
||||
options
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SharpCompress.Common.Options;
|
||||
using SharpCompress.Readers;
|
||||
|
||||
namespace SharpCompress.Common.GZip;
|
||||
|
|
@ -9,7 +10,11 @@ public partial class GZipEntry : Entry
|
|||
{
|
||||
private readonly GZipFilePart? _filePart;
|
||||
|
||||
internal GZipEntry(GZipFilePart? filePart) => _filePart = filePart;
|
||||
internal GZipEntry(GZipFilePart? filePart, IReaderOptions readerOptions)
|
||||
: base(readerOptions)
|
||||
{
|
||||
_filePart = filePart;
|
||||
}
|
||||
|
||||
public override CompressionType CompressionType => CompressionType.GZip;
|
||||
|
||||
|
|
@ -41,9 +46,7 @@ public partial class GZipEntry : Entry
|
|||
|
||||
internal static IEnumerable<GZipEntry> GetEntries(Stream stream, ReaderOptions options)
|
||||
{
|
||||
yield return new GZipEntry(
|
||||
GZipFilePart.Create(stream, options.ArchiveEncoding, options.CompressionProviders)
|
||||
);
|
||||
yield return new GZipEntry(GZipFilePart.Create(stream, options.ArchiveEncoding), options);
|
||||
}
|
||||
|
||||
// Async methods moved to GZipEntry.Async.cs
|
||||
|
|
|
|||
|
|
@ -30,12 +30,12 @@ internal sealed partial class GZipFilePart
|
|||
{
|
||||
var part = new GZipFilePart(stream, archiveEncoding, compressionProviders);
|
||||
|
||||
await part.ReadAndValidateGzipHeaderAsync(cancellationToken);
|
||||
await part.ReadAndValidateGzipHeaderAsync(cancellationToken).ConfigureAwait(false);
|
||||
if (stream.CanSeek)
|
||||
{
|
||||
var position = stream.Position;
|
||||
stream.Position = stream.Length - 8;
|
||||
await part.ReadTrailerAsync(cancellationToken);
|
||||
await part.ReadTrailerAsync(cancellationToken).ConfigureAwait(false);
|
||||
stream.Position = position;
|
||||
part.EntryStartPosition = position;
|
||||
}
|
||||
|
|
@ -52,7 +52,7 @@ internal sealed partial class GZipFilePart
|
|||
{
|
||||
// Read and potentially verify the GZIP trailer: CRC32 and size mod 2^32
|
||||
var trailer = new byte[8];
|
||||
_ = await _stream.ReadFullyAsync(trailer, 0, 8, cancellationToken);
|
||||
_ = await _stream.ReadFullyAsync(trailer, 0, 8, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
Crc = BinaryPrimitives.ReadUInt32LittleEndian(trailer);
|
||||
UncompressedSize = BinaryPrimitives.ReadUInt32LittleEndian(trailer.AsSpan().Slice(4));
|
||||
|
|
@ -64,7 +64,7 @@ internal sealed partial class GZipFilePart
|
|||
{
|
||||
// read the header on the first read
|
||||
var header = new byte[10];
|
||||
var n = await _stream.ReadAsync(header, 0, 10, cancellationToken);
|
||||
var n = await _stream.ReadAsync(header, 0, 10, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
// workitem 8501: handle edge case (decompress empty stream)
|
||||
if (n == 0)
|
||||
|
|
@ -88,28 +88,29 @@ internal sealed partial class GZipFilePart
|
|||
{
|
||||
// read and discard extra field
|
||||
var lengthField = new byte[2];
|
||||
_ = await _stream.ReadAsync(lengthField, 0, 2, cancellationToken);
|
||||
_ = await _stream.ReadAsync(lengthField, 0, 2, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var extraLength = (short)(lengthField[0] + (lengthField[1] * 256));
|
||||
var extra = new byte[extraLength];
|
||||
|
||||
if (!await _stream.ReadFullyAsync(extra, cancellationToken))
|
||||
if (!await _stream.ReadFullyAsync(extra, cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
throw new ZlibException("Unexpected end-of-file reading GZIP header.");
|
||||
}
|
||||
}
|
||||
if ((header[3] & 0x08) == 0x08)
|
||||
{
|
||||
_name = await ReadZeroTerminatedStringAsync(_stream, cancellationToken);
|
||||
_name = await ReadZeroTerminatedStringAsync(_stream, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
if ((header[3] & 0x10) == 0x010)
|
||||
{
|
||||
await ReadZeroTerminatedStringAsync(_stream, cancellationToken);
|
||||
await ReadZeroTerminatedStringAsync(_stream, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
if ((header[3] & 0x02) == 0x02)
|
||||
{
|
||||
var buf = new byte[1];
|
||||
_ = await _stream.ReadAsync(buf, 0, 1, cancellationToken); // CRC16, ignore
|
||||
_ = await _stream.ReadAsync(buf, 0, 1, cancellationToken).ConfigureAwait(false); // CRC16, ignore
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -124,7 +125,7 @@ internal sealed partial class GZipFilePart
|
|||
do
|
||||
{
|
||||
// workitem 7740
|
||||
var n = await stream.ReadAsync(buf1, 0, 1, cancellationToken);
|
||||
var n = await stream.ReadAsync(buf1, 0, 1, cancellationToken).ConfigureAwait(false);
|
||||
if (n != 1)
|
||||
{
|
||||
throw new ZlibException("Unexpected EOF reading GZIP header.");
|
||||
|
|
|
|||
|
|
@ -4,13 +4,9 @@ namespace SharpCompress.Common;
|
|||
|
||||
internal static class EntryExtensions
|
||||
{
|
||||
internal static void PreserveExtractionOptions(
|
||||
this IEntry entry,
|
||||
string destinationFileName,
|
||||
ExtractionOptions options
|
||||
)
|
||||
internal static void PreserveExtractionOptions(this IEntry entry, string destinationFileName)
|
||||
{
|
||||
if (options.PreserveFileTime || options.PreserveAttributes)
|
||||
if (entry.Options.PreserveFileTime || entry.Options.PreserveAttributes)
|
||||
{
|
||||
var nf = new FileInfo(destinationFileName);
|
||||
if (!nf.Exists)
|
||||
|
|
@ -19,7 +15,7 @@ internal static class EntryExtensions
|
|||
}
|
||||
|
||||
// update file time to original packed time
|
||||
if (options.PreserveFileTime)
|
||||
if (entry.Options.PreserveFileTime)
|
||||
{
|
||||
if (entry.CreatedTime.HasValue)
|
||||
{
|
||||
|
|
@ -37,7 +33,7 @@ internal static class EntryExtensions
|
|||
}
|
||||
}
|
||||
|
||||
if (options.PreserveAttributes)
|
||||
if (entry.Options.PreserveAttributes)
|
||||
{
|
||||
if (entry.Attrib.HasValue)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using SharpCompress.Common.Options;
|
||||
|
||||
namespace SharpCompress.Common;
|
||||
|
||||
|
|
@ -21,4 +22,9 @@ public interface IEntry
|
|||
DateTime? LastModifiedTime { get; }
|
||||
long Size { get; }
|
||||
int? Attrib { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The options used when opening this entry's source (reader or archive).
|
||||
/// </summary>
|
||||
IReaderOptions Options { get; }
|
||||
}
|
||||
|
|
|
|||
22
src/SharpCompress/Common/Lzw/LzwEntry.Async.cs
Normal file
22
src/SharpCompress/Common/Lzw/LzwEntry.Async.cs
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using SharpCompress.Readers;
|
||||
|
||||
namespace SharpCompress.Common.Lzw;
|
||||
|
||||
public partial class LzwEntry
|
||||
{
|
||||
internal static async IAsyncEnumerable<LzwEntry> GetEntriesAsync(
|
||||
Stream stream,
|
||||
ReaderOptions options,
|
||||
[EnumeratorCancellation] CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
yield return new LzwEntry(
|
||||
await LzwFilePart.CreateAsync(stream, options.ArchiveEncoding, cancellationToken),
|
||||
options
|
||||
);
|
||||
}
|
||||
}
|
||||
53
src/SharpCompress/Common/Lzw/LzwEntry.cs
Normal file
53
src/SharpCompress/Common/Lzw/LzwEntry.cs
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SharpCompress.Common.Options;
|
||||
using SharpCompress.Readers;
|
||||
|
||||
namespace SharpCompress.Common.Lzw;
|
||||
|
||||
public partial class LzwEntry : Entry
|
||||
{
|
||||
private readonly LzwFilePart? _filePart;
|
||||
|
||||
internal LzwEntry(LzwFilePart? filePart, IReaderOptions readerOptions)
|
||||
: base(readerOptions)
|
||||
{
|
||||
_filePart = filePart;
|
||||
}
|
||||
|
||||
public override CompressionType CompressionType => CompressionType.Lzw;
|
||||
|
||||
public override long Crc => 0;
|
||||
|
||||
public override string? Key => _filePart?.FilePartName;
|
||||
|
||||
public override string? LinkTarget => null;
|
||||
|
||||
public override long CompressedSize => 0;
|
||||
|
||||
public override long Size => 0;
|
||||
|
||||
public override DateTime? LastModifiedTime => null;
|
||||
|
||||
public override DateTime? CreatedTime => null;
|
||||
|
||||
public override DateTime? LastAccessedTime => null;
|
||||
|
||||
public override DateTime? ArchivedTime => null;
|
||||
|
||||
public override bool IsEncrypted => false;
|
||||
|
||||
public override bool IsDirectory => false;
|
||||
|
||||
public override bool IsSplitAfter => false;
|
||||
|
||||
internal override IEnumerable<FilePart> Parts => _filePart.Empty();
|
||||
|
||||
internal static IEnumerable<LzwEntry> GetEntries(Stream stream, ReaderOptions options)
|
||||
{
|
||||
yield return new LzwEntry(LzwFilePart.Create(stream, options.ArchiveEncoding), options);
|
||||
}
|
||||
|
||||
// Async methods moved to LzwEntry.Async.cs
|
||||
}
|
||||
23
src/SharpCompress/Common/Lzw/LzwFilePart.Async.cs
Normal file
23
src/SharpCompress/Common/Lzw/LzwFilePart.Async.cs
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SharpCompress.Common.Lzw;
|
||||
|
||||
internal sealed partial class LzwFilePart
|
||||
{
|
||||
internal static async ValueTask<LzwFilePart> CreateAsync(
|
||||
Stream stream,
|
||||
IArchiveEncoding archiveEncoding,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var part = new LzwFilePart(stream, archiveEncoding);
|
||||
|
||||
// For non-seekable streams, we can't track position, so use 0 since the stream will be
|
||||
// read sequentially from its current position.
|
||||
part.EntryStartPosition = stream.CanSeek ? stream.Position : 0;
|
||||
return part;
|
||||
}
|
||||
}
|
||||
60
src/SharpCompress/Common/Lzw/LzwFilePart.cs
Normal file
60
src/SharpCompress/Common/Lzw/LzwFilePart.cs
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
using System.IO;
|
||||
using SharpCompress.Compressors.Lzw;
|
||||
|
||||
namespace SharpCompress.Common.Lzw;
|
||||
|
||||
internal sealed partial class LzwFilePart : FilePart
|
||||
{
|
||||
private readonly Stream _stream;
|
||||
private readonly string? _name;
|
||||
|
||||
internal static LzwFilePart Create(Stream stream, IArchiveEncoding archiveEncoding)
|
||||
{
|
||||
var part = new LzwFilePart(stream, archiveEncoding);
|
||||
|
||||
// For non-seekable streams, we can't track position, so use 0 since the stream will be
|
||||
// read sequentially from its current position.
|
||||
part.EntryStartPosition = stream.CanSeek ? stream.Position : 0;
|
||||
return part;
|
||||
}
|
||||
|
||||
private LzwFilePart(Stream stream, IArchiveEncoding archiveEncoding)
|
||||
: base(archiveEncoding)
|
||||
{
|
||||
_stream = stream;
|
||||
_name = DeriveFileName(stream);
|
||||
}
|
||||
|
||||
internal long EntryStartPosition { get; private set; }
|
||||
|
||||
internal override string? FilePartName => _name;
|
||||
|
||||
internal override Stream GetCompressedStream() =>
|
||||
new LzwStream(_stream) { IsStreamOwner = false };
|
||||
|
||||
internal override Stream GetRawStream() => _stream;
|
||||
|
||||
private static string? DeriveFileName(Stream stream)
|
||||
{
|
||||
// Unwrap SharpCompressStream to get to the underlying FileStream
|
||||
var unwrappedStream = stream;
|
||||
if (stream is SharpCompress.IO.IStreamStack streamStack)
|
||||
{
|
||||
unwrappedStream = streamStack.BaseStream();
|
||||
}
|
||||
|
||||
// Try to derive filename from FileStream
|
||||
if (unwrappedStream is FileStream fileStream && !string.IsNullOrEmpty(fileStream.Name))
|
||||
{
|
||||
var fileName = Path.GetFileName(fileStream.Name);
|
||||
// Strip .Z extension if present
|
||||
if (fileName.EndsWith(".Z", System.StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return fileName.Substring(0, fileName.Length - 2);
|
||||
}
|
||||
return fileName;
|
||||
}
|
||||
// Default name for non-file streams
|
||||
return "data";
|
||||
}
|
||||
}
|
||||
17
src/SharpCompress/Common/Lzw/LzwVolume.cs
Normal file
17
src/SharpCompress/Common/Lzw/LzwVolume.cs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
using System.IO;
|
||||
using SharpCompress.Readers;
|
||||
|
||||
namespace SharpCompress.Common.Lzw;
|
||||
|
||||
public class LzwVolume : Volume
|
||||
{
|
||||
public LzwVolume(Stream stream, ReaderOptions? options, int index)
|
||||
: base(stream, options, index) { }
|
||||
|
||||
public LzwVolume(FileInfo fileInfo, ReaderOptions options)
|
||||
: base(fileInfo.OpenRead(), options with { LeaveStreamOpen = false }) { }
|
||||
|
||||
public override bool IsFirstVolume => true;
|
||||
|
||||
public override bool IsMultiVolume => false;
|
||||
}
|
||||
39
src/SharpCompress/Common/Options/IExtractionOptions.cs
Normal file
39
src/SharpCompress/Common/Options/IExtractionOptions.cs
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
using System;
|
||||
|
||||
namespace SharpCompress.Common.Options;
|
||||
|
||||
/// <summary>
|
||||
/// Options for configuring extraction behavior when extracting archive entries to the filesystem.
|
||||
/// </summary>
|
||||
public interface IExtractionOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Overwrite target if it exists.
|
||||
/// <para><b>Breaking change:</b> Default changed from false to true in version 0.40.0.</para>
|
||||
/// </summary>
|
||||
bool Overwrite { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Extract with internal directory structure.
|
||||
/// <para><b>Breaking change:</b> Default changed from false to true in version 0.40.0.</para>
|
||||
/// </summary>
|
||||
bool ExtractFullPath { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Preserve file time.
|
||||
/// <para><b>Breaking change:</b> Default changed from false to true in version 0.40.0.</para>
|
||||
/// </summary>
|
||||
bool PreserveFileTime { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Preserve windows file attributes.
|
||||
/// </summary>
|
||||
bool PreserveAttributes { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Delegate for writing symbolic links to disk.
|
||||
/// The first parameter is the source path (where the symlink is created).
|
||||
/// The second parameter is the target path (what the symlink refers to).
|
||||
/// </summary>
|
||||
Action<string, string>? SymbolicLinkHandler { get; init; }
|
||||
}
|
||||
|
|
@ -2,10 +2,11 @@ using SharpCompress.Compressors;
|
|||
|
||||
namespace SharpCompress.Common.Options;
|
||||
|
||||
/// <summary>
|
||||
/// Options for configuring reader behavior when opening archives.
|
||||
/// </summary>
|
||||
public interface IReaderOptions : IStreamOptions, IEncodingOptions, IProgressOptions
|
||||
public interface IReaderOptions
|
||||
: IStreamOptions,
|
||||
IEncodingOptions,
|
||||
IProgressOptions,
|
||||
IExtractionOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Look for RarArchive (Check for self-extracting archives or cases where RarArchive isn't at the start of the file)
|
||||
|
|
|
|||
|
|
@ -26,7 +26,9 @@ internal sealed class AsyncRarCryptoBinaryReader : AsyncRarCrcBinaryReader
|
|||
var binary = new AsyncRarCryptoBinaryReader(stream);
|
||||
if (salt == null)
|
||||
{
|
||||
salt = await binary.ReadBytesAsyncBase(EncryptionConstV5.SIZE_SALT30);
|
||||
salt = await binary
|
||||
.ReadBytesAsyncBase(EncryptionConstV5.SIZE_SALT30)
|
||||
.ConfigureAwait(false);
|
||||
binary._readCount += EncryptionConstV5.SIZE_SALT30;
|
||||
}
|
||||
binary._rijndael = new BlockTransformer(cryptKey.Transformer(salt));
|
||||
|
|
|
|||
|
|
@ -27,6 +27,6 @@ internal sealed partial class ArchiveCryptHeader
|
|||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
CryptInfo = await Rar5CryptoInfo.CreateAsync(reader, false);
|
||||
CryptInfo = await Rar5CryptoInfo.CreateAsync(reader, false).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -329,7 +329,9 @@ internal partial class FileHeader
|
|||
|
||||
if (HasFlag(FileFlagsV4.SALT))
|
||||
{
|
||||
R4Salt = await reader.ReadBytesAsync(EncryptionConstV5.SIZE_SALT30, cancellationToken);
|
||||
R4Salt = await reader
|
||||
.ReadBytesAsync(EncryptionConstV5.SIZE_SALT30, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
if (HasFlag(FileFlagsV4.EXT_TIME))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -64,19 +64,19 @@ public partial class RarHeaderFactory
|
|||
|
||||
if (_isRar5 && _cryptInfo != null)
|
||||
{
|
||||
await _cryptInfo.ReadInitVAsync(new AsyncMarkingBinaryReader(stream));
|
||||
await _cryptInfo
|
||||
.ReadInitVAsync(new AsyncMarkingBinaryReader(stream))
|
||||
.ConfigureAwait(false);
|
||||
var _headerKey = new CryptKey5(Options.Password!, _cryptInfo);
|
||||
|
||||
reader = await AsyncRarCryptoBinaryReader.Create(
|
||||
stream,
|
||||
_headerKey,
|
||||
_cryptInfo.Salt
|
||||
);
|
||||
reader = await AsyncRarCryptoBinaryReader
|
||||
.Create(stream, _headerKey, _cryptInfo.Salt)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
var key = new CryptKey3(Options.Password);
|
||||
reader = await AsyncRarCryptoBinaryReader.Create(stream, key);
|
||||
reader = await AsyncRarCryptoBinaryReader.Create(stream, key).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -57,47 +57,46 @@ internal class Rar5CryptoInfo
|
|||
)
|
||||
{
|
||||
var cryptoInfo = new Rar5CryptoInfo();
|
||||
var cryptVersion = await reader.ReadRarVIntUInt32Async(
|
||||
cancellationToken: CancellationToken.None
|
||||
);
|
||||
var cryptVersion = await reader
|
||||
.ReadRarVIntUInt32Async(cancellationToken: CancellationToken.None)
|
||||
.ConfigureAwait(false);
|
||||
if (cryptVersion > EncryptionConstV5.VERSION)
|
||||
{
|
||||
throw new CryptographicException($"Unsupported crypto version of {cryptVersion}");
|
||||
}
|
||||
var encryptionFlags = await reader.ReadRarVIntUInt32Async(
|
||||
cancellationToken: CancellationToken.None
|
||||
);
|
||||
var encryptionFlags = await reader
|
||||
.ReadRarVIntUInt32Async(cancellationToken: CancellationToken.None)
|
||||
.ConfigureAwait(false);
|
||||
cryptoInfo.UsePswCheck = FlagUtility.HasFlag(
|
||||
encryptionFlags,
|
||||
EncryptionFlagsV5.CHFL_CRYPT_PSWCHECK
|
||||
);
|
||||
cryptoInfo.LG2Count = (int)
|
||||
await reader.ReadRarVIntUInt32Async(cancellationToken: CancellationToken.None);
|
||||
await reader
|
||||
.ReadRarVIntUInt32Async(cancellationToken: CancellationToken.None)
|
||||
.ConfigureAwait(false);
|
||||
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
|
||||
);
|
||||
cryptoInfo.Salt = await reader
|
||||
.ReadBytesAsync(EncryptionConstV5.SIZE_SALT50, CancellationToken.None)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (readInitV)
|
||||
{
|
||||
await cryptoInfo.ReadInitVAsync(reader);
|
||||
await cryptoInfo.ReadInitVAsync(reader).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (cryptoInfo.UsePswCheck)
|
||||
{
|
||||
cryptoInfo.PswCheck = await reader.ReadBytesAsync(
|
||||
EncryptionConstV5.SIZE_PSWCHECK,
|
||||
CancellationToken.None
|
||||
);
|
||||
var _pswCheckCsm = await reader.ReadBytesAsync(
|
||||
EncryptionConstV5.SIZE_PSWCHECK_CSUM,
|
||||
CancellationToken.None
|
||||
);
|
||||
cryptoInfo.PswCheck = await reader
|
||||
.ReadBytesAsync(EncryptionConstV5.SIZE_PSWCHECK, CancellationToken.None)
|
||||
.ConfigureAwait(false);
|
||||
var _pswCheckCsm = await reader
|
||||
.ReadBytesAsync(EncryptionConstV5.SIZE_PSWCHECK_CSUM, CancellationToken.None)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
var sha = SHA256.Create();
|
||||
cryptoInfo.UsePswCheck = sha.ComputeHash(cryptoInfo.PswCheck)
|
||||
|
|
@ -111,7 +110,9 @@ internal class Rar5CryptoInfo
|
|||
InitV = reader.ReadBytes(EncryptionConstV5.SIZE_INITV);
|
||||
|
||||
public async ValueTask ReadInitVAsync(AsyncMarkingBinaryReader reader) =>
|
||||
InitV = await reader.ReadBytesAsync(EncryptionConstV5.SIZE_INITV, CancellationToken.None);
|
||||
InitV = await reader
|
||||
.ReadBytesAsync(EncryptionConstV5.SIZE_INITV, CancellationToken.None)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
public bool UsePswCheck = false;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using SharpCompress.Common.Options;
|
||||
using SharpCompress.Common.Rar.Headers;
|
||||
|
||||
namespace SharpCompress.Common.Rar;
|
||||
|
|
@ -7,6 +8,9 @@ public abstract class RarEntry : Entry
|
|||
{
|
||||
internal abstract FileHeader FileHeader { get; }
|
||||
|
||||
protected RarEntry(IReaderOptions readerOptions)
|
||||
: base(readerOptions) { }
|
||||
|
||||
/// <summary>
|
||||
/// As the V2017 port isn't complete, add this check to use the legacy Rar code.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -118,7 +118,8 @@ public abstract class RarVolume : Volume
|
|||
var buffer = new byte[fh.CompressedSize];
|
||||
await fh
|
||||
.PackedStream.NotNull()
|
||||
.ReadFullyAsync(buffer, cancellationToken);
|
||||
.ReadFullyAsync(buffer, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
Comment = Encoding.UTF8.GetString(buffer, 0, buffer.Length - 1);
|
||||
}
|
||||
}
|
||||
|
|
@ -184,7 +185,7 @@ public abstract class RarVolume : Volume
|
|||
|
||||
public async ValueTask<bool> IsSolidArchiveAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
await EnsureArchiveHeaderLoadedAsync(cancellationToken);
|
||||
await EnsureArchiveHeaderLoadedAsync(cancellationToken).ConfigureAwait(false);
|
||||
return ArchiveHeader?.IsSolid ?? false;
|
||||
}
|
||||
|
||||
|
|
@ -248,7 +249,7 @@ public abstract class RarVolume : Volume
|
|||
}
|
||||
|
||||
// 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();
|
||||
await GetVolumeFilePartsAsync(cancellationToken).FirstAsync().ConfigureAwait(false);
|
||||
Stream.Position = 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ internal sealed partial class ArchiveReader
|
|||
{
|
||||
// TODO: Check Signature!
|
||||
_header = new byte[0x20];
|
||||
await stream.ReadExactAsync(_header, 0, 0x20, cancellationToken);
|
||||
await stream.ReadExactAsync(_header, 0, 0x20, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (
|
||||
!lookForHeader
|
||||
|
|
@ -107,7 +107,9 @@ internal sealed partial class ArchiveReader
|
|||
_stream.Seek(nextHeaderOffset, SeekOrigin.Current);
|
||||
|
||||
var header = new byte[nextHeaderSize];
|
||||
await _stream.ReadExactAsync(header, 0, header.Length, cancellationToken);
|
||||
await _stream
|
||||
.ReadExactAsync(header, 0, header.Length, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (Crc.Finish(Crc.Update(Crc.INIT_CRC, header, 0, header.Length)) != nextHeaderCrc)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,11 +1,16 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using SharpCompress.Common.Options;
|
||||
|
||||
namespace SharpCompress.Common.SevenZip;
|
||||
|
||||
public class SevenZipEntry : Entry
|
||||
{
|
||||
internal SevenZipEntry(SevenZipFilePart filePart) => FilePart = filePart;
|
||||
internal SevenZipEntry(SevenZipFilePart filePart, IReaderOptions readerOptions)
|
||||
: base(readerOptions)
|
||||
{
|
||||
FilePart = filePart;
|
||||
}
|
||||
|
||||
internal SevenZipFilePart FilePart { get; }
|
||||
|
||||
|
|
|
|||
|
|
@ -68,12 +68,9 @@ internal class SevenZipFilePart : FilePart
|
|||
{
|
||||
return Stream.Null;
|
||||
}
|
||||
var folderStream = await _database.GetFolderStreamAsync(
|
||||
_stream,
|
||||
Folder!,
|
||||
_database.PasswordProvider,
|
||||
cancellationToken
|
||||
);
|
||||
var folderStream = await _database
|
||||
.GetFolderStreamAsync(_stream, Folder!, _database.PasswordProvider, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
var firstFileIndex = _database._folderStartFileIndex[_database._folders.IndexOf(Folder!)];
|
||||
var skipCount = Index - firstFileIndex;
|
||||
|
|
@ -84,7 +81,7 @@ internal class SevenZipFilePart : FilePart
|
|||
}
|
||||
if (skipSize > 0)
|
||||
{
|
||||
await folderStream.SkipAsync(skipSize, cancellationToken);
|
||||
await folderStream.SkipAsync(skipSize, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
return new ReadOnlySubStream(folderStream, Header.Size, leaveOpen: false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,10 +20,10 @@ internal sealed partial class TarHeader
|
|||
switch (WriteFormat)
|
||||
{
|
||||
case TarHeaderWriteFormat.GNU_TAR_LONG_LINK:
|
||||
await WriteGnuTarLongLinkAsync(output, cancellationToken);
|
||||
await WriteGnuTarLongLinkAsync(output, cancellationToken).ConfigureAwait(false);
|
||||
break;
|
||||
case TarHeaderWriteFormat.USTAR:
|
||||
await WriteUstarAsync(output, cancellationToken);
|
||||
await WriteUstarAsync(output, cancellationToken).ConfigureAwait(false);
|
||||
break;
|
||||
default:
|
||||
throw new Exception("This should be impossible...");
|
||||
|
|
@ -162,13 +162,13 @@ internal sealed partial class TarHeader
|
|||
|
||||
if (nameByteCount > 100)
|
||||
{
|
||||
await WriteLongFilenameHeaderAsync(output, cancellationToken);
|
||||
await WriteLongFilenameHeaderAsync(output, cancellationToken).ConfigureAwait(false);
|
||||
Name = ArchiveEncoding.Decode(
|
||||
ArchiveEncoding.Encode(Name.NotNull("Name is null")),
|
||||
0,
|
||||
100 - ArchiveEncoding.GetEncoding().GetMaxByteCount(1)
|
||||
);
|
||||
await WriteGnuTarLongLinkAsync(output, cancellationToken);
|
||||
await WriteGnuTarLongLinkAsync(output, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -203,7 +203,7 @@ internal sealed partial class TarHeader
|
|||
|
||||
do
|
||||
{
|
||||
buffer = await ReadBlockAsync(reader);
|
||||
buffer = await ReadBlockAsync(reader).ConfigureAwait(false);
|
||||
|
||||
if (buffer.Length == 0)
|
||||
{
|
||||
|
|
@ -216,12 +216,12 @@ internal sealed partial class TarHeader
|
|||
// to apply to the header that follows them.
|
||||
if (entryType == EntryType.LongName)
|
||||
{
|
||||
longName = await ReadLongNameAsync(reader, buffer);
|
||||
longName = await ReadLongNameAsync(reader, buffer).ConfigureAwait(false);
|
||||
continue;
|
||||
}
|
||||
else if (entryType == EntryType.LongLink)
|
||||
{
|
||||
longLinkName = await ReadLongNameAsync(reader, buffer);
|
||||
longLinkName = await ReadLongNameAsync(reader, buffer).ConfigureAwait(false);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -282,7 +282,7 @@ internal sealed partial class TarHeader
|
|||
var buffer = ArrayPool<byte>.Shared.Rent(BLOCK_SIZE);
|
||||
try
|
||||
{
|
||||
await reader.ReadBytesAsync(buffer, 0, BLOCK_SIZE);
|
||||
await reader.ReadBytesAsync(buffer, 0, BLOCK_SIZE).ConfigureAwait(false);
|
||||
|
||||
if (buffer.Length != 0 && buffer.Length < BLOCK_SIZE)
|
||||
{
|
||||
|
|
@ -313,7 +313,7 @@ internal sealed partial class TarHeader
|
|||
var nameBytes = ArrayPool<byte>.Shared.Rent(nameLength);
|
||||
try
|
||||
{
|
||||
await reader.ReadBytesAsync(nameBytes, 0, nameLength);
|
||||
await reader.ReadBytesAsync(nameBytes, 0, nameLength).ConfigureAwait(false);
|
||||
var remainingBytesToRead = BLOCK_SIZE - (nameLength % BLOCK_SIZE);
|
||||
|
||||
// Read the rest of the block and discard the data
|
||||
|
|
@ -322,7 +322,9 @@ internal sealed partial class TarHeader
|
|||
var remainingBytes = ArrayPool<byte>.Shared.Rent(remainingBytesToRead);
|
||||
try
|
||||
{
|
||||
await reader.ReadBytesAsync(remainingBytes, 0, remainingBytesToRead);
|
||||
await reader
|
||||
.ReadBytesAsync(remainingBytes, 0, remainingBytesToRead)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SharpCompress.Common.Options;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Common.Tar;
|
||||
|
|
@ -10,7 +11,8 @@ public partial class TarEntry
|
|||
StreamingMode mode,
|
||||
Stream stream,
|
||||
CompressionType compressionType,
|
||||
IArchiveEncoding archiveEncoding
|
||||
IArchiveEncoding archiveEncoding,
|
||||
IReaderOptions readerOptions
|
||||
)
|
||||
{
|
||||
await foreach (
|
||||
|
|
@ -21,11 +23,19 @@ public partial class TarEntry
|
|||
{
|
||||
if (mode == StreamingMode.Seekable)
|
||||
{
|
||||
yield return new TarEntry(new TarFilePart(header, stream), compressionType);
|
||||
yield return new TarEntry(
|
||||
new TarFilePart(header, stream),
|
||||
compressionType,
|
||||
readerOptions
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return new TarEntry(new TarFilePart(header, null), compressionType);
|
||||
yield return new TarEntry(
|
||||
new TarFilePart(header, null),
|
||||
compressionType,
|
||||
readerOptions
|
||||
);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SharpCompress.Common.Options;
|
||||
using SharpCompress.Common.Tar.Headers;
|
||||
using SharpCompress.IO;
|
||||
|
||||
|
|
@ -10,7 +11,8 @@ public partial class TarEntry : Entry
|
|||
{
|
||||
private readonly TarFilePart? _filePart;
|
||||
|
||||
internal TarEntry(TarFilePart? filePart, CompressionType type)
|
||||
internal TarEntry(TarFilePart? filePart, CompressionType type, IReaderOptions readerOptions)
|
||||
: base(readerOptions)
|
||||
{
|
||||
_filePart = filePart;
|
||||
CompressionType = type;
|
||||
|
|
@ -54,7 +56,8 @@ public partial class TarEntry : Entry
|
|||
StreamingMode mode,
|
||||
Stream stream,
|
||||
CompressionType compressionType,
|
||||
IArchiveEncoding archiveEncoding
|
||||
IArchiveEncoding archiveEncoding,
|
||||
IReaderOptions readerOptions
|
||||
)
|
||||
{
|
||||
foreach (var header in TarHeaderFactory.ReadHeader(mode, stream, archiveEncoding))
|
||||
|
|
@ -63,11 +66,19 @@ public partial class TarEntry : Entry
|
|||
{
|
||||
if (mode == StreamingMode.Seekable)
|
||||
{
|
||||
yield return new TarEntry(new TarFilePart(header, stream), compressionType);
|
||||
yield return new TarEntry(
|
||||
new TarFilePart(header, stream),
|
||||
compressionType,
|
||||
readerOptions
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return new TarEntry(new TarFilePart(header, null), compressionType);
|
||||
yield return new TarEntry(
|
||||
new TarFilePart(header, null),
|
||||
compressionType,
|
||||
readerOptions
|
||||
);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ internal static partial class TarHeaderFactory
|
|||
try
|
||||
{
|
||||
header = new TarHeader(archiveEncoding);
|
||||
if (!await header.ReadAsync(reader))
|
||||
if (!await header.ReadAsync(reader).ConfigureAwait(false))
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,14 +8,14 @@ internal partial class DirectoryEndHeader
|
|||
{
|
||||
internal override async ValueTask Read(AsyncBinaryReader reader)
|
||||
{
|
||||
VolumeNumber = await reader.ReadUInt16Async();
|
||||
FirstVolumeWithDirectory = await reader.ReadUInt16Async();
|
||||
TotalNumberOfEntriesInDisk = await reader.ReadUInt16Async();
|
||||
TotalNumberOfEntries = await reader.ReadUInt16Async();
|
||||
DirectorySize = await reader.ReadUInt32Async();
|
||||
DirectoryStartOffsetRelativeToDisk = await reader.ReadUInt32Async();
|
||||
CommentLength = await reader.ReadUInt16Async();
|
||||
VolumeNumber = await reader.ReadUInt16Async().ConfigureAwait(false);
|
||||
FirstVolumeWithDirectory = await reader.ReadUInt16Async().ConfigureAwait(false);
|
||||
TotalNumberOfEntriesInDisk = await reader.ReadUInt16Async().ConfigureAwait(false);
|
||||
TotalNumberOfEntries = await reader.ReadUInt16Async().ConfigureAwait(false);
|
||||
DirectorySize = await reader.ReadUInt32Async().ConfigureAwait(false);
|
||||
DirectoryStartOffsetRelativeToDisk = await reader.ReadUInt32Async().ConfigureAwait(false);
|
||||
CommentLength = await reader.ReadUInt16Async().ConfigureAwait(false);
|
||||
Comment = new byte[CommentLength];
|
||||
await reader.ReadBytesAsync(Comment, 0, CommentLength);
|
||||
await reader.ReadBytesAsync(Comment, 0, CommentLength).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,28 +10,33 @@ internal partial class DirectoryEntryHeader
|
|||
{
|
||||
internal override async ValueTask Read(AsyncBinaryReader reader)
|
||||
{
|
||||
Version = await reader.ReadUInt16Async();
|
||||
VersionNeededToExtract = await reader.ReadUInt16Async();
|
||||
Flags = (HeaderFlags)await reader.ReadUInt16Async();
|
||||
CompressionMethod = (ZipCompressionMethod)await reader.ReadUInt16Async();
|
||||
OriginalLastModifiedTime = LastModifiedTime = await reader.ReadUInt16Async();
|
||||
OriginalLastModifiedDate = LastModifiedDate = await reader.ReadUInt16Async();
|
||||
Crc = await reader.ReadUInt32Async();
|
||||
CompressedSize = await reader.ReadUInt32Async();
|
||||
UncompressedSize = await reader.ReadUInt32Async();
|
||||
var nameLength = await reader.ReadUInt16Async();
|
||||
var extraLength = await reader.ReadUInt16Async();
|
||||
var commentLength = await reader.ReadUInt16Async();
|
||||
DiskNumberStart = await reader.ReadUInt16Async();
|
||||
InternalFileAttributes = await reader.ReadUInt16Async();
|
||||
ExternalFileAttributes = await reader.ReadUInt32Async();
|
||||
RelativeOffsetOfEntryHeader = await reader.ReadUInt32Async();
|
||||
Version = await reader.ReadUInt16Async().ConfigureAwait(false);
|
||||
VersionNeededToExtract = await reader.ReadUInt16Async().ConfigureAwait(false);
|
||||
Flags = (HeaderFlags)await reader.ReadUInt16Async().ConfigureAwait(false);
|
||||
CompressionMethod = (ZipCompressionMethod)
|
||||
await reader.ReadUInt16Async().ConfigureAwait(false);
|
||||
OriginalLastModifiedTime = LastModifiedTime = await reader
|
||||
.ReadUInt16Async()
|
||||
.ConfigureAwait(false);
|
||||
OriginalLastModifiedDate = LastModifiedDate = await reader
|
||||
.ReadUInt16Async()
|
||||
.ConfigureAwait(false);
|
||||
Crc = await reader.ReadUInt32Async().ConfigureAwait(false);
|
||||
CompressedSize = await reader.ReadUInt32Async().ConfigureAwait(false);
|
||||
UncompressedSize = await reader.ReadUInt32Async().ConfigureAwait(false);
|
||||
var nameLength = await reader.ReadUInt16Async().ConfigureAwait(false);
|
||||
var extraLength = await reader.ReadUInt16Async().ConfigureAwait(false);
|
||||
var commentLength = await reader.ReadUInt16Async().ConfigureAwait(false);
|
||||
DiskNumberStart = await reader.ReadUInt16Async().ConfigureAwait(false);
|
||||
InternalFileAttributes = await reader.ReadUInt16Async().ConfigureAwait(false);
|
||||
ExternalFileAttributes = await reader.ReadUInt32Async().ConfigureAwait(false);
|
||||
RelativeOffsetOfEntryHeader = await reader.ReadUInt32Async().ConfigureAwait(false);
|
||||
var name = new byte[nameLength];
|
||||
var extra = new byte[extraLength];
|
||||
var comment = new byte[commentLength];
|
||||
await reader.ReadBytesAsync(name, 0, nameLength);
|
||||
await reader.ReadBytesAsync(extra, 0, extraLength);
|
||||
await reader.ReadBytesAsync(comment, 0, commentLength);
|
||||
await reader.ReadBytesAsync(name, 0, nameLength).ConfigureAwait(false);
|
||||
await reader.ReadBytesAsync(extra, 0, extraLength).ConfigureAwait(false);
|
||||
await reader.ReadBytesAsync(comment, 0, commentLength).ConfigureAwait(false);
|
||||
|
||||
ProcessReadData(name, extra, comment);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,20 +9,25 @@ internal partial class LocalEntryHeader
|
|||
{
|
||||
internal override async ValueTask Read(AsyncBinaryReader reader)
|
||||
{
|
||||
Version = await reader.ReadUInt16Async();
|
||||
Flags = (HeaderFlags)await reader.ReadUInt16Async();
|
||||
CompressionMethod = (ZipCompressionMethod)await reader.ReadUInt16Async();
|
||||
OriginalLastModifiedTime = LastModifiedTime = await reader.ReadUInt16Async();
|
||||
OriginalLastModifiedDate = LastModifiedDate = await reader.ReadUInt16Async();
|
||||
Crc = await reader.ReadUInt32Async();
|
||||
CompressedSize = await reader.ReadUInt32Async();
|
||||
UncompressedSize = await reader.ReadUInt32Async();
|
||||
var nameLength = await reader.ReadUInt16Async();
|
||||
var extraLength = await reader.ReadUInt16Async();
|
||||
Version = await reader.ReadUInt16Async().ConfigureAwait(false);
|
||||
Flags = (HeaderFlags)await reader.ReadUInt16Async().ConfigureAwait(false);
|
||||
CompressionMethod = (ZipCompressionMethod)
|
||||
await reader.ReadUInt16Async().ConfigureAwait(false);
|
||||
OriginalLastModifiedTime = LastModifiedTime = await reader
|
||||
.ReadUInt16Async()
|
||||
.ConfigureAwait(false);
|
||||
OriginalLastModifiedDate = LastModifiedDate = await reader
|
||||
.ReadUInt16Async()
|
||||
.ConfigureAwait(false);
|
||||
Crc = await reader.ReadUInt32Async().ConfigureAwait(false);
|
||||
CompressedSize = await reader.ReadUInt32Async().ConfigureAwait(false);
|
||||
UncompressedSize = await reader.ReadUInt32Async().ConfigureAwait(false);
|
||||
var nameLength = await reader.ReadUInt16Async().ConfigureAwait(false);
|
||||
var extraLength = await reader.ReadUInt16Async().ConfigureAwait(false);
|
||||
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).ConfigureAwait(false);
|
||||
await reader.ReadBytesAsync(extra, 0, extraLength).ConfigureAwait(false);
|
||||
|
||||
ProcessReadData(name, extra);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,19 +8,20 @@ internal partial class Zip64DirectoryEndHeader
|
|||
{
|
||||
internal override async ValueTask Read(AsyncBinaryReader reader)
|
||||
{
|
||||
SizeOfDirectoryEndRecord = (long)await reader.ReadUInt64Async();
|
||||
VersionMadeBy = await reader.ReadUInt16Async();
|
||||
VersionNeededToExtract = await reader.ReadUInt16Async();
|
||||
VolumeNumber = await reader.ReadUInt32Async();
|
||||
FirstVolumeWithDirectory = await reader.ReadUInt32Async();
|
||||
TotalNumberOfEntriesInDisk = (long)await reader.ReadUInt64Async();
|
||||
TotalNumberOfEntries = (long)await reader.ReadUInt64Async();
|
||||
DirectorySize = (long)await reader.ReadUInt64Async();
|
||||
DirectoryStartOffsetRelativeToDisk = (long)await reader.ReadUInt64Async();
|
||||
SizeOfDirectoryEndRecord = (long)await reader.ReadUInt64Async().ConfigureAwait(false);
|
||||
VersionMadeBy = await reader.ReadUInt16Async().ConfigureAwait(false);
|
||||
VersionNeededToExtract = await reader.ReadUInt16Async().ConfigureAwait(false);
|
||||
VolumeNumber = await reader.ReadUInt32Async().ConfigureAwait(false);
|
||||
FirstVolumeWithDirectory = await reader.ReadUInt32Async().ConfigureAwait(false);
|
||||
TotalNumberOfEntriesInDisk = (long)await reader.ReadUInt64Async().ConfigureAwait(false);
|
||||
TotalNumberOfEntries = (long)await reader.ReadUInt64Async().ConfigureAwait(false);
|
||||
DirectorySize = (long)await reader.ReadUInt64Async().ConfigureAwait(false);
|
||||
DirectoryStartOffsetRelativeToDisk = (long)
|
||||
await reader.ReadUInt64Async().ConfigureAwait(false);
|
||||
var size = (int)(
|
||||
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).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue