omnizip 0.3.9 → 0.3.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 -2
  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 -1
  73. data/lib/omnizip/formats/format_spec_loader.rb +1 -1
  74. data/lib/omnizip/formats/gzip.rb +0 -2
  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 -25
  79. data/lib/omnizip/formats/lzma_alone.rb +7 -25
  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 -4
  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 -1
  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 -1
  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 -4
  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 -1
  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 -1
  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 -1
  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,133 +1,109 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- #
4
- # Copyright (C) 2024 Ribose Inc.
5
- #
6
- # This file is part of Omnizip.
7
- #
8
- # Omnizip is a pure Ruby port of 7-Zip compression algorithms.
9
- # Based on the 7-Zip LZMA SDK by Igor Pavlov.
10
- #
11
- # This library is free software; you can redistribute it and/or
12
- # modify it under the terms of the GNU Lesser General Public
13
- # License as published by the Free Software Foundation; either
14
- # version 2.1 of the License, or (at your option) any later version.
15
- #
16
- # See the COPYING file for the complete text of the license.
17
- #
18
-
19
3
  module Omnizip
20
- # Registry for managing filter classes.
21
- #
22
- # This class provides a centralized registry for preprocessing filters,
23
- # allowing filters to self-register and be retrieved by name.
24
- # It implements a plugin-style architecture for extensibility.
25
- class FilterRegistry
26
- @filters = {}
4
+ class FilterRegistry < Omnizip::Registry
5
+ DEFAULT_FORMATS = %i[xz seven_zip].freeze
6
+
7
+ 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",
17
+ }.freeze
27
18
 
28
19
  class << self
29
- # Register a filter class with format support.
30
- #
31
- # @param name [Symbol, String] The name identifier for the filter
32
- # @param filter_class [Class] The filter class to register
33
- # @param formats [Array<Symbol>] Supported formats (default: [:xz,
34
- # :seven_zip])
35
- # @return [void]
36
- def register(name, filter_class, formats: %i[xz seven_zip])
20
+ def not_found_error_class
21
+ Omnizip::UnknownFilterError
22
+ end
23
+
24
+ def label
25
+ "Filter"
26
+ end
27
+
28
+ def register(name, filter_class, formats: DEFAULT_FORMATS)
37
29
  raise ArgumentError, "Filter name cannot be nil" if name.nil?
38
30
  raise ArgumentError, "Filter class cannot be nil" if filter_class.nil?
39
31
 
40
- @filters[name.to_sym] = {
41
- class: filter_class,
42
- formats: formats,
43
- }
32
+ synchronize do
33
+ storage[normalize_key(name)] = {
34
+ class: filter_class,
35
+ formats: formats,
36
+ }
37
+ end
38
+ filter_class
44
39
  end
45
40
  alias register_with_formats register
46
41
 
47
- # Retrieve a filter class by name.
48
- #
49
- # @param name [Symbol, String] The name identifier for the filter
50
- # @raise [UnknownFilterError] If filter is not registered
51
- # @return [Class] The registered filter class
52
42
  def get(name)
53
- filter = @filters[name.to_sym]
54
- unless filter
55
- raise UnknownFilterError,
56
- "Unknown filter: #{name}. " \
43
+ entry = entry_for(name)
44
+ unless entry
45
+ raise not_found_error_class,
46
+ "#{label} not registered: #{name.inspect}. " \
57
47
  "Available: #{available.join(', ')}"
58
48
  end
59
49
 
60
- filter[:class]
50
+ entry[:class]
61
51
  end
62
52
 
63
- # Check if a filter is registered.
64
- #
65
- # @param name [Symbol, String] The name identifier for the filter
66
- # @return [Boolean] True if filter is registered, false otherwise
67
- def registered?(name)
68
- @filters.key?(name.to_sym)
69
- end
70
-
71
- # Get list of all registered filter names.
72
- #
73
- # @return [Array<Symbol>] Array of registered filter names
74
- def available
75
- @filters.keys
76
- end
77
-
78
- # Reset the registry (primarily for testing).
79
- #
80
- # @return [void]
81
- def reset!
82
- @filters.clear
83
- end
84
-
85
- # Get filter instance for specific format.
86
- #
87
- # Returns a new filter instance after verifying the filter supports
88
- # the specified format.
89
- #
90
- # @param name [Symbol, String] The name identifier for the filter
91
- # @param format [Symbol] Format identifier (:xz, :seven_zip)
92
- # @raise [KeyError] If filter is not registered
93
- # @raise [ArgumentError] If filter doesn't support the format
94
- # @return [Object] New filter instance
95
53
  def get_for_format(name, format)
96
- filter_info = @filters[name.to_sym]
97
- raise KeyError, "Filter not found: #{name}" unless filter_info
54
+ entry = entry_for(name)
55
+ raise KeyError, "Filter not found: #{name}" unless entry
98
56
 
99
- unless filter_info[:formats].include?(format)
57
+ unless entry[:formats].include?(format)
100
58
  raise ArgumentError,
101
59
  "Filter #{name} not supported for format #{format}"
102
60
  end
103
61
 
104
- filter_info[:class].new
62
+ entry[:class].new
105
63
  end
106
64
 
107
- # Check if filter supports specific format.
108
- #
109
- # @param name [Symbol, String] The name identifier for the filter
110
- # @param format [Symbol] Format identifier
111
- # @return [Boolean] True if filter supports the format
112
65
  def supports_format?(name, format)
113
- return false unless @filters[name.to_sym]
66
+ entry = entry_for(name)
67
+ return false unless entry
114
68
 
115
- filter_info = @filters[name.to_sym]
116
- filter_info[:formats]&.include?(format)
69
+ entry[:formats]&.include?(format)
117
70
  end
118
71
 
119
- # Get all filters supporting a specific format.
120
- #
121
- # @param format [Symbol] Format identifier
122
- # @return [Array<Symbol>] Filter names supporting the format
123
72
  def filters_for_format(format)
124
- @filters.select do |_, info|
125
- info[:formats]&.include?(format)
126
- end.keys
73
+ # Ensure all lazy triggers have fired so the format filter list
74
+ # reflects every registered builtin.
75
+ lazy_triggers.keys.dup.each { |name| get(name) }
76
+
77
+ storage.select { |_, info| info[:formats]&.include?(format) }.keys
78
+ end
79
+
80
+ private
81
+
82
+ def entry_for(name)
83
+ normalized = normalize_key(name)
84
+ entry = storage[normalized]
85
+ return entry if entry
86
+
87
+ trigger = lazy_triggers[normalized]
88
+ if trigger
89
+ synchronize { lazy_triggers.delete(normalized) }
90
+ trigger.call
91
+ entry = storage[normalized]
92
+ return entry if entry
93
+ end
94
+
95
+ nil
127
96
  end
128
97
  end
129
98
  end
99
+ end
130
100
 
131
- # Error raised when an unknown filter is requested
132
- class UnknownFilterError < StandardError; end
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|
106
+ Omnizip::FilterRegistry.register_lazy(name) do
107
+ Omnizip::Filters::Registry.register_all
108
+ end
133
109
  end
@@ -16,8 +16,6 @@
16
16
  # See the COPYING file for the complete text of the license.
17
17
  #
18
18
 
19
- require "omnizip/filters"
20
-
21
19
  module Omnizip
22
20
  module Filters
23
21
  # BCJ2 filter for x86/x64 executables (4-stream variant).
@@ -16,8 +16,6 @@
16
16
  # See the COPYING file for the complete text of the license.
17
17
  #
18
18
 
19
- require "omnizip/filters"
20
-
21
19
  module Omnizip
22
20
  module Filters
23
21
  # BCJ (Branch/Call/Jump) filter for x86/x64 executables.
@@ -1,72 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- #
4
- # Copyright (C) 2024 Ribose Inc.
5
- #
6
- # This file is part of Omnizip.
7
- #
8
- # Omnizip is a pure Ruby port of 7-Zip compression algorithms.
9
- # Based on the 7-Zip LZMA SDK by Igor Pavlov.
10
- #
11
- # This library is free software; you can redistribute it and/or
12
- # modify it under the terms of the GNU Lesser General Public
13
- # License as published by the Free Software Foundation; either
14
- # version 2.1 of the License, or (at your option) any later version.
15
- #
16
- # See the COPYING file for the complete text of the license.
17
- #
18
-
19
- module Omnizip
20
- module Filters
21
- # Abstract base class for preprocessing filters.
22
- #
23
- # Filters are reversible transformations applied to data before
24
- # compression to improve compression ratios. They are particularly
25
- # effective for executable files and other structured data.
26
- #
27
- # All filter implementations should inherit from this class and
28
- # implement the required methods.
29
- class FilterBase
30
- # Encode (preprocess) data for compression.
31
- #
32
- # This method transforms data to make it more compressible. The
33
- # transformation must be reversible - decode(encode(data)) == data.
34
- #
35
- # @param data [String] Binary data to encode
36
- # @param position [Integer] Current position in stream (for
37
- # multi-block filtering)
38
- # @raise [NotImplementedError] Must be implemented by subclass
39
- # @return [String] Encoded binary data
40
- def encode(data, position = 0)
41
- raise NotImplementedError,
42
- "#{self.class} must implement #encode"
43
- end
44
-
45
- # Decode (postprocess) data after decompression.
46
- #
47
- # This method reverses the encoding transformation, restoring
48
- # original data.
49
- #
50
- # @param data [String] Binary data to decode
51
- # @param position [Integer] Current position in stream (for
52
- # multi-block filtering)
53
- # @raise [NotImplementedError] Must be implemented by subclass
54
- # @return [String] Decoded binary data
55
- def decode(data, position = 0)
56
- raise NotImplementedError,
57
- "#{self.class} must implement #decode"
58
- end
59
-
60
- class << self
61
- # Get metadata about this filter.
62
- #
63
- # @raise [NotImplementedError] Must be implemented by subclass
64
- # @return [Hash] Filter metadata including name, description
65
- def metadata
66
- raise NotImplementedError,
67
- "#{self} must implement .metadata"
68
- end
69
- end
70
- end
71
- end
72
- end
3
+ # FilterBase is a historical alias for Omnizip::Filter. New code should
4
+ # inherit from Omnizip::Filter directly; FilterBase is kept for
5
+ # backward compatibility with existing subclasses (BcjX86, BcjArm, …).
6
+ Omnizip::Filters::FilterBase = Omnizip::Filter
@@ -16,8 +16,6 @@
16
16
  # See the COPYING file for the complete text of the license.
17
17
  #
18
18
 
19
- require "omnizip/filters"
20
-
21
19
  module Omnizip
22
20
  module Filters
23
21
  # Registry for auto-registering all preprocessing filters.
@@ -104,23 +104,32 @@ module Omnizip
104
104
  detect(file_path) == :rar4
105
105
  end
106
106
 
107
+ # Mapping of detected format symbols to (extension, constant-path)
108
+ # tuples. The constant path is resolved lazily so autoload fires
109
+ # only when a format is actually requested.
110
+ READER_FOR_FORMAT = {
111
+ xz: ["omnizip/formats/xz", "Omnizip::Formats::Xz"],
112
+ seven_zip: ["omnizip/formats/seven_zip", "Omnizip::Formats::SevenZip::Reader"],
113
+ rar5: ["omnizip/formats/rar5", "Omnizip::Formats::Rar5::Reader"],
114
+ rar4: ["omnizip/formats/rar3", "Omnizip::Formats::Rar3::Reader"],
115
+ zip: ["omnizip/formats/zip", "Omnizip::Formats::Zip::Reader"],
116
+ gzip: ["omnizip/formats/gzip", "Omnizip::Formats::Gzip"],
117
+ bzip2: ["omnizip/formats/bzip2_file", "Omnizip::Formats::Bzip2File"],
118
+ }.freeze
119
+
107
120
  # Get the appropriate reader class for the format
108
121
  #
109
122
  # @param file_path [String] Path to the archive file
110
123
  # @return [Class, nil] Reader class or nil if unknown format
111
124
  def self.reader_for(file_path)
112
- case detect(file_path)
113
- when :xz
114
- Omnizip::Formats::Xz
115
- when :seven_zip
116
- Omnizip::Formats::SevenZip::Reader
117
- when :rar5
118
- Omnizip::Formats::Rar5::Reader
119
- when :rar4
120
- Omnizip::Formats::Rar3::Reader
121
- when :zip
122
- Omnizip::Formats::Zip::Reader
123
- end
125
+ format = detect(file_path)
126
+ return nil unless format
127
+
128
+ mapping = READER_FOR_FORMAT[format]
129
+ return nil unless mapping
130
+
131
+ _path, const_path = mapping
132
+ Object.const_get(const_path)
124
133
  end
125
134
 
126
135
  # Detect LZMA_Alone format
@@ -1,42 +1,71 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Omnizip
4
- # Registry for archive format handlers
5
- # Manages different archive format readers (7z, zip, tar, etc.)
6
- class FormatRegistry
4
+ class FormatRegistry < Omnizip::Registry
5
+ BUILTIN_FORMATS = {
6
+ ".7z" => "Omnizip::Formats::SevenZip::Reader",
7
+ ".zip" => "Omnizip::Formats::Zip",
8
+ ".rar" => "Omnizip::Formats::Rar::Reader",
9
+ ".tar" => "Omnizip::Formats::Tar",
10
+ ".gz" => "Omnizip::Formats::Gzip",
11
+ ".gzip" => "Omnizip::Formats::Gzip",
12
+ ".bz2" => "Omnizip::Formats::Bzip2File",
13
+ ".bzip2" => "Omnizip::Formats::Bzip2File",
14
+ ".xz" => "Omnizip::Formats::Xz",
15
+ ".msi" => "Omnizip::Formats::Msi",
16
+ ".msp" => "Omnizip::Formats::Msi",
17
+ ".cpio" => "Omnizip::Formats::Cpio::Reader",
18
+ ".iso" => "Omnizip::Formats::Iso::Reader",
19
+ ".xar" => "Omnizip::Formats::Xar::Reader",
20
+ ".lz" => "Omnizip::Formats::Lzip",
21
+ ".lzip" => "Omnizip::Formats::Lzip",
22
+ ".lzma" => "Omnizip::Formats::LzmaAlone",
23
+ ".ole" => "Omnizip::Formats::Ole::Storage",
24
+ ".doc" => "Omnizip::Formats::Ole::Storage",
25
+ ".xls" => "Omnizip::Formats::Ole::Storage",
26
+ ".ppt" => "Omnizip::Formats::Ole::Storage",
27
+ }.freeze
28
+
7
29
  class << self
8
- # Register a format handler
9
- #
10
- # @param extension [String] File extension (e.g., ".7z", ".zip")
11
- # @param handler_class [Class, String] Format handler class or class name string
12
- def register(extension, handler_class)
13
- registry[normalize_extension(extension)] = handler_class
30
+ def label
31
+ "Format"
32
+ end
33
+
34
+ def normalize_key(extension)
35
+ ext = extension.to_s
36
+ ext = ".#{ext}" unless ext.start_with?(".")
37
+ ext.downcase
14
38
  end
15
39
 
16
- # Get format handler for extension
17
- #
18
- # @param extension [String] File extension
19
- # @return [Class, Module, nil] Handler class/module or nil if not found
20
40
  def get(extension)
21
- handler = registry[normalize_extension(extension)]
22
- return handler if handler.nil? || handler.is_a?(Class) || handler.is_a?(Module)
41
+ normalized = normalize_key(extension)
42
+ handler = storage[normalized]
43
+ return resolve_handler(handler, extension) if handler
23
44
 
24
- # Resolve string to class if needed
25
- resolve_constant(handler)
45
+ trigger = lazy_triggers[normalized]
46
+ if trigger
47
+ synchronize { lazy_triggers.delete(normalized) }
48
+ trigger.call
49
+ handler = storage[normalized]
50
+ return resolve_handler(handler, extension) if handler
51
+ end
52
+
53
+ nil
54
+ end
55
+
56
+ def supported?(extension)
57
+ registered?(extension) || lazy_triggers.key?(normalize_key(extension))
26
58
  end
59
+ alias supported_formats available
27
60
 
28
- # Resolve a string to a constant (class/module)
29
- #
30
- # @param name [String] Fully qualified class name
31
- # @return [Class, Module, nil] Resolved constant or nil
32
61
  def resolve_constant(name)
33
62
  return nil unless name.is_a?(String)
34
63
 
35
- names = name.split("::")
36
- names.shift if names.empty? || names.first.empty?
64
+ parts = name.split("::")
65
+ parts.shift if parts.empty? || parts.first.empty?
37
66
 
38
67
  constant = Object
39
- names.each do |n|
68
+ parts.each do |n|
40
69
  return nil unless constant.const_defined?(n, false)
41
70
 
42
71
  constant = constant.const_get(n)
@@ -46,39 +75,26 @@ module Omnizip
46
75
  nil
47
76
  end
48
77
 
49
- # Check if format is supported
50
- #
51
- # @param extension [String] File extension
52
- # @return [Boolean] true if supported
53
- def supported?(extension)
54
- registry.key?(normalize_extension(extension))
55
- end
56
-
57
- # List all supported formats
58
- #
59
- # @return [Array<String>] Supported extensions
60
- def supported_formats
61
- registry.keys.sort
62
- end
63
-
64
78
  private
65
79
 
66
- # Format registry storage
67
- #
68
- # @return [Hash] Extension to handler class mapping
69
- def registry
70
- @registry ||= {}
71
- end
80
+ def resolve_handler(handler, _original_extension)
81
+ return nil if handler.nil?
82
+ return handler if handler.is_a?(Class) || handler.is_a?(Module)
72
83
 
73
- # Normalize file extension
74
- #
75
- # @param ext [String] Extension
76
- # @return [String] Normalized extension
77
- def normalize_extension(ext)
78
- ext = ext.to_s
79
- ext = ".#{ext}" unless ext.start_with?(".")
80
- ext.downcase
84
+ resolve_constant(handler)
81
85
  end
82
86
  end
83
87
  end
84
88
  end
89
+
90
+ # Lazy triggers — explicitly re-register on each miss so the entry
91
+ # survives reset!. Object.const_get alone doesn't re-run the file body
92
+ # once the constant is loaded.
93
+ Omnizip::FormatRegistry::BUILTIN_FORMATS.each do |ext, const_path|
94
+ Omnizip::FormatRegistry.register_lazy(ext) do
95
+ handler = Object.const_get(const_path)
96
+ # Register under the original extension; some formats (e.g. Rar)
97
+ # store a string class path that needs resolve_constant on get.
98
+ Omnizip::FormatRegistry.register(ext, handler)
99
+ end
100
+ end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "stringio"
4
- require "omnizip/algorithm_registry"
5
4
 
6
5
  module Omnizip
7
6
  module Formats
@@ -115,7 +114,6 @@ module Omnizip
115
114
 
116
115
  # Register BZIP2 format when loaded
117
116
  def register!
118
- require "omnizip/format_registry"
119
117
  FormatRegistry.register(".bz2", Omnizip::Formats::Bzip2File)
120
118
  FormatRegistry.register(".bzip2", Omnizip::Formats::Bzip2File)
121
119
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "omnizip/formats/cpio"
4
-
5
3
  module Omnizip
6
4
  module Formats
7
5
  module Cpio
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "fileutils"
4
- require "omnizip/formats/cpio"
5
4
 
6
5
  module Omnizip
7
6
  module Formats
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "fileutils"
4
- require "omnizip/formats/cpio"
5
4
 
6
5
  module Omnizip
7
6
  module Formats
@@ -129,7 +129,6 @@ module Omnizip
129
129
 
130
130
  # Auto-register CPIO format when loaded
131
131
  def register!
132
- require "omnizip/format_registry"
133
132
  FormatRegistry.register(".cpio", Reader)
134
133
  end
135
134
  end
@@ -200,7 +200,7 @@ module Omnizip
200
200
  # @param key [Symbol, String] The key to retrieve
201
201
  # @return [Object] The value for the key
202
202
  def [](key)
203
- format.send(key)
203
+ format.public_send(key)
204
204
  end
205
205
 
206
206
  # Get magic bytes as an array of integers
@@ -2,7 +2,6 @@
2
2
 
3
3
  require "zlib"
4
4
  require "stringio"
5
- require "omnizip/algorithm_registry"
6
5
 
7
6
  module Omnizip
8
7
  module Formats
@@ -130,7 +129,6 @@ module Omnizip
130
129
 
131
130
  # Register GZIP format when loaded
132
131
  def register!
133
- require "omnizip/format_registry"
134
132
  FormatRegistry.register(".gz", Omnizip::Formats::Gzip)
135
133
  FormatRegistry.register(".gzip", Omnizip::Formats::Gzip)
136
134
  end
@@ -6,9 +6,12 @@ module Omnizip
6
6
  # ISO 9660 Directory Record
7
7
  # Represents a file or directory entry
8
8
  class DirectoryRecord
9
- attr_reader :length, :extended_attr_length, :location, :data_length,
10
- :recording_date, :flags, :file_unit_size, :interleave_gap_size,
11
- :volume_sequence_number, :name, :system_use
9
+ attr_accessor :full_path
10
+ 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
+
12
+ # Set the full path for this record, used by Reader when walking
13
+ # a directory tree. Allows callers to look up records by path
14
+ # without re-joining parent names.
12
15
 
13
16
  # Parse directory record from binary data
14
17
  #
@@ -158,7 +158,7 @@ module Omnizip
158
158
  unless record.current_directory? || record.parent_directory?
159
159
  # Set full path
160
160
  full_path = path_prefix.empty? ? record.name : "#{path_prefix}/#{record.name}"
161
- record.instance_variable_set(:@full_path, full_path)
161
+ record.full_path = full_path
162
162
 
163
163
  @entries << record
164
164
 
@@ -177,7 +177,7 @@ module Omnizip
177
177
  def find_entry(path)
178
178
  # Normalize path
179
179
  path = path.gsub(%r{^/+}, "").gsub(%r{/+$}, "")
180
- @entries.find { |e| e.instance_variable_get(:@full_path) == path }
180
+ @entries.find { |e| e.full_path == path }
181
181
  end
182
182
 
183
183
  # Read file data
@@ -130,7 +130,6 @@ module Omnizip
130
130
 
131
131
  # Auto-register ISO format when loaded
132
132
  def self.register!
133
- require "omnizip/format_registry"
134
133
  FormatRegistry.register(".iso", Reader)
135
134
  end
136
135
  end