mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-02-06 21:26:07 +00:00
Compare commits
8 Commits
copilot/fi
...
adam/multi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2e301becb4 | ||
|
|
2e7d4eb74b | ||
|
|
5b2030bb98 | ||
|
|
c1169539ea | ||
|
|
8d2463f575 | ||
|
|
af7e270b2d | ||
|
|
1984da6997 | ||
|
|
4536fddec2 |
2
.github/workflows/dotnetcore.yml
vendored
2
.github/workflows/dotnetcore.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-dotnet@v5
|
||||
with:
|
||||
dotnet-version: 10.0.x
|
||||
dotnet-version: 8.0.x
|
||||
- run: dotnet run --project build/build.csproj
|
||||
- uses: actions/upload-artifact@v5
|
||||
with:
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -15,6 +15,7 @@ tests/TestArchives/*/Scratch
|
||||
tests/TestArchives/*/Scratch2
|
||||
.vs
|
||||
tools
|
||||
.vscode
|
||||
.idea/
|
||||
|
||||
.DS_Store
|
||||
|
||||
9
.vscode/extensions.json
vendored
9
.vscode/extensions.json
vendored
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"ms-dotnettools.csdevkit",
|
||||
"ms-dotnettools.csharp",
|
||||
"ms-dotnettools.vscode-dotnet-runtime",
|
||||
"csharpier.csharpier-vscode",
|
||||
"formulahendry.dotnet-test-explorer"
|
||||
]
|
||||
}
|
||||
97
.vscode/launch.json
vendored
97
.vscode/launch.json
vendored
@@ -1,97 +0,0 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Debug Tests (net10.0)",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
"program": "dotnet",
|
||||
"args": [
|
||||
"test",
|
||||
"${workspaceFolder}/tests/SharpCompress.Test/SharpCompress.Test.csproj",
|
||||
"-f",
|
||||
"net10.0",
|
||||
"--no-build",
|
||||
"--verbosity=normal"
|
||||
],
|
||||
"cwd": "${workspaceFolder}",
|
||||
"console": "internalConsole",
|
||||
"stopAtEntry": false
|
||||
},
|
||||
{
|
||||
"name": "Debug Specific Test (net10.0)",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
"program": "dotnet",
|
||||
"args": [
|
||||
"test",
|
||||
"${workspaceFolder}/tests/SharpCompress.Test/SharpCompress.Test.csproj",
|
||||
"-f",
|
||||
"net10.0",
|
||||
"--no-build",
|
||||
"--filter",
|
||||
"FullyQualifiedName~${input:testName}"
|
||||
],
|
||||
"cwd": "${workspaceFolder}",
|
||||
"console": "internalConsole",
|
||||
"stopAtEntry": false
|
||||
},
|
||||
{
|
||||
"name": "Debug Performance Tests",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
"program": "dotnet",
|
||||
"args": [
|
||||
"run",
|
||||
"--project",
|
||||
"${workspaceFolder}/tests/SharpCompress.Performance/SharpCompress.Performance.csproj",
|
||||
"--no-build"
|
||||
],
|
||||
"cwd": "${workspaceFolder}",
|
||||
"console": "internalConsole",
|
||||
"stopAtEntry": false
|
||||
},
|
||||
{
|
||||
"name": "Debug Build Script",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"program": "dotnet",
|
||||
"args": [
|
||||
"run",
|
||||
"--project",
|
||||
"${workspaceFolder}/build/build.csproj",
|
||||
"--",
|
||||
"${input:buildTarget}"
|
||||
],
|
||||
"cwd": "${workspaceFolder}",
|
||||
"console": "internalConsole",
|
||||
"stopAtEntry": false
|
||||
}
|
||||
],
|
||||
"inputs": [
|
||||
{
|
||||
"id": "testName",
|
||||
"type": "promptString",
|
||||
"description": "Enter test name or pattern (e.g., TestMethodName or ClassName)",
|
||||
"default": ""
|
||||
},
|
||||
{
|
||||
"id": "buildTarget",
|
||||
"type": "pickString",
|
||||
"description": "Select build target",
|
||||
"options": [
|
||||
"clean",
|
||||
"restore",
|
||||
"build",
|
||||
"test",
|
||||
"format",
|
||||
"publish",
|
||||
"default"
|
||||
],
|
||||
"default": "build"
|
||||
}
|
||||
]
|
||||
}
|
||||
29
.vscode/settings.json
vendored
29
.vscode/settings.json
vendored
@@ -1,29 +0,0 @@
|
||||
{
|
||||
"dotnet.defaultSolution": "SharpCompress.sln",
|
||||
"files.exclude": {
|
||||
"**/bin": true,
|
||||
"**/obj": true
|
||||
},
|
||||
"files.watcherExclude": {
|
||||
"**/bin/**": true,
|
||||
"**/obj/**": true,
|
||||
"**/artifacts/**": true
|
||||
},
|
||||
"search.exclude": {
|
||||
"**/bin": true,
|
||||
"**/obj": true,
|
||||
"**/artifacts": true
|
||||
},
|
||||
"editor.formatOnSave": false,
|
||||
"[csharp]": {
|
||||
"editor.defaultFormatter": "csharpier.csharpier-vscode",
|
||||
"editor.formatOnSave": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll": "explicit"
|
||||
}
|
||||
},
|
||||
"csharpier.enableDebugLogs": false,
|
||||
"omnisharp.enableRoslynAnalyzers": true,
|
||||
"omnisharp.enableEditorConfigSupport": true,
|
||||
"dotnet-test-explorer.testProjectPath": "tests/**/*.csproj"
|
||||
}
|
||||
178
.vscode/tasks.json
vendored
178
.vscode/tasks.json
vendored
@@ -1,178 +0,0 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "build",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"build",
|
||||
"${workspaceFolder}/SharpCompress.sln",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary;ForceNoAlign"
|
||||
],
|
||||
"problemMatcher": "$msCompile",
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "build-release",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"build",
|
||||
"${workspaceFolder}/SharpCompress.sln",
|
||||
"-c",
|
||||
"Release",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary;ForceNoAlign"
|
||||
],
|
||||
"problemMatcher": "$msCompile",
|
||||
"group": "build"
|
||||
},
|
||||
{
|
||||
"label": "build-library",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"build",
|
||||
"${workspaceFolder}/src/SharpCompress/SharpCompress.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary;ForceNoAlign"
|
||||
],
|
||||
"problemMatcher": "$msCompile",
|
||||
"group": "build"
|
||||
},
|
||||
{
|
||||
"label": "restore",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"restore",
|
||||
"${workspaceFolder}/SharpCompress.sln"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "clean",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"clean",
|
||||
"${workspaceFolder}/SharpCompress.sln"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "test",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"test",
|
||||
"${workspaceFolder}/tests/SharpCompress.Test/SharpCompress.Test.csproj",
|
||||
"--no-build",
|
||||
"--verbosity=normal"
|
||||
],
|
||||
"problemMatcher": "$msCompile",
|
||||
"group": {
|
||||
"kind": "test",
|
||||
"isDefault": true
|
||||
},
|
||||
"dependsOn": "build"
|
||||
},
|
||||
{
|
||||
"label": "test-net10",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"test",
|
||||
"${workspaceFolder}/tests/SharpCompress.Test/SharpCompress.Test.csproj",
|
||||
"-f",
|
||||
"net10.0",
|
||||
"--no-build",
|
||||
"--verbosity=normal"
|
||||
],
|
||||
"problemMatcher": "$msCompile",
|
||||
"group": "test",
|
||||
"dependsOn": "build"
|
||||
},
|
||||
{
|
||||
"label": "test-net48",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"test",
|
||||
"${workspaceFolder}/tests/SharpCompress.Test/SharpCompress.Test.csproj",
|
||||
"-f",
|
||||
"net48",
|
||||
"--no-build",
|
||||
"--verbosity=normal"
|
||||
],
|
||||
"problemMatcher": "$msCompile",
|
||||
"group": "test",
|
||||
"dependsOn": "build"
|
||||
},
|
||||
{
|
||||
"label": "format",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"csharpier",
|
||||
"."
|
||||
],
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "format-check",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"csharpier",
|
||||
"check",
|
||||
"."
|
||||
],
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "run-build-script",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"run",
|
||||
"--project",
|
||||
"${workspaceFolder}/build/build.csproj"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "pack",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"pack",
|
||||
"${workspaceFolder}/src/SharpCompress/SharpCompress.csproj",
|
||||
"-c",
|
||||
"Release",
|
||||
"-o",
|
||||
"${workspaceFolder}/artifacts/"
|
||||
],
|
||||
"problemMatcher": "$msCompile",
|
||||
"dependsOn": "build-release"
|
||||
},
|
||||
{
|
||||
"label": "performance-tests",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"run",
|
||||
"--project",
|
||||
"${workspaceFolder}/tests/SharpCompress.Performance/SharpCompress.Performance.csproj",
|
||||
"-c",
|
||||
"Release"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
}
|
||||
]
|
||||
}
|
||||
39
AGENTS.md
39
AGENTS.md
@@ -49,30 +49,6 @@ SharpCompress is a pure C# compression library supporting multiple archive forma
|
||||
- Use `dotnet test` to run tests
|
||||
- Solution file: `SharpCompress.sln`
|
||||
|
||||
### Directory Structure
|
||||
```
|
||||
src/SharpCompress/
|
||||
├── Archives/ # IArchive implementations (Zip, Tar, Rar, 7Zip, GZip)
|
||||
├── Readers/ # IReader implementations (forward-only)
|
||||
├── Writers/ # IWriter implementations (forward-only)
|
||||
├── Compressors/ # Low-level compression streams (BZip2, Deflate, LZMA, etc.)
|
||||
├── Factories/ # Format detection and factory pattern
|
||||
├── Common/ # Shared types (ArchiveType, Entry, Options)
|
||||
├── Crypto/ # Encryption implementations
|
||||
└── IO/ # Stream utilities and wrappers
|
||||
|
||||
tests/SharpCompress.Test/
|
||||
├── Zip/, Tar/, Rar/, SevenZip/, GZip/, BZip2/ # Format-specific tests
|
||||
├── TestBase.cs # Base test class with helper methods
|
||||
└── TestArchives/ # Test data (not checked into main test project)
|
||||
```
|
||||
|
||||
### Factory Pattern
|
||||
All format types implement factory interfaces (`IArchiveFactory`, `IReaderFactory`, `IWriterFactory`) for auto-detection:
|
||||
- `ReaderFactory.Open()` - Auto-detects format by probing stream
|
||||
- `WriterFactory.Open()` - Creates writer for specified `ArchiveType`
|
||||
- Factories located in: `src/SharpCompress/Factories/`
|
||||
|
||||
## Nullable Reference Types
|
||||
|
||||
- Declare variables non-nullable, and check for `null` at entry points.
|
||||
@@ -140,18 +116,3 @@ SharpCompress supports multiple archive and compression formats:
|
||||
- Use test archives from `tests/TestArchives` directory for consistency.
|
||||
- Test stream disposal and `LeaveStreamOpen` behavior.
|
||||
- Test edge cases: empty archives, large files, corrupted archives, encrypted archives.
|
||||
|
||||
### Test Organization
|
||||
- Base class: `TestBase` - Provides `TEST_ARCHIVES_PATH`, `SCRATCH_FILES_PATH`, temp directory management
|
||||
- Framework: xUnit with AwesomeAssertions
|
||||
- Test archives: `tests/TestArchives/` - Use existing archives, don't create new ones unnecessarily
|
||||
- Match naming style of nearby test files
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
1. **Don't mix Archive and Reader APIs** - Archive needs seekable stream, Reader doesn't
|
||||
2. **Solid archives (Rar, 7Zip)** - Use `ExtractAllEntries()` for best performance, not individual entry extraction
|
||||
3. **Stream disposal** - Always set `LeaveStreamOpen` explicitly when needed (default is to close)
|
||||
4. **Tar + non-seekable stream** - Must provide file size or it will throw
|
||||
5. **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
|
||||
|
||||
@@ -4,17 +4,17 @@
|
||||
<PackageVersion Include="AwesomeAssertions" Version="9.3.0" />
|
||||
<PackageVersion Include="Glob" Version="1.1.9" />
|
||||
<PackageVersion Include="JetBrains.Profiler.SelfApi" Version="2.5.14" />
|
||||
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="10.0.0" />
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
|
||||
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.0.0" />
|
||||
<PackageVersion Include="Mono.Posix.NETStandard" Version="1.0.0" />
|
||||
<PackageVersion Include="SimpleExec" Version="12.0.0" />
|
||||
<PackageVersion Include="System.Buffers" Version="4.6.1" />
|
||||
<PackageVersion Include="System.Memory" Version="4.6.3" />
|
||||
<PackageVersion Include="System.Text.Encoding.CodePages" Version="10.0.0" />
|
||||
<PackageVersion Include="System.Text.Encoding.CodePages" Version="8.0.0" />
|
||||
<PackageVersion Include="xunit" Version="2.9.3" />
|
||||
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" />
|
||||
<PackageVersion Include="ZstdSharp.Port" Version="0.8.6" />
|
||||
<PackageVersion Include="Microsoft.NET.ILLink.Tasks" Version="10.0.0" />
|
||||
<PackageVersion Include="Microsoft.NET.ILLink.Tasks" Version="8.0.21" />
|
||||
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -22,16 +22,11 @@
|
||||
| 7Zip (4) | LZMA, LZMA2, BZip2, PPMd, BCJ, BCJ2, Deflate | Decompress | SevenZipArchive | N/A | N/A |
|
||||
|
||||
1. SOLID Rars are only supported in the RarReader API.
|
||||
2. Zip format supports pkware and WinzipAES encryption. However, encrypted LZMA is not supported. Zip64 reading/writing is supported but only with seekable streams as the Zip spec doesn't support Zip64 data in post data descriptors. Deflate64 is only supported for reading. See [Zip Format Notes](#zip-format-notes) for details on multi-volume archives and streaming behavior.
|
||||
2. Zip format supports pkware and WinzipAES encryption. However, encrypted LZMA is not supported. Zip64 reading/writing is supported but only with seekable streams as the Zip spec doesn't support Zip64 data in post data descriptors. Deflate64 is only supported for reading.
|
||||
3. The Tar format requires a file size in the header. If no size is specified to the TarWriter and the stream is not seekable, then an exception will be thrown.
|
||||
4. The 7Zip format doesn't allow for reading as a forward-only stream so 7Zip is only supported through the Archive API
|
||||
5. LZip has no support for extra data like the file name or timestamp. There is a default filename used when looking at the entry Key on the archive.
|
||||
|
||||
### Zip Format Notes
|
||||
|
||||
- Multi-volume/split ZIP archives require ZipArchive (seekable streams) as ZipReader cannot seek across volume files.
|
||||
- ZipReader processes entries from LocalEntry headers (which include directory entries ending with `/`) and intentionally skips DirectoryEntry headers from the central directory, as they are redundant in streaming mode - all entry data comes from LocalEntry headers which ZipReader has already processed.
|
||||
|
||||
## Compression Streams
|
||||
|
||||
For those who want to directly compress/decompress bits. The single file formats are represented here as well. However, BZip2, LZip and XZ have no metadata (GZip has a little) so using them without something like a Tar file makes little sense.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# SharpCompress
|
||||
|
||||
SharpCompress is a compression library in pure C# for .NET Framework 4.8, .NET 8.0 and .NET 10.0 that can unrar, un7zip, unzip, untar unbzip2, ungzip, unlzip, unzstd, unarc and unarj with forward-only reading and file random access APIs. Write support for zip/tar/bzip2/gzip/lzip are implemented.
|
||||
SharpCompress is a compression library in pure C# for .NET Framework 4.62, .NET Standard 2.1, .NET 6.0 and NET 8.0 that can unrar, un7zip, unzip, untar unbzip2, ungzip, unlzip, unzstd, unarc and unarj with forward-only reading and file random access APIs. Write support for zip/tar/bzip2/gzip/lzip are implemented.
|
||||
|
||||
The major feature is support for non-seekable streams so large files can be processed on the fly (i.e. download stream).
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Bullseye" />
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"version": 2,
|
||||
"dependencies": {
|
||||
"net10.0": {
|
||||
"net8.0": {
|
||||
"Bullseye": {
|
||||
"type": "Direct",
|
||||
"requested": "[6.0.0, )",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"sdk": {
|
||||
"version": "10.0.100",
|
||||
"version": "8.0.100",
|
||||
"rollForward": "latestFeature"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,11 +161,6 @@ public abstract class AbstractArchive<TEntry, TVolume> : IArchive, IArchiveExtra
|
||||
/// </summary>
|
||||
public virtual bool IsSolid => false;
|
||||
|
||||
/// <summary>
|
||||
/// Archive is ENCRYPTED (this means the Archive has password-protected files).
|
||||
/// </summary>
|
||||
public virtual bool IsEncrypted => false;
|
||||
|
||||
/// <summary>
|
||||
/// The archive can find all the parts of the archive needed to fully extract the archive. This forces the parsing of the entire archive.
|
||||
/// </summary>
|
||||
@@ -177,4 +172,9 @@ public abstract class AbstractArchive<TEntry, TVolume> : IArchive, IArchiveExtra
|
||||
return Entries.All(x => x.IsComplete);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool IsMultiVolume =>
|
||||
_sourceStream?.Files.Count > 1 || _sourceStream?.Streams.Count > 1;
|
||||
|
||||
public virtual bool SupportsMultiThreading => false;
|
||||
}
|
||||
|
||||
@@ -3,10 +3,6 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Common.Tar.Headers;
|
||||
using SharpCompress.Compressors;
|
||||
using SharpCompress.Compressors.BZip2;
|
||||
using SharpCompress.Compressors.LZMA;
|
||||
using SharpCompress.Factories;
|
||||
using SharpCompress.IO;
|
||||
using SharpCompress.Readers;
|
||||
@@ -135,10 +131,10 @@ public static class ArchiveFactory
|
||||
{
|
||||
finfo.NotNull(nameof(finfo));
|
||||
using Stream stream = finfo.OpenRead();
|
||||
return FindFactory<T>(stream, finfo.Name);
|
||||
return FindFactory<T>(stream);
|
||||
}
|
||||
|
||||
private static T FindFactory<T>(Stream stream, string? fileName = null)
|
||||
private static T FindFactory<T>(Stream stream)
|
||||
where T : IFactory
|
||||
{
|
||||
stream.NotNull(nameof(stream));
|
||||
@@ -163,16 +159,6 @@ public static class ArchiveFactory
|
||||
}
|
||||
}
|
||||
|
||||
stream.Seek(startPosition, SeekOrigin.Begin);
|
||||
|
||||
// Check if this is a compressed tar file (tar.bz2, tar.lz, etc.)
|
||||
// These formats are supported by ReaderFactory but not by ArchiveFactory
|
||||
var compressedTarMessage = TryGetCompressedTarMessage(stream, fileName);
|
||||
if (compressedTarMessage != null)
|
||||
{
|
||||
throw new InvalidOperationException(compressedTarMessage);
|
||||
}
|
||||
|
||||
var extensions = string.Join(", ", factories.Select(item => item.Name));
|
||||
|
||||
throw new InvalidOperationException(
|
||||
@@ -262,111 +248,4 @@ public static class ArchiveFactory
|
||||
}
|
||||
|
||||
public static IArchiveFactory AutoFactory { get; } = new AutoArchiveFactory();
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the stream is a compressed tar file (tar.bz2, tar.lz, etc.) that should use ReaderFactory instead.
|
||||
/// Returns an error message if detected, null otherwise.
|
||||
/// </summary>
|
||||
private static string? TryGetCompressedTarMessage(Stream stream, string? fileName)
|
||||
{
|
||||
var startPosition = stream.Position;
|
||||
try
|
||||
{
|
||||
// Check if it's a BZip2 file
|
||||
if (BZip2Stream.IsBZip2(stream))
|
||||
{
|
||||
stream.Seek(startPosition, SeekOrigin.Begin);
|
||||
|
||||
// Try to decompress and check if it contains a tar archive
|
||||
using var decompressed = new BZip2Stream(stream, CompressionMode.Decompress, true);
|
||||
if (IsTarStream(decompressed))
|
||||
{
|
||||
return "This appears to be a tar.bz2 archive. The Archive API requires seekable streams, but decompression streams are not seekable. "
|
||||
+ "Please use ReaderFactory.Open() instead for forward-only extraction, "
|
||||
+ "or decompress the file first and then open the resulting tar file with ArchiveFactory.Open().";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
stream.Seek(startPosition, SeekOrigin.Begin);
|
||||
|
||||
// Check if it's an LZip file
|
||||
if (LZipStream.IsLZipFile(stream))
|
||||
{
|
||||
stream.Seek(startPosition, SeekOrigin.Begin);
|
||||
|
||||
// Try to decompress and check if it contains a tar archive
|
||||
using var decompressed = new LZipStream(stream, CompressionMode.Decompress);
|
||||
if (IsTarStream(decompressed))
|
||||
{
|
||||
return "This appears to be a tar.lz archive. The Archive API requires seekable streams, but decompression streams are not seekable. "
|
||||
+ "Please use ReaderFactory.Open() instead for forward-only extraction, "
|
||||
+ "or decompress the file first and then open the resulting tar file with ArchiveFactory.Open().";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check file extension as a fallback for other compressed tar formats
|
||||
if (fileName != null)
|
||||
{
|
||||
var lowerFileName = fileName.ToLowerInvariant();
|
||||
if (
|
||||
lowerFileName.EndsWith(".tar.bz2")
|
||||
|| lowerFileName.EndsWith(".tbz")
|
||||
|| lowerFileName.EndsWith(".tbz2")
|
||||
|| lowerFileName.EndsWith(".tb2")
|
||||
|| lowerFileName.EndsWith(".tz2")
|
||||
|| lowerFileName.EndsWith(".tar.lz")
|
||||
|| lowerFileName.EndsWith(".tar.xz")
|
||||
|| lowerFileName.EndsWith(".txz")
|
||||
|| lowerFileName.EndsWith(".tar.zst")
|
||||
|| lowerFileName.EndsWith(".tar.zstd")
|
||||
|| lowerFileName.EndsWith(".tzst")
|
||||
|| lowerFileName.EndsWith(".tzstd")
|
||||
|| lowerFileName.EndsWith(".tar.z")
|
||||
|| lowerFileName.EndsWith(".tz")
|
||||
|| lowerFileName.EndsWith(".taz")
|
||||
)
|
||||
{
|
||||
return $"The file '{fileName}' appears to be a compressed tar archive. The Archive API requires seekable streams, but decompression streams are not seekable. "
|
||||
+ "Please use ReaderFactory.Open() instead for forward-only extraction, "
|
||||
+ "or decompress the file first and then open the resulting tar file with ArchiveFactory.Open().";
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// If we can't determine, just return null and let the normal error handling proceed
|
||||
return null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
stream.Seek(startPosition, SeekOrigin.Begin);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore seek failures
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a stream contains a tar archive by trying to read a tar header.
|
||||
/// </summary>
|
||||
private static bool IsTarStream(Stream stream)
|
||||
{
|
||||
try
|
||||
{
|
||||
var tarHeader = new TarHeader(new ArchiveEncoding());
|
||||
return tarHeader.Read(new BinaryReader(stream));
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,4 +45,14 @@ public interface IArchive : IDisposable
|
||||
/// The total size of the files as uncompressed in the archive.
|
||||
/// </summary>
|
||||
long TotalUncompressSize { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Is the archive part of a multi-volume set.
|
||||
/// </summary>
|
||||
bool IsMultiVolume { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Does the archive support multi-threaded extraction.
|
||||
/// </summary>
|
||||
bool SupportsMultiThreading { get; }
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ public static class IArchiveEntryExtensions
|
||||
entry,
|
||||
destinationDirectory,
|
||||
options,
|
||||
(x, opt) => entry.WriteToFileAsync(x, opt, cancellationToken),
|
||||
entry.WriteToFileAsync,
|
||||
cancellationToken
|
||||
);
|
||||
|
||||
@@ -124,10 +124,10 @@ public static class IArchiveEntryExtensions
|
||||
entry,
|
||||
destinationFileName,
|
||||
options,
|
||||
async (x, fm) =>
|
||||
async (x, fm, ct) =>
|
||||
{
|
||||
using var fs = File.Open(destinationFileName, fm);
|
||||
await entry.WriteToAsync(fs, cancellationToken).ConfigureAwait(false);
|
||||
await entry.WriteToAsync(fs, ct).ConfigureAwait(false);
|
||||
},
|
||||
cancellationToken
|
||||
);
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using SharpCompress.Common.Rar;
|
||||
using SharpCompress.Common.Rar.Headers;
|
||||
using SharpCompress.IO;
|
||||
using SharpCompress.Readers;
|
||||
|
||||
namespace SharpCompress.Archives.Rar;
|
||||
|
||||
/// <summary>
|
||||
/// A rar part based on a FileInfo object
|
||||
/// </summary>
|
||||
internal class FileInfoRarArchiveVolume : RarVolume
|
||||
{
|
||||
internal FileInfoRarArchiveVolume(FileInfo fileInfo, ReaderOptions options, int index)
|
||||
: base(StreamingMode.Seekable, fileInfo.OpenRead(), FixOptions(options), index)
|
||||
{
|
||||
FileInfo = fileInfo;
|
||||
FileParts = GetVolumeFileParts().ToArray().ToReadOnly();
|
||||
}
|
||||
|
||||
private static ReaderOptions FixOptions(ReaderOptions options)
|
||||
{
|
||||
//make sure we're closing streams with fileinfo
|
||||
options.LeaveStreamOpen = false;
|
||||
return options;
|
||||
}
|
||||
|
||||
internal ReadOnlyCollection<RarFilePart> FileParts { get; }
|
||||
|
||||
internal FileInfo FileInfo { get; }
|
||||
|
||||
internal override RarFilePart CreateFilePart(MarkHeader markHeader, FileHeader fileHeader) =>
|
||||
new FileInfoRarFilePart(this, ReaderOptions.Password, markHeader, fileHeader, FileInfo);
|
||||
|
||||
internal override IEnumerable<RarFilePart> ReadFileParts() => FileParts;
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
using System.IO;
|
||||
using SharpCompress.Common.Rar.Headers;
|
||||
|
||||
namespace SharpCompress.Archives.Rar;
|
||||
|
||||
internal sealed class FileInfoRarFilePart : SeekableFilePart
|
||||
{
|
||||
internal FileInfoRarFilePart(
|
||||
FileInfoRarArchiveVolume volume,
|
||||
string? password,
|
||||
MarkHeader mh,
|
||||
FileHeader fh,
|
||||
FileInfo fi
|
||||
)
|
||||
: base(mh, fh, volume.Index, volume.Stream, password) => FileInfo = fi;
|
||||
|
||||
internal FileInfo FileInfo { get; }
|
||||
|
||||
internal override string FilePartName =>
|
||||
"Rar File: " + FileInfo.FullName + " File Entry: " + FileHeader.FileName;
|
||||
}
|
||||
@@ -47,9 +47,9 @@ public class RarArchive : AbstractArchive<RarArchiveEntry, RarVolume>
|
||||
{
|
||||
sourceStream.LoadAllParts(); //request all streams
|
||||
var streams = sourceStream.Streams.ToArray();
|
||||
var i = 0;
|
||||
if (streams.Length > 1 && IsRarFile(streams[1], ReaderOptions)) //test part 2 - true = multipart not split
|
||||
{
|
||||
var i = 0;
|
||||
sourceStream.IsVolumes = true;
|
||||
streams[1].Position = 0;
|
||||
sourceStream.Position = 0;
|
||||
@@ -57,12 +57,18 @@ public class RarArchive : AbstractArchive<RarArchiveEntry, RarVolume>
|
||||
return sourceStream.Streams.Select(a => new StreamRarArchiveVolume(
|
||||
a,
|
||||
ReaderOptions,
|
||||
i++
|
||||
i++,
|
||||
IsMultiVolume
|
||||
));
|
||||
}
|
||||
|
||||
//split mode or single file
|
||||
return new StreamRarArchiveVolume(sourceStream, ReaderOptions, i++).AsEnumerable();
|
||||
return new StreamRarArchiveVolume(
|
||||
sourceStream,
|
||||
ReaderOptions,
|
||||
0,
|
||||
IsMultiVolume
|
||||
).AsEnumerable();
|
||||
}
|
||||
|
||||
protected override IReader CreateReaderForSolidExtraction()
|
||||
@@ -83,8 +89,7 @@ public class RarArchive : AbstractArchive<RarArchiveEntry, RarVolume>
|
||||
}
|
||||
|
||||
public override bool IsSolid => Volumes.First().IsSolidArchive;
|
||||
|
||||
public override bool IsEncrypted => Entries.First(x => !x.IsDirectory).IsEncrypted;
|
||||
public override bool SupportsMultiThreading => !IsMultiVolume && !IsSolid;
|
||||
|
||||
public virtual int MinVersion => Volumes.First().MinVersion;
|
||||
public virtual int MaxVersion => Volumes.First().MaxVersion;
|
||||
|
||||
@@ -134,4 +134,6 @@ public class RarArchiveEntry : RarEntry, IArchiveEntry
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool SupportsMultiThreading => Parts.Single().SupportsMultiThreading;
|
||||
}
|
||||
|
||||
@@ -1,25 +1,29 @@
|
||||
using System.IO;
|
||||
using SharpCompress.Common.Rar;
|
||||
using SharpCompress.Common.Rar.Headers;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Archives.Rar;
|
||||
|
||||
internal class SeekableFilePart : RarFilePart
|
||||
internal class SeekableRarFilePart : RarFilePart
|
||||
{
|
||||
private readonly Stream _stream;
|
||||
private readonly string? _password;
|
||||
private readonly bool _isMultiVolume;
|
||||
|
||||
internal SeekableFilePart(
|
||||
internal SeekableRarFilePart(
|
||||
MarkHeader mh,
|
||||
FileHeader fh,
|
||||
int index,
|
||||
Stream stream,
|
||||
string? password
|
||||
string? password,
|
||||
bool isMultiVolume
|
||||
)
|
||||
: base(mh, fh, index)
|
||||
{
|
||||
_stream = stream;
|
||||
_password = password;
|
||||
_isMultiVolume = isMultiVolume;
|
||||
}
|
||||
|
||||
internal override Stream GetCompressedStream()
|
||||
@@ -42,4 +46,7 @@ internal class SeekableFilePart : RarFilePart
|
||||
}
|
||||
|
||||
internal override string FilePartName => "Unknown Stream - File Entry: " + FileHeader.FileName;
|
||||
|
||||
public override bool SupportsMultiThreading =>
|
||||
!_isMultiVolume && _stream is SourceStream ss && ss.IsFileMode && ss.Files.Count == 1;
|
||||
}
|
||||
@@ -9,11 +9,28 @@ namespace SharpCompress.Archives.Rar;
|
||||
|
||||
internal class StreamRarArchiveVolume : RarVolume
|
||||
{
|
||||
internal StreamRarArchiveVolume(Stream stream, ReaderOptions options, int index)
|
||||
: base(StreamingMode.Seekable, stream, options, index) { }
|
||||
private readonly bool _isMultiVolume;
|
||||
|
||||
internal StreamRarArchiveVolume(
|
||||
Stream stream,
|
||||
ReaderOptions options,
|
||||
int index,
|
||||
bool isMultiVolume
|
||||
)
|
||||
: base(StreamingMode.Seekable, stream, options, index)
|
||||
{
|
||||
_isMultiVolume = isMultiVolume;
|
||||
}
|
||||
|
||||
internal override IEnumerable<RarFilePart> ReadFileParts() => GetVolumeFileParts();
|
||||
|
||||
internal override RarFilePart CreateFilePart(MarkHeader markHeader, FileHeader fileHeader) =>
|
||||
new SeekableFilePart(markHeader, fileHeader, Index, Stream, ReaderOptions.Password);
|
||||
new SeekableRarFilePart(
|
||||
markHeader,
|
||||
fileHeader,
|
||||
Index,
|
||||
Stream,
|
||||
ReaderOptions.Password,
|
||||
_isMultiVolume
|
||||
);
|
||||
}
|
||||
|
||||
@@ -205,8 +205,6 @@ public class SevenZipArchive : AbstractArchive<SevenZipArchiveEntry, SevenZipVol
|
||||
.GroupBy(x => x.FilePart.Folder)
|
||||
.Any(folder => folder.Count() > 1);
|
||||
|
||||
public override bool IsEncrypted => Entries.First(x => !x.IsDirectory).IsEncrypted;
|
||||
|
||||
public override long TotalSize =>
|
||||
_database?._packSizes.Aggregate(0L, (total, packSize) => total + packSize) ?? 0;
|
||||
|
||||
|
||||
@@ -283,7 +283,12 @@ public class ZipArchive : AbstractWritableArchive<ZipArchiveEntry, ZipVolume>
|
||||
|
||||
yield return new ZipArchiveEntry(
|
||||
this,
|
||||
new SeekableZipFilePart(headerFactory.NotNull(), deh, s)
|
||||
new SeekableZipFilePart(
|
||||
headerFactory.NotNull(),
|
||||
deh,
|
||||
s,
|
||||
IsMultiVolume
|
||||
)
|
||||
);
|
||||
}
|
||||
break;
|
||||
@@ -385,4 +390,6 @@ public class ZipArchive : AbstractWritableArchive<ZipArchiveEntry, ZipVolume>
|
||||
((IStreamStack)stream).StackSeek(0);
|
||||
return ZipReader.Open(stream, ReaderOptions, Entries);
|
||||
}
|
||||
|
||||
public override bool SupportsMultiThreading => !IsMultiVolume;
|
||||
}
|
||||
|
||||
@@ -23,5 +23,7 @@ public class ZipArchiveEntry : ZipEntry, IArchiveEntry
|
||||
|
||||
public bool IsComplete => true;
|
||||
|
||||
public override bool SupportsMultiThreading => Parts.Single().SupportsMultiThreading;
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -87,4 +87,5 @@ public abstract class Entry : IEntry
|
||||
/// Entry file attribute.
|
||||
/// </summary>
|
||||
public virtual int? Attrib => throw new NotImplementedException();
|
||||
public virtual bool SupportsMultiThreading => false;
|
||||
}
|
||||
|
||||
@@ -128,7 +128,7 @@ internal static class ExtractionMethods
|
||||
IEntry entry,
|
||||
string destinationDirectory,
|
||||
ExtractionOptions? options,
|
||||
Func<string, ExtractionOptions?, Task> writeAsync,
|
||||
Func<string, ExtractionOptions?, CancellationToken, Task> writeAsync,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
@@ -189,7 +189,7 @@ internal static class ExtractionMethods
|
||||
"Entry is trying to write a file outside of the destination directory."
|
||||
);
|
||||
}
|
||||
await writeAsync(destinationFileName, options).ConfigureAwait(false);
|
||||
await writeAsync(destinationFileName, options, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
else if (options.ExtractFullPath && !Directory.Exists(destinationFileName))
|
||||
{
|
||||
@@ -201,7 +201,7 @@ internal static class ExtractionMethods
|
||||
IEntry entry,
|
||||
string destinationFileName,
|
||||
ExtractionOptions? options,
|
||||
Func<string, FileMode, Task> openAndWriteAsync,
|
||||
Func<string, FileMode, CancellationToken, Task> openAndWriteAsync,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
@@ -225,7 +225,8 @@ internal static class ExtractionMethods
|
||||
fm = FileMode.CreateNew;
|
||||
}
|
||||
|
||||
await openAndWriteAsync(destinationFileName, fm).ConfigureAwait(false);
|
||||
await openAndWriteAsync(destinationFileName, fm, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
entry.PreserveExtractionOptions(destinationFileName, options);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,4 +14,6 @@ public abstract class FilePart
|
||||
internal abstract Stream? GetCompressedStream();
|
||||
internal abstract Stream? GetRawStream();
|
||||
internal bool Skipped { get; set; }
|
||||
|
||||
public virtual bool SupportsMultiThreading => false;
|
||||
}
|
||||
|
||||
@@ -21,4 +21,5 @@ public interface IEntry
|
||||
DateTime? LastModifiedTime { get; }
|
||||
long Size { get; }
|
||||
int? Attrib { get; }
|
||||
bool SupportsMultiThreading { get; }
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.IO;
|
||||
using SharpCompress.Common.Zip.Headers;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Common.Zip;
|
||||
|
||||
@@ -7,13 +8,19 @@ internal class SeekableZipFilePart : ZipFilePart
|
||||
{
|
||||
private bool _isLocalHeaderLoaded;
|
||||
private readonly SeekableZipHeaderFactory _headerFactory;
|
||||
private readonly bool _isMultiVolume;
|
||||
|
||||
internal SeekableZipFilePart(
|
||||
SeekableZipHeaderFactory headerFactory,
|
||||
DirectoryEntryHeader header,
|
||||
Stream stream
|
||||
Stream stream,
|
||||
bool isMultiVolume
|
||||
)
|
||||
: base(header, stream) => _headerFactory = headerFactory;
|
||||
: base(header, stream)
|
||||
{
|
||||
_headerFactory = headerFactory;
|
||||
_isMultiVolume = isMultiVolume;
|
||||
}
|
||||
|
||||
internal override Stream GetCompressedStream()
|
||||
{
|
||||
@@ -30,8 +37,20 @@ internal class SeekableZipFilePart : ZipFilePart
|
||||
|
||||
protected override Stream CreateBaseStream()
|
||||
{
|
||||
if (!_isMultiVolume && BaseStream is SourceStream ss)
|
||||
{
|
||||
if (ss.IsFileMode && ss.Files.Count == 1)
|
||||
{
|
||||
var fileStream = ss.CurrentFile.OpenRead();
|
||||
fileStream.Position = Header.DataStartPosition.NotNull();
|
||||
return fileStream;
|
||||
}
|
||||
}
|
||||
BaseStream.Position = Header.DataStartPosition.NotNull();
|
||||
|
||||
return BaseStream;
|
||||
}
|
||||
|
||||
public override bool SupportsMultiThreading =>
|
||||
!_isMultiVolume && BaseStream is SourceStream ss && ss.IsFileMode && ss.Files.Count == 1;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Buffers.Binary;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace SharpCompress.Common.Zip;
|
||||
|
||||
@@ -20,24 +19,8 @@ internal class WinzipAesEncryptionData
|
||||
{
|
||||
_keySize = keySize;
|
||||
|
||||
#if NETFRAMEWORK
|
||||
#if NETFRAMEWORK || NETSTANDARD2_0
|
||||
var rfc2898 = new Rfc2898DeriveBytes(password, salt, RFC2898_ITERATIONS);
|
||||
KeyBytes = rfc2898.GetBytes(KeySizeInBytes);
|
||||
IvBytes = rfc2898.GetBytes(KeySizeInBytes);
|
||||
var generatedVerifyValue = rfc2898.GetBytes(2);
|
||||
#elif NET10_0_OR_GREATER
|
||||
var derivedKeySize = (KeySizeInBytes * 2) + 2;
|
||||
var passwordBytes = Encoding.UTF8.GetBytes(password);
|
||||
var derivedKey = Rfc2898DeriveBytes.Pbkdf2(
|
||||
passwordBytes,
|
||||
salt,
|
||||
RFC2898_ITERATIONS,
|
||||
HashAlgorithmName.SHA1,
|
||||
derivedKeySize
|
||||
);
|
||||
KeyBytes = derivedKey.AsSpan(0, KeySizeInBytes).ToArray();
|
||||
IvBytes = derivedKey.AsSpan(KeySizeInBytes, KeySizeInBytes).ToArray();
|
||||
var generatedVerifyValue = derivedKey.AsSpan((KeySizeInBytes * 2), 2).ToArray();
|
||||
#else
|
||||
var rfc2898 = new Rfc2898DeriveBytes(
|
||||
password,
|
||||
@@ -45,10 +28,11 @@ internal class WinzipAesEncryptionData
|
||||
RFC2898_ITERATIONS,
|
||||
HashAlgorithmName.SHA1
|
||||
);
|
||||
KeyBytes = rfc2898.GetBytes(KeySizeInBytes);
|
||||
#endif
|
||||
|
||||
KeyBytes = rfc2898.GetBytes(KeySizeInBytes); // 16 or 24 or 32 ???
|
||||
IvBytes = rfc2898.GetBytes(KeySizeInBytes);
|
||||
var generatedVerifyValue = rfc2898.GetBytes(2);
|
||||
#endif
|
||||
|
||||
var verify = BinaryPrimitives.ReadInt16LittleEndian(passwordVerifyValue);
|
||||
var generated = BinaryPrimitives.ReadInt16LittleEndian(generatedVerifyValue);
|
||||
|
||||
@@ -544,12 +544,6 @@ internal sealed class CBZip2OutputStream : Stream, IStreamStack
|
||||
|
||||
private void EndBlock()
|
||||
{
|
||||
// Skip block processing for empty input (no data written)
|
||||
if (last < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
blockCRC = mCrc.GetFinalCRC();
|
||||
combinedCRC = (combinedCRC << 1) | (int)(((uint)combinedCRC) >> 31);
|
||||
combinedCRC ^= blockCRC;
|
||||
|
||||
@@ -62,10 +62,6 @@ internal sealed class MultiVolumeReadOnlyStream : Stream, IStreamStack
|
||||
base.Dispose(disposing);
|
||||
if (disposing)
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugDispose(typeof(MultiVolumeReadOnlyStream));
|
||||
#endif
|
||||
|
||||
if (filePartEnumerator != null)
|
||||
{
|
||||
filePartEnumerator.Dispose();
|
||||
|
||||
@@ -82,9 +82,6 @@ internal class RarStream : Stream, IStreamStack
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugDispose(typeof(RarStream));
|
||||
#endif
|
||||
ArrayPool<byte>.Shared.Return(this.tmpBuffer);
|
||||
this.tmpBuffer = null;
|
||||
}
|
||||
|
||||
@@ -57,238 +57,19 @@ public class TarFactory
|
||||
Stream stream,
|
||||
string? password = null,
|
||||
int bufferSize = ReaderOptions.DefaultBufferSize
|
||||
)
|
||||
{
|
||||
if (!stream.CanSeek)
|
||||
{
|
||||
return TarArchive.IsTarFile(stream); // For non-seekable streams, just check if it's a tar file
|
||||
}
|
||||
|
||||
var startPosition = stream.Position;
|
||||
|
||||
// First check if it's a regular tar file
|
||||
if (TarArchive.IsTarFile(stream))
|
||||
{
|
||||
stream.Seek(startPosition, SeekOrigin.Begin); // Seek back for consistency
|
||||
return true;
|
||||
}
|
||||
|
||||
// Seek back after the tar file check
|
||||
stream.Seek(startPosition, SeekOrigin.Begin);
|
||||
|
||||
if (compressionOptions == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Try each compression option to see if it contains a tar file
|
||||
foreach (var testOption in compressionOptions)
|
||||
{
|
||||
if (testOption.Type == CompressionType.None)
|
||||
{
|
||||
continue; // Skip uncompressed
|
||||
}
|
||||
|
||||
stream.Seek(startPosition, SeekOrigin.Begin);
|
||||
|
||||
try
|
||||
{
|
||||
if (testOption.CanHandle(stream))
|
||||
{
|
||||
stream.Seek(startPosition, SeekOrigin.Begin);
|
||||
|
||||
// Try to decompress and check if it contains a tar archive
|
||||
// For compression formats that don't support leaveOpen, we need to save/restore position
|
||||
var positionBeforeDecompress = stream.Position;
|
||||
Stream? decompressedStream = null;
|
||||
bool streamWasClosed = false;
|
||||
|
||||
try
|
||||
{
|
||||
decompressedStream = testOption.Type switch
|
||||
{
|
||||
CompressionType.BZip2 => new BZip2Stream(stream, CompressionMode.Decompress, true),
|
||||
_ => testOption.CreateStream(stream) // For other types, may close the stream
|
||||
};
|
||||
|
||||
if (TarArchive.IsTarFile(decompressedStream))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
streamWasClosed = true;
|
||||
throw; // Stream was closed, can't continue
|
||||
}
|
||||
finally
|
||||
{
|
||||
decompressedStream?.Dispose();
|
||||
|
||||
if (!streamWasClosed && stream.CanSeek)
|
||||
{
|
||||
try
|
||||
{
|
||||
stream.Seek(positionBeforeDecompress, SeekOrigin.Begin);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// If seek fails, the stream might have been closed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Seek back to start after decompression attempt
|
||||
stream.Seek(startPosition, SeekOrigin.Begin);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// If decompression fails, it's not this format - continue to next option
|
||||
try
|
||||
{
|
||||
stream.Seek(startPosition, SeekOrigin.Begin);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore seek failures
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
stream.Seek(startPosition, SeekOrigin.Begin);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore seek failures
|
||||
}
|
||||
}
|
||||
}
|
||||
) => TarArchive.IsTarFile(stream);
|
||||
|
||||
#endregion
|
||||
|
||||
#region IArchiveFactory
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IArchive Open(Stream stream, ReaderOptions? readerOptions = null)
|
||||
{
|
||||
readerOptions ??= new ReaderOptions();
|
||||
|
||||
// Try to detect and handle compressed tar formats
|
||||
if (stream.CanSeek)
|
||||
{
|
||||
var startPosition = stream.Position;
|
||||
|
||||
// Try each compression option to see if we can decompress it
|
||||
foreach (var testOption in compressionOptions)
|
||||
{
|
||||
if (testOption.Type == CompressionType.None)
|
||||
{
|
||||
continue; // Skip uncompressed
|
||||
}
|
||||
|
||||
stream.Seek(startPosition, SeekOrigin.Begin);
|
||||
|
||||
if (testOption.CanHandle(stream))
|
||||
{
|
||||
stream.Seek(startPosition, SeekOrigin.Begin);
|
||||
|
||||
// Decompress the entire stream into a seekable MemoryStream
|
||||
using var decompressedStream = testOption.CreateStream(stream);
|
||||
var memoryStream = new MemoryStream();
|
||||
decompressedStream.CopyTo(memoryStream);
|
||||
memoryStream.Position = 0;
|
||||
|
||||
// Verify it's actually a tar file
|
||||
if (TarArchive.IsTarFile(memoryStream))
|
||||
{
|
||||
memoryStream.Position = 0;
|
||||
// Return a TarArchive from the decompressed memory stream
|
||||
// The TarArchive will own the MemoryStream and dispose it when disposed
|
||||
var options = new ReaderOptions
|
||||
{
|
||||
LeaveStreamOpen = false, // Ensure the MemoryStream is disposed with the archive
|
||||
ArchiveEncoding = readerOptions?.ArchiveEncoding ?? new ArchiveEncoding()
|
||||
};
|
||||
return TarArchive.Open(memoryStream, options);
|
||||
}
|
||||
|
||||
memoryStream.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
stream.Seek(startPosition, SeekOrigin.Begin);
|
||||
}
|
||||
|
||||
// Fall back to normal tar archive opening
|
||||
return TarArchive.Open(stream, readerOptions);
|
||||
}
|
||||
public IArchive Open(Stream stream, ReaderOptions? readerOptions = null) =>
|
||||
TarArchive.Open(stream, readerOptions);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null)
|
||||
{
|
||||
readerOptions ??= new ReaderOptions();
|
||||
|
||||
// Try to detect and handle compressed tar formats by file extension and content
|
||||
using var fileStream = fileInfo.OpenRead();
|
||||
|
||||
// Try each compression option
|
||||
foreach (var testOption in compressionOptions)
|
||||
{
|
||||
if (testOption.Type == CompressionType.None)
|
||||
{
|
||||
continue; // Skip uncompressed
|
||||
}
|
||||
|
||||
// Check if file extension matches
|
||||
var fileName = fileInfo.Name.ToLowerInvariant();
|
||||
if (testOption.KnownExtensions.Any(ext => fileName.EndsWith(ext)))
|
||||
{
|
||||
fileStream.Position = 0;
|
||||
|
||||
// Verify it's the right compression format
|
||||
if (testOption.CanHandle(fileStream))
|
||||
{
|
||||
fileStream.Position = 0;
|
||||
|
||||
// Decompress the entire file into a seekable MemoryStream
|
||||
using var decompressedStream = testOption.CreateStream(fileStream);
|
||||
var memoryStream = new MemoryStream();
|
||||
decompressedStream.CopyTo(memoryStream);
|
||||
memoryStream.Position = 0;
|
||||
|
||||
// Verify it's actually a tar file
|
||||
if (TarArchive.IsTarFile(memoryStream))
|
||||
{
|
||||
memoryStream.Position = 0;
|
||||
// Return a TarArchive from the decompressed memory stream
|
||||
// The TarArchive will own the MemoryStream and dispose it when disposed
|
||||
var options = new ReaderOptions
|
||||
{
|
||||
LeaveStreamOpen = false, // Ensure the MemoryStream is disposed with the archive
|
||||
ArchiveEncoding = readerOptions?.ArchiveEncoding ?? new ArchiveEncoding()
|
||||
};
|
||||
return TarArchive.Open(memoryStream, options);
|
||||
}
|
||||
|
||||
memoryStream.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fileStream will be closed by the using statement
|
||||
|
||||
// Fall back to normal tar archive opening
|
||||
return TarArchive.Open(fileInfo, readerOptions);
|
||||
}
|
||||
public IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) =>
|
||||
TarArchive.Open(fileInfo, readerOptions);
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ public class SourceStream : Stream, IStreamStack
|
||||
#endif
|
||||
int IStreamStack.DefaultBufferSize { get; set; }
|
||||
|
||||
Stream IStreamStack.BaseStream() => _streams[_stream];
|
||||
Stream IStreamStack.BaseStream() => _streams[_streamIndex];
|
||||
|
||||
int IStreamStack.BufferSize
|
||||
{
|
||||
@@ -35,7 +35,7 @@ public class SourceStream : Stream, IStreamStack
|
||||
private readonly List<Stream> _streams;
|
||||
private readonly Func<int, FileInfo?>? _getFilePart;
|
||||
private readonly Func<int, Stream?>? _getStreamPart;
|
||||
private int _stream;
|
||||
private int _streamIndex;
|
||||
|
||||
public SourceStream(FileInfo file, Func<int, FileInfo?> getPart, ReaderOptions options)
|
||||
: this(null, null, file, getPart, options) { }
|
||||
@@ -59,7 +59,7 @@ public class SourceStream : Stream, IStreamStack
|
||||
|
||||
if (!IsFileMode)
|
||||
{
|
||||
_streams.Add(stream!);
|
||||
_streams.Add(stream.NotNull("stream is null"));
|
||||
_getStreamPart = getStreamPart;
|
||||
_getFilePart = _ => null;
|
||||
if (stream is FileStream fileStream)
|
||||
@@ -69,12 +69,12 @@ public class SourceStream : Stream, IStreamStack
|
||||
}
|
||||
else
|
||||
{
|
||||
_files.Add(file!);
|
||||
_files.Add(file.NotNull("file is null"));
|
||||
_streams.Add(_files[0].OpenRead());
|
||||
_getFilePart = getFilePart;
|
||||
_getStreamPart = _ => null;
|
||||
}
|
||||
_stream = 0;
|
||||
_streamIndex = 0;
|
||||
_prevSize = 0;
|
||||
|
||||
#if DEBUG_STREAMS
|
||||
@@ -93,10 +93,12 @@ public class SourceStream : Stream, IStreamStack
|
||||
public ReaderOptions ReaderOptions { get; }
|
||||
public bool IsFileMode { get; }
|
||||
|
||||
public IEnumerable<FileInfo> Files => _files;
|
||||
public IEnumerable<Stream> Streams => _streams;
|
||||
public IReadOnlyList<FileInfo> Files => _files;
|
||||
public IReadOnlyList<Stream> Streams => _streams;
|
||||
|
||||
private Stream Current => _streams[_stream];
|
||||
private Stream Current => _streams[_streamIndex];
|
||||
|
||||
public FileInfo CurrentFile => _files[_streamIndex];
|
||||
|
||||
public bool LoadStream(int index) //ensure all parts to id are loaded
|
||||
{
|
||||
@@ -107,7 +109,7 @@ public class SourceStream : Stream, IStreamStack
|
||||
var f = _getFilePart.NotNull("GetFilePart is null")(_streams.Count);
|
||||
if (f == null)
|
||||
{
|
||||
_stream = _streams.Count - 1;
|
||||
_streamIndex = _streams.Count - 1;
|
||||
return false;
|
||||
}
|
||||
//throw new Exception($"File part {idx} not available.");
|
||||
@@ -119,7 +121,7 @@ public class SourceStream : Stream, IStreamStack
|
||||
var s = _getStreamPart.NotNull("GetStreamPart is null")(_streams.Count);
|
||||
if (s == null)
|
||||
{
|
||||
_stream = _streams.Count - 1;
|
||||
_streamIndex = _streams.Count - 1;
|
||||
return false;
|
||||
}
|
||||
//throw new Exception($"Stream part {idx} not available.");
|
||||
@@ -137,10 +139,10 @@ public class SourceStream : Stream, IStreamStack
|
||||
{
|
||||
if (LoadStream(idx))
|
||||
{
|
||||
_stream = idx;
|
||||
_streamIndex = idx;
|
||||
}
|
||||
|
||||
return _stream == idx;
|
||||
return _streamIndex == idx;
|
||||
}
|
||||
|
||||
public override bool CanRead => true;
|
||||
@@ -184,7 +186,7 @@ public class SourceStream : Stream, IStreamStack
|
||||
var length = Current.Length;
|
||||
|
||||
// Load next file if present
|
||||
if (!SetStream(_stream + 1))
|
||||
if (!SetStream(_streamIndex + 1))
|
||||
{
|
||||
break;
|
||||
}
|
||||
@@ -223,7 +225,7 @@ public class SourceStream : Stream, IStreamStack
|
||||
while (_prevSize + Current.Length < pos)
|
||||
{
|
||||
_prevSize += Current.Length;
|
||||
SetStream(_stream + 1);
|
||||
SetStream(_streamIndex + 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -273,7 +275,7 @@ public class SourceStream : Stream, IStreamStack
|
||||
var length = Current.Length;
|
||||
|
||||
// Load next file if present
|
||||
if (!SetStream(_stream + 1))
|
||||
if (!SetStream(_streamIndex + 1))
|
||||
{
|
||||
break;
|
||||
}
|
||||
@@ -322,7 +324,7 @@ public class SourceStream : Stream, IStreamStack
|
||||
var length = Current.Length;
|
||||
|
||||
// Load next file if present
|
||||
if (!SetStream(_stream + 1))
|
||||
if (!SetStream(_streamIndex + 1))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ public static class IReaderExtensions
|
||||
reader.Entry,
|
||||
destinationDirectory,
|
||||
options,
|
||||
(fileName, opts) => reader.WriteEntryToFileAsync(fileName, opts, cancellationToken),
|
||||
reader.WriteEntryToFileAsync,
|
||||
cancellationToken
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
@@ -101,10 +101,10 @@ public static class IReaderExtensions
|
||||
reader.Entry,
|
||||
destinationFileName,
|
||||
options,
|
||||
async (x, fm) =>
|
||||
async (x, fm, ct) =>
|
||||
{
|
||||
using var fs = File.Open(destinationFileName, fm);
|
||||
await reader.WriteEntryToAsync(fs, cancellationToken).ConfigureAwait(false);
|
||||
await reader.WriteEntryToAsync(fs, ct).ConfigureAwait(false);
|
||||
},
|
||||
cancellationToken
|
||||
)
|
||||
|
||||
@@ -75,14 +75,6 @@ public class ZipReader : AbstractReader<ZipEntry, ZipVolume>
|
||||
);
|
||||
}
|
||||
break;
|
||||
case ZipHeaderType.DirectoryEntry:
|
||||
// DirectoryEntry headers in the central directory are intentionally skipped.
|
||||
// In streaming mode, we can only read forward, and DirectoryEntry headers
|
||||
// reference LocalEntry headers that have already been processed. The file
|
||||
// data comes from LocalEntry headers, not DirectoryEntry headers.
|
||||
// For multi-volume ZIPs where file data spans multiple files, use ZipArchive
|
||||
// instead, which requires seekable streams.
|
||||
break;
|
||||
case ZipHeaderType.DirectoryEnd:
|
||||
{
|
||||
yield break;
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
<PropertyGroup>
|
||||
<AssemblyTitle>SharpCompress - Pure C# Decompression/Compression</AssemblyTitle>
|
||||
<NeutralLanguage>en-US</NeutralLanguage>
|
||||
<VersionPrefix>0.42.0</VersionPrefix>
|
||||
<AssemblyVersion>0.42.0</AssemblyVersion>
|
||||
<FileVersion>0.42.0</FileVersion>
|
||||
<VersionPrefix>0.41.0</VersionPrefix>
|
||||
<AssemblyVersion>0.41.0</AssemblyVersion>
|
||||
<FileVersion>0.41.0</FileVersion>
|
||||
<Authors>Adam Hathcock</Authors>
|
||||
<TargetFrameworks>net48;net8.0;net10.0</TargetFrameworks>
|
||||
<TargetFrameworks>net48;net481;netstandard2.0;net6.0;net8.0</TargetFrameworks>
|
||||
<AssemblyName>SharpCompress</AssemblyName>
|
||||
<AssemblyOriginatorKeyFile>../../SharpCompress.snk</AssemblyOriginatorKeyFile>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
@@ -17,7 +17,7 @@
|
||||
<Copyright>Copyright (c) 2025 Adam Hathcock</Copyright>
|
||||
<GenerateAssemblyTitleAttribute>false</GenerateAssemblyTitleAttribute>
|
||||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
|
||||
<Description>SharpCompress is a compression library for NET 4.8/NET 8.0/NET 10.0 that can unrar, decompress 7zip, decompress xz, zip/unzip, tar/untar lzip/unlzip, bzip2/unbzip2 and gzip/ungzip with forward-only reading and file random access APIs. Write support for zip/tar/bzip2/gzip is implemented.</Description>
|
||||
<Description>SharpCompress is a compression library for NET Standard 2.0/NET 4.8/NET 4.8.1/NET 6.0/NET 8.0 that can unrar, decompress 7zip, decompress xz, zip/unzip, tar/untar lzip/unlzip, bzip2/unbzip2 and gzip/ungzip with forward-only reading and file random access APIs. Write support for zip/tar/bzip2/gzip is implemented.</Description>
|
||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<DebugType>embedded</DebugType>
|
||||
@@ -28,29 +28,31 @@
|
||||
<EmbedUntrackedSources>true</EmbedUntrackedSources>
|
||||
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(TargetFramework)' == 'net8.0' Or '$(TargetFramework)' == 'net10.0' ">
|
||||
<PropertyGroup Condition=" '$(TargetFramework)' == 'net8.0' ">
|
||||
<IsTrimmable>true</IsTrimmable>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net8.0|AnyCPU'">
|
||||
<DefineConstants>$(DefineConstants);DEBUG_STREAMS</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net10.0|AnyCPU'">
|
||||
<DefineConstants>$(DefineConstants);DEBUG_STREAMS</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ZstdSharp.Port" />
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' Or '$(TargetFramework)' == 'net10.0' ">
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0'">
|
||||
<PackageReference Include="Microsoft.NET.ILLink.Tasks" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net48' ">
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net48' Or '$(TargetFramework)' == 'net481' ">
|
||||
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" />
|
||||
<PackageReference Include="System.Text.Encoding.CodePages" />
|
||||
<PackageReference Include="System.Buffers" />
|
||||
<PackageReference Include="System.Memory" />
|
||||
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
|
||||
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" />
|
||||
<PackageReference Include="System.Text.Encoding.CodePages" />
|
||||
<PackageReference Include="System.Memory" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\..\README.md" Pack="true" PackagePath="\" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
".NETFramework,Version=v4.8": {
|
||||
"Microsoft.Bcl.AsyncInterfaces": {
|
||||
"type": "Direct",
|
||||
"requested": "[10.0.0, )",
|
||||
"resolved": "10.0.0",
|
||||
"contentHash": "vFuwSLj9QJBbNR0NeNO4YVASUbokxs+i/xbuu8B+Fs4FAZg5QaFa6eGrMaRqTzzNI5tAb97T7BhSxtLckFyiRA==",
|
||||
"requested": "[8.0.0, )",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==",
|
||||
"dependencies": {
|
||||
"System.Threading.Tasks.Extensions": "4.6.3"
|
||||
"System.Threading.Tasks.Extensions": "4.5.4"
|
||||
}
|
||||
},
|
||||
"Microsoft.NETFramework.ReferenceAssemblies": {
|
||||
@@ -49,13 +49,12 @@
|
||||
},
|
||||
"System.Text.Encoding.CodePages": {
|
||||
"type": "Direct",
|
||||
"requested": "[10.0.0, )",
|
||||
"resolved": "10.0.0",
|
||||
"contentHash": "QLP54mIATaBpjGlsZIxga38VPk1G9js0Kw651B+bvrXi2kSgGZYrxJSpM3whhTZCBK4HEBHX3fzfDQMw7CXHGQ==",
|
||||
"requested": "[8.0.0, )",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "OZIsVplFGaVY90G2SbpgU7EnCoOO5pw1t4ic21dBF3/1omrJFpAGoNAVpPyMVOC90/hvgkGG3VFqR13YgZMQfg==",
|
||||
"dependencies": {
|
||||
"System.Memory": "4.6.3",
|
||||
"System.Runtime.CompilerServices.Unsafe": "6.1.2",
|
||||
"System.ValueTuple": "4.6.1"
|
||||
"System.Memory": "4.5.5",
|
||||
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
|
||||
}
|
||||
},
|
||||
"ZstdSharp.Port": {
|
||||
@@ -96,25 +95,216 @@
|
||||
},
|
||||
"System.Threading.Tasks.Extensions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.6.3",
|
||||
"contentHash": "7sCiwilJLYbTZELaKnc7RecBBXWXA+xMLQWZKWawBxYjp6DBlSE3v9/UcvKBvr1vv2tTOhipiogM8rRmxlhrVA==",
|
||||
"resolved": "4.5.4",
|
||||
"contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
|
||||
"dependencies": {
|
||||
"System.Runtime.CompilerServices.Unsafe": "4.5.3"
|
||||
}
|
||||
}
|
||||
},
|
||||
".NETFramework,Version=v4.8.1": {
|
||||
"Microsoft.Bcl.AsyncInterfaces": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.0.0, )",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==",
|
||||
"dependencies": {
|
||||
"System.Threading.Tasks.Extensions": "4.5.4"
|
||||
}
|
||||
},
|
||||
"Microsoft.NETFramework.ReferenceAssemblies": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.0.3, )",
|
||||
"resolved": "1.0.3",
|
||||
"contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==",
|
||||
"dependencies": {
|
||||
"Microsoft.NETFramework.ReferenceAssemblies.net481": "1.0.3"
|
||||
}
|
||||
},
|
||||
"Microsoft.SourceLink.GitHub": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.0.0, )",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
|
||||
"dependencies": {
|
||||
"Microsoft.Build.Tasks.Git": "8.0.0",
|
||||
"Microsoft.SourceLink.Common": "8.0.0"
|
||||
}
|
||||
},
|
||||
"System.Buffers": {
|
||||
"type": "Direct",
|
||||
"requested": "[4.6.1, )",
|
||||
"resolved": "4.6.1",
|
||||
"contentHash": "N8GXpmiLMtljq7gwvyS+1QvKT/W2J8sNAvx+HVg4NGmsG/H+2k/y9QI23auLJRterrzCiDH+IWAw4V/GPwsMlw=="
|
||||
},
|
||||
"System.Memory": {
|
||||
"type": "Direct",
|
||||
"requested": "[4.6.3, )",
|
||||
"resolved": "4.6.3",
|
||||
"contentHash": "qdcDOgnFZY40+Q9876JUHnlHu7bosOHX8XISRoH94fwk6hgaeQGSgfZd8srWRZNt5bV9ZW2TljcegDNxsf+96A==",
|
||||
"dependencies": {
|
||||
"System.Buffers": "4.6.1",
|
||||
"System.Numerics.Vectors": "4.6.1",
|
||||
"System.Runtime.CompilerServices.Unsafe": "6.1.2"
|
||||
}
|
||||
},
|
||||
"System.ValueTuple": {
|
||||
"System.Text.Encoding.CodePages": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.0.0, )",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "OZIsVplFGaVY90G2SbpgU7EnCoOO5pw1t4ic21dBF3/1omrJFpAGoNAVpPyMVOC90/hvgkGG3VFqR13YgZMQfg==",
|
||||
"dependencies": {
|
||||
"System.Memory": "4.5.5",
|
||||
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
|
||||
}
|
||||
},
|
||||
"ZstdSharp.Port": {
|
||||
"type": "Direct",
|
||||
"requested": "[0.8.6, )",
|
||||
"resolved": "0.8.6",
|
||||
"contentHash": "iP4jVLQoQmUjMU88g1WObiNr6YKZGvh4aOXn3yOJsHqZsflwRsxZPcIBvNXgjXO3vQKSLctXGLTpcBPLnWPS8A==",
|
||||
"dependencies": {
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
"System.Memory": "4.5.5",
|
||||
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Build.Tasks.Git": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
|
||||
},
|
||||
"Microsoft.NETFramework.ReferenceAssemblies.net481": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.0.3",
|
||||
"contentHash": "Vv/20vgHS7VglVOVh8J3Iz/MA+VYKVRp9f7r2qiKBMuzviTOmocG70yq0Q8T5OTmCONkEAIJwETD1zhEfLkAXQ=="
|
||||
},
|
||||
"Microsoft.SourceLink.Common": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
|
||||
},
|
||||
"System.Numerics.Vectors": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.6.1",
|
||||
"contentHash": "+RJT4qaekpZ7DDLhf+LTjq+E48jieKiY9ulJ+BoxKmZblIJfIJT8Ufcaa/clQqnYvWs8jugfGSMu8ylS0caG0w=="
|
||||
"contentHash": "sQxefTnhagrhoq2ReR0D/6K0zJcr9Hrd6kikeXsA1I8kOCboTavcUC4r7TSfpKFeE163uMuxZcyfO1mGO3EN8Q=="
|
||||
},
|
||||
"System.Runtime.CompilerServices.Unsafe": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.1.2",
|
||||
"contentHash": "2hBr6zdbIBTDE3EhK7NSVNdX58uTK6iHW/P/Axmm9sl1xoGSLqDvMtpecn226TNwHByFokYwJmt/aQQNlO5CRw=="
|
||||
},
|
||||
"System.Threading.Tasks.Extensions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.4",
|
||||
"contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
|
||||
"dependencies": {
|
||||
"System.Runtime.CompilerServices.Unsafe": "4.5.3"
|
||||
}
|
||||
}
|
||||
},
|
||||
"net10.0": {
|
||||
"Microsoft.NET.ILLink.Tasks": {
|
||||
".NETStandard,Version=v2.0": {
|
||||
"Microsoft.Bcl.AsyncInterfaces": {
|
||||
"type": "Direct",
|
||||
"requested": "[10.0.0, )",
|
||||
"resolved": "10.0.0",
|
||||
"contentHash": "kICGrGYEzCNI3wPzfEXcwNHgTvlvVn9yJDhSdRK+oZQy4jvYH529u7O0xf5ocQKzOMjfS07+3z9PKRIjrFMJDA=="
|
||||
"requested": "[8.0.0, )",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==",
|
||||
"dependencies": {
|
||||
"System.Threading.Tasks.Extensions": "4.5.4"
|
||||
}
|
||||
},
|
||||
"Microsoft.SourceLink.GitHub": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.0.0, )",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
|
||||
"dependencies": {
|
||||
"Microsoft.Build.Tasks.Git": "8.0.0",
|
||||
"Microsoft.SourceLink.Common": "8.0.0"
|
||||
}
|
||||
},
|
||||
"NETStandard.Library": {
|
||||
"type": "Direct",
|
||||
"requested": "[2.0.3, )",
|
||||
"resolved": "2.0.3",
|
||||
"contentHash": "st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==",
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.Platforms": "1.1.0"
|
||||
}
|
||||
},
|
||||
"System.Memory": {
|
||||
"type": "Direct",
|
||||
"requested": "[4.6.3, )",
|
||||
"resolved": "4.6.3",
|
||||
"contentHash": "qdcDOgnFZY40+Q9876JUHnlHu7bosOHX8XISRoH94fwk6hgaeQGSgfZd8srWRZNt5bV9ZW2TljcegDNxsf+96A==",
|
||||
"dependencies": {
|
||||
"System.Buffers": "4.6.1",
|
||||
"System.Numerics.Vectors": "4.6.1",
|
||||
"System.Runtime.CompilerServices.Unsafe": "6.1.2"
|
||||
}
|
||||
},
|
||||
"System.Text.Encoding.CodePages": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.0.0, )",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "OZIsVplFGaVY90G2SbpgU7EnCoOO5pw1t4ic21dBF3/1omrJFpAGoNAVpPyMVOC90/hvgkGG3VFqR13YgZMQfg==",
|
||||
"dependencies": {
|
||||
"System.Memory": "4.5.5",
|
||||
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
|
||||
}
|
||||
},
|
||||
"ZstdSharp.Port": {
|
||||
"type": "Direct",
|
||||
"requested": "[0.8.6, )",
|
||||
"resolved": "0.8.6",
|
||||
"contentHash": "iP4jVLQoQmUjMU88g1WObiNr6YKZGvh4aOXn3yOJsHqZsflwRsxZPcIBvNXgjXO3vQKSLctXGLTpcBPLnWPS8A==",
|
||||
"dependencies": {
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
"System.Memory": "4.5.5",
|
||||
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Build.Tasks.Git": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
|
||||
},
|
||||
"Microsoft.NETCore.Platforms": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.1.0",
|
||||
"contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A=="
|
||||
},
|
||||
"Microsoft.SourceLink.Common": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
|
||||
},
|
||||
"System.Numerics.Vectors": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.6.1",
|
||||
"contentHash": "sQxefTnhagrhoq2ReR0D/6K0zJcr9Hrd6kikeXsA1I8kOCboTavcUC4r7TSfpKFeE163uMuxZcyfO1mGO3EN8Q=="
|
||||
},
|
||||
"System.Runtime.CompilerServices.Unsafe": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.1.2",
|
||||
"contentHash": "2hBr6zdbIBTDE3EhK7NSVNdX58uTK6iHW/P/Axmm9sl1xoGSLqDvMtpecn226TNwHByFokYwJmt/aQQNlO5CRw=="
|
||||
},
|
||||
"System.Threading.Tasks.Extensions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.4",
|
||||
"contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
|
||||
"dependencies": {
|
||||
"System.Runtime.CompilerServices.Unsafe": "4.5.3"
|
||||
}
|
||||
},
|
||||
"System.Buffers": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[4.6.1, )",
|
||||
"resolved": "4.6.1",
|
||||
"contentHash": "N8GXpmiLMtljq7gwvyS+1QvKT/W2J8sNAvx+HVg4NGmsG/H+2k/y9QI23auLJRterrzCiDH+IWAw4V/GPwsMlw=="
|
||||
}
|
||||
},
|
||||
"net6.0": {
|
||||
"Microsoft.SourceLink.GitHub": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.0.0, )",
|
||||
@@ -145,9 +335,9 @@
|
||||
"net8.0": {
|
||||
"Microsoft.NET.ILLink.Tasks": {
|
||||
"type": "Direct",
|
||||
"requested": "[10.0.0, )",
|
||||
"resolved": "10.0.0",
|
||||
"contentHash": "kICGrGYEzCNI3wPzfEXcwNHgTvlvVn9yJDhSdRK+oZQy4jvYH529u7O0xf5ocQKzOMjfS07+3z9PKRIjrFMJDA=="
|
||||
"requested": "[8.0.21, )",
|
||||
"resolved": "8.0.21",
|
||||
"contentHash": "s8H5PZQs50OcNkaB6Si54+v3GWM7vzs6vxFRMlD3aXsbM+aPCtod62gmK0BYWou9diGzmo56j8cIf/PziijDqQ=="
|
||||
},
|
||||
"Microsoft.SourceLink.GitHub": {
|
||||
"type": "Direct",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="JetBrains.Profiler.SelfApi" />
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"version": 2,
|
||||
"dependencies": {
|
||||
"net10.0": {
|
||||
"net8.0": {
|
||||
"JetBrains.Profiler.SelfApi": {
|
||||
"type": "Direct",
|
||||
"requested": "[2.5.14, )",
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using SharpCompress.Archives;
|
||||
using Xunit;
|
||||
|
||||
namespace SharpCompress.Test;
|
||||
|
||||
public class ArchiveFactoryCompressedTarTests : TestBase
|
||||
{
|
||||
[Fact]
|
||||
public void ArchiveFactory_Open_TarBz2_ThrowsHelpfulException()
|
||||
{
|
||||
var testFile = Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.bz2");
|
||||
var exception = Assert.Throws<InvalidOperationException>(() =>
|
||||
{
|
||||
using var archive = ArchiveFactory.Open(testFile);
|
||||
});
|
||||
|
||||
Assert.Contains("tar.bz2", exception.Message);
|
||||
Assert.Contains("ReaderFactory", exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ArchiveFactory_Open_TarLz_ThrowsHelpfulException()
|
||||
{
|
||||
var testFile = Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.lz");
|
||||
var exception = Assert.Throws<InvalidOperationException>(() =>
|
||||
{
|
||||
using var archive = ArchiveFactory.Open(testFile);
|
||||
});
|
||||
|
||||
Assert.Contains("tar.lz", exception.Message);
|
||||
Assert.Contains("ReaderFactory", exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ArchiveFactory_Open_TarBz2Stream_ThrowsHelpfulException()
|
||||
{
|
||||
var testFile = Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.bz2");
|
||||
using var stream = File.OpenRead(testFile);
|
||||
var exception = Assert.Throws<InvalidOperationException>(() =>
|
||||
{
|
||||
using var archive = ArchiveFactory.Open(stream);
|
||||
});
|
||||
|
||||
Assert.Contains("tar.bz2", exception.Message);
|
||||
Assert.Contains("ReaderFactory", exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ArchiveFactory_Open_TarLzStream_ThrowsHelpfulException()
|
||||
{
|
||||
var testFile = Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.lz");
|
||||
using var stream = File.OpenRead(testFile);
|
||||
var exception = Assert.Throws<InvalidOperationException>(() =>
|
||||
{
|
||||
using var archive = ArchiveFactory.Open(stream);
|
||||
});
|
||||
|
||||
Assert.Contains("tar.lz", exception.Message);
|
||||
Assert.Contains("ReaderFactory", exception.Message);
|
||||
}
|
||||
}
|
||||
@@ -134,6 +134,7 @@ public class ArchiveTests : ReaderTests
|
||||
{
|
||||
foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory))
|
||||
{
|
||||
Assert.False(entry.SupportsMultiThreading);
|
||||
entry.WriteToDirectory(
|
||||
SCRATCH_FILES_PATH,
|
||||
new ExtractionOptions { ExtractFullPath = true, Overwrite = true }
|
||||
@@ -266,6 +267,31 @@ public class ArchiveTests : ReaderTests
|
||||
VerifyFiles();
|
||||
}
|
||||
|
||||
protected async Task ArchiveFileRead_Multithreaded(
|
||||
IArchiveFactory archiveFactory,
|
||||
string testArchive,
|
||||
ReaderOptions? readerOptions = null
|
||||
)
|
||||
{
|
||||
testArchive = Path.Combine(TEST_ARCHIVES_PATH, testArchive);
|
||||
var tasks = new List<Task>();
|
||||
using (var archive = archiveFactory.Open(new FileInfo(testArchive), readerOptions))
|
||||
{
|
||||
Assert.True(archive.SupportsMultiThreading);
|
||||
foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory))
|
||||
{
|
||||
Assert.True(entry.SupportsMultiThreading);
|
||||
var t = entry.WriteToDirectoryAsync(
|
||||
SCRATCH_FILES_PATH,
|
||||
new ExtractionOptions { ExtractFullPath = true, Overwrite = true }
|
||||
);
|
||||
tasks.Add(t);
|
||||
}
|
||||
}
|
||||
await Task.WhenAll(tasks);
|
||||
VerifyFiles();
|
||||
}
|
||||
|
||||
protected void ArchiveFileRead(
|
||||
IArchiveFactory archiveFactory,
|
||||
string testArchive,
|
||||
@@ -289,6 +315,11 @@ public class ArchiveTests : ReaderTests
|
||||
protected void ArchiveFileRead(string testArchive, ReaderOptions? readerOptions = null) =>
|
||||
ArchiveFileRead(ArchiveFactory.AutoFactory, testArchive, readerOptions);
|
||||
|
||||
protected Task ArchiveFileRead_Multithreaded(
|
||||
string testArchive,
|
||||
ReaderOptions? readerOptions = null
|
||||
) => ArchiveFileRead_Multithreaded(ArchiveFactory.AutoFactory, testArchive, readerOptions);
|
||||
|
||||
protected void ArchiveFileSkip(
|
||||
string testArchive,
|
||||
string fileOrder,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Archives.Rar;
|
||||
using SharpCompress.Common;
|
||||
@@ -292,9 +293,15 @@ public class RarArchiveTests : ArchiveTests
|
||||
[Fact]
|
||||
public void Rar_ArchiveFileRead() => ArchiveFileRead("Rar.rar");
|
||||
|
||||
[Fact]
|
||||
public Task Rar_ArchiveFileRead_Multithreaded() => ArchiveFileRead_Multithreaded("Rar.rar");
|
||||
|
||||
[Fact]
|
||||
public void Rar5_ArchiveFileRead() => ArchiveFileRead("Rar5.rar");
|
||||
|
||||
[Fact]
|
||||
public Task Rar5_ArchiveFileRead_Multithreaded() => ArchiveFileRead_Multithreaded("Rar5.rar");
|
||||
|
||||
[Fact]
|
||||
public void Rar_ArchiveFileRead_HasDirectories() =>
|
||||
DoRar_ArchiveFileRead_HasDirectories("Rar.rar");
|
||||
@@ -359,6 +366,9 @@ public class RarArchiveTests : ArchiveTests
|
||||
[Fact]
|
||||
public void Rar2_ArchiveFileRead() => ArchiveFileRead("Rar2.rar");
|
||||
|
||||
[Fact]
|
||||
public Task Rar2_ArchiveFileRead_Multithreaded() => ArchiveFileRead_Multithreaded("Rar2.rar");
|
||||
|
||||
[Fact]
|
||||
public void Rar15_ArchiveFileRead()
|
||||
{
|
||||
@@ -633,13 +643,4 @@ public class RarArchiveTests : ArchiveTests
|
||||
"Rar5.encrypted_filesOnly.rar",
|
||||
"Failure jpg exe Empty тест.txt jpg\\test.jpg exe\\test.exe"
|
||||
);
|
||||
|
||||
[Fact]
|
||||
public void Rar_TestEncryptedDetection()
|
||||
{
|
||||
using var passwordProtectedFilesArchive = RarArchive.Open(
|
||||
Path.Combine(TEST_ARCHIVES_PATH, "Rar.encrypted_filesOnly.rar")
|
||||
);
|
||||
Assert.True(passwordProtectedFilesArchive.IsEncrypted);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,15 +224,6 @@ public class SevenZipArchiveTests : ArchiveTests
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SevenZipArchive_TestEncryptedDetection()
|
||||
{
|
||||
using var passwordProtectedFilesArchive = SevenZipArchive.Open(
|
||||
Path.Combine(TEST_ARCHIVES_PATH, "7Zip.encryptedFiles.7z")
|
||||
);
|
||||
Assert.True(passwordProtectedFilesArchive.IsEncrypted);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SevenZipArchive_TestSolidDetection()
|
||||
{
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net10.0;net48</TargetFrameworks>
|
||||
<TargetFrameworks>net8.0;net48</TargetFrameworks>
|
||||
<AssemblyName>SharpCompress.Test</AssemblyName>
|
||||
<PackageId>SharpCompress.Test</PackageId>
|
||||
<AssemblyOriginatorKeyFile>SharpCompress.Test.snk</AssemblyOriginatorKeyFile>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net10.0|AnyCPU'">
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net8.0|AnyCPU'">
|
||||
<DefineConstants>$(DefineConstants);DEBUG_STREAMS</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))">
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Archives.Zip;
|
||||
using SharpCompress.Common;
|
||||
@@ -88,6 +89,10 @@ public class ZipArchiveTests : ArchiveTests
|
||||
[Fact]
|
||||
public void Zip_Deflate_ArchiveFileRead() => ArchiveFileRead("Zip.deflate.zip");
|
||||
|
||||
[Fact]
|
||||
public Task Zip_Deflate_ArchiveFileRead_Multithreaded() =>
|
||||
ArchiveFileRead_Multithreaded("Zip.deflate.zip");
|
||||
|
||||
[Fact]
|
||||
public void Zip_Deflate_ArchiveExtractToDirectory() =>
|
||||
ArchiveExtractToDirectory("Zip.deflate.zip");
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.IO;
|
||||
@@ -399,41 +397,4 @@ public class ZipReaderTests : ReaderTests
|
||||
Assert.Equal("second.txt", reader.Entry.Key);
|
||||
Assert.Equal(197, reader.Entry.Size);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ZipReader_Returns_Same_Entries_As_ZipArchive()
|
||||
{
|
||||
// Verifies that ZipReader and ZipArchive return the same entries
|
||||
// for standard single-volume ZIP files. ZipReader processes LocalEntry
|
||||
// headers sequentially, while ZipArchive uses DirectoryEntry headers
|
||||
// from the central directory and seeks to LocalEntry headers for data.
|
||||
var testFiles = new[] { "Zip.none.zip", "Zip.deflate.zip", "Zip.none.issue86.zip" };
|
||||
|
||||
foreach (var testFile in testFiles)
|
||||
{
|
||||
var path = Path.Combine(TEST_ARCHIVES_PATH, testFile);
|
||||
|
||||
var readerKeys = new List<string>();
|
||||
using (var stream = File.OpenRead(path))
|
||||
using (var reader = ZipReader.Open(stream))
|
||||
{
|
||||
while (reader.MoveToNextEntry())
|
||||
{
|
||||
readerKeys.Add(reader.Entry.Key!);
|
||||
}
|
||||
}
|
||||
|
||||
var archiveKeys = new List<string>();
|
||||
using (var archive = Archives.Zip.ZipArchive.Open(path))
|
||||
{
|
||||
foreach (var entry in archive.Entries)
|
||||
{
|
||||
archiveKeys.Add(entry.Key!);
|
||||
}
|
||||
}
|
||||
|
||||
Assert.Equal(archiveKeys.Count, readerKeys.Count);
|
||||
Assert.Equal(archiveKeys.OrderBy(k => k), readerKeys.OrderBy(k => k));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Writers;
|
||||
using Xunit;
|
||||
|
||||
namespace SharpCompress.Test.Zip;
|
||||
@@ -11,42 +9,6 @@ public class ZipWriterTests : WriterTests
|
||||
public ZipWriterTests()
|
||||
: base(ArchiveType.Zip) { }
|
||||
|
||||
[Fact]
|
||||
public void Zip_BZip2_Write_EmptyFile()
|
||||
{
|
||||
// Test that writing an empty file with BZip2 compression doesn't throw DivideByZeroException
|
||||
using var memoryStream = new MemoryStream();
|
||||
var options = new WriterOptions(CompressionType.BZip2)
|
||||
{
|
||||
ArchiveEncoding = new ArchiveEncoding { Default = new UTF8Encoding(false) },
|
||||
};
|
||||
|
||||
using (var writer = WriterFactory.Open(memoryStream, ArchiveType.Zip, options))
|
||||
{
|
||||
writer.Write("test-folder/zero-byte-file.txt", Stream.Null);
|
||||
}
|
||||
|
||||
Assert.True(memoryStream.Length > 0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Zip_BZip2_Write_EmptyFolder()
|
||||
{
|
||||
// Test that writing an empty folder entry with BZip2 compression doesn't throw DivideByZeroException
|
||||
using var memoryStream = new MemoryStream();
|
||||
var options = new WriterOptions(CompressionType.BZip2)
|
||||
{
|
||||
ArchiveEncoding = new ArchiveEncoding { Default = new UTF8Encoding(false) },
|
||||
};
|
||||
|
||||
using (var writer = WriterFactory.Open(memoryStream, ArchiveType.Zip, options))
|
||||
{
|
||||
writer.Write("test-empty-folder/", Stream.Null);
|
||||
}
|
||||
|
||||
Assert.True(memoryStream.Length > 0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Zip_Deflate_Write() =>
|
||||
Write(
|
||||
|
||||
@@ -13,11 +13,11 @@
|
||||
},
|
||||
"Microsoft.NET.Test.Sdk": {
|
||||
"type": "Direct",
|
||||
"requested": "[18.0.1, )",
|
||||
"resolved": "18.0.1",
|
||||
"contentHash": "WNpu6vI2rA0pXY4r7NKxCN16XRWl5uHu6qjuyVLoDo6oYEggIQefrMjkRuibQHm/NslIUNCcKftvoWAN80MSAg==",
|
||||
"requested": "[18.0.0, )",
|
||||
"resolved": "18.0.0",
|
||||
"contentHash": "bvxj2Asb7nT+tqOFFerrhQeEjUYLwx0Poi0Rznu63WbqN+A4uDn1t5NWXfAOOQsF6lpmK6N2v+Vvgso7KWZS7g==",
|
||||
"dependencies": {
|
||||
"Microsoft.CodeCoverage": "18.0.1"
|
||||
"Microsoft.CodeCoverage": "18.0.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.NETFramework.ReferenceAssemblies": {
|
||||
@@ -29,12 +29,6 @@
|
||||
"Microsoft.NETFramework.ReferenceAssemblies.net48": "1.0.3"
|
||||
}
|
||||
},
|
||||
"Mono.Posix.NETStandard": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.0.0, )",
|
||||
"resolved": "1.0.0",
|
||||
"contentHash": "vSN/L1uaVwKsiLa95bYu2SGkF0iY3xMblTfxc8alSziPuVfJpj3geVqHGAA75J7cZkMuKpFVikz82Lo6y6LLdA=="
|
||||
},
|
||||
"xunit": {
|
||||
"type": "Direct",
|
||||
"requested": "[2.9.3, )",
|
||||
@@ -57,8 +51,8 @@
|
||||
},
|
||||
"Microsoft.CodeCoverage": {
|
||||
"type": "Transitive",
|
||||
"resolved": "18.0.1",
|
||||
"contentHash": "O+utSr97NAJowIQT/OVp3Lh9QgW/wALVTP4RG1m2AfFP4IyJmJz0ZBmFJUsRQiAPgq6IRC0t8AAzsiPIsaUDEA=="
|
||||
"resolved": "18.0.0",
|
||||
"contentHash": "DFPhMrsIofgJ1DDU3ModqqRArDm15/bNl4ecmcuBspZkZ4ONYnCC0R8U27WzK7cYv6r8l6Q/fRmvg7cb+I/dJA=="
|
||||
},
|
||||
"Microsoft.NETFramework.ReferenceAssemblies.net48": {
|
||||
"type": "Transitive",
|
||||
@@ -98,17 +92,12 @@
|
||||
},
|
||||
"System.Threading.Tasks.Extensions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.6.3",
|
||||
"contentHash": "7sCiwilJLYbTZELaKnc7RecBBXWXA+xMLQWZKWawBxYjp6DBlSE3v9/UcvKBvr1vv2tTOhipiogM8rRmxlhrVA==",
|
||||
"resolved": "4.5.4",
|
||||
"contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
|
||||
"dependencies": {
|
||||
"System.Runtime.CompilerServices.Unsafe": "6.1.2"
|
||||
"System.Runtime.CompilerServices.Unsafe": "4.5.3"
|
||||
}
|
||||
},
|
||||
"System.ValueTuple": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.6.1",
|
||||
"contentHash": "+RJT4qaekpZ7DDLhf+LTjq+E48jieKiY9ulJ+BoxKmZblIJfIJT8Ufcaa/clQqnYvWs8jugfGSMu8ylS0caG0w=="
|
||||
},
|
||||
"xunit.abstractions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.0.3",
|
||||
@@ -152,20 +141,20 @@
|
||||
"sharpcompress": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Bcl.AsyncInterfaces": "[10.0.0, )",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "[8.0.0, )",
|
||||
"System.Buffers": "[4.6.1, )",
|
||||
"System.Memory": "[4.6.3, )",
|
||||
"System.Text.Encoding.CodePages": "[10.0.0, )",
|
||||
"System.Text.Encoding.CodePages": "[8.0.0, )",
|
||||
"ZstdSharp.Port": "[0.8.6, )"
|
||||
}
|
||||
},
|
||||
"Microsoft.Bcl.AsyncInterfaces": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[10.0.0, )",
|
||||
"resolved": "10.0.0",
|
||||
"contentHash": "vFuwSLj9QJBbNR0NeNO4YVASUbokxs+i/xbuu8B+Fs4FAZg5QaFa6eGrMaRqTzzNI5tAb97T7BhSxtLckFyiRA==",
|
||||
"requested": "[8.0.0, )",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==",
|
||||
"dependencies": {
|
||||
"System.Threading.Tasks.Extensions": "4.6.3"
|
||||
"System.Threading.Tasks.Extensions": "4.5.4"
|
||||
}
|
||||
},
|
||||
"System.Buffers": {
|
||||
@@ -187,13 +176,12 @@
|
||||
},
|
||||
"System.Text.Encoding.CodePages": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[10.0.0, )",
|
||||
"resolved": "10.0.0",
|
||||
"contentHash": "QLP54mIATaBpjGlsZIxga38VPk1G9js0Kw651B+bvrXi2kSgGZYrxJSpM3whhTZCBK4HEBHX3fzfDQMw7CXHGQ==",
|
||||
"requested": "[8.0.0, )",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "OZIsVplFGaVY90G2SbpgU7EnCoOO5pw1t4ic21dBF3/1omrJFpAGoNAVpPyMVOC90/hvgkGG3VFqR13YgZMQfg==",
|
||||
"dependencies": {
|
||||
"System.Memory": "4.6.3",
|
||||
"System.Runtime.CompilerServices.Unsafe": "6.1.2",
|
||||
"System.ValueTuple": "4.6.1"
|
||||
"System.Memory": "4.5.5",
|
||||
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
|
||||
}
|
||||
},
|
||||
"ZstdSharp.Port": {
|
||||
@@ -208,7 +196,7 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"net10.0": {
|
||||
"net8.0": {
|
||||
"AwesomeAssertions": {
|
||||
"type": "Direct",
|
||||
"requested": "[9.3.0, )",
|
||||
@@ -217,12 +205,12 @@
|
||||
},
|
||||
"Microsoft.NET.Test.Sdk": {
|
||||
"type": "Direct",
|
||||
"requested": "[18.0.1, )",
|
||||
"resolved": "18.0.1",
|
||||
"contentHash": "WNpu6vI2rA0pXY4r7NKxCN16XRWl5uHu6qjuyVLoDo6oYEggIQefrMjkRuibQHm/NslIUNCcKftvoWAN80MSAg==",
|
||||
"requested": "[18.0.0, )",
|
||||
"resolved": "18.0.0",
|
||||
"contentHash": "bvxj2Asb7nT+tqOFFerrhQeEjUYLwx0Poi0Rznu63WbqN+A4uDn1t5NWXfAOOQsF6lpmK6N2v+Vvgso7KWZS7g==",
|
||||
"dependencies": {
|
||||
"Microsoft.CodeCoverage": "18.0.1",
|
||||
"Microsoft.TestPlatform.TestHost": "18.0.1"
|
||||
"Microsoft.CodeCoverage": "18.0.0",
|
||||
"Microsoft.TestPlatform.TestHost": "18.0.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.NETFramework.ReferenceAssemblies": {
|
||||
@@ -259,8 +247,8 @@
|
||||
},
|
||||
"Microsoft.CodeCoverage": {
|
||||
"type": "Transitive",
|
||||
"resolved": "18.0.1",
|
||||
"contentHash": "O+utSr97NAJowIQT/OVp3Lh9QgW/wALVTP4RG1m2AfFP4IyJmJz0ZBmFJUsRQiAPgq6IRC0t8AAzsiPIsaUDEA=="
|
||||
"resolved": "18.0.0",
|
||||
"contentHash": "DFPhMrsIofgJ1DDU3ModqqRArDm15/bNl4ecmcuBspZkZ4ONYnCC0R8U27WzK7cYv6r8l6Q/fRmvg7cb+I/dJA=="
|
||||
},
|
||||
"Microsoft.NETFramework.ReferenceAssemblies.net461": {
|
||||
"type": "Transitive",
|
||||
@@ -269,18 +257,18 @@
|
||||
},
|
||||
"Microsoft.TestPlatform.ObjectModel": {
|
||||
"type": "Transitive",
|
||||
"resolved": "18.0.1",
|
||||
"contentHash": "qT/mwMcLF9BieRkzOBPL2qCopl8hQu6A1P7JWAoj/FMu5i9vds/7cjbJ/LLtaiwWevWLAeD5v5wjQJ/l6jvhWQ==",
|
||||
"resolved": "18.0.0",
|
||||
"contentHash": "Al/a99ymb8UdEEh6DKNiaoFn5i8fvX5PdM9LfU9Z/Q8NJrlyHHzF+LRHLbR+t89gRsJ2fFMpwYxgEn3eH1BQwA==",
|
||||
"dependencies": {
|
||||
"System.Reflection.Metadata": "8.0.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.TestPlatform.TestHost": {
|
||||
"type": "Transitive",
|
||||
"resolved": "18.0.1",
|
||||
"contentHash": "uDJKAEjFTaa2wHdWlfo6ektyoh+WD4/Eesrwb4FpBFKsLGehhACVnwwTI4qD3FrIlIEPlxdXg3SyrYRIcO+RRQ==",
|
||||
"resolved": "18.0.0",
|
||||
"contentHash": "aAxE8Thr9ZHGrljOYaDeLJqitQi75iE4xeEFn6CEGFirlHSn1KwpKPniuEn6zCLZ90Z3XqNlrC3ZJTuvBov45w==",
|
||||
"dependencies": {
|
||||
"Microsoft.TestPlatform.ObjectModel": "18.0.1",
|
||||
"Microsoft.TestPlatform.ObjectModel": "18.0.0",
|
||||
"Newtonsoft.Json": "13.0.3"
|
||||
}
|
||||
},
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user