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
@@ -0,0 +1,38 @@
1
+ # 11 — Convenience module decoupling
2
+
3
+ Priority: **medium**.
4
+ Status: TODO.
5
+
6
+ ## Problem
7
+
8
+ `lib/omnizip/convenience.rb` (359 lines) hardcodes `Omnizip::Zip::File`
9
+ in 7 method bodies (lines 45, 87, 112, 149, 175, 207, 227) —
10
+ `compress_file`, `compress_directory`, `extract_archive`,
11
+ `list_archive`, `read_from_archive`, `add_to_archive`,
12
+ `remove_from_archive`. The codebase supports 7z, XZ, tar, gzip, bzip2,
13
+ RAR, but convenience methods are ZIP-only.
14
+
15
+ ## Plan
16
+
17
+ 1. Add a `format:` keyword argument to each convenience method.
18
+ Default to `:zip` for backward compatibility.
19
+ 2. Resolve the format handler via `FormatRegistry.get(format)` and
20
+ delegate. If the handler doesn't expose the needed operation, raise a
21
+ clear `UnsupportedFormatError` describing what's missing.
22
+ 3. Each format handler (zip, seven_zip, tar, gzip, xz, bzip2) should
23
+ implement a shared `Handler` interface with `#add_file`,
24
+ `#add_directory`, `#extract`, `#list`, `#read`, `#remove`. This
25
+ becomes the contract for being usable from `convenience.rb`.
26
+
27
+ ## Acceptance
28
+
29
+ - Convenience methods accept `format:` and dispatch via registry.
30
+ - ZIP path remains the default with no behavior change.
31
+ - At least one non-ZIP format (e.g., tar) is wired up end-to-end.
32
+
33
+ ## Scope note
34
+
35
+ Full format handler interface extraction is large. This pass:
36
+ 1. Adds the `format:` keyword with ZIP default.
37
+ 2. Wires tar as a second supported format (simplest after zip).
38
+ 3. Leaves the remaining formats as documented future work.
@@ -0,0 +1,8 @@
1
+ # 12 — Thread safety
2
+
3
+ Priority: covered by track 01.
4
+ Status: n/a.
5
+
6
+ The `Registry` base class introduced in track 01 includes `Mutex`-based
7
+ synchronization around mutations (`register`, `reset!`). All migrated
8
+ registries inherit this for free. No standalone work.
@@ -0,0 +1,82 @@
1
+ # 13 — lutaml-model migration
2
+
3
+ Priority: **medium**.
4
+ Status: TODO (large; this pass establishes the pattern, full migration
5
+ is follow-up).
6
+
7
+ ## Rule
8
+
9
+ > ALL (de)serialization goes through the framework. Declare attributes
10
+ > with `attribute :name, :type`. Declare wire names with a `mapping do
11
+ > ... end` block. Never write `def to_h` / `def from_h` / `def to_json`
12
+ > / `def from_json` on a model class.
13
+
14
+ ## Inventory
15
+
16
+ 26 `def to_h` occurrences across 22 files. 2 `def to_json`. None use
17
+ lutaml-model today even though the gemspec declares the dependency.
18
+
19
+ ### Models (lib/omnizip/models/)
20
+
21
+ - `compression_options.rb` (`to_h`, `to_json`)
22
+ - `algorithm_metadata.rb` (`to_h`, `to_json`)
23
+ - `conversion_result.rb`
24
+ - `eta_result.rb`
25
+ - `filter_config.rb`
26
+ - `match_result.rb`
27
+ - `optimization_suggestion.rb`
28
+ - `parallel_options.rb`
29
+ - `performance_result.rb`
30
+ - `profile_report.rb`
31
+ - `progress_options.rb`
32
+ - `conversion_options.rb`
33
+
34
+ ### Format entry/metadata classes
35
+
36
+ - `formats/xar/entry.rb`
37
+ - `formats/zip/unix_extra_field.rb`
38
+ - `formats/rpm/header.rb`
39
+ - `metadata/entry_metadata.rb`
40
+ - `metadata/archive_metadata.rb`
41
+
42
+ ### Other
43
+
44
+ - `progress/operation_progress.rb`
45
+ - `profile/profile_registry.rb`, `custom_profile.rb`, `compression_profile.rb`
46
+ - `link_handler/symbolic_link.rb`, `hard_link.rb`
47
+
48
+ ## Plan (this pass)
49
+
50
+ 1. Establish the pattern by migrating **two representative models**:
51
+ `Omnizip::Models::CompressionOptions` and
52
+ `Omnizip::Models::AlgorithmMetadata`. Convert to lutaml-model:
53
+ ```ruby
54
+ class CompressionOptions < Lutaml::Model::Serializable
55
+ attribute :algorithm, :string
56
+ attribute :level, :integer, default: 6
57
+ # ...
58
+ mapping do
59
+ map :algorithm, to: :algorithm
60
+ map :level, to: :level
61
+ end
62
+ end
63
+ ```
64
+ The framework provides `to_hash`, `to_json`, `from_hash`,
65
+ `from_json` automatically — remove the hand-rolled versions.
66
+
67
+ 2. Delete the manual `to_h` / `to_json` from those two files.
68
+
69
+ 3. Document the migration recipe in this TODO file for the remaining
70
+ models.
71
+
72
+ ## Acceptance
73
+
74
+ - Two models migrated; their specs still pass.
75
+ - `grep -rn "def to_h\|def to_json" lib/omnizip/models/` count drops by
76
+ at least 2.
77
+
78
+ ## Remaining work (future pass)
79
+
80
+ Migrate the other ~24 files following the established recipe. The
81
+ metadata/entry classes likely share structure and may collapse into a
82
+ smaller set of richer models — review for MECE opportunities.
@@ -0,0 +1,25 @@
1
+ # 14 — Add missing specs
2
+
3
+ Priority: **high**.
4
+ Status: TODO.
5
+
6
+ ## Plan
7
+
8
+ Add or strengthen specs for:
9
+
10
+ 1. `Omnizip::Registry` (new base class from track 01) — register / get /
11
+ registered? / available / reset! / thread-safety smoke test / custom
12
+ not-found error.
13
+ 2. `Omnizip::Error` hierarchy (track 02) — confirm inheritance and
14
+ aliases.
15
+ 3. `Omnizip::Cli::Shared` (track 09) — `handle_error` and
16
+ `format_bytes`.
17
+ 4. `Omnizip::IO::Source` / `Omnizip::IO::Sink` (track 07) — adapter
18
+ behavior for String / StringIO / File / Pathname.
19
+ 5. Promoted public methods (track 05) — wherever a method moves from
20
+ private to public, ensure a spec covers its new visibility.
21
+
22
+ ## Acceptance
23
+
24
+ - All new specs pass.
25
+ - No `double()` introduced.
@@ -16,8 +16,6 @@
16
16
  # See the COPYING file for the complete text of the license.
17
17
  #
18
18
 
19
- require "omnizip/algorithm_registry"
20
-
21
19
  module Omnizip
22
20
  # Abstract base class for compression algorithms.
23
21
  #
@@ -17,70 +17,42 @@
17
17
  #
18
18
 
19
19
  module Omnizip
20
- # Registry for managing compression algorithm classes.
21
- #
22
- # This class provides a centralized registry for compression algorithms,
23
- # allowing algorithms to self-register and be retrieved by name.
24
- # It implements a plugin-style architecture for extensibility.
25
- class AlgorithmRegistry
26
- class << self
27
- # Register an algorithm class with the registry.
28
- #
29
- # @param name [Symbol, String] The name identifier for the algorithm
30
- # @param klass [Class] The algorithm class to register
31
- # @raise [ArgumentError] If name or klass is nil
32
- # @return [void]
33
- def register(name, klass)
34
- raise ArgumentError, "Algorithm name cannot be nil" if name.nil?
35
- raise ArgumentError, "Algorithm class cannot be nil" if klass.nil?
36
-
37
- algorithms[name.to_sym] = klass
38
- end
39
-
40
- # Retrieve an algorithm class by name.
41
- #
42
- # @param name [Symbol, String] The name identifier for the algorithm
43
- # @raise [UnknownAlgorithmError] If algorithm is not registered
44
- # @return [Class] The registered algorithm class
45
- def get(name)
46
- algorithm = algorithms[name.to_sym]
47
- return algorithm if algorithm
48
-
49
- raise UnknownAlgorithmError,
50
- "Unknown algorithm: #{name}. " \
51
- "Available: #{available.join(', ')}"
52
- end
20
+ class AlgorithmRegistry < Omnizip::Registry
21
+ BUILTIN_ALGORITHMS = {
22
+ lzma: "Omnizip::Algorithms::LZMA",
23
+ lzma2: "Omnizip::Algorithms::LZMA2",
24
+ ppmd7: "Omnizip::Algorithms::PPMd7",
25
+ ppmd8: "Omnizip::Algorithms::PPMd8",
26
+ bzip2: "Omnizip::Algorithms::BZip2",
27
+ deflate: "Omnizip::Algorithms::Deflate",
28
+ deflate64: "Omnizip::Algorithms::Deflate64",
29
+ zstandard: "Omnizip::Algorithms::Zstandard",
30
+ }.freeze
53
31
 
54
- # Check if an algorithm is registered.
55
- #
56
- # @param name [Symbol, String] The name identifier for the algorithm
57
- # @return [Boolean] True if algorithm is registered, false otherwise
58
- def registered?(name)
59
- algorithms.key?(name.to_sym)
60
- end
61
-
62
- # Get list of all registered algorithm names.
63
- #
64
- # @return [Array<Symbol>] Array of registered algorithm names
65
- def available
66
- algorithms.keys
32
+ class << self
33
+ def not_found_error_class
34
+ Omnizip::UnknownAlgorithmError
67
35
  end
68
36
 
69
- # Reset the registry (primarily for testing).
70
- #
71
- # @return [void]
72
- def reset!
73
- algorithms.clear
37
+ def label
38
+ "Algorithm"
74
39
  end
75
40
 
76
- private
41
+ def register(name, klass)
42
+ raise ArgumentError, "Algorithm name cannot be nil" if name.nil?
43
+ raise ArgumentError, "Algorithm class cannot be nil" if klass.nil?
77
44
 
78
- # Get or initialize the algorithms hash.
79
- #
80
- # @return [Hash] The algorithms registry
81
- def algorithms
82
- @algorithms ||= {}
45
+ super
83
46
  end
84
47
  end
85
48
  end
86
49
  end
50
+
51
+ # Lazy triggers — explicitly re-register on each miss so the entry
52
+ # survives reset!. Object.const_get alone doesn't re-run the file body
53
+ # once the constant is loaded.
54
+ Omnizip::AlgorithmRegistry::BUILTIN_ALGORITHMS.each do |name, const_path|
55
+ Omnizip::AlgorithmRegistry.register_lazy(name) do
56
+ Omnizip::AlgorithmRegistry.register(name, Object.const_get(const_path))
57
+ end
58
+ end
@@ -6,7 +6,8 @@ module Omnizip
6
6
  # Circular buffer dictionary for LZMA sliding window
7
7
  # Ported from XZ Utils lzma_decoder.c
8
8
  class Dictionary
9
- attr_reader :size, :position, :buffer
9
+ attr_accessor :position, :buffer
10
+ attr_reader :size
10
11
 
11
12
  def initialize(size)
12
13
  @size = size
@@ -59,8 +60,8 @@ module Omnizip
59
60
  # Clone dictionary
60
61
  def clone
61
62
  dict = Dictionary.new(@size)
62
- dict.instance_variable_set(:@buffer, @buffer.dup)
63
- dict.instance_variable_set(:@position, @position)
63
+ dict.buffer = @buffer.dup
64
+ dict.position = @position
64
65
  dict
65
66
  end
66
67
  end
@@ -52,6 +52,8 @@ module Omnizip
52
52
  # result = decoder.decode_stream
53
53
  #
54
54
  class LzipDecoder
55
+ attr_reader :version, :dict_size, :member_size
56
+
55
57
  # Lzip magic bytes: "LZIP" in ASCII
56
58
  # Reference: lzip_decoder.c:106
57
59
  MAGIC = [0x4C, 0x5A, 0x49, 0x50].freeze
@@ -33,7 +33,7 @@ module Omnizip
33
33
  # extract the original bit values. It maintains a code value
34
34
  # that represents the current position within the range.
35
35
  class RangeDecoder < RangeCoder
36
- attr_reader :code, :init_bytes_remaining
36
+ attr_reader :code, :init_bytes_remaining, :range, :stream
37
37
 
38
38
  # Initialize the range decoder
39
39
  #
@@ -86,7 +86,8 @@ module Omnizip
86
86
  RangeDecoder = LZMA::RangeDecoder
87
87
  SdkStateMachine = Implementations::SevenZip::LZMA::StateMachine
88
88
 
89
- attr_reader :lc, :lp, :pb, :dict_size, :uncompressed_size
89
+ attr_reader :lc, :lp, :pb, :dict_size, :uncompressed_size,
90
+ :range_decoder
90
91
 
91
92
  # XZ Utils dictionary constants (from lz_decoder.h)
92
93
  # See: /Users/mulgogi/src/external/xz/src/liblzma/lz/lz_decoder.h
@@ -431,8 +432,8 @@ check_rc_finished: true)
431
432
  #
432
433
  # If EOPM was NOT encountered (code != 0), leftover data is expected
433
434
  # (it's part of the compressed stream that we haven't read yet).
434
- if @allow_eopm && @range_decoder&.code&.zero? && @range_decoder.instance_variable_get(:@stream)
435
- stream = @range_decoder.instance_variable_get(:@stream)
435
+ if @allow_eopm && @range_decoder&.code&.zero? && @range_decoder&.stream
436
+ stream = @range_decoder.stream
436
437
  # Try to peek at the next byte - if available, there's data AFTER EOPM
437
438
  begin
438
439
  next_byte = stream.getbyte
@@ -445,9 +446,9 @@ check_rc_finished: true)
445
446
  rescue IOError, EOFError
446
447
  # Stream doesn't support peeking or is exhausted, that's fine
447
448
  end
448
- elsif !@allow_eopm && @range_decoder&.instance_variable_get(:@stream)
449
+ elsif !@allow_eopm && @range_decoder&.stream
449
450
  # For LZMA2 mode (EOPM not allowed): check for leftover data
450
- stream = @range_decoder.instance_variable_get(:@stream)
451
+ stream = @range_decoder.stream
451
452
  begin
452
453
  next_byte = stream.getbyte
453
454
  if next_byte
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "omnizip/algorithms"
4
-
5
3
  module Omnizip
6
4
  module Algorithms
7
5
  # LZMA2 encoder - delegates to XzLZMA2Encoder
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "omnizip/algorithms"
4
-
5
3
  module Omnizip
6
4
  module Algorithms
7
5
  class LZMA2 < Algorithm
@@ -20,8 +20,6 @@
20
20
  # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21
21
  # DEALINGS IN THE SOFTWARE.
22
22
 
23
- require "omnizip/algorithms"
24
-
25
23
  module Omnizip
26
24
  module Algorithms
27
25
  class LZMA2
@@ -36,7 +36,7 @@ module Omnizip
36
36
  class Model
37
37
  include Constants
38
38
 
39
- attr_reader :max_order, :root_context, :current_context
39
+ attr_reader :max_order, :root_context, :current_context, :mem_size
40
40
 
41
41
  # Initialize the PPMd7 model
42
42
  #
@@ -2,8 +2,6 @@
2
2
 
3
3
  # Copyright (C) 2025 Ribose Inc.
4
4
 
5
- require "omnizip/algorithms/ppmd7/context"
6
-
7
5
  module Omnizip
8
6
  module Algorithms
9
7
  class PPMd8 < PPMdBase
@@ -35,7 +35,7 @@ module Omnizip
35
35
  include Constants
36
36
 
37
37
  attr_reader :max_order, :root_context, :current_context,
38
- :restoration_method, :run_length, :glue_count
38
+ :restoration_method, :run_length, :glue_count, :mem_size
39
39
  attr_accessor :order_fall, :prev_success
40
40
 
41
41
  # Initialize the PPMd8 model
@@ -20,8 +20,6 @@
20
20
  # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21
21
  # DEALINGS IN THE SOFTWARE.
22
22
 
23
- require "omnizip/algorithms"
24
-
25
23
  module Omnizip
26
24
  module Algorithms
27
25
  # PPMd8 (PPMdI) compression algorithm
@@ -20,8 +20,6 @@
20
20
  # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21
21
  # DEALINGS IN THE SOFTWARE.
22
22
 
23
- require "omnizip/algorithm"
24
-
25
23
  module Omnizip
26
24
  module Algorithms
27
25
  # Base class for PPMd (Prediction by Partial Matching) algorithms
@@ -20,11 +20,6 @@
20
20
  # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21
21
  # DEALINGS IN THE SOFTWARE.
22
22
 
23
- require "omnizip/algorithm"
24
- require "omnizip/models/algorithm_metadata"
25
- require "omnizip/algorithms/lzma2/encoder"
26
- require "omnizip/algorithms/lzma2/decoder"
27
-
28
23
  module Omnizip
29
24
  module Algorithms
30
25
  # 7-Zip LZMA2 compression algorithm.
@@ -20,11 +20,6 @@
20
20
  # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21
21
  # DEALINGS IN THE SOFTWARE.
22
22
 
23
- require "omnizip/algorithm"
24
- require "omnizip/models/algorithm_metadata"
25
- require "omnizip/implementations/xz_utils/lzma2/encoder"
26
- require "omnizip/implementations/xz_utils/lzma2/decoder"
27
-
28
23
  module Omnizip
29
24
  module Algorithms
30
25
  # XZ Utils LZMA2 compression algorithm.
@@ -69,15 +69,15 @@ module Omnizip
69
69
 
70
70
  # Parse optional fields based on descriptor bits
71
71
  if header.window_descriptor?
72
- header.send(:parse_window_descriptor, input)
72
+ header.parse_window_descriptor(input)
73
73
  end
74
74
 
75
75
  if header.dictionary_id?
76
- header.send(:parse_dictionary_id, input)
76
+ header.parse_dictionary_id(input)
77
77
  end
78
78
 
79
79
  if header.content_size?
80
- header.send(:parse_content_size, input)
80
+ header.parse_content_size(input)
81
81
  end
82
82
 
83
83
  header
@@ -174,8 +174,6 @@ module Omnizip
174
174
  (1 << @window_log) + (mantissa << (@window_log - 4))
175
175
  end
176
176
 
177
- private
178
-
179
177
  # Parse window descriptor byte
180
178
  def parse_window_descriptor(input)
181
179
  byte = input.read(1).ord
@@ -3,6 +3,7 @@
3
3
  module Omnizip
4
4
  # Compression algorithm implementations
5
5
  module Algorithms
6
+ autoload :PPMdBase, "omnizip/algorithms/ppmd_base"
6
7
  autoload :LZMA2Const, "omnizip/algorithms/lzma2/constants"
7
8
  autoload :LZMA, "omnizip/algorithms/lzma"
8
9
  autoload :LZMA2, "omnizip/algorithms/lzma2"
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Omnizip
4
+ # Unified archive handler interface for the Convenience module.
5
+ #
6
+ # Each format that wants to participate in Omnizip.compress_file /
7
+ # extract_archive / etc. registers a Handler here. The Handler exposes
8
+ # a small canonical API (+create+, +open+, +extract_to+, +list+) that
9
+ # the Convenience module calls; the Handler then delegates to the
10
+ # format-specific reader/writer.
11
+ #
12
+ # @example Register a format
13
+ # Omnizip::ArchiveHandler.register(:tar, TarHandler.new)
14
+ #
15
+ # @example Use from a caller
16
+ # Omnizip::ArchiveHandler.for(:tar).create(path) { |w| w.add_file(...) }
17
+ module ArchiveHandler
18
+ @handlers = {}
19
+
20
+ class << self
21
+ # Register a handler for a format symbol.
22
+ #
23
+ # @param format [Symbol] e.g. +:zip+, +:tar+
24
+ # @param handler [Object] responding to the Handler interface
25
+ # @return [void]
26
+ def register(format, handler)
27
+ @handlers[format.to_sym] = handler
28
+ end
29
+
30
+ # Look up the handler for +format+. Triggers the registered lazy
31
+ # load constant if the format is one of the built-ins.
32
+ #
33
+ # @param format [Symbol]
34
+ # @return [Object]
35
+ # @raise [Omnizip::UnsupportedFormatError] if +format+ is unknown
36
+ def for(format)
37
+ handler = @handlers[format.to_sym]
38
+ return handler if handler
39
+
40
+ trigger = LAZY_LOAD_TRIGGERS[format.to_sym]
41
+ if trigger
42
+ trigger.call
43
+ handler = @handlers[format.to_sym]
44
+ return handler if handler
45
+ end
46
+
47
+ raise Omnizip::UnsupportedFormatError,
48
+ "No archive handler registered for #{format.inspect}. " \
49
+ "Registered: #{@handlers.keys.join(', ')}"
50
+ end
51
+
52
+ # List of registered format names.
53
+ #
54
+ # @return [Array<Symbol>]
55
+ def available
56
+ @handlers.keys
57
+ end
58
+
59
+ # Remove a handler (primarily for testing).
60
+ #
61
+ # @return [void]
62
+ def unregister(format)
63
+ @handlers.delete(format.to_sym)
64
+ end
65
+ end
66
+
67
+ # Lazy triggers — referencing the constant autoloads the handler's
68
+ # file, which then self-registers at the bottom of the file.
69
+ LAZY_LOAD_TRIGGERS = {
70
+ zip: -> { Omnizip::ArchiveHandlers::ZipHandler },
71
+ tar: -> { Omnizip::ArchiveHandlers::TarHandler },
72
+ }.freeze
73
+ private_constant :LAZY_LOAD_TRIGGERS
74
+ end
75
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Omnizip
4
+ module ArchiveHandlers
5
+ # Adapter that exposes the canonical +create+/+open+/+extract_to+
6
+ # /+list+ interface for the TAR format. Wraps +Omnizip::Formats::Tar+.
7
+ class TarHandler
8
+ def create(path, &block)
9
+ Omnizip::Formats::Tar.create(path, &block)
10
+ end
11
+
12
+ def open(path, &block)
13
+ Omnizip::Formats::Tar.open(path, &block)
14
+ end
15
+
16
+ def extract_to(path, output_dir, **_)
17
+ Omnizip::Formats::Tar.extract(path, output_dir)
18
+ end
19
+
20
+ def list(path, details: false, **_)
21
+ entries = Omnizip::Formats::Tar.list(path)
22
+ if details
23
+ entries.map do |entry|
24
+ {
25
+ name: entry.name,
26
+ size: entry.size,
27
+ directory: entry.directory?,
28
+ mtime: entry.mtime,
29
+ mode: entry.mode,
30
+ }
31
+ end
32
+ else
33
+ entries.map(&:name)
34
+ end
35
+ end
36
+
37
+ def read_entry(path, entry_name)
38
+ data = nil
39
+ Omnizip::Formats::Tar.open(path) do |reader|
40
+ entry = reader.entries.find { |e| e.name == entry_name }
41
+ raise Errno::ENOENT, "Entry not found: #{entry_name}" unless entry
42
+
43
+ data = entry.data
44
+ end
45
+ data
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ Omnizip::ArchiveHandler.register(:tar, Omnizip::ArchiveHandlers::TarHandler.new)