omnizip 0.3.9 → 0.3.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/TODO.refactor/00-overview.md +67 -0
- data/TODO.refactor/01-registry-base-class.md +81 -0
- data/TODO.refactor/02-error-hierarchy.md +57 -0
- data/TODO.refactor/03-autoload-entry-point.md +69 -0
- data/TODO.refactor/04-filter-base-consolidation.md +49 -0
- data/TODO.refactor/05-send-private-methods.md +64 -0
- data/TODO.refactor/06-instance-variable-access.md +69 -0
- data/TODO.refactor/07-respond-to-replacement.md +91 -0
- data/TODO.refactor/08-spec-doubles.md +27 -0
- data/TODO.refactor/09-cli-shared-module.md +35 -0
- data/TODO.refactor/10-format-detector-ocp.md +34 -0
- data/TODO.refactor/11-convenience-decoupling.md +38 -0
- data/TODO.refactor/12-thread-safety.md +8 -0
- data/TODO.refactor/13-lutaml-model-migration.md +82 -0
- data/TODO.refactor/14-add-missing-specs.md +25 -0
- data/lib/omnizip/algorithm.rb +0 -2
- data/lib/omnizip/algorithm_registry.rb +29 -57
- data/lib/omnizip/algorithms/lzma/dictionary.rb +4 -3
- data/lib/omnizip/algorithms/lzma/lzip_decoder.rb +2 -0
- data/lib/omnizip/algorithms/lzma/range_decoder.rb +1 -1
- data/lib/omnizip/algorithms/lzma/xz_utils_decoder.rb +6 -5
- data/lib/omnizip/algorithms/lzma2/encoder.rb +0 -2
- data/lib/omnizip/algorithms/lzma2/lzma2_chunk.rb +0 -2
- data/lib/omnizip/algorithms/lzma2/properties.rb +0 -2
- data/lib/omnizip/algorithms/ppmd7/model.rb +1 -1
- data/lib/omnizip/algorithms/ppmd8/context.rb +0 -2
- data/lib/omnizip/algorithms/ppmd8/model.rb +1 -1
- data/lib/omnizip/algorithms/ppmd8.rb +0 -2
- data/lib/omnizip/algorithms/ppmd_base.rb +0 -2
- data/lib/omnizip/algorithms/sevenzip_lzma2.rb +0 -5
- data/lib/omnizip/algorithms/xz_lzma2.rb +0 -5
- data/lib/omnizip/algorithms/zstandard/frame/header.rb +3 -5
- data/lib/omnizip/algorithms.rb +1 -0
- data/lib/omnizip/archive_handler.rb +75 -0
- data/lib/omnizip/archive_handlers/tar_handler.rb +51 -0
- data/lib/omnizip/archive_handlers/zip_handler.rb +73 -0
- data/lib/omnizip/archive_handlers.rb +11 -0
- data/lib/omnizip/checksum_registry.rb +23 -96
- data/lib/omnizip/cli/shared.rb +31 -0
- data/lib/omnizip/cli.rb +6 -43
- data/lib/omnizip/commands/archive_create_command.rb +0 -6
- data/lib/omnizip/commands/archive_extract_command.rb +0 -4
- data/lib/omnizip/commands/archive_list_command.rb +0 -4
- data/lib/omnizip/commands/archive_repair_command.rb +0 -2
- data/lib/omnizip/commands/archive_verify_command.rb +0 -2
- data/lib/omnizip/commands/compress_command.rb +0 -2
- data/lib/omnizip/commands/decompress_command.rb +0 -2
- data/lib/omnizip/commands/list_command.rb +0 -2
- data/lib/omnizip/commands/metadata_command.rb +0 -5
- data/lib/omnizip/commands/parity_create_command.rb +0 -2
- data/lib/omnizip/commands/parity_repair_command.rb +0 -2
- data/lib/omnizip/commands/parity_verify_command.rb +0 -2
- data/lib/omnizip/commands/profile_list_command.rb +0 -2
- data/lib/omnizip/commands/profile_show_command.rb +0 -2
- data/lib/omnizip/convenience.rb +111 -234
- data/lib/omnizip/converter/seven_zip_to_zip_strategy.rb +1 -1
- data/lib/omnizip/crypto/aes256/cipher.rb +6 -1
- data/lib/omnizip/error.rb +22 -18
- data/lib/omnizip/filter.rb +5 -3
- data/lib/omnizip/filter_registry.rb +74 -98
- data/lib/omnizip/filters/bcj2.rb +0 -2
- data/lib/omnizip/filters/bcj_x86.rb +0 -2
- data/lib/omnizip/filters/filter_base.rb +4 -70
- data/lib/omnizip/filters/registry.rb +0 -2
- data/lib/omnizip/format_detector.rb +21 -12
- data/lib/omnizip/format_registry.rb +69 -53
- data/lib/omnizip/formats/bzip2_file.rb +0 -2
- data/lib/omnizip/formats/cpio/entry.rb +0 -2
- data/lib/omnizip/formats/cpio/reader.rb +0 -1
- data/lib/omnizip/formats/cpio/writer.rb +0 -1
- data/lib/omnizip/formats/cpio.rb +0 -1
- data/lib/omnizip/formats/format_spec_loader.rb +1 -1
- data/lib/omnizip/formats/gzip.rb +0 -2
- data/lib/omnizip/formats/iso/directory_record.rb +6 -3
- data/lib/omnizip/formats/iso/reader.rb +2 -2
- data/lib/omnizip/formats/iso.rb +0 -1
- data/lib/omnizip/formats/lzip.rb +5 -25
- data/lib/omnizip/formats/lzma_alone.rb +7 -25
- data/lib/omnizip/formats/msi/reader.rb +1 -0
- data/lib/omnizip/formats/msi/string_pool.rb +2 -3
- data/lib/omnizip/formats/msi.rb +1 -4
- data/lib/omnizip/formats/ole/allocation_table.rb +0 -1
- data/lib/omnizip/formats/ole/dirent.rb +9 -4
- data/lib/omnizip/formats/ole/header.rb +0 -1
- data/lib/omnizip/formats/ole/ranges_io.rb +1 -1
- data/lib/omnizip/formats/ole/storage.rb +5 -6
- data/lib/omnizip/formats/ole.rb +0 -1
- data/lib/omnizip/formats/rar/block_parser.rb +0 -1
- data/lib/omnizip/formats/rar/compression/ppmd/encoder.rb +1 -1
- data/lib/omnizip/formats/rar/decompressor.rb +1 -0
- data/lib/omnizip/formats/rar/header.rb +0 -1
- data/lib/omnizip/formats/rar/rar5/writer.rb +0 -3
- data/lib/omnizip/formats/rar/reader.rb +2 -3
- data/lib/omnizip/formats/rar/writer.rb +0 -1
- data/lib/omnizip/formats/rar.rb +0 -1
- data/lib/omnizip/formats/rpm/entry.rb +0 -1
- data/lib/omnizip/formats/rpm/header.rb +1 -6
- data/lib/omnizip/formats/rpm/lead.rb +37 -26
- data/lib/omnizip/formats/rpm/tag.rb +0 -1
- data/lib/omnizip/formats/rpm/writer.rb +0 -1
- data/lib/omnizip/formats/seven_zip/bcj2_stream_decompressor.rb +0 -1
- data/lib/omnizip/formats/seven_zip/coder_chain.rb +1 -2
- data/lib/omnizip/formats/seven_zip/encoded_header.rb +0 -1
- data/lib/omnizip/formats/seven_zip/encrypted_header.rb +0 -1
- data/lib/omnizip/formats/seven_zip/header.rb +0 -1
- data/lib/omnizip/formats/seven_zip/header_encryptor.rb +0 -1
- data/lib/omnizip/formats/seven_zip/header_writer.rb +0 -1
- data/lib/omnizip/formats/seven_zip/models/file_entry.rb +1 -1
- data/lib/omnizip/formats/seven_zip/parser.rb +1 -2
- data/lib/omnizip/formats/seven_zip/reader.rb +13 -2
- data/lib/omnizip/formats/seven_zip/split_archive_reader.rb +0 -1
- data/lib/omnizip/formats/seven_zip/split_archive_writer.rb +0 -1
- data/lib/omnizip/formats/seven_zip/writer.rb +4 -6
- data/lib/omnizip/formats/seven_zip.rb +2 -4
- data/lib/omnizip/formats/tar/entry.rb +0 -2
- data/lib/omnizip/formats/tar/header.rb +0 -2
- data/lib/omnizip/formats/tar/reader.rb +0 -2
- data/lib/omnizip/formats/tar/writer.rb +0 -2
- data/lib/omnizip/formats/tar.rb +0 -1
- data/lib/omnizip/formats/xar/entry.rb +0 -1
- data/lib/omnizip/formats/xar/header.rb +0 -1
- data/lib/omnizip/formats/xar/reader.rb +0 -1
- data/lib/omnizip/formats/xar/toc.rb +1 -2
- data/lib/omnizip/formats/xar/writer.rb +11 -3
- data/lib/omnizip/formats/xar.rb +0 -1
- data/lib/omnizip/formats/xz/reader.rb +7 -3
- data/lib/omnizip/formats/xz.rb +3 -40
- data/lib/omnizip/formats/xz_impl/block_decoder.rb +4 -10
- data/lib/omnizip/formats/xz_impl/block_encoder.rb +0 -2
- data/lib/omnizip/formats/xz_impl/block_header_parser.rb +0 -2
- data/lib/omnizip/formats/xz_impl/index_decoder.rb +0 -1
- data/lib/omnizip/formats/xz_impl/index_encoder.rb +0 -1
- data/lib/omnizip/formats/xz_impl/stream_encoder.rb +1 -1
- data/lib/omnizip/formats/xz_impl/stream_footer.rb +0 -1
- data/lib/omnizip/formats/xz_impl/stream_footer_parser.rb +0 -1
- data/lib/omnizip/formats/xz_impl/stream_header.rb +0 -1
- data/lib/omnizip/formats/xz_impl/stream_header_parser.rb +0 -2
- data/lib/omnizip/formats/zip/central_directory_header.rb +2 -3
- data/lib/omnizip/formats/zip/end_of_central_directory.rb +0 -2
- data/lib/omnizip/formats/zip/local_file_header.rb +0 -2
- data/lib/omnizip/formats/zip/reader.rb +12 -2
- data/lib/omnizip/formats/zip/writer.rb +73 -1
- data/lib/omnizip/formats/zip/zip64_end_of_central_directory.rb +0 -2
- data/lib/omnizip/formats/zip/zip64_end_of_central_directory_locator.rb +0 -2
- data/lib/omnizip/formats/zip/zip64_extra_field.rb +0 -2
- data/lib/omnizip/formats/zip.rb +0 -1
- data/lib/omnizip/formats.rb +8 -24
- data/lib/omnizip/implementations/seven_zip/lzma/range_decoder.rb +2 -2
- data/lib/omnizip/implementations/seven_zip/lzma/range_encoder.rb +1 -1
- data/lib/omnizip/implementations/seven_zip/lzma2/encoder.rb +0 -6
- data/lib/omnizip/implementations/xz_utils/lzma2/decoder.rb +3 -4
- data/lib/omnizip/implementations/xz_utils/lzma2/encoder.rb +0 -1
- data/lib/omnizip/io/source.rb +136 -0
- data/lib/omnizip/io.rb +2 -0
- data/lib/omnizip/link_handler.rb +7 -3
- data/lib/omnizip/metadata/metadata_editor.rb +1 -1
- data/lib/omnizip/models/algorithm_metadata.rb +33 -60
- data/lib/omnizip/models/compression_options.rb +40 -53
- data/lib/omnizip/models/conversion_options.rb +9 -18
- data/lib/omnizip/models/parallel_options.rb +25 -0
- data/lib/omnizip/optimization_registry.rb +8 -44
- data/lib/omnizip/parallel/job_scheduler.rb +26 -8
- data/lib/omnizip/parallel/parallel_compressor.rb +10 -20
- data/lib/omnizip/parallel/parallel_extractor.rb +20 -11
- data/lib/omnizip/parity/par2_repairer.rb +5 -5
- data/lib/omnizip/parity/par2_verifier.rb +27 -22
- data/lib/omnizip/parity.rb +4 -4
- data/lib/omnizip/password/encryption_registry.rb +11 -50
- data/lib/omnizip/password/zip_crypto_strategy.rb +1 -4
- data/lib/omnizip/pipe/stream_decompressor.rb +2 -3
- data/lib/omnizip/profile/archive_profile.rb +0 -2
- data/lib/omnizip/profile/binary_profile.rb +0 -2
- data/lib/omnizip/profile/compression_profile.rb +0 -1
- data/lib/omnizip/profile/profile_detector.rb +0 -2
- data/lib/omnizip/profile/text_profile.rb +0 -2
- data/lib/omnizip/profile.rb +0 -2
- data/lib/omnizip/registry.rb +112 -0
- data/lib/omnizip/version.rb +1 -1
- data/lib/omnizip/zip/file.rb +20 -9
- data/lib/omnizip.rb +26 -58
- metadata +24 -2
|
@@ -1,133 +1,109 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
#
|
|
4
|
-
# Copyright (C) 2024 Ribose Inc.
|
|
5
|
-
#
|
|
6
|
-
# This file is part of Omnizip.
|
|
7
|
-
#
|
|
8
|
-
# Omnizip is a pure Ruby port of 7-Zip compression algorithms.
|
|
9
|
-
# Based on the 7-Zip LZMA SDK by Igor Pavlov.
|
|
10
|
-
#
|
|
11
|
-
# This library is free software; you can redistribute it and/or
|
|
12
|
-
# modify it under the terms of the GNU Lesser General Public
|
|
13
|
-
# License as published by the Free Software Foundation; either
|
|
14
|
-
# version 2.1 of the License, or (at your option) any later version.
|
|
15
|
-
#
|
|
16
|
-
# See the COPYING file for the complete text of the license.
|
|
17
|
-
#
|
|
18
|
-
|
|
19
3
|
module Omnizip
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
4
|
+
class FilterRegistry < Omnizip::Registry
|
|
5
|
+
DEFAULT_FORMATS = %i[xz seven_zip].freeze
|
|
6
|
+
|
|
7
|
+
BUILTIN_FILTERS = {
|
|
8
|
+
"bcj-x86": "Omnizip::Filters::BcjX86",
|
|
9
|
+
"bcj-arm": "Omnizip::Filters::BcjArm",
|
|
10
|
+
"bcj-arm64": "Omnizip::Filters::BcjArm64",
|
|
11
|
+
"bcj-ia64": "Omnizip::Filters::BcjIa64",
|
|
12
|
+
"bcj-ppc": "Omnizip::Filters::BcjPpc",
|
|
13
|
+
"bcj-sparc": "Omnizip::Filters::BcjSparc",
|
|
14
|
+
bcj: "Omnizip::Filters::BCJ",
|
|
15
|
+
bcj2: "Omnizip::Filters::Bcj2",
|
|
16
|
+
delta: "Omnizip::Filters::Delta",
|
|
17
|
+
}.freeze
|
|
27
18
|
|
|
28
19
|
class << self
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
20
|
+
def not_found_error_class
|
|
21
|
+
Omnizip::UnknownFilterError
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def label
|
|
25
|
+
"Filter"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def register(name, filter_class, formats: DEFAULT_FORMATS)
|
|
37
29
|
raise ArgumentError, "Filter name cannot be nil" if name.nil?
|
|
38
30
|
raise ArgumentError, "Filter class cannot be nil" if filter_class.nil?
|
|
39
31
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
32
|
+
synchronize do
|
|
33
|
+
storage[normalize_key(name)] = {
|
|
34
|
+
class: filter_class,
|
|
35
|
+
formats: formats,
|
|
36
|
+
}
|
|
37
|
+
end
|
|
38
|
+
filter_class
|
|
44
39
|
end
|
|
45
40
|
alias register_with_formats register
|
|
46
41
|
|
|
47
|
-
# Retrieve a filter class by name.
|
|
48
|
-
#
|
|
49
|
-
# @param name [Symbol, String] The name identifier for the filter
|
|
50
|
-
# @raise [UnknownFilterError] If filter is not registered
|
|
51
|
-
# @return [Class] The registered filter class
|
|
52
42
|
def get(name)
|
|
53
|
-
|
|
54
|
-
unless
|
|
55
|
-
raise
|
|
56
|
-
"
|
|
43
|
+
entry = entry_for(name)
|
|
44
|
+
unless entry
|
|
45
|
+
raise not_found_error_class,
|
|
46
|
+
"#{label} not registered: #{name.inspect}. " \
|
|
57
47
|
"Available: #{available.join(', ')}"
|
|
58
48
|
end
|
|
59
49
|
|
|
60
|
-
|
|
50
|
+
entry[:class]
|
|
61
51
|
end
|
|
62
52
|
|
|
63
|
-
# Check if a filter is registered.
|
|
64
|
-
#
|
|
65
|
-
# @param name [Symbol, String] The name identifier for the filter
|
|
66
|
-
# @return [Boolean] True if filter is registered, false otherwise
|
|
67
|
-
def registered?(name)
|
|
68
|
-
@filters.key?(name.to_sym)
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
# Get list of all registered filter names.
|
|
72
|
-
#
|
|
73
|
-
# @return [Array<Symbol>] Array of registered filter names
|
|
74
|
-
def available
|
|
75
|
-
@filters.keys
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
# Reset the registry (primarily for testing).
|
|
79
|
-
#
|
|
80
|
-
# @return [void]
|
|
81
|
-
def reset!
|
|
82
|
-
@filters.clear
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
# Get filter instance for specific format.
|
|
86
|
-
#
|
|
87
|
-
# Returns a new filter instance after verifying the filter supports
|
|
88
|
-
# the specified format.
|
|
89
|
-
#
|
|
90
|
-
# @param name [Symbol, String] The name identifier for the filter
|
|
91
|
-
# @param format [Symbol] Format identifier (:xz, :seven_zip)
|
|
92
|
-
# @raise [KeyError] If filter is not registered
|
|
93
|
-
# @raise [ArgumentError] If filter doesn't support the format
|
|
94
|
-
# @return [Object] New filter instance
|
|
95
53
|
def get_for_format(name, format)
|
|
96
|
-
|
|
97
|
-
raise KeyError, "Filter not found: #{name}" unless
|
|
54
|
+
entry = entry_for(name)
|
|
55
|
+
raise KeyError, "Filter not found: #{name}" unless entry
|
|
98
56
|
|
|
99
|
-
unless
|
|
57
|
+
unless entry[:formats].include?(format)
|
|
100
58
|
raise ArgumentError,
|
|
101
59
|
"Filter #{name} not supported for format #{format}"
|
|
102
60
|
end
|
|
103
61
|
|
|
104
|
-
|
|
62
|
+
entry[:class].new
|
|
105
63
|
end
|
|
106
64
|
|
|
107
|
-
# Check if filter supports specific format.
|
|
108
|
-
#
|
|
109
|
-
# @param name [Symbol, String] The name identifier for the filter
|
|
110
|
-
# @param format [Symbol] Format identifier
|
|
111
|
-
# @return [Boolean] True if filter supports the format
|
|
112
65
|
def supports_format?(name, format)
|
|
113
|
-
|
|
66
|
+
entry = entry_for(name)
|
|
67
|
+
return false unless entry
|
|
114
68
|
|
|
115
|
-
|
|
116
|
-
filter_info[:formats]&.include?(format)
|
|
69
|
+
entry[:formats]&.include?(format)
|
|
117
70
|
end
|
|
118
71
|
|
|
119
|
-
# Get all filters supporting a specific format.
|
|
120
|
-
#
|
|
121
|
-
# @param format [Symbol] Format identifier
|
|
122
|
-
# @return [Array<Symbol>] Filter names supporting the format
|
|
123
72
|
def filters_for_format(format)
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
73
|
+
# Ensure all lazy triggers have fired so the format filter list
|
|
74
|
+
# reflects every registered builtin.
|
|
75
|
+
lazy_triggers.keys.dup.each { |name| get(name) }
|
|
76
|
+
|
|
77
|
+
storage.select { |_, info| info[:formats]&.include?(format) }.keys
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
private
|
|
81
|
+
|
|
82
|
+
def entry_for(name)
|
|
83
|
+
normalized = normalize_key(name)
|
|
84
|
+
entry = storage[normalized]
|
|
85
|
+
return entry if entry
|
|
86
|
+
|
|
87
|
+
trigger = lazy_triggers[normalized]
|
|
88
|
+
if trigger
|
|
89
|
+
synchronize { lazy_triggers.delete(normalized) }
|
|
90
|
+
trigger.call
|
|
91
|
+
entry = storage[normalized]
|
|
92
|
+
return entry if entry
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
nil
|
|
127
96
|
end
|
|
128
97
|
end
|
|
129
98
|
end
|
|
99
|
+
end
|
|
130
100
|
|
|
131
|
-
|
|
132
|
-
|
|
101
|
+
# Lazy triggers — calling the Filters::Registry.register_all re-runs
|
|
102
|
+
# the registration of every builtin filter. Idempotent: register just
|
|
103
|
+
# overwrites the storage entry. Survives reset! because the trigger
|
|
104
|
+
# proc is held by FilterRegistry, not the storage.
|
|
105
|
+
Omnizip::FilterRegistry::BUILTIN_FILTERS.each_key do |name|
|
|
106
|
+
Omnizip::FilterRegistry.register_lazy(name) do
|
|
107
|
+
Omnizip::Filters::Registry.register_all
|
|
108
|
+
end
|
|
133
109
|
end
|
data/lib/omnizip/filters/bcj2.rb
CHANGED
|
@@ -1,72 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
|
|
7
|
-
#
|
|
8
|
-
# Omnizip is a pure Ruby port of 7-Zip compression algorithms.
|
|
9
|
-
# Based on the 7-Zip LZMA SDK by Igor Pavlov.
|
|
10
|
-
#
|
|
11
|
-
# This library is free software; you can redistribute it and/or
|
|
12
|
-
# modify it under the terms of the GNU Lesser General Public
|
|
13
|
-
# License as published by the Free Software Foundation; either
|
|
14
|
-
# version 2.1 of the License, or (at your option) any later version.
|
|
15
|
-
#
|
|
16
|
-
# See the COPYING file for the complete text of the license.
|
|
17
|
-
#
|
|
18
|
-
|
|
19
|
-
module Omnizip
|
|
20
|
-
module Filters
|
|
21
|
-
# Abstract base class for preprocessing filters.
|
|
22
|
-
#
|
|
23
|
-
# Filters are reversible transformations applied to data before
|
|
24
|
-
# compression to improve compression ratios. They are particularly
|
|
25
|
-
# effective for executable files and other structured data.
|
|
26
|
-
#
|
|
27
|
-
# All filter implementations should inherit from this class and
|
|
28
|
-
# implement the required methods.
|
|
29
|
-
class FilterBase
|
|
30
|
-
# Encode (preprocess) data for compression.
|
|
31
|
-
#
|
|
32
|
-
# This method transforms data to make it more compressible. The
|
|
33
|
-
# transformation must be reversible - decode(encode(data)) == data.
|
|
34
|
-
#
|
|
35
|
-
# @param data [String] Binary data to encode
|
|
36
|
-
# @param position [Integer] Current position in stream (for
|
|
37
|
-
# multi-block filtering)
|
|
38
|
-
# @raise [NotImplementedError] Must be implemented by subclass
|
|
39
|
-
# @return [String] Encoded binary data
|
|
40
|
-
def encode(data, position = 0)
|
|
41
|
-
raise NotImplementedError,
|
|
42
|
-
"#{self.class} must implement #encode"
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
# Decode (postprocess) data after decompression.
|
|
46
|
-
#
|
|
47
|
-
# This method reverses the encoding transformation, restoring
|
|
48
|
-
# original data.
|
|
49
|
-
#
|
|
50
|
-
# @param data [String] Binary data to decode
|
|
51
|
-
# @param position [Integer] Current position in stream (for
|
|
52
|
-
# multi-block filtering)
|
|
53
|
-
# @raise [NotImplementedError] Must be implemented by subclass
|
|
54
|
-
# @return [String] Decoded binary data
|
|
55
|
-
def decode(data, position = 0)
|
|
56
|
-
raise NotImplementedError,
|
|
57
|
-
"#{self.class} must implement #decode"
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
class << self
|
|
61
|
-
# Get metadata about this filter.
|
|
62
|
-
#
|
|
63
|
-
# @raise [NotImplementedError] Must be implemented by subclass
|
|
64
|
-
# @return [Hash] Filter metadata including name, description
|
|
65
|
-
def metadata
|
|
66
|
-
raise NotImplementedError,
|
|
67
|
-
"#{self} must implement .metadata"
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
-
end
|
|
72
|
-
end
|
|
3
|
+
# FilterBase is a historical alias for Omnizip::Filter. New code should
|
|
4
|
+
# inherit from Omnizip::Filter directly; FilterBase is kept for
|
|
5
|
+
# backward compatibility with existing subclasses (BcjX86, BcjArm, …).
|
|
6
|
+
Omnizip::Filters::FilterBase = Omnizip::Filter
|
|
@@ -104,23 +104,32 @@ module Omnizip
|
|
|
104
104
|
detect(file_path) == :rar4
|
|
105
105
|
end
|
|
106
106
|
|
|
107
|
+
# Mapping of detected format symbols to (extension, constant-path)
|
|
108
|
+
# tuples. The constant path is resolved lazily so autoload fires
|
|
109
|
+
# only when a format is actually requested.
|
|
110
|
+
READER_FOR_FORMAT = {
|
|
111
|
+
xz: ["omnizip/formats/xz", "Omnizip::Formats::Xz"],
|
|
112
|
+
seven_zip: ["omnizip/formats/seven_zip", "Omnizip::Formats::SevenZip::Reader"],
|
|
113
|
+
rar5: ["omnizip/formats/rar5", "Omnizip::Formats::Rar5::Reader"],
|
|
114
|
+
rar4: ["omnizip/formats/rar3", "Omnizip::Formats::Rar3::Reader"],
|
|
115
|
+
zip: ["omnizip/formats/zip", "Omnizip::Formats::Zip::Reader"],
|
|
116
|
+
gzip: ["omnizip/formats/gzip", "Omnizip::Formats::Gzip"],
|
|
117
|
+
bzip2: ["omnizip/formats/bzip2_file", "Omnizip::Formats::Bzip2File"],
|
|
118
|
+
}.freeze
|
|
119
|
+
|
|
107
120
|
# Get the appropriate reader class for the format
|
|
108
121
|
#
|
|
109
122
|
# @param file_path [String] Path to the archive file
|
|
110
123
|
# @return [Class, nil] Reader class or nil if unknown format
|
|
111
124
|
def self.reader_for(file_path)
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
Omnizip::Formats::Rar3::Reader
|
|
121
|
-
when :zip
|
|
122
|
-
Omnizip::Formats::Zip::Reader
|
|
123
|
-
end
|
|
125
|
+
format = detect(file_path)
|
|
126
|
+
return nil unless format
|
|
127
|
+
|
|
128
|
+
mapping = READER_FOR_FORMAT[format]
|
|
129
|
+
return nil unless mapping
|
|
130
|
+
|
|
131
|
+
_path, const_path = mapping
|
|
132
|
+
Object.const_get(const_path)
|
|
124
133
|
end
|
|
125
134
|
|
|
126
135
|
# Detect LZMA_Alone format
|
|
@@ -1,42 +1,71 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Omnizip
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
class FormatRegistry < Omnizip::Registry
|
|
5
|
+
BUILTIN_FORMATS = {
|
|
6
|
+
".7z" => "Omnizip::Formats::SevenZip::Reader",
|
|
7
|
+
".zip" => "Omnizip::Formats::Zip",
|
|
8
|
+
".rar" => "Omnizip::Formats::Rar::Reader",
|
|
9
|
+
".tar" => "Omnizip::Formats::Tar",
|
|
10
|
+
".gz" => "Omnizip::Formats::Gzip",
|
|
11
|
+
".gzip" => "Omnizip::Formats::Gzip",
|
|
12
|
+
".bz2" => "Omnizip::Formats::Bzip2File",
|
|
13
|
+
".bzip2" => "Omnizip::Formats::Bzip2File",
|
|
14
|
+
".xz" => "Omnizip::Formats::Xz",
|
|
15
|
+
".msi" => "Omnizip::Formats::Msi",
|
|
16
|
+
".msp" => "Omnizip::Formats::Msi",
|
|
17
|
+
".cpio" => "Omnizip::Formats::Cpio::Reader",
|
|
18
|
+
".iso" => "Omnizip::Formats::Iso::Reader",
|
|
19
|
+
".xar" => "Omnizip::Formats::Xar::Reader",
|
|
20
|
+
".lz" => "Omnizip::Formats::Lzip",
|
|
21
|
+
".lzip" => "Omnizip::Formats::Lzip",
|
|
22
|
+
".lzma" => "Omnizip::Formats::LzmaAlone",
|
|
23
|
+
".ole" => "Omnizip::Formats::Ole::Storage",
|
|
24
|
+
".doc" => "Omnizip::Formats::Ole::Storage",
|
|
25
|
+
".xls" => "Omnizip::Formats::Ole::Storage",
|
|
26
|
+
".ppt" => "Omnizip::Formats::Ole::Storage",
|
|
27
|
+
}.freeze
|
|
28
|
+
|
|
7
29
|
class << self
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def
|
|
13
|
-
|
|
30
|
+
def label
|
|
31
|
+
"Format"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def normalize_key(extension)
|
|
35
|
+
ext = extension.to_s
|
|
36
|
+
ext = ".#{ext}" unless ext.start_with?(".")
|
|
37
|
+
ext.downcase
|
|
14
38
|
end
|
|
15
39
|
|
|
16
|
-
# Get format handler for extension
|
|
17
|
-
#
|
|
18
|
-
# @param extension [String] File extension
|
|
19
|
-
# @return [Class, Module, nil] Handler class/module or nil if not found
|
|
20
40
|
def get(extension)
|
|
21
|
-
|
|
22
|
-
|
|
41
|
+
normalized = normalize_key(extension)
|
|
42
|
+
handler = storage[normalized]
|
|
43
|
+
return resolve_handler(handler, extension) if handler
|
|
23
44
|
|
|
24
|
-
|
|
25
|
-
|
|
45
|
+
trigger = lazy_triggers[normalized]
|
|
46
|
+
if trigger
|
|
47
|
+
synchronize { lazy_triggers.delete(normalized) }
|
|
48
|
+
trigger.call
|
|
49
|
+
handler = storage[normalized]
|
|
50
|
+
return resolve_handler(handler, extension) if handler
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
nil
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def supported?(extension)
|
|
57
|
+
registered?(extension) || lazy_triggers.key?(normalize_key(extension))
|
|
26
58
|
end
|
|
59
|
+
alias supported_formats available
|
|
27
60
|
|
|
28
|
-
# Resolve a string to a constant (class/module)
|
|
29
|
-
#
|
|
30
|
-
# @param name [String] Fully qualified class name
|
|
31
|
-
# @return [Class, Module, nil] Resolved constant or nil
|
|
32
61
|
def resolve_constant(name)
|
|
33
62
|
return nil unless name.is_a?(String)
|
|
34
63
|
|
|
35
|
-
|
|
36
|
-
|
|
64
|
+
parts = name.split("::")
|
|
65
|
+
parts.shift if parts.empty? || parts.first.empty?
|
|
37
66
|
|
|
38
67
|
constant = Object
|
|
39
|
-
|
|
68
|
+
parts.each do |n|
|
|
40
69
|
return nil unless constant.const_defined?(n, false)
|
|
41
70
|
|
|
42
71
|
constant = constant.const_get(n)
|
|
@@ -46,39 +75,26 @@ module Omnizip
|
|
|
46
75
|
nil
|
|
47
76
|
end
|
|
48
77
|
|
|
49
|
-
# Check if format is supported
|
|
50
|
-
#
|
|
51
|
-
# @param extension [String] File extension
|
|
52
|
-
# @return [Boolean] true if supported
|
|
53
|
-
def supported?(extension)
|
|
54
|
-
registry.key?(normalize_extension(extension))
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
# List all supported formats
|
|
58
|
-
#
|
|
59
|
-
# @return [Array<String>] Supported extensions
|
|
60
|
-
def supported_formats
|
|
61
|
-
registry.keys.sort
|
|
62
|
-
end
|
|
63
|
-
|
|
64
78
|
private
|
|
65
79
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
def registry
|
|
70
|
-
@registry ||= {}
|
|
71
|
-
end
|
|
80
|
+
def resolve_handler(handler, _original_extension)
|
|
81
|
+
return nil if handler.nil?
|
|
82
|
+
return handler if handler.is_a?(Class) || handler.is_a?(Module)
|
|
72
83
|
|
|
73
|
-
|
|
74
|
-
#
|
|
75
|
-
# @param ext [String] Extension
|
|
76
|
-
# @return [String] Normalized extension
|
|
77
|
-
def normalize_extension(ext)
|
|
78
|
-
ext = ext.to_s
|
|
79
|
-
ext = ".#{ext}" unless ext.start_with?(".")
|
|
80
|
-
ext.downcase
|
|
84
|
+
resolve_constant(handler)
|
|
81
85
|
end
|
|
82
86
|
end
|
|
83
87
|
end
|
|
84
88
|
end
|
|
89
|
+
|
|
90
|
+
# Lazy triggers — explicitly re-register on each miss so the entry
|
|
91
|
+
# survives reset!. Object.const_get alone doesn't re-run the file body
|
|
92
|
+
# once the constant is loaded.
|
|
93
|
+
Omnizip::FormatRegistry::BUILTIN_FORMATS.each do |ext, const_path|
|
|
94
|
+
Omnizip::FormatRegistry.register_lazy(ext) do
|
|
95
|
+
handler = Object.const_get(const_path)
|
|
96
|
+
# Register under the original extension; some formats (e.g. Rar)
|
|
97
|
+
# store a string class path that needs resolve_constant on get.
|
|
98
|
+
Omnizip::FormatRegistry.register(ext, handler)
|
|
99
|
+
end
|
|
100
|
+
end
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "stringio"
|
|
4
|
-
require "omnizip/algorithm_registry"
|
|
5
4
|
|
|
6
5
|
module Omnizip
|
|
7
6
|
module Formats
|
|
@@ -115,7 +114,6 @@ module Omnizip
|
|
|
115
114
|
|
|
116
115
|
# Register BZIP2 format when loaded
|
|
117
116
|
def register!
|
|
118
|
-
require "omnizip/format_registry"
|
|
119
117
|
FormatRegistry.register(".bz2", Omnizip::Formats::Bzip2File)
|
|
120
118
|
FormatRegistry.register(".bzip2", Omnizip::Formats::Bzip2File)
|
|
121
119
|
end
|
data/lib/omnizip/formats/cpio.rb
CHANGED
data/lib/omnizip/formats/gzip.rb
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
require "zlib"
|
|
4
4
|
require "stringio"
|
|
5
|
-
require "omnizip/algorithm_registry"
|
|
6
5
|
|
|
7
6
|
module Omnizip
|
|
8
7
|
module Formats
|
|
@@ -130,7 +129,6 @@ module Omnizip
|
|
|
130
129
|
|
|
131
130
|
# Register GZIP format when loaded
|
|
132
131
|
def register!
|
|
133
|
-
require "omnizip/format_registry"
|
|
134
132
|
FormatRegistry.register(".gz", Omnizip::Formats::Gzip)
|
|
135
133
|
FormatRegistry.register(".gzip", Omnizip::Formats::Gzip)
|
|
136
134
|
end
|
|
@@ -6,9 +6,12 @@ module Omnizip
|
|
|
6
6
|
# ISO 9660 Directory Record
|
|
7
7
|
# Represents a file or directory entry
|
|
8
8
|
class DirectoryRecord
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
attr_accessor :full_path
|
|
10
|
+
attr_reader :length, :extended_attr_length, :location, :data_length, :recording_date, :flags, :file_unit_size, :interleave_gap_size, :volume_sequence_number, :name, :system_use
|
|
11
|
+
|
|
12
|
+
# Set the full path for this record, used by Reader when walking
|
|
13
|
+
# a directory tree. Allows callers to look up records by path
|
|
14
|
+
# without re-joining parent names.
|
|
12
15
|
|
|
13
16
|
# Parse directory record from binary data
|
|
14
17
|
#
|
|
@@ -158,7 +158,7 @@ module Omnizip
|
|
|
158
158
|
unless record.current_directory? || record.parent_directory?
|
|
159
159
|
# Set full path
|
|
160
160
|
full_path = path_prefix.empty? ? record.name : "#{path_prefix}/#{record.name}"
|
|
161
|
-
record.
|
|
161
|
+
record.full_path = full_path
|
|
162
162
|
|
|
163
163
|
@entries << record
|
|
164
164
|
|
|
@@ -177,7 +177,7 @@ module Omnizip
|
|
|
177
177
|
def find_entry(path)
|
|
178
178
|
# Normalize path
|
|
179
179
|
path = path.gsub(%r{^/+}, "").gsub(%r{/+$}, "")
|
|
180
|
-
@entries.find { |e| e.
|
|
180
|
+
@entries.find { |e| e.full_path == path }
|
|
181
181
|
end
|
|
182
182
|
|
|
183
183
|
# Read file data
|