omnizip 0.3.13 → 0.3.15
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/lib/omnizip/algorithm.rb +36 -1
- data/lib/omnizip/algorithms/bzip2.rb +1 -3
- data/lib/omnizip/algorithms/deflate.rb +1 -3
- data/lib/omnizip/algorithms/deflate64.rb +2 -2
- data/lib/omnizip/algorithms/lzma/optimal_encoder.rb +7 -8
- data/lib/omnizip/algorithms/lzma.rb +1 -1
- data/lib/omnizip/algorithms/lzma2/lzma2_chunk.rb +5 -1
- data/lib/omnizip/algorithms/lzma2.rb +4 -12
- data/lib/omnizip/algorithms/zstandard/constants.rb +38 -25
- data/lib/omnizip/algorithms/zstandard/decoder.rb +101 -136
- data/lib/omnizip/algorithms/zstandard/encoder.rb +81 -138
- data/lib/omnizip/algorithms/zstandard/frame/block.rb +17 -0
- data/lib/omnizip/algorithms/zstandard/frame/header.rb +42 -29
- data/lib/omnizip/algorithms/zstandard/fse/bitstream.rb +114 -117
- data/lib/omnizip/algorithms/zstandard/fse/encoder.rb +434 -222
- data/lib/omnizip/algorithms/zstandard/fse/interleaved.rb +92 -0
- data/lib/omnizip/algorithms/zstandard/fse/table.rb +98 -176
- data/lib/omnizip/algorithms/zstandard/fse/table_description.rb +203 -0
- data/lib/omnizip/algorithms/zstandard/fse.rb +21 -2
- data/lib/omnizip/algorithms/zstandard/huffman.rb +195 -188
- data/lib/omnizip/algorithms/zstandard/huffman_encoder.rb +232 -255
- data/lib/omnizip/algorithms/zstandard/literals.rb +170 -103
- data/lib/omnizip/algorithms/zstandard/literals_encoder.rb +47 -199
- data/lib/omnizip/algorithms/zstandard/sequences.rb +260 -252
- data/lib/omnizip/algorithms/zstandard/xxhash.rb +159 -0
- data/lib/omnizip/algorithms/zstandard.rb +20 -24
- data/lib/omnizip/algorithms.rb +14 -1
- data/lib/omnizip/buffer.rb +1 -2
- data/lib/omnizip/commands/archive_list_command.rb +9 -7
- data/lib/omnizip/convenience.rb +1 -2
- data/lib/omnizip/entry.rb +44 -0
- data/lib/omnizip/extraction/selective_extractor.rb +2 -10
- data/lib/omnizip/filter_pipeline.rb +1 -1
- data/lib/omnizip/filter_registry.rb +18 -16
- data/lib/omnizip/filters/bcj2.rb +1 -1
- data/lib/omnizip/filters/bcj_arm.rb +1 -1
- data/lib/omnizip/filters/bcj_arm64.rb +1 -1
- data/lib/omnizip/filters/bcj_ia64.rb +1 -1
- data/lib/omnizip/filters/bcj_ppc.rb +1 -1
- data/lib/omnizip/filters/bcj_sparc.rb +1 -1
- data/lib/omnizip/filters/bcj_x86.rb +1 -1
- data/lib/omnizip/filters.rb +0 -2
- data/lib/omnizip/formats/bzip2_file.rb +0 -7
- data/lib/omnizip/formats/cpio/entry.rb +6 -0
- data/lib/omnizip/formats/cpio.rb +0 -6
- data/lib/omnizip/formats/gzip.rb +0 -7
- data/lib/omnizip/formats/iso/directory_record.rb +7 -0
- data/lib/omnizip/formats/iso.rb +0 -5
- data/lib/omnizip/formats/lzip.rb +0 -7
- data/lib/omnizip/formats/lzma_alone.rb +0 -6
- data/lib/omnizip/formats/msi/entry.rb +6 -0
- data/lib/omnizip/formats/msi.rb +0 -9
- data/lib/omnizip/formats/ole/dirent.rb +6 -0
- data/lib/omnizip/formats/ole.rb +0 -9
- data/lib/omnizip/formats/rar.rb +0 -6
- data/lib/omnizip/formats/rar3/reader.rb +7 -0
- data/lib/omnizip/formats/rar5/reader.rb +7 -0
- data/lib/omnizip/formats/rpm/entry.rb +6 -0
- data/lib/omnizip/formats/seven_zip/models/file_entry.rb +7 -0
- data/lib/omnizip/formats/seven_zip.rb +0 -6
- data/lib/omnizip/formats/tar/entry.rb +6 -0
- data/lib/omnizip/formats/tar.rb +2 -10
- data/lib/omnizip/formats/xar/entry.rb +6 -0
- data/lib/omnizip/formats/xar.rb +0 -6
- data/lib/omnizip/formats/zip.rb +0 -6
- data/lib/omnizip/implementations/seven_zip/lzma2/encoder.rb +33 -31
- data/lib/omnizip/implementations/xz_utils/lzma2/encoder.rb +177 -163
- data/lib/omnizip/implementations.rb +18 -4
- data/lib/omnizip/io/source.rb +3 -0
- data/lib/omnizip/metadata/entry_metadata.rb +7 -0
- data/lib/omnizip/parallel/engine.rb +48 -0
- data/lib/omnizip/parallel/parallel_compressor.rb +5 -16
- data/lib/omnizip/parallel/parallel_extractor.rb +5 -16
- data/lib/omnizip/parallel.rb +1 -0
- data/lib/omnizip/profile/profile_registry.rb +1 -2
- data/lib/omnizip/registry.rb +0 -1
- data/lib/omnizip/rubyzip_compat.rb +0 -1
- data/lib/omnizip/temp/temp_file.rb +1 -2
- data/lib/omnizip/version.rb +1 -1
- data/lib/omnizip/zip/entry.rb +7 -0
- data/lib/omnizip/zip/file.rb +4 -8
- data/lib/omnizip.rb +1 -1
- metadata +7 -6
- data/lib/omnizip/filters/filter_base.rb +0 -6
- data/lib/omnizip/filters/registration.rb +0 -22
- data/lib/omnizip/filters/registry.rb +0 -111
- data/lib/omnizip/format_registry.rb +0 -100
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Copyright (C) 2025 Ribose Inc.
|
|
4
|
+
#
|
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a
|
|
6
|
+
# copy of this software and associated documentation files (the "Software"),
|
|
7
|
+
# to deal in the Software without restriction, including without limitation
|
|
8
|
+
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
9
|
+
# and/or sell copies of the Software, and to permit persons to whom the
|
|
10
|
+
# Software is furnished to do so, subject to the following conditions:
|
|
11
|
+
#
|
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
|
13
|
+
# all copies or substantial portions of the Software.
|
|
14
|
+
#
|
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
20
|
+
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
21
|
+
# DEALINGS IN THE SOFTWARE.
|
|
22
|
+
|
|
23
|
+
module Omnizip
|
|
24
|
+
module Algorithms
|
|
25
|
+
class Zstandard
|
|
26
|
+
# XXHash64 (https://github.com/Cyan4973/xxHash), single-shot.
|
|
27
|
+
#
|
|
28
|
+
# Zstandard frame checksums are the low 32 bits of XXHash64 of the
|
|
29
|
+
# decoded content with seed 0 (the zstd C reference truncates
|
|
30
|
+
# XXH64_digest to a U32; RFC 8878's "xxh64" wording agrees).
|
|
31
|
+
module XXHash64
|
|
32
|
+
MASK64 = 0xFFFFFFFFFFFFFFFF
|
|
33
|
+
MASK32 = 0xFFFFFFFF
|
|
34
|
+
|
|
35
|
+
PRIME64_1 = 0x9E3779B185EBCA87
|
|
36
|
+
PRIME64_2 = 0xC2B2AE3D27D4EB4F
|
|
37
|
+
PRIME64_3 = 0x165667B19E3779F9
|
|
38
|
+
PRIME64_4 = 0x85EBCA77C2B2AE63
|
|
39
|
+
PRIME64_5 = 0x27D4EB2F165667C5
|
|
40
|
+
|
|
41
|
+
module_function
|
|
42
|
+
|
|
43
|
+
# Digest `data` with seed 0.
|
|
44
|
+
#
|
|
45
|
+
# @param data [String] binary string
|
|
46
|
+
# @return [Integer] 64-bit digest
|
|
47
|
+
def digest(data)
|
|
48
|
+
digest_seeded(data, 0)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def digest_seeded(data, seed)
|
|
52
|
+
len = data.bytesize
|
|
53
|
+
hash = 0
|
|
54
|
+
tail_start = 0
|
|
55
|
+
|
|
56
|
+
if len >= 32
|
|
57
|
+
v1 = (seed + PRIME64_1 + PRIME64_2) & MASK64
|
|
58
|
+
v2 = (seed + PRIME64_2) & MASK64
|
|
59
|
+
v3 = seed & MASK64
|
|
60
|
+
v4 = (seed - PRIME64_1) & MASK64
|
|
61
|
+
|
|
62
|
+
i = 0
|
|
63
|
+
while i + 32 <= len
|
|
64
|
+
v1 = round64(v1, read_u64(data, i))
|
|
65
|
+
v2 = round64(v2, read_u64(data, i + 8))
|
|
66
|
+
v3 = round64(v3, read_u64(data, i + 16))
|
|
67
|
+
v4 = round64(v4, read_u64(data, i + 24))
|
|
68
|
+
i += 32
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
hash = rotl64(v1, 1)
|
|
72
|
+
hash = (hash + rotl64(v2, 7)) & MASK64
|
|
73
|
+
hash = (hash + rotl64(v3, 12)) & MASK64
|
|
74
|
+
hash = (hash + rotl64(v4, 18)) & MASK64
|
|
75
|
+
|
|
76
|
+
hash = merge_round64(hash, v1)
|
|
77
|
+
hash = merge_round64(hash, v2)
|
|
78
|
+
hash = merge_round64(hash, v3)
|
|
79
|
+
hash = merge_round64(hash, v4)
|
|
80
|
+
|
|
81
|
+
tail_start = len - (len % 32)
|
|
82
|
+
else
|
|
83
|
+
hash = (seed + PRIME64_5) & MASK64
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
hash = (hash + len) & MASK64
|
|
87
|
+
|
|
88
|
+
i = tail_start
|
|
89
|
+
while i + 8 <= len
|
|
90
|
+
k1 = round64(0, read_u64(data, i))
|
|
91
|
+
hash ^= k1
|
|
92
|
+
hash = ((rotl64(hash, 27) * PRIME64_1) & MASK64) + PRIME64_4
|
|
93
|
+
hash &= MASK64
|
|
94
|
+
i += 8
|
|
95
|
+
end
|
|
96
|
+
if i + 4 <= len
|
|
97
|
+
hash ^= (read_u32(data, i) * PRIME64_1) & MASK64
|
|
98
|
+
hash = ((rotl64(hash, 23) * PRIME64_2) & MASK64) + PRIME64_3
|
|
99
|
+
hash &= MASK64
|
|
100
|
+
i += 4
|
|
101
|
+
end
|
|
102
|
+
while i < len
|
|
103
|
+
hash ^= (data.getbyte(i) * PRIME64_5) & MASK64
|
|
104
|
+
hash = (rotl64(hash, 11) * PRIME64_1) & MASK64
|
|
105
|
+
i += 1
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
hash ^= hash >> 33
|
|
109
|
+
hash = (hash * PRIME64_2) & MASK64
|
|
110
|
+
hash ^= hash >> 29
|
|
111
|
+
hash = (hash * PRIME64_3) & MASK64
|
|
112
|
+
hash ^= hash >> 32
|
|
113
|
+
hash
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Zstandard frame checksum: low 32 bits of XXHash64.
|
|
117
|
+
#
|
|
118
|
+
# @return [Integer] 32-bit checksum
|
|
119
|
+
def frame_checksum(data)
|
|
120
|
+
digest(data) & MASK32
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def read_u64(data, offset)
|
|
124
|
+
data.getbyte(offset) |
|
|
125
|
+
(data.getbyte(offset + 1) << 8) |
|
|
126
|
+
(data.getbyte(offset + 2) << 16) |
|
|
127
|
+
(data.getbyte(offset + 3) << 24) |
|
|
128
|
+
(data.getbyte(offset + 4) << 32) |
|
|
129
|
+
(data.getbyte(offset + 5) << 40) |
|
|
130
|
+
(data.getbyte(offset + 6) << 48) |
|
|
131
|
+
(data.getbyte(offset + 7) << 56)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def read_u32(data, offset)
|
|
135
|
+
data.getbyte(offset) |
|
|
136
|
+
(data.getbyte(offset + 1) << 8) |
|
|
137
|
+
(data.getbyte(offset + 2) << 16) |
|
|
138
|
+
(data.getbyte(offset + 3) << 24)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def rotl64(value, count)
|
|
142
|
+
((value << count) | (value >> (64 - count))) & MASK64
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def round64(acc, input)
|
|
146
|
+
acc = ((acc + (input * PRIME64_2)) & MASK64) & MASK64
|
|
147
|
+
acc = rotl64(acc, 31)
|
|
148
|
+
(acc * PRIME64_1) & MASK64
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def merge_round64(acc, value)
|
|
152
|
+
value = round64(0, value)
|
|
153
|
+
acc ^= value
|
|
154
|
+
(((acc * PRIME64_1) & MASK64) + PRIME64_4) & MASK64
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end
|
|
@@ -48,22 +48,17 @@ module Omnizip
|
|
|
48
48
|
autoload :Encoder, "omnizip/algorithms/zstandard/encoder"
|
|
49
49
|
autoload :Decoder, "omnizip/algorithms/zstandard/decoder"
|
|
50
50
|
autoload :Huffman, "omnizip/algorithms/zstandard/huffman"
|
|
51
|
+
autoload :HuffmanTableReader, "omnizip/algorithms/zstandard/huffman"
|
|
51
52
|
autoload :HuffmanEncoder, "omnizip/algorithms/zstandard/huffman_encoder"
|
|
52
|
-
autoload :
|
|
53
|
+
autoload :LiteralsDecoder, "omnizip/algorithms/zstandard/literals"
|
|
53
54
|
autoload :LiteralsEncoder, "omnizip/algorithms/zstandard/literals_encoder"
|
|
54
|
-
autoload :
|
|
55
|
+
autoload :SequencesDecoder, "omnizip/algorithms/zstandard/sequences"
|
|
56
|
+
autoload :SequenceExecutor, "omnizip/algorithms/zstandard/sequences"
|
|
57
|
+
autoload :XXHash64, "omnizip/algorithms/zstandard/xxhash"
|
|
55
58
|
|
|
56
|
-
# Frame
|
|
59
|
+
# Frame and FSE modules
|
|
57
60
|
autoload :Frame, "omnizip/algorithms/zstandard/frame"
|
|
58
|
-
|
|
59
|
-
# Nested Frame module classes
|
|
60
|
-
autoload :FrameHeader, "omnizip/algorithms/zstandard/frame/header"
|
|
61
|
-
autoload :FrameBlock, "omnizip/algorithms/zstandard/frame/block"
|
|
62
|
-
|
|
63
|
-
# Nested FSE module classes
|
|
64
|
-
autoload :FseEncoder, "omnizip/algorithms/zstandard/fse/encoder"
|
|
65
|
-
autoload :FseTable, "omnizip/algorithms/zstandard/fse/table"
|
|
66
|
-
autoload :FseBitstream, "omnizip/algorithms/zstandard/fse/bitstream"
|
|
61
|
+
autoload :FSE, "omnizip/algorithms/zstandard/fse"
|
|
67
62
|
|
|
68
63
|
# Get algorithm metadata
|
|
69
64
|
#
|
|
@@ -96,9 +91,7 @@ module Omnizip
|
|
|
96
91
|
# @param options [Models::CompressionOptions] Decompression options
|
|
97
92
|
# @return [void]
|
|
98
93
|
def decompress(input_stream, output_stream, _options = nil)
|
|
99
|
-
|
|
100
|
-
output_stream.set_encoding(Encoding::BINARY)
|
|
101
|
-
end
|
|
94
|
+
output_stream.set_encoding(Encoding::BINARY)
|
|
102
95
|
decoder = Decoder.new(input_stream)
|
|
103
96
|
decompressed = decoder.decode_stream
|
|
104
97
|
output_stream.write(decompressed)
|
|
@@ -108,18 +101,21 @@ module Omnizip
|
|
|
108
101
|
|
|
109
102
|
# Build encoder options from compression options
|
|
110
103
|
#
|
|
111
|
-
#
|
|
104
|
+
# Always sets :level, defaulting to the zstd default (3) so that
|
|
105
|
+
# `compress(data)` never silently degrades to stored frames
|
|
106
|
+
# (issue #27). Accepts a Hash (as forwarded by Algorithm.compress)
|
|
107
|
+
# or a Models::CompressionOptions.
|
|
108
|
+
#
|
|
109
|
+
# @param options [Models::CompressionOptions, Hash, nil]
|
|
112
110
|
# @return [Hash] Encoder options
|
|
113
111
|
def build_encoder_options(options)
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
opts[:level] = map_compression_level(options.level)
|
|
120
|
-
end
|
|
112
|
+
level = case options
|
|
113
|
+
when nil then nil
|
|
114
|
+
when Hash then options[:level]
|
|
115
|
+
else options.level if options.respond_to?(:level)
|
|
116
|
+
end
|
|
121
117
|
|
|
122
|
-
|
|
118
|
+
{ level: map_compression_level(level) }
|
|
123
119
|
end
|
|
124
120
|
|
|
125
121
|
# Map generic compression level (0-9) to Zstd level (1-22)
|
data/lib/omnizip/algorithms.rb
CHANGED
|
@@ -1,7 +1,20 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Omnizip
|
|
4
|
-
#
|
|
4
|
+
# High-level compression algorithm facades.
|
|
5
|
+
#
|
|
6
|
+
# Each top-level constant in this namespace (Deflate, BZip2, LZMA,
|
|
7
|
+
# LZMA2, Zstandard, PPMd7, PPMd8) is a facade that exposes the
|
|
8
|
+
# streaming +.compress+/+.decompress+ contract from +Omnizip::Algorithm+.
|
|
9
|
+
# Format-specific readers/writers call these facades for the actual
|
|
10
|
+
# compression work; the facades themselves do not know about archive
|
|
11
|
+
# container structure.
|
|
12
|
+
#
|
|
13
|
+
# Bit-level SDK ports (state machines, range coders, match finders)
|
|
14
|
+
# live under +Omnizip::Implementations+, not here. This namespace
|
|
15
|
+
# owns the *streaming contract*; that namespace owns the *primitives*.
|
|
16
|
+
# The boundary is: +Algorithms+ calls +Implementations+, never the
|
|
17
|
+
# reverse.
|
|
5
18
|
module Algorithms
|
|
6
19
|
autoload :PPMdBase, "omnizip/algorithms/ppmd_base"
|
|
7
20
|
autoload :LZMA2Const, "omnizip/algorithms/lzma2/constants"
|
data/lib/omnizip/buffer.rb
CHANGED
|
@@ -46,7 +46,7 @@ module Omnizip
|
|
|
46
46
|
# archive.add('dir/file2.txt', 'more content')
|
|
47
47
|
# end
|
|
48
48
|
# File.binwrite('output.zip', buffer.string)
|
|
49
|
-
# rubocop:disable Naming/BlockForwarding, Style/ArgumentsForwarding -- Ruby 3.0 compatibility
|
|
49
|
+
# rubocop:disable-next Naming/BlockForwarding, Style/ArgumentsForwarding -- Ruby 3.0 compatibility
|
|
50
50
|
def create(format = :zip, **options, &block)
|
|
51
51
|
buffer = StringIO.new(String.new(encoding: Encoding::BINARY))
|
|
52
52
|
|
|
@@ -61,7 +61,6 @@ module Omnizip
|
|
|
61
61
|
|
|
62
62
|
buffer.tap(&:rewind)
|
|
63
63
|
end
|
|
64
|
-
# rubocop:enable Naming/BlockForwarding, Style/ArgumentsForwarding
|
|
65
64
|
|
|
66
65
|
# Open archive from memory
|
|
67
66
|
#
|
|
@@ -185,29 +185,31 @@ module Omnizip
|
|
|
185
185
|
end
|
|
186
186
|
end
|
|
187
187
|
|
|
188
|
-
# Helper methods to handle different entry types
|
|
188
|
+
# Helper methods to handle different entry types. Each delegates
|
|
189
|
+
# to the Omnizip::Entry contract; format-specific accessors stay
|
|
190
|
+
# on the format entry classes themselves.
|
|
189
191
|
def entry_name(entry)
|
|
190
|
-
entry.
|
|
192
|
+
entry.entry_name || entry.to_s
|
|
191
193
|
end
|
|
192
194
|
|
|
193
195
|
def entry_directory?(entry)
|
|
194
|
-
entry.
|
|
196
|
+
entry.entry_directory?
|
|
195
197
|
end
|
|
196
198
|
|
|
197
199
|
def entry_size(entry)
|
|
198
|
-
entry.
|
|
200
|
+
entry.entry_size || 0
|
|
199
201
|
end
|
|
200
202
|
|
|
201
203
|
def entry_compressed_size(entry)
|
|
202
|
-
entry.
|
|
204
|
+
entry.compressed_size if entry.is_a?(Omnizip::Formats::SevenZip::Models::FileEntry)
|
|
203
205
|
end
|
|
204
206
|
|
|
205
207
|
def entry_has_stream?(entry)
|
|
206
|
-
entry.
|
|
208
|
+
entry.has_stream? if entry.is_a?(Omnizip::Formats::SevenZip::Models::FileEntry)
|
|
207
209
|
end
|
|
208
210
|
|
|
209
211
|
def entry_mtime(entry)
|
|
210
|
-
entry.
|
|
212
|
+
entry.entry_mtime
|
|
211
213
|
end
|
|
212
214
|
end
|
|
213
215
|
end
|
data/lib/omnizip/convenience.rb
CHANGED
|
@@ -152,12 +152,11 @@ module Omnizip
|
|
|
152
152
|
end
|
|
153
153
|
|
|
154
154
|
# Create a RAR archive (requires RAR license — see NotLicensedError).
|
|
155
|
-
# rubocop:disable Naming/BlockForwarding, Style/ArgumentsForwarding -- Ruby 3.0 compatibility
|
|
155
|
+
# rubocop:disable-next Naming/BlockForwarding, Style/ArgumentsForwarding -- Ruby 3.0 compatibility
|
|
156
156
|
def create_rar(archive_path, **options, &block)
|
|
157
157
|
options[:version] ||= 5
|
|
158
158
|
Omnizip::Formats::Rar.create(archive_path, options, &block)
|
|
159
159
|
end
|
|
160
|
-
# rubocop:enable Naming/BlockForwarding, Style/ArgumentsForwarding
|
|
161
160
|
|
|
162
161
|
private
|
|
163
162
|
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Omnizip
|
|
4
|
+
# Mix-in that establishes the common contract every archive entry
|
|
5
|
+
# satisfies, regardless of format.
|
|
6
|
+
#
|
|
7
|
+
# Each format-specific entry class (Zip::Entry, Tar::Entry,
|
|
8
|
+
# SevenZip::Models::FileEntry, Iso::DirectoryRecord, Ole::Dirent,
|
|
9
|
+
# Xar::Entry, Rpm::Entry, Msi::Entry, Cpio::Entry, Rar3::Entry,
|
|
10
|
+
# Rar5::Entry, Xz::Entry, Metadata::EntryMetadata) includes this
|
|
11
|
+
# module so callers can rely on a single narrow interface instead
|
|
12
|
+
# of duck-typing through `respond_to?(:name) / :path / :filename`
|
|
13
|
+
# cascades.
|
|
14
|
+
#
|
|
15
|
+
# Including classes MUST override the four methods below. The
|
|
16
|
+
# defaults raise `NotImplementedError` to surface missing
|
|
17
|
+
# implementations at call time rather than at include time.
|
|
18
|
+
module Entry
|
|
19
|
+
# @return [String, nil] in-archive path, or nil if the entry has
|
|
20
|
+
# no name (e.g. empty anti-entry)
|
|
21
|
+
def entry_name
|
|
22
|
+
raise NotImplementedError,
|
|
23
|
+
"#{self.class} must implement #entry_name"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# @return [Boolean] true if the entry is a directory
|
|
27
|
+
def entry_directory?
|
|
28
|
+
raise NotImplementedError,
|
|
29
|
+
"#{self.class} must implement #entry_directory?"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# @return [Integer, nil] uncompressed size in bytes
|
|
33
|
+
def entry_size
|
|
34
|
+
raise NotImplementedError,
|
|
35
|
+
"#{self.class} must implement #entry_size"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# @return [Time, nil] modification time
|
|
39
|
+
def entry_mtime
|
|
40
|
+
raise NotImplementedError,
|
|
41
|
+
"#{self.class} must implement #entry_mtime"
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -184,18 +184,10 @@ module Omnizip
|
|
|
184
184
|
|
|
185
185
|
# Get filename from entry
|
|
186
186
|
#
|
|
187
|
-
# @param entry [
|
|
187
|
+
# @param entry [#entry_name] Entry (Omnizip::Entry or compatible)
|
|
188
188
|
# @return [String] Filename
|
|
189
189
|
def entry_filename(entry)
|
|
190
|
-
|
|
191
|
-
entry.name
|
|
192
|
-
elsif entry.respond_to?(:path)
|
|
193
|
-
entry.path
|
|
194
|
-
elsif entry.respond_to?(:filename)
|
|
195
|
-
entry.filename
|
|
196
|
-
else
|
|
197
|
-
entry.to_s
|
|
198
|
-
end
|
|
190
|
+
entry.entry_name || entry.to_s
|
|
199
191
|
end
|
|
200
192
|
|
|
201
193
|
# Update progress tracker
|
|
@@ -36,7 +36,7 @@ module Omnizip
|
|
|
36
36
|
# Filters are applied in the order they are added during encoding,
|
|
37
37
|
# and in reverse order during decoding.
|
|
38
38
|
#
|
|
39
|
-
# @param filter [
|
|
39
|
+
# @param filter [Omnizip::Filter] Filter instance to add
|
|
40
40
|
# @return [self] For method chaining
|
|
41
41
|
def add_filter(filter)
|
|
42
42
|
@filters << filter
|
|
@@ -4,16 +4,18 @@ module Omnizip
|
|
|
4
4
|
class FilterRegistry < Omnizip::Registry
|
|
5
5
|
DEFAULT_FORMATS = %i[xz seven_zip].freeze
|
|
6
6
|
|
|
7
|
+
# Single source of truth for builtin filters. Each entry binds the
|
|
8
|
+
# filter name to its class and the formats that support it.
|
|
7
9
|
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",
|
|
10
|
+
"bcj-x86": { const: "Omnizip::Filters::BcjX86", formats: %i[xz seven_zip] },
|
|
11
|
+
"bcj-arm": { const: "Omnizip::Filters::BcjArm", formats: %i[xz seven_zip] },
|
|
12
|
+
"bcj-arm64": { const: "Omnizip::Filters::BcjArm64", formats: [:seven_zip] },
|
|
13
|
+
"bcj-ia64": { const: "Omnizip::Filters::BcjIa64", formats: %i[xz seven_zip] },
|
|
14
|
+
"bcj-ppc": { const: "Omnizip::Filters::BcjPpc", formats: %i[xz seven_zip] },
|
|
15
|
+
"bcj-sparc": { const: "Omnizip::Filters::BcjSparc", formats: %i[xz seven_zip] },
|
|
16
|
+
bcj: { const: "Omnizip::Filters::BCJ", formats: [:seven_zip] },
|
|
17
|
+
bcj2: { const: "Omnizip::Filters::Bcj2", formats: [:seven_zip] },
|
|
18
|
+
delta: { const: "Omnizip::Filters::Delta", formats: %i[xz seven_zip] },
|
|
17
19
|
}.freeze
|
|
18
20
|
|
|
19
21
|
class << self
|
|
@@ -86,7 +88,6 @@ module Omnizip
|
|
|
86
88
|
|
|
87
89
|
trigger = lazy_triggers[normalized]
|
|
88
90
|
if trigger
|
|
89
|
-
synchronize { lazy_triggers.delete(normalized) }
|
|
90
91
|
trigger.call
|
|
91
92
|
entry = storage[normalized]
|
|
92
93
|
return entry if entry
|
|
@@ -98,12 +99,13 @@ module Omnizip
|
|
|
98
99
|
end
|
|
99
100
|
end
|
|
100
101
|
|
|
101
|
-
# Lazy triggers —
|
|
102
|
-
#
|
|
103
|
-
# overwrites the storage entry. Survives reset! because the
|
|
104
|
-
# proc is held by FilterRegistry, not the storage.
|
|
105
|
-
Omnizip::FilterRegistry::BUILTIN_FILTERS.
|
|
102
|
+
# Lazy triggers — each trigger resolves the filter's class (firing
|
|
103
|
+
# autoload) and registers it with its declared formats. Idempotent:
|
|
104
|
+
# register overwrites the storage entry. Survives reset! because the
|
|
105
|
+
# trigger proc is held by FilterRegistry, not the storage.
|
|
106
|
+
Omnizip::FilterRegistry::BUILTIN_FILTERS.each do |name, spec|
|
|
106
107
|
Omnizip::FilterRegistry.register_lazy(name) do
|
|
107
|
-
|
|
108
|
+
klass = Object.const_get(spec[:const])
|
|
109
|
+
Omnizip::FilterRegistry.register(name, klass, formats: spec[:formats])
|
|
108
110
|
end
|
|
109
111
|
end
|
data/lib/omnizip/filters/bcj2.rb
CHANGED
|
@@ -34,7 +34,7 @@ module Omnizip
|
|
|
34
34
|
# NOTE: Currently only decoding (decompression) is implemented.
|
|
35
35
|
# Encoding is extremely complex and will be added in a future version.
|
|
36
36
|
# For compression use cases, the simpler BCJ-x86 filter is recommended.
|
|
37
|
-
class Bcj2 <
|
|
37
|
+
class Bcj2 < Omnizip::Filter
|
|
38
38
|
# Encode is not currently supported for BCJ2.
|
|
39
39
|
#
|
|
40
40
|
# @param _data [String] Binary data to encode
|
|
@@ -28,7 +28,7 @@ module Omnizip
|
|
|
28
28
|
# The filter improves compression by making branch targets
|
|
29
29
|
# position-independent. The offset in ARM BL instructions is stored
|
|
30
30
|
# as word offset (divided by 4), and is relative to PC+8.
|
|
31
|
-
class BcjArm <
|
|
31
|
+
class BcjArm < Omnizip::Filter
|
|
32
32
|
# ARM BL (Branch and Link) opcode
|
|
33
33
|
OPCODE_BL = 0xEB
|
|
34
34
|
|
|
@@ -27,7 +27,7 @@ module Omnizip
|
|
|
27
27
|
#
|
|
28
28
|
# The filter improves compression by making branch targets and
|
|
29
29
|
# page-aligned addresses position-independent.
|
|
30
|
-
class BcjArm64 <
|
|
30
|
+
class BcjArm64 < Omnizip::Filter
|
|
31
31
|
# B/BL instruction base opcode
|
|
32
32
|
OPCODE_B_BL = 0x94000000
|
|
33
33
|
|
|
@@ -27,7 +27,7 @@ module Omnizip
|
|
|
27
27
|
#
|
|
28
28
|
# The filter improves compression by making branch targets
|
|
29
29
|
# position-independent.
|
|
30
|
-
class BcjIa64 <
|
|
30
|
+
class BcjIa64 < Omnizip::Filter
|
|
31
31
|
# Size of IA-64 instruction bundle (16 bytes, little-endian)
|
|
32
32
|
BUNDLE_SIZE = 16
|
|
33
33
|
|
data/lib/omnizip/filters.rb
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
module Omnizip
|
|
4
4
|
# Filter implementations for preprocessing data before compression
|
|
5
5
|
module Filters
|
|
6
|
-
autoload :FilterBase, "omnizip/filters/filter_base"
|
|
7
6
|
autoload :BCJ, "omnizip/filters/bcj"
|
|
8
7
|
autoload :BcjX86, "omnizip/filters/bcj_x86"
|
|
9
8
|
autoload :Bcj2, "omnizip/filters/bcj2"
|
|
@@ -17,6 +16,5 @@ module Omnizip
|
|
|
17
16
|
autoload :BcjSparc, "omnizip/filters/bcj_sparc"
|
|
18
17
|
autoload :BcjIa64, "omnizip/filters/bcj_ia64"
|
|
19
18
|
autoload :Delta, "omnizip/filters/delta"
|
|
20
|
-
autoload :Registry, "omnizip/filters/registry"
|
|
21
19
|
end
|
|
22
20
|
end
|
|
@@ -112,12 +112,6 @@ module Omnizip
|
|
|
112
112
|
output_io.write(decompressed_io.read)
|
|
113
113
|
end
|
|
114
114
|
|
|
115
|
-
# Register BZIP2 format when loaded
|
|
116
|
-
def register!
|
|
117
|
-
FormatRegistry.register(".bz2", Omnizip::Formats::Bzip2File)
|
|
118
|
-
FormatRegistry.register(".bzip2", Omnizip::Formats::Bzip2File)
|
|
119
|
-
end
|
|
120
|
-
|
|
121
115
|
private
|
|
122
116
|
|
|
123
117
|
# Write BZIP2 header
|
|
@@ -163,4 +157,3 @@ module Omnizip
|
|
|
163
157
|
end
|
|
164
158
|
end
|
|
165
159
|
end
|
|
166
|
-
|
|
@@ -8,6 +8,7 @@ module Omnizip
|
|
|
8
8
|
# Represents a file, directory, or special file in a CPIO archive.
|
|
9
9
|
# Supports newc, CRC, and ODC formats.
|
|
10
10
|
class Entry
|
|
11
|
+
include Omnizip::Entry
|
|
11
12
|
include Omnizip::Formats::Cpio::Constants
|
|
12
13
|
|
|
13
14
|
# @return [String] Magic number identifying format
|
|
@@ -58,6 +59,11 @@ module Omnizip
|
|
|
58
59
|
# @return [String] File data
|
|
59
60
|
attr_accessor :data
|
|
60
61
|
|
|
62
|
+
def entry_name = name
|
|
63
|
+
def entry_directory? = directory?
|
|
64
|
+
def entry_size = filesize
|
|
65
|
+
def entry_mtime = mtime
|
|
66
|
+
|
|
61
67
|
# Initialize CPIO entry
|
|
62
68
|
#
|
|
63
69
|
# @param attributes [Hash] Entry attributes
|
data/lib/omnizip/formats/cpio.rb
CHANGED
data/lib/omnizip/formats/gzip.rb
CHANGED
|
@@ -127,12 +127,6 @@ module Omnizip
|
|
|
127
127
|
metadata
|
|
128
128
|
end
|
|
129
129
|
|
|
130
|
-
# Register GZIP format when loaded
|
|
131
|
-
def register!
|
|
132
|
-
FormatRegistry.register(".gz", Omnizip::Formats::Gzip)
|
|
133
|
-
FormatRegistry.register(".gzip", Omnizip::Formats::Gzip)
|
|
134
|
-
end
|
|
135
|
-
|
|
136
130
|
private
|
|
137
131
|
|
|
138
132
|
# Write GZIP header
|
|
@@ -231,4 +225,3 @@ module Omnizip
|
|
|
231
225
|
end
|
|
232
226
|
end
|
|
233
227
|
end
|
|
234
|
-
|
|
@@ -6,9 +6,16 @@ module Omnizip
|
|
|
6
6
|
# ISO 9660 Directory Record
|
|
7
7
|
# Represents a file or directory entry
|
|
8
8
|
class DirectoryRecord
|
|
9
|
+
include Omnizip::Entry
|
|
10
|
+
|
|
9
11
|
attr_accessor :full_path
|
|
10
12
|
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
13
|
|
|
14
|
+
def entry_name = full_path || name
|
|
15
|
+
def entry_directory? = directory?
|
|
16
|
+
def entry_size = data_length
|
|
17
|
+
def entry_mtime = recording_date
|
|
18
|
+
|
|
12
19
|
# Set the full path for this record, used by Reader when walking
|
|
13
20
|
# a directory tree. Allows callers to look up records by path
|
|
14
21
|
# without re-joining parent names.
|
data/lib/omnizip/formats/iso.rb
CHANGED
data/lib/omnizip/formats/lzip.rb
CHANGED
|
@@ -132,12 +132,6 @@ module Omnizip
|
|
|
132
132
|
}
|
|
133
133
|
end
|
|
134
134
|
|
|
135
|
-
# Register LZIP format when loaded
|
|
136
|
-
def register!
|
|
137
|
-
FormatRegistry.register(".lz", Omnizip::Formats::Lzip)
|
|
138
|
-
FormatRegistry.register(".lzip", Omnizip::Formats::Lzip)
|
|
139
|
-
end
|
|
140
|
-
|
|
141
135
|
# Detect if data is LZIP format
|
|
142
136
|
#
|
|
143
137
|
# @param data [String] Data to check
|
|
@@ -149,4 +143,3 @@ module Omnizip
|
|
|
149
143
|
end
|
|
150
144
|
end
|
|
151
145
|
end
|
|
152
|
-
|