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
|
@@ -74,14 +74,14 @@ module Omnizip
|
|
|
74
74
|
# UPDATE_0: bit is 0
|
|
75
75
|
@range = bound & 0xFFFFFFFF
|
|
76
76
|
new_prob = prob + ((BIT_MODEL_TOTAL - prob) >> MOVE_BITS)
|
|
77
|
-
model.
|
|
77
|
+
model.probability = new_prob
|
|
78
78
|
0
|
|
79
79
|
else
|
|
80
80
|
# UPDATE_1: bit is 1
|
|
81
81
|
@range = (@range - bound) & 0xFFFFFFFF
|
|
82
82
|
@code = (@code - bound) & 0xFFFFFFFF
|
|
83
83
|
new_prob = prob - (prob >> MOVE_BITS)
|
|
84
|
-
model.
|
|
84
|
+
model.probability = new_prob
|
|
85
85
|
1
|
|
86
86
|
end
|
|
87
87
|
end
|
|
@@ -91,7 +91,7 @@ module Omnizip
|
|
|
91
91
|
# Update probability: ttt -= ttt >> kNumMoveBits
|
|
92
92
|
prob - (prob >> MOVE_BITS)
|
|
93
93
|
end
|
|
94
|
-
model.
|
|
94
|
+
model.probability = new_prob
|
|
95
95
|
|
|
96
96
|
# RC_NORM: Normalize AFTER encoding (key SDK difference!)
|
|
97
97
|
normalize
|
|
@@ -71,12 +71,6 @@ module Omnizip
|
|
|
71
71
|
|
|
72
72
|
# Initialize shared state across all chunks
|
|
73
73
|
# Using XZ Utils components (tested and working)
|
|
74
|
-
require "omnizip/algorithms/lzma/dictionary"
|
|
75
|
-
require "omnizip/algorithms/lzma/lzma_state"
|
|
76
|
-
require "omnizip/algorithms/lzma/xz_probability_models"
|
|
77
|
-
require "omnizip/algorithms/lzma/match_finder"
|
|
78
|
-
require "omnizip/algorithms/lzma/optimal_encoder"
|
|
79
|
-
require "omnizip/algorithms/lzma/xz_range_encoder_exact"
|
|
80
74
|
|
|
81
75
|
@dictionary = Omnizip::Algorithms::LZMA::Dictionary.new(dict_size)
|
|
82
76
|
@state = Omnizip::Algorithms::LZMA::LZMAState.new(0)
|
|
@@ -21,7 +21,6 @@
|
|
|
21
21
|
# DEALINGS IN THE SOFTWARE.
|
|
22
22
|
|
|
23
23
|
require "stringio"
|
|
24
|
-
require "omnizip/algorithms"
|
|
25
24
|
|
|
26
25
|
module Omnizip
|
|
27
26
|
module Implementations
|
|
@@ -418,10 +417,10 @@ module Omnizip
|
|
|
418
417
|
if @lzma2_debug
|
|
419
418
|
warn "DEBUG: decompress_lzma_chunk - After set_input, checking range_decoder..."
|
|
420
419
|
# Check if the decoder has a range_decoder variable
|
|
421
|
-
if @lzma_decoder.
|
|
422
|
-
range_decoder = @lzma_decoder.
|
|
420
|
+
if @lzma_decoder.respond_to?(:range_decoder)
|
|
421
|
+
range_decoder = @lzma_decoder.range_decoder
|
|
423
422
|
if range_decoder
|
|
424
|
-
warn " range_decoder exists: code=0x#{range_decoder.
|
|
423
|
+
warn " range_decoder exists: code=0x#{range_decoder.code.to_s(16)}, range=0x#{range_decoder.range.to_s(16)}, init_bytes_remaining=#{range_decoder.init_bytes_remaining}"
|
|
425
424
|
else
|
|
426
425
|
warn " range_decoder is nil"
|
|
427
426
|
end
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Omnizip
|
|
4
|
+
module IO
|
|
5
|
+
# Polymorphic adapter for "things we can read bytes from".
|
|
6
|
+
#
|
|
7
|
+
# Callers that previously did:
|
|
8
|
+
#
|
|
9
|
+
# data = input.respond_to?(:read) ? input.read : File.binread(input)
|
|
10
|
+
#
|
|
11
|
+
# should now do:
|
|
12
|
+
#
|
|
13
|
+
# data = Omnizip::IO::Source.for(input).read
|
|
14
|
+
#
|
|
15
|
+
# Adapters:
|
|
16
|
+
# - +IO+/+StringIO+/+Tempfile+ → wrapped as-is
|
|
17
|
+
# - +String+ → treated as a file path; read with +File.binread+
|
|
18
|
+
# unless the string contains no NUL bytes and does not exist on
|
|
19
|
+
# disk, in which case it is treated as literal data
|
|
20
|
+
# - +Object+ responding to +:read+ → delegated to
|
|
21
|
+
class Source
|
|
22
|
+
# Build a Source wrapper appropriate for +input+.
|
|
23
|
+
#
|
|
24
|
+
# @param input [IO, StringIO, String, #read] the read target
|
|
25
|
+
# @return [Source]
|
|
26
|
+
def self.for(input)
|
|
27
|
+
case input
|
|
28
|
+
when ::IO, ::StringIO, ::Tempfile then new(input)
|
|
29
|
+
when String then StringSource.new(input)
|
|
30
|
+
else
|
|
31
|
+
unless input.respond_to?(:read)
|
|
32
|
+
raise ArgumentError,
|
|
33
|
+
"Cannot adapt #{input.inspect} to Omnizip::IO::Source"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
new(input)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def initialize(io)
|
|
41
|
+
@io = io
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Delegate reading to the underlying IO-like object.
|
|
45
|
+
def read(length = nil, outbuf = nil)
|
|
46
|
+
if length.nil?
|
|
47
|
+
@io.read
|
|
48
|
+
elsif outbuf
|
|
49
|
+
@io.read(length, outbuf)
|
|
50
|
+
else
|
|
51
|
+
@io.read(length)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def close
|
|
56
|
+
@io.close if @io.respond_to?(:close)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# String input adapter. Treats the value as a file path if it
|
|
60
|
+
# exists on disk and contains no NUL bytes, otherwise as raw
|
|
61
|
+
# bytes.
|
|
62
|
+
class StringSource < Source
|
|
63
|
+
def initialize(value)
|
|
64
|
+
super(nil)
|
|
65
|
+
@value = value
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def read(_length = nil, _outbuf = nil)
|
|
69
|
+
if file_path?
|
|
70
|
+
File.binread(@value)
|
|
71
|
+
else
|
|
72
|
+
@value.b
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def close; end
|
|
77
|
+
|
|
78
|
+
private
|
|
79
|
+
|
|
80
|
+
def file_path?
|
|
81
|
+
!@value.include?("\0") && File.exist?(@value)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Polymorphic adapter for "things we can write bytes to".
|
|
87
|
+
#
|
|
88
|
+
# Adapters:
|
|
89
|
+
# - +IO+/+StringIO+/+Tempfile+ → wrapped as-is
|
|
90
|
+
# - +String+ → treated as a file path; writes via +File.binwrite+
|
|
91
|
+
# - +Object+ responding to +:write+ → delegated to
|
|
92
|
+
class Sink
|
|
93
|
+
def self.for(output)
|
|
94
|
+
case output
|
|
95
|
+
when ::IO, ::StringIO, ::Tempfile then new(output)
|
|
96
|
+
when String then PathSink.new(output)
|
|
97
|
+
else
|
|
98
|
+
unless output.respond_to?(:write)
|
|
99
|
+
raise ArgumentError,
|
|
100
|
+
"Cannot adapt #{output.inspect} to Omnizip::IO::Sink"
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
new(output)
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def initialize(io)
|
|
108
|
+
@io = io
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def write(data)
|
|
112
|
+
@io.write(data)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def close
|
|
116
|
+
@io.close if @io.respond_to?(:close)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# File-path sink: opens the file for binary write on #write and
|
|
120
|
+
# closes after.
|
|
121
|
+
class PathSink < Sink
|
|
122
|
+
def initialize(path)
|
|
123
|
+
super(nil)
|
|
124
|
+
@path = path
|
|
125
|
+
@file = nil
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def write(data)
|
|
129
|
+
File.binwrite(@path, data)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def close; end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
data/lib/omnizip/io.rb
CHANGED
|
@@ -9,5 +9,7 @@ module Omnizip
|
|
|
9
9
|
autoload :BufferedInput, "omnizip/io/buffered_input"
|
|
10
10
|
autoload :BufferedOutput, "omnizip/io/buffered_output"
|
|
11
11
|
autoload :StreamManager, "omnizip/io/stream_manager"
|
|
12
|
+
autoload :Source, "omnizip/io/source"
|
|
13
|
+
autoload :Sink, "omnizip/io/source"
|
|
12
14
|
end
|
|
13
15
|
end
|
data/lib/omnizip/link_handler.rb
CHANGED
|
@@ -11,14 +11,18 @@ module Omnizip
|
|
|
11
11
|
!windows_platform?
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
-
# Check if symbolic links are supported
|
|
14
|
+
# Check if symbolic links are supported on this platform.
|
|
15
|
+
# Delegates to Platform but also gates on LinkHandler#supported?
|
|
16
|
+
# (Windows is treated as unsupported here for historical
|
|
17
|
+
# compatibility — even if Windows Developer Mode would allow
|
|
18
|
+
# symlinks, callers should opt into that explicitly via Platform).
|
|
15
19
|
def symlink_supported?
|
|
16
|
-
|
|
20
|
+
supported? && Omnizip::Platform.supports_symlinks?
|
|
17
21
|
end
|
|
18
22
|
|
|
19
23
|
# Check if hard links are supported
|
|
20
24
|
def hardlink_supported?
|
|
21
|
-
|
|
25
|
+
supported? && Omnizip::Platform.supports_hardlinks?
|
|
22
26
|
end
|
|
23
27
|
|
|
24
28
|
# Detect if a path is a symbolic link
|
|
@@ -1,72 +1,45 @@
|
|
|
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
|
-
require "json"
|
|
3
|
+
require "lutaml/model"
|
|
20
4
|
|
|
21
5
|
module Omnizip
|
|
22
6
|
module Models
|
|
23
|
-
#
|
|
7
|
+
# Metadata describing a compression algorithm.
|
|
24
8
|
#
|
|
25
|
-
#
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
author: @author,
|
|
48
|
-
max_compression_level: @max_compression_level,
|
|
49
|
-
min_compression_level: @min_compression_level,
|
|
50
|
-
default_compression_level: @default_compression_level,
|
|
51
|
-
supports_streaming: @supports_streaming,
|
|
52
|
-
supports_multithreading: @supports_multithreading,
|
|
53
|
-
}.compact
|
|
9
|
+
# Serialized via lutaml-model — no hand-rolled +to_h+ / +to_json+.
|
|
10
|
+
class AlgorithmMetadata < Lutaml::Model::Serializable
|
|
11
|
+
attribute :name, :string
|
|
12
|
+
attribute :description, :string
|
|
13
|
+
attribute :version, :string
|
|
14
|
+
attribute :author, :string
|
|
15
|
+
attribute :max_compression_level, :integer
|
|
16
|
+
attribute :min_compression_level, :integer
|
|
17
|
+
attribute :default_compression_level, :integer
|
|
18
|
+
attribute :supports_streaming, :boolean, default: false
|
|
19
|
+
attribute :supports_multithreading, :boolean, default: false
|
|
20
|
+
|
|
21
|
+
json do
|
|
22
|
+
map :name, to: :name
|
|
23
|
+
map :description, to: :description
|
|
24
|
+
map :version, to: :version
|
|
25
|
+
map :author, to: :author
|
|
26
|
+
map :max_compression_level, to: :max_compression_level
|
|
27
|
+
map :min_compression_level, to: :min_compression_level
|
|
28
|
+
map :default_compression_level, to: :default_compression_level
|
|
29
|
+
map :supports_streaming, to: :supports_streaming
|
|
30
|
+
map :supports_multithreading, to: :supports_multithreading
|
|
54
31
|
end
|
|
55
32
|
|
|
56
|
-
#
|
|
33
|
+
# Apply a hash of attributes (whitelisted to declared attributes).
|
|
57
34
|
#
|
|
58
|
-
# @
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
# @return [AlgorithmMetadata] Deserialized instance
|
|
67
|
-
def self.from_json(json)
|
|
68
|
-
data = JSON.parse(json)
|
|
69
|
-
new(**data.transform_keys(&:to_sym))
|
|
35
|
+
# @param attributes [Hash{Symbol=>Object}]
|
|
36
|
+
# @return [self]
|
|
37
|
+
def apply(attributes)
|
|
38
|
+
self.class.attributes.each do |attr|
|
|
39
|
+
name = attr.name
|
|
40
|
+
public_send("#{name}=", attributes[name]) if attributes.key?(name)
|
|
41
|
+
end
|
|
42
|
+
self
|
|
70
43
|
end
|
|
71
44
|
end
|
|
72
45
|
end
|
|
@@ -1,70 +1,57 @@
|
|
|
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
|
-
|
|
3
|
+
require "lutaml/model"
|
|
19
4
|
require "json"
|
|
20
5
|
|
|
21
6
|
module Omnizip
|
|
22
7
|
module Models
|
|
23
8
|
# Model representing compression operation options.
|
|
24
9
|
#
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
10
|
+
# Serialized via lutaml-model — callers should not write hand-rolled
|
|
11
|
+
# +to_h+ / +to_json+ / +from_json+ on this class. The framework
|
|
12
|
+
# provides +#to_hash+, +#to_json+, and +.from_hash+ / +.from_json+
|
|
13
|
+
# automatically from the +attribute+ declarations below.
|
|
14
|
+
class CompressionOptions < Lutaml::Model::Serializable
|
|
15
|
+
attribute :level, :integer, default: 5
|
|
16
|
+
attribute :dictionary_size, :integer
|
|
17
|
+
attribute :num_fast_bytes, :integer
|
|
18
|
+
attribute :match_finder, :string
|
|
19
|
+
attribute :num_threads, :integer, default: 1
|
|
20
|
+
attribute :solid, :boolean, default: false
|
|
21
|
+
attribute :buffer_size, :integer, default: 65_536
|
|
22
|
+
|
|
23
|
+
# JSON / hash mappings (1:1, no renames needed).
|
|
24
|
+
json do
|
|
25
|
+
map :level, to: :level
|
|
26
|
+
map :dictionary_size, to: :dictionary_size
|
|
27
|
+
map :num_fast_bytes, to: :num_fast_bytes
|
|
28
|
+
map :match_finder, to: :match_finder
|
|
29
|
+
map :num_threads, to: :num_threads
|
|
30
|
+
map :solid, to: :solid
|
|
31
|
+
map :buffer_size, to: :buffer_size
|
|
40
32
|
end
|
|
41
33
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
level: @level,
|
|
45
|
-
dictionary_size: @dictionary_size,
|
|
46
|
-
num_fast_bytes: @num_fast_bytes,
|
|
47
|
-
match_finder: @match_finder,
|
|
48
|
-
num_threads: @num_threads,
|
|
49
|
-
solid: @solid,
|
|
50
|
-
buffer_size: @buffer_size,
|
|
51
|
-
}.compact
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
# Serialize to JSON
|
|
34
|
+
# Apply a hash of attributes. Only keys declared as +attribute+s
|
|
35
|
+
# above are applied; unknown keys are silently ignored.
|
|
55
36
|
#
|
|
56
|
-
# @
|
|
57
|
-
|
|
58
|
-
|
|
37
|
+
# @param attributes [Hash{Symbol=>Object}]
|
|
38
|
+
# @return [self]
|
|
39
|
+
def apply(attributes)
|
|
40
|
+
self.class.attributes.each do |attr|
|
|
41
|
+
name = attr.name
|
|
42
|
+
public_send("#{name}=", attributes[name]) if attributes.key?(name)
|
|
43
|
+
end
|
|
44
|
+
self
|
|
59
45
|
end
|
|
60
46
|
|
|
61
|
-
#
|
|
47
|
+
# Validate that all set values are within their type's domain.
|
|
62
48
|
#
|
|
63
|
-
# @
|
|
64
|
-
# @return [
|
|
65
|
-
def
|
|
66
|
-
|
|
67
|
-
|
|
49
|
+
# @raise [ArgumentError] if any value is invalid
|
|
50
|
+
# @return [Boolean] true if valid
|
|
51
|
+
def validate!
|
|
52
|
+
raise ArgumentError, "level must be 0-9" unless (0..9).cover?(level)
|
|
53
|
+
|
|
54
|
+
true
|
|
68
55
|
end
|
|
69
56
|
end
|
|
70
57
|
end
|
|
@@ -2,22 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
module Omnizip
|
|
4
4
|
module Models
|
|
5
|
-
#
|
|
5
|
+
# Options for converting between archive formats.
|
|
6
|
+
#
|
|
7
|
+
# Plain Ruby value object. (Track 13 of TODO.refactor recommends
|
|
8
|
+
# migrating this to lutaml-model once symbol-typed attributes are
|
|
9
|
+
# available.)
|
|
6
10
|
class ConversionOptions
|
|
11
|
+
VALID_FORMATS = %i[zip seven_zip 7z].freeze
|
|
12
|
+
|
|
7
13
|
attr_accessor :source_format, :target_format, :compression,
|
|
8
14
|
:compression_level, :filter, :preserve_metadata,
|
|
9
15
|
:temp_directory, :solid, :delete_source
|
|
10
16
|
|
|
11
|
-
# Initialize conversion options
|
|
12
|
-
# @param source_format [Symbol, nil] Source format (auto-detect if nil)
|
|
13
|
-
# @param target_format [Symbol] Target format
|
|
14
|
-
# @param compression [Symbol, nil] Compression algorithm
|
|
15
|
-
# @param compression_level [Integer] Compression level (1-9)
|
|
16
|
-
# @param filter [Symbol, nil] Filter to apply
|
|
17
|
-
# @param preserve_metadata [Boolean] Preserve metadata
|
|
18
|
-
# @param temp_directory [String, nil] Temporary directory
|
|
19
|
-
# @param solid [Boolean] Use solid compression (7z only)
|
|
20
|
-
# @param delete_source [Boolean] Delete source after conversion
|
|
21
17
|
def initialize(
|
|
22
18
|
source_format: nil,
|
|
23
19
|
target_format: :seven_zip,
|
|
@@ -40,8 +36,6 @@ module Omnizip
|
|
|
40
36
|
@delete_source = delete_source
|
|
41
37
|
end
|
|
42
38
|
|
|
43
|
-
# Convert to hash
|
|
44
|
-
# @return [Hash] Options as hash
|
|
45
39
|
def to_h
|
|
46
40
|
{
|
|
47
41
|
source_format: source_format,
|
|
@@ -56,8 +50,6 @@ module Omnizip
|
|
|
56
50
|
}
|
|
57
51
|
end
|
|
58
52
|
|
|
59
|
-
# Validate options
|
|
60
|
-
# @raise [ArgumentError] If options are invalid
|
|
61
53
|
def validate
|
|
62
54
|
validate_format(target_format, "target")
|
|
63
55
|
validate_format(source_format, "source") if source_format
|
|
@@ -69,11 +61,10 @@ module Omnizip
|
|
|
69
61
|
private
|
|
70
62
|
|
|
71
63
|
def validate_format(format, type)
|
|
72
|
-
|
|
73
|
-
return if valid_formats.include?(format)
|
|
64
|
+
return if VALID_FORMATS.include?(format)
|
|
74
65
|
|
|
75
66
|
raise ArgumentError, "Invalid #{type} format: #{format}. " \
|
|
76
|
-
"Valid formats: #{
|
|
67
|
+
"Valid formats: #{VALID_FORMATS.join(', ')}"
|
|
77
68
|
end
|
|
78
69
|
|
|
79
70
|
def validate_compression_level
|
|
@@ -60,6 +60,31 @@ module Omnizip
|
|
|
60
60
|
true
|
|
61
61
|
end
|
|
62
62
|
|
|
63
|
+
# Apply a hash of attributes to this options object. Only keys
|
|
64
|
+
# that correspond to known writers (+threads=+, +queue_size=+,
|
|
65
|
+
# etc.) are applied; unknown keys are silently ignored.
|
|
66
|
+
#
|
|
67
|
+
# @param attributes [Hash{Symbol=>Object}] attributes to set
|
|
68
|
+
# @return [self]
|
|
69
|
+
def apply(attributes)
|
|
70
|
+
SETTERS.each do |key, setter|
|
|
71
|
+
next unless attributes.key?(key)
|
|
72
|
+
|
|
73
|
+
public_send(setter, attributes[key])
|
|
74
|
+
end
|
|
75
|
+
self
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
SETTERS = {
|
|
79
|
+
threads: :threads=,
|
|
80
|
+
queue_size: :queue_size=,
|
|
81
|
+
chunk_size: :chunk_size=,
|
|
82
|
+
strategy: :strategy=,
|
|
83
|
+
verbose: :verbose=,
|
|
84
|
+
batch_size: :batch_size=,
|
|
85
|
+
}.freeze
|
|
86
|
+
private_constant :SETTERS
|
|
87
|
+
|
|
63
88
|
# Create a copy of options
|
|
64
89
|
#
|
|
65
90
|
# @return [ParallelOptions] new instance with same values
|
|
@@ -1,59 +1,25 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Omnizip
|
|
4
|
-
|
|
5
|
-
class OptimizationRegistry
|
|
4
|
+
class OptimizationRegistry < Omnizip::Registry
|
|
6
5
|
class << self
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
strategies[name] = strategy_class
|
|
6
|
+
def not_found_error_class
|
|
7
|
+
Omnizip::OptimizationNotFoundError
|
|
10
8
|
end
|
|
11
9
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
strategies[name] || raise(
|
|
15
|
-
Omnizip::OptimizationNotFound,
|
|
16
|
-
"Optimization strategy not found: #{name}",
|
|
17
|
-
)
|
|
10
|
+
def label
|
|
11
|
+
"Optimization strategy"
|
|
18
12
|
end
|
|
19
13
|
|
|
20
|
-
# Check if an optimization strategy is registered
|
|
21
|
-
def registered?(name)
|
|
22
|
-
strategies.key?(name)
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
# List all registered optimization strategies
|
|
26
|
-
def all
|
|
27
|
-
strategies.keys
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
# Get all optimization strategies as a hash
|
|
31
|
-
def strategies
|
|
32
|
-
@strategies ||= {}
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
# Clear all registered strategies (useful for testing)
|
|
36
|
-
def clear!
|
|
37
|
-
@strategies = {}
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
# Apply an optimization strategy to a target
|
|
41
14
|
def apply(name, target, **options)
|
|
42
|
-
|
|
43
|
-
strategy = strategy_class.new(**options)
|
|
44
|
-
strategy.optimize(target)
|
|
15
|
+
get(name).new(**options).optimize(target)
|
|
45
16
|
end
|
|
46
17
|
|
|
47
|
-
# Get optimization metadata
|
|
48
18
|
def metadata(name)
|
|
49
|
-
|
|
50
|
-
return {} unless strategy_class.respond_to?(:metadata)
|
|
51
|
-
|
|
52
|
-
strategy_class.metadata
|
|
19
|
+
get(name).metadata
|
|
53
20
|
end
|
|
54
21
|
end
|
|
55
22
|
|
|
56
|
-
# Base class for optimization strategies
|
|
57
23
|
class Strategy
|
|
58
24
|
attr_reader :options
|
|
59
25
|
|
|
@@ -61,13 +27,11 @@ module Omnizip
|
|
|
61
27
|
@options = options
|
|
62
28
|
end
|
|
63
29
|
|
|
64
|
-
|
|
65
|
-
def optimize(target)
|
|
30
|
+
def optimize(_target)
|
|
66
31
|
raise NotImplementedError,
|
|
67
32
|
"#{self.class} must implement #optimize"
|
|
68
33
|
end
|
|
69
34
|
|
|
70
|
-
# Override in subclasses to provide strategy metadata
|
|
71
35
|
def self.metadata
|
|
72
36
|
{
|
|
73
37
|
name: name,
|