mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-02-04 05:25:00 +00:00
Compare commits
59 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3fa7e854d3 | ||
|
|
0f5c080be6 | ||
|
|
871009ef8d | ||
|
|
89a565c6c4 | ||
|
|
6ed60e4d5f | ||
|
|
4dab1df3ae | ||
|
|
7b7aa2cdf0 | ||
|
|
d1e1a65f32 | ||
|
|
9d332b4ac5 | ||
|
|
fc2462e281 | ||
|
|
fb2cddabf0 | ||
|
|
33481a9465 | ||
|
|
45de87cb97 | ||
|
|
553c533ada | ||
|
|
634e562b93 | ||
|
|
c789ead590 | ||
|
|
d23560a441 | ||
|
|
9f306966db | ||
|
|
8eea2cef97 | ||
|
|
3abbb89c2e | ||
|
|
76de7d54c7 | ||
|
|
35aee6e066 | ||
|
|
8a0152fe7c | ||
|
|
7769435fc8 | ||
|
|
ae311ea66e | ||
|
|
d15816f162 | ||
|
|
5cbb31c559 | ||
|
|
b7f5b36f2b | ||
|
|
e9dd413de6 | ||
|
|
c5de3d8cc1 | ||
|
|
624c385e27 | ||
|
|
893890c985 | ||
|
|
e12efc8b52 | ||
|
|
015dfa41b1 | ||
|
|
5e72ce5fbe | ||
|
|
236e653176 | ||
|
|
3946c3ba03 | ||
|
|
5bdd39e6eb | ||
|
|
46267c0531 | ||
|
|
7b96eb7704 | ||
|
|
60d5955101 | ||
|
|
587675e488 | ||
|
|
c0ae319c63 | ||
|
|
d810f8d8db | ||
|
|
a88390a546 | ||
|
|
8575e5615d | ||
|
|
5fe9516c09 | ||
|
|
938775789d | ||
|
|
f37bebe51f | ||
|
|
21f14cd3f2 | ||
|
|
4e7baeb2c9 | ||
|
|
d78a682dd8 | ||
|
|
44021b7abc | ||
|
|
7f9e543213 | ||
|
|
c489e5a59b | ||
|
|
8de30db7f9 | ||
|
|
415f0c6774 | ||
|
|
aa9cf195a5 | ||
|
|
6412fc8135 |
@@ -3,7 +3,7 @@
|
||||
"isRoot": true,
|
||||
"tools": {
|
||||
"csharpier": {
|
||||
"version": "1.0.2",
|
||||
"version": "1.1.2",
|
||||
"commands": [
|
||||
"csharpier"
|
||||
],
|
||||
|
||||
64
AGENTS.md
Normal file
64
AGENTS.md
Normal file
@@ -0,0 +1,64 @@
|
||||
---
|
||||
description: 'Guidelines for building C# applications'
|
||||
applyTo: '**/*.cs'
|
||||
---
|
||||
|
||||
# C# Development
|
||||
|
||||
## C# Instructions
|
||||
- Always use the latest version C#, currently C# 13 features.
|
||||
- Write clear and concise comments for each function.
|
||||
|
||||
## General Instructions
|
||||
- Make only high confidence suggestions when reviewing code changes.
|
||||
- Write code with good maintainability practices, including comments on why certain design decisions were made.
|
||||
- Handle edge cases and write clear exception handling.
|
||||
- For libraries or external dependencies, mention their usage and purpose in comments.
|
||||
|
||||
## Naming Conventions
|
||||
|
||||
- Follow PascalCase for component names, method names, and public members.
|
||||
- Use camelCase for private fields and local variables.
|
||||
- Prefix interface names with "I" (e.g., IUserService).
|
||||
|
||||
## Code Formatting
|
||||
|
||||
- Use CSharpier for all code formatting to ensure consistent style across the project.
|
||||
- Install CSharpier globally: `dotnet tool install -g csharpier`
|
||||
- Format files with: `dotnet csharpier format .`
|
||||
- Configure your IDE to format on save using CSharpier.
|
||||
- CSharpier configuration can be customized via `.csharpierrc` file in the project root.
|
||||
- Trust CSharpier's opinionated formatting decisions to maintain consistency.
|
||||
|
||||
## Project Setup and Structure
|
||||
|
||||
- Guide users through creating a new .NET project with the appropriate templates.
|
||||
- Explain the purpose of each generated file and folder to build understanding of the project structure.
|
||||
- Demonstrate how to organize code using feature folders or domain-driven design principles.
|
||||
- Show proper separation of concerns with models, services, and data access layers.
|
||||
- Explain the Program.cs and configuration system in ASP.NET Core 9 including environment-specific settings.
|
||||
|
||||
## Nullable Reference Types
|
||||
|
||||
- Declare variables non-nullable, and check for `null` at entry points.
|
||||
- Always use `is null` or `is not null` instead of `== null` or `!= null`.
|
||||
- Trust the C# null annotations and don't add null checks when the type system says a value cannot be null.
|
||||
|
||||
## Testing
|
||||
|
||||
- Always include test cases for critical paths of the application.
|
||||
- Guide users through creating unit tests.
|
||||
- Do not emit "Act", "Arrange" or "Assert" comments.
|
||||
- Copy existing style in nearby files for test method names and capitalization.
|
||||
- Explain integration testing approaches for API endpoints.
|
||||
- Demonstrate how to mock dependencies for effective testing.
|
||||
- Show how to test authentication and authorization logic.
|
||||
- Explain test-driven development principles as applied to API development.
|
||||
|
||||
## Performance Optimization
|
||||
|
||||
- Guide users on implementing caching strategies (in-memory, distributed, response caching).
|
||||
- Explain asynchronous programming patterns and why they matter for API performance.
|
||||
- Demonstrate pagination, filtering, and sorting for large data sets.
|
||||
- Show how to implement compression and other performance optimizations.
|
||||
- Explain how to measure and benchmark API performance.
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project>
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="Bullseye" Version="6.0.0" />
|
||||
<PackageVersion Include="AwesomeAssertions" Version="9.0.0" />
|
||||
<PackageVersion Include="AwesomeAssertions" Version="9.2.0" />
|
||||
<PackageVersion Include="Glob" Version="1.1.9" />
|
||||
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
|
||||
@@ -11,9 +11,10 @@
|
||||
<PackageVersion Include="System.Memory" Version="4.6.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.0" />
|
||||
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" />
|
||||
<PackageVersion Include="xunit.SkippableFact" Version="1.5.23" />
|
||||
<PackageVersion Include="ZstdSharp.Port" Version="0.8.5" />
|
||||
<GlobalPackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
|
||||
<PackageVersion Include="ZstdSharp.Port" Version="0.8.6" />
|
||||
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
| Tar | None | Both | TarArchive | TarReader | TarWriter (3) |
|
||||
| Tar.GZip | DEFLATE | Both | TarArchive | TarReader | TarWriter (3) |
|
||||
| Tar.BZip2 | BZip2 | Both | TarArchive | TarReader | TarWriter (3) |
|
||||
| Tar.Zstandard | ZStandard | Decompress | TarArchive | TarReader | N/A |
|
||||
| Tar.LZip | LZMA | Both | TarArchive | TarReader | TarWriter (3) |
|
||||
| Tar.XZ | LZMA2 | Decompress | TarArchive | TarReader | TarWriter (3) |
|
||||
| GZip (single file) | DEFLATE | Both | GZipArchive | GZipReader | GZipWriter |
|
||||
@@ -41,6 +42,7 @@ For those who want to directly compress/decompress bits. The single file formats
|
||||
| ADCStream | Decompress |
|
||||
| LZipStream | Both |
|
||||
| XZStream | Decompress |
|
||||
| ZStandardStream | Decompress |
|
||||
|
||||
## Archive Formats vs Compression
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# SharpCompress
|
||||
|
||||
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 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 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).
|
||||
|
||||
@@ -20,10 +20,12 @@ In general, I recommend GZip (Deflate)/BZip2 (BZip)/LZip (LZMA) as the simplicit
|
||||
|
||||
Zip is okay, but it's a very hap-hazard format and the variation in headers and implementations makes it hard to get correct. Uses Deflate by default but supports a lot of compression methods.
|
||||
|
||||
RAR is not recommended as it's a propriatory format and the compression is closed source. Use Tar/LZip for LZMA
|
||||
RAR is not recommended as it's a proprietary format and the compression is closed source. Use Tar/LZip for LZMA
|
||||
|
||||
7Zip and XZ both are overly complicated. 7Zip does not support streamable formats. XZ has known holes explained here: (http://www.nongnu.org/lzip/xz_inadequate.html) Use Tar/LZip for LZMA compression instead.
|
||||
|
||||
ZStandard is an efficient format that works well for streaming with a flexible compression level to tweak the speed/performance trade off you are looking for. We currently only implement decompression for ZStandard but as we leverage the [ZstdSharp](https://github.com/oleg-st/ZstdSharp) library one could likely add compression support without much trouble (PRs are welcome!).
|
||||
|
||||
## A Simple Request
|
||||
|
||||
Hi everyone. I hope you're using SharpCompress and finding it useful. Please give me feedback on what you'd like to see changed especially as far as usability goes. New feature suggestions are always welcome as well. I would also like to know what projects SharpCompress is being used in. I like seeing how it is used to give me ideas for future versions. Thanks!
|
||||
@@ -40,6 +42,7 @@ I'm always looking for help or ideas. Please submit code or email with ideas. Un
|
||||
* 7Zip writing
|
||||
* Zip64 (Need writing and extend Reading)
|
||||
* Multi-volume Zip support.
|
||||
* ZStandard writing
|
||||
|
||||
## Version Log
|
||||
|
||||
|
||||
@@ -15,17 +15,17 @@
|
||||
|
||||
<s:String x:Key="/Default/CodeStyle/CodeCleanup/SilentCleanupProfile/@EntryValue">Basic Clean</s:String>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/APPLY_ON_COMPLETION/@EntryValue">True</s:Boolean>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/ARGUMENTS_NAMED/@EntryValue">Named</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/ARGUMENTS_NAMED/@EntryValue">Positional</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_FOR/@EntryValue">Required</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_FOREACH/@EntryValue">Required</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_IFELSE/@EntryValue">Required</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_WHILE/@EntryValue">Required</s:String>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_FIRST_ARG_BY_PAREN/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_FIRST_ARG_BY_PAREN/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_LINQ_QUERY/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_MULTILINE_ARGUMENT/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_MULTILINE_ARRAY_AND_OBJECT_INITIALIZER/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_MULTILINE_ARRAY_AND_OBJECT_INITIALIZER/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_MULTILINE_CALLS_CHAIN/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_MULTILINE_EXPRESSION/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_MULTILINE_EXPRESSION/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_MULTILINE_EXTENDS_LIST/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_MULTILINE_FOR_STMT/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALIGN_MULTILINE_PARAMETER/@EntryValue">True</s:Boolean>
|
||||
@@ -42,7 +42,7 @@
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/FORCE_IFELSE_BRACES_STYLE/@EntryValue">ALWAYS_ADD</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/FORCE_USING_BRACES_STYLE/@EntryValue">ALWAYS_ADD</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/FORCE_WHILE_BRACES_STYLE/@EntryValue">ALWAYS_ADD</s:String>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/INDENT_ANONYMOUS_METHOD_BLOCK/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/INDENT_ANONYMOUS_METHOD_BLOCK/@EntryValue">False</s:Boolean>
|
||||
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_BLANK_LINES_IN_CODE/@EntryValue">1</s:Int64>
|
||||
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_BLANK_LINES_IN_DECLARATIONS/@EntryValue">1</s:Int64>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_ACCESSOR_ATTRIBUTE_ON_SAME_LINE_EX/@EntryValue">NEVER</s:String>
|
||||
@@ -50,12 +50,12 @@
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_CONSTRUCTOR_INITIALIZER_ON_SAME_LINE/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_FIELD_ATTRIBUTE_ON_SAME_LINE/@EntryValue">False</s:Boolean>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_FIELD_ATTRIBUTE_ON_SAME_LINE_EX/@EntryValue">NEVER</s:String>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_SIMPLE_ACCESSORHOLDER_ON_SINGLE_LINE/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_SIMPLE_ACCESSORHOLDER_ON_SINGLE_LINE/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_SIMPLE_ACCESSOR_ATTRIBUTE_ON_SAME_LINE/@EntryValue">False</s:Boolean>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_SIMPLE_EMBEDDED_STATEMENT_ON_SAME_LINE/@EntryValue">NEVER</s:String>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_SIMPLE_INITIALIZER_ON_SINGLE_LINE/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_SIMPLE_INITIALIZER_ON_SINGLE_LINE/@EntryValue">False</s:Boolean>
|
||||
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_WHILE_ON_NEW_LINE/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_WHILE_ON_NEW_LINE/@EntryValue">False</s:Boolean>
|
||||
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SIMPLE_EMBEDDED_STATEMENT_STYLE/@EntryValue">LINE_BREAK</s:String>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_AFTER_TYPECAST_PARENTHESES/@EntryValue">False</s:Boolean>
|
||||
@@ -67,13 +67,13 @@
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_BEFORE_TYPEOF_PARENTHESES/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/STICK_COMMENT/@EntryValue">False</s:Boolean>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_ARGUMENTS_STYLE/@EntryValue">CHOP_IF_LONG</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_ARRAY_INITIALIZER_STYLE/@EntryValue">CHOP_IF_LONG</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_ARRAY_INITIALIZER_STYLE/@EntryValue">CHOP_ALWAYS</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_EXTENDS_LIST_STYLE/@EntryValue">CHOP_IF_LONG</s:String>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_LINES/@EntryValue">False</s:Boolean>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_PARAMETERS_STYLE/@EntryValue">CHOP_IF_LONG</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForBuiltInTypes/@EntryValue">UseVarWhenEvident</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForOtherTypes/@EntryValue">UseVarWhenEvident</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForSimpleTypes/@EntryValue">UseVarWhenEvident</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForBuiltInTypes/@EntryValue">UseVar</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForOtherTypes/@EntryValue">UseVar</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForSimpleTypes/@EntryValue">UseVar</s:String>
|
||||
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateInstanceFields/@EntryIndexedValue"><Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticFields/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /></s:String>
|
||||
@@ -122,6 +122,7 @@
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/UserRules/=NAMESPACE_005FALIAS/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/UserRules/=XAML_005FFIELD/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/UserRules/=XAML_005FRESOURCE/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CustomTools/CustomToolsData/@EntryValue"></s:String>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpAttributeForSingleLineMethodUpgrade/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
|
||||
@@ -14,31 +14,11 @@
|
||||
"resolved": "1.1.9",
|
||||
"contentHash": "AfK5+ECWYTP7G3AAdnU8IfVj+QpGjrh9GC2mpdcJzCvtQ4pnerAGwHsxJ9D4/RnhDUz2DSzd951O/lQjQby2Sw=="
|
||||
},
|
||||
"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"
|
||||
}
|
||||
},
|
||||
"SimpleExec": {
|
||||
"type": "Direct",
|
||||
"requested": "[12.0.0, )",
|
||||
"resolved": "12.0.0",
|
||||
"contentHash": "ptxlWtxC8vM6Y6e3h9ZTxBBkOWnWrm/Sa1HT+2i1xcXY3Hx2hmKDZP5RShPf8Xr9D+ivlrXNy57ktzyH8kyt+Q=="
|
||||
},
|
||||
"Microsoft.Build.Tasks.Git": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
|
||||
},
|
||||
"Microsoft.SourceLink.Common": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Factories;
|
||||
using SharpCompress.IO;
|
||||
using SharpCompress.Readers;
|
||||
|
||||
namespace SharpCompress.Archives;
|
||||
@@ -19,7 +20,7 @@ public static class ArchiveFactory
|
||||
public static IArchive Open(Stream stream, ReaderOptions? readerOptions = null)
|
||||
{
|
||||
readerOptions ??= new ReaderOptions();
|
||||
|
||||
stream = new SharpCompressStream(stream, bufferSize: readerOptions.BufferSize);
|
||||
return FindFactory<IArchiveFactory>(stream).Open(stream, readerOptions);
|
||||
}
|
||||
|
||||
@@ -165,14 +166,22 @@ public static class ArchiveFactory
|
||||
);
|
||||
}
|
||||
|
||||
public static bool IsArchive(string filePath, out ArchiveType? type)
|
||||
public static bool IsArchive(
|
||||
string filePath,
|
||||
out ArchiveType? type,
|
||||
int bufferSize = ReaderOptions.DefaultBufferSize
|
||||
)
|
||||
{
|
||||
filePath.CheckNotNullOrEmpty(nameof(filePath));
|
||||
using Stream s = File.OpenRead(filePath);
|
||||
return IsArchive(s, out type);
|
||||
return IsArchive(s, out type, bufferSize);
|
||||
}
|
||||
|
||||
public static bool IsArchive(Stream stream, out ArchiveType? type)
|
||||
public static bool IsArchive(
|
||||
Stream stream,
|
||||
out ArchiveType? type,
|
||||
int bufferSize = ReaderOptions.DefaultBufferSize
|
||||
)
|
||||
{
|
||||
type = null;
|
||||
stream.CheckNotNull(nameof(stream));
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SharpCompress.Common;
|
||||
@@ -14,8 +14,11 @@ class AutoArchiveFactory : IArchiveFactory
|
||||
|
||||
public IEnumerable<string> GetSupportedExtensions() => throw new NotSupportedException();
|
||||
|
||||
public bool IsArchive(Stream stream, string? password = null) =>
|
||||
throw new NotSupportedException();
|
||||
public bool IsArchive(
|
||||
Stream stream,
|
||||
string? password = null,
|
||||
int bufferSize = ReaderOptions.DefaultBufferSize
|
||||
) => throw new NotSupportedException();
|
||||
|
||||
public FileInfo? GetFilePart(int index, FileInfo part1) => throw new NotSupportedException();
|
||||
|
||||
|
||||
@@ -89,6 +89,12 @@ public class GZipArchive : AbstractWritableArchive<GZipArchiveEntry, GZipVolume>
|
||||
public static GZipArchive Open(Stream stream, ReaderOptions? readerOptions = null)
|
||||
{
|
||||
stream.CheckNotNull(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())
|
||||
);
|
||||
|
||||
@@ -58,7 +58,7 @@ internal sealed class GZipWritableArchiveEntry : GZipArchiveEntry, IWritableArch
|
||||
{
|
||||
//ensure new stream is at the start, this could be reset
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
return NonDisposingStream.Create(stream);
|
||||
return SharpCompressStream.Create(stream, leaveOpen: true);
|
||||
}
|
||||
|
||||
internal override void Close()
|
||||
|
||||
@@ -131,6 +131,12 @@ public class RarArchive : AbstractArchive<RarArchiveEntry, RarVolume>
|
||||
public static RarArchive Open(Stream stream, ReaderOptions? options = null)
|
||||
{
|
||||
stream.CheckNotNull(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()));
|
||||
}
|
||||
|
||||
|
||||
@@ -92,6 +92,12 @@ public class SevenZipArchive : AbstractArchive<SevenZipArchiveEntry, SevenZipVol
|
||||
public static SevenZipArchive Open(Stream stream, ReaderOptions? readerOptions = null)
|
||||
{
|
||||
stream.CheckNotNull("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())
|
||||
);
|
||||
@@ -194,7 +200,10 @@ public class SevenZipArchive : AbstractArchive<SevenZipArchiveEntry, SevenZipVol
|
||||
new SevenZipReader(ReaderOptions, this);
|
||||
|
||||
public override bool IsSolid =>
|
||||
Entries.Where(x => !x.IsDirectory).GroupBy(x => x.FilePart.Folder).Count() > 1;
|
||||
Entries
|
||||
.Where(x => !x.IsDirectory)
|
||||
.GroupBy(x => x.FilePart.Folder)
|
||||
.Any(folder => folder.Count() > 1);
|
||||
|
||||
public override long TotalSize =>
|
||||
_database?._packSizes.Aggregate(0L, (total, packSize) => total + packSize) ?? 0;
|
||||
|
||||
@@ -90,6 +90,12 @@ public class TarArchive : AbstractWritableArchive<TarArchiveEntry, TarVolume>
|
||||
public static TarArchive Open(Stream stream, ReaderOptions? readerOptions = null)
|
||||
{
|
||||
stream.CheckNotNull(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())
|
||||
);
|
||||
|
||||
@@ -58,7 +58,7 @@ internal sealed class TarWritableArchiveEntry : TarArchiveEntry, IWritableArchiv
|
||||
{
|
||||
//ensure new stream is at the start, this could be reset
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
return NonDisposingStream.Create(stream);
|
||||
return SharpCompressStream.Create(stream, leaveOpen: true);
|
||||
}
|
||||
|
||||
internal override void Close()
|
||||
|
||||
@@ -111,29 +111,51 @@ public class ZipArchive : AbstractWritableArchive<ZipArchiveEntry, ZipVolume>
|
||||
public static ZipArchive Open(Stream stream, ReaderOptions? readerOptions = null)
|
||||
{
|
||||
stream.CheckNotNull(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 bool IsZipFile(string filePath, string? password = null) =>
|
||||
IsZipFile(new FileInfo(filePath), password);
|
||||
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)
|
||||
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);
|
||||
return IsZipFile(stream, password, bufferSize);
|
||||
}
|
||||
|
||||
public static bool IsZipFile(Stream stream, string? password = null)
|
||||
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);
|
||||
@@ -153,11 +175,20 @@ public class ZipArchive : AbstractWritableArchive<ZipArchiveEntry, ZipVolume>
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsZipMulti(Stream stream, string? password = null)
|
||||
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);
|
||||
@@ -196,7 +227,7 @@ public class ZipArchive : AbstractWritableArchive<ZipArchiveEntry, ZipVolume>
|
||||
if (streams.Count() > 1) //test part 2 - true = multipart not split
|
||||
{
|
||||
streams[1].Position += 4; //skip the POST_DATA_DESCRIPTOR to prevent an exception
|
||||
var isZip = IsZipFile(streams[1], ReaderOptions.Password);
|
||||
var isZip = IsZipFile(streams[1], ReaderOptions.Password, ReaderOptions.BufferSize);
|
||||
streams[1].Position -= 4;
|
||||
if (isZip)
|
||||
{
|
||||
@@ -299,7 +330,7 @@ public class ZipArchive : AbstractWritableArchive<ZipArchiveEntry, ZipVolume>
|
||||
protected override IReader CreateReaderForSolidExtraction()
|
||||
{
|
||||
var stream = Volumes.Single().Stream;
|
||||
stream.Position = 0;
|
||||
((IStreamStack)stream).StackSeek(0);
|
||||
return ZipReader.Open(stream, ReaderOptions, Entries);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,4 @@ public class ZipArchiveEntry : ZipEntry, IArchiveEntry
|
||||
public bool IsComplete => true;
|
||||
|
||||
#endregion
|
||||
|
||||
public string? Comment => ((SeekableZipFilePart)Parts.Single()).Comment;
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ internal class ZipWritableArchiveEntry : ZipArchiveEntry, IWritableArchiveEntry
|
||||
{
|
||||
//ensure new stream is at the start, this could be reset
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
return NonDisposingStream.Create(stream);
|
||||
return SharpCompressStream.Create(stream, leaveOpen: true);
|
||||
}
|
||||
|
||||
internal override void Close()
|
||||
|
||||
@@ -28,4 +28,5 @@ public enum CompressionType
|
||||
Squashed,
|
||||
Crushed,
|
||||
Distilled,
|
||||
ZStandard,
|
||||
}
|
||||
|
||||
@@ -1,11 +1,33 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using SharpCompress.IO;
|
||||
using SharpCompress.Readers;
|
||||
|
||||
namespace SharpCompress.Common;
|
||||
|
||||
public class EntryStream : Stream
|
||||
public class EntryStream : Stream, IStreamStack
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
long IStreamStack.InstanceId { get; set; }
|
||||
#endif
|
||||
int IStreamStack.DefaultBufferSize { get; set; }
|
||||
|
||||
Stream IStreamStack.BaseStream() => _stream;
|
||||
|
||||
int IStreamStack.BufferSize
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
int IStreamStack.BufferPosition
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
|
||||
void IStreamStack.SetPosition(long position) { }
|
||||
|
||||
private readonly IReader _reader;
|
||||
private readonly Stream _stream;
|
||||
private bool _completed;
|
||||
@@ -15,6 +37,9 @@ public class EntryStream : Stream
|
||||
{
|
||||
_reader = reader;
|
||||
_stream = stream;
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugConstruct(typeof(EntryStream));
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -32,11 +57,28 @@ public class EntryStream : Stream
|
||||
{
|
||||
SkipEntry();
|
||||
}
|
||||
|
||||
//Need a safe standard approach to this - it's okay for compression to overreads. Handling needs to be standardised
|
||||
if (_stream is IStreamStack ss)
|
||||
{
|
||||
if (ss.BaseStream() is SharpCompress.Compressors.Deflate.DeflateStream deflateStream)
|
||||
{
|
||||
deflateStream.Flush(); //Deflate over reads. Knock it back
|
||||
}
|
||||
else if (ss.BaseStream() is SharpCompress.Compressors.LZMA.LzmaStream lzmaStream)
|
||||
{
|
||||
lzmaStream.Flush(); //Lzma over reads. Knock it back
|
||||
}
|
||||
}
|
||||
|
||||
if (_isDisposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_isDisposed = true;
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugDispose(typeof(EntryStream));
|
||||
#endif
|
||||
base.Dispose(disposing);
|
||||
_stream.Dispose();
|
||||
}
|
||||
@@ -53,7 +95,7 @@ public class EntryStream : Stream
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get => throw new NotSupportedException();
|
||||
get => _stream.Position; //throw new NotSupportedException();
|
||||
set => throw new NotSupportedException();
|
||||
}
|
||||
|
||||
|
||||
@@ -4,13 +4,38 @@ using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Common.Tar;
|
||||
|
||||
internal class TarReadOnlySubStream : NonDisposingStream
|
||||
internal class TarReadOnlySubStream : SharpCompressStream, IStreamStack
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
long IStreamStack.InstanceId { get; set; }
|
||||
#endif
|
||||
|
||||
Stream IStreamStack.BaseStream() => base.Stream;
|
||||
|
||||
int IStreamStack.BufferSize
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
int IStreamStack.BufferPosition
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
|
||||
void IStreamStack.SetPosition(long position) { }
|
||||
|
||||
private bool _isDisposed;
|
||||
private long _amountRead;
|
||||
|
||||
public TarReadOnlySubStream(Stream stream, long bytesToRead)
|
||||
: base(stream, throwOnDispose: false) => BytesLeftToRead = bytesToRead;
|
||||
: base(stream, leaveOpen: true, throwOnDispose: false)
|
||||
{
|
||||
BytesLeftToRead = bytesToRead;
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugConstruct(typeof(TarReadOnlySubStream));
|
||||
#endif
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
@@ -20,7 +45,9 @@ internal class TarReadOnlySubStream : NonDisposingStream
|
||||
}
|
||||
|
||||
_isDisposed = true;
|
||||
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugDispose(typeof(TarReadOnlySubStream));
|
||||
#endif
|
||||
if (disposing)
|
||||
{
|
||||
// Ensure we read all remaining blocks for this entry.
|
||||
|
||||
@@ -7,16 +7,22 @@ namespace SharpCompress.Common;
|
||||
|
||||
public abstract class Volume : IVolume
|
||||
{
|
||||
private readonly Stream _baseStream;
|
||||
private readonly Stream _actualStream;
|
||||
|
||||
internal Volume(Stream stream, ReaderOptions? readerOptions, int index = 0)
|
||||
{
|
||||
Index = index;
|
||||
ReaderOptions = readerOptions ?? new ReaderOptions();
|
||||
_baseStream = stream;
|
||||
if (ReaderOptions.LeaveStreamOpen)
|
||||
{
|
||||
stream = NonDisposingStream.Create(stream);
|
||||
stream = SharpCompressStream.Create(stream, leaveOpen: true);
|
||||
}
|
||||
|
||||
if (stream is IStreamStack ss)
|
||||
ss.SetBuffer(ReaderOptions.BufferSize, true);
|
||||
|
||||
_actualStream = stream;
|
||||
}
|
||||
|
||||
@@ -32,7 +38,7 @@ public abstract class Volume : IVolume
|
||||
|
||||
public virtual int Index { get; internal set; }
|
||||
|
||||
public string? FileName => (_actualStream as FileStream)?.Name;
|
||||
public string? FileName => (_baseStream as FileStream)?.Name;
|
||||
|
||||
/// <summary>
|
||||
/// RarArchive is part of a multi-part archive.
|
||||
|
||||
@@ -123,11 +123,7 @@ internal class DirectoryEntryHeader : ZipFileEntry
|
||||
|
||||
public long RelativeOffsetOfEntryHeader { get; set; }
|
||||
|
||||
public uint ExternalFileAttributes { get; set; }
|
||||
|
||||
public ushort InternalFileAttributes { get; set; }
|
||||
|
||||
public ushort DiskNumberStart { get; set; }
|
||||
|
||||
public string? Comment { get; private set; }
|
||||
}
|
||||
|
||||
@@ -120,4 +120,8 @@ internal abstract class ZipFileEntry : ZipHeader
|
||||
internal ZipFilePart? Part { get; set; }
|
||||
|
||||
internal bool IsZip64 => CompressedSize >= uint.MaxValue;
|
||||
|
||||
internal uint ExternalFileAttributes { get; set; }
|
||||
|
||||
internal string? Comment { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Common.Zip;
|
||||
|
||||
@@ -9,8 +10,28 @@ internal enum CryptoMode
|
||||
Decrypt,
|
||||
}
|
||||
|
||||
internal class PkwareTraditionalCryptoStream : Stream
|
||||
internal class PkwareTraditionalCryptoStream : Stream, IStreamStack
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
long IStreamStack.InstanceId { get; set; }
|
||||
#endif
|
||||
int IStreamStack.DefaultBufferSize { get; set; }
|
||||
|
||||
Stream IStreamStack.BaseStream() => _stream;
|
||||
|
||||
int IStreamStack.BufferSize
|
||||
{
|
||||
get => 0;
|
||||
set { return; }
|
||||
}
|
||||
int IStreamStack.BufferPosition
|
||||
{
|
||||
get => 0;
|
||||
set { return; }
|
||||
}
|
||||
|
||||
void IStreamStack.SetPosition(long position) { }
|
||||
|
||||
private readonly PkwareTraditionalEncryptionData _encryptor;
|
||||
private readonly CryptoMode _mode;
|
||||
private readonly Stream _stream;
|
||||
@@ -25,6 +46,10 @@ internal class PkwareTraditionalCryptoStream : Stream
|
||||
_encryptor = encryptor;
|
||||
_stream = stream;
|
||||
_mode = mode;
|
||||
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugConstruct(typeof(PkwareTraditionalCryptoStream));
|
||||
#endif
|
||||
}
|
||||
|
||||
public override bool CanRead => (_mode == CryptoMode.Decrypt);
|
||||
@@ -100,6 +125,9 @@ internal class PkwareTraditionalCryptoStream : Stream
|
||||
return;
|
||||
}
|
||||
_isDisposed = true;
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugDispose(typeof(PkwareTraditionalCryptoStream));
|
||||
#endif
|
||||
base.Dispose(disposing);
|
||||
_stream.Dispose();
|
||||
}
|
||||
|
||||
@@ -25,14 +25,8 @@ internal class SeekableZipFilePart : ZipFilePart
|
||||
return base.GetCompressedStream();
|
||||
}
|
||||
|
||||
internal string? Comment => ((DirectoryEntryHeader)Header).Comment;
|
||||
|
||||
private void LoadLocalHeader()
|
||||
{
|
||||
var hasData = Header.HasData;
|
||||
Header = _headerFactory.GetLocalHeader(BaseStream, ((DirectoryEntryHeader)Header));
|
||||
Header.HasData = hasData;
|
||||
}
|
||||
private void LoadLocalHeader() =>
|
||||
Header = _headerFactory.GetLocalHeader(BaseStream, (DirectoryEntryHeader)Header);
|
||||
|
||||
protected override Stream CreateBaseStream()
|
||||
{
|
||||
|
||||
@@ -149,6 +149,12 @@ internal sealed class SeekableZipHeaderFactory : ZipHeaderFactory
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
// populate fields only known from the DirectoryEntryHeader
|
||||
localEntryHeader.HasData = directoryEntryHeader.HasData;
|
||||
localEntryHeader.ExternalFileAttributes = directoryEntryHeader.ExternalFileAttributes;
|
||||
localEntryHeader.Comment = directoryEntryHeader.Comment;
|
||||
|
||||
if (FlagUtility.HasFlag(localEntryHeader.Flags, HeaderFlags.UsePostDataDescriptor))
|
||||
{
|
||||
localEntryHeader.Crc = directoryEntryHeader.Crc;
|
||||
|
||||
@@ -26,12 +26,12 @@ internal sealed class StreamingZipFilePart : ZipFilePart
|
||||
);
|
||||
if (LeaveStreamOpen)
|
||||
{
|
||||
return NonDisposingStream.Create(_decompressionStream);
|
||||
return SharpCompressStream.Create(_decompressionStream, leaveOpen: true);
|
||||
}
|
||||
return _decompressionStream;
|
||||
}
|
||||
|
||||
internal BinaryReader FixStreamedFileLocation(ref RewindableStream rewindableStream)
|
||||
internal BinaryReader FixStreamedFileLocation(ref SharpCompressStream rewindableStream)
|
||||
{
|
||||
if (Header.IsDirectory)
|
||||
{
|
||||
@@ -49,7 +49,7 @@ internal sealed class StreamingZipFilePart : ZipFilePart
|
||||
|
||||
if (_decompressionStream is DeflateStream deflateStream)
|
||||
{
|
||||
rewindableStream.Rewind(deflateStream.InputBuffer);
|
||||
((IStreamStack)rewindableStream).StackSeek(0);
|
||||
}
|
||||
|
||||
Skipped = true;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@@ -19,16 +20,24 @@ internal class StreamingZipHeaderFactory : ZipHeaderFactory
|
||||
|
||||
internal IEnumerable<ZipHeader> ReadStreamHeader(Stream stream)
|
||||
{
|
||||
RewindableStream rewindableStream;
|
||||
if (stream is not SharpCompressStream) //ensure the stream is already a SharpCompressStream. So the buffer/size will already be set
|
||||
{
|
||||
//the original code wrapped this with RewindableStream. Wrap with SharpCompressStream as we can get the buffer size
|
||||
if (stream is SourceStream src)
|
||||
{
|
||||
stream = new SharpCompressStream(
|
||||
stream,
|
||||
src.ReaderOptions.LeaveStreamOpen,
|
||||
bufferSize: src.ReaderOptions.BufferSize
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException("Stream must be a SharpCompressStream", nameof(stream));
|
||||
}
|
||||
}
|
||||
SharpCompressStream rewindableStream = (SharpCompressStream)stream;
|
||||
|
||||
if (stream is RewindableStream rs)
|
||||
{
|
||||
rewindableStream = rs;
|
||||
}
|
||||
else
|
||||
{
|
||||
rewindableStream = new RewindableStream(stream);
|
||||
}
|
||||
while (true)
|
||||
{
|
||||
ZipHeader? header;
|
||||
@@ -43,9 +52,8 @@ internal class StreamingZipHeaderFactory : ZipHeaderFactory
|
||||
{
|
||||
continue;
|
||||
}
|
||||
reader = ((StreamingZipFilePart)_lastEntryHeader.Part).FixStreamedFileLocation(
|
||||
ref rewindableStream
|
||||
);
|
||||
|
||||
// removed requirement for FixStreamedFileLocation()
|
||||
|
||||
var pos = rewindableStream.CanSeek ? (long?)rewindableStream.Position : null;
|
||||
|
||||
@@ -56,26 +64,32 @@ internal class StreamingZipHeaderFactory : ZipHeaderFactory
|
||||
}
|
||||
_lastEntryHeader.Crc = crc;
|
||||
|
||||
// The DataDescriptor can be either 64bit or 32bit
|
||||
var compressed_size = reader.ReadUInt32();
|
||||
var uncompressed_size = reader.ReadUInt32();
|
||||
|
||||
// Check if we have header or 64bit DataDescriptor
|
||||
//attempt 32bit read
|
||||
ulong compSize = reader.ReadUInt32();
|
||||
ulong uncompSize = reader.ReadUInt32();
|
||||
headerBytes = reader.ReadUInt32();
|
||||
var test_header = !(headerBytes == 0x04034b50 || headerBytes == 0x02014b50);
|
||||
|
||||
var test_64bit = ((long)uncompressed_size << 32) | compressed_size;
|
||||
if (test_64bit == _lastEntryHeader.CompressedSize && test_header)
|
||||
//check for zip64 sentinel or unexpected header
|
||||
bool isSentinel = compSize == 0xFFFFFFFF || uncompSize == 0xFFFFFFFF;
|
||||
bool isHeader = headerBytes == 0x04034b50 || headerBytes == 0x02014b50;
|
||||
|
||||
if (!isHeader && !isSentinel)
|
||||
{
|
||||
_lastEntryHeader.UncompressedSize =
|
||||
((long)reader.ReadUInt32() << 32) | headerBytes;
|
||||
//reshuffle into 64-bit values
|
||||
compSize = (uncompSize << 32) | compSize;
|
||||
uncompSize = ((ulong)headerBytes << 32) | reader.ReadUInt32();
|
||||
headerBytes = reader.ReadUInt32();
|
||||
}
|
||||
else
|
||||
else if (isSentinel)
|
||||
{
|
||||
_lastEntryHeader.UncompressedSize = uncompressed_size;
|
||||
//standards-compliant zip64 descriptor
|
||||
compSize = reader.ReadUInt64();
|
||||
uncompSize = reader.ReadUInt64();
|
||||
}
|
||||
|
||||
_lastEntryHeader.CompressedSize = (long)compSize;
|
||||
_lastEntryHeader.UncompressedSize = (long)uncompSize;
|
||||
|
||||
if (pos.HasValue)
|
||||
{
|
||||
_lastEntryHeader.DataStartPosition = pos - _lastEntryHeader.CompressedSize;
|
||||
@@ -86,9 +100,9 @@ internal class StreamingZipHeaderFactory : ZipHeaderFactory
|
||||
if (_lastEntryHeader.Part is null)
|
||||
continue;
|
||||
|
||||
reader = ((StreamingZipFilePart)_lastEntryHeader.Part).FixStreamedFileLocation(
|
||||
ref rewindableStream
|
||||
);
|
||||
//reader = ((StreamingZipFilePart)_lastEntryHeader.Part).FixStreamedFileLocation(
|
||||
// ref rewindableStream
|
||||
//);
|
||||
|
||||
var pos = rewindableStream.CanSeek ? (long?)rewindableStream.Position : null;
|
||||
|
||||
@@ -173,16 +187,11 @@ internal class StreamingZipHeaderFactory : ZipHeaderFactory
|
||||
} // Check if zip is streaming ( Length is 0 and is declared in PostDataDescriptor )
|
||||
else if (local_header.Flags.HasFlag(HeaderFlags.UsePostDataDescriptor))
|
||||
{
|
||||
var isRecording = rewindableStream.IsRecording;
|
||||
if (!isRecording)
|
||||
{
|
||||
rewindableStream.StartRecording();
|
||||
}
|
||||
var nextHeaderBytes = reader.ReadUInt32();
|
||||
((IStreamStack)rewindableStream).Rewind(sizeof(uint));
|
||||
|
||||
// Check if next data is PostDataDescriptor, streamed file with 0 length
|
||||
header.HasData = !IsHeader(nextHeaderBytes);
|
||||
rewindableStream.Rewind(!isRecording);
|
||||
}
|
||||
else // We are not streaming and compressed size is 0, we have no data
|
||||
{
|
||||
|
||||
@@ -2,11 +2,32 @@ using System;
|
||||
using System.Buffers.Binary;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Common.Zip;
|
||||
|
||||
internal class WinzipAesCryptoStream : Stream
|
||||
internal class WinzipAesCryptoStream : Stream, IStreamStack
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
long IStreamStack.InstanceId { get; set; }
|
||||
#endif
|
||||
int IStreamStack.DefaultBufferSize { get; set; }
|
||||
|
||||
Stream IStreamStack.BaseStream() => _stream;
|
||||
|
||||
int IStreamStack.BufferSize
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
int IStreamStack.BufferPosition
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
|
||||
void IStreamStack.SetPosition(long position) { }
|
||||
|
||||
private const int BLOCK_SIZE_IN_BYTES = 16;
|
||||
private readonly SymmetricAlgorithm _cipher;
|
||||
private readonly byte[] _counter = new byte[BLOCK_SIZE_IN_BYTES];
|
||||
@@ -27,6 +48,10 @@ internal class WinzipAesCryptoStream : Stream
|
||||
_stream = stream;
|
||||
_totalBytesLeftToRead = length;
|
||||
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugConstruct(typeof(WinzipAesCryptoStream));
|
||||
#endif
|
||||
|
||||
_cipher = CreateCipher(winzipAesEncryptionData);
|
||||
|
||||
var iv = new byte[BLOCK_SIZE_IN_BYTES];
|
||||
@@ -64,6 +89,9 @@ internal class WinzipAesCryptoStream : Stream
|
||||
return;
|
||||
}
|
||||
_isDisposed = true;
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugDispose(typeof(WinzipAesCryptoStream));
|
||||
#endif
|
||||
if (disposing)
|
||||
{
|
||||
//read out last 10 auth bytes
|
||||
|
||||
@@ -13,7 +13,7 @@ internal enum ZipCompressionMethod
|
||||
Deflate64 = 9,
|
||||
BZip2 = 12,
|
||||
LZMA = 14,
|
||||
ZStd = 93,
|
||||
ZStandard = 93,
|
||||
Xz = 95,
|
||||
PPMd = 98,
|
||||
WinzipAes = 0x63, //http://www.winzip.com/aes_info.htm
|
||||
|
||||
@@ -46,6 +46,7 @@ public class ZipEntry : Entry
|
||||
ZipCompressionMethod.Reduce3 => CompressionType.Reduce3,
|
||||
ZipCompressionMethod.Reduce4 => CompressionType.Reduce4,
|
||||
ZipCompressionMethod.Explode => CompressionType.Explode,
|
||||
ZipCompressionMethod.ZStandard => CompressionType.ZStandard,
|
||||
_ => CompressionType.Unknown,
|
||||
};
|
||||
|
||||
@@ -83,4 +84,8 @@ public class ZipEntry : Entry
|
||||
public override bool IsSplitAfter => false;
|
||||
|
||||
internal override IEnumerable<FilePart> Parts => _filePart.Empty();
|
||||
|
||||
public override int? Attrib => (int?)_filePart?.Header.ExternalFileAttributes;
|
||||
|
||||
public string? Comment => _filePart?.Header.Comment;
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ internal abstract class ZipFilePart : FilePart
|
||||
);
|
||||
if (LeaveStreamOpen)
|
||||
{
|
||||
return NonDisposingStream.Create(decompressionStream);
|
||||
return SharpCompressStream.Create(decompressionStream, leaveOpen: true);
|
||||
}
|
||||
return decompressionStream;
|
||||
}
|
||||
@@ -70,17 +70,12 @@ internal abstract class ZipFilePart : FilePart
|
||||
{
|
||||
case ZipCompressionMethod.None:
|
||||
{
|
||||
if (stream is ReadOnlySubStream)
|
||||
if (Header.CompressedSize is 0)
|
||||
{
|
||||
return stream;
|
||||
return new DataDescriptorStream(stream);
|
||||
}
|
||||
|
||||
if (Header.CompressedSize > 0)
|
||||
{
|
||||
return new ReadOnlySubStream(stream, Header.CompressedSize);
|
||||
}
|
||||
|
||||
return new DataDescriptorStream(stream);
|
||||
return stream;
|
||||
}
|
||||
case ZipCompressionMethod.Shrink:
|
||||
{
|
||||
@@ -152,7 +147,7 @@ internal abstract class ZipFilePart : FilePart
|
||||
{
|
||||
return new XZStream(stream);
|
||||
}
|
||||
case ZipCompressionMethod.ZStd:
|
||||
case ZipCompressionMethod.ZStandard:
|
||||
{
|
||||
return new DecompressionStream(stream);
|
||||
}
|
||||
@@ -218,7 +213,7 @@ internal abstract class ZipFilePart : FilePart
|
||||
) || Header.IsZip64
|
||||
)
|
||||
{
|
||||
plainStream = NonDisposingStream.Create(plainStream); //make sure AES doesn't close
|
||||
plainStream = SharpCompressStream.Create(plainStream, leaveOpen: true); //make sure AES doesn't close
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//
|
||||
//
|
||||
// ADC.cs
|
||||
//
|
||||
// Author:
|
||||
@@ -28,14 +28,35 @@
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Compressors.ADC;
|
||||
|
||||
/// <summary>
|
||||
/// Provides a forward readable only stream that decompresses ADC data
|
||||
/// </summary>
|
||||
public sealed class ADCStream : Stream
|
||||
public sealed class ADCStream : Stream, IStreamStack
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
long IStreamStack.InstanceId { get; set; }
|
||||
#endif
|
||||
int IStreamStack.DefaultBufferSize { get; set; }
|
||||
|
||||
Stream IStreamStack.BaseStream() => _stream;
|
||||
|
||||
int IStreamStack.BufferSize
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
int IStreamStack.BufferPosition
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
|
||||
void IStreamStack.SetPosition(long position) { }
|
||||
|
||||
/// <summary>
|
||||
/// This stream holds the compressed data
|
||||
/// </summary>
|
||||
@@ -74,6 +95,9 @@ public sealed class ADCStream : Stream
|
||||
}
|
||||
|
||||
_stream = stream;
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugConstruct(typeof(ADCStream));
|
||||
#endif
|
||||
}
|
||||
|
||||
public override bool CanRead => _stream.CanRead;
|
||||
@@ -99,6 +123,9 @@ public sealed class ADCStream : Stream
|
||||
return;
|
||||
}
|
||||
_isDisposed = true;
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugDispose(typeof(ADCStream));
|
||||
#endif
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,9 +4,30 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using SharpCompress.Compressors.RLE90;
|
||||
using SharpCompress.Compressors.Squeezed;
|
||||
using SharpCompress.IO;
|
||||
|
||||
public partial class ArcLzwStream : Stream
|
||||
public partial class ArcLzwStream : Stream, IStreamStack
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
long IStreamStack.InstanceId { get; set; }
|
||||
#endif
|
||||
int IStreamStack.DefaultBufferSize { get; set; }
|
||||
|
||||
Stream IStreamStack.BaseStream() => _stream;
|
||||
|
||||
int IStreamStack.BufferSize
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
int IStreamStack.BufferPosition
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
|
||||
void IStreamStack.SetPosition(long position) { }
|
||||
|
||||
private Stream _stream;
|
||||
private bool _processed;
|
||||
private bool _useCrunched;
|
||||
@@ -33,6 +54,9 @@ public partial class ArcLzwStream : Stream
|
||||
public ArcLzwStream(Stream stream, int compressedSize, bool useCrunched = true)
|
||||
{
|
||||
_stream = stream;
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugConstruct(typeof(ArcLzwStream));
|
||||
#endif
|
||||
_useCrunched = useCrunched;
|
||||
_compressedSize = compressedSize;
|
||||
|
||||
@@ -196,4 +220,12 @@ public partial class ArcLzwStream : Stream
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count) =>
|
||||
throw new NotImplementedException();
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugDispose(typeof(ArcLzwStream));
|
||||
#endif
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,31 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.IO;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Compressors.BZip2;
|
||||
|
||||
public sealed class BZip2Stream : Stream
|
||||
public sealed class BZip2Stream : Stream, IStreamStack
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
long IStreamStack.InstanceId { get; set; }
|
||||
#endif
|
||||
int IStreamStack.DefaultBufferSize { get; set; }
|
||||
|
||||
Stream IStreamStack.BaseStream() => stream;
|
||||
|
||||
int IStreamStack.BufferSize
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
int IStreamStack.BufferPosition
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
|
||||
void IStreamStack.SetPosition(long position) { }
|
||||
|
||||
private readonly Stream stream;
|
||||
private bool isDisposed;
|
||||
|
||||
@@ -16,6 +37,9 @@ public sealed class BZip2Stream : Stream
|
||||
/// <param name="decompressConcatenated">Decompress Concatenated</param>
|
||||
public BZip2Stream(Stream stream, CompressionMode compressionMode, bool decompressConcatenated)
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugConstruct(typeof(BZip2Stream));
|
||||
#endif
|
||||
Mode = compressionMode;
|
||||
if (Mode == CompressionMode.Compress)
|
||||
{
|
||||
@@ -36,6 +60,9 @@ public sealed class BZip2Stream : Stream
|
||||
return;
|
||||
}
|
||||
isDisposed = true;
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugDispose(typeof(BZip2Stream));
|
||||
#endif
|
||||
if (disposing)
|
||||
{
|
||||
stream.Dispose();
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using SharpCompress.IO;
|
||||
|
||||
/*
|
||||
* Copyright 2001,2004-2005 The Apache Software Foundation
|
||||
@@ -37,8 +38,28 @@ namespace SharpCompress.Compressors.BZip2;
|
||||
* start of the BZIP2 stream to make it compatible with other PGP programs.
|
||||
*/
|
||||
|
||||
internal class CBZip2InputStream : Stream
|
||||
internal class CBZip2InputStream : Stream, IStreamStack
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
long IStreamStack.InstanceId { get; set; }
|
||||
#endif
|
||||
int IStreamStack.DefaultBufferSize { get; set; }
|
||||
|
||||
Stream IStreamStack.BaseStream() => bsStream;
|
||||
|
||||
int IStreamStack.BufferSize
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
int IStreamStack.BufferPosition
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
|
||||
void IStreamStack.SetPosition(long position) { }
|
||||
|
||||
private static void Cadvise()
|
||||
{
|
||||
//System.out.Println("CRC Error");
|
||||
@@ -164,6 +185,10 @@ internal class CBZip2InputStream : Stream
|
||||
ll8 = null;
|
||||
tt = null;
|
||||
BsSetStream(zStream);
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugConstruct(typeof(CBZip2InputStream));
|
||||
#endif
|
||||
|
||||
Initialize(true);
|
||||
InitBlock();
|
||||
SetupBlock();
|
||||
@@ -176,6 +201,9 @@ internal class CBZip2InputStream : Stream
|
||||
return;
|
||||
}
|
||||
isDisposed = true;
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugDispose(typeof(CBZip2InputStream));
|
||||
#endif
|
||||
base.Dispose(disposing);
|
||||
bsStream?.Dispose();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using SharpCompress.IO;
|
||||
|
||||
/*
|
||||
* Copyright 2001,2004-2005 The Apache Software Foundation
|
||||
@@ -38,8 +39,28 @@ namespace SharpCompress.Compressors.BZip2;
|
||||
* start of the BZIP2 stream to make it compatible with other PGP programs.
|
||||
*/
|
||||
|
||||
internal sealed class CBZip2OutputStream : Stream
|
||||
internal sealed class CBZip2OutputStream : Stream, IStreamStack
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
long IStreamStack.InstanceId { get; set; }
|
||||
#endif
|
||||
int IStreamStack.DefaultBufferSize { get; set; }
|
||||
|
||||
Stream IStreamStack.BaseStream() => bsStream;
|
||||
|
||||
int IStreamStack.BufferSize
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
int IStreamStack.BufferPosition
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
|
||||
void IStreamStack.SetPosition(long position) { }
|
||||
|
||||
private const int SETMASK = (1 << 21);
|
||||
private const int CLEARMASK = (~SETMASK);
|
||||
private const int GREATER_ICOST = 15;
|
||||
@@ -334,6 +355,10 @@ internal sealed class CBZip2OutputStream : Stream
|
||||
|
||||
BsSetStream(inStream);
|
||||
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugConstruct(typeof(CBZip2OutputStream));
|
||||
#endif
|
||||
|
||||
workFactor = 50;
|
||||
if (inBlockSize > 9)
|
||||
{
|
||||
@@ -450,6 +475,9 @@ internal sealed class CBZip2OutputStream : Stream
|
||||
Finish();
|
||||
|
||||
disposed = true;
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugDispose(typeof(CBZip2OutputStream));
|
||||
#endif
|
||||
Dispose();
|
||||
bsStream?.Dispose();
|
||||
bsStream = null;
|
||||
|
||||
@@ -27,11 +27,33 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Compressors.Deflate;
|
||||
|
||||
public class DeflateStream : Stream
|
||||
public class DeflateStream : Stream, IStreamStack
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
long IStreamStack.InstanceId { get; set; }
|
||||
#endif
|
||||
int IStreamStack.DefaultBufferSize { get; set; }
|
||||
|
||||
Stream IStreamStack.BaseStream() => _baseStream;
|
||||
|
||||
int IStreamStack.BufferSize
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
int IStreamStack.BufferPosition
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
|
||||
void IStreamStack.SetPosition(long position) { }
|
||||
|
||||
private readonly ZlibBaseStream _baseStream;
|
||||
private bool _disposed;
|
||||
|
||||
@@ -40,7 +62,8 @@ public class DeflateStream : Stream
|
||||
CompressionMode mode,
|
||||
CompressionLevel level = CompressionLevel.Default,
|
||||
Encoding? forceEncoding = null
|
||||
) =>
|
||||
)
|
||||
{
|
||||
_baseStream = new ZlibBaseStream(
|
||||
stream,
|
||||
mode,
|
||||
@@ -49,6 +72,11 @@ public class DeflateStream : Stream
|
||||
forceEncoding
|
||||
);
|
||||
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugConstruct(typeof(DeflateStream));
|
||||
#endif
|
||||
}
|
||||
|
||||
#region Zlib properties
|
||||
|
||||
/// <summary>
|
||||
@@ -233,6 +261,9 @@ public class DeflateStream : Stream
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugDispose(typeof(DeflateStream));
|
||||
#endif
|
||||
if (disposing)
|
||||
{
|
||||
_baseStream?.Dispose();
|
||||
@@ -290,6 +321,7 @@ public class DeflateStream : Stream
|
||||
{
|
||||
throw new ObjectDisposedException("DeflateStream");
|
||||
}
|
||||
|
||||
return _baseStream.Read(buffer, offset, count);
|
||||
}
|
||||
|
||||
|
||||
@@ -30,11 +30,32 @@ using System;
|
||||
using System.Buffers.Binary;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Compressors.Deflate;
|
||||
|
||||
public class GZipStream : Stream
|
||||
public class GZipStream : Stream, IStreamStack
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
long IStreamStack.InstanceId { get; set; }
|
||||
#endif
|
||||
int IStreamStack.DefaultBufferSize { get; set; }
|
||||
|
||||
Stream IStreamStack.BaseStream() => BaseStream;
|
||||
|
||||
int IStreamStack.BufferSize
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
int IStreamStack.BufferPosition
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
|
||||
void IStreamStack.SetPosition(long position) { }
|
||||
|
||||
internal static readonly DateTime UNIX_EPOCH = new(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
private string? _comment;
|
||||
@@ -62,6 +83,9 @@ public class GZipStream : Stream
|
||||
)
|
||||
{
|
||||
BaseStream = new ZlibBaseStream(stream, mode, level, ZlibStreamFlavor.GZIP, encoding);
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugConstruct(typeof(GZipStream));
|
||||
#endif
|
||||
_encoding = encoding;
|
||||
}
|
||||
|
||||
@@ -210,6 +234,9 @@ public class GZipStream : Stream
|
||||
Crc32 = BaseStream.Crc32;
|
||||
}
|
||||
_disposed = true;
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugDispose(typeof(GZipStream));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
finally
|
||||
|
||||
@@ -32,6 +32,7 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using SharpCompress.Common.Tar.Headers;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Compressors.Deflate;
|
||||
|
||||
@@ -42,8 +43,28 @@ internal enum ZlibStreamFlavor
|
||||
GZIP = 1952,
|
||||
}
|
||||
|
||||
internal class ZlibBaseStream : Stream
|
||||
internal class ZlibBaseStream : Stream, IStreamStack
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
long IStreamStack.InstanceId { get; set; }
|
||||
#endif
|
||||
int IStreamStack.DefaultBufferSize { get; set; }
|
||||
|
||||
Stream IStreamStack.BaseStream() => _stream;
|
||||
|
||||
int IStreamStack.BufferSize
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
int IStreamStack.BufferPosition
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
|
||||
void IStreamStack.SetPosition(long position) { }
|
||||
|
||||
protected internal ZlibCodec _z; // deferred init... new ZlibCodec();
|
||||
|
||||
protected internal StreamMode _streamMode = StreamMode.Undefined;
|
||||
@@ -87,6 +108,10 @@ internal class ZlibBaseStream : Stream
|
||||
|
||||
_encoding = encoding;
|
||||
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugConstruct(typeof(ZlibBaseStream));
|
||||
#endif
|
||||
|
||||
// workitem 7159
|
||||
if (flavor == ZlibStreamFlavor.GZIP)
|
||||
{
|
||||
@@ -334,6 +359,9 @@ internal class ZlibBaseStream : Stream
|
||||
return;
|
||||
}
|
||||
isDisposed = true;
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugDispose(typeof(ZlibBaseStream));
|
||||
#endif
|
||||
base.Dispose(disposing);
|
||||
if (disposing)
|
||||
{
|
||||
@@ -354,7 +382,13 @@ internal class ZlibBaseStream : Stream
|
||||
}
|
||||
}
|
||||
|
||||
public override void Flush() => _stream.Flush();
|
||||
public override void Flush()
|
||||
{
|
||||
_stream.Flush();
|
||||
//rewind the buffer
|
||||
((IStreamStack)this).Rewind(z.AvailableBytesIn); //unused
|
||||
z.AvailableBytesIn = 0;
|
||||
}
|
||||
|
||||
public override Int64 Seek(Int64 offset, SeekOrigin origin) =>
|
||||
throw new NotSupportedException();
|
||||
@@ -634,6 +668,13 @@ internal class ZlibBaseStream : Stream
|
||||
crc.SlurpBlock(buffer, offset, rc);
|
||||
}
|
||||
|
||||
if (rc == ZlibConstants.Z_STREAM_END && z.AvailableBytesIn != 0 && !_wantCompress)
|
||||
{
|
||||
//rewind the buffer
|
||||
((IStreamStack)this).Rewind(z.AvailableBytesIn); //unused
|
||||
z.AvailableBytesIn = 0;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,11 +28,32 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Compressors.Deflate;
|
||||
|
||||
public class ZlibStream : Stream
|
||||
public class ZlibStream : Stream, IStreamStack
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
long IStreamStack.InstanceId { get; set; }
|
||||
#endif
|
||||
int IStreamStack.DefaultBufferSize { get; set; }
|
||||
|
||||
Stream IStreamStack.BaseStream() => _baseStream;
|
||||
|
||||
int IStreamStack.BufferSize
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
int IStreamStack.BufferPosition
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
|
||||
void IStreamStack.SetPosition(long position) { }
|
||||
|
||||
private readonly ZlibBaseStream _baseStream;
|
||||
private bool _disposed;
|
||||
|
||||
@@ -47,7 +68,13 @@ public class ZlibStream : Stream
|
||||
CompressionMode mode,
|
||||
CompressionLevel level,
|
||||
Encoding encoding
|
||||
) => _baseStream = new ZlibBaseStream(stream, mode, level, ZlibStreamFlavor.ZLIB, encoding);
|
||||
)
|
||||
{
|
||||
_baseStream = new ZlibBaseStream(stream, mode, level, ZlibStreamFlavor.ZLIB, encoding);
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugConstruct(typeof(ZlibStream));
|
||||
#endif
|
||||
}
|
||||
|
||||
#region Zlib properties
|
||||
|
||||
@@ -216,6 +243,9 @@ public class ZlibStream : Stream
|
||||
_baseStream?.Dispose();
|
||||
}
|
||||
_disposed = true;
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugDispose(typeof(ZlibStream));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
finally
|
||||
|
||||
@@ -10,11 +10,32 @@ using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Common.Zip;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Compressors.Deflate64;
|
||||
|
||||
public sealed class Deflate64Stream : Stream
|
||||
public sealed class Deflate64Stream : Stream, IStreamStack
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
long IStreamStack.InstanceId { get; set; }
|
||||
#endif
|
||||
int IStreamStack.DefaultBufferSize { get; set; }
|
||||
|
||||
Stream IStreamStack.BaseStream() => _stream;
|
||||
|
||||
int IStreamStack.BufferSize
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
int IStreamStack.BufferPosition
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
|
||||
void IStreamStack.SetPosition(long position) { }
|
||||
|
||||
private const int DEFAULT_BUFFER_SIZE = 8192;
|
||||
|
||||
private Stream _stream;
|
||||
@@ -42,6 +63,9 @@ public sealed class Deflate64Stream : Stream
|
||||
}
|
||||
|
||||
InitializeInflater(stream, ZipCompressionMethod.Deflate64);
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugConstruct(typeof(Deflate64Stream));
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -252,6 +276,9 @@ public sealed class Deflate64Stream : Stream
|
||||
// In this case, we still need to clean up internal resources, hence the inner finally blocks.
|
||||
try
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugDispose(typeof(Deflate64Stream));
|
||||
#endif
|
||||
if (disposing)
|
||||
{
|
||||
_stream?.Dispose();
|
||||
|
||||
@@ -1,11 +1,32 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using SharpCompress.Common.Zip.Headers;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Compressors.Explode;
|
||||
|
||||
public class ExplodeStream : Stream
|
||||
public class ExplodeStream : Stream, IStreamStack
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
long IStreamStack.InstanceId { get; set; }
|
||||
#endif
|
||||
int IStreamStack.DefaultBufferSize { get; set; }
|
||||
|
||||
Stream IStreamStack.BaseStream() => inStream;
|
||||
|
||||
int IStreamStack.BufferSize
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
int IStreamStack.BufferPosition
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
|
||||
void IStreamStack.SetPosition(long position) { }
|
||||
|
||||
private const int INVALID_CODE = 99;
|
||||
private const int WSIZE = 64 * 1024;
|
||||
|
||||
@@ -45,6 +66,9 @@ public class ExplodeStream : Stream
|
||||
)
|
||||
{
|
||||
inStream = inStr;
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugConstruct(typeof(ExplodeStream));
|
||||
#endif
|
||||
this.compressedSize = (int)compressedSize;
|
||||
unCompressedSize = (long)uncompressedSize;
|
||||
this.generalPurposeBitFlag = generalPurposeBitFlag;
|
||||
@@ -54,6 +78,14 @@ public class ExplodeStream : Stream
|
||||
explode_var_init();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugDispose(typeof(ExplodeStream));
|
||||
#endif
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
|
||||
@@ -3,11 +3,32 @@ using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using SharpCompress.Compressors.LZMA.Utilites;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Compressors.LZMA;
|
||||
|
||||
internal sealed class AesDecoderStream : DecoderStream2
|
||||
internal sealed class AesDecoderStream : DecoderStream2, IStreamStack
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
long IStreamStack.InstanceId { get; set; }
|
||||
#endif
|
||||
int IStreamStack.DefaultBufferSize { get; set; }
|
||||
|
||||
Stream IStreamStack.BaseStream() => mStream;
|
||||
|
||||
int IStreamStack.BufferSize
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
int IStreamStack.BufferPosition
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
|
||||
void IStreamStack.SetPosition(long position) { }
|
||||
|
||||
private readonly Stream mStream;
|
||||
private readonly ICryptoTransform mDecoder;
|
||||
private readonly byte[] mBuffer;
|
||||
@@ -31,6 +52,10 @@ internal sealed class AesDecoderStream : DecoderStream2
|
||||
mStream = input;
|
||||
mLimit = limit;
|
||||
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugConstruct(typeof(AesDecoderStream));
|
||||
#endif
|
||||
|
||||
if (((uint)input.Length & 15) != 0)
|
||||
{
|
||||
throw new NotSupportedException("AES decoder does not support padding.");
|
||||
@@ -64,6 +89,9 @@ internal sealed class AesDecoderStream : DecoderStream2
|
||||
return;
|
||||
}
|
||||
isDisposed = true;
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugDispose(typeof(AesDecoderStream));
|
||||
#endif
|
||||
if (disposing)
|
||||
{
|
||||
mStream.Dispose();
|
||||
|
||||
@@ -1,11 +1,32 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Compressors.LZMA;
|
||||
|
||||
internal class Bcj2DecoderStream : DecoderStream2
|
||||
internal class Bcj2DecoderStream : DecoderStream2, IStreamStack
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
long IStreamStack.InstanceId { get; set; }
|
||||
#endif
|
||||
int IStreamStack.DefaultBufferSize { get; set; }
|
||||
|
||||
Stream IStreamStack.BaseStream() => _mMainStream;
|
||||
|
||||
int IStreamStack.BufferSize
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
int IStreamStack.BufferPosition
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
|
||||
void IStreamStack.SetPosition(long position) { }
|
||||
|
||||
private const int K_NUM_TOP_BITS = 24;
|
||||
private const uint K_TOP_VALUE = (1 << K_NUM_TOP_BITS);
|
||||
|
||||
@@ -109,6 +130,10 @@ internal class Bcj2DecoderStream : DecoderStream2
|
||||
_mStatusDecoder[i] = new StatusDecoder();
|
||||
}
|
||||
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugConstruct(typeof(Bcj2DecoderStream));
|
||||
#endif
|
||||
|
||||
_mIter = Run().GetEnumerator();
|
||||
}
|
||||
|
||||
@@ -119,6 +144,9 @@ internal class Bcj2DecoderStream : DecoderStream2
|
||||
return;
|
||||
}
|
||||
_isDisposed = true;
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugDispose(typeof(Bcj2DecoderStream));
|
||||
#endif
|
||||
base.Dispose(disposing);
|
||||
_mMainStream.Dispose();
|
||||
_mCallStream.Dispose();
|
||||
|
||||
@@ -15,10 +15,30 @@ namespace SharpCompress.Compressors.LZMA;
|
||||
/// <summary>
|
||||
/// Stream supporting the LZIP format, as documented at http://www.nongnu.org/lzip/manual/lzip_manual.html
|
||||
/// </summary>
|
||||
public sealed class LZipStream : Stream
|
||||
public sealed class LZipStream : Stream, IStreamStack
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
long IStreamStack.InstanceId { get; set; }
|
||||
#endif
|
||||
int IStreamStack.DefaultBufferSize { get; set; }
|
||||
|
||||
Stream IStreamStack.BaseStream() => _stream;
|
||||
|
||||
int IStreamStack.BufferSize
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
int IStreamStack.BufferPosition
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
|
||||
void IStreamStack.SetPosition(long position) { }
|
||||
|
||||
private readonly Stream _stream;
|
||||
private readonly CountingWritableSubStream? _countingWritableSubStream;
|
||||
private readonly SharpCompressStream? _countingWritableSubStream;
|
||||
private bool _disposed;
|
||||
private bool _finished;
|
||||
|
||||
@@ -44,7 +64,7 @@ public sealed class LZipStream : Stream
|
||||
var dSize = 104 * 1024;
|
||||
WriteHeaderSize(stream);
|
||||
|
||||
_countingWritableSubStream = new CountingWritableSubStream(stream);
|
||||
_countingWritableSubStream = new SharpCompressStream(stream, leaveOpen: true);
|
||||
_stream = new Crc32Stream(
|
||||
new LzmaStream(
|
||||
new LzmaEncoderProperties(true, dSize),
|
||||
@@ -52,6 +72,9 @@ public sealed class LZipStream : Stream
|
||||
_countingWritableSubStream
|
||||
)
|
||||
);
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugConstruct(typeof(LZipStream));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +87,7 @@ public sealed class LZipStream : Stream
|
||||
var crc32Stream = (Crc32Stream)_stream;
|
||||
crc32Stream.WrappedStream.Dispose();
|
||||
crc32Stream.Dispose();
|
||||
var compressedCount = _countingWritableSubStream.NotNull().Count;
|
||||
var compressedCount = _countingWritableSubStream.NotNull().InternalPosition;
|
||||
|
||||
Span<byte> intBuf = stackalloc byte[8];
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(intBuf, crc32Stream.Crc);
|
||||
@@ -74,7 +97,10 @@ public sealed class LZipStream : Stream
|
||||
_countingWritableSubStream?.Write(intBuf);
|
||||
|
||||
//total with headers
|
||||
BinaryPrimitives.WriteUInt64LittleEndian(intBuf, compressedCount + 6 + 20);
|
||||
BinaryPrimitives.WriteUInt64LittleEndian(
|
||||
intBuf,
|
||||
(ulong)compressedCount + (ulong)(6 + 20)
|
||||
);
|
||||
_countingWritableSubStream?.Write(intBuf);
|
||||
}
|
||||
_finished = true;
|
||||
@@ -90,6 +116,9 @@ public sealed class LZipStream : Stream
|
||||
return;
|
||||
}
|
||||
_disposed = true;
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugDispose(typeof(LZipStream));
|
||||
#endif
|
||||
if (disposing)
|
||||
{
|
||||
Finish();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
@@ -28,52 +28,68 @@ internal static class Log
|
||||
if (NEEDS_INDENT)
|
||||
{
|
||||
NEEDS_INDENT = false;
|
||||
#if DEBUG_LZMA
|
||||
Debug.Write(INDENT.Peek());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
public static void Write(object value)
|
||||
{
|
||||
EnsureIndent();
|
||||
#if DEBUG_LZMA
|
||||
Debug.Write(value);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void Write(string text)
|
||||
{
|
||||
EnsureIndent();
|
||||
#if DEBUG_LZMA
|
||||
Debug.Write(text);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void Write(string format, params object[] args)
|
||||
{
|
||||
EnsureIndent();
|
||||
#if DEBUG_LZMA
|
||||
Debug.Write(string.Format(format, args));
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void WriteLine()
|
||||
{
|
||||
#if DEBUG_LZMA
|
||||
Debug.WriteLine("");
|
||||
#endif
|
||||
NEEDS_INDENT = true;
|
||||
}
|
||||
|
||||
public static void WriteLine(object value)
|
||||
{
|
||||
EnsureIndent();
|
||||
#if DEBUG_LZMA
|
||||
Debug.WriteLine(value);
|
||||
#endif
|
||||
NEEDS_INDENT = true;
|
||||
}
|
||||
|
||||
public static void WriteLine(string text)
|
||||
{
|
||||
EnsureIndent();
|
||||
#if DEBUG_LZMA
|
||||
Debug.WriteLine(text);
|
||||
#endif
|
||||
NEEDS_INDENT = true;
|
||||
}
|
||||
|
||||
public static void WriteLine(string format, params object[] args)
|
||||
{
|
||||
EnsureIndent();
|
||||
#if DEBUG_LZMA
|
||||
Debug.WriteLine(string.Format(format, args));
|
||||
#endif
|
||||
NEEDS_INDENT = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,11 +4,32 @@ using System;
|
||||
using System.Buffers.Binary;
|
||||
using System.IO;
|
||||
using SharpCompress.Compressors.LZMA.LZ;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Compressors.LZMA;
|
||||
|
||||
public class LzmaStream : Stream
|
||||
public class LzmaStream : Stream, IStreamStack
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
long IStreamStack.InstanceId { get; set; }
|
||||
#endif
|
||||
int IStreamStack.DefaultBufferSize { get; set; }
|
||||
|
||||
Stream IStreamStack.BaseStream() => _inputStream;
|
||||
|
||||
int IStreamStack.BufferSize
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
int IStreamStack.BufferPosition
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
|
||||
void IStreamStack.SetPosition(long position) { }
|
||||
|
||||
private readonly Stream _inputStream;
|
||||
private readonly long _inputSize;
|
||||
private readonly long _outputSize;
|
||||
@@ -56,6 +77,10 @@ public class LzmaStream : Stream
|
||||
_outputSize = outputSize;
|
||||
_isLzma2 = isLzma2;
|
||||
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugConstruct(typeof(LzmaStream));
|
||||
#endif
|
||||
|
||||
if (!isLzma2)
|
||||
{
|
||||
_dictionarySize = BinaryPrimitives.ReadInt32LittleEndian(properties.AsSpan(1));
|
||||
@@ -117,6 +142,11 @@ public class LzmaStream : Stream
|
||||
Properties = prop;
|
||||
|
||||
_encoder.SetStreams(null, outputStream, -1, -1);
|
||||
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugConstruct(typeof(LzmaStream));
|
||||
#endif
|
||||
|
||||
if (presetDictionary != null)
|
||||
{
|
||||
_encoder.Train(presetDictionary);
|
||||
@@ -138,6 +168,9 @@ public class LzmaStream : Stream
|
||||
return;
|
||||
}
|
||||
_isDisposed = true;
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugDispose(typeof(LzmaStream));
|
||||
#endif
|
||||
if (disposing)
|
||||
{
|
||||
if (_encoder != null)
|
||||
|
||||
@@ -1,10 +1,31 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.IO;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Compressors.LZMA.Utilites;
|
||||
|
||||
internal class CrcBuilderStream : Stream
|
||||
internal class CrcBuilderStream : Stream, IStreamStack
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
long IStreamStack.InstanceId { get; set; }
|
||||
#endif
|
||||
int IStreamStack.DefaultBufferSize { get; set; }
|
||||
|
||||
Stream IStreamStack.BaseStream() => _mTarget;
|
||||
|
||||
int IStreamStack.BufferSize
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
int IStreamStack.BufferPosition
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
|
||||
void IStreamStack.SetPosition(long position) { }
|
||||
|
||||
private readonly Stream _mTarget;
|
||||
private uint _mCrc;
|
||||
private bool _mFinished;
|
||||
@@ -13,6 +34,9 @@ internal class CrcBuilderStream : Stream
|
||||
public CrcBuilderStream(Stream target)
|
||||
{
|
||||
_mTarget = target;
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugConstruct(typeof(CrcBuilderStream));
|
||||
#endif
|
||||
_mCrc = Crc.INIT_CRC;
|
||||
}
|
||||
|
||||
@@ -23,6 +47,9 @@ internal class CrcBuilderStream : Stream
|
||||
return;
|
||||
}
|
||||
_isDisposed = true;
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugDispose(typeof(CrcBuilderStream));
|
||||
#endif
|
||||
_mTarget.Dispose();
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Compressors.Lzw
|
||||
{
|
||||
@@ -42,8 +43,28 @@ namespace SharpCompress.Compressors.Lzw
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public class LzwStream : Stream
|
||||
public class LzwStream : Stream, IStreamStack
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
long IStreamStack.InstanceId { get; set; }
|
||||
#endif
|
||||
int IStreamStack.DefaultBufferSize { get; set; }
|
||||
|
||||
Stream IStreamStack.BaseStream() => baseInputStream;
|
||||
|
||||
int IStreamStack.BufferSize
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
int IStreamStack.BufferPosition
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
|
||||
void IStreamStack.SetPosition(long position) { }
|
||||
|
||||
public static bool IsLzwStream(Stream stream)
|
||||
{
|
||||
try
|
||||
@@ -90,6 +111,9 @@ namespace SharpCompress.Compressors.Lzw
|
||||
public LzwStream(Stream baseInputStream)
|
||||
{
|
||||
this.baseInputStream = baseInputStream;
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugConstruct(typeof(LzwStream));
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -539,6 +563,9 @@ namespace SharpCompress.Compressors.Lzw
|
||||
if (!isClosed)
|
||||
{
|
||||
isClosed = true;
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugDispose(typeof(LzwStream));
|
||||
#endif
|
||||
if (IsStreamOwner)
|
||||
{
|
||||
baseInputStream.Dispose();
|
||||
|
||||
@@ -1,15 +1,36 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using SharpCompress.Compressors.LZMA.RangeCoder;
|
||||
using SharpCompress.Compressors.PPMd.H;
|
||||
using SharpCompress.Compressors.PPMd.I1;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Compressors.PPMd;
|
||||
|
||||
public class PpmdStream : Stream
|
||||
public class PpmdStream : Stream, IStreamStack
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
long IStreamStack.InstanceId { get; set; }
|
||||
#endif
|
||||
int IStreamStack.DefaultBufferSize { get; set; }
|
||||
|
||||
Stream IStreamStack.BaseStream() => _stream;
|
||||
|
||||
int IStreamStack.BufferSize
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
int IStreamStack.BufferPosition
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
|
||||
void IStreamStack.SetPosition(long position) { }
|
||||
|
||||
private readonly PpmdProperties _properties;
|
||||
private readonly Stream _stream;
|
||||
private readonly bool _compress;
|
||||
@@ -25,6 +46,10 @@ public class PpmdStream : Stream
|
||||
_stream = stream;
|
||||
_compress = compress;
|
||||
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugConstruct(typeof(PpmdStream));
|
||||
#endif
|
||||
|
||||
if (properties.Version == PpmdVersion.I1)
|
||||
{
|
||||
_model = new Model();
|
||||
@@ -74,6 +99,9 @@ public class PpmdStream : Stream
|
||||
return;
|
||||
}
|
||||
_isDisposed = true;
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugDispose(typeof(PpmdStream));
|
||||
#endif
|
||||
if (isDisposing)
|
||||
{
|
||||
if (_compress)
|
||||
|
||||
@@ -4,11 +4,32 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Compressors.RLE90
|
||||
{
|
||||
public class RunLength90Stream : Stream
|
||||
public class RunLength90Stream : Stream, IStreamStack
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
long IStreamStack.InstanceId { get; set; }
|
||||
#endif
|
||||
int IStreamStack.DefaultBufferSize { get; set; }
|
||||
|
||||
Stream IStreamStack.BaseStream() => _stream;
|
||||
|
||||
int IStreamStack.BufferSize
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
int IStreamStack.BufferPosition
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
|
||||
void IStreamStack.SetPosition(long position) { }
|
||||
|
||||
private readonly Stream _stream;
|
||||
private const byte DLE = 0x90;
|
||||
private int _compressedSize;
|
||||
@@ -18,6 +39,17 @@ namespace SharpCompress.Compressors.RLE90
|
||||
{
|
||||
_stream = stream;
|
||||
_compressedSize = compressedSize;
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugConstruct(typeof(RunLength90Stream));
|
||||
#endif
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugDispose(typeof(RunLength90Stream));
|
||||
#endif
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
public override bool CanRead => true;
|
||||
|
||||
@@ -5,11 +5,32 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Common.Rar;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Compressors.Rar;
|
||||
|
||||
internal sealed class MultiVolumeReadOnlyStream : Stream
|
||||
internal sealed class MultiVolumeReadOnlyStream : Stream, IStreamStack
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
long IStreamStack.InstanceId { get; set; }
|
||||
#endif
|
||||
int IStreamStack.DefaultBufferSize { get; set; }
|
||||
|
||||
Stream IStreamStack.BaseStream() => currentStream;
|
||||
|
||||
int IStreamStack.BufferSize
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
int IStreamStack.BufferPosition
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
|
||||
void IStreamStack.SetPosition(long position) { }
|
||||
|
||||
private long currentPosition;
|
||||
private long maxPosition;
|
||||
|
||||
@@ -31,6 +52,9 @@ internal sealed class MultiVolumeReadOnlyStream : Stream
|
||||
filePartEnumerator = parts.GetEnumerator();
|
||||
filePartEnumerator.MoveNext();
|
||||
InitializeNextFilePart();
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugConstruct(typeof(MultiVolumeReadOnlyStream));
|
||||
#endif
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
@@ -38,6 +62,10 @@ internal sealed class MultiVolumeReadOnlyStream : Stream
|
||||
base.Dispose(disposing);
|
||||
if (disposing)
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugDispose(typeof(MultiVolumeReadOnlyStream));
|
||||
#endif
|
||||
|
||||
if (filePartEnumerator != null)
|
||||
{
|
||||
filePartEnumerator.Dispose();
|
||||
|
||||
@@ -3,11 +3,31 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Common.Rar.Headers;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Compressors.Rar;
|
||||
|
||||
internal class RarBLAKE2spStream : RarStream
|
||||
internal class RarBLAKE2spStream : RarStream, IStreamStack
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
long IStreamStack.InstanceId { get; set; }
|
||||
#endif
|
||||
|
||||
Stream IStreamStack.BaseStream() => readStream;
|
||||
|
||||
int IStreamStack.BufferSize
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
int IStreamStack.BufferPosition
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
|
||||
void IStreamStack.SetPosition(long position) { }
|
||||
|
||||
private readonly MultiVolumeReadOnlyStream readStream;
|
||||
private readonly bool disableCRCCheck;
|
||||
|
||||
@@ -91,12 +111,24 @@ internal class RarBLAKE2spStream : RarStream
|
||||
: base(unpack, fileHeader, readStream)
|
||||
{
|
||||
this.readStream = readStream;
|
||||
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugConstruct(typeof(RarBLAKE2spStream));
|
||||
#endif
|
||||
disableCRCCheck = fileHeader.IsEncrypted;
|
||||
_hash = fileHeader.FileCrc.NotNull();
|
||||
_blake2sp = new BLAKE2SP();
|
||||
ResetCrc();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugDispose(typeof(RarBLAKE2spStream));
|
||||
#endif
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
public byte[] GetCrc() => _hash;
|
||||
|
||||
internal void ResetCrc(BLAKE2S hash)
|
||||
|
||||
@@ -1,11 +1,32 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Common.Rar.Headers;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Compressors.Rar;
|
||||
|
||||
internal class RarCrcStream : RarStream
|
||||
internal class RarCrcStream : RarStream, IStreamStack
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
long IStreamStack.InstanceId { get; set; }
|
||||
#endif
|
||||
|
||||
Stream IStreamStack.BaseStream() => readStream;
|
||||
|
||||
int IStreamStack.BufferSize
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
int IStreamStack.BufferPosition
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
|
||||
void IStreamStack.SetPosition(long position) { }
|
||||
|
||||
private readonly MultiVolumeReadOnlyStream readStream;
|
||||
private uint currentCrc;
|
||||
private readonly bool disableCRC;
|
||||
@@ -18,10 +39,21 @@ internal class RarCrcStream : RarStream
|
||||
: base(unpack, fileHeader, readStream)
|
||||
{
|
||||
this.readStream = readStream;
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugConstruct(typeof(RarCrcStream));
|
||||
#endif
|
||||
disableCRC = fileHeader.IsEncrypted;
|
||||
ResetCrc();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugDispose(typeof(RarCrcStream));
|
||||
#endif
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
public uint GetCrc() => ~currentCrc;
|
||||
|
||||
public void ResetCrc() => currentCrc = 0xffffffff;
|
||||
|
||||
@@ -4,11 +4,32 @@ using System;
|
||||
using System.Buffers;
|
||||
using System.IO;
|
||||
using SharpCompress.Common.Rar.Headers;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Compressors.Rar;
|
||||
|
||||
internal class RarStream : Stream
|
||||
internal class RarStream : Stream, IStreamStack
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
long IStreamStack.InstanceId { get; set; }
|
||||
#endif
|
||||
int IStreamStack.DefaultBufferSize { get; set; }
|
||||
|
||||
Stream IStreamStack.BaseStream() => readStream;
|
||||
|
||||
int IStreamStack.BufferSize
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
int IStreamStack.BufferPosition
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
|
||||
void IStreamStack.SetPosition(long position) { }
|
||||
|
||||
private readonly IRarUnpack unpack;
|
||||
private readonly FileHeader fileHeader;
|
||||
private readonly Stream readStream;
|
||||
@@ -31,6 +52,11 @@ internal class RarStream : Stream
|
||||
this.unpack = unpack;
|
||||
this.fileHeader = fileHeader;
|
||||
this.readStream = readStream;
|
||||
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugConstruct(typeof(RarStream));
|
||||
#endif
|
||||
|
||||
fetch = true;
|
||||
unpack.DoUnpack(fileHeader, readStream, this);
|
||||
fetch = false;
|
||||
@@ -43,6 +69,9 @@ internal class RarStream : Stream
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugDispose(typeof(RarStream));
|
||||
#endif
|
||||
ArrayPool<byte>.Shared.Return(this.tmpBuffer);
|
||||
this.tmpBuffer = null;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,31 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Compressors.Reduce;
|
||||
|
||||
public class ReduceStream : Stream
|
||||
public class ReduceStream : Stream, IStreamStack
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
long IStreamStack.InstanceId { get; set; }
|
||||
#endif
|
||||
int IStreamStack.DefaultBufferSize { get; set; }
|
||||
|
||||
Stream IStreamStack.BaseStream() => inStream;
|
||||
|
||||
int IStreamStack.BufferSize
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
int IStreamStack.BufferPosition
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
|
||||
void IStreamStack.SetPosition(long position) { }
|
||||
|
||||
private readonly long unCompressedSize;
|
||||
private readonly long compressedSize;
|
||||
private readonly Stream inStream;
|
||||
@@ -31,6 +52,10 @@ public class ReduceStream : Stream
|
||||
inByteCount = 0;
|
||||
outBytesCount = 0;
|
||||
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugConstruct(typeof(ReduceStream));
|
||||
#endif
|
||||
|
||||
this.factor = factor;
|
||||
distanceMask = (int)mask_bits[factor] << 8;
|
||||
lengthMask = 0xff >> factor;
|
||||
@@ -47,6 +72,14 @@ public class ReduceStream : Stream
|
||||
LoadNextByteTable();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugDispose(typeof(ReduceStream));
|
||||
#endif
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
|
||||
@@ -1,10 +1,31 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Compressors.Shrink;
|
||||
|
||||
internal class ShrinkStream : Stream
|
||||
internal class ShrinkStream : Stream, IStreamStack
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
long IStreamStack.InstanceId { get; set; }
|
||||
#endif
|
||||
int IStreamStack.DefaultBufferSize { get; set; }
|
||||
|
||||
Stream IStreamStack.BaseStream() => inStream;
|
||||
|
||||
int IStreamStack.BufferSize
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
int IStreamStack.BufferPosition
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
|
||||
void IStreamStack.SetPosition(long position) { }
|
||||
|
||||
private Stream inStream;
|
||||
private CompressionMode _compressionMode;
|
||||
|
||||
@@ -23,12 +44,24 @@ internal class ShrinkStream : Stream
|
||||
inStream = stream;
|
||||
_compressionMode = compressionMode;
|
||||
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugConstruct(typeof(ShrinkStream));
|
||||
#endif
|
||||
|
||||
_compressedSize = (ulong)compressedSize;
|
||||
_uncompressedSize = uncompressedSize;
|
||||
_byteOut = new byte[_uncompressedSize];
|
||||
_outBytesCount = 0L;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugDispose(typeof(ShrinkStream));
|
||||
#endif
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
public override bool CanRead => true;
|
||||
|
||||
public override bool CanSeek => true;
|
||||
|
||||
@@ -5,12 +5,32 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Compressors.RLE90;
|
||||
using ZstdSharp.Unsafe;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Compressors.Squeezed
|
||||
{
|
||||
public class SqueezeStream : Stream
|
||||
public class SqueezeStream : Stream, IStreamStack
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
long IStreamStack.InstanceId { get; set; }
|
||||
#endif
|
||||
int IStreamStack.DefaultBufferSize { get; set; }
|
||||
|
||||
Stream IStreamStack.BaseStream() => _stream;
|
||||
|
||||
int IStreamStack.BufferSize
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
int IStreamStack.BufferPosition
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
|
||||
void IStreamStack.SetPosition(long position) { }
|
||||
|
||||
private readonly Stream _stream;
|
||||
private readonly int _compressedSize;
|
||||
private const int NUMVALS = 257;
|
||||
@@ -21,6 +41,17 @@ namespace SharpCompress.Compressors.Squeezed
|
||||
{
|
||||
_stream = stream;
|
||||
_compressedSize = compressedSize;
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugConstruct(typeof(SqueezeStream));
|
||||
#endif
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugDispose(typeof(SqueezeStream));
|
||||
#endif
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
public override bool CanRead => true;
|
||||
|
||||
@@ -23,7 +23,7 @@ public class XZFooter
|
||||
public static XZFooter FromStream(Stream stream)
|
||||
{
|
||||
var footer = new XZFooter(
|
||||
new BinaryReader(NonDisposingStream.Create(stream), Encoding.UTF8)
|
||||
new BinaryReader(SharpCompressStream.Create(stream, leaveOpen: true), Encoding.UTF8)
|
||||
);
|
||||
footer.Process();
|
||||
return footer;
|
||||
|
||||
@@ -19,7 +19,7 @@ public class XZHeader
|
||||
public static XZHeader FromStream(Stream stream)
|
||||
{
|
||||
var header = new XZHeader(
|
||||
new BinaryReader(NonDisposingStream.Create(stream), Encoding.UTF8)
|
||||
new BinaryReader(SharpCompressStream.Create(stream, leaveOpen: true), Encoding.UTF8)
|
||||
);
|
||||
header.Process();
|
||||
return header;
|
||||
|
||||
@@ -32,7 +32,7 @@ public class XZIndex
|
||||
public static XZIndex FromStream(Stream stream, bool indexMarkerAlreadyVerified)
|
||||
{
|
||||
var index = new XZIndex(
|
||||
new BinaryReader(NonDisposingStream.Create(stream), Encoding.UTF8),
|
||||
new BinaryReader(SharpCompressStream.Create(stream, leaveOpen: true), Encoding.UTF8),
|
||||
indexMarkerAlreadyVerified
|
||||
);
|
||||
index.Process();
|
||||
|
||||
@@ -1,10 +1,31 @@
|
||||
using System.IO;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Compressors.Xz;
|
||||
|
||||
public abstract class XZReadOnlyStream : ReadOnlyStream
|
||||
public abstract class XZReadOnlyStream : ReadOnlyStream, IStreamStack
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
long IStreamStack.InstanceId { get; set; }
|
||||
#endif
|
||||
int IStreamStack.DefaultBufferSize { get; set; }
|
||||
|
||||
Stream IStreamStack.BaseStream() => base.BaseStream;
|
||||
|
||||
int IStreamStack.BufferSize
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
int IStreamStack.BufferPosition
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
|
||||
void IStreamStack.SetPosition(long position) { }
|
||||
|
||||
public XZReadOnlyStream(Stream stream)
|
||||
{
|
||||
BaseStream = stream;
|
||||
@@ -12,5 +33,16 @@ public abstract class XZReadOnlyStream : ReadOnlyStream
|
||||
{
|
||||
throw new InvalidFormatException("Must be able to read from stream");
|
||||
}
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugConstruct(typeof(XZReadOnlyStream));
|
||||
#endif
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugDispose(typeof(XZReadOnlyStream));
|
||||
#endif
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,12 +3,49 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Compressors.Xz;
|
||||
|
||||
[CLSCompliant(false)]
|
||||
public sealed class XZStream : XZReadOnlyStream
|
||||
public sealed class XZStream : XZReadOnlyStream, IStreamStack
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
long IStreamStack.InstanceId { get; set; }
|
||||
#endif
|
||||
|
||||
Stream IStreamStack.BaseStream() => _baseStream;
|
||||
|
||||
int IStreamStack.BufferSize
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
int IStreamStack.BufferPosition
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
|
||||
void IStreamStack.SetPosition(long position) { }
|
||||
|
||||
public XZStream(Stream baseStream)
|
||||
: base(baseStream)
|
||||
{
|
||||
_baseStream = baseStream;
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugConstruct(typeof(XZStream));
|
||||
#endif
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugDispose(typeof(XZStream));
|
||||
#endif
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
public static bool IsXZStream(Stream stream)
|
||||
{
|
||||
try
|
||||
@@ -35,6 +72,7 @@ public sealed class XZStream : XZReadOnlyStream
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Stream _baseStream;
|
||||
public XZHeader Header { get; private set; }
|
||||
public XZIndex Index { get; private set; }
|
||||
public XZFooter Footer { get; private set; }
|
||||
@@ -43,9 +81,6 @@ public sealed class XZStream : XZReadOnlyStream
|
||||
|
||||
private bool _endOfStream;
|
||||
|
||||
public XZStream(Stream stream)
|
||||
: base(stream) { }
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
var bytesRead = 0;
|
||||
|
||||
65
src/SharpCompress/Compressors/ZStandard/ZStandardStream.cs
Normal file
65
src/SharpCompress/Compressors/ZStandard/ZStandardStream.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Compressors.ZStandard;
|
||||
|
||||
internal class ZStandardStream : ZstdSharp.DecompressionStream, IStreamStack
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
long IStreamStack.InstanceId { get; set; }
|
||||
#endif
|
||||
|
||||
public int DefaultBufferSize { get; set; }
|
||||
int IStreamStack.BufferSize
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
int IStreamStack.BufferPosition
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
|
||||
private readonly Stream stream;
|
||||
|
||||
Stream IStreamStack.BaseStream() => stream;
|
||||
|
||||
void IStreamStack.SetPosition(long position) { }
|
||||
|
||||
internal static bool IsZStandard(Stream stream)
|
||||
{
|
||||
var br = new BinaryReader(stream);
|
||||
var magic = br.ReadUInt32();
|
||||
if (ZstandardConstants.MAGIC != magic)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public ZStandardStream(Stream baseInputStream)
|
||||
: base(baseInputStream)
|
||||
{
|
||||
this.stream = baseInputStream;
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugConstruct(typeof(ZStandardStream));
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The current position within the stream.
|
||||
/// Throws a NotSupportedException when attempting to set the position
|
||||
/// </summary>
|
||||
/// <exception cref="NotSupportedException">Attempting to set the position</exception>
|
||||
public override long Position
|
||||
{
|
||||
get { return stream.Position; }
|
||||
set { throw new NotSupportedException("InflaterInputStream Position not supported"); }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SharpCompress.Compressors.ZStandard;
|
||||
|
||||
internal class ZstandardConstants
|
||||
{
|
||||
/// <summary>
|
||||
/// Magic number found at start of ZStandard frame: 0xFD 0x2F 0xB5 0x28
|
||||
/// </summary>
|
||||
public const uint MAGIC = 0xFD2FB528;
|
||||
}
|
||||
@@ -2,22 +2,63 @@
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Crypto;
|
||||
|
||||
[CLSCompliant(false)]
|
||||
public sealed class Crc32Stream(Stream stream, uint polynomial, uint seed) : Stream
|
||||
public sealed class Crc32Stream : Stream, IStreamStack
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
long IStreamStack.InstanceId { get; set; }
|
||||
#endif
|
||||
int IStreamStack.DefaultBufferSize { get; set; }
|
||||
|
||||
Stream IStreamStack.BaseStream() => stream;
|
||||
|
||||
int IStreamStack.BufferSize
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
int IStreamStack.BufferPosition
|
||||
{
|
||||
get => 0;
|
||||
set { }
|
||||
}
|
||||
|
||||
void IStreamStack.SetPosition(long position) { }
|
||||
|
||||
private readonly Stream stream;
|
||||
private readonly uint[] _table;
|
||||
private uint seed;
|
||||
|
||||
public const uint DEFAULT_POLYNOMIAL = 0xedb88320u;
|
||||
public const uint DEFAULT_SEED = 0xffffffffu;
|
||||
|
||||
private static uint[] _defaultTable;
|
||||
|
||||
private readonly uint[] _table = InitializeTable(polynomial);
|
||||
|
||||
public Crc32Stream(Stream stream)
|
||||
: this(stream, DEFAULT_POLYNOMIAL, DEFAULT_SEED) { }
|
||||
|
||||
public Crc32Stream(Stream stream, uint polynomial, uint seed)
|
||||
{
|
||||
this.stream = stream;
|
||||
_table = InitializeTable(polynomial);
|
||||
this.seed = seed;
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugConstruct(typeof(Crc32Stream));
|
||||
#endif
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugDispose(typeof(Crc32Stream));
|
||||
#endif
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
public Stream WrappedStream => stream;
|
||||
|
||||
public override void Flush() => stream.Flush();
|
||||
|
||||
@@ -23,7 +23,11 @@ namespace SharpCompress.Factories
|
||||
yield return "arc";
|
||||
}
|
||||
|
||||
public override bool IsArchive(Stream stream, string? password = null)
|
||||
public override bool IsArchive(
|
||||
Stream stream,
|
||||
string? password = null,
|
||||
int bufferSize = ReaderOptions.DefaultBufferSize
|
||||
)
|
||||
{
|
||||
//You may have to use some(paranoid) checks to ensure that you actually are
|
||||
//processing an ARC file, since other archivers also adopted the idea of putting
|
||||
|
||||
@@ -49,23 +49,27 @@ public abstract class Factory : IFactory
|
||||
public abstract IEnumerable<string> GetSupportedExtensions();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public abstract bool IsArchive(Stream stream, string? password = null);
|
||||
public abstract bool IsArchive(
|
||||
Stream stream,
|
||||
string? password = null,
|
||||
int bufferSize = ReaderOptions.DefaultBufferSize
|
||||
);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual FileInfo? GetFilePart(int index, FileInfo part1) => null;
|
||||
|
||||
/// <summary>
|
||||
/// Tries to open an <see cref="IReader"/> from a <see cref="RewindableStream"/>.
|
||||
/// Tries to open an <see cref="IReader"/> from a <see cref="SharpCompressStream"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method provides extra insight to support loading compressed TAR files.
|
||||
/// </remarks>
|
||||
/// <param name="rewindableStream"></param>
|
||||
/// <param name="stream"></param>
|
||||
/// <param name="options"></param>
|
||||
/// <param name="reader"></param>
|
||||
/// <returns></returns>
|
||||
internal virtual bool TryOpenReader(
|
||||
RewindableStream rewindableStream,
|
||||
SharpCompressStream stream,
|
||||
ReaderOptions options,
|
||||
out IReader? reader
|
||||
)
|
||||
@@ -74,11 +78,12 @@ public abstract class Factory : IFactory
|
||||
|
||||
if (this is IReaderFactory readerFactory)
|
||||
{
|
||||
rewindableStream.Rewind(false);
|
||||
if (IsArchive(rewindableStream, options.Password))
|
||||
long pos = ((IStreamStack)stream).GetPosition();
|
||||
|
||||
if (IsArchive(stream, options.Password, options.BufferSize))
|
||||
{
|
||||
rewindableStream.Rewind(true);
|
||||
reader = readerFactory.OpenReader(rewindableStream, options);
|
||||
((IStreamStack)stream).StackSeek(pos);
|
||||
reader = readerFactory.OpenReader(stream, options);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,8 +40,11 @@ public class GZipFactory
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool IsArchive(Stream stream, string? password = null) =>
|
||||
GZipArchive.IsGZipFile(stream);
|
||||
public override bool IsArchive(
|
||||
Stream stream,
|
||||
string? password = null,
|
||||
int bufferSize = ReaderOptions.DefaultBufferSize
|
||||
) => GZipArchive.IsGZipFile(stream);
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -73,26 +76,27 @@ public class GZipFactory
|
||||
|
||||
/// <inheritdoc/>
|
||||
internal override bool TryOpenReader(
|
||||
RewindableStream rewindableStream,
|
||||
SharpCompressStream rewindableStream,
|
||||
ReaderOptions options,
|
||||
out IReader? reader
|
||||
)
|
||||
{
|
||||
reader = null;
|
||||
|
||||
rewindableStream.Rewind(false);
|
||||
long pos = ((IStreamStack)rewindableStream).GetPosition();
|
||||
|
||||
if (GZipArchive.IsGZipFile(rewindableStream))
|
||||
{
|
||||
rewindableStream.Rewind(false);
|
||||
((IStreamStack)rewindableStream).StackSeek(pos);
|
||||
var testStream = new GZipStream(rewindableStream, CompressionMode.Decompress);
|
||||
if (TarArchive.IsTarFile(testStream))
|
||||
{
|
||||
rewindableStream.Rewind(true);
|
||||
((IStreamStack)rewindableStream).StackSeek(pos);
|
||||
reader = new TarReader(rewindableStream, options, CompressionType.GZip);
|
||||
return true;
|
||||
}
|
||||
|
||||
rewindableStream.Rewind(true);
|
||||
((IStreamStack)rewindableStream).StackSeek(pos);
|
||||
reader = OpenReader(rewindableStream, options);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SharpCompress.Readers;
|
||||
|
||||
namespace SharpCompress.Factories;
|
||||
|
||||
@@ -35,7 +36,11 @@ public interface IFactory
|
||||
/// </summary>
|
||||
/// <param name="stream">A stream, pointing to the beginning of the archive.</param>
|
||||
/// <param name="password">optional password</param>
|
||||
bool IsArchive(Stream stream, string? password = null);
|
||||
bool IsArchive(
|
||||
Stream stream,
|
||||
string? password = null,
|
||||
int bufferSize = ReaderOptions.DefaultBufferSize
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// From a passed in archive (zip, rar, 7z, 001), return all parts.
|
||||
|
||||
@@ -29,8 +29,11 @@ public class RarFactory : Factory, IArchiveFactory, IMultiArchiveFactory, IReade
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool IsArchive(Stream stream, string? password = null) =>
|
||||
RarArchive.IsRarFile(stream);
|
||||
public override bool IsArchive(
|
||||
Stream stream,
|
||||
string? password = null,
|
||||
int bufferSize = ReaderOptions.DefaultBufferSize
|
||||
) => RarArchive.IsRarFile(stream);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override FileInfo? GetFilePart(int index, FileInfo part1) =>
|
||||
|
||||
@@ -28,8 +28,11 @@ public class SevenZipFactory : Factory, IArchiveFactory, IMultiArchiveFactory
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool IsArchive(Stream stream, string? password = null) =>
|
||||
SevenZipArchive.IsSevenZipFile(stream);
|
||||
public override bool IsArchive(
|
||||
Stream stream,
|
||||
string? password = null,
|
||||
int bufferSize = ReaderOptions.DefaultBufferSize
|
||||
) => SevenZipArchive.IsSevenZipFile(stream);
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -60,7 +63,7 @@ public class SevenZipFactory : Factory, IArchiveFactory, IMultiArchiveFactory
|
||||
#region reader
|
||||
|
||||
internal override bool TryOpenReader(
|
||||
RewindableStream rewindableStream,
|
||||
SharpCompressStream rewindableStream,
|
||||
ReaderOptions options,
|
||||
out IReader? reader
|
||||
)
|
||||
|
||||
@@ -1,18 +1,23 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Archives.Tar;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Compressors;
|
||||
using SharpCompress.Compressors.BZip2;
|
||||
using SharpCompress.Compressors.Deflate;
|
||||
using SharpCompress.Compressors.LZMA;
|
||||
using SharpCompress.Compressors.Lzw;
|
||||
using SharpCompress.Compressors.Xz;
|
||||
using SharpCompress.Compressors.ZStandard;
|
||||
using SharpCompress.IO;
|
||||
using SharpCompress.Readers;
|
||||
using SharpCompress.Readers.Tar;
|
||||
using SharpCompress.Writers;
|
||||
using SharpCompress.Writers.Tar;
|
||||
using GZipArchive = SharpCompress.Archives.GZip.GZipArchive;
|
||||
|
||||
namespace SharpCompress.Factories;
|
||||
|
||||
@@ -38,37 +43,21 @@ public class TarFactory
|
||||
/// <inheritdoc/>
|
||||
public override IEnumerable<string> GetSupportedExtensions()
|
||||
{
|
||||
// from https://en.wikipedia.org/wiki/Tar_(computing)#Suffixes_for_compressed_files
|
||||
|
||||
yield return "tar";
|
||||
|
||||
// gzip
|
||||
yield return "taz";
|
||||
yield return "tgz";
|
||||
|
||||
// bzip2
|
||||
yield return "tb2";
|
||||
yield return "tbz";
|
||||
yield return "tbz2";
|
||||
yield return "tz2";
|
||||
|
||||
// lzma
|
||||
// yield return "tlz"; // unsupported
|
||||
|
||||
// xz
|
||||
// yield return "txz"; // unsupported
|
||||
|
||||
// compress
|
||||
yield return "tZ";
|
||||
yield return "taZ";
|
||||
|
||||
// zstd
|
||||
// yield return "tzst"; // unsupported
|
||||
foreach (var testOption in compressionOptions)
|
||||
{
|
||||
foreach (var ext in testOption.KnownExtensions)
|
||||
{
|
||||
yield return ext;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool IsArchive(Stream stream, string? password = null) =>
|
||||
TarArchive.IsTarFile(stream);
|
||||
public override bool IsArchive(
|
||||
Stream stream,
|
||||
string? password = null,
|
||||
int bufferSize = ReaderOptions.DefaultBufferSize
|
||||
) => TarArchive.IsTarFile(stream);
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -98,78 +87,115 @@ public class TarFactory
|
||||
|
||||
#region IReaderFactory
|
||||
|
||||
|
||||
protected class TestOption
|
||||
{
|
||||
public readonly CompressionType Type;
|
||||
public readonly Func<Stream, bool> CanHandle;
|
||||
public readonly bool WrapInSharpCompressStream;
|
||||
|
||||
public readonly Func<Stream, Stream> CreateStream;
|
||||
|
||||
public readonly IEnumerable<string> KnownExtensions;
|
||||
|
||||
public TestOption(
|
||||
CompressionType Type,
|
||||
Func<Stream, bool> CanHandle,
|
||||
Func<Stream, Stream> CreateStream,
|
||||
IEnumerable<string> KnownExtensions,
|
||||
bool WrapInSharpCompressStream = true
|
||||
)
|
||||
{
|
||||
this.Type = Type;
|
||||
this.CanHandle = CanHandle;
|
||||
this.WrapInSharpCompressStream = WrapInSharpCompressStream;
|
||||
this.CreateStream = CreateStream;
|
||||
this.KnownExtensions = KnownExtensions;
|
||||
}
|
||||
}
|
||||
|
||||
// https://en.wikipedia.org/wiki/Tar_(computing)#Suffixes_for_compressed_files
|
||||
protected TestOption[] compressionOptions =
|
||||
[
|
||||
new(CompressionType.None, (stream) => true, (stream) => stream, ["tar"], false), // We always do a test for IsTarFile later
|
||||
new(
|
||||
CompressionType.BZip2,
|
||||
BZip2Stream.IsBZip2,
|
||||
(stream) => new BZip2Stream(stream, CompressionMode.Decompress, false),
|
||||
["tar.bz2", "tb2", "tbz", "tbz2", "tz2"]
|
||||
),
|
||||
new(
|
||||
CompressionType.GZip,
|
||||
GZipArchive.IsGZipFile,
|
||||
(stream) => new GZipStream(stream, CompressionMode.Decompress),
|
||||
["tar.gz", "taz", "tgz"]
|
||||
),
|
||||
new(
|
||||
CompressionType.ZStandard,
|
||||
ZStandardStream.IsZStandard,
|
||||
(stream) => new ZStandardStream(stream),
|
||||
["tar.zst", "tar.zstd", "tzst", "tzstd"]
|
||||
),
|
||||
new(
|
||||
CompressionType.LZip,
|
||||
LZipStream.IsLZipFile,
|
||||
(stream) => new LZipStream(stream, CompressionMode.Decompress),
|
||||
["tar.lz"]
|
||||
),
|
||||
new(
|
||||
CompressionType.Xz,
|
||||
XZStream.IsXZStream,
|
||||
(stream) => new XZStream(stream),
|
||||
["tar.xz", "txz"],
|
||||
false
|
||||
),
|
||||
new(
|
||||
CompressionType.Lzw,
|
||||
LzwStream.IsLzwStream,
|
||||
(stream) => new LzwStream(stream),
|
||||
["tar.Z", "tZ", "taZ"],
|
||||
false
|
||||
),
|
||||
];
|
||||
|
||||
/// <inheritdoc/>
|
||||
internal override bool TryOpenReader(
|
||||
RewindableStream rewindableStream,
|
||||
SharpCompressStream rewindableStream,
|
||||
ReaderOptions options,
|
||||
out IReader? reader
|
||||
)
|
||||
{
|
||||
reader = null;
|
||||
|
||||
rewindableStream.Rewind(false);
|
||||
if (TarArchive.IsTarFile(rewindableStream))
|
||||
long pos = ((IStreamStack)rewindableStream).GetPosition();
|
||||
TestOption? testedOption = null;
|
||||
if (!string.IsNullOrWhiteSpace(options.ExtensionHint))
|
||||
{
|
||||
rewindableStream.Rewind(true);
|
||||
reader = OpenReader(rewindableStream, options);
|
||||
return true;
|
||||
}
|
||||
|
||||
rewindableStream.Rewind(false);
|
||||
if (BZip2Stream.IsBZip2(rewindableStream))
|
||||
{
|
||||
rewindableStream.Rewind(false);
|
||||
var testStream = new BZip2Stream(
|
||||
NonDisposingStream.Create(rewindableStream),
|
||||
CompressionMode.Decompress,
|
||||
false
|
||||
testedOption = compressionOptions.FirstOrDefault(a =>
|
||||
a.KnownExtensions.Contains(
|
||||
options.ExtensionHint,
|
||||
StringComparer.CurrentCultureIgnoreCase
|
||||
)
|
||||
);
|
||||
if (TarArchive.IsTarFile(testStream))
|
||||
if (testedOption != null)
|
||||
{
|
||||
rewindableStream.Rewind(true);
|
||||
reader = new TarReader(rewindableStream, options, CompressionType.BZip2);
|
||||
return true;
|
||||
reader = TryOption(rewindableStream, options, pos, testedOption);
|
||||
if (reader != null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rewindableStream.Rewind(false);
|
||||
if (LZipStream.IsLZipFile(rewindableStream))
|
||||
foreach (var testOption in compressionOptions)
|
||||
{
|
||||
rewindableStream.Rewind(false);
|
||||
var testStream = new LZipStream(
|
||||
NonDisposingStream.Create(rewindableStream),
|
||||
CompressionMode.Decompress
|
||||
);
|
||||
if (TarArchive.IsTarFile(testStream))
|
||||
if (testedOption == testOption)
|
||||
{
|
||||
rewindableStream.Rewind(true);
|
||||
reader = new TarReader(rewindableStream, options, CompressionType.LZip);
|
||||
return true;
|
||||
continue; // Already tested above
|
||||
}
|
||||
}
|
||||
|
||||
rewindableStream.Rewind(false);
|
||||
if (XZStream.IsXZStream(rewindableStream))
|
||||
{
|
||||
rewindableStream.Rewind(true);
|
||||
var testStream = new XZStream(rewindableStream);
|
||||
if (TarArchive.IsTarFile(testStream))
|
||||
((IStreamStack)rewindableStream).StackSeek(pos);
|
||||
reader = TryOption(rewindableStream, options, pos, testOption);
|
||||
if (reader != null)
|
||||
{
|
||||
rewindableStream.Rewind(true);
|
||||
reader = new TarReader(rewindableStream, options, CompressionType.Xz);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
rewindableStream.Rewind(false);
|
||||
if (LzwStream.IsLzwStream(rewindableStream))
|
||||
{
|
||||
rewindableStream.Rewind(false);
|
||||
var testStream = new LzwStream(rewindableStream);
|
||||
if (TarArchive.IsTarFile(testStream))
|
||||
{
|
||||
rewindableStream.Rewind(true);
|
||||
reader = new TarReader(rewindableStream, options, CompressionType.Lzw);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -177,6 +203,33 @@ public class TarFactory
|
||||
return false;
|
||||
}
|
||||
|
||||
private static IReader? TryOption(
|
||||
SharpCompressStream rewindableStream,
|
||||
ReaderOptions options,
|
||||
long pos,
|
||||
TestOption testOption
|
||||
)
|
||||
{
|
||||
if (testOption.CanHandle(rewindableStream))
|
||||
{
|
||||
((IStreamStack)rewindableStream).StackSeek(pos);
|
||||
var inStream = rewindableStream;
|
||||
if (testOption.WrapInSharpCompressStream)
|
||||
{
|
||||
inStream = SharpCompressStream.Create(rewindableStream, leaveOpen: true);
|
||||
}
|
||||
var testStream = testOption.CreateStream(rewindableStream);
|
||||
|
||||
if (TarArchive.IsTarFile(testStream))
|
||||
{
|
||||
((IStreamStack)rewindableStream).StackSeek(pos);
|
||||
return new TarReader(rewindableStream, options, testOption.Type);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IReader OpenReader(Stream stream, ReaderOptions? options) =>
|
||||
TarReader.Open(stream, options);
|
||||
|
||||
28
src/SharpCompress/Factories/ZStandardFactory.cs
Normal file
28
src/SharpCompress/Factories/ZStandardFactory.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Compressors.ZStandard;
|
||||
using SharpCompress.Readers;
|
||||
|
||||
namespace SharpCompress.Factories;
|
||||
|
||||
internal class ZStandardFactory : Factory
|
||||
{
|
||||
public override string Name => "ZStandard";
|
||||
|
||||
public override IEnumerable<string> GetSupportedExtensions()
|
||||
{
|
||||
yield return "zst";
|
||||
yield return "zstd";
|
||||
}
|
||||
|
||||
public override bool IsArchive(
|
||||
Stream stream,
|
||||
string? password = null,
|
||||
int bufferSize = 65536
|
||||
) => ZStandardStream.IsZStandard(stream);
|
||||
}
|
||||
@@ -3,6 +3,7 @@ using System.IO;
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Archives.Zip;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.IO;
|
||||
using SharpCompress.Readers;
|
||||
using SharpCompress.Readers.Zip;
|
||||
using SharpCompress.Writers;
|
||||
@@ -38,13 +39,22 @@ public class ZipFactory
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool IsArchive(Stream stream, string? password = null)
|
||||
public override bool IsArchive(
|
||||
Stream stream,
|
||||
string? password = null,
|
||||
int bufferSize = ReaderOptions.DefaultBufferSize
|
||||
)
|
||||
{
|
||||
var startPosition = stream.CanSeek ? stream.Position : -1;
|
||||
|
||||
// probe for single volume zip
|
||||
|
||||
if (ZipArchive.IsZipFile(stream, password))
|
||||
if (stream is not SharpCompressStream) // wrap to provide buffer bef
|
||||
{
|
||||
stream = new SharpCompressStream(stream, bufferSize: bufferSize);
|
||||
}
|
||||
|
||||
if (ZipArchive.IsZipFile(stream, password, bufferSize))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -59,11 +69,13 @@ public class ZipFactory
|
||||
stream.Position = startPosition;
|
||||
|
||||
//test the zip (last) file of a multipart zip
|
||||
if (ZipArchive.IsZipMulti(stream))
|
||||
if (ZipArchive.IsZipMulti(stream, password, bufferSize))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
stream.Position = startPosition;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,14 +3,39 @@ using System.IO;
|
||||
|
||||
namespace SharpCompress.IO;
|
||||
|
||||
internal class BufferedSubStream(Stream stream, long origin, long bytesToRead)
|
||||
: NonDisposingStream(stream, throwOnDispose: false)
|
||||
internal class BufferedSubStream : SharpCompressStream, IStreamStack
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
long IStreamStack.InstanceId { get; set; }
|
||||
#endif
|
||||
|
||||
Stream IStreamStack.BaseStream() => base.Stream;
|
||||
|
||||
public BufferedSubStream(Stream stream, long origin, long bytesToRead)
|
||||
: base(stream, leaveOpen: true, throwOnDispose: false)
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugConstruct(typeof(BufferedSubStream));
|
||||
#endif
|
||||
this.origin = origin;
|
||||
this.BytesLeftToRead = bytesToRead;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugDispose(typeof(BufferedSubStream));
|
||||
#endif
|
||||
if (disposing) { }
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
private int _cacheOffset;
|
||||
private int _cacheLength;
|
||||
private readonly byte[] _cache = new byte[32 << 10];
|
||||
private long origin;
|
||||
|
||||
private long BytesLeftToRead { get; set; } = bytesToRead;
|
||||
private long BytesLeftToRead { get; set; }
|
||||
|
||||
public override bool CanRead => true;
|
||||
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace SharpCompress.IO;
|
||||
|
||||
internal class CountingWritableSubStream : NonDisposingStream
|
||||
{
|
||||
internal CountingWritableSubStream(Stream stream)
|
||||
: base(stream, throwOnDispose: false) { }
|
||||
|
||||
public ulong Count { get; private set; }
|
||||
|
||||
public override bool CanRead => false;
|
||||
|
||||
public override bool CanSeek => false;
|
||||
|
||||
public override bool CanWrite => true;
|
||||
|
||||
public override void Flush() => Stream.Flush();
|
||||
|
||||
public override long Length => throw new NotSupportedException();
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get => throw new NotSupportedException();
|
||||
set => throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count) =>
|
||||
throw new NotSupportedException();
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();
|
||||
|
||||
public override void SetLength(long value) => throw new NotSupportedException();
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
Stream.Write(buffer, offset, count);
|
||||
Count += (uint)count;
|
||||
}
|
||||
|
||||
public override void WriteByte(byte value)
|
||||
{
|
||||
Stream.WriteByte(value);
|
||||
++Count;
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,28 @@ using System.IO;
|
||||
|
||||
namespace SharpCompress.IO;
|
||||
|
||||
public class DataDescriptorStream : Stream
|
||||
public class DataDescriptorStream : Stream, IStreamStack
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
long IStreamStack.InstanceId { get; set; }
|
||||
#endif
|
||||
int IStreamStack.DefaultBufferSize { get; set; }
|
||||
|
||||
Stream IStreamStack.BaseStream() => _stream;
|
||||
|
||||
int IStreamStack.BufferSize
|
||||
{
|
||||
get => 0;
|
||||
set { return; }
|
||||
}
|
||||
int IStreamStack.BufferPosition
|
||||
{
|
||||
get => 0;
|
||||
set { return; }
|
||||
}
|
||||
|
||||
void IStreamStack.SetPosition(long position) { }
|
||||
|
||||
private readonly Stream _stream;
|
||||
private long _start;
|
||||
private int _searchPosition;
|
||||
@@ -20,6 +40,10 @@ public class DataDescriptorStream : Stream
|
||||
_start = _stream.Position;
|
||||
_searchPosition = 0;
|
||||
_done = false;
|
||||
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugConstruct(typeof(DataDescriptorStream));
|
||||
#endif
|
||||
}
|
||||
|
||||
internal bool IsRecording { get; private set; }
|
||||
@@ -31,6 +55,9 @@ public class DataDescriptorStream : Stream
|
||||
return;
|
||||
}
|
||||
_isDisposed = true;
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugDispose(typeof(DataDescriptorStream));
|
||||
#endif
|
||||
base.Dispose(disposing);
|
||||
if (disposing)
|
||||
{
|
||||
|
||||
364
src/SharpCompress/IO/IStreamStack.cs
Normal file
364
src/SharpCompress/IO/IStreamStack.cs
Normal file
@@ -0,0 +1,364 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace SharpCompress.IO
|
||||
{
|
||||
public interface IStreamStack
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the default buffer size to be applied when buffering is enabled for this stream stack.
|
||||
/// This value is used by the SetBuffer extension method to configure buffering on the appropriate stream
|
||||
/// in the stack hierarchy. A value of 0 indicates no default buffer size is set.
|
||||
/// </summary>
|
||||
int DefaultBufferSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the immediate underlying stream in the stack.
|
||||
/// </summary>
|
||||
Stream BaseStream();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the size of the buffer if the stream supports buffering; otherwise, returns 0.
|
||||
/// This property must not throw.
|
||||
/// </summary>
|
||||
int BufferSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the current position within the buffer if the stream supports buffering; otherwise, returns 0.
|
||||
/// This property must not throw.
|
||||
/// </summary>
|
||||
int BufferPosition { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Updates the internal position state of the stream. This should not perform seeking on the underlying stream,
|
||||
/// but should update any internal position or buffer state as appropriate for the stream implementation.
|
||||
/// </summary>
|
||||
/// <param name="position">The absolute position to set within the stream stack.</param>
|
||||
void SetPosition(long position);
|
||||
|
||||
#if DEBUG_STREAMS
|
||||
/// <summary>
|
||||
/// Gets or sets the unique instance identifier for debugging purposes.
|
||||
/// </summary>
|
||||
long InstanceId { get; set; }
|
||||
#endif
|
||||
}
|
||||
|
||||
internal static class StackStreamExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the logical position of the first buffering stream in the stack, or 0 if none exist.
|
||||
/// </summary>
|
||||
/// <param name="stream">The most derived (outermost) stream in the stack.</param>
|
||||
/// <returns>The position of the first buffering stream, or 0 if not found.</returns>
|
||||
internal static long GetPosition(this IStreamStack stream)
|
||||
{
|
||||
IStreamStack? current = stream;
|
||||
|
||||
while (current != null)
|
||||
{
|
||||
if (current.BufferSize != 0 && current is Stream st)
|
||||
{
|
||||
return st.Position;
|
||||
}
|
||||
current = current?.BaseStream() as IStreamStack;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rewinds the buffer of the outermost buffering stream in the stack by the specified count, if supported.
|
||||
/// Only the most derived buffering stream is affected.
|
||||
/// </summary>
|
||||
/// <param name="stream">The most derived (outermost) stream in the stack.</param>
|
||||
/// <param name="count">The number of bytes to rewind within the buffer.</param>
|
||||
internal static void Rewind(this IStreamStack stream, int count)
|
||||
{
|
||||
Stream baseStream = stream.BaseStream();
|
||||
Stream thisStream = (Stream)stream;
|
||||
IStreamStack? buffStream = null;
|
||||
IStreamStack? current = stream;
|
||||
|
||||
while (buffStream == null && current != null)
|
||||
{
|
||||
if (current.BufferSize != 0)
|
||||
{
|
||||
buffStream = current;
|
||||
buffStream.BufferPosition -= Math.Min(buffStream.BufferPosition, count);
|
||||
}
|
||||
current = current?.BaseStream() as IStreamStack;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the buffer size on the first buffering stream in the stack, or on the outermost stream if none exist.
|
||||
/// If <paramref name="force"/> is true, sets the buffer size regardless of current value.
|
||||
/// </summary>
|
||||
/// <param name="stream">The most derived (outermost) stream in the stack.</param>
|
||||
/// <param name="bufferSize">The buffer size to set.</param>
|
||||
/// <param name="force">If true, forces the buffer size to be set even if already set.</param>
|
||||
internal static void SetBuffer(this IStreamStack stream, int bufferSize, bool force)
|
||||
{
|
||||
if (bufferSize == 0 || stream == null)
|
||||
return;
|
||||
|
||||
IStreamStack? current = stream;
|
||||
IStreamStack defaultBuffer = stream;
|
||||
IStreamStack? buffer = null;
|
||||
|
||||
// First pass: find the deepest IStreamStack
|
||||
while (current != null)
|
||||
{
|
||||
defaultBuffer = current;
|
||||
if (buffer == null && ((current.BufferSize != 0 && bufferSize != 0) || force))
|
||||
buffer = current;
|
||||
if (defaultBuffer.DefaultBufferSize != 0)
|
||||
break;
|
||||
current = current.BaseStream() as IStreamStack;
|
||||
}
|
||||
if (defaultBuffer.DefaultBufferSize == 0)
|
||||
defaultBuffer.DefaultBufferSize = bufferSize;
|
||||
(buffer ?? stream).BufferSize = bufferSize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to set the position in the stream stack. If a buffering stream is present and the position is within its buffer,
|
||||
/// BufferPosition is set on the outermost buffering stream and all intermediate streams update their internal state via SetPosition.
|
||||
/// If no buffering stream is present, seeks as close to the root stream as possible and updates all intermediate streams' state via SetPosition.
|
||||
/// Seeking is never performed if any intermediate stream in the stack is buffering.
|
||||
/// Throws if the position cannot be set.
|
||||
/// </summary>
|
||||
/// <param name="stream">
|
||||
/// The most derived (outermost) stream in the stack. The method traverses up the stack via BaseStream() until a stream can satisfy the buffer or seek request.
|
||||
/// </param>
|
||||
/// <param name="position">The absolute position to set.</param>
|
||||
/// <returns>The position that was set.</returns>
|
||||
internal static long StackSeek(this IStreamStack stream, long position)
|
||||
{
|
||||
var stack = new List<IStreamStack>();
|
||||
Stream? current = stream as Stream;
|
||||
int lastBufferingIndex = -1;
|
||||
int firstSeekableIndex = -1;
|
||||
Stream? firstSeekableStream = null;
|
||||
|
||||
// Traverse the stack, collecting info
|
||||
while (current is IStreamStack stackStream)
|
||||
{
|
||||
stack.Add(stackStream);
|
||||
if (stackStream.BufferSize > 0)
|
||||
{
|
||||
lastBufferingIndex = stack.Count - 1;
|
||||
break;
|
||||
}
|
||||
current = stackStream.BaseStream();
|
||||
}
|
||||
|
||||
// Find the first seekable stream (closest to the root)
|
||||
if (current != null && current.CanSeek)
|
||||
{
|
||||
firstSeekableIndex = stack.Count;
|
||||
firstSeekableStream = current;
|
||||
}
|
||||
|
||||
// If any buffering stream exists, try to set BufferPosition on the outermost one
|
||||
if (lastBufferingIndex != -1)
|
||||
{
|
||||
var bufferingStream = stack[lastBufferingIndex];
|
||||
if (position >= 0 && position < bufferingStream.BufferSize)
|
||||
{
|
||||
bufferingStream.BufferPosition = (int)position;
|
||||
return position;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If position is not in buffer, reset buffer and proceed as non-buffering
|
||||
bufferingStream.BufferPosition = 0;
|
||||
}
|
||||
// Continue to seek as if no buffer is present
|
||||
}
|
||||
|
||||
// If no buffering, or buffer was reset, seek at the first seekable stream (closest to the root)
|
||||
if (firstSeekableStream != null)
|
||||
{
|
||||
firstSeekableStream.Seek(position, SeekOrigin.Begin);
|
||||
return firstSeekableStream.Position;
|
||||
}
|
||||
|
||||
throw new NotSupportedException(
|
||||
"Cannot set position on this stream stack (no seekable or buffering stream supports the requested position)."
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads bytes from the stream, using the position to observe how much was actually consumed and rewind the buffer to ensure further reads are correct.
|
||||
/// This is required to prevent buffered reads from skipping data, while also benefiting from buffering and reduced stream IO reads.
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream to read from.</param>
|
||||
/// <param name="buffer">The buffer to read data into.</param>
|
||||
/// <param name="offset">The offset in the buffer to start writing data.</param>
|
||||
/// <param name="count">The maximum number of bytes to read.</param>
|
||||
/// <param name="buffStream">Returns the buffering stream found in the stack, or null if none exists.</param>
|
||||
/// <param name="baseReadCount">Returns the number of bytes actually read from the base stream, or -1 if no buffering stream was found.</param>
|
||||
/// <returns>The number of bytes read into the buffer.</returns>
|
||||
internal static int Read(
|
||||
this IStreamStack stream,
|
||||
byte[] buffer,
|
||||
int offset,
|
||||
int count,
|
||||
out IStreamStack? buffStream,
|
||||
out int baseReadCount
|
||||
)
|
||||
{
|
||||
Stream baseStream = stream.BaseStream();
|
||||
Stream thisStream = (Stream)stream;
|
||||
IStreamStack? current = stream;
|
||||
buffStream = null;
|
||||
baseReadCount = -1;
|
||||
|
||||
while (buffStream == null && (current = current?.BaseStream() as IStreamStack) != null)
|
||||
{
|
||||
if (current.BufferSize != 0)
|
||||
{
|
||||
buffStream = current;
|
||||
}
|
||||
}
|
||||
|
||||
long buffPos = buffStream == null ? -1 : ((Stream)buffStream).Position;
|
||||
|
||||
int read = baseStream.Read(buffer, offset, count); //amount read in to buffer
|
||||
|
||||
if (buffPos != -1)
|
||||
{
|
||||
baseReadCount = (int)(((Stream)buffStream!).Position - buffPos);
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
#if DEBUG_STREAMS
|
||||
private static long _instanceCounter = 0;
|
||||
|
||||
private static string cleansePos(long pos)
|
||||
{
|
||||
if (pos < 0)
|
||||
return "";
|
||||
return "Px" + pos.ToString("x");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or creates a unique instance ID for the stream stack for debugging purposes.
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream stack.</param>
|
||||
/// <param name="instanceId">Reference to the instance ID field.</param>
|
||||
/// <param name="construct">Whether this is being called during construction.</param>
|
||||
/// <returns>The instance ID.</returns>
|
||||
public static long GetInstanceId(
|
||||
this IStreamStack stream,
|
||||
ref long instanceId,
|
||||
bool construct
|
||||
)
|
||||
{
|
||||
if (instanceId == 0) //will not be equal to 0 when inherited IStackStream types are being used
|
||||
instanceId = System.Threading.Interlocked.Increment(ref _instanceCounter);
|
||||
return instanceId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a debug message for stream construction.
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream stack.</param>
|
||||
/// <param name="constructing">The type being constructed.</param>
|
||||
public static void DebugConstruct(this IStreamStack stream, Type constructing)
|
||||
{
|
||||
long id = stream.InstanceId;
|
||||
stream.InstanceId = GetInstanceId(stream, ref id, true);
|
||||
var frame = (new StackTrace()).GetFrame(3);
|
||||
string parentInfo =
|
||||
frame != null
|
||||
? $"{frame.GetMethod()?.DeclaringType?.Name}.{frame.GetMethod()?.Name}()"
|
||||
: "Unknown";
|
||||
if (constructing.FullName == stream.GetType().FullName) //don't debug base IStackStream types
|
||||
Debug.WriteLine(
|
||||
$"{GetStreamStackString(stream, true)} : Constructed by [{parentInfo}]"
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a debug message for stream disposal.
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream stack.</param>
|
||||
/// <param name="constructing">The type being disposed.</param>
|
||||
public static void DebugDispose(this IStreamStack stream, Type constructing)
|
||||
{
|
||||
var frame = (new StackTrace()).GetFrame(3);
|
||||
string parentInfo =
|
||||
frame != null
|
||||
? $"{frame.GetMethod()?.DeclaringType?.Name}.{frame.GetMethod()?.Name}()"
|
||||
: "Unknown";
|
||||
if (constructing.FullName == stream.GetType().FullName) //don't debug base IStackStream types
|
||||
Debug.WriteLine($"{GetStreamStackString(stream, false)} : Disposed by [{parentInfo}]");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a debug trace message for the stream.
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream stack.</param>
|
||||
/// <param name="message">The debug message to write.</param>
|
||||
public static void DebugTrace(this IStreamStack stream, string message)
|
||||
{
|
||||
Debug.WriteLine(
|
||||
$"{GetStreamStackString(stream, false)} : [{stream.GetType().Name}]{message}"
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the full stream chain as a string, including instance IDs and positions.
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream stack to represent.</param>
|
||||
/// <param name="construct">Whether this is being called during construction.</param>
|
||||
/// <returns>A string representation of the entire stream stack.</returns>
|
||||
public static string GetStreamStackString(this IStreamStack stream, bool construct)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
Stream? current = stream as Stream;
|
||||
while (current != null)
|
||||
{
|
||||
IStreamStack? sStack = current as IStreamStack;
|
||||
string id = sStack != null ? "#" + sStack.InstanceId.ToString() : "";
|
||||
string buffSize = sStack != null ? "Bx" + sStack.BufferSize.ToString("x") : "";
|
||||
string defBuffSize =
|
||||
sStack != null ? "Dx" + sStack.DefaultBufferSize.ToString("x") : "";
|
||||
|
||||
if (sb.Length > 0)
|
||||
sb.Insert(0, "/");
|
||||
try
|
||||
{
|
||||
sb.Insert(
|
||||
0,
|
||||
$"{current.GetType().Name}{id}[{cleansePos(current.Position)}:{buffSize}:{defBuffSize}]"
|
||||
);
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (current is SharpCompressStream scs)
|
||||
sb.Insert(
|
||||
0,
|
||||
$"{current.GetType().Name}{id}[{cleansePos(scs.InternalPosition)}:{buffSize}:{defBuffSize}]"
|
||||
);
|
||||
else
|
||||
sb.Insert(0, $"{current.GetType().Name}{id}[:{buffSize}]");
|
||||
}
|
||||
if (sStack != null)
|
||||
current = sStack.BaseStream(); //current may not be a IStreamStack, allow one more loop
|
||||
else
|
||||
break;
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,30 @@
|
||||
using System.IO;
|
||||
using System.IO;
|
||||
using SharpCompress.Common;
|
||||
|
||||
namespace SharpCompress.IO;
|
||||
|
||||
internal class ListeningStream : Stream
|
||||
internal class ListeningStream : Stream, IStreamStack
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
long IStreamStack.InstanceId { get; set; }
|
||||
#endif
|
||||
int IStreamStack.DefaultBufferSize { get; set; }
|
||||
|
||||
Stream IStreamStack.BaseStream() => Stream;
|
||||
|
||||
int IStreamStack.BufferSize
|
||||
{
|
||||
get => 0;
|
||||
set { return; }
|
||||
}
|
||||
int IStreamStack.BufferPosition
|
||||
{
|
||||
get => 0;
|
||||
set { return; }
|
||||
}
|
||||
|
||||
void IStreamStack.SetPosition(long position) { }
|
||||
|
||||
private long _currentEntryTotalReadBytes;
|
||||
private readonly IExtractionListener _listener;
|
||||
|
||||
@@ -12,10 +32,16 @@ internal class ListeningStream : Stream
|
||||
{
|
||||
Stream = stream;
|
||||
this._listener = listener;
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugConstruct(typeof(ListeningStream));
|
||||
#endif
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugDispose(typeof(ListeningStream));
|
||||
#endif
|
||||
if (disposing)
|
||||
{
|
||||
Stream.Dispose();
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace SharpCompress.IO;
|
||||
|
||||
public class NonDisposingStream : Stream
|
||||
{
|
||||
public static NonDisposingStream Create(Stream stream, bool throwOnDispose = false)
|
||||
{
|
||||
if (
|
||||
stream is NonDisposingStream nonDisposingStream
|
||||
&& nonDisposingStream.ThrowOnDispose == throwOnDispose
|
||||
)
|
||||
{
|
||||
return nonDisposingStream;
|
||||
}
|
||||
return new NonDisposingStream(stream, throwOnDispose);
|
||||
}
|
||||
|
||||
protected NonDisposingStream(Stream stream, bool throwOnDispose = false)
|
||||
{
|
||||
Stream = stream;
|
||||
ThrowOnDispose = throwOnDispose;
|
||||
}
|
||||
|
||||
public bool ThrowOnDispose { get; set; }
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (ThrowOnDispose)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Attempt to dispose of a {nameof(NonDisposingStream)} when {nameof(ThrowOnDispose)} is {ThrowOnDispose}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected Stream Stream { get; }
|
||||
|
||||
public override bool CanRead => Stream.CanRead;
|
||||
|
||||
public override bool CanSeek => Stream.CanSeek;
|
||||
|
||||
public override bool CanWrite => Stream.CanWrite;
|
||||
|
||||
public override void Flush() => Stream.Flush();
|
||||
|
||||
public override long Length => Stream.Length;
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get => Stream.Position;
|
||||
set => Stream.Position = value;
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count) =>
|
||||
Stream.Read(buffer, offset, count);
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin) => Stream.Seek(offset, origin);
|
||||
|
||||
public override void SetLength(long value) => Stream.SetLength(value);
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count) =>
|
||||
Stream.Write(buffer, offset, count);
|
||||
|
||||
#if !NETFRAMEWORK && !NETSTANDARD2_0
|
||||
|
||||
public override int Read(Span<byte> buffer) => Stream.Read(buffer);
|
||||
|
||||
public override void Write(ReadOnlySpan<byte> buffer) => Stream.Write(buffer);
|
||||
|
||||
#endif
|
||||
}
|
||||
@@ -1,17 +1,24 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
||||
namespace SharpCompress.IO;
|
||||
|
||||
internal class ReadOnlySubStream : NonDisposingStream
|
||||
internal class ReadOnlySubStream : SharpCompressStream, IStreamStack
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
long IStreamStack.InstanceId { get; set; }
|
||||
#endif
|
||||
|
||||
Stream IStreamStack.BaseStream() => base.Stream;
|
||||
|
||||
private long _position;
|
||||
|
||||
public ReadOnlySubStream(Stream stream, long bytesToRead)
|
||||
: this(stream, null, bytesToRead) { }
|
||||
|
||||
public ReadOnlySubStream(Stream stream, long? origin, long bytesToRead)
|
||||
: base(stream, throwOnDispose: false)
|
||||
: base(stream, leaveOpen: true, throwOnDispose: false)
|
||||
{
|
||||
if (origin != null && stream.Position != origin.Value)
|
||||
{
|
||||
@@ -19,6 +26,9 @@ internal class ReadOnlySubStream : NonDisposingStream
|
||||
}
|
||||
BytesLeftToRead = bytesToRead;
|
||||
_position = 0;
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugConstruct(typeof(ReadOnlySubStream));
|
||||
#endif
|
||||
}
|
||||
|
||||
private long BytesLeftToRead { get; set; }
|
||||
@@ -89,4 +99,12 @@ internal class ReadOnlySubStream : NonDisposingStream
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count) =>
|
||||
throw new NotSupportedException();
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugDispose(typeof(ReadOnlySubStream));
|
||||
#endif
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,149 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace SharpCompress.IO;
|
||||
|
||||
public class RewindableStream : Stream
|
||||
{
|
||||
private readonly Stream _stream;
|
||||
private MemoryStream _bufferStream = new();
|
||||
private bool _isRewound;
|
||||
private bool _isDisposed;
|
||||
|
||||
public RewindableStream(Stream stream) => this._stream = stream;
|
||||
|
||||
internal bool IsRecording { get; private set; }
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (_isDisposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_isDisposed = true;
|
||||
base.Dispose(disposing);
|
||||
if (disposing)
|
||||
{
|
||||
_stream.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public void Rewind(bool stopRecording)
|
||||
{
|
||||
_isRewound = true;
|
||||
IsRecording = !stopRecording;
|
||||
_bufferStream.Position = 0;
|
||||
}
|
||||
|
||||
public void Rewind(MemoryStream buffer)
|
||||
{
|
||||
if (_bufferStream.Position >= buffer.Length)
|
||||
{
|
||||
_bufferStream.Position -= buffer.Length;
|
||||
}
|
||||
else
|
||||
{
|
||||
_bufferStream.TransferTo(buffer);
|
||||
//create new memorystream to allow proper resizing as memorystream could be a user provided buffer
|
||||
//https://github.com/adamhathcock/sharpcompress/issues/306
|
||||
_bufferStream = new MemoryStream();
|
||||
buffer.Position = 0;
|
||||
buffer.TransferTo(_bufferStream);
|
||||
_bufferStream.Position = 0;
|
||||
}
|
||||
_isRewound = true;
|
||||
}
|
||||
|
||||
public void StartRecording()
|
||||
{
|
||||
//if (isRewound && bufferStream.Position != 0)
|
||||
// throw new System.NotImplementedException();
|
||||
if (_bufferStream.Position != 0)
|
||||
{
|
||||
var data = _bufferStream.ToArray();
|
||||
var position = _bufferStream.Position;
|
||||
_bufferStream.SetLength(0);
|
||||
_bufferStream.Write(data, (int)position, data.Length - (int)position);
|
||||
_bufferStream.Position = 0;
|
||||
}
|
||||
IsRecording = true;
|
||||
}
|
||||
|
||||
public override bool CanRead => true;
|
||||
|
||||
public override bool CanSeek => _stream.CanSeek;
|
||||
|
||||
public override bool CanWrite => false;
|
||||
|
||||
public override void Flush() { }
|
||||
|
||||
public override long Length => _stream.Length;
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get => _stream.Position + _bufferStream.Position - _bufferStream.Length;
|
||||
set
|
||||
{
|
||||
if (!_isRewound)
|
||||
{
|
||||
_stream.Position = value;
|
||||
}
|
||||
else if (value < _stream.Position - _bufferStream.Length || value >= _stream.Position)
|
||||
{
|
||||
_stream.Position = value;
|
||||
_isRewound = false;
|
||||
_bufferStream.SetLength(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
_bufferStream.Position = value - _stream.Position + _bufferStream.Length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
//don't actually read if we don't really want to read anything
|
||||
//currently a network stream bug on Windows for .NET Core
|
||||
if (count == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
int read;
|
||||
if (_isRewound && _bufferStream.Position != _bufferStream.Length)
|
||||
{
|
||||
// don't read more than left
|
||||
var readCount = Math.Min(count, (int)(_bufferStream.Length - _bufferStream.Position));
|
||||
read = _bufferStream.Read(buffer, offset, readCount);
|
||||
if (read < readCount)
|
||||
{
|
||||
var tempRead = _stream.Read(buffer, offset + read, count - read);
|
||||
if (IsRecording)
|
||||
{
|
||||
_bufferStream.Write(buffer, offset + read, tempRead);
|
||||
}
|
||||
read += tempRead;
|
||||
}
|
||||
if (_bufferStream.Position == _bufferStream.Length && !IsRecording)
|
||||
{
|
||||
_isRewound = false;
|
||||
_bufferStream.SetLength(0);
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
read = _stream.Read(buffer, offset, count);
|
||||
if (IsRecording)
|
||||
{
|
||||
_bufferStream.Write(buffer, offset, read);
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();
|
||||
|
||||
public override void SetLength(long value) => throw new NotSupportedException();
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count) =>
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
335
src/SharpCompress/IO/SharpCompressStream.cs
Normal file
335
src/SharpCompress/IO/SharpCompressStream.cs
Normal file
@@ -0,0 +1,335 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace SharpCompress.IO;
|
||||
|
||||
public class SharpCompressStream : Stream, IStreamStack
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
long IStreamStack.InstanceId { get; set; }
|
||||
#endif
|
||||
int IStreamStack.DefaultBufferSize { get; set; }
|
||||
|
||||
Stream IStreamStack.BaseStream() => Stream;
|
||||
|
||||
// Buffering fields
|
||||
private int _bufferSize;
|
||||
private byte[]? _buffer;
|
||||
private int _bufferPosition;
|
||||
private int _bufferedLength;
|
||||
private bool _bufferingEnabled;
|
||||
private long _baseInitialPos;
|
||||
|
||||
private void ValidateBufferState()
|
||||
{
|
||||
if (_bufferPosition < 0 || _bufferPosition > _bufferedLength)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Buffer state is inconsistent: _bufferPosition is out of range."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
int IStreamStack.BufferSize
|
||||
{
|
||||
get => _bufferingEnabled ? _bufferSize : 0;
|
||||
set //need to adjust an already existing buffer
|
||||
{
|
||||
if (_bufferSize != value)
|
||||
{
|
||||
_bufferSize = value;
|
||||
_bufferingEnabled = _bufferSize > 0;
|
||||
if (_bufferingEnabled)
|
||||
{
|
||||
_buffer = new byte[_bufferSize];
|
||||
_bufferPosition = 0;
|
||||
_bufferedLength = 0;
|
||||
if (_bufferingEnabled)
|
||||
{
|
||||
ValidateBufferState(); // Add here
|
||||
}
|
||||
try
|
||||
{
|
||||
_internalPosition = Stream.Position;
|
||||
}
|
||||
catch
|
||||
{
|
||||
_internalPosition = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int IStreamStack.BufferPosition
|
||||
{
|
||||
get => _bufferingEnabled ? _bufferPosition : 0;
|
||||
set
|
||||
{
|
||||
if (_bufferingEnabled)
|
||||
{
|
||||
if (value < 0 || value > _bufferedLength)
|
||||
throw new ArgumentOutOfRangeException(nameof(value));
|
||||
_internalPosition = value;
|
||||
_bufferPosition = value;
|
||||
ValidateBufferState(); // Add here
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IStreamStack.SetPosition(long position) { }
|
||||
|
||||
public Stream Stream { get; }
|
||||
|
||||
//private MemoryStream _bufferStream = new();
|
||||
|
||||
private bool _readOnly; //some archive detection requires seek to be disabled to cause it to exception to try the next arc type
|
||||
|
||||
//private bool _isRewound;
|
||||
private bool _isDisposed;
|
||||
private long _internalPosition = 0;
|
||||
|
||||
public bool ThrowOnDispose { get; set; }
|
||||
public bool LeaveOpen { get; set; }
|
||||
|
||||
public long InternalPosition => _internalPosition;
|
||||
|
||||
public static SharpCompressStream Create(
|
||||
Stream stream,
|
||||
bool leaveOpen = false,
|
||||
bool throwOnDispose = false,
|
||||
int bufferSize = 0,
|
||||
bool forceBuffer = false
|
||||
)
|
||||
{
|
||||
if (
|
||||
stream is SharpCompressStream sc
|
||||
&& sc.LeaveOpen == leaveOpen
|
||||
&& sc.ThrowOnDispose == throwOnDispose
|
||||
)
|
||||
{
|
||||
if (bufferSize != 0)
|
||||
((IStreamStack)stream).SetBuffer(bufferSize, forceBuffer);
|
||||
return sc;
|
||||
}
|
||||
return new SharpCompressStream(stream, leaveOpen, throwOnDispose, bufferSize, forceBuffer);
|
||||
}
|
||||
|
||||
public SharpCompressStream(
|
||||
Stream stream,
|
||||
bool leaveOpen = false,
|
||||
bool throwOnDispose = false,
|
||||
int bufferSize = 0,
|
||||
bool forceBuffer = false
|
||||
)
|
||||
{
|
||||
Stream = stream;
|
||||
this.LeaveOpen = leaveOpen;
|
||||
this.ThrowOnDispose = throwOnDispose;
|
||||
_readOnly = !Stream.CanSeek;
|
||||
|
||||
((IStreamStack)this).SetBuffer(bufferSize, forceBuffer);
|
||||
try
|
||||
{
|
||||
_baseInitialPos = stream.Position;
|
||||
}
|
||||
catch
|
||||
{
|
||||
_baseInitialPos = 0;
|
||||
}
|
||||
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugConstruct(typeof(SharpCompressStream));
|
||||
#endif
|
||||
}
|
||||
|
||||
internal bool IsRecording { get; private set; }
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugDispose(typeof(SharpCompressStream));
|
||||
#endif
|
||||
if (_isDisposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_isDisposed = true;
|
||||
base.Dispose(disposing);
|
||||
|
||||
if (this.LeaveOpen)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (ThrowOnDispose)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Attempt to dispose of a {nameof(SharpCompressStream)} when {nameof(ThrowOnDispose)} is {ThrowOnDispose}"
|
||||
);
|
||||
}
|
||||
if (disposing)
|
||||
{
|
||||
Stream.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanRead => Stream.CanRead;
|
||||
|
||||
public override bool CanSeek => !_readOnly && Stream.CanSeek;
|
||||
|
||||
public override bool CanWrite => !_readOnly && Stream.CanWrite;
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
Stream.Flush();
|
||||
}
|
||||
|
||||
public override long Length
|
||||
{
|
||||
get { return Stream.Length; }
|
||||
}
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get
|
||||
{
|
||||
long pos = _internalPosition; // Stream.Position + _bufferStream.Position - _bufferStream.Length;
|
||||
return pos;
|
||||
}
|
||||
set { Seek(value, SeekOrigin.Begin); }
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (count == 0)
|
||||
return 0;
|
||||
|
||||
if (_bufferingEnabled)
|
||||
{
|
||||
ValidateBufferState();
|
||||
|
||||
// Fill buffer if needed
|
||||
if (_bufferedLength == 0)
|
||||
{
|
||||
_bufferedLength = Stream.Read(_buffer!, 0, _bufferSize);
|
||||
_bufferPosition = 0;
|
||||
}
|
||||
int available = _bufferedLength - _bufferPosition;
|
||||
int toRead = Math.Min(count, available);
|
||||
if (toRead > 0)
|
||||
{
|
||||
Array.Copy(_buffer!, _bufferPosition, buffer, offset, toRead);
|
||||
_bufferPosition += toRead;
|
||||
_internalPosition += toRead;
|
||||
return toRead;
|
||||
}
|
||||
// If buffer exhausted, refill
|
||||
int r = Stream.Read(_buffer!, 0, _bufferSize);
|
||||
if (r == 0)
|
||||
return 0;
|
||||
_bufferedLength = r;
|
||||
_bufferPosition = 0;
|
||||
if (_bufferedLength == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
toRead = Math.Min(count, _bufferedLength);
|
||||
Array.Copy(_buffer!, 0, buffer, offset, toRead);
|
||||
_bufferPosition = toRead;
|
||||
_internalPosition += toRead;
|
||||
return toRead;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (count == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
int read;
|
||||
read = Stream.Read(buffer, offset, count);
|
||||
_internalPosition += read;
|
||||
return read;
|
||||
}
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
if (_bufferingEnabled)
|
||||
{
|
||||
ValidateBufferState();
|
||||
}
|
||||
|
||||
long orig = _internalPosition;
|
||||
long targetPos;
|
||||
// Calculate the absolute target position based on origin
|
||||
switch (origin)
|
||||
{
|
||||
case SeekOrigin.Begin:
|
||||
targetPos = offset;
|
||||
break;
|
||||
case SeekOrigin.Current:
|
||||
targetPos = _internalPosition + offset;
|
||||
break;
|
||||
case SeekOrigin.End:
|
||||
targetPos = this.Length + offset;
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(origin), origin, null);
|
||||
}
|
||||
|
||||
long bufferPos = _internalPosition - _bufferPosition;
|
||||
|
||||
if (targetPos >= bufferPos && targetPos < bufferPos + _bufferedLength)
|
||||
{
|
||||
_bufferPosition = (int)(targetPos - bufferPos); //repoint within the buffer
|
||||
_internalPosition = targetPos;
|
||||
}
|
||||
else
|
||||
{
|
||||
long newStreamPos =
|
||||
Stream.Seek(targetPos + _baseInitialPos, SeekOrigin.Begin) - _baseInitialPos;
|
||||
_internalPosition = newStreamPos;
|
||||
_bufferPosition = 0;
|
||||
_bufferedLength = 0;
|
||||
}
|
||||
|
||||
return _internalPosition;
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override void WriteByte(byte value)
|
||||
{
|
||||
Stream.WriteByte(value);
|
||||
++_internalPosition;
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
Stream.Write(buffer, offset, count);
|
||||
_internalPosition += count;
|
||||
}
|
||||
|
||||
#if !NETFRAMEWORK && !NETSTANDARD2_0
|
||||
|
||||
//public override int Read(Span<byte> buffer)
|
||||
//{
|
||||
// int bytesRead = Stream.Read(buffer);
|
||||
// _internalPosition += bytesRead;
|
||||
// return bytesRead;
|
||||
//}
|
||||
|
||||
// public override void Write(ReadOnlySpan<byte> buffer)
|
||||
// {
|
||||
// Stream.Write(buffer);
|
||||
// _internalPosition += buffer.Length;
|
||||
// }
|
||||
|
||||
#endif
|
||||
}
|
||||
@@ -6,8 +6,28 @@ using SharpCompress.Readers;
|
||||
|
||||
namespace SharpCompress.IO;
|
||||
|
||||
public class SourceStream : Stream
|
||||
public class SourceStream : Stream, IStreamStack
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
long IStreamStack.InstanceId { get; set; }
|
||||
#endif
|
||||
int IStreamStack.DefaultBufferSize { get; set; }
|
||||
|
||||
Stream IStreamStack.BaseStream() => _streams[_stream];
|
||||
|
||||
int IStreamStack.BufferSize
|
||||
{
|
||||
get => 0;
|
||||
set { return; }
|
||||
}
|
||||
int IStreamStack.BufferPosition
|
||||
{
|
||||
get => 0;
|
||||
set { return; }
|
||||
}
|
||||
|
||||
void IStreamStack.SetPosition(long position) { }
|
||||
|
||||
private long _prevSize;
|
||||
private readonly List<FileInfo> _files;
|
||||
private readonly List<Stream> _streams;
|
||||
@@ -54,6 +74,10 @@ public class SourceStream : Stream
|
||||
}
|
||||
_stream = 0;
|
||||
_prevSize = 0;
|
||||
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugConstruct(typeof(SourceStream));
|
||||
#endif
|
||||
}
|
||||
|
||||
public void LoadAllParts()
|
||||
@@ -236,6 +260,9 @@ public class SourceStream : Stream
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
#if DEBUG_STREAMS
|
||||
this.DebugDispose(typeof(SourceStream));
|
||||
#endif
|
||||
Close();
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Factories;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Readers;
|
||||
@@ -18,19 +20,45 @@ public static class ReaderFactory
|
||||
stream.CheckNotNull(nameof(stream));
|
||||
options ??= new ReaderOptions() { LeaveStreamOpen = false };
|
||||
|
||||
var rewindableStream = new RewindableStream(stream);
|
||||
rewindableStream.StartRecording();
|
||||
var bStream = new SharpCompressStream(stream, bufferSize: options.BufferSize);
|
||||
|
||||
foreach (var factory in Factories.Factory.Factories.OfType<Factories.Factory>())
|
||||
long pos = ((IStreamStack)bStream).GetPosition();
|
||||
|
||||
var factories = Factories.Factory.Factories.OfType<Factories.Factory>();
|
||||
|
||||
Factory? testedFactory = null;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(options.ExtensionHint))
|
||||
{
|
||||
if (factory.TryOpenReader(rewindableStream, options, out var reader) && reader != null)
|
||||
testedFactory = factories.FirstOrDefault(a =>
|
||||
a.GetSupportedExtensions()
|
||||
.Contains(options.ExtensionHint, StringComparer.CurrentCultureIgnoreCase)
|
||||
);
|
||||
if (
|
||||
testedFactory?.TryOpenReader(bStream, options, out var reader) == true
|
||||
&& reader != null
|
||||
)
|
||||
{
|
||||
return reader;
|
||||
}
|
||||
((IStreamStack)bStream).StackSeek(pos);
|
||||
}
|
||||
|
||||
foreach (var factory in factories)
|
||||
{
|
||||
if (testedFactory == factory)
|
||||
{
|
||||
continue; // Already tested above
|
||||
}
|
||||
((IStreamStack)bStream).StackSeek(pos);
|
||||
if (factory.TryOpenReader(bStream, options, out var reader) && reader != null)
|
||||
{
|
||||
return reader;
|
||||
}
|
||||
}
|
||||
|
||||
throw new InvalidFormatException(
|
||||
"Cannot determine compressed stream type. Supported Reader Formats: Arc, Zip, GZip, BZip2, Tar, Rar, LZip, XZ"
|
||||
"Cannot determine compressed stream type. Supported Reader Formats: Arc, Zip, GZip, BZip2, Tar, Rar, LZip, XZ, ZStandard"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ namespace SharpCompress.Readers;
|
||||
|
||||
public class ReaderOptions : OptionsBase
|
||||
{
|
||||
public const int DefaultBufferSize = 0x10000;
|
||||
|
||||
/// <summary>
|
||||
/// Look for RarArchive (Check for self-extracting archives or cases where RarArchive isn't at the start of the file)
|
||||
/// </summary>
|
||||
@@ -12,4 +14,11 @@ public class ReaderOptions : OptionsBase
|
||||
public string? Password { get; set; }
|
||||
|
||||
public bool DisableCheckIncomplete { get; set; }
|
||||
|
||||
public int BufferSize { get; set; } = DefaultBufferSize;
|
||||
|
||||
/// <summary>
|
||||
/// Provide a hint for the extension of the archive being read, can speed up finding the correct decoder. Should be without the leading period in the form like: tar.gz or zip
|
||||
/// </summary>
|
||||
public string? ExtensionHint { get; set; }
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ using SharpCompress.Compressors.Deflate;
|
||||
using SharpCompress.Compressors.LZMA;
|
||||
using SharpCompress.Compressors.Lzw;
|
||||
using SharpCompress.Compressors.Xz;
|
||||
using SharpCompress.Compressors.ZStandard;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Readers.Tar;
|
||||
@@ -35,6 +36,7 @@ public class TarReader : AbstractReader<TarEntry, TarVolume>
|
||||
{
|
||||
CompressionType.BZip2 => new BZip2Stream(stream, CompressionMode.Decompress, false),
|
||||
CompressionType.GZip => new GZipStream(stream, CompressionMode.Decompress),
|
||||
CompressionType.ZStandard => new ZStandardStream(stream),
|
||||
CompressionType.LZip => new LZipStream(stream, CompressionMode.Decompress),
|
||||
CompressionType.Xz => new XZStream(stream),
|
||||
CompressionType.Lzw => new LzwStream(stream),
|
||||
@@ -55,46 +57,61 @@ public class TarReader : AbstractReader<TarEntry, TarVolume>
|
||||
{
|
||||
stream.CheckNotNull(nameof(stream));
|
||||
options = options ?? new ReaderOptions();
|
||||
var rewindableStream = new RewindableStream(stream);
|
||||
rewindableStream.StartRecording();
|
||||
var rewindableStream = new SharpCompressStream(stream);
|
||||
|
||||
long pos = ((IStreamStack)rewindableStream).GetPosition();
|
||||
|
||||
if (GZipArchive.IsGZipFile(rewindableStream))
|
||||
{
|
||||
rewindableStream.Rewind(false);
|
||||
((IStreamStack)rewindableStream).StackSeek(pos);
|
||||
var testStream = new GZipStream(rewindableStream, CompressionMode.Decompress);
|
||||
if (TarArchive.IsTarFile(testStream))
|
||||
{
|
||||
rewindableStream.Rewind(true);
|
||||
((IStreamStack)rewindableStream).StackSeek(pos);
|
||||
return new TarReader(rewindableStream, options, CompressionType.GZip);
|
||||
}
|
||||
throw new InvalidFormatException("Not a tar file.");
|
||||
}
|
||||
|
||||
rewindableStream.Rewind(false);
|
||||
((IStreamStack)rewindableStream).StackSeek(pos);
|
||||
if (BZip2Stream.IsBZip2(rewindableStream))
|
||||
{
|
||||
rewindableStream.Rewind(false);
|
||||
((IStreamStack)rewindableStream).StackSeek(pos);
|
||||
var testStream = new BZip2Stream(rewindableStream, CompressionMode.Decompress, false);
|
||||
if (TarArchive.IsTarFile(testStream))
|
||||
{
|
||||
rewindableStream.Rewind(true);
|
||||
((IStreamStack)rewindableStream).StackSeek(pos);
|
||||
return new TarReader(rewindableStream, options, CompressionType.BZip2);
|
||||
}
|
||||
throw new InvalidFormatException("Not a tar file.");
|
||||
}
|
||||
|
||||
rewindableStream.Rewind(false);
|
||||
((IStreamStack)rewindableStream).StackSeek(pos);
|
||||
if (ZStandardStream.IsZStandard(rewindableStream))
|
||||
{
|
||||
((IStreamStack)rewindableStream).StackSeek(pos);
|
||||
var testStream = new ZStandardStream(rewindableStream);
|
||||
if (TarArchive.IsTarFile(testStream))
|
||||
{
|
||||
((IStreamStack)rewindableStream).StackSeek(pos);
|
||||
return new TarReader(rewindableStream, options, CompressionType.ZStandard);
|
||||
}
|
||||
throw new InvalidFormatException("Not a tar file.");
|
||||
}
|
||||
((IStreamStack)rewindableStream).StackSeek(pos);
|
||||
if (LZipStream.IsLZipFile(rewindableStream))
|
||||
{
|
||||
rewindableStream.Rewind(false);
|
||||
((IStreamStack)rewindableStream).StackSeek(pos);
|
||||
var testStream = new LZipStream(rewindableStream, CompressionMode.Decompress);
|
||||
if (TarArchive.IsTarFile(testStream))
|
||||
{
|
||||
rewindableStream.Rewind(true);
|
||||
((IStreamStack)rewindableStream).StackSeek(pos);
|
||||
return new TarReader(rewindableStream, options, CompressionType.LZip);
|
||||
}
|
||||
throw new InvalidFormatException("Not a tar file.");
|
||||
}
|
||||
rewindableStream.Rewind(true);
|
||||
|
||||
((IStreamStack)rewindableStream).StackSeek(pos);
|
||||
return new TarReader(rewindableStream, options, CompressionType.None);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<AssemblyTitle>SharpCompress - Pure C# Decompression/Compression</AssemblyTitle>
|
||||
<NeutralLanguage>en-US</NeutralLanguage>
|
||||
<VersionPrefix>0.39.0</VersionPrefix>
|
||||
<AssemblyVersion>0.39.0</AssemblyVersion>
|
||||
<FileVersion>0.39.0</FileVersion>
|
||||
<VersionPrefix>0.41.0</VersionPrefix>
|
||||
<AssemblyVersion>0.41.0</AssemblyVersion>
|
||||
<FileVersion>0.41.0</FileVersion>
|
||||
<Authors>Adam Hathcock</Authors>
|
||||
<TargetFrameworks>net48;net481;netstandard2.0;net6.0;net8.0</TargetFrameworks>
|
||||
<AssemblyName>SharpCompress</AssemblyName>
|
||||
@@ -20,7 +20,7 @@
|
||||
<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>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
<DebugType>embedded</DebugType>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
@@ -31,9 +31,14 @@
|
||||
<PropertyGroup Condition=" '$(TargetFramework)' == 'net8.0' ">
|
||||
<IsTrimmable>true</IsTrimmable>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net8.0|AnyCPU'">
|
||||
<DefineConstants>$(DefineConstants);DEBUG_STREAMS</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Buffers" />
|
||||
<PackageReference Include="ZstdSharp.Port" />
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" PrivateAssets="All" />
|
||||
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
|
||||
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" />
|
||||
|
||||
@@ -16,13 +16,13 @@ public sealed class GZipWriter : AbstractWriter
|
||||
{
|
||||
if (WriterOptions.LeaveStreamOpen)
|
||||
{
|
||||
destination = NonDisposingStream.Create(destination);
|
||||
destination = SharpCompressStream.Create(destination, leaveOpen: true);
|
||||
}
|
||||
InitializeStream(
|
||||
new GZipStream(
|
||||
destination,
|
||||
CompressionMode.Compress,
|
||||
options?.CompressionLevel ?? CompressionLevel.Default,
|
||||
(CompressionLevel)(options?.CompressionLevel ?? (int)CompressionLevel.Default),
|
||||
WriterOptions.ArchiveEncoding.GetEncoding()
|
||||
)
|
||||
);
|
||||
|
||||
@@ -1,24 +1,18 @@
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Compressors.Deflate;
|
||||
using D = SharpCompress.Compressors.Deflate;
|
||||
|
||||
namespace SharpCompress.Writers.GZip;
|
||||
|
||||
public class GZipWriterOptions : WriterOptions
|
||||
{
|
||||
public GZipWriterOptions()
|
||||
: base(CompressionType.GZip) { }
|
||||
: base(CompressionType.GZip, (int)(D.CompressionLevel.Default)) { }
|
||||
|
||||
internal GZipWriterOptions(WriterOptions options)
|
||||
: base(options.CompressionType)
|
||||
: base(options.CompressionType, (int)(D.CompressionLevel.Default))
|
||||
{
|
||||
LeaveStreamOpen = options.LeaveStreamOpen;
|
||||
ArchiveEncoding = options.ArchiveEncoding;
|
||||
|
||||
if (options is GZipWriterOptions writerOptions)
|
||||
{
|
||||
CompressionLevel = writerOptions.CompressionLevel;
|
||||
}
|
||||
CompressionLevel = options.CompressionLevel;
|
||||
}
|
||||
|
||||
public CompressionLevel CompressionLevel { get; set; } = CompressionLevel.Default;
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ public class TarWriter : AbstractWriter
|
||||
}
|
||||
if (WriterOptions.LeaveStreamOpen)
|
||||
{
|
||||
destination = NonDisposingStream.Create(destination);
|
||||
destination = SharpCompressStream.Create(destination, leaveOpen: true);
|
||||
}
|
||||
switch (options.CompressionType)
|
||||
{
|
||||
|
||||
@@ -1,13 +1,41 @@
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Common;
|
||||
using D = SharpCompress.Compressors.Deflate;
|
||||
|
||||
namespace SharpCompress.Writers;
|
||||
|
||||
public class WriterOptions : OptionsBase
|
||||
{
|
||||
public WriterOptions(CompressionType compressionType) => CompressionType = compressionType;
|
||||
public WriterOptions(CompressionType compressionType)
|
||||
{
|
||||
CompressionType = compressionType;
|
||||
CompressionLevel = compressionType switch
|
||||
{
|
||||
CompressionType.ZStandard => 3,
|
||||
CompressionType.Deflate => (int)D.CompressionLevel.Default,
|
||||
CompressionType.Deflate64 => (int)D.CompressionLevel.Default,
|
||||
CompressionType.GZip => (int)D.CompressionLevel.Default,
|
||||
_ => 0,
|
||||
};
|
||||
}
|
||||
|
||||
public WriterOptions(CompressionType compressionType, int compressionLevel)
|
||||
{
|
||||
CompressionType = compressionType;
|
||||
CompressionLevel = compressionLevel;
|
||||
}
|
||||
|
||||
public CompressionType CompressionType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The compression level to be used when the compression type supports variable levels.
|
||||
/// Valid ranges depend on the compression algorithm:
|
||||
/// - Deflate/GZip: 0-9 (0=no compression, 6=default, 9=best compression)
|
||||
/// - ZStandard: 1-22 (1=fastest, 3=default, 22=best compression)
|
||||
/// Note: BZip2 and LZMA do not support compression levels in this implementation.
|
||||
/// Defaults are set automatically based on compression type in the constructor.
|
||||
/// </summary>
|
||||
public int CompressionLevel { get; set; }
|
||||
|
||||
public static implicit operator WriterOptions(CompressionType compressionType) =>
|
||||
new(compressionType);
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace SharpCompress.Writers.Zip;
|
||||
public class ZipWriter : AbstractWriter
|
||||
{
|
||||
private readonly CompressionType compressionType;
|
||||
private readonly CompressionLevel compressionLevel;
|
||||
private readonly int compressionLevel;
|
||||
private readonly List<ZipCentralDirectoryEntry> entries = new();
|
||||
private readonly string zipComment;
|
||||
private long streamPosition;
|
||||
@@ -36,11 +36,11 @@ public class ZipWriter : AbstractWriter
|
||||
}
|
||||
|
||||
compressionType = zipWriterOptions.CompressionType;
|
||||
compressionLevel = zipWriterOptions.DeflateCompressionLevel;
|
||||
compressionLevel = zipWriterOptions.CompressionLevel;
|
||||
|
||||
if (WriterOptions.LeaveStreamOpen)
|
||||
{
|
||||
destination = NonDisposingStream.Create(destination);
|
||||
destination = SharpCompressStream.Create(destination, leaveOpen: true);
|
||||
}
|
||||
InitializeStream(destination);
|
||||
}
|
||||
@@ -69,6 +69,7 @@ public class ZipWriter : AbstractWriter
|
||||
CompressionType.BZip2 => ZipCompressionMethod.BZip2,
|
||||
CompressionType.LZMA => ZipCompressionMethod.LZMA,
|
||||
CompressionType.PPMd => ZipCompressionMethod.PPMd,
|
||||
CompressionType.ZStandard => ZipCompressionMethod.ZStandard,
|
||||
_ => throw new InvalidFormatException("Invalid compression method: " + compressionType),
|
||||
};
|
||||
|
||||
@@ -117,7 +118,7 @@ public class ZipWriter : AbstractWriter
|
||||
OutputStream.NotNull(),
|
||||
entry,
|
||||
compression,
|
||||
options.DeflateCompressionLevel ?? compressionLevel
|
||||
options.CompressionLevel ?? compressionLevel
|
||||
);
|
||||
}
|
||||
|
||||
@@ -312,8 +313,8 @@ public class ZipWriter : AbstractWriter
|
||||
private readonly Stream writeStream;
|
||||
private readonly ZipWriter writer;
|
||||
private readonly ZipCompressionMethod zipCompressionMethod;
|
||||
private readonly CompressionLevel compressionLevel;
|
||||
private CountingWritableSubStream? counting;
|
||||
private readonly int compressionLevel;
|
||||
private SharpCompressStream? counting;
|
||||
private ulong decompressed;
|
||||
|
||||
// Flag to prevent throwing exceptions on Dispose
|
||||
@@ -325,7 +326,7 @@ public class ZipWriter : AbstractWriter
|
||||
Stream originalStream,
|
||||
ZipCentralDirectoryEntry entry,
|
||||
ZipCompressionMethod zipCompressionMethod,
|
||||
CompressionLevel compressionLevel
|
||||
int compressionLevel
|
||||
)
|
||||
{
|
||||
this.writer = writer;
|
||||
@@ -353,7 +354,7 @@ public class ZipWriter : AbstractWriter
|
||||
|
||||
private Stream GetWriteStream(Stream writeStream)
|
||||
{
|
||||
counting = new CountingWritableSubStream(writeStream);
|
||||
counting = new SharpCompressStream(writeStream, leaveOpen: true);
|
||||
Stream output = counting;
|
||||
switch (zipCompressionMethod)
|
||||
{
|
||||
@@ -363,7 +364,11 @@ public class ZipWriter : AbstractWriter
|
||||
}
|
||||
case ZipCompressionMethod.Deflate:
|
||||
{
|
||||
return new DeflateStream(counting, CompressionMode.Compress, compressionLevel);
|
||||
return new DeflateStream(
|
||||
counting,
|
||||
CompressionMode.Compress,
|
||||
(CompressionLevel)compressionLevel
|
||||
);
|
||||
}
|
||||
case ZipCompressionMethod.BZip2:
|
||||
{
|
||||
@@ -389,6 +394,10 @@ public class ZipWriter : AbstractWriter
|
||||
counting.Write(writer.PpmdProperties.Properties, 0, 2);
|
||||
return new PpmdStream(writer.PpmdProperties, counting, true);
|
||||
}
|
||||
case ZipCompressionMethod.ZStandard:
|
||||
{
|
||||
return new ZstdSharp.CompressionStream(counting, compressionLevel);
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new NotSupportedException("CompressionMethod: " + zipCompressionMethod);
|
||||
@@ -419,9 +428,9 @@ public class ZipWriter : AbstractWriter
|
||||
return;
|
||||
}
|
||||
|
||||
var countingCount = counting?.Count ?? 0;
|
||||
var countingCount = counting?.InternalPosition ?? 0;
|
||||
entry.Crc = (uint)crc.Crc32Result;
|
||||
entry.Compressed = countingCount;
|
||||
entry.Compressed = (ulong)countingCount;
|
||||
entry.Decompressed = decompressed;
|
||||
|
||||
var zip64 =
|
||||
@@ -521,7 +530,7 @@ public class ZipWriter : AbstractWriter
|
||||
// if we can prevent the writes from happening
|
||||
if (entry.Zip64HeaderOffset == 0)
|
||||
{
|
||||
var countingCount = counting?.Count ?? 0;
|
||||
var countingCount = counting?.InternalPosition ?? 0;
|
||||
// Pre-check, the counting.Count is not exact, as we do not know the size before having actually compressed it
|
||||
if (
|
||||
limitsExceeded
|
||||
@@ -541,7 +550,7 @@ public class ZipWriter : AbstractWriter
|
||||
|
||||
if (entry.Zip64HeaderOffset == 0)
|
||||
{
|
||||
var countingCount = counting?.Count ?? 0;
|
||||
var countingCount = counting?.InternalPosition ?? 0;
|
||||
// Post-check, this is accurate
|
||||
if ((decompressed > uint.MaxValue) || countingCount > uint.MaxValue)
|
||||
{
|
||||
|
||||
@@ -9,9 +9,34 @@ public class ZipWriterEntryOptions
|
||||
public CompressionType? CompressionType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// When CompressionType.Deflate is used, this property is referenced. Defaults to CompressionLevel.Default.
|
||||
/// The compression level to be used when the compression type supports variable levels.
|
||||
/// Valid ranges depend on the compression algorithm:
|
||||
/// - Deflate/GZip: 0-9 (0=no compression, 6=default, 9=best compression)
|
||||
/// - ZStandard: 1-22 (1=fastest, 3=default, 22=best compression)
|
||||
/// When null, uses the archive's default compression level for the specified compression type.
|
||||
/// Note: BZip2 and LZMA do not support compression levels in this implementation.
|
||||
/// </summary>
|
||||
public CompressionLevel? DeflateCompressionLevel { get; set; }
|
||||
public int? CompressionLevel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// When CompressionType.Deflate is used, this property is referenced.
|
||||
/// Valid range: 0-9 (0=no compression, 6=default, 9=best compression).
|
||||
/// When null, uses the archive's default compression level.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This property is deprecated. Use <see cref="CompressionLevel"/> instead.
|
||||
/// </remarks>
|
||||
[Obsolete(
|
||||
"Use CompressionLevel property instead. This property will be removed in a future version."
|
||||
)]
|
||||
public CompressionLevel? DeflateCompressionLevel
|
||||
{
|
||||
get =>
|
||||
CompressionLevel.HasValue
|
||||
? (CompressionLevel)Math.Min(CompressionLevel.Value, 9)
|
||||
: null;
|
||||
set => CompressionLevel = value.HasValue ? (int)value.Value : null;
|
||||
}
|
||||
|
||||
public string? EntryComment { get; set; }
|
||||
|
||||
|
||||
@@ -1,31 +1,74 @@
|
||||
using System;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Compressors.Deflate;
|
||||
using D = SharpCompress.Compressors.Deflate;
|
||||
|
||||
namespace SharpCompress.Writers.Zip;
|
||||
|
||||
public class ZipWriterOptions : WriterOptions
|
||||
{
|
||||
public ZipWriterOptions(CompressionType compressionType)
|
||||
: base(compressionType) { }
|
||||
public ZipWriterOptions(
|
||||
CompressionType compressionType,
|
||||
CompressionLevel compressionLevel = D.CompressionLevel.Default
|
||||
)
|
||||
: base(compressionType, (int)compressionLevel) { }
|
||||
|
||||
internal ZipWriterOptions(WriterOptions options)
|
||||
: base(options.CompressionType)
|
||||
{
|
||||
LeaveStreamOpen = options.LeaveStreamOpen;
|
||||
ArchiveEncoding = options.ArchiveEncoding;
|
||||
CompressionLevel = options.CompressionLevel;
|
||||
|
||||
if (options is ZipWriterOptions writerOptions)
|
||||
{
|
||||
UseZip64 = writerOptions.UseZip64;
|
||||
DeflateCompressionLevel = writerOptions.DeflateCompressionLevel;
|
||||
ArchiveComment = writerOptions.ArchiveComment;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When CompressionType.Deflate is used, this property is referenced. Defaults to CompressionLevel.Default.
|
||||
/// Sets the compression level for Deflate compression (0-9).
|
||||
/// This is a convenience method that sets the CompressionLevel property for Deflate compression.
|
||||
/// </summary>
|
||||
public CompressionLevel DeflateCompressionLevel { get; set; } = CompressionLevel.Default;
|
||||
/// <param name="level">Deflate compression level (0=no compression, 6=default, 9=best compression)</param>
|
||||
public void SetDeflateCompressionLevel(CompressionLevel level)
|
||||
{
|
||||
CompressionLevel = (int)level;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the compression level for ZStandard compression (1-22).
|
||||
/// This is a convenience method that sets the CompressionLevel property for ZStandard compression.
|
||||
/// </summary>
|
||||
/// <param name="level">ZStandard compression level (1=fastest, 3=default, 22=best compression)</param>
|
||||
/// <exception cref="ArgumentOutOfRangeException">Thrown when level is not between 1 and 22</exception>
|
||||
public void SetZStandardCompressionLevel(int level)
|
||||
{
|
||||
if (level < 1 || level > 22)
|
||||
throw new ArgumentOutOfRangeException(
|
||||
nameof(level),
|
||||
"ZStandard compression level must be between 1 and 22"
|
||||
);
|
||||
|
||||
CompressionLevel = level;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Legacy property for Deflate compression levels.
|
||||
/// Valid range: 0-9 (0=no compression, 6=default, 9=best compression).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This property is deprecated. Use <see cref="WriterOptions.CompressionLevel"/> or <see cref="SetDeflateCompressionLevel"/> instead.
|
||||
/// </remarks>
|
||||
[Obsolete(
|
||||
"Use CompressionLevel property or SetDeflateCompressionLevel method instead. This property will be removed in a future version."
|
||||
)]
|
||||
public CompressionLevel DeflateCompressionLevel
|
||||
{
|
||||
get => (CompressionLevel)Math.Min(CompressionLevel, 9);
|
||||
set => CompressionLevel = (int)value;
|
||||
}
|
||||
|
||||
public string? ArchiveComment { get; set; }
|
||||
|
||||
|
||||
@@ -2,6 +2,15 @@
|
||||
"version": 2,
|
||||
"dependencies": {
|
||||
".NETFramework,Version=v4.8": {
|
||||
"Microsoft.NETFramework.ReferenceAssemblies": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.0.3, )",
|
||||
"resolved": "1.0.3",
|
||||
"contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==",
|
||||
"dependencies": {
|
||||
"Microsoft.NETFramework.ReferenceAssemblies.net48": "1.0.3"
|
||||
}
|
||||
},
|
||||
"Microsoft.SourceLink.GitHub": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.0.0, )",
|
||||
@@ -20,9 +29,9 @@
|
||||
},
|
||||
"ZstdSharp.Port": {
|
||||
"type": "Direct",
|
||||
"requested": "[0.8.5, )",
|
||||
"resolved": "0.8.5",
|
||||
"contentHash": "TR4j17WeVSEb3ncgL2NqlXEqcy04I+Kk9CaebNDplUeL8XOgjkZ7fP4Wg4grBdPLIqsV86p2QaXTkZoRMVOsew==",
|
||||
"requested": "[0.8.6, )",
|
||||
"resolved": "0.8.6",
|
||||
"contentHash": "iP4jVLQoQmUjMU88g1WObiNr6YKZGvh4aOXn3yOJsHqZsflwRsxZPcIBvNXgjXO3vQKSLctXGLTpcBPLnWPS8A==",
|
||||
"dependencies": {
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
"System.Memory": "4.5.5",
|
||||
@@ -34,6 +43,11 @@
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
|
||||
},
|
||||
"Microsoft.NETFramework.ReferenceAssemblies.net48": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.0.3",
|
||||
"contentHash": "zMk4D+9zyiEWByyQ7oPImPN/Jhpj166Ky0Nlla4eXlNL8hI/BtSJsgR8Inldd4NNpIAH3oh8yym0W2DrhXdSLQ=="
|
||||
},
|
||||
"Microsoft.SourceLink.Common": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
@@ -106,9 +120,9 @@
|
||||
},
|
||||
"ZstdSharp.Port": {
|
||||
"type": "Direct",
|
||||
"requested": "[0.8.5, )",
|
||||
"resolved": "0.8.5",
|
||||
"contentHash": "TR4j17WeVSEb3ncgL2NqlXEqcy04I+Kk9CaebNDplUeL8XOgjkZ7fP4Wg4grBdPLIqsV86p2QaXTkZoRMVOsew==",
|
||||
"requested": "[0.8.6, )",
|
||||
"resolved": "0.8.6",
|
||||
"contentHash": "iP4jVLQoQmUjMU88g1WObiNr6YKZGvh4aOXn3yOJsHqZsflwRsxZPcIBvNXgjXO3vQKSLctXGLTpcBPLnWPS8A==",
|
||||
"dependencies": {
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
"System.Memory": "4.5.5",
|
||||
@@ -179,6 +193,15 @@
|
||||
"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.net461": "1.0.3"
|
||||
}
|
||||
},
|
||||
"Microsoft.SourceLink.GitHub": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.0.0, )",
|
||||
@@ -227,9 +250,9 @@
|
||||
},
|
||||
"ZstdSharp.Port": {
|
||||
"type": "Direct",
|
||||
"requested": "[0.8.5, )",
|
||||
"resolved": "0.8.5",
|
||||
"contentHash": "TR4j17WeVSEb3ncgL2NqlXEqcy04I+Kk9CaebNDplUeL8XOgjkZ7fP4Wg4grBdPLIqsV86p2QaXTkZoRMVOsew==",
|
||||
"requested": "[0.8.6, )",
|
||||
"resolved": "0.8.6",
|
||||
"contentHash": "iP4jVLQoQmUjMU88g1WObiNr6YKZGvh4aOXn3yOJsHqZsflwRsxZPcIBvNXgjXO3vQKSLctXGLTpcBPLnWPS8A==",
|
||||
"dependencies": {
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
"System.Memory": "4.5.5",
|
||||
@@ -246,6 +269,11 @@
|
||||
"resolved": "1.1.0",
|
||||
"contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A=="
|
||||
},
|
||||
"Microsoft.NETFramework.ReferenceAssemblies.net461": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.0.3",
|
||||
"contentHash": "AmOJZwCqnOCNp6PPcf9joyogScWLtwy0M1WkqfEQ0M9nYwyDD7EX9ZjscKS5iYnyvteX7kzSKFCKt9I9dXA6mA=="
|
||||
},
|
||||
"Microsoft.SourceLink.Common": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
@@ -271,6 +299,15 @@
|
||||
}
|
||||
},
|
||||
"net6.0": {
|
||||
"Microsoft.NETFramework.ReferenceAssemblies": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.0.3, )",
|
||||
"resolved": "1.0.3",
|
||||
"contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==",
|
||||
"dependencies": {
|
||||
"Microsoft.NETFramework.ReferenceAssemblies.net461": "1.0.3"
|
||||
}
|
||||
},
|
||||
"Microsoft.SourceLink.GitHub": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.0.0, )",
|
||||
@@ -289,15 +326,20 @@
|
||||
},
|
||||
"ZstdSharp.Port": {
|
||||
"type": "Direct",
|
||||
"requested": "[0.8.5, )",
|
||||
"resolved": "0.8.5",
|
||||
"contentHash": "TR4j17WeVSEb3ncgL2NqlXEqcy04I+Kk9CaebNDplUeL8XOgjkZ7fP4Wg4grBdPLIqsV86p2QaXTkZoRMVOsew=="
|
||||
"requested": "[0.8.6, )",
|
||||
"resolved": "0.8.6",
|
||||
"contentHash": "iP4jVLQoQmUjMU88g1WObiNr6YKZGvh4aOXn3yOJsHqZsflwRsxZPcIBvNXgjXO3vQKSLctXGLTpcBPLnWPS8A=="
|
||||
},
|
||||
"Microsoft.Build.Tasks.Git": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
|
||||
},
|
||||
"Microsoft.NETFramework.ReferenceAssemblies.net461": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.0.3",
|
||||
"contentHash": "AmOJZwCqnOCNp6PPcf9joyogScWLtwy0M1WkqfEQ0M9nYwyDD7EX9ZjscKS5iYnyvteX7kzSKFCKt9I9dXA6mA=="
|
||||
},
|
||||
"Microsoft.SourceLink.Common": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
@@ -307,9 +349,18 @@
|
||||
"net8.0": {
|
||||
"Microsoft.NET.ILLink.Tasks": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.0.16, )",
|
||||
"resolved": "8.0.16",
|
||||
"contentHash": "0H1QaKpVibe++Zx6EYJQGhrpfz2bBPGiQ7Rpsmx8I3+oKv+ZRRIfVfmcj50KuZlhhRE6V02y5bUjP+V2oPM2ng=="
|
||||
"requested": "[8.0.17, )",
|
||||
"resolved": "8.0.17",
|
||||
"contentHash": "x5/y4l8AtshpBOrCZdlE4txw8K3e3s9meBFeZeR3l8hbbku2V7kK6ojhXvrbjg1rk3G+JqL1BI26gtgc1ZrdUw=="
|
||||
},
|
||||
"Microsoft.NETFramework.ReferenceAssemblies": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.0.3, )",
|
||||
"resolved": "1.0.3",
|
||||
"contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==",
|
||||
"dependencies": {
|
||||
"Microsoft.NETFramework.ReferenceAssemblies.net461": "1.0.3"
|
||||
}
|
||||
},
|
||||
"Microsoft.SourceLink.GitHub": {
|
||||
"type": "Direct",
|
||||
@@ -329,15 +380,20 @@
|
||||
},
|
||||
"ZstdSharp.Port": {
|
||||
"type": "Direct",
|
||||
"requested": "[0.8.5, )",
|
||||
"resolved": "0.8.5",
|
||||
"contentHash": "TR4j17WeVSEb3ncgL2NqlXEqcy04I+Kk9CaebNDplUeL8XOgjkZ7fP4Wg4grBdPLIqsV86p2QaXTkZoRMVOsew=="
|
||||
"requested": "[0.8.6, )",
|
||||
"resolved": "0.8.6",
|
||||
"contentHash": "iP4jVLQoQmUjMU88g1WObiNr6YKZGvh4aOXn3yOJsHqZsflwRsxZPcIBvNXgjXO3vQKSLctXGLTpcBPLnWPS8A=="
|
||||
},
|
||||
"Microsoft.Build.Tasks.Git": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
|
||||
},
|
||||
"Microsoft.NETFramework.ReferenceAssemblies.net461": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.0.3",
|
||||
"contentHash": "AmOJZwCqnOCNp6PPcf9joyogScWLtwy0M1WkqfEQ0M9nYwyDD7EX9ZjscKS5iYnyvteX7kzSKFCKt9I9dXA6mA=="
|
||||
},
|
||||
"Microsoft.SourceLink.Common": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
|
||||
@@ -4,8 +4,12 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Compressors.Xz;
|
||||
using SharpCompress.Crypto;
|
||||
using SharpCompress.IO;
|
||||
using SharpCompress.Readers;
|
||||
using SharpCompress.Writers;
|
||||
using SharpCompress.Writers.Zip;
|
||||
using Xunit;
|
||||
|
||||
namespace SharpCompress.Test;
|
||||
@@ -36,7 +40,13 @@ public class ArchiveTests : ReaderTests
|
||||
{
|
||||
foreach (var path in testArchives)
|
||||
{
|
||||
using (var stream = NonDisposingStream.Create(File.OpenRead(path), true))
|
||||
using (
|
||||
var stream = SharpCompressStream.Create(
|
||||
File.OpenRead(path),
|
||||
leaveOpen: true,
|
||||
throwOnDispose: true
|
||||
)
|
||||
)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -110,7 +120,13 @@ public class ArchiveTests : ReaderTests
|
||||
{
|
||||
foreach (var path in testArchives)
|
||||
{
|
||||
using (var stream = NonDisposingStream.Create(File.OpenRead(path), true))
|
||||
using (
|
||||
var stream = SharpCompressStream.Create(
|
||||
File.OpenRead(path),
|
||||
leaveOpen: true,
|
||||
throwOnDispose: true
|
||||
)
|
||||
)
|
||||
using (var archive = archiveFactory.Open(stream, readerOptions))
|
||||
{
|
||||
try
|
||||
@@ -236,6 +252,25 @@ public class ArchiveTests : ReaderTests
|
||||
}
|
||||
}
|
||||
|
||||
protected void ArchiveExtractToDirectory(
|
||||
string testArchive,
|
||||
ReaderOptions? readerOptions = null
|
||||
) => ArchiveExtractToDirectory(ArchiveFactory.AutoFactory, testArchive, readerOptions);
|
||||
|
||||
protected void ArchiveExtractToDirectory(
|
||||
IArchiveFactory archiveFactory,
|
||||
string testArchive,
|
||||
ReaderOptions? readerOptions = null
|
||||
)
|
||||
{
|
||||
testArchive = Path.Combine(TEST_ARCHIVES_PATH, testArchive);
|
||||
using (var archive = archiveFactory.Open(new FileInfo(testArchive), readerOptions))
|
||||
{
|
||||
archive.ExtractToDirectory(SCRATCH_FILES_PATH);
|
||||
}
|
||||
VerifyFiles();
|
||||
}
|
||||
|
||||
protected void ArchiveFileRead(
|
||||
IArchiveFactory archiveFactory,
|
||||
string testArchive,
|
||||
@@ -327,4 +362,218 @@ public class ArchiveTests : ReaderTests
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates CRC32 for the given data using SharpCompress implementation
|
||||
/// </summary>
|
||||
protected static uint CalculateCrc32(byte[] data) => Crc32.Compute(data);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a writer with the specified compression type and level
|
||||
/// </summary>
|
||||
protected static IWriter CreateWriterWithLevel(
|
||||
Stream stream,
|
||||
CompressionType compressionType,
|
||||
int? compressionLevel = null
|
||||
)
|
||||
{
|
||||
var writerOptions = new ZipWriterOptions(compressionType);
|
||||
if (compressionLevel.HasValue)
|
||||
{
|
||||
writerOptions.CompressionLevel = compressionLevel.Value;
|
||||
}
|
||||
return WriterFactory.Open(stream, ArchiveType.Zip, writerOptions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies archive content against expected files with CRC32 validation
|
||||
/// </summary>
|
||||
protected void VerifyArchiveContent(
|
||||
MemoryStream zipStream,
|
||||
Dictionary<string, (byte[] data, uint crc)> expectedFiles
|
||||
)
|
||||
{
|
||||
zipStream.Position = 0;
|
||||
using var archive = ArchiveFactory.Open(zipStream);
|
||||
Assert.Equal(expectedFiles.Count, archive.Entries.Count());
|
||||
|
||||
foreach (var entry in archive.Entries.Where(e => !e.IsDirectory))
|
||||
{
|
||||
using var entryStream = entry.OpenEntryStream();
|
||||
using var extractedStream = new MemoryStream();
|
||||
entryStream.CopyTo(extractedStream);
|
||||
var extractedData = extractedStream.ToArray();
|
||||
|
||||
Assert.True(
|
||||
expectedFiles.ContainsKey(entry.Key.NotNull()),
|
||||
$"Unexpected entry: {entry.Key}"
|
||||
);
|
||||
|
||||
var (expectedData, expectedCrc) = expectedFiles[entry.Key.NotNull()];
|
||||
var actualCrc = CalculateCrc32(extractedData);
|
||||
|
||||
Assert.Equal(expectedCrc, actualCrc);
|
||||
Assert.Equal(expectedData.Length, extractedData.Length);
|
||||
|
||||
// For large files, spot check rather than full comparison for performance
|
||||
if (expectedData.Length > 1024 * 1024)
|
||||
{
|
||||
VerifyDataSpotCheck(expectedData, extractedData);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Equal(expectedData, extractedData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs efficient spot checks on large data arrays
|
||||
/// </summary>
|
||||
protected static void VerifyDataSpotCheck(byte[] expected, byte[] actual)
|
||||
{
|
||||
// Check first, middle, and last 1KB
|
||||
Assert.Equal(expected.Take(1024), actual.Take(1024));
|
||||
var mid = expected.Length / 2;
|
||||
Assert.Equal(expected.Skip(mid).Take(1024), actual.Skip(mid).Take(1024));
|
||||
Assert.Equal(
|
||||
expected.Skip(Math.Max(0, expected.Length - 1024)),
|
||||
actual.Skip(Math.Max(0, actual.Length - 1024))
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies compression ratio meets expectations
|
||||
/// </summary>
|
||||
protected void VerifyCompressionRatio(
|
||||
long originalSize,
|
||||
long compressedSize,
|
||||
double maxRatio,
|
||||
string context
|
||||
)
|
||||
{
|
||||
var compressionRatio = (double)compressedSize / originalSize;
|
||||
Assert.True(
|
||||
compressionRatio < maxRatio,
|
||||
$"Expected better compression for {context}. Original: {originalSize}, Compressed: {compressedSize}, Ratio: {compressionRatio:P}"
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a memory-based archive with specified files and compression
|
||||
/// </summary>
|
||||
protected MemoryStream CreateMemoryArchive(
|
||||
Dictionary<string, byte[]> files,
|
||||
CompressionType compressionType,
|
||||
int? compressionLevel = null
|
||||
)
|
||||
{
|
||||
var zipStream = new MemoryStream();
|
||||
using (var writer = CreateWriterWithLevel(zipStream, compressionType, compressionLevel))
|
||||
{
|
||||
foreach (var kvp in files)
|
||||
{
|
||||
writer.Write(kvp.Key, new MemoryStream(kvp.Value));
|
||||
}
|
||||
}
|
||||
return zipStream;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies streaming CRC calculation for large data
|
||||
/// </summary>
|
||||
protected void VerifyStreamingCrc(Stream entryStream, uint expectedCrc, long expectedLength)
|
||||
{
|
||||
using var crcStream = new Crc32Stream(Stream.Null);
|
||||
const int bufferSize = 64 * 1024;
|
||||
var buffer = new byte[bufferSize];
|
||||
int totalBytesRead = 0;
|
||||
int bytesRead;
|
||||
|
||||
while ((bytesRead = entryStream.Read(buffer, 0, bufferSize)) > 0)
|
||||
{
|
||||
crcStream.Write(buffer, 0, bytesRead);
|
||||
totalBytesRead += bytesRead;
|
||||
}
|
||||
|
||||
var actualCrc = crcStream.Crc;
|
||||
Assert.Equal(expectedCrc, actualCrc);
|
||||
Assert.Equal(expectedLength, totalBytesRead);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates and verifies a basic archive with compression testing
|
||||
/// </summary>
|
||||
protected void CreateAndVerifyBasicArchive(
|
||||
Dictionary<string, byte[]> testFiles,
|
||||
CompressionType compressionType,
|
||||
int? compressionLevel = null,
|
||||
double maxCompressionRatio = 0.8
|
||||
)
|
||||
{
|
||||
// Calculate expected CRCs
|
||||
var expectedFiles = testFiles.ToDictionary(
|
||||
kvp => kvp.Key,
|
||||
kvp => (data: kvp.Value, crc: CalculateCrc32(kvp.Value))
|
||||
);
|
||||
|
||||
// Create archive
|
||||
using var zipStream = CreateMemoryArchive(testFiles, compressionType, compressionLevel);
|
||||
|
||||
// Verify compression occurred if expected
|
||||
if (compressionType != CompressionType.None)
|
||||
{
|
||||
var originalSize = testFiles.Values.Sum(data => (long)data.Length);
|
||||
VerifyCompressionRatio(
|
||||
originalSize,
|
||||
zipStream.Length,
|
||||
maxCompressionRatio,
|
||||
compressionType.ToString()
|
||||
);
|
||||
}
|
||||
|
||||
// Verify content
|
||||
VerifyArchiveContent(zipStream, expectedFiles);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies archive entries have correct compression type
|
||||
/// </summary>
|
||||
protected void VerifyCompressionType(
|
||||
MemoryStream zipStream,
|
||||
CompressionType expectedCompressionType
|
||||
)
|
||||
{
|
||||
zipStream.Position = 0;
|
||||
using var archive = ArchiveFactory.Open(zipStream);
|
||||
|
||||
foreach (var entry in archive.Entries.Where(e => !e.IsDirectory))
|
||||
{
|
||||
Assert.Equal(expectedCompressionType, entry.CompressionType);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts and verifies a single entry from archive
|
||||
/// </summary>
|
||||
protected (byte[] data, uint crc) ExtractAndVerifyEntry(
|
||||
MemoryStream zipStream,
|
||||
string entryName
|
||||
)
|
||||
{
|
||||
zipStream.Position = 0;
|
||||
using var archive = ArchiveFactory.Open(zipStream);
|
||||
|
||||
var entry = archive.Entries.FirstOrDefault(e => e.Key == entryName && !e.IsDirectory);
|
||||
Assert.NotNull(entry);
|
||||
|
||||
using var entryStream = entry.OpenEntryStream();
|
||||
using var extractedStream = new MemoryStream();
|
||||
entryStream.CopyTo(extractedStream);
|
||||
|
||||
var extractedData = extractedStream.ToArray();
|
||||
var crc = CalculateCrc32(extractedData);
|
||||
|
||||
return (extractedData, crc);
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user