diff --git a/.editorconfig b/.editorconfig
index 96f2a953b..ec46ee724 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -307,7 +307,6 @@ dotnet_diagnostic.CS8602.severity = error
dotnet_diagnostic.CS8604.severity = error
dotnet_diagnostic.CS8618.severity = error
dotnet_diagnostic.CS0618.severity = suggestion
-dotnet_diagnostic.CS1998.severity = error
dotnet_diagnostic.CS4014.severity = error
dotnet_diagnostic.CS8600.severity = error
dotnet_diagnostic.CS8603.severity = error
diff --git a/.gitignore b/.gitignore
index 6c6a863dc..17b167043 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,8 +4,8 @@ _ReSharper.SharpCompress/
bin/
*.suo
*.user
-TestArchives/Scratch/
-TestArchives/Scratch2/
+tests/TestArchives/Scratch/
+tests/TestArchives/Scratch2/
TestResults/
*.nupkg
packages/*/
diff --git a/AGENTS.md b/AGENTS.md
index 38ae56925..35e77d700 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -178,5 +178,4 @@ SharpCompress supports multiple archive and compression formats:
2. **Solid archives (Rar, 7Zip)** - Use `ExtractAllEntries()` for best performance, not individual entry extraction
3. **Stream disposal** - Always set `LeaveStreamOpen` explicitly when needed (default is to close)
4. **Tar + non-seekable stream** - Must provide file size or it will throw
-5. **Multi-framework differences** - Some features differ between .NET Framework and modern .NET (e.g., Mono.Posix)
6. **Format detection** - Use `ReaderFactory.Open()` for auto-detection, test with actual archive files
diff --git a/Directory.Build.props b/Directory.Build.props
index b3d0b5954..f03c2d6e9 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -12,5 +12,6 @@
False
true
true
+ true
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 9f2e0c864..d5994bd76 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -5,6 +5,7 @@
+
diff --git a/SharpCompress.sln b/SharpCompress.sln
index ba04063bf..af132f4c5 100644
--- a/SharpCompress.sln
+++ b/SharpCompress.sln
@@ -18,12 +18,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Config", "Config", "{CDB425
Directory.Build.props = Directory.Build.props
global.json = global.json
.editorconfig = .editorconfig
+ .gitignore = .gitignore
Directory.Packages.props = Directory.Packages.props
NuGet.config = NuGet.config
.github\workflows\nuget-release.yml = .github\workflows\nuget-release.yml
- USAGE.md = USAGE.md
README.md = README.md
- FORMATS.md = FORMATS.md
AGENTS.md = AGENTS.md
EndProjectSection
EndProject
diff --git a/docs/API.md b/docs/API.md
index 88dbf2bb1..eb8f007a7 100644
--- a/docs/API.md
+++ b/docs/API.md
@@ -8,49 +8,49 @@ Quick reference for commonly used SharpCompress APIs.
```csharp
// Auto-detect format
-using (var reader = ReaderFactory.Open(stream))
+using (var reader = ReaderFactory.OpenReader(stream))
{
// Works with Zip, Tar, GZip, Rar, 7Zip, etc.
}
// Specific format - Archive API
-using (var archive = ZipArchive.Open("file.zip"))
-using (var archive = TarArchive.Open("file.tar"))
-using (var archive = RarArchive.Open("file.rar"))
-using (var archive = SevenZipArchive.Open("file.7z"))
-using (var archive = GZipArchive.Open("file.gz"))
+using (var archive = ZipArchive.OpenArchive("file.zip"))
+using (var archive = TarArchive.OpenArchive("file.tar"))
+using (var archive = RarArchive.OpenArchive("file.rar"))
+using (var archive = SevenZipArchive.OpenArchive("file.7z"))
+using (var archive = GZipArchive.OpenArchive("file.gz"))
// With options
-var options = new ReaderOptions
-{
+var options = new ReaderOptions
+{
Password = "password",
LeaveStreamOpen = true,
ArchiveEncoding = new ArchiveEncoding { Default = Encoding.GetEncoding(932) }
};
-using (var archive = ZipArchive.Open("encrypted.zip", options))
+using (var archive = ZipArchive.OpenArchive("encrypted.zip", options))
```
### Creating Archives
```csharp
// Writer Factory
-using (var writer = WriterFactory.Open(stream, ArchiveType.Zip, CompressionType.Deflate))
+using (var writer = WriterFactory.OpenWriter(stream, ArchiveType.Zip, CompressionType.Deflate))
{
// Write entries
}
// Specific writer
-using (var archive = ZipArchive.Create())
-using (var archive = TarArchive.Create())
-using (var archive = GZipArchive.Create())
+using (var archive = ZipArchive.CreateArchive())
+using (var archive = TarArchive.CreateArchive())
+using (var archive = GZipArchive.CreateArchive())
// With options
-var options = new WriterOptions(CompressionType.Deflate)
-{
+var options = new WriterOptions(CompressionType.Deflate)
+{
CompressionLevel = 9,
LeaveStreamOpen = false
};
-using (var archive = ZipArchive.Create())
+using (var archive = ZipArchive.CreateArchive())
{
archive.SaveTo("output.zip", options);
}
@@ -63,26 +63,26 @@ using (var archive = ZipArchive.Create())
### Reading/Extracting
```csharp
-using (var archive = ZipArchive.Open("file.zip"))
+using (var archive = ZipArchive.OpenArchive("file.zip"))
{
// Get all entries
- IEnumerable entries = archive.Entries;
-
+ IEnumerable entries = archive.Entries;
+
// Find specific entry
var entry = archive.Entries.FirstOrDefault(e => e.Key == "file.txt");
-
+
// Extract all
archive.WriteToDirectory(@"C:\output", new ExtractionOptions
{
ExtractFullPath = true,
Overwrite = true
});
-
+
// 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())
{
@@ -90,8 +90,15 @@ using (var archive = ZipArchive.Open("file.zip"))
}
}
-// Async variants
-await archive.WriteToDirectoryAsync(@"C:\output", options, cancellationToken);
+// Async extraction (requires IAsyncArchive)
+using (var asyncArchive = await ZipArchive.OpenAsyncArchive("file.zip"))
+{
+ await asyncArchive.WriteToDirectoryAsync(
+ @"C:\output",
+ new ExtractionOptions { ExtractFullPath = true, Overwrite = true },
+ cancellationToken: cancellationToken
+ );
+}
using (var stream = await entry.OpenEntryStreamAsync(cancellationToken))
{
// ...
@@ -115,18 +122,18 @@ foreach (var entry in archive.Entries)
### Creating Archives
```csharp
-using (var archive = ZipArchive.Create())
+using (var archive = ZipArchive.CreateArchive())
{
// Add file
- archive.AddEntry("file.txt", "C:\\source\\file.txt");
-
+ archive.AddEntry("file.txt", @"C:\source\file.txt");
+
// Add multiple files
- archive.AddAllFromDirectory("C:\\source");
- archive.AddAllFromDirectory("C:\\source", "*.txt"); // Pattern
-
+ archive.AddAllFromDirectory(@"C:\source");
+ archive.AddAllFromDirectory(@"C:\source", "*.txt"); // Pattern
+
// Save to file
archive.SaveTo("output.zip", CompressionType.Deflate);
-
+
// Save to stream
archive.SaveTo(outputStream, new WriterOptions(CompressionType.Deflate)
{
@@ -144,18 +151,18 @@ using (var archive = ZipArchive.Create())
```csharp
using (var stream = File.OpenRead("file.zip"))
-using (var reader = ReaderFactory.Open(stream))
+using (var reader = ReaderFactory.OpenReader(stream))
{
while (reader.MoveToNextEntry())
{
- IEntry entry = reader.Entry;
-
+ IArchiveEntry entry = reader.Entry;
+
if (!entry.IsDirectory)
{
// Extract entry
reader.WriteEntryToDirectory(@"C:\output");
reader.WriteEntryToFile(@"C:\output\file.txt");
-
+
// Or get stream
using (var entryStream = reader.OpenEntryStream())
{
@@ -165,16 +172,25 @@ using (var reader = ReaderFactory.Open(stream))
}
}
-// Async variants
-while (await reader.MoveToNextEntryAsync())
+// Async variants (use OpenAsyncReader to get IAsyncReader)
+using (var stream = File.OpenRead("file.zip"))
+using (var reader = await ReaderFactory.OpenAsyncReader(stream))
{
- await reader.WriteEntryToFileAsync(@"C:\output\" + reader.Entry.Key, cancellationToken);
-}
+ while (await reader.MoveToNextEntryAsync())
+ {
+ await reader.WriteEntryToFileAsync(
+ @"C:\output\" + reader.Entry.Key,
+ cancellationToken: cancellationToken
+ );
+ }
-// Async extraction
-await reader.WriteAllToDirectoryAsync(@"C:\output",
- new ExtractionOptions { ExtractFullPath = true, Overwrite = true },
- cancellationToken);
+ // Async extraction of all entries
+ await reader.WriteAllToDirectoryAsync(
+ @"C:\output",
+ new ExtractionOptions { ExtractFullPath = true, Overwrite = true },
+ cancellationToken
+ );
+}
```
---
@@ -185,7 +201,7 @@ await reader.WriteAllToDirectoryAsync(@"C:\output",
```csharp
using (var stream = File.Create("output.zip"))
-using (var writer = WriterFactory.Open(stream, ArchiveType.Zip, CompressionType.Deflate))
+using (var writer = WriterFactory.OpenWriter(stream, ArchiveType.Zip, CompressionType.Deflate))
{
// Write single file
using (var fileStream = File.OpenRead("source.txt"))
@@ -223,7 +239,7 @@ var options = new ReaderOptions
Default = Encoding.GetEncoding(932)
}
};
-using (var archive = ZipArchive.Open("file.zip", options))
+using (var archive = ZipArchive.OpenArchive("file.zip", options))
{
// ...
}
@@ -262,15 +278,20 @@ archive.WriteToDirectory(@"C:\output", options);
// For creating archives
CompressionType.None // No compression (store)
CompressionType.Deflate // DEFLATE (default for ZIP/GZip)
+CompressionType.Deflate64 // Deflate64
CompressionType.BZip2 // BZip2
CompressionType.LZMA // LZMA (for 7Zip, LZip, XZ)
CompressionType.PPMd // PPMd (for ZIP)
CompressionType.Rar // RAR compression (read-only)
-
-// For Tar archives
-// Use CompressionType in TarWriter constructor
-using (var writer = TarWriter(stream, CompressionType.GZip)) // Tar.GZip
-using (var writer = TarWriter(stream, CompressionType.BZip2)) // Tar.BZip2
+CompressionType.ZStandard // ZStandard
+ArchiveType.Arc
+ArchiveType.Arj
+ArchiveType.Ace
+
+// For Tar archives with compression
+// Use WriterFactory to create compressed tar archives
+using (var writer = WriterFactory.OpenWriter(stream, ArchiveType.Tar, CompressionType.GZip)) // Tar.GZip
+using (var writer = WriterFactory.OpenWriter(stream, ArchiveType.Tar, CompressionType.BZip2)) // Tar.BZip2
```
### Archive Types
@@ -328,7 +349,7 @@ var progress = new Progress(report =>
});
var options = new ReaderOptions { Progress = progress };
-using (var archive = ZipArchive.Open("archive.zip", options))
+using (var archive = ZipArchive.OpenArchive("archive.zip", options))
{
archive.WriteToDirectory(@"C:\output");
}
@@ -342,11 +363,13 @@ cts.CancelAfter(TimeSpan.FromMinutes(5));
try
{
- using (var archive = ZipArchive.Open("archive.zip"))
+ using (var archive = await ZipArchive.OpenAsyncArchive("archive.zip"))
{
- await archive.WriteToDirectoryAsync(@"C:\output",
+ await archive.WriteToDirectoryAsync(
+ @"C:\output",
new ExtractionOptions { ExtractFullPath = true, Overwrite = true },
- cts.Token);
+ cancellationToken: cts.Token
+ );
}
}
catch (OperationCanceledException)
@@ -358,23 +381,23 @@ catch (OperationCanceledException)
### Create with Custom Compression
```csharp
-using (var archive = ZipArchive.Create())
+using (var archive = ZipArchive.CreateArchive())
{
archive.AddAllFromDirectory(@"D:\source");
// Fastest
- archive.SaveTo("fast.zip", new WriterOptions(CompressionType.Deflate)
- {
- CompressionLevel = 1
+ archive.SaveTo("fast.zip", new WriterOptions(CompressionType.Deflate)
+ {
+ CompressionLevel = 1
});
// Balanced (default)
archive.SaveTo("normal.zip", CompressionType.Deflate);
// Best compression
- archive.SaveTo("best.zip", new WriterOptions(CompressionType.Deflate)
- {
- CompressionLevel = 9
+ archive.SaveTo("best.zip", new WriterOptions(CompressionType.Deflate)
+ {
+ CompressionLevel = 9
});
}
```
@@ -383,7 +406,7 @@ using (var archive = ZipArchive.Create())
```csharp
using (var outputStream = new MemoryStream())
-using (var archive = ZipArchive.Create())
+using (var archive = ZipArchive.CreateArchive())
{
// Add content from memory
using (var contentStream = new MemoryStream(Encoding.UTF8.GetBytes("Hello")))
@@ -402,7 +425,7 @@ using (var archive = ZipArchive.Create())
### Extract Specific Files
```csharp
-using (var archive = ZipArchive.Open("archive.zip"))
+using (var archive = ZipArchive.OpenArchive("archive.zip"))
{
var filesToExtract = new[] { "file1.txt", "file2.txt" };
@@ -416,7 +439,7 @@ using (var archive = ZipArchive.Open("archive.zip"))
### List Archive Contents
```csharp
-using (var archive = ZipArchive.Open("archive.zip"))
+using (var archive = ZipArchive.OpenArchive("archive.zip"))
{
foreach (var entry in archive.Entries)
{
@@ -436,7 +459,7 @@ using (var archive = ZipArchive.Open("archive.zip"))
```csharp
var stream = File.OpenRead("archive.zip");
-var archive = ZipArchive.Open(stream);
+var archive = ZipArchive.OpenArchive(stream);
archive.WriteToDirectory(@"C:\output");
// stream not disposed - leaked resource
```
@@ -445,7 +468,7 @@ archive.WriteToDirectory(@"C:\output");
```csharp
using (var stream = File.OpenRead("archive.zip"))
-using (var archive = ZipArchive.Open(stream))
+using (var archive = ZipArchive.OpenArchive(stream))
{
archive.WriteToDirectory(@"C:\output");
}
@@ -456,7 +479,7 @@ using (var archive = ZipArchive.Open(stream))
```csharp
// Loading entire archive then iterating
-using (var archive = ZipArchive.Open("large.zip"))
+using (var archive = ZipArchive.OpenArchive("large.zip"))
{
var entries = archive.Entries.ToList(); // Loads all in memory
foreach (var e in entries)
@@ -471,7 +494,7 @@ using (var archive = ZipArchive.Open("large.zip"))
```csharp
// Streaming iteration
using (var stream = File.OpenRead("large.zip"))
-using (var reader = ReaderFactory.Open(stream))
+using (var reader = ReaderFactory.OpenReader(stream))
{
while (reader.MoveToNextEntry())
{
diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md
index 0ba6f5819..2a3bb985e 100644
--- a/docs/ARCHITECTURE.md
+++ b/docs/ARCHITECTURE.md
@@ -76,7 +76,7 @@ Factory classes for auto-detecting archive format and creating appropriate reade
- Format-specific: `ZipFactory.cs`, `TarFactory.cs`, `RarFactory.cs`, etc.
**How It Works:**
-1. `ReaderFactory.Open(stream)` probes stream signatures
+1. `ReaderFactory.OpenReader(stream)` probes stream signatures
2. Identifies format by magic bytes
3. Creates appropriate reader instance
4. Returns generic `IReader` interface
@@ -142,7 +142,7 @@ Stream wrappers and utilities.
**Example:**
```csharp
// User calls factory
-using (var reader = ReaderFactory.Open(stream)) // Returns IReader
+using (var reader = ReaderFactory.OpenReader(stream)) // Returns IReader
{
while (reader.MoveToNextEntry())
{
@@ -175,7 +175,7 @@ CompressionType.LZMA // LZMA
CompressionType.PPMd // PPMd
// Writer uses strategy pattern
-var archive = ZipArchive.Create();
+var archive = ZipArchive.CreateArchive();
archive.SaveTo("output.zip", CompressionType.Deflate); // Use Deflate
archive.SaveTo("output.bz2", CompressionType.BZip2); // Use BZip2
```
@@ -248,7 +248,7 @@ foreach (var entry in entries)
}
// Reader API - provides iterator
-IReader reader = ReaderFactory.Open(stream);
+IReader reader = ReaderFactory.OpenReader(stream);
while (reader.MoveToNextEntry())
{
// Forward-only iteration - one entry at a time
@@ -381,7 +381,7 @@ public class NewFormatArchive : AbstractArchive
private NewFormatHeader _header;
private List _entries;
- public static NewFormatArchive Open(Stream stream)
+ public static NewFormatArchive OpenArchive(Stream stream)
{
var archive = new NewFormatArchive();
archive._header = NewFormatHeader.Read(stream);
@@ -442,8 +442,8 @@ public class NewFormatFactory : Factory, IArchiveFactory, IReaderFactory
public static NewFormatFactory Instance { get; } = new();
- public IArchive CreateArchive(Stream stream)
- => NewFormatArchive.Open(stream);
+ public IArchive CreateArchive(Stream stream)
+ => NewFormatArchive.OpenArchive(stream);
public IReader CreateReader(Stream stream, ReaderOptions options)
=> new NewFormatReader(stream) { Options = options };
@@ -481,7 +481,7 @@ public class NewFormatTests : TestBase
public void NewFormat_Extracts_Successfully()
{
var archivePath = Path.Combine(TEST_ARCHIVES_PATH, "archive.newformat");
- using (var archive = NewFormatArchive.Open(archivePath))
+ using (var archive = NewFormatArchive.OpenArchive(archivePath))
{
archive.WriteToDirectory(SCRATCH_FILES_PATH);
// Assert extraction
@@ -561,7 +561,7 @@ public class CustomStream : Stream
```csharp
// Correct: Nested using blocks
using (var fileStream = File.OpenRead("archive.zip"))
-using (var archive = ZipArchive.Open(fileStream))
+using (var archive = ZipArchive.OpenArchive(fileStream))
{
archive.WriteToDirectory(@"C:\output");
}
@@ -570,7 +570,7 @@ using (var archive = ZipArchive.Open(fileStream))
// Correct: Using with options
var options = new ReaderOptions { LeaveStreamOpen = true };
var stream = File.OpenRead("archive.zip");
-using (var archive = ZipArchive.Open(stream, options))
+using (var archive = ZipArchive.OpenArchive(stream, options))
{
archive.WriteToDirectory(@"C:\output");
}
@@ -641,7 +641,7 @@ public void Archive_Extraction_Works()
var testArchive = Path.Combine(TEST_ARCHIVES_PATH, "test.zip");
// Act
- using (var archive = ZipArchive.Open(testArchive))
+ using (var archive = ZipArchive.OpenArchive(testArchive))
{
archive.WriteToDirectory(SCRATCH_FILES_PATH);
}
diff --git a/docs/ENCODING.md b/docs/ENCODING.md
index 1737ace93..8200e756f 100644
--- a/docs/ENCODING.md
+++ b/docs/ENCODING.md
@@ -27,7 +27,7 @@ var options = new ReaderOptions
}
};
-using (var archive = ZipArchive.Open("japanese.zip", options))
+using (var archive = ZipArchive.OpenArchive("japanese.zip", options))
{
foreach (var entry in archive.Entries)
{
@@ -51,7 +51,7 @@ var options = new ReaderOptions
{
ArchiveEncoding = new ArchiveEncoding { Default = Encoding.GetEncoding(932) }
};
-using (var archive = ZipArchive.Open("file.zip", options))
+using (var archive = ZipArchive.OpenArchive("file.zip", options))
{
// Use archive with correct encoding
}
@@ -64,7 +64,7 @@ var options = new ReaderOptions
ArchiveEncoding = new ArchiveEncoding { Default = Encoding.GetEncoding(932) }
};
using (var stream = File.OpenRead("file.zip"))
-using (var reader = ReaderFactory.Open(stream, options))
+using (var reader = ReaderFactory.OpenReader(stream, options))
{
while (reader.MoveToNextEntry())
{
@@ -89,7 +89,7 @@ var options = new ReaderOptions
Default = Encoding.GetEncoding(932)
}
};
-using (var archive = ZipArchive.Open("japanese.zip", options))
+using (var archive = ZipArchive.OpenArchive("japanese.zip", options))
{
// Correctly decodes Japanese filenames
}
@@ -266,7 +266,7 @@ SharpCompress attempts to auto-detect encoding, but this isn't always reliable:
```csharp
// Auto-detection (default)
-using (var archive = ZipArchive.Open("file.zip")) // Uses UTF8 by default
+using (var archive = ZipArchive.OpenArchive("file.zip")) // Uses UTF8 by default
{
// May show corrupted characters if archive uses different encoding
}
@@ -276,7 +276,7 @@ var options = new ReaderOptions
{
ArchiveEncoding = new ArchiveEncoding { Default = Encoding.GetEncoding(932) }
};
-using (var archive = ZipArchive.Open("file.zip", options))
+using (var archive = ZipArchive.OpenArchive("file.zip", options))
{
// Correct characters displayed
}
@@ -324,7 +324,7 @@ var options = new ReaderOptions
}
};
-using (var archive = ZipArchive.Open("mixed.zip", options))
+using (var archive = ZipArchive.OpenArchive("mixed.zip", options))
{
foreach (var entry in archive.Entries)
{
@@ -388,7 +388,7 @@ var options = new ReaderOptions
}
};
-using (var archive = ZipArchive.Open("japanese_files.zip", options))
+using (var archive = ZipArchive.OpenArchive("japanese_files.zip", options))
{
archive.WriteToDirectory(@"C:\output", new ExtractionOptions
{
@@ -410,7 +410,7 @@ var options = new ReaderOptions
}
};
-using (var archive = ZipArchive.Open("french_files.zip", options))
+using (var archive = ZipArchive.OpenArchive("french_files.zip", options))
{
archive.WriteToDirectory(@"C:\output");
}
@@ -428,7 +428,7 @@ var options = new ReaderOptions
}
};
-using (var archive = ZipArchive.Open("chinese_files.zip", options))
+using (var archive = ZipArchive.OpenArchive("chinese_files.zip", options))
{
archive.WriteToDirectory(@"C:\output");
}
@@ -445,7 +445,7 @@ var options = new ReaderOptions
}
};
-using (var archive = ZipArchive.Open("russian_files.zip", options))
+using (var archive = ZipArchive.OpenArchive("russian_files.zip", options))
{
archive.WriteToDirectory(@"C:\output");
}
@@ -463,7 +463,7 @@ var options = new ReaderOptions
};
using (var stream = File.OpenRead("japanese.zip"))
-using (var reader = ReaderFactory.Open(stream, options))
+using (var reader = ReaderFactory.OpenReader(stream, options))
{
while (reader.MoveToNextEntry())
{
@@ -484,7 +484,7 @@ When creating archives, SharpCompress uses UTF8 by default (recommended):
```csharp
// Create with UTF8 (default, recommended)
-using (var archive = ZipArchive.Create())
+using (var archive = ZipArchive.CreateArchive())
{
archive.AddAllFromDirectory(@"D:\my_files");
archive.SaveTo("output.zip", CompressionType.Deflate);
diff --git a/docs/PERFORMANCE.md b/docs/PERFORMANCE.md
index 03c12553b..51b2f9206 100644
--- a/docs/PERFORMANCE.md
+++ b/docs/PERFORMANCE.md
@@ -24,7 +24,7 @@ Choose the right API based on your use case:
// - You need random access to entries
// - Stream is seekable (file, MemoryStream)
-using (var archive = ZipArchive.Open("archive.zip"))
+using (var archive = ZipArchive.OpenArchive("archive.zip"))
{
// Random access - all entries available
var specific = archive.Entries.FirstOrDefault(e => e.Key == "file.txt");
@@ -51,7 +51,7 @@ using (var archive = ZipArchive.Open("archive.zip"))
// - Forward-only processing is acceptable
using (var stream = File.OpenRead("large.zip"))
-using (var reader = ReaderFactory.Open(stream))
+using (var reader = ReaderFactory.OpenReader(stream))
{
while (reader.MoveToNextEntry())
{
@@ -129,7 +129,7 @@ For processing archives from downloads or pipes:
```csharp
// Download stream (non-seekable)
using (var httpStream = await httpClient.GetStreamAsync(url))
-using (var reader = ReaderFactory.Open(httpStream))
+using (var reader = ReaderFactory.OpenReader(httpStream))
{
// Process entries as they arrive
while (reader.MoveToNextEntry())
@@ -159,14 +159,14 @@ Choose based on your constraints:
```csharp
// Download then extract (requires disk space)
var archivePath = await DownloadFile(url, @"C:\temp\archive.zip");
-using (var archive = ZipArchive.Open(archivePath))
+using (var archive = ZipArchive.OpenArchive(archivePath))
{
archive.WriteToDirectory(@"C:\output");
}
// Stream during download (on-the-fly extraction)
using (var httpStream = await httpClient.GetStreamAsync(url))
-using (var reader = ReaderFactory.Open(httpStream))
+using (var reader = ReaderFactory.OpenReader(httpStream))
{
while (reader.MoveToNextEntry())
{
@@ -198,7 +198,7 @@ Extracting File3 requires decompressing File1 and File2 first.
**Random Extraction (Slow):**
```csharp
-using (var archive = RarArchive.Open("solid.rar"))
+using (var archive = RarArchive.OpenArchive("solid.rar"))
{
foreach (var entry in archive.Entries)
{
@@ -210,7 +210,7 @@ using (var archive = RarArchive.Open("solid.rar"))
**Sequential Extraction (Fast):**
```csharp
-using (var archive = RarArchive.Open("solid.rar"))
+using (var archive = RarArchive.OpenArchive("solid.rar"))
{
// Method 1: Use WriteToDirectory (recommended)
archive.WriteToDirectory(@"C:\output", new ExtractionOptions
@@ -256,7 +256,7 @@ using (var archive = RarArchive.Open("solid.rar"))
// Level 9 = Slowest, best compression
// Write with different compression levels
-using (var archive = ZipArchive.Create())
+using (var archive = ZipArchive.CreateArchive())
{
archive.AddAllFromDirectory(@"D:\data");
@@ -293,7 +293,7 @@ using (var archive = ZipArchive.Create())
// Smaller block size = lower memory, faster
// Larger block size = better compression, slower
-using (var archive = TarArchive.Create())
+using (var archive = TarArchive.CreateArchive())
{
archive.AddAllFromDirectory(@"D:\data");
@@ -313,7 +313,7 @@ LZMA compression is very powerful but memory-intensive:
// - Better compression: larger dictionary
// Preset via CompressionType
-using (var archive = TarArchive.Create())
+using (var archive = TarArchive.CreateArchive())
{
archive.AddAllFromDirectory(@"D:\data");
archive.SaveTo("archive.tar.xz", CompressionType.LZMA); // Default settings
@@ -333,7 +333,7 @@ Async is beneficial when:
```csharp
// Async extraction (non-blocking)
-using (var archive = ZipArchive.Open("archive.zip"))
+using (var archive = ZipArchive.OpenArchive("archive.zip"))
{
await archive.WriteToDirectoryAsync(
@"C:\output",
@@ -353,7 +353,7 @@ Async doesn't improve performance for:
```csharp
// Sync extraction (simpler, same performance on fast I/O)
-using (var archive = ZipArchive.Open("archive.zip"))
+using (var archive = ZipArchive.OpenArchive("archive.zip"))
{
archive.WriteToDirectory(
@"C:\output",
@@ -373,7 +373,7 @@ cts.CancelAfter(TimeSpan.FromMinutes(5));
try
{
- using (var archive = ZipArchive.Open("archive.zip"))
+ using (var archive = ZipArchive.OpenArchive("archive.zip"))
{
await archive.WriteToDirectoryAsync(
@"C:\output",
@@ -408,14 +408,14 @@ catch (OperationCanceledException)
// ✗ Slow - opens each archive separately
foreach (var file in files)
{
- using (var archive = ZipArchive.Open("archive.zip"))
+ using (var archive = ZipArchive.OpenArchive("archive.zip"))
{
archive.WriteToDirectory(@"C:\output");
}
}
// ✓ Better - process multiple entries at once
-using (var archive = ZipArchive.Open("archive.zip"))
+using (var archive = ZipArchive.OpenArchive("archive.zip"))
{
archive.WriteToDirectory(@"C:\output");
}
@@ -425,7 +425,7 @@ using (var archive = ZipArchive.Open("archive.zip"))
```csharp
var sw = Stopwatch.StartNew();
-using (var archive = ZipArchive.Open("large.zip"))
+using (var archive = ZipArchive.OpenArchive("large.zip"))
{
archive.WriteToDirectory(@"C:\output");
}
diff --git a/docs/USAGE.md b/docs/USAGE.md
index 8e636cab8..52d690a32 100644
--- a/docs/USAGE.md
+++ b/docs/USAGE.md
@@ -48,7 +48,7 @@ Also, look over the tests for more thorough [examples](https://github.com/adamha
### Create Zip Archive from multiple files
```C#
-using(var archive = ZipArchive.Create())
+using(var archive = ZipArchive.CreateArchive())
{
archive.AddEntry("file01.txt", "C:\\file01.txt");
archive.AddEntry("file02.txt", "C:\\file02.txt");
@@ -61,7 +61,7 @@ using(var archive = ZipArchive.Create())
### Create Zip Archive from all files in a directory to a file
```C#
-using (var archive = ZipArchive.Create())
+using (var archive = ZipArchive.CreateArchive())
{
archive.AddAllFromDirectory("D:\\temp");
archive.SaveTo("C:\\temp.zip", CompressionType.Deflate);
@@ -72,7 +72,7 @@ using (var archive = ZipArchive.Create())
```C#
var memoryStream = new MemoryStream();
-using (var archive = ZipArchive.Create())
+using (var archive = ZipArchive.CreateArchive())
{
archive.AddAllFromDirectory("D:\\temp");
archive.SaveTo(memoryStream, new WriterOptions(CompressionType.Deflate)
@@ -90,7 +90,7 @@ Note: Extracting a solid rar or 7z file needs to be done in sequential order to
`ExtractAllEntries` is primarily intended for solid archives (like solid Rar) or 7Zip archives, where sequential extraction provides the best performance. For general/simple extraction with any supported archive type, use `archive.WriteToDirectory()` instead.
```C#
-using (var archive = RarArchive.Open("Test.rar"))
+using (var archive = RarArchive.OpenArchive("Test.rar"))
{
// Simple extraction with RarArchive; this WriteToDirectory pattern works for all archive types
archive.WriteToDirectory(@"D:\temp", new ExtractionOptions()
@@ -104,7 +104,7 @@ using (var archive = RarArchive.Open("Test.rar"))
### Iterate over all files from a Rar file using RarArchive
```C#
-using (var archive = RarArchive.Open("Test.rar"))
+using (var archive = RarArchive.OpenArchive("Test.rar"))
{
foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory))
{
@@ -126,7 +126,7 @@ var progress = new Progress(report =>
Console.WriteLine($"Extracting {report.EntryPath}: {report.PercentComplete}%");
});
-using (var archive = RarArchive.Open("archive.rar", new ReaderOptions { Progress = progress })) // Must be solid Rar or 7Zip
+using (var archive = RarArchive.OpenArchive("archive.rar", new ReaderOptions { Progress = progress })) // Must be solid Rar or 7Zip
{
archive.WriteToDirectory(@"D:\output", new ExtractionOptions()
{
@@ -140,7 +140,7 @@ using (var archive = RarArchive.Open("archive.rar", new ReaderOptions { Progress
```C#
using (Stream stream = File.OpenRead("Tar.tar.bz2"))
-using (var reader = ReaderFactory.Open(stream))
+using (var reader = ReaderFactory.OpenReader(stream))
{
while (reader.MoveToNextEntry())
{
@@ -161,7 +161,7 @@ using (var reader = ReaderFactory.Open(stream))
```C#
using (Stream stream = File.OpenRead("Tar.tar.bz2"))
-using (var reader = ReaderFactory.Open(stream))
+using (var reader = ReaderFactory.OpenReader(stream))
{
while (reader.MoveToNextEntry())
{
@@ -180,7 +180,7 @@ using (var reader = ReaderFactory.Open(stream))
```C#
using (Stream stream = File.OpenWrite("C:\\temp.tgz"))
-using (var writer = WriterFactory.Open(stream, ArchiveType.Tar, new WriterOptions(CompressionType.GZip)
+using (var writer = WriterFactory.OpenWriter(stream, ArchiveType.Tar, new WriterOptions(CompressionType.GZip)
{
LeaveOpenStream = true
}))
@@ -199,7 +199,7 @@ opts.ArchiveEncoding.CustomDecoder = (data, x, y) =>
{
return encoding.GetString(data);
};
-var tr = SharpCompress.Archives.Zip.ZipArchive.Open("test.zip", opts);
+var tr = SharpCompress.Archives.Zip.ZipArchive.OpenArchive("test.zip", opts);
foreach(var entry in tr.Entries)
{
Console.WriteLine($"{entry.Key}");
@@ -213,7 +213,7 @@ foreach(var entry in tr.Entries)
**Extract single entry asynchronously:**
```C#
using (Stream stream = File.OpenRead("archive.zip"))
-using (var reader = ReaderFactory.Open(stream))
+using (var reader = ReaderFactory.OpenReader(stream))
{
while (reader.MoveToNextEntry())
{
@@ -234,7 +234,7 @@ using (var reader = ReaderFactory.Open(stream))
**Extract all entries asynchronously:**
```C#
using (Stream stream = File.OpenRead("archive.tar.gz"))
-using (var reader = ReaderFactory.Open(stream))
+using (var reader = ReaderFactory.OpenReader(stream))
{
await reader.WriteAllToDirectoryAsync(
@"D:\temp",
@@ -250,7 +250,7 @@ using (var reader = ReaderFactory.Open(stream))
**Open and process entry stream asynchronously:**
```C#
-using (var archive = ZipArchive.Open("archive.zip"))
+using (var archive = ZipArchive.OpenArchive("archive.zip"))
{
foreach (var entry in archive.Entries.Where(e => !e.IsDirectory))
{
@@ -268,7 +268,7 @@ using (var archive = ZipArchive.Open("archive.zip"))
**Write single file asynchronously:**
```C#
using (Stream archiveStream = File.OpenWrite("output.zip"))
-using (var writer = WriterFactory.Open(archiveStream, ArchiveType.Zip, CompressionType.Deflate))
+using (var writer = WriterFactory.OpenWriter(archiveStream, ArchiveType.Zip, CompressionType.Deflate))
{
using (Stream fileStream = File.OpenRead("input.txt"))
{
@@ -280,7 +280,7 @@ using (var writer = WriterFactory.Open(archiveStream, ArchiveType.Zip, Compressi
**Write entire directory asynchronously:**
```C#
using (Stream stream = File.OpenWrite("backup.tar.gz"))
-using (var writer = WriterFactory.Open(stream, ArchiveType.Tar, new WriterOptions(CompressionType.GZip)))
+using (var writer = WriterFactory.OpenWriter(stream, ArchiveType.Tar, new WriterOptions(CompressionType.GZip)))
{
await writer.WriteAllAsync(
@"D:\files",
@@ -299,7 +299,7 @@ var cts = new CancellationTokenSource();
cts.CancelAfter(TimeSpan.FromMinutes(5));
using (Stream stream = File.OpenWrite("archive.zip"))
-using (var writer = WriterFactory.Open(stream, ArchiveType.Zip, CompressionType.Deflate))
+using (var writer = WriterFactory.OpenWriter(stream, ArchiveType.Zip, CompressionType.Deflate))
{
try
{
@@ -316,7 +316,7 @@ using (var writer = WriterFactory.Open(stream, ArchiveType.Zip, CompressionType.
**Extract from archive asynchronously:**
```C#
-using (var archive = ZipArchive.Open("archive.zip"))
+using (var archive = ZipArchive.OpenArchive("archive.zip"))
{
// Simple async extraction - works for all archive types
await archive.WriteToDirectoryAsync(
diff --git a/src/SharpCompress/Archives/AbstractArchive.cs b/src/SharpCompress/Archives/AbstractArchive.cs
index 12ab15a6b..63e67e69b 100644
--- a/src/SharpCompress/Archives/AbstractArchive.cs
+++ b/src/SharpCompress/Archives/AbstractArchive.cs
@@ -68,7 +68,7 @@ internal AbstractArchive(ArchiveType type)
///
/// The total size of the files as uncompressed in the archive.
///
- public virtual long TotalUncompressSize =>
+ public virtual long TotalUncompressedSize =>
Entries.Aggregate(0L, (total, cf) => total + cf.Size);
protected abstract IEnumerable LoadVolumes(SourceStream sourceStream);
@@ -187,10 +187,26 @@ private async ValueTask EnsureEntriesLoadedAsync()
}
public virtual IAsyncEnumerable EntriesAsync => _lazyEntriesAsync;
- IAsyncEnumerable IAsyncArchive.EntriesAsync =>
- EntriesAsync.Cast();
- public IAsyncEnumerable VolumesAsync => _lazyVolumesAsync.Cast();
+ private async IAsyncEnumerable EntriesAsyncCast()
+ {
+ await foreach (var entry in EntriesAsync)
+ {
+ yield return entry;
+ }
+ }
+
+ IAsyncEnumerable IAsyncArchive.EntriesAsync => EntriesAsyncCast();
+
+ private async IAsyncEnumerable VolumesAsyncCast()
+ {
+ await foreach (var volume in VolumesAsync)
+ {
+ yield return volume;
+ }
+ }
+
+ public IAsyncEnumerable VolumesAsync => VolumesAsyncCast();
public async ValueTask ExtractAllEntriesAsync()
{
@@ -209,14 +225,16 @@ public async ValueTask ExtractAllEntriesAsync()
public async ValueTask IsCompleteAsync()
{
await EnsureEntriesLoadedAsync();
- return await EntriesAsync.All(x => x.IsComplete);
+ return await EntriesAsync.AllAsync(x => x.IsComplete);
}
public async ValueTask TotalSizeAsync() =>
- await EntriesAsync.Aggregate(0L, (total, cf) => total + cf.CompressedSize);
+ await EntriesAsync.AggregateAsync(0L, (total, cf) => total + cf.CompressedSize);
+
+ public async ValueTask TotalUncompressedSizeAsync() =>
+ await EntriesAsync.AggregateAsync(0L, (total, cf) => total + cf.Size);
- public async ValueTask TotalUncompressSizeAsync() =>
- await EntriesAsync.Aggregate(0L, (total, cf) => total + cf.Size);
+ public ValueTask IsEncryptedAsync() => new(IsEncrypted);
#endregion
}
diff --git a/src/SharpCompress/Archives/AbstractWritableArchive.cs b/src/SharpCompress/Archives/AbstractWritableArchive.cs
index 13fb66f9a..6e482f235 100644
--- a/src/SharpCompress/Archives/AbstractWritableArchive.cs
+++ b/src/SharpCompress/Archives/AbstractWritableArchive.cs
@@ -12,7 +12,8 @@ namespace SharpCompress.Archives;
public abstract class AbstractWritableArchive
: AbstractArchive,
- IWritableArchive
+ IWritableArchive,
+ IWritableAsyncArchive
where TEntry : IArchiveEntry
where TVolume : IVolume
{
@@ -83,12 +84,12 @@ public void RemoveEntry(TEntry entry)
}
}
- void IWritableArchive.RemoveEntry(IArchiveEntry entry) => RemoveEntry((TEntry)entry);
+ void IWritableArchiveCommon.RemoveEntry(IArchiveEntry entry) => RemoveEntry((TEntry)entry);
public TEntry AddEntry(string key, Stream source, long size = 0, DateTime? modified = null) =>
AddEntry(key, source, false, size, modified);
- IArchiveEntry IWritableArchive.AddEntry(
+ IArchiveEntry IWritableArchiveCommon.AddEntry(
string key,
Stream source,
bool closeStream,
@@ -96,7 +97,7 @@ IArchiveEntry IWritableArchive.AddEntry(
DateTime? modified
) => AddEntry(key, source, closeStream, size, modified);
- IArchiveEntry IWritableArchive.AddDirectoryEntry(string key, DateTime? modified) =>
+ IArchiveEntry IWritableArchiveCommon.AddDirectoryEntry(string key, DateTime? modified) =>
AddDirectoryEntry(key, modified);
public TEntry AddEntry(
diff --git a/src/SharpCompress/Archives/ArchiveFactory.cs b/src/SharpCompress/Archives/ArchiveFactory.cs
index e052c3e80..a9800acd9 100644
--- a/src/SharpCompress/Archives/ArchiveFactory.cs
+++ b/src/SharpCompress/Archives/ArchiveFactory.cs
@@ -13,27 +13,14 @@ namespace SharpCompress.Archives;
public static class ArchiveFactory
{
- ///
- /// Opens an Archive for random access
- ///
- ///
- ///
- ///
- public static IArchive Open(Stream stream, ReaderOptions? readerOptions = null)
+ public static IArchive OpenArchive(Stream stream, ReaderOptions? readerOptions = null)
{
readerOptions ??= new ReaderOptions();
stream = SharpCompressStream.Create(stream, bufferSize: readerOptions.BufferSize);
- return FindFactory(stream).Open(stream, readerOptions);
+ return FindFactory(stream).OpenArchive(stream, readerOptions);
}
- ///
- /// Opens an Archive for random access asynchronously
- ///
- ///
- ///
- ///
- ///
- public static async ValueTask OpenAsync(
+ public static async ValueTask OpenAsyncArchive(
Stream stream,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
@@ -41,14 +28,11 @@ public static async ValueTask OpenAsync(
{
readerOptions ??= new ReaderOptions();
stream = SharpCompressStream.Create(stream, bufferSize: readerOptions.BufferSize);
- var factory = await FindFactoryAsync(stream, cancellationToken)
- .ConfigureAwait(false);
- return await factory
- .OpenAsync(stream, readerOptions, cancellationToken)
- .ConfigureAwait(false);
+ var factory = await FindFactoryAsync(stream, cancellationToken);
+ return factory.OpenAsyncArchive(stream, readerOptions);
}
- public static IWritableArchive Create(ArchiveType type)
+ public static IWritableArchive CreateArchive(ArchiveType type)
{
var factory = Factory
.Factories.OfType()
@@ -56,58 +40,36 @@ public static IWritableArchive Create(ArchiveType type)
if (factory != null)
{
- return factory.CreateWriteableArchive();
+ return factory.CreateArchive();
}
throw new NotSupportedException("Cannot create Archives of type: " + type);
}
- ///
- /// Constructor expects a filepath to an existing file.
- ///
- ///
- ///
- public static IArchive Open(string filePath, ReaderOptions? options = null)
+ public static IArchive OpenArchive(string filePath, ReaderOptions? options = null)
{
filePath.NotNullOrEmpty(nameof(filePath));
- return Open(new FileInfo(filePath), options);
+ return OpenArchive(new FileInfo(filePath), options);
}
- ///
- /// Opens an Archive from a filepath asynchronously.
- ///
- ///
- ///
- ///
- public static ValueTask OpenAsync(
+ public static ValueTask OpenAsyncArchive(
string filePath,
ReaderOptions? options = null,
CancellationToken cancellationToken = default
)
{
filePath.NotNullOrEmpty(nameof(filePath));
- return OpenAsync(new FileInfo(filePath), options, cancellationToken);
+ return OpenAsyncArchive(new FileInfo(filePath), options, cancellationToken);
}
- ///
- /// Constructor with a FileInfo object to an existing file.
- ///
- ///
- ///
- public static IArchive Open(FileInfo fileInfo, ReaderOptions? options = null)
+ public static IArchive OpenArchive(FileInfo fileInfo, ReaderOptions? options = null)
{
options ??= new ReaderOptions { LeaveStreamOpen = false };
- return FindFactory(fileInfo).Open(fileInfo, options);
+ return FindFactory(fileInfo).OpenArchive(fileInfo, options);
}
- ///
- /// Opens an Archive from a FileInfo object asynchronously.
- ///
- ///
- ///
- ///
- public static async ValueTask OpenAsync(
+ public static async ValueTask OpenAsyncArchive(
FileInfo fileInfo,
ReaderOptions? options = null,
CancellationToken cancellationToken = default
@@ -115,17 +77,14 @@ public static async ValueTask OpenAsync(
{
options ??= new ReaderOptions { LeaveStreamOpen = false };
- var factory = await FindFactoryAsync(fileInfo, cancellationToken)
- .ConfigureAwait(false);
- return await factory.OpenAsync(fileInfo, options, cancellationToken).ConfigureAwait(false);
+ var factory = await FindFactoryAsync(fileInfo, cancellationToken);
+ return factory.OpenAsyncArchive(fileInfo, options, cancellationToken);
}
- ///
- /// Constructor with IEnumerable FileInfo objects, multi and split support.
- ///
- ///
- ///
- public static IArchive Open(IEnumerable fileInfos, ReaderOptions? options = null)
+ public static IArchive OpenArchive(
+ IEnumerable fileInfos,
+ ReaderOptions? options = null
+ )
{
fileInfos.NotNull(nameof(fileInfos));
var filesArray = fileInfos.ToArray();
@@ -137,22 +96,16 @@ public static IArchive Open(IEnumerable fileInfos, ReaderOptions? opti
var fileInfo = filesArray[0];
if (filesArray.Length == 1)
{
- return Open(fileInfo, options);
+ return OpenArchive(fileInfo, options);
}
fileInfo.NotNull(nameof(fileInfo));
options ??= new ReaderOptions { LeaveStreamOpen = false };
- return FindFactory(fileInfo).Open(filesArray, options);
+ return FindFactory(fileInfo).OpenArchive(filesArray, options);
}
- ///
- /// Opens a multi-part archive from files asynchronously.
- ///
- ///
- ///
- ///
- public static async ValueTask OpenAsync(
+ public static async ValueTask OpenAsyncArchive(
IEnumerable fileInfos,
ReaderOptions? options = null,
CancellationToken cancellationToken = default
@@ -168,24 +121,17 @@ public static async ValueTask OpenAsync(
var fileInfo = filesArray[0];
if (filesArray.Length == 1)
{
- return await OpenAsync(fileInfo, options, cancellationToken).ConfigureAwait(false);
+ return await OpenAsyncArchive(fileInfo, options, cancellationToken);
}
fileInfo.NotNull(nameof(fileInfo));
options ??= new ReaderOptions { LeaveStreamOpen = false };
- var factory = FindFactory(fileInfo);
- return await factory
- .OpenAsync(filesArray, options, cancellationToken)
- .ConfigureAwait(false);
+ var factory = await FindFactoryAsync(fileInfo, cancellationToken);
+ return factory.OpenAsyncArchive(filesArray, options, cancellationToken);
}
- ///
- /// Constructor with IEnumerable FileInfo objects, multi and split support.
- ///
- ///
- ///
- public static IArchive Open(IEnumerable streams, ReaderOptions? options = null)
+ public static IArchive OpenArchive(IEnumerable streams, ReaderOptions? options = null)
{
streams.NotNull(nameof(streams));
var streamsArray = streams.ToArray();
@@ -197,22 +143,16 @@ public static IArchive Open(IEnumerable streams, ReaderOptions? options
var firstStream = streamsArray[0];
if (streamsArray.Length == 1)
{
- return Open(firstStream, options);
+ return OpenArchive(firstStream, options);
}
firstStream.NotNull(nameof(firstStream));
options ??= new ReaderOptions();
- return FindFactory(firstStream).Open(streamsArray, options);
+ return FindFactory(firstStream).OpenArchive(streamsArray, options);
}
- ///
- /// Opens a multi-part archive from streams asynchronously.
- ///
- ///
- ///
- ///
- public static async ValueTask OpenAsync(
+ public static async ValueTask OpenAsyncArchive(
IEnumerable streams,
ReaderOptions? options = null,
CancellationToken cancellationToken = default
@@ -229,28 +169,23 @@ public static async ValueTask OpenAsync(
var firstStream = streamsArray[0];
if (streamsArray.Length == 1)
{
- return await OpenAsync(firstStream, options, cancellationToken).ConfigureAwait(false);
+ return await OpenAsyncArchive(firstStream, options, cancellationToken);
}
firstStream.NotNull(nameof(firstStream));
options ??= new ReaderOptions();
var factory = FindFactory(firstStream);
- return await factory
- .OpenAsync(streamsArray, options, cancellationToken)
- .ConfigureAwait(false);
+ return factory.OpenAsyncArchive(streamsArray, options);
}
- ///
- /// Extract to specific directory, retaining filename
- ///
public static void WriteToDirectory(
string sourceArchive,
string destinationDirectory,
ExtractionOptions? options = null
)
{
- using var archive = Open(sourceArchive);
+ using var archive = OpenArchive(sourceArchive);
archive.WriteToDirectory(destinationDirectory, options);
}
@@ -382,22 +317,12 @@ public static bool IsArchive(
return false;
}
- ///
- /// From a passed in archive (zip, rar, 7z, 001), return all parts.
- ///
- ///
- ///
public static IEnumerable GetFileParts(string part1)
{
part1.NotNullOrEmpty(nameof(part1));
return GetFileParts(new FileInfo(part1)).Select(a => a.FullName);
}
- ///
- /// From a passed in archive (zip, rar, 7z, 001), return all parts.
- ///
- ///
- ///
public static IEnumerable GetFileParts(FileInfo part1)
{
part1.NotNull(nameof(part1));
@@ -411,7 +336,7 @@ public static IEnumerable GetFileParts(FileInfo part1)
if (part != null)
{
yield return part;
- while ((part = factory.GetFilePart(i++, part1)) != null) //tests split too
+ while ((part = factory.GetFilePart(i++, part1)) != null)
{
yield return part;
}
diff --git a/src/SharpCompress/Archives/AutoArchiveFactory.cs b/src/SharpCompress/Archives/AutoArchiveFactory.cs
index 7751c7e01..f588b0851 100644
--- a/src/SharpCompress/Archives/AutoArchiveFactory.cs
+++ b/src/SharpCompress/Archives/AutoArchiveFactory.cs
@@ -31,21 +31,22 @@ public ValueTask IsArchiveAsync(
public FileInfo? GetFilePart(int index, FileInfo part1) => throw new NotSupportedException();
- public IArchive Open(Stream stream, ReaderOptions? readerOptions = null) =>
- ArchiveFactory.Open(stream, readerOptions);
+ public IArchive OpenArchive(Stream stream, ReaderOptions? readerOptions = null) =>
+ ArchiveFactory.OpenArchive(stream, readerOptions);
- public async ValueTask OpenAsync(
- Stream stream,
- ReaderOptions? readerOptions = null,
- CancellationToken cancellationToken = default
- ) => await ArchiveFactory.OpenAsync(stream, readerOptions, cancellationToken);
+ public IAsyncArchive OpenAsyncArchive(Stream stream, ReaderOptions? readerOptions = null) =>
+ (IAsyncArchive)OpenArchive(stream, readerOptions);
- public IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) =>
- ArchiveFactory.Open(fileInfo, readerOptions);
+ public IArchive OpenArchive(FileInfo fileInfo, ReaderOptions? readerOptions = null) =>
+ ArchiveFactory.OpenArchive(fileInfo, readerOptions);
- public async ValueTask OpenAsync(
+ public IAsyncArchive OpenAsyncArchive(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
- ) => await ArchiveFactory.OpenAsync(fileInfo, readerOptions, cancellationToken);
+ )
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ return (IAsyncArchive)OpenArchive(fileInfo, readerOptions);
+ }
}
diff --git a/src/SharpCompress/Archives/GZip/GZipArchive.Factory.cs b/src/SharpCompress/Archives/GZip/GZipArchive.Factory.cs
new file mode 100644
index 000000000..4d413e63f
--- /dev/null
+++ b/src/SharpCompress/Archives/GZip/GZipArchive.Factory.cs
@@ -0,0 +1,197 @@
+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.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
+#endif
+{
+ public static IWritableAsyncArchive OpenAsyncArchive(
+ string path,
+ ReaderOptions? readerOptions = null,
+ CancellationToken cancellationToken = default
+ )
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ path.NotNullOrEmpty(nameof(path));
+ return (IWritableAsyncArchive)OpenArchive(
+ new FileInfo(path),
+ readerOptions ?? new ReaderOptions()
+ );
+ }
+
+ public static IWritableArchive OpenArchive(string filePath, ReaderOptions? readerOptions = null)
+ {
+ filePath.NotNullOrEmpty(nameof(filePath));
+ return OpenArchive(new FileInfo(filePath), readerOptions ?? new ReaderOptions());
+ }
+
+ public static IWritableArchive OpenArchive(
+ FileInfo fileInfo,
+ ReaderOptions? readerOptions = null
+ )
+ {
+ fileInfo.NotNull(nameof(fileInfo));
+ return new GZipArchive(
+ new SourceStream(
+ fileInfo,
+ i => ArchiveVolumeFactory.GetFilePart(i, fileInfo),
+ readerOptions ?? new ReaderOptions()
+ )
+ );
+ }
+
+ public static IWritableArchive OpenArchive(
+ IEnumerable fileInfos,
+ ReaderOptions? readerOptions = null
+ )
+ {
+ fileInfos.NotNull(nameof(fileInfos));
+ var files = fileInfos.ToArray();
+ return new GZipArchive(
+ new SourceStream(
+ files[0],
+ i => i < files.Length ? files[i] : null,
+ readerOptions ?? new ReaderOptions()
+ )
+ );
+ }
+
+ public static IWritableArchive OpenArchive(
+ IEnumerable streams,
+ ReaderOptions? readerOptions = null
+ )
+ {
+ streams.NotNull(nameof(streams));
+ var strms = streams.ToArray();
+ return new GZipArchive(
+ new SourceStream(
+ strms[0],
+ i => i < strms.Length ? strms[i] : null,
+ readerOptions ?? new ReaderOptions()
+ )
+ );
+ }
+
+ public static IWritableArchive OpenArchive(Stream stream, ReaderOptions? readerOptions = null)
+ {
+ stream.NotNull(nameof(stream));
+
+ if (stream is not { CanSeek: true })
+ {
+ throw new ArgumentException("Stream must be seekable", nameof(stream));
+ }
+
+ return new GZipArchive(
+ new SourceStream(stream, _ => null, readerOptions ?? new ReaderOptions())
+ );
+ }
+
+ public static IWritableAsyncArchive OpenAsyncArchive(
+ Stream stream,
+ ReaderOptions? readerOptions = null,
+ CancellationToken cancellationToken = default
+ )
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ return (IWritableAsyncArchive)OpenArchive(stream, readerOptions);
+ }
+
+ public static IWritableAsyncArchive OpenAsyncArchive(
+ FileInfo fileInfo,
+ ReaderOptions? readerOptions = null,
+ CancellationToken cancellationToken = default
+ )
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ return (IWritableAsyncArchive)OpenArchive(fileInfo, readerOptions);
+ }
+
+ public static IWritableAsyncArchive OpenAsyncArchive(
+ IReadOnlyList streams,
+ ReaderOptions? readerOptions = null,
+ CancellationToken cancellationToken = default
+ )
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ return (IWritableAsyncArchive)OpenArchive(streams, readerOptions);
+ }
+
+ public static IWritableAsyncArchive OpenAsyncArchive(
+ IReadOnlyList fileInfos,
+ ReaderOptions? readerOptions = null,
+ CancellationToken cancellationToken = default
+ )
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ return (IWritableAsyncArchive)OpenArchive(fileInfos, readerOptions);
+ }
+
+ public static IWritableArchive CreateArchive() => new GZipArchive();
+
+ public static IWritableAsyncArchive CreateAsyncArchive() => new GZipArchive();
+
+ public static bool IsGZipFile(string filePath) => IsGZipFile(new FileInfo(filePath));
+
+ public static bool IsGZipFile(FileInfo fileInfo)
+ {
+ if (!fileInfo.Exists)
+ {
+ return false;
+ }
+
+ using Stream stream = fileInfo.OpenRead();
+ return IsGZipFile(stream);
+ }
+
+ public static bool IsGZipFile(Stream stream)
+ {
+ Span header = stackalloc byte[10];
+
+ if (!stream.ReadFully(header))
+ {
+ return false;
+ }
+
+ if (header[0] != 0x1F || header[1] != 0x8B || header[2] != 8)
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ public static async ValueTask IsGZipFileAsync(
+ Stream stream,
+ CancellationToken cancellationToken = default
+ )
+ {
+ byte[] header = new byte[10];
+
+ if (!await stream.ReadFullyAsync(header, cancellationToken).ConfigureAwait(false))
+ {
+ return false;
+ }
+
+ if (header[0] != 0x1F || header[1] != 0x8B || header[2] != 8)
+ {
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/src/SharpCompress/Archives/GZip/GZipArchive.cs b/src/SharpCompress/Archives/GZip/GZipArchive.cs
index 7c8c08f7c..7d4c345d1 100644
--- a/src/SharpCompress/Archives/GZip/GZipArchive.cs
+++ b/src/SharpCompress/Archives/GZip/GZipArchive.cs
@@ -14,186 +14,20 @@
namespace SharpCompress.Archives.GZip;
-public class GZipArchive : AbstractWritableArchive
+public partial class GZipArchive : AbstractWritableArchive
{
- ///
- /// Constructor expects a filepath to an existing file.
- ///
- ///
- ///
- public static GZipArchive Open(string filePath, ReaderOptions? readerOptions = null)
- {
- filePath.NotNullOrEmpty(nameof(filePath));
- return Open(new FileInfo(filePath), readerOptions ?? new ReaderOptions());
- }
-
- ///
- /// Constructor with a FileInfo object to an existing file.
- ///
- ///
- ///
- public static GZipArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null)
- {
- fileInfo.NotNull(nameof(fileInfo));
- return new GZipArchive(
- new SourceStream(
- fileInfo,
- i => ArchiveVolumeFactory.GetFilePart(i, fileInfo),
- readerOptions ?? new ReaderOptions()
- )
- );
- }
-
- ///
- /// Constructor with all file parts passed in
- ///
- ///
- ///
- public static GZipArchive Open(
- IEnumerable fileInfos,
- ReaderOptions? readerOptions = null
- )
- {
- fileInfos.NotNull(nameof(fileInfos));
- var files = fileInfos.ToArray();
- return new GZipArchive(
- new SourceStream(
- files[0],
- i => i < files.Length ? files[i] : null,
- readerOptions ?? new ReaderOptions()
- )
- );
- }
-
- ///
- /// Constructor with all stream parts passed in
- ///
- ///
- ///
- public static GZipArchive Open(IEnumerable streams, ReaderOptions? readerOptions = null)
- {
- streams.NotNull(nameof(streams));
- var strms = streams.ToArray();
- return new GZipArchive(
- new SourceStream(
- strms[0],
- i => i < strms.Length ? strms[i] : null,
- readerOptions ?? new ReaderOptions()
- )
- );
- }
-
- ///
- /// Takes a seekable Stream as a source
- ///
- ///
- ///
- public static GZipArchive Open(Stream stream, ReaderOptions? readerOptions = null)
- {
- stream.NotNull(nameof(stream));
-
- if (stream is not { CanSeek: true })
- {
- throw new ArgumentException("Stream must be seekable", nameof(stream));
- }
-
- return new GZipArchive(
- new SourceStream(stream, _ => null, readerOptions ?? new ReaderOptions())
- );
- }
-
- ///
- /// Opens a GZipArchive asynchronously from a stream.
- ///
- ///
- ///
- ///
- public static ValueTask OpenAsync(
- Stream stream,
- ReaderOptions? readerOptions = null,
- CancellationToken cancellationToken = default
- )
- {
- cancellationToken.ThrowIfCancellationRequested();
- return new(Open(stream, readerOptions));
- }
-
- ///
- /// Opens a GZipArchive asynchronously from a FileInfo.
- ///
- ///
- ///
- ///
- public static ValueTask OpenAsync(
- FileInfo fileInfo,
- ReaderOptions? readerOptions = null,
- CancellationToken cancellationToken = default
- )
- {
- cancellationToken.ThrowIfCancellationRequested();
- return new(Open(fileInfo, readerOptions));
- }
-
- ///
- /// Opens a GZipArchive asynchronously from multiple streams.
- ///
- ///
- ///
- ///
- public static ValueTask OpenAsync(
- IReadOnlyList streams,
- ReaderOptions? readerOptions = null,
- CancellationToken cancellationToken = default
- )
- {
- cancellationToken.ThrowIfCancellationRequested();
- return new(Open(streams, readerOptions));
- }
-
- ///
- /// Opens a GZipArchive asynchronously from multiple FileInfo objects.
- ///
- ///
- ///
- ///
- public static ValueTask OpenAsync(
- IReadOnlyList fileInfos,
- ReaderOptions? readerOptions = null,
- CancellationToken cancellationToken = default
- )
- {
- cancellationToken.ThrowIfCancellationRequested();
- return new(Open(fileInfos, readerOptions));
- }
-
- public static GZipArchive Create() => new();
-
- ///
- /// Constructor with a SourceStream able to handle FileInfo and Streams.
- ///
- ///
private GZipArchive(SourceStream sourceStream)
: base(ArchiveType.GZip, sourceStream) { }
+ internal GZipArchive()
+ : base(ArchiveType.GZip) { }
+
protected override IEnumerable LoadVolumes(SourceStream sourceStream)
{
sourceStream.LoadAllParts();
return sourceStream.Streams.Select(a => new GZipVolume(a, ReaderOptions, 0));
}
- public static bool IsGZipFile(string filePath) => IsGZipFile(new FileInfo(filePath));
-
- public static bool IsGZipFile(FileInfo fileInfo)
- {
- if (!fileInfo.Exists)
- {
- return false;
- }
-
- using Stream stream = fileInfo.OpenRead();
- return IsGZipFile(stream);
- }
-
public void SaveTo(string filePath) => SaveTo(new FileInfo(filePath));
public void SaveTo(FileInfo fileInfo)
@@ -215,50 +49,6 @@ public async ValueTask SaveToAsync(
.ConfigureAwait(false);
}
- public static bool IsGZipFile(Stream stream)
- {
- // read the header on the first read
- Span header = stackalloc byte[10];
-
- // workitem 8501: handle edge case (decompress empty stream)
- if (!stream.ReadFully(header))
- {
- return false;
- }
-
- if (header[0] != 0x1F || header[1] != 0x8B || header[2] != 8)
- {
- return false;
- }
-
- return true;
- }
-
- public static async ValueTask IsGZipFileAsync(
- Stream stream,
- CancellationToken cancellationToken = default
- )
- {
- // read the header on the first read
- byte[] header = new byte[10];
-
- // workitem 8501: handle edge case (decompress empty stream)
- if (!await stream.ReadFullyAsync(header, cancellationToken).ConfigureAwait(false))
- {
- return false;
- }
-
- if (header[0] != 0x1F || header[1] != 0x8B || header[2] != 8)
- {
- return false;
- }
-
- return true;
- }
-
- internal GZipArchive()
- : base(ArchiveType.GZip) { }
-
protected override GZipArchiveEntry CreateEntryInternal(
string filePath,
Stream source,
@@ -329,7 +119,18 @@ protected override IEnumerable LoadEntries(IEnumerable LoadEntriesAsync(
+ IAsyncEnumerable volumes
+ )
+ {
+ var stream = (await volumes.SingleAsync()).Stream;
+ yield return new GZipArchiveEntry(
+ this,
+ await GZipFilePart.CreateAsync(stream, ReaderOptions.ArchiveEncoding)
);
}
@@ -337,13 +138,13 @@ protected override IReader CreateReaderForSolidExtraction()
{
var stream = Volumes.Single().Stream;
stream.Position = 0;
- return GZipReader.Open(stream);
+ return GZipReader.OpenReader(stream);
}
protected override ValueTask CreateReaderForSolidExtractionAsync()
{
var stream = Volumes.Single().Stream;
stream.Position = 0;
- return new(GZipReader.Open(stream));
+ return new((IAsyncReader)GZipReader.OpenReader(stream));
}
}
diff --git a/src/SharpCompress/Archives/GZip/GZipArchiveEntry.cs b/src/SharpCompress/Archives/GZip/GZipArchiveEntry.cs
index 049c7262a..a8f7b7772 100644
--- a/src/SharpCompress/Archives/GZip/GZipArchiveEntry.cs
+++ b/src/SharpCompress/Archives/GZip/GZipArchiveEntry.cs
@@ -23,12 +23,10 @@ public virtual Stream OpenEntryStream()
return Parts.Single().GetCompressedStream().NotNull();
}
- public async ValueTask OpenEntryStreamAsync(
- CancellationToken cancellationToken = default
- )
+ public ValueTask OpenEntryStreamAsync(CancellationToken cancellationToken = default)
{
// GZip synchronous implementation is fast enough, just wrap it
- return OpenEntryStream();
+ return new(OpenEntryStream());
}
#region IArchiveEntry Members
diff --git a/src/SharpCompress/Archives/IArchive.cs b/src/SharpCompress/Archives/IArchive.cs
index 3ed7490d3..6016214b7 100644
--- a/src/SharpCompress/Archives/IArchive.cs
+++ b/src/SharpCompress/Archives/IArchive.cs
@@ -38,5 +38,10 @@ public interface IArchive : IDisposable
///
/// The total size of the files as uncompressed in the archive.
///
- long TotalUncompressSize { get; }
+ long TotalUncompressedSize { get; }
+
+ ///
+ /// Returns whether the archive is encrypted.
+ ///
+ bool IsEncrypted { get; }
}
diff --git a/src/SharpCompress/Archives/IArchiveExtensions.cs b/src/SharpCompress/Archives/IArchiveExtensions.cs
index c1d2ac987..80857a25c 100644
--- a/src/SharpCompress/Archives/IArchiveExtensions.cs
+++ b/src/SharpCompress/Archives/IArchiveExtensions.cs
@@ -8,7 +8,6 @@ namespace SharpCompress.Archives;
public static class IArchiveExtensions
{
- /// The archive to extract.
extension(IArchive archive)
{
///
@@ -23,7 +22,6 @@ public void WriteToDirectory(
IProgress? progress = null
)
{
- // For solid archives (Rar, 7Zip), use the optimized reader-based approach
if (archive.IsSolid || archive.Type == ArchiveType.SevenZip)
{
using var reader = archive.ExtractAllEntries();
@@ -31,7 +29,6 @@ public void WriteToDirectory(
}
else
{
- // For non-solid archives, extract entries directly
archive.WriteToDirectoryInternal(destinationDirectory, options, progress);
}
}
@@ -42,14 +39,10 @@ private void WriteToDirectoryInternal(
IProgress? progress
)
{
- // Prepare for progress reporting
- var totalBytes = archive.TotalUncompressSize;
+ var totalBytes = archive.TotalUncompressedSize;
var bytesRead = 0L;
-
- // Tracking for created directories.
var seenDirectories = new HashSet();
- // Extract
foreach (var entry in archive.Entries)
{
if (entry.IsDirectory)
@@ -68,10 +61,8 @@ private void WriteToDirectoryInternal(
continue;
}
- // Use the entry's WriteToDirectory method which respects ExtractionOptions
entry.WriteToDirectory(destinationDirectory, options);
- // Update progress
bytesRead += entry.Size;
progress?.Report(
new ProgressReport(entry.Key ?? string.Empty, bytesRead, totalBytes)
diff --git a/src/SharpCompress/Archives/IArchiveFactory.cs b/src/SharpCompress/Archives/IArchiveFactory.cs
index 1c1253f6a..eb80e072a 100644
--- a/src/SharpCompress/Archives/IArchiveFactory.cs
+++ b/src/SharpCompress/Archives/IArchiveFactory.cs
@@ -1,6 +1,5 @@
using System.IO;
using System.Threading;
-using System.Threading.Tasks;
using SharpCompress.Factories;
using SharpCompress.Readers;
@@ -26,26 +25,21 @@ public interface IArchiveFactory : IFactory
///
/// An open, readable and seekable stream.
/// reading options.
- IArchive Open(Stream stream, ReaderOptions? readerOptions = null);
+ IArchive OpenArchive(Stream stream, ReaderOptions? readerOptions = null);
///
/// Opens an Archive for random access asynchronously.
///
/// An open, readable and seekable stream.
/// reading options.
- /// Cancellation token.
- ValueTask OpenAsync(
- Stream stream,
- ReaderOptions? readerOptions = null,
- CancellationToken cancellationToken = default
- );
+ IAsyncArchive OpenAsyncArchive(Stream stream, ReaderOptions? readerOptions = null);
///
/// Constructor with a FileInfo object to an existing file.
///
/// the file to open.
/// reading options.
- IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null);
+ IArchive OpenArchive(FileInfo fileInfo, ReaderOptions? readerOptions = null);
///
/// Opens an Archive from a FileInfo object asynchronously.
@@ -53,7 +47,7 @@ ValueTask OpenAsync(
/// the file to open.
/// reading options.
/// Cancellation token.
- ValueTask OpenAsync(
+ IAsyncArchive OpenAsyncArchive(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
diff --git a/src/SharpCompress/Archives/IArchiveOpenable.cs b/src/SharpCompress/Archives/IArchiveOpenable.cs
new file mode 100644
index 000000000..e5ae52b32
--- /dev/null
+++ b/src/SharpCompress/Archives/IArchiveOpenable.cs
@@ -0,0 +1,40 @@
+#if NET8_0_OR_GREATER
+using System.IO;
+using System.Threading;
+using SharpCompress.Readers;
+
+namespace SharpCompress.Archives;
+
+public interface IArchiveOpenable
+ where TSync : IArchive
+ where TASync : IAsyncArchive
+{
+ public static abstract TSync OpenArchive(string filePath, ReaderOptions? readerOptions = null);
+
+ public static abstract TSync OpenArchive(
+ FileInfo fileInfo,
+ ReaderOptions? readerOptions = null
+ );
+
+ public static abstract TSync OpenArchive(Stream stream, ReaderOptions? readerOptions = null);
+
+ public static abstract TASync OpenAsyncArchive(
+ string path,
+ ReaderOptions? readerOptions = null,
+ CancellationToken cancellationToken = default
+ );
+
+ public static abstract TASync OpenAsyncArchive(
+ Stream stream,
+ ReaderOptions? readerOptions = null,
+ CancellationToken cancellationToken = default
+ );
+
+ public static abstract TASync OpenAsyncArchive(
+ FileInfo fileInfo,
+ ReaderOptions? readerOptions = null,
+ CancellationToken cancellationToken = default
+ );
+}
+
+#endif
diff --git a/src/SharpCompress/Archives/IAsyncArchive.cs b/src/SharpCompress/Archives/IAsyncArchive.cs
index bd3f290ea..2994e8b86 100644
--- a/src/SharpCompress/Archives/IAsyncArchive.cs
+++ b/src/SharpCompress/Archives/IAsyncArchive.cs
@@ -39,5 +39,10 @@ public interface IAsyncArchive : IAsyncDisposable
///
/// The total size of the files as uncompressed in the archive.
///
- ValueTask TotalUncompressSizeAsync();
+ ValueTask TotalUncompressedSizeAsync();
+
+ ///
+ /// Returns whether the archive is encrypted.
+ ///
+ ValueTask IsEncryptedAsync();
}
diff --git a/src/SharpCompress/Archives/IAsyncArchiveExtensions.cs b/src/SharpCompress/Archives/IAsyncArchiveExtensions.cs
index b6b0cad1e..df4cb05c4 100644
--- a/src/SharpCompress/Archives/IAsyncArchiveExtensions.cs
+++ b/src/SharpCompress/Archives/IAsyncArchiveExtensions.cs
@@ -10,84 +10,83 @@ namespace SharpCompress.Archives;
public static class IAsyncArchiveExtensions
{
- ///
- /// Extract to specific directory asynchronously with progress reporting and cancellation support
- ///
- /// The archive to extract.
- /// The folder to extract into.
- /// Extraction options.
- /// Optional progress reporter for tracking extraction progress.
- /// Optional cancellation token.
- public static async Task WriteToDirectoryAsync(
- this IAsyncArchive archive,
- string destinationDirectory,
- ExtractionOptions? options = null,
- IProgress? progress = null,
- CancellationToken cancellationToken = default
- )
+ extension(IAsyncArchive archive)
{
- // For solid archives (Rar, 7Zip), use the optimized reader-based approach
- if (await archive.IsSolidAsync() || archive.Type == ArchiveType.SevenZip)
+ ///
+ /// Extract to specific directory asynchronously with progress reporting and cancellation support
+ ///
+ /// The archive to extract.
+ /// The folder to extract into.
+ /// Extraction options.
+ /// Optional progress reporter for tracking extraction progress.
+ /// Optional cancellation token.
+ public async Task WriteToDirectoryAsync(
+ string destinationDirectory,
+ ExtractionOptions? options = null,
+ IProgress? progress = null,
+ CancellationToken cancellationToken = default
+ )
{
- await using var reader = await archive.ExtractAllEntriesAsync();
- await reader.WriteAllToDirectoryAsync(destinationDirectory, options, cancellationToken);
- }
- else
- {
- // For non-solid archives, extract entries directly
- await archive.WriteToDirectoryAsyncInternal(
- destinationDirectory,
- options,
- progress,
- cancellationToken
- );
+ if (await archive.IsSolidAsync() || archive.Type == ArchiveType.SevenZip)
+ {
+ await using var reader = await archive.ExtractAllEntriesAsync();
+ await reader.WriteAllToDirectoryAsync(
+ destinationDirectory,
+ options,
+ cancellationToken
+ );
+ }
+ else
+ {
+ await archive.WriteToDirectoryAsyncInternal(
+ destinationDirectory,
+ options,
+ progress,
+ cancellationToken
+ );
+ }
}
- }
-
- private static async Task WriteToDirectoryAsyncInternal(
- this IAsyncArchive archive,
- string destinationDirectory,
- ExtractionOptions? options,
- IProgress? progress,
- CancellationToken cancellationToken
- )
- {
- // Prepare for progress reporting
- var totalBytes = await archive.TotalUncompressSizeAsync();
- var bytesRead = 0L;
- // Tracking for created directories.
- var seenDirectories = new HashSet();
-
- // Extract
- await foreach (var entry in archive.EntriesAsync.WithCancellation(cancellationToken))
+ private async Task WriteToDirectoryAsyncInternal(
+ string destinationDirectory,
+ ExtractionOptions? options,
+ IProgress? progress,
+ CancellationToken cancellationToken
+ )
{
- cancellationToken.ThrowIfCancellationRequested();
+ var totalBytes = await archive.TotalUncompressedSizeAsync();
+ var bytesRead = 0L;
+ var seenDirectories = new HashSet();
- if (entry.IsDirectory)
+ await foreach (var entry in archive.EntriesAsync.WithCancellation(cancellationToken))
{
- var dirPath = Path.Combine(
- destinationDirectory,
- entry.Key.NotNull("Entry Key is null")
- );
- if (
- Path.GetDirectoryName(dirPath + "/") is { } parentDirectory
- && seenDirectories.Add(dirPath)
- )
+ cancellationToken.ThrowIfCancellationRequested();
+
+ if (entry.IsDirectory)
{
- Directory.CreateDirectory(parentDirectory);
+ var dirPath = Path.Combine(
+ destinationDirectory,
+ entry.Key.NotNull("Entry Key is null")
+ );
+ if (
+ Path.GetDirectoryName(dirPath + "/") is { } parentDirectory
+ && seenDirectories.Add(dirPath)
+ )
+ {
+ Directory.CreateDirectory(parentDirectory);
+ }
+ continue;
}
- continue;
- }
- // Use the entry's WriteToDirectoryAsync method which respects ExtractionOptions
- await entry
- .WriteToDirectoryAsync(destinationDirectory, options, cancellationToken)
- .ConfigureAwait(false);
+ await entry
+ .WriteToDirectoryAsync(destinationDirectory, options, cancellationToken)
+ .ConfigureAwait(false);
- // Update progress
- bytesRead += entry.Size;
- progress?.Report(new ProgressReport(entry.Key ?? string.Empty, bytesRead, totalBytes));
+ bytesRead += entry.Size;
+ progress?.Report(
+ new ProgressReport(entry.Key ?? string.Empty, bytesRead, totalBytes)
+ );
+ }
}
}
}
diff --git a/src/SharpCompress/Archives/IMultiArchiveFactory.cs b/src/SharpCompress/Archives/IMultiArchiveFactory.cs
index 4fa94d7fe..fc418ad42 100644
--- a/src/SharpCompress/Archives/IMultiArchiveFactory.cs
+++ b/src/SharpCompress/Archives/IMultiArchiveFactory.cs
@@ -1,7 +1,6 @@
using System.Collections.Generic;
using System.IO;
using System.Threading;
-using System.Threading.Tasks;
using SharpCompress.Factories;
using SharpCompress.Readers;
@@ -27,18 +26,16 @@ public interface IMultiArchiveFactory : IFactory
///
///
/// reading options.
- IArchive Open(IReadOnlyList streams, ReaderOptions? readerOptions = null);
+ IArchive OpenArchive(IReadOnlyList streams, ReaderOptions? readerOptions = null);
///
/// Opens a multi-part archive from streams asynchronously.
///
///
/// reading options.
- /// Cancellation token.
- ValueTask OpenAsync(
+ IAsyncArchive OpenAsyncArchive(
IReadOnlyList streams,
- ReaderOptions? readerOptions = null,
- CancellationToken cancellationToken = default
+ ReaderOptions? readerOptions = null
);
///
@@ -46,7 +43,7 @@ ValueTask OpenAsync(
///
///
/// reading options.
- IArchive Open(IReadOnlyList fileInfos, ReaderOptions? readerOptions = null);
+ IArchive OpenArchive(IReadOnlyList fileInfos, ReaderOptions? readerOptions = null);
///
/// Opens a multi-part archive from files asynchronously.
@@ -54,7 +51,7 @@ ValueTask OpenAsync(
///
/// reading options.
/// Cancellation token.
- ValueTask OpenAsync(
+ IAsyncArchive OpenAsyncArchive(
IReadOnlyList fileInfos,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
diff --git a/src/SharpCompress/Archives/IMultiArchiveOpenable.cs b/src/SharpCompress/Archives/IMultiArchiveOpenable.cs
new file mode 100644
index 000000000..53f375c09
--- /dev/null
+++ b/src/SharpCompress/Archives/IMultiArchiveOpenable.cs
@@ -0,0 +1,35 @@
+#if NET8_0_OR_GREATER
+using System.Collections.Generic;
+using System.IO;
+using System.Threading;
+using SharpCompress.Readers;
+
+namespace SharpCompress.Archives;
+
+public interface IMultiArchiveOpenable
+ where TSync : IArchive
+ where TASync : IAsyncArchive
+{
+ public static abstract TSync OpenArchive(
+ IEnumerable fileInfos,
+ ReaderOptions? readerOptions = null
+ );
+
+ public static abstract TSync OpenArchive(
+ IEnumerable streams,
+ ReaderOptions? readerOptions = null
+ );
+
+ public static abstract TASync OpenAsyncArchive(
+ IReadOnlyList streams,
+ ReaderOptions? readerOptions = null,
+ CancellationToken cancellationToken = default
+ );
+
+ public static abstract TASync OpenAsyncArchive(
+ IReadOnlyList fileInfos,
+ ReaderOptions? readerOptions = null,
+ CancellationToken cancellationToken = default
+ );
+}
+#endif
diff --git a/src/SharpCompress/Archives/IWritableArchive.cs b/src/SharpCompress/Archives/IWritableArchive.cs
index 74d8da763..28496b48e 100644
--- a/src/SharpCompress/Archives/IWritableArchive.cs
+++ b/src/SharpCompress/Archives/IWritableArchive.cs
@@ -6,8 +6,17 @@
namespace SharpCompress.Archives;
-public interface IWritableArchive : IArchive
+public interface IWritableArchiveCommon
{
+ ///
+ /// Use this to pause entry rebuilding when adding large collections of entries. Dispose when complete. A using statement is recommended.
+ ///
+ /// IDisposeable to resume entry rebuilding
+ IDisposable PauseEntryRebuilding();
+
+ ///
+ /// Removes the specified entry from the archive.
+ ///
void RemoveEntry(IArchiveEntry entry);
IArchiveEntry AddEntry(
@@ -19,18 +28,24 @@ IArchiveEntry AddEntry(
);
IArchiveEntry AddDirectoryEntry(string key, DateTime? modified = null);
+}
+public interface IWritableArchive : IArchive, IWritableArchiveCommon
+{
+ ///
+ /// Saves the archive to the specified stream using the given writer options.
+ ///
void SaveTo(Stream stream, WriterOptions options);
+}
+public interface IWritableAsyncArchive : IAsyncArchive, IWritableArchiveCommon
+{
+ ///
+ /// Asynchronously saves the archive to the specified stream using the given writer options.
+ ///
ValueTask SaveToAsync(
Stream stream,
WriterOptions options,
CancellationToken cancellationToken = default
);
-
- ///
- /// Use this to pause entry rebuilding when adding large collections of entries. Dispose when complete. A using statement is recommended.
- ///
- /// IDisposeable to resume entry rebuilding
- IDisposable PauseEntryRebuilding();
}
diff --git a/src/SharpCompress/Archives/IWritableArchiveCommonExtensions.cs b/src/SharpCompress/Archives/IWritableArchiveCommonExtensions.cs
new file mode 100644
index 000000000..7f276ecee
--- /dev/null
+++ b/src/SharpCompress/Archives/IWritableArchiveCommonExtensions.cs
@@ -0,0 +1,59 @@
+using System;
+using System.IO;
+
+namespace SharpCompress.Archives;
+
+public static class IWritableArchiveCommonExtensions
+{
+ extension(IWritableArchiveCommon writableArchive)
+ {
+ public void AddAllFromDirectory(
+ string filePath,
+ string searchPattern = "*.*",
+ SearchOption searchOption = SearchOption.AllDirectories
+ )
+ {
+ using (writableArchive.PauseEntryRebuilding())
+ {
+ foreach (
+ var path in Directory.EnumerateFiles(filePath, searchPattern, searchOption)
+ )
+ {
+ var fileInfo = new FileInfo(path);
+ writableArchive.AddEntry(
+ path.Substring(filePath.Length),
+ fileInfo.OpenRead(),
+ true,
+ fileInfo.Length,
+ fileInfo.LastWriteTime
+ );
+ }
+ }
+ }
+
+ public IArchiveEntry AddEntry(string key, string file) =>
+ writableArchive.AddEntry(key, new FileInfo(file));
+
+ public IArchiveEntry AddEntry(
+ string key,
+ Stream source,
+ long size = 0,
+ DateTime? modified = null
+ ) => writableArchive.AddEntry(key, source, false, size, modified);
+
+ public IArchiveEntry AddEntry(string key, FileInfo fileInfo)
+ {
+ if (!fileInfo.Exists)
+ {
+ throw new ArgumentException("FileInfo does not exist.");
+ }
+ return writableArchive.AddEntry(
+ key,
+ fileInfo.OpenRead(),
+ true,
+ fileInfo.Length,
+ fileInfo.LastWriteTime
+ );
+ }
+ }
+}
diff --git a/src/SharpCompress/Archives/IWritableArchiveExtensions.cs b/src/SharpCompress/Archives/IWritableArchiveExtensions.cs
index 60ec83d85..60971bfc1 100644
--- a/src/SharpCompress/Archives/IWritableArchiveExtensions.cs
+++ b/src/SharpCompress/Archives/IWritableArchiveExtensions.cs
@@ -1,106 +1,20 @@
-using System;
using System.IO;
-using System.Threading;
-using System.Threading.Tasks;
+using SharpCompress.Common;
using SharpCompress.Writers;
namespace SharpCompress.Archives;
public static class IWritableArchiveExtensions
{
- public static void AddEntry(
- this IWritableArchive writableArchive,
- string entryPath,
- string filePath
- )
+ extension(IWritableArchive writableArchive)
{
- var fileInfo = new FileInfo(filePath);
- if (!fileInfo.Exists)
- {
- throw new FileNotFoundException("Could not AddEntry: " + filePath);
- }
- writableArchive.AddEntry(
- entryPath,
- new FileInfo(filePath).OpenRead(),
- true,
- fileInfo.Length,
- fileInfo.LastWriteTime
- );
- }
+ public void SaveTo(string filePath, WriterOptions? options = null) =>
+ writableArchive.SaveTo(new FileInfo(filePath), options ?? new(CompressionType.Deflate));
- public static void SaveTo(
- this IWritableArchive writableArchive,
- string filePath,
- WriterOptions options
- ) => writableArchive.SaveTo(new FileInfo(filePath), options);
-
- public static void SaveTo(
- this IWritableArchive writableArchive,
- FileInfo fileInfo,
- WriterOptions options
- )
- {
- using var stream = fileInfo.Open(FileMode.Create, FileAccess.Write);
- writableArchive.SaveTo(stream, options);
- }
-
- public static ValueTask SaveToAsync(
- this IWritableArchive writableArchive,
- string filePath,
- WriterOptions options,
- CancellationToken cancellationToken = default
- ) => writableArchive.SaveToAsync(new FileInfo(filePath), options, cancellationToken);
-
- public static async ValueTask SaveToAsync(
- this IWritableArchive writableArchive,
- FileInfo fileInfo,
- WriterOptions options,
- CancellationToken cancellationToken = default
- )
- {
- using var stream = fileInfo.Open(FileMode.Create, FileAccess.Write);
- await writableArchive.SaveToAsync(stream, options, cancellationToken).ConfigureAwait(false);
- }
-
- public static void AddAllFromDirectory(
- this IWritableArchive writableArchive,
- string filePath,
- string searchPattern = "*.*",
- SearchOption searchOption = SearchOption.AllDirectories
- )
- {
- using (writableArchive.PauseEntryRebuilding())
- {
- foreach (var path in Directory.EnumerateFiles(filePath, searchPattern, searchOption))
- {
- var fileInfo = new FileInfo(path);
- writableArchive.AddEntry(
- path.Substring(filePath.Length),
- fileInfo.OpenRead(),
- true,
- fileInfo.Length,
- fileInfo.LastWriteTime
- );
- }
- }
- }
-
- public static IArchiveEntry AddEntry(
- this IWritableArchive writableArchive,
- string key,
- FileInfo fileInfo
- )
- {
- if (!fileInfo.Exists)
+ public void SaveTo(FileInfo fileInfo, WriterOptions? options = null)
{
- throw new ArgumentException("FileInfo does not exist.");
+ using var stream = fileInfo.Open(FileMode.Create, FileAccess.Write);
+ writableArchive.SaveTo(stream, options ?? new(CompressionType.Deflate));
}
- return writableArchive.AddEntry(
- key,
- fileInfo.OpenRead(),
- true,
- fileInfo.Length,
- fileInfo.LastWriteTime
- );
}
}
diff --git a/src/SharpCompress/Archives/IWritableArchiveOpenable.cs b/src/SharpCompress/Archives/IWritableArchiveOpenable.cs
new file mode 100644
index 000000000..b0b761a2f
--- /dev/null
+++ b/src/SharpCompress/Archives/IWritableArchiveOpenable.cs
@@ -0,0 +1,10 @@
+#if NET8_0_OR_GREATER
+namespace SharpCompress.Archives;
+
+public interface IWritableArchiveOpenable
+ : IArchiveOpenable
+{
+ public static abstract IWritableArchive CreateArchive();
+ public static abstract IWritableAsyncArchive CreateAsyncArchive();
+}
+#endif
diff --git a/src/SharpCompress/Archives/IWritableAsyncArchiveExtensions.cs b/src/SharpCompress/Archives/IWritableAsyncArchiveExtensions.cs
new file mode 100644
index 000000000..a69bfc0a3
--- /dev/null
+++ b/src/SharpCompress/Archives/IWritableAsyncArchiveExtensions.cs
@@ -0,0 +1,36 @@
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+using SharpCompress.Common;
+using SharpCompress.Writers;
+
+namespace SharpCompress.Archives;
+
+public static class IWritableAsyncArchiveExtensions
+{
+ extension(IWritableAsyncArchive writableArchive)
+ {
+ public ValueTask SaveToAsync(
+ string filePath,
+ WriterOptions? options = null,
+ CancellationToken cancellationToken = default
+ ) =>
+ writableArchive.SaveToAsync(
+ new FileInfo(filePath),
+ options ?? new(CompressionType.Deflate),
+ cancellationToken
+ );
+
+ public async ValueTask SaveToAsync(
+ FileInfo fileInfo,
+ WriterOptions? options = null,
+ CancellationToken cancellationToken = default
+ )
+ {
+ using var stream = fileInfo.Open(FileMode.Create, FileAccess.Write);
+ await writableArchive
+ .SaveToAsync(stream, options ?? new(CompressionType.Deflate), cancellationToken)
+ .ConfigureAwait(false);
+ }
+ }
+}
diff --git a/src/SharpCompress/Archives/IWriteableArchiveFactory.cs b/src/SharpCompress/Archives/IWriteableArchiveFactory.cs
index 4fae9f558..f1de642d0 100644
--- a/src/SharpCompress/Archives/IWriteableArchiveFactory.cs
+++ b/src/SharpCompress/Archives/IWriteableArchiveFactory.cs
@@ -16,5 +16,5 @@ public interface IWriteableArchiveFactory : Factories.IFactory
/// Creates a new, empty archive, ready to be written.
///
///
- IWritableArchive CreateWriteableArchive();
+ IWritableArchive CreateArchive();
}
diff --git a/src/SharpCompress/Archives/Rar/RarArchive.Extensions.cs b/src/SharpCompress/Archives/Rar/RarArchive.Extensions.cs
index bf6f0cd44..eb7c1f3be 100644
--- a/src/SharpCompress/Archives/Rar/RarArchive.Extensions.cs
+++ b/src/SharpCompress/Archives/Rar/RarArchive.Extensions.cs
@@ -1,18 +1,36 @@
-using System.Linq;
+using System.Linq;
+using System.Threading.Tasks;
+using SharpCompress.Common.Rar;
namespace SharpCompress.Archives.Rar;
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.
- ///
- public static bool IsFirstVolume(this RarArchive archive) =>
- archive.Volumes.First().IsFirstVolume;
+ extension(IRarArchive archive)
+ {
+ ///
+ /// 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.
+ ///
+ public bool IsFirstVolume() => archive.Volumes.Cast().First().IsFirstVolume;
- ///
- /// RarArchive is part of a multi-part archive.
- ///
- public static bool IsMultipartVolume(this RarArchive archive) =>
- archive.Volumes.First().IsMultiVolume;
+ ///
+ /// RarArchive is part of a multi-part archive.
+ ///
+ public bool IsMultipartVolume() => archive.Volumes.Cast().First().IsMultiVolume;
+ }
+
+ extension(IRarAsyncArchive archive)
+ {
+ ///
+ /// 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.
+ ///
+ public async ValueTask IsFirstVolumeAsync() =>
+ (await archive.VolumesAsync.CastAsync().FirstAsync()).IsFirstVolume;
+
+ ///
+ /// RarArchive is part of a multi-part archive.
+ ///
+ public async ValueTask IsMultipartVolumeAsync() =>
+ (await archive.VolumesAsync.CastAsync().FirstAsync()).IsMultiVolume;
+ }
}
diff --git a/src/SharpCompress/Archives/Rar/RarArchive.Factory.cs b/src/SharpCompress/Archives/Rar/RarArchive.Factory.cs
new file mode 100644
index 000000000..f3e9a99d2
--- /dev/null
+++ b/src/SharpCompress/Archives/Rar/RarArchive.Factory.cs
@@ -0,0 +1,166 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading;
+using SharpCompress.Common;
+using SharpCompress.Common.Rar;
+using SharpCompress.Common.Rar.Headers;
+using SharpCompress.Compressors.Rar;
+using SharpCompress.IO;
+using SharpCompress.Readers;
+using SharpCompress.Readers.Rar;
+
+namespace SharpCompress.Archives.Rar;
+
+public partial class RarArchive
+#if NET8_0_OR_GREATER
+ : IArchiveOpenable,
+ IMultiArchiveOpenable
+#endif
+{
+ public static IRarAsyncArchive OpenAsyncArchive(
+ string path,
+ ReaderOptions? readerOptions = null,
+ CancellationToken cancellationToken = default
+ )
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ path.NotNullOrEmpty(nameof(path));
+ return (IRarAsyncArchive)OpenArchive(new FileInfo(path), readerOptions);
+ }
+
+ public static IRarArchive OpenArchive(string filePath, ReaderOptions? options = null)
+ {
+ filePath.NotNullOrEmpty(nameof(filePath));
+ var fileInfo = new FileInfo(filePath);
+ return new RarArchive(
+ new SourceStream(
+ fileInfo,
+ i => RarArchiveVolumeFactory.GetFilePart(i, fileInfo),
+ options ?? new ReaderOptions()
+ )
+ );
+ }
+
+ public static IRarArchive OpenArchive(FileInfo fileInfo, ReaderOptions? options = null)
+ {
+ fileInfo.NotNull(nameof(fileInfo));
+ return new RarArchive(
+ new SourceStream(
+ fileInfo,
+ i => RarArchiveVolumeFactory.GetFilePart(i, fileInfo),
+ options ?? new ReaderOptions()
+ )
+ );
+ }
+
+ public static IRarArchive OpenArchive(Stream stream, ReaderOptions? options = null)
+ {
+ stream.NotNull(nameof(stream));
+
+ if (stream is not { CanSeek: true })
+ {
+ throw new ArgumentException("Stream must be seekable", nameof(stream));
+ }
+
+ return new RarArchive(new SourceStream(stream, _ => null, options ?? new ReaderOptions()));
+ }
+
+ public static IRarArchive OpenArchive(
+ IEnumerable fileInfos,
+ ReaderOptions? readerOptions = null
+ )
+ {
+ fileInfos.NotNull(nameof(fileInfos));
+ var files = fileInfos.ToArray();
+ return new RarArchive(
+ new SourceStream(
+ files[0],
+ i => i < files.Length ? files[i] : null,
+ readerOptions ?? new ReaderOptions()
+ )
+ );
+ }
+
+ public static IRarArchive OpenArchive(
+ IEnumerable streams,
+ ReaderOptions? readerOptions = null
+ )
+ {
+ streams.NotNull(nameof(streams));
+ var strms = streams.ToArray();
+ return new RarArchive(
+ new SourceStream(
+ strms[0],
+ i => i < strms.Length ? strms[i] : null,
+ readerOptions ?? new ReaderOptions()
+ )
+ );
+ }
+
+ public static IRarAsyncArchive OpenAsyncArchive(
+ Stream stream,
+ ReaderOptions? readerOptions = null,
+ CancellationToken cancellationToken = default
+ )
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ return (IRarAsyncArchive)OpenArchive(stream, readerOptions);
+ }
+
+ public static IRarAsyncArchive OpenAsyncArchive(
+ FileInfo fileInfo,
+ ReaderOptions? readerOptions = null,
+ CancellationToken cancellationToken = default
+ )
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ return (IRarAsyncArchive)OpenArchive(fileInfo, readerOptions);
+ }
+
+ public static IRarAsyncArchive OpenAsyncArchive(
+ IReadOnlyList streams,
+ ReaderOptions? readerOptions = null,
+ CancellationToken cancellationToken = default
+ )
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ return (IRarAsyncArchive)OpenArchive(streams, readerOptions);
+ }
+
+ public static IRarAsyncArchive OpenAsyncArchive(
+ IReadOnlyList fileInfos,
+ ReaderOptions? readerOptions = null,
+ CancellationToken cancellationToken = default
+ )
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ return (IRarAsyncArchive)OpenArchive(fileInfos, readerOptions);
+ }
+
+ public static bool IsRarFile(string filePath) => IsRarFile(new FileInfo(filePath));
+
+ public static bool IsRarFile(FileInfo fileInfo)
+ {
+ if (!fileInfo.Exists)
+ {
+ return false;
+ }
+ using Stream stream = fileInfo.OpenRead();
+ return IsRarFile(stream);
+ }
+
+ public static bool IsRarFile(Stream stream, ReaderOptions? options = null)
+ {
+ try
+ {
+ MarkHeader.Read(stream, true, false);
+ return true;
+ }
+ catch
+ {
+ return false;
+ }
+ }
+}
diff --git a/src/SharpCompress/Archives/Rar/RarArchive.cs b/src/SharpCompress/Archives/Rar/RarArchive.cs
index 03b8d4d90..08e896860 100644
--- a/src/SharpCompress/Archives/Rar/RarArchive.cs
+++ b/src/SharpCompress/Archives/Rar/RarArchive.cs
@@ -14,17 +14,23 @@
namespace SharpCompress.Archives.Rar;
-public class RarArchive : AbstractArchive
+public interface IRarArchiveCommon
+{
+ int MinVersion { get; }
+ int MaxVersion { get; }
+}
+
+public interface IRarArchive : IArchive, IRarArchiveCommon { }
+
+public interface IRarAsyncArchive : IAsyncArchive, IRarArchiveCommon { }
+
+public partial class RarArchive : AbstractArchive, IRarArchive
{
private bool _disposed;
internal Lazy UnpackV2017 { get; } =
new(() => new Compressors.Rar.UnpackV2017.Unpack());
internal Lazy UnpackV1 { get; } = new(() => new Compressors.Rar.UnpackV1.Unpack());
- ///
- /// Constructor with a SourceStream able to handle FileInfo and Streams.
- ///
- ///
private RarArchive(SourceStream sourceStream)
: base(ArchiveType.Rar, sourceStream) { }
@@ -47,10 +53,10 @@ protected override IEnumerable LoadEntries(IEnumerable LoadVolumes(SourceStream sourceStream)
{
- sourceStream.LoadAllParts(); //request all streams
+ sourceStream.LoadAllParts();
var streams = sourceStream.Streams.ToArray();
var i = 0;
- if (streams.Length > 1 && IsRarFile(streams[1], ReaderOptions)) //test part 2 - true = multipart not split
+ if (streams.Length > 1 && IsRarFile(streams[1], ReaderOptions))
{
sourceStream.IsVolumes = true;
streams[1].Position = 0;
@@ -63,7 +69,6 @@ protected override IEnumerable LoadVolumes(SourceStream sourceStream)
));
}
- //split mode or single file
return new StreamRarArchiveVolume(sourceStream, ReaderOptions, i++).AsEnumerable();
}
@@ -82,12 +87,12 @@ private RarReader CreateReaderForSolidExtractionInternal()
volume.Stream.Position = 0;
return volume.Stream;
});
- return RarReader.Open(streams, ReaderOptions);
+ return (RarReader)RarReader.OpenReader(streams, ReaderOptions);
}
var stream = Volumes.First().Stream;
stream.Position = 0;
- return RarReader.Open(stream, ReaderOptions);
+ return (RarReader)RarReader.OpenReader(stream, ReaderOptions);
}
public override bool IsSolid => Volumes.First().IsSolidArchive;
@@ -96,187 +101,4 @@ private RarReader CreateReaderForSolidExtractionInternal()
public virtual int MinVersion => Volumes.First().MinVersion;
public virtual int MaxVersion => Volumes.First().MaxVersion;
-
- #region Creation
- ///
- /// Constructor with a FileInfo object to an existing file.
- ///
- ///
- ///
- public static RarArchive Open(string filePath, ReaderOptions? options = null)
- {
- filePath.NotNullOrEmpty(nameof(filePath));
- var fileInfo = new FileInfo(filePath);
- return new RarArchive(
- new SourceStream(
- fileInfo,
- i => RarArchiveVolumeFactory.GetFilePart(i, fileInfo),
- options ?? new ReaderOptions()
- )
- );
- }
-
- ///
- /// Constructor with a FileInfo object to an existing file.
- ///
- ///
- ///
- public static RarArchive Open(FileInfo fileInfo, ReaderOptions? options = null)
- {
- fileInfo.NotNull(nameof(fileInfo));
- return new RarArchive(
- new SourceStream(
- fileInfo,
- i => RarArchiveVolumeFactory.GetFilePart(i, fileInfo),
- options ?? new ReaderOptions()
- )
- );
- }
-
- ///
- /// Takes a seekable Stream as a source
- ///
- ///
- ///
- public static RarArchive Open(Stream stream, ReaderOptions? options = null)
- {
- stream.NotNull(nameof(stream));
-
- if (stream is not { CanSeek: true })
- {
- throw new ArgumentException("Stream must be seekable", nameof(stream));
- }
-
- return new RarArchive(new SourceStream(stream, _ => null, options ?? new ReaderOptions()));
- }
-
- ///
- /// Constructor with all file parts passed in
- ///
- ///
- ///
- public static RarArchive Open(
- IEnumerable fileInfos,
- ReaderOptions? readerOptions = null
- )
- {
- fileInfos.NotNull(nameof(fileInfos));
- var files = fileInfos.ToArray();
- return new RarArchive(
- new SourceStream(
- files[0],
- i => i < files.Length ? files[i] : null,
- readerOptions ?? new ReaderOptions()
- )
- );
- }
-
- ///
- /// Constructor with all stream parts passed in
- ///
- ///
- ///
- public static RarArchive Open(IEnumerable streams, ReaderOptions? readerOptions = null)
- {
- streams.NotNull(nameof(streams));
- var strms = streams.ToArray();
- return new RarArchive(
- new SourceStream(
- strms[0],
- i => i < strms.Length ? strms[i] : null,
- readerOptions ?? new ReaderOptions()
- )
- );
- }
-
- ///
- /// Opens a RarArchive asynchronously from a stream.
- ///
- ///
- ///
- ///
- public static ValueTask OpenAsync(
- Stream stream,
- ReaderOptions? readerOptions = null,
- CancellationToken cancellationToken = default
- )
- {
- cancellationToken.ThrowIfCancellationRequested();
- return new(Open(stream, readerOptions));
- }
-
- ///
- /// Opens a RarArchive asynchronously from a FileInfo.
- ///
- ///
- ///
- ///
- public static ValueTask OpenAsync(
- FileInfo fileInfo,
- ReaderOptions? readerOptions = null,
- CancellationToken cancellationToken = default
- )
- {
- cancellationToken.ThrowIfCancellationRequested();
- return new(Open(fileInfo, readerOptions));
- }
-
- ///
- /// Opens a RarArchive asynchronously from multiple streams.
- ///
- ///
- ///
- ///
- public static ValueTask OpenAsync(
- IReadOnlyList streams,
- ReaderOptions? readerOptions = null,
- CancellationToken cancellationToken = default
- )
- {
- cancellationToken.ThrowIfCancellationRequested();
- return new(Open(streams, readerOptions));
- }
-
- ///
- /// Opens a RarArchive asynchronously from multiple FileInfo objects.
- ///
- ///
- ///
- ///
- public static ValueTask OpenAsync(
- IReadOnlyList fileInfos,
- ReaderOptions? readerOptions = null,
- CancellationToken cancellationToken = default
- )
- {
- cancellationToken.ThrowIfCancellationRequested();
- return new(Open(fileInfos, readerOptions));
- }
-
- public static bool IsRarFile(string filePath) => IsRarFile(new FileInfo(filePath));
-
- public static bool IsRarFile(FileInfo fileInfo)
- {
- if (!fileInfo.Exists)
- {
- return false;
- }
- using Stream stream = fileInfo.OpenRead();
- return IsRarFile(stream);
- }
-
- public static bool IsRarFile(Stream stream, ReaderOptions? options = null)
- {
- try
- {
- MarkHeader.Read(stream, true, false);
- return true;
- }
- catch
- {
- return false;
- }
- }
-
- #endregion
}
diff --git a/src/SharpCompress/Archives/SevenZip/SevenZipArchive.Factory.cs b/src/SharpCompress/Archives/SevenZip/SevenZipArchive.Factory.cs
new file mode 100644
index 000000000..2abb00ab4
--- /dev/null
+++ b/src/SharpCompress/Archives/SevenZip/SevenZipArchive.Factory.cs
@@ -0,0 +1,169 @@
+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.SevenZip;
+using SharpCompress.Compressors.LZMA.Utilites;
+using SharpCompress.IO;
+using SharpCompress.Readers;
+
+namespace SharpCompress.Archives.SevenZip;
+
+public partial class SevenZipArchive
+#if NET8_0_OR_GREATER
+ : IArchiveOpenable,
+ IMultiArchiveOpenable
+#endif
+{
+ public static IAsyncArchive OpenAsyncArchive(
+ string path,
+ ReaderOptions? readerOptions = null,
+ CancellationToken cancellationToken = default
+ )
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ path.NotNullOrEmpty("path");
+ return (IAsyncArchive)OpenArchive(new FileInfo(path), readerOptions ?? new ReaderOptions());
+ }
+
+ public static IArchive OpenArchive(string filePath, ReaderOptions? readerOptions = null)
+ {
+ filePath.NotNullOrEmpty("filePath");
+ return OpenArchive(new FileInfo(filePath), readerOptions ?? new ReaderOptions());
+ }
+
+ public static IArchive OpenArchive(FileInfo fileInfo, ReaderOptions? readerOptions = null)
+ {
+ fileInfo.NotNull("fileInfo");
+ return new SevenZipArchive(
+ new SourceStream(
+ fileInfo,
+ i => ArchiveVolumeFactory.GetFilePart(i, fileInfo),
+ readerOptions ?? new ReaderOptions()
+ )
+ );
+ }
+
+ public static IArchive OpenArchive(
+ IEnumerable fileInfos,
+ ReaderOptions? readerOptions = null
+ )
+ {
+ fileInfos.NotNull(nameof(fileInfos));
+ var files = fileInfos.ToArray();
+ return new SevenZipArchive(
+ new SourceStream(
+ files[0],
+ i => i < files.Length ? files[i] : null,
+ readerOptions ?? new ReaderOptions()
+ )
+ );
+ }
+
+ public static IArchive OpenArchive(
+ IEnumerable streams,
+ ReaderOptions? readerOptions = null
+ )
+ {
+ streams.NotNull(nameof(streams));
+ var strms = streams.ToArray();
+ return new SevenZipArchive(
+ new SourceStream(
+ strms[0],
+ i => i < strms.Length ? strms[i] : null,
+ readerOptions ?? new ReaderOptions()
+ )
+ );
+ }
+
+ public static IArchive OpenArchive(Stream stream, ReaderOptions? readerOptions = null)
+ {
+ stream.NotNull("stream");
+
+ if (stream is not { CanSeek: true })
+ {
+ throw new ArgumentException("Stream must be seekable", nameof(stream));
+ }
+
+ return new SevenZipArchive(
+ new SourceStream(stream, _ => null, readerOptions ?? new ReaderOptions())
+ );
+ }
+
+ public static IAsyncArchive OpenAsyncArchive(
+ Stream stream,
+ ReaderOptions? readerOptions = null,
+ CancellationToken cancellationToken = default
+ )
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ return (IAsyncArchive)OpenArchive(stream, readerOptions);
+ }
+
+ public static IAsyncArchive OpenAsyncArchive(
+ FileInfo fileInfo,
+ ReaderOptions? readerOptions = null,
+ CancellationToken cancellationToken = default
+ )
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ return (IAsyncArchive)OpenArchive(fileInfo, readerOptions);
+ }
+
+ public static IAsyncArchive OpenAsyncArchive(
+ IReadOnlyList streams,
+ ReaderOptions? readerOptions = null,
+ CancellationToken cancellationToken = default
+ )
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ return (IAsyncArchive)OpenArchive(streams, readerOptions);
+ }
+
+ public static IAsyncArchive OpenAsyncArchive(
+ IReadOnlyList fileInfos,
+ ReaderOptions? readerOptions = null,
+ CancellationToken cancellationToken = default
+ )
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ return (IAsyncArchive)OpenArchive(fileInfos, readerOptions);
+ }
+
+ public static bool IsSevenZipFile(string filePath) => IsSevenZipFile(new FileInfo(filePath));
+
+ public static bool IsSevenZipFile(FileInfo fileInfo)
+ {
+ if (!fileInfo.Exists)
+ {
+ return false;
+ }
+ using Stream stream = fileInfo.OpenRead();
+ return IsSevenZipFile(stream);
+ }
+
+ public static bool IsSevenZipFile(Stream stream)
+ {
+ try
+ {
+ return SignatureMatch(stream);
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
+ private static ReadOnlySpan Signature =>
+ new byte[] { (byte)'7', (byte)'z', 0xBC, 0xAF, 0x27, 0x1C };
+
+ private static bool SignatureMatch(Stream stream)
+ {
+ var reader = new BinaryReader(stream);
+ ReadOnlySpan signatureBytes = reader.ReadBytes(6);
+ return signatureBytes.SequenceEqual(Signature);
+ }
+}
diff --git a/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs b/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs
index 43f49abed..e6b511d83 100644
--- a/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs
+++ b/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs
@@ -12,191 +12,22 @@
namespace SharpCompress.Archives.SevenZip;
-public class SevenZipArchive : AbstractArchive
+public partial class SevenZipArchive : AbstractArchive
{
private ArchiveDatabase? _database;
- ///
- /// Constructor expects a filepath to an existing file.
- ///
- ///
- ///
- public static SevenZipArchive Open(string filePath, ReaderOptions? readerOptions = null)
- {
- filePath.NotNullOrEmpty("filePath");
- return Open(new FileInfo(filePath), readerOptions ?? new ReaderOptions());
- }
-
- ///
- /// Constructor with a FileInfo object to an existing file.
- ///
- ///
- ///
- public static SevenZipArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null)
- {
- fileInfo.NotNull("fileInfo");
- return new SevenZipArchive(
- new SourceStream(
- fileInfo,
- i => ArchiveVolumeFactory.GetFilePart(i, fileInfo),
- readerOptions ?? new ReaderOptions()
- )
- );
- }
-
- ///
- /// Constructor with all file parts passed in
- ///
- ///
- ///
- public static SevenZipArchive Open(
- IEnumerable fileInfos,
- ReaderOptions? readerOptions = null
- )
- {
- fileInfos.NotNull(nameof(fileInfos));
- var files = fileInfos.ToArray();
- return new SevenZipArchive(
- new SourceStream(
- files[0],
- i => i < files.Length ? files[i] : null,
- readerOptions ?? new ReaderOptions()
- )
- );
- }
-
- ///
- /// Constructor with all stream parts passed in
- ///
- ///
- ///
- public static SevenZipArchive Open(
- IEnumerable streams,
- ReaderOptions? readerOptions = null
- )
- {
- streams.NotNull(nameof(streams));
- var strms = streams.ToArray();
- return new SevenZipArchive(
- new SourceStream(
- strms[0],
- i => i < strms.Length ? strms[i] : null,
- readerOptions ?? new ReaderOptions()
- )
- );
- }
-
- ///
- /// Takes a seekable Stream as a source
- ///
- ///
- ///
- public static SevenZipArchive Open(Stream stream, ReaderOptions? readerOptions = null)
- {
- stream.NotNull("stream");
-
- if (stream is not { CanSeek: true })
- {
- throw new ArgumentException("Stream must be seekable", nameof(stream));
- }
-
- return new SevenZipArchive(
- new SourceStream(stream, _ => null, readerOptions ?? new ReaderOptions())
- );
- }
-
- ///
- /// Opens a SevenZipArchive asynchronously from a stream.
- ///
- ///
- ///
- ///
- public static ValueTask OpenAsync(
- Stream stream,
- ReaderOptions? readerOptions = null,
- CancellationToken cancellationToken = default
- )
- {
- cancellationToken.ThrowIfCancellationRequested();
- return new(Open(stream, readerOptions));
- }
-
- ///
- /// Opens a SevenZipArchive asynchronously from a FileInfo.
- ///
- ///
- ///
- ///
- public static ValueTask OpenAsync(
- FileInfo fileInfo,
- ReaderOptions? readerOptions = null,
- CancellationToken cancellationToken = default
- )
- {
- cancellationToken.ThrowIfCancellationRequested();
- return new(Open(fileInfo, readerOptions));
- }
-
- ///
- /// Opens a SevenZipArchive asynchronously from multiple streams.
- ///
- ///
- ///
- ///
- public static ValueTask OpenAsync(
- IReadOnlyList streams,
- ReaderOptions? readerOptions = null,
- CancellationToken cancellationToken = default
- )
- {
- cancellationToken.ThrowIfCancellationRequested();
- return new(Open(streams, readerOptions));
- }
-
- ///
- /// Opens a SevenZipArchive asynchronously from multiple FileInfo objects.
- ///
- ///
- ///
- ///
- public static ValueTask OpenAsync(
- IReadOnlyList fileInfos,
- ReaderOptions? readerOptions = null,
- CancellationToken cancellationToken = default
- )
- {
- cancellationToken.ThrowIfCancellationRequested();
- return new(Open(fileInfos, readerOptions));
- }
-
- ///
- /// Constructor with a SourceStream able to handle FileInfo and Streams.
- ///
- ///
private SevenZipArchive(SourceStream sourceStream)
: base(ArchiveType.SevenZip, sourceStream) { }
- protected override IEnumerable LoadVolumes(SourceStream sourceStream)
- {
- sourceStream.NotNull("SourceStream is null").LoadAllParts(); //request all streams
- return new SevenZipVolume(sourceStream, ReaderOptions, 0).AsEnumerable(); //simple single volume or split, multivolume not supported
- }
-
- public static bool IsSevenZipFile(string filePath) => IsSevenZipFile(new FileInfo(filePath));
+ internal SevenZipArchive()
+ : base(ArchiveType.SevenZip) { }
- public static bool IsSevenZipFile(FileInfo fileInfo)
+ protected override IEnumerable LoadVolumes(SourceStream sourceStream)
{
- if (!fileInfo.Exists)
- {
- return false;
- }
- using Stream stream = fileInfo.OpenRead();
- return IsSevenZipFile(stream);
+ sourceStream.NotNull("SourceStream is null").LoadAllParts();
+ return new SevenZipVolume(sourceStream, ReaderOptions, 0).AsEnumerable();
}
- internal SevenZipArchive()
- : base(ArchiveType.SevenZip) { }
-
protected override IEnumerable LoadEntries(
IEnumerable volumes
)
@@ -222,7 +53,7 @@ IEnumerable volumes
foreach (var entry in group)
{
entry.IsSolid = isSolid;
- isSolid = true; //mark others in this group as solid - same as rar behaviour.
+ isSolid = true;
}
}
@@ -240,28 +71,6 @@ private void LoadFactory(Stream stream)
}
}
- public static bool IsSevenZipFile(Stream stream)
- {
- try
- {
- return SignatureMatch(stream);
- }
- catch
- {
- return false;
- }
- }
-
- private static ReadOnlySpan Signature =>
- new byte[] { (byte)'7', (byte)'z', 0xBC, 0xAF, 0x27, 0x1C };
-
- private static bool SignatureMatch(Stream stream)
- {
- var reader = new BinaryReader(stream);
- ReadOnlySpan signatureBytes = reader.ReadBytes(6);
- return signatureBytes.SequenceEqual(Signature);
- }
-
protected override IReader CreateReaderForSolidExtraction() =>
new SevenZipReader(ReaderOptions, this);
@@ -298,9 +107,6 @@ protected override IEnumerable GetEntries(Stream stream)
_currentEntry = dir;
yield return dir;
}
- // For non-directory entries, yield them without creating shared streams
- // Each call to GetEntryStream() will create a fresh decompression stream
- // to avoid state corruption issues with async operations
foreach (var entry in entries.Where(x => !x.IsDirectory))
{
_currentEntry = entry;
@@ -310,13 +116,6 @@ protected override IEnumerable GetEntries(Stream stream)
protected override EntryStream GetEntryStream()
{
- // Create a fresh decompression stream for each file (no state sharing).
- // However, the LZMA decoder has bugs in its async implementation that cause
- // state corruption even on fresh streams. The SyncOnlyStream wrapper
- // works around these bugs by forcing async operations to use sync equivalents.
- //
- // TODO: Fix the LZMA decoder async bugs (in LzmaStream, Decoder, OutWindow)
- // so this wrapper is no longer necessary.
var entry = _currentEntry.NotNull("currentEntry is not null");
if (entry.IsDirectory)
{
@@ -326,15 +125,6 @@ protected override EntryStream GetEntryStream()
}
}
- ///
- /// WORKAROUND: Forces async operations to use synchronous equivalents.
- /// This is necessary because the LZMA decoder has bugs in its async implementation
- /// that cause state corruption (IndexOutOfRangeException, DataErrorException).
- ///
- /// The proper fix would be to repair the LZMA decoder's async methods
- /// (LzmaStream.ReadAsync, Decoder.CodeAsync, OutWindow async operations),
- /// but that requires deep changes to the decoder state machine.
- ///
private sealed class SyncOnlyStream : Stream
{
private readonly Stream _baseStream;
@@ -364,7 +154,6 @@ public override long Seek(long offset, SeekOrigin origin) =>
public override void Write(byte[] buffer, int offset, int count) =>
_baseStream.Write(buffer, offset, count);
- // Force async operations to use sync equivalents to avoid LZMA decoder bugs
public override Task ReadAsync(
byte[] buffer,
int offset,
diff --git a/src/SharpCompress/Archives/SevenZip/SevenZipArchiveEntry.cs b/src/SharpCompress/Archives/SevenZip/SevenZipArchiveEntry.cs
index a0d4a50d8..5b785e7c4 100644
--- a/src/SharpCompress/Archives/SevenZip/SevenZipArchiveEntry.cs
+++ b/src/SharpCompress/Archives/SevenZip/SevenZipArchiveEntry.cs
@@ -12,9 +12,8 @@ internal SevenZipArchiveEntry(SevenZipArchive archive, SevenZipFilePart part)
public Stream OpenEntryStream() => FilePart.GetCompressedStream();
- public async ValueTask OpenEntryStreamAsync(
- CancellationToken cancellationToken = default
- ) => OpenEntryStream();
+ public ValueTask OpenEntryStreamAsync(CancellationToken cancellationToken = default) =>
+ new(OpenEntryStream());
public IArchive Archive { get; }
diff --git a/src/SharpCompress/Archives/Tar/TarArchive.Factory.cs b/src/SharpCompress/Archives/Tar/TarArchive.Factory.cs
new file mode 100644
index 000000000..f6e03f4f8
--- /dev/null
+++ b/src/SharpCompress/Archives/Tar/TarArchive.Factory.cs
@@ -0,0 +1,171 @@
+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.Tar;
+using SharpCompress.Common.Tar.Headers;
+using SharpCompress.IO;
+using SharpCompress.Readers;
+using SharpCompress.Writers;
+using SharpCompress.Writers.Tar;
+
+namespace SharpCompress.Archives.Tar;
+
+public partial class TarArchive
+#if NET8_0_OR_GREATER
+ : IWritableArchiveOpenable,
+ IMultiArchiveOpenable
+#endif
+{
+ public static IWritableArchive OpenArchive(string filePath, ReaderOptions? readerOptions = null)
+ {
+ filePath.NotNullOrEmpty(nameof(filePath));
+ return OpenArchive(new FileInfo(filePath), readerOptions ?? new ReaderOptions());
+ }
+
+ public static IWritableArchive OpenArchive(
+ FileInfo fileInfo,
+ ReaderOptions? readerOptions = null
+ )
+ {
+ fileInfo.NotNull(nameof(fileInfo));
+ return new TarArchive(
+ new SourceStream(
+ fileInfo,
+ i => ArchiveVolumeFactory.GetFilePart(i, fileInfo),
+ readerOptions ?? new ReaderOptions()
+ )
+ );
+ }
+
+ public static IWritableArchive OpenArchive(
+ IEnumerable fileInfos,
+ ReaderOptions? readerOptions = null
+ )
+ {
+ fileInfos.NotNull(nameof(fileInfos));
+ var files = fileInfos.ToArray();
+ return new TarArchive(
+ new SourceStream(
+ files[0],
+ i => i < files.Length ? files[i] : null,
+ readerOptions ?? new ReaderOptions()
+ )
+ );
+ }
+
+ public static IWritableArchive OpenArchive(
+ IEnumerable streams,
+ ReaderOptions? readerOptions = null
+ )
+ {
+ streams.NotNull(nameof(streams));
+ var strms = streams.ToArray();
+ return new TarArchive(
+ new SourceStream(
+ strms[0],
+ i => i < strms.Length ? strms[i] : null,
+ readerOptions ?? new ReaderOptions()
+ )
+ );
+ }
+
+ public static IWritableArchive OpenArchive(Stream stream, ReaderOptions? readerOptions = null)
+ {
+ stream.NotNull(nameof(stream));
+
+ if (stream is not { CanSeek: true })
+ {
+ throw new ArgumentException("Stream must be seekable", nameof(stream));
+ }
+
+ return new TarArchive(
+ new SourceStream(stream, i => null, readerOptions ?? new ReaderOptions())
+ );
+ }
+
+ public static IWritableAsyncArchive OpenAsyncArchive(
+ Stream stream,
+ ReaderOptions? readerOptions = null,
+ CancellationToken cancellationToken = default
+ )
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ return (IWritableAsyncArchive)OpenArchive(stream, readerOptions);
+ }
+
+ public static IWritableAsyncArchive OpenAsyncArchive(
+ string path,
+ ReaderOptions? readerOptions = null,
+ CancellationToken cancellationToken = default
+ )
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ return (IWritableAsyncArchive)OpenArchive(new FileInfo(path), readerOptions);
+ }
+
+ public static IWritableAsyncArchive OpenAsyncArchive(
+ FileInfo fileInfo,
+ ReaderOptions? readerOptions = null,
+ CancellationToken cancellationToken = default
+ )
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ return (IWritableAsyncArchive)OpenArchive(fileInfo, readerOptions);
+ }
+
+ public static IWritableAsyncArchive OpenAsyncArchive(
+ IReadOnlyList streams,
+ ReaderOptions? readerOptions = null,
+ CancellationToken cancellationToken = default
+ )
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ return (IWritableAsyncArchive)OpenArchive(streams, readerOptions);
+ }
+
+ public static IWritableAsyncArchive OpenAsyncArchive(
+ IReadOnlyList fileInfos,
+ ReaderOptions? readerOptions = null,
+ CancellationToken cancellationToken = default
+ )
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ return (IWritableAsyncArchive)OpenArchive(fileInfos, readerOptions);
+ }
+
+ public static bool IsTarFile(string filePath) => IsTarFile(new FileInfo(filePath));
+
+ public static bool IsTarFile(FileInfo fileInfo)
+ {
+ if (!fileInfo.Exists)
+ {
+ return false;
+ }
+ using Stream stream = fileInfo.OpenRead();
+ return IsTarFile(stream);
+ }
+
+ public static bool IsTarFile(Stream stream)
+ {
+ try
+ {
+ var tarHeader = new TarHeader(new ArchiveEncoding());
+ var readSucceeded = tarHeader.Read(new BinaryReader(stream));
+ var isEmptyArchive =
+ tarHeader.Name?.Length == 0
+ && tarHeader.Size == 0
+ && Enum.IsDefined(typeof(EntryType), tarHeader.EntryType);
+ return readSucceeded || isEmptyArchive;
+ }
+ catch { }
+ return false;
+ }
+
+ public static IWritableArchive CreateArchive() => new TarArchive();
+
+ public static IWritableAsyncArchive CreateAsyncArchive() => new TarArchive();
+}
diff --git a/src/SharpCompress/Archives/Tar/TarArchive.cs b/src/SharpCompress/Archives/Tar/TarArchive.cs
index 1aeaf9a7a..16eddbebf 100644
--- a/src/SharpCompress/Archives/Tar/TarArchive.cs
+++ b/src/SharpCompress/Archives/Tar/TarArchive.cs
@@ -15,196 +15,14 @@
namespace SharpCompress.Archives.Tar;
-public class TarArchive : AbstractWritableArchive
+public partial class TarArchive : AbstractWritableArchive
{
- ///
- /// Constructor expects a filepath to an existing file.
- ///
- ///
- ///
- public static TarArchive Open(string filePath, ReaderOptions? readerOptions = null)
- {
- filePath.NotNullOrEmpty(nameof(filePath));
- return Open(new FileInfo(filePath), readerOptions ?? new ReaderOptions());
- }
-
- ///
- /// Constructor with a FileInfo object to an existing file.
- ///
- ///
- ///
- public static TarArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null)
- {
- fileInfo.NotNull(nameof(fileInfo));
- return new TarArchive(
- new SourceStream(
- fileInfo,
- i => ArchiveVolumeFactory.GetFilePart(i, fileInfo),
- readerOptions ?? new ReaderOptions()
- )
- );
- }
-
- ///
- /// Constructor with all file parts passed in
- ///
- ///
- ///
- public static TarArchive Open(
- IEnumerable fileInfos,
- ReaderOptions? readerOptions = null
- )
- {
- fileInfos.NotNull(nameof(fileInfos));
- var files = fileInfos.ToArray();
- return new TarArchive(
- new SourceStream(
- files[0],
- i => i < files.Length ? files[i] : null,
- readerOptions ?? new ReaderOptions()
- )
- );
- }
-
- ///
- /// Constructor with all stream parts passed in
- ///
- ///
- ///
- public static TarArchive Open(IEnumerable streams, ReaderOptions? readerOptions = null)
- {
- streams.NotNull(nameof(streams));
- var strms = streams.ToArray();
- return new TarArchive(
- new SourceStream(
- strms[0],
- i => i < strms.Length ? strms[i] : null,
- readerOptions ?? new ReaderOptions()
- )
- );
- }
-
- ///
- /// Takes a seekable Stream as a source
- ///
- ///
- ///
- public static TarArchive Open(Stream stream, ReaderOptions? readerOptions = null)
- {
- stream.NotNull(nameof(stream));
-
- if (stream is not { CanSeek: true })
- {
- throw new ArgumentException("Stream must be seekable", nameof(stream));
- }
-
- return new TarArchive(
- new SourceStream(stream, i => null, readerOptions ?? new ReaderOptions())
- );
- }
-
- ///
- /// Opens a TarArchive asynchronously from a stream.
- ///
- ///
- ///
- ///
- public static ValueTask OpenAsync(
- Stream stream,
- ReaderOptions? readerOptions = null,
- CancellationToken cancellationToken = default
- )
- {
- cancellationToken.ThrowIfCancellationRequested();
- return new(Open(stream, readerOptions));
- }
-
- ///
- /// Opens a TarArchive asynchronously from a FileInfo.
- ///
- ///
- ///
- ///
- public static ValueTask OpenAsync(
- FileInfo fileInfo,
- ReaderOptions? readerOptions = null,
- CancellationToken cancellationToken = default
- )
- {
- cancellationToken.ThrowIfCancellationRequested();
- return new(Open(fileInfo, readerOptions));
- }
-
- ///
- /// Opens a TarArchive asynchronously from multiple streams.
- ///
- ///
- ///
- ///
- public static ValueTask OpenAsync(
- IReadOnlyList streams,
- ReaderOptions? readerOptions = null,
- CancellationToken cancellationToken = default
- )
- {
- cancellationToken.ThrowIfCancellationRequested();
- return new(Open(streams, readerOptions));
- }
-
- ///
- /// Opens a TarArchive asynchronously from multiple FileInfo objects.
- ///
- ///
- ///
- ///
- public static ValueTask OpenAsync(
- IReadOnlyList fileInfos,
- ReaderOptions? readerOptions = null,
- CancellationToken cancellationToken = default
- )
- {
- cancellationToken.ThrowIfCancellationRequested();
- return new(Open(fileInfos, readerOptions));
- }
-
- public static bool IsTarFile(string filePath) => IsTarFile(new FileInfo(filePath));
-
- public static bool IsTarFile(FileInfo fileInfo)
- {
- if (!fileInfo.Exists)
- {
- return false;
- }
- using Stream stream = fileInfo.OpenRead();
- return IsTarFile(stream);
- }
-
- public static bool IsTarFile(Stream stream)
- {
- try
- {
- var tarHeader = new TarHeader(new ArchiveEncoding());
- var readSucceeded = tarHeader.Read(new BinaryReader(stream));
- var isEmptyArchive =
- tarHeader.Name?.Length == 0
- && tarHeader.Size == 0
- && Enum.IsDefined(typeof(EntryType), tarHeader.EntryType);
- return readSucceeded || isEmptyArchive;
- }
- catch { }
- return false;
- }
-
protected override IEnumerable LoadVolumes(SourceStream sourceStream)
{
- sourceStream.NotNull("SourceStream is null").LoadAllParts(); //request all streams
- return new TarVolume(sourceStream, ReaderOptions, 1).AsEnumerable(); //simple single volume or split, multivolume not supported
+ sourceStream.NotNull("SourceStream is null").LoadAllParts();
+ return new TarVolume(sourceStream, ReaderOptions, 1).AsEnumerable();
}
- ///
- /// Constructor with a SourceStream able to handle FileInfo and Streams.
- ///
- ///
private TarArchive(SourceStream sourceStream)
: base(ArchiveType.Tar, sourceStream) { }
@@ -269,8 +87,6 @@ var header in TarHeaderFactory.ReadHeader(
}
}
- public static TarArchive Create() => new();
-
protected override TarArchiveEntry CreateEntryInternal(
string filePath,
Stream source,
@@ -364,13 +180,13 @@ protected override IReader CreateReaderForSolidExtraction()
{
var stream = Volumes.Single().Stream;
stream.Position = 0;
- return TarReader.Open(stream);
+ return TarReader.OpenReader(stream);
}
protected override ValueTask CreateReaderForSolidExtractionAsync()
{
var stream = Volumes.Single().Stream;
stream.Position = 0;
- return new(TarReader.Open(stream));
+ return new((IAsyncReader)TarReader.OpenReader(stream));
}
}
diff --git a/src/SharpCompress/Archives/Tar/TarArchiveEntry.cs b/src/SharpCompress/Archives/Tar/TarArchiveEntry.cs
index cbea2c717..b09876e98 100644
--- a/src/SharpCompress/Archives/Tar/TarArchiveEntry.cs
+++ b/src/SharpCompress/Archives/Tar/TarArchiveEntry.cs
@@ -14,9 +14,8 @@ internal TarArchiveEntry(TarArchive archive, TarFilePart? part, CompressionType
public virtual Stream OpenEntryStream() => Parts.Single().GetCompressedStream().NotNull();
- public async ValueTask OpenEntryStreamAsync(
- CancellationToken cancellationToken = default
- ) => OpenEntryStream();
+ public ValueTask OpenEntryStreamAsync(CancellationToken cancellationToken = default) =>
+ new(OpenEntryStream());
#region IArchiveEntry Members
diff --git a/src/SharpCompress/Archives/Zip/ZipArchive.Factory.cs b/src/SharpCompress/Archives/Zip/ZipArchive.Factory.cs
new file mode 100644
index 000000000..bb51552ed
--- /dev/null
+++ b/src/SharpCompress/Archives/Zip/ZipArchive.Factory.cs
@@ -0,0 +1,324 @@
+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.Zip;
+using SharpCompress.Common.Zip.Headers;
+using SharpCompress.IO;
+using SharpCompress.Readers;
+
+namespace SharpCompress.Archives.Zip;
+
+public partial class ZipArchive
+#if NET8_0_OR_GREATER
+ : IWritableArchiveOpenable,
+ IMultiArchiveOpenable
+#endif
+{
+ public static IWritableArchive OpenArchive(string filePath, ReaderOptions? readerOptions = null)
+ {
+ filePath.NotNullOrEmpty(nameof(filePath));
+ return OpenArchive(new FileInfo(filePath), readerOptions ?? new ReaderOptions());
+ }
+
+ public static IWritableArchive OpenArchive(
+ FileInfo fileInfo,
+ ReaderOptions? readerOptions = null
+ )
+ {
+ fileInfo.NotNull(nameof(fileInfo));
+ return new ZipArchive(
+ new SourceStream(
+ fileInfo,
+ i => ZipArchiveVolumeFactory.GetFilePart(i, fileInfo),
+ readerOptions ?? new ReaderOptions()
+ )
+ );
+ }
+
+ public static IWritableArchive OpenArchive(
+ IEnumerable fileInfos,
+ ReaderOptions? readerOptions = null
+ )
+ {
+ fileInfos.NotNull(nameof(fileInfos));
+ var files = fileInfos.ToArray();
+ return new ZipArchive(
+ new SourceStream(
+ files[0],
+ i => i < files.Length ? files[i] : null,
+ readerOptions ?? new ReaderOptions()
+ )
+ );
+ }
+
+ public static IWritableArchive OpenArchive(
+ IEnumerable streams,
+ ReaderOptions? readerOptions = null
+ )
+ {
+ streams.NotNull(nameof(streams));
+ var strms = streams.ToArray();
+ return new ZipArchive(
+ new SourceStream(
+ strms[0],
+ i => i < strms.Length ? strms[i] : null,
+ readerOptions ?? new ReaderOptions()
+ )
+ );
+ }
+
+ public static IWritableArchive OpenArchive(Stream stream, ReaderOptions? readerOptions = null)
+ {
+ stream.NotNull(nameof(stream));
+
+ if (stream is not { CanSeek: true })
+ {
+ throw new ArgumentException("Stream must be seekable", nameof(stream));
+ }
+
+ return new ZipArchive(
+ new SourceStream(stream, i => null, readerOptions ?? new ReaderOptions())
+ );
+ }
+
+ public static IWritableAsyncArchive OpenAsyncArchive(
+ string path,
+ ReaderOptions? readerOptions = null,
+ CancellationToken cancellationToken = default
+ )
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ return (IWritableAsyncArchive)OpenArchive(path, readerOptions);
+ }
+
+ public static IWritableAsyncArchive OpenAsyncArchive(
+ Stream stream,
+ ReaderOptions? readerOptions = null,
+ CancellationToken cancellationToken = default
+ )
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ return (IWritableAsyncArchive)OpenArchive(stream, readerOptions);
+ }
+
+ public static IWritableAsyncArchive OpenAsyncArchive(
+ FileInfo fileInfo,
+ ReaderOptions? readerOptions = null,
+ CancellationToken cancellationToken = default
+ )
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ return (IWritableAsyncArchive)OpenArchive(fileInfo, readerOptions);
+ }
+
+ public static IWritableAsyncArchive OpenAsyncArchive(
+ IReadOnlyList streams,
+ ReaderOptions? readerOptions = null,
+ CancellationToken cancellationToken = default
+ )
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ return (IWritableAsyncArchive)OpenArchive(streams, readerOptions);
+ }
+
+ public static IWritableAsyncArchive OpenAsyncArchive(
+ IReadOnlyList fileInfos,
+ ReaderOptions? readerOptions = null,
+ CancellationToken cancellationToken = default
+ )
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ return (IWritableAsyncArchive)OpenArchive(fileInfos, readerOptions);
+ }
+
+ public static bool IsZipFile(
+ string filePath,
+ string? password = null,
+ int bufferSize = ReaderOptions.DefaultBufferSize
+ ) => IsZipFile(new FileInfo(filePath), password, bufferSize);
+
+ public static bool IsZipFile(
+ FileInfo fileInfo,
+ string? password = null,
+ int bufferSize = ReaderOptions.DefaultBufferSize
+ )
+ {
+ if (!fileInfo.Exists)
+ {
+ return false;
+ }
+ using Stream stream = fileInfo.OpenRead();
+ return IsZipFile(stream, password, bufferSize);
+ }
+
+ public static bool IsZipFile(
+ Stream stream,
+ string? password = null,
+ int bufferSize = ReaderOptions.DefaultBufferSize
+ )
+ {
+ var headerFactory = new StreamingZipHeaderFactory(password, new ArchiveEncoding(), null);
+ try
+ {
+ if (stream is not SharpCompressStream)
+ {
+ stream = new SharpCompressStream(stream, bufferSize: bufferSize);
+ }
+
+ var header = headerFactory
+ .ReadStreamHeader(stream)
+ .FirstOrDefault(x => x.ZipHeaderType != ZipHeaderType.Split);
+ if (header is null)
+ {
+ return false;
+ }
+ return Enum.IsDefined(typeof(ZipHeaderType), header.ZipHeaderType);
+ }
+ catch (CryptographicException)
+ {
+ return true;
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
+ public static bool IsZipMulti(
+ Stream stream,
+ string? password = null,
+ int bufferSize = ReaderOptions.DefaultBufferSize
+ )
+ {
+ var headerFactory = new StreamingZipHeaderFactory(password, new ArchiveEncoding(), null);
+ try
+ {
+ if (stream is not SharpCompressStream)
+ {
+ stream = new SharpCompressStream(stream, bufferSize: bufferSize);
+ }
+
+ var header = headerFactory
+ .ReadStreamHeader(stream)
+ .FirstOrDefault(x => x.ZipHeaderType != ZipHeaderType.Split);
+ if (header is null)
+ {
+ if (stream.CanSeek)
+ {
+ var z = new SeekableZipHeaderFactory(password, new ArchiveEncoding());
+ var x = z.ReadSeekableHeader(stream, useSync: true).FirstOrDefault();
+ return x?.ZipHeaderType == ZipHeaderType.DirectoryEntry;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ return Enum.IsDefined(typeof(ZipHeaderType), header.ZipHeaderType);
+ }
+ catch (CryptographicException)
+ {
+ return true;
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
+ public static async ValueTask IsZipFileAsync(
+ Stream stream,
+ string? password = null,
+ int bufferSize = ReaderOptions.DefaultBufferSize,
+ CancellationToken cancellationToken = default
+ )
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ var headerFactory = new StreamingZipHeaderFactory(password, new ArchiveEncoding(), null);
+ try
+ {
+ if (stream is not SharpCompressStream)
+ {
+ stream = new SharpCompressStream(stream, bufferSize: bufferSize);
+ }
+
+ var header = await headerFactory
+ .ReadStreamHeaderAsync(stream)
+ .Where(x => x.ZipHeaderType != ZipHeaderType.Split)
+ .FirstOrDefaultAsync(cancellationToken);
+ if (header is null)
+ {
+ return false;
+ }
+ return Enum.IsDefined(typeof(ZipHeaderType), header.ZipHeaderType);
+ }
+ catch (CryptographicException)
+ {
+ return true;
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
+ public static IWritableArchive CreateArchive() => new ZipArchive();
+
+ public static IWritableAsyncArchive CreateAsyncArchive() => new ZipArchive();
+
+ public static async ValueTask IsZipMultiAsync(
+ Stream stream,
+ string? password = null,
+ int bufferSize = ReaderOptions.DefaultBufferSize,
+ CancellationToken cancellationToken = default
+ )
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ var headerFactory = new StreamingZipHeaderFactory(password, new ArchiveEncoding(), null);
+ try
+ {
+ if (stream is not SharpCompressStream)
+ {
+ stream = new SharpCompressStream(stream, bufferSize: bufferSize);
+ }
+
+ var header = headerFactory
+ .ReadStreamHeader(stream)
+ .FirstOrDefault(x => x.ZipHeaderType != ZipHeaderType.Split);
+ if (header is null)
+ {
+ if (stream.CanSeek)
+ {
+ var z = new SeekableZipHeaderFactory(password, new ArchiveEncoding());
+ ZipHeader? x = null;
+ await foreach (
+ var h in z.ReadSeekableHeaderAsync(stream)
+ .WithCancellation(cancellationToken)
+ )
+ {
+ x = h;
+ break;
+ }
+ return x?.ZipHeaderType == ZipHeaderType.DirectoryEntry;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ return Enum.IsDefined(typeof(ZipHeaderType), header.ZipHeaderType);
+ }
+ catch (CryptographicException)
+ {
+ return true;
+ }
+ catch
+ {
+ return false;
+ }
+ }
+}
diff --git a/src/SharpCompress/Archives/Zip/ZipArchive.cs b/src/SharpCompress/Archives/Zip/ZipArchive.cs
index 756bc8863..9e2d80e11 100644
--- a/src/SharpCompress/Archives/Zip/ZipArchive.cs
+++ b/src/SharpCompress/Archives/Zip/ZipArchive.cs
@@ -16,21 +16,12 @@
namespace SharpCompress.Archives.Zip;
-public class ZipArchive : AbstractWritableArchive
+public partial class ZipArchive : AbstractWritableArchive
{
private readonly SeekableZipHeaderFactory? headerFactory;
- ///
- /// Gets or sets the compression level applied to files added to the archive,
- /// if the compression method is set to deflate
- ///
public CompressionLevel DeflateCompressionLevel { get; set; }
- ///
- /// Constructor with a SourceStream able to handle FileInfo and Streams.
- ///
- ///
- ///
internal ZipArchive(SourceStream sourceStream)
: base(ArchiveType.Zip, sourceStream) =>
headerFactory = new SeekableZipHeaderFactory(
@@ -38,371 +29,36 @@ internal ZipArchive(SourceStream sourceStream)
sourceStream.ReaderOptions.ArchiveEncoding
);
- ///
- /// Constructor expects a filepath to an existing file.
- ///
- ///
- ///
- public static ZipArchive Open(string filePath, ReaderOptions? readerOptions = null)
- {
- filePath.NotNullOrEmpty(nameof(filePath));
- return Open(new FileInfo(filePath), readerOptions ?? new ReaderOptions());
- }
-
- ///
- /// Constructor with a FileInfo object to an existing file.
- ///
- ///
- ///
- public static ZipArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null)
- {
- fileInfo.NotNull(nameof(fileInfo));
- return new ZipArchive(
- new SourceStream(
- fileInfo,
- i => ZipArchiveVolumeFactory.GetFilePart(i, fileInfo),
- readerOptions ?? new ReaderOptions()
- )
- );
- }
-
- ///
- /// Constructor with all file parts passed in
- ///
- ///
- ///
- public static ZipArchive Open(
- IEnumerable fileInfos,
- ReaderOptions? readerOptions = null
- )
- {
- fileInfos.NotNull(nameof(fileInfos));
- var files = fileInfos.ToArray();
- return new ZipArchive(
- new SourceStream(
- files[0],
- i => i < files.Length ? files[i] : null,
- readerOptions ?? new ReaderOptions()
- )
- );
- }
-
- ///
- /// Constructor with all stream parts passed in
- ///
- ///
- ///
- public static ZipArchive Open(IEnumerable streams, ReaderOptions? readerOptions = null)
- {
- streams.NotNull(nameof(streams));
- var strms = streams.ToArray();
- return new ZipArchive(
- new SourceStream(
- strms[0],
- i => i < strms.Length ? strms[i] : null,
- readerOptions ?? new ReaderOptions()
- )
- );
- }
-
- ///
- /// Takes a seekable Stream as a source
- ///
- ///
- ///
- public static ZipArchive Open(Stream stream, ReaderOptions? readerOptions = null)
- {
- stream.NotNull(nameof(stream));
-
- if (stream is not { CanSeek: true })
- {
- throw new ArgumentException("Stream must be seekable", nameof(stream));
- }
-
- return new ZipArchive(
- new SourceStream(stream, i => null, readerOptions ?? new ReaderOptions())
- );
- }
-
- ///
- /// Opens a ZipArchive asynchronously from a stream.
- ///
- ///
- ///
- ///
- public static ValueTask OpenAsync(
- Stream stream,
- ReaderOptions? readerOptions = null,
- CancellationToken cancellationToken = default
- )
- {
- cancellationToken.ThrowIfCancellationRequested();
- return new(Open(stream, readerOptions));
- }
-
- ///
- /// Opens a ZipArchive asynchronously from a FileInfo.
- ///
- ///
- ///
- ///
- public static ValueTask OpenAsync(
- FileInfo fileInfo,
- ReaderOptions? readerOptions = null,
- CancellationToken cancellationToken = default
- )
- {
- cancellationToken.ThrowIfCancellationRequested();
- return new(Open(fileInfo, readerOptions));
- }
-
- ///
- /// Opens a ZipArchive asynchronously from multiple streams.
- ///
- ///
- ///
- ///
- public static ValueTask OpenAsync(
- IReadOnlyList streams,
- ReaderOptions? readerOptions = null,
- CancellationToken cancellationToken = default
- )
- {
- cancellationToken.ThrowIfCancellationRequested();
- return new(Open(streams, readerOptions));
- }
-
- ///
- /// Opens a ZipArchive asynchronously from multiple FileInfo objects.
- ///
- ///
- ///
- ///
- public static ValueTask OpenAsync(
- IReadOnlyList fileInfos,
- ReaderOptions? readerOptions = null,
- CancellationToken cancellationToken = default
- )
- {
- cancellationToken.ThrowIfCancellationRequested();
- return new(Open(fileInfos, readerOptions));
- }
-
- public static bool IsZipFile(
- string filePath,
- string? password = null,
- int bufferSize = ReaderOptions.DefaultBufferSize
- ) => IsZipFile(new FileInfo(filePath), password, bufferSize);
-
- public static bool IsZipFile(
- FileInfo fileInfo,
- string? password = null,
- int bufferSize = ReaderOptions.DefaultBufferSize
- )
- {
- if (!fileInfo.Exists)
- {
- return false;
- }
- using Stream stream = fileInfo.OpenRead();
- return IsZipFile(stream, password, bufferSize);
- }
-
- public static bool IsZipFile(
- Stream stream,
- string? password = null,
- int bufferSize = ReaderOptions.DefaultBufferSize
- )
- {
- var headerFactory = new StreamingZipHeaderFactory(password, new ArchiveEncoding(), null);
- try
- {
- if (stream is not SharpCompressStream)
- {
- stream = new SharpCompressStream(stream, bufferSize: bufferSize);
- }
-
- var header = headerFactory
- .ReadStreamHeader(stream)
- .FirstOrDefault(x => x.ZipHeaderType != ZipHeaderType.Split);
- if (header is null)
- {
- return false;
- }
- return Enum.IsDefined(typeof(ZipHeaderType), header.ZipHeaderType);
- }
- catch (CryptographicException)
- {
- return true;
- }
- catch
- {
- return false;
- }
- }
-
- public static bool IsZipMulti(
- Stream stream,
- string? password = null,
- int bufferSize = ReaderOptions.DefaultBufferSize
- )
- {
- var headerFactory = new StreamingZipHeaderFactory(password, new ArchiveEncoding(), null);
- try
- {
- if (stream is not SharpCompressStream)
- {
- stream = new SharpCompressStream(stream, bufferSize: bufferSize);
- }
-
- var header = headerFactory
- .ReadStreamHeader(stream)
- .FirstOrDefault(x => x.ZipHeaderType != ZipHeaderType.Split);
- if (header is null)
- {
- if (stream.CanSeek) //could be multipart. Test for central directory - might not be z64 safe
- {
- var z = new SeekableZipHeaderFactory(password, new ArchiveEncoding());
- var x = z.ReadSeekableHeader(stream, useSync: true).FirstOrDefault();
- return x?.ZipHeaderType == ZipHeaderType.DirectoryEntry;
- }
- else
- {
- return false;
- }
- }
- return Enum.IsDefined(typeof(ZipHeaderType), header.ZipHeaderType);
- }
- catch (CryptographicException)
- {
- return true;
- }
- catch
- {
- return false;
- }
- }
-
- public static async ValueTask IsZipFileAsync(
- Stream stream,
- string? password = null,
- int bufferSize = ReaderOptions.DefaultBufferSize,
- CancellationToken cancellationToken = default
- )
- {
- cancellationToken.ThrowIfCancellationRequested();
- var headerFactory = new StreamingZipHeaderFactory(password, new ArchiveEncoding(), null);
- try
- {
- if (stream is not SharpCompressStream)
- {
- stream = new SharpCompressStream(stream, bufferSize: bufferSize);
- }
-
- var header = await headerFactory
- .ReadStreamHeaderAsync(stream)
- .Where(x => x.ZipHeaderType != ZipHeaderType.Split)
- .FirstOrDefaultAsync();
- if (header is null)
- {
- return false;
- }
- return Enum.IsDefined(typeof(ZipHeaderType), header.ZipHeaderType);
- }
- catch (CryptographicException)
- {
- return true;
- }
- catch
- {
- return false;
- }
- }
-
- public static async ValueTask IsZipMultiAsync(
- Stream stream,
- string? password = null,
- int bufferSize = ReaderOptions.DefaultBufferSize,
- CancellationToken cancellationToken = default
- )
- {
- cancellationToken.ThrowIfCancellationRequested();
- var headerFactory = new StreamingZipHeaderFactory(password, new ArchiveEncoding(), null);
- try
- {
- if (stream is not SharpCompressStream)
- {
- stream = new SharpCompressStream(stream, bufferSize: bufferSize);
- }
-
- var header = headerFactory
- .ReadStreamHeader(stream)
- .FirstOrDefault(x => x.ZipHeaderType != ZipHeaderType.Split);
- if (header is null)
- {
- if (stream.CanSeek) //could be multipart. Test for central directory - might not be z64 safe
- {
- var z = new SeekableZipHeaderFactory(password, new ArchiveEncoding());
- ZipHeader? x = null;
- await foreach (
- var h in z.ReadSeekableHeaderAsync(stream)
- .WithCancellation(cancellationToken)
- )
- {
- x = h;
- break;
- }
- return x?.ZipHeaderType == ZipHeaderType.DirectoryEntry;
- }
- else
- {
- return false;
- }
- }
- return Enum.IsDefined(typeof(ZipHeaderType), header.ZipHeaderType);
- }
- catch (CryptographicException)
- {
- return true;
- }
- catch
- {
- return false;
- }
- }
+ internal ZipArchive()
+ : base(ArchiveType.Zip) { }
protected override IEnumerable LoadVolumes(SourceStream stream)
{
- stream.LoadAllParts(); //request all streams
+ stream.LoadAllParts();
stream.Position = 0;
var streams = stream.Streams.ToList();
var idx = 0;
- if (streams.Count() > 1) //test part 2 - true = multipart not split
+ if (streams.Count() > 1)
{
- streams[1].Position += 4; //skip the POST_DATA_DESCRIPTOR to prevent an exception
+ streams[1].Position += 4;
var isZip = IsZipFile(streams[1], ReaderOptions.Password, ReaderOptions.BufferSize);
streams[1].Position -= 4;
if (isZip)
{
stream.IsVolumes = true;
- var tmp = streams[0]; //arcs as zip, z01 ... swap the zip the end
+ var tmp = streams[0];
streams.RemoveAt(0);
streams.Add(tmp);
- //streams[0].Position = 4; //skip the POST_DATA_DESCRIPTOR to prevent an exception
return streams.Select(a => new ZipVolume(a, ReaderOptions, idx++));
}
}
- //split mode or single file
return new ZipVolume(stream, ReaderOptions, idx++).AsEnumerable();
}
- internal ZipArchive()
- : base(ArchiveType.Zip) { }
-
protected override IEnumerable LoadEntries(IEnumerable volumes)
{
var vols = volumes.ToArray();
@@ -584,19 +240,17 @@ protected override ZipArchiveEntry CreateDirectoryEntry(
DateTime? modified
) => new ZipWritableArchiveEntry(this, directoryPath, modified);
- public static ZipArchive Create() => new();
-
protected override IReader CreateReaderForSolidExtraction()
{
var stream = Volumes.Single().Stream;
((IStreamStack)stream).StackSeek(0);
- return ZipReader.Open(stream, ReaderOptions, Entries);
+ return ZipReader.OpenReader(stream, ReaderOptions, Entries);
}
protected override ValueTask CreateReaderForSolidExtractionAsync()
{
var stream = Volumes.Single().Stream;
stream.Position = 0;
- return new(ZipReader.Open(stream));
+ return new((IAsyncReader)ZipReader.OpenReader(stream));
}
}
diff --git a/src/SharpCompress/Common/GZip/GZipEntry.cs b/src/SharpCompress/Common/GZip/GZipEntry.cs
index 9a551d07f..c0cc6b107 100644
--- a/src/SharpCompress/Common/GZip/GZipEntry.cs
+++ b/src/SharpCompress/Common/GZip/GZipEntry.cs
@@ -40,6 +40,6 @@ public class GZipEntry : Entry
internal static IEnumerable GetEntries(Stream stream, OptionsBase options)
{
- yield return new GZipEntry(new GZipFilePart(stream, options.ArchiveEncoding));
+ yield return new GZipEntry(GZipFilePart.Create(stream, options.ArchiveEncoding));
}
}
diff --git a/src/SharpCompress/Common/GZip/GZipFilePart.cs b/src/SharpCompress/Common/GZip/GZipFilePart.cs
index f1d0eaf1a..a2cb0a764 100644
--- a/src/SharpCompress/Common/GZip/GZipFilePart.cs
+++ b/src/SharpCompress/Common/GZip/GZipFilePart.cs
@@ -2,6 +2,8 @@
using System.Buffers.Binary;
using System.Collections.Generic;
using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
using SharpCompress.Common.Tar.Headers;
using SharpCompress.Compressors;
using SharpCompress.Compressors.Deflate;
@@ -13,28 +15,58 @@ internal sealed class GZipFilePart : FilePart
private string? _name;
private readonly Stream _stream;
- internal GZipFilePart(Stream stream, IArchiveEncoding archiveEncoding)
- : base(archiveEncoding)
+ internal static GZipFilePart Create(Stream stream, IArchiveEncoding archiveEncoding)
{
- _stream = stream;
- ReadAndValidateGzipHeader();
+ var part = new GZipFilePart(stream, archiveEncoding);
+
+ part.ReadAndValidateGzipHeader();
+ if (stream.CanSeek)
+ {
+ var position = stream.Position;
+ stream.Position = stream.Length - 8;
+ part.ReadTrailer();
+ stream.Position = position;
+ part.EntryStartPosition = position;
+ }
+ else
+ {
+ // For non-seekable streams, we can't read the trailer or track position.
+ // Set to 0 since the stream will be read sequentially from its current position.
+ part.EntryStartPosition = 0;
+ }
+ return part;
+ }
+
+ internal static async ValueTask CreateAsync(
+ Stream stream,
+ IArchiveEncoding archiveEncoding,
+ CancellationToken cancellationToken = default
+ )
+ {
+ var part = new GZipFilePart(stream, archiveEncoding);
+
+ await part.ReadAndValidateGzipHeaderAsync(cancellationToken);
if (stream.CanSeek)
{
var position = stream.Position;
stream.Position = stream.Length - 8;
- ReadTrailer();
+ await part.ReadTrailerAsync(cancellationToken);
stream.Position = position;
- EntryStartPosition = position;
+ part.EntryStartPosition = position;
}
else
{
// For non-seekable streams, we can't read the trailer or track position.
// Set to 0 since the stream will be read sequentially from its current position.
- EntryStartPosition = 0;
+ part.EntryStartPosition = 0;
}
+ return part;
}
- internal long EntryStartPosition { get; }
+ private GZipFilePart(Stream stream, IArchiveEncoding archiveEncoding)
+ : base(archiveEncoding) => _stream = stream;
+
+ internal long EntryStartPosition { get; private set; }
internal DateTime? DateModified { get; private set; }
internal uint? Crc { get; private set; }
@@ -51,12 +83,22 @@ private void ReadTrailer()
{
// Read and potentially verify the GZIP trailer: CRC32 and size mod 2^32
Span trailer = stackalloc byte[8];
- var n = _stream.Read(trailer);
+ _stream.ReadFully(trailer);
Crc = BinaryPrimitives.ReadUInt32LittleEndian(trailer);
UncompressedSize = BinaryPrimitives.ReadUInt32LittleEndian(trailer.Slice(4));
}
+ private async ValueTask ReadTrailerAsync(CancellationToken cancellationToken = default)
+ {
+ // 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);
+
+ Crc = BinaryPrimitives.ReadUInt32LittleEndian(trailer);
+ UncompressedSize = BinaryPrimitives.ReadUInt32LittleEndian(trailer.AsSpan().Slice(4));
+ }
+
private void ReadAndValidateGzipHeader()
{
// read the header on the first read
@@ -109,6 +151,61 @@ private void ReadAndValidateGzipHeader()
}
}
+ private async ValueTask ReadAndValidateGzipHeaderAsync(
+ CancellationToken cancellationToken = default
+ )
+ {
+ // read the header on the first read
+ var header = new byte[10];
+ var n = await _stream.ReadAsync(header, 0, 10, cancellationToken);
+
+ // workitem 8501: handle edge case (decompress empty stream)
+ if (n == 0)
+ {
+ return;
+ }
+
+ if (n != 10)
+ {
+ throw new ZlibException("Not a valid GZIP stream.");
+ }
+
+ if (header[0] != 0x1F || header[1] != 0x8B || header[2] != 8)
+ {
+ throw new ZlibException("Bad GZIP header.");
+ }
+
+ var timet = BinaryPrimitives.ReadInt32LittleEndian(header.AsSpan().Slice(4));
+ DateModified = TarHeader.EPOCH.AddSeconds(timet);
+ if ((header[3] & 0x04) == 0x04)
+ {
+ // read and discard extra field
+ var lengthField = new byte[2];
+ _ = await _stream.ReadAsync(lengthField, 0, 2, cancellationToken);
+
+ var extraLength = (short)(lengthField[0] + (lengthField[1] * 256));
+ var extra = new byte[extraLength];
+
+ if (!await _stream.ReadFullyAsync(extra, cancellationToken))
+ {
+ throw new ZlibException("Unexpected end-of-file reading GZIP header.");
+ }
+ }
+ if ((header[3] & 0x08) == 0x08)
+ {
+ _name = await ReadZeroTerminatedStringAsync(_stream, cancellationToken);
+ }
+ if ((header[3] & 0x10) == 0x010)
+ {
+ await ReadZeroTerminatedStringAsync(_stream, cancellationToken);
+ }
+ if ((header[3] & 0x02) == 0x02)
+ {
+ var buf = new byte[1];
+ _ = await _stream.ReadAsync(buf, 0, 1, cancellationToken); // CRC16, ignore
+ }
+ }
+
private string ReadZeroTerminatedString(Stream stream)
{
Span buf1 = stackalloc byte[1];
@@ -134,4 +231,33 @@ private string ReadZeroTerminatedString(Stream stream)
var buffer = list.ToArray();
return ArchiveEncoding.Decode(buffer);
}
+
+ private async ValueTask ReadZeroTerminatedStringAsync(
+ Stream stream,
+ CancellationToken cancellationToken = default
+ )
+ {
+ var buf1 = new byte[1];
+ var list = new List();
+ var done = false;
+ do
+ {
+ // workitem 7740
+ var n = await stream.ReadAsync(buf1, 0, 1, cancellationToken);
+ if (n != 1)
+ {
+ throw new ZlibException("Unexpected EOF reading GZIP header.");
+ }
+ if (buf1[0] == 0)
+ {
+ done = true;
+ }
+ else
+ {
+ list.Add(buf1[0]);
+ }
+ } while (!done);
+ var buffer = list.ToArray();
+ return ArchiveEncoding.Decode(buffer);
+ }
}
diff --git a/src/SharpCompress/Factories/AceFactory.cs b/src/SharpCompress/Factories/AceFactory.cs
index 95f647ddb..6691f1321 100644
--- a/src/SharpCompress/Factories/AceFactory.cs
+++ b/src/SharpCompress/Factories/AceFactory.cs
@@ -30,13 +30,17 @@ public override bool IsArchive(
) => AceHeader.IsArchive(stream);
public IReader OpenReader(Stream stream, ReaderOptions? options) =>
- AceReader.Open(stream, options);
+ AceReader.OpenReader(stream, options);
- public ValueTask OpenReaderAsync(
+ public IAsyncReader OpenAsyncReader(
Stream stream,
ReaderOptions? options,
CancellationToken cancellationToken = default
- ) => new(AceReader.Open(stream, options));
+ )
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ return (IAsyncReader)AceReader.OpenReader(stream, options);
+ }
public override ValueTask IsArchiveAsync(
Stream stream,
diff --git a/src/SharpCompress/Factories/ArcFactory.cs b/src/SharpCompress/Factories/ArcFactory.cs
index 37984112a..9ed581ae4 100644
--- a/src/SharpCompress/Factories/ArcFactory.cs
+++ b/src/SharpCompress/Factories/ArcFactory.cs
@@ -42,13 +42,17 @@ public override bool IsArchive(
}
public IReader OpenReader(Stream stream, ReaderOptions? options) =>
- ArcReader.Open(stream, options);
+ ArcReader.OpenReader(stream, options);
- public ValueTask OpenReaderAsync(
+ public IAsyncReader OpenAsyncReader(
Stream stream,
ReaderOptions? options,
CancellationToken cancellationToken = default
- ) => new(ArcReader.Open(stream, options));
+ )
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ return (IAsyncReader)ArcReader.OpenReader(stream, options);
+ }
public override ValueTask IsArchiveAsync(
Stream stream,
diff --git a/src/SharpCompress/Factories/ArjFactory.cs b/src/SharpCompress/Factories/ArjFactory.cs
index 6e5f7a309..c58314d29 100644
--- a/src/SharpCompress/Factories/ArjFactory.cs
+++ b/src/SharpCompress/Factories/ArjFactory.cs
@@ -33,13 +33,17 @@ public override bool IsArchive(
}
public IReader OpenReader(Stream stream, ReaderOptions? options) =>
- ArjReader.Open(stream, options);
+ ArjReader.OpenReader(stream, options);
- public ValueTask OpenReaderAsync(
+ public IAsyncReader OpenAsyncReader(
Stream stream,
ReaderOptions? options,
CancellationToken cancellationToken = default
- ) => new(ArjReader.Open(stream, options));
+ )
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ return (IAsyncReader)ArjReader.OpenReader(stream, options);
+ }
public override ValueTask IsArchiveAsync(
Stream stream,
diff --git a/src/SharpCompress/Factories/Factory.cs b/src/SharpCompress/Factories/Factory.cs
index f28f3d249..cd6da6a78 100644
--- a/src/SharpCompress/Factories/Factory.cs
+++ b/src/SharpCompress/Factories/Factory.cs
@@ -133,10 +133,7 @@ await IsArchiveAsync(
)
{
((IStreamStack)stream).StackSeek(pos);
- return (
- true,
- await readerFactory.OpenReaderAsync(stream, options, cancellationToken)
- );
+ return (true, readerFactory.OpenAsyncReader(stream, options, cancellationToken));
}
}
diff --git a/src/SharpCompress/Factories/GZipFactory.cs b/src/SharpCompress/Factories/GZipFactory.cs
index 48f5c63e5..6e92a90d6 100644
--- a/src/SharpCompress/Factories/GZipFactory.cs
+++ b/src/SharpCompress/Factories/GZipFactory.cs
@@ -61,15 +61,12 @@ public override ValueTask IsArchiveAsync(
#region IArchiveFactory
///
- public IArchive Open(Stream stream, ReaderOptions? readerOptions = null) =>
- GZipArchive.Open(stream, readerOptions);
+ public IArchive OpenArchive(Stream stream, ReaderOptions? readerOptions = null) =>
+ GZipArchive.OpenArchive(stream, readerOptions);
///
- public ValueTask OpenAsync(
- Stream stream,
- ReaderOptions? readerOptions = null,
- CancellationToken cancellationToken = default
- ) => GZipArchive.OpenAsync(stream, readerOptions, cancellationToken);
+ public IAsyncArchive OpenAsyncArchive(Stream stream, ReaderOptions? readerOptions = null) =>
+ (IAsyncArchive)OpenArchive(stream, readerOptions);
public override ValueTask IsArchiveAsync(
Stream stream,
@@ -78,41 +75,48 @@ public override ValueTask IsArchiveAsync(
) => new(IsArchive(stream, password, bufferSize));
///
- public IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) =>
- GZipArchive.Open(fileInfo, readerOptions);
-
- ///
- public ValueTask OpenAsync(
+ public IAsyncArchive OpenAsyncArchive(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
- ) => GZipArchive.OpenAsync(fileInfo, readerOptions, cancellationToken);
+ )
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ return (IAsyncArchive)OpenArchive(fileInfo, readerOptions);
+ }
#endregion
#region IMultiArchiveFactory
///
- public IArchive Open(IReadOnlyList streams, ReaderOptions? readerOptions = null) =>
- GZipArchive.Open(streams, readerOptions);
+ public IArchive OpenArchive(
+ IReadOnlyList streams,
+ ReaderOptions? readerOptions = null
+ ) => GZipArchive.OpenArchive(streams, readerOptions);
///
- public ValueTask OpenAsync(
+ public IAsyncArchive OpenAsyncArchive(
IReadOnlyList streams,
- ReaderOptions? readerOptions = null,
- CancellationToken cancellationToken = default
- ) => GZipArchive.OpenAsync(streams, readerOptions, cancellationToken);
+ ReaderOptions? readerOptions = null
+ ) => (IAsyncArchive)OpenArchive(streams, readerOptions);
///
- public IArchive Open(IReadOnlyList fileInfos, ReaderOptions? readerOptions = null) =>
- GZipArchive.Open(fileInfos, readerOptions);
+ public IArchive OpenArchive(
+ IReadOnlyList fileInfos,
+ ReaderOptions? readerOptions = null
+ ) => GZipArchive.OpenArchive(fileInfos, readerOptions);
///
- public ValueTask OpenAsync(
+ public IAsyncArchive OpenAsyncArchive(
IReadOnlyList fileInfos,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
- ) => GZipArchive.OpenAsync(fileInfos, readerOptions, cancellationToken);
+ )
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ return (IAsyncArchive)OpenArchive(fileInfos, readerOptions);
+ }
#endregion
@@ -150,25 +154,29 @@ out IReader? reader
///
public IReader OpenReader(Stream stream, ReaderOptions? options) =>
- GZipReader.Open(stream, options);
+ GZipReader.OpenReader(stream, options);
///
- public ValueTask OpenReaderAsync(
+ public IAsyncReader OpenAsyncReader(
Stream stream,
ReaderOptions? options,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
- return new(GZipReader.Open(stream, options));
+ return (IAsyncReader)GZipReader.OpenReader(stream, options);
}
+ ///
+ public IArchive OpenArchive(FileInfo fileInfo, ReaderOptions? readerOptions = null) =>
+ GZipArchive.OpenArchive(fileInfo, readerOptions);
+
#endregion
#region IWriterFactory
///
- public IWriter Open(Stream stream, WriterOptions writerOptions)
+ public IWriter OpenWriter(Stream stream, WriterOptions writerOptions)
{
if (writerOptions.CompressionType != CompressionType.GZip)
{
@@ -178,14 +186,18 @@ public IWriter Open(Stream stream, WriterOptions writerOptions)
}
///
- public ValueTask OpenAsync(
+ public IAsyncWriter OpenAsyncWriter(
Stream stream,
WriterOptions writerOptions,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
- return new(Open(stream, writerOptions));
+ if (writerOptions.CompressionType != CompressionType.GZip)
+ {
+ throw new InvalidFormatException("GZip archives only support GZip compression type.");
+ }
+ return (IAsyncWriter)OpenWriter(stream, writerOptions);
}
#endregion
@@ -193,7 +205,7 @@ public ValueTask OpenAsync(
#region IWriteableArchiveFactory
///
- public IWritableArchive CreateWriteableArchive() => GZipArchive.Create();
+ public IWritableArchive CreateArchive() => GZipArchive.CreateArchive();
#endregion
}
diff --git a/src/SharpCompress/Factories/RarFactory.cs b/src/SharpCompress/Factories/RarFactory.cs
index fb9e03abb..40f9216c2 100644
--- a/src/SharpCompress/Factories/RarFactory.cs
+++ b/src/SharpCompress/Factories/RarFactory.cs
@@ -46,26 +46,27 @@ public override bool IsArchive(
#region IArchiveFactory
///
- public IArchive Open(Stream stream, ReaderOptions? readerOptions = null) =>
- RarArchive.Open(stream, readerOptions);
+ public IArchive OpenArchive(Stream stream, ReaderOptions? readerOptions = null) =>
+ RarArchive.OpenArchive(stream, readerOptions);
///
- public ValueTask OpenAsync(
- Stream stream,
- ReaderOptions? readerOptions = null,
- CancellationToken cancellationToken = default
- ) => RarArchive.OpenAsync(stream, readerOptions, cancellationToken);
+ public IAsyncArchive OpenAsyncArchive(Stream stream, ReaderOptions? readerOptions = null) =>
+ (IAsyncArchive)OpenArchive(stream, readerOptions);
///
- public IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) =>
- RarArchive.Open(fileInfo, readerOptions);
+ public IArchive OpenArchive(FileInfo fileInfo, ReaderOptions? readerOptions = null) =>
+ RarArchive.OpenArchive(fileInfo, readerOptions);
///
- public ValueTask OpenAsync(
+ public IAsyncArchive OpenAsyncArchive(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
- ) => RarArchive.OpenAsync(fileInfo, readerOptions, cancellationToken);
+ )
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ return (IAsyncArchive)OpenArchive(fileInfo, readerOptions);
+ }
public override ValueTask IsArchiveAsync(
Stream stream,
@@ -78,26 +79,33 @@ public override ValueTask IsArchiveAsync(
#region IMultiArchiveFactory
///
- public IArchive Open(IReadOnlyList streams, ReaderOptions? readerOptions = null) =>
- RarArchive.Open(streams, readerOptions);
+ public IArchive OpenArchive(
+ IReadOnlyList streams,
+ ReaderOptions? readerOptions = null
+ ) => RarArchive.OpenArchive(streams, readerOptions);
///
- public ValueTask OpenAsync(
+ public IAsyncArchive OpenAsyncArchive(
IReadOnlyList streams,
- ReaderOptions? readerOptions = null,
- CancellationToken cancellationToken = default
- ) => RarArchive.OpenAsync(streams, readerOptions, cancellationToken);
+ ReaderOptions? readerOptions = null
+ ) => (IAsyncArchive)OpenArchive(streams, readerOptions);
///
- public IArchive Open(IReadOnlyList fileInfos, ReaderOptions? readerOptions = null) =>
- RarArchive.Open(fileInfos, readerOptions);
+ public IArchive OpenArchive(
+ IReadOnlyList fileInfos,
+ ReaderOptions? readerOptions = null
+ ) => RarArchive.OpenArchive(fileInfos, readerOptions);
///
- public ValueTask OpenAsync(
+ public IAsyncArchive OpenAsyncArchive(
IReadOnlyList fileInfos,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
- ) => RarArchive.OpenAsync(fileInfos, readerOptions, cancellationToken);
+ )
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ return (IAsyncArchive)OpenArchive(fileInfos, readerOptions);
+ }
#endregion
@@ -105,17 +113,17 @@ public ValueTask OpenAsync(
///
public IReader OpenReader(Stream stream, ReaderOptions? options) =>
- RarReader.Open(stream, options);
+ RarReader.OpenReader(stream, options);
///
- public ValueTask OpenReaderAsync(
+ public IAsyncReader OpenAsyncReader(
Stream stream,
ReaderOptions? options,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
- return new(RarReader.Open(stream, options));
+ return (IAsyncReader)RarReader.OpenReader(stream, options);
}
#endregion
diff --git a/src/SharpCompress/Factories/SevenZipFactory.cs b/src/SharpCompress/Factories/SevenZipFactory.cs
index c387e3b30..c2ea69f16 100644
--- a/src/SharpCompress/Factories/SevenZipFactory.cs
+++ b/src/SharpCompress/Factories/SevenZipFactory.cs
@@ -41,26 +41,27 @@ public override bool IsArchive(
#region IArchiveFactory
///
- public IArchive Open(Stream stream, ReaderOptions? readerOptions = null) =>
- SevenZipArchive.Open(stream, readerOptions);
+ public IArchive OpenArchive(Stream stream, ReaderOptions? readerOptions = null) =>
+ SevenZipArchive.OpenArchive(stream, readerOptions);
///
- public ValueTask OpenAsync(
- Stream stream,
- ReaderOptions? readerOptions = null,
- CancellationToken cancellationToken = default
- ) => SevenZipArchive.OpenAsync(stream, readerOptions, cancellationToken);
+ public IAsyncArchive OpenAsyncArchive(Stream stream, ReaderOptions? readerOptions = null) =>
+ (IAsyncArchive)OpenArchive(stream, readerOptions);
///
- public IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) =>
- SevenZipArchive.Open(fileInfo, readerOptions);
+ public IArchive OpenArchive(FileInfo fileInfo, ReaderOptions? readerOptions = null) =>
+ SevenZipArchive.OpenArchive(fileInfo, readerOptions);
///
- public ValueTask OpenAsync(
+ public IAsyncArchive OpenAsyncArchive(
FileInfo fileInfo,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
- ) => SevenZipArchive.OpenAsync(fileInfo, readerOptions, cancellationToken);
+ )
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ return (IAsyncArchive)OpenArchive(fileInfo, readerOptions);
+ }
public override ValueTask IsArchiveAsync(
Stream stream,
@@ -73,26 +74,33 @@ public override ValueTask IsArchiveAsync(
#region IMultiArchiveFactory
///
- public IArchive Open(IReadOnlyList streams, ReaderOptions? readerOptions = null) =>
- SevenZipArchive.Open(streams, readerOptions);
+ public IArchive OpenArchive(
+ IReadOnlyList streams,
+ ReaderOptions? readerOptions = null
+ ) => SevenZipArchive.OpenArchive(streams, readerOptions);
///
- public ValueTask OpenAsync(
+ public IAsyncArchive OpenAsyncArchive(
IReadOnlyList streams,
- ReaderOptions? readerOptions = null,
- CancellationToken cancellationToken = default
- ) => SevenZipArchive.OpenAsync(streams, readerOptions, cancellationToken);
+ ReaderOptions? readerOptions = null
+ ) => (IAsyncArchive)OpenArchive(streams, readerOptions);
///
- public IArchive Open(IReadOnlyList fileInfos, ReaderOptions? readerOptions = null) =>
- SevenZipArchive.Open(fileInfos, readerOptions);
+ public IArchive OpenArchive(
+ IReadOnlyList fileInfos,
+ ReaderOptions? readerOptions = null
+ ) => SevenZipArchive.OpenArchive(fileInfos, readerOptions);
///
- public ValueTask OpenAsync(
+ public IAsyncArchive OpenAsyncArchive(
IReadOnlyList fileInfos,
ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default
- ) => SevenZipArchive.OpenAsync(fileInfos, readerOptions, cancellationToken);
+ )
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ return (IAsyncArchive)OpenArchive(fileInfos, readerOptions);
+ }
#endregion
diff --git a/src/SharpCompress/Factories/TarFactory.cs b/src/SharpCompress/Factories/TarFactory.cs
index 4e22e0bd4..3ba7687e2 100644
--- a/src/SharpCompress/Factories/TarFactory.cs
+++ b/src/SharpCompress/Factories/TarFactory.cs
@@ -72,52 +72,60 @@ public override ValueTask