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.
Files changed (88) hide show
  1. checksums.yaml +4 -4
  2. data/lib/omnizip/algorithm.rb +36 -1
  3. data/lib/omnizip/algorithms/bzip2.rb +1 -3
  4. data/lib/omnizip/algorithms/deflate.rb +1 -3
  5. data/lib/omnizip/algorithms/deflate64.rb +2 -2
  6. data/lib/omnizip/algorithms/lzma/optimal_encoder.rb +7 -8
  7. data/lib/omnizip/algorithms/lzma.rb +1 -1
  8. data/lib/omnizip/algorithms/lzma2/lzma2_chunk.rb +5 -1
  9. data/lib/omnizip/algorithms/lzma2.rb +4 -12
  10. data/lib/omnizip/algorithms/zstandard/constants.rb +38 -25
  11. data/lib/omnizip/algorithms/zstandard/decoder.rb +101 -136
  12. data/lib/omnizip/algorithms/zstandard/encoder.rb +81 -138
  13. data/lib/omnizip/algorithms/zstandard/frame/block.rb +17 -0
  14. data/lib/omnizip/algorithms/zstandard/frame/header.rb +42 -29
  15. data/lib/omnizip/algorithms/zstandard/fse/bitstream.rb +114 -117
  16. data/lib/omnizip/algorithms/zstandard/fse/encoder.rb +434 -222
  17. data/lib/omnizip/algorithms/zstandard/fse/interleaved.rb +92 -0
  18. data/lib/omnizip/algorithms/zstandard/fse/table.rb +98 -176
  19. data/lib/omnizip/algorithms/zstandard/fse/table_description.rb +203 -0
  20. data/lib/omnizip/algorithms/zstandard/fse.rb +21 -2
  21. data/lib/omnizip/algorithms/zstandard/huffman.rb +195 -188
  22. data/lib/omnizip/algorithms/zstandard/huffman_encoder.rb +232 -255
  23. data/lib/omnizip/algorithms/zstandard/literals.rb +170 -103
  24. data/lib/omnizip/algorithms/zstandard/literals_encoder.rb +47 -199
  25. data/lib/omnizip/algorithms/zstandard/sequences.rb +260 -252
  26. data/lib/omnizip/algorithms/zstandard/xxhash.rb +159 -0
  27. data/lib/omnizip/algorithms/zstandard.rb +20 -24
  28. data/lib/omnizip/algorithms.rb +14 -1
  29. data/lib/omnizip/buffer.rb +1 -2
  30. data/lib/omnizip/commands/archive_list_command.rb +9 -7
  31. data/lib/omnizip/convenience.rb +1 -2
  32. data/lib/omnizip/entry.rb +44 -0
  33. data/lib/omnizip/extraction/selective_extractor.rb +2 -10
  34. data/lib/omnizip/filter_pipeline.rb +1 -1
  35. data/lib/omnizip/filter_registry.rb +18 -16
  36. data/lib/omnizip/filters/bcj2.rb +1 -1
  37. data/lib/omnizip/filters/bcj_arm.rb +1 -1
  38. data/lib/omnizip/filters/bcj_arm64.rb +1 -1
  39. data/lib/omnizip/filters/bcj_ia64.rb +1 -1
  40. data/lib/omnizip/filters/bcj_ppc.rb +1 -1
  41. data/lib/omnizip/filters/bcj_sparc.rb +1 -1
  42. data/lib/omnizip/filters/bcj_x86.rb +1 -1
  43. data/lib/omnizip/filters.rb +0 -2
  44. data/lib/omnizip/formats/bzip2_file.rb +0 -7
  45. data/lib/omnizip/formats/cpio/entry.rb +6 -0
  46. data/lib/omnizip/formats/cpio.rb +0 -6
  47. data/lib/omnizip/formats/gzip.rb +0 -7
  48. data/lib/omnizip/formats/iso/directory_record.rb +7 -0
  49. data/lib/omnizip/formats/iso.rb +0 -5
  50. data/lib/omnizip/formats/lzip.rb +0 -7
  51. data/lib/omnizip/formats/lzma_alone.rb +0 -6
  52. data/lib/omnizip/formats/msi/entry.rb +6 -0
  53. data/lib/omnizip/formats/msi.rb +0 -9
  54. data/lib/omnizip/formats/ole/dirent.rb +6 -0
  55. data/lib/omnizip/formats/ole.rb +0 -9
  56. data/lib/omnizip/formats/rar.rb +0 -6
  57. data/lib/omnizip/formats/rar3/reader.rb +7 -0
  58. data/lib/omnizip/formats/rar5/reader.rb +7 -0
  59. data/lib/omnizip/formats/rpm/entry.rb +6 -0
  60. data/lib/omnizip/formats/seven_zip/models/file_entry.rb +7 -0
  61. data/lib/omnizip/formats/seven_zip.rb +0 -6
  62. data/lib/omnizip/formats/tar/entry.rb +6 -0
  63. data/lib/omnizip/formats/tar.rb +2 -10
  64. data/lib/omnizip/formats/xar/entry.rb +6 -0
  65. data/lib/omnizip/formats/xar.rb +0 -6
  66. data/lib/omnizip/formats/zip.rb +0 -6
  67. data/lib/omnizip/implementations/seven_zip/lzma2/encoder.rb +33 -31
  68. data/lib/omnizip/implementations/xz_utils/lzma2/encoder.rb +177 -163
  69. data/lib/omnizip/implementations.rb +18 -4
  70. data/lib/omnizip/io/source.rb +3 -0
  71. data/lib/omnizip/metadata/entry_metadata.rb +7 -0
  72. data/lib/omnizip/parallel/engine.rb +48 -0
  73. data/lib/omnizip/parallel/parallel_compressor.rb +5 -16
  74. data/lib/omnizip/parallel/parallel_extractor.rb +5 -16
  75. data/lib/omnizip/parallel.rb +1 -0
  76. data/lib/omnizip/profile/profile_registry.rb +1 -2
  77. data/lib/omnizip/registry.rb +0 -1
  78. data/lib/omnizip/rubyzip_compat.rb +0 -1
  79. data/lib/omnizip/temp/temp_file.rb +1 -2
  80. data/lib/omnizip/version.rb +1 -1
  81. data/lib/omnizip/zip/entry.rb +7 -0
  82. data/lib/omnizip/zip/file.rb +4 -8
  83. data/lib/omnizip.rb +1 -1
  84. metadata +7 -6
  85. data/lib/omnizip/filters/filter_base.rb +0 -6
  86. data/lib/omnizip/filters/registration.rb +0 -22
  87. data/lib/omnizip/filters/registry.rb +0 -111
  88. 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 :Literals, "omnizip/algorithms/zstandard/literals"
53
+ autoload :LiteralsDecoder, "omnizip/algorithms/zstandard/literals"
53
54
  autoload :LiteralsEncoder, "omnizip/algorithms/zstandard/literals_encoder"
54
- autoload :Sequences, "omnizip/algorithms/zstandard/sequences"
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 module
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
- if output_stream.respond_to?(:set_encoding)
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
- # @param options [Models::CompressionOptions, nil] Compression opts
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
- return {} if options.nil?
115
-
116
- opts = {}
117
-
118
- if options.respond_to?(:level)
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
- opts
118
+ { level: map_compression_level(level) }
123
119
  end
124
120
 
125
121
  # Map generic compression level (0-9) to Zstd level (1-22)
@@ -1,7 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Omnizip
4
- # Compression algorithm implementations
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"
@@ -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.respond_to?(:name) ? entry.name : entry.to_s
192
+ entry.entry_name || entry.to_s
191
193
  end
192
194
 
193
195
  def entry_directory?(entry)
194
- entry.respond_to?(:directory?) && entry.directory?
196
+ entry.entry_directory?
195
197
  end
196
198
 
197
199
  def entry_size(entry)
198
- entry.respond_to?(:size) ? entry.size : 0
200
+ entry.entry_size || 0
199
201
  end
200
202
 
201
203
  def entry_compressed_size(entry)
202
- entry.respond_to?(:compressed_size) ? entry.compressed_size : nil
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.respond_to?(:has_stream?) ? entry.has_stream? : true
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.respond_to?(:mtime) ? entry.mtime : nil
212
+ entry.entry_mtime
211
213
  end
212
214
  end
213
215
  end
@@ -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 [Object] Entry
187
+ # @param entry [#entry_name] Entry (Omnizip::Entry or compatible)
188
188
  # @return [String] Filename
189
189
  def entry_filename(entry)
190
- if entry.respond_to?(:name)
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 [Filters::FilterBase] Filter instance to add
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 — 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|
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
- Omnizip::Filters::Registry.register_all
108
+ klass = Object.const_get(spec[:const])
109
+ Omnizip::FilterRegistry.register(name, klass, formats: spec[:formats])
108
110
  end
109
111
  end
@@ -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 < FilterBase
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 < FilterBase
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 < FilterBase
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 < FilterBase
30
+ class BcjIa64 < Omnizip::Filter
31
31
  # Size of IA-64 instruction bundle (16 bytes, little-endian)
32
32
  BUNDLE_SIZE = 16
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 BcjPpc < FilterBase
30
+ class BcjPpc < Omnizip::Filter
31
31
  # PPC B/BL instruction base (0x48000000)
32
32
  OPCODE_BASE = 0x48000000
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 BcjSparc < FilterBase
30
+ class BcjSparc < Omnizip::Filter
31
31
  # Size of SPARC instruction (4 bytes, big-endian)
32
32
  INSTRUCTION_SIZE = 4
33
33
 
@@ -27,7 +27,7 @@ module Omnizip
27
27
  #
28
28
  # The filter is reversible and works on 5-byte boundaries (1-byte
29
29
  # opcode + 4-byte address).
30
- class BcjX86 < FilterBase
30
+ class BcjX86 < Omnizip::Filter
31
31
  # x86 CALL opcode
32
32
  OPCODE_CALL = 0xE8
33
33
 
@@ -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
@@ -126,13 +126,7 @@ module Omnizip
126
126
  }
127
127
  end
128
128
  end
129
-
130
- # Auto-register CPIO format when loaded
131
- def register!
132
- FormatRegistry.register(".cpio", Reader)
133
- end
134
129
  end
135
130
  end
136
131
  end
137
132
  end
138
-
@@ -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.
@@ -127,11 +127,6 @@ module Omnizip
127
127
 
128
128
  writer.write
129
129
  end
130
-
131
- # Auto-register ISO format when loaded
132
- def self.register!
133
- FormatRegistry.register(".iso", Reader)
134
- end
135
130
  end
136
131
  end
137
132
  end
@@ -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
-