omnizip 0.3.54 → 0.3.55

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 87089cc22eb4f5edd1cb37e7a0de297527fb2f7c0a2815f29d1ff271c73f7461
4
- data.tar.gz: e6b7848399a98ae4f38f80cbc71833ada099e071d4f4126437ebbfcca820dc48
3
+ metadata.gz: 7de280d85fcea7072c88798c925b5aa3e03bcff124c1294e72abeb6f976cc5e8
4
+ data.tar.gz: 579759d99f27af57c070e5e1b6a6d6b121651932b10681da88e96331adb36bee
5
5
  SHA512:
6
- metadata.gz: cfd421337992988889d3e5e8d4e85177248c5586238c2c79a5d87e5fc2229de3620f53e93d582ac72992c90b213421b1832cd44ecb9e1ff94e94622a48fa6b88
7
- data.tar.gz: 33ca53cfe50c72a1d31afbbf0848ed9559824867edf34f1a4fdc37484300c029fcbf36bdb3135d92f607e31c517d6a519e945065a6c17f817cfbdc0af9ee6de9
6
+ metadata.gz: a08a53e33284555a3fe7493834f522e0bf00a8609705e06d6c05578bb342bb3db4e4ba1c3c0793f18a001ef55305fd3396a7300b6c7f1c82196a9702931fc374
7
+ data.tar.gz: 7b0550e981ff0cb317beffdcb7eff7b233a7fff0bc4812eae01a566f1a0d255de8896e217d1d1ce62618b56bb5ea284dc414c98b53c171fa4af0b54a1a048b86
data/CHANGELOG.md CHANGED
@@ -7,6 +7,41 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.3.54] - 2026-09-01
11
+
12
+ ### Changed
13
+ - The 7z tree's 38 bare `raise "string"` sites gain their matching
14
+ classes: `PasswordError` for encrypted-header problems (with a
15
+ new autoload entry — the class lives at the top of password.rb,
16
+ so referencing it never loaded the file),
17
+ `InvalidArchiveError`/`UnsupportedFormatError` for parse and
18
+ structure failures, `ChecksumError` for CRC mismatches,
19
+ `AlgorithmNotFoundError`/`UnknownFilterError` in CoderChain, and
20
+ `Errno::ENOENT` for missing entries and volumes. The reader's
21
+ encrypted-header probe re-raises password errors by class instead
22
+ of matching message text.
23
+ - In-memory ZIP leaves the rubyzip compat layer: `Buffer.create`
24
+ writes through the native Writer (which gains a per-entry
25
+ compression-method override so `:store` entries work beside
26
+ deflated ones), and `Buffer.open`/`MemoryExtractor` read through
27
+ the native Reader via a temporary-file spill.
28
+ `MemoryArchive::Entry#read` now has true IO streaming semantics —
29
+ successive chunks and nil at EOF (an always-returning read looped
30
+ forever in `Pipe::StreamDecompressor`).
31
+ - The parallel subsystem is revived: Engine drives our own
32
+ WorkerPool instead of a phantom `Fractor::WorkerPool`,
33
+ ParallelExtractor reads through the native ZIP reader and runs on
34
+ a bounded ThreadPool — Ractor workers cannot trigger Omnizip's
35
+ autoloads or cross into Proc-holding codec constants on
36
+ Ruby <= 3.3 — JobScheduler's implemented round_robin/bin_packing
37
+ strategies are reachable, directory entries extract idempotently,
38
+ and the subsystem gains specs.
39
+
40
+ ### Fixed
41
+ - par2_compatibility_spec was missing `require "tmpdir"` and failed
42
+ 9 of 15 examples whenever no earlier spec happened to load tmpdir
43
+ first.
44
+
10
45
  ## [0.3.53] - 2026-09-01
11
46
 
12
47
  ### Changed
@@ -31,7 +31,7 @@ module Omnizip
31
31
 
32
32
  # Read bytes from dictionary at a distance back
33
33
  def read_bytes(distance, length)
34
- raise "Invalid distance: #{distance}" if distance > @buffer.bytesize
34
+ raise Omnizip::DecompressionError, "Invalid distance: #{distance}" if distance > @buffer.bytesize
35
35
 
36
36
  result = String.new(encoding: Encoding::BINARY)
37
37
  src_pos = @buffer.bytesize - distance
@@ -46,7 +46,7 @@ module Omnizip
46
46
 
47
47
  # Get byte at distance back
48
48
  def get_byte(distance)
49
- raise "Invalid distance: #{distance}" if distance > @buffer.bytesize
49
+ raise Omnizip::DecompressionError, "Invalid distance: #{distance}" if distance > @buffer.bytesize
50
50
 
51
51
  @buffer.getbyte(@buffer.bytesize - distance)
52
52
  end
@@ -118,12 +118,12 @@ module Omnizip
118
118
  # Step 1: Parse properties byte (SEQ_PROPERTIES)
119
119
  # Reference: alone_decoder.c:64-68
120
120
  props = @input.getbyte
121
- raise "Invalid .lzma header: missing properties byte" if props.nil?
121
+ raise Omnizip::DecompressionError, "Invalid .lzma header: missing properties byte" if props.nil?
122
122
 
123
123
  # Use XZ Utils property byte parsing
124
124
  # Reference: /Users/mulgogi/src/external/xz/src/liblzma/lzma/lzma_decoder.c:1216-1228
125
125
  if props > MAX_PROPERTY_BYTE
126
- raise "Invalid .lzma header: properties byte #{props} exceeds maximum #{MAX_PROPERTY_BYTE}"
126
+ raise Omnizip::DecompressionError, "Invalid .lzma header: properties byte #{props} exceeds maximum #{MAX_PROPERTY_BYTE}"
127
127
  end
128
128
 
129
129
  # Parse lc, lp, pb from properties byte
@@ -136,7 +136,7 @@ module Omnizip
136
136
  # Validate lc + lp <= 4 (LZMA_LCLP_MAX)
137
137
  # Reference: lzma_decoder.c:1227
138
138
  if @lc + @lp > 4
139
- raise "Invalid .lzma header: lc (#{@lc}) + lp (#{@lp}) exceeds maximum 4"
139
+ raise Omnizip::DecompressionError, "Invalid .lzma header: lc (#{@lc}) + lp (#{@lp}) exceeds maximum 4"
140
140
  end
141
141
 
142
142
  # Step 2: Parse dictionary size (SEQ_DICTIONARY_SIZE)
@@ -144,7 +144,7 @@ module Omnizip
144
144
  @dict_size = 0
145
145
  4.times do |i|
146
146
  byte = @input.getbyte
147
- raise "Incomplete .lzma header: missing dictionary size byte" if byte.nil?
147
+ raise Omnizip::DecompressionError, "Incomplete .lzma header: missing dictionary size byte" if byte.nil?
148
148
 
149
149
  @dict_size |= (byte << (i * 8))
150
150
  end
@@ -163,7 +163,7 @@ module Omnizip
163
163
  d += 1
164
164
 
165
165
  if d != @dict_size
166
- raise "Invalid .lzma header: dictionary size #{@dict_size} is not 2^n or 2^n + 2^(n-1)"
166
+ raise Omnizip::DecompressionError, "Invalid .lzma header: dictionary size #{@dict_size} is not 2^n or 2^n + 2^(n-1)"
167
167
  end
168
168
  end
169
169
 
@@ -172,7 +172,7 @@ module Omnizip
172
172
  @uncompressed_size = 0
173
173
  8.times do |i|
174
174
  byte = @input.getbyte
175
- raise "Incomplete .lzma header: missing uncompressed size byte" if byte.nil?
175
+ raise Omnizip::DecompressionError, "Incomplete .lzma header: missing uncompressed size byte" if byte.nil?
176
176
 
177
177
  @uncompressed_size |= (byte << (i * 8))
178
178
  end
@@ -182,7 +182,7 @@ module Omnizip
182
182
  # Reference: alone_decoder.c:116-120
183
183
  if @picky && @uncompressed_size != 0xFFFFFFFFFFFFFFFF &&
184
184
  @uncompressed_size >= MAX_UNCOMPRESSED_SIZE
185
- raise "Invalid .lzma header: uncompressed size #{@uncompressed_size} exceeds maximum #{MAX_UNCOMPRESSED_SIZE}"
185
+ raise Omnizip::DecompressionError, "Invalid .lzma header: uncompressed size #{@uncompressed_size} exceeds maximum #{MAX_UNCOMPRESSED_SIZE}"
186
186
  end
187
187
 
188
188
  # Note: XZ Utils uses UINT64_MAX (0xFFFFFFFFFFFFFFFF) for unknown size
@@ -97,7 +97,7 @@ module Omnizip
97
97
  # @param prob [BitModel] Probability model
98
98
  # @param bit [Integer] Bit value (0 or 1)
99
99
  def queue_bit(prob, bit)
100
- raise "Symbol buffer overflow" if @count >= RC_SYMBOLS_MAX
100
+ raise Omnizip::CompressionError, "Symbol buffer overflow" if @count >= RC_SYMBOLS_MAX
101
101
 
102
102
  @symbols[@count] = bit.zero? ? RC_BIT_0 : RC_BIT_1
103
103
  @probs[@count] = prob
@@ -111,7 +111,7 @@ module Omnizip
111
111
  def queue_direct_bits(value, num_bits)
112
112
  num_bits.downto(1) do |i|
113
113
  bit = (value >> (i - 1)) & 1
114
- raise "Symbol buffer overflow" if @count >= RC_SYMBOLS_MAX
114
+ raise Omnizip::CompressionError, "Symbol buffer overflow" if @count >= RC_SYMBOLS_MAX
115
115
 
116
116
  @symbols[@count] = bit.zero? ? RC_DIRECT_0 : RC_DIRECT_1
117
117
  @probs[@count] = nil
@@ -122,7 +122,7 @@ module Omnizip
122
122
  # Queue flush operation
123
123
  def queue_flush
124
124
  5.times do
125
- raise "Symbol buffer overflow" if @count >= RC_SYMBOLS_MAX
125
+ raise Omnizip::CompressionError, "Symbol buffer overflow" if @count >= RC_SYMBOLS_MAX
126
126
 
127
127
  @symbols[@count] = RC_FLUSH
128
128
  @probs[@count] = nil
@@ -237,7 +237,7 @@ module Omnizip
237
237
 
238
238
  # Forget queued symbols (e.g., when output limit reached)
239
239
  def forget
240
- raise "Cannot forget with partial encoding" unless @pos.zero?
240
+ raise Omnizip::CompressionError, "Cannot forget with partial encoding" unless @pos.zero?
241
241
 
242
242
  @count = 0
243
243
  end
@@ -63,7 +63,7 @@ module Omnizip
63
63
 
64
64
  # Forget pending symbols (matches XZ Utils rc_forget)
65
65
  def forget
66
- raise "Cannot forget while encoding" if @pos != 0
66
+ raise Omnizip::CompressionError, "Cannot forget while encoding" if @pos != 0
67
67
 
68
68
  @count = 0
69
69
  end
@@ -73,7 +73,7 @@ module Omnizip
73
73
  # @param prob [Probability] Probability model
74
74
  # @param bit [Integer] Bit value (0 or 1)
75
75
  def bit(prob, bit)
76
- raise "Symbol buffer overflow" if @count >= RC_SYMBOLS_MAX
76
+ raise Omnizip::CompressionError, "Symbol buffer overflow" if @count >= RC_SYMBOLS_MAX
77
77
 
78
78
  @symbols[@count] = bit
79
79
  @probs[@count] = prob
@@ -118,7 +118,7 @@ module Omnizip
118
118
  # @param bit_count [Integer] Number of bits
119
119
  def direct(value, bit_count)
120
120
  bit_count.times do
121
- raise "Symbol buffer overflow" if @count >= RC_SYMBOLS_MAX
121
+ raise Omnizip::CompressionError, "Symbol buffer overflow" if @count >= RC_SYMBOLS_MAX
122
122
 
123
123
  @symbols[@count] = RC_DIRECT_0 | ((value >> (bit_count -= 1)) & 1)
124
124
  @probs[@count] = nil
@@ -129,7 +129,7 @@ module Omnizip
129
129
  # Queue flush operation (matches XZ Utils rc_flush)
130
130
  def flush
131
131
  5.times do
132
- raise "Symbol buffer overflow" if @count >= RC_SYMBOLS_MAX
132
+ raise Omnizip::CompressionError, "Symbol buffer overflow" if @count >= RC_SYMBOLS_MAX
133
133
 
134
134
  @symbols[@count] = RC_FLUSH
135
135
  @probs[@count] = nil
@@ -159,7 +159,7 @@ module Omnizip
159
159
  # @param out_size [Integer] Output buffer size
160
160
  # @return [Boolean] True if output buffer filled before encoding complete
161
161
  def encode(out, out_pos, out_size)
162
- raise "Symbol buffer overflow" if @count > RC_SYMBOLS_MAX
162
+ raise Omnizip::CompressionError, "Symbol buffer overflow" if @count > RC_SYMBOLS_MAX
163
163
 
164
164
  skip_increment = false
165
165
 
@@ -214,7 +214,7 @@ module Omnizip
214
214
  break
215
215
 
216
216
  else
217
- raise "Unknown symbol type: #{@symbols[@pos]}"
217
+ raise Omnizip::CompressionError, "Unknown symbol type: #{@symbols[@pos]}"
218
218
  end
219
219
 
220
220
  @pos += 1 unless skip_increment
@@ -288,7 +288,7 @@ module Omnizip
288
288
 
289
289
  # Forget pending symbols (matches XZ Utils rc_forget)
290
290
  def forget
291
- raise "Cannot forget while encoding" if @pos != 0
291
+ raise Omnizip::CompressionError, "Cannot forget while encoding" if @pos != 0
292
292
 
293
293
  @count = 0
294
294
  end
@@ -318,7 +318,7 @@ check_rc_finished: true)
318
318
 
319
319
  # Handle nil @pos or limit gracefully
320
320
  if limit && (@pos.nil? || limit.nil?)
321
- raise "Invalid state: @pos=#{@pos.inspect}, limit=#{limit.inspect}"
321
+ raise Omnizip::DecompressionError, "Invalid state: @pos=#{@pos.inspect}, limit=#{limit.inspect}"
322
322
  end
323
323
 
324
324
  # Circular buffer: flush output before it can be overwritten.
@@ -757,7 +757,7 @@ check_rc_finished: true)
757
757
  def read_header
758
758
  # Property byte
759
759
  props = @input.getbyte
760
- raise "Invalid LZMA header" if props.nil?
760
+ raise Omnizip::DecompressionError, "Invalid LZMA header" if props.nil?
761
761
 
762
762
  @lc = props % 9
763
763
  remainder = props / 9
@@ -768,7 +768,7 @@ check_rc_finished: true)
768
768
  @dict_size = 0
769
769
  4.times do |i|
770
770
  byte = @input.getbyte
771
- raise "Incomplete header" if byte.nil?
771
+ raise Omnizip::DecompressionError, "Incomplete header" if byte.nil?
772
772
 
773
773
  @dict_size |= (byte << (i * 8))
774
774
  end
@@ -780,7 +780,7 @@ check_rc_finished: true)
780
780
  @uncompressed_size = 0
781
781
  8.times do |i|
782
782
  byte = @input.getbyte
783
- raise "Incomplete header" if byte.nil?
783
+ raise Omnizip::DecompressionError, "Incomplete header" if byte.nil?
784
784
 
785
785
  @uncompressed_size |= (byte << (i * 8))
786
786
  end
@@ -791,9 +791,9 @@ check_rc_finished: true)
791
791
  # @return [void]
792
792
  # @raise [RuntimeError] If parameters are invalid
793
793
  def validate_parameters
794
- raise "Invalid lc (#{@lc})" unless @lc.between?(0, 8)
795
- raise "Invalid lp (#{@lp})" unless @lp.between?(0, 4)
796
- raise "Invalid pb (#{@pb})" unless @pb.between?(0, 4)
794
+ raise Omnizip::DecompressionError, "Invalid lc (#{@lc})" unless @lc.between?(0, 8)
795
+ raise Omnizip::DecompressionError, "Invalid lp (#{@lp})" unless @lp.between?(0, 4)
796
+ raise Omnizip::DecompressionError, "Invalid pb (#{@pb})" unless @pb.between?(0, 4)
797
797
  end
798
798
 
799
799
  # Initialize probability models
@@ -1201,7 +1201,7 @@ check_rc_finished: true)
1201
1201
  # @dict_full is clamped at @dict_size. Use actual bytes written for validation.
1202
1202
  actual_bytes_written = @pos - LZ_DICT_INIT_POS
1203
1203
  unless actual_bytes_written > distance
1204
- raise "Invalid rep distance: #{distance} (bytes_written: #{actual_bytes_written})"
1204
+ raise Omnizip::DecompressionError, "Invalid rep distance: #{distance} (bytes_written: #{actual_bytes_written})"
1205
1205
  end
1206
1206
 
1207
1207
  # IMPORTANT: Limit match length to not exceed uncompressed_size
@@ -84,16 +84,24 @@ module Omnizip
84
84
  end
85
85
  end
86
86
 
87
+ # The display is driven by the registry's field contract for
88
+ # :seven_zip — optional lines print only for registered fields.
87
89
  def show_seven_zip_entry(entry)
88
- puts "Entry: #{entry[:name]}"
89
- puts " Type: #{entry[:directory] ? 'Directory' : 'File'}"
90
- puts " Size: #{format_size(entry[:size])}"
90
+ registry = Omnizip::Metadata::MetadataRegistry
91
+ puts "Entry: #{entry[:name]}" if registry.supports?(:seven_zip, :name)
92
+ puts " Type: #{entry[:directory] ? 'Directory' : 'File'}" if registry.supports?(:seven_zip, :directory)
93
+ puts " Size: #{format_size(entry[:size])}" if registry.supports?(:seven_zip, :size)
91
94
  # Per-entry packed sizes are not tracked by the parser
92
- if entry[:compressed_size]&.positive?
95
+ if registry.supports?(:seven_zip, :compressed_size) &&
96
+ entry[:compressed_size]&.positive?
93
97
  puts " Compressed: #{format_size(entry[:compressed_size])}"
94
98
  end
95
- puts " Modified: #{entry[:mtime]}" if entry[:mtime]
96
- puts " CRC32: 0x#{entry[:crc].to_s(16).upcase}" if entry[:crc]
99
+ if registry.supports?(:seven_zip, :mtime) && entry[:mtime]
100
+ puts " Modified: #{entry[:mtime]}"
101
+ end
102
+ if registry.supports?(:seven_zip, :crc) && entry[:crc]
103
+ puts " CRC32: 0x#{entry[:crc].to_s(16).upcase}"
104
+ end
97
105
  end
98
106
 
99
107
  def show_metadata(archive, pattern)
@@ -136,19 +136,19 @@ module Omnizip
136
136
  # Parse LZMA header
137
137
  def parse_header
138
138
  props = @input.getbyte
139
- raise "Invalid LZMA header: missing properties" unless props
139
+ raise Omnizip::DecompressionError, "Invalid LZMA header: missing properties" unless props
140
140
 
141
141
  @lc = props % 9
142
142
  rem = props / 9
143
143
  @lp = rem % 5
144
144
  @pb = rem / 5
145
145
 
146
- raise "Invalid LZMA properties: pb=#{@pb} > 4" if @pb > 4
146
+ raise Omnizip::DecompressionError, "Invalid LZMA properties: pb=#{@pb} > 4" if @pb > 4
147
147
 
148
148
  @dict_size = 0
149
149
  4.times do |i|
150
150
  byte = @input.getbyte
151
- raise "Invalid LZMA header: missing dictionary size" unless byte
151
+ raise Omnizip::DecompressionError, "Invalid LZMA header: missing dictionary size" unless byte
152
152
 
153
153
  @dict_size |= (byte << (i * 8))
154
154
  end
@@ -159,7 +159,7 @@ module Omnizip
159
159
  @uncompressed_size = 0
160
160
  8.times do |i|
161
161
  byte = @input.getbyte
162
- raise "Invalid LZMA header: missing uncompressed size" unless byte
162
+ raise Omnizip::DecompressionError, "Invalid LZMA header: missing uncompressed size" unless byte
163
163
 
164
164
  @uncompressed_size |= (byte * (1 << (i * 8)))
165
165
  end
@@ -328,7 +328,7 @@ module Omnizip
328
328
  # Decoder returns distance before +1, so add 1 to get actual distance
329
329
  distance += 1
330
330
 
331
- raise "Invalid distance: #{distance}" if distance >= @dict_size && @dict_full
331
+ raise Omnizip::DecompressionError, "Invalid distance: #{distance}" if distance >= @dict_size && @dict_full
332
332
 
333
333
  # Update reps
334
334
  @reps[3] = @reps[2]
@@ -309,7 +309,7 @@ module Omnizip
309
309
  # @return [void]
310
310
  def encode_match(match, data)
311
311
  # Defensive check: distance must be >= 1
312
- raise "Invalid match distance: #{match.distance}" if match.distance < 1
312
+ raise Omnizip::CompressionError, "Invalid match distance: #{match.distance}" if match.distance < 1
313
313
 
314
314
  pos_state = @pos & ((1 << @pb) - 1)
315
315
 
@@ -158,7 +158,7 @@ module Omnizip
158
158
  def init_decoder
159
159
  # Read first byte (should be 0 for valid LZMA stream)
160
160
  first = read_byte
161
- raise "Invalid LZMA stream: first byte not 0" unless first.zero?
161
+ raise Omnizip::DecompressionError, "Invalid LZMA stream: first byte not 0" unless first.zero?
162
162
 
163
163
  # Read 4 bytes for initial code value
164
164
  @code = 0
@@ -108,7 +108,7 @@ module Omnizip
108
108
  when 9
109
109
  CATEGORY_SHORT_REP
110
110
  else
111
- raise "Invalid state: #{@index}"
111
+ raise Omnizip::CompressionError, "Invalid state: #{@index}"
112
112
  end
113
113
  end
114
114
 
@@ -55,10 +55,17 @@ module Omnizip
55
55
  extra_field
56
56
  ])
57
57
 
58
- # Register 7z format metadata support (limited)
58
+ # Register 7z format metadata support: the readable fields the
59
+ # handler detail contract carries (name/size/directory always
60
+ # present; compressed_size when tracked, mtime/crc when stored).
61
+ # Editing is ZIP-only — the metadata command raises truthfully.
59
62
  MetadataRegistry.register(:seven_zip, %i[
63
+ name
64
+ size
65
+ directory
66
+ compressed_size
60
67
  mtime
61
- attributes
68
+ crc
62
69
  ])
63
70
  end
64
71
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Omnizip
4
- VERSION = "0.3.54"
4
+ VERSION = "0.3.55"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: omnizip
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.54
4
+ version: 0.3.55
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.