omnizip 0.3.9 → 0.3.11

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 (182) hide show
  1. checksums.yaml +4 -4
  2. data/TODO.refactor/00-overview.md +67 -0
  3. data/TODO.refactor/01-registry-base-class.md +81 -0
  4. data/TODO.refactor/02-error-hierarchy.md +57 -0
  5. data/TODO.refactor/03-autoload-entry-point.md +69 -0
  6. data/TODO.refactor/04-filter-base-consolidation.md +49 -0
  7. data/TODO.refactor/05-send-private-methods.md +64 -0
  8. data/TODO.refactor/06-instance-variable-access.md +69 -0
  9. data/TODO.refactor/07-respond-to-replacement.md +91 -0
  10. data/TODO.refactor/08-spec-doubles.md +27 -0
  11. data/TODO.refactor/09-cli-shared-module.md +35 -0
  12. data/TODO.refactor/10-format-detector-ocp.md +34 -0
  13. data/TODO.refactor/11-convenience-decoupling.md +38 -0
  14. data/TODO.refactor/12-thread-safety.md +8 -0
  15. data/TODO.refactor/13-lutaml-model-migration.md +82 -0
  16. data/TODO.refactor/14-add-missing-specs.md +25 -0
  17. data/lib/omnizip/algorithm.rb +0 -2
  18. data/lib/omnizip/algorithm_registry.rb +29 -57
  19. data/lib/omnizip/algorithms/lzma/dictionary.rb +4 -3
  20. data/lib/omnizip/algorithms/lzma/lzip_decoder.rb +2 -0
  21. data/lib/omnizip/algorithms/lzma/range_decoder.rb +1 -1
  22. data/lib/omnizip/algorithms/lzma/xz_utils_decoder.rb +6 -5
  23. data/lib/omnizip/algorithms/lzma2/encoder.rb +0 -2
  24. data/lib/omnizip/algorithms/lzma2/lzma2_chunk.rb +0 -2
  25. data/lib/omnizip/algorithms/lzma2/properties.rb +0 -2
  26. data/lib/omnizip/algorithms/ppmd7/model.rb +1 -1
  27. data/lib/omnizip/algorithms/ppmd8/context.rb +0 -2
  28. data/lib/omnizip/algorithms/ppmd8/model.rb +1 -1
  29. data/lib/omnizip/algorithms/ppmd8.rb +0 -2
  30. data/lib/omnizip/algorithms/ppmd_base.rb +0 -2
  31. data/lib/omnizip/algorithms/sevenzip_lzma2.rb +0 -5
  32. data/lib/omnizip/algorithms/xz_lzma2.rb +0 -5
  33. data/lib/omnizip/algorithms/zstandard/frame/header.rb +3 -5
  34. data/lib/omnizip/algorithms.rb +1 -0
  35. data/lib/omnizip/archive_handler.rb +75 -0
  36. data/lib/omnizip/archive_handlers/tar_handler.rb +51 -0
  37. data/lib/omnizip/archive_handlers/zip_handler.rb +73 -0
  38. data/lib/omnizip/archive_handlers.rb +11 -0
  39. data/lib/omnizip/checksum_registry.rb +23 -96
  40. data/lib/omnizip/cli/shared.rb +31 -0
  41. data/lib/omnizip/cli.rb +6 -43
  42. data/lib/omnizip/commands/archive_create_command.rb +0 -6
  43. data/lib/omnizip/commands/archive_extract_command.rb +0 -4
  44. data/lib/omnizip/commands/archive_list_command.rb +0 -4
  45. data/lib/omnizip/commands/archive_repair_command.rb +0 -2
  46. data/lib/omnizip/commands/archive_verify_command.rb +0 -2
  47. data/lib/omnizip/commands/compress_command.rb +0 -2
  48. data/lib/omnizip/commands/decompress_command.rb +0 -2
  49. data/lib/omnizip/commands/list_command.rb +0 -2
  50. data/lib/omnizip/commands/metadata_command.rb +0 -5
  51. data/lib/omnizip/commands/parity_create_command.rb +0 -2
  52. data/lib/omnizip/commands/parity_repair_command.rb +0 -2
  53. data/lib/omnizip/commands/parity_verify_command.rb +0 -2
  54. data/lib/omnizip/commands/profile_list_command.rb +0 -2
  55. data/lib/omnizip/commands/profile_show_command.rb +0 -2
  56. data/lib/omnizip/convenience.rb +111 -234
  57. data/lib/omnizip/converter/seven_zip_to_zip_strategy.rb +1 -1
  58. data/lib/omnizip/crypto/aes256/cipher.rb +6 -1
  59. data/lib/omnizip/error.rb +22 -18
  60. data/lib/omnizip/filter.rb +5 -3
  61. data/lib/omnizip/filter_registry.rb +74 -98
  62. data/lib/omnizip/filters/bcj2.rb +0 -2
  63. data/lib/omnizip/filters/bcj_x86.rb +0 -2
  64. data/lib/omnizip/filters/filter_base.rb +4 -70
  65. data/lib/omnizip/filters/registry.rb +0 -2
  66. data/lib/omnizip/format_detector.rb +21 -12
  67. data/lib/omnizip/format_registry.rb +69 -53
  68. data/lib/omnizip/formats/bzip2_file.rb +0 -4
  69. data/lib/omnizip/formats/cpio/entry.rb +0 -2
  70. data/lib/omnizip/formats/cpio/reader.rb +0 -1
  71. data/lib/omnizip/formats/cpio/writer.rb +0 -1
  72. data/lib/omnizip/formats/cpio.rb +0 -3
  73. data/lib/omnizip/formats/format_spec_loader.rb +1 -1
  74. data/lib/omnizip/formats/gzip.rb +0 -4
  75. data/lib/omnizip/formats/iso/directory_record.rb +6 -3
  76. data/lib/omnizip/formats/iso/reader.rb +2 -2
  77. data/lib/omnizip/formats/iso.rb +0 -1
  78. data/lib/omnizip/formats/lzip.rb +5 -27
  79. data/lib/omnizip/formats/lzma_alone.rb +7 -27
  80. data/lib/omnizip/formats/msi/reader.rb +1 -0
  81. data/lib/omnizip/formats/msi/string_pool.rb +2 -3
  82. data/lib/omnizip/formats/msi.rb +1 -6
  83. data/lib/omnizip/formats/ole/allocation_table.rb +0 -1
  84. data/lib/omnizip/formats/ole/dirent.rb +9 -4
  85. data/lib/omnizip/formats/ole/header.rb +0 -1
  86. data/lib/omnizip/formats/ole/ranges_io.rb +1 -1
  87. data/lib/omnizip/formats/ole/storage.rb +5 -6
  88. data/lib/omnizip/formats/ole.rb +0 -3
  89. data/lib/omnizip/formats/rar/block_parser.rb +0 -1
  90. data/lib/omnizip/formats/rar/compression/ppmd/encoder.rb +1 -1
  91. data/lib/omnizip/formats/rar/decompressor.rb +1 -0
  92. data/lib/omnizip/formats/rar/header.rb +0 -1
  93. data/lib/omnizip/formats/rar/rar5/writer.rb +0 -3
  94. data/lib/omnizip/formats/rar/reader.rb +2 -3
  95. data/lib/omnizip/formats/rar/writer.rb +0 -1
  96. data/lib/omnizip/formats/rar.rb +0 -4
  97. data/lib/omnizip/formats/rpm/entry.rb +0 -1
  98. data/lib/omnizip/formats/rpm/header.rb +1 -6
  99. data/lib/omnizip/formats/rpm/lead.rb +37 -26
  100. data/lib/omnizip/formats/rpm/tag.rb +0 -1
  101. data/lib/omnizip/formats/rpm/writer.rb +0 -1
  102. data/lib/omnizip/formats/seven_zip/bcj2_stream_decompressor.rb +0 -1
  103. data/lib/omnizip/formats/seven_zip/coder_chain.rb +1 -2
  104. data/lib/omnizip/formats/seven_zip/encoded_header.rb +0 -1
  105. data/lib/omnizip/formats/seven_zip/encrypted_header.rb +0 -1
  106. data/lib/omnizip/formats/seven_zip/header.rb +0 -1
  107. data/lib/omnizip/formats/seven_zip/header_encryptor.rb +0 -1
  108. data/lib/omnizip/formats/seven_zip/header_writer.rb +0 -1
  109. data/lib/omnizip/formats/seven_zip/models/file_entry.rb +1 -1
  110. data/lib/omnizip/formats/seven_zip/parser.rb +1 -2
  111. data/lib/omnizip/formats/seven_zip/reader.rb +13 -2
  112. data/lib/omnizip/formats/seven_zip/split_archive_reader.rb +0 -1
  113. data/lib/omnizip/formats/seven_zip/split_archive_writer.rb +0 -1
  114. data/lib/omnizip/formats/seven_zip/writer.rb +4 -6
  115. data/lib/omnizip/formats/seven_zip.rb +2 -5
  116. data/lib/omnizip/formats/tar/entry.rb +0 -2
  117. data/lib/omnizip/formats/tar/header.rb +0 -2
  118. data/lib/omnizip/formats/tar/reader.rb +0 -2
  119. data/lib/omnizip/formats/tar/writer.rb +0 -2
  120. data/lib/omnizip/formats/tar.rb +0 -3
  121. data/lib/omnizip/formats/xar/entry.rb +0 -1
  122. data/lib/omnizip/formats/xar/header.rb +0 -1
  123. data/lib/omnizip/formats/xar/reader.rb +0 -1
  124. data/lib/omnizip/formats/xar/toc.rb +1 -2
  125. data/lib/omnizip/formats/xar/writer.rb +11 -3
  126. data/lib/omnizip/formats/xar.rb +0 -3
  127. data/lib/omnizip/formats/xz/reader.rb +7 -3
  128. data/lib/omnizip/formats/xz.rb +3 -40
  129. data/lib/omnizip/formats/xz_impl/block_decoder.rb +4 -10
  130. data/lib/omnizip/formats/xz_impl/block_encoder.rb +0 -2
  131. data/lib/omnizip/formats/xz_impl/block_header_parser.rb +0 -2
  132. data/lib/omnizip/formats/xz_impl/index_decoder.rb +0 -1
  133. data/lib/omnizip/formats/xz_impl/index_encoder.rb +0 -1
  134. data/lib/omnizip/formats/xz_impl/stream_encoder.rb +1 -1
  135. data/lib/omnizip/formats/xz_impl/stream_footer.rb +0 -1
  136. data/lib/omnizip/formats/xz_impl/stream_footer_parser.rb +0 -1
  137. data/lib/omnizip/formats/xz_impl/stream_header.rb +0 -1
  138. data/lib/omnizip/formats/xz_impl/stream_header_parser.rb +0 -2
  139. data/lib/omnizip/formats/zip/central_directory_header.rb +2 -3
  140. data/lib/omnizip/formats/zip/end_of_central_directory.rb +0 -2
  141. data/lib/omnizip/formats/zip/local_file_header.rb +0 -2
  142. data/lib/omnizip/formats/zip/reader.rb +12 -2
  143. data/lib/omnizip/formats/zip/writer.rb +73 -1
  144. data/lib/omnizip/formats/zip/zip64_end_of_central_directory.rb +0 -2
  145. data/lib/omnizip/formats/zip/zip64_end_of_central_directory_locator.rb +0 -2
  146. data/lib/omnizip/formats/zip/zip64_extra_field.rb +0 -2
  147. data/lib/omnizip/formats/zip.rb +0 -3
  148. data/lib/omnizip/formats.rb +8 -24
  149. data/lib/omnizip/implementations/seven_zip/lzma/range_decoder.rb +2 -2
  150. data/lib/omnizip/implementations/seven_zip/lzma/range_encoder.rb +1 -1
  151. data/lib/omnizip/implementations/seven_zip/lzma2/encoder.rb +0 -6
  152. data/lib/omnizip/implementations/xz_utils/lzma2/decoder.rb +3 -4
  153. data/lib/omnizip/implementations/xz_utils/lzma2/encoder.rb +0 -1
  154. data/lib/omnizip/io/source.rb +136 -0
  155. data/lib/omnizip/io.rb +2 -0
  156. data/lib/omnizip/link_handler.rb +7 -3
  157. data/lib/omnizip/metadata/metadata_editor.rb +1 -1
  158. data/lib/omnizip/models/algorithm_metadata.rb +33 -60
  159. data/lib/omnizip/models/compression_options.rb +40 -53
  160. data/lib/omnizip/models/conversion_options.rb +9 -18
  161. data/lib/omnizip/models/parallel_options.rb +25 -0
  162. data/lib/omnizip/optimization_registry.rb +8 -44
  163. data/lib/omnizip/parallel/job_scheduler.rb +26 -8
  164. data/lib/omnizip/parallel/parallel_compressor.rb +10 -20
  165. data/lib/omnizip/parallel/parallel_extractor.rb +20 -11
  166. data/lib/omnizip/parity/par2_repairer.rb +5 -5
  167. data/lib/omnizip/parity/par2_verifier.rb +27 -22
  168. data/lib/omnizip/parity.rb +4 -4
  169. data/lib/omnizip/password/encryption_registry.rb +11 -50
  170. data/lib/omnizip/password/zip_crypto_strategy.rb +1 -4
  171. data/lib/omnizip/pipe/stream_decompressor.rb +2 -3
  172. data/lib/omnizip/profile/archive_profile.rb +0 -2
  173. data/lib/omnizip/profile/binary_profile.rb +0 -2
  174. data/lib/omnizip/profile/compression_profile.rb +0 -1
  175. data/lib/omnizip/profile/profile_detector.rb +0 -2
  176. data/lib/omnizip/profile/text_profile.rb +0 -2
  177. data/lib/omnizip/profile.rb +0 -2
  178. data/lib/omnizip/registry.rb +112 -0
  179. data/lib/omnizip/version.rb +1 -1
  180. data/lib/omnizip/zip/file.rb +20 -9
  181. data/lib/omnizip.rb +26 -58
  182. metadata +24 -2
@@ -1,59 +1,25 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Omnizip
4
- # Registry for performance optimization strategies using the Registry pattern
5
- class OptimizationRegistry
4
+ class OptimizationRegistry < Omnizip::Registry
6
5
  class << self
7
- # Register an optimization strategy
8
- def register(name, strategy_class)
9
- strategies[name] = strategy_class
6
+ def not_found_error_class
7
+ Omnizip::OptimizationNotFoundError
10
8
  end
11
9
 
12
- # Get an optimization strategy by name
13
- def get(name)
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
- strategy_class = get(name)
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
- strategy_class = get(name)
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
- # Override in subclasses to implement optimization logic
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,
@@ -49,7 +49,7 @@ module Omnizip
49
49
  # @return [Float] estimated seconds to completion
50
50
  def estimate_completion_time(jobs, worker_count:,
51
51
  bytes_per_second: 10_000_000)
52
- total_bytes = jobs.sum { |job| job.respond_to?(:size) ? job.size : 0 }
52
+ total_bytes = jobs.sum { |job| job_size(job) }
53
53
  return 0.0 if total_bytes.zero? || worker_count.zero?
54
54
 
55
55
  # Simple estimate: total bytes / (workers * rate)
@@ -65,7 +65,7 @@ bytes_per_second: 10_000_000)
65
65
 
66
66
  # Calculate total size per worker
67
67
  worker_sizes = assignments.transform_values do |jobs|
68
- jobs.sum { |job| job.respond_to?(:size) ? job.size : 1 }
68
+ jobs.sum { |job| job_size(job) }
69
69
  end
70
70
 
71
71
  sizes = worker_sizes.values
@@ -77,6 +77,24 @@ bytes_per_second: 10_000_000)
77
77
 
78
78
  private
79
79
 
80
+ # Job size helper. Returns the job's #size if defined, else 1
81
+ # (counts as one unit of work). Centralized so the rest of the
82
+ # scheduler doesn't need +respond_to?+ checks.
83
+ #
84
+ # @param job [Object]
85
+ # @return [Integer]
86
+ def job_size(job)
87
+ if job.is_a?(::Numeric)
88
+ job
89
+ else
90
+ begin
91
+ job.size
92
+ rescue StandardError
93
+ nil
94
+ end || 1
95
+ end
96
+ end
97
+
80
98
  # Validate scheduling strategy
81
99
  #
82
100
  # @raise [ArgumentError] if strategy is invalid
@@ -116,7 +134,7 @@ bytes_per_second: 10_000_000)
116
134
 
117
135
  # Sort jobs by size (largest first) for better balance
118
136
  sorted_jobs = jobs.sort_by do |job|
119
- -(job.respond_to?(:size) ? job.size : 0)
137
+ -job_size(job)
120
138
  end
121
139
 
122
140
  # Initialize worker assignments
@@ -125,14 +143,14 @@ bytes_per_second: 10_000_000)
125
143
 
126
144
  # Assign each job to worker with smallest current load
127
145
  sorted_jobs.each do |job|
128
- job_size = job.respond_to?(:size) ? job.size : 1
146
+ job_size_value = job_size(job)
129
147
 
130
148
  # Find worker with minimum load
131
149
  min_worker = worker_loads.each_with_index.min_by { |load, _| load }[1]
132
150
 
133
151
  # Assign job to this worker
134
152
  assignments[min_worker] << job
135
- worker_loads[min_worker] += job_size
153
+ worker_loads[min_worker] += job_size_value
136
154
  end
137
155
 
138
156
  # Add metadata
@@ -175,19 +193,19 @@ bytes_per_second: 10_000_000)
175
193
 
176
194
  # Sort jobs by size (largest first)
177
195
  sorted_jobs = jobs.sort_by do |job|
178
- -(job.respond_to?(:size) ? job.size : 0)
196
+ -job_size(job)
179
197
  end
180
198
 
181
199
  # First-fit decreasing bin packing
182
200
  bins = Array.new(worker_count) { { jobs: [], total_size: 0 } }
183
201
 
184
202
  sorted_jobs.each do |job|
185
- job_size = job.respond_to?(:size) ? job.size : 1
203
+ job_size_value = job_size(job)
186
204
 
187
205
  # Find bin with minimum total size
188
206
  min_bin = bins.min_by { |bin| bin[:total_size] }
189
207
  min_bin[:jobs] << job
190
- min_bin[:total_size] += job_size
208
+ min_bin[:total_size] += job_size_value
191
209
  end
192
210
 
193
211
  # Convert to standard format
@@ -131,11 +131,7 @@ level: 6)
131
131
  when Omnizip::Models::ParallelOptions
132
132
  options.dup
133
133
  when Hash
134
- Omnizip::Models::ParallelOptions.new.tap do |opts|
135
- options.each do |k, v|
136
- opts.send(:"#{k}=", v) if opts.respond_to?(:"#{k}=")
137
- end
138
- end
134
+ Omnizip::Models::ParallelOptions.new.apply(options)
139
135
  else
140
136
  Omnizip::Models::ParallelOptions.new
141
137
  end
@@ -302,26 +298,20 @@ level: 6)
302
298
  result = work_result.result
303
299
  next unless result
304
300
 
305
- # Add compressed entry to writer
306
- entry = writer.send(:create_entry,
307
- filename: result[:archive_path],
308
- uncompressed_data: "",
309
- stat: result[:stat])
310
-
311
- # Override with pre-compressed data
312
- entry[:compressed_size] = result[:compressed_size]
313
- entry[:uncompressed_size] = result[:uncompressed_size]
314
- entry[:crc32] = result[:crc32]
315
- entry[:compressed_data] = result[:compressed_data]
316
-
317
- writer.instance_variable_get(:@entries) << entry
301
+ writer.add_precompressed_entry(
302
+ filename: result[:archive_path],
303
+ uncompressed_size: result[:uncompressed_size],
304
+ compressed_size: result[:compressed_size],
305
+ crc32: result[:crc32],
306
+ compressed_data: result[:compressed_data],
307
+ stat: result[:stat],
308
+ )
318
309
 
319
310
  @stats[:bytes_processed] += result[:uncompressed_size]
320
311
  @stats[:bytes_compressed] += result[:compressed_size]
321
312
  end
322
313
 
323
- # Write with pre-compressed data
324
- writer.send(:write_with_precompressed_data, compression)
314
+ writer.write_precompressed(compression_method: compression)
325
315
  end
326
316
 
327
317
  # Calculate compression ratio
@@ -64,7 +64,7 @@ module Omnizip
64
64
  dest_path: dest_path,
65
65
  data: data,
66
66
  directory: entry.directory?,
67
- unix_perms: entry.respond_to?(:unix_perms) ? entry.unix_perms : 0,
67
+ unix_perms: safe_unix_perms(entry),
68
68
  },
69
69
  work: work,
70
70
  )
@@ -107,10 +107,11 @@ module Omnizip
107
107
  return "" unless compressed_data
108
108
 
109
109
  # Decompress
110
- reader.send(:decompress_data,
111
- compressed_data,
112
- reader_entry.compression_method,
113
- reader_entry.uncompressed_size)
110
+ reader.decompress(
111
+ compressed_data,
112
+ reader_entry.compression_method,
113
+ reader_entry.uncompressed_size,
114
+ )
114
115
  end
115
116
  end
116
117
  end
@@ -130,11 +131,7 @@ module Omnizip
130
131
  when Omnizip::Models::ParallelOptions
131
132
  options.dup
132
133
  when Hash
133
- Omnizip::Models::ParallelOptions.new.tap do |opts|
134
- options.each do |k, v|
135
- opts.send(:"#{k}=", v) if opts.respond_to?(:"#{k}=")
136
- end
137
- end
134
+ Omnizip::Models::ParallelOptions.new.apply(options)
138
135
  else
139
136
  Omnizip::Models::ParallelOptions.new
140
137
  end
@@ -182,7 +179,7 @@ module Omnizip
182
179
 
183
180
  # Schedule jobs
184
181
  entries.each do |entry|
185
- file_size = entry.respond_to?(:size) ? entry.size : 0
182
+ file_size = safe_entry_size(entry)
186
183
 
187
184
  job_queue.push_with_size(
188
185
  file: entry.name,
@@ -256,6 +253,18 @@ module Omnizip
256
253
 
257
254
  private
258
255
 
256
+ def safe_unix_perms(entry)
257
+ entry.unix_perms
258
+ rescue NoMethodError
259
+ 0
260
+ end
261
+
262
+ def safe_entry_size(entry)
263
+ entry.size
264
+ rescue NoMethodError
265
+ 0
266
+ end
267
+
259
268
  # Read archive entries
260
269
  #
261
270
  # @param archive_path [String] archive path
@@ -162,7 +162,7 @@ module Omnizip
162
162
  blocks = []
163
163
  block_size = @verifier.metadata[:block_size]
164
164
 
165
- file_list = @verifier.instance_variable_get(:@file_list)
165
+ file_list = @verifier.file_list
166
166
 
167
167
  # Get list of damaged/missing files from verification
168
168
  damaged_files = verification.damaged_files
@@ -171,7 +171,7 @@ module Omnizip
171
171
  block_index = 0
172
172
 
173
173
  file_list.each_with_index do |file_info, _file_idx|
174
- file_path = @verifier.send(:find_file_path, file_info[:filename])
174
+ file_path = @verifier.find_file_path(file_info[:filename])
175
175
  (file_info[:size].to_f / block_size).ceil
176
176
 
177
177
  # Treat damaged files same as missing files - don't read corrupted data
@@ -212,7 +212,7 @@ module Omnizip
212
212
  #
213
213
  # @return [Hash] Map of exponent => recovery_block_data
214
214
  def load_parity_blocks_by_exponent
215
- recovery_blocks = @verifier.instance_variable_get(:@recovery_blocks)
215
+ recovery_blocks = @verifier.recovery_blocks
216
216
 
217
217
  # Group by exponent and take first block for each
218
218
  # (PAR2 can have multiple slices with same exponent for large data)
@@ -234,7 +234,7 @@ module Omnizip
234
234
 
235
235
  # Build file-to-blocks mapping using verifier's file list
236
236
  # Note: @verifier was populated by the verify() call in repair()
237
- file_list = @verifier.instance_variable_get(:@file_list)
237
+ file_list = @verifier.file_list
238
238
  block_size = @verifier.metadata[:block_size]
239
239
 
240
240
  # Ensure metadata is loaded
@@ -281,7 +281,7 @@ module Omnizip
281
281
  output_dir ||= File.dirname(@par2_file)
282
282
  FileUtils.mkdir_p(output_dir)
283
283
 
284
- @verifier.instance_variable_get(:@file_list).each do |file_info|
284
+ @verifier.file_list.each do |file_info|
285
285
  num_blocks = (file_info[:size].to_f / @verifier.metadata[:block_size]).ceil
286
286
  file_blocks_range = (block_idx...(block_idx + num_blocks)).to_a
287
287
 
@@ -58,6 +58,12 @@ module Omnizip
58
58
  # @return [Hash] Parsed PAR2 metadata
59
59
  attr_reader :metadata
60
60
 
61
+ # @return [Array<Hash>] Parsed file list (one entry per managed file)
62
+ attr_reader :file_list
63
+
64
+ # @return [Array] Available recovery blocks
65
+ attr_reader :recovery_blocks
66
+
61
67
  # Initialize verifier
62
68
  #
63
69
  # @param par2_file [String] Path to .par2 index file
@@ -125,11 +131,11 @@ module Omnizip
125
131
  )
126
132
  end
127
133
 
128
- private
129
-
130
- # Parse PAR2 index file using packet models
134
+ # Parse the PAR2 index file and populate +metadata+, +file_list+,
135
+ # and +recovery_blocks+. Idempotent — safe to call multiple times.
136
+ #
137
+ # @return [void]
131
138
  def parse_par2_file
132
- # Reset state to prevent accumulation if called multiple times
133
139
  @metadata = {}
134
140
  @file_list = []
135
141
  @recovery_blocks = []
@@ -159,24 +165,6 @@ module Omnizip
159
165
  load_recovery_volumes
160
166
  end
161
167
 
162
- # Process packet using model-based approach
163
- #
164
- # @param packet [Models::Packet] Parsed packet model
165
- def process_packet_model(packet)
166
- case packet
167
- when Models::MainPacket
168
- process_main_packet_model(packet)
169
- when Models::FileDescriptionPacket
170
- process_file_description_packet_model(packet)
171
- when Models::IfscPacket
172
- process_ifsc_packet_model(packet)
173
- when Models::RecoverySlicePacket
174
- process_recovery_packet_model(packet)
175
- when Models::CreatorPacket
176
- # Creator packets are informational only
177
- end
178
- end
179
-
180
168
  # Process main packet model
181
169
  #
182
170
  # @param packet [Models::MainPacket] Main packet
@@ -358,6 +346,23 @@ module Omnizip
358
346
  (file_info[:size].to_f / @metadata[:block_size]).ceil
359
347
  end
360
348
  end
349
+
350
+ private
351
+
352
+ def process_packet_model(packet)
353
+ case packet
354
+ when Models::MainPacket
355
+ process_main_packet_model(packet)
356
+ when Models::FileDescriptionPacket
357
+ process_file_description_packet_model(packet)
358
+ when Models::IfscPacket
359
+ process_ifsc_packet_model(packet)
360
+ when Models::RecoverySlicePacket
361
+ process_recovery_packet_model(packet)
362
+ when Models::CreatorPacket
363
+ # Creator packets are informational only
364
+ end
365
+ end
361
366
  end
362
367
  end
363
368
  end
@@ -175,16 +175,16 @@ module Omnizip
175
175
  return nil unless File.exist?(par2_file)
176
176
 
177
177
  verifier = Par2Verifier.new(par2_file)
178
- verifier.send(:parse_par2_file)
178
+ verifier.parse_par2_file
179
179
 
180
- total_blocks = verifier.send(:calculate_total_blocks)
181
- recovery_blocks = verifier.instance_variable_get(:@recovery_blocks).size
180
+ total_blocks = verifier.calculate_total_blocks
181
+ recovery_blocks = verifier.recovery_blocks.size
182
182
 
183
183
  {
184
184
  par2_file: par2_file,
185
185
  block_size: verifier.metadata[:block_size],
186
186
  total_blocks: total_blocks,
187
- file_count: verifier.metadata[:file_count] || verifier.instance_variable_get(:@file_list).size,
187
+ file_count: verifier.metadata[:file_count] || verifier.file_list.size,
188
188
  recovery_blocks: recovery_blocks,
189
189
  redundancy: calculate_redundancy(total_blocks, recovery_blocks),
190
190
  }
@@ -2,64 +2,25 @@
2
2
 
3
3
  module Omnizip
4
4
  module Password
5
- # Registry for encryption strategies
6
- class EncryptionRegistry
7
- @strategies = {}
8
-
5
+ class EncryptionRegistry < Omnizip::Registry
9
6
  class << self
10
- # Register an encryption strategy
11
- # @param name [Symbol] Strategy name
12
- # @param strategy_class [Class] Strategy class
13
- def register(name, strategy_class)
14
- @strategies[name] = strategy_class
15
- end
16
-
17
- # Get a strategy by name
18
- # @param name [Symbol] Strategy name
19
- # @return [Class] Strategy class
20
- # @raise [ArgumentError] If strategy not found
21
- def get(name)
22
- strategy = @strategies[name]
23
- return strategy if strategy
24
-
25
- raise ArgumentError, "Unknown encryption strategy: #{name}. " \
26
- "Available: #{@strategies.keys.join(', ')}"
27
- end
28
-
29
- # Check if strategy is registered
30
- # @param name [Symbol] Strategy name
31
- # @return [Boolean] True if registered
32
- def registered?(name)
33
- @strategies.key?(name)
7
+ def not_found_error_class
8
+ Omnizip::UnknownEncryptionStrategyError
34
9
  end
35
10
 
36
- # Get all registered strategy names
37
- # @return [Array<Symbol>] Strategy names
38
- def strategies
39
- @strategies.keys
11
+ def label
12
+ "Encryption strategy"
40
13
  end
41
14
 
42
- # Create a strategy instance
43
- # @param name [Symbol] Strategy name
44
- # @param password [String] Password
45
- # @param options [Hash] Strategy options
46
- # @return [EncryptionStrategy] Strategy instance
47
15
  def create(name, password, **options)
48
- strategy_class = get(name)
49
- strategy_class.new(password, **options)
50
- end
51
-
52
- # Reset registry (for testing)
53
- def reset
54
- @strategies = {}
16
+ get(name).new(password, **options)
55
17
  end
56
18
  end
57
19
  end
58
-
59
- # Register built-in strategies
60
- EncryptionRegistry.register(:traditional, ZipCryptoStrategy)
61
- EncryptionRegistry.register(:zip_crypto, ZipCryptoStrategy)
62
- EncryptionRegistry.register(:winzip_aes, WinzipAesStrategy)
63
- EncryptionRegistry.register(:aes256, WinzipAesStrategy)
64
20
  end
65
21
  end
22
+
23
+ Omnizip::Password::EncryptionRegistry.register(:traditional, Omnizip::Password::ZipCryptoStrategy)
24
+ Omnizip::Password::EncryptionRegistry.register(:zip_crypto, Omnizip::Password::ZipCryptoStrategy)
25
+ Omnizip::Password::EncryptionRegistry.register(:winzip_aes, Omnizip::Password::WinzipAesStrategy)
26
+ Omnizip::Password::EncryptionRegistry.register(:aes256, Omnizip::Password::WinzipAesStrategy)
@@ -114,10 +114,7 @@ module Omnizip
114
114
  # @param byte [Integer] Byte to add
115
115
  # @return [Integer] Updated CRC
116
116
  def crc32_update(crc, byte)
117
- crc32 = Omnizip::Checksums::Crc32.new
118
- crc32.instance_variable_set(:@crc, crc)
119
- crc32.update(byte.chr)
120
- crc32.value
117
+ Omnizip::Checksums::Crc32.new.process_byte(crc, byte)
121
118
  end
122
119
 
123
120
  # Generate encryption header
@@ -163,9 +163,8 @@ module Omnizip
163
163
  end
164
164
 
165
165
  # Preserve timestamp if available
166
- if entry.respond_to?(:mtime) && entry.mtime
167
- File.utime(entry.mtime, entry.mtime, dest_path)
168
- end
166
+ mtime = entry.mtime if entry.is_a?(Omnizip::Metadata::EntryMetadata)
167
+ File.utime(mtime, mtime, dest_path) if mtime
169
168
 
170
169
  bytes
171
170
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "omnizip/file_type/mime_classifier"
4
-
5
3
  module Omnizip
6
4
  module Profile
7
5
  # Archive compression profile
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "omnizip/file_type/mime_classifier"
4
-
5
3
  module Omnizip
6
4
  module Profile
7
5
  # Binary compression profile
@@ -148,7 +148,6 @@ module Omnizip
148
148
 
149
149
  # Auto-select BCJ filter based on MIME type
150
150
  # For executables, default to x86 architecture
151
- require "omnizip/file_type/mime_classifier"
152
151
  if FileType::MimeClassifier.executable?(mime_type)
153
152
  :bcj_x86
154
153
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "omnizip/file_type/mime_classifier"
4
-
5
3
  module Omnizip
6
4
  module Profile
7
5
  # Automatic profile detection based on file analysis
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "omnizip/file_type/mime_classifier"
4
-
5
3
  module Omnizip
6
4
  module Profile
7
5
  # Text compression profile
@@ -161,8 +161,6 @@ module Omnizip
161
161
  # @param mime_type [String] MIME type string
162
162
  # @return [CompressionProfile] Best profile
163
163
  def select_best_profile_for_mime(profiles, mime_type)
164
- require "omnizip/file_type/mime_classifier"
165
-
166
164
  # Determine category from MIME type
167
165
  category = FileType::MimeClassifier.profile_category(mime_type)
168
166