Compare commits

..

33 Commits

Author SHA1 Message Date
Adam Hathcock
e08e4e5d9f Enabling Bzip2 but something else is broken 2021-02-21 18:01:56 +00:00
Adam Hathcock
dd710ec308 fixed read only sub stream 2021-02-21 13:37:58 +00:00
Adam Hathcock
5cfc608010 More fixes? 2021-02-21 13:21:33 +00:00
Adam Hathcock
997c11ef25 Bug fix on counting 2021-02-21 12:14:07 +00:00
Adam Hathcock
249f11f543 Rework some zip writing 2021-02-21 12:10:17 +00:00
Adam Hathcock
eeb6761a9f Reuse gzip header reading 2021-02-20 13:11:40 +00:00
Adam Hathcock
0c35abdebe Explicit exception for read shortcut 2021-02-20 12:52:29 +00:00
Adam Hathcock
30da0b91ed Fixed Gzip by reverting EmitHeaderAsync 2021-02-20 11:47:38 +00:00
Adam Hathcock
d9c53e1c82 ZLIbStreamfile fixes? 2021-02-20 11:41:46 +00:00
Adam Hathcock
14e6d95559 More clean up doesn’t help 2021-02-14 18:09:22 +00:00
Adam Hathcock
8cdc49cb85 ReadByteAsync 2021-02-14 14:09:25 +00:00
Adam Hathcock
5c11075d36 Updates for merge 2021-02-14 13:52:55 +00:00
Adam Hathcock
be34fe2056 Merge branch 'master' into async
# Conflicts:
#	src/SharpCompress/Archives/GZip/GZipArchive.cs
#	src/SharpCompress/Common/GZip/GZipFilePart.cs
#	src/SharpCompress/Common/Tar/Headers/TarHeader.cs
#	src/SharpCompress/Common/Zip/SeekableZipHeaderFactory.cs
#	src/SharpCompress/Common/Zip/ZipFilePart.cs
#	src/SharpCompress/Compressors/Deflate/ZlibBaseStream.cs
#	src/SharpCompress/Compressors/LZMA/LZipStream.cs
#	src/SharpCompress/Compressors/Xz/BinaryUtils.cs
#	src/SharpCompress/Compressors/Xz/Crc32.cs
#	src/SharpCompress/Writers/Tar/TarWriter.cs
#	src/SharpCompress/Writers/Zip/ZipCentralDirectoryEntry.cs
#	src/SharpCompress/Writers/Zip/ZipWriter.cs
2021-02-14 13:38:58 +00:00
Adam Hathcock
7e9fb645cb Minor changes 2021-02-14 12:55:05 +00:00
Adam Hathcock
15209178ce AsyncStream for BZip2 2021-02-13 18:09:58 +00:00
Adam Hathcock
ea688e1f4c Writer problems still :( 2021-02-13 17:52:31 +00:00
Adam Hathcock
fe4cc8e6cb Zip LZMA write will roundtrip 2021-02-13 16:44:53 +00:00
Adam Hathcock
1f37ced35a AsyncStream everything 2021-02-13 16:16:03 +00:00
Adam Hathcock
949e90351f More LZMA conversion going, BZip2 not for now 2021-02-13 10:45:57 +00:00
Adam Hathcock
db02e8b634 Minor fixes 2021-02-08 18:25:14 +00:00
Adam Hathcock
d6fe729068 create async 2021-02-08 12:07:45 +00:00
Adam Hathcock
ef3d4da286 Fix test and some zip writing 2021-02-08 11:18:57 +00:00
Adam Hathcock
813bd5ae80 Async open entry 2021-02-08 10:17:34 +00:00
Adam Hathcock
f40d3342c8 Tar and Xz mostly work 2021-02-07 18:58:24 +00:00
Adam Hathcock
9738b812c4 Fix rewindable stream and encoding tests 2021-02-07 18:08:29 +00:00
Adam Hathcock
c6a011df17 Fixed reader issue 2021-02-07 18:03:17 +00:00
Adam Hathcock
7d2dc58766 More API fixes 2021-02-07 13:38:41 +00:00
Adam Hathcock
d234f2d509 First pass of trying tar 2021-02-07 11:30:44 +00:00
Adam Hathcock
cdba5ec419 AsyncEnumerable usage in entries 2021-02-07 09:08:15 +00:00
Adam Hathcock
9cf8a3dbbe more awaits 2021-02-06 09:08:09 +00:00
Adam Hathcock
2b4f02997e more async await 2021-02-01 08:34:12 +00:00
Adam Hathcock
bcdfd992a3 async dispose and fix tests? 2021-01-24 09:18:38 +00:00
Adam Hathcock
3a820c52bd async Deflate. Start of writer 2021-01-24 09:06:02 +00:00
234 changed files with 3994 additions and 7111 deletions

View File

@@ -1,543 +0,0 @@
# Version: 2.0.1 (Using https://semver.org/)
# Updated: 2020-12-11
# See https://github.com/RehanSaeed/EditorConfig/releases for release notes.
# See https://github.com/RehanSaeed/EditorConfig for updates to this file.
# See http://EditorConfig.org for more information about .editorconfig files.
##########################################
# Common Settings
##########################################
# This file is the top-most EditorConfig file
root = true
# All Files
[*]
charset = utf-8
indent_style = space
indent_size = 4
insert_final_newline = true
trim_trailing_whitespace = true
##########################################
# File Extension Settings
##########################################
# Visual Studio Solution Files
[*.sln]
indent_style = tab
# Visual Studio XML Project Files
[*.{csproj,vbproj,vcxproj.filters,proj,projitems,shproj}]
indent_size = 2
# XML Configuration Files
[*.{xml,config,props,targets,nuspec,resx,ruleset,vsixmanifest,vsct}]
indent_size = 2
# JSON Files
[*.{json,json5,webmanifest}]
indent_size = 2
# YAML Files
[*.{yml,yaml}]
indent_size = 2
# Markdown Files
[*.md]
trim_trailing_whitespace = false
# Web Files
[*.{htm,html,js,jsm,ts,tsx,css,sass,scss,less,svg,vue}]
indent_size = 2
# Batch Files
[*.{cmd,bat}]
end_of_line = crlf
# Bash Files
[*.sh]
end_of_line = lf
# Makefiles
[Makefile]
indent_style = tab
##########################################
# Default .NET Code Style Severities
# https://docs.microsoft.com/dotnet/fundamentals/code-analysis/configuration-options#scope
##########################################
[*.{cs,csx,cake,vb,vbx}]
# Default Severity for all .NET Code Style rules below
dotnet_analyzer_diagnostic.severity = warning
##########################################
# File Header (Uncomment to support file headers)
# https://docs.microsoft.com/visualstudio/ide/reference/add-file-header
##########################################
# [*.{cs,csx,cake,vb,vbx}]
# file_header_template = <copyright file="{fileName}" company="PROJECT-AUTHOR">\n© PROJECT-AUTHOR\n</copyright>
# SA1636: File header copyright text should match
# Justification: .editorconfig supports file headers. If this is changed to a value other than "none", a stylecop.json file will need to added to the project.
# dotnet_diagnostic.SA1636.severity = none
##########################################
# .NET Language Conventions
# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions
##########################################
# .NET Code Style Settings
# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#net-code-style-settings
[*.{cs,csx,cake,vb,vbx}]
# "this." and "Me." qualifiers
# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#this-and-me
#dotnet_style_qualification_for_field = true:warning
#dotnet_style_qualification_for_property = true:warning
#dotnet_style_qualification_for_method = true:warning
#dotnet_style_qualification_for_event = true:warning
# Language keywords instead of framework type names for type references
# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#language-keywords
dotnet_style_predefined_type_for_locals_parameters_members = true:warning
dotnet_style_predefined_type_for_member_access = true:warning
# Modifier preferences
# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#normalize-modifiers
dotnet_style_require_accessibility_modifiers = always:warning
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:warning
visual_basic_preferred_modifier_order = Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async:warning
dotnet_style_readonly_field = true:warning
# Parentheses preferences
# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#parentheses-preferences
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:warning
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:warning
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:warning
dotnet_style_parentheses_in_other_operators = always_for_clarity:suggestion
# Expression-level preferences
# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#expression-level-preferences
dotnet_style_object_initializer = true:warning
dotnet_style_collection_initializer = true:warning
dotnet_style_explicit_tuple_names = true:warning
dotnet_style_prefer_inferred_tuple_names = true:warning
dotnet_style_prefer_inferred_anonymous_type_member_names = true:warning
dotnet_style_prefer_auto_properties = true:warning
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:warning
dotnet_style_prefer_conditional_expression_over_assignment = false:suggestion
dotnet_diagnostic.IDE0045.severity = suggestion
dotnet_style_prefer_conditional_expression_over_return = false:suggestion
dotnet_diagnostic.IDE0046.severity = suggestion
dotnet_style_prefer_compound_assignment = true:warning
# Null-checking preferences
# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#null-checking-preferences
dotnet_style_coalesce_expression = true:warning
dotnet_style_null_propagation = true:warning
# Parameter preferences
# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#parameter-preferences
dotnet_code_quality_unused_parameters = all:warning
# More style options (Undocumented)
# https://github.com/MicrosoftDocs/visualstudio-docs/issues/3641
dotnet_style_operator_placement_when_wrapping = end_of_line
# https://github.com/dotnet/roslyn/pull/40070
dotnet_style_prefer_simplified_interpolation = true:warning
# C# Code Style Settings
# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#c-code-style-settings
[*.{cs,csx,cake}]
# Implicit and explicit types
# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#implicit-and-explicit-types
csharp_style_var_for_built_in_types = true:warning
csharp_style_var_when_type_is_apparent = true:warning
csharp_style_var_elsewhere = true:warning
# Expression-bodied members
# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#expression-bodied-members
csharp_style_expression_bodied_methods = true:warning
csharp_style_expression_bodied_constructors = true:warning
csharp_style_expression_bodied_operators = true:warning
csharp_style_expression_bodied_properties = true:warning
csharp_style_expression_bodied_indexers = true:warning
csharp_style_expression_bodied_accessors = true:warning
csharp_style_expression_bodied_lambdas = true:warning
csharp_style_expression_bodied_local_functions = true:warning
# Pattern matching
# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#pattern-matching
csharp_style_pattern_matching_over_is_with_cast_check = true:warning
csharp_style_pattern_matching_over_as_with_null_check = true:warning
# Inlined variable declarations
# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#inlined-variable-declarations
csharp_style_inlined_variable_declaration = true:warning
# Expression-level preferences
# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#expression-level-preferences
csharp_prefer_simple_default_expression = true:warning
# "Null" checking preferences
# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#c-null-checking-preferences
csharp_style_throw_expression = true:warning
csharp_style_conditional_delegate_call = true:warning
# Code block preferences
# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#code-block-preferences
csharp_prefer_braces = true:warning
# Unused value preferences
# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#unused-value-preferences
csharp_style_unused_value_expression_statement_preference = discard_variable:suggestion
dotnet_diagnostic.IDE0058.severity = suggestion
csharp_style_unused_value_assignment_preference = discard_variable:suggestion
dotnet_diagnostic.IDE0059.severity = suggestion
# Index and range preferences
# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#index-and-range-preferences
csharp_style_prefer_index_operator = true:warning
csharp_style_prefer_range_operator = true:warning
# Miscellaneous preferences
# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#miscellaneous-preferences
csharp_style_deconstructed_variable_declaration = true:warning
csharp_style_pattern_local_over_anonymous_function = true:warning
csharp_using_directive_placement = outside_namespace:warning
csharp_prefer_static_local_function = true:warning
csharp_prefer_simple_using_statement = true:suggestion
dotnet_diagnostic.IDE0063.severity = suggestion
csharp_style_namespace_declarations = file_scoped
##########################################
# .NET Formatting Conventions
# https://docs.microsoft.com/visualstudio/ide/editorconfig-code-style-settings-reference#formatting-conventions
##########################################
# Organize usings
# https://docs.microsoft.com/visualstudio/ide/editorconfig-formatting-conventions#organize-using-directives
dotnet_sort_system_directives_first = true
# Newline options
# https://docs.microsoft.com/visualstudio/ide/editorconfig-formatting-conventions#new-line-options
csharp_new_line_before_open_brace = all
csharp_new_line_before_else = true
csharp_new_line_before_catch = true
csharp_new_line_before_finally = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_between_query_expression_clauses = true
# Indentation options
# https://docs.microsoft.com/visualstudio/ide/editorconfig-formatting-conventions#indentation-options
csharp_indent_case_contents = true
csharp_indent_switch_labels = true
csharp_indent_labels = no_change
csharp_indent_block_contents = true
csharp_indent_braces = false
csharp_indent_case_contents_when_block = false
# Spacing options
# https://docs.microsoft.com/visualstudio/ide/editorconfig-formatting-conventions#spacing-options
csharp_space_after_cast = false
csharp_space_after_keywords_in_control_flow_statements = true
csharp_space_between_parentheses = false
csharp_space_before_colon_in_inheritance_clause = true
csharp_space_after_colon_in_inheritance_clause = true
csharp_space_around_binary_operators = before_and_after
csharp_space_between_method_declaration_parameter_list_parentheses = false
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
csharp_space_between_method_declaration_name_and_open_parenthesis = false
csharp_space_between_method_call_parameter_list_parentheses = false
csharp_space_between_method_call_empty_parameter_list_parentheses = false
csharp_space_between_method_call_name_and_opening_parenthesis = false
csharp_space_after_comma = true
csharp_space_before_comma = false
csharp_space_after_dot = false
csharp_space_before_dot = false
csharp_space_after_semicolon_in_for_statement = true
csharp_space_before_semicolon_in_for_statement = false
csharp_space_around_declaration_statements = false
csharp_space_before_open_square_brackets = false
csharp_space_between_empty_square_brackets = false
csharp_space_between_square_brackets = false
# Wrapping options
# https://docs.microsoft.com/visualstudio/ide/editorconfig-formatting-conventions#wrap-options
csharp_preserve_single_line_statements = false
csharp_preserve_single_line_blocks = true
##########################################
# .NET Naming Conventions
# https://docs.microsoft.com/visualstudio/ide/editorconfig-naming-conventions
##########################################
[*.{cs,csx,cake,vb,vbx}]
dotnet_diagnostic.CA1000.severity = suggestion
dotnet_diagnostic.CA1001.severity = error
dotnet_diagnostic.CA1018.severity = error
dotnet_diagnostic.CA1051.severity = suggestion
dotnet_diagnostic.CA1068.severity = error
dotnet_diagnostic.CA1069.severity = error
dotnet_diagnostic.CA1304.severity = error
dotnet_diagnostic.CA1305.severity = suggestion
dotnet_diagnostic.CA1309.severity = suggestion
dotnet_diagnostic.CA1310.severity = error
dotnet_diagnostic.CA1707.severity = suggestion
dotnet_diagnostic.CA1708.severity = suggestion
dotnet_diagnostic.CA1711.severity = suggestion
dotnet_diagnostic.CA1716.severity = suggestion
dotnet_diagnostic.CA1720.severity = suggestion
dotnet_diagnostic.CA1725.severity = suggestion
dotnet_diagnostic.CA1805.severity = suggestion
dotnet_diagnostic.CA1816.severity = suggestion
dotnet_diagnostic.CA1822.severity = suggestion
dotnet_diagnostic.CA1825.severity = error
dotnet_diagnostic.CA1826.severity = silent
dotnet_diagnostic.CA1827.severity = error
dotnet_diagnostic.CA1829.severity = suggestion
dotnet_diagnostic.CA1834.severity = error
dotnet_diagnostic.CA1845.severity = suggestion
dotnet_diagnostic.CA1848.severity = suggestion
dotnet_diagnostic.CA2016.severity = suggestion
dotnet_diagnostic.CA2201.severity = error
dotnet_diagnostic.CA2206.severity = error
dotnet_diagnostic.CA2208.severity = error
dotnet_diagnostic.CA2211.severity = error
dotnet_diagnostic.CA2249.severity = error
dotnet_diagnostic.CA2251.severity = error
dotnet_diagnostic.CA2252.severity = none
dotnet_diagnostic.CA2254.severity = suggestion
dotnet_diagnostic.CS0169.severity = error
dotnet_diagnostic.CS0219.severity = error
dotnet_diagnostic.CS1998.severity = error
dotnet_diagnostic.CS8602.severity = error
dotnet_diagnostic.CS8604.severity = error
dotnet_diagnostic.CS8618.severity = error
dotnet_diagnostic.CS0618.severity = error
dotnet_diagnostic.CS1998.severity = error
dotnet_diagnostic.CS4014.severity = error
dotnet_diagnostic.CS8600.severity = error
dotnet_diagnostic.CS8603.severity = error
dotnet_diagnostic.CS8625.severity = error
dotnet_diagnostic.BL0005.severity = suggestion
dotnet_diagnostic.MVC1000.severity = suggestion
dotnet_diagnostic.IDE0055.severity = suggestion # Fix formatting
dotnet_diagnostic.IDE0023.severity = suggestion # use expression body for operators
dotnet_diagnostic.IDE0025.severity = suggestion # use expression body for properties
dotnet_diagnostic.IDE1006.severity = suggestion # Naming rule violation: These words cannot contain lower case characters
dotnet_diagnostic.IDE0072.severity = suggestion # Populate switch - forces population of all cases even when default specified
dotnet_diagnostic.IDE0027.severity = suggestion # Use expression body for accessors
dotnet_diagnostic.IDE0032.severity = suggestion # Use auto property
dotnet_diagnostic.IDE0007.severity = error # Use var
dotnet_diagnostic.IDE0160.severity = suggestion # Use block scoped
dotnet_diagnostic.IDE0011.severity = error # Use braces on if statements
dotnet_diagnostic.IDE0057.severity = suggestion # substring can be simplified
dotnet_diagnostic.IDE0004.severity = error # redundant cast
dotnet_diagnostic.IDE0010.severity = silent # populate switch
dotnet_diagnostic.IDE0021.severity = silent # expression body for constructors
dotnet_diagnostic.IDE0022.severity = silent # expression body for methods
dotnet_diagnostic.IDE0024.severity = silent # expression body for operators
dotnet_diagnostic.IDE0028.severity = silent
dotnet_diagnostic.IDE0033.severity = error # prefer tuple name
dotnet_diagnostic.IDE0040.severity = error # modifiers required
dotnet_diagnostic.IDE0041.severity = error # simplify null
dotnet_diagnostic.IDE0042.severity = error # deconstruct variable
dotnet_diagnostic.IDE0044.severity = error # make field only when possible
dotnet_diagnostic.IDE0047.severity = suggestion # paratemeter name
dotnet_diagnostic.IDE0051.severity = error # unused field
dotnet_diagnostic.IDE0052.severity = error # unused member
dotnet_diagnostic.IDE0060.severity = suggestion # unused parameters
dotnet_diagnostic.IDE0066.severity = suggestion # switch expression
dotnet_diagnostic.IDE0078.severity = suggestion # use pattern matching
dotnet_diagnostic.IDE0090.severity = suggestion # new can be simplified
dotnet_diagnostic.IDE0130.severity = suggestion # namespace folder structure
dotnet_diagnostic.IDE0160.severity = silent # Use block namespaces ARE NOT required
dotnet_diagnostic.IDE0161.severity = suggestion # Please use file namespaces
##########################################
# Styles
##########################################
# camel_case_style - Define the camelCase style
dotnet_naming_style.camel_case_style.capitalization = camel_case
# pascal_case_style - Define the PascalCase style
dotnet_naming_style.pascal_case_style.capitalization = pascal_case
# constant_case - Define the CONSTANT_CASE style
dotnet_naming_style.constant_case.capitalization = all_upper
dotnet_naming_style.constant_case.word_separator = _
# first_upper_style - The first character must start with an upper-case character
dotnet_naming_style.first_upper_style.capitalization = first_word_upper
# prefix_interface_with_i_style - Interfaces must be PascalCase and the first character of an interface must be an 'I'
dotnet_naming_style.prefix_interface_with_i_style.capitalization = pascal_case
dotnet_naming_style.prefix_interface_with_i_style.required_prefix = I
# prefix_type_parameters_with_t_style - Generic Type Parameters must be PascalCase and the first character must be a 'T'
dotnet_naming_style.prefix_type_parameters_with_t_style.capitalization = pascal_case
dotnet_naming_style.prefix_type_parameters_with_t_style.required_prefix = T
# disallowed_style - Anything that has this style applied is marked as disallowed
dotnet_naming_style.disallowed_style.capitalization = pascal_case
dotnet_naming_style.disallowed_style.required_prefix = ____RULE_VIOLATION____
dotnet_naming_style.disallowed_style.required_suffix = ____RULE_VIOLATION____
# internal_error_style - This style should never occur... if it does, it indicates a bug in file or in the parser using the file
dotnet_naming_style.internal_error_style.capitalization = pascal_case
dotnet_naming_style.internal_error_style.required_prefix = ____INTERNAL_ERROR____
dotnet_naming_style.internal_error_style.required_suffix = ____INTERNAL_ERROR____
# prefix_interface_with_i_style - Interfaces must be PascalCase and the first character of an interface must be an 'I'
dotnet_naming_style.underscore_camel_case_style.capitalization = camel_case
dotnet_naming_style.underscore_camel_case_style.required_prefix = _
##########################################
# .NET Design Guideline Field Naming Rules
# Naming rules for fields follow the .NET Framework design guidelines
# https://docs.microsoft.com/dotnet/standard/design-guidelines/index
##########################################
# All public/protected/protected_internal constant fields must be constant_case
# https://docs.microsoft.com/dotnet/standard/design-guidelines/field
dotnet_naming_symbols.public_protected_constant_fields_group.applicable_accessibilities = public, protected, protected_internal
dotnet_naming_symbols.public_protected_constant_fields_group.required_modifiers = const
dotnet_naming_symbols.public_protected_constant_fields_group.applicable_kinds = field
dotnet_naming_rule.public_protected_constant_fields_must_be_pascal_case_rule.symbols = public_protected_constant_fields_group
dotnet_naming_rule.public_protected_constant_fields_must_be_pascal_case_rule.style = constant_case
dotnet_naming_rule.public_protected_constant_fields_must_be_pascal_case_rule.severity = warning
# All public/protected/protected_internal static readonly fields must be constant_case
# https://docs.microsoft.com/dotnet/standard/design-guidelines/field
dotnet_naming_symbols.public_protected_static_readonly_fields_group.applicable_accessibilities = public, protected, protected_internal
dotnet_naming_symbols.public_protected_static_readonly_fields_group.required_modifiers = static, readonly
dotnet_naming_symbols.public_protected_static_readonly_fields_group.applicable_kinds = field
dotnet_naming_rule.public_protected_static_readonly_fields_must_be_pascal_case_rule.symbols = public_protected_static_readonly_fields_group
dotnet_naming_rule.public_protected_static_readonly_fields_must_be_pascal_case_rule.style = constant_case
dotnet_naming_rule.public_protected_static_readonly_fields_must_be_pascal_case_rule.severity = warning
# No other public/protected/protected_internal fields are allowed
# https://docs.microsoft.com/dotnet/standard/design-guidelines/field
dotnet_naming_symbols.other_public_protected_fields_group.applicable_accessibilities = public, protected, protected_internal
dotnet_naming_symbols.other_public_protected_fields_group.applicable_kinds = field
dotnet_naming_rule.other_public_protected_fields_disallowed_rule.symbols = other_public_protected_fields_group
dotnet_naming_rule.other_public_protected_fields_disallowed_rule.style = disallowed_style
dotnet_naming_rule.other_public_protected_fields_disallowed_rule.severity = error
##########################################
# StyleCop Field Naming Rules
# Naming rules for fields follow the StyleCop analyzers
# This does not override any rules using disallowed_style above
# https://github.com/DotNetAnalyzers/StyleCopAnalyzers
##########################################
# All constant fields must be constant_case
# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1303.md
dotnet_naming_symbols.stylecop_constant_fields_group.applicable_accessibilities = public, internal, protected_internal, protected, private_protected, private
dotnet_naming_symbols.stylecop_constant_fields_group.required_modifiers = const
dotnet_naming_symbols.stylecop_constant_fields_group.applicable_kinds = field
dotnet_naming_rule.stylecop_constant_fields_must_be_pascal_case_rule.symbols = stylecop_constant_fields_group
dotnet_naming_rule.stylecop_constant_fields_must_be_pascal_case_rule.style = constant_case
dotnet_naming_rule.stylecop_constant_fields_must_be_pascal_case_rule.severity = warning
# All static readonly fields must be constant_case
# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1311.md
dotnet_naming_symbols.stylecop_static_readonly_fields_group.applicable_accessibilities = public, internal, protected_internal, protected, private_protected, private
dotnet_naming_symbols.stylecop_static_readonly_fields_group.required_modifiers = static, readonly
dotnet_naming_symbols.stylecop_static_readonly_fields_group.applicable_kinds = field
dotnet_naming_rule.stylecop_static_readonly_fields_must_be_pascal_case_rule.symbols = stylecop_static_readonly_fields_group
dotnet_naming_rule.stylecop_static_readonly_fields_must_be_pascal_case_rule.style = constant_case
dotnet_naming_rule.stylecop_static_readonly_fields_must_be_pascal_case_rule.severity = warning
# No non-private instance fields are allowed
# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1401.md
dotnet_naming_symbols.stylecop_fields_must_be_private_group.applicable_accessibilities = public, internal, protected_internal, protected, private_protected
dotnet_naming_symbols.stylecop_fields_must_be_private_group.applicable_kinds = field
dotnet_naming_rule.stylecop_instance_fields_must_be_private_rule.symbols = stylecop_fields_must_be_private_group
dotnet_naming_rule.stylecop_instance_fields_must_be_private_rule.style = disallowed_style
dotnet_naming_rule.stylecop_instance_fields_must_be_private_rule.severity = error
# Private fields must be camelCase
# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1306.md
dotnet_naming_symbols.stylecop_private_fields_group.applicable_accessibilities = private
dotnet_naming_symbols.stylecop_private_fields_group.applicable_kinds = field
dotnet_naming_rule.stylecop_private_fields_must_be_camel_case_rule.symbols = stylecop_private_fields_group
dotnet_naming_rule.stylecop_private_fields_must_be_camel_case_rule.style = underscore_camel_case_style
dotnet_naming_rule.stylecop_private_fields_must_be_camel_case_rule.severity = warning
# Local variables must be camelCase
# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1312.md
dotnet_naming_symbols.stylecop_local_fields_group.applicable_accessibilities = local
dotnet_naming_symbols.stylecop_local_fields_group.applicable_kinds = local
dotnet_naming_rule.stylecop_local_fields_must_be_camel_case_rule.symbols = stylecop_local_fields_group
dotnet_naming_rule.stylecop_local_fields_must_be_camel_case_rule.style = camel_case_style
dotnet_naming_rule.stylecop_local_fields_must_be_camel_case_rule.severity = warning
# This rule should never fire. However, it's included for at least two purposes:
# First, it helps to understand, reason about, and root-case certain types of issues, such as bugs in .editorconfig parsers.
# Second, it helps to raise immediate awareness if a new field type is added (as occurred recently in C#).
dotnet_naming_symbols.sanity_check_uncovered_field_case_group.applicable_accessibilities = *
dotnet_naming_symbols.sanity_check_uncovered_field_case_group.applicable_kinds = field
dotnet_naming_rule.sanity_check_uncovered_field_case_rule.symbols = sanity_check_uncovered_field_case_group
dotnet_naming_rule.sanity_check_uncovered_field_case_rule.style = internal_error_style
dotnet_naming_rule.sanity_check_uncovered_field_case_rule.severity = error
##########################################
# Other Naming Rules
##########################################
# All of the following must be PascalCase:
# - Namespaces
# https://docs.microsoft.com/dotnet/standard/design-guidelines/names-of-namespaces
# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1300.md
# - Classes and Enumerations
# https://docs.microsoft.com/dotnet/standard/design-guidelines/names-of-classes-structs-and-interfaces
# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1300.md
# - Delegates
# https://docs.microsoft.com/dotnet/standard/design-guidelines/names-of-classes-structs-and-interfaces#names-of-common-types
# - Constructors, Properties, Events, Methods
# https://docs.microsoft.com/dotnet/standard/design-guidelines/names-of-type-members
dotnet_naming_symbols.element_group.applicable_kinds = namespace, class, enum, struct, delegate, event, method, property
dotnet_naming_rule.element_rule.symbols = element_group
dotnet_naming_rule.element_rule.style = pascal_case_style
dotnet_naming_rule.element_rule.severity = warning
# Interfaces use PascalCase and are prefixed with uppercase 'I'
# https://docs.microsoft.com/dotnet/standard/design-guidelines/names-of-classes-structs-and-interfaces
dotnet_naming_symbols.interface_group.applicable_kinds = interface
dotnet_naming_rule.interface_rule.symbols = interface_group
dotnet_naming_rule.interface_rule.style = prefix_interface_with_i_style
dotnet_naming_rule.interface_rule.severity = warning
# Generics Type Parameters use PascalCase and are prefixed with uppercase 'T'
# https://docs.microsoft.com/dotnet/standard/design-guidelines/names-of-classes-structs-and-interfaces
dotnet_naming_symbols.type_parameter_group.applicable_kinds = type_parameter
dotnet_naming_rule.type_parameter_rule.symbols = type_parameter_group
dotnet_naming_rule.type_parameter_rule.style = prefix_type_parameters_with_t_style
dotnet_naming_rule.type_parameter_rule.severity = warning
# Function parameters use camelCase
# https://docs.microsoft.com/dotnet/standard/design-guidelines/naming-parameters
dotnet_naming_symbols.parameters_group.applicable_kinds = parameter
dotnet_naming_rule.parameters_rule.symbols = parameters_group
dotnet_naming_rule.parameters_rule.style = camel_case_style
dotnet_naming_rule.parameters_rule.severity = warning
##########################################
# License
##########################################
# The following applies as to the .editorconfig file ONLY, and is
# included below for reference, per the requirements of the license
# corresponding to this .editorconfig file.
# See: https://github.com/RehanSaeed/EditorConfig
#
# MIT License
#
# Copyright (c) 2017-2019 Muhammad Rehan Saeed
# Copyright (c) 2019 Henry Gabryjelski
#
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
# documentation files (the "Software"), to deal in the
# Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute,
# sublicense, and/or sell copies of the Software, and to permit
# persons to whom the Software is furnished to do so, subject
# to the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
##########################################

View File

@@ -1,6 +0,0 @@
version: 2
updates:
- package-ecosystem: "github-actions" # search for actions - there are other options available
directory: "/" # search in .github/workflows under root `/`
schedule:
interval: "weekly" # check for action update every week

View File

@@ -9,12 +9,16 @@ jobs:
os: [windows-latest, ubuntu-latest]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-dotnet@v2
- uses: actions/checkout@v1
- uses: actions/setup-dotnet@v1
with:
dotnet-version: 6.0.x
dotnet-version: 5.0.101
- run: dotnet run -p build/build.csproj
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v2
with:
name: ${{ matrix.os }}-sharpcompress.nupkg
path: artifacts/*
- uses: actions/upload-artifact@v2
with:
name: ${{ matrix.os }}-sharpcompress.snupkg
path: artifacts/*

1
.gitignore vendored
View File

@@ -18,4 +18,3 @@ tools
.DS_Store
*.snupkg
/tests/TestArchives/6d23a38c-f064-4ef1-ad89-b942396f53b9/Scratch

View File

@@ -19,6 +19,7 @@
| Tar.XZ | LZMA2 | Decompress | TarArchive | TarReader | TarWriter (3) |
| GZip (single file) | DEFLATE | Both | GZipArchive | GZipReader | GZipWriter |
| 7Zip (4) | LZMA, LZMA2, BZip2, PPMd, BCJ, BCJ2, Deflate | Decompress | SevenZipArchive | N/A | N/A |
| LZip (single file) (5) | LZip (LZMA) | Both | LZipArchive | LZipReader | LZipWriter |
1. SOLID Rars are only supported in the RarReader API.
2. Zip format supports pkware and WinzipAES encryption. However, encrypted LZMA is not supported. Zip64 reading/writing is supported but only with seekable streams as the Zip spec doesn't support Zip64 data in post data descriptors. Deflate64 is only supported for reading.

View File

@@ -182,8 +182,6 @@ I'm always looking for help or ideas. Please submit code or email with ideas. Un
XZ implementation based on: https://github.com/sambott/XZ.NET by @sambott
XZ BCJ filters support contributed by Louis-Michel Bergeron, on behalf of aDolus Technology Inc. - 2022
7Zip implementation based on: https://code.google.com/p/managed-lzma/
LICENSE

View File

@@ -6,78 +6,78 @@ using GlobExpressions;
using static Bullseye.Targets;
using static SimpleExec.Command;
const string Clean = "clean";
const string Format = "format";
const string Build = "build";
const string Test = "test";
const string Publish = "publish";
class Program
{
private const string Clean = "clean";
private const string Format = "format";
private const string Build = "build";
private const string Test = "test";
private const string Publish = "publish";
Target(Clean,
ForEach("**/bin", "**/obj"),
dir =>
{
IEnumerable<string> GetDirectories(string d)
{
return Glob.Directories(".", d);
}
static void Main(string[] args)
{
Target(Clean,
ForEach("**/bin", "**/obj"),
dir =>
{
IEnumerable<string> GetDirectories(string d)
{
return Glob.Directories(".", d);
}
void RemoveDirectory(string d)
{
if (Directory.Exists(d))
void RemoveDirectory(string d)
{
if (Directory.Exists(d))
{
Console.WriteLine(d);
Directory.Delete(d, true);
}
}
foreach (var d in GetDirectories(dir))
{
RemoveDirectory(d);
}
});
Target(Format, () =>
{
Run("dotnet", "tool restore");
Run("dotnet", "format --check");
});
Target(Build, DependsOn(Format),
framework =>
{
Console.WriteLine(d);
Directory.Delete(d, true);
}
}
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && framework == "net46")
{
return;
}
Run("dotnet", "build src/SharpCompress/SharpCompress.csproj -c Release");
});
foreach (var d in GetDirectories(dir))
{
RemoveDirectory(d);
}
});
Target(Test, DependsOn(Build), ForEach("net5.0"),
framework =>
{
IEnumerable<string> GetFiles(string d)
{
return Glob.Files(".", d);
}
Target(Format,
() =>
{
Run("dotnet", "tool restore");
Run("dotnet", "format --check");
});
foreach (var file in GetFiles("**/*.Test.csproj"))
{
Run("dotnet", $"test {file} -c Release -f {framework}");
}
});
Target(Build,
DependsOn(Format),
framework =>
{
Run("dotnet", "build src/SharpCompress/SharpCompress.csproj -c Release");
});
Target(Publish, DependsOn(Test),
() =>
{
Run("dotnet", "pack src/SharpCompress/SharpCompress.csproj -c Release -o artifacts/");
});
Target(Test,
DependsOn(Build),
ForEach("net6.0", "net461"),
framework =>
{
IEnumerable<string> GetFiles(string d)
{
return Glob.Files(".", d);
}
Target("default", DependsOn(Publish), () => Console.WriteLine("Done!"));
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && framework == "net461")
{
return;
}
foreach (var file in GetFiles("**/*.Test.csproj"))
{
Run("dotnet", $"test {file} -c Release -f {framework}");
}
});
Target(Publish,
DependsOn(Test),
() =>
{
Run("dotnet", "pack src/SharpCompress/SharpCompress.csproj -c Release -o artifacts/");
});
Target("default", DependsOn(Publish), () => Console.WriteLine("Done!"));
await RunTargetsAndExitAsync(args);
RunTargetsAndExit(args);
}
}

View File

@@ -2,13 +2,13 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Bullseye" Version="4.0.0" />
<PackageReference Include="Glob" Version="1.1.9" />
<PackageReference Include="SimpleExec" Version="10.0.0" />
<PackageReference Include="Bullseye" Version="3.6.0" />
<PackageReference Include="Glob" Version="1.1.8" />
<PackageReference Include="SimpleExec" Version="6.4.0" />
</ItemGroup>
</Project>

View File

@@ -1,6 +1,5 @@
{
"sdk": {
"version": "6.0.200",
"rollForward": "latestFeature"
"version": "5.0.101"
}
}

View File

@@ -1,420 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
#if !NETSTANDARD2_0 && !NETSTANDARD2_1 && !NETFRAMEWORK
#define SUPPORTS_RUNTIME_INTRINSICS
#define SUPPORTS_HOTPATH
#endif
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if SUPPORTS_RUNTIME_INTRINSICS
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
#endif
#pragma warning disable IDE0007 // Use implicit type
namespace SharpCompress.Algorithms
{
/// <summary>
/// Calculates the 32 bit Adler checksum of a given buffer according to
/// RFC 1950. ZLIB Compressed Data Format Specification version 3.3)
/// </summary>
internal static class Adler32 // From https://github.com/SixLabors/ImageSharp/blob/main/src/ImageSharp/Compression/Zlib/Adler32.cs
{
/// <summary>
/// Global inlining options. Helps temporarily disable inlining for better profiler output.
/// </summary>
private static class InliningOptions // From https://github.com/SixLabors/ImageSharp/blob/main/src/ImageSharp/Common/Helpers/InliningOptions.cs
{
/// <summary>
/// <see cref="MethodImplOptions.AggressiveInlining"/> regardless of the build conditions.
/// </summary>
public const MethodImplOptions AlwaysInline = MethodImplOptions.AggressiveInlining;
#if PROFILING
public const MethodImplOptions HotPath = MethodImplOptions.NoInlining;
public const MethodImplOptions ShortMethod = MethodImplOptions.NoInlining;
#else
#if SUPPORTS_HOTPATH
public const MethodImplOptions HotPath = MethodImplOptions.AggressiveOptimization;
#else
public const MethodImplOptions HotPath = MethodImplOptions.AggressiveInlining;
#endif
public const MethodImplOptions ShortMethod = MethodImplOptions.AggressiveInlining;
#endif
public const MethodImplOptions ColdPath = MethodImplOptions.NoInlining;
}
#if SUPPORTS_RUNTIME_INTRINSICS
/// <summary>
/// Provides optimized static methods for trigonometric, logarithmic,
/// and other common mathematical functions.
/// </summary>
private static class Numerics // From https://github.com/SixLabors/ImageSharp/blob/main/src/ImageSharp/Common/Helpers/Numerics.cs
{
/// <summary>
/// Reduces elements of the vector into one sum.
/// </summary>
/// <param name="accumulator">The accumulator to reduce.</param>
/// <returns>The sum of all elements.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int ReduceSum(Vector256<int> accumulator)
{
// Add upper lane to lower lane.
Vector128<int> vsum = Sse2.Add(accumulator.GetLower(), accumulator.GetUpper());
// Add odd to even.
vsum = Sse2.Add(vsum, Sse2.Shuffle(vsum, 0b_11_11_01_01));
// Add high to low.
vsum = Sse2.Add(vsum, Sse2.Shuffle(vsum, 0b_11_10_11_10));
return Sse2.ConvertToInt32(vsum);
}
/// <summary>
/// Reduces even elements of the vector into one sum.
/// </summary>
/// <param name="accumulator">The accumulator to reduce.</param>
/// <returns>The sum of even elements.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int EvenReduceSum(Vector256<int> accumulator)
{
Vector128<int> vsum = Sse2.Add(accumulator.GetLower(), accumulator.GetUpper()); // add upper lane to lower lane
vsum = Sse2.Add(vsum, Sse2.Shuffle(vsum, 0b_11_10_11_10)); // add high to low
// Vector128<int>.ToScalar() isn't optimized pre-net5.0 https://github.com/dotnet/runtime/pull/37882
return Sse2.ConvertToInt32(vsum);
}
}
#endif
/// <summary>
/// The default initial seed value of a Adler32 checksum calculation.
/// </summary>
public const uint SeedValue = 1U;
// Largest prime smaller than 65536
private const uint BASE = 65521;
// NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1
private const uint NMAX = 5552;
#if SUPPORTS_RUNTIME_INTRINSICS
private const int MinBufferSize = 64;
private const int BlockSize = 1 << 5;
// The C# compiler emits this as a compile-time constant embedded in the PE file.
private static ReadOnlySpan<byte> Tap1Tap2 => new byte[]
{
32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, // tap1
16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 // tap2
};
#endif
/// <summary>
/// Calculates the Adler32 checksum with the bytes taken from the span.
/// </summary>
/// <param name="buffer">The readonly span of bytes.</param>
/// <returns>The <see cref="uint"/>.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static uint Calculate(ReadOnlySpan<byte> buffer)
=> Calculate(SeedValue, buffer);
/// <summary>
/// Calculates the Adler32 checksum with the bytes taken from the span and seed.
/// </summary>
/// <param name="adler">The input Adler32 value.</param>
/// <param name="buffer">The readonly span of bytes.</param>
/// <returns>The <see cref="uint"/>.</returns>
[MethodImpl(InliningOptions.HotPath | InliningOptions.ShortMethod)]
public static uint Calculate(uint adler, ReadOnlySpan<byte> buffer)
{
if (buffer.IsEmpty)
{
return adler;
}
#if SUPPORTS_RUNTIME_INTRINSICS
if (Avx2.IsSupported && buffer.Length >= MinBufferSize)
{
return CalculateAvx2(adler, buffer);
}
if (Ssse3.IsSupported && buffer.Length >= MinBufferSize)
{
return CalculateSse(adler, buffer);
}
return CalculateScalar(adler, buffer);
#else
return CalculateScalar(adler, buffer);
#endif
}
// Based on https://github.com/chromium/chromium/blob/master/third_party/zlib/adler32_simd.c
#if SUPPORTS_RUNTIME_INTRINSICS
[MethodImpl(InliningOptions.HotPath | InliningOptions.ShortMethod)]
private static unsafe uint CalculateSse(uint adler, ReadOnlySpan<byte> buffer)
{
uint s1 = adler & 0xFFFF;
uint s2 = (adler >> 16) & 0xFFFF;
// Process the data in blocks.
uint length = (uint)buffer.Length;
uint blocks = length / BlockSize;
length -= blocks * BlockSize;
fixed (byte* bufferPtr = &MemoryMarshal.GetReference(buffer))
{
fixed (byte* tapPtr = &MemoryMarshal.GetReference(Tap1Tap2))
{
byte* localBufferPtr = bufferPtr;
// _mm_setr_epi8 on x86
Vector128<sbyte> tap1 = Sse2.LoadVector128((sbyte*)tapPtr);
Vector128<sbyte> tap2 = Sse2.LoadVector128((sbyte*)(tapPtr + 0x10));
Vector128<byte> zero = Vector128<byte>.Zero;
var ones = Vector128.Create((short)1);
while (blocks > 0)
{
uint n = NMAX / BlockSize; /* The NMAX constraint. */
if (n > blocks)
{
n = blocks;
}
blocks -= n;
// Process n blocks of data. At most NMAX data bytes can be
// processed before s2 must be reduced modulo BASE.
Vector128<uint> v_ps = Vector128.CreateScalar(s1 * n);
Vector128<uint> v_s2 = Vector128.CreateScalar(s2);
Vector128<uint> v_s1 = Vector128<uint>.Zero;
do
{
// Load 32 input bytes.
Vector128<byte> bytes1 = Sse3.LoadDquVector128(localBufferPtr);
Vector128<byte> bytes2 = Sse3.LoadDquVector128(localBufferPtr + 0x10);
// Add previous block byte sum to v_ps.
v_ps = Sse2.Add(v_ps, v_s1);
// Horizontally add the bytes for s1, multiply-adds the
// bytes by [ 32, 31, 30, ... ] for s2.
v_s1 = Sse2.Add(v_s1, Sse2.SumAbsoluteDifferences(bytes1, zero).AsUInt32());
Vector128<short> mad1 = Ssse3.MultiplyAddAdjacent(bytes1, tap1);
v_s2 = Sse2.Add(v_s2, Sse2.MultiplyAddAdjacent(mad1, ones).AsUInt32());
v_s1 = Sse2.Add(v_s1, Sse2.SumAbsoluteDifferences(bytes2, zero).AsUInt32());
Vector128<short> mad2 = Ssse3.MultiplyAddAdjacent(bytes2, tap2);
v_s2 = Sse2.Add(v_s2, Sse2.MultiplyAddAdjacent(mad2, ones).AsUInt32());
localBufferPtr += BlockSize;
}
while (--n > 0);
v_s2 = Sse2.Add(v_s2, Sse2.ShiftLeftLogical(v_ps, 5));
// Sum epi32 ints v_s1(s2) and accumulate in s1(s2).
const byte S2301 = 0b1011_0001; // A B C D -> B A D C
const byte S1032 = 0b0100_1110; // A B C D -> C D A B
v_s1 = Sse2.Add(v_s1, Sse2.Shuffle(v_s1, S1032));
s1 += v_s1.ToScalar();
v_s2 = Sse2.Add(v_s2, Sse2.Shuffle(v_s2, S2301));
v_s2 = Sse2.Add(v_s2, Sse2.Shuffle(v_s2, S1032));
s2 = v_s2.ToScalar();
// Reduce.
s1 %= BASE;
s2 %= BASE;
}
if (length > 0)
{
HandleLeftOver(localBufferPtr, length, ref s1, ref s2);
}
return s1 | (s2 << 16);
}
}
}
// Based on: https://github.com/zlib-ng/zlib-ng/blob/develop/arch/x86/adler32_avx2.c
[MethodImpl(InliningOptions.HotPath | InliningOptions.ShortMethod)]
public static unsafe uint CalculateAvx2(uint adler, ReadOnlySpan<byte> buffer)
{
uint s1 = adler & 0xFFFF;
uint s2 = (adler >> 16) & 0xFFFF;
uint length = (uint)buffer.Length;
fixed (byte* bufferPtr = &MemoryMarshal.GetReference(buffer))
{
byte* localBufferPtr = bufferPtr;
Vector256<byte> zero = Vector256<byte>.Zero;
var dot3v = Vector256.Create((short)1);
var dot2v = Vector256.Create(32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1);
// Process n blocks of data. At most NMAX data bytes can be
// processed before s2 must be reduced modulo BASE.
var vs1 = Vector256.CreateScalar(s1);
var vs2 = Vector256.CreateScalar(s2);
while (length >= 32)
{
int k = length < NMAX ? (int)length : (int)NMAX;
k -= k % 32;
length -= (uint)k;
Vector256<uint> vs10 = vs1;
Vector256<uint> vs3 = Vector256<uint>.Zero;
while (k >= 32)
{
// Load 32 input bytes.
Vector256<byte> block = Avx.LoadVector256(localBufferPtr);
// Sum of abs diff, resulting in 2 x int32's
Vector256<ushort> vs1sad = Avx2.SumAbsoluteDifferences(block, zero);
vs1 = Avx2.Add(vs1, vs1sad.AsUInt32());
vs3 = Avx2.Add(vs3, vs10);
// sum 32 uint8s to 16 shorts.
Vector256<short> vshortsum2 = Avx2.MultiplyAddAdjacent(block, dot2v);
// sum 16 shorts to 8 uint32s.
Vector256<int> vsum2 = Avx2.MultiplyAddAdjacent(vshortsum2, dot3v);
vs2 = Avx2.Add(vsum2.AsUInt32(), vs2);
vs10 = vs1;
localBufferPtr += BlockSize;
k -= 32;
}
// Defer the multiplication with 32 to outside of the loop.
vs3 = Avx2.ShiftLeftLogical(vs3, 5);
vs2 = Avx2.Add(vs2, vs3);
s1 = (uint)Numerics.EvenReduceSum(vs1.AsInt32());
s2 = (uint)Numerics.ReduceSum(vs2.AsInt32());
s1 %= BASE;
s2 %= BASE;
vs1 = Vector256.CreateScalar(s1);
vs2 = Vector256.CreateScalar(s2);
}
if (length > 0)
{
HandleLeftOver(localBufferPtr, length, ref s1, ref s2);
}
return s1 | (s2 << 16);
}
}
private static unsafe void HandleLeftOver(byte* localBufferPtr, uint length, ref uint s1, ref uint s2)
{
if (length >= 16)
{
s2 += s1 += localBufferPtr[0];
s2 += s1 += localBufferPtr[1];
s2 += s1 += localBufferPtr[2];
s2 += s1 += localBufferPtr[3];
s2 += s1 += localBufferPtr[4];
s2 += s1 += localBufferPtr[5];
s2 += s1 += localBufferPtr[6];
s2 += s1 += localBufferPtr[7];
s2 += s1 += localBufferPtr[8];
s2 += s1 += localBufferPtr[9];
s2 += s1 += localBufferPtr[10];
s2 += s1 += localBufferPtr[11];
s2 += s1 += localBufferPtr[12];
s2 += s1 += localBufferPtr[13];
s2 += s1 += localBufferPtr[14];
s2 += s1 += localBufferPtr[15];
localBufferPtr += 16;
length -= 16;
}
while (length-- > 0)
{
s2 += s1 += *localBufferPtr++;
}
if (s1 >= BASE)
{
s1 -= BASE;
}
s2 %= BASE;
}
#endif
[MethodImpl(InliningOptions.HotPath | InliningOptions.ShortMethod)]
private static unsafe uint CalculateScalar(uint adler, ReadOnlySpan<byte> buffer)
{
uint s1 = adler & 0xFFFF;
uint s2 = (adler >> 16) & 0xFFFF;
uint k;
fixed (byte* bufferPtr = buffer)
{
var localBufferPtr = bufferPtr;
uint length = (uint)buffer.Length;
while (length > 0)
{
k = length < NMAX ? length : NMAX;
length -= k;
while (k >= 16)
{
s2 += s1 += localBufferPtr[0];
s2 += s1 += localBufferPtr[1];
s2 += s1 += localBufferPtr[2];
s2 += s1 += localBufferPtr[3];
s2 += s1 += localBufferPtr[4];
s2 += s1 += localBufferPtr[5];
s2 += s1 += localBufferPtr[6];
s2 += s1 += localBufferPtr[7];
s2 += s1 += localBufferPtr[8];
s2 += s1 += localBufferPtr[9];
s2 += s1 += localBufferPtr[10];
s2 += s1 += localBufferPtr[11];
s2 += s1 += localBufferPtr[12];
s2 += s1 += localBufferPtr[13];
s2 += s1 += localBufferPtr[14];
s2 += s1 += localBufferPtr[15];
localBufferPtr += 16;
k -= 16;
}
while (k-- > 0)
{
s2 += s1 += *localBufferPtr++;
}
s1 %= BASE;
s2 %= BASE;
}
return (s2 << 16) | s1;
}
}
}
}

View File

@@ -0,0 +1,285 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the GNU Affero General Public License, Version 3.
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if !NETSTANDARD2_0 && !NETSTANDARD2_1
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
#endif
namespace SharpCompress.Algorithms
{
/// <summary>
/// Calculates the 32 bit Adler checksum of a given buffer according to
/// RFC 1950. ZLIB Compressed Data Format Specification version 3.3)
/// </summary>
internal static class Adler32
{
/// <summary>
/// The default initial seed value of a Adler32 checksum calculation.
/// </summary>
public const uint SeedValue = 1U;
#if !NETSTANDARD2_0 && !NETSTANDARD2_1
private const int MinBufferSize = 64;
#endif
// Largest prime smaller than 65536
private const uint BASE = 65521;
// NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1
private const uint NMAX = 5552;
/// <summary>
/// Calculates the Adler32 checksum with the bytes taken from the span.
/// </summary>
/// <param name="buffer">The readonly span of bytes.</param>
/// <returns>The <see cref="uint"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint Calculate(ReadOnlySpan<byte> buffer)
{
return Calculate(SeedValue, buffer);
}
/// <summary>
/// Calculates the Adler32 checksum with the bytes taken from the span and seed.
/// </summary>
/// <param name="adler">The input Adler32 value.</param>
/// <param name="buffer">The readonly span of bytes.</param>
/// <returns>The <see cref="uint"/>.</returns>
public static uint Calculate(uint adler, ReadOnlySpan<byte> buffer)
{
if (buffer.IsEmpty)
{
return SeedValue;
}
#if !NETSTANDARD2_0 && !NETSTANDARD2_1
if (Sse3.IsSupported && buffer.Length >= MinBufferSize)
{
return CalculateSse(adler, buffer);
}
return CalculateScalar(adler, buffer);
#else
return CalculateScalar(adler, buffer);
#endif
}
// Based on https://github.com/chromium/chromium/blob/master/third_party/zlib/adler32_simd.c
#if !NETSTANDARD2_0 && !NETSTANDARD2_1
private static unsafe uint CalculateSse(uint adler, ReadOnlySpan<byte> buffer)
{
uint s1 = adler & 0xFFFF;
uint s2 = (adler >> 16) & 0xFFFF;
// Process the data in blocks.
const int BLOCK_SIZE = 1 << 5;
uint length = (uint)buffer.Length;
uint blocks = length / BLOCK_SIZE;
length -= blocks * BLOCK_SIZE;
int index = 0;
fixed (byte* bufferPtr = &buffer[0])
{
index += (int)blocks * BLOCK_SIZE;
var localBufferPtr = bufferPtr;
// _mm_setr_epi8 on x86
var tap1 = Vector128.Create(32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17);
var tap2 = Vector128.Create(16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1);
Vector128<byte> zero = Vector128<byte>.Zero;
var ones = Vector128.Create((short)1);
while (blocks > 0)
{
uint n = NMAX / BLOCK_SIZE; /* The NMAX constraint. */
if (n > blocks)
{
n = blocks;
}
blocks -= n;
// Process n blocks of data. At most NMAX data bytes can be
// processed before s2 must be reduced modulo BASE.
Vector128<int> v_ps = Vector128.CreateScalar(s1 * n).AsInt32();
Vector128<int> v_s2 = Vector128.CreateScalar(s2).AsInt32();
Vector128<int> v_s1 = Vector128<int>.Zero;
do
{
// Load 32 input bytes.
Vector128<byte> bytes1 = Sse3.LoadDquVector128(localBufferPtr);
Vector128<byte> bytes2 = Sse3.LoadDquVector128(localBufferPtr + 16);
// Add previous block byte sum to v_ps.
v_ps = Sse2.Add(v_ps, v_s1);
// Horizontally add the bytes for s1, multiply-adds the
// bytes by [ 32, 31, 30, ... ] for s2.
v_s1 = Sse2.Add(v_s1, Sse2.SumAbsoluteDifferences(bytes1, zero).AsInt32());
Vector128<short> mad1 = Ssse3.MultiplyAddAdjacent(bytes1, tap1);
v_s2 = Sse2.Add(v_s2, Sse2.MultiplyAddAdjacent(mad1, ones));
v_s1 = Sse2.Add(v_s1, Sse2.SumAbsoluteDifferences(bytes2, zero).AsInt32());
Vector128<short> mad2 = Ssse3.MultiplyAddAdjacent(bytes2, tap2);
v_s2 = Sse2.Add(v_s2, Sse2.MultiplyAddAdjacent(mad2, ones));
localBufferPtr += BLOCK_SIZE;
}
while (--n > 0);
v_s2 = Sse2.Add(v_s2, Sse2.ShiftLeftLogical(v_ps, 5));
// Sum epi32 ints v_s1(s2) and accumulate in s1(s2).
const byte S2301 = 0b1011_0001; // A B C D -> B A D C
const byte S1032 = 0b0100_1110; // A B C D -> C D A B
v_s1 = Sse2.Add(v_s1, Sse2.Shuffle(v_s1, S2301));
v_s1 = Sse2.Add(v_s1, Sse2.Shuffle(v_s1, S1032));
s1 += (uint)v_s1.ToScalar();
v_s2 = Sse2.Add(v_s2, Sse2.Shuffle(v_s2, S2301));
v_s2 = Sse2.Add(v_s2, Sse2.Shuffle(v_s2, S1032));
s2 = (uint)v_s2.ToScalar();
// Reduce.
s1 %= BASE;
s2 %= BASE;
}
}
ref byte bufferRef = ref MemoryMarshal.GetReference(buffer);
if (length > 0)
{
if (length >= 16)
{
s1 += Unsafe.Add(ref bufferRef, index++);
s2 += s1;
s1 += Unsafe.Add(ref bufferRef, index++);
s2 += s1;
s1 += Unsafe.Add(ref bufferRef, index++);
s2 += s1;
s1 += Unsafe.Add(ref bufferRef, index++);
s2 += s1;
s1 += Unsafe.Add(ref bufferRef, index++);
s2 += s1;
s1 += Unsafe.Add(ref bufferRef, index++);
s2 += s1;
s1 += Unsafe.Add(ref bufferRef, index++);
s2 += s1;
s1 += Unsafe.Add(ref bufferRef, index++);
s2 += s1;
s1 += Unsafe.Add(ref bufferRef, index++);
s2 += s1;
s1 += Unsafe.Add(ref bufferRef, index++);
s2 += s1;
s1 += Unsafe.Add(ref bufferRef, index++);
s2 += s1;
s1 += Unsafe.Add(ref bufferRef, index++);
s2 += s1;
s1 += Unsafe.Add(ref bufferRef, index++);
s2 += s1;
s1 += Unsafe.Add(ref bufferRef, index++);
s2 += s1;
s1 += Unsafe.Add(ref bufferRef, index++);
s2 += s1;
s1 += Unsafe.Add(ref bufferRef, index++);
s2 += s1;
length -= 16;
}
while (length-- > 0)
{
s2 += s1 += Unsafe.Add(ref bufferRef, index++);
}
if (s1 >= BASE)
{
s1 -= BASE;
}
s2 %= BASE;
}
return s1 | (s2 << 16);
}
#endif
private static uint CalculateScalar(uint adler, ReadOnlySpan<byte> buffer)
{
uint s1 = adler & 0xFFFF;
uint s2 = (adler >> 16) & 0xFFFF;
uint k;
ref byte bufferRef = ref MemoryMarshal.GetReference<byte>(buffer);
uint length = (uint)buffer.Length;
int index = 0;
while (length > 0)
{
k = length < NMAX ? length : NMAX;
length -= k;
while (k >= 16)
{
s1 += Unsafe.Add(ref bufferRef, index++);
s2 += s1;
s1 += Unsafe.Add(ref bufferRef, index++);
s2 += s1;
s1 += Unsafe.Add(ref bufferRef, index++);
s2 += s1;
s1 += Unsafe.Add(ref bufferRef, index++);
s2 += s1;
s1 += Unsafe.Add(ref bufferRef, index++);
s2 += s1;
s1 += Unsafe.Add(ref bufferRef, index++);
s2 += s1;
s1 += Unsafe.Add(ref bufferRef, index++);
s2 += s1;
s1 += Unsafe.Add(ref bufferRef, index++);
s2 += s1;
s1 += Unsafe.Add(ref bufferRef, index++);
s2 += s1;
s1 += Unsafe.Add(ref bufferRef, index++);
s2 += s1;
s1 += Unsafe.Add(ref bufferRef, index++);
s2 += s1;
s1 += Unsafe.Add(ref bufferRef, index++);
s2 += s1;
s1 += Unsafe.Add(ref bufferRef, index++);
s2 += s1;
s1 += Unsafe.Add(ref bufferRef, index++);
s2 += s1;
s1 += Unsafe.Add(ref bufferRef, index++);
s2 += s1;
s1 += Unsafe.Add(ref bufferRef, index++);
s2 += s1;
k -= 16;
}
if (k != 0)
{
do
{
s1 += Unsafe.Add(ref bufferRef, index++);
s2 += s1;
}
while (--k != 0);
}
s1 %= BASE;
s2 %= BASE;
}
return (s2 << 16) | s1;
}
}
}

View File

@@ -1,61 +1,58 @@
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
using SharpCompress.IO;
using SharpCompress.Readers;
namespace SharpCompress.Archives
{
public abstract class AbstractArchive<TEntry, TVolume> : IArchive, IArchiveExtractionListener
public abstract class AbstractArchive<TEntry, TVolume> : IArchive
where TEntry : IArchiveEntry
where TVolume : IVolume
{
private readonly LazyReadOnlyCollection<TVolume> lazyVolumes;
private readonly LazyReadOnlyCollection<TEntry> lazyEntries;
public event EventHandler<ArchiveExtractionEventArgs<IArchiveEntry>>? EntryExtractionBegin;
public event EventHandler<ArchiveExtractionEventArgs<IArchiveEntry>>? EntryExtractionEnd;
public event EventHandler<CompressedBytesReadEventArgs>? CompressedBytesRead;
public event EventHandler<FilePartExtractionBeginEventArgs>? FilePartExtractionBegin;
protected ReaderOptions ReaderOptions { get; }
protected ReaderOptions ReaderOptions { get; } = new ();
private bool disposed;
protected SourceStream SrcStream;
internal AbstractArchive(ArchiveType type, SourceStream srcStream)
internal AbstractArchive(ArchiveType type, FileInfo fileInfo, ReaderOptions readerOptions, CancellationToken cancellationToken)
{
Type = type;
ReaderOptions = srcStream.ReaderOptions;
SrcStream = srcStream;
lazyVolumes = new LazyReadOnlyCollection<TVolume>(LoadVolumes(SrcStream));
lazyEntries = new LazyReadOnlyCollection<TEntry>(LoadEntries(Volumes));
if (!fileInfo.Exists)
{
throw new ArgumentException("File does not exist: " + fileInfo.FullName);
}
ReaderOptions = readerOptions;
readerOptions.LeaveStreamOpen = false;
lazyVolumes = new LazyReadOnlyCollection<TVolume>(LoadVolumes(fileInfo, cancellationToken));
lazyEntries = new LazyReadOnlyCollection<TEntry>(LoadEntries(Volumes, cancellationToken));
}
protected abstract IAsyncEnumerable<TVolume> LoadVolumes(FileInfo file, CancellationToken cancellationToken);
internal AbstractArchive(ArchiveType type, IAsyncEnumerable<Stream> streams, ReaderOptions readerOptions, CancellationToken cancellationToken)
{
Type = type;
ReaderOptions = readerOptions;
lazyVolumes = new LazyReadOnlyCollection<TVolume>(LoadVolumes(streams.Select(CheckStreams), cancellationToken));
lazyEntries = new LazyReadOnlyCollection<TEntry>(LoadEntries(Volumes, cancellationToken));
}
#nullable disable
internal AbstractArchive(ArchiveType type)
{
Type = type;
lazyVolumes = new LazyReadOnlyCollection<TVolume>(Enumerable.Empty<TVolume>());
lazyEntries = new LazyReadOnlyCollection<TEntry>(Enumerable.Empty<TEntry>());
lazyVolumes = new LazyReadOnlyCollection<TVolume>( AsyncEnumerable.Empty<TVolume>());
lazyEntries = new LazyReadOnlyCollection<TEntry>(AsyncEnumerable.Empty<TEntry>());
}
#nullable enable
public ArchiveType Type { get; }
void IArchiveExtractionListener.FireEntryExtractionBegin(IArchiveEntry entry)
{
EntryExtractionBegin?.Invoke(this, new ArchiveExtractionEventArgs<IArchiveEntry>(entry));
}
void IArchiveExtractionListener.FireEntryExtractionEnd(IArchiveEntry entry)
{
EntryExtractionEnd?.Invoke(this, new ArchiveExtractionEventArgs<IArchiveEntry>(entry));
}
private static Stream CheckStreams(Stream stream)
{
if (!stream.CanSeek || !stream.CanRead)
@@ -68,65 +65,48 @@ namespace SharpCompress.Archives
/// <summary>
/// Returns an ReadOnlyCollection of all the RarArchiveEntries across the one or many parts of the RarArchive.
/// </summary>
public virtual ICollection<TEntry> Entries => lazyEntries;
public virtual IAsyncEnumerable<TEntry> Entries => lazyEntries;
/// <summary>
/// Returns an ReadOnlyCollection of all the RarArchiveVolumes across the one or many parts of the RarArchive.
/// </summary>
public ICollection<TVolume> Volumes => lazyVolumes;
public IAsyncEnumerable<TVolume> Volumes => lazyVolumes;
/// <summary>
/// The total size of the files compressed in the archive.
/// </summary>
public virtual long TotalSize => Entries.Aggregate(0L, (total, cf) => total + cf.CompressedSize);
public virtual async ValueTask<long> TotalSizeAsync()
{
await EnsureEntriesLoaded();
return await Entries.AggregateAsync(0L, (total, cf) => total + cf.CompressedSize);
}
/// <summary>
/// The total size of the files as uncompressed in the archive.
/// </summary>
public virtual long TotalUncompressSize => Entries.Aggregate(0L, (total, cf) => total + cf.Size);
public virtual async ValueTask<long> TotalUncompressedSizeAsync()
{
await EnsureEntriesLoaded();
return await Entries.AggregateAsync(0L, (total, cf) => total + cf.Size);
}
protected abstract IEnumerable<TVolume> LoadVolumes(SourceStream srcStream);
protected abstract IEnumerable<TEntry> LoadEntries(IEnumerable<TVolume> volumes);
protected abstract IAsyncEnumerable<TVolume> LoadVolumes(IAsyncEnumerable<Stream> streams, CancellationToken cancellationToken);
protected abstract IAsyncEnumerable<TEntry> LoadEntries(IAsyncEnumerable<TVolume> volumes, CancellationToken cancellationToken);
IEnumerable<IArchiveEntry> IArchive.Entries => Entries.Cast<IArchiveEntry>();
IAsyncEnumerable<IArchiveEntry> IArchive.Entries => Entries.Select(x => (IArchiveEntry)x);
IEnumerable<IVolume> IArchive.Volumes => lazyVolumes.Cast<IVolume>();
IAsyncEnumerable<IVolume> IArchive.Volumes => lazyVolumes.Select(x => (IVolume)x);
public virtual void Dispose()
public virtual async ValueTask DisposeAsync()
{
if (!disposed)
{
lazyVolumes.ForEach(v => v.Dispose());
lazyEntries.GetLoaded().Cast<Entry>().ForEach(x => x.Close());
if (SrcStream != null)
SrcStream.Dispose();
await lazyVolumes.ForEachAsync(async v => await v.DisposeAsync());
await lazyEntries.GetLoaded().Cast<Entry>().ForEachAsync(async x => await x.CloseAsync());
disposed = true;
}
}
void IArchiveExtractionListener.EnsureEntriesLoaded()
{
lazyEntries.EnsureFullyLoaded();
lazyVolumes.EnsureFullyLoaded();
}
void IExtractionListener.FireCompressedBytesRead(long currentPartCompressedBytes, long compressedReadBytes)
{
CompressedBytesRead?.Invoke(this, new CompressedBytesReadEventArgs(
currentFilePartCompressedBytesRead: currentPartCompressedBytes,
compressedBytesRead: compressedReadBytes
));
}
void IExtractionListener.FireFilePartExtractionBegin(string name, long size, long compressedSize)
{
FilePartExtractionBegin?.Invoke(this, new FilePartExtractionBeginEventArgs(
compressedSize: compressedSize,
size: size,
name: name
));
}
/// <summary>
/// Use this method to extract all entries in an archive in order.
/// This is primarily for SOLID Rar Archives or 7Zip Archives as they need to be
@@ -138,29 +118,32 @@ namespace SharpCompress.Archives
/// occur if this is used at the same time as other extraction methods on this instance.
/// </summary>
/// <returns></returns>
public IReader ExtractAllEntries()
public async ValueTask<IReader> ExtractAllEntries()
{
((IArchiveExtractionListener)this).EnsureEntriesLoaded();
return CreateReaderForSolidExtraction();
await EnsureEntriesLoaded();
return await CreateReaderForSolidExtraction();
}
public async ValueTask EnsureEntriesLoaded()
{
await lazyEntries.EnsureFullyLoaded();
await lazyVolumes.EnsureFullyLoaded();
}
protected abstract IReader CreateReaderForSolidExtraction();
protected abstract ValueTask<IReader> CreateReaderForSolidExtraction();
/// <summary>
/// Archive is SOLID (this means the Archive saved bytes by reusing information which helps for archives containing many small files).
/// </summary>
public virtual bool IsSolid => false;
public virtual ValueTask<bool> IsSolidAsync() => new(false);
/// <summary>
/// The archive can find all the parts of the archive needed to fully extract the archive. This forces the parsing of the entire archive.
/// </summary>
public bool IsComplete
public async ValueTask<bool> IsCompleteAsync()
{
get
{
((IArchiveExtractionListener)this).EnsureEntriesLoaded();
return Entries.All(x => x.IsComplete);
}
await EnsureEntriesLoaded();
return await Entries.AllAsync(x => x.IsComplete);
}
}
}

View File

@@ -1,9 +1,10 @@
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
using SharpCompress.IO;
using SharpCompress.Readers;
using SharpCompress.Writers;
@@ -13,7 +14,7 @@ namespace SharpCompress.Archives
where TEntry : IArchiveEntry
where TVolume : IVolume
{
private class RebuildPauseDisposable : IDisposable
private class RebuildPauseDisposable : IAsyncDisposable
{
private readonly AbstractWritableArchive<TEntry, TVolume> archive;
@@ -23,16 +24,16 @@ namespace SharpCompress.Archives
archive.pauseRebuilding = true;
}
public void Dispose()
public async ValueTask DisposeAsync()
{
archive.pauseRebuilding = false;
archive.RebuildModifiedCollection();
await archive.RebuildModifiedCollection();
}
}
private readonly List<TEntry> newEntries = new List<TEntry>();
private readonly List<TEntry> removedEntries = new List<TEntry>();
private readonly List<TEntry> newEntries = new();
private readonly List<TEntry> removedEntries = new();
private readonly List<TEntry> modifiedEntries = new List<TEntry>();
private readonly List<TEntry> modifiedEntries = new();
private bool hasModifications;
private bool pauseRebuilding;
@@ -41,29 +42,36 @@ namespace SharpCompress.Archives
{
}
internal AbstractWritableArchive(ArchiveType type, SourceStream srcStream)
: base(type, srcStream)
internal AbstractWritableArchive(ArchiveType type, Stream stream, ReaderOptions readerFactoryOptions,
CancellationToken cancellationToken)
: base(type, stream.AsAsyncEnumerable(), readerFactoryOptions, cancellationToken)
{
}
public override ICollection<TEntry> Entries
internal AbstractWritableArchive(ArchiveType type, FileInfo fileInfo, ReaderOptions readerFactoryOptions,
CancellationToken cancellationToken)
: base(type, fileInfo, readerFactoryOptions, cancellationToken)
{
}
public override IAsyncEnumerable<TEntry> Entries
{
get
{
if (hasModifications)
{
return modifiedEntries;
return modifiedEntries.ToAsyncEnumerable();
}
return base.Entries;
}
}
public IDisposable PauseEntryRebuilding()
public IAsyncDisposable PauseEntryRebuilding()
{
return new RebuildPauseDisposable(this);
}
private void RebuildModifiedCollection()
private async ValueTask RebuildModifiedCollection()
{
if (pauseRebuilding)
{
@@ -72,56 +80,57 @@ namespace SharpCompress.Archives
hasModifications = true;
newEntries.RemoveAll(v => removedEntries.Contains(v));
modifiedEntries.Clear();
modifiedEntries.AddRange(OldEntries.Concat(newEntries));
modifiedEntries.AddRange(await OldEntries.Concat(newEntries.ToAsyncEnumerable()).ToListAsync());
}
private IEnumerable<TEntry> OldEntries { get { return base.Entries.Where(x => !removedEntries.Contains(x)); } }
private IAsyncEnumerable<TEntry> OldEntries { get { return base.Entries.Where(x => !removedEntries.Contains(x)); } }
public void RemoveEntry(TEntry entry)
public async ValueTask RemoveEntryAsync(TEntry entry)
{
if (!removedEntries.Contains(entry))
{
removedEntries.Add(entry);
RebuildModifiedCollection();
await RebuildModifiedCollection();
}
}
void IWritableArchive.RemoveEntry(IArchiveEntry entry)
ValueTask IWritableArchive.RemoveEntryAsync(IArchiveEntry entry, CancellationToken cancellationToken)
{
RemoveEntry((TEntry)entry);
return RemoveEntryAsync((TEntry)entry);
}
public TEntry AddEntry(string key, Stream source,
long size = 0, DateTime? modified = null)
public ValueTask<TEntry> AddEntryAsync(string key, Stream source,
long size = 0, DateTime? modified = null,
CancellationToken cancellationToken = default)
{
return AddEntry(key, source, false, size, modified);
return AddEntryAsync(key, source, false, size, modified, cancellationToken);
}
IArchiveEntry IWritableArchive.AddEntry(string key, Stream source, bool closeStream, long size, DateTime? modified)
async ValueTask<IArchiveEntry> IWritableArchive.AddEntryAsync(string key, Stream source, bool closeStream, long size, DateTime? modified, CancellationToken cancellationToken)
{
return AddEntry(key, source, closeStream, size, modified);
return await AddEntryAsync(key, source, closeStream, size, modified, cancellationToken);
}
public TEntry AddEntry(string key, Stream source, bool closeStream,
long size = 0, DateTime? modified = null)
public async ValueTask<TEntry> AddEntryAsync(string key, Stream source, bool closeStream,
long size = 0, DateTime? modified = null, CancellationToken cancellationToken = default)
{
if (key.Length > 0 && key[0] is '/' or '\\')
{
key = key.Substring(1);
}
if (DoesKeyMatchExisting(key))
if (await DoesKeyMatchExisting(key))
{
throw new ArchiveException("Cannot add entry with duplicate key: " + key);
}
var entry = CreateEntry(key, source, size, modified, closeStream);
var entry = await CreateEntry(key, source, size, modified, closeStream, cancellationToken);
newEntries.Add(entry);
RebuildModifiedCollection();
await RebuildModifiedCollection();
return entry;
}
private bool DoesKeyMatchExisting(string key)
private async ValueTask<bool> DoesKeyMatchExisting(string key)
{
foreach (var path in Entries.Select(x => x.Key))
await foreach (var path in Entries.Select(x => x.Key))
{
var p = path.Replace('/', '\\');
if (p.Length > 0 && p[0] == '\\')
@@ -133,34 +142,35 @@ namespace SharpCompress.Archives
return false;
}
public void SaveTo(Stream stream, WriterOptions options)
public async ValueTask SaveToAsync(Stream stream, WriterOptions options, CancellationToken cancellationToken = default)
{
//reset streams of new entries
newEntries.Cast<IWritableArchiveEntry>().ForEach(x => x.Stream.Seek(0, SeekOrigin.Begin));
SaveTo(stream, options, OldEntries, newEntries);
await SaveToAsync(stream, options, OldEntries, newEntries.ToAsyncEnumerable(), cancellationToken);
}
protected TEntry CreateEntry(string key, Stream source, long size, DateTime? modified,
bool closeStream)
protected ValueTask<TEntry> CreateEntry(string key, Stream source, long size, DateTime? modified,
bool closeStream, CancellationToken cancellationToken)
{
if (!source.CanRead || !source.CanSeek)
{
throw new ArgumentException("Streams must be readable and seekable to use the Writing Archive API");
}
return CreateEntryInternal(key, source, size, modified, closeStream);
return CreateEntryInternal(key, source, size, modified, closeStream, cancellationToken);
}
protected abstract TEntry CreateEntryInternal(string key, Stream source, long size, DateTime? modified,
bool closeStream);
protected abstract ValueTask<TEntry> CreateEntryInternal(string key, Stream source, long size, DateTime? modified,
bool closeStream, CancellationToken cancellationToken);
protected abstract void SaveTo(Stream stream, WriterOptions options, IEnumerable<TEntry> oldEntries, IEnumerable<TEntry> newEntries);
protected abstract ValueTask SaveToAsync(Stream stream, WriterOptions options, IAsyncEnumerable<TEntry> oldEntries, IAsyncEnumerable<TEntry> newEntries,
CancellationToken cancellationToken = default);
public override void Dispose()
public override async ValueTask DisposeAsync()
{
base.Dispose();
newEntries.Cast<Entry>().ForEach(x => x.Close());
removedEntries.Cast<Entry>().ForEach(x => x.Close());
modifiedEntries.Cast<Entry>().ForEach(x => x.Close());
await base.DisposeAsync();
await newEntries.Cast<Entry>().ForEachAsync(async x => await x.CloseAsync());
await removedEntries.Cast<Entry>().ForEachAsync(async x => await x.CloseAsync());
await modifiedEntries.Cast<Entry>().ForEachAsync(async x => await x.CloseAsync());
}
}
}

View File

@@ -1,14 +1,13 @@
using System;
using System.Collections.Generic;
using System;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Archives.GZip;
using SharpCompress.Archives.Rar;
using SharpCompress.Archives.SevenZip;
//using SharpCompress.Archives.Rar;
//using SharpCompress.Archives.SevenZip;
using SharpCompress.Archives.Tar;
using SharpCompress.Archives.Zip;
using SharpCompress.Common;
using SharpCompress.IO;
using SharpCompress.Readers;
namespace SharpCompress.Archives
@@ -21,7 +20,7 @@ namespace SharpCompress.Archives
/// <param name="stream"></param>
/// <param name="readerOptions"></param>
/// <returns></returns>
public static IArchive Open(Stream stream, ReaderOptions? readerOptions = null)
public static async ValueTask<IArchive> OpenAsync(Stream stream, ReaderOptions? readerOptions = null, CancellationToken cancellationToken = default)
{
stream.CheckNotNull(nameof(stream));
if (!stream.CanRead || !stream.CanSeek)
@@ -29,26 +28,35 @@ namespace SharpCompress.Archives
throw new ArgumentException("Stream should be readable and seekable");
}
readerOptions ??= new ReaderOptions();
ArchiveType? type;
IsArchive(stream, out type); //test and reset stream position
if (type != null)
if (await ZipArchive.IsZipFileAsync(stream, null, cancellationToken))
{
switch (type.Value)
{
case ArchiveType.Zip:
return ZipArchive.Open(stream, readerOptions);
case ArchiveType.SevenZip:
return SevenZipArchive.Open(stream, readerOptions);
case ArchiveType.GZip:
return GZipArchive.Open(stream, readerOptions);
case ArchiveType.Rar:
return RarArchive.Open(stream, readerOptions);
case ArchiveType.Tar:
return TarArchive.Open(stream, readerOptions);
}
stream.Seek(0, SeekOrigin.Begin);
return ZipArchive.Open(stream, readerOptions);
}
stream.Seek(0, SeekOrigin.Begin);
/*if (SevenZipArchive.IsSevenZipFile(stream))
{
stream.Seek(0, SeekOrigin.Begin);
return SevenZipArchive.Open(stream, readerOptions);
}
stream.Seek(0, SeekOrigin.Begin); */
if (await GZipArchive.IsGZipFileAsync(stream, cancellationToken))
{
stream.Seek(0, SeekOrigin.Begin);
return GZipArchive.Open(stream, readerOptions);
}
stream.Seek(0, SeekOrigin.Begin);
/* if (RarArchive.IsRarFile(stream, readerOptions))
{
stream.Seek(0, SeekOrigin.Begin);
return RarArchive.Open(stream, readerOptions);
}
stream.Seek(0, SeekOrigin.Begin); */
if (await TarArchive.IsTarFileAsync(stream, cancellationToken))
{
stream.Seek(0, SeekOrigin.Begin);
return TarArchive.Open(stream, readerOptions);
}
throw new InvalidOperationException("Cannot determine compressed stream type. Supported Archive Formats: Zip, GZip, Tar, Rar, 7Zip, LZip");
}
@@ -57,7 +65,7 @@ namespace SharpCompress.Archives
return type switch
{
ArchiveType.Zip => ZipArchive.Create(),
ArchiveType.Tar => TarArchive.Create(),
//ArchiveType.Tar => TarArchive.Create(),
ArchiveType.GZip => GZipArchive.Create(),
_ => throw new NotSupportedException("Cannot create Archives of type: " + type)
};
@@ -68,10 +76,10 @@ namespace SharpCompress.Archives
/// </summary>
/// <param name="filePath"></param>
/// <param name="options"></param>
public static IArchive Open(string filePath, ReaderOptions? options = null)
public static ValueTask<IArchive> OpenAsync(string filePath, ReaderOptions? options = null)
{
filePath.CheckNotNullOrEmpty(nameof(filePath));
return Open(new FileInfo(filePath), options);
return OpenAsync(new FileInfo(filePath), options);
}
/// <summary>
@@ -79,217 +87,51 @@ namespace SharpCompress.Archives
/// </summary>
/// <param name="fileInfo"></param>
/// <param name="options"></param>
public static IArchive Open(FileInfo fileInfo, ReaderOptions? options = null)
public static async ValueTask<IArchive> OpenAsync(FileInfo fileInfo, ReaderOptions? options = null, CancellationToken cancellationToken = default)
{
fileInfo.CheckNotNull(nameof(fileInfo));
options ??= new ReaderOptions { LeaveStreamOpen = false };
ArchiveType? type;
using (Stream stream = fileInfo.OpenRead())
await using var stream = fileInfo.OpenRead();
if (await ZipArchive.IsZipFileAsync(stream, null, cancellationToken))
{
IsArchive(stream, out type); //test and reset stream position
if (type != null)
{
switch (type.Value)
{
case ArchiveType.Zip:
return ZipArchive.Open(fileInfo, options);
case ArchiveType.SevenZip:
return SevenZipArchive.Open(fileInfo, options);
case ArchiveType.GZip:
return GZipArchive.Open(fileInfo, options);
case ArchiveType.Rar:
return RarArchive.Open(fileInfo, options);
case ArchiveType.Tar:
return TarArchive.Open(fileInfo, options);
}
}
return ZipArchive.Open(fileInfo, options);
}
throw new InvalidOperationException("Cannot determine compressed stream type. Supported Archive Formats: Zip, GZip, Tar, Rar, 7Zip");
}
/// <summary>
/// Constructor with IEnumerable FileInfo objects, multi and split support.
/// </summary>
/// <param name="fileInfos"></param>
/// <param name="options"></param>
public static IArchive Open(IEnumerable<FileInfo> fileInfos, ReaderOptions? options = null)
{
fileInfos.CheckNotNull(nameof(fileInfos));
FileInfo[] files = fileInfos.ToArray();
if (files.Length == 0)
throw new InvalidOperationException("No files to open");
FileInfo fileInfo = files[0];
if (files.Length == 1)
return Open(fileInfo, options);
fileInfo.CheckNotNull(nameof(fileInfo));
options ??= new ReaderOptions { LeaveStreamOpen = false };
ArchiveType? type;
using (Stream stream = fileInfo.OpenRead())
IsArchive(stream, out type); //test and reset stream position
if (type != null)
stream.Seek(0, SeekOrigin.Begin);
/*if (SevenZipArchive.IsSevenZipFile(stream))
{
switch (type.Value)
{
case ArchiveType.Zip:
return ZipArchive.Open(files, options);
case ArchiveType.SevenZip:
return SevenZipArchive.Open(files, options);
case ArchiveType.GZip:
return GZipArchive.Open(files, options);
case ArchiveType.Rar:
return RarArchive.Open(files, options);
case ArchiveType.Tar:
return TarArchive.Open(files, options);
}
return SevenZipArchive.Open(fileInfo, options);
}
throw new InvalidOperationException("Cannot determine compressed stream type. Supported Archive Formats: Zip, GZip, Tar, Rar, 7Zip");
}
/// <summary>
/// Constructor with IEnumerable FileInfo objects, multi and split support.
/// </summary>
/// <param name="streams"></param>
/// <param name="options"></param>
public static IArchive Open(IEnumerable<Stream> streams, ReaderOptions? options = null)
{
streams.CheckNotNull(nameof(streams));
if (streams.Count() == 0)
throw new InvalidOperationException("No streams");
if (streams.Count() == 1)
return Open(streams.First(), options);
options ??= new ReaderOptions();
ArchiveType? type;
using (Stream stream = streams.First())
IsArchive(stream, out type); //test and reset stream position
if (type != null)
stream.Seek(0, SeekOrigin.Begin); */
if (await GZipArchive.IsGZipFileAsync(stream, cancellationToken))
{
switch (type.Value)
{
case ArchiveType.Zip:
return ZipArchive.Open(streams, options);
case ArchiveType.SevenZip:
return SevenZipArchive.Open(streams, options);
case ArchiveType.GZip:
return GZipArchive.Open(streams, options);
case ArchiveType.Rar:
return RarArchive.Open(streams, options);
case ArchiveType.Tar:
return TarArchive.Open(streams, options);
}
return GZipArchive.Open(fileInfo, options);
}
stream.Seek(0, SeekOrigin.Begin);
/*if (RarArchive.IsRarFile(stream, options))
{
return RarArchive.Open(fileInfo, options);
}
stream.Seek(0, SeekOrigin.Begin);
if (TarArchive.IsTarFile(stream))
{
return TarArchive.Open(fileInfo, options);
} */
throw new InvalidOperationException("Cannot determine compressed stream type. Supported Archive Formats: Zip, GZip, Tar, Rar, 7Zip");
}
/// <summary>
/// Extract to specific directory, retaining filename
/// </summary>
public static void WriteToDirectory(string sourceArchive, string destinationDirectory,
ExtractionOptions? options = null)
public static async ValueTask WriteToDirectory(string sourceArchive,
string destinationDirectory,
ExtractionOptions? options = null,
CancellationToken cancellationToken = default)
{
using IArchive archive = Open(sourceArchive);
foreach (IArchiveEntry entry in archive.Entries)
await using IArchive archive = await OpenAsync(sourceArchive);
await foreach (IArchiveEntry entry in archive.Entries.WithCancellation(cancellationToken))
{
entry.WriteToDirectory(destinationDirectory, options);
}
}
public static bool IsArchive(string filePath, out ArchiveType? type)
{
filePath.CheckNotNullOrEmpty(nameof(filePath));
using (Stream s = File.OpenRead(filePath))
return IsArchive(s, out type);
}
private static bool IsArchive(Stream stream, out ArchiveType? type)
{
type = null;
stream.CheckNotNull(nameof(stream));
if (!stream.CanRead || !stream.CanSeek)
{
throw new ArgumentException("Stream should be readable and seekable");
}
if (ZipArchive.IsZipFile(stream, null))
type = ArchiveType.Zip;
stream.Seek(0, SeekOrigin.Begin);
if (type == null)
{
if (SevenZipArchive.IsSevenZipFile(stream))
type = ArchiveType.SevenZip;
stream.Seek(0, SeekOrigin.Begin);
}
if (type == null)
{
if (GZipArchive.IsGZipFile(stream))
type = ArchiveType.GZip;
stream.Seek(0, SeekOrigin.Begin);
}
if (type == null)
{
if (RarArchive.IsRarFile(stream))
type = ArchiveType.Rar;
stream.Seek(0, SeekOrigin.Begin);
}
if (type == null)
{
if (TarArchive.IsTarFile(stream))
type = ArchiveType.Tar;
stream.Seek(0, SeekOrigin.Begin);
}
if (type == null) //test multipartzip as it could find zips in other non compressed archive types?
{
if (ZipArchive.IsZipMulti(stream)) //test the zip (last) file of a multipart zip
type = ArchiveType.Zip;
stream.Seek(0, SeekOrigin.Begin);
}
return type != null;
}
/// <summary>
/// From a passed in archive (zip, rar, 7z, 001), return all parts.
/// </summary>
/// <param name="part1"></param>
/// <returns></returns>
public static IEnumerable<string> GetFileParts(string part1)
{
part1.CheckNotNullOrEmpty(nameof(part1));
return GetFileParts(new FileInfo(part1)).Select(a => a.FullName);
}
/// <summary>
/// From a passed in archive (zip, rar, 7z, 001), return all parts.
/// </summary>
/// <param name="part1"></param>
/// <returns></returns>
public static IEnumerable<FileInfo> GetFileParts(FileInfo part1)
{
part1.CheckNotNull(nameof(part1));
yield return part1;
int i = 1;
FileInfo? part = RarArchiveVolumeFactory.GetFilePart(i++, part1);
if (part != null)
{
yield return part;
while ((part = RarArchiveVolumeFactory.GetFilePart(i++, part1)) != null) //tests split too
yield return part;
}
else
{
i = 1;
while ((part = ZipArchiveVolumeFactory.GetFilePart(i++, part1)) != null) //tests split too
yield return part;
await entry.WriteEntryToDirectoryAsync(destinationDirectory, options, cancellationToken);
}
}
}

View File

@@ -1,29 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using SharpCompress.Readers;
using System.Linq;
using System.Text;
using SharpCompress.Common.Rar.Headers;
using System.Text.RegularExpressions;
namespace SharpCompress.Archives
{
internal abstract class ArchiveVolumeFactory
{
internal static FileInfo? GetFilePart(int index, FileInfo part1) //base the name on the first part
{
FileInfo? item = null;
//split 001, 002 ...
Match m = Regex.Match(part1.Name, @"^(.*\.)([0-9]+)$", RegexOptions.IgnoreCase);
if (m.Success)
item = new FileInfo(Path.Combine(part1.DirectoryName!, String.Concat(m.Groups[1].Value, (index + 1).ToString().PadLeft(m.Groups[2].Value.Length, '0'))));
if (item != null && item.Exists)
return item;
return null;
}
}
}

View File

@@ -1,10 +1,13 @@
using System;
using System;
using System.Buffers;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
using SharpCompress.Common.GZip;
using SharpCompress.IO;
using SharpCompress.Readers;
using SharpCompress.Readers.GZip;
using SharpCompress.Writers;
@@ -30,34 +33,11 @@ namespace SharpCompress.Archives.GZip
/// </summary>
/// <param name="fileInfo"></param>
/// <param name="readerOptions"></param>
public static GZipArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null)
public static GZipArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default)
{
fileInfo.CheckNotNull(nameof(fileInfo));
return new GZipArchive(new SourceStream(fileInfo, i => ArchiveVolumeFactory.GetFilePart(i, fileInfo), readerOptions ?? new ReaderOptions()));
}
/// <summary>
/// Constructor with all file parts passed in
/// </summary>
/// <param name="fileInfos"></param>
/// <param name="readerOptions"></param>
public static GZipArchive Open(IEnumerable<FileInfo> fileInfos, ReaderOptions? readerOptions = null)
{
fileInfos.CheckNotNull(nameof(fileInfos));
FileInfo[] files = fileInfos.ToArray();
return new GZipArchive(new SourceStream(files[0], i => i < files.Length ? files[i] : null, readerOptions ?? new ReaderOptions()));
}
/// <summary>
/// Constructor with all stream parts passed in
/// </summary>
/// <param name="streams"></param>
/// <param name="readerOptions"></param>
public static GZipArchive Open(IEnumerable<Stream> streams, ReaderOptions? readerOptions = null)
{
streams.CheckNotNull(nameof(streams));
Stream[] strms = streams.ToArray();
return new GZipArchive(new SourceStream(strms[0], i => i < strms.Length ? strms[i] : null, readerOptions ?? new ReaderOptions()));
return new GZipArchive(fileInfo, readerOptions ?? new ReaderOptions(), cancellationToken);
}
/// <summary>
@@ -65,10 +45,11 @@ namespace SharpCompress.Archives.GZip
/// </summary>
/// <param name="stream"></param>
/// <param name="readerOptions"></param>
public static GZipArchive Open(Stream stream, ReaderOptions? readerOptions = null)
public static GZipArchive Open(Stream stream, ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default)
{
stream.CheckNotNull(nameof(stream));
return new GZipArchive(new SourceStream(stream, i => null, readerOptions ?? new ReaderOptions()));
return new GZipArchive(stream, readerOptions ?? new ReaderOptions(), cancellationToken);
}
public static GZipArchive Create()
@@ -77,62 +58,62 @@ namespace SharpCompress.Archives.GZip
}
/// <summary>
/// Constructor with a SourceStream able to handle FileInfo and Streams.
/// Constructor with a FileInfo object to an existing file.
/// </summary>
/// <param name="srcStream"></param>
/// <param name="fileInfo"></param>
/// <param name="options"></param>
internal GZipArchive(SourceStream srcStream)
: base(ArchiveType.Tar, srcStream)
internal GZipArchive(FileInfo fileInfo, ReaderOptions options,
CancellationToken cancellationToken)
: base(ArchiveType.GZip, fileInfo, options, cancellationToken)
{
}
protected override IEnumerable<GZipVolume> LoadVolumes(SourceStream srcStream)
protected override IAsyncEnumerable<GZipVolume> LoadVolumes(FileInfo file,
CancellationToken cancellationToken)
{
srcStream.LoadAllParts();
int idx = 0;
return srcStream.Streams.Select(a => new GZipVolume(a, ReaderOptions, idx++));
}
public static bool IsGZipFile(string filePath)
{
return IsGZipFile(new FileInfo(filePath));
return new GZipVolume(file, ReaderOptions).AsAsyncEnumerable();
}
public static bool IsGZipFile(FileInfo fileInfo)
public static ValueTask<bool> IsGZipFileAsync(string filePath, CancellationToken cancellationToken = default)
{
return IsGZipFileAsync(new FileInfo(filePath), cancellationToken);
}
public static async ValueTask<bool> IsGZipFileAsync(FileInfo fileInfo, CancellationToken cancellationToken = default)
{
if (!fileInfo.Exists)
{
return false;
}
using Stream stream = fileInfo.OpenRead();
return IsGZipFile(stream);
await using Stream stream = fileInfo.OpenRead();
return await IsGZipFileAsync(stream, cancellationToken);
}
public void SaveTo(string filePath)
public Task SaveToAsync(string filePath, CancellationToken cancellationToken = default)
{
SaveTo(new FileInfo(filePath));
return SaveToAsync(new FileInfo(filePath), cancellationToken);
}
public void SaveTo(FileInfo fileInfo)
public async Task SaveToAsync(FileInfo fileInfo, CancellationToken cancellationToken = default)
{
using (var stream = fileInfo.Open(FileMode.Create, FileAccess.Write))
{
SaveTo(stream, new WriterOptions(CompressionType.GZip));
}
await using var stream = fileInfo.Open(FileMode.Create, FileAccess.Write);
await SaveToAsync(stream, new WriterOptions(CompressionType.GZip), cancellationToken);
}
public static bool IsGZipFile(Stream stream)
public static async ValueTask<bool> IsGZipFileAsync(Stream stream, CancellationToken cancellationToken = default)
{
// read the header on the first read
Span<byte> header = stackalloc byte[10];
using var header = MemoryPool<byte>.Shared.Rent(10);
var slice = header.Memory.Slice(0, 10);
// workitem 8501: handle edge case (decompress empty stream)
if (!stream.ReadFully(header))
if (await stream.ReadAsync(slice, cancellationToken) != 10)
{
return false;
}
if (header[0] != 0x1F || header[1] != 0x8B || header[2] != 8)
if (slice.Span[0] != 0x1F || slice.Span[1] != 0x8B || slice.Span[2] != 8)
{
return false;
}
@@ -140,51 +121,70 @@ namespace SharpCompress.Archives.GZip
return true;
}
/// <summary>
/// Takes multiple seekable Streams for a multi-part archive
/// </summary>
/// <param name="stream"></param>
/// <param name="options"></param>
internal GZipArchive(Stream stream, ReaderOptions options,
CancellationToken cancellationToken)
: base(ArchiveType.GZip, stream, options, cancellationToken)
{
}
internal GZipArchive()
: base(ArchiveType.GZip)
{
}
protected override GZipArchiveEntry CreateEntryInternal(string filePath, Stream source, long size, DateTime? modified,
bool closeStream)
protected override async ValueTask<GZipArchiveEntry> CreateEntryInternal(string filePath, Stream source, long size, DateTime? modified,
bool closeStream, CancellationToken cancellationToken = default)
{
if (Entries.Any())
if (await Entries.AnyAsync(cancellationToken: cancellationToken))
{
throw new InvalidOperationException("Only one entry is allowed in a GZip Archive");
}
return new GZipWritableArchiveEntry(this, source, filePath, size, modified, closeStream);
}
protected override void SaveTo(Stream stream, WriterOptions options,
IEnumerable<GZipArchiveEntry> oldEntries,
IEnumerable<GZipArchiveEntry> newEntries)
protected override async ValueTask SaveToAsync(Stream stream, WriterOptions options,
IAsyncEnumerable<GZipArchiveEntry> oldEntries,
IAsyncEnumerable<GZipArchiveEntry> newEntries,
CancellationToken cancellationToken = default)
{
if (Entries.Count > 1)
if (await Entries.CountAsync(cancellationToken: cancellationToken) > 1)
{
throw new InvalidOperationException("Only one entry is allowed in a GZip Archive");
}
using (var writer = new GZipWriter(stream, new GZipWriterOptions(options)))
await using var writer = new GZipWriter(stream, new GZipWriterOptions(options));
await foreach (var entry in oldEntries.Concat(newEntries)
.Where(x => !x.IsDirectory)
.WithCancellation(cancellationToken))
{
foreach (var entry in oldEntries.Concat(newEntries)
.Where(x => !x.IsDirectory))
{
using (var entryStream = entry.OpenEntryStream())
{
writer.Write(entry.Key, entryStream, entry.LastModifiedTime);
}
}
await using var entryStream = await entry.OpenEntryStreamAsync(cancellationToken);
await writer.WriteAsync(entry.Key, entryStream, entry.LastModifiedTime, cancellationToken);
}
}
protected override IEnumerable<GZipArchiveEntry> LoadEntries(IEnumerable<GZipVolume> volumes)
protected override async IAsyncEnumerable<GZipVolume> LoadVolumes(IAsyncEnumerable<Stream> streams,
[EnumeratorCancellation]CancellationToken cancellationToken)
{
Stream stream = volumes.Single().Stream;
yield return new GZipArchiveEntry(this, new GZipFilePart(stream, ReaderOptions.ArchiveEncoding));
yield return new GZipVolume(await streams.FirstAsync(cancellationToken: cancellationToken), ReaderOptions);
}
protected override IReader CreateReaderForSolidExtraction()
protected override async IAsyncEnumerable<GZipArchiveEntry> LoadEntries(IAsyncEnumerable<GZipVolume> volumes,
[EnumeratorCancellation]CancellationToken cancellationToken)
{
var stream = Volumes.Single().Stream;
Stream stream = (await volumes.SingleAsync(cancellationToken: cancellationToken)).Stream;
var part = new GZipFilePart(ReaderOptions.ArchiveEncoding);
await part.Initialize(stream, cancellationToken);
yield return new GZipArchiveEntry(this, part);
}
protected override async ValueTask<IReader> CreateReaderForSolidExtraction()
{
var stream = (await Volumes.SingleAsync()).Stream;
stream.Position = 0;
return GZipReader.Open(stream);
}

View File

@@ -1,5 +1,7 @@
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common.GZip;
namespace SharpCompress.Archives.GZip
@@ -12,7 +14,7 @@ namespace SharpCompress.Archives.GZip
Archive = archive;
}
public virtual Stream OpenEntryStream()
public virtual async ValueTask<Stream> OpenEntryStreamAsync(CancellationToken cancellationToken = default)
{
//this is to reset the stream to be read multiple times
var part = (GZipFilePart)Parts.Single();
@@ -20,7 +22,7 @@ namespace SharpCompress.Archives.GZip
{
part.GetRawStream().Position = part.EntryStartPosition;
}
return Parts.Single().GetCompressedStream();
return await Parts.Single().GetCompressedStreamAsync(cancellationToken);
}
#region IArchiveEntry Members

View File

@@ -1,8 +1,10 @@
#nullable disable
#nullable disable
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
using SharpCompress.IO;
@@ -50,19 +52,19 @@ namespace SharpCompress.Archives.GZip
Stream IWritableArchiveEntry.Stream => stream;
public override Stream OpenEntryStream()
public override ValueTask<Stream> OpenEntryStreamAsync(CancellationToken cancellationToken = default)
{
//ensure new stream is at the start, this could be reset
stream.Seek(0, SeekOrigin.Begin);
return NonDisposingStream.Create(stream);
return new(new NonDisposingStream(stream));
}
internal override void Close()
internal override async ValueTask CloseAsync()
{
if (closeStream)
{
stream.Dispose();
await stream.DisposeAsync();
}
}
}
}
}

View File

@@ -1,49 +1,44 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using SharpCompress.Common;
using SharpCompress.Readers;
namespace SharpCompress.Archives
{
public interface IArchive : IDisposable
public interface IArchive : IAsyncDisposable
{
event EventHandler<ArchiveExtractionEventArgs<IArchiveEntry>> EntryExtractionBegin;
event EventHandler<ArchiveExtractionEventArgs<IArchiveEntry>> EntryExtractionEnd;
event EventHandler<CompressedBytesReadEventArgs> CompressedBytesRead;
event EventHandler<FilePartExtractionBeginEventArgs> FilePartExtractionBegin;
IEnumerable<IArchiveEntry> Entries { get; }
IEnumerable<IVolume> Volumes { get; }
IAsyncEnumerable<IArchiveEntry> Entries { get; }
IAsyncEnumerable<IVolume> Volumes { get; }
ArchiveType Type { get; }
ValueTask EnsureEntriesLoaded();
/// <summary>
/// Use this method to extract all entries in an archive in order.
/// This is primarily for SOLID Rar Archives or 7Zip Archives as they need to be
/// extracted sequentially for the best performance.
/// </summary>
IReader ExtractAllEntries();
ValueTask<IReader> ExtractAllEntries();
/// <summary>
/// Archive is SOLID (this means the Archive saved bytes by reusing information which helps for archives containing many small files).
/// Rar Archives can be SOLID while all 7Zip archives are considered SOLID.
/// </summary>
bool IsSolid { get; }
ValueTask<bool> IsSolidAsync();
/// <summary>
/// This checks to see if all the known entries have IsComplete = true
/// </summary>
bool IsComplete { get; }
ValueTask<bool> IsCompleteAsync();
/// <summary>
/// The total size of the files compressed in the archive.
/// </summary>
long TotalSize { get; }
ValueTask<long> TotalSizeAsync();
/// <summary>
/// The total size of the files as uncompressed in the archive.
/// </summary>
long TotalUncompressSize { get; }
ValueTask<long> TotalUncompressedSizeAsync();
}
}

View File

@@ -1,4 +1,6 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
namespace SharpCompress.Archives
@@ -9,7 +11,7 @@ namespace SharpCompress.Archives
/// Opens the current entry as a stream that will decompress as it is read.
/// Read the entire stream or use SkipEntry on EntryStream.
/// </summary>
Stream OpenEntryStream();
ValueTask<Stream> OpenEntryStreamAsync(CancellationToken cancellationToken = default);
/// <summary>
/// The archive can find all the parts of the archive needed to extract this entry.

View File

@@ -1,4 +1,6 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
using SharpCompress.IO;
@@ -6,58 +8,53 @@ namespace SharpCompress.Archives
{
public static class IArchiveEntryExtensions
{
public static void WriteTo(this IArchiveEntry archiveEntry, Stream streamToWriteTo)
public static async ValueTask WriteToAsync(this IArchiveEntry archiveEntry, Stream streamToWriteTo, CancellationToken cancellationToken = default)
{
if (archiveEntry.IsDirectory)
{
throw new ExtractionException("Entry is a file directory and cannot be extracted.");
}
var streamListener = (IArchiveExtractionListener)archiveEntry.Archive;
streamListener.EnsureEntriesLoaded();
streamListener.FireEntryExtractionBegin(archiveEntry);
streamListener.FireFilePartExtractionBegin(archiveEntry.Key, archiveEntry.Size, archiveEntry.CompressedSize);
var entryStream = archiveEntry.OpenEntryStream();
var archive = archiveEntry.Archive;
await archive.EnsureEntriesLoaded();
var entryStream = await archiveEntry.OpenEntryStreamAsync(cancellationToken);
if (entryStream is null)
{
return;
}
using (entryStream)
await using (entryStream)
{
using (Stream s = new ListeningStream(streamListener, entryStream))
{
s.TransferTo(streamToWriteTo);
}
await entryStream.TransferToAsync(streamToWriteTo, cancellationToken);
}
streamListener.FireEntryExtractionEnd(archiveEntry);
}
/// <summary>
/// Extract to specific directory, retaining filename
/// </summary>
public static void WriteToDirectory(this IArchiveEntry entry, string destinationDirectory,
ExtractionOptions? options = null)
public static ValueTask WriteEntryToDirectoryAsync(this IArchiveEntry entry,
string destinationDirectory,
ExtractionOptions? options = null,
CancellationToken cancellationToken = default)
{
ExtractionMethods.WriteEntryToDirectory(entry, destinationDirectory, options,
entry.WriteToFile);
return ExtractionMethods.WriteEntryToDirectoryAsync(entry, destinationDirectory, options,
entry.WriteToFileAsync, cancellationToken);
}
/// <summary>
/// Extract to specific file
/// </summary>
public static void WriteToFile(this IArchiveEntry entry,
public static ValueTask WriteToFileAsync(this IArchiveEntry entry,
string destinationFileName,
ExtractionOptions? options = null)
ExtractionOptions? options = null,
CancellationToken cancellationToken = default)
{
ExtractionMethods.WriteEntryToFile(entry, destinationFileName, options,
(x, fm) =>
return ExtractionMethods.WriteEntryToFileAsync(entry, destinationFileName, options,
async (x, fm, ct) =>
{
using (FileStream fs = File.Open(destinationFileName, fm))
{
entry.WriteTo(fs);
}
});
await using FileStream fs = File.Open(x, fm);
await entry.WriteToAsync(fs, ct);
}, cancellationToken);
}
}
}

View File

@@ -1,4 +1,6 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
namespace SharpCompress.Archives
@@ -8,12 +10,14 @@ namespace SharpCompress.Archives
/// <summary>
/// Extract to specific directory, retaining filename
/// </summary>
public static void WriteToDirectory(this IArchive archive, string destinationDirectory,
ExtractionOptions? options = null)
public static async ValueTask WriteToDirectoryAsync(this IArchive archive,
string destinationDirectory,
ExtractionOptions? options = null,
CancellationToken cancellationToken = default)
{
foreach (IArchiveEntry entry in archive.Entries.Where(x => !x.IsDirectory))
await foreach (IArchiveEntry entry in archive.Entries.Where(x => !x.IsDirectory).WithCancellation(cancellationToken))
{
entry.WriteToDirectory(destinationDirectory, options);
await entry.WriteEntryToDirectoryAsync(destinationDirectory, options, cancellationToken);
}
}
}

View File

@@ -1,11 +0,0 @@
using SharpCompress.Common;
namespace SharpCompress.Archives
{
internal interface IArchiveExtractionListener : IExtractionListener
{
void EnsureEntriesLoaded();
void FireEntryExtractionBegin(IArchiveEntry entry);
void FireEntryExtractionEnd(IArchiveEntry entry);
}
}

View File

@@ -1,21 +1,23 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Writers;
namespace SharpCompress.Archives
{
public interface IWritableArchive : IArchive
{
void RemoveEntry(IArchiveEntry entry);
ValueTask RemoveEntryAsync(IArchiveEntry entry, CancellationToken cancellationToken = default);
IArchiveEntry AddEntry(string key, Stream source, bool closeStream, long size = 0, DateTime? modified = null);
ValueTask<IArchiveEntry> AddEntryAsync(string key, Stream source, bool closeStream, long size = 0, DateTime? modified = null, CancellationToken cancellationToken = default);
void SaveTo(Stream stream, WriterOptions options);
ValueTask SaveToAsync(Stream stream, WriterOptions options, CancellationToken cancellationToken = default);
/// <summary>
/// Use this to pause entry rebuilding when adding large collections of entries. Dispose when complete. A using statement is recommended.
/// </summary>
/// <returns>IDisposeable to resume entry rebuilding</returns>
IDisposable PauseEntryRebuilding();
IAsyncDisposable PauseEntryRebuilding();
}
}

View File

@@ -1,57 +1,62 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Writers;
namespace SharpCompress.Archives
{
public static class IWritableArchiveExtensions
{
public static void AddEntry(this IWritableArchive writableArchive,
string entryPath, string filePath)
public static async ValueTask AddEntryAsync(this IWritableArchive writableArchive,
string entryPath, string filePath,
CancellationToken cancellationToken = default)
{
var fileInfo = new FileInfo(filePath);
if (!fileInfo.Exists)
{
throw new FileNotFoundException("Could not AddEntry: " + filePath);
}
writableArchive.AddEntry(entryPath, new FileInfo(filePath).OpenRead(), true, fileInfo.Length,
fileInfo.LastWriteTime);
await writableArchive.AddEntryAsync(entryPath, new FileInfo(filePath).OpenRead(), true, fileInfo.Length,
fileInfo.LastWriteTime, cancellationToken);
}
public static void SaveTo(this IWritableArchive writableArchive, string filePath, WriterOptions options)
public static Task SaveToAsync(this IWritableArchive writableArchive, string filePath, WriterOptions options, CancellationToken cancellationToken = default)
{
writableArchive.SaveTo(new FileInfo(filePath), options);
return writableArchive.SaveToAsync(new FileInfo(filePath), options, cancellationToken);
}
public static void SaveTo(this IWritableArchive writableArchive, FileInfo fileInfo, WriterOptions options)
public static async Task SaveToAsync(this IWritableArchive writableArchive, FileInfo fileInfo, WriterOptions options, CancellationToken cancellationToken = default)
{
using (var stream = fileInfo.Open(FileMode.Create, FileAccess.Write))
{
writableArchive.SaveTo(stream, options);
}
await using var stream = fileInfo.Open(FileMode.Create, FileAccess.Write);
await writableArchive.SaveToAsync(stream, options, cancellationToken);
}
public static void AddAllFromDirectory(
public static async ValueTask AddAllFromDirectoryAsync(
this IWritableArchive writableArchive,
string filePath, string searchPattern = "*.*", SearchOption searchOption = SearchOption.AllDirectories)
string filePath, string searchPattern = "*.*",
SearchOption searchOption = SearchOption.AllDirectories,
CancellationToken cancellationToken = default)
{
using (writableArchive.PauseEntryRebuilding())
await using (writableArchive.PauseEntryRebuilding())
{
foreach (var path in Directory.EnumerateFiles(filePath, searchPattern, searchOption))
{
var fileInfo = new FileInfo(path);
writableArchive.AddEntry(path.Substring(filePath.Length), fileInfo.OpenRead(), true, fileInfo.Length,
fileInfo.LastWriteTime);
await writableArchive.AddEntryAsync(path.Substring(filePath.Length), fileInfo.OpenRead(), true, fileInfo.Length,
fileInfo.LastWriteTime,
cancellationToken);
}
}
}
public static IArchiveEntry AddEntry(this IWritableArchive writableArchive, string key, FileInfo fileInfo)
public static ValueTask<IArchiveEntry> AddEntryAsync(this IWritableArchive writableArchive, string key, FileInfo fileInfo,
CancellationToken cancellationToken = default)
{
if (!fileInfo.Exists)
{
throw new ArgumentException("FileInfo does not exist.");
}
return writableArchive.AddEntry(key, fileInfo.OpenRead(), true, fileInfo.Length, fileInfo.LastWriteTime);
return writableArchive.AddEntryAsync(key, fileInfo.OpenRead(), true, fileInfo.Length, fileInfo.LastWriteTime, cancellationToken);
}
}
}

View File

@@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using SharpCompress.Common.Rar;
@@ -13,8 +13,8 @@ namespace SharpCompress.Archives.Rar
/// </summary>
internal class FileInfoRarArchiveVolume : RarVolume
{
internal FileInfoRarArchiveVolume(FileInfo fileInfo, ReaderOptions options, int index = 0)
: base(StreamingMode.Seekable, fileInfo.OpenRead(), FixOptions(options), index)
internal FileInfoRarArchiveVolume(FileInfo fileInfo, ReaderOptions options)
: base(StreamingMode.Seekable, fileInfo.OpenRead(), FixOptions(options))
{
FileInfo = fileInfo;
FileParts = GetVolumeFileParts().ToArray().ToReadOnly();

View File

@@ -1,4 +1,4 @@
using System.IO;
using System.IO;
using SharpCompress.Common.Rar.Headers;
namespace SharpCompress.Archives.Rar
@@ -6,7 +6,7 @@ namespace SharpCompress.Archives.Rar
internal sealed class FileInfoRarFilePart : SeekableFilePart
{
internal FileInfoRarFilePart(FileInfoRarArchiveVolume volume, string? password, MarkHeader mh, FileHeader fh, FileInfo fi)
: base(mh, fh, volume.Index, volume.Stream, password)
: base(mh, fh, volume.Stream, password)
{
FileInfo = fi;
}

View File

@@ -5,26 +5,39 @@ using SharpCompress.Common;
using SharpCompress.Common.Rar;
using SharpCompress.Common.Rar.Headers;
using SharpCompress.Compressors.Rar;
using SharpCompress.IO;
using SharpCompress.Readers;
using SharpCompress.Readers.Rar;
namespace SharpCompress.Archives.Rar
{
public class
public class
RarArchive : AbstractArchive<RarArchiveEntry, RarVolume>
{
internal Lazy<IRarUnpack> UnpackV2017 { get; } = new Lazy<IRarUnpack>(() => new SharpCompress.Compressors.Rar.UnpackV2017.Unpack());
internal Lazy<IRarUnpack> UnpackV1 { get; } = new Lazy<IRarUnpack>(() => new SharpCompress.Compressors.Rar.UnpackV1.Unpack());
/// <summary>
/// Constructor with a FileInfo object to an existing file.
/// </summary>
/// <param name="fileInfo"></param>
/// <param name="options"></param>
internal RarArchive(FileInfo fileInfo, ReaderOptions options)
: base(ArchiveType.Rar, fileInfo, options)
{
}
protected override IEnumerable<RarVolume> LoadVolumes(FileInfo file)
{
return RarArchiveVolumeFactory.GetParts(file, ReaderOptions);
}
/// <summary>
/// Constructor with a SourceStream able to handle FileInfo and Streams.
/// Takes multiple seekable Streams for a multi-part archive
/// </summary>
/// <param name="srcStream"></param>
/// <param name="streams"></param>
/// <param name="options"></param>
internal RarArchive(SourceStream srcStream)
: base(ArchiveType.Rar, srcStream)
internal RarArchive(IEnumerable<Stream> streams, ReaderOptions options)
: base(ArchiveType.Rar, streams, options)
{
}
@@ -33,21 +46,9 @@ namespace SharpCompress.Archives.Rar
return RarArchiveEntryFactory.GetEntries(this, volumes, ReaderOptions);
}
protected override IEnumerable<RarVolume> LoadVolumes(SourceStream srcStream)
protected override IEnumerable<RarVolume> LoadVolumes(IEnumerable<Stream> streams)
{
base.SrcStream.LoadAllParts(); //request all streams
Stream[] streams = base.SrcStream.Streams.ToArray();
int idx = 0;
if (streams.Length > 1 && IsRarFile(streams[1], ReaderOptions)) //test part 2 - true = multipart not split
{
base.SrcStream.IsVolumes = true;
streams[1].Position = 0;
base.SrcStream.Position = 0;
return srcStream.Streams.Select(a => new StreamRarArchiveVolume(a, ReaderOptions, idx++));
}
else //split mode or single file
return new StreamRarArchiveVolume(base.SrcStream, ReaderOptions, idx++).AsEnumerable();
return RarArchiveVolumeFactory.GetParts(streams, ReaderOptions);
}
protected override IReader CreateReaderForSolidExtraction()
@@ -59,9 +60,6 @@ namespace SharpCompress.Archives.Rar
public override bool IsSolid => Volumes.First().IsSolidArchive;
public virtual int MinVersion => Volumes.First().MinVersion;
public virtual int MaxVersion => Volumes.First().MaxVersion;
#region Creation
/// <summary>
/// Constructor with a FileInfo object to an existing file.
@@ -71,8 +69,7 @@ namespace SharpCompress.Archives.Rar
public static RarArchive Open(string filePath, ReaderOptions? options = null)
{
filePath.CheckNotNullOrEmpty(nameof(filePath));
FileInfo fileInfo = new FileInfo(filePath);
return new RarArchive(new SourceStream(fileInfo, i => RarArchiveVolumeFactory.GetFilePart(i, fileInfo), options ?? new ReaderOptions()));
return new RarArchive(new FileInfo(filePath), options ?? new ReaderOptions());
}
/// <summary>
@@ -83,7 +80,7 @@ namespace SharpCompress.Archives.Rar
public static RarArchive Open(FileInfo fileInfo, ReaderOptions? options = null)
{
fileInfo.CheckNotNull(nameof(fileInfo));
return new RarArchive(new SourceStream(fileInfo, i => RarArchiveVolumeFactory.GetFilePart(i, fileInfo), options ?? new ReaderOptions()));
return new RarArchive(fileInfo, options ?? new ReaderOptions());
}
/// <summary>
@@ -94,34 +91,20 @@ namespace SharpCompress.Archives.Rar
public static RarArchive Open(Stream stream, ReaderOptions? options = null)
{
stream.CheckNotNull(nameof(stream));
return new RarArchive(new SourceStream(stream, i => null, options ?? new ReaderOptions()));
return Open(stream.AsEnumerable(), options ?? new ReaderOptions());
}
/// <summary>
/// Constructor with all file parts passed in
/// </summary>
/// <param name="fileInfos"></param>
/// <param name="readerOptions"></param>
public static RarArchive Open(IEnumerable<FileInfo> fileInfos, ReaderOptions? readerOptions = null)
{
fileInfos.CheckNotNull(nameof(fileInfos));
FileInfo[] files = fileInfos.ToArray();
return new RarArchive(new SourceStream(files[0], i => i < files.Length ? files[i] : null, readerOptions ?? new ReaderOptions()));
}
/// <summary>
/// Constructor with all stream parts passed in
/// Takes multiple seekable Streams for a multi-part archive
/// </summary>
/// <param name="streams"></param>
/// <param name="readerOptions"></param>
public static RarArchive Open(IEnumerable<Stream> streams, ReaderOptions? readerOptions = null)
/// <param name="options"></param>
public static RarArchive Open(IEnumerable<Stream> streams, ReaderOptions? options = null)
{
streams.CheckNotNull(nameof(streams));
Stream[] strms = streams.ToArray();
return new RarArchive(new SourceStream(strms[0], i => i < strms.Length ? strms[i] : null, readerOptions ?? new ReaderOptions()));
return new RarArchive(streams, options ?? new ReaderOptions());
}
public static bool IsRarFile(string filePath)
{
return IsRarFile(new FileInfo(filePath));

View File

@@ -21,7 +21,6 @@ namespace SharpCompress.Archives.Rar
this.parts = parts.ToList();
this.archive = archive;
this.readerOptions = readerOptions;
this.IsSolid = this.FileHeader.IsSolid;
}
public override CompressionType CompressionType => CompressionType.Rar;
@@ -86,4 +85,4 @@ namespace SharpCompress.Archives.Rar
}
}
}
}
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.IO;
using SharpCompress.Common.Rar;
@@ -6,35 +6,135 @@ using SharpCompress.Readers;
using System.Linq;
using System.Text;
using SharpCompress.Common.Rar.Headers;
using System.Text.RegularExpressions;
namespace SharpCompress.Archives.Rar
{
internal static class RarArchiveVolumeFactory
{
internal static FileInfo? GetFilePart(int index, FileInfo part1) //base the name on the first part
internal static IEnumerable<RarVolume> GetParts(IEnumerable<Stream> streams, ReaderOptions options)
{
FileInfo? item = null;
//new style rar - ..part1 | /part01 | part001 ....
Match m = Regex.Match(part1.Name, @"^(.*\.part)([0-9]+)(\.rar)$", RegexOptions.IgnoreCase);
if (m.Success)
item = new FileInfo(Path.Combine(part1.DirectoryName!, String.Concat(m.Groups[1].Value, (index + 1).ToString().PadLeft(m.Groups[2].Value.Length, '0'), m.Groups[3].Value)));
else
foreach (Stream s in streams)
{
//old style - ...rar, .r00, .r01 ...
m = Regex.Match(part1.Name, @"^(.*\.r)(ar|[0-9]+)$", RegexOptions.IgnoreCase);
if (m.Success)
item = new FileInfo(Path.Combine(part1.DirectoryName!, String.Concat(m.Groups[1].Value, index == 0 ? "ar" : (index - 1).ToString().PadLeft(m.Groups[2].Value.Length, '0'))));
else //split .001, .002 ....
return ArchiveVolumeFactory.GetFilePart(index, part1);
if (!s.CanRead || !s.CanSeek)
{
throw new ArgumentException("Stream is not readable and seekable");
}
StreamRarArchiveVolume part = new StreamRarArchiveVolume(s, options);
yield return part;
}
if (item != null && item.Exists)
return item;
return null; //no more items
}
internal static IEnumerable<RarVolume> GetParts(FileInfo fileInfo, ReaderOptions options)
{
FileInfoRarArchiveVolume part = new FileInfoRarArchiveVolume(fileInfo, options);
yield return part;
ArchiveHeader ah = part.ArchiveHeader;
if (!ah.IsVolume)
{
yield break; //if file isn't volume then there is no reason to look
}
fileInfo = GetNextFileInfo(ah, part.FileParts.FirstOrDefault() as FileInfoRarFilePart)!;
//we use fileinfo because rar is dumb and looks at file names rather than archive info for another volume
while (fileInfo != null && fileInfo.Exists)
{
part = new FileInfoRarArchiveVolume(fileInfo, options);
fileInfo = GetNextFileInfo(ah, part.FileParts.FirstOrDefault() as FileInfoRarFilePart)!;
yield return part;
}
}
private static FileInfo? GetNextFileInfo(ArchiveHeader ah, FileInfoRarFilePart? currentFilePart)
{
if (currentFilePart is null)
{
return null;
}
bool oldNumbering = ah.OldNumberingFormat
|| currentFilePart.MarkHeader.OldNumberingFormat;
if (oldNumbering)
{
return FindNextFileWithOldNumbering(currentFilePart.FileInfo);
}
else
{
return FindNextFileWithNewNumbering(currentFilePart.FileInfo);
}
}
private static FileInfo FindNextFileWithOldNumbering(FileInfo currentFileInfo)
{
// .rar, .r00, .r01, ...
string extension = currentFileInfo.Extension;
var buffer = new StringBuilder(currentFileInfo.FullName.Length);
buffer.Append(currentFileInfo.FullName.Substring(0,
currentFileInfo.FullName.Length - extension.Length));
if (string.Compare(extension, ".rar", StringComparison.OrdinalIgnoreCase) == 0)
{
buffer.Append(".r00");
}
else
{
if (int.TryParse(extension.Substring(2, 2), out int num))
{
num++;
buffer.Append(".r");
if (num < 10)
{
buffer.Append('0');
}
buffer.Append(num);
}
else
{
ThrowInvalidFileName(currentFileInfo);
}
}
return new FileInfo(buffer.ToString());
}
private static FileInfo FindNextFileWithNewNumbering(FileInfo currentFileInfo)
{
// part1.rar, part2.rar, ...
string extension = currentFileInfo.Extension;
if (string.Compare(extension, ".rar", StringComparison.OrdinalIgnoreCase) != 0)
{
throw new ArgumentException("Invalid extension, expected 'rar': " + currentFileInfo.FullName);
}
int startIndex = currentFileInfo.FullName.LastIndexOf(".part");
if (startIndex < 0)
{
ThrowInvalidFileName(currentFileInfo);
}
StringBuilder buffer = new StringBuilder(currentFileInfo.FullName.Length);
buffer.Append(currentFileInfo.FullName, 0, startIndex);
string numString = currentFileInfo.FullName.Substring(startIndex + 5,
currentFileInfo.FullName.IndexOf('.', startIndex + 5) -
startIndex - 5);
buffer.Append(".part");
if (int.TryParse(numString, out int num))
{
num++;
for (int i = 0; i < numString.Length - num.ToString().Length; i++)
{
buffer.Append('0');
}
buffer.Append(num);
}
else
{
ThrowInvalidFileName(currentFileInfo);
}
buffer.Append(".rar");
return new FileInfo(buffer.ToString());
}
private static void ThrowInvalidFileName(FileInfo fileInfo)
{
throw new ArgumentException("Filename invalid or next archive could not be found:"
+ fileInfo.FullName);
}
}
}
}

View File

@@ -1,4 +1,4 @@
using System.IO;
using System.IO;
using SharpCompress.Common.Rar;
using SharpCompress.Common.Rar.Headers;
@@ -9,8 +9,8 @@ namespace SharpCompress.Archives.Rar
private readonly Stream stream;
private readonly string? password;
internal SeekableFilePart(MarkHeader mh, FileHeader fh, int index, Stream stream, string? password)
: base(mh, fh, index)
internal SeekableFilePart(MarkHeader mh, FileHeader fh, Stream stream, string? password)
: base(mh, fh)
{
this.stream = stream;
this.password = password;
@@ -28,4 +28,4 @@ namespace SharpCompress.Archives.Rar
internal override string FilePartName => "Unknown Stream - File Entry: " + FileHeader.FileName;
}
}
}

View File

@@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.IO;
using SharpCompress.Common.Rar;
using SharpCompress.Common.Rar.Headers;
@@ -9,8 +9,8 @@ namespace SharpCompress.Archives.Rar
{
internal class StreamRarArchiveVolume : RarVolume
{
internal StreamRarArchiveVolume(Stream stream, ReaderOptions options, int index = 0)
: base(StreamingMode.Seekable, stream, options, index)
internal StreamRarArchiveVolume(Stream stream, ReaderOptions options)
: base(StreamingMode.Seekable, stream, options)
{
}
@@ -21,7 +21,7 @@ namespace SharpCompress.Archives.Rar
internal override RarFilePart CreateFilePart(MarkHeader markHeader, FileHeader fileHeader)
{
return new SeekableFilePart(markHeader, fileHeader, this.Index, Stream, ReaderOptions.Password);
return new SeekableFilePart(markHeader, fileHeader, Stream, ReaderOptions.Password);
}
}
}
}

View File

@@ -1,4 +1,4 @@
#nullable disable
#nullable disable
using System;
using System.Collections.Generic;
@@ -34,33 +34,8 @@ namespace SharpCompress.Archives.SevenZip
public static SevenZipArchive Open(FileInfo fileInfo, ReaderOptions readerOptions = null)
{
fileInfo.CheckNotNull("fileInfo");
return new SevenZipArchive(new SourceStream(fileInfo, i => ArchiveVolumeFactory.GetFilePart(i, fileInfo), readerOptions ?? new ReaderOptions()));
return new SevenZipArchive(fileInfo, readerOptions ?? new ReaderOptions());
}
/// <summary>
/// Constructor with all file parts passed in
/// </summary>
/// <param name="fileInfos"></param>
/// <param name="readerOptions"></param>
public static SevenZipArchive Open(IEnumerable<FileInfo> fileInfos, ReaderOptions readerOptions = null)
{
fileInfos.CheckNotNull(nameof(fileInfos));
FileInfo[] files = fileInfos.ToArray();
return new SevenZipArchive(new SourceStream(files[0], i => i < files.Length ? files[i] : null, readerOptions ?? new ReaderOptions()));
}
/// <summary>
/// Constructor with all stream parts passed in
/// </summary>
/// <param name="streams"></param>
/// <param name="readerOptions"></param>
public static SevenZipArchive Open(IEnumerable<Stream> streams, ReaderOptions readerOptions = null)
{
streams.CheckNotNull(nameof(streams));
Stream[] strms = streams.ToArray();
return new SevenZipArchive(new SourceStream(strms[0], i => i < strms.Length ? strms[i] : null, readerOptions ?? new ReaderOptions()));
}
/// <summary>
/// Takes a seekable Stream as a source
/// </summary>
@@ -69,24 +44,17 @@ namespace SharpCompress.Archives.SevenZip
public static SevenZipArchive Open(Stream stream, ReaderOptions readerOptions = null)
{
stream.CheckNotNull("stream");
return new SevenZipArchive(new SourceStream(stream, i => null, readerOptions ?? new ReaderOptions()));
return new SevenZipArchive(stream, readerOptions ?? new ReaderOptions());
}
/// <summary>
/// Constructor with a SourceStream able to handle FileInfo and Streams.
/// </summary>
/// <param name="srcStream"></param>
/// <param name="options"></param>
internal SevenZipArchive(SourceStream srcStream)
: base(ArchiveType.SevenZip, srcStream)
internal SevenZipArchive(FileInfo fileInfo, ReaderOptions readerOptions)
: base(ArchiveType.SevenZip, fileInfo, readerOptions)
{
}
protected override IEnumerable<SevenZipVolume> LoadVolumes(SourceStream srcStream)
protected override IEnumerable<SevenZipVolume> LoadVolumes(FileInfo file)
{
base.SrcStream.LoadAllParts(); //request all streams
int idx = 0;
return new SevenZipVolume(srcStream, ReaderOptions, idx++).AsEnumerable(); //simple single volume or split, multivolume not supported
return new SevenZipVolume(file.OpenRead(), ReaderOptions).AsEnumerable();
}
public static bool IsSevenZipFile(string filePath)
@@ -106,32 +74,38 @@ namespace SharpCompress.Archives.SevenZip
}
}
internal SevenZipArchive(Stream stream, ReaderOptions readerOptions)
: base(ArchiveType.SevenZip, stream.AsEnumerable(), readerOptions)
{
}
internal SevenZipArchive()
: base(ArchiveType.SevenZip)
{
}
protected override IEnumerable<SevenZipVolume> LoadVolumes(IEnumerable<Stream> streams)
{
foreach (Stream s in streams)
{
if (!s.CanRead || !s.CanSeek)
{
throw new ArgumentException("Stream is not readable and seekable");
}
SevenZipVolume volume = new SevenZipVolume(s, ReaderOptions);
yield return volume;
}
}
protected override IEnumerable<SevenZipArchiveEntry> LoadEntries(IEnumerable<SevenZipVolume> volumes)
{
var stream = volumes.Single().Stream;
LoadFactory(stream);
var entries = new SevenZipArchiveEntry[database._files.Count];
for (int i = 0; i < database._files.Count; i++)
{
var file = database._files[i];
entries[i] = new SevenZipArchiveEntry(this, new SevenZipFilePart(stream, database, i, file, ReaderOptions.ArchiveEncoding));
yield return new SevenZipArchiveEntry(this, new SevenZipFilePart(stream, database, i, file, ReaderOptions.ArchiveEncoding));
}
foreach (var group in entries.Where(x => !x.IsDirectory).GroupBy(x => x.FilePart.Folder))
{
var isSolid = false;
foreach (var entry in group)
{
entry.IsSolid = isSolid;
isSolid = true; //mark others in this group as solid - same as rar behaviour.
}
}
return entries;
}
private void LoadFactory(Stream stream)

View File

@@ -1,7 +1,10 @@
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
using SharpCompress.Common.Tar;
using SharpCompress.Common.Tar.Headers;
@@ -31,34 +34,11 @@ namespace SharpCompress.Archives.Tar
/// </summary>
/// <param name="fileInfo"></param>
/// <param name="readerOptions"></param>
public static TarArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null)
public static TarArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default)
{
fileInfo.CheckNotNull(nameof(fileInfo));
return new TarArchive(new SourceStream(fileInfo, i => ArchiveVolumeFactory.GetFilePart(i, fileInfo), readerOptions ?? new ReaderOptions()));
}
/// <summary>
/// Constructor with all file parts passed in
/// </summary>
/// <param name="fileInfos"></param>
/// <param name="readerOptions"></param>
public static TarArchive Open(IEnumerable<FileInfo> fileInfos, ReaderOptions? readerOptions = null)
{
fileInfos.CheckNotNull(nameof(fileInfos));
FileInfo[] files = fileInfos.ToArray();
return new TarArchive(new SourceStream(files[0], i => i < files.Length ? files[i] : null, readerOptions ?? new ReaderOptions()));
}
/// <summary>
/// Constructor with all stream parts passed in
/// </summary>
/// <param name="streams"></param>
/// <param name="readerOptions"></param>
public static TarArchive Open(IEnumerable<Stream> streams, ReaderOptions? readerOptions = null)
{
streams.CheckNotNull(nameof(streams));
Stream[] strms = streams.ToArray();
return new TarArchive(new SourceStream(strms[0], i => i < strms.Length ? strms[i] : null, readerOptions ?? new ReaderOptions()));
return new TarArchive(fileInfo, readerOptions ?? new ReaderOptions(), cancellationToken);
}
/// <summary>
@@ -66,35 +46,35 @@ namespace SharpCompress.Archives.Tar
/// </summary>
/// <param name="stream"></param>
/// <param name="readerOptions"></param>
public static TarArchive Open(Stream stream, ReaderOptions? readerOptions = null)
public static TarArchive Open(Stream stream, ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default)
{
stream.CheckNotNull(nameof(stream));
return new TarArchive(new SourceStream(stream, i => null, readerOptions ?? new ReaderOptions()));
return new TarArchive(stream, readerOptions ?? new ReaderOptions(), cancellationToken);
}
public static bool IsTarFile(string filePath)
public static ValueTask<bool> IsTarFileAsync(string filePath, CancellationToken cancellationToken = default)
{
return IsTarFile(new FileInfo(filePath));
return IsTarFileAsync(new FileInfo(filePath), cancellationToken);
}
public static bool IsTarFile(FileInfo fileInfo)
public static async ValueTask<bool> IsTarFileAsync(FileInfo fileInfo, CancellationToken cancellationToken = default)
{
if (!fileInfo.Exists)
{
return false;
}
using (Stream stream = fileInfo.OpenRead())
{
return IsTarFile(stream);
}
await using Stream stream = fileInfo.OpenRead();
return await IsTarFileAsync(stream, cancellationToken);
}
public static bool IsTarFile(Stream stream)
public static async ValueTask<bool> IsTarFileAsync(Stream stream, CancellationToken cancellationToken = default)
{
try
{
TarHeader tarHeader = new TarHeader(new ArchiveEncoding());
bool readSucceeded = tarHeader.Read(new BinaryReader(stream));
TarHeader tarHeader = new(new ArchiveEncoding());
bool readSucceeded = await tarHeader.Read(stream, cancellationToken);
bool isEmptyArchive = tarHeader.Name.Length == 0 && tarHeader.Size == 0 && Enum.IsDefined(typeof(EntryType), tarHeader.EntryType);
return readSucceeded || isEmptyArchive;
}
@@ -104,20 +84,30 @@ namespace SharpCompress.Archives.Tar
return false;
}
protected override IEnumerable<TarVolume> LoadVolumes(SourceStream srcStream)
/// <summary>
/// Constructor with a FileInfo object to an existing file.
/// </summary>
/// <param name="fileInfo"></param>
/// <param name="readerOptions"></param>
internal TarArchive(FileInfo fileInfo, ReaderOptions readerOptions,
CancellationToken cancellationToken)
: base(ArchiveType.Tar, fileInfo, readerOptions, cancellationToken)
{
base.SrcStream.LoadAllParts(); //request all streams
int idx = 0;
return new TarVolume(srcStream, ReaderOptions, idx++).AsEnumerable(); //simple single volume or split, multivolume not supported
}
protected override IAsyncEnumerable<TarVolume> LoadVolumes(FileInfo file, CancellationToken cancellationToken)
{
return new TarVolume(file.OpenRead(), ReaderOptions).AsAsyncEnumerable();
}
/// <summary>
/// Constructor with a SourceStream able to handle FileInfo and Streams.
/// Takes multiple seekable Streams for a multi-part archive
/// </summary>
/// <param name="srcStream"></param>
/// <param name="options"></param>
internal TarArchive(SourceStream srcStream)
: base(ArchiveType.Tar, srcStream)
/// <param name="stream"></param>
/// <param name="readerOptions"></param>
internal TarArchive(Stream stream, ReaderOptions readerOptions,
CancellationToken cancellationToken)
: base(ArchiveType.Tar, stream, readerOptions, cancellationToken)
{
}
@@ -126,11 +116,18 @@ namespace SharpCompress.Archives.Tar
{
}
protected override IEnumerable<TarArchiveEntry> LoadEntries(IEnumerable<TarVolume> volumes)
protected override async IAsyncEnumerable<TarVolume> LoadVolumes(IAsyncEnumerable<Stream> streams,
[EnumeratorCancellation]CancellationToken cancellationToken)
{
Stream stream = volumes.Single().Stream;
yield return new TarVolume(await streams.FirstAsync(cancellationToken: cancellationToken), ReaderOptions);
}
protected override async IAsyncEnumerable<TarArchiveEntry> LoadEntries(IAsyncEnumerable<TarVolume> volumes,
[EnumeratorCancellation]CancellationToken cancellationToken)
{
Stream stream = (await volumes.SingleAsync(cancellationToken: cancellationToken)).Stream;
TarHeader? previousHeader = null;
foreach (TarHeader? header in TarHeaderFactory.ReadHeader(StreamingMode.Seekable, stream, ReaderOptions.ArchiveEncoding))
await foreach (TarHeader? header in TarHeaderFactory.ReadHeader(StreamingMode.Seekable, stream, ReaderOptions.ArchiveEncoding, cancellationToken))
{
if (header != null)
{
@@ -147,11 +144,11 @@ namespace SharpCompress.Archives.Tar
var oldStreamPos = stream.Position;
using (var entryStream = entry.OpenEntryStream())
await using (var entryStream = await entry.OpenEntryStreamAsync(cancellationToken))
{
using (var memoryStream = new MemoryStream())
await using (var memoryStream = new MemoryStream())
{
entryStream.TransferTo(memoryStream);
await entryStream.TransferToAsync(memoryStream, cancellationToken);
memoryStream.Position = 0;
var bytes = memoryStream.ToArray();
@@ -171,38 +168,37 @@ namespace SharpCompress.Archives.Tar
public static TarArchive Create()
{
return new TarArchive();
return new();
}
protected override TarArchiveEntry CreateEntryInternal(string filePath, Stream source,
long size, DateTime? modified, bool closeStream)
protected override ValueTask<TarArchiveEntry> CreateEntryInternal(string filePath, Stream source,
long size, DateTime? modified, bool closeStream,
CancellationToken cancellationToken)
{
return new TarWritableArchiveEntry(this, source, CompressionType.Unknown, filePath, size, modified,
closeStream);
return new (new TarWritableArchiveEntry(this, source, CompressionType.Unknown, filePath, size, modified,
closeStream));
}
protected override void SaveTo(Stream stream, WriterOptions options,
IEnumerable<TarArchiveEntry> oldEntries,
IEnumerable<TarArchiveEntry> newEntries)
protected override async ValueTask SaveToAsync(Stream stream, WriterOptions options,
IAsyncEnumerable<TarArchiveEntry> oldEntries,
IAsyncEnumerable<TarArchiveEntry> newEntries,
CancellationToken cancellationToken = default)
{
using (var writer = new TarWriter(stream, new TarWriterOptions(options)))
await using var writer = await TarWriter.CreateAsync(stream, new TarWriterOptions(options), cancellationToken);
await foreach (var entry in oldEntries.Concat(newEntries)
.Where(x => !x.IsDirectory)
.WithCancellation(cancellationToken))
{
foreach (var entry in oldEntries.Concat(newEntries)
.Where(x => !x.IsDirectory))
{
using (var entryStream = entry.OpenEntryStream())
{
writer.Write(entry.Key, entryStream, entry.LastModifiedTime, entry.Size);
}
}
await using var entryStream = await entry.OpenEntryStreamAsync(cancellationToken);
await writer.WriteAsync(entry.Key, entryStream, entry.LastModifiedTime, entry.Size, cancellationToken);
}
}
protected override IReader CreateReaderForSolidExtraction()
protected override async ValueTask<IReader> CreateReaderForSolidExtraction()
{
var stream = Volumes.Single().Stream;
var stream = (await Volumes.SingleAsync()).Stream;
stream.Position = 0;
return TarReader.Open(stream);
return await TarReader.OpenAsync(stream);
}
}
}

View File

@@ -1,5 +1,7 @@
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
using SharpCompress.Common.Tar;
@@ -13,9 +15,9 @@ namespace SharpCompress.Archives.Tar
Archive = archive;
}
public virtual Stream OpenEntryStream()
public virtual async ValueTask<Stream> OpenEntryStreamAsync(CancellationToken cancellationToken = default)
{
return Parts.Single().GetCompressedStream();
return await Parts.Single().GetCompressedStreamAsync(cancellationToken);
}
#region IArchiveEntry Members

View File

@@ -1,8 +1,10 @@
#nullable disable
#nullable disable
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
using SharpCompress.IO;
@@ -49,19 +51,19 @@ namespace SharpCompress.Archives.Tar
internal override IEnumerable<FilePart> Parts => throw new NotImplementedException();
Stream IWritableArchiveEntry.Stream => stream;
public override Stream OpenEntryStream()
public override ValueTask<Stream> OpenEntryStreamAsync(CancellationToken cancellationToken = default)
{
//ensure new stream is at the start, this could be reset
stream.Seek(0, SeekOrigin.Begin);
return NonDisposingStream.Create(stream);
return new(new NonDisposingStream(stream));
}
internal override void Close()
internal override async ValueTask CloseAsync()
{
if (closeStream)
{
stream.Dispose();
await stream.DisposeAsync();
}
}
}
}
}

View File

@@ -1,7 +1,10 @@
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
using SharpCompress.Common.Zip;
using SharpCompress.Common.Zip.Headers;
@@ -26,17 +29,6 @@ namespace SharpCompress.Archives.Zip
/// </summary>
public CompressionLevel DeflateCompressionLevel { get; set; }
/// <summary>
/// Constructor with a SourceStream able to handle FileInfo and Streams.
/// </summary>
/// <param name="srcStream"></param>
/// <param name="options"></param>
internal ZipArchive(SourceStream srcStream)
: base(ArchiveType.Zip, srcStream)
{
headerFactory = new SeekableZipHeaderFactory(srcStream.ReaderOptions.Password, srcStream.ReaderOptions.ArchiveEncoding);
}
/// <summary>
/// Constructor expects a filepath to an existing file.
/// </summary>
@@ -53,34 +45,11 @@ namespace SharpCompress.Archives.Zip
/// </summary>
/// <param name="fileInfo"></param>
/// <param name="readerOptions"></param>
public static ZipArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null)
public static ZipArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default)
{
fileInfo.CheckNotNull(nameof(fileInfo));
return new ZipArchive(new SourceStream(fileInfo, i => ZipArchiveVolumeFactory.GetFilePart(i, fileInfo), readerOptions ?? new ReaderOptions()));
}
/// <summary>
/// Constructor with all file parts passed in
/// </summary>
/// <param name="fileInfos"></param>
/// <param name="readerOptions"></param>
public static ZipArchive Open(IEnumerable<FileInfo> fileInfos, ReaderOptions? readerOptions = null)
{
fileInfos.CheckNotNull(nameof(fileInfos));
FileInfo[] files = fileInfos.ToArray();
return new ZipArchive(new SourceStream(files[0], i => i < files.Length ? files[i] : null, readerOptions ?? new ReaderOptions()));
}
/// <summary>
/// Constructor with all stream parts passed in
/// </summary>
/// <param name="streams"></param>
/// <param name="readerOptions"></param>
public static ZipArchive Open(IEnumerable<Stream> streams, ReaderOptions? readerOptions = null)
{
streams.CheckNotNull(nameof(streams));
Stream[] strms = streams.ToArray();
return new ZipArchive(new SourceStream(strms[0], i => i < strms.Length ? strms[i] : null, readerOptions ?? new ReaderOptions()));
return new ZipArchive(fileInfo, readerOptions ?? new ReaderOptions(), cancellationToken);
}
/// <summary>
@@ -88,35 +57,45 @@ namespace SharpCompress.Archives.Zip
/// </summary>
/// <param name="stream"></param>
/// <param name="readerOptions"></param>
public static ZipArchive Open(Stream stream, ReaderOptions? readerOptions = null)
public static ZipArchive Open(Stream stream, ReaderOptions? readerOptions = null,
CancellationToken cancellationToken = default)
{
stream.CheckNotNull(nameof(stream));
return new ZipArchive(new SourceStream(stream, i => null, readerOptions ?? new ReaderOptions()));
return new ZipArchive(stream, readerOptions ?? new ReaderOptions(), cancellationToken);
}
public static bool IsZipFile(string filePath, string? password = null)
public static ValueTask<bool> IsZipFile(string filePath, string? password = null)
{
return IsZipFile(new FileInfo(filePath), password);
return IsZipFileAsync(new FileInfo(filePath), password);
}
public static bool IsZipFile(FileInfo fileInfo, string? password = null)
public static async ValueTask<bool> IsZipFileAsync(FileInfo fileInfo, string? password = null)
{
if (!fileInfo.Exists)
{
return false;
}
using (Stream stream = fileInfo.OpenRead())
{
return IsZipFile(stream, password);
}
await using Stream stream = fileInfo.OpenRead();
return await IsZipFileAsync(stream, password);
}
public static bool IsZipFile(Stream stream, string? password = null)
public static async ValueTask<bool> IsZipFileAsync(Stream stream, string? password = null, CancellationToken cancellationToken = default)
{
StreamingZipHeaderFactory headerFactory = new StreamingZipHeaderFactory(password, new ArchiveEncoding());
StreamingZipHeaderFactory headerFactory = new(password, new ArchiveEncoding());
try
{
ZipHeader? header = headerFactory.ReadStreamHeader(stream).FirstOrDefault(x => x.ZipHeaderType != ZipHeaderType.Split);
RewindableStream rewindableStream;
if (stream is RewindableStream rs)
{
rewindableStream = rs;
}
else
{
rewindableStream = new RewindableStream(stream);
}
ZipHeader? header = await headerFactory.ReadStreamHeader(rewindableStream, cancellationToken)
.FirstOrDefaultAsync(x => x.ZipHeaderType != ZipHeaderType.Split, cancellationToken: cancellationToken);
if (header is null)
{
return false;
@@ -133,62 +112,22 @@ namespace SharpCompress.Archives.Zip
}
}
public static bool IsZipMulti(Stream stream, string? password = null)
/// <summary>
/// Constructor with a FileInfo object to an existing file.
/// </summary>
/// <param name="fileInfo"></param>
/// <param name="readerOptions"></param>
internal ZipArchive(FileInfo fileInfo, ReaderOptions readerOptions,
CancellationToken cancellationToken)
: base(ArchiveType.Zip, fileInfo, readerOptions, cancellationToken)
{
StreamingZipHeaderFactory headerFactory = new StreamingZipHeaderFactory(password, new ArchiveEncoding());
try
{
ZipHeader? header = headerFactory.ReadStreamHeader(stream).FirstOrDefault(x => x.ZipHeaderType != ZipHeaderType.Split);
if (header is null)
{
if (stream.CanSeek) //could be multipart. Test for central directory - might not be z64 safe
{
SeekableZipHeaderFactory z = new SeekableZipHeaderFactory(password, new ArchiveEncoding());
var x = z.ReadSeekableHeader(stream).FirstOrDefault();
return x?.ZipHeaderType == ZipHeaderType.DirectoryEntry;
}
else
return false;
}
return Enum.IsDefined(typeof(ZipHeaderType), header.ZipHeaderType);
}
catch (CryptographicException)
{
return true;
}
catch
{
return false;
}
headerFactory = new SeekableZipHeaderFactory(readerOptions.Password, readerOptions.ArchiveEncoding);
}
protected override IEnumerable<ZipVolume> LoadVolumes(SourceStream srcStream)
protected override IAsyncEnumerable<ZipVolume> LoadVolumes(FileInfo file,
CancellationToken cancellationToken)
{
base.SrcStream.LoadAllParts(); //request all streams
base.SrcStream.Position = 0;
List<Stream> streams = base.SrcStream.Streams.ToList();
int idx = 0;
if (streams.Count > 1) //test part 2 - true = multipart not split
{
streams[1].Position += 4; //skip the POST_DATA_DESCRIPTOR to prevent an exception
bool isZip = IsZipFile(streams[1], ReaderOptions.Password);
streams[1].Position -= 4;
if (isZip)
{
base.SrcStream.IsVolumes = true;
var tmp = streams[0]; //arcs as zip, z01 ... swap the zip the end
streams.RemoveAt(0);
streams.Add(tmp);
//streams[0].Position = 4; //skip the POST_DATA_DESCRIPTOR to prevent an exception
return streams.Select(a => new ZipVolume(a, ReaderOptions, idx++));
}
}
//split mode or single file
return new ZipVolume(base.SrcStream, ReaderOptions, idx++).AsEnumerable();
return new ZipVolume(file.OpenRead(), ReaderOptions).AsAsyncEnumerable();
}
internal ZipArchive()
@@ -196,10 +135,31 @@ namespace SharpCompress.Archives.Zip
{
}
protected override IEnumerable<ZipArchiveEntry> LoadEntries(IEnumerable<ZipVolume> volumes)
/// <summary>
/// Takes multiple seekable Streams for a multi-part archive
/// </summary>
/// <param name="stream"></param>
/// <param name="readerOptions"></param>
internal ZipArchive(Stream stream, ReaderOptions readerOptions,
CancellationToken cancellationToken)
: base(ArchiveType.Zip, stream, readerOptions, cancellationToken)
{
var vols = volumes.ToArray();
foreach (ZipHeader h in headerFactory.ReadSeekableHeader(vols.Last().Stream))
headerFactory = new SeekableZipHeaderFactory(readerOptions.Password, readerOptions.ArchiveEncoding);
}
protected override async IAsyncEnumerable<ZipVolume> LoadVolumes(IAsyncEnumerable<Stream> streams,
[EnumeratorCancellation]CancellationToken cancellationToken)
{
yield return new ZipVolume(await streams.FirstAsync(cancellationToken: cancellationToken), ReaderOptions);
}
protected override async IAsyncEnumerable<ZipArchiveEntry> LoadEntries(IAsyncEnumerable<ZipVolume> volumes,
[EnumeratorCancellation]CancellationToken cancellationToken)
{
await Task.CompletedTask;
var volume = await volumes.SingleAsync(cancellationToken: cancellationToken);
Stream stream = volume.Stream;
await foreach (ZipHeader h in headerFactory.ReadSeekableHeader(stream, cancellationToken))
{
if (h != null)
{
@@ -207,22 +167,16 @@ namespace SharpCompress.Archives.Zip
{
case ZipHeaderType.DirectoryEntry:
{
DirectoryEntryHeader deh = (DirectoryEntryHeader)h;
Stream s;
if (deh.RelativeOffsetOfEntryHeader + deh.CompressedSize > vols[deh.DiskNumberStart].Stream.Length)
{
var v = vols.Skip(deh.DiskNumberStart).ToArray();
s = new SourceStream(v[0].Stream, i => i < v.Length ? v[i].Stream : null, new ReaderOptions() { LeaveStreamOpen = true });
}
else
s = vols[deh.DiskNumberStart].Stream;
yield return new ZipArchiveEntry(this, new SeekableZipFilePart(headerFactory, deh, s));
yield return new ZipArchiveEntry(this,
new SeekableZipFilePart(headerFactory,
(DirectoryEntryHeader)h,
stream));
}
break;
break;
case ZipHeaderType.DirectoryEnd:
{
byte[] bytes = ((DirectoryEndHeader)h).Comment ?? Array.Empty<byte>();
volumes.Last().Comment = ReaderOptions.ArchiveEncoding.Decode(bytes);
volume.Comment = ReaderOptions.ArchiveEncoding.Decode(bytes);
yield break;
}
}
@@ -230,42 +184,42 @@ namespace SharpCompress.Archives.Zip
}
}
public void SaveTo(Stream stream)
public ValueTask SaveToAsync(Stream stream, CancellationToken cancellationToken = default)
{
SaveTo(stream, new WriterOptions(CompressionType.Deflate));
return SaveToAsync(stream, new WriterOptions(CompressionType.Deflate), cancellationToken);
}
protected override void SaveTo(Stream stream, WriterOptions options,
IEnumerable<ZipArchiveEntry> oldEntries,
IEnumerable<ZipArchiveEntry> newEntries)
protected override async ValueTask SaveToAsync(Stream stream, WriterOptions options,
IAsyncEnumerable<ZipArchiveEntry> oldEntries,
IAsyncEnumerable<ZipArchiveEntry> newEntries,
CancellationToken cancellationToken = default)
{
using (var writer = new ZipWriter(stream, new ZipWriterOptions(options)))
await using var writer = new ZipWriter(stream, new ZipWriterOptions(options));
await foreach (var entry in oldEntries.Concat(newEntries)
.Where(x => !x.IsDirectory)
.WithCancellation(cancellationToken))
{
foreach (var entry in oldEntries.Concat(newEntries)
.Where(x => !x.IsDirectory))
await using (var entryStream = await entry.OpenEntryStreamAsync(cancellationToken))
{
using (var entryStream = entry.OpenEntryStream())
{
writer.Write(entry.Key, entryStream, entry.LastModifiedTime);
}
await writer.WriteAsync(entry.Key, entryStream, entry.LastModifiedTime, cancellationToken);
}
}
}
protected override ZipArchiveEntry CreateEntryInternal(string filePath, Stream source, long size, DateTime? modified,
bool closeStream)
protected override ValueTask<ZipArchiveEntry> CreateEntryInternal(string filePath, Stream source, long size, DateTime? modified,
bool closeStream, CancellationToken cancellationToken = default)
{
return new ZipWritableArchiveEntry(this, source, filePath, size, modified, closeStream);
return new(new ZipWritableArchiveEntry(this, source, filePath, size, modified, closeStream));
}
public static ZipArchive Create()
{
return new ZipArchive();
return new();
}
protected override IReader CreateReaderForSolidExtraction()
protected override async ValueTask<IReader> CreateReaderForSolidExtraction()
{
var stream = Volumes.Single().Stream;
var stream = (await Volumes.SingleAsync()).Stream;
stream.Position = 0;
return ZipReader.Open(stream, ReaderOptions);
}

View File

@@ -1,5 +1,7 @@
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common.Zip;
namespace SharpCompress.Archives.Zip
@@ -12,9 +14,9 @@ namespace SharpCompress.Archives.Zip
Archive = archive;
}
public virtual Stream OpenEntryStream()
public virtual ValueTask<Stream> OpenEntryStreamAsync(CancellationToken cancellationToken = default)
{
return Parts.Single().GetCompressedStream();
return Parts.Single().GetCompressedStreamAsync(cancellationToken);
}
#region IArchiveEntry Members

View File

@@ -1,34 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using SharpCompress.Common.Rar;
using SharpCompress.Readers;
using System.Linq;
using System.Text;
using SharpCompress.Common.Rar.Headers;
using System.Text.RegularExpressions;
namespace SharpCompress.Archives.Zip
{
internal static class ZipArchiveVolumeFactory
{
internal static FileInfo? GetFilePart(int index, FileInfo part1) //base the name on the first part
{
FileInfo? item = null;
//load files with zip/zipx first. Swapped to end once loaded in ZipArchive
//new style .zip, z01.. | .zipx, zx01 - if the numbers go beyond 99 then they use 100 ...1000 etc
Match m = Regex.Match(part1.Name, @"^(.*\.)(zipx?|zx?[0-9]+)$", RegexOptions.IgnoreCase);
if (m.Success)
item = new FileInfo(Path.Combine(part1.DirectoryName!, String.Concat(m.Groups[1].Value, Regex.Replace(m.Groups[2].Value, @"[^xz]", ""), index.ToString().PadLeft(2, '0'))));
else //split - 001, 002 ...
return ArchiveVolumeFactory.GetFilePart(index, part1);
if (item != null && item.Exists)
return item;
return null; //no more items
}
}
}

View File

@@ -1,6 +1,8 @@
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
using SharpCompress.IO;
@@ -49,20 +51,20 @@ namespace SharpCompress.Archives.Zip
Stream IWritableArchiveEntry.Stream => stream;
public override Stream OpenEntryStream()
public override ValueTask<Stream> OpenEntryStreamAsync(CancellationToken cancellationToken = default)
{
//ensure new stream is at the start, this could be reset
stream.Seek(0, SeekOrigin.Begin);
return NonDisposingStream.Create(stream);
return new(new NonDisposingStream(stream));
}
internal override void Close()
internal override async ValueTask CloseAsync()
{
if (closeStream && !isDisposed)
{
stream.Dispose();
await stream.DisposeAsync();
isDisposed = true;
}
}
}
}
}

View File

@@ -0,0 +1,25 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace SharpCompress
{
public static class AsyncEnumerable
{
public static IAsyncEnumerable<T> Empty<T>() => EmptyAsyncEnumerable<T>.Instance;
private class EmptyAsyncEnumerable<T> : IAsyncEnumerator<T>, IAsyncEnumerable<T>
{
public static readonly EmptyAsyncEnumerable<T> Instance =
new();
public T Current => default!;
public ValueTask DisposeAsync() => default;
public ValueTask<bool> MoveNextAsync() => new(false);
public IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = new CancellationToken())
{
return this;
}
}
}
}

View File

@@ -24,7 +24,7 @@ namespace SharpCompress.Common
/// Set this when you want to use a custom method for all decoding operations.
/// </summary>
/// <returns>string Func(bytes, index, length)</returns>
public Func<byte[], int, int, string>? CustomDecoder { get; set; }
//public Func<byte[], int, int, string>? CustomDecoder { get; set; }
public ArchiveEncoding()
: this(Encoding.Default, Encoding.Default)
@@ -36,7 +36,7 @@ namespace SharpCompress.Common
Password = password;
}
#if !NETFRAMEWORK
#if !NET461
static ArchiveEncoding()
{
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
@@ -50,7 +50,12 @@ namespace SharpCompress.Common
public string Decode(byte[] bytes, int start, int length)
{
return GetDecoder().Invoke(bytes, start, length);
return GetEncoding().GetString(bytes, start, length);
}
public string Decode(ReadOnlySpan<byte> span)
{
return GetEncoding().GetString(span);
}
public string DecodeUTF8(byte[] bytes)
@@ -67,10 +72,5 @@ namespace SharpCompress.Common
{
return Forced ?? Default ?? Encoding.UTF8;
}
public Func<byte[], int, int, string> GetDecoder()
{
return CustomDecoder ?? ((bytes, index, count) => GetEncoding().GetString(bytes, index, count));
}
}
}

View File

@@ -1,6 +1,6 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace SharpCompress.Common
{
@@ -71,17 +71,16 @@ namespace SharpCompress.Common
/// </summary>
public abstract bool IsSplitAfter { get; }
public int VolumeIndexFirst => this.Parts?.FirstOrDefault()?.Index ?? 0;
public int VolumeIndexLast => this.Parts?.LastOrDefault()?.Index ?? 0;
/// <inheritdoc/>
public override string ToString() => Key;
internal abstract IEnumerable<FilePart> Parts { get; }
public bool IsSolid { get; set; }
internal bool IsSolid { get; set; }
internal virtual void Close()
internal virtual ValueTask CloseAsync()
{
return new ();
}
/// <summary>

View File

@@ -1,10 +1,13 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.IO;
using SharpCompress.Readers;
namespace SharpCompress.Common
{
public class EntryStream : Stream
public class EntryStream : AsyncStream
{
private readonly IReader _reader;
private readonly Stream _stream;
@@ -20,25 +23,24 @@ namespace SharpCompress.Common
/// <summary>
/// When reading a stream from OpenEntryStream, the stream must be completed so use this to finish reading the entire entry.
/// </summary>
public void SkipEntry()
public async ValueTask SkipEntryAsync(CancellationToken cancellationToken = default)
{
this.Skip();
await this.SkipAsync(cancellationToken);
_completed = true;
}
protected override void Dispose(bool disposing)
public override async ValueTask DisposeAsync()
{
if (!(_completed || _reader.Cancelled))
{
SkipEntry();
await SkipEntryAsync();
}
if (_isDisposed)
{
return;
}
_isDisposed = true;
base.Dispose(disposing);
_stream.Dispose();
await _stream.DisposeAsync();
}
public override bool CanRead => true;
@@ -46,18 +48,13 @@ namespace SharpCompress.Common
public override bool CanSeek => false;
public override bool CanWrite => false;
public override void Flush()
{
}
public override long Length => _stream.Length;
public override long Position { get => throw new NotSupportedException(); set => throw new NotSupportedException(); }
public override int Read(byte[] buffer, int offset, int count)
public override async ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
{
int read = _stream.Read(buffer, offset, count);
int read = await _stream.ReadAsync(buffer, cancellationToken);
if (read <= 0)
{
_completed = true;
@@ -65,14 +62,14 @@ namespace SharpCompress.Common
return read;
}
public override int ReadByte()
public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
{
int value = _stream.ReadByte();
if (value == -1)
{
_completed = true;
}
return value;
throw new NotSupportedException();
}
public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
throw new NotSupportedException();
}
public override long Seek(long offset, SeekOrigin origin)
@@ -84,10 +81,5 @@ namespace SharpCompress.Common
{
throw new NotSupportedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotSupportedException();
}
}
}

View File

@@ -1,5 +1,7 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace SharpCompress.Common
{
@@ -8,31 +10,21 @@ namespace SharpCompress.Common
/// <summary>
/// Extract to specific directory, retaining filename
/// </summary>
public static void WriteEntryToDirectory(IEntry entry,
public static async ValueTask WriteEntryToDirectoryAsync(IEntry entry,
string destinationDirectory,
ExtractionOptions? options,
Action<string, ExtractionOptions?> write)
Func<string, ExtractionOptions?, CancellationToken, ValueTask> write,
CancellationToken cancellationToken = default)
{
string destinationFileName;
string file = Path.GetFileName(entry.Key);
string fullDestinationDirectoryPath = Path.GetFullPath(destinationDirectory);
//check for trailing slash.
if (fullDestinationDirectoryPath[fullDestinationDirectoryPath.Length - 1] != Path.DirectorySeparatorChar)
{
fullDestinationDirectoryPath += Path.DirectorySeparatorChar;
}
if (!Directory.Exists(fullDestinationDirectoryPath))
{
throw new ExtractionException($"Directory does not exist to extract to: {fullDestinationDirectoryPath}");
}
options ??= new ExtractionOptions()
{
Overwrite = true
};
string file = Path.GetFileName(entry.Key);
if (options.ExtractFullPath)
{
string folder = Path.GetDirectoryName(entry.Key)!;
@@ -63,7 +55,7 @@ namespace SharpCompress.Common
{
throw new ExtractionException("Entry is trying to write a file outside of the destination directory.");
}
write(destinationFileName, options);
await write(destinationFileName, options, cancellationToken);
}
else if (options.ExtractFullPath && !Directory.Exists(destinationFileName))
{
@@ -71,11 +63,12 @@ namespace SharpCompress.Common
}
}
public static void WriteEntryToFile(IEntry entry, string destinationFileName,
public static async ValueTask WriteEntryToFileAsync(IEntry entry, string destinationFileName,
ExtractionOptions? options,
Action<string, FileMode> openAndWrite)
Func<string, FileMode, CancellationToken, ValueTask> openAndWrite,
CancellationToken cancellationToken = default)
{
if (entry.LinkTarget != null)
if (entry.LinkTarget is not null)
{
if (options?.WriteSymbolicLink is null)
{
@@ -96,7 +89,7 @@ namespace SharpCompress.Common
fm = FileMode.CreateNew;
}
openAndWrite(destinationFileName, fm);
await openAndWrite(destinationFileName, fm, cancellationToken);
entry.PreserveExtractionOptions(destinationFileName, options);
}
}

View File

@@ -1,4 +1,6 @@
using System.IO;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace SharpCompress.Common
{
@@ -11,10 +13,9 @@ namespace SharpCompress.Common
internal ArchiveEncoding ArchiveEncoding { get; }
internal abstract string FilePartName { get; }
public int Index { get; set; }
internal abstract string? FilePartName { get; }
internal abstract Stream GetCompressedStream();
internal abstract ValueTask<Stream> GetCompressedStreamAsync(CancellationToken cancellationToken);
internal abstract Stream? GetRawStream();
internal bool Skipped { get; set; }
}

View File

@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;
using System.Threading;
namespace SharpCompress.Common.GZip
{
@@ -17,7 +19,7 @@ namespace SharpCompress.Common.GZip
public override long Crc => _filePart.Crc ?? 0;
public override string Key => _filePart.FilePartName;
public override string Key => _filePart.FilePartName ?? string.Empty;
public override string? LinkTarget => null;
@@ -41,9 +43,12 @@ namespace SharpCompress.Common.GZip
internal override IEnumerable<FilePart> Parts => _filePart.AsEnumerable<FilePart>();
internal static IEnumerable<GZipEntry> GetEntries(Stream stream, OptionsBase options)
internal static async IAsyncEnumerable<GZipEntry> GetEntries(Stream stream, OptionsBase options,
[EnumeratorCancellation] CancellationToken cancellationToken)
{
yield return new GZipEntry(new GZipFilePart(stream, options.ArchiveEncoding));
var part = new GZipFilePart(options.ArchiveEncoding);
await part.Initialize(stream, cancellationToken);
yield return new GZipEntry(part);
}
}
}

View File

@@ -1,7 +1,10 @@
using System;
using System;
using System.Buffers;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common.Tar.Headers;
using SharpCompress.Compressors;
using SharpCompress.Compressors.Deflate;
@@ -11,34 +14,44 @@ namespace SharpCompress.Common.GZip
internal sealed class GZipFilePart : FilePart
{
private string? _name;
private readonly Stream _stream;
//init only
#nullable disable
private Stream _stream;
#nullable enable
internal GZipFilePart(Stream stream, ArchiveEncoding archiveEncoding)
internal GZipFilePart(ArchiveEncoding archiveEncoding)
: base(archiveEncoding)
{
}
internal async ValueTask Initialize(Stream stream, CancellationToken cancellationToken)
{
_stream = stream;
ReadAndValidateGzipHeader();
if (stream.CanSeek)
{
long position = stream.Position;
stream.Position = stream.Length - 8;
ReadTrailer();
await ReadTrailerAsync(cancellationToken);
stream.Position = position;
}
EntryStartPosition = stream.Position;
}
internal long EntryStartPosition { get; }
internal long EntryStartPosition { get; private set; }
internal DateTime? DateModified { get; private set; }
internal uint? Crc { get; private set; }
internal uint? UncompressedSize { get; private set; }
internal int? Crc { get; private set; }
internal int? UncompressedSize { get; private set; }
internal override string FilePartName => _name!;
internal override string? FilePartName => _name;
internal override Stream GetCompressedStream()
internal override async ValueTask<Stream> GetCompressedStreamAsync(CancellationToken cancellationToken)
{
return new DeflateStream(_stream, CompressionMode.Decompress, CompressionLevel.Default);
var stream = new GZipStream(_stream, CompressionMode.Decompress, CompressionLevel.Default);
await stream.ReadAsync(Array.Empty<byte>(), 0, 0, cancellationToken);
_name = stream.FileName;
DateModified = stream.LastModified;
return stream;
}
internal override Stream GetRawStream()
@@ -46,93 +59,12 @@ namespace SharpCompress.Common.GZip
return _stream;
}
private void ReadTrailer()
private async ValueTask ReadTrailerAsync(CancellationToken cancellationToken)
{
// Read and potentially verify the GZIP trailer: CRC32 and size mod 2^32
Span<byte> trailer = stackalloc byte[8];
int n = _stream.Read(trailer);
Crc = BinaryPrimitives.ReadUInt32LittleEndian(trailer);
UncompressedSize = BinaryPrimitives.ReadUInt32LittleEndian(trailer.Slice(4));
}
private void ReadAndValidateGzipHeader()
{
// read the header on the first read
Span<byte> header = stackalloc byte[10];
int n = _stream.Read(header);
// workitem 8501: handle edge case (decompress empty stream)
if (n == 0)
{
return;
}
if (n != 10)
{
throw new ZlibException("Not a valid GZIP stream.");
}
if (header[0] != 0x1F || header[1] != 0x8B || header[2] != 8)
{
throw new ZlibException("Bad GZIP header.");
}
int timet = BinaryPrimitives.ReadInt32LittleEndian(header.Slice(4));
DateModified = TarHeader.EPOCH.AddSeconds(timet);
if ((header[3] & 0x04) == 0x04)
{
// read and discard extra field
n = _stream.Read(header.Slice(0, 2)); // 2-byte length field
short extraLength = (short)(header[0] + header[1] * 256);
byte[] extra = new byte[extraLength];
if (!_stream.ReadFully(extra))
{
throw new ZlibException("Unexpected end-of-file reading GZIP header.");
}
n = extraLength;
}
if ((header[3] & 0x08) == 0x08)
{
_name = ReadZeroTerminatedString(_stream);
}
if ((header[3] & 0x10) == 0x010)
{
ReadZeroTerminatedString(_stream);
}
if ((header[3] & 0x02) == 0x02)
{
_stream.ReadByte(); // CRC16, ignore
}
}
private string ReadZeroTerminatedString(Stream stream)
{
Span<byte> buf1 = stackalloc byte[1];
var list = new List<byte>();
bool done = false;
do
{
// workitem 7740
int n = stream.Read(buf1);
if (n != 1)
{
throw new ZlibException("Unexpected EOF reading GZIP header.");
}
if (buf1[0] == 0)
{
done = true;
}
else
{
list.Add(buf1[0]);
}
}
while (!done);
byte[] buffer = list.ToArray();
return ArchiveEncoding.Decode(buffer);
Crc = await _stream.ReadInt32(cancellationToken);
UncompressedSize = await _stream.ReadInt32(cancellationToken);
}
}
}

View File

@@ -1,12 +1,12 @@
using System.IO;
using System.IO;
using SharpCompress.Readers;
namespace SharpCompress.Common.GZip
{
public class GZipVolume : Volume
{
public GZipVolume(Stream stream, ReaderOptions options, int index = 0)
: base(stream, options, index)
public GZipVolume(Stream stream, ReaderOptions options)
: base(stream, options)
{
}
@@ -20,4 +20,4 @@ namespace SharpCompress.Common.GZip
public override bool IsMultiVolume => true;
}
}
}

View File

@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System;
namespace SharpCompress.Common
{
@@ -15,12 +14,9 @@ namespace SharpCompress.Common
bool IsDirectory { get; }
bool IsEncrypted { get; }
bool IsSplitAfter { get; }
bool IsSolid { get; }
int VolumeIndexFirst { get; }
int VolumeIndexLast { get; }
DateTime? LastAccessedTime { get; }
DateTime? LastModifiedTime { get; }
long Size { get; }
int? Attrib { get; }
}
}
}

View File

@@ -1,11 +1,8 @@
using System;
using System;
namespace SharpCompress.Common
{
public interface IVolume : IDisposable
public interface IVolume : IAsyncDisposable
{
int Index { get; }
string FileName { get; }
}
}
}

View File

@@ -10,7 +10,7 @@ namespace SharpCompress.Common.Rar
/// <summary>
/// As the V2017 port isn't complete, add this check to use the legacy Rar code.
/// </summary>
internal bool IsRarV3 => FileHeader.CompressionAlgorithm == 20 || FileHeader.CompressionAlgorithm == 26 || FileHeader.CompressionAlgorithm == 29 || FileHeader.CompressionAlgorithm == 36; //Nanook - Added 20+26 as Test arc from WinRar2.8 (algo 20) was failing with 2017 code
internal bool IsRarV3 => FileHeader.CompressionAlgorithm == 29 || FileHeader.CompressionAlgorithm == 36;
/// <summary>
/// The File's 32 bit CRC Hash

View File

@@ -1,4 +1,4 @@
using System.IO;
using System.IO;
using SharpCompress.Common.Rar.Headers;
namespace SharpCompress.Common.Rar
@@ -8,12 +8,11 @@ namespace SharpCompress.Common.Rar
/// </summary>
internal abstract class RarFilePart : FilePart
{
internal RarFilePart(MarkHeader mh, FileHeader fh, int index)
internal RarFilePart(MarkHeader mh, FileHeader fh)
: base(fh.ArchiveEncoding)
{
MarkHeader = mh;
FileHeader = fh;
Index = index;
}
internal MarkHeader MarkHeader { get; }
@@ -25,4 +24,4 @@ namespace SharpCompress.Common.Rar
return null;
}
}
}
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -14,10 +14,9 @@ namespace SharpCompress.Common.Rar
public abstract class RarVolume : Volume
{
private readonly RarHeaderFactory _headerFactory;
internal int _maxCompressionAlgorithm;
internal RarVolume(StreamingMode mode, Stream stream, ReaderOptions options, int index = 0)
: base(stream, options, index)
internal RarVolume(StreamingMode mode, Stream stream, ReaderOptions options)
: base(stream, options)
{
_headerFactory = new RarHeaderFactory(mode, options);
}
@@ -52,8 +51,6 @@ namespace SharpCompress.Common.Rar
case HeaderType.File:
{
var fh = (FileHeader)header;
if (_maxCompressionAlgorithm < fh.CompressionAlgorithm)
_maxCompressionAlgorithm = fh.CompressionAlgorithm;
yield return CreateFilePart(lastMarkHeader!, fh);
}
break;
@@ -113,37 +110,5 @@ namespace SharpCompress.Common.Rar
return ArchiveHeader.IsSolid;
}
}
public int MinVersion
{
get
{
EnsureArchiveHeaderLoaded();
if (_maxCompressionAlgorithm >= 50)
return 5; //5-6
else if (_maxCompressionAlgorithm >= 29)
return 3; //3-4
else if (_maxCompressionAlgorithm >= 20)
return 2; //2
else
return 1;
}
}
public int MaxVersion
{
get
{
EnsureArchiveHeaderLoaded();
if (_maxCompressionAlgorithm >= 50)
return 6; //5-6
else if (_maxCompressionAlgorithm >= 29)
return 4; //3-4
else if (_maxCompressionAlgorithm >= 20)
return 2; //2
else
return 1;
}
}
}
}
}

View File

@@ -5,6 +5,8 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Compressors.LZMA;
using SharpCompress.Compressors.LZMA.Utilites;
using SharpCompress.IO;
@@ -783,7 +785,7 @@ namespace SharpCompress.Common.SevenZip
}
}
private List<byte[]> ReadAndDecodePackedStreams(long baseOffset, IPasswordProvider pass)
private async ValueTask<List<byte[]>> ReadAndDecodePackedStreams(long baseOffset, IPasswordProvider pass, CancellationToken cancellationToken)
{
#if DEBUG
Log.WriteLine("-- ReadAndDecodePackedStreams --");
@@ -815,8 +817,8 @@ namespace SharpCompress.Common.SevenZip
dataStartPos += packSize;
}
var outStream = DecoderStreamHelper.CreateDecoderStream(_stream, oldDataStartPos, myPackSizes,
folder, pass);
var outStream = await DecoderStreamHelper.CreateDecoderStream(_stream, oldDataStartPos, myPackSizes,
folder, pass, cancellationToken);
int unpackSize = checked((int)folder.GetUnpackSize());
byte[] data = new byte[unpackSize];
@@ -845,7 +847,7 @@ namespace SharpCompress.Common.SevenZip
}
}
private void ReadHeader(ArchiveDatabase db, IPasswordProvider getTextPassword)
private async ValueTask ReadHeader(ArchiveDatabase db, IPasswordProvider getTextPassword, CancellationToken cancellationToken)
{
#if DEBUG
Log.WriteLine("-- ReadHeader --");
@@ -864,7 +866,7 @@ namespace SharpCompress.Common.SevenZip
List<byte[]> dataVector = null;
if (type == BlockType.AdditionalStreamsInfo)
{
dataVector = ReadAndDecodePackedStreams(db._startPositionAfterHeader, getTextPassword);
dataVector = await ReadAndDecodePackedStreams(db._startPositionAfterHeader, getTextPassword, cancellationToken);
type = ReadId();
}
@@ -1248,7 +1250,7 @@ namespace SharpCompress.Common.SevenZip
if (nextHeaderOffset > _streamEnding - db._startPositionAfterHeader)
{
throw new InvalidOperationException("nextHeaderOffset is invalid");
throw new IndexOutOfRangeException();
}
_stream.Seek(nextHeaderOffset, SeekOrigin.Current);

View File

@@ -30,7 +30,7 @@ namespace SharpCompress.Common.SevenZip
}
}
throw new InvalidOperationException();
throw new Exception();
}
public int GetNumOutStreams()
@@ -185,4 +185,4 @@ namespace SharpCompress.Common.SevenZip
return true;
}
}
}
}

View File

@@ -32,7 +32,7 @@ namespace SharpCompress.Common.SevenZip
public override DateTime? ArchivedTime => null;
public override bool IsEncrypted => FilePart.IsEncrypted;
public override bool IsEncrypted => false;
public override bool IsDirectory => FilePart.Header.IsDir;

View File

@@ -1,6 +1,8 @@
using System;
using System;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.IO;
namespace SharpCompress.Common.SevenZip
@@ -26,6 +28,7 @@ namespace SharpCompress.Common.SevenZip
internal CFileItem Header { get; }
internal CFolder? Folder { get; }
internal int Index { get; }
internal override string FilePartName => Header.Name;
@@ -34,11 +37,11 @@ namespace SharpCompress.Common.SevenZip
return null;
}
internal override Stream GetCompressedStream()
internal override async ValueTask<Stream> GetCompressedStreamAsync(CancellationToken cancellationToken)
{
if (!Header.HasStream)
{
return null!;
return Stream.Null;
}
var folderStream = _database.GetFolderStream(_stream, Folder!, _database.PasswordProvider);
@@ -51,7 +54,7 @@ namespace SharpCompress.Common.SevenZip
}
if (skipSize > 0)
{
folderStream.Skip(skipSize);
await folderStream.SkipAsync(skipSize, cancellationToken);
}
return new ReadOnlySubStream(folderStream, Header.Size);
}
@@ -101,7 +104,5 @@ namespace SharpCompress.Common.SevenZip
throw new NotImplementedException();
}
}
internal bool IsEncrypted => Folder!._coders.FindIndex(c => c._methodId._id == CMethodId.K_AES_ID) != -1;
}
}
}

View File

@@ -1,13 +1,13 @@
using System.IO;
using System.IO;
using SharpCompress.Readers;
namespace SharpCompress.Common.SevenZip
{
public class SevenZipVolume : Volume
{
public SevenZipVolume(Stream stream, ReaderOptions readerFactoryOptions, int index = 0)
: base(stream, readerFactoryOptions, index)
public SevenZipVolume(Stream stream, ReaderOptions readerFactoryOptions)
: base(stream, readerFactoryOptions)
{
}
}
}
}

View File

@@ -1,9 +1,12 @@
#nullable disable
using System;
using System.Buffers;
using System.Buffers.Binary;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace SharpCompress.Common.Tar.Headers
{
@@ -19,9 +22,11 @@ namespace SharpCompress.Common.Tar.Headers
internal string Name { get; set; }
internal string LinkName { get; set; }
internal long Mode { get; set; }
internal long UserId { get; set; }
internal long GroupId { get; set; }
//internal int Mode { get; set; }
//internal int UserId { get; set; }
//internal string UserName { get; set; }
//internal int GroupId { get; set; }
//internal string GroupName { get; set; }
internal long Size { get; set; }
internal DateTime LastModifiedTime { get; set; }
internal EntryType EntryType { get; set; }
@@ -30,48 +35,48 @@ namespace SharpCompress.Common.Tar.Headers
internal const int BLOCK_SIZE = 512;
internal void Write(Stream output)
internal async Task WriteAsync(Stream output)
{
byte[] buffer = new byte[BLOCK_SIZE];
using var buffer = MemoryPool<byte>.Shared.Rent(BLOCK_SIZE);
WriteOctalBytes(511, buffer, 100, 8); // file mode
WriteOctalBytes(0, buffer, 108, 8); // owner ID
WriteOctalBytes(0, buffer, 116, 8); // group ID
WriteOctalBytes(511, buffer.Memory.Span, 100, 8); // file mode
WriteOctalBytes(0, buffer.Memory.Span, 108, 8); // owner ID
WriteOctalBytes(0, buffer.Memory.Span, 116, 8); // group ID
//ArchiveEncoding.UTF8.GetBytes("magic").CopyTo(buffer, 257);
var nameByteCount = ArchiveEncoding.GetEncoding().GetByteCount(Name);
if (nameByteCount > 100)
{
// Set mock filename and filetype to indicate the next block is the actual name of the file
WriteStringBytes("././@LongLink", buffer, 0, 100);
buffer[156] = (byte)EntryType.LongName;
WriteOctalBytes(nameByteCount + 1, buffer, 124, 12);
WriteStringBytes("././@LongLink", buffer.Memory.Span, 0, 100);
buffer.Memory.Span[156] = (byte)EntryType.LongName;
WriteOctalBytes(nameByteCount + 1, buffer.Memory.Span, 124, 12);
}
else
{
WriteStringBytes(ArchiveEncoding.Encode(Name), buffer, 100);
WriteOctalBytes(Size, buffer, 124, 12);
WriteStringBytes(ArchiveEncoding.Encode(Name), buffer.Memory, 100);
WriteOctalBytes(Size, buffer.Memory.Span, 124, 12);
var time = (long)(LastModifiedTime.ToUniversalTime() - EPOCH).TotalSeconds;
WriteOctalBytes(time, buffer, 136, 12);
buffer[156] = (byte)EntryType;
WriteOctalBytes(time, buffer.Memory.Span, 136, 12);
buffer.Memory.Span[156] = (byte)EntryType;
if (Size >= 0x1FFFFFFFF)
{
Span<byte> bytes12 = stackalloc byte[12];
BinaryPrimitives.WriteInt64BigEndian(bytes12.Slice(4), Size);
bytes12[0] |= 0x80;
bytes12.CopyTo(buffer.AsSpan(124));
using var bytes12 = MemoryPool<byte>.Shared.Rent(12);
BinaryPrimitives.WriteInt64BigEndian(bytes12.Memory.Span.Slice(4), Size);
bytes12.Memory.Span[0] |= 0x80;
bytes12.Memory.CopyTo(buffer.Memory.Slice(124));
}
}
int crc = RecalculateChecksum(buffer);
WriteOctalBytes(crc, buffer, 148, 8);
int crc = RecalculateChecksum(buffer.Memory);
WriteOctalBytes(crc, buffer.Memory.Span, 148, 8);
output.Write(buffer, 0, buffer.Length);
await output.WriteAsync(buffer.Memory.Slice(0, BLOCK_SIZE));
if (nameByteCount > 100)
{
WriteLongFilenameHeader(output);
await WriteLongFilenameHeaderAsync(output);
// update to short name lower than 100 - [max bytes of one character].
// subtracting bytes is needed because preventing infinite loop(example code is here).
//
@@ -80,14 +85,14 @@ namespace SharpCompress.Common.Tar.Headers
//
// and then infinite recursion is occured in WriteLongFilenameHeader because truncated.Length is 102.
Name = ArchiveEncoding.Decode(ArchiveEncoding.Encode(Name), 0, 100 - ArchiveEncoding.GetEncoding().GetMaxByteCount(1));
Write(output);
await WriteAsync(output);
}
}
private void WriteLongFilenameHeader(Stream output)
private async Task WriteLongFilenameHeaderAsync(Stream output)
{
byte[] nameBytes = ArchiveEncoding.Encode(Name);
output.Write(nameBytes, 0, nameBytes.Length);
await output.WriteAsync(nameBytes.AsMemory());
// pad to multiple of BlockSize bytes, and make sure a terminating null is added
int numPaddingBytes = BLOCK_SIZE - (nameBytes.Length % BLOCK_SIZE);
@@ -95,51 +100,56 @@ namespace SharpCompress.Common.Tar.Headers
{
numPaddingBytes = BLOCK_SIZE;
}
output.Write(stackalloc byte[numPaddingBytes]);
using var padding = MemoryPool<byte>.Shared.Rent(numPaddingBytes);
padding.Memory.Span.Clear();
await output.WriteAsync(padding.Memory.Slice(0, numPaddingBytes));
}
internal bool Read(BinaryReader reader)
internal async ValueTask<bool> Read(Stream stream, CancellationToken cancellationToken)
{
var buffer = ReadBlock(reader);
if (buffer.Length == 0)
var block = MemoryPool<byte>.Shared.Rent(BLOCK_SIZE);
bool readFullyAsync = await stream.ReadAsync(block.Memory.Slice(0, BLOCK_SIZE), cancellationToken) == BLOCK_SIZE;
if (readFullyAsync is false)
{
return false;
}
// for symlinks, additionally read the linkname
if (ReadEntryType(buffer) == EntryType.SymLink)
if (ReadEntryType(block.Memory.Span) == EntryType.SymLink)
{
LinkName = ArchiveEncoding.Decode(buffer, 157, 100).TrimNulls();
LinkName = ArchiveEncoding.Decode(block.Memory.Span.Slice(157, 100)).TrimNulls();
}
if (ReadEntryType(buffer) == EntryType.LongName)
if (ReadEntryType(block.Memory.Span) == EntryType.LongName)
{
Name = ReadLongName(reader, buffer);
buffer = ReadBlock(reader);
Name = await ReadLongName(stream, block.Memory.Slice(0,BLOCK_SIZE), cancellationToken);
readFullyAsync = await stream.ReadAsync(block.Memory.Slice(0, BLOCK_SIZE), cancellationToken) == BLOCK_SIZE;
if (readFullyAsync is false)
{
return false;
}
}
else
{
Name = ArchiveEncoding.Decode(buffer, 0, 100).TrimNulls();
Name = ArchiveEncoding.Decode(block.Memory.Span.Slice( 0, 100)).TrimNulls();
}
EntryType = ReadEntryType(buffer);
Size = ReadSize(buffer);
EntryType = ReadEntryType(block.Memory.Span);
Size = ReadSize(block.Memory.Slice(0, BLOCK_SIZE));
Mode = ReadAsciiInt64Base8(buffer, 100, 7);
if(EntryType == EntryType.Directory)
Mode |= 0b1_000_000_000;
UserId = ReadAsciiInt64Base8(buffer, 108, 7);
GroupId = ReadAsciiInt64Base8(buffer, 116, 7);
long unixTimeStamp = ReadAsciiInt64Base8(buffer, 136, 11);
//Mode = ReadASCIIInt32Base8(buffer, 100, 7);
//UserId = ReadASCIIInt32Base8(buffer, 108, 7);
//GroupId = ReadASCIIInt32Base8(buffer, 116, 7);
long unixTimeStamp = ReadAsciiInt64Base8(block.Memory.Span.Slice(136, 11));
LastModifiedTime = EPOCH.AddSeconds(unixTimeStamp).ToLocalTime();
Magic = ArchiveEncoding.Decode(buffer, 257, 6).TrimNulls();
Magic = ArchiveEncoding.Decode(block.Memory.Span.Slice( 257, 6)).TrimNulls();
if (!string.IsNullOrEmpty(Magic)
&& "ustar".Equals(Magic))
{
string namePrefix = ArchiveEncoding.Decode(buffer, 345, 157);
string namePrefix = ArchiveEncoding.Decode(block.Memory.Span.Slice( 345, 157));
namePrefix = namePrefix.TrimNulls();
if (!string.IsNullOrEmpty(namePrefix))
{
@@ -154,55 +164,46 @@ namespace SharpCompress.Common.Tar.Headers
return true;
}
private string ReadLongName(BinaryReader reader, byte[] buffer)
private async ValueTask<string> ReadLongName(Stream reader, ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken)
{
var size = ReadSize(buffer);
var nameLength = (int)size;
var nameBytes = reader.ReadBytes(nameLength);
using var rented = MemoryPool<byte>.Shared.Rent(nameLength);
var nameBytes = rented.Memory.Slice(0, nameLength);
await reader.ReadAsync(nameBytes, cancellationToken);
var remainingBytesToRead = BLOCK_SIZE - (nameLength % BLOCK_SIZE);
// Read the rest of the block and discard the data
if (remainingBytesToRead < BLOCK_SIZE)
{
reader.ReadBytes(remainingBytesToRead);
using var remaining = MemoryPool<byte>.Shared.Rent(remainingBytesToRead);
await reader.ReadAsync(remaining.Memory.Slice(0, remainingBytesToRead), cancellationToken);
}
return ArchiveEncoding.Decode(nameBytes, 0, nameBytes.Length).TrimNulls();
return ArchiveEncoding.Decode(nameBytes.Span).TrimNulls();
}
private static EntryType ReadEntryType(byte[] buffer)
private static EntryType ReadEntryType(Span<byte> buffer)
{
return (EntryType)buffer[156];
}
private long ReadSize(byte[] buffer)
private long ReadSize(ReadOnlyMemory<byte> buffer)
{
if ((buffer[124] & 0x80) == 0x80) // if size in binary
if ((buffer.Span[124] & 0x80) == 0x80) // if size in binary
{
return BinaryPrimitives.ReadInt64BigEndian(buffer.AsSpan(0x80));
return BinaryPrimitives.ReadInt64BigEndian(buffer.Span.Slice(0x80));
}
return ReadAsciiInt64Base8(buffer, 124, 11);
return ReadAsciiInt64Base8(buffer.Span.Slice(124, 11));
}
private static byte[] ReadBlock(BinaryReader reader)
private static void WriteStringBytes(ReadOnlySpan<byte> name, Memory<byte> buffer, int length)
{
byte[] buffer = reader.ReadBytes(BLOCK_SIZE);
if (buffer.Length != 0 && buffer.Length < BLOCK_SIZE)
{
throw new InvalidOperationException("Buffer is invalid size");
}
return buffer;
}
private static void WriteStringBytes(ReadOnlySpan<byte> name, Span<byte> buffer, int length)
{
name.CopyTo(buffer);
name.CopyTo(buffer.Span.Slice(0));
int i = Math.Min(length, name.Length);
buffer.Slice(i, length - i).Clear();
buffer.Slice(i, length - i).Span.Clear();
}
private static void WriteStringBytes(string name, byte[] buffer, int offset, int length)
private static void WriteStringBytes(string name, Span<byte> buffer, int offset, int length)
{
int i;
@@ -217,7 +218,7 @@ namespace SharpCompress.Common.Tar.Headers
}
}
private static void WriteOctalBytes(long value, byte[] buffer, int offset, int length)
private static void WriteOctalBytes(long value, Span<byte> buffer, int offset, int length)
{
string val = Convert.ToString(value, 8);
int shift = length - val.Length - 1;
@@ -231,19 +232,9 @@ namespace SharpCompress.Common.Tar.Headers
}
}
private static int ReadAsciiInt32Base8(byte[] buffer, int offset, int count)
private static long ReadAsciiInt64Base8(ReadOnlySpan<byte> buffer)
{
string s = Encoding.UTF8.GetString(buffer, offset, count).TrimNulls();
if (string.IsNullOrEmpty(s))
{
return 0;
}
return Convert.ToInt32(s, 8);
}
private static long ReadAsciiInt64Base8(byte[] buffer, int offset, int count)
{
string s = Encoding.UTF8.GetString(buffer, offset, count).TrimNulls();
string s = Encoding.UTF8.GetString(buffer).TrimNulls();
if (string.IsNullOrEmpty(s))
{
return 0;
@@ -267,38 +258,20 @@ namespace SharpCompress.Common.Tar.Headers
(byte)' ', (byte)' ', (byte)' ', (byte)' '
};
internal static int RecalculateChecksum(byte[] buf)
private static int RecalculateChecksum(Memory<byte> buf)
{
// Set default value for checksum. That is 8 spaces.
eightSpaces.CopyTo(buf, 148);
eightSpaces.CopyTo(buf.Slice(148));
// Calculate checksum
int headerChecksum = 0;
foreach (byte b in buf)
foreach (byte b in buf.Span)
{
headerChecksum += b;
}
return headerChecksum;
}
internal static int RecalculateAltChecksum(byte[] buf)
{
eightSpaces.CopyTo(buf, 148);
int headerChecksum = 0;
foreach (byte b in buf)
{
if ((b & 0x80) == 0x80)
{
headerChecksum -= b ^ 0x80;
}
else
{
headerChecksum += b;
}
}
return headerChecksum;
}
public long? DataStartPosition { get; set; }
public string Magic { get; set; }

View File

@@ -3,6 +3,8 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;
using System.Threading;
using SharpCompress.Common.Tar.Headers;
using SharpCompress.IO;
@@ -44,18 +46,13 @@ namespace SharpCompress.Common.Tar
public override bool IsSplitAfter => false;
public long Mode => _filePart.Header.Mode;
public long UserID => _filePart.Header.UserId;
public long GroupId => _filePart.Header.GroupId;
internal override IEnumerable<FilePart> Parts => _filePart.AsEnumerable<FilePart>();
internal static IEnumerable<TarEntry> GetEntries(StreamingMode mode, Stream stream,
CompressionType compressionType, ArchiveEncoding archiveEncoding)
internal static async IAsyncEnumerable<TarEntry> GetEntries(StreamingMode mode, Stream stream,
CompressionType compressionType, ArchiveEncoding archiveEncoding,
[EnumeratorCancellation]CancellationToken cancellationToken)
{
foreach (TarHeader h in TarHeaderFactory.ReadHeader(mode, stream, archiveEncoding))
await foreach (TarHeader h in TarHeaderFactory.ReadHeader(mode, stream, archiveEncoding, cancellationToken))
{
if (h != null)
{

View File

@@ -1,6 +1,7 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common.Tar.Headers;
using SharpCompress.IO;
namespace SharpCompress.Common.Tar
{
@@ -19,14 +20,14 @@ namespace SharpCompress.Common.Tar
internal override string FilePartName => Header.Name;
internal override Stream GetCompressedStream()
internal override ValueTask<Stream> GetCompressedStreamAsync(CancellationToken cancellationToken)
{
if (_seekableStream != null)
{
_seekableStream.Position = Header.DataStartPosition!.Value;
return new TarReadOnlySubStream(_seekableStream, Header.Size);
return new(new TarReadOnlySubStream(_seekableStream, Header.Size));
}
return Header.PackedStream;
return new(Header.PackedStream);
}
internal override Stream? GetRawStream()

View File

@@ -1,5 +1,7 @@
using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;
using System.Threading;
using SharpCompress.Common.Tar.Headers;
using SharpCompress.IO;
@@ -7,17 +9,17 @@ namespace SharpCompress.Common.Tar
{
internal static class TarHeaderFactory
{
internal static IEnumerable<TarHeader?> ReadHeader(StreamingMode mode, Stream stream, ArchiveEncoding archiveEncoding)
internal static async IAsyncEnumerable<TarHeader?> ReadHeader(StreamingMode mode, Stream stream, ArchiveEncoding archiveEncoding,
[EnumeratorCancellation]CancellationToken cancellationToken)
{
while (true)
{
TarHeader? header = null;
try
{
BinaryReader reader = new BinaryReader(stream);
header = new TarHeader(archiveEncoding);
if (!header.Read(reader))
if (!await header.Read(stream, cancellationToken))
{
yield break;
}
@@ -25,10 +27,10 @@ namespace SharpCompress.Common.Tar
{
case StreamingMode.Seekable:
{
header.DataStartPosition = reader.BaseStream.Position;
header.DataStartPosition = stream.Position;
//skip to nearest 512
reader.BaseStream.Position += PadTo512(header.Size);
stream.Position += PadTo512(header.Size);
}
break;
case StreamingMode.Streaming:

View File

@@ -1,6 +1,8 @@
using SharpCompress.IO;
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace SharpCompress.Common.Tar
{
@@ -14,7 +16,7 @@ namespace SharpCompress.Common.Tar
BytesLeftToRead = bytesToRead;
}
protected override void Dispose(bool disposing)
public override async ValueTask DisposeAsync()
{
if (_isDisposed)
{
@@ -23,22 +25,17 @@ namespace SharpCompress.Common.Tar
_isDisposed = true;
if (disposing)
// Ensure we read all remaining blocks for this entry.
await Stream.SkipAsync(BytesLeftToRead);
_amountRead += BytesLeftToRead;
// If the last block wasn't a full 512 bytes, skip the remaining padding bytes.
var bytesInLastBlock = _amountRead % 512;
if (bytesInLastBlock != 0)
{
// Ensure we read all remaining blocks for this entry.
Stream.Skip(BytesLeftToRead);
_amountRead += BytesLeftToRead;
// If the last block wasn't a full 512 bytes, skip the remaining padding bytes.
var bytesInLastBlock = _amountRead % 512;
if (bytesInLastBlock != 0)
{
Stream.Skip(512 - bytesInLastBlock);
}
await Stream.SkipAsync(512 - bytesInLastBlock);
}
base.Dispose(disposing);
}
private long BytesLeftToRead { get; set; }
@@ -49,22 +46,18 @@ namespace SharpCompress.Common.Tar
public override bool CanWrite => false;
public override void Flush()
{
throw new NotSupportedException();
}
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)
public override async ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
{
if (BytesLeftToRead < count)
var count = buffer.Length;
if (BytesLeftToRead < buffer.Length)
{
count = (int)BytesLeftToRead;
}
int read = Stream.Read(buffer, offset, count);
int read = await Stream.ReadAsync(buffer.Slice(0, count), cancellationToken);
if (read > 0)
{
BytesLeftToRead -= read;
@@ -73,20 +66,9 @@ namespace SharpCompress.Common.Tar
return read;
}
public override int ReadByte()
public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
if (BytesLeftToRead <= 0)
{
return -1;
}
int value = Stream.ReadByte();
if (value != -1)
{
--BytesLeftToRead;
++_amountRead;
}
return value;
throw new NotSupportedException();
}
public override long Seek(long offset, SeekOrigin origin)
@@ -98,10 +80,5 @@ namespace SharpCompress.Common.Tar
{
throw new NotSupportedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotSupportedException();
}
}
}

View File

@@ -1,13 +1,13 @@
using System.IO;
using System.IO;
using SharpCompress.Readers;
namespace SharpCompress.Common.Tar
{
public class TarVolume : Volume
{
public TarVolume(Stream stream, ReaderOptions readerOptions, int index = 0)
: base(stream, readerOptions, index)
public TarVolume(Stream stream, ReaderOptions readerOptions)
: base(stream, readerOptions)
{
}
}
}
}

View File

@@ -1,5 +1,5 @@
using System;
using System.IO;
using System.IO;
using System.Threading.Tasks;
using SharpCompress.IO;
using SharpCompress.Readers;
@@ -9,13 +9,12 @@ namespace SharpCompress.Common
{
private readonly Stream _actualStream;
internal Volume(Stream stream, ReaderOptions readerOptions, int index = 0)
internal Volume(Stream stream, ReaderOptions readerOptions)
{
Index = index;
ReaderOptions = readerOptions;
if (readerOptions.LeaveStreamOpen)
{
stream = NonDisposingStream.Create(stream);
stream = new NonDisposingStream(stream);
}
_actualStream = stream;
}
@@ -30,27 +29,14 @@ namespace SharpCompress.Common
/// </summary>
public virtual bool IsFirstVolume => true;
public virtual int Index { get; internal set; }
public string FileName { get { return (_actualStream as FileStream)?.Name!; } }
/// <summary>
/// RarArchive is part of a multi-part archive.
/// </summary>
public virtual bool IsMultiVolume => true;
protected virtual void Dispose(bool disposing)
public ValueTask DisposeAsync()
{
if (disposing)
{
_actualStream.Dispose();
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
return _actualStream.DisposeAsync();
}
}
}
}

View File

@@ -1,4 +1,6 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace SharpCompress.Common.Zip.Headers
{
@@ -9,29 +11,29 @@ namespace SharpCompress.Common.Zip.Headers
{
}
internal override void Read(BinaryReader reader)
internal override async ValueTask Read(Stream stream, CancellationToken cancellationToken)
{
VolumeNumber = reader.ReadUInt16();
FirstVolumeWithDirectory = reader.ReadUInt16();
TotalNumberOfEntriesInDisk = reader.ReadUInt16();
TotalNumberOfEntries = reader.ReadUInt16();
DirectorySize = reader.ReadUInt32();
DirectoryStartOffsetRelativeToDisk = reader.ReadUInt32();
CommentLength = reader.ReadUInt16();
Comment = reader.ReadBytes(CommentLength);
VolumeNumber = await stream.ReadUInt16(cancellationToken);
FirstVolumeWithDirectory = await stream.ReadUInt16(cancellationToken);
TotalNumberOfEntriesInDisk = await stream.ReadUInt16(cancellationToken);
TotalNumberOfEntries = await stream.ReadUInt16(cancellationToken);
DirectorySize = await stream.ReadUInt32(cancellationToken);
DirectoryStartOffsetRelativeToDisk = await stream.ReadUInt32(cancellationToken);
CommentLength = await stream.ReadUInt16(cancellationToken);
Comment = await stream.ReadBytes(CommentLength ?? 0, cancellationToken);
}
public ushort VolumeNumber { get; private set; }
public ushort? VolumeNumber { get; private set; }
public ushort FirstVolumeWithDirectory { get; private set; }
public ushort? FirstVolumeWithDirectory { get; private set; }
public ushort TotalNumberOfEntriesInDisk { get; private set; }
public ushort? TotalNumberOfEntriesInDisk { get; private set; }
public uint DirectorySize { get; private set; }
public uint? DirectorySize { get; private set; }
public uint DirectoryStartOffsetRelativeToDisk { get; private set; }
public uint? DirectoryStartOffsetRelativeToDisk { get; private set; }
public ushort CommentLength { get; private set; }
public ushort? CommentLength { get; private set; }
public byte[]? Comment { get; private set; }

View File

@@ -1,5 +1,7 @@
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace SharpCompress.Common.Zip.Headers
{
@@ -10,28 +12,28 @@ namespace SharpCompress.Common.Zip.Headers
{
}
internal override void Read(BinaryReader reader)
internal override async ValueTask Read(Stream stream, CancellationToken cancellationToken)
{
Version = reader.ReadUInt16();
VersionNeededToExtract = reader.ReadUInt16();
Flags = (HeaderFlags)reader.ReadUInt16();
CompressionMethod = (ZipCompressionMethod)reader.ReadUInt16();
LastModifiedTime = reader.ReadUInt16();
LastModifiedDate = reader.ReadUInt16();
Crc = reader.ReadUInt32();
CompressedSize = reader.ReadUInt32();
UncompressedSize = reader.ReadUInt32();
ushort nameLength = reader.ReadUInt16();
ushort extraLength = reader.ReadUInt16();
ushort commentLength = reader.ReadUInt16();
DiskNumberStart = reader.ReadUInt16();
InternalFileAttributes = reader.ReadUInt16();
ExternalFileAttributes = reader.ReadUInt32();
RelativeOffsetOfEntryHeader = reader.ReadUInt32();
Version = await stream.ReadUInt16(cancellationToken);
VersionNeededToExtract = await stream.ReadUInt16(cancellationToken);
Flags = (HeaderFlags)await stream.ReadUInt16(cancellationToken);
CompressionMethod = (ZipCompressionMethod)await stream.ReadUInt16(cancellationToken);
LastModifiedTime = await stream.ReadUInt16(cancellationToken);
LastModifiedDate = await stream.ReadUInt16(cancellationToken);
Crc = await stream.ReadUInt32(cancellationToken);
CompressedSize = await stream.ReadUInt32(cancellationToken);
UncompressedSize = await stream.ReadUInt32(cancellationToken);
ushort nameLength = await stream.ReadUInt16(cancellationToken);
ushort extraLength = await stream.ReadUInt16(cancellationToken);
ushort commentLength = await stream.ReadUInt16(cancellationToken);
DiskNumberStart = await stream.ReadUInt16(cancellationToken);
InternalFileAttributes = await stream.ReadUInt16(cancellationToken);
ExternalFileAttributes = await stream.ReadUInt32(cancellationToken);
RelativeOffsetOfEntryHeader = await stream.ReadUInt32(cancellationToken);
byte[] name = reader.ReadBytes(nameLength);
byte[] extra = reader.ReadBytes(extraLength);
byte[] comment = reader.ReadBytes(commentLength);
byte[] name = await stream.ReadBytes(nameLength, cancellationToken);
byte[] extra = await stream.ReadBytes(extraLength, cancellationToken);
byte[] comment = await stream.ReadBytes(commentLength, cancellationToken);
// According to .ZIP File Format Specification
//
@@ -63,8 +65,6 @@ namespace SharpCompress.Common.Zip.Headers
var zip64ExtraData = Extra.OfType<Zip64ExtendedInformationExtraField>().FirstOrDefault();
if (zip64ExtraData != null)
{
zip64ExtraData.Process(UncompressedSize, CompressedSize, RelativeOffsetOfEntryHeader, DiskNumberStart);
if (CompressedSize == uint.MaxValue)
{
CompressedSize = zip64ExtraData.CompressedSize;

View File

@@ -1,4 +1,6 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace SharpCompress.Common.Zip.Headers
{
@@ -9,8 +11,9 @@ namespace SharpCompress.Common.Zip.Headers
{
}
internal override void Read(BinaryReader reader)
internal override ValueTask Read(Stream stream, CancellationToken cancellationToken)
{
return new();
}
}
}

View File

@@ -1,5 +1,7 @@
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace SharpCompress.Common.Zip.Headers
{
@@ -10,20 +12,20 @@ namespace SharpCompress.Common.Zip.Headers
{
}
internal override void Read(BinaryReader reader)
internal override async ValueTask Read(Stream stream, CancellationToken cancellationToken)
{
Version = reader.ReadUInt16();
Flags = (HeaderFlags)reader.ReadUInt16();
CompressionMethod = (ZipCompressionMethod)reader.ReadUInt16();
LastModifiedTime = reader.ReadUInt16();
LastModifiedDate = reader.ReadUInt16();
Crc = reader.ReadUInt32();
CompressedSize = reader.ReadUInt32();
UncompressedSize = reader.ReadUInt32();
ushort nameLength = reader.ReadUInt16();
ushort extraLength = reader.ReadUInt16();
byte[] name = reader.ReadBytes(nameLength);
byte[] extra = reader.ReadBytes(extraLength);
Version = await stream.ReadUInt16(cancellationToken);
Flags = (HeaderFlags)await stream.ReadUInt16(cancellationToken);
CompressionMethod = (ZipCompressionMethod)await stream.ReadUInt16(cancellationToken);
LastModifiedTime = await stream.ReadUInt16(cancellationToken);
LastModifiedDate = await stream.ReadUInt16(cancellationToken);
Crc = await stream.ReadUInt32(cancellationToken);
CompressedSize = await stream.ReadUInt32(cancellationToken);
UncompressedSize = await stream.ReadUInt32(cancellationToken);
ushort nameLength = await stream.ReadUInt16(cancellationToken);
ushort extraLength = await stream.ReadUInt16(cancellationToken);
byte[] name = await stream.ReadBytes(nameLength, cancellationToken);
byte[] extra = await stream.ReadBytes(extraLength, cancellationToken);
// According to .ZIP File Format Specification
//
@@ -53,8 +55,6 @@ namespace SharpCompress.Common.Zip.Headers
var zip64ExtraData = Extra.OfType<Zip64ExtendedInformationExtraField>().FirstOrDefault();
if (zip64ExtraData != null)
{
zip64ExtraData.Process(UncompressedSize, CompressedSize, 0, 0);
if (CompressedSize == uint.MaxValue)
{
CompressedSize = zip64ExtraData.CompressedSize;

View File

@@ -66,74 +66,46 @@ namespace SharpCompress.Common.Zip.Headers
public Zip64ExtendedInformationExtraField(ExtraDataType type, ushort length, byte[] dataBytes)
: base(type, length, dataBytes)
{
Process();
}
// From the spec, values are only in the extradata if the standard
// value is set to 0xFFFFFFFF (or 0xFFFF for the Disk Start Number).
// Values, if present, must appear in the following order:
// - Original Size
// - Compressed Size
// - Relative Header Offset
// - Disk Start Number
public void Process(long uncompressedFileSize, long compressedFileSize, long relativeHeaderOffset, ushort diskNumber)
private void Process()
{
var bytesRequired = ((uncompressedFileSize == uint.MaxValue) ? 8 : 0)
+ ((compressedFileSize == uint.MaxValue) ? 8 : 0)
+ ((relativeHeaderOffset == uint.MaxValue) ? 8 : 0)
+ ((diskNumber == ushort.MaxValue) ? 4 : 0);
var currentIndex = 0;
if (bytesRequired > DataBytes.Length)
if (DataBytes.Length >= 8)
{
throw new ArchiveException("Zip64 extended information extra field is not large enough for the required information");
UncompressedSize = BinaryPrimitives.ReadInt64LittleEndian(DataBytes);
}
if (uncompressedFileSize == uint.MaxValue)
if (DataBytes.Length >= 16)
{
UncompressedSize = BinaryPrimitives.ReadInt64LittleEndian(DataBytes.AsSpan(currentIndex));
currentIndex += 8;
CompressedSize = BinaryPrimitives.ReadInt64LittleEndian(DataBytes.AsSpan(8));
}
if (compressedFileSize == uint.MaxValue)
if (DataBytes.Length >= 24)
{
CompressedSize = BinaryPrimitives.ReadInt64LittleEndian(DataBytes.AsSpan(currentIndex));
currentIndex += 8;
RelativeOffsetOfEntryHeader = BinaryPrimitives.ReadInt64LittleEndian(DataBytes.AsSpan(16));
}
if (relativeHeaderOffset == uint.MaxValue)
if (DataBytes.Length >= 28)
{
RelativeOffsetOfEntryHeader = BinaryPrimitives.ReadInt64LittleEndian(DataBytes.AsSpan(currentIndex));
currentIndex += 8;
VolumeNumber = BinaryPrimitives.ReadUInt32LittleEndian(DataBytes.AsSpan(24));
}
if (diskNumber == ushort.MaxValue)
switch (DataBytes.Length)
{
VolumeNumber = BinaryPrimitives.ReadUInt32LittleEndian(DataBytes.AsSpan(currentIndex));
case 8:
case 16:
case 24:
case 28:
break;
default:
throw new ArchiveException($"Unexpected size of of Zip64 extended information extra field: {DataBytes.Length}");
}
}
/// <summary>
/// Uncompressed file size. Only valid after <see cref="Process(long, long, long, ushort)"/> has been called and if the
/// original entry header had a corresponding 0xFFFFFFFF value.
/// </summary>
public long UncompressedSize { get; private set; }
/// <summary>
/// Compressed file size. Only valid after <see cref="Process(long, long, long, ushort)"/> has been called and if the
/// original entry header had a corresponding 0xFFFFFFFF value.
/// </summary>
public long CompressedSize { get; private set; }
/// <summary>
/// Relative offset of the entry header. Only valid after <see cref="Process(long, long, long, ushort)"/> has been called and if the
/// original entry header had a corresponding 0xFFFFFFFF value.
/// </summary>
public long RelativeOffsetOfEntryHeader { get; private set; }
/// <summary>
/// Volume number. Only valid after <see cref="Process(long, long, long, ushort)"/> has been called and if the
/// original entry header had a corresponding 0xFFFF value.
/// </summary>
public uint VolumeNumber { get; private set; }
}

View File

@@ -1,5 +1,7 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace SharpCompress.Common.Zip.Headers
{
@@ -10,7 +12,7 @@ namespace SharpCompress.Common.Zip.Headers
{
}
internal override void Read(BinaryReader reader)
internal override ValueTask Read(Stream stream, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}

View File

@@ -1,4 +1,6 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace SharpCompress.Common.Zip.Headers
{
@@ -9,18 +11,18 @@ namespace SharpCompress.Common.Zip.Headers
{
}
internal override void Read(BinaryReader reader)
internal override async ValueTask Read(Stream stream, CancellationToken cancellationToken)
{
SizeOfDirectoryEndRecord = (long)reader.ReadUInt64();
VersionMadeBy = reader.ReadUInt16();
VersionNeededToExtract = reader.ReadUInt16();
VolumeNumber = reader.ReadUInt32();
FirstVolumeWithDirectory = reader.ReadUInt32();
TotalNumberOfEntriesInDisk = (long)reader.ReadUInt64();
TotalNumberOfEntries = (long)reader.ReadUInt64();
DirectorySize = (long)reader.ReadUInt64();
DirectoryStartOffsetRelativeToDisk = (long)reader.ReadUInt64();
DataSector = reader.ReadBytes((int)(SizeOfDirectoryEndRecord - SIZE_OF_FIXED_HEADER_DATA_EXCEPT_SIGNATURE_AND_SIZE_FIELDS));
SizeOfDirectoryEndRecord = (long)await stream.ReadUInt64(cancellationToken);
VersionMadeBy = await stream.ReadUInt16(cancellationToken);
VersionNeededToExtract = await stream.ReadUInt16(cancellationToken);
VolumeNumber = await stream.ReadUInt32(cancellationToken);
FirstVolumeWithDirectory = await stream.ReadUInt32(cancellationToken);
TotalNumberOfEntriesInDisk = (long)await stream.ReadUInt64(cancellationToken);
TotalNumberOfEntries = (long)await stream.ReadUInt64(cancellationToken);
DirectorySize = (long)await stream.ReadUInt64(cancellationToken);
DirectoryStartOffsetRelativeToDisk = (long)await stream.ReadUInt64(cancellationToken);
DataSector = await stream.ReadBytes((int)(SizeOfDirectoryEndRecord - SIZE_OF_FIXED_HEADER_DATA_EXCEPT_SIGNATURE_AND_SIZE_FIELDS), cancellationToken);
}
private const int SIZE_OF_FIXED_HEADER_DATA_EXCEPT_SIGNATURE_AND_SIZE_FIELDS = 44;

View File

@@ -1,4 +1,6 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace SharpCompress.Common.Zip.Headers
{
@@ -9,11 +11,11 @@ namespace SharpCompress.Common.Zip.Headers
{
}
internal override void Read(BinaryReader reader)
internal override async ValueTask Read(Stream stream, CancellationToken cancellationToken)
{
FirstVolumeWithDirectory = reader.ReadUInt32();
RelativeOffsetOfTheEndOfDirectoryRecord = (long)reader.ReadUInt64();
TotalNumberOfVolumes = reader.ReadUInt32();
FirstVolumeWithDirectory = await stream.ReadUInt32(cancellationToken);
RelativeOffsetOfTheEndOfDirectoryRecord = (long)await stream.ReadUInt64(cancellationToken);
TotalNumberOfVolumes = await stream.ReadUInt32(cancellationToken);
}
public uint FirstVolumeWithDirectory { get; private set; }

View File

@@ -105,6 +105,6 @@ namespace SharpCompress.Common.Zip.Headers
internal ZipFilePart Part { get; set; }
internal bool IsZip64 => CompressedSize >= uint.MaxValue;
internal bool IsZip64 => CompressedSize == uint.MaxValue;
}
}

View File

@@ -1,4 +1,6 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace SharpCompress.Common.Zip.Headers
{
@@ -12,7 +14,7 @@ namespace SharpCompress.Common.Zip.Headers
internal ZipHeaderType ZipHeaderType { get; }
internal abstract void Read(BinaryReader reader);
internal abstract ValueTask Read(Stream stream, CancellationToken cancellationToken);
internal bool HasData { get; set; }
}

View File

@@ -1,4 +1,6 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common.Zip.Headers;
using SharpCompress.IO;
@@ -17,22 +19,22 @@ namespace SharpCompress.Common.Zip
_directoryEntryHeader = header;
}
internal override Stream GetCompressedStream()
internal override async ValueTask<Stream> GetCompressedStreamAsync(CancellationToken cancellationToken)
{
if (!_isLocalHeaderLoaded)
{
LoadLocalHeader();
await LoadLocalHeader(cancellationToken);
_isLocalHeaderLoaded = true;
}
return base.GetCompressedStream();
return await base.GetCompressedStreamAsync(cancellationToken);
}
internal string? Comment => ((DirectoryEntryHeader)Header).Comment;
private void LoadLocalHeader()
private async ValueTask LoadLocalHeader(CancellationToken cancellationToken)
{
bool hasData = Header.HasData;
Header = _headerFactory.GetLocalHeader(BaseStream, ((DirectoryEntryHeader)Header));
Header = await _headerFactory.GetLocalHeader(BaseStream, (DirectoryEntryHeader)Header, cancellationToken);
Header.HasData = hasData;
}

View File

@@ -1,7 +1,12 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common.Zip.Headers;
using SharpCompress.Compressors.Xz;
using SharpCompress.IO;
namespace SharpCompress.Common.Zip
@@ -19,15 +24,13 @@ namespace SharpCompress.Common.Zip
{
}
internal IEnumerable<ZipHeader> ReadSeekableHeader(Stream stream)
internal async IAsyncEnumerable<ZipHeader> ReadSeekableHeader(Stream stream, [EnumeratorCancellation]CancellationToken cancellationToken)
{
var reader = new BinaryReader(stream);
SeekBackToHeader(stream, reader);
await SeekBackToHeaderAsync(stream);
var eocd_location = stream.Position;
var entry = new DirectoryEndHeader();
entry.Read(reader);
await entry.Read(stream, cancellationToken);
if (entry.IsZip64)
{
@@ -35,37 +38,37 @@ namespace SharpCompress.Common.Zip
// ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR should be before the EOCD
stream.Seek(eocd_location - ZIP64_EOCD_LENGTH - 4, SeekOrigin.Begin);
uint zip64_locator = reader.ReadUInt32();
if (zip64_locator != ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR)
uint zip64_locator = await stream.ReadUInt32(cancellationToken);
if( zip64_locator != ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR )
{
throw new ArchiveException("Failed to locate the Zip64 Directory Locator");
}
var zip64Locator = new Zip64DirectoryEndLocatorHeader();
zip64Locator.Read(reader);
await zip64Locator.Read(stream, cancellationToken);
stream.Seek(zip64Locator.RelativeOffsetOfTheEndOfDirectoryRecord, SeekOrigin.Begin);
uint zip64Signature = reader.ReadUInt32();
uint zip64Signature = await stream.ReadUInt32(cancellationToken);
if (zip64Signature != ZIP64_END_OF_CENTRAL_DIRECTORY)
{
throw new ArchiveException("Failed to locate the Zip64 Header");
}
var zip64Entry = new Zip64DirectoryEndHeader();
zip64Entry.Read(reader);
await zip64Entry.Read(stream, cancellationToken);
stream.Seek(zip64Entry.DirectoryStartOffsetRelativeToDisk, SeekOrigin.Begin);
}
else
{
stream.Seek(entry.DirectoryStartOffsetRelativeToDisk, SeekOrigin.Begin);
stream.Seek(entry.DirectoryStartOffsetRelativeToDisk ?? 0, SeekOrigin.Begin);
}
long position = stream.Position;
while (true)
{
stream.Position = position;
uint signature = reader.ReadUInt32();
var nextHeader = ReadHeader(signature, reader, _zip64);
uint signature = await stream.ReadUInt32(cancellationToken);
var nextHeader = await ReadHeader(signature, stream, cancellationToken, _zip64);
position = stream.Position;
if (nextHeader is null)
@@ -86,11 +89,11 @@ namespace SharpCompress.Common.Zip
}
}
private static bool IsMatch(byte[] haystack, int position, byte[] needle)
private static bool IsMatch (Span<byte> haystack, int position, byte[] needle)
{
for (int i = 0; i < needle.Length; i++)
for( int i = 0; i < needle.Length; i++ )
{
if (haystack[position + i] != needle[i])
if( haystack[ position + i ] != needle[ i ] )
{
return false;
}
@@ -98,7 +101,7 @@ namespace SharpCompress.Common.Zip
return true;
}
private static void SeekBackToHeader(Stream stream, BinaryReader reader)
private static async ValueTask SeekBackToHeaderAsync(Stream stream)
{
// Minimum EOCD length
if (stream.Length < MINIMUM_EOCD_LENGTH)
@@ -112,17 +115,18 @@ namespace SharpCompress.Common.Zip
stream.Seek(-len, SeekOrigin.End);
byte[] seek = reader.ReadBytes(len);
using var rented = MemoryPool<byte>.Shared.Rent(len);
var buffer = rented.Memory.Slice(0, len);
await stream.ReadAsync(buffer);
// Search in reverse
Array.Reverse(seek);
buffer.Span.Reverse();
// don't exclude the minimum eocd region, otherwise you fail to locate the header in empty zip files
var max_search_area = len; // - MINIMUM_EOCD_LENGTH;
var max_search_area = len - MINIMUM_EOCD_LENGTH;
for (int pos_from_end = 0; pos_from_end < max_search_area; ++pos_from_end)
for( int pos_from_end = 0; pos_from_end < max_search_area; ++pos_from_end)
{
if (IsMatch(seek, pos_from_end, needle))
if( IsMatch( buffer.Span, pos_from_end, needle) )
{
stream.Seek(-pos_from_end, SeekOrigin.End);
return;
@@ -132,12 +136,11 @@ namespace SharpCompress.Common.Zip
throw new ArchiveException("Failed to locate the Zip Header");
}
internal LocalEntryHeader GetLocalHeader(Stream stream, DirectoryEntryHeader directoryEntryHeader)
internal async ValueTask<LocalEntryHeader> GetLocalHeader(Stream stream, DirectoryEntryHeader directoryEntryHeader, CancellationToken cancellationToken)
{
stream.Seek(directoryEntryHeader.RelativeOffsetOfEntryHeader, SeekOrigin.Begin);
BinaryReader reader = new BinaryReader(stream);
uint signature = reader.ReadUInt32();
var localEntryHeader = ReadHeader(signature, reader, _zip64) as LocalEntryHeader;
uint signature = await stream.ReadUInt32(cancellationToken);
var localEntryHeader = await ReadHeader(signature, stream, cancellationToken, _zip64) as LocalEntryHeader;
if (localEntryHeader is null)
{
throw new InvalidOperationException();

View File

@@ -1,4 +1,6 @@
using System.IO;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common.Zip.Headers;
using SharpCompress.Compressors.Deflate;
using SharpCompress.IO;
@@ -19,67 +21,39 @@ namespace SharpCompress.Common.Zip
return Header.PackedStream;
}
internal override Stream GetCompressedStream()
internal override async ValueTask<Stream> GetCompressedStreamAsync(CancellationToken cancellationToken)
{
if (!Header.HasData)
{
return Stream.Null;
}
_decompressionStream = CreateDecompressionStream(GetCryptoStream(CreateBaseStream()), Header.CompressionMethod);
_decompressionStream = await CreateDecompressionStream(GetCryptoStream(CreateBaseStream()), Header.CompressionMethod, cancellationToken);
if (LeaveStreamOpen)
{
return NonDisposingStream.Create(_decompressionStream);
return new NonDisposingStream(_decompressionStream);
}
return _decompressionStream;
}
internal BinaryReader FixStreamedFileLocation(ref RewindableStream rewindableStream)
internal async ValueTask FixStreamedFileLocation(RewindableStream rewindableStream, CancellationToken cancellationToken)
{
if (Header.IsDirectory)
{
return new BinaryReader(rewindableStream);
return;
}
if (Header.HasData && !Skipped)
{
_decompressionStream ??= GetCompressedStream();
_decompressionStream ??= await GetCompressedStreamAsync(cancellationToken);
if( Header.CompressionMethod != ZipCompressionMethod.None )
await _decompressionStream.SkipAsync(cancellationToken);
if (_decompressionStream is DeflateStream deflateStream)
{
_decompressionStream.Skip();
if (_decompressionStream is DeflateStream deflateStream)
{
rewindableStream.Rewind(deflateStream.InputBuffer);
}
rewindableStream.Rewind(deflateStream.InputBuffer);
}
else
{
// We would need to search for the magic word
rewindableStream.Position -= 4;
var pos = rewindableStream.Position;
while( Utility.Find(rewindableStream, new byte[] { 0x50,0x4b,0x07,0x08 } ) )
{
// We should probably check CRC32 for positive matching as well
var size = rewindableStream.Position - pos;
var br = new BinaryReader(rewindableStream);
br.ReadUInt32();
br.ReadUInt32(); // CRC32
var compressed_size = br.ReadUInt32();
var uncompressed_size = br.ReadUInt32();
if (compressed_size == size && compressed_size == uncompressed_size )
{
rewindableStream.Position -= 16;
break;
}
rewindableStream.Position -= 12;
}
}
Skipped = true;
}
var reader = new BinaryReader(rewindableStream);
_decompressionStream = null;
return reader;
}
}
}
}

View File

@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;
using System.Threading;
using SharpCompress.Common.Zip.Headers;
using SharpCompress.IO;
@@ -12,43 +13,36 @@ namespace SharpCompress.Common.Zip
{
}
internal IEnumerable<ZipHeader> ReadStreamHeader(Stream stream)
internal async IAsyncEnumerable<ZipHeader> ReadStreamHeader(RewindableStream rewindableStream, [EnumeratorCancellation] CancellationToken cancellationToken)
{
RewindableStream rewindableStream;
if (stream is RewindableStream rs)
{
rewindableStream = rs;
}
else
{
rewindableStream = new RewindableStream(stream);
}
while (true)
{
ZipHeader? header;
BinaryReader reader = new BinaryReader(rewindableStream);
if (_lastEntryHeader != null &&
(FlagUtility.HasFlag(_lastEntryHeader.Flags, HeaderFlags.UsePostDataDescriptor) || _lastEntryHeader.IsZip64))
{
reader = ((StreamingZipFilePart)_lastEntryHeader.Part).FixStreamedFileLocation(ref rewindableStream);
await ((StreamingZipFilePart)_lastEntryHeader.Part).FixStreamedFileLocation(rewindableStream, cancellationToken);
long? pos = rewindableStream.CanSeek ? (long?)rewindableStream.Position : null;
uint crc = reader.ReadUInt32();
uint crc = await rewindableStream.ReadUInt32(cancellationToken);
if (crc == POST_DATA_DESCRIPTOR)
{
crc = reader.ReadUInt32();
crc = await rewindableStream.ReadUInt32(cancellationToken);
}
_lastEntryHeader.Crc = crc;
_lastEntryHeader.CompressedSize = reader.ReadUInt32();
_lastEntryHeader.UncompressedSize = reader.ReadUInt32();
_lastEntryHeader.CompressedSize = await rewindableStream.ReadUInt32(cancellationToken);
_lastEntryHeader.UncompressedSize = await rewindableStream.ReadUInt32(cancellationToken);
if (pos.HasValue)
{
_lastEntryHeader.DataStartPosition = pos - _lastEntryHeader.CompressedSize;
}
}
_lastEntryHeader = null;
uint headerBytes = reader.ReadUInt32();
header = ReadHeader(headerBytes, reader);
var headerBytes = await rewindableStream.ReadUInt32OrNull(cancellationToken);
if (headerBytes is null)
{
yield break;
}
header = await ReadHeader(headerBytes.Value, rewindableStream, cancellationToken);
if (header is null)
{
yield break;
@@ -71,10 +65,10 @@ namespace SharpCompress.Common.Zip
{
rewindableStream.StartRecording();
}
uint nextHeaderBytes = reader.ReadUInt32();
uint nextHeaderBytes = await rewindableStream.ReadUInt32(cancellationToken);
// Check if next data is PostDataDescriptor, streamed file with 0 length
header.HasData = !IsHeader(nextHeaderBytes);
header.HasData = nextHeaderBytes != POST_DATA_DESCRIPTOR;
rewindableStream.Rewind(!isRecording);
}
else // We are not streaming and compressed size is 0, we have no data

View File

@@ -1,14 +1,16 @@
using System;
using System;
using System.Buffers.Binary;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common.Zip.Headers;
using SharpCompress.Compressors;
using SharpCompress.Compressors.BZip2;
using SharpCompress.Compressors.Deflate;
using SharpCompress.Compressors.Deflate64;
using SharpCompress.Compressors.LZMA;
using SharpCompress.Compressors.PPMd;
//using SharpCompress.Compressors.PPMd;
using SharpCompress.IO;
namespace SharpCompress.Common.Zip
@@ -28,16 +30,16 @@ namespace SharpCompress.Common.Zip
internal override string FilePartName => Header.Name;
internal override Stream GetCompressedStream()
internal override async ValueTask<Stream> GetCompressedStreamAsync(CancellationToken cancellationToken)
{
if (!Header.HasData)
{
return Stream.Null;
}
Stream decompressionStream = CreateDecompressionStream(GetCryptoStream(CreateBaseStream()), Header.CompressionMethod);
Stream decompressionStream = await CreateDecompressionStream(GetCryptoStream(CreateBaseStream()), Header.CompressionMethod, cancellationToken);
if (LeaveStreamOpen)
{
return NonDisposingStream.Create(decompressionStream);
return new NonDisposingStream(decompressionStream);
}
return decompressionStream;
}
@@ -55,7 +57,7 @@ namespace SharpCompress.Common.Zip
protected bool LeaveStreamOpen => FlagUtility.HasFlag(Header.Flags, HeaderFlags.UsePostDataDescriptor) || Header.IsZip64;
protected Stream CreateDecompressionStream(Stream stream, ZipCompressionMethod method)
protected async ValueTask<Stream> CreateDecompressionStream(Stream stream, ZipCompressionMethod method, CancellationToken cancellationToken)
{
switch (method)
{
@@ -73,30 +75,29 @@ namespace SharpCompress.Common.Zip
}
case ZipCompressionMethod.BZip2:
{
return new BZip2Stream(stream, CompressionMode.Decompress, false);
}
return await BZip2Stream.CreateAsync(stream, CompressionMode.Decompress, false, cancellationToken);
}
case ZipCompressionMethod.LZMA:
{
if (FlagUtility.HasFlag(Header.Flags, HeaderFlags.Encrypted))
{
throw new NotSupportedException("LZMA with pkware encryption.");
}
var reader = new BinaryReader(stream);
reader.ReadUInt16(); //LZMA version
var props = new byte[reader.ReadUInt16()];
reader.Read(props, 0, props.Length);
return new LzmaStream(props, stream,
await stream.ReadUInt16(cancellationToken); //LZMA version
var props = new byte[await stream.ReadUInt16(cancellationToken)];
await stream.ReadAsync(props, 0, props.Length, cancellationToken);
return await LzmaStream.CreateAsync(props, stream,
Header.CompressedSize > 0 ? Header.CompressedSize - 4 - props.Length : -1,
FlagUtility.HasFlag(Header.Flags, HeaderFlags.Bit1)
? -1
: (long)Header.UncompressedSize);
: (long)Header.UncompressedSize,
cancellationToken: cancellationToken);
}
case ZipCompressionMethod.PPMd:
{
Span<byte> props = stackalloc byte[2];
stream.ReadFully(props);
/* case ZipCompressionMethod.PPMd:
{
var props = await stream.ReadBytes(2, cancellationToken);
return new PpmdStream(new PpmdProperties(props), stream, false);
}
} */
case ZipCompressionMethod.WinzipAes:
{
ExtraData? data = Header.Extra.SingleOrDefault(x => x.Type == ExtraDataType.WinZipAes);
@@ -120,7 +121,7 @@ namespace SharpCompress.Common.Zip
{
throw new InvalidFormatException("Unexpected vendor ID for WinZip AES metadata");
}
return CreateDecompressionStream(stream, (ZipCompressionMethod)BinaryPrimitives.ReadUInt16LittleEndian(data.DataBytes.AsSpan(5)));
return await CreateDecompressionStream(stream, (ZipCompressionMethod)BinaryPrimitives.ReadUInt16LittleEndian(data.DataBytes.AsSpan(5)), cancellationToken);
}
default:
{
@@ -142,7 +143,7 @@ namespace SharpCompress.Common.Zip
&& FlagUtility.HasFlag(Header.Flags, HeaderFlags.UsePostDataDescriptor))
|| Header.IsZip64)
{
plainStream = NonDisposingStream.Create(plainStream); //make sure AES doesn't close
plainStream = new NonDisposingStream(plainStream); //make sure AES doesn't close
}
else
{
@@ -174,7 +175,7 @@ namespace SharpCompress.Common.Zip
default:
{
throw new InvalidOperationException("Header.CompressionMethod is invalid");
throw new ArgumentOutOfRangeException();
}
}

View File

@@ -1,6 +1,8 @@
using System;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common.Zip.Headers;
using SharpCompress.IO;
@@ -30,15 +32,15 @@ namespace SharpCompress.Common.Zip
this._archiveEncoding = archiveEncoding;
}
protected ZipHeader? ReadHeader(uint headerBytes, BinaryReader reader, bool zip64 = false)
protected async ValueTask<ZipHeader?> ReadHeader(uint headerBytes, Stream stream, CancellationToken cancellationToken, bool zip64 = false)
{
switch (headerBytes)
{
case ENTRY_HEADER_BYTES:
{
var entryHeader = new LocalEntryHeader(_archiveEncoding);
entryHeader.Read(reader);
LoadHeader(entryHeader, reader.BaseStream);
await entryHeader.Read(stream, cancellationToken);
await LoadHeader(entryHeader, stream, cancellationToken);
_lastEntryHeader = entryHeader;
return entryHeader;
@@ -46,20 +48,20 @@ namespace SharpCompress.Common.Zip
case DIRECTORY_START_HEADER_BYTES:
{
var entry = new DirectoryEntryHeader(_archiveEncoding);
entry.Read(reader);
await entry.Read(stream, cancellationToken);
return entry;
}
case POST_DATA_DESCRIPTOR:
{
if (FlagUtility.HasFlag(_lastEntryHeader!.Flags, HeaderFlags.UsePostDataDescriptor))
{
_lastEntryHeader.Crc = reader.ReadUInt32();
_lastEntryHeader.CompressedSize = zip64 ? (long)reader.ReadUInt64() : reader.ReadUInt32();
_lastEntryHeader.UncompressedSize = zip64 ? (long)reader.ReadUInt64() : reader.ReadUInt32();
_lastEntryHeader.Crc = await stream.ReadUInt32(cancellationToken);
_lastEntryHeader.CompressedSize = zip64 ? (long)await stream.ReadUInt64(cancellationToken) : await stream.ReadUInt32(cancellationToken);
_lastEntryHeader.UncompressedSize = zip64 ? (long)await stream.ReadUInt64(cancellationToken) : await stream.ReadUInt32(cancellationToken);
}
else
{
reader.ReadBytes(zip64 ? 20 : 12);
await stream.ReadBytes(zip64 ? 20 : 12, cancellationToken);
}
return null;
}
@@ -68,7 +70,7 @@ namespace SharpCompress.Common.Zip
case DIRECTORY_END_HEADER_BYTES:
{
var entry = new DirectoryEndHeader();
entry.Read(reader);
await entry.Read(stream, cancellationToken);
return entry;
}
case SPLIT_ARCHIVE_HEADER_BYTES:
@@ -78,13 +80,13 @@ namespace SharpCompress.Common.Zip
case ZIP64_END_OF_CENTRAL_DIRECTORY:
{
var entry = new Zip64DirectoryEndHeader();
entry.Read(reader);
await entry.Read(stream, cancellationToken);
return entry;
}
case ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR:
{
var entry = new Zip64DirectoryEndLocatorHeader();
entry.Read(reader);
await entry.Read(stream, cancellationToken);
return entry;
}
default:
@@ -110,7 +112,7 @@ namespace SharpCompress.Common.Zip
}
}
private void LoadHeader(ZipFileEntry entryHeader, Stream stream)
private async ValueTask LoadHeader(ZipFileEntry entryHeader, Stream stream, CancellationToken cancellationToken)
{
if (FlagUtility.HasFlag(entryHeader.Flags, HeaderFlags.Encrypted))
{
@@ -134,10 +136,8 @@ namespace SharpCompress.Common.Zip
{
var keySize = (WinzipAesKeySize)data.DataBytes[4];
var salt = new byte[WinzipAesEncryptionData.KeyLengthInBytes(keySize) / 2];
var passwordVerifyValue = new byte[2];
stream.Read(salt, 0, salt.Length);
stream.Read(passwordVerifyValue, 0, 2);
var salt = await stream.ReadBytes(WinzipAesEncryptionData.KeyLengthInBytes(keySize) / 2, cancellationToken);
var passwordVerifyValue = await stream.ReadBytes(2, cancellationToken);
entryHeader.WinzipAesEncryptionData =
new WinzipAesEncryptionData(keySize, salt, passwordVerifyValue, _password);

View File

@@ -1,15 +1,15 @@
using System.IO;
using System.IO;
using SharpCompress.Readers;
namespace SharpCompress.Common.Zip
{
public class ZipVolume : Volume
{
public ZipVolume(Stream stream, ReaderOptions readerOptions, int index = 0)
: base(stream, readerOptions, index)
public ZipVolume(Stream stream, ReaderOptions readerOptions)
: base(stream, readerOptions)
{
}
public string? Comment { get; internal set; }
}
}
}

View File

@@ -1,9 +1,13 @@
using System;
using System.Buffers;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.IO;
namespace SharpCompress.Compressors.BZip2
{
public sealed class BZip2Stream : Stream
public sealed class BZip2Stream : AsyncStream
{
private readonly Stream stream;
private bool isDisposed;
@@ -33,17 +37,14 @@ namespace SharpCompress.Compressors.BZip2
(stream as CBZip2OutputStream)?.Finish();
}
protected override void Dispose(bool disposing)
public override async ValueTask DisposeAsync()
{
if (isDisposed)
{
return;
}
isDisposed = true;
if (disposing)
{
stream.Dispose();
}
await stream.DisposeAsync();
}
public CompressionMode Mode { get; }
@@ -54,23 +55,18 @@ namespace SharpCompress.Compressors.BZip2
public override bool CanWrite => stream.CanWrite;
public override void Flush()
public override Task FlushAsync(CancellationToken cancellationToken)
{
stream.Flush();
return stream.FlushAsync(cancellationToken);
}
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)
public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
{
return stream.Read(buffer, offset, count);
}
public override int ReadByte()
{
return stream.ReadByte();
return stream.ReadAsync(buffer, cancellationToken);
}
public override long Seek(long offset, SeekOrigin origin)
@@ -83,28 +79,14 @@ namespace SharpCompress.Compressors.BZip2
stream.SetLength(value);
}
#if !NETFRAMEWORK && !NETSTANDARD2_0
public override int Read(Span<byte> buffer)
public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
return stream.Read(buffer);
return stream.WriteAsync(buffer, offset, count, cancellationToken);
}
public override void Write(ReadOnlySpan<byte> buffer)
public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = new CancellationToken())
{
stream.Write(buffer);
}
#endif
public override void Write(byte[] buffer, int offset, int count)
{
stream.Write(buffer, offset, count);
}
public override void WriteByte(byte value)
{
stream.WriteByte(value);
return stream.WriteAsync(buffer, cancellationToken);
}
/// <summary>
@@ -112,15 +94,16 @@ namespace SharpCompress.Compressors.BZip2
/// </summary>
/// <param name="stream"></param>
/// <returns></returns>
public static bool IsBZip2(Stream stream)
public static async ValueTask<bool> IsBZip2Async(Stream stream, CancellationToken cancellationToken)
{
BinaryReader br = new BinaryReader(stream);
byte[] chars = br.ReadBytes(2);
if (chars.Length < 2 || chars[0] != 'B' || chars[1] != 'Z')
using var rented = MemoryPool<byte>.Shared.Rent(2);
var chars = rented.Memory.Slice(0, 2);
await stream.ReadAsync(chars, cancellationToken);
if (chars.Length < 2 || chars.Span[0] != 'B' || chars.Span[1] != 'Z')
{
return false;
}
return true;
}
}
}
}

View File

@@ -27,10 +27,13 @@
using System;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.IO;
namespace SharpCompress.Compressors.Deflate
{
public class DeflateStream : Stream
public class DeflateStream : AsyncStream
{
private readonly ZlibBaseStream _baseStream;
private bool _disposed;
@@ -216,35 +219,25 @@ namespace SharpCompress.Compressors.Deflate
/// <remarks>
/// This may or may not result in a <c>Close()</c> call on the captive stream.
/// </remarks>
protected override void Dispose(bool disposing)
public override async ValueTask DisposeAsync()
{
try
if (!_disposed)
{
if (!_disposed)
{
if (disposing)
{
_baseStream?.Dispose();
}
_disposed = true;
}
}
finally
{
base.Dispose(disposing);
await _baseStream.DisposeAsync();
_disposed = true;
}
}
/// <summary>
/// Flush the stream.
/// </summary>
public override void Flush()
public override async Task FlushAsync(CancellationToken cancellationToken)
{
if (_disposed)
{
throw new ObjectDisposedException("DeflateStream");
}
_baseStream.Flush();
await _baseStream.FlushAsync(cancellationToken);
}
/// <summary>
@@ -273,24 +266,14 @@ namespace SharpCompress.Compressors.Deflate
/// <param name="offset">the offset within that data array to put the first byte read.</param>
/// <param name="count">the number of bytes to read.</param>
/// <returns>the number of bytes actually read</returns>
public override int Read(byte[] buffer, int offset, int count)
public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
if (_disposed)
{
throw new ObjectDisposedException("DeflateStream");
}
return _baseStream.Read(buffer, offset, count);
return await _baseStream.ReadAsync(buffer, offset, count, cancellationToken);
}
public override int ReadByte()
{
if (_disposed)
{
throw new ObjectDisposedException("DeflateStream");
}
return _baseStream.ReadByte();
}
/// <summary>
/// Calling this method always throws a <see cref="NotImplementedException"/>.
/// </summary>
@@ -340,22 +323,13 @@ namespace SharpCompress.Compressors.Deflate
/// <param name="buffer">The buffer holding data to write to the stream.</param>
/// <param name="offset">the offset within that data array to find the first byte to write.</param>
/// <param name="count">the number of bytes to write.</param>
public override void Write(byte[] buffer, int offset, int count)
public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
if (_disposed)
{
throw new ObjectDisposedException("DeflateStream");
}
_baseStream.Write(buffer, offset, count);
}
public override void WriteByte(byte value)
{
if (_disposed)
{
throw new ObjectDisposedException("DeflateStream");
}
_baseStream.WriteByte(value);
await _baseStream.WriteAsync(buffer, offset, count, cancellationToken);
}
#endregion

View File

@@ -27,21 +27,25 @@
// ------------------------------------------------------------------
using System;
using System.Buffers;
using System.Buffers.Binary;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.IO;
namespace SharpCompress.Compressors.Deflate
{
public class GZipStream : Stream
public class GZipStream : AsyncStream
{
internal static readonly DateTime UNIX_EPOCH = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
private static readonly DateTime UNIX_EPOCH = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
private string? _comment;
private string? _fileName;
private DateTime? _lastModified;
internal ZlibBaseStream BaseStream;
private readonly ZlibBaseStream _baseStream;
private bool _disposed;
private bool _firstReadDone;
private int _headerByteCount;
@@ -60,7 +64,7 @@ namespace SharpCompress.Compressors.Deflate
public GZipStream(Stream stream, CompressionMode mode, CompressionLevel level, Encoding encoding)
{
BaseStream = new ZlibBaseStream(stream, mode, level, ZlibStreamFlavor.GZIP, encoding);
_baseStream = new ZlibBaseStream(stream, mode, level, ZlibStreamFlavor.GZIP, encoding);
_encoding = encoding;
}
@@ -68,27 +72,27 @@ namespace SharpCompress.Compressors.Deflate
public virtual FlushType FlushMode
{
get => (BaseStream._flushMode);
get => (_baseStream._flushMode);
set
{
if (_disposed)
{
throw new ObjectDisposedException("GZipStream");
}
BaseStream._flushMode = value;
_baseStream._flushMode = value;
}
}
public int BufferSize
{
get => BaseStream._bufferSize;
get => _baseStream._bufferSize;
set
{
if (_disposed)
{
throw new ObjectDisposedException("GZipStream");
}
if (BaseStream._workingBuffer != null)
if (_baseStream._workingBuffer != null)
{
throw new ZlibException("The working buffer is already set.");
}
@@ -98,13 +102,13 @@ namespace SharpCompress.Compressors.Deflate
String.Format("Don't be silly. {0} bytes?? Use a bigger buffer, at least {1}.", value,
ZlibConstants.WorkingBufferSizeMin));
}
BaseStream._bufferSize = value;
_baseStream._bufferSize = value;
}
}
internal virtual long TotalIn => BaseStream._z.TotalBytesIn;
internal virtual long TotalIn => _baseStream._z.TotalBytesIn;
internal virtual long TotalOut => BaseStream._z.TotalBytesOut;
internal virtual long TotalOut => _baseStream._z.TotalBytesOut;
#endregion
@@ -124,7 +128,7 @@ namespace SharpCompress.Compressors.Deflate
{
throw new ObjectDisposedException("GZipStream");
}
return BaseStream._stream.CanRead;
return _baseStream._stream.CanRead;
}
}
@@ -150,7 +154,7 @@ namespace SharpCompress.Compressors.Deflate
{
throw new ObjectDisposedException("GZipStream");
}
return BaseStream._stream.CanWrite;
return _baseStream._stream.CanWrite;
}
}
@@ -174,13 +178,13 @@ namespace SharpCompress.Compressors.Deflate
{
get
{
if (BaseStream._streamMode == ZlibBaseStream.StreamMode.Writer)
if (_baseStream._streamMode == ZlibBaseStream.StreamMode.Writer)
{
return BaseStream._z.TotalBytesOut + _headerByteCount;
return _baseStream._z.TotalBytesOut + _headerByteCount;
}
if (BaseStream._streamMode == ZlibBaseStream.StreamMode.Reader)
if (_baseStream._streamMode == ZlibBaseStream.StreamMode.Reader)
{
return BaseStream._z.TotalBytesIn + BaseStream._gzipHeaderByteCount;
return _baseStream._z.TotalBytesIn + _baseStream._gzipHeaderByteCount;
}
return 0;
}
@@ -194,36 +198,29 @@ namespace SharpCompress.Compressors.Deflate
/// <remarks>
/// This may or may not result in a <c>Close()</c> call on the captive stream.
/// </remarks>
protected override void Dispose(bool disposing)
{
try
public override async ValueTask DisposeAsync()
{
if (!_disposed)
{
if (disposing && (BaseStream != null))
if (_baseStream is not null)
{
BaseStream.Dispose();
Crc32 = BaseStream.Crc32;
await _baseStream.DisposeAsync();
Crc32 = _baseStream.Crc32;
}
_disposed = true;
}
}
finally
{
base.Dispose(disposing);
}
}
/// <summary>
/// Flush the stream.
/// </summary>
public override void Flush()
public override Task FlushAsync(CancellationToken cancellationToken)
{
if (_disposed)
{
throw new ObjectDisposedException("GZipStream");
}
BaseStream.Flush();
return _baseStream.FlushAsync(cancellationToken);
}
/// <summary>
@@ -257,13 +254,13 @@ namespace SharpCompress.Compressors.Deflate
/// <param name="offset">the offset within that data array to put the first byte read.</param>
/// <param name="count">the number of bytes to read.</param>
/// <returns>the number of bytes actually read</returns>
public override int Read(byte[] buffer, int offset, int count)
public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
if (_disposed)
{
throw new ObjectDisposedException("GZipStream");
}
int n = BaseStream.Read(buffer, offset, count);
int n = await _baseStream.ReadAsync(buffer, offset, count, cancellationToken);
// Console.WriteLine("GZipStream::Read(buffer, off({0}), c({1}) = {2}", offset, count, n);
// Console.WriteLine( Util.FormatByteArray(buffer, offset, n) );
@@ -271,9 +268,9 @@ namespace SharpCompress.Compressors.Deflate
if (!_firstReadDone)
{
_firstReadDone = true;
FileName = BaseStream._GzipFileName;
Comment = BaseStream._GzipComment;
LastModified = BaseStream._GzipMtime;
FileName = _baseStream._GzipFileName;
Comment = _baseStream._GzipComment;
LastModified = _baseStream._GzipMtime;
}
return n;
}
@@ -320,19 +317,19 @@ namespace SharpCompress.Compressors.Deflate
/// <param name="buffer">The buffer holding data to write to the stream.</param>
/// <param name="offset">the offset within that data array to find the first byte to write.</param>
/// <param name="count">the number of bytes to write.</param>
public override void Write(byte[] buffer, int offset, int count)
public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
if (_disposed)
{
throw new ObjectDisposedException("GZipStream");
}
if (BaseStream._streamMode == ZlibBaseStream.StreamMode.Undefined)
if (_baseStream._streamMode == ZlibBaseStream.StreamMode.Undefined)
{
//Console.WriteLine("GZipStream: First write");
if (BaseStream._wantCompress)
if (_baseStream._wantCompress)
{
// first write in compression, therefore, emit the GZIP header
_headerByteCount = EmitHeader();
_headerByteCount = await EmitHeaderAsync();
}
else
{
@@ -340,7 +337,7 @@ namespace SharpCompress.Compressors.Deflate
}
}
BaseStream.Write(buffer, offset, count);
await _baseStream.WriteAsync(buffer, offset, count, cancellationToken);
}
#endregion Stream methods
@@ -405,7 +402,7 @@ namespace SharpCompress.Compressors.Deflate
public int Crc32 { get; private set; }
private int EmitHeader()
private async ValueTask<int> EmitHeaderAsync()
{
byte[]? commentBytes = (Comment is null) ? null
: _encoding.GetBytes(Comment);
@@ -474,7 +471,7 @@ namespace SharpCompress.Compressors.Deflate
header[i++] = 0; // terminate
}
BaseStream._stream.Write(header, 0, header.Length);
await _baseStream._stream.WriteAsync(header, 0, header.Length);
return header.Length; // bytes written
}

View File

@@ -1,20 +1,20 @@
// Zlib.cs
// ------------------------------------------------------------------
//
// Copyright (c) 2009 Dino Chiesa and Microsoft Corporation.
// Copyright (c) 2009 Dino Chiesa and Microsoft Corporation.
// All rights reserved.
//
// This code module is part of DotNetZip, a zipfile class library.
//
// ------------------------------------------------------------------
//
// This code is licensed under the Microsoft Public License.
// This code is licensed under the Microsoft Public License.
// See the file License.txt for the license details.
// More info on: http://dotnetzip.codeplex.com
//
// ------------------------------------------------------------------
//
// last saved (in emacs):
// last saved (in emacs):
// Time-stamp: <2009-November-07 05:26:55>
//
// ------------------------------------------------------------------
@@ -27,22 +27,22 @@
// included below.
//
// ------------------------------------------------------------------
//
//
// Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved.
//
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in
// the documentation and/or other materials provided with the distribution.
//
//
// 3. The names of the authors may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
//
// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
@@ -53,7 +53,7 @@
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//
// -----------------------------------------------------------------------
//
// This program is based on zlib-1.1.3; credit to authors
@@ -82,7 +82,7 @@ namespace SharpCompress.Compressors.Deflate
/// <summary>
/// Same as None.
/// </summary>
Level0 = None,
Level0 = 0,
/// <summary>
/// The fastest but least effective compression.
@@ -92,7 +92,7 @@ namespace SharpCompress.Compressors.Deflate
/// <summary>
/// A synonym for BestSpeed.
/// </summary>
Level1 = BestSpeed,
Level1 = 1,
/// <summary>
/// A little slower, but better, than level 1.
@@ -115,14 +115,14 @@ namespace SharpCompress.Compressors.Deflate
Level5 = 5,
/// <summary>
/// The default compression level, with a good balance of speed and compression efficiency.
/// The default compression level, with a good balance of speed and compression efficiency.
/// </summary>
Default = 6,
/// <summary>
/// A synonym for Default.
/// </summary>
Level6 = Default,
Level6 = 6,
/// <summary>
/// Pretty good compression!
@@ -135,7 +135,7 @@ namespace SharpCompress.Compressors.Deflate
Level8 = 8,
/// <summary>
/// The "best" compression, where best means greatest reduction in size of the input data stream.
/// The "best" compression, where best means greatest reduction in size of the input data stream.
/// This is also the slowest compression.
/// </summary>
BestCompression = 9,
@@ -143,7 +143,7 @@ namespace SharpCompress.Compressors.Deflate
/// <summary>
/// A synonym for BestCompression.
/// </summary>
Level9 = BestCompression
Level9 = 9
}
/// <summary>
@@ -154,7 +154,7 @@ namespace SharpCompress.Compressors.Deflate
public enum CompressionStrategy
{
/// <summary>
/// The default strategy is probably the best for normal data.
/// The default strategy is probably the best for normal data.
/// </summary>
Default = 0,
@@ -181,7 +181,7 @@ namespace SharpCompress.Compressors.Deflate
{
/// <summary>
/// The ZlibException class captures exception information generated
/// by the Zlib library.
/// by the Zlib library.
/// </summary>
public ZlibException()
{
@@ -233,7 +233,7 @@ namespace SharpCompress.Compressors.Deflate
/// <param name="target">Contains the array of characteres read from the source TextReader.</param>
/// <param name="start">The starting index of the target array.</param>
/// <param name="count">The maximum number of characters to read from the source TextReader.</param>
///
///
/// <returns>
/// The number of characters read. The number will be less than or equal to
/// count depending on the data available in the source TextReader. Returns -1
@@ -405,4 +405,4 @@ namespace SharpCompress.Compressors.Deflate
BitLengths = new StaticTree(null, extra_blbits, 0, InternalConstants.BL_CODES, InternalConstants.MAX_BL_BITS);
}
}
}
}

View File

@@ -27,11 +27,15 @@
// ------------------------------------------------------------------
using System;
using System.Buffers;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.IO;
using SharpCompress.Common.Tar.Headers;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.IO;
namespace SharpCompress.Compressors.Deflate
{
@@ -42,18 +46,18 @@ namespace SharpCompress.Compressors.Deflate
GZIP = 1952
}
internal class ZlibBaseStream : Stream
internal class ZlibBaseStream : AsyncStream
{
protected internal ZlibCodec _z; // deferred init... new ZlibCodec();
protected internal StreamMode _streamMode = StreamMode.Undefined;
protected internal FlushType _flushMode;
protected internal ZlibStreamFlavor _flavor;
protected internal CompressionMode _compressionMode;
protected internal CompressionLevel _level;
private readonly ZlibStreamFlavor _flavor;
private readonly CompressionMode _compressionMode;
private readonly CompressionLevel _level;
protected internal byte[] _workingBuffer;
protected internal int _bufferSize = ZlibConstants.WorkingBufferSizeDefault;
protected internal byte[] _buf1 = new byte[1];
private readonly byte[] _buf1 = new byte[1];
protected internal Stream _stream;
protected internal CompressionStrategy Strategy = CompressionStrategy.Default;
@@ -116,19 +120,13 @@ namespace SharpCompress.Compressors.Deflate
}
}
private byte[] workingBuffer
{
get => _workingBuffer ??= new byte[_bufferSize];
}
private byte[] workingBuffer => _workingBuffer ??= new byte[_bufferSize];
public override void Write(byte[] buffer, int offset, int count)
public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
// workitem 7159
// calculate the CRC on the unccompressed data (before writing)
if (crc != null)
{
crc.SlurpBlock(buffer, offset, count);
}
crc?.SlurpBlock(buffer, offset, count);
if (_streamMode == StreamMode.Undefined)
{
@@ -148,7 +146,7 @@ namespace SharpCompress.Compressors.Deflate
z.InputBuffer = buffer;
_z.NextIn = offset;
_z.AvailableBytesIn = count;
bool done = false;
var done = false;
do
{
_z.OutputBuffer = workingBuffer;
@@ -163,7 +161,7 @@ namespace SharpCompress.Compressors.Deflate
}
//if (_workingBuffer.Length - _z.AvailableBytesOut > 0)
_stream.Write(_workingBuffer, 0, _workingBuffer.Length - _z.AvailableBytesOut);
await _stream.WriteAsync(_workingBuffer, 0, _workingBuffer.Length - _z.AvailableBytesOut, cancellationToken);
done = _z.AvailableBytesIn == 0 && _z.AvailableBytesOut != 0;
@@ -176,7 +174,7 @@ namespace SharpCompress.Compressors.Deflate
while (!done);
}
private void finish()
private async Task FinishAsync()
{
if (_z is null)
{
@@ -185,7 +183,7 @@ namespace SharpCompress.Compressors.Deflate
if (_streamMode == StreamMode.Writer)
{
bool done = false;
var done = false;
do
{
_z.OutputBuffer = workingBuffer;
@@ -200,14 +198,14 @@ namespace SharpCompress.Compressors.Deflate
string verb = (_wantCompress ? "de" : "in") + "flating";
if (_z.Message is null)
{
throw new ZlibException(String.Format("{0}: (rc = {1})", verb, rc));
throw new ZlibException($"{verb}: (rc = {rc})");
}
throw new ZlibException(verb + ": " + _z.Message);
}
if (_workingBuffer.Length - _z.AvailableBytesOut > 0)
{
_stream.Write(_workingBuffer, 0, _workingBuffer.Length - _z.AvailableBytesOut);
await _stream.WriteAsync(_workingBuffer, 0, _workingBuffer.Length - _z.AvailableBytesOut);
}
done = _z.AvailableBytesIn == 0 && _z.AvailableBytesOut != 0;
@@ -220,7 +218,7 @@ namespace SharpCompress.Compressors.Deflate
}
while (!done);
Flush();
await FlushAsync();
// workitem 7159
if (_flavor == ZlibStreamFlavor.GZIP)
@@ -228,12 +226,13 @@ namespace SharpCompress.Compressors.Deflate
if (_wantCompress)
{
// Emit the GZIP trailer: CRC32 and size mod 2^32
Span<byte> intBuf = stackalloc byte[4];
BinaryPrimitives.WriteInt32LittleEndian(intBuf, crc.Crc32Result);
_stream.Write(intBuf);
using var rented = MemoryPool<byte>.Shared.Rent(4);
var intBuf = rented.Memory.Slice(0, 4);
BinaryPrimitives.WriteInt32LittleEndian(intBuf.Span, crc.Crc32Result);
await _stream.WriteAsync(intBuf, CancellationToken.None);
int c2 = (int)(crc.TotalBytesRead & 0x00000000FFFFFFFF);
BinaryPrimitives.WriteInt32LittleEndian(intBuf, c2);
_stream.Write(intBuf);
BinaryPrimitives.WriteInt32LittleEndian(intBuf.Span, c2);
await _stream.WriteAsync(intBuf, CancellationToken.None);
}
else
{
@@ -256,44 +255,41 @@ namespace SharpCompress.Compressors.Deflate
}
// Read and potentially verify the GZIP trailer: CRC32 and size mod 2^32
Span<byte> trailer = stackalloc byte[8];
using var rented = MemoryPool<byte>.Shared.Rent(8);
var trailer = rented.Memory.Slice(0, 8);
// workitem 8679
if (_z.AvailableBytesIn != 8)
{
// Make sure we have read to the end of the stream
_z.InputBuffer.AsSpan(_z.NextIn, _z.AvailableBytesIn).CopyTo(trailer);
_z.InputBuffer.AsSpan(_z.NextIn, _z.AvailableBytesIn).CopyTo(trailer.Span);
int bytesNeeded = 8 - _z.AvailableBytesIn;
int bytesRead = _stream.Read(trailer.Slice(_z.AvailableBytesIn, bytesNeeded));
int bytesRead = await _stream.ReadAsync(trailer.Slice(_z.AvailableBytesIn, bytesNeeded));
if (bytesNeeded != bytesRead)
{
throw new ZlibException(String.Format(
"Protocol error. AvailableBytesIn={0}, expected 8",
_z.AvailableBytesIn + bytesRead));
throw new ZlibException($"Protocol error. AvailableBytesIn={_z.AvailableBytesIn + bytesRead}, expected 8");
}
}
else
{
_z.InputBuffer.AsSpan(_z.NextIn, trailer.Length).CopyTo(trailer);
_z.InputBuffer.AsSpan(_z.NextIn, trailer.Length).CopyTo(trailer.Span);
}
Int32 crc32_expected = BinaryPrimitives.ReadInt32LittleEndian(trailer);
Int32 crc32_expected = BinaryPrimitives.ReadInt32LittleEndian(trailer.Span);
Int32 crc32_actual = crc.Crc32Result;
Int32 isize_expected = BinaryPrimitives.ReadInt32LittleEndian(trailer.Slice(4));
Int32 isize_expected = BinaryPrimitives.ReadInt32LittleEndian(trailer.Span.Slice(4));
Int32 isize_actual = (Int32)(_z.TotalBytesOut & 0x00000000FFFFFFFF);
if (crc32_actual != crc32_expected)
{
throw new ZlibException(
String.Format("Bad CRC32 in GZIP stream. (actual({0:X8})!=expected({1:X8}))",
crc32_actual, crc32_expected));
$"Bad CRC32 in GZIP stream. (actual({crc32_actual:X8})!=expected({crc32_expected:X8}))");
}
if (isize_actual != isize_expected)
{
throw new ZlibException(
String.Format("Bad size in GZIP stream. (actual({0})!=expected({1}))", isize_actual,
isize_expected));
$"Bad size in GZIP stream. (actual({isize_actual})!=expected({isize_expected}))");
}
}
else
@@ -304,7 +300,7 @@ namespace SharpCompress.Compressors.Deflate
}
}
private void end()
private void End()
{
if (z is null)
{
@@ -321,36 +317,32 @@ namespace SharpCompress.Compressors.Deflate
_z = null;
}
protected override void Dispose(bool disposing)
public override async ValueTask DisposeAsync()
{
if (isDisposed)
if (_isDisposed)
{
return;
}
isDisposed = true;
base.Dispose(disposing);
if (disposing)
{
_isDisposed = true;
if (_stream is null)
{
return;
}
try
{
finish();
await FinishAsync();
}
finally
{
end();
_stream?.Dispose();
End();
_stream?.DisposeAsync();
_stream = null;
}
}
}
public override void Flush()
public override Task FlushAsync(CancellationToken cancellationToken)
{
_stream.Flush();
return _stream.FlushAsync(cancellationToken);
}
public override Int64 Seek(Int64 offset, SeekOrigin origin)
@@ -365,7 +357,7 @@ namespace SharpCompress.Compressors.Deflate
_stream.SetLength(value);
}
#if NOT
/*
public int Read()
{
if (Read(_buf1, 0, 1) == 0)
@@ -375,19 +367,19 @@ namespace SharpCompress.Compressors.Deflate
crc.SlurpBlock(_buf1,0,1);
return (_buf1[0] & 0xFF);
}
#endif
*/
private bool nomoreinput;
private bool isDisposed;
private bool _nomoreinput;
private bool _isDisposed;
private string ReadZeroTerminatedString()
private async Task<string> ReadZeroTerminatedStringAsync()
{
var list = new List<byte>();
bool done = false;
var done = false;
do
{
// workitem 7740
int n = _stream.Read(_buf1, 0, 1);
int n = await _stream.ReadAsync(_buf1, 0, 1);
if (n != 1)
{
throw new ZlibException("Unexpected EOF reading GZIP header.");
@@ -406,13 +398,14 @@ namespace SharpCompress.Compressors.Deflate
return _encoding.GetString(buffer, 0, buffer.Length);
}
private int _ReadAndValidateGzipHeader()
private async Task<int> ReadAndValidateGzipHeaderAsync(CancellationToken cancellationToken)
{
int totalBytesRead = 0;
var totalBytesRead = 0;
// read the header on the first read
Span<byte> header = stackalloc byte[10];
int n = _stream.Read(header);
using var rented = MemoryPool<byte>.Shared.Rent(10);
var header = rented.Memory.Slice(0, 10);
int n = await _stream.ReadAsync(header, cancellationToken);
// workitem 8501: handle edge case (decompress empty stream)
if (n == 0)
@@ -425,46 +418,46 @@ namespace SharpCompress.Compressors.Deflate
throw new ZlibException("Not a valid GZIP stream.");
}
if (header[0] != 0x1F || header[1] != 0x8B || header[2] != 8)
if (header.Span[0] != 0x1F || header.Span[1] != 0x8B || header.Span[2] != 8)
{
throw new ZlibException("Bad GZIP header.");
}
int timet = BinaryPrimitives.ReadInt32LittleEndian(header.Slice(4));
int timet = BinaryPrimitives.ReadInt32LittleEndian(header.Span.Slice(4));
_GzipMtime = TarHeader.EPOCH.AddSeconds(timet);
totalBytesRead += n;
if ((header[3] & 0x04) == 0x04)
if ((header.Span[3] & 0x04) == 0x04)
{
// read and discard extra field
n = _stream.Read(header.Slice(0, 2)); // 2-byte length field
n = await _stream.ReadAsync(header.Slice(0, 2), cancellationToken); // 2-byte length field
totalBytesRead += n;
short extraLength = (short)(header[0] + header[1] * 256);
short extraLength = (short)(header.Span[0] + header.Span[1] * 256);
byte[] extra = new byte[extraLength];
n = _stream.Read(extra, 0, extra.Length);
n = await _stream.ReadAsync(extra, 0, extra.Length, cancellationToken);
if (n != extraLength)
{
throw new ZlibException("Unexpected end-of-file reading GZIP header.");
}
totalBytesRead += n;
}
if ((header[3] & 0x08) == 0x08)
if ((header.Span[3] & 0x08) == 0x08)
{
_GzipFileName = ReadZeroTerminatedString();
_GzipFileName = await ReadZeroTerminatedStringAsync();
}
if ((header[3] & 0x10) == 0x010)
if ((header.Span[3] & 0x10) == 0x010)
{
_GzipComment = ReadZeroTerminatedString();
_GzipComment = await ReadZeroTerminatedStringAsync();
}
if ((header[3] & 0x02) == 0x02)
if ((header.Span[3] & 0x02) == 0x02)
{
Read(_buf1, 0, 1); // CRC16, ignore
await ReadAsync(_buf1, 0, 1, cancellationToken); // CRC16, ignore
}
return totalBytesRead;
}
public override Int32 Read(Byte[] buffer, Int32 offset, Int32 count)
public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
// According to MS documentation, any implementation of the IO.Stream.Read function must:
// (a) throw an exception if offset & count reference an invalid part of the buffer,
@@ -487,7 +480,7 @@ namespace SharpCompress.Compressors.Deflate
z.AvailableBytesIn = 0;
if (_flavor == ZlibStreamFlavor.GZIP)
{
_gzipHeaderByteCount = _ReadAndValidateGzipHeader();
_gzipHeaderByteCount = await ReadAndValidateGzipHeaderAsync(cancellationToken);
// workitem 8501: handle edge case (decompress empty stream)
if (_gzipHeaderByteCount == 0)
@@ -502,37 +495,13 @@ namespace SharpCompress.Compressors.Deflate
throw new ZlibException("Cannot Read after Writing.");
}
int rc = 0;
// set up the output of the deflate/inflate codec:
_z.OutputBuffer = buffer;
_z.NextOut = offset;
_z.AvailableBytesOut = count;
if (count == 0)
{
return 0;
}
if (nomoreinput && _wantCompress)
if (_nomoreinput && _wantCompress)
{
// no more input data available; therefore we flush to
// try to complete the read
rc = _z.Deflate(FlushType.Finish);
if (rc != ZlibConstants.Z_OK && rc != ZlibConstants.Z_STREAM_END)
{
throw new ZlibException(String.Format("Deflating: rc={0} msg={1}", rc, _z.Message));
}
rc = (count - _z.AvailableBytesOut);
// calculate CRC after reading
if (crc != null)
{
crc.SlurpBlock(buffer, offset, rc);
}
return rc;
return 0; // workitem 8557
}
if (buffer is null)
{
@@ -551,6 +520,13 @@ namespace SharpCompress.Compressors.Deflate
throw new ArgumentOutOfRangeException(nameof(count));
}
var rc = 0;
// set up the output of the deflate/inflate codec:
_z.OutputBuffer = buffer;
_z.NextOut = offset;
_z.AvailableBytesOut = count;
// This is necessary in case _workingBuffer has been resized. (new byte[])
// (The first reference to _workingBuffer goes through the private accessor which
// may initialize it.)
@@ -559,14 +535,14 @@ namespace SharpCompress.Compressors.Deflate
do
{
// need data in _workingBuffer in order to deflate/inflate. Here, we check if we have any.
if ((_z.AvailableBytesIn == 0) && (!nomoreinput))
if ((_z.AvailableBytesIn == 0) && (!_nomoreinput))
{
// No data available, so try to Read data from the captive stream.
_z.NextIn = 0;
_z.AvailableBytesIn = _stream.Read(_workingBuffer, 0, _workingBuffer.Length);
_z.AvailableBytesIn = await _stream.ReadAsync(_workingBuffer, 0, _workingBuffer.Length, cancellationToken);
if (_z.AvailableBytesIn == 0)
{
nomoreinput = true;
_nomoreinput = true;
}
}
@@ -575,23 +551,22 @@ namespace SharpCompress.Compressors.Deflate
? _z.Deflate(_flushMode)
: _z.Inflate(_flushMode);
if (nomoreinput && (rc == ZlibConstants.Z_BUF_ERROR))
if (_nomoreinput && (rc == ZlibConstants.Z_BUF_ERROR))
{
return 0;
}
if (rc != ZlibConstants.Z_OK && rc != ZlibConstants.Z_STREAM_END)
{
throw new ZlibException(String.Format("{0}flating: rc={1} msg={2}", (_wantCompress ? "de" : "in"),
rc, _z.Message));
throw new ZlibException($"{(_wantCompress ? "de" : "in")}flating: rc={rc} msg={_z.Message}");
}
if ((nomoreinput || rc == ZlibConstants.Z_STREAM_END) && (_z.AvailableBytesOut == count))
if ((_nomoreinput || rc == ZlibConstants.Z_STREAM_END) && (_z.AvailableBytesOut == count))
{
break; // nothing more to read
}
} //while (_z.AvailableBytesOut == count && rc == ZlibConstants.Z_OK);
while (_z.AvailableBytesOut > 0 && !nomoreinput && rc == ZlibConstants.Z_OK);
while (_z.AvailableBytesOut > 0 && !_nomoreinput && rc == ZlibConstants.Z_OK);
// workitem 8557
// is there more room in output?
@@ -603,7 +578,7 @@ namespace SharpCompress.Compressors.Deflate
}
// are we completely done reading?
if (nomoreinput)
if (_nomoreinput)
{
// and in compression?
if (_wantCompress)
@@ -614,7 +589,7 @@ namespace SharpCompress.Compressors.Deflate
if (rc != ZlibConstants.Z_OK && rc != ZlibConstants.Z_STREAM_END)
{
throw new ZlibException(String.Format("Deflating: rc={0} msg={1}", rc, _z.Message));
throw new ZlibException($"Deflating: rc={rc} msg={_z.Message}");
}
}
}
@@ -623,10 +598,7 @@ namespace SharpCompress.Compressors.Deflate
rc = (count - _z.AvailableBytesOut);
// calculate CRC after reading
if (crc != null)
{
crc.SlurpBlock(buffer, offset, rc);
}
crc?.SlurpBlock(buffer, offset, rc);
return rc;
}

View File

@@ -28,10 +28,13 @@
using System;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.IO;
namespace SharpCompress.Compressors.Deflate
{
public class ZlibStream : Stream
public class ZlibStream : AsyncStream
{
private readonly ZlibBaseStream _baseStream;
private bool _disposed;
@@ -204,35 +207,25 @@ namespace SharpCompress.Compressors.Deflate
/// <remarks>
/// This may or may not result in a <c>Close()</c> call on the captive stream.
/// </remarks>
protected override void Dispose(bool disposing)
public override async ValueTask DisposeAsync()
{
try
if (!_disposed)
{
if (!_disposed)
{
if (disposing)
{
_baseStream?.Dispose();
}
_disposed = true;
}
}
finally
{
base.Dispose(disposing);
await _baseStream.DisposeAsync();
_disposed = true;
}
}
/// <summary>
/// Flush the stream.
/// </summary>
public override void Flush()
public override Task FlushAsync(CancellationToken cancellationToken)
{
if (_disposed)
{
throw new ObjectDisposedException("ZlibStream");
}
_baseStream.Flush();
return _baseStream.FlushAsync(cancellationToken);
}
/// <summary>
@@ -261,22 +254,13 @@ namespace SharpCompress.Compressors.Deflate
/// <param name="buffer">The buffer into which the read data should be placed.</param>
/// <param name="offset">the offset within that data array to put the first byte read.</param>
/// <param name="count">the number of bytes to read.</param>
public override int Read(byte[] buffer, int offset, int count)
public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
if (_disposed)
{
throw new ObjectDisposedException("ZlibStream");
}
return _baseStream.Read(buffer, offset, count);
}
public override int ReadByte()
{
if (_disposed)
{
throw new ObjectDisposedException("ZlibStream");
}
return _baseStream.ReadByte();
return await _baseStream.ReadAsync(buffer, offset, count, cancellationToken);
}
/// <summary>
@@ -321,24 +305,14 @@ namespace SharpCompress.Compressors.Deflate
/// <param name="buffer">The buffer holding data to write to the stream.</param>
/// <param name="offset">the offset within that data array to find the first byte to write.</param>
/// <param name="count">the number of bytes to write.</param>
public override void Write(byte[] buffer, int offset, int count)
public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
if (_disposed)
{
throw new ObjectDisposedException("ZlibStream");
}
_baseStream.Write(buffer, offset, count);
await _baseStream.WriteAsync(buffer, offset, count, cancellationToken);
}
public override void WriteByte(byte value)
{
if (_disposed)
{
throw new ObjectDisposedException("ZlibStream");
}
_baseStream.WriteByte(value);
}
#endregion System.IO.Stream methods
}
}

View File

@@ -1,297 +0,0 @@
/*
* BranchExecFilter.cs -- Converters for executable
* <Contribution by Louis-Michel Bergeron, on behalf of aDolus Technolog Inc., 2022>
* @TODO Encoding
*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SharpCompress.Compressors.Filters
{
[CLSCompliant(false)]
public sealed class BranchExecFilter
{
public enum Alignment : int
{
ARCH_x86_ALIGNMENT = 1,
ARCH_PowerPC_ALIGNMENT = 4,
ARCH_IA64_ALIGNMENT = 16,
ARCH_ARM_ALIGNMENT = 4,
ARCH_ARMTHUMB_ALIGNMENT = 2,
ARCH_SPARC_ALIGNMENT = 4,
}
public static void X86Converter(byte[] data, UInt32 ip, ref UInt32 state) {
long i = 0;
long size = data.Length;
UInt32 pos = 0;
UInt32 mask = state & 7;
if (size < 5)
return;
size -= 4;
ip += 5;
for (;;)
{
i = pos;
for (; i < size; i++)
{
if ((data[i] & 0xFE) == 0xE8)
{
break;
}
}
UInt32 d = (UInt32)(i) - pos;
pos = (UInt32)i;
if (i >= size)
{
state = (d > 2 ? 0 : mask >> (int)d);
return;
}
if (d > 2)
{
mask = 0;
}
else
{
mask >>= (int)d;
if (mask != 0 && (mask > 4 || mask == 3 || (((((data[(UInt32)(mask >> 1) + 1])) + 1) & 0xFE) == 0) ))
{
mask = (mask >> 1) | 4;
pos++;
continue;
}
}
if ((((data[i + 4]) + 1) & 0xFE) == 0)
{
UInt32 inst = ((UInt32)data[i + 4] << 24) | ((UInt32)data[i + 3] << 16) | ((UInt32)data[i + 2] << 8) | ((UInt32)data[i + 1]);
UInt32 cur = ip + (UInt32)pos;
pos += 5;
inst -= cur;
if (mask != 0)
{
UInt32 sh = (mask & 6) << 2;
if (((((((Byte)(inst >> (int)sh))) + 1) & 0xFE) == 0))
{
inst ^= (((UInt32)0x100 << (int)sh) - 1);
inst -= cur;
}
mask = 0;
}
data[i + 1] = (Byte)inst;
data[i + 2] = (Byte)(inst >> 8);
data[i + 3] = (Byte)(inst >> 16);
data[i + 4] = (Byte)(0 - ((inst >> 24) & 1));
}
else
{
mask = (mask >> 1) | 4;
pos++;
}
}
}
public static void PowerPCConverter(byte[] data, UInt32 ip)
{
long i = 0;
long size = data.Length;
size &= ~(UInt32)3;
ip -= 4;
for (;; ) // infinite loop
{
for (;; ) // infinite loop
{
if (i >= size)
return;
i += 4;
if ((data[i - 4] & 0xFC) == 0x48 && (data[i - 1] & 3) == 1)
break;
}
{
UInt32 inst = BitConverter.ToUInt32(data, (int)i - 4);
if (BitConverter.IsLittleEndian)
{
inst = Utility.SwapUINT32(inst);
}
inst -= (UInt32)(ip + i);
inst &= 0x03FFFFFF;
inst |= 0x48000000;
Utility.SetBigUInt32(ref data, inst, (i - 4));
}
}
}
public static void ARMConverter(byte[] data, UInt32 ip)
{
long i = 0;
long size = data.Length;
size &= ~(UInt32)3;
ip += 4;
for (;;) // infinite loop
{
for (;;) // infinite loop
{
if (i >= size)
{
return;
}
i += 4;
if (data[i - 1] == 0xEB)
break;
}
UInt32 inst = BitConverter.ToUInt32(data, (int)i - 4);
inst <<= 2;
inst -= (UInt32)(ip + i);
inst >>= 2;
inst &= 0x00FFFFFF;
inst |= 0xEB000000;
Utility.SetLittleUInt32(ref data, inst, i - 4);
}
}
public static void ARMTConverter(byte[] data, UInt32 ip)
{
long i = 0;
long size = data.Length;
size &= ~(UInt32)1;
long lim = size - 4;
for (;;)
{
UInt32 b1;
for (;;)
{
UInt32 b3;
if (i > lim)
return;
b1 = data[i + 1];
b3 = data[i + 3];
i += 2;
b1 ^= 8;
if ((b3 & b1) >= 0xF8)
break;
}
UInt32 inst = ((UInt32)b1 << 19)
+ (((UInt32)data[i + 1] & 0x7) << 8)
+ (((UInt32)data[i - 2] << 11))
+ (data[i]);
i += 2;
UInt32 cur = ((UInt32)(ip + i)) >> 1;
inst -= cur;
data[i - 4] = (Byte)(inst >> 11);
data[i - 3] = (Byte)(0xF0 | ((inst >> 19) & 0x7));
data[i - 2] = (Byte)inst;
data[i - 1] = (Byte)(0xF8 | (inst >> 8));
}
}
public static void IA64Converter(byte[] data, UInt32 ip)
{
UInt32 i = 0;
long size = data.Length;
if (size < 16)
throw new InvalidDataException("Unexpected data size");
size -= 16;
do
{
UInt32 m = ((UInt32)0x334B0000 >> (data[i] & 0x1E)) & 3;
if (m != 0)
{
m++;
do
{
UInt32 iterator = (UInt32)( (i + (m * 5) - 8));
if (((data[iterator + 3] >> (int)m) & 15) == 5
&& (((data[iterator - 1] | ((UInt32)data[iterator] << 8)) >> (int)m) & 0x70) == 0)
{
UInt32 raw = BitConverter.ToUInt32(data, (int)iterator);
UInt32 inst = raw >> (int)m;
inst = (inst & 0xFFFFF) | ((inst & (1 << 23)) >> 3);
inst <<= 4;
inst -= (ip + (UInt32)i);
inst >>= 4;
inst &= 0x1FFFFF;
inst += 0x700000;
inst &= 0x8FFFFF;
raw &= ~((UInt32)0x8FFFFF << (int)m);
raw |= (inst << (int)m);
Utility.SetLittleUInt32(ref data, raw, iterator);
}
}
while (++m <= 4);
}
i += 16;
}
while (i <= size);
return;
}
public static void SPARCConverter(byte[] data, UInt32 ip)
{
long i = 0;
long size = data.Length;
size &= ~(UInt32)3;
ip -= 4;
for (;;) // infinite loop
{
for (;;) // infinite loop
{
if (i >= size)
return;
i += 4;
if ((data[i - 4] == 0x40 && (data[i - 3] & 0xC0) == 0) ||
(data[i - 4] == 0x7F && (data[i - 3] >= 0xC0)))
break;
}
UInt32 inst = BitConverter.ToUInt32(data, (int)i - 4);
if (BitConverter.IsLittleEndian)
{
inst = Utility.SwapUINT32(inst);
}
inst <<= 2;
inst -= (UInt32)(ip + i);
inst &= 0x01FFFFFF;
inst -= (UInt32)1 << 24;
inst ^= 0xFF000000;
inst >>= 2;
inst |= 0x40000000;
Utility.SetBigUInt32(ref data, inst, (i - 4));
}
}
}
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
@@ -20,11 +20,6 @@ namespace SharpCompress.Compressors.LZMA
public AesDecoderStream(Stream input, byte[] info, IPasswordProvider pass, long limit)
{
if (pass.CryptoGetTextPassword() == null)
{
throw new SharpCompress.Common.CryptographicException("Encrypted 7Zip archive has no password specified.");
}
mStream = input;
mLimit = limit;
@@ -148,8 +143,8 @@ namespace SharpCompress.Compressors.LZMA
if ((bt & 0xC0) == 0)
{
salt = Array.Empty<byte>();
iv = Array.Empty<byte>();
salt = new byte[0];
iv = new byte[0];
return;
}

View File

@@ -1,5 +1,7 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common.SevenZip;
using SharpCompress.Compressors.LZMA.Utilites;
using SharpCompress.IO;
@@ -91,8 +93,9 @@ namespace SharpCompress.Compressors.LZMA
}
}
private static Stream CreateDecoderStream(Stream[] packStreams, long[] packSizes, Stream[] outStreams,
CFolder folderInfo, int coderIndex, IPasswordProvider pass)
private static async ValueTask<Stream> CreateDecoderStream(Stream[] packStreams, long[] packSizes, Stream[] outStreams,
CFolder folderInfo, int coderIndex, IPasswordProvider pass,
CancellationToken cancellationToken)
{
var coderInfo = folderInfo._coders[coderIndex];
if (coderInfo._numOutStreams != 1)
@@ -127,8 +130,8 @@ namespace SharpCompress.Compressors.LZMA
}
int otherCoderIndex = FindCoderIndexForOutStreamIndex(folderInfo, pairedOutIndex);
inStreams[i] = CreateDecoderStream(packStreams, packSizes, outStreams, folderInfo, otherCoderIndex,
pass);
inStreams[i] = await CreateDecoderStream(packStreams, packSizes, outStreams, folderInfo, otherCoderIndex,
pass, cancellationToken);
//inStreamSizes[i] = folderInfo.UnpackSizes[pairedOutIndex];
@@ -154,11 +157,11 @@ namespace SharpCompress.Compressors.LZMA
}
long unpackSize = folderInfo._unpackSizes[outStreamId];
return DecoderRegistry.CreateDecoderStream(coderInfo._methodId, inStreams, coderInfo._props, pass, unpackSize);
return await DecoderRegistry.CreateDecoderStream(coderInfo._methodId, inStreams, coderInfo._props, pass, unpackSize, cancellationToken);
}
internal static Stream CreateDecoderStream(Stream inStream, long startPos, long[] packSizes, CFolder folderInfo,
IPasswordProvider pass)
internal static async ValueTask<Stream> CreateDecoderStream(Stream inStream, long startPos, long[] packSizes, CFolder folderInfo,
IPasswordProvider pass, CancellationToken cancellationToken)
{
if (!folderInfo.CheckStructure())
{
@@ -176,7 +179,7 @@ namespace SharpCompress.Compressors.LZMA
int primaryCoderIndex, primaryOutStreamIndex;
FindPrimaryOutStreamIndex(folderInfo, out primaryCoderIndex, out primaryOutStreamIndex);
return CreateDecoderStream(inStreams, packSizes, outStreams, folderInfo, primaryCoderIndex, pass);
return await CreateDecoderStream(inStreams, packSizes, outStreams, folderInfo, primaryCoderIndex, pass, cancellationToken);
}
}
}

View File

@@ -1,5 +1,7 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace SharpCompress.Compressors.LZMA
{
@@ -59,8 +61,8 @@ namespace SharpCompress.Compressors.LZMA
/// <param name="progress">
/// callback progress reference.
/// </param>
void Code(Stream inStream, Stream outStream,
Int64 inSize, Int64 outSize, ICodeProgress progress);
ValueTask CodeAsync(Stream inStream, Stream outStream,
Int64 inSize, Int64 outSize, ICodeProgress progress, CancellationToken cancellationToken);
}
/*

View File

@@ -103,7 +103,7 @@ namespace SharpCompress.Compressors.LZMA.LZ
{
if (historySize > K_MAX_VAL_FOR_NORMALIZE - 256)
{
throw new ArgumentOutOfRangeException(nameof(historySize));
throw new Exception();
}
_cutValue = 16 + (matchMaxLen >> 1);
@@ -423,4 +423,4 @@ namespace SharpCompress.Compressors.LZMA.LZ
_cutValue = cutValue;
}
}
}
}

View File

@@ -1,6 +1,9 @@
using System;
using System.Buffers;
using System.Buffers.Binary;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Crypto;
using SharpCompress.IO;
@@ -14,61 +17,70 @@ 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 : AsyncStream
{
private readonly Stream _stream;
private readonly CountingWritableSubStream? _countingWritableSubStream;
#nullable disable
private Stream _stream;
#nullable enable
private CountingWritableSubStream? _countingWritableSubStream;
private bool _disposed;
private bool _finished;
private long _writeCount;
public LZipStream(Stream stream, CompressionMode mode)
private LZipStream()
{
Mode = mode;
}
public static async ValueTask<LZipStream> CreateAsync(Stream stream, CompressionMode mode)
{
var lzip = new LZipStream();
lzip.Mode = mode;
if (mode == CompressionMode.Decompress)
{
int dSize = ValidateAndReadSize(stream);
int dSize = await ValidateAndReadSize(stream);
if (dSize == 0)
{
throw new IOException("Not an LZip stream");
}
byte[] properties = GetProperties(dSize);
_stream = new LzmaStream(properties, stream);
lzip._stream = await LzmaStream.CreateAsync(properties, stream);
}
else
{
//default
int dSize = 104 * 1024;
WriteHeaderSize(stream);
await WriteHeaderSizeAsync(stream);
_countingWritableSubStream = new CountingWritableSubStream(stream);
_stream = new Crc32Stream(new LzmaStream(new LzmaEncoderProperties(true, dSize), false, _countingWritableSubStream));
lzip._countingWritableSubStream = new CountingWritableSubStream(stream);
lzip._stream = new Crc32Stream(new LzmaStream(new LzmaEncoderProperties(true, dSize), false, lzip._countingWritableSubStream));
}
return lzip;
}
public void Finish()
public async ValueTask FinishAsync()
{
if (!_finished)
{
if (Mode == CompressionMode.Compress)
{
var crc32Stream = (Crc32Stream)_stream;
crc32Stream.WrappedStream.Dispose();
crc32Stream.Dispose();
await crc32Stream.WrappedStream.DisposeAsync();
await crc32Stream.DisposeAsync();
var compressedCount = _countingWritableSubStream!.Count;
Span<byte> intBuf = stackalloc byte[8];
byte[] intBuf = new byte[8];
BinaryPrimitives.WriteUInt32LittleEndian(intBuf, crc32Stream.Crc);
_countingWritableSubStream.Write(intBuf.Slice(0, 4));
await _countingWritableSubStream.WriteAsync(intBuf, 0, 4);
BinaryPrimitives.WriteInt64LittleEndian(intBuf, _writeCount);
_countingWritableSubStream.Write(intBuf);
await _countingWritableSubStream.WriteAsync(intBuf, 0, 8);
//total with headers
BinaryPrimitives.WriteUInt64LittleEndian(intBuf, compressedCount + 6 + 20);
_countingWritableSubStream.Write(intBuf);
await _countingWritableSubStream.WriteAsync(intBuf, 0, 8);
}
_finished = true;
}
@@ -76,21 +88,18 @@ namespace SharpCompress.Compressors.LZMA
#region Stream methods
protected override void Dispose(bool disposing)
public override async ValueTask DisposeAsync()
{
if (_disposed)
{
return;
}
_disposed = true;
if (disposing)
{
Finish();
_stream.Dispose();
}
await FinishAsync();
await _stream.DisposeAsync();
}
public CompressionMode Mode { get; }
public CompressionMode Mode { get; private set; }
public override bool CanRead => Mode == CompressionMode.Decompress;
@@ -98,54 +107,38 @@ namespace SharpCompress.Compressors.LZMA
public override bool CanWrite => Mode == CompressionMode.Compress;
public override void Flush()
public override Task FlushAsync(CancellationToken cancellationToken)
{
_stream.Flush();
return _stream.FlushAsync(cancellationToken);
}
// TODO: Both Length and Position are sometimes feasible, but would require
// reading the output length when we initialize.
public override long Length => throw new NotImplementedException();
public override long Length => throw new NotSupportedException();
public override long Position { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public override long Position { get => throw new NotImplementedException(); set => throw new NotSupportedException(); }
public override int Read(byte[] buffer, int offset, int count) => _stream.Read(buffer, offset, count);
public override int ReadByte() => _stream.ReadByte();
public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = new CancellationToken())
{
return _stream.ReadAsync(buffer, cancellationToken);
}
public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();
public override void SetLength(long value) => throw new NotImplementedException();
public override void SetLength(long value) => throw new NotSupportedException();
#if !NETFRAMEWORK && !NETSTANDARD2_0
public override int Read(Span<byte> buffer)
public override async ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = new CancellationToken())
{
return _stream.Read(buffer);
}
public override void Write(ReadOnlySpan<byte> buffer)
{
_stream.Write(buffer);
await _stream.WriteAsync(buffer, cancellationToken);
_writeCount += buffer.Length;
}
#endif
public override void Write(byte[] buffer, int offset, int count)
public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
_stream.Write(buffer, offset, count);
await _stream.WriteAsync(buffer, offset, count, cancellationToken);
_writeCount += count;
}
public override void WriteByte(byte value)
{
_stream.WriteByte(value);
++_writeCount;
}
#endregion
/// <summary>
@@ -155,14 +148,14 @@ namespace SharpCompress.Compressors.LZMA
/// </summary>
/// <param name="stream">The stream to read from. Must not be null.</param>
/// <returns><c>true</c> if the given stream is an LZip file, <c>false</c> otherwise.</returns>
public static bool IsLZipFile(Stream stream) => ValidateAndReadSize(stream) != 0;
public static async ValueTask<bool> IsLZipFileAsync(Stream stream) => await ValidateAndReadSize(stream) != 0;
/// <summary>
/// Reads the 6-byte header of the stream, and returns 0 if either the header
/// couldn't be read or it isn't a validate LZIP header, or the dictionary
/// size if it *is* a valid LZIP file.
/// </summary>
public static int ValidateAndReadSize(Stream stream)
private static async ValueTask<int> ValidateAndReadSize(Stream stream)
{
if (stream is null)
{
@@ -170,8 +163,9 @@ namespace SharpCompress.Compressors.LZMA
}
// Read the header
Span<byte> header = stackalloc byte[6];
int n = stream.Read(header);
using var buffer = MemoryPool<byte>.Shared.Rent(6);
var header = buffer.Memory.Slice(0,6);
int n = await stream.ReadAsync(header);
// TODO: Handle reading only part of the header?
@@ -180,18 +174,18 @@ namespace SharpCompress.Compressors.LZMA
return 0;
}
if (header[0] != 'L' || header[1] != 'Z' || header[2] != 'I' || header[3] != 'P' || header[4] != 1 /* version 1 */)
if (header.Span[0] != 'L' || header.Span[1] != 'Z' || header.Span[2] != 'I' || header.Span[3] != 'P' || header.Span[4] != 1 /* version 1 */)
{
return 0;
}
int basePower = header[5] & 0x1F;
int subtractionNumerator = (header[5] & 0xE0) >> 5;
int basePower = header.Span[5] & 0x1F;
int subtractionNumerator = (header.Span[5] & 0xE0) >> 5;
return (1 << basePower) - subtractionNumerator * (1 << (basePower - 4));
}
private static readonly byte[] headerBytes = new byte[6] { (byte)'L', (byte)'Z', (byte)'I', (byte)'P', 1, 113 };
public static void WriteHeaderSize(Stream stream)
public static async ValueTask WriteHeaderSizeAsync(Stream stream)
{
if (stream is null)
{
@@ -199,7 +193,7 @@ namespace SharpCompress.Compressors.LZMA
}
// hard coding the dictionary size encoding
stream.Write(headerBytes, 0, 6);
await stream.WriteAsync(headerBytes, 0, 6);
}
/// <summary>

View File

@@ -1,7 +1,7 @@
#nullable disable
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Compressors.LZMA.LZ;
using SharpCompress.Compressors.LZMA.RangeCoder;
@@ -11,11 +11,11 @@ namespace SharpCompress.Compressors.LZMA
{
private class LenDecoder
{
private BitDecoder _choice = new BitDecoder();
private BitDecoder _choice2 = new BitDecoder();
private BitDecoder _choice = new();
private BitDecoder _choice2 = new();
private readonly BitTreeDecoder[] _lowCoder = new BitTreeDecoder[Base.K_NUM_POS_STATES_MAX];
private readonly BitTreeDecoder[] _midCoder = new BitTreeDecoder[Base.K_NUM_POS_STATES_MAX];
private BitTreeDecoder _highCoder = new BitTreeDecoder(Base.K_NUM_HIGH_LEN_BITS);
private BitTreeDecoder _highCoder = new(Base.K_NUM_HIGH_LEN_BITS);
private uint _numPosStates;
public void Create(uint numPosStates)
@@ -40,21 +40,21 @@ namespace SharpCompress.Compressors.LZMA
_highCoder.Init();
}
public uint Decode(RangeCoder.Decoder rangeDecoder, uint posState)
public async ValueTask<uint> DecodeAsync(RangeCoder.Decoder rangeDecoder, uint posState, CancellationToken cancellationToken)
{
if (_choice.Decode(rangeDecoder) == 0)
if (await _choice.DecodeAsync(rangeDecoder, cancellationToken) == 0)
{
return _lowCoder[posState].Decode(rangeDecoder);
return await _lowCoder[posState].DecodeAsync(rangeDecoder, cancellationToken);
}
uint symbol = Base.K_NUM_LOW_LEN_SYMBOLS;
if (_choice2.Decode(rangeDecoder) == 0)
if (await _choice2.DecodeAsync(rangeDecoder, cancellationToken) == 0)
{
symbol += _midCoder[posState].Decode(rangeDecoder);
symbol += await _midCoder[posState].DecodeAsync(rangeDecoder, cancellationToken);
}
else
{
symbol += Base.K_NUM_MID_LEN_SYMBOLS;
symbol += _highCoder.Decode(rangeDecoder);
symbol += await _highCoder.DecodeAsync(rangeDecoder, cancellationToken);
}
return symbol;
}
@@ -79,31 +79,31 @@ namespace SharpCompress.Compressors.LZMA
}
}
public byte DecodeNormal(RangeCoder.Decoder rangeDecoder)
public async ValueTask<byte> DecodeNormalAsync(RangeCoder.Decoder rangeDecoder, CancellationToken cancellationToken)
{
uint symbol = 1;
do
{
symbol = (symbol << 1) | _decoders[symbol].Decode(rangeDecoder);
symbol = (symbol << 1) | await _decoders[symbol].DecodeAsync(rangeDecoder, cancellationToken);
}
while (symbol < 0x100);
return (byte)symbol;
}
public byte DecodeWithMatchByte(RangeCoder.Decoder rangeDecoder, byte matchByte)
public async ValueTask<byte> DecodeWithMatchByteAsync(RangeCoder.Decoder rangeDecoder, byte matchByte, CancellationToken cancellationToken)
{
uint symbol = 1;
do
{
uint matchBit = (uint)(matchByte >> 7) & 1;
matchByte <<= 1;
uint bit = _decoders[((1 + matchBit) << 8) + symbol].Decode(rangeDecoder);
uint bit = await _decoders[((1 + matchBit) << 8) + symbol].DecodeAsync(rangeDecoder, cancellationToken);
symbol = (symbol << 1) | bit;
if (matchBit != bit)
{
while (symbol < 0x100)
{
symbol = (symbol << 1) | _decoders[symbol].Decode(rangeDecoder);
symbol = (symbol << 1) | await _decoders[symbol].DecodeAsync(rangeDecoder, cancellationToken);
}
break;
}
@@ -113,12 +113,12 @@ namespace SharpCompress.Compressors.LZMA
}
}
private Decoder2[] _coders;
private int _numPrevBits;
private int _numPosBits;
private uint _posMask;
public void Create(int numPosBits, int numPrevBits)
private readonly Decoder2[]_coders;
private readonly int _numPrevBits;
private readonly int _numPosBits;
private readonly uint _posMask;
public LiteralDecoder(int numPosBits, int numPrevBits)
{
if (_coders != null && _numPrevBits == numPrevBits &&
_numPosBits == numPosBits)
@@ -150,18 +150,18 @@ namespace SharpCompress.Compressors.LZMA
return ((pos & _posMask) << _numPrevBits) + (uint)(prevByte >> (8 - _numPrevBits));
}
public byte DecodeNormal(RangeCoder.Decoder rangeDecoder, uint pos, byte prevByte)
public ValueTask<byte> DecodeNormalAsync(RangeCoder.Decoder rangeDecoder, uint pos, byte prevByte, CancellationToken cancellationToken)
{
return _coders[GetState(pos, prevByte)].DecodeNormal(rangeDecoder);
return _coders[GetState(pos, prevByte)].DecodeNormalAsync(rangeDecoder, cancellationToken);
}
public byte DecodeWithMatchByte(RangeCoder.Decoder rangeDecoder, uint pos, byte prevByte, byte matchByte)
public ValueTask<byte> DecodeWithMatchByteAsync(RangeCoder.Decoder rangeDecoder, uint pos, byte prevByte, byte matchByte, CancellationToken cancellationToken)
{
return _coders[GetState(pos, prevByte)].DecodeWithMatchByte(rangeDecoder, matchByte);
return _coders[GetState(pos, prevByte)].DecodeWithMatchByteAsync(rangeDecoder, matchByte, cancellationToken);
}
}
private OutWindow _outWindow;
private OutWindow? _outWindow;
private readonly BitDecoder[] _isMatchDecoders = new BitDecoder[Base.K_NUM_STATES << Base.K_NUM_POS_STATES_BITS_MAX];
private readonly BitDecoder[] _isRepDecoders = new BitDecoder[Base.K_NUM_STATES];
@@ -173,18 +173,18 @@ namespace SharpCompress.Compressors.LZMA
private readonly BitTreeDecoder[] _posSlotDecoder = new BitTreeDecoder[Base.K_NUM_LEN_TO_POS_STATES];
private readonly BitDecoder[] _posDecoders = new BitDecoder[Base.K_NUM_FULL_DISTANCES - Base.K_END_POS_MODEL_INDEX];
private BitTreeDecoder _posAlignDecoder = new BitTreeDecoder(Base.K_NUM_ALIGN_BITS);
private BitTreeDecoder _posAlignDecoder = new(Base.K_NUM_ALIGN_BITS);
private readonly LenDecoder _lenDecoder = new LenDecoder();
private readonly LenDecoder _repLenDecoder = new LenDecoder();
private readonly LenDecoder _lenDecoder = new();
private readonly LenDecoder _repLenDecoder = new();
private readonly LiteralDecoder _literalDecoder = new LiteralDecoder();
private LiteralDecoder? _literalDecoder;
private int _dictionarySize;
private uint _posStateMask;
private Base.State _state = new Base.State();
private Base.State _state = new();
private uint _rep0, _rep1, _rep2, _rep3;
public Decoder()
@@ -196,15 +196,16 @@ namespace SharpCompress.Compressors.LZMA
}
}
private void CreateDictionary()
private OutWindow CreateDictionary()
{
if (_dictionarySize < 0)
{
throw new InvalidParamException();
}
_outWindow = new OutWindow();
var outWindow = new OutWindow();
int blockSize = Math.Max(_dictionarySize, (1 << 12));
_outWindow.Create(blockSize);
outWindow.Create(blockSize);
return outWindow;
}
private void SetLiteralProperties(int lp, int lc)
@@ -217,7 +218,7 @@ namespace SharpCompress.Compressors.LZMA
{
throw new InvalidParamException();
}
_literalDecoder.Create(lp, lc);
_literalDecoder = new(lp, lc);
}
private void SetPosBitsProperties(int pb)
@@ -249,7 +250,7 @@ namespace SharpCompress.Compressors.LZMA
_isRepG2Decoders[i].Init();
}
_literalDecoder.Init();
_literalDecoder!.Init();
for (i = 0; i < Base.K_NUM_LEN_TO_POS_STATES; i++)
{
_posSlotDecoder[i].Init();
@@ -272,12 +273,12 @@ namespace SharpCompress.Compressors.LZMA
_rep3 = 0;
}
public void Code(Stream inStream, Stream outStream,
Int64 inSize, Int64 outSize, ICodeProgress progress)
public async ValueTask CodeAsync(Stream inStream, Stream outStream,
Int64 inSize, Int64 outSize, ICodeProgress progress, CancellationToken cancellationToken)
{
if (_outWindow is null)
{
CreateDictionary();
_outWindow = CreateDictionary();
}
_outWindow.Init(outStream);
if (outSize > 0)
@@ -290,18 +291,27 @@ namespace SharpCompress.Compressors.LZMA
}
RangeCoder.Decoder rangeDecoder = new RangeCoder.Decoder();
rangeDecoder.Init(inStream);
await rangeDecoder.InitAsync(inStream, cancellationToken);
Code(_dictionarySize, _outWindow, rangeDecoder);
await CodeAsync(_dictionarySize, _outWindow, rangeDecoder, cancellationToken);
_outWindow.ReleaseStream();
rangeDecoder.ReleaseStream();
if (!rangeDecoder.IsFinished || (inSize > 0 && rangeDecoder._total != inSize))
{
throw new DataErrorException();
}
if (_outWindow.HasPending)
{
throw new DataErrorException();
}
_outWindow = null;
}
internal bool Code(int dictionarySize, OutWindow outWindow, RangeCoder.Decoder rangeDecoder)
internal async ValueTask<bool> CodeAsync(int dictionarySize, OutWindow outWindow, RangeCoder.Decoder rangeDecoder, CancellationToken cancellationToken)
{
_literalDecoder ??= _literalDecoder.CheckNotNull(nameof(_literalDecoder));
int dictionarySizeCheck = Math.Max(dictionarySize, 1);
outWindow.CopyPending();
@@ -309,19 +319,19 @@ namespace SharpCompress.Compressors.LZMA
while (outWindow.HasSpace)
{
uint posState = (uint)outWindow._total & _posStateMask;
if (_isMatchDecoders[(_state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState].Decode(rangeDecoder) == 0)
if (await _isMatchDecoders[(_state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState].DecodeAsync(rangeDecoder, cancellationToken) == 0)
{
byte b;
byte prevByte = outWindow.GetByte(0);
if (!_state.IsCharState())
{
b = _literalDecoder.DecodeWithMatchByte(rangeDecoder,
b = await _literalDecoder.DecodeWithMatchByteAsync(rangeDecoder,
(uint)outWindow._total, prevByte,
outWindow.GetByte((int)_rep0));
outWindow.GetByte((int)_rep0), cancellationToken);
}
else
{
b = _literalDecoder.DecodeNormal(rangeDecoder, (uint)outWindow._total, prevByte);
b = await _literalDecoder.DecodeNormalAsync(rangeDecoder, (uint)outWindow._total, prevByte, cancellationToken);
}
outWindow.PutByte(b);
_state.UpdateChar();
@@ -329,13 +339,13 @@ namespace SharpCompress.Compressors.LZMA
else
{
uint len;
if (_isRepDecoders[_state._index].Decode(rangeDecoder) == 1)
if (await _isRepDecoders[_state._index].DecodeAsync(rangeDecoder, cancellationToken) == 1)
{
if (_isRepG0Decoders[_state._index].Decode(rangeDecoder) == 0)
if (await _isRepG0Decoders[_state._index].DecodeAsync(rangeDecoder, cancellationToken) == 0)
{
if (
_isRep0LongDecoders[(_state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState].Decode(
rangeDecoder) == 0)
await _isRep0LongDecoders[(_state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState].DecodeAsync(
rangeDecoder, cancellationToken) == 0)
{
_state.UpdateShortRep();
outWindow.PutByte(outWindow.GetByte((int)_rep0));
@@ -345,13 +355,13 @@ namespace SharpCompress.Compressors.LZMA
else
{
UInt32 distance;
if (_isRepG1Decoders[_state._index].Decode(rangeDecoder) == 0)
if (await _isRepG1Decoders[_state._index].DecodeAsync(rangeDecoder, cancellationToken) == 0)
{
distance = _rep1;
}
else
{
if (_isRepG2Decoders[_state._index].Decode(rangeDecoder) == 0)
if (await _isRepG2Decoders[_state._index].DecodeAsync(rangeDecoder, cancellationToken) == 0)
{
distance = _rep2;
}
@@ -365,7 +375,7 @@ namespace SharpCompress.Compressors.LZMA
_rep1 = _rep0;
_rep0 = distance;
}
len = _repLenDecoder.Decode(rangeDecoder, posState) + Base.K_MATCH_MIN_LEN;
len = await _repLenDecoder.DecodeAsync(rangeDecoder, posState, cancellationToken) + Base.K_MATCH_MIN_LEN;
_state.UpdateRep();
}
else
@@ -373,23 +383,22 @@ namespace SharpCompress.Compressors.LZMA
_rep3 = _rep2;
_rep2 = _rep1;
_rep1 = _rep0;
len = Base.K_MATCH_MIN_LEN + _lenDecoder.Decode(rangeDecoder, posState);
len = Base.K_MATCH_MIN_LEN + await _lenDecoder.DecodeAsync(rangeDecoder, posState, cancellationToken);
_state.UpdateMatch();
uint posSlot = _posSlotDecoder[Base.GetLenToPosState(len)].Decode(rangeDecoder);
uint posSlot = await _posSlotDecoder[Base.GetLenToPosState(len)].DecodeAsync(rangeDecoder, cancellationToken);
if (posSlot >= Base.K_START_POS_MODEL_INDEX)
{
int numDirectBits = (int)((posSlot >> 1) - 1);
_rep0 = ((2 | (posSlot & 1)) << numDirectBits);
if (posSlot < Base.K_END_POS_MODEL_INDEX)
{
_rep0 += BitTreeDecoder.ReverseDecode(_posDecoders,
_rep0 - posSlot - 1, rangeDecoder, numDirectBits);
_rep0 += await BitTreeDecoder.ReverseDecode(_posDecoders,
_rep0 - posSlot - 1, rangeDecoder, numDirectBits, cancellationToken);
}
else
{
_rep0 += (rangeDecoder.DecodeDirectBits(
numDirectBits - Base.K_NUM_ALIGN_BITS) << Base.K_NUM_ALIGN_BITS);
_rep0 += _posAlignDecoder.ReverseDecode(rangeDecoder);
_rep0 += (await rangeDecoder.DecodeDirectBitsAsync(numDirectBits - Base.K_NUM_ALIGN_BITS, cancellationToken) << Base.K_NUM_ALIGN_BITS);
_rep0 += await _posAlignDecoder.ReverseDecode(rangeDecoder, cancellationToken);
}
}
else
@@ -442,7 +451,7 @@ namespace SharpCompress.Compressors.LZMA
{
if (_outWindow is null)
{
CreateDictionary();
_outWindow = CreateDictionary();
}
_outWindow.Train(stream);
}
@@ -472,4 +481,4 @@ namespace SharpCompress.Compressors.LZMA
public override void SetLength(long value) {}
*/
}
}
}

View File

@@ -2,6 +2,8 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Compressors.LZMA.LZ;
using SharpCompress.Compressors.LZMA.RangeCoder;
@@ -61,7 +63,7 @@ namespace SharpCompress.Compressors.LZMA
return (UInt32)(G_FAST_POS[pos >> 26] + 52);
}
private Base.State _state = new Base.State();
private Base.State _state = new();
private Byte _previousByte;
private readonly UInt32[] _repDistances = new UInt32[Base.K_NUM_REP_DISTANCES];
@@ -97,18 +99,18 @@ namespace SharpCompress.Compressors.LZMA
}
}
public void Encode(RangeCoder.Encoder rangeEncoder, byte symbol)
public async ValueTask EncodeAsync(RangeCoder.Encoder rangeEncoder, byte symbol)
{
uint context = 1;
for (int i = 7; i >= 0; i--)
{
uint bit = (uint)((symbol >> i) & 1);
_encoders[context].Encode(rangeEncoder, bit);
await _encoders[context].EncodeAsync(rangeEncoder, bit);
context = (context << 1) | bit;
}
}
public void EncodeMatched(RangeCoder.Encoder rangeEncoder, byte matchByte, byte symbol)
public async ValueTask EncodeMatchedAsync(RangeCoder.Encoder rangeEncoder, byte matchByte, byte symbol)
{
uint context = 1;
bool same = true;
@@ -122,7 +124,7 @@ namespace SharpCompress.Compressors.LZMA
state += ((1 + matchBit) << 8);
same = (matchBit == bit);
}
_encoders[state].Encode(rangeEncoder, bit);
await _encoders[state].EncodeAsync(rangeEncoder, bit);
context = (context << 1) | bit;
}
}
@@ -196,11 +198,11 @@ namespace SharpCompress.Compressors.LZMA
private class LenEncoder
{
private BitEncoder _choice = new BitEncoder();
private BitEncoder _choice2 = new BitEncoder();
private BitEncoder _choice = new();
private BitEncoder _choice2 = new();
private readonly BitTreeEncoder[] _lowCoder = new BitTreeEncoder[Base.K_NUM_POS_STATES_ENCODING_MAX];
private readonly BitTreeEncoder[] _midCoder = new BitTreeEncoder[Base.K_NUM_POS_STATES_ENCODING_MAX];
private BitTreeEncoder _highCoder = new BitTreeEncoder(Base.K_NUM_HIGH_LEN_BITS);
private BitTreeEncoder _highCoder = new(Base.K_NUM_HIGH_LEN_BITS);
public LenEncoder()
{
@@ -223,26 +225,26 @@ namespace SharpCompress.Compressors.LZMA
_highCoder.Init();
}
public void Encode(RangeCoder.Encoder rangeEncoder, UInt32 symbol, UInt32 posState)
public async ValueTask EncodeAsync(RangeCoder.Encoder rangeEncoder, UInt32 symbol, UInt32 posState)
{
if (symbol < Base.K_NUM_LOW_LEN_SYMBOLS)
{
_choice.Encode(rangeEncoder, 0);
_lowCoder[posState].Encode(rangeEncoder, symbol);
await _choice.EncodeAsync(rangeEncoder, 0);
await _lowCoder[posState].EncodeAsync(rangeEncoder, symbol);
}
else
{
symbol -= Base.K_NUM_LOW_LEN_SYMBOLS;
_choice.Encode(rangeEncoder, 1);
await _choice.EncodeAsync(rangeEncoder, 1);
if (symbol < Base.K_NUM_MID_LEN_SYMBOLS)
{
_choice2.Encode(rangeEncoder, 0);
_midCoder[posState].Encode(rangeEncoder, symbol);
await _choice2.EncodeAsync(rangeEncoder, 0);
await _midCoder[posState].EncodeAsync(rangeEncoder, symbol);
}
else
{
_choice2.Encode(rangeEncoder, 1);
_highCoder.Encode(rangeEncoder, symbol - Base.K_NUM_MID_LEN_SYMBOLS);
await _choice2.EncodeAsync(rangeEncoder, 1);
await _highCoder.EncodeAsync(rangeEncoder, symbol - Base.K_NUM_MID_LEN_SYMBOLS);
}
}
}
@@ -309,9 +311,9 @@ namespace SharpCompress.Compressors.LZMA
}
}
public new void Encode(RangeCoder.Encoder rangeEncoder, UInt32 symbol, UInt32 posState)
public new async ValueTask EncodeAsync(RangeCoder.Encoder rangeEncoder, UInt32 symbol, UInt32 posState)
{
base.Encode(rangeEncoder, symbol, posState);
await base.EncodeAsync(rangeEncoder, symbol, posState);
if (--_counters[posState] == 0)
{
UpdateTable(posState);
@@ -361,7 +363,7 @@ namespace SharpCompress.Compressors.LZMA
private readonly Optimal[] _optimum = new Optimal[K_NUM_OPTS];
private BinTree _matchFinder;
private readonly RangeCoder.Encoder _rangeEncoder = new RangeCoder.Encoder();
private readonly RangeCoder.Encoder _rangeEncoder = new();
private readonly BitEncoder[] _isMatch =
new BitEncoder[Base.K_NUM_STATES << Base.K_NUM_POS_STATES_BITS_MAX];
@@ -379,12 +381,12 @@ namespace SharpCompress.Compressors.LZMA
private readonly BitEncoder[] _posEncoders =
new BitEncoder[Base.K_NUM_FULL_DISTANCES - Base.K_END_POS_MODEL_INDEX];
private BitTreeEncoder _posAlignEncoder = new BitTreeEncoder(Base.K_NUM_ALIGN_BITS);
private BitTreeEncoder _posAlignEncoder = new(Base.K_NUM_ALIGN_BITS);
private readonly LenPriceTableEncoder _lenEncoder = new LenPriceTableEncoder();
private readonly LenPriceTableEncoder _repMatchLenEncoder = new LenPriceTableEncoder();
private readonly LenPriceTableEncoder _lenEncoder = new();
private readonly LenPriceTableEncoder _repMatchLenEncoder = new();
private readonly LiteralEncoder _literalEncoder = new LiteralEncoder();
private readonly LiteralEncoder _literalEncoder = new();
private readonly UInt32[] _matchDistances = new UInt32[Base.K_MATCH_MAX_LEN * 2 + 2];
@@ -1189,40 +1191,40 @@ namespace SharpCompress.Compressors.LZMA
return (smallDist < ((UInt32)(1) << (32 - kDif)) && bigDist >= (smallDist << kDif));
}
private void WriteEndMarker(UInt32 posState)
private async ValueTask WriteEndMarkerAsync(UInt32 posState)
{
if (!_writeEndMark)
{
return;
}
_isMatch[(_state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState].Encode(_rangeEncoder, 1);
_isRep[_state._index].Encode(_rangeEncoder, 0);
await _isMatch[(_state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState].EncodeAsync(_rangeEncoder, 1);
await _isRep[_state._index].EncodeAsync(_rangeEncoder, 0);
_state.UpdateMatch();
UInt32 len = Base.K_MATCH_MIN_LEN;
_lenEncoder.Encode(_rangeEncoder, len - Base.K_MATCH_MIN_LEN, posState);
await _lenEncoder.EncodeAsync(_rangeEncoder, len - Base.K_MATCH_MIN_LEN, posState);
UInt32 posSlot = (1 << Base.K_NUM_POS_SLOT_BITS) - 1;
UInt32 lenToPosState = Base.GetLenToPosState(len);
_posSlotEncoder[lenToPosState].Encode(_rangeEncoder, posSlot);
await _posSlotEncoder[lenToPosState].EncodeAsync(_rangeEncoder, posSlot);
int footerBits = 30;
UInt32 posReduced = (((UInt32)1) << footerBits) - 1;
_rangeEncoder.EncodeDirectBits(posReduced >> Base.K_NUM_ALIGN_BITS, footerBits - Base.K_NUM_ALIGN_BITS);
_posAlignEncoder.ReverseEncode(_rangeEncoder, posReduced & Base.K_ALIGN_MASK);
await _rangeEncoder.EncodeDirectBits(posReduced >> Base.K_NUM_ALIGN_BITS, footerBits - Base.K_NUM_ALIGN_BITS);
await _posAlignEncoder.ReverseEncodeAsync(_rangeEncoder, posReduced & Base.K_ALIGN_MASK);
}
private void Flush(UInt32 nowPos)
private async ValueTask FlushAsync(UInt32 nowPos)
{
ReleaseMfStream();
WriteEndMarker(nowPos & _posStateMask);
_rangeEncoder.FlushData();
_rangeEncoder.FlushStream();
await WriteEndMarkerAsync(nowPos & _posStateMask);
await _rangeEncoder.FlushData();
await _rangeEncoder.FlushAsync();
}
public void CodeOneBlock(out Int64 inSize, out Int64 outSize, out bool finished)
public async ValueTask<(Int64, Int64, bool)> CodeOneBlockAsync()
{
inSize = 0;
outSize = 0;
finished = true;
long inSize = 0;
long outSize = 0;
var finished = true;
if (_inStream != null)
{
@@ -1233,7 +1235,7 @@ namespace SharpCompress.Compressors.LZMA
if (_finished)
{
return;
return (inSize, outSize, finished);
}
_finished = true;
@@ -1254,20 +1256,20 @@ namespace SharpCompress.Compressors.LZMA
if (_processingMode && _matchFinder.IsDataStarved)
{
_finished = false;
return;
return (inSize, outSize, finished);
}
if (_matchFinder.GetNumAvailableBytes() == 0)
{
Flush((UInt32)_nowPos64);
return;
await FlushAsync((UInt32)_nowPos64);
return (inSize, outSize, finished);
}
UInt32 len, numDistancePairs; // it's not used
ReadMatchDistances(out len, out numDistancePairs);
UInt32 posState = (UInt32)(_nowPos64) & _posStateMask;
_isMatch[(_state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState].Encode(_rangeEncoder, 0);
await _isMatch[(_state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState].EncodeAsync(_rangeEncoder, 0);
_state.UpdateChar();
Byte curByte = _matchFinder.GetIndexByte((Int32)(0 - _additionalOffset));
_literalEncoder.GetSubCoder((UInt32)(_nowPos64), _previousByte).Encode(_rangeEncoder, curByte);
await _literalEncoder.GetSubCoder((UInt32)(_nowPos64), _previousByte).EncodeAsync(_rangeEncoder, curByte);
_previousByte = curByte;
_additionalOffset--;
_nowPos64++;
@@ -1275,19 +1277,19 @@ namespace SharpCompress.Compressors.LZMA
if (_processingMode && _matchFinder.IsDataStarved)
{
_finished = false;
return;
return (inSize, outSize, finished);
}
if (_matchFinder.GetNumAvailableBytes() == 0)
{
Flush((UInt32)_nowPos64);
return;
await FlushAsync((UInt32)_nowPos64);
return (inSize, outSize, finished);
}
while (true)
{
if (_processingMode && _matchFinder.IsDataStarved)
{
_finished = false;
return;
return (inSize, outSize, finished);
}
UInt32 pos;
@@ -1297,51 +1299,51 @@ namespace SharpCompress.Compressors.LZMA
UInt32 complexState = (_state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState;
if (len == 1 && pos == 0xFFFFFFFF)
{
_isMatch[complexState].Encode(_rangeEncoder, 0);
await _isMatch[complexState].EncodeAsync(_rangeEncoder, 0);
Byte curByte = _matchFinder.GetIndexByte((Int32)(0 - _additionalOffset));
LiteralEncoder.Encoder2 subCoder = _literalEncoder.GetSubCoder((UInt32)_nowPos64, _previousByte);
if (!_state.IsCharState())
{
Byte matchByte =
_matchFinder.GetIndexByte((Int32)(0 - _repDistances[0] - 1 - _additionalOffset));
subCoder.EncodeMatched(_rangeEncoder, matchByte, curByte);
await subCoder.EncodeMatchedAsync(_rangeEncoder, matchByte, curByte);
}
else
{
subCoder.Encode(_rangeEncoder, curByte);
await subCoder.EncodeAsync(_rangeEncoder, curByte);
}
_previousByte = curByte;
_state.UpdateChar();
}
else
{
_isMatch[complexState].Encode(_rangeEncoder, 1);
await _isMatch[complexState].EncodeAsync(_rangeEncoder, 1);
if (pos < Base.K_NUM_REP_DISTANCES)
{
_isRep[_state._index].Encode(_rangeEncoder, 1);
await _isRep[_state._index].EncodeAsync(_rangeEncoder, 1);
if (pos == 0)
{
_isRepG0[_state._index].Encode(_rangeEncoder, 0);
await _isRepG0[_state._index].EncodeAsync(_rangeEncoder, 0);
if (len == 1)
{
_isRep0Long[complexState].Encode(_rangeEncoder, 0);
await _isRep0Long[complexState].EncodeAsync(_rangeEncoder, 0);
}
else
{
_isRep0Long[complexState].Encode(_rangeEncoder, 1);
await _isRep0Long[complexState].EncodeAsync(_rangeEncoder, 1);
}
}
else
{
_isRepG0[_state._index].Encode(_rangeEncoder, 1);
await _isRepG0[_state._index].EncodeAsync(_rangeEncoder, 1);
if (pos == 1)
{
_isRepG1[_state._index].Encode(_rangeEncoder, 0);
await _isRepG1[_state._index].EncodeAsync(_rangeEncoder, 0);
}
else
{
_isRepG1[_state._index].Encode(_rangeEncoder, 1);
_isRepG2[_state._index].Encode(_rangeEncoder, pos - 2);
await _isRepG1[_state._index].EncodeAsync(_rangeEncoder, 1);
await _isRepG2[_state._index].EncodeAsync(_rangeEncoder, pos - 2);
}
}
if (len == 1)
@@ -1350,7 +1352,7 @@ namespace SharpCompress.Compressors.LZMA
}
else
{
_repMatchLenEncoder.Encode(_rangeEncoder, len - Base.K_MATCH_MIN_LEN, posState);
await _repMatchLenEncoder.EncodeAsync(_rangeEncoder, len - Base.K_MATCH_MIN_LEN, posState);
_state.UpdateRep();
}
UInt32 distance = _repDistances[pos];
@@ -1365,13 +1367,13 @@ namespace SharpCompress.Compressors.LZMA
}
else
{
_isRep[_state._index].Encode(_rangeEncoder, 0);
await _isRep[_state._index].EncodeAsync(_rangeEncoder, 0);
_state.UpdateMatch();
_lenEncoder.Encode(_rangeEncoder, len - Base.K_MATCH_MIN_LEN, posState);
await _lenEncoder.EncodeAsync(_rangeEncoder, len - Base.K_MATCH_MIN_LEN, posState);
pos -= Base.K_NUM_REP_DISTANCES;
UInt32 posSlot = GetPosSlot(pos);
UInt32 lenToPosState = Base.GetLenToPosState(len);
_posSlotEncoder[lenToPosState].Encode(_rangeEncoder, posSlot);
await _posSlotEncoder[lenToPosState].EncodeAsync(_rangeEncoder, posSlot);
if (posSlot >= Base.K_START_POS_MODEL_INDEX)
{
@@ -1381,15 +1383,15 @@ namespace SharpCompress.Compressors.LZMA
if (posSlot < Base.K_END_POS_MODEL_INDEX)
{
BitTreeEncoder.ReverseEncode(_posEncoders,
baseVal - posSlot - 1, _rangeEncoder, footerBits,
posReduced);
await BitTreeEncoder.ReverseEncodeAsync(_posEncoders,
baseVal - posSlot - 1, _rangeEncoder, footerBits,
posReduced);
}
else
{
_rangeEncoder.EncodeDirectBits(posReduced >> Base.K_NUM_ALIGN_BITS,
await _rangeEncoder.EncodeDirectBits(posReduced >> Base.K_NUM_ALIGN_BITS,
footerBits - Base.K_NUM_ALIGN_BITS);
_posAlignEncoder.ReverseEncode(_rangeEncoder, posReduced & Base.K_ALIGN_MASK);
await _posAlignEncoder.ReverseEncodeAsync(_rangeEncoder, posReduced & Base.K_ALIGN_MASK);
_alignPriceCount++;
}
}
@@ -1421,19 +1423,19 @@ namespace SharpCompress.Compressors.LZMA
if (_processingMode && _matchFinder.IsDataStarved)
{
_finished = false;
return;
return (inSize, outSize, finished);
}
if (_matchFinder.GetNumAvailableBytes() == 0)
{
Flush((UInt32)_nowPos64);
return;
await FlushAsync((UInt32)_nowPos64);
return (inSize, outSize, finished);
}
if (_nowPos64 - progressPosValuePrev >= (1 << 12))
{
_finished = false;
finished = false;
return;
return (inSize, outSize, finished);
}
}
}
@@ -1488,8 +1490,8 @@ namespace SharpCompress.Compressors.LZMA
_nowPos64 = 0;
}
public void Code(Stream inStream, Stream outStream,
Int64 inSize, Int64 outSize, ICodeProgress progress)
public async ValueTask CodeAsync(Stream inStream, Stream outStream,
Int64 inSize, Int64 outSize, ICodeProgress progress, CancellationToken cancellationToken)
{
_needReleaseMfStream = false;
_processingMode = false;
@@ -1498,10 +1500,7 @@ namespace SharpCompress.Compressors.LZMA
SetStreams(inStream, outStream, inSize, outSize);
while (true)
{
Int64 processedInSize;
Int64 processedOutSize;
bool finished;
CodeOneBlock(out processedInSize, out processedOutSize, out finished);
var (processedInSize, processedOutSize, finished) = await CodeOneBlockAsync();
if (finished)
{
return;
@@ -1518,7 +1517,7 @@ namespace SharpCompress.Compressors.LZMA
}
}
public long Code(Stream inStream, bool final)
public async ValueTask<long> CodeAsync(Stream inStream, bool final)
{
_matchFinder.SetStream(inStream);
_processingMode = !final;
@@ -1526,10 +1525,7 @@ namespace SharpCompress.Compressors.LZMA
{
while (true)
{
Int64 processedInSize;
Int64 processedOutSize;
bool finished;
CodeOneBlock(out processedInSize, out processedOutSize, out finished);
var (processedInSize, processedOutSize, finished) = await CodeOneBlockAsync();
if (finished)
{
return processedInSize;
@@ -1649,7 +1645,7 @@ namespace SharpCompress.Compressors.LZMA
{
for (int m = 0; m < K_MATCH_FINDER_I_DS.Length; m++)
{
if (string.Equals(s, K_MATCH_FINDER_I_DS[m], StringComparison.OrdinalIgnoreCase))
if (s == K_MATCH_FINDER_I_DS[m])
{
return m;
}
@@ -1696,7 +1692,7 @@ namespace SharpCompress.Compressors.LZMA
throw new InvalidParamException();
}
EMatchFinderType matchFinderIndexPrev = _matchFinderType;
int m = FindMatchFinder(((string)prop));
int m = FindMatchFinder(((string)prop).ToUpper());
if (m < 0)
{
throw new InvalidParamException();

View File

@@ -1,21 +1,25 @@
#nullable disable
using System;
using System.Buffers;
using System.Buffers.Binary;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Compressors.LZMA.LZ;
using SharpCompress.IO;
namespace SharpCompress.Compressors.LZMA
{
public class LzmaStream : Stream
public class LzmaStream : AsyncStream
{
private readonly Stream _inputStream;
private readonly long _inputSize;
private readonly long _outputSize;
private Stream _inputStream;
private long _inputSize;
private long _outputSize;
private readonly int _dictionarySize;
private readonly OutWindow _outWindow = new OutWindow();
private readonly RangeCoder.Decoder _rangeDecoder = new RangeCoder.Decoder();
private int _dictionarySize;
private OutWindow _outWindow = new OutWindow();
private RangeCoder.Decoder _rangeDecoder = new RangeCoder.Decoder();
private Decoder _decoder;
private long _position;
@@ -25,70 +29,60 @@ namespace SharpCompress.Compressors.LZMA
private long _inputPosition;
// LZMA2
private readonly bool _isLzma2;
private bool _isLzma2;
private bool _uncompressedChunk;
private bool _needDictReset = true;
private bool _needProps = true;
private readonly Encoder _encoder;
private bool _isDisposed;
private LzmaStream() {}
public LzmaStream(byte[] properties, Stream inputStream)
: this(properties, inputStream, -1, -1, null, properties.Length < 5)
public static async ValueTask<LzmaStream> CreateAsync(byte[] properties, Stream inputStream, long inputSize = -1, long outputSize = -1,
Stream presetDictionary = null, bool? isLzma2 = null, CancellationToken cancellationToken = default)
{
}
var ls = new LzmaStream();
ls._inputStream = inputStream;
ls._inputSize = inputSize;
ls._outputSize = outputSize;
ls._isLzma2 = isLzma2 ?? properties.Length < 5;
public LzmaStream(byte[] properties, Stream inputStream, long inputSize)
: this(properties, inputStream, inputSize, -1, null, properties.Length < 5)
{
}
public LzmaStream(byte[] properties, Stream inputStream, long inputSize, long outputSize)
: this(properties, inputStream, inputSize, outputSize, null, properties.Length < 5)
{
}
public LzmaStream(byte[] properties, Stream inputStream, long inputSize, long outputSize,
Stream presetDictionary, bool isLzma2)
{
_inputStream = inputStream;
_inputSize = inputSize;
_outputSize = outputSize;
_isLzma2 = isLzma2;
if (!isLzma2)
if (!ls._isLzma2)
{
_dictionarySize = BinaryPrimitives.ReadInt32LittleEndian(properties.AsSpan(1));
_outWindow.Create(_dictionarySize);
ls._dictionarySize = BinaryPrimitives.ReadInt32LittleEndian(properties.AsSpan(1));
ls._outWindow.Create(ls._dictionarySize);
if (presetDictionary != null)
{
_outWindow.Train(presetDictionary);
ls._outWindow.Train(presetDictionary);
}
_rangeDecoder.Init(inputStream);
await ls._rangeDecoder.InitAsync(inputStream, cancellationToken);
_decoder = new Decoder();
_decoder.SetDecoderProperties(properties);
Properties = properties;
ls._decoder = new Decoder();
ls._decoder.SetDecoderProperties(properties);
ls.Properties = properties;
_availableBytes = outputSize < 0 ? long.MaxValue : outputSize;
_rangeDecoderLimit = inputSize;
ls._availableBytes = outputSize < 0 ? long.MaxValue : outputSize;
ls._rangeDecoderLimit = inputSize;
}
else
{
_dictionarySize = 2 | (properties[0] & 1);
_dictionarySize <<= (properties[0] >> 1) + 11;
ls. _dictionarySize = 2 | (properties[0] & 1);
ls. _dictionarySize <<= (properties[0] >> 1) + 11;
_outWindow.Create(_dictionarySize);
ls._outWindow.Create(ls._dictionarySize);
if (presetDictionary != null)
{
_outWindow.Train(presetDictionary);
_needDictReset = false;
ls._outWindow.Train(presetDictionary);
ls._needDictReset = false;
}
Properties = new byte[1];
_availableBytes = 0;
ls. Properties = new byte[1];
ls._availableBytes = 0;
}
return ls;
}
public LzmaStream(LzmaEncoderProperties properties, bool isLzma2, Stream outputStream)
@@ -126,33 +120,25 @@ namespace SharpCompress.Compressors.LZMA
public override bool CanWrite => _encoder != null;
public override void Flush()
{
}
protected override void Dispose(bool disposing)
public override async ValueTask DisposeAsync()
{
if (_isDisposed)
{
return;
}
_isDisposed = true;
if (disposing)
if (_encoder != null)
{
if (_encoder != null)
{
_position = _encoder.Code(null, true);
}
_inputStream?.Dispose();
_position = await _encoder.CodeAsync(null, true);
}
base.Dispose(disposing);
_inputStream?.DisposeAsync();
}
public override long Length => _position + _availableBytes;
public override long Position { get => _position; set => throw new NotSupportedException(); }
public override int Read(byte[] buffer, int offset, int count)
public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
if (_endReached)
{
@@ -166,7 +152,7 @@ namespace SharpCompress.Compressors.LZMA
{
if (_isLzma2)
{
DecodeChunkHeader();
await DecodeChunkHeader(cancellationToken);
}
else
{
@@ -189,7 +175,7 @@ namespace SharpCompress.Compressors.LZMA
{
_inputPosition += _outWindow.CopyStream(_inputStream, toProcess);
}
else if (_decoder.Code(_dictionarySize, _outWindow, _rangeDecoder)
else if (await _decoder.CodeAsync(_dictionarySize, _outWindow, _rangeDecoder, cancellationToken)
&& _outputSize < 0)
{
_availableBytes = _outWindow.AvailableBytes;
@@ -231,7 +217,7 @@ namespace SharpCompress.Compressors.LZMA
return total;
}
private void DecodeChunkHeader()
private async ValueTask DecodeChunkHeader(CancellationToken cancellationToken)
{
int control = _inputStream.ReadByte();
_inputPosition++;
@@ -283,7 +269,7 @@ namespace SharpCompress.Compressors.LZMA
_decoder.SetDecoderProperties(Properties);
}
_rangeDecoder.Init(_inputStream);
await _rangeDecoder.InitAsync(_inputStream, cancellationToken);
}
else if (control > 0x02)
{
@@ -307,14 +293,25 @@ namespace SharpCompress.Compressors.LZMA
throw new NotSupportedException();
}
public override void Write(byte[] buffer, int offset, int count)
public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
if (_encoder != null)
{
_position = _encoder.Code(new MemoryStream(buffer, offset, count), false);
_position = await _encoder.CodeAsync(new MemoryStream(buffer, offset, count), false);
}
}
public byte[] Properties { get; } = new byte[5];
public override async ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = new CancellationToken())
{
if (_encoder != null)
{
var m = ArrayPool<byte>.Shared.Rent(buffer.Length);
buffer.CopyTo(m.AsMemory().Slice(0, buffer.Length));
_position = await _encoder.CodeAsync(new MemoryStream(m, 0, buffer.Length), false);
ArrayPool<byte>.Shared.Return(m);
}
}
public byte[] Properties { get; private set; }
}
}

View File

@@ -1,11 +1,14 @@
#nullable disable
using System;
using System.Buffers;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace SharpCompress.Compressors.LZMA.RangeCoder
{
internal class Encoder
internal class Encoder : IAsyncDisposable
{
public const uint K_TOP_VALUE = (1 << 24);
@@ -38,43 +41,46 @@ namespace SharpCompress.Compressors.LZMA.RangeCoder
_cache = 0;
}
public void FlushData()
public async ValueTask FlushData()
{
for (int i = 0; i < 5; i++)
{
ShiftLow();
await ShiftLowAsync();
}
}
public void FlushStream()
public Task FlushAsync()
{
_stream.Flush();
return _stream.FlushAsync();
}
public void CloseStream()
public ValueTask DisposeAsync()
{
_stream.Dispose();
return _stream.DisposeAsync();
}
public void Encode(uint start, uint size, uint total)
public async ValueTask EncodeAsync(uint start, uint size, uint total)
{
_low += start * (_range /= total);
_range *= size;
while (_range < K_TOP_VALUE)
{
_range <<= 8;
ShiftLow();
await ShiftLowAsync();
}
}
public void ShiftLow()
public async ValueTask ShiftLowAsync()
{
if ((uint)_low < 0xFF000000 || (uint)(_low >> 32) == 1)
{
using var buffer = MemoryPool<byte>.Shared.Rent(1);
var b = buffer.Memory.Slice(0,1);
byte temp = _cache;
do
{
_stream.WriteByte((byte)(temp + (_low >> 32)));
b.Span[0] = (byte)(temp + (_low >> 32));
await _stream.WriteAsync(b);
temp = 0xFF;
}
while (--_cacheSize != 0);
@@ -84,7 +90,7 @@ namespace SharpCompress.Compressors.LZMA.RangeCoder
_low = ((uint)_low) << 8;
}
public void EncodeDirectBits(uint v, int numTotalBits)
public async ValueTask EncodeDirectBits(uint v, int numTotalBits)
{
for (int i = numTotalBits - 1; i >= 0; i--)
{
@@ -96,12 +102,12 @@ namespace SharpCompress.Compressors.LZMA.RangeCoder
if (_range < K_TOP_VALUE)
{
_range <<= 8;
ShiftLow();
await ShiftLowAsync();
}
}
}
public void EncodeBit(uint size0, int numTotalBits, uint symbol)
public async ValueTask EncodeBitAsync(uint size0, int numTotalBits, uint symbol)
{
uint newBound = (_range >> numTotalBits) * size0;
if (symbol == 0)
@@ -116,7 +122,7 @@ namespace SharpCompress.Compressors.LZMA.RangeCoder
while (_range < K_TOP_VALUE)
{
_range <<= 8;
ShiftLow();
await ShiftLowAsync();
}
}
@@ -129,7 +135,7 @@ namespace SharpCompress.Compressors.LZMA.RangeCoder
}
}
internal class Decoder
internal class Decoder: IAsyncDisposable
{
public const uint K_TOP_VALUE = (1 << 24);
public uint _range;
@@ -139,7 +145,7 @@ namespace SharpCompress.Compressors.LZMA.RangeCoder
public Stream _stream;
public long _total;
public void Init(Stream stream)
public async ValueTask InitAsync(Stream stream, CancellationToken cancellationToken)
{
// Stream.Init(stream);
_stream = stream;
@@ -148,7 +154,7 @@ namespace SharpCompress.Compressors.LZMA.RangeCoder
_range = 0xFFFFFFFF;
for (int i = 0; i < 5; i++)
{
_code = (_code << 8) | (byte)_stream.ReadByte();
_code = (_code << 8) | await _stream.ReadByteAsync(cancellationToken);
}
_total = 5;
}
@@ -159,44 +165,34 @@ namespace SharpCompress.Compressors.LZMA.RangeCoder
_stream = null;
}
public void CloseStream()
public ValueTask DisposeAsync()
{
_stream.Dispose();
return _stream.DisposeAsync();
}
public void Normalize()
public async ValueTask NormalizeAsync(CancellationToken cancellationToken)
{
while (_range < K_TOP_VALUE)
{
_code = (_code << 8) | (byte)_stream.ReadByte();
_code = (_code << 8) | await _stream.ReadByteAsync(cancellationToken);
_range <<= 8;
_total++;
}
}
public void Normalize2()
{
if (_range < K_TOP_VALUE)
{
_code = (_code << 8) | (byte)_stream.ReadByte();
_range <<= 8;
_total++;
}
}
public uint GetThreshold(uint total)
{
return _code / (_range /= total);
}
public void Decode(uint start, uint size)
public async ValueTask DecodeAsync(uint start, uint size, CancellationToken cancellationToken)
{
_code -= start * _range;
_range *= size;
Normalize();
await NormalizeAsync(cancellationToken);
}
public uint DecodeDirectBits(int numTotalBits)
public async ValueTask<uint> DecodeDirectBitsAsync(int numTotalBits, CancellationToken cancellationToken)
{
uint range = _range;
uint code = _code;
@@ -218,7 +214,7 @@ namespace SharpCompress.Compressors.LZMA.RangeCoder
if (range < K_TOP_VALUE)
{
code = (code << 8) | (byte)_stream.ReadByte();
code = (code << 8) | await _stream.ReadByteAsync(cancellationToken);
range <<= 8;
_total++;
}
@@ -228,7 +224,7 @@ namespace SharpCompress.Compressors.LZMA.RangeCoder
return result;
}
public uint DecodeBit(uint size0, int numTotalBits)
public async ValueTask<uint> DecodeBitAsync(uint size0, int numTotalBits, CancellationToken cancellationToken)
{
uint newBound = (_range >> numTotalBits) * size0;
uint symbol;
@@ -243,7 +239,7 @@ namespace SharpCompress.Compressors.LZMA.RangeCoder
_code -= newBound;
_range -= newBound;
}
Normalize();
await NormalizeAsync(cancellationToken);
return symbol;
}

View File

@@ -1,4 +1,7 @@
using System;
using System.Buffers;
using System.Threading;
using System.Threading.Tasks;
namespace SharpCompress.Compressors.LZMA.RangeCoder
{
@@ -29,7 +32,7 @@ namespace SharpCompress.Compressors.LZMA.RangeCoder
}
}
public void Encode(Encoder encoder, uint symbol)
public async ValueTask EncodeAsync(Encoder encoder, uint symbol)
{
// encoder.EncodeBit(Prob, kNumBitModelTotalBits, symbol);
// UpdateModel(symbol);
@@ -48,7 +51,7 @@ namespace SharpCompress.Compressors.LZMA.RangeCoder
if (encoder._range < Encoder.K_TOP_VALUE)
{
encoder._range <<= 8;
encoder.ShiftLow();
await encoder.ShiftLowAsync();
}
}
@@ -110,7 +113,7 @@ namespace SharpCompress.Compressors.LZMA.RangeCoder
_prob = K_BIT_MODEL_TOTAL >> 1;
}
public uint Decode(Decoder rangeDecoder)
public async ValueTask<uint> DecodeAsync(Decoder rangeDecoder, CancellationToken cancellationToken)
{
uint newBound = (rangeDecoder._range >> K_NUM_BIT_MODEL_TOTAL_BITS) * _prob;
if (rangeDecoder._code < newBound)
@@ -119,7 +122,7 @@ namespace SharpCompress.Compressors.LZMA.RangeCoder
_prob += (K_BIT_MODEL_TOTAL - _prob) >> K_NUM_MOVE_BITS;
if (rangeDecoder._range < Decoder.K_TOP_VALUE)
{
rangeDecoder._code = (rangeDecoder._code << 8) | (byte)rangeDecoder._stream.ReadByte();
rangeDecoder._code = (rangeDecoder._code << 8) | await rangeDecoder._stream.ReadByteAsync(cancellationToken);
rangeDecoder._range <<= 8;
rangeDecoder._total++;
}
@@ -130,7 +133,7 @@ namespace SharpCompress.Compressors.LZMA.RangeCoder
_prob -= (_prob) >> K_NUM_MOVE_BITS;
if (rangeDecoder._range < Decoder.K_TOP_VALUE)
{
rangeDecoder._code = (rangeDecoder._code << 8) | (byte)rangeDecoder._stream.ReadByte();
rangeDecoder._code = (rangeDecoder._code << 8) | await rangeDecoder._stream.ReadByteAsync(cancellationToken);
rangeDecoder._range <<= 8;
rangeDecoder._total++;
}

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